chore(release): 0.4.0
[risinglegends.git] / src / server / map.ts
1 import { City, Location, LocationWithCity, Path } from "../shared/map";
2 import type { Player } from '../shared/player';
3 import type { Travel, TravelWithNames } from '../shared/travel';
4 import { db } from './lib/db';
5 import { random } from 'lodash';
6
7 export async function getAllServices(city_id: number, min_level: number): Promise<Location[]> {
8   return db.select('*')
9             .from<Location>('locations')
10             .where({city_id})
11             .andWhere('min_level', '<=', min_level)
12             .orderBy('type')
13             .orderBy('display_order');
14 }
15
16 export async function getService(location_id: number): Promise<LocationWithCity> {
17   return db.select(['locations.*', 'cities.name as city_name']).
18           from<Location>('locations').join('cities', 'locations.city_id', '=', 'cities.id').where({
19     'locations.id': location_id
20   }).first();
21 }
22
23 export async function getDungeon(dungeon_id: string): Promise<LocationWithCity> {
24   return db.select(['locations.*', 'cities.name as city_name']).
25           from<Location>('locations').join('cities', 'locations.city_id', '=', 'cities.id').where({
26     'locations.event_name': dungeon_id
27   }).first();
28 }
29
30 export async function getAllPaths(city_id: number): Promise<Path[]> {
31   const res = await db.raw(`
32                 select 
33                   paths.*, c1.name as starting_city_name, c2.name as ending_city_name
34                 from paths
35                 join cities c1 on c1.id = paths.starting_city 
36                 join cities c2 on c2.id = paths.ending_city
37                 where paths.starting_city = ?
38                 `, [city_id]);
39
40   return res.rows.map(row => {
41     return {
42       starting_city: row.starting_city,
43       ending_city: row.ending_city,
44       starting_city_name: row.starting_city_name,
45       ending_city_name: row.ending_city_name
46     }
47   });
48 }
49
50 export async function getCityDetails(city_id: number): Promise<City> {
51   return db.first().select('*').from<City>('cities').where({id: city_id});
52 }
53
54 export async function travel(player: Player, dest_id: number): Promise<TravelWithNames> {
55   const city = await getCityDetails(dest_id);
56   const path = await db.first().select('*').from('paths').where({
57     starting_city: player.city_id,
58     ending_city: dest_id
59   });
60   if(!city) {
61     throw new Error('Invalid destination city');
62   }
63   if(!path) {
64     throw new Error(`Invalid path ${player.city_id} => ${dest_id}`);
65   }
66
67   const deviation = Math.floor(path.distance * (random(3,10)/100));
68   const steps = random(path.distance - deviation, path.distance + deviation);
69
70   const rows = await db('travel').insert({
71     player_id: player.id,
72     source_id: player.city_id,
73     destination_id: dest_id,
74     total_distance: steps
75   });
76   
77   return getTravelPlan(player.id);
78 }
79
80 export async function stepForward(player_id: string): Promise<TravelWithNames> {
81   await db('travel').increment('current_position');
82
83   return getTravelPlan(player_id);
84 }
85
86 export async function clearTravelPlan(player_id: string): Promise<Travel> {
87   return db('travel').where({player_id}).delete();
88 }
89
90 export async function completeTravel(player_id: string): Promise<Travel> {
91   const rows = await db('travel').where({player_id}).delete().returning('*');
92   if(rows.length !== 1) {
93     throw new Error('Unexpected response when moving');
94   }
95
96   return rows[0] as Travel;
97 }
98
99 export async function getTravelPlan(player_id: string): Promise<TravelWithNames> {
100   return db.select([
101     'travel.*',
102     'source.name as source_city_name',
103     'destination.name as destination_city_name'
104   ]).from<Travel>('travel')
105     .join('cities as source', 'travel.source_id', '=', 'source.id')
106     .join('cities as destination', 'travel.destination_id', '=', 'destination.id')
107     .where({
108       'travel.player_id': player_id
109   }).first();
110 }