update city
This commit is contained in:
@@ -7,9 +7,10 @@ import inspect
|
||||
|
||||
from src.classes.essence import Essence, EssenceType
|
||||
from src.classes.root import Root, corres_essence_type
|
||||
from src.classes.region import Region, CultivateRegion, NormalRegion
|
||||
from src.classes.region import Region, CultivateRegion, NormalRegion, CityRegion
|
||||
from src.classes.event import Event, NULL_EVENT
|
||||
from src.classes.item import Item
|
||||
from src.classes.item import Item, items_by_name
|
||||
from src.classes.prices import prices
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.classes.avatar import Avatar
|
||||
@@ -431,7 +432,52 @@ class Harvest(DefineAction, ActualActionMixin):
|
||||
return False
|
||||
|
||||
|
||||
ALL_ACTION_CLASSES = [Move, Cultivate, Breakthrough, MoveToRegion, Play, Hunt, Harvest]
|
||||
ALL_ACTUAL_ACTION_CLASSES = [Cultivate, Breakthrough, MoveToRegion, Play, Hunt, Harvest]
|
||||
ALL_ACTION_NAMES = ["Move", "Cultivate", "Breakthrough", "MoveToRegion", "Play", "Hunt", "Harvest"]
|
||||
ALL_ACTUAL_ACTION_NAMES = ["Cultivate", "Breakthrough", "MoveToRegion", "Play", "Hunt", "Harvest"]
|
||||
@long_action(step_month=1)
|
||||
class Sold(DefineAction, ActualActionMixin):
|
||||
"""
|
||||
在城镇出售指定名称的物品,一次性卖出持有的全部数量。
|
||||
收益为 item_price * item_num,动作耗时1个月。
|
||||
"""
|
||||
COMMENT = "在城镇出售持有的某类物品的全部"
|
||||
PARAMS = {"item_name": "str"}
|
||||
|
||||
def _execute(self, item_name: str) -> None:
|
||||
region = self.avatar.tile.region
|
||||
if not isinstance(region, CityRegion):
|
||||
return
|
||||
|
||||
# 找到物品
|
||||
item = items_by_name.get(item_name)
|
||||
if item is None:
|
||||
return
|
||||
|
||||
# 检查持有数量
|
||||
quantity = self.avatar.get_item_quantity(item)
|
||||
if quantity <= 0:
|
||||
return
|
||||
|
||||
# 计算价格并结算
|
||||
price_per = prices.get_price(item)
|
||||
total_gain = price_per * quantity
|
||||
|
||||
# 扣除物品并增加灵石
|
||||
removed = self.avatar.remove_item(item, quantity)
|
||||
if not removed:
|
||||
return
|
||||
|
||||
self.avatar.magic_stone = self.avatar.magic_stone + total_gain
|
||||
|
||||
def get_event(self, item_name: str) -> Event:
|
||||
return Event(self.world.month_stamp, f"{self.avatar.name} 在城镇出售 {item_name}")
|
||||
|
||||
@property
|
||||
def is_doable(self) -> bool:
|
||||
# 只允许在城镇且背包非空时出现在动作空间
|
||||
region = self.avatar.tile.region
|
||||
return isinstance(region, CityRegion) and bool(self.avatar.items)
|
||||
|
||||
|
||||
ALL_ACTION_CLASSES = [Move, Cultivate, Breakthrough, MoveToRegion, Play, Hunt, Harvest, Sold]
|
||||
ALL_ACTUAL_ACTION_CLASSES = [Cultivate, Breakthrough, MoveToRegion, Play, Hunt, Harvest, Sold]
|
||||
ALL_ACTION_NAMES = ["Move", "Cultivate", "Breakthrough", "MoveToRegion", "Play", "Hunt", "Harvest", "Sold"]
|
||||
ALL_ACTUAL_ACTION_NAMES = ["Cultivate", "Breakthrough", "MoveToRegion", "Play", "Hunt", "Harvest", "Sold"]
|
||||
@@ -113,7 +113,7 @@ class LLMAI(AI):
|
||||
"""
|
||||
异步决策逻辑:通过LLM决定执行什么动作和参数
|
||||
"""
|
||||
global_info = world.get_prompt()
|
||||
global_info = world.get_info()
|
||||
avatar_infos = {avatar.id: avatar.get_prompt() for avatar in avatars_to_decide}
|
||||
info = {
|
||||
"avatar_infos": avatar_infos,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
|
||||
|
||||
from typing import Union
|
||||
|
||||
class MagicStone(int):
|
||||
"""
|
||||
灵石,实际上是一个int类,代表持有的下品灵石的数量。
|
||||
@@ -15,4 +17,14 @@ class MagicStone(int):
|
||||
|
||||
def __str__(self) -> str:
|
||||
_upper, _middle, _value = self.exchange()
|
||||
return f"上品灵石:{_upper},中品灵石:{_middle},下品灵石:{_value}"
|
||||
return f"上品灵石:{_upper},中品灵石:{_middle},下品灵石:{_value}"
|
||||
|
||||
def __add__(self, other: Union['MagicStone', int]) -> 'MagicStone':
|
||||
if isinstance(other, int):
|
||||
return MagicStone(self.value + other)
|
||||
return MagicStone(self.value + other.value)
|
||||
|
||||
def __sub__(self, other: Union['MagicStone', int]) -> 'MagicStone':
|
||||
if isinstance(other, int):
|
||||
return MagicStone(self.value - other)
|
||||
return MagicStone(self.value - other.value)
|
||||
100
src/classes/map.py
Normal file
100
src/classes/map.py
Normal file
@@ -0,0 +1,100 @@
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from src.classes.tile import Tile, TileType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.classes.region import Region
|
||||
|
||||
|
||||
class Map():
|
||||
"""
|
||||
通过dict记录position 到 tile。
|
||||
"""
|
||||
def __init__(self, width: int, height: int):
|
||||
self.tiles = {}
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
# 加载所有region数据到Map中
|
||||
self._load_regions()
|
||||
|
||||
def _load_regions(self):
|
||||
"""从配置文件加载所有区域数据到Map实例中"""
|
||||
# 延迟导入避免循环导入
|
||||
from src.classes.region import regions_by_id, regions_by_name
|
||||
from src.classes.region import normal_regions_by_id, normal_regions_by_name
|
||||
from src.classes.region import cultivate_regions_by_id, cultivate_regions_by_name
|
||||
from src.classes.region import city_regions_by_id, city_regions_by_name
|
||||
|
||||
self.regions = regions_by_id
|
||||
self.region_names = regions_by_name
|
||||
self.normal_regions = normal_regions_by_id
|
||||
self.normal_region_names = normal_regions_by_name
|
||||
self.cultivate_regions = cultivate_regions_by_id
|
||||
self.cultivate_region_names = cultivate_regions_by_name
|
||||
self.city_regions = city_regions_by_id
|
||||
self.city_region_names = city_regions_by_name
|
||||
|
||||
def is_in_bounds(self, x: int, y: int) -> bool:
|
||||
"""
|
||||
判断坐标是否在地图边界内。
|
||||
"""
|
||||
return 0 <= x < self.width and 0 <= y < self.height
|
||||
|
||||
def create_tile(self, x: int, y: int, tile_type: TileType):
|
||||
self.tiles[(x, y)] = Tile(tile_type, x, y, region=None)
|
||||
|
||||
def get_tile(self, x: int, y: int) -> Tile:
|
||||
return self.tiles[(x, y)]
|
||||
|
||||
def get_center_locs(self, locs: list[tuple[int, int]]) -> tuple[int, int]:
|
||||
"""
|
||||
获取locs的中心位置。
|
||||
如果几何中心恰好在位置列表中,返回几何中心;
|
||||
否则返回距离几何中心最近的实际位置。
|
||||
"""
|
||||
if not locs:
|
||||
return (0, 0)
|
||||
|
||||
# 分别计算x和y坐标的平均值
|
||||
avg_x = sum(loc[0] for loc in locs) // len(locs)
|
||||
avg_y = sum(loc[1] for loc in locs) // len(locs)
|
||||
center = (avg_x, avg_y)
|
||||
|
||||
# 如果几何中心恰好在位置列表中,直接返回
|
||||
if center in locs:
|
||||
return center
|
||||
|
||||
# 否则找到距离几何中心最近的实际位置
|
||||
def distance_squared(loc: tuple[int, int]) -> int:
|
||||
"""计算到中心点的距离平方(避免开方运算)"""
|
||||
return (loc[0] - avg_x) ** 2 + (loc[1] - avg_y) ** 2
|
||||
|
||||
return min(locs, key=distance_squared)
|
||||
|
||||
def get_region(self, x: int, y: int) -> Optional['Region']:
|
||||
"""
|
||||
获取一个region。
|
||||
"""
|
||||
return self.tiles[(x, y)].region
|
||||
|
||||
def get_info(self) -> str:
|
||||
"""返回地图的简要信息,按类型分组罗列区域并说明用途。"""
|
||||
parts: list[str] = []
|
||||
|
||||
# 修炼区域
|
||||
parts.append("修炼区域(可以修炼以增进修为):")
|
||||
parts.extend([f"- {str(region)}" for region in self.cultivate_regions.values()])
|
||||
parts.append("")
|
||||
|
||||
# 普通区域
|
||||
parts.append("普通区域(可以狩猎或采集):")
|
||||
parts.extend([f"- {str(region)}" for region in self.normal_regions.values()])
|
||||
parts.append("")
|
||||
|
||||
# 城市区域
|
||||
parts.append("城市区域(可以交易):")
|
||||
parts.extend([f"- {str(region)}" for region in self.city_regions.values()])
|
||||
return "\n".join(parts)
|
||||
|
||||
|
||||
23
src/classes/prices.py
Normal file
23
src/classes/prices.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from src.classes.cultivation import Realm
|
||||
from src.classes.item import Item
|
||||
|
||||
class Prices:
|
||||
"""
|
||||
价格体系。
|
||||
刚开始我只准备做一个比较简单的价格体系,之后可能复杂化。
|
||||
目前是所有的城镇都可以出售材料,同时这些材料的价格是固定的,并且全局公开。
|
||||
价格只和对应的realm绑定。
|
||||
"""
|
||||
def __init__(self):
|
||||
self.realm_to_prices = {
|
||||
Realm.Qi_Refinement: 10,
|
||||
Realm.Foundation_Establishment: 50,
|
||||
Realm.Core_Formation: 100,
|
||||
Realm.Nascent_Soul: 200,
|
||||
}
|
||||
|
||||
def get_price(self, item: Item) -> int:
|
||||
return self.realm_to_prices[item.realm]
|
||||
|
||||
# 预先创建全局价格实例,供全局使用
|
||||
prices = Prices()
|
||||
@@ -32,64 +32,3 @@ class Tile():
|
||||
y: int
|
||||
region: 'Region' = None # 可以是一个region的一部分,也可以不属于任何region
|
||||
|
||||
class Map():
|
||||
"""
|
||||
通过dict记录position 到 tile。
|
||||
"""
|
||||
def __init__(self, width: int, height: int):
|
||||
self.tiles = {}
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
# 加载所有region数据到Map中
|
||||
self._load_regions()
|
||||
|
||||
def _load_regions(self):
|
||||
"""从配置文件加载所有区域数据到Map实例中"""
|
||||
# 延迟导入避免循环导入
|
||||
from src.classes.region import load_all_regions
|
||||
|
||||
self.regions, self.region_names = load_all_regions()
|
||||
|
||||
def is_in_bounds(self, x: int, y: int) -> bool:
|
||||
"""
|
||||
判断坐标是否在地图边界内。
|
||||
"""
|
||||
return 0 <= x < self.width and 0 <= y < self.height
|
||||
|
||||
def create_tile(self, x: int, y: int, tile_type: TileType):
|
||||
self.tiles[(x, y)] = Tile(tile_type, x, y, region=None)
|
||||
|
||||
def get_tile(self, x: int, y: int) -> Tile:
|
||||
return self.tiles[(x, y)]
|
||||
|
||||
def get_center_locs(self, locs: list[tuple[int, int]]) -> tuple[int, int]:
|
||||
"""
|
||||
获取locs的中心位置。
|
||||
如果几何中心恰好在位置列表中,返回几何中心;
|
||||
否则返回距离几何中心最近的实际位置。
|
||||
"""
|
||||
if not locs:
|
||||
return (0, 0)
|
||||
|
||||
# 分别计算x和y坐标的平均值
|
||||
avg_x = sum(loc[0] for loc in locs) // len(locs)
|
||||
avg_y = sum(loc[1] for loc in locs) // len(locs)
|
||||
center = (avg_x, avg_y)
|
||||
|
||||
# 如果几何中心恰好在位置列表中,直接返回
|
||||
if center in locs:
|
||||
return center
|
||||
|
||||
# 否则找到距离几何中心最近的实际位置
|
||||
def distance_squared(loc: tuple[int, int]) -> int:
|
||||
"""计算到中心点的距离平方(避免开方运算)"""
|
||||
return (loc[0] - avg_x) ** 2 + (loc[1] - avg_y) ** 2
|
||||
|
||||
return min(locs, key=distance_squared)
|
||||
|
||||
def get_region(self, x: int, y: int) -> 'Region | None':
|
||||
"""
|
||||
获取一个region。
|
||||
"""
|
||||
return self.tiles[(x, y)].region
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from src.classes.tile import Map
|
||||
from src.classes.map import Map
|
||||
from src.classes.calendar import Year, Month, MonthStamp
|
||||
|
||||
@dataclass
|
||||
@@ -8,6 +8,8 @@ class World():
|
||||
map: Map
|
||||
month_stamp: MonthStamp
|
||||
|
||||
def get_prompt(self) -> str:
|
||||
regions_str = "\n".join([str(region) for region in self.map.regions.values()])
|
||||
return f"世界地图上存在的区域为:{regions_str}"
|
||||
def get_info(self) -> str:
|
||||
map_intro = "世界上的区域为:"
|
||||
map_info = self.map.get_info()
|
||||
info = f"{map_intro}\n{map_info}"
|
||||
return info
|
||||
@@ -1,4 +1,5 @@
|
||||
from src.classes.tile import Map, TileType
|
||||
from src.classes.map import Map
|
||||
from src.classes.tile import TileType
|
||||
from src.classes.essence import Essence, EssenceType
|
||||
|
||||
def create_cultivation_world_map() -> Map:
|
||||
|
||||
@@ -11,11 +11,10 @@ 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.tile import Map, TileType
|
||||
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.action import Move
|
||||
from src.classes.essence import Essence, EssenceType
|
||||
from src.classes.cultivation import CultivationProgress
|
||||
from src.classes.root import Root
|
||||
from src.classes.age import Age
|
||||
|
||||
Reference in New Issue
Block a user