import { DataSource } from 'typeorm'; import { DEMO_CHARACTER_ID } from '../../demo/demo-character.constants'; import { Character } from '../../characters/entities/character.entity'; import { EncounterType } from '../../monsters/entities/encounter-type.enum'; import { LocationMonster } from '../../monsters/entities/location-monster.entity'; import { MonsterDefinition } from '../../monsters/entities/monster-definition.entity'; import { LocationConnection } from '../../world/entities/location-connection.entity'; import { LocationDefinition } from '../../world/entities/location-definition.entity'; import { BURNED_ROAD_LOCAL_CONTENT, SOUTH_GATE_LOCAL_CONTENT, } from './local-location.content'; import { ASH_RAT_MONSTER_ID, BURNED_ROAD_ID, CHARRED_LOOTER_MONSTER_ID, ROAD_BANDIT_MONSTER_ID, SOUTH_GATE_ID, WILD_ROAD_DOG_MONSTER_ID, } from './vertical-slice.constants'; export async function seedVisibleVerticalSlice( dataSource: DataSource, ): Promise { const locationRepository = dataSource.getRepository(LocationDefinition); const connectionRepository = dataSource.getRepository(LocationConnection); const characterRepository = dataSource.getRepository(Character); const monsterRepository = dataSource.getRepository(MonsterDefinition); const locationMonsterRepository = dataSource.getRepository(LocationMonster); const locations = [ { id: SOUTH_GATE_ID, key: 'south-gate', name: 'Südtor von Graufurt', description: 'Am schwarzen Südtor endet der Schutz Graufurts. Hinter den Wachtfeuern beginnt die stille Weite der Aschenfelder.', regionKey: 'ashen-fields', minRecommendedLevel: 1, maxRecommendedLevel: 1, dangerLevel: 0, isSafe: true, huntingEnabled: false, artworkPath: '/images/backgrounds/Suedtor.png', ...SOUTH_GATE_LOCAL_CONTENT, }, { id: BURNED_ROAD_ID, key: 'burned-road', name: 'Verbrannte Straße', description: 'Die alte Handelsstraße führt durch verkohlte Felder. Zwischen Asche und zerbrochenen Wagen warten die ersten Gefahren.', regionKey: 'ashen-fields', minRecommendedLevel: 1, maxRecommendedLevel: 2, dangerLevel: 1, isSafe: false, huntingEnabled: true, artworkPath: '/images/backgrounds/Aschestrasse.png', ...BURNED_ROAD_LOCAL_CONTENT, }, ]; const locationIds = new Map(); for (const location of locations) { const existing = await locationRepository.findOneBy({ key: location.key }); const { id, key, ...definition } = location; if (existing) { await locationRepository.update(existing.id, definition); } else { await locationRepository.insert(location); } locationIds.set(key, existing?.id ?? id); } const southGateId = locationIds.get('south-gate') ?? SOUTH_GATE_ID; const burnedRoadId = locationIds.get('burned-road') ?? BURNED_ROAD_ID; await connectionRepository.upsert( [ { fromLocationId: southGateId, toLocationId: burnedRoadId, travelDurationSeconds: 10, ambushChance: '0.0500', enabled: true, }, { fromLocationId: burnedRoadId, toLocationId: southGateId, travelDurationSeconds: 10, ambushChance: '0.0500', enabled: true, }, ], ['fromLocationId', 'toLocationId'], ); const monsters = [ { id: ASH_RAT_MONSTER_ID, key: 'ash-rat', name: 'Aschenratte', level: 1, maxHp: 45, attack: 5, armor: 0, experienceReward: 8, silverMin: 4, silverMax: 7, artworkPath: '/images/monsters/ash-rat.png', iconPath: '/images/monsters/icons/ash-rat-128.png', }, { id: WILD_ROAD_DOG_MONSTER_ID, key: 'wild-road-dog', name: 'Verwilderter Straßenhund', level: 1, maxHp: 55, attack: 7, armor: 0, experienceReward: 11, silverMin: 5, silverMax: 9, artworkPath: '/images/monsters/wild-road-dog.png', iconPath: '/images/monsters/icons/wild-road-dog-128.png', }, { id: ROAD_BANDIT_MONSTER_ID, key: 'road-bandit', name: 'Straßenräuber', level: 2, maxHp: 75, attack: 9, armor: 5, experienceReward: 16, silverMin: 9, silverMax: 15, artworkPath: '/images/monsters/road-bandit.png', iconPath: '/images/monsters/icons/road-bandit-128.png', }, { id: CHARRED_LOOTER_MONSTER_ID, key: 'charred-looter', name: 'Verkohlter Plünderer', level: 2, maxHp: 85, attack: 11, armor: 6, experienceReward: 20, silverMin: 12, silverMax: 19, artworkPath: '/images/monsters/charred-looter.png', iconPath: '/images/monsters/icons/charred-looter-128.png', }, ]; const monsterIds = new Map(); for (const monster of monsters) { const existingMonster = await monsterRepository.findOneBy({ key: monster.key, }); const { id, key, ...definition } = monster; if (existingMonster) { await monsterRepository.update(existingMonster.id, definition); } else { await monsterRepository.insert(monster); } monsterIds.set(key, existingMonster?.id ?? id); } // Weights read as "how often you meet this on the road". They also drive the // location's danger rating, which is computed from the weighted average of // the pool rather than from its single worst entry. const encounterWeights: Readonly> = { 'ash-rat': 40, 'wild-road-dog': 30, 'road-bandit': 20, 'charred-looter': 10, }; await locationMonsterRepository.upsert( Object.entries(encounterWeights).map(([key, weight]) => ({ locationId: burnedRoadId, monsterId: monsterIds.get(key) as string, weight, encounterType: EncounterType.NORMAL, enabled: true, })), ['locationId', 'monsterId'], ); const existing = await characterRepository.findOneBy({ id: DEMO_CHARACTER_ID, }); if (!existing) { await characterRepository.insert({ id: DEMO_CHARACTER_ID, name: 'Aric Duskwalker', level: 1, experience: 0, baseHp: 100, baseAttack: 6, currentHp: 100, currentLocationId: southGateId, }); } }