feat(monsters): add monster icons for fights
authorxangelo <me@xangelo.ca>
Fri, 6 Dec 2024 20:56:25 +0000 (15:56 -0500)
committerxangelo <me@xangelo.ca>
Fri, 6 Dec 2024 21:19:37 +0000 (16:19 -0500)
migrations/20241206170712_monster_images.ts [new file with mode: 0644]
public/assets/css/combat.css
seeds/monsters.ts
src/server/monster.ts
src/server/views/fight.ts
src/shared/monsters.ts

diff --git a/migrations/20241206170712_monster_images.ts b/migrations/20241206170712_monster_images.ts
new file mode 100644 (file)
index 0000000..84ee4a0
--- /dev/null
@@ -0,0 +1,16 @@
+import { Knex } from "knex";
+
+
+export async function up(knex: Knex): Promise<void> {
+    return knex.schema.alterTable('monsters', (table) => {
+        table.string('icon').nullable();
+    });
+}
+
+
+export async function down(knex: Knex): Promise<void> {
+    return knex.schema.alterTable('monsters', (table) => {
+        table.dropColumn('icon');
+    });
+}
+
index 2c0b42752e7391dfd92ca6efaf3cbc8de9b05bac..2fb9e20034cb0935aef6674602c97d877cb6683b 100644 (file)
 
 #fight-results {
     margin: 1rem 0;
+}
+
+#fight-container {
+    .icon-container {
+        .icon {
+            width: 64px;
+            height: 64px;
+        }
+    }
 }
\ No newline at end of file
index a8dafe88144fb05e3d4aac517fcc83479e9d4ba3..94aa0b109bf01b166ae451411b94a00cc3e4c62a 100644 (file)
@@ -63,7 +63,8 @@ export async function createMonsters(): Promise<void> {
           defence: Math.floor(parseInt(r.fields.Defence.toString() || '0')),
           location_id: r.fields.location_id[0],
           faction_id: factionId,
-          time_period: r.fields.time_period ? r.fields.time_period : 'any'
+          time_period: r.fields.time_period ? r.fields.time_period : 'any',
+          icon: r.fields.icon ? r.fields.icon : null
         }
       })).onConflict('id').merge();
 
index 3e7e813d690ea8f2362eec45d2506cc3b12faef6..16c12d5b3078d75848b86a365e55153d605888d0 100644 (file)
@@ -37,7 +37,7 @@ export async function loadMonster(id: number): Promise<Monster> {
 }
 
 export async function loadMonsterFromFight(player_id: string): Promise<Fight> {
-  return await db.first().select('*').from<Fight>('fight').where({
+  return await db.first().select(['fight.*', 'monsters.icon']).from<Fight>('fight').join('monsters', 'fight.ref_id', '=', 'monsters.id').where({
     player_id,
   });
 }
@@ -46,7 +46,7 @@ export async function loadMonsterWithFaction(player_id: string): Promise<Monster
   const res = await db.raw(`
                       select 
                         f.*, fa.id as faction_id, fa.name as faction_name,
-                        m."minLevel", m."maxLevel"
+                        m."minLevel", m."maxLevel", m.icon,
                       from fight f
                       join monsters m on f.ref_id = m.id
                       left outer join factions fa on m.faction_id = fa.id
@@ -87,7 +87,7 @@ export async function createFight(playerId: string, monster: Monster, fightTrigg
     variant = sample(MonsterVariants);
   }
 
-  const monsterData: Omit<Fight, 'id'> = {
+  const monsterData: Omit<Fight, 'id' | 'icon'> = {
     player_id: playerId,
     variant: variant.name,
     name: variant.display.replace("{{name}}", monster.name),
@@ -108,7 +108,11 @@ export async function createFight(playerId: string, monster: Monster, fightTrigg
   const res = await db('fight').insert(monsterData)
                     .returning<Fight[]>('*');
 
-  return res.pop();
+  const fight = res.pop();
+
+  fight.icon = monster.icon;
+
+  return fight;
 }
 
 export async function getMonsterLocation(monsterId: number): Promise<LocationWithCity> {
index b0d9711836b53c773a7eb7a125b4fb809d3ad935..1207e87deca364198a57f270d1eb654ce57524e3 100644 (file)
@@ -76,14 +76,23 @@ function AttackButton(blockTime?: number): string {
   }
 }
 
+function placeholderMonsterIcon(icon?: string): string {
+  if(icon) {
+    return `<img class="icon" src="/assets/img/icons/monsters/${icon}">`;
+  }
+  else {
+    return `<img class="icon" src="https://via.placeholder.com/64x64">`;
+  }
+}
+
 export function renderFight(monster: Fight, results: string = '', displayFightActions: boolean = true, blockTime: number = 0) {
   const hpPercent = Math.floor((monster.hp / monster.maxHp) * 100);
 
   let html = `
   <div id="fight-container">
     <div id="defender-info">
-      <div class="avatar-container">
-        <img id="avatar" src="https://via.placeholder.com/64x64">
+      <div class="icon-container">
+        ${placeholderMonsterIcon(monster.icon)}
       </div>
       <div id="defender-stat-bars">
         <div class="monster-identifier ${monster.variant}"><span id="defender-name">${monster.name}</span>, level ${monster.level}</div>
index 203d6e232e968def1427b77b805852846d34a923..423d78abadfd9172c6de1337fd3ce5d168f1a114 100644 (file)
@@ -17,6 +17,7 @@ export type Monster = {
   location_id: number;
   faction_id: number;
   time_period: TimePeriod;
+  icon: string;
 }
 
 export type MonsterForList = {