任务更新,素材更新

This commit is contained in:
李鹏宇
2026-09-24 16:41:02 +08:00
parent e93621ba02
commit 886db2821a
8 changed files with 20 additions and 30 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
'name': '宇森世界 - 沙盘与微观世界',
'version': '0.8',
'version': '0.10',
'summary': '地表存储(沙盘记忆) + 微观世界(1:1预留) + 资源(框架)',
'description': '每个玩家拥有一个确定性生成的世界;仅持久化玩家改过的地表地块(种子+增量覆盖)。微观世界独立为 1:1 模型仅预留,资源模型为框架。',
'category': 'Game',
+1 -1
View File
@@ -20,7 +20,7 @@ BUILD_SPECS = {
'village': {'stat_yield_instant': {'population': 6}, 'stat_yield_tick': {'food': 1}},
# ===== 每分钟资源(tick)=====
'farmland': {'stat_yield_instant': {}, 'stat_yield_tick': {'food': 3}},
'farmland': {'stat_yield_instant': {}, 'stat_yield_tick': {'grain': 3}},
'well': {'stat_yield_instant': {}, 'stat_yield_tick': {'water': 2}},
'windmill': {'stat_yield_instant': {}, 'stat_yield_tick': {'flour': 2}},
'blacksmith': {'stat_yield_instant': {}, 'stat_yield_tick': {'metal': 2}},
+5 -1
View File
@@ -219,6 +219,10 @@ class YtWorldApi(http.Controller):
if not instant and not tick:
# 旧数据尚未拆分,全部视为一次性产出(保守处理)
instant = legacy
# statYield 同样以代码规格为真源:spec 已定义时由 instant+tick 合成,
# 避免 DB 里 noupdate XML 种子的旧 legacy 字段(如 farmland 的 food)泄漏到面板
merged_yield = dict(instant)
merged_yield.update(tick)
out.append({
'code': r['code'], 'name': r['name'], 'category': r['category'],
'icon': r['icon'], 'w': r['footprint_w'], 'h': r['footprint_h'],
@@ -229,7 +233,7 @@ class YtWorldApi(http.Controller):
'statLevel': r['stat_level'] or 1,
'maxLevel': r['max_level'] or 1,
'statHp': r['stat_hp'] or 100,
'statYield': r['stat_yield'] or {},
'statYield': merged_yield or (r['stat_yield'] or {}),
'statYieldInstant': instant,
'statYieldTick': tick,
'actions': r['actions'] or [],
+3 -3
View File
@@ -155,11 +155,11 @@
<field name="category">facility</field><field name="icon">assets/buildings/spring/farmland/farmland.png</field>
<field name="footprint_w">2</field> <field name="footprint_h">2</field><field name="sequence">310</field>
<field name="cost_gold">10</field>
<field name="flat">1</field>
<field name="flat">0</field>
<field name="desc">春季播种的耕地,定期产出粮食作物。</field>
<field name="stat_level">1</field><field name="stat_hp">60</field>
<field name="stat_yield" eval="{'food': 3}"/>
<field name="stat_yield_tick" eval="{'food': 3}"/>
<field name="stat_yield" eval="{'grain': 3}"/>
<field name="stat_yield_tick" eval="{'grain': 3}"/>
</record>
<record id="bi_fence" model="yt.world.build.item">
<field name="code">fence</field><field name="name">栅栏</field>
+6 -6
View File
@@ -44,9 +44,9 @@
<record id="task_ch2_harvest" model="yt.world.task">
<field name="chapter">2</field>
<field name="code">ch2_harvest</field>
<field name="title">第一次收获</field>
<field name="desc">建造农田并在农田上使用「收获」动作,体验建筑产出资源的乐趣。</field>
<field name="objectives" eval="[{'id':'o1','type':'building_action','key':'farmland_harvest','count':1,'label':'收获一次农田','hint':'选中农田 → 收获'}]"/>
<field name="title">第一次升级</field>
<field name="desc">建造农舍并把它升级到 2 级,体验建筑成长带来的变化。</field>
<field name="objectives" eval="[{'id':'o1','type':'building_action','key':'cottage_upgrade','count':1,'label':'升级一次农舍','hint':'选中农舍 → 升级'}]"/>
<field name="reward_exp">40</field>
<field name="reward_res" eval="{'gold': 60}"/>
<field name="prereq" eval="['ch2_build']"/>
@@ -58,9 +58,9 @@
<record id="task_ch3_food" model="yt.world.task">
<field name="chapter">3</field>
<field name="code">ch3_food</field>
<field name="title">粮仓初盈</field>
<field name="desc">反复经营你的农田与作坊,累计收获 5 份粮食,支撑世界运转。</field>
<field name="objectives" eval="[{'id':'o1','type':'resource_collected','key':'food','count':5,'label':'累计收获粮食 ×5','hint':'多次使用农田「收获」/风车「加工」'}]"/>
<field name="title">领地扩建</field>
<field name="desc">继续经营你的世界,累计放置 3 座建筑,让领地初具规模。</field>
<field name="objectives" eval="[{'id':'o1','type':'build_placed','count':3,'label':'累计放置 3 座建筑','hint':'在微观沙盘中放置任意素材建筑'}]"/>
<field name="reward_exp">60</field>
<field name="reward_res" eval="{'gold': 100}"/>
<field name="prereq" eval="['ch2_harvest']"/>
-14
View File
@@ -1,14 +0,0 @@
# -*- coding: utf-8 -*-
"""升级到 0.4:建筑产出规格迁移到代码单一真源(BUILD_SPECS)。
此前产出写在 noupdate 的 XML 记录里,模块升级时 Odoo 不会重放已存在记录的字段,
导致改了产出必须手动补写 DB。本次把单一真源移到 build_spec.py,
本迁移在每次跨越 0.3→0.4 的升级时执行一次:把代码规格刷进 item 字段,
并对所有世界做全量对账(速率 tick + 即时属性 instant)。
以后调任何建筑产出:改 build_spec.BUILD_SPECS + 升一次级(如需再次 sync 则 bump 版本)即可。
运行时结算/算速率/前端目录均已直接读 BUILD_SPECS,即使不跑本迁移游戏也正确。
"""
def migrate(cr, version):
from odoo.addons.yt_world import sync_specs_from_code
sync_specs_from_code(cr, None)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 94 KiB

+4 -4
View File
@@ -2628,7 +2628,7 @@ let microCreatureOverlay = {}; // cid -> 已保存生物状态(等级/
let microOpenBuilding = null; // 当前打开的建筑交互对象(用于升级等更新)
// 建筑动作效果表(费用/产出)——真源在后端 BUILD_EFFECTS,登录后拉取一次缓存
let MICRO_BUILD_EFFECTS = {};
const MICRO_RES_NAMES = { food:'🍖 食物', wood:'🪵 木材', metal:'⛏ 金属', gold:'💰 金币', culture:'🏛 文化', research:'🔬 科研', defense:'🛡 防御', military:'⚔ 军事', pop:'👥 人口', population:'👥 人口', water:'💧 水', meat:'🥩 肉', milk:'🥛 奶', coin:'🪙 硬币', flour:'🌾 面粉', stone:'🪨 石材', prestige:'⭐ 声望', rare:'💎 稀有' };
const MICRO_RES_NAMES = { food:'🍖 食物', grain:'🍚 谷物', wood:'🪵 木材', metal:'⛏ 金属', gold:'💰 金币', culture:'🏛 文化', research:'🔬 科研', defense:'🛡 防御', military:'⚔ 军事', pop:'👥 人口', population:'👥 人口', water:'💧 水', meat:'🥩 肉', milk:'🥛 奶', coin:'🪙 硬币', flour:'🌾 面粉', stone:'🪨 石材', prestige:'⭐ 声望', rare:'💎 稀有' };
async function microLoadBuildEffects() {
try {
const r = await API.buildEffects();
@@ -2697,7 +2697,7 @@ const MICRO_BUILD_EXTRA = [
{ id: 'restaurant', cat: 'build', icon: 'assets/buildings/spring/restaurant/restaurant_l.png?v=5',name: '酒馆', desc: '烹制料理,为居民提供饱食与心情加成。', flat: false, w: 2, h: 3, statLevel: 1, statHp: 95, statYield: { satiety: 3 }, costGold: 45 },
{ id: 'pasture', cat: 'business', icon: 'assets/buildings/spring/pasture/pasture_l.png?v=17', name: '牧场', desc: '放养牲畜,稳定产出肉奶类资源。', flat: false, w: 4, h: 4, statLevel: 1, statHp: 105, statYield: { meat: 2, milk: 1 }, costGold: 70 },
{ id: 'blackmarket', cat: 'build', icon: 'assets/buildings/spring/blackmarket/blackmarket_l.png?v=7',name:'商城', desc: '灰色交易点,可换取稀有物资但伴随风险。', flat: false, w: 2, h: 2, statLevel: 1, statHp: 80, statYield: { rare: 1 }, costGold: 120 },
{ id: 'farmland', cat: 'business', icon: 'assets/buildings/spring/farmland/farmland.png?v=202608251713', name: '农田', desc: '春季播种的耕地,定期产出粮食作物。', flat: true, w: 2, h: 2, statLevel: 1, statHp: 60, statYield: { food: 3 }, costGold: 10 },
{ id: 'farmland', cat: 'business', icon: 'assets/buildings/spring/farmland/farmland.png?v=2026092401', name: '农田', desc: '春季播种的耕地,定期产出粮食作物。', flat: false, w: 2, h: 2, statLevel: 1, statHp: 60, statYield: { grain: 3 }, costGold: 10 },
{ id: 'well', cat: 'build', icon: 'assets/buildings/default/props/well_l.png', name: '水井', desc: '汲取地下水的圆井,保障饮水与灌溉。', flat: false, w: 2, h: 2, statLevel: 1, statHp: 90, statYield: { water: 2 }, costGold: 15 },
{ id: 'school', cat: 'build', icon: 'assets/buildings/spring/school/school_l.png?v=16', name: '学堂', desc: '授业解惑之所,研读典籍提升领地科研与基础教学产出。', flat: false, w: 3, h: 2, statLevel: 1, statHp: 100, statYield: { research: 3, prestige: 1 }, costGold: 90 },
{ id: 'library', cat: 'build', icon: 'assets/buildings/spring/library/library_l.png?v=5', name: '图书馆', desc: '收藏典籍与卷轴,提升领地科研与知识产出。', flat: false, w: 2, h: 3, statLevel: 1, statHp: 110, statYield: { research: 4 }, costGold: 110 },
@@ -3379,7 +3379,7 @@ let taskData = null; // {chapters, tasks, char}
let _knownUnitCids = new Set(); // 已记录的单位 cid,用于检测"新入驻居民"
// 建筑动作 → 产出的资源(用于发射 resource_collected 事件)
const TASK_RES = {
'farmland_harvest': 'food', 'farmland_sow': 'food', 'farmland_upgrade': 'food',
'farmland_harvest': 'grain', 'farmland_sow': 'grain', 'farmland_upgrade': 'grain',
'windmill_collect': 'flour', 'windmill_produce': 'food',
'pasture_breed': 'meat', 'well_draw': 'water', 'blacksmith_forge': 'metal',
'grocery_restock': 'food', 'market_stall': 'gold',
@@ -4896,7 +4896,7 @@ function updateSidebar() {
const stockEntries = Object.entries(stocks).filter(([, s]) => (s.amount > 0.0001) || (s.rate_per_min > 0.0001));
if (stockEntries.length > 0) {
const resNames = MICRO_RES_NAMES;
const order = ['food','water','wood','meat','milk','metal','gold','coin','culture','research','defense','military','pop','prestige','rare'];
const order = ['food','grain','water','wood','meat','milk','metal','gold','coin','culture','research','defense','military','pop','prestige','rare'];
stockEntries.sort((a, b) => {
const ia = order.indexOf(a[0]), ib = order.indexOf(b[0]);
return (ia < 0 ? 99 : ia) - (ib < 0 ? 99 : ib);