106 lines
3.1 KiB
TypeScript
106 lines
3.1 KiB
TypeScript
import {
|
|
Column,
|
|
CreateDateColumn,
|
|
Entity,
|
|
Index,
|
|
JoinColumn,
|
|
ManyToOne,
|
|
PrimaryGeneratedColumn,
|
|
UpdateDateColumn,
|
|
} from 'typeorm';
|
|
import { Character } from '../../characters/entities/character.entity';
|
|
import { HuntEncounter } from '../../hunting/entities/hunt-encounter.entity';
|
|
import { MonsterDefinition } from '../../monsters/entities/monster-definition.entity';
|
|
import { ActiveStatusEffect, CombatIntent } from '../combat-engine.types';
|
|
import { CombatStatus } from '../combat-status.enum';
|
|
import type { MonsterAbilities } from '../../monsters/monster-abilities';
|
|
|
|
export interface CombatCombatantState {
|
|
attack: number;
|
|
armor: number;
|
|
pendingAction?: CombatIntent;
|
|
statusEffects?: ActiveStatusEffect[];
|
|
}
|
|
|
|
export interface CombatMonsterState extends CombatCombatantState {
|
|
// Snapshotted from MonsterDefinition when the fight starts, so retuning
|
|
// content mid-fight cannot change the rules of a running combat.
|
|
abilities: MonsterAbilities;
|
|
// Snapshotted monster combat state persisted during the fight.
|
|
activeGuard?: { remainingRounds: number; armorBonus: number };
|
|
enraged?: boolean;
|
|
}
|
|
|
|
export interface CombatPlayerState extends CombatCombatantState {
|
|
weaponDamage: number;
|
|
potionsRemaining: number;
|
|
}
|
|
|
|
@Entity({ name: 'combats' })
|
|
// Deliberately not unique: a lost fight frees the encounter to be retried,
|
|
// which creates a second combat row for the same encounter.
|
|
@Index('IDX_combats_hunt_encounter', ['huntEncounterId'])
|
|
export class Combat {
|
|
@PrimaryGeneratedColumn('uuid', { name: 'id' })
|
|
id!: string;
|
|
|
|
@Column({ name: 'character_id', type: 'uuid' })
|
|
characterId!: string;
|
|
|
|
@Column({ name: 'hunt_encounter_id', type: 'uuid' })
|
|
huntEncounterId!: string;
|
|
|
|
@Column({ name: 'monster_definition_id', type: 'uuid' })
|
|
monsterDefinitionId!: string;
|
|
|
|
@Column({
|
|
name: 'status',
|
|
type: 'enum',
|
|
enum: CombatStatus,
|
|
enumName: 'combat_status_enum',
|
|
})
|
|
status!: CombatStatus;
|
|
|
|
@Column({ name: 'round', type: 'integer' })
|
|
round!: number;
|
|
|
|
@Column({ name: 'player_max_hp', type: 'integer' })
|
|
playerMaxHp!: number;
|
|
|
|
@Column({ name: 'player_current_hp', type: 'integer' })
|
|
playerCurrentHp!: number;
|
|
|
|
@Column({ name: 'monster_max_hp', type: 'integer' })
|
|
monsterMaxHp!: number;
|
|
|
|
@Column({ name: 'monster_current_hp', type: 'integer' })
|
|
monsterCurrentHp!: number;
|
|
|
|
@Column({ name: 'player_state', type: 'jsonb' })
|
|
playerState!: CombatPlayerState;
|
|
|
|
@Column({ name: 'monster_state', type: 'jsonb' })
|
|
monsterState!: CombatMonsterState;
|
|
|
|
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
|
createdAt!: Date;
|
|
|
|
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
|
|
updatedAt!: Date;
|
|
|
|
@Column({ name: 'completed_at', type: 'timestamptz', nullable: true })
|
|
completedAt!: Date | null;
|
|
|
|
@ManyToOne(() => Character, { onDelete: 'RESTRICT' })
|
|
@JoinColumn({ name: 'character_id' })
|
|
character!: Character;
|
|
|
|
@ManyToOne(() => HuntEncounter, { onDelete: 'RESTRICT' })
|
|
@JoinColumn({ name: 'hunt_encounter_id' })
|
|
huntEncounter!: HuntEncounter;
|
|
|
|
@ManyToOne(() => MonsterDefinition, { onDelete: 'RESTRICT' })
|
|
@JoinColumn({ name: 'monster_definition_id' })
|
|
monster!: MonsterDefinition;
|
|
}
|