feat: monster variants
authorxangelo <me@xangelo.ca>
Wed, 6 Sep 2023 17:16:29 +0000 (13:16 -0400)
committerxangelo <me@xangelo.ca>
Wed, 6 Sep 2023 17:16:29 +0000 (13:16 -0400)
Introduced Elder, Skittish, and Brute monster variants. These adjust the
stats/rewards of the monsters further.

public/assets/css/game.css
src/server/monster.ts
src/server/views/fight.ts
src/shared/constants.ts
src/shared/monsters.ts

index da277db1e50850ccff9d65c80cb9da9e7eff7efa..898b78a43d311b0cd1a15146b291c2912b7eb7cf 100644 (file)
@@ -441,9 +441,21 @@ nav.filter-result.active {
   width: 70%;
   margin: 0 auto 1rem;
 }
-#defender-name {
+.monster-identifier {
   text-align: left;
 }
+#defender-name {
+  font-weight: bold;
+}
+.Elder #defender-name {
+  color: #2b2b2b;
+}
+.Skittish #defender-name {
+  color: #8700ff;
+}
+.Brute #defender-name {
+  color: #a91313;
+}
 #fight-results {
   margin-top: 1rem;
 }
index 54a49dbfc2c76fc4365ac5b36b3b4bdbd34647e4..a0b7aac669e7b2c400180381cfde0f3b22fb8cad 100644 (file)
@@ -1,8 +1,9 @@
 import { db } from './lib/db';
-import { Fight, Monster, MonsterWithFaction, MonsterForList, FightTrigger } from '../shared/monsters';
+import { Fight, Monster, MonsterWithFaction, MonsterForList, FightTrigger, MonsterVariant, MonsterVariants } from '../shared/monsters';
 import { TimePeriod, TimeManager } from '../shared/time';
-import { LocationWithCity } from 'shared/map';
-import { max, random } from 'lodash';
+import { LocationWithCity } from '../shared/map';
+import { random, sample } from 'lodash';
+import { CHANCE_TO_FIGHT_SPECIAL } from '../shared/constants';
 
 const time = new TimeManager();
 
@@ -67,23 +68,43 @@ export async function createFight(playerId: string, monster: Monster, fightTrigg
   const chosenLevel = random(monster.minLevel, monster.maxLevel);
   // 30% boost per level difference
   const modifier = Math.pow(Math.E, (chosenLevel - monster.minLevel)/monster.maxLevel);
+  let variant: MonsterVariant = {
+    name: '',
+    display: '{{name}}',
+    strength: 1,
+    constitution: 1,
+    dexterity: 1,
+    intelligence: 1,
+    exp: 1,
+    gold: 1,
+    maxHp: 1,
+    defence: 1
+  };
+
+  if(monster.maxLevel >= 5 && random(0,100) <= CHANCE_TO_FIGHT_SPECIAL) {
+    variant = sample(MonsterVariants);
+  }
 
-  const res = await db('fight').insert({
+  const monsterData: Omit<Fight, 'id'> = {
     player_id: playerId,
-    name: monster.name,
-    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),
+    variant: variant.name,
+    name: variant.display.replace("{{name}}", monster.name),
+    strength: Math.floor(monster.strength * modifier * variant.strength),
+    constitution: Math.floor(monster.constitution * modifier * variant.constitution),
+    dexterity: Math.floor(monster.dexterity * modifier * variant.dexterity),
+    intelligence: Math.floor(monster.intelligence * modifier * variant.intelligence),
+    exp: Math.floor(monster.exp * modifier * variant.exp),
     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),
+    gold: Math.floor(monster.gold * modifier * variant.exp),
+    hp: Math.floor(monster.hp * modifier * variant.maxHp),
+    defence: Math.floor(monster.defence * modifier * variant.defence),
+    maxHp: Math.floor(monster.maxHp * modifier * variant.maxHp),
     ref_id: monster.id,
     fight_trigger: fightTrigger
-  }).returning<Fight[]>('*');
+  }
+
+  const res = await db('fight').insert(monsterData)
+                    .returning<Fight[]>('*');
 
   return res.pop();
 }
index b89f32e2423887fa8d53307d3a532e13ac8e40a1..c66cdbd87b2c40415a9ccb958940891457ac5190 100644 (file)
@@ -79,7 +79,7 @@ export function renderFight(monster: Fight, results: string = '', displayFightAc
         <img id="avatar" src="https://via.placeholder.com/64x64">
       </div>
       <div id="defender-stat-bars">
-        <div id="defender-name">${monster.name}, level ${monster.level}</div>
+        <div class="monster-identifier ${monster.variant}"><span id="defender-name">${monster.name}</span>, level ${monster.level}</div>
         <div class="progress-bar" id="defender-hp-bar" style="background: linear-gradient(to right, red, red ${hpPercent}%, transparent ${hpPercent}%, transparent)" title="${hpPercent}% - ${monster.hp}/${monster.maxHp}">${hpPercent}% - ${monster.hp} / ${monster.maxHp}</div>
       </div>
     </div>
index fae88bf2f54ae71561d9c8da885d7fc330921319..c95c80dd1f4a17e3b33d7bedd6a7926cf4f0a4d5 100644 (file)
@@ -1,3 +1,5 @@
 export const FIGHT_ATTACK_DELAY = 1500;
 export const STEP_DELAY = 2000;
 export const ALERT_DISPLAY_LENGTH = 3000;
+// this is displayed as a percentage out of 100
+export const CHANCE_TO_FIGHT_SPECIAL = 100;
index f5147ff8a8f41c048659fc1143f8b5c656a8b93d..c7f8700c86560a60baad62fb2f8ad1b19970de86 100644 (file)
@@ -27,9 +27,10 @@ export type MonsterForList = {
 
 export type FightTrigger = 'explore' | 'travel';
 
-export type Fight = Omit<Monster, 'id' | 'faction_id' | 'location_id' | 'minLevel' | 'maxLevel'> & { 
+export type Fight = Omit<Monster, 'id' | 'faction_id' | 'location_id' | 'minLevel' | 'maxLevel' | 'time_period'> & { 
   id: string;
   player_id: string;
+  variant: string;
   level: number;
   ref_id: number;
   fight_trigger: FightTrigger;
@@ -49,3 +50,55 @@ export type MonsterForFight = {
   maxLevel: number;
   fight_trigger: FightTrigger;
 }
+
+export type MonsterVariant = {
+  name: string;
+  display: string;
+  strength: number;
+  constitution: number;
+  dexterity: number;
+  intelligence: number;
+  exp: number;
+  gold: number;
+  maxHp: number;
+  defence: number;
+};
+
+export const MonsterVariants: MonsterVariant[] = [
+  {
+    name: 'Brute',
+    display: '{{name}} Brute',
+    strength: 1,
+    constitution: 1,
+    dexterity: 0.6,
+    intelligence: 0.2,
+    exp: 4,
+    gold: 3,
+    maxHp: 2,
+    defence: 3
+  },
+  {
+    name: 'Elder',
+    display: 'Elder {{name}}',
+    strength: 0.8,
+    constitution: 1.2,
+    dexterity: 0.6,
+    intelligence: 1.6,
+    exp: 2,
+    gold: 1,
+    maxHp: 1,
+    defence: 1
+  },
+  {
+    name: 'Skittish',
+    display: 'Skittish {{name}}',
+    strength: 0.8,
+    constitution: 1.2,
+    dexterity: 0.6,
+    intelligence: 1.6,
+    exp: 1,
+    gold: 1.2,
+    maxHp: 1,
+    defence: 0.8
+  }
+];