Spaces:
Running
Running
Fix: roam ranged ranges were whole-screen — scale to ~8 tiles
Browse filesRanged basic attacks + ranged skills used arena GW ranges (BOW_GW=1000 ~ 37 roam tiles)
because the roam map is far smaller in field units. Apply ROAM_RANGE_MUL (0.22) to the
hero + enemies so bow reach ~ 8 tiles and kite ~ 5 tiles. Melee + sandboxes untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- web/classesSandbox.js +14 -3
- web/comboBattler.js +5 -2
- web/enemiesSandbox.js +14 -3
- web/engine.js +5 -2
web/classesSandbox.js
CHANGED
|
@@ -979,7 +979,10 @@ function makeActor(unit, team, id, slot) {
|
|
| 979 |
const hpMul = unit.hpMul ?? unit.statMul ?? 1;
|
| 980 |
const dmgMul = unit.dmgMul ?? unit.statMul ?? 1;
|
| 981 |
const maxHp = Math.max(1, Math.round(tpl.maxHp * hpMul));
|
| 982 |
-
const
|
|
|
|
|
|
|
|
|
|
| 983 |
const p = FORMATION[slot % FORMATION.length];
|
| 984 |
const pt = team === "player" ? { x: p.x, y: p.y } : { x: 1 - p.x, y: 1 - p.y };
|
| 985 |
const bar = (unit.skills || []).map((s) => s && typeof s === "object" ? s : skillById(s)).filter(Boolean);
|
|
@@ -997,7 +1000,7 @@ function makeActor(unit, team, id, slot) {
|
|
| 997 |
armor: tpl.armor ?? 0,
|
| 998 |
weapon,
|
| 999 |
moveSpeed: tpl.moveSpeed,
|
| 1000 |
-
preferredRange
|
| 1001 |
radius: radiusOf(unit, tpl),
|
| 1002 |
maxEnergy: tpl.maxEnergy,
|
| 1003 |
energyRegen: tpl.energyRegen,
|
|
@@ -1126,6 +1129,10 @@ function healActor(b, a, amount, empowered) {
|
|
| 1126 |
}
|
| 1127 |
function dealDamage(b, src, tgt, amount, label, opts = {}) {
|
| 1128 |
if (!tgt.alive) return 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1129 |
const { damageType = "physical", delivery = "spell", armorIgnoring = false, empowered = false } = opts;
|
| 1130 |
const physical = delivery === "melee" || delivery === "projectile";
|
| 1131 |
if (physical) {
|
|
@@ -2592,12 +2599,16 @@ async function createCombatRenderer({ pixi, defsById = {}, layers, coords, getBa
|
|
| 2592 |
const v = view[e.who];
|
| 2593 |
if (v) v.flash = 130;
|
| 2594 |
playHurt(e.who, actorOf(e.who));
|
|
|
|
| 2595 |
e.empowered ? floatText(e.who, "-" + e.amount + "!", GOLD, { big: true }) : floatText(e.who, "-" + e.amount, 16743018);
|
| 2596 |
} else if (e.kind === "heal" && e.amount > 0) e.empowered ? floatText(e.who, "+" + e.amount + "!", GOLD, { big: true }) : floatText(e.who, "+" + e.amount, 9429896);
|
| 2597 |
else if (e.kind === "cond" && e.empowered) floatText(e.who, (e.cond === "interrupted" ? "INTERRUPT" : String(e.cond).toUpperCase()) + "!", GOLD, { big: true });
|
| 2598 |
else if (e.kind === "empower") flagEmpower(e.who);
|
| 2599 |
else if (e.kind === "miss") floatText(e.who, "dodge", 10405352);
|
| 2600 |
-
else if (e.kind === "death")
|
|
|
|
|
|
|
|
|
|
| 2601 |
}
|
| 2602 |
}
|
| 2603 |
return {
|
|
|
|
| 979 |
const hpMul = unit.hpMul ?? unit.statMul ?? 1;
|
| 980 |
const dmgMul = unit.dmgMul ?? unit.statMul ?? 1;
|
| 981 |
const maxHp = Math.max(1, Math.round(tpl.maxHp * hpMul));
|
| 982 |
+
const rangeMul = unit.rangeMul ?? 1;
|
| 983 |
+
const ranged = tpl.role === "ranged";
|
| 984 |
+
const weapon = { ...tpl.weapon, min: Math.max(1, Math.round(tpl.weapon.min * dmgMul)), max: Math.max(1, Math.round(tpl.weapon.max * dmgMul)), range: Math.round((tpl.weapon.range ?? 0) * (ranged ? rangeMul : 1)) };
|
| 985 |
+
const preferredRange = tpl.preferredRange != null ? Math.round(tpl.preferredRange * rangeMul) : void 0;
|
| 986 |
const p = FORMATION[slot % FORMATION.length];
|
| 987 |
const pt = team === "player" ? { x: p.x, y: p.y } : { x: 1 - p.x, y: 1 - p.y };
|
| 988 |
const bar = (unit.skills || []).map((s) => s && typeof s === "object" ? s : skillById(s)).filter(Boolean);
|
|
|
|
| 1000 |
armor: tpl.armor ?? 0,
|
| 1001 |
weapon,
|
| 1002 |
moveSpeed: tpl.moveSpeed,
|
| 1003 |
+
preferredRange,
|
| 1004 |
radius: radiusOf(unit, tpl),
|
| 1005 |
maxEnergy: tpl.maxEnergy,
|
| 1006 |
energyRegen: tpl.energyRegen,
|
|
|
|
| 1129 |
}
|
| 1130 |
function dealDamage(b, src, tgt, amount, label, opts = {}) {
|
| 1131 |
if (!tgt.alive) return 0;
|
| 1132 |
+
if (tgt.invulnUntil && b.t < tgt.invulnUntil) {
|
| 1133 |
+
log(b, "miss", tgt, { name: label });
|
| 1134 |
+
return 0;
|
| 1135 |
+
}
|
| 1136 |
const { damageType = "physical", delivery = "spell", armorIgnoring = false, empowered = false } = opts;
|
| 1137 |
const physical = delivery === "melee" || delivery === "projectile";
|
| 1138 |
if (physical) {
|
|
|
|
| 2599 |
const v = view[e.who];
|
| 2600 |
if (v) v.flash = 130;
|
| 2601 |
playHurt(e.who, actorOf(e.who));
|
| 2602 |
+
hooks.onHit?.(e);
|
| 2603 |
e.empowered ? floatText(e.who, "-" + e.amount + "!", GOLD, { big: true }) : floatText(e.who, "-" + e.amount, 16743018);
|
| 2604 |
} else if (e.kind === "heal" && e.amount > 0) e.empowered ? floatText(e.who, "+" + e.amount + "!", GOLD, { big: true }) : floatText(e.who, "+" + e.amount, 9429896);
|
| 2605 |
else if (e.kind === "cond" && e.empowered) floatText(e.who, (e.cond === "interrupted" ? "INTERRUPT" : String(e.cond).toUpperCase()) + "!", GOLD, { big: true });
|
| 2606 |
else if (e.kind === "empower") flagEmpower(e.who);
|
| 2607 |
else if (e.kind === "miss") floatText(e.who, "dodge", 10405352);
|
| 2608 |
+
else if (e.kind === "death") {
|
| 2609 |
+
playDie(e.who, actorOf(e.who));
|
| 2610 |
+
hooks.onDeath?.(e);
|
| 2611 |
+
}
|
| 2612 |
}
|
| 2613 |
}
|
| 2614 |
return {
|
web/comboBattler.js
CHANGED
|
@@ -5063,7 +5063,10 @@ function makeActor(unit, team, id, slot) {
|
|
| 5063 |
const hpMul = unit.hpMul ?? unit.statMul ?? 1;
|
| 5064 |
const dmgMul = unit.dmgMul ?? unit.statMul ?? 1;
|
| 5065 |
const maxHp = Math.max(1, Math.round(tpl.maxHp * hpMul));
|
| 5066 |
-
const
|
|
|
|
|
|
|
|
|
|
| 5067 |
const p = FORMATION[slot % FORMATION.length];
|
| 5068 |
const pt = team === "player" ? { x: p.x, y: p.y } : { x: 1 - p.x, y: 1 - p.y };
|
| 5069 |
const bar = (unit.skills || []).map((s) => s && typeof s === "object" ? s : skillById(s)).filter(Boolean);
|
|
@@ -5081,7 +5084,7 @@ function makeActor(unit, team, id, slot) {
|
|
| 5081 |
armor: tpl.armor ?? 0,
|
| 5082 |
weapon,
|
| 5083 |
moveSpeed: tpl.moveSpeed,
|
| 5084 |
-
preferredRange
|
| 5085 |
radius: radiusOf(unit, tpl),
|
| 5086 |
maxEnergy: tpl.maxEnergy,
|
| 5087 |
energyRegen: tpl.energyRegen,
|
|
|
|
| 5063 |
const hpMul = unit.hpMul ?? unit.statMul ?? 1;
|
| 5064 |
const dmgMul = unit.dmgMul ?? unit.statMul ?? 1;
|
| 5065 |
const maxHp = Math.max(1, Math.round(tpl.maxHp * hpMul));
|
| 5066 |
+
const rangeMul = unit.rangeMul ?? 1;
|
| 5067 |
+
const ranged = tpl.role === "ranged";
|
| 5068 |
+
const weapon = { ...tpl.weapon, min: Math.max(1, Math.round(tpl.weapon.min * dmgMul)), max: Math.max(1, Math.round(tpl.weapon.max * dmgMul)), range: Math.round((tpl.weapon.range ?? 0) * (ranged ? rangeMul : 1)) };
|
| 5069 |
+
const preferredRange = tpl.preferredRange != null ? Math.round(tpl.preferredRange * rangeMul) : void 0;
|
| 5070 |
const p = FORMATION[slot % FORMATION.length];
|
| 5071 |
const pt = team === "player" ? { x: p.x, y: p.y } : { x: 1 - p.x, y: 1 - p.y };
|
| 5072 |
const bar = (unit.skills || []).map((s) => s && typeof s === "object" ? s : skillById(s)).filter(Boolean);
|
|
|
|
| 5084 |
armor: tpl.armor ?? 0,
|
| 5085 |
weapon,
|
| 5086 |
moveSpeed: tpl.moveSpeed,
|
| 5087 |
+
preferredRange,
|
| 5088 |
radius: radiusOf(unit, tpl),
|
| 5089 |
maxEnergy: tpl.maxEnergy,
|
| 5090 |
energyRegen: tpl.energyRegen,
|
web/enemiesSandbox.js
CHANGED
|
@@ -812,7 +812,10 @@ function makeActor(unit, team, id, slot) {
|
|
| 812 |
const hpMul = unit.hpMul ?? unit.statMul ?? 1;
|
| 813 |
const dmgMul = unit.dmgMul ?? unit.statMul ?? 1;
|
| 814 |
const maxHp = Math.max(1, Math.round(tpl.maxHp * hpMul));
|
| 815 |
-
const
|
|
|
|
|
|
|
|
|
|
| 816 |
const p = FORMATION[slot % FORMATION.length];
|
| 817 |
const pt = team === "player" ? { x: p.x, y: p.y } : { x: 1 - p.x, y: 1 - p.y };
|
| 818 |
const bar = (unit.skills || []).map((s) => s && typeof s === "object" ? s : skillById(s)).filter(Boolean);
|
|
@@ -830,7 +833,7 @@ function makeActor(unit, team, id, slot) {
|
|
| 830 |
armor: tpl.armor ?? 0,
|
| 831 |
weapon,
|
| 832 |
moveSpeed: tpl.moveSpeed,
|
| 833 |
-
preferredRange
|
| 834 |
radius: radiusOf(unit, tpl),
|
| 835 |
maxEnergy: tpl.maxEnergy,
|
| 836 |
energyRegen: tpl.energyRegen,
|
|
@@ -959,6 +962,10 @@ function healActor(b, a, amount, empowered) {
|
|
| 959 |
}
|
| 960 |
function dealDamage(b, src, tgt, amount, label, opts = {}) {
|
| 961 |
if (!tgt.alive) return 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 962 |
const { damageType = "physical", delivery = "spell", armorIgnoring = false, empowered = false } = opts;
|
| 963 |
const physical = delivery === "melee" || delivery === "projectile";
|
| 964 |
if (physical) {
|
|
@@ -2555,12 +2562,16 @@ async function createCombatRenderer({ pixi, defsById = {}, layers, coords, getBa
|
|
| 2555 |
const v = view[e.who];
|
| 2556 |
if (v) v.flash = 130;
|
| 2557 |
playHurt(e.who, actorOf(e.who));
|
|
|
|
| 2558 |
e.empowered ? floatText(e.who, "-" + e.amount + "!", GOLD, { big: true }) : floatText(e.who, "-" + e.amount, 16743018);
|
| 2559 |
} else if (e.kind === "heal" && e.amount > 0) e.empowered ? floatText(e.who, "+" + e.amount + "!", GOLD, { big: true }) : floatText(e.who, "+" + e.amount, 9429896);
|
| 2560 |
else if (e.kind === "cond" && e.empowered) floatText(e.who, (e.cond === "interrupted" ? "INTERRUPT" : String(e.cond).toUpperCase()) + "!", GOLD, { big: true });
|
| 2561 |
else if (e.kind === "empower") flagEmpower(e.who);
|
| 2562 |
else if (e.kind === "miss") floatText(e.who, "dodge", 10405352);
|
| 2563 |
-
else if (e.kind === "death")
|
|
|
|
|
|
|
|
|
|
| 2564 |
}
|
| 2565 |
}
|
| 2566 |
return {
|
|
|
|
| 812 |
const hpMul = unit.hpMul ?? unit.statMul ?? 1;
|
| 813 |
const dmgMul = unit.dmgMul ?? unit.statMul ?? 1;
|
| 814 |
const maxHp = Math.max(1, Math.round(tpl.maxHp * hpMul));
|
| 815 |
+
const rangeMul = unit.rangeMul ?? 1;
|
| 816 |
+
const ranged = tpl.role === "ranged";
|
| 817 |
+
const weapon = { ...tpl.weapon, min: Math.max(1, Math.round(tpl.weapon.min * dmgMul)), max: Math.max(1, Math.round(tpl.weapon.max * dmgMul)), range: Math.round((tpl.weapon.range ?? 0) * (ranged ? rangeMul : 1)) };
|
| 818 |
+
const preferredRange = tpl.preferredRange != null ? Math.round(tpl.preferredRange * rangeMul) : void 0;
|
| 819 |
const p = FORMATION[slot % FORMATION.length];
|
| 820 |
const pt = team === "player" ? { x: p.x, y: p.y } : { x: 1 - p.x, y: 1 - p.y };
|
| 821 |
const bar = (unit.skills || []).map((s) => s && typeof s === "object" ? s : skillById(s)).filter(Boolean);
|
|
|
|
| 833 |
armor: tpl.armor ?? 0,
|
| 834 |
weapon,
|
| 835 |
moveSpeed: tpl.moveSpeed,
|
| 836 |
+
preferredRange,
|
| 837 |
radius: radiusOf(unit, tpl),
|
| 838 |
maxEnergy: tpl.maxEnergy,
|
| 839 |
energyRegen: tpl.energyRegen,
|
|
|
|
| 962 |
}
|
| 963 |
function dealDamage(b, src, tgt, amount, label, opts = {}) {
|
| 964 |
if (!tgt.alive) return 0;
|
| 965 |
+
if (tgt.invulnUntil && b.t < tgt.invulnUntil) {
|
| 966 |
+
log(b, "miss", tgt, { name: label });
|
| 967 |
+
return 0;
|
| 968 |
+
}
|
| 969 |
const { damageType = "physical", delivery = "spell", armorIgnoring = false, empowered = false } = opts;
|
| 970 |
const physical = delivery === "melee" || delivery === "projectile";
|
| 971 |
if (physical) {
|
|
|
|
| 2562 |
const v = view[e.who];
|
| 2563 |
if (v) v.flash = 130;
|
| 2564 |
playHurt(e.who, actorOf(e.who));
|
| 2565 |
+
hooks.onHit?.(e);
|
| 2566 |
e.empowered ? floatText(e.who, "-" + e.amount + "!", GOLD, { big: true }) : floatText(e.who, "-" + e.amount, 16743018);
|
| 2567 |
} else if (e.kind === "heal" && e.amount > 0) e.empowered ? floatText(e.who, "+" + e.amount + "!", GOLD, { big: true }) : floatText(e.who, "+" + e.amount, 9429896);
|
| 2568 |
else if (e.kind === "cond" && e.empowered) floatText(e.who, (e.cond === "interrupted" ? "INTERRUPT" : String(e.cond).toUpperCase()) + "!", GOLD, { big: true });
|
| 2569 |
else if (e.kind === "empower") flagEmpower(e.who);
|
| 2570 |
else if (e.kind === "miss") floatText(e.who, "dodge", 10405352);
|
| 2571 |
+
else if (e.kind === "death") {
|
| 2572 |
+
playDie(e.who, actorOf(e.who));
|
| 2573 |
+
hooks.onDeath?.(e);
|
| 2574 |
+
}
|
| 2575 |
}
|
| 2576 |
}
|
| 2577 |
return {
|
web/engine.js
CHANGED
|
@@ -786,7 +786,10 @@ function makeActor(unit, team, id, slot) {
|
|
| 786 |
const hpMul = unit.hpMul ?? unit.statMul ?? 1;
|
| 787 |
const dmgMul = unit.dmgMul ?? unit.statMul ?? 1;
|
| 788 |
const maxHp = Math.max(1, Math.round(tpl.maxHp * hpMul));
|
| 789 |
-
const
|
|
|
|
|
|
|
|
|
|
| 790 |
const p = FORMATION[slot % FORMATION.length];
|
| 791 |
const pt = team === "player" ? { x: p.x, y: p.y } : { x: 1 - p.x, y: 1 - p.y };
|
| 792 |
const bar = (unit.skills || []).map((s) => s && typeof s === "object" ? s : skillById(s)).filter(Boolean);
|
|
@@ -804,7 +807,7 @@ function makeActor(unit, team, id, slot) {
|
|
| 804 |
armor: tpl.armor ?? 0,
|
| 805 |
weapon,
|
| 806 |
moveSpeed: tpl.moveSpeed,
|
| 807 |
-
preferredRange
|
| 808 |
radius: radiusOf(unit, tpl),
|
| 809 |
maxEnergy: tpl.maxEnergy,
|
| 810 |
energyRegen: tpl.energyRegen,
|
|
|
|
| 786 |
const hpMul = unit.hpMul ?? unit.statMul ?? 1;
|
| 787 |
const dmgMul = unit.dmgMul ?? unit.statMul ?? 1;
|
| 788 |
const maxHp = Math.max(1, Math.round(tpl.maxHp * hpMul));
|
| 789 |
+
const rangeMul = unit.rangeMul ?? 1;
|
| 790 |
+
const ranged = tpl.role === "ranged";
|
| 791 |
+
const weapon = { ...tpl.weapon, min: Math.max(1, Math.round(tpl.weapon.min * dmgMul)), max: Math.max(1, Math.round(tpl.weapon.max * dmgMul)), range: Math.round((tpl.weapon.range ?? 0) * (ranged ? rangeMul : 1)) };
|
| 792 |
+
const preferredRange = tpl.preferredRange != null ? Math.round(tpl.preferredRange * rangeMul) : void 0;
|
| 793 |
const p = FORMATION[slot % FORMATION.length];
|
| 794 |
const pt = team === "player" ? { x: p.x, y: p.y } : { x: 1 - p.x, y: 1 - p.y };
|
| 795 |
const bar = (unit.skills || []).map((s) => s && typeof s === "object" ? s : skillById(s)).filter(Boolean);
|
|
|
|
| 807 |
armor: tpl.armor ?? 0,
|
| 808 |
weapon,
|
| 809 |
moveSpeed: tpl.moveSpeed,
|
| 810 |
+
preferredRange,
|
| 811 |
radius: radiusOf(unit, tpl),
|
| 812 |
maxEnergy: tpl.maxEnergy,
|
| 813 |
energyRegen: tpl.energyRegen,
|