This commit is contained in:
Bastian Wagner
2026-08-22 16:41:47 +02:00
parent dfa62fd152
commit 081c9f83f9
137 changed files with 11594 additions and 1302 deletions

View File

@@ -9,6 +9,8 @@ import {
UpdateDateColumn,
} from 'typeorm';
import { LootTable } from '../../loot/entities/loot-table.entity';
import { MonsterCategory } from '../monster-category.enum';
import type { MonsterAbilities } from '../monster-abilities';
@Entity({ name: 'monster_definitions' })
@Index('IDX_monster_definitions_key', ['key'], { unique: true })
@@ -22,6 +24,16 @@ export class MonsterDefinition {
@Column({ name: 'name', type: 'varchar', length: 150 })
name!: string;
// Broad gameplay kind (Playable Slice 0.7.5 §5). Classification for later
// systems to key off; nothing branches on it yet.
@Column({
name: 'monster_category',
type: 'enum',
enum: MonsterCategory,
enumName: 'monster_category_enum',
})
monsterCategory!: MonsterCategory;
@Column({ name: 'level', type: 'integer' })
level!: number;
@@ -34,11 +46,15 @@ export class MonsterDefinition {
@Column({ name: 'armor', type: 'integer' })
armor!: number;
@Column({ name: 'silver_min', type: 'integer' })
silverMin!: number;
// Short line of atmosphere shown on the hunt encounter card (spec §9).
// Nullable: a monster without one simply shows no flavor line.
@Column({ name: 'flavor_text', type: 'text', nullable: true })
flavorText!: string | null;
@Column({ name: 'silver_max', type: 'integer' })
silverMax!: number;
// Combat mechanics as content, not code (spec §4, §8). `{}` is a monster
// with no special mechanic.
@Column({ name: 'abilities', type: 'jsonb', default: () => "'{}'::jsonb" })
abilities!: MonsterAbilities;
@Column({ name: 'artwork_path', type: 'varchar', length: 255 })
artworkPath!: string;

View File

@@ -0,0 +1,34 @@
/**
* Per-monster combat mechanics, authored as content on `MonsterDefinition`
* (Playable Slice 0.7 V2 spec §4, §8).
*
* The combat engine reads this configuration instead of branching on a
* monster key, so a new enemy is a seed row rather than an engine change.
* An empty object is a monster with no special mechanic at all -- the Ash
* Rat's "pure baseline combat" (spec §4).
*/
export interface MonsterTelegraphAbility {
/**
* The monster announces a Heavy Attack on every round divisible by this
* and resolves it the round after, unless SHIELD_BASH interrupts it.
* A fixed cadence rather than RNG keeps combat deterministic (AGENTS §10).
*/
roundInterval: number;
damageMultiplier: number;
}
export interface MonsterBleedAbility {
/** Applies Bleeding alongside its normal attack on rounds divisible by this. */
roundInterval: number;
/** Damage dealt at the end of every round the effect is still active. */
damagePerRound: number;
/** How many rounds the effect ticks for, counting the round it lands. */
durationRounds: number;
}
export interface MonsterAbilities {
telegraph?: MonsterTelegraphAbility;
bleed?: MonsterBleedAbility;
}
export const NO_MONSTER_ABILITIES: MonsterAbilities = {};

View File

@@ -0,0 +1,13 @@
/**
* A monster's broad gameplay kind (Playable Slice 0.7.5 §5).
*
* Content classification only: nothing in this slice branches on it. It exists
* so later systems -- damage types, faction reputation, quest objectives
* ("slay 10 beasts") -- have a reusable handle that is not a monster key.
*
* Not the same axis as LootCategory. See that enum's note.
*/
export enum MonsterCategory {
BEAST = 'BEAST',
HUMANOID = 'HUMANOID',
}