170 lines
6.8 KiB
Python
170 lines
6.8 KiB
Python
import random
|
||
import asyncio
|
||
import sys
|
||
import os
|
||
from typing import List, Tuple, Dict, Any
|
||
|
||
# 添加项目根目录到Python路径
|
||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
||
|
||
# 依赖项目内部模块
|
||
from src.front.front import Front
|
||
from src.sim.simulator import Simulator
|
||
from src.classes.world import World
|
||
from src.classes.map import Map
|
||
from src.classes.tile import TileType
|
||
from src.classes.avatar import Avatar, Gender
|
||
from src.classes.calendar import Month, Year, MonthStamp, create_month_stamp
|
||
from src.classes.cultivation import CultivationProgress
|
||
from src.classes.root import Root
|
||
from src.classes.age import Age
|
||
from src.run.create_map import create_cultivation_world_map, add_sect_headquarters
|
||
from src.utils.names import get_random_name, get_random_name_for_sect
|
||
from src.utils.id_generator import get_avatar_id
|
||
from src.utils.config import CONFIG
|
||
from src.classes.sect import sects_by_id
|
||
from src.classes.alignment import Alignment
|
||
from src.run.log import get_logger
|
||
from src.classes.relation import Relation
|
||
from src.classes.technique import get_technique_by_sect, attribute_to_root
|
||
|
||
|
||
def clamp(value: int, lo: int, hi: int) -> int:
|
||
return max(lo, min(hi, value))
|
||
|
||
|
||
def circle_points(cx: int, cy: int, r: int, width: int, height: int) -> List[Tuple[int, int]]:
|
||
pts: List[Tuple[int, int]] = []
|
||
r2 = r * r
|
||
for y in range(clamp(cy - r, 0, height - 1), clamp(cy + r, 0, height - 1) + 1):
|
||
for x in range(clamp(cx - r, 0, width - 1), clamp(cx + r, 0, width - 1) + 1):
|
||
if (x - cx) * (x - cx) + (y - cy) * (y - cy) <= r2:
|
||
pts.append((x, y))
|
||
return pts
|
||
|
||
def random_gender() -> Gender:
|
||
return Gender.MALE if random.random() < 0.5 else Gender.FEMALE
|
||
|
||
|
||
def make_avatars(world: World, count: int = 12, current_month_stamp: MonthStamp = MonthStamp(100 * 12)) -> dict[str, Avatar]:
|
||
avatars: dict[str, Avatar] = {}
|
||
width, height = world.map.width, world.map.height
|
||
# 依据配置决定宗门人数占比:当 init_npc_num > sect_num 时启用宗门逻辑
|
||
num_total = int(count)
|
||
max_sects = int(getattr(CONFIG.game, "sect_num", 0) or 0)
|
||
use_sects = num_total > max_sects and max_sects > 0
|
||
# 约 2/3 为宗门弟子,1/3 为散修
|
||
sect_member_target = int(num_total * 2 / 3) if use_sects else 0
|
||
# 随机抽取启用的宗门列表
|
||
enabled_sects = list(sects_by_id.values())
|
||
random.shuffle(enabled_sects)
|
||
enabled_sects = enabled_sects[:max_sects] if use_sects else []
|
||
# 在地图上添加启用宗门的总部(仅显示名称与描述)
|
||
if enabled_sects:
|
||
add_sect_headquarters(world.map, enabled_sects)
|
||
# 循环均匀分配宗门成员(轮询宗门)
|
||
sect_assign_index = 0
|
||
sect_member_count = 0
|
||
for i in range(count):
|
||
# 随机生成年龄,范围从16到60岁
|
||
age_years = random.randint(16, 60)
|
||
# 根据当前时间戳和年龄计算出生时间戳
|
||
birth_month_stamp = current_month_stamp - age_years * 12 + random.randint(0, 11) # 在出生年内随机选择月份
|
||
gender = random_gender()
|
||
# 分配宗门或散修
|
||
assigned_sect = None
|
||
if use_sects and sect_member_count < sect_member_target and enabled_sects:
|
||
assigned_sect = enabled_sects[sect_assign_index % len(enabled_sects)]
|
||
sect_assign_index += 1
|
||
sect_member_count += 1
|
||
# 根据宗门生成姓名
|
||
name = get_random_name_for_sect(gender, assigned_sect)
|
||
|
||
# 随机生成level,范围从0到120(对应四个大境界)
|
||
level = random.randint(0, 120)
|
||
cultivation_progress = CultivationProgress(level)
|
||
|
||
# 创建Age实例,传入年龄与当前境界
|
||
age = Age(age_years, cultivation_progress.realm)
|
||
|
||
# 找一个非海域的出生点
|
||
for _ in range(200):
|
||
x = random.randint(0, width - 1)
|
||
y = random.randint(0, height - 1)
|
||
t = world.map.get_tile(x, y)
|
||
if t.type not in (TileType.WATER, TileType.SEA, TileType.MOUNTAIN, TileType.VOLCANO, TileType.SWAMP, TileType.CAVE, TileType.RUINS):
|
||
break
|
||
else:
|
||
x, y = random.randint(0, width - 1), random.randint(0, height - 1)
|
||
|
||
avatar = Avatar(
|
||
world=world,
|
||
name=name,
|
||
id=get_avatar_id(),
|
||
birth_month_stamp=MonthStamp(birth_month_stamp),
|
||
age=age,
|
||
gender=gender,
|
||
cultivation_progress=cultivation_progress,
|
||
pos_x=x,
|
||
pos_y=y,
|
||
root=random.choice(list(Root)), # 随机选择灵根
|
||
sect=assigned_sect,
|
||
)
|
||
avatar.tile = world.map.get_tile(x, y)
|
||
# 依据宗门设定阵营(若有宗门则与宗门阵营一致,否则保留默认随机)
|
||
if assigned_sect is not None:
|
||
avatar.alignment = assigned_sect.alignment
|
||
# 宗门弟子:按宗门功法随机
|
||
t = get_technique_by_sect(assigned_sect)
|
||
avatar.technique = t
|
||
# 将灵根改为功法对应灵根(邪功法不变)
|
||
mapped_root = attribute_to_root(avatar.technique.attribute)
|
||
if mapped_root is not None:
|
||
avatar.root = mapped_root
|
||
avatars[avatar.id] = avatar
|
||
# # —— 为演示添加少量示例关系 ——
|
||
avatar_list = list(avatars.values())
|
||
if len(avatar_list) >= 2:
|
||
avatar_list[0].set_relation(avatar_list[1], Relation.ENEMY)
|
||
if len(avatar_list) >= 4:
|
||
avatar_list[2].set_relation(avatar_list[3], Relation.FRIEND)
|
||
if len(avatar_list) >= 6:
|
||
# 师徒(有向):第5位是师傅,第6位是徒弟
|
||
avatar_list[4].set_relation(avatar_list[5], Relation.MASTER)
|
||
if len(avatar_list) >= 8:
|
||
# 道侣
|
||
avatar_list[6].set_relation(avatar_list[7], Relation.LOVERS)
|
||
return avatars
|
||
|
||
|
||
async def main():
|
||
# 为了每次更丰富,使用随机种子;如需复现可将 seed 固定
|
||
|
||
# 初始化日志系统(会自动清理旧日志)
|
||
logger = get_logger()
|
||
print(f"日志系统已初始化,日志文件:{logger.log_file_path}")
|
||
|
||
game_map = create_cultivation_world_map()
|
||
world = World(map=game_map, month_stamp=create_month_stamp(Year(100), Month.JANUARY))
|
||
|
||
# 创建模拟器
|
||
sim = Simulator(world)
|
||
|
||
# 创建角色,传入当前年份确保年龄与生日匹配,使用配置文件中的NPC数量
|
||
world.avatar_manager.avatars.update(make_avatars(world, count=CONFIG.game.init_npc_num, current_month_stamp=world.month_stamp))
|
||
|
||
front = Front(
|
||
simulator=sim,
|
||
tile_size=24, # 每个tile扩大约25%像素(与tile数量缩减相抵,窗口不变)
|
||
margin=8,
|
||
step_interval_ms=750,
|
||
window_title="Cultivation World — Front Demo",
|
||
sidebar_width=350,
|
||
)
|
||
await front.run_async()
|
||
|
||
|
||
if __name__ == "__main__":
|
||
asyncio.run(main())
|
||
|