Value isn't updating

I'm working on a browser game with Excalibur.js and Solid, and most of it is working as expected. But this one nested value, while definitely changing, isn't updating in my <For> component. I've tried using an <Index> as well, but the value doesn't update either way. The currentHP outside of the For loop does update, but the values in the For do not. Can someone help me figure out what I may be doing wrong?
<p class='bg-white'>
{globalStore.initiativeOrder?.[0].currentHP}
</p>
<For
each={globalStore.initiativeOrder}
fallback={<></>}>
{(combatUnit, index) => {
return (
<>
<div
data-index={index}
class={`flex flex-row items-start justify-around gap-4 text-3xl cursor-pointer m-4 ${combatUnit.isInParty ? 'text-slate-700 hover:text-black' : 'text-red-700 hover:text-red-900'}`}
onclick={() => handleUnitNameClick(combatUnit)}>
<img
src={combatUnit.characterPortrait}
alt={combatUnit.name}
class='border-2'
/>
<span class='w-full'>
{combatUnit.name}
{combatUnit.currentHP}
</span>
</div>
</>
);
}}
</For>
<p class='bg-white'>
{globalStore.initiativeOrder?.[0].currentHP}
</p>
<For
each={globalStore.initiativeOrder}
fallback={<></>}>
{(combatUnit, index) => {
return (
<>
<div
data-index={index}
class={`flex flex-row items-start justify-around gap-4 text-3xl cursor-pointer m-4 ${combatUnit.isInParty ? 'text-slate-700 hover:text-black' : 'text-red-700 hover:text-red-900'}`}
onclick={() => handleUnitNameClick(combatUnit)}>
<img
src={combatUnit.characterPortrait}
alt={combatUnit.name}
class='border-2'
/>
<span class='w-full'>
{combatUnit.name}
{combatUnit.currentHP}
</span>
</div>
</>
);
}}
</For>
5 Replies
bigmistqke
bigmistqke2mo ago
any way that you could make a minimal reproduction of it in the solid playground?
mosesintech
mosesintechOP2mo ago
I forgot to link to the repo: https://github.com/Totality-Games/base-tower-tactics The snippet in the post comes from /src/components/menus/combat/CombatMenu.tsx. Running pnpm dev:setup will have the project at localhost:9999. The snippets that change the currentHP value are in /src/actors//combatUtils/EnemyUnit.ts:
async combatAttack(engine: Engine) {
const children = this.children as GridAttackSquareChild[];
const enemySquares = children.filter(async (square) => {
return square.unitInRange !== undefined;
});

if (!enemySquares.length) {
this.shouldMoveFirst = true;
this.children.map((child) => child.kill());
this.removeAllChildren();
} else {
const randomChild =
enemySquares[Math.floor(Math.random() * enemySquares.length - 1)];

const unitInRange = randomChild.unitInRange!;
unitInRange.currentHP = Number(unitInRange.currentHP) - 1;
unitInRange.actions.flash(Color.Red, 750);
unitInRange.damageVisual.text = '-1';
unitInRange.addChild(unitInRange.damageVisual as Label);

engine.currentScene.camera.shake(2, 2, 250);

this.hasAttacked = true;
this.showAttackSquares = false;
this.children.map((child) => child.kill());
this.removeAllChildren();
setTimeout(() => {
unitInRange.children.map((child) => child.kill());
unitInRange.removeAllChildren();
}, 1000);
}
}
async combatAttack(engine: Engine) {
const children = this.children as GridAttackSquareChild[];
const enemySquares = children.filter(async (square) => {
return square.unitInRange !== undefined;
});

if (!enemySquares.length) {
this.shouldMoveFirst = true;
this.children.map((child) => child.kill());
this.removeAllChildren();
} else {
const randomChild =
enemySquares[Math.floor(Math.random() * enemySquares.length - 1)];

const unitInRange = randomChild.unitInRange!;
unitInRange.currentHP = Number(unitInRange.currentHP) - 1;
unitInRange.actions.flash(Color.Red, 750);
unitInRange.damageVisual.text = '-1';
unitInRange.addChild(unitInRange.damageVisual as Label);

engine.currentScene.camera.shake(2, 2, 250);

this.hasAttacked = true;
this.showAttackSquares = false;
this.children.map((child) => child.kill());
this.removeAllChildren();
setTimeout(() => {
unitInRange.children.map((child) => child.kill());
unitInRange.removeAllChildren();
}, 1000);
}
}
GitHub
GitHub - Totality-Games/base-tower-tactics
Contribute to Totality-Games/base-tower-tactics development by creating an account on GitHub.
mosesintech
mosesintechOP2mo ago
And in /src/actors/combatUtils/GridAttackSquares.ts:
combatAttack(engine: Engine) {
this.on('pointerdown', () => {
if (this.parent === null) return;
const parent = this.parent as CombatUnit;
if (this.unitInRange) {
const unitInRange = this.unitInRange;
unitInRange.currentHP = Number(unitInRange.currentHP) - 1;
unitInRange.actions.flash(Color.Red, 750);
unitInRange.damageVisual.text = '-1';
unitInRange.addChild(unitInRange.damageVisual as Label);

engine.currentScene.camera.shake(2, 2, 250);

parent.hasAttacked = true;
parent.showAttackSquares = false;
parent.children.map((child) => child.kill());
parent.removeAllChildren();
setTimeout(() => {
unitInRange.children.map((child) => child.kill());
unitInRange.removeAllChildren();
}, 1000);
}
});
}
combatAttack(engine: Engine) {
this.on('pointerdown', () => {
if (this.parent === null) return;
const parent = this.parent as CombatUnit;
if (this.unitInRange) {
const unitInRange = this.unitInRange;
unitInRange.currentHP = Number(unitInRange.currentHP) - 1;
unitInRange.actions.flash(Color.Red, 750);
unitInRange.damageVisual.text = '-1';
unitInRange.addChild(unitInRange.damageVisual as Label);

engine.currentScene.camera.shake(2, 2, 250);

parent.hasAttacked = true;
parent.showAttackSquares = false;
parent.children.map((child) => child.kill());
parent.removeAllChildren();
setTimeout(() => {
unitInRange.children.map((child) => child.kill());
unitInRange.removeAllChildren();
}, 1000);
}
});
}
Simon Chan
Simon Chan5w ago
You didn't update currentHP via setStore so it won't work Signal systems generally only support primitives and plain objects and arrays. Some libraries have tried to support class instances, but they're having issues with private fields You can make the class field a signal so it's reactive, but i'm not sure whether you should do it
diff --git a/src/actors/combatUtils/CombatUnit.ts b/src/actors/combatUtils/CombatUnit.ts
index 61b7e9b..1ff5d3f 100644
--- a/src/actors/combatUtils/CombatUnit.ts
+++ b/src/actors/combatUtils/CombatUnit.ts
@@ -15,6 +15,7 @@ import type {
} from '../../context/store';
import { GridMovementSquareChild } from './GridMovementSquares';
import { GridAttackSquareChild } from './GridAttackSquares';
+import { createSignal } from "solid-js";

export class CombatUnit extends Actor {
globalStore: GlobalStoreType;
@@ -35,7 +36,11 @@ export class CombatUnit extends Actor {
hasAttacked: boolean;
isTurnUnit: boolean;
totalHP?: number;
- currentHP?: number;
+ #currentHP = createSignal<number | undefined>(undefined);
+ get currentHP() { return this.#currentHP[0]() }
+ set currentHP(value: number | undefined) {
+ this.#currentHP[1](value)
+ }
damageVisual: Label;
characterPortrait: string;
constructor(pos: Vector, context: ContextProps, isInParty?: boolean) {
diff --git a/src/actors/combatUtils/CombatUnit.ts b/src/actors/combatUtils/CombatUnit.ts
index 61b7e9b..1ff5d3f 100644
--- a/src/actors/combatUtils/CombatUnit.ts
+++ b/src/actors/combatUtils/CombatUnit.ts
@@ -15,6 +15,7 @@ import type {
} from '../../context/store';
import { GridMovementSquareChild } from './GridMovementSquares';
import { GridAttackSquareChild } from './GridAttackSquares';
+import { createSignal } from "solid-js";

export class CombatUnit extends Actor {
globalStore: GlobalStoreType;
@@ -35,7 +36,11 @@ export class CombatUnit extends Actor {
hasAttacked: boolean;
isTurnUnit: boolean;
totalHP?: number;
- currentHP?: number;
+ #currentHP = createSignal<number | undefined>(undefined);
+ get currentHP() { return this.#currentHP[0]() }
+ set currentHP(value: number | undefined) {
+ this.#currentHP[1](value)
+ }
damageVisual: Label;
characterPortrait: string;
constructor(pos: Vector, context: ContextProps, isInParty?: boolean) {
mosesintech
mosesintechOP5w ago
Thank you Simon. That is good info! I'm still pretty new to signals so I tried getting this to work for a week now with no luck. Making the field a signal does seem to work, so I'll do it like this for now unless it causes more issues down the line lol. Gracias!

Did you find this page helpful?