1 import { db } from './lib/db';
2 import { Fight, Monster, MonsterWithFaction, MonsterForList, FightTrigger, MonsterVariant, MonsterVariants } from '../shared/monsters';
3 import { TimePeriod, TimeManager } from '../shared/time';
4 import { LocationWithCity } from '../shared/map';
5 import { random, sample } from 'lodash';
6 import { CHANCE_TO_FIGHT_SPECIAL } from '../shared/constants';
8 const time = new TimeManager();
11 * return a list of monsters that
12 * - are at the current location
13 * - in the current time period
14 * - in any time period
16 export async function getMonsterList(location_id: number, timePeriod: TimePeriod[] = []): Promise<Monster[]> {
17 if(timePeriod.length === 0) {
18 timePeriod.push('any');
19 timePeriod.push(time.getTimePeriod());
22 const res: Monster[] = await db.select('*')
23 .where({ location_id })
24 .whereIn('time_period', timePeriod)
25 .from<Monster>('monsters')
31 export async function loadMonster(id: number): Promise<Monster> {
32 return db.select('*').from<Monster>('monsters').where({
37 export async function loadMonsterFromFight(player_id: string): Promise<Fight> {
38 return await db.first().select('*').from<Fight>('fight').where({
43 export async function loadMonsterWithFaction(player_id: string): Promise<MonsterWithFaction> {
44 const res = await db.raw(`
46 f.*, fa.id as faction_id, fa.name as faction_name,
47 m.minLevel, m.maxLevel
49 join monsters m on f.ref_id = m.id
50 left outer join factions fa on m.faction_id = fa.id
58 export async function saveFightState(authToken: string, monster: Fight) {
59 return db('fight').where({
67 export async function createFight(playerId: string, monster: Monster, fightTrigger: FightTrigger): Promise<Fight> {
68 const chosenLevel = random(monster.minLevel, monster.maxLevel);
69 // 30% boost per level difference
70 const modifier = Math.pow(Math.E, (chosenLevel - monster.minLevel)/monster.maxLevel);
71 let variant: MonsterVariant = {
84 if(monster.maxLevel >= 5 && random(0,100) <= CHANCE_TO_FIGHT_SPECIAL) {
85 variant = sample(MonsterVariants);
88 const monsterData: Omit<Fight, 'id'> = {
90 variant: variant.name,
91 name: variant.display.replace("{{name}}", monster.name),
92 strength: Math.floor(monster.strength * modifier * variant.strength),
93 constitution: Math.floor(monster.constitution * modifier * variant.constitution),
94 dexterity: Math.floor(monster.dexterity * modifier * variant.dexterity),
95 intelligence: Math.floor(monster.intelligence * modifier * variant.intelligence),
96 exp: Math.floor(monster.exp * modifier * variant.exp),
98 gold: Math.floor(monster.gold * modifier * variant.exp),
99 hp: Math.floor(monster.hp * modifier * variant.maxHp),
100 defence: Math.floor(monster.defence * modifier * variant.defence),
101 maxHp: Math.floor(monster.maxHp * modifier * variant.maxHp),
103 fight_trigger: fightTrigger
106 const res = await db('fight').insert(monsterData)
107 .returning<Fight[]>('*');
112 export async function getMonsterLocation(monsterId: number): Promise<LocationWithCity> {
113 return db.select(['locations.*', 'cities.name as city_name'])
114 .from<Monster>('monsters')
115 .join('locations', 'monsters.location_id', '=', 'locations.id')
116 .join('cities', 'cities.id', '=', 'locations.city_id')
118 'monsters.id': monsterId
123 * Given a list of cities, it will return a monster that
124 * exists in any of the exploration zones with every monster
125 * having an equal probability of appearing
127 export async function getRandomMonster(city_id: number[]): Promise<MonsterForList> {
128 const res = await db.raw('select id,name,level from monsters where location_id in (select id from locations where city_id in (?)) order by random() limit 1', [city_id.join(',')])
130 return res.rows[0] as MonsterForList;
133 export async function clearFight(authToken: string) {
134 return db('fight').where({