import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { Character } from '../characters/entities/character.entity'; import { LocationMonster } from '../monsters/entities/location-monster.entity'; import { TravelService } from '../travel/travel.service'; import { LocationConnection } from './entities/location-connection.entity'; export interface LocationSummary { id: string; key: string; name: string; } export interface CurrentLocationConnection { targetLocation: LocationSummary; travelDurationSeconds: number; danger: 'LOW' | 'HIGH'; } export interface CurrentLocationResponse { id: string; key: string; name: string; description: string; regionKey: string; minRecommendedLevel: number; maxRecommendedLevel: number; dangerLevel: number; isSafe: boolean; huntingEnabled: boolean; artworkPath: string; connections: CurrentLocationConnection[]; possibleMonsters: string[]; } @Injectable() export class WorldService { constructor( private readonly travelService: TravelService, @InjectRepository(Character) private readonly characters: Repository, @InjectRepository(LocationConnection) private readonly connections: Repository, @InjectRepository(LocationMonster) private readonly locationMonsters: Repository, ) {} async getCurrentLocation( characterId: string, ): Promise { await this.travelService.completeTravelIfDue(characterId); const character = await this.characters.findOne({ where: { id: characterId }, relations: { currentLocation: true }, }); if (!character) { throw new NotFoundException('Character not found.'); } const connections = await this.connections.find({ where: { fromLocationId: character.currentLocationId, enabled: true }, relations: { toLocation: true }, }); const location = character.currentLocation; const possibleMonsters = location.huntingEnabled ? await this.getPossibleMonsters(location.id) : []; return { id: location.id, key: location.key, name: location.name, description: location.description, regionKey: location.regionKey, minRecommendedLevel: location.minRecommendedLevel, maxRecommendedLevel: location.maxRecommendedLevel, dangerLevel: location.dangerLevel, isSafe: location.isSafe, huntingEnabled: location.huntingEnabled, artworkPath: location.artworkPath, connections: connections .filter((connection) => connection.enabled) .map((connection) => ({ targetLocation: { id: connection.toLocation.id, key: connection.toLocation.key, name: connection.toLocation.name, }, travelDurationSeconds: connection.travelDurationSeconds, danger: this.toDangerRating(connection.ambushChance), })), possibleMonsters, }; } private async getPossibleMonsters(locationId: string): Promise { const pool = await this.locationMonsters.find({ where: { locationId, enabled: true }, relations: { monster: true }, order: { weight: 'DESC' }, }); return pool.map((entry) => entry.monster.name); } private toDangerRating(ambushChance: string): 'LOW' | 'HIGH' { return Number(ambushChance) <= 0.05 ? 'LOW' : 'HIGH'; } }