diff --git a/apps/api/src/database/seeds/vertical-slice.constants.ts b/apps/api/src/database/seeds/vertical-slice.constants.ts index 9587882..b7b8248 100644 --- a/apps/api/src/database/seeds/vertical-slice.constants.ts +++ b/apps/api/src/database/seeds/vertical-slice.constants.ts @@ -1,2 +1,4 @@ export const SOUTH_GATE_ID = '20000000-0000-4000-8000-000000000001'; export const BURNED_ROAD_ID = '20000000-0000-4000-8000-000000000002'; +export const ASH_RAT_MONSTER_ID = '30000000-0000-4000-8000-000000000001'; +export const ROAD_BANDIT_MONSTER_ID = '30000000-0000-4000-8000-000000000002'; diff --git a/apps/api/src/database/seeds/vertical-slice.seed.spec.ts b/apps/api/src/database/seeds/vertical-slice.seed.spec.ts index d13e2bb..39e0c44 100644 --- a/apps/api/src/database/seeds/vertical-slice.seed.spec.ts +++ b/apps/api/src/database/seeds/vertical-slice.seed.spec.ts @@ -1,5 +1,7 @@ import { DataSource } from 'typeorm'; import { Character } from '../../characters/entities/character.entity'; +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 { seedVisibleVerticalSlice } from './vertical-slice.seed'; @@ -9,6 +11,8 @@ type Row = Record; const DEMO_CHARACTER_ID = '10000000-0000-4000-8000-000000000001'; const SOUTH_GATE_ID = '20000000-0000-4000-8000-000000000001'; const BURNED_ROAD_ID = '20000000-0000-4000-8000-000000000002'; +const ASH_RAT_MONSTER_ID = '30000000-0000-4000-8000-000000000001'; +const ROAD_BANDIT_MONSTER_ID = '30000000-0000-4000-8000-000000000002'; class InMemoryRepository { readonly rows: Row[] = []; @@ -61,6 +65,8 @@ function createDataSource( locationRepository: InMemoryRepository, connectionRepository: InMemoryRepository, characterRepository: InMemoryRepository, + monsterRepository: InMemoryRepository, + locationMonsterRepository: InMemoryRepository, ): DataSource { return { getRepository: jest.fn((entity: unknown) => { @@ -73,6 +79,12 @@ function createDataSource( if (entity === Character) { return characterRepository; } + if (entity === MonsterDefinition) { + return monsterRepository; + } + if (entity === LocationMonster) { + return locationMonsterRepository; + } throw new Error('Unexpected repository'); }), @@ -84,10 +96,14 @@ describe('seedVisibleVerticalSlice', () => { const locationRepository = new InMemoryRepository(); const connectionRepository = new InMemoryRepository(); const characterRepository = new InMemoryRepository(); + const monsterRepository = new InMemoryRepository(); + const locationMonsterRepository = new InMemoryRepository(); const dataSource = createDataSource( locationRepository, connectionRepository, characterRepository, + monsterRepository, + locationMonsterRepository, ); await seedVisibleVerticalSlice(dataSource); @@ -136,13 +152,63 @@ describe('seedVisibleVerticalSlice', () => { experience: 39, }), ); + + expect(monsterRepository.insert).toHaveBeenCalledTimes(2); + expect(monsterRepository.rows).toHaveLength(2); + expect(monsterRepository.rows).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + 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', + }), + expect.objectContaining({ + 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', + }), + ]), + ); + + expect(locationMonsterRepository.upsert).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ + locationId: BURNED_ROAD_ID, + monsterId: ASH_RAT_MONSTER_ID, + weight: 70, + }), + expect.objectContaining({ + locationId: BURNED_ROAD_ID, + monsterId: ROAD_BANDIT_MONSTER_ID, + weight: 30, + }), + ]), + ['locationId', 'monsterId'], + ); + expect(locationMonsterRepository.rows).toHaveLength(2); }); it('preserves existing location IDs and uses them for the directed connections', async () => { const locationRepository = new InMemoryRepository(); const connectionRepository = new InMemoryRepository(); const characterRepository = new InMemoryRepository(); - const persistedSouthGateId = '30000000-0000-4000-8000-000000000001'; + const monsterRepository = new InMemoryRepository(); + const locationMonsterRepository = new InMemoryRepository(); + const persistedSouthGateId = '40000000-0000-4000-8000-000000000001'; locationRepository.rows.push({ id: persistedSouthGateId, key: 'south-gate', @@ -152,6 +218,8 @@ describe('seedVisibleVerticalSlice', () => { locationRepository, connectionRepository, characterRepository, + monsterRepository, + locationMonsterRepository, ); await seedVisibleVerticalSlice(dataSource); diff --git a/apps/api/src/database/seeds/vertical-slice.seed.ts b/apps/api/src/database/seeds/vertical-slice.seed.ts index 3909a45..64afb79 100644 --- a/apps/api/src/database/seeds/vertical-slice.seed.ts +++ b/apps/api/src/database/seeds/vertical-slice.seed.ts @@ -1,9 +1,17 @@ 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_ID, SOUTH_GATE_ID } from './vertical-slice.constants'; +import { + ASH_RAT_MONSTER_ID, + BURNED_ROAD_ID, + ROAD_BANDIT_MONSTER_ID, + SOUTH_GATE_ID, +} from './vertical-slice.constants'; export async function seedVisibleVerticalSlice( dataSource: DataSource, @@ -11,6 +19,9 @@ export async function seedVisibleVerticalSlice( 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 = [ { @@ -85,6 +96,77 @@ export async function seedVisibleVerticalSlice( ['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', + }, + { + 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', + }, + ]; + let ashRatId = ASH_RAT_MONSTER_ID; + let roadBanditId = ROAD_BANDIT_MONSTER_ID; + + for (const monster of monsters) { + const existingMonster = await monsterRepository.findOneBy({ + key: monster.key, + }); + const { id, key, ...definition } = monster; + const persistedId = existingMonster?.id ?? id; + + if (existingMonster) { + await monsterRepository.update(existingMonster.id, definition); + } else { + await monsterRepository.insert(monster); + } + + if (key === 'ash-rat') { + ashRatId = persistedId; + } else { + roadBanditId = persistedId; + } + } + + await locationMonsterRepository.upsert( + [ + { + locationId: burnedRoadId, + monsterId: ashRatId, + weight: 70, + encounterType: EncounterType.NORMAL, + enabled: true, + }, + { + locationId: burnedRoadId, + monsterId: roadBanditId, + weight: 30, + encounterType: EncounterType.NORMAL, + enabled: true, + }, + ], + ['locationId', 'monsterId'], + ); + const existing = await characterRepository.findOneBy({ id: DEMO_CHARACTER_ID, }); diff --git a/apps/web/public/images/monsters/ash-rat.png b/apps/web/public/images/monsters/ash-rat.png new file mode 100644 index 0000000..3b1bd66 Binary files /dev/null and b/apps/web/public/images/monsters/ash-rat.png differ diff --git a/apps/web/public/images/monsters/road-bandit.png b/apps/web/public/images/monsters/road-bandit.png new file mode 100644 index 0000000..cf233e0 Binary files /dev/null and b/apps/web/public/images/monsters/road-bandit.png differ