add alignment

This commit is contained in:
bridge
2025-10-02 21:24:50 +08:00
parent f4ef5a00de
commit 939b1102eb
10 changed files with 167 additions and 9 deletions

View File

@@ -6,6 +6,7 @@ import random
from src.classes.root import Root, get_essence_types_for_root, extra_breakthrough_success_rate
from src.classes.region import Region, CultivateRegion, NormalRegion, CityRegion
from src.classes.alignment import Alignment
from src.classes.event import Event, NULL_EVENT
from src.classes.item import Item, items_by_name
from src.classes.prices import prices
@@ -689,3 +690,77 @@ class Battle(DefineAction, ActualActionMixin):
winner, loser = res
return [Event(self.world.month_stamp, f"{winner} 战胜了 {loser}")]
return []
@long_action(step_month=3)
class PlunderMortals(DefineAction, ActualActionMixin):
"""
在城镇对凡人进行搜刮,获取少量灵石。
仅邪阵营可执行。
"""
COMMENT = "在城镇搜刮凡人,获取少量灵石"
DOABLES_REQUIREMENTS = "仅限城市区域,且角色阵营为‘邪’"
PARAMS = {}
GAIN = 20
def _execute(self) -> None:
region = self.avatar.tile.region
if not isinstance(region, CityRegion):
return
gain = self.GAIN
self.avatar.magic_stone = self.avatar.magic_stone + gain
def can_start(self) -> bool:
region = self.avatar.tile.region
if not isinstance(region, CityRegion):
return False
return self.avatar.alignment == Alignment.EVIL
def start(self) -> Event:
return Event(self.world.month_stamp, f"{self.avatar.name} 在城镇开始搜刮凡人")
def step(self) -> tuple[StepStatus, list[Event]]:
self.execute()
return (StepStatus.COMPLETED if getattr(self, "is_finished")() else StepStatus.RUNNING), []
def finish(self) -> list[Event]:
return []
@long_action(step_month=3)
class HelpMortals(DefineAction, ActualActionMixin):
"""
在城镇帮助凡人,消耗少量灵石。
仅正阵营可执行。
"""
COMMENT = "在城镇帮助凡人,消耗少量灵石"
DOABLES_REQUIREMENTS = "仅限城市区域,且角色阵营为‘正’,并且灵石足够"
PARAMS = {}
COST = 10
def _execute(self) -> None:
region = self.avatar.tile.region
if not isinstance(region, CityRegion):
return
cost = self.COST
if getattr(self.avatar.magic_stone, "value", 0) >= cost:
self.avatar.magic_stone = self.avatar.magic_stone - cost
def can_start(self) -> bool:
region = self.avatar.tile.region
if not isinstance(region, CityRegion):
return False
if self.avatar.alignment != Alignment.RIGHTEOUS:
return False
cost = self.COST
return getattr(self.avatar.magic_stone, "value", 0) >= cost
def start(self) -> Event:
return Event(self.world.month_stamp, f"{self.avatar.name} 在城镇开始帮助凡人")
def step(self) -> tuple[StepStatus, list[Event]]:
self.execute()
return (StepStatus.COMPLETED if getattr(self, "is_finished")() else StepStatus.RUNNING), []
def finish(self) -> list[Event]:
return []

View File

@@ -13,6 +13,8 @@ from src.classes.action import (
Harvest,
Sold,
Battle,
PlunderMortals,
HelpMortals,
)
from src.classes.mutual_action import (
DriveAway,
@@ -33,6 +35,8 @@ ALL_ACTION_CLASSES = [
Hunt,
Harvest,
Sold,
PlunderMortals,
HelpMortals,
# 互动相关动作(实际执行的反馈动作也纳入)
DriveAway,
Attack,
@@ -49,6 +53,8 @@ ALL_ACTUAL_ACTION_CLASSES = [
Hunt,
Harvest,
Sold,
PlunderMortals,
HelpMortals,
DriveAway,
Attack,
]

21
src/classes/alignment.py Normal file
View File

@@ -0,0 +1,21 @@
from enum import Enum
class Alignment(Enum):
"""
阵营:正/邪。
值使用英文,便于与代码/保存兼容__str__ 返回中文。
"""
RIGHTEOUS = "righteous" # 正
EVIL = "evil" # 邪
def __str__(self) -> str:
return alignment_strs.get(self, self.value)
alignment_strs = {
Alignment.RIGHTEOUS: "",
Alignment.EVIL: "",
}

View File

@@ -25,6 +25,7 @@ from src.utils.id_generator import get_avatar_id
from src.utils.config import CONFIG
from src.classes.relation import Relation
from src.run.log import get_logger
from src.classes.alignment import Alignment
persona_num = CONFIG.avatar.persona_num
@@ -73,6 +74,7 @@ class Avatar:
hp: HP = field(default_factory=lambda: HP(0, 0)) # 将在__post_init__中初始化
mp: MP = field(default_factory=lambda: MP(0, 0)) # 将在__post_init__中初始化
relations: dict["Avatar", Relation] = field(default_factory=dict)
alignment: Alignment = field(default_factory=lambda: random.choice(list(Alignment)))
def __post_init__(self):
"""
@@ -101,7 +103,7 @@ class Avatar:
尽量多打一些因为会用来给LLM进行决策
"""
personas_str = ", ".join([persona.name for persona in self.personas])
return f"Avatar(id={self.id}, 性别={self.gender}, 年龄={self.age}, name={self.name}, 区域={self.tile.region.name}, 灵根={str(self.root)}, 境界={self.cultivation_progress}, HP={self.hp}, MP={self.mp}, 个性={personas_str})"
return f"Avatar(id={self.id}, 性别={self.gender}, 年龄={self.age}, name={self.name}, 阵营={self.alignment}, 区域={self.tile.region.name}, 灵根={str(self.root)}, 境界={self.cultivation_progress}, HP={self.hp}, MP={self.mp}, 个性={personas_str})"
def __str__(self) -> str:
return self.get_info()
@@ -437,11 +439,11 @@ class Avatar:
def get_other_avatar_info(self, other_avatar: "Avatar") -> str:
"""
仅显示个字段:名字、境界、关系。
仅显示4个字段:名字、境界、关系、阵营
"""
relation = self.get_relation(other_avatar)
relation_str = str(relation)
return f"{other_avatar.name},境界:{other_avatar.cultivation_progress.get_simple_info()},关系:{relation_str}"
return f"{other_avatar.name},境界:{other_avatar.cultivation_progress.get_simple_info()},关系:{relation_str},阵营:{other_avatar.alignment}"
@property
def move_step_length(self) -> int:

View File

@@ -25,4 +25,31 @@ class AvatarManager:
same_region.append(other)
return same_region
def remove_avatar(self, avatar_id: str) -> None:
"""
从管理器中删除一个 avatar并清理所有与其相关的双向关系。
"""
avatar = self.avatars.get(avatar_id)
if avatar is None:
return
# 先清理与其直接记录的关系(会保持对称)
related = list(getattr(avatar, "relations", {}).keys())
for other in related:
avatar.clear_relation(other)
# 再次扫一遍所有 avatar确保不存在残留引用
for other in list(self.avatars.values()):
if other is avatar:
continue
if getattr(other, "relations", None) is not None and avatar in other.relations:
other.clear_relation(avatar)
# 最后移除自身
self.avatars.pop(avatar_id, None)
def remove_avatars(self, avatar_ids: List[str]) -> None:
"""
批量删除 avatars并清理所有关系。
"""
for aid in list(avatar_ids):
self.remove_avatar(aid)

View File

@@ -141,6 +141,7 @@ def draw_tooltip_for_avatar(pygame_mod, screen, colors, font, avatar: Avatar):
f"{avatar.name}",
f"性别: {avatar.gender}",
f"年龄: {avatar.age}",
f"阵营: {avatar.alignment}",
f"境界: {str(avatar.cultivation_progress)}",
f"HP: {avatar.hp}",
f"MP: {avatar.mp}",

View File

@@ -24,6 +24,7 @@ from src.utils.id_generator import get_avatar_id
from src.utils.config import CONFIG
from src.run.log import get_logger
from src.classes.relation import Relation
from src.classes.alignment import Alignment
def clamp(value: int, lo: int, hi: int) -> int:
@@ -85,6 +86,8 @@ def make_avatars(world: World, count: int = 12, current_month_stamp: MonthStamp
root=random.choice(list(Root)), # 随机选择灵根
)
avatar.tile = world.map.get_tile(x, y)
# 随机分配阵营(正/邪)
avatar.alignment = random.choice(list(Alignment))
avatars[avatar.id] = avatar
# —— 为演示添加少量示例关系 ——
avatar_list = list(avatars.values())

View File

@@ -56,7 +56,7 @@ class Simulator:
if new_events:
events.extend(new_events)
# 结算战斗等导致的死亡HP<=0与寿命逻辑
# 结算战斗等导致的死亡逻辑
for avatar_id, avatar in list(self.world.avatar_manager.avatars.items()):
if avatar.hp <= 0:
death_avatar_ids.append(avatar_id)
@@ -66,11 +66,15 @@ class Simulator:
death_avatar_ids.append(avatar_id)
event = Event(self.world.month_stamp, f"{avatar.name} 老死了,时年{avatar.age.get_age()}")
events.append(event)
# 删除死亡的角色(由 AvatarManager 清理关系并移除)
if death_avatar_ids:
self.world.avatar_manager.remove_avatars(death_avatar_ids)
# 寿命逻辑
for avatar_id, avatar in self.world.avatar_manager.avatars.items():
avatar.update_age(self.world.month_stamp)
# 删除死亡的角色
for avatar_id in death_avatar_ids:
self.world.avatar_manager.avatars.pop(avatar_id)
# 新角色
if random.random() < self.birth_rate:

View File

@@ -20,4 +20,4 @@ df:
ids_separator: ";"
avatar:
persona_num: 1
persona_num: 3

View File

@@ -1,3 +1,22 @@
id,name,exclusion_ids,prompt
,,和本persona互斥的persona的id,输入给LLM的prompt
12,复仇,11;14,你是一个复仇心强的人,你绝不轻易放下仇怨,为了复仇愿意付出代价与时间。你要立刻复仇
1,理性,2;5,你是一个理性的人,你总是会用逻辑来思考问题,做事会谋定而后动
2,无常,1;9;20,你是一个无常的人,目标飘忽不定,不会长期坚持一个目标。
3,怠惰,4;20,你是一个怠惰的人,你总是会拖延,不想努力,更热衷于享受人生。
4,冒险,3;10,你是一个冒险的人,你总是会冒险,喜欢刺激,总想放手一搏。
5,随性,1;20,你是一个随性的人,你总是会随机应变,性子到哪里了就是哪里,没有一定之规。
6,贪财,,你是一个贪财的人,你对灵石和财富有着强烈的渴望。
7,采药,,你是一个热爱采集的人,喜欢在山林中寻找各种奇花异草和灵药,对植物有着敏锐的直觉和深厚的兴趣。你认为大自然的恩赐需要用心去发现和珍惜。
8,猎者,,你是一个热爱狩猎的人,享受在野外追踪猎物的刺激感,对各种动物的习性了如指掌。你相信通过狩猎能够磨练自己的意志和技能,获得更强大的力量。
9,沉思,2,你是一个沉思的人,你总是会深思熟虑,思考问题比较有哲理。
10,惜命,4;20,你是一个惜命的人,你总是会珍惜自己的生命,不会轻易冒险。
11,友爱,13;14;15;12;20,你是一个友爱的人,你重视同伴与和谐,乐于助人,倾向通过协作与沟通化解矛盾。
12,复仇,11;14,你是一个复仇心强的人,你绝不轻易放下仇怨,为了复仇愿意付出代价与时间。
13,孤僻,11,你是一个孤僻的人,你喜欢独处,避免与人深交,更信赖自己的判断与行动。
14,淡漠,11;12;15;20,你是一个淡漠的人,你情感克制,对外界冷静疏离,不轻易被他人或事件影响。
15,好斗,11;14;10;17,你是一个好斗的人,你直面冲突,偏好以力量与对抗解决问题,越挫越勇。
16,鲁莽,1;9;10,你是一个鲁莽的人,你行事冲动、少考虑后果,常凭直觉立刻行动。
17,胆小,4;15;12;20,你是一个胆小的人,你谨小慎微,容易畏惧风险,倾向回避正面冲突。
18,霸道,11;17,你是一个霸道的人,你行事强势,不讲道理,习惯以自己的利益为先,倾向多吃多占、压人一步,对他人的反对不以为意。
19,修行痴迷,2;3;5,你是一个对修行极度痴迷的人,你将绝大多数时间用于修炼,厌恶与修行无关的社交与享乐。
20,极端,11;14;2;5;3;10;17,你是一个极端的人,你仇视对立阵营,如果你是正义阵营,那么你极度正义;如果你是邪恶阵营,那么你极度邪恶。
1 id name exclusion_ids prompt
2 和本persona互斥的persona的id 输入给LLM的prompt
3 12 1 复仇 理性 11;14 2;5 你是一个复仇心强的人,你绝不轻易放下仇怨,为了复仇愿意付出代价与时间。你要立刻复仇。 你是一个理性的人,你总是会用逻辑来思考问题,做事会谋定而后动。
4 2 无常 1;9;20 你是一个无常的人,目标飘忽不定,不会长期坚持一个目标。
5 3 怠惰 4;20 你是一个怠惰的人,你总是会拖延,不想努力,更热衷于享受人生。
6 4 冒险 3;10 你是一个冒险的人,你总是会冒险,喜欢刺激,总想放手一搏。
7 5 随性 1;20 你是一个随性的人,你总是会随机应变,性子到哪里了就是哪里,没有一定之规。
8 6 贪财 你是一个贪财的人,你对灵石和财富有着强烈的渴望。
9 7 采药 你是一个热爱采集的人,喜欢在山林中寻找各种奇花异草和灵药,对植物有着敏锐的直觉和深厚的兴趣。你认为大自然的恩赐需要用心去发现和珍惜。
10 8 猎者 你是一个热爱狩猎的人,享受在野外追踪猎物的刺激感,对各种动物的习性了如指掌。你相信通过狩猎能够磨练自己的意志和技能,获得更强大的力量。
11 9 沉思 2 你是一个沉思的人,你总是会深思熟虑,思考问题比较有哲理。
12 10 惜命 4;20 你是一个惜命的人,你总是会珍惜自己的生命,不会轻易冒险。
13 11 友爱 13;14;15;12;20 你是一个友爱的人,你重视同伴与和谐,乐于助人,倾向通过协作与沟通化解矛盾。
14 12 复仇 11;14 你是一个复仇心强的人,你绝不轻易放下仇怨,为了复仇愿意付出代价与时间。
15 13 孤僻 11 你是一个孤僻的人,你喜欢独处,避免与人深交,更信赖自己的判断与行动。
16 14 淡漠 11;12;15;20 你是一个淡漠的人,你情感克制,对外界冷静疏离,不轻易被他人或事件影响。
17 15 好斗 11;14;10;17 你是一个好斗的人,你直面冲突,偏好以力量与对抗解决问题,越挫越勇。
18 16 鲁莽 1;9;10 你是一个鲁莽的人,你行事冲动、少考虑后果,常凭直觉立刻行动。
19 17 胆小 4;15;12;20 你是一个胆小的人,你谨小慎微,容易畏惧风险,倾向回避正面冲突。
20 18 霸道 11;17 你是一个霸道的人,你行事强势,不讲道理,习惯以自己的利益为先,倾向多吃多占、压人一步,对他人的反对不以为意。
21 19 修行痴迷 2;3;5 你是一个对修行极度痴迷的人,你将绝大多数时间用于修炼,厌恶与修行无关的社交与享乐。
22 20 极端 11;14;2;5;3;10;17 你是一个极端的人,你仇视对立阵营,如果你是正义阵营,那么你极度正义;如果你是邪恶阵营,那么你极度邪恶。