feat: implement compound molt armor mechanics and update balance
- Add permanent +1 baseArmor bonus on each molt to enable compound armor growth across multiple molts - Track molt activation with _moltZeroed flag to ensure armor restoration only occurs after a molt event - Extend protect/fortify skills to also boost baseArmor when applied after a molt (to preserve interim boosts) - Update card venom values for progressive difficulty curve - Adjust mission rewards and replace bt_hemomancer_1 with bt_hemorrhage_1 in reward pool - Add 23 new mission briefing videos (mission_001 through mission_023) This refines the molt mechanic to support long-term armor progression while maintaining balance across campaigns.
This commit is contained in:
parent
ab4225bd42
commit
fe229d7462
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -7009,7 +7009,7 @@
|
|||
},
|
||||
{
|
||||
"name": "venom",
|
||||
"value": 2,
|
||||
"value": 4,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
]
|
||||
|
|
@ -7027,7 +7027,7 @@
|
|||
},
|
||||
{
|
||||
"name": "venom",
|
||||
"value": 4,
|
||||
"value": 5,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
]
|
||||
|
|
@ -7045,7 +7045,7 @@
|
|||
},
|
||||
{
|
||||
"name": "venom",
|
||||
"value": 4,
|
||||
"value": 7,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
]
|
||||
|
|
@ -7063,7 +7063,7 @@
|
|||
},
|
||||
{
|
||||
"name": "venom",
|
||||
"value": 6,
|
||||
"value": 10,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
]
|
||||
|
|
@ -7081,7 +7081,7 @@
|
|||
},
|
||||
{
|
||||
"name": "venom",
|
||||
"value": 10,
|
||||
"value": 14,
|
||||
"trigger": "on_attack"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -240,7 +240,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 700,
|
||||
"gold": 600,
|
||||
"cards": [
|
||||
"bt_ghoul_1"
|
||||
]
|
||||
|
|
@ -268,7 +268,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 800,
|
||||
"gold": 600,
|
||||
"cards": [
|
||||
"bt_revenant_1"
|
||||
]
|
||||
|
|
@ -297,7 +297,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 900,
|
||||
"gold": 600,
|
||||
"cards": [
|
||||
"bt_bloodmage_1"
|
||||
]
|
||||
|
|
@ -321,12 +321,12 @@
|
|||
"bt_harbinger_1",
|
||||
"bt_vampire_1",
|
||||
"bt_bloodlord_1",
|
||||
"bt_hemomancer_1",
|
||||
"bt_hemorrhage_1",
|
||||
"bt_crimsonguard_1"
|
||||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 1000,
|
||||
"gold": 600,
|
||||
"cards": [
|
||||
"bt_harbinger_1"
|
||||
]
|
||||
|
|
@ -355,7 +355,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 1100,
|
||||
"gold": 1000,
|
||||
"cards": [
|
||||
"bt_bloodlord_1"
|
||||
]
|
||||
|
|
@ -381,7 +381,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 1200,
|
||||
"gold": 600,
|
||||
"cards": [
|
||||
"xeno_drone_1"
|
||||
]
|
||||
|
|
@ -408,7 +408,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 1300,
|
||||
"gold": 600,
|
||||
"cards": [
|
||||
"xeno_spitter_1"
|
||||
]
|
||||
|
|
@ -435,7 +435,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 1400,
|
||||
"gold": 600,
|
||||
"cards": [
|
||||
"xeno_beetle_1"
|
||||
]
|
||||
|
|
@ -463,7 +463,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 1500,
|
||||
"gold": 800,
|
||||
"cards": [
|
||||
"xeno_worker_1"
|
||||
]
|
||||
|
|
@ -491,7 +491,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 1600,
|
||||
"gold": 600,
|
||||
"cards": [
|
||||
"xeno_guard_1"
|
||||
]
|
||||
|
|
@ -519,7 +519,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 1700,
|
||||
"gold": 600,
|
||||
"cards": [
|
||||
"xeno_mantis_1"
|
||||
]
|
||||
|
|
@ -547,7 +547,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 1800,
|
||||
"gold": 600,
|
||||
"cards": [
|
||||
"xeno_horror_1"
|
||||
]
|
||||
|
|
@ -575,7 +575,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 1900,
|
||||
"gold": 1200,
|
||||
"cards": [
|
||||
"xeno_hiveguard_1"
|
||||
]
|
||||
|
|
@ -602,7 +602,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 2000,
|
||||
"gold": 700,
|
||||
"cards": [
|
||||
"right_acolyte_1"
|
||||
]
|
||||
|
|
@ -629,7 +629,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 2100,
|
||||
"gold": 700,
|
||||
"cards": [
|
||||
"right_pilgrim_1"
|
||||
]
|
||||
|
|
@ -656,7 +656,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 2200,
|
||||
"gold": 700,
|
||||
"cards": [
|
||||
"right_sentinel_1"
|
||||
]
|
||||
|
|
@ -683,7 +683,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 2300,
|
||||
"gold": 700,
|
||||
"cards": [
|
||||
"right_hacker_1"
|
||||
]
|
||||
|
|
@ -710,7 +710,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 2400,
|
||||
"gold": 700,
|
||||
"cards": [
|
||||
"right_zealot_1"
|
||||
]
|
||||
|
|
@ -737,7 +737,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 2500,
|
||||
"gold": 700,
|
||||
"cards": [
|
||||
"right_templar_1"
|
||||
]
|
||||
|
|
@ -762,7 +762,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 2600,
|
||||
"gold": 700,
|
||||
"cards": [
|
||||
"right_purifier_1"
|
||||
]
|
||||
|
|
@ -789,7 +789,7 @@
|
|||
]
|
||||
},
|
||||
"rewards": {
|
||||
"gold": 2700,
|
||||
"gold": 1300,
|
||||
"cards": [
|
||||
"right_prophet_1"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -239,6 +239,7 @@ export class CombatEngine {
|
|||
[commander, ...cards], [enemyCommander, ...enemyLanes], ctx);
|
||||
if (ctx.moltHeal > 0) {
|
||||
this._log(`${card.name} molts: heals ${ctx.moltHeal}, loses ${ctx.moltArmorLost} armor`);
|
||||
card._moltZeroed = true;
|
||||
this.events.push({ type: 'molt', card, heal: ctx.moltHeal, armorLost: ctx.moltArmorLost });
|
||||
}
|
||||
}
|
||||
|
|
@ -986,13 +987,15 @@ export class CombatEngine {
|
|||
}
|
||||
}
|
||||
|
||||
// Molt preAttack restore: if attacker has molt and armor was zeroed by molt this
|
||||
// preBattle, restore it to (baseArmor + 1) before the attack so the card fights
|
||||
// with a fresh shell. Condition: molt skill present, armor currently 0, base armor > 0.
|
||||
// Molt preAttack restore: if molt fired this preBattle, restore armor to (baseArmor + 1).
|
||||
// baseArmor reflects any interim armor boosts (e.g. protect) that fired while armor was zeroed.
|
||||
// The +1 is permanently added to baseArmor so it compounds over multiple molts.
|
||||
const moltSkill = pending.attacker.skills.find(s => s.name === 'molt');
|
||||
if (moltSkill && pending.attacker.currentArmor === 0 && (pending.attacker.baseArmor ?? 0) > 0) {
|
||||
if (moltSkill && pending.attacker._moltZeroed) {
|
||||
const armorGain = (pending.attacker.baseArmor ?? 0) + 1;
|
||||
pending.attacker.currentArmor = armorGain;
|
||||
pending.attacker.baseArmor = armorGain;
|
||||
pending.attacker._moltZeroed = false;
|
||||
preAttackFires.push({ skill: 'moltRestore', card: pending.attacker, armorGain });
|
||||
this._log(`${pending.attacker.name} molt restore: +${armorGain} ARM before attacking`);
|
||||
}
|
||||
|
|
@ -1094,6 +1097,7 @@ export class CombatEngine {
|
|||
for (const buff of card._tempBuffs) {
|
||||
if (buff.stat === 'currentAttack') card.currentAttack = Math.max(0, card.currentAttack - buff.amount);
|
||||
if (buff.stat === 'currentArmor') card.currentArmor = Math.max(0, card.currentArmor - buff.amount);
|
||||
if (buff.stat === 'baseArmor') card.baseArmor = Math.max(0, (card.baseArmor ?? 0) - buff.amount);
|
||||
if (buff.stat === 'skillValues') {
|
||||
for (const { index, amount } of buff.amounts) {
|
||||
if (card.skills[index]) card.skills[index].value = (card.skills[index].value || 0) + amount;
|
||||
|
|
|
|||
|
|
@ -186,6 +186,10 @@ export class SkillProcessor {
|
|||
t.currentArmor += skill.value;
|
||||
t._tempBuffs = t._tempBuffs || [];
|
||||
t._tempBuffs.push({ stat: 'currentArmor', amount: skill.value, source: 'protect' });
|
||||
if (t._moltZeroed) {
|
||||
t.baseArmor = (t.baseArmor ?? 0) + skill.value;
|
||||
t._tempBuffs.push({ stat: 'baseArmor', amount: skill.value, source: 'protect' });
|
||||
}
|
||||
protectTargets.push({ card: t, amount: skill.value, laneOffset });
|
||||
}
|
||||
if (context) context.protectTargets = protectTargets;
|
||||
|
|
@ -657,6 +661,10 @@ export class SkillProcessor {
|
|||
t.currentArmor += skill.value;
|
||||
t._tempBuffs = t._tempBuffs || [];
|
||||
t._tempBuffs.push({ stat: 'currentArmor', amount: skill.value, source: 'fortify' });
|
||||
if (t._moltZeroed) {
|
||||
t.baseArmor = (t.baseArmor ?? 0) + skill.value;
|
||||
t._tempBuffs.push({ stat: 'baseArmor', amount: skill.value, source: 'fortify' });
|
||||
}
|
||||
allTargets.push({ target: t, amount: skill.value });
|
||||
}
|
||||
if (context && allTargets.length > 0) context.fortifyAllTargets = allTargets;
|
||||
|
|
@ -665,6 +673,10 @@ export class SkillProcessor {
|
|||
attacker.currentArmor += skill.value;
|
||||
attacker._tempBuffs = attacker._tempBuffs || [];
|
||||
attacker._tempBuffs.push({ stat: 'currentArmor', amount: skill.value, source: 'fortify' });
|
||||
if (attacker._moltZeroed) {
|
||||
attacker.baseArmor = (attacker.baseArmor ?? 0) + skill.value;
|
||||
attacker._tempBuffs.push({ stat: 'baseArmor', amount: skill.value, source: 'fortify' });
|
||||
}
|
||||
if (context) {
|
||||
context.fortifyTarget = attacker;
|
||||
context.fortifyAmount = skill.value;
|
||||
|
|
|
|||
|
|
@ -2,32 +2,32 @@ import { SaveManager } from '../managers/SaveManager.js';
|
|||
import { TutorialManager } from '../managers/TutorialManager.js';
|
||||
|
||||
// ── Layout Constants ────────────────────────────────────────────────────────────
|
||||
const LEFT_PANEL_W = 420;
|
||||
const DIVIDER_X = 420;
|
||||
const LEFT_PANEL_W = 420;
|
||||
const DIVIDER_X = 420;
|
||||
|
||||
const MISSION_BTN_X = 210;
|
||||
const MISSION_BTN_W = 360;
|
||||
const MISSION_BTN_H = 90;
|
||||
const MISSION_BTN_X = 210;
|
||||
const MISSION_BTN_W = 360;
|
||||
const MISSION_BTN_H = 90;
|
||||
const MISSION_BTN_GAP = 10;
|
||||
const MISSION_LIST_TOP = 130;
|
||||
|
||||
const TERM_PAD = 30;
|
||||
const TERM_X = LEFT_PANEL_W + TERM_PAD;
|
||||
const TERM_Y = 120;
|
||||
const TERM_W = 1920 - LEFT_PANEL_W - TERM_PAD * 2;
|
||||
const TERM_H = 940;
|
||||
const TERM_PAD = 30;
|
||||
const TERM_X = LEFT_PANEL_W + TERM_PAD;
|
||||
const TERM_Y = 120;
|
||||
const TERM_W = 1920 - LEFT_PANEL_W - TERM_PAD * 2;
|
||||
const TERM_H = 940;
|
||||
|
||||
const VIDEO_W = 640;
|
||||
const VIDEO_H = 360;
|
||||
const VIDEO_W = 640;
|
||||
const VIDEO_H = 360;
|
||||
|
||||
// ── Amber CRT Palette ───────────────────────────────────────────────────────────
|
||||
const AMBER = '#ffaa33';
|
||||
const AMBER_DIM = '#cc8822';
|
||||
const AMBER_BRIGHT = '#ff8800';
|
||||
const AMBER_HEX = 0xffaa33;
|
||||
const AMBER = '#ffaa33';
|
||||
const AMBER_DIM = '#cc8822';
|
||||
const AMBER_BRIGHT = '#ff8800';
|
||||
const AMBER_HEX = 0xffaa33;
|
||||
const AMBER_DARK_HEX = 0x332200;
|
||||
const AMBER_BG_HEX = 0x1a1100;
|
||||
const TERM_BG_HEX = 0x0a0800;
|
||||
const AMBER_BG_HEX = 0x1a1100;
|
||||
const TERM_BG_HEX = 0x0a0800;
|
||||
|
||||
const FACTION_HEX = {
|
||||
raider: 0xcc4444,
|
||||
|
|
@ -55,7 +55,7 @@ export class CampaignScene extends Phaser.Scene {
|
|||
preload() {
|
||||
this.load.video('corridors', 'assets/video/corridors.mp4');
|
||||
// Load mission briefing videos
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
for (let i = 1; i <= 23; i++) {
|
||||
const id = `mission_${String(i).padStart(3, '0')}`;
|
||||
this.load.video(id, `assets/video/${id}.mp4`);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue