refactor: replace number health with Health value object in MagicalObject
- MagicalObject: internal #health and #maxHealth now Health type - HealingObject: constructor, create, and heal use Health value object - MagicalWeapon: constructor, create, and use use Health value object - DamageDealer and Healer interfaces: health: number -> health: Health - magical-objects.spec.ts: all assertions use .health.value - Run npm run checks: 0 errors, 70 tests passing
This commit is contained in:
@@ -1,18 +1,19 @@
|
|||||||
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
||||||
import { isToolCallEventType } from "@earendil-works/pi-coding-agent";
|
import { isToolCallEventType } from '@earendil-works/pi-coding-agent';
|
||||||
|
|
||||||
export default function (pi: ExtensionAPI) {
|
export default function (pi: ExtensionAPI) {
|
||||||
pi.on("tool_call", async (event, ctx) => {
|
pi.on('tool_call', async (event, ctx) => {
|
||||||
if (!isToolCallEventType("bash", event)) return;
|
if (!isToolCallEventType('bash', event)) return;
|
||||||
|
|
||||||
const cmd = event.input.command || "";
|
const cmd = event.input.command || '';
|
||||||
const isYxCommand = cmd.includes("yx ");
|
const isYxCommand = cmd.includes('yx ');
|
||||||
|
|
||||||
// Allow yaks in interactive mode, block in print mode (sub-agents)
|
// Allow yaks in interactive mode, block in print mode (sub-agents)
|
||||||
if (isYxCommand && ctx.mode === "print") {
|
if (isYxCommand && ctx.mode === 'print') {
|
||||||
return {
|
return {
|
||||||
block: true,
|
block: true,
|
||||||
reason: "yx commands are disabled in print mode. Sub-agents must focus on domain work only.",
|
reason:
|
||||||
|
'yx commands are disabled in print mode. Sub-agents must focus on domain work only.',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ Coordinate sub-agent work using the `yx` CLI. The main agent creates tasks and d
|
|||||||
## Running Sub-Agents
|
## Running Sub-Agents
|
||||||
|
|
||||||
Sub-agents execute in print mode and are **hard-blocked from using `yx` commands** (see `.pi/extensions/yak-mode-gate.ts`). They receive:
|
Sub-agents execute in print mode and are **hard-blocked from using `yx` commands** (see `.pi/extensions/yak-mode-gate.ts`). They receive:
|
||||||
|
|
||||||
- The yak's `.context.md` (task description)
|
- The yak's `.context.md` (task description)
|
||||||
- `AGENTS.md` (project conventions)
|
- `AGENTS.md` (project conventions)
|
||||||
- Domain files to work on
|
- Domain files to work on
|
||||||
@@ -43,6 +44,7 @@ cat .yaks/<task-id>/.state
|
|||||||
```
|
```
|
||||||
|
|
||||||
States:
|
States:
|
||||||
|
|
||||||
- `pending` — not yet started
|
- `pending` — not yet started
|
||||||
- `in-progress` — being worked on
|
- `in-progress` — being worked on
|
||||||
- `done` — completed
|
- `done` — completed
|
||||||
@@ -54,6 +56,7 @@ yx done <task-id>
|
|||||||
```
|
```
|
||||||
|
|
||||||
Always verify before marking done:
|
Always verify before marking done:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Check state
|
# 1. Check state
|
||||||
cat .yaks/<task-id>/.state
|
cat .yaks/<task-id>/.state
|
||||||
@@ -68,6 +71,7 @@ npm run checks
|
|||||||
## Sub-Agent Communication
|
## Sub-Agent Communication
|
||||||
|
|
||||||
Sub-agents can read yak state files directly (no yx CLI needed):
|
Sub-agents can read yak state files directly (no yx CLI needed):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cat .yaks/<task-id>/.name # task name
|
cat .yaks/<task-id>/.name # task name
|
||||||
cat .yaks/<task-id>/.state # current state
|
cat .yaks/<task-id>/.state # current state
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ The TypeScript implementation embraces functional patterns:
|
|||||||
All five user stories are implemented and verified:
|
All five user stories are implemented and verified:
|
||||||
|
|
||||||
| Story | Topic | Status |
|
| Story | Topic | Status |
|
||||||
|-------|-------|--------|
|
| ----- | --------------------------- | ------- |
|
||||||
| 1 | Character Creation & Damage | ✅ Done |
|
| 1 | Character Creation & Damage | ✅ Done |
|
||||||
| 2 | Levels | ✅ Done |
|
| 2 | Levels | ✅ Done |
|
||||||
| 3 | Factions | ✅ Done |
|
| 3 | Factions | ✅ Done |
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ import { join, parse } from 'node:path';
|
|||||||
const VALUE_OBJECTS_DIR = join(import.meta.dirname, '..', 'src', 'value-objects');
|
const VALUE_OBJECTS_DIR = join(import.meta.dirname, '..', 'src', 'value-objects');
|
||||||
|
|
||||||
function discoverValueObjects() {
|
function discoverValueObjects() {
|
||||||
const files = readdirSync(VALUE_OBJECTS_DIR).filter(
|
const files = readdirSync(VALUE_OBJECTS_DIR).filter((f) => f.endsWith('.ts') && f !== 'index.ts');
|
||||||
(f) => f.endsWith('.ts') && f !== 'index.ts',
|
|
||||||
);
|
|
||||||
return files.map((f) => parse(f).name);
|
return files.map((f) => parse(f).name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-14
@@ -38,7 +38,7 @@ describe('Magical Objects', () => {
|
|||||||
const target = Character.create({ name: 'goblin', level: Level.create(1) });
|
const target = Character.create({ name: 'goblin', level: Level.create(1) });
|
||||||
const weapon = MagicalWeapon.create({ damage, maxHealth: weaponHP, owner: attacker });
|
const weapon = MagicalWeapon.create({ damage, maxHealth: weaponHP, owner: attacker });
|
||||||
const result = attacker.useWeapon(weapon, target);
|
const result = attacker.useWeapon(weapon, target);
|
||||||
return result.weapon.health === weaponHP - 1;
|
return result.weapon.health.value === weaponHP - 1;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -86,11 +86,11 @@ describe('Magical Objects', () => {
|
|||||||
// Kill the attacker using a separate killer
|
// Kill the attacker using a separate killer
|
||||||
const killer = Character.create({ name: 'boss', level: Level.create(1) });
|
const killer = Character.create({ name: 'boss', level: Level.create(1) });
|
||||||
const deadAttacker = killer.dealDamage(attacker, 10000);
|
const deadAttacker = killer.dealDamage(attacker, 10000);
|
||||||
const weaponHPBefore = weapon.health;
|
const weaponHPBefore = weapon.health.value;
|
||||||
const targetHealthBefore = target.health.value;
|
const targetHealthBefore = target.health.value;
|
||||||
const result = deadAttacker.useWeapon(weapon, target);
|
const result = deadAttacker.useWeapon(weapon, target);
|
||||||
return (
|
return (
|
||||||
result.weapon.health === weaponHPBefore &&
|
result.weapon.health.value === weaponHPBefore &&
|
||||||
result.target.health.value === targetHealthBefore
|
result.target.health.value === targetHealthBefore
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -110,11 +110,11 @@ describe('Magical Objects', () => {
|
|||||||
const thief = Character.create({ name: 'thief', level: Level.create(1) });
|
const thief = Character.create({ name: 'thief', level: Level.create(1) });
|
||||||
const target = Character.create({ name: 'goblin', level: Level.create(1) });
|
const target = Character.create({ name: 'goblin', level: Level.create(1) });
|
||||||
const weapon = MagicalWeapon.create({ damage, maxHealth: weaponHP, owner });
|
const weapon = MagicalWeapon.create({ damage, maxHealth: weaponHP, owner });
|
||||||
const weaponHPBefore = weapon.health;
|
const weaponHPBefore = weapon.health.value;
|
||||||
const targetHealthBefore = target.health.value;
|
const targetHealthBefore = target.health.value;
|
||||||
const result = thief.useWeapon(weapon, target);
|
const result = thief.useWeapon(weapon, target);
|
||||||
return (
|
return (
|
||||||
result.weapon.health === weaponHPBefore &&
|
result.weapon.health.value === weaponHPBefore &&
|
||||||
result.target.health.value === targetHealthBefore
|
result.target.health.value === targetHealthBefore
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -136,7 +136,9 @@ describe('Magical Objects', () => {
|
|||||||
const targetHealthBefore = firstUse.target.health.value;
|
const targetHealthBefore = firstUse.target.health.value;
|
||||||
// Try to use again on the destroyed weapon
|
// Try to use again on the destroyed weapon
|
||||||
const result = owner.useWeapon(destroyedWeapon, firstUse.target);
|
const result = owner.useWeapon(destroyedWeapon, firstUse.target);
|
||||||
return result.weapon.health === 0 && result.target.health.value === targetHealthBefore;
|
return (
|
||||||
|
result.weapon.health.value === 0 && result.target.health.value === targetHealthBefore
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -203,7 +205,7 @@ describe('Magical Objects', () => {
|
|||||||
});
|
});
|
||||||
const object = HealingObject.create({ maxHealth: objectHP, currentHealth: objectHP });
|
const object = HealingObject.create({ maxHealth: objectHP, currentHealth: objectHP });
|
||||||
const result = character.useHealingObject(object, healAmount);
|
const result = character.useHealingObject(object, healAmount);
|
||||||
return result.object.health === objectHP - healAmount;
|
return result.object.health.value === objectHP - healAmount;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -255,11 +257,11 @@ describe('Magical Objects', () => {
|
|||||||
// Kill the character using a separate killer
|
// Kill the character using a separate killer
|
||||||
const killer = Character.create({ name: 'boss', level: Level.create(1) });
|
const killer = Character.create({ name: 'boss', level: Level.create(1) });
|
||||||
const deadCharacter = killer.dealDamage(character, 10000);
|
const deadCharacter = killer.dealDamage(character, 10000);
|
||||||
const objectHPBefore = object.health;
|
const objectHPBefore = object.health.value;
|
||||||
const characterHealthBefore = deadCharacter.health.value;
|
const characterHealthBefore = deadCharacter.health.value;
|
||||||
const result = deadCharacter.useHealingObject(object, 100);
|
const result = deadCharacter.useHealingObject(object, 100);
|
||||||
return (
|
return (
|
||||||
result.object.health === objectHPBefore &&
|
result.object.health.value === objectHPBefore &&
|
||||||
result.character.health.value === characterHealthBefore
|
result.character.health.value === characterHealthBefore
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@@ -287,7 +289,8 @@ describe('Magical Objects', () => {
|
|||||||
// Try to use again on the destroyed object
|
// Try to use again on the destroyed object
|
||||||
const result = healedCharacter.useHealingObject(destroyedObject, 100);
|
const result = healedCharacter.useHealingObject(destroyedObject, 100);
|
||||||
return (
|
return (
|
||||||
result.object.health === 0 && result.character.health.value === characterHealthBefore
|
result.object.health.value === 0 &&
|
||||||
|
result.character.health.value === characterHealthBefore
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -305,7 +308,7 @@ describe('Magical Objects', () => {
|
|||||||
const target = Character.create({ name: 'goblin', level: Level.create(1) });
|
const target = Character.create({ name: 'goblin', level: Level.create(1) });
|
||||||
const weapon = MagicalWeapon.create({ damage, maxHealth: weaponHP, owner: attacker });
|
const weapon = MagicalWeapon.create({ damage, maxHealth: weaponHP, owner: attacker });
|
||||||
const result = attacker.useWeapon(weapon, target);
|
const result = attacker.useWeapon(weapon, target);
|
||||||
return result.weapon.health >= 0;
|
return result.weapon.health.value >= 0;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -320,7 +323,7 @@ describe('Magical Objects', () => {
|
|||||||
const character = Character.create({ name: 'hero', level: Level.create(1) });
|
const character = Character.create({ name: 'hero', level: Level.create(1) });
|
||||||
const object = HealingObject.create({ maxHealth: objectHP, currentHealth: objectHP });
|
const object = HealingObject.create({ maxHealth: objectHP, currentHealth: objectHP });
|
||||||
const result = character.useHealingObject(object, healAmount);
|
const result = character.useHealingObject(object, healAmount);
|
||||||
return result.object.health >= 0;
|
return result.object.health.value >= 0;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -336,7 +339,7 @@ describe('Magical Objects', () => {
|
|||||||
const target = Character.create({ name: 'goblin', level: Level.create(1) });
|
const target = Character.create({ name: 'goblin', level: Level.create(1) });
|
||||||
const weapon = MagicalWeapon.create({ damage, maxHealth: weaponHP, owner: attacker });
|
const weapon = MagicalWeapon.create({ damage, maxHealth: weaponHP, owner: attacker });
|
||||||
const result = attacker.useWeapon(weapon, target);
|
const result = attacker.useWeapon(weapon, target);
|
||||||
return result.weapon.health <= weaponHP;
|
return result.weapon.health.value <= weaponHP;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -351,7 +354,7 @@ describe('Magical Objects', () => {
|
|||||||
const character = Character.create({ name: 'hero', level: Level.create(1) });
|
const character = Character.create({ name: 'hero', level: Level.create(1) });
|
||||||
const object = HealingObject.create({ maxHealth: objectHP, currentHealth: objectHP });
|
const object = HealingObject.create({ maxHealth: objectHP, currentHealth: objectHP });
|
||||||
const result = character.useHealingObject(object, healAmount);
|
const result = character.useHealingObject(object, healAmount);
|
||||||
return result.object.health <= objectHP;
|
return result.object.health.value <= objectHP;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,14 +7,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Character } from '../characters/Character.ts';
|
import { Character } from '../characters/Character.ts';
|
||||||
|
import { Health } from '../value-objects/Health.ts';
|
||||||
import { Level } from '../value-objects/Level.ts';
|
import { Level } from '../value-objects/Level.ts';
|
||||||
import { MagicalObject } from './MagicalObject.ts';
|
import { MagicalObject } from './MagicalObject.ts';
|
||||||
import type { Healer } from './magical-object-types.ts';
|
import type { Healer } from './magical-object-types.ts';
|
||||||
|
|
||||||
export class HealingObject extends MagicalObject implements Healer {
|
export class HealingObject extends MagicalObject implements Healer {
|
||||||
private constructor(
|
private constructor(
|
||||||
health: number,
|
health: Health,
|
||||||
maxHealth: number,
|
maxHealth: Health,
|
||||||
status: { readonly kind: 'alive' } | { readonly kind: 'destroyed' },
|
status: { readonly kind: 'alive' } | { readonly kind: 'destroyed' },
|
||||||
) {
|
) {
|
||||||
super(health, maxHealth, status);
|
super(health, maxHealth, status);
|
||||||
@@ -32,7 +33,7 @@ export class HealingObject extends MagicalObject implements Healer {
|
|||||||
if (currentHealth > maxHealth) throw new Error('CurrentHealth cannot exceed maxHealth');
|
if (currentHealth > maxHealth) throw new Error('CurrentHealth cannot exceed maxHealth');
|
||||||
const status =
|
const status =
|
||||||
currentHealth === 0 ? { kind: 'destroyed' as const } : { kind: 'alive' as const };
|
currentHealth === 0 ? { kind: 'destroyed' as const } : { kind: 'alive' as const };
|
||||||
return new HealingObject(currentHealth, maxHealth, status);
|
return new HealingObject(Health.create(currentHealth), Health.create(maxHealth), status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Use this object to heal a character. Returns updated object and character. */
|
/** Use this object to heal a character. Returns updated object and character. */
|
||||||
@@ -44,7 +45,7 @@ export class HealingObject extends MagicalObject implements Healer {
|
|||||||
// Negative amount is invalid
|
// Negative amount is invalid
|
||||||
if (amount < 0) throw new Error('Heal amount must be non-negative');
|
if (amount < 0) throw new Error('Heal amount must be non-negative');
|
||||||
// Calculate actual heal amount: min of requested, object remaining, character headroom
|
// Calculate actual heal amount: min of requested, object remaining, character headroom
|
||||||
const objectRemaining = this.health;
|
const objectRemaining = this.health.value;
|
||||||
const characterMax = Level.maxHealthForLevel(character.level.value);
|
const characterMax = Level.maxHealthForLevel(character.level.value);
|
||||||
const characterHeadroom = characterMax - character.health.value;
|
const characterHeadroom = characterMax - character.health.value;
|
||||||
const actualHeal = Math.min(amount, objectRemaining, characterHeadroom);
|
const actualHeal = Math.min(amount, objectRemaining, characterHeadroom);
|
||||||
@@ -53,7 +54,7 @@ export class HealingObject extends MagicalObject implements Healer {
|
|||||||
return { object: this, character };
|
return { object: this, character };
|
||||||
}
|
}
|
||||||
// Create updated object
|
// Create updated object
|
||||||
const newObjectHealth = this.health - actualHeal;
|
const newObjectHealth = this.health.value - actualHeal;
|
||||||
const newObjectStatus =
|
const newObjectStatus =
|
||||||
newObjectHealth === 0 ? { kind: 'destroyed' as const } : { kind: 'alive' as const };
|
newObjectHealth === 0 ? { kind: 'destroyed' as const } : { kind: 'alive' as const };
|
||||||
// Create updated character
|
// Create updated character
|
||||||
@@ -64,7 +65,7 @@ export class HealingObject extends MagicalObject implements Healer {
|
|||||||
health: newCharacterHealth,
|
health: newCharacterHealth,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
object: new HealingObject(newObjectHealth, this.maxHealth, newObjectStatus),
|
object: new HealingObject(Health.create(newObjectHealth), this.maxHealth, newObjectStatus),
|
||||||
character: newCharacter,
|
character: newCharacter,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,24 +7,26 @@
|
|||||||
* - Status derived from health (0 = destroyed, > 0 = alive)
|
* - Status derived from health (0 = destroyed, > 0 = alive)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Health } from '../value-objects/Health.ts';
|
||||||
|
|
||||||
export type MagicalObjectStatus = { readonly kind: 'alive' } | { readonly kind: 'destroyed' };
|
export type MagicalObjectStatus = { readonly kind: 'alive' } | { readonly kind: 'destroyed' };
|
||||||
|
|
||||||
export class MagicalObject {
|
export class MagicalObject {
|
||||||
readonly #health: number;
|
readonly #health: Health;
|
||||||
readonly #maxHealth: number;
|
readonly #maxHealth: Health;
|
||||||
readonly #status: MagicalObjectStatus;
|
readonly #status: MagicalObjectStatus;
|
||||||
|
|
||||||
protected constructor(health: number, maxHealth: number, status: MagicalObjectStatus) {
|
protected constructor(health: Health, maxHealth: Health, status: MagicalObjectStatus) {
|
||||||
this.#health = health;
|
this.#health = health;
|
||||||
this.#maxHealth = maxHealth;
|
this.#maxHealth = maxHealth;
|
||||||
this.#status = status;
|
this.#status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
get health(): number {
|
get health(): Health {
|
||||||
return this.#health;
|
return this.#health;
|
||||||
}
|
}
|
||||||
|
|
||||||
get maxHealth(): number {
|
get maxHealth(): Health {
|
||||||
return this.#maxHealth;
|
return this.#maxHealth;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +37,7 @@ export class MagicalObject {
|
|||||||
/** Create a destroyed object (health = 0). */
|
/** Create a destroyed object (health = 0). */
|
||||||
static createDestroyed(maxHealth: number): MagicalObject {
|
static createDestroyed(maxHealth: number): MagicalObject {
|
||||||
if (maxHealth < 0) throw new Error('MaxHealth cannot be negative');
|
if (maxHealth < 0) throw new Error('MaxHealth cannot be negative');
|
||||||
return new MagicalObject(0, maxHealth, { kind: 'destroyed' });
|
return new MagicalObject(Health.create(0), Health.create(maxHealth), { kind: 'destroyed' });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check if this object is alive. */
|
/** Check if this object is alive. */
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
* - Damage is non-negative
|
* - Damage is non-negative
|
||||||
*/
|
*/
|
||||||
import { Character } from '../characters/Character.ts';
|
import { Character } from '../characters/Character.ts';
|
||||||
|
import { Health } from '../value-objects/Health.ts';
|
||||||
import { MagicalObject } from './MagicalObject.ts';
|
import { MagicalObject } from './MagicalObject.ts';
|
||||||
import type { DamageDealer } from './magical-object-types.ts';
|
import type { DamageDealer } from './magical-object-types.ts';
|
||||||
|
|
||||||
@@ -14,8 +15,8 @@ export class MagicalWeapon extends MagicalObject implements DamageDealer {
|
|||||||
readonly #owner: Character;
|
readonly #owner: Character;
|
||||||
|
|
||||||
private constructor(
|
private constructor(
|
||||||
health: number,
|
health: Health,
|
||||||
maxHealth: number,
|
maxHealth: Health,
|
||||||
status: { readonly kind: 'alive' } | { readonly kind: 'destroyed' },
|
status: { readonly kind: 'alive' } | { readonly kind: 'destroyed' },
|
||||||
damage: number,
|
damage: number,
|
||||||
owner: Character,
|
owner: Character,
|
||||||
@@ -36,7 +37,13 @@ export class MagicalWeapon extends MagicalObject implements DamageDealer {
|
|||||||
}): MagicalWeapon {
|
}): MagicalWeapon {
|
||||||
if (maxHealth < 0) throw new Error('MaxHealth cannot be negative');
|
if (maxHealth < 0) throw new Error('MaxHealth cannot be negative');
|
||||||
if (damage < 0) throw new Error('Damage cannot be negative');
|
if (damage < 0) throw new Error('Damage cannot be negative');
|
||||||
return new MagicalWeapon(maxHealth, maxHealth, { kind: 'alive' }, damage, owner);
|
return new MagicalWeapon(
|
||||||
|
Health.create(maxHealth),
|
||||||
|
Health.create(maxHealth),
|
||||||
|
{ kind: 'alive' },
|
||||||
|
damage,
|
||||||
|
owner,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get damage(): number {
|
get damage(): number {
|
||||||
@@ -63,12 +70,12 @@ export class MagicalWeapon extends MagicalObject implements DamageDealer {
|
|||||||
status: newTargetStatus,
|
status: newTargetStatus,
|
||||||
});
|
});
|
||||||
// Reduce weapon health by 1
|
// Reduce weapon health by 1
|
||||||
const newWeaponHealth = this.health - 1;
|
const newWeaponHealth = this.health.value - 1;
|
||||||
const newWeaponStatus =
|
const newWeaponStatus =
|
||||||
newWeaponHealth === 0 ? { kind: 'destroyed' as const } : { kind: 'alive' as const };
|
newWeaponHealth === 0 ? { kind: 'destroyed' as const } : { kind: 'alive' as const };
|
||||||
return {
|
return {
|
||||||
weapon: new MagicalWeapon(
|
weapon: new MagicalWeapon(
|
||||||
newWeaponHealth,
|
Health.create(newWeaponHealth),
|
||||||
this.maxHealth,
|
this.maxHealth,
|
||||||
newWeaponStatus,
|
newWeaponStatus,
|
||||||
this.#damage,
|
this.#damage,
|
||||||
|
|||||||
@@ -6,19 +6,20 @@
|
|||||||
* so Character can depend on abstractions rather than concrete classes.
|
* so Character can depend on abstractions rather than concrete classes.
|
||||||
*/
|
*/
|
||||||
import type { Character } from '../characters/Character.ts';
|
import type { Character } from '../characters/Character.ts';
|
||||||
|
import type { Health } from '../value-objects/Health.ts';
|
||||||
import type { MagicalObjectStatus } from './MagicalObject.ts';
|
import type { MagicalObjectStatus } from './MagicalObject.ts';
|
||||||
|
|
||||||
/** A magical object that deals damage — from Character's point of view */
|
/** A magical object that deals damage — from Character's point of view */
|
||||||
export interface DamageDealer {
|
export interface DamageDealer {
|
||||||
readonly owner: Character;
|
readonly owner: Character;
|
||||||
readonly health: number;
|
readonly health: Health;
|
||||||
readonly status: MagicalObjectStatus;
|
readonly status: MagicalObjectStatus;
|
||||||
use(target: Character): { weapon: DamageDealer; target: Character };
|
use(target: Character): { weapon: DamageDealer; target: Character };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A magical object that heals — from Character's point of view */
|
/** A magical object that heals — from Character's point of view */
|
||||||
export interface Healer {
|
export interface Healer {
|
||||||
readonly health: number;
|
readonly health: Health;
|
||||||
readonly status: MagicalObjectStatus;
|
readonly status: MagicalObjectStatus;
|
||||||
heal(character: Character, amount: number): { object: Healer; character: Character };
|
heal(character: Character, amount: number): { object: Healer; character: Character };
|
||||||
}
|
}
|
||||||
|
|||||||
+10248
-1392
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+10248
-1392
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user