From b5577b4abc9f6f3aabee6bdac9256faf268399c4 Mon Sep 17 00:00:00 2001 From: xangelo Date: Fri, 1 Sep 2023 14:47:24 -0400 Subject: [PATCH] feat: variable level monsters Monsters now define a min/max level. When you start a fight a monster is generated within that level range. We use that to define a modifier for all stats on the monster and adjust accordingly. The stats defined on the monster sheet in airtable reference the min-stats on the monster. --- migrations/20230901182406_monster-variance.ts | 20 ++++++++++++++ seeds/monsters.ts | 11 ++++---- src/server/fight.ts | 2 +- src/server/monster.ts | 27 +++++++++++-------- src/server/views/fight.ts | 4 +-- src/server/views/monster-selector.ts | 6 ++--- src/shared/monsters.ts | 14 +++++----- 7 files changed, 56 insertions(+), 28 deletions(-) create mode 100644 migrations/20230901182406_monster-variance.ts diff --git a/migrations/20230901182406_monster-variance.ts b/migrations/20230901182406_monster-variance.ts new file mode 100644 index 0000000..35e6db0 --- /dev/null +++ b/migrations/20230901182406_monster-variance.ts @@ -0,0 +1,20 @@ +import { Knex } from "knex"; + + +export async function up(knex: Knex): Promise { + return knex.schema.alterTable('monsters', function(table) { + table.dropColumn('level'); + table.integer('minLevel').notNullable().defaultTo(1); + table.integer('maxLevel').notNullable().defaultTo(1); + }); +} + + +export async function down(knex: Knex): Promise { + return knex.schema.alterTable('monsters', function(table) { + table.dropColumn('minLevel'); + table.dropColumn('maxLevel'); + table.integer('level').notNullable().defaultTo(1); + }); +} + diff --git a/seeds/monsters.ts b/seeds/monsters.ts index 44afa4f..02eecb8 100644 --- a/seeds/monsters.ts +++ b/seeds/monsters.ts @@ -50,12 +50,13 @@ export async function createMonsters(): Promise { return { id: r.fields.id, name: r.fields.Name, - strength: r.fields['STR_override'] || r.fields.STR, - constitution: r.fields['CON_override'] || r.fields.CON, - dexterity: r.fields['DEX_override'] || r.fields.DEX, - intelligence: r.fields['INT_override'] || r.fields.INT, + strength: r.fields.STR, + constitution: r.fields.CON, + dexterity: r.fields.DEX, + intelligence: r.fields.INT, exp: r.fields.EXP, - level: r.fields.Level, + minLevel: r.fields.minLevel, + maxLevel: r.fields.maxLevel ?? r.fields.minLevel, gold: r.fields.GOLD, hp: r.fields.HP, maxHp: r.fields.HP, diff --git a/src/server/fight.ts b/src/server/fight.ts index 2c9383d..6e262df 100644 --- a/src/server/fight.ts +++ b/src/server/fight.ts @@ -196,7 +196,7 @@ export async function fightRound(player: Player, monster: MonsterWithFaction, d return { id: monster.id, name: monster.name, - level: monster.level, + level: monster.minLevel, hp: monster.hp, maxHp: monster.maxHp, fight_trigger: 'explore' diff --git a/src/server/monster.ts b/src/server/monster.ts index 943a868..18c1243 100644 --- a/src/server/monster.ts +++ b/src/server/monster.ts @@ -2,6 +2,7 @@ import { db } from './lib/db'; import { Fight, Monster, MonsterWithFaction, MonsterForList, FightTrigger } from '../shared/monsters'; import { TimePeriod, TimeManager } from '../shared/time'; import { LocationWithCity } from 'shared/map'; +import { max, random } from 'lodash'; const time = new TimeManager(); @@ -21,7 +22,7 @@ export async function getMonsterList(location_id: number, timePeriod: TimePeriod .where({ location_id }) .whereIn('time_period', timePeriod) .from('monsters') - .orderBy('level'); + .orderBy('minLevel'); return res; } @@ -62,19 +63,23 @@ export async function saveFightState(authToken: string, monster: Fight) { } export async function createFight(playerId: string, monster: Monster, fightTrigger: FightTrigger): Promise { + const chosenLevel = random(monster.minLevel, monster.maxLevel); + // 30% boost per level difference + const modifier = Math.pow(Math.E, (chosenLevel - monster.minLevel)/monster.maxLevel); + const res = await db('fight').insert({ player_id: playerId, name: monster.name, - strength: monster.strength, - constitution: monster.constitution, - dexterity: monster.dexterity, - intelligence: monster.intelligence, - exp: monster.exp, - level: monster.level, - gold: monster.gold, - hp: monster.hp, - defence: monster.defence, - maxHp: monster.maxHp, + strength: Math.floor(monster.strength * modifier), + constitution: Math.floor(monster.constitution * modifier), + dexterity: Math.floor(monster.dexterity * modifier), + intelligence: Math.floor(monster.intelligence * modifier), + exp: Math.floor(monster.exp * modifier), + level: chosenLevel, + gold: Math.floor(monster.gold * modifier), + hp: Math.floor(monster.hp * modifier), + defence: Math.floor(monster.defence * modifier), + maxHp: Math.floor(monster.maxHp * modifier), ref_id: monster.id, fight_trigger: fightTrigger }).returning('*'); diff --git a/src/server/views/fight.ts b/src/server/views/fight.ts index 06ebcdb..f09ab41 100644 --- a/src/server/views/fight.ts +++ b/src/server/views/fight.ts @@ -79,7 +79,7 @@ export function renderFight(monster: MonsterForFight, results: string = '', disp
-
${monster.name}
+
${monster.name}, level ${monster.level}
${hpPercent}% - ${monster.hp} / ${monster.maxHp}
@@ -122,7 +122,7 @@ export function renderFightPreRound(monster: MonsterForFight, displayFightActio
-
${monster.name}
+
${monster.name}, level ${monster.level}
${hpPercent}% - ${monster.hp} / ${monster.maxHp}
diff --git a/src/server/views/monster-selector.ts b/src/server/views/monster-selector.ts index e357835..03d6197 100644 --- a/src/server/views/monster-selector.ts +++ b/src/server/views/monster-selector.ts @@ -22,9 +22,9 @@ export function renderMonsterSelector(monsters: Monster[] | MonsterForFight[], a


diff --git a/src/shared/monsters.ts b/src/shared/monsters.ts index c29a477..91c0dfa 100644 --- a/src/shared/monsters.ts +++ b/src/shared/monsters.ts @@ -7,7 +7,8 @@ export type Monster = { dexterity: number; intelligence: number; constitution: number; - level: number; + minLevel: number; + maxLevel: number; gold: number; exp: number; hp: number; @@ -26,11 +27,12 @@ export type MonsterForList = { export type FightTrigger = 'explore' | 'travel'; -export type Fight = Omit & { - id: string, - player_id: string, - ref_id: number - fight_trigger: FightTrigger +export type Fight = Omit & { + id: string; + player_id: string; + level: number; + ref_id: number; + fight_trigger: FightTrigger; }; export type MonsterWithFaction = Fight & { -- 2.25.1