1 import { Fight } from "shared/monsters";
2 import { Dungeon, DungeonRoom, DungeonPlayer, DungeonState, DungeonStateSummaryVists, DungeonStateSummaryFights } from "../shared/dungeon";
3 import { db } from './lib/db';
4 import { Request, Response } from 'express';
5 import { ErrorAlert } from "./views/alert";
6 import { addEvent } from './events';
8 export async function blockPlayerInDungeon(req: Request, res: Response, next: any) {
9 const state = await getActiveDungeon(req.player.id);
14 res.send(ErrorAlert('You are currently exploring a dungeon'));
18 export async function getActiveDungeon(player_id: string): Promise<DungeonPlayer> {
19 return db.select('*').from<DungeonPlayer>('dungeon_players').where({
24 export async function loadDungeon(dungeon_id: string): Promise<Dungeon> {
25 return db.select('*').from<Dungeon>('dungeons').where({id: dungeon_id}).first();
28 export async function getDungeonState(player_id: string, dungeon_id: string) {
29 return db.select('*').from<DungeonState>('dungeon_state').where({
35 export async function addNewState(player_id: string, dungeon_id: string, event_name: string, props: Record<string, any> = {}) {
36 return db('dungeon_state').insert({
43 // in a dungeon fight, we dont care what room the player is in,
44 // but we care what room they are going to
45 export async function completeDungeonFight(player_id: string, monster: Fight): Promise<DungeonPlayer> {
46 const activeDungeon = await getActiveDungeon(player_id);
48 await addNewState(player_id, activeDungeon.dungeon_id, 'FIGHT_COMPLETE', {
49 monster_id: monster.ref_id,
50 target_room: activeDungeon.target_room_id,
51 source_room: activeDungeon.current_room_id
54 await movePlayerToRoomInDungeon(player_id, activeDungeon.dungeon_id, activeDungeon.target_room_id);
56 return getActiveDungeon(player_id);
59 export async function movePlayerToRoomInDungeon(player_id: string, dungeon_id: string, room_id: string) {
60 await updatePlayerDungeonState(player_id, dungeon_id, {
61 current_room_id: room_id,
62 target_room_id: room_id
65 await addNewState(player_id, dungeon_id, 'ROOM_VISIT', {
70 export async function getRoomVists(player_id: string, dungeon_id: string): Promise<DungeonStateSummaryVists> {
72 const res = await db.raw(`select
74 event_props->>'room_id' as room_id
76 group by event_props->>'room_id',player_id,dungeon_id,event_name
77 having player_id = ? and dungeon_id = ? and event_name = ?
85 res.rows.forEach((row: {room_id: string, visits: string}) => {
86 data[row.room_id] = parseInt(row.visits);
92 export async function getUniqueFights(player_id: string, dungeon_id: string): Promise<DungeonStateSummaryFights> {
93 // fights always happen between movement source->target. So when we record
94 // the state of a fight, we record the source/target room props.
95 // however, when we are checking if a fight occurrent in a room.. we really
96 // only care about the TARGET room.
97 const res = await db.raw(`select
99 event_props->>'monster_id' as monster_id,
100 event_props->>'target_room' as room_id
102 group by event_props->>'monster_id',event_props->>'target_room',
103 player_id,dungeon_id,event_name
104 having player_id = ? and dungeon_id = ? and event_name = ?
112 res.rows.forEach((row: {fights: string, monster_id: string, room_id: string}) => {
113 if(!data[row.room_id]) {
114 data[row.room_id] = {};
117 if(!data[row.room_id][row.monster_id]) {
118 data[row.room_id][row.monster_id] = row.fights;
125 export async function putPlayerInDungeon(player_id: string, dungeon_id: string): Promise<DungeonPlayer> {
126 const res: {rows: DungeonPlayer[] }= await db.raw(`
127 insert into dungeon_players
128 (player_id, dungeon_id, current_room_id)
130 (?, ?, (select starting_room from dungeons where id = ?)) returning *`, [player_id, dungeon_id, dungeon_id])
132 const data = res.rows.pop();
134 await addNewState(player_id, dungeon_id, 'ROOM_VISIT', {
135 room_id: data.current_room_id
142 export async function updatePlayerDungeonState(player_id: string, dungeon_id: string, fields: any) {
143 const res = await db('dungeon_players').where({
146 }).update(fields).returning('*');
151 export async function loadRoom(room_id: string): Promise<DungeonRoom | undefined> {
152 return db.select('*')
153 .from<DungeonRoom>('dungeon_rooms')
154 .where({id: room_id})
158 export async function completeDungeon(player_id: string) {
159 const dungeonPlayer = await getActiveDungeon(player_id);
160 const dungeonState = await getDungeonState(player_id, dungeonPlayer.dungeon_id);
162 await db('dungeon_players').where({player_id}).delete();
163 await db('dungeon_state').where({player_id, dungeon_id: dungeonPlayer.dungeon_id}).delete();
165 let startTime = Number.MAX_VALUE;
167 dungeonState.forEach(s => {
168 startTime = Math.min(startTime, s.create_date);
169 endTime = Math.max(endTime, s.create_date);
172 addEvent('DUNGEON_COMPLETE', player_id, {
173 dungeon_id: dungeonPlayer.dungeon_id,
176 duration: endTime - startTime