生物素材 #40

Merged
Ghost merged 1 commits from why_ga into main 2026-09-15 16:40:57 +08:00
21 changed files with 145 additions and 5 deletions
+109
View File
@@ -20,6 +20,7 @@
<field name="trait">均衡型。各项能力标准,无特殊被动,适合新手。</field> <field name="trait">均衡型。各项能力标准,无特殊被动,适合新手。</field>
<field name="sequence">10</field> <field name="sequence">10</field>
<field name="active">1</field> <field name="active">1</field>
<field name="category">sentient</field>
</record> </record>
<record id="world_race_demon" model="yt.world.race"> <record id="world_race_demon" model="yt.world.race">
<field name="code">demon</field> <field name="code">demon</field>
@@ -40,6 +41,7 @@
<field name="trait">均衡型。无特殊被动,与人类齐平的基础战力。</field> <field name="trait">均衡型。无特殊被动,与人类齐平的基础战力。</field>
<field name="sequence">20</field> <field name="sequence">20</field>
<field name="active">1</field> <field name="active">1</field>
<field name="category">sentient</field>
</record> </record>
<record id="world_race_dwarf" model="yt.world.race"> <record id="world_race_dwarf" model="yt.world.race">
<field name="code">dwarf</field> <field name="code">dwarf</field>
@@ -200,5 +202,112 @@
<field name="trait">均衡型。眼中发光的岩石巨像,无特殊被动,基础战力与人类一致。</field> <field name="trait">均衡型。眼中发光的岩石巨像,无特殊被动,基础战力与人类一致。</field>
<field name="sequence">100</field> <field name="sequence">100</field>
<field name="active">1</field> <field name="active">1</field>
<field name="category">sentient</field>
</record>
<!-- 非智慧生物(牲畜/家禽):category=animal,不会主动捡宝箱 -->
<record id="world_race_cow" model="yt.world.race">
<field name="code">cow</field>
<field name="name">牛</field>
<field name="icon">🐄</field>
<field name="sprite_walk">assets/characters/cow/cow_walk_1x6.png</field>
<field name="sprite_idle">assets/characters/cow/cow_idle_attack_1x6.png</field>
<field name="sprite_work">assets/characters/cow/cow_work_comm_1x6.png</field>
<field name="hp_max">30</field>
<field name="energy_max">80</field>
<field name="attack">2</field>
<field name="defense">2</field>
<field name="speed">50</field>
<field name="attack_range">30</field>
<field name="regen">0</field>
<field name="enrage">0</field>
<field name="shield_ratio">0.0</field>
<field name="trait">家畜。体型大、移动慢,产肉产奶。</field>
<field name="sequence">110</field>
<field name="active">1</field>
<field name="category">animal</field>
</record>
<record id="world_race_sheep" model="yt.world.race">
<field name="code">sheep</field>
<field name="name">羊</field>
<field name="icon">🐑</field>
<field name="sprite_walk">assets/characters/sheep/sheep_walk_1x6.png</field>
<field name="sprite_idle">assets/characters/sheep/sheep_idle_attack_1x6.png</field>
<field name="sprite_work">assets/characters/sheep/sheep_work_comm_1x6.png</field>
<field name="hp_max">20</field>
<field name="energy_max">60</field>
<field name="attack">1</field>
<field name="defense">1</field>
<field name="speed">70</field>
<field name="attack_range">20</field>
<field name="regen">0</field>
<field name="enrage">0</field>
<field name="shield_ratio">0.0</field>
<field name="trait">家畜。温顺群居,产毛产肉。</field>
<field name="sequence">115</field>
<field name="active">1</field>
<field name="category">animal</field>
</record>
<record id="world_race_chicken" model="yt.world.race">
<field name="code">chicken</field>
<field name="name">鸡</field>
<field name="icon">🐔</field>
<field name="sprite_walk">assets/characters/chicken/chicken_walk_1x6.png</field>
<field name="sprite_idle">assets/characters/chicken/chicken_idle_attack_1x6.png</field>
<field name="sprite_work">assets/characters/chicken/chicken_work_comm_1x6.png</field>
<field name="hp_max">10</field>
<field name="energy_max">40</field>
<field name="attack">1</field>
<field name="defense">1</field>
<field name="speed">100</field>
<field name="attack_range">15</field>
<field name="regen">0</field>
<field name="enrage">0</field>
<field name="shield_ratio">0.0</field>
<field name="trait">家禽。体型小、移动快,产蛋。</field>
<field name="sequence">120</field>
<field name="active">1</field>
<field name="category">animal</field>
</record>
<record id="world_race_pig" model="yt.world.race">
<field name="code">pig</field>
<field name="name">猪</field>
<field name="icon">🐷</field>
<field name="sprite_walk">assets/characters/pig/pig_walk_1x6.png</field>
<field name="sprite_idle">assets/characters/pig/pig_idle_attack_1x6.png</field>
<field name="sprite_work">assets/characters/pig/pig_work_comm_1x6.png</field>
<field name="hp_max">25</field>
<field name="energy_max">60</field>
<field name="attack">2</field>
<field name="defense">2</field>
<field name="speed">65</field>
<field name="attack_range">25</field>
<field name="regen">0</field>
<field name="enrage">0</field>
<field name="shield_ratio">0.0</field>
<field name="trait">家畜。杂食健壮,产肉。</field>
<field name="sequence">125</field>
<field name="active">1</field>
<field name="category">animal</field>
</record>
<record id="world_race_horse" model="yt.world.race">
<field name="code">horse</field>
<field name="name">马</field>
<field name="icon">🐴</field>
<field name="sprite_walk">assets/characters/horse/horse_walk_1x6.png</field>
<field name="sprite_idle">assets/characters/horse/horse_idle_attack_1x6.png</field>
<field name="sprite_work">assets/characters/horse/horse_work_comm_1x6.png</field>
<field name="hp_max">35</field>
<field name="energy_max">90</field>
<field name="attack">3</field>
<field name="defense">3</field>
<field name="speed">140</field>
<field name="attack_range">40</field>
<field name="regen">0</field>
<field name="enrage">0</field>
<field name="shield_ratio">0.0</field>
<field name="trait">家畜。速度最快的动物,可骑乘。</field>
<field name="sequence">130</field>
<field name="active">1</field>
<field name="category">animal</field>
</record> </record>
</odoo> </odoo>
+6
View File
@@ -361,6 +361,11 @@ class YtWorldRace(models.Model):
trait = fields.Text(string='特性文案') trait = fields.Text(string='特性文案')
sequence = fields.Integer(string='排序', default=10) sequence = fields.Integer(string='排序', default=10)
active = fields.Boolean(string='启用', default=True) active = fields.Boolean(string='启用', default=True)
category = fields.Selection(
[('sentient', '智慧生物'), ('animal', '非智慧生物')],
string='类别', default='sentient',
help='智慧生物会主动捡宝箱;非智慧生物(牲畜/家禽)不会。'
)
_sql_constraints = [ _sql_constraints = [
('uniq_code', 'unique(code)', '种族编码必须唯一'), ('uniq_code', 'unique(code)', '种族编码必须唯一'),
@@ -394,5 +399,6 @@ class YtWorldRace(models.Model):
'shield_ratio': self.shield_ratio, 'shield_ratio': self.shield_ratio,
'trait': self.trait or '', 'trait': self.trait or '',
'active': self.active, 'active': self.active,
'category': self.category or 'sentient',
} }
+24 -4
View File
@@ -17,6 +17,7 @@ async function loadRaces() {
map[rc.code] = { map[rc.code] = {
name: rc.name, name: rc.name,
icon: rc.icon || '👤', icon: rc.icon || '👤',
category: rc.category || 'sentient',
speed: rc.speed, speed: rc.speed,
attack: rc.attack, attack: rc.attack,
defense: rc.defense, defense: rc.defense,
@@ -100,7 +101,9 @@ function allSheetsLoaded() {
} }
function preloadAllSheets() { function preloadAllSheets() {
Object.keys(RACE_CONFIG).forEach(race => { Object.keys(RACE_CONFIG).forEach(race => {
['walk','idle_attack','work_comm'].forEach(k => loadCharSheet(race, k)); const cfg = RACE_CONFIG[race];
['walk','idle_attack'].forEach(k => loadCharSheet(race, k));
if (cfg.category !== 'animal') loadCharSheet(race, 'work_comm'); // 动物无工作/交流动作
}); });
} }
@@ -382,6 +385,9 @@ function clearChars() {
function triggerCharAction(charId, action) { function triggerCharAction(charId, action) {
const ch = getChar(charId); const ch = getChar(charId);
if (!ch || !CHAR_STATES[action]) return false; if (!ch || !CHAR_STATES[action]) return false;
const raceCfg = RACE_CONFIG[ch.race];
// 非智慧生物(动物)无工作/交流动作,禁止触发
if (raceCfg && raceCfg.category === 'animal' && (action === 'work' || action === 'communicate')) return false;
ch.state = action; ch.state = action;
ch.frame = 0; ch.frame = 0;
ch.frameT = 0; ch.frameT = 0;
@@ -531,6 +537,12 @@ function updateChars(dt) {
// 原有漫游 / 行走逻辑 // 原有漫游 / 行走逻辑
// 自由移动开关:ch.freeMove !== false 才允许自主漫游(默认开,可被面板按钮关闭) // 自由移动开关:ch.freeMove !== false 才允许自主漫游(默认开,可被面板按钮关闭)
if (ch.freeMove !== false && !ch.selected && ch.state === 'idle') { if (ch.freeMove !== false && !ch.selected && ch.state === 'idle') {
// 非智慧生物(动物)不捡宝箱,直接走漫游逻辑
const raceCfg = RACE_CONFIG[ch.race];
const isAnimal = raceCfg && raceCfg.category === 'animal';
let goToChest = false;
if (!isAnimal) {
// 宝箱吸引:附近存在活跃宝箱时优先去开启(测试阶段吸引半径全屏) // 宝箱吸引:附近存在活跃宝箱时优先去开启(测试阶段吸引半径全屏)
const chests = window.microChests; const chests = window.microChests;
const c2s = window.microCellToScreen; const c2s = window.microCellToScreen;
@@ -553,13 +565,19 @@ function updateChars(dt) {
ch._userMove = false; ch._userMove = false;
ch.wanderT = 0; ch.wanderT = 0;
ch.trail = []; ch.trail = [];
} else { goToChest = true;
}
} // end if (!isAnimal) — 动物跳过整段宝箱吸引
// 漫游逻辑:动物始终走这里;智慧生物在无宝箱时走这里
if (!goToChest) {
ch.wanderT = (ch.wanderT || 0) + dt; ch.wanderT = (ch.wanderT || 0) + dt;
const interval = ch.wanderInterval || (2 + Math.random() * 4); const interval = ch.wanderInterval || (2 + Math.random() * 4);
if (ch.wanderT >= interval) { if (ch.wanderT >= interval) {
ch.wanderT = 0; ch.wanderT = 0;
ch.wanderInterval = 2 + Math.random() * 4; ch.wanderInterval = 2 + Math.random() * 4;
// 多意图漫游目标:闲逛 / 朝中心 / 远行(带岛内+建筑校验)
// 动物只做漫游走动(不捡宝箱、不吃草)
const target = generateWanderTarget(ch); const target = generateWanderTarget(ch);
if (!target) { if (!target) {
ch.wanderInterval = 1 + Math.random() * 2; // 找不到就短等重试 ch.wanderInterval = 1 + Math.random() * 2; // 找不到就短等重试
@@ -699,6 +717,7 @@ function updateChars(dt) {
if (st.loop) { if (st.loop) {
ch.frame = (ch.frame + 1) % st.count; ch.frame = (ch.frame + 1) % st.count;
} else if (ch.frame >= st.count - 1) { } else if (ch.frame >= st.count - 1) {
// 非循环动画(attack/work/communicate)播完 → 回 idle
ch.state = 'idle'; ch.state = 'idle';
ch.frame = 0; ch.frame = 0;
} else { } else {
@@ -945,7 +964,8 @@ function updateCharPanel() {
const shieldHtml = ch.shieldMax > 0 const shieldHtml = ch.shieldMax > 0
? `<div style="margin-top:5px;color:#8a9aaa;font-size:11px;">🛡 护盾 <b style="color:#37c7f0;">${ch.shield}</b>/${ch.shieldMax}<span style="color:#667;">${ch.shield > 0 ? '' : '(未触发)'}</span></div>` ? `<div style="margin-top:5px;color:#8a9aaa;font-size:11px;">🛡 护盾 <b style="color:#37c7f0;">${ch.shield}</b>/${ch.shieldMax}<span style="color:#667;">${ch.shield > 0 ? '' : '(未触发)'}</span></div>`
: ''; : '';
const actions = ['idle', 'attack', 'work', 'communicate']; const isAnimal = rc && rc.category === 'animal';
const actions = isAnimal ? ['idle', 'attack'] : ['idle', 'attack', 'work', 'communicate'];
const btns = actions.map(a => { const btns = actions.map(a => {
const active = ch.state === a ? 'background:#2a4a6a;border-color:#5a9acc;' : ''; const active = ch.state === a ? 'background:#2a4a6a;border-color:#5a9acc;' : '';
return `<button class="charActBtn" data-act="${a}" style="${active}background:#1d2b4a;color:#cde;border:1px solid #3a5a8e;padding:5px 10px;border-radius:5px;cursor:pointer;font-size:12px;transition:background .15s;">${CHAR_CN[a]}</button>`; return `<button class="charActBtn" data-act="${a}" style="${active}background:#1d2b4a;color:#cde;border:1px solid #3a5a8e;padding:5px 10px;border-radius:5px;cursor:pointer;font-size:12px;transition:background .15s;">${CHAR_CN[a]}</button>`;
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

+6 -1
View File
@@ -2540,7 +2540,7 @@ const MICRO_BUILD_EXTRA = [
{ id: 'barracks', cat: 'build', icon: 'assets/buildings/spring/barracks/barracks_l.png?v=9', name: '兵营', desc: '训练士兵与储备武备的军事建筑,提升领地军事产出。', flat: false, w: 3, h: 3, statLevel: 1, statHp: 130, statYield: { military: 4 }, costGold: 130 }, { id: 'barracks', cat: 'build', icon: 'assets/buildings/spring/barracks/barracks_l.png?v=9', name: '兵营', desc: '训练士兵与储备武备的军事建筑,提升领地军事产出。', flat: false, w: 3, h: 3, statLevel: 1, statHp: 130, statYield: { military: 4 }, costGold: 130 },
{ id: 'guild', cat: 'build', icon: 'assets/buildings/spring/guild/guild_l.png?v=4', name: '公会', desc: '冒险者与住民集结之所,可发布委托与招募成员。', flat: false, w: 2, h: 2, statLevel: 1, statHp: 140, statYield: { prestige: 2, coin: 1 }, costGold: 100 }, { id: 'guild', cat: 'build', icon: 'assets/buildings/spring/guild/guild_l.png?v=4', name: '公会', desc: '冒险者与住民集结之所,可发布委托与招募成员。', flat: false, w: 2, h: 2, statLevel: 1, statHp: 140, statYield: { prestige: 2, coin: 1 }, costGold: 100 },
{ id: 'village', cat: 'build', icon: 'assets/buildings/spring/village/village_l.png?v=5', name: '村庄', desc: '族群聚落的核心,提升人口上限与基础产出。', flat: false, w: 3, h: 3, statLevel: 1, statHp: 150, statYield: { population: 6, food: 1 }, costGold: 200 }, { id: 'village', cat: 'build', icon: 'assets/buildings/spring/village/village_l.png?v=5', name: '村庄', desc: '族群聚落的核心,提升人口上限与基础产出。', flat: false, w: 3, h: 3, statLevel: 1, statHp: 150, statYield: { population: 6, food: 1 }, costGold: 200 },
// 生物分类:9 种族角色(点击进入"点地面放置"模式,不进入建筑模式) // 生物分类:10 智慧种族 + 5 非智慧动物(点击进入"点地面放置"模式,不进入建筑模式)
{ id: 'race_human', cat: 'bio', icon: 'assets/characters/human/human_thumb.png', sprite: 'assets/characters/human/human_idle_attack_1x6.png', name: '人族', isChar: true, race: 'human' }, { id: 'race_human', cat: 'bio', icon: 'assets/characters/human/human_thumb.png', sprite: 'assets/characters/human/human_idle_attack_1x6.png', name: '人族', isChar: true, race: 'human' },
{ id: 'race_demon', cat: 'bio', icon: 'assets/characters/demon/demon_thumb.png', sprite: 'assets/characters/demon/demon_idle_attack_1x6.png', name: '妖族', isChar: true, race: 'demon' }, { id: 'race_demon', cat: 'bio', icon: 'assets/characters/demon/demon_thumb.png', sprite: 'assets/characters/demon/demon_idle_attack_1x6.png', name: '妖族', isChar: true, race: 'demon' },
{ id: 'race_dwarf', cat: 'bio', icon: 'assets/characters/dwarf/dwarf_thumb.png', sprite: 'assets/characters/dwarf/dwarf_idle_attack_1x6.png', name: '矮人', isChar: true, race: 'dwarf' }, { id: 'race_dwarf', cat: 'bio', icon: 'assets/characters/dwarf/dwarf_thumb.png', sprite: 'assets/characters/dwarf/dwarf_idle_attack_1x6.png', name: '矮人', isChar: true, race: 'dwarf' },
@@ -2551,6 +2551,11 @@ const MICRO_BUILD_EXTRA = [
{ id: 'race_angel', cat: 'bio', icon: 'assets/characters/angel/angel_thumb.png', sprite: 'assets/characters/angel/angel_idle_attack_1x6.png', name: '天使', isChar: true, race: 'angel' }, { id: 'race_angel', cat: 'bio', icon: 'assets/characters/angel/angel_thumb.png', sprite: 'assets/characters/angel/angel_idle_attack_1x6.png', name: '天使', isChar: true, race: 'angel' },
{ id: 'race_werewolf',cat: 'bio', icon: 'assets/characters/werewolf/werewolf_thumb.png', sprite: 'assets/characters/werewolf/werewolf_idle_attack_1x6.png', name: '狼人', isChar: true, race: 'werewolf' }, { id: 'race_werewolf',cat: 'bio', icon: 'assets/characters/werewolf/werewolf_thumb.png', sprite: 'assets/characters/werewolf/werewolf_idle_attack_1x6.png', name: '狼人', isChar: true, race: 'werewolf' },
{ id: 'race_golem', cat: 'bio', icon: 'assets/characters/golem/golem_thumb.png', sprite: 'assets/characters/golem/golem_idle_attack_1x6.png', name: '石傀儡', isChar: true, race: 'golem' }, { id: 'race_golem', cat: 'bio', icon: 'assets/characters/golem/golem_thumb.png', sprite: 'assets/characters/golem/golem_idle_attack_1x6.png', name: '石傀儡', isChar: true, race: 'golem' },
{ id: 'race_cow', cat: 'bio', icon: 'assets/characters/cow/cow_thumb.png', sprite: 'assets/characters/cow/cow_idle_attack_1x6.png', name: '牛', isChar: true, race: 'cow' },
{ id: 'race_sheep', cat: 'bio', icon: 'assets/characters/sheep/sheep_thumb.png', sprite: 'assets/characters/sheep/sheep_idle_attack_1x6.png', name: '羊', isChar: true, race: 'sheep' },
{ id: 'race_chicken', cat: 'bio', icon: 'assets/characters/chicken/chicken_thumb.png', sprite: 'assets/characters/chicken/chicken_idle_attack_1x6.png', name: '鸡', isChar: true, race: 'chicken' },
{ id: 'race_pig', cat: 'bio', icon: 'assets/characters/pig/pig_thumb.png', sprite: 'assets/characters/pig/pig_idle_attack_1x6.png', name: '猪', isChar: true, race: 'pig' },
{ id: 'race_horse', cat: 'bio', icon: 'assets/characters/horse/horse_thumb.png', sprite: 'assets/characters/horse/horse_idle_attack_1x6.png', name: '马', isChar: true, race: 'horse' },
]; ];
// 建筑交互配置(数据驱动,与后端 BUILD_EFFECTS 的 (code,action) 对应)。 // 建筑交互配置(数据驱动,与后端 BUILD_EFFECTS 的 (code,action) 对应)。