story 3 was not completely done
This commit is contained in:
parent
f6605bbbfd
commit
350e8073e9
@ -1,20 +1,18 @@
|
||||
/**
|
||||
* CharacterState — immutable record of all character state at a point in time.
|
||||
*
|
||||
* Groups the five character properties into a single value object,
|
||||
* keeping the Character constructor at one parameter (max-params: 4).
|
||||
* Groups the five character properties into a single value type,
|
||||
* keeping the Character constructor at one parameter.
|
||||
*/
|
||||
import type { Health } from './Health.ts';
|
||||
import type { Level } from './Level.ts';
|
||||
import type { Status } from './Status.ts';
|
||||
import type { Faction } from './Faction.ts';
|
||||
|
||||
export class CharacterState {
|
||||
constructor(
|
||||
readonly name: string,
|
||||
readonly health: Health,
|
||||
readonly status: Status,
|
||||
readonly level: Level,
|
||||
readonly factions: ReadonlySet<Faction>,
|
||||
) {}
|
||||
}
|
||||
export type CharacterState = {
|
||||
readonly name: string;
|
||||
readonly health: Health;
|
||||
readonly status: Status;
|
||||
readonly level: Level;
|
||||
readonly factions: ReadonlySet<Faction>;
|
||||
};
|
||||
|
||||
@ -1,24 +1,22 @@
|
||||
/**
|
||||
* Healing Object — a Magical Object that gives health to Characters.
|
||||
*
|
||||
* Inherits health/status management from MagicalObject.
|
||||
* Invariants enforced at construction:
|
||||
* - Health is non-negative
|
||||
* - Health never exceeds maxHealth
|
||||
* - CurrentHealth never exceeds maxHealth
|
||||
*/
|
||||
|
||||
import { Character } from './Character.ts';
|
||||
import { Level } from './Level.ts';
|
||||
import { MagicalObject } from './MagicalObject.ts';
|
||||
|
||||
export type ObjectStatus = { kind: 'alive' } | { kind: 'destroyed' };
|
||||
|
||||
export class HealingObject {
|
||||
readonly #health: number;
|
||||
readonly #maxHealth: number;
|
||||
readonly #status: ObjectStatus;
|
||||
|
||||
private constructor(health: number, maxHealth: number, status: ObjectStatus) {
|
||||
this.#health = health;
|
||||
this.#maxHealth = maxHealth;
|
||||
this.#status = status;
|
||||
export class HealingObject extends MagicalObject {
|
||||
private constructor(
|
||||
health: number,
|
||||
maxHealth: number,
|
||||
status: { readonly kind: 'alive' } | { readonly kind: 'destroyed' },
|
||||
) {
|
||||
super(health, maxHealth, status);
|
||||
}
|
||||
|
||||
static create({
|
||||
@ -36,29 +34,17 @@ export class HealingObject {
|
||||
return new HealingObject(currentHealth, maxHealth, status);
|
||||
}
|
||||
|
||||
get health(): number {
|
||||
return this.#health;
|
||||
}
|
||||
|
||||
get maxHealth(): number {
|
||||
return this.#maxHealth;
|
||||
}
|
||||
|
||||
get status(): ObjectStatus {
|
||||
return this.#status;
|
||||
}
|
||||
|
||||
/** Use this object to heal a character. Returns updated object and character. */
|
||||
heal(character: Character, amount: number): { object: HealingObject; character: Character } {
|
||||
// Destroyed objects can't heal
|
||||
if (this.#status.kind === 'destroyed') {
|
||||
if (this.status.kind === 'destroyed') {
|
||||
return { object: this, character };
|
||||
}
|
||||
// Negative amount is invalid
|
||||
if (amount < 0) throw new Error('Heal amount must be non-negative');
|
||||
// Calculate actual heal amount: min of requested, object remaining, character headroom
|
||||
const objectRemaining = this.#health;
|
||||
const characterMax = character.level.value >= 6 ? 1500 : 1000;
|
||||
const objectRemaining = this.health;
|
||||
const characterMax = Level.maxHealthForLevel(character.level.value);
|
||||
const characterHeadroom = characterMax - character.health.value;
|
||||
const actualHeal = Math.min(amount, objectRemaining, characterHeadroom);
|
||||
// If actualHeal is 0, nothing changes
|
||||
@ -66,7 +52,7 @@ export class HealingObject {
|
||||
return { object: this, character };
|
||||
}
|
||||
// Create updated object
|
||||
const newObjectHealth = this.#health - actualHeal;
|
||||
const newObjectHealth = this.health - actualHeal;
|
||||
const newObjectStatus =
|
||||
newObjectHealth === 0 ? { kind: 'destroyed' as const } : { kind: 'alive' as const };
|
||||
// Create updated character
|
||||
@ -77,7 +63,7 @@ export class HealingObject {
|
||||
health: newCharacterHealth,
|
||||
});
|
||||
return {
|
||||
object: new HealingObject(newObjectHealth, this.#maxHealth, newObjectStatus),
|
||||
object: new HealingObject(newObjectHealth, this.maxHealth, newObjectStatus),
|
||||
character: newCharacter,
|
||||
};
|
||||
}
|
||||
|
||||
45
src/MagicalObject.ts
Normal file
45
src/MagicalObject.ts
Normal file
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* MagicalObject — shared base for all magical items in the game.
|
||||
*
|
||||
* Invariants enforced at construction:
|
||||
* - Health is non-negative
|
||||
* - Health never exceeds maxHealth
|
||||
* - Status derived from health (0 = destroyed, > 0 = alive)
|
||||
*/
|
||||
|
||||
export type MagicalObjectStatus = { readonly kind: 'alive' } | { readonly kind: 'destroyed' };
|
||||
|
||||
export class MagicalObject {
|
||||
readonly #health: number;
|
||||
readonly #maxHealth: number;
|
||||
readonly #status: MagicalObjectStatus;
|
||||
|
||||
protected constructor(health: number, maxHealth: number, status: MagicalObjectStatus) {
|
||||
this.#health = health;
|
||||
this.#maxHealth = maxHealth;
|
||||
this.#status = status;
|
||||
}
|
||||
|
||||
get health(): number {
|
||||
return this.#health;
|
||||
}
|
||||
|
||||
get maxHealth(): number {
|
||||
return this.#maxHealth;
|
||||
}
|
||||
|
||||
get status(): MagicalObjectStatus {
|
||||
return this.#status;
|
||||
}
|
||||
|
||||
/** Create a destroyed object (health = 0). */
|
||||
static createDestroyed(maxHealth: number): MagicalObject {
|
||||
if (maxHealth < 0) throw new Error('MaxHealth cannot be negative');
|
||||
return new MagicalObject(0, maxHealth, { kind: 'destroyed' });
|
||||
}
|
||||
|
||||
/** Check if this object is alive. */
|
||||
isAlive(): boolean {
|
||||
return this.#status.kind === 'alive';
|
||||
}
|
||||
}
|
||||
@ -1,32 +1,25 @@
|
||||
/**
|
||||
* Magical Weapon — a Magical Object that deals fixed damage.
|
||||
*
|
||||
* Inherits health/status management from MagicalObject.
|
||||
* Invariants enforced at construction:
|
||||
* - Health is non-negative
|
||||
* - Health never exceeds maxHealth
|
||||
* - Damage is non-negative
|
||||
*/
|
||||
import { Character } from './Character.ts';
|
||||
import { MagicalObject } from './MagicalObject.ts';
|
||||
|
||||
export type WeaponStatus = { kind: 'alive' } | { kind: 'destroyed' };
|
||||
|
||||
export class MagicalWeapon {
|
||||
readonly #health: number;
|
||||
readonly #maxHealth: number;
|
||||
readonly #status: WeaponStatus;
|
||||
export class MagicalWeapon extends MagicalObject {
|
||||
readonly #damage: number;
|
||||
readonly #owner: Character;
|
||||
|
||||
private constructor(
|
||||
health: number,
|
||||
maxHealth: number,
|
||||
status: WeaponStatus,
|
||||
status: { readonly kind: 'alive' } | { readonly kind: 'destroyed' },
|
||||
damage: number,
|
||||
owner: Character,
|
||||
) {
|
||||
this.#health = health;
|
||||
this.#maxHealth = maxHealth;
|
||||
this.#status = status;
|
||||
super(health, maxHealth, status);
|
||||
this.#damage = damage;
|
||||
this.#owner = owner;
|
||||
}
|
||||
@ -45,18 +38,6 @@ export class MagicalWeapon {
|
||||
return new MagicalWeapon(maxHealth, maxHealth, { kind: 'alive' }, damage, owner);
|
||||
}
|
||||
|
||||
get health(): number {
|
||||
return this.#health;
|
||||
}
|
||||
|
||||
get maxHealth(): number {
|
||||
return this.#maxHealth;
|
||||
}
|
||||
|
||||
get status(): WeaponStatus {
|
||||
return this.#status;
|
||||
}
|
||||
|
||||
get damage(): number {
|
||||
return this.#damage;
|
||||
}
|
||||
@ -68,7 +49,7 @@ export class MagicalWeapon {
|
||||
/** Use this weapon to deal damage. Returns updated weapon and target. */
|
||||
use(target: Character): { weapon: MagicalWeapon; target: Character } {
|
||||
// Destroyed weapons can't be used
|
||||
if (this.#status.kind === 'destroyed') {
|
||||
if (this.status.kind === 'destroyed') {
|
||||
return { weapon: this, target };
|
||||
}
|
||||
// Deal fixed damage
|
||||
@ -81,18 +62,17 @@ export class MagicalWeapon {
|
||||
status: newTargetStatus,
|
||||
});
|
||||
// Reduce weapon health by 1
|
||||
const newWeaponHealth = this.#health - 1;
|
||||
const newWeaponHealth = this.health - 1;
|
||||
const newWeaponStatus =
|
||||
newWeaponHealth === 0 ? { kind: 'destroyed' as const } : { kind: 'alive' as const };
|
||||
return {
|
||||
weapon: new MagicalWeapon(
|
||||
newWeaponHealth,
|
||||
this.#maxHealth,
|
||||
this.maxHealth,
|
||||
newWeaponStatus,
|
||||
this.#damage,
|
||||
this.#owner,
|
||||
),
|
||||
|
||||
target: newTarget,
|
||||
};
|
||||
}
|
||||
|
||||
@ -7,11 +7,3 @@ export type Status = { readonly kind: 'alive' } | { readonly kind: 'dead' };
|
||||
|
||||
export const StatusAlive: Status = { kind: 'alive' };
|
||||
export const StatusDead: Status = { kind: 'dead' };
|
||||
|
||||
export function isAlive(s: Status): s is { kind: 'alive' } {
|
||||
return s.kind === 'alive';
|
||||
}
|
||||
|
||||
export function isDead(s: Status): s is { kind: 'dead' } {
|
||||
return s.kind === 'dead';
|
||||
}
|
||||
|
||||
13112
transcripts/found-out-story-3-is-not-done.html
Normal file
13112
transcripts/found-out-story-3-is-not-done.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
13112
transcripts/story-4-built.html
Normal file
13112
transcripts/story-4-built.html
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user