From 18e87bf7cf7f67bc80e7518d5a0fce9090087931 Mon Sep 17 00:00:00 2001 From: xangelo Date: Thu, 3 Aug 2023 15:52:24 -0400 Subject: [PATCH] fix: migrate healer to htmx --- src/client/index.ts | 4 + src/events/healer/client.ts | 6 - src/events/healer/server.ts | 171 --------------------------- src/server/api.ts | 78 ++++++------ src/server/auth.ts | 12 ++ src/server/locations/healer/index.ts | 171 +++++++++++++++++++++++++++ src/server/views/map.ts | 9 +- 7 files changed, 228 insertions(+), 223 deletions(-) delete mode 100644 src/events/healer/client.ts delete mode 100644 src/events/healer/server.ts create mode 100644 src/server/locations/healer/index.ts diff --git a/src/client/index.ts b/src/client/index.ts index ce08207..b460ead 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -1111,3 +1111,7 @@ function bootstrap() { $('nav a').first().click(); } +document.body.addEventListener('htmx:configRequest', function(evt) { + //@ts-ignore + evt.detail.headers['x-authtoken'] = authToken(); +}); diff --git a/src/events/healer/client.ts b/src/events/healer/client.ts deleted file mode 100644 index 56a0da1..0000000 --- a/src/events/healer/client.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { SocketEvent } from '../../client/socket-event.client'; -import $ from 'jquery'; - -export const displayHealerDetauls: SocketEvent = new SocketEvent('city:service:healer', (api, data: {text: string}) => { - $('#map').html(data.text); -}); diff --git a/src/events/healer/server.ts b/src/events/healer/server.ts deleted file mode 100644 index df8fb8a..0000000 --- a/src/events/healer/server.ts +++ /dev/null @@ -1,171 +0,0 @@ -import {SocketEvent} from "../../server/socket-event.server"; -import { getCityDetails, getService } from "../../server/map"; -import { City, Location } from "../../shared/map"; -import {maxHp, Player} from '../../shared/player'; -import { updatePlayer } from "../../server/player"; -import { sample } from 'lodash'; - -type TextSegment = 'intro' | 'insufficient_money' | 'heal_successful'; - -type HealText = Record; - -const healCost = 10; - -const defaultTexts: HealText = { - intro: [ - `Welcome traveller, I am {{NAME}}, Healer of {{CITY_NAME}}`, - "Please come in traveller, I am {{NAME}}, Healer of {{CITY_NAME}}", - ], - insufficient_money: [ - "Sorry friend, you don't have enough money..", - "Sorry, that won't be enough..", - "Healing is hard work.. I'm afraid that won't cover it.." - ], - heal_successful: [ - "I hope you feel better now", - "Good luck on your travels!", - "Glad to be of service..." - ] -} - -// overrides for specific areas -const playerTexts: Record = { - [8]: { - intro: [ - 'Welcome to Midfield traveller, I am Casim - healer in these parts', - 'I am Casim the Healer here... how are you enjoying your stay at Midfield?' - ], - insufficient_money: [ - 'Sorry friend, you don\'t have enough money', - 'Look.. I\'m sorry.. that won\'t be enough...' - ], - heal_successful: [ - 'Glad to help!' - ] - }, - [16]: { - intro: [ - 'Ah, welcome to Wildegard, one of the few safehavens in the Akari Woods. I am Adovras, healer in these parts.', - 'Welcome traveller, I am Adovras - healer in these parts' - ], - insufficient_money: [ - `Sorry friend, you don't have enough money...` - ], - heal_successful: [ - "Hope this small healing will be helpful on your journeys" - ] - - }, - [11]: { - intro: [ - 'Ah, welcome traveler - I am Uthar, healer of Davelfell', - 'Hello, I am Uthar, healer of Davelfell', - 'Sorry I\'m a bit busy today, I am Uthar, healer of Davelfell' - ], - insufficient_money: [ - "Bah, don't bother me if you don't have the money", - "Look, I'm very busy - come back when you have the money" - ], - heal_successful: [ - "*Fizz* *POOF* YOU'RE HEALED!" - ] - } -} - -function getText(type: TextSegment, location: Location, city: City): string { - let selected = sample(defaultTexts[type]); - - if(playerTexts[location.id]) { - if(playerTexts[location.id][type].length) { - selected = sample(playerTexts[location.id][type]); - } - } - - return selected.replace("{{NAME}}", location.name).replace("{{CITY_NAME}}", city.name); - -} - -export const healer: SocketEvent = { - eventName: 'city:services:healer', - handler: async (api, data: { args: number }) => { - const text: string[] = []; - const healerId = data.args; - const service = await getService(healerId); - - if(service.city_id !== api.player.city_id) { - api.socket.emit('alert', { - type: 'error', - text: `You don't seem to be in the right city...` - }); - return; - } - - const city = await getCityDetails(service.city_id); - - text.push(`

${service.name}

`); - text.push(`

"${getText('intro', service, city)}"

`); - - - if(api.player.hp === maxHp(api.player.constitution, api.player.level)) { - text.push(`

You're already at full health?

`); - } - else { - if(api.player.gold <= (healCost * 2)) { - text.push(`

You don't seem to have too much money... I guess I can do it for free this time...

`); - text.push(`

`); - } - else { - text.push(`

`); - } - - } - - api.socket.emit('city:service:healer', { - text: text.join("\n") - }); - } -} - -export const heal: SocketEvent = { - eventName: 'city:services:healer:heal', - handler: async (api, data: { args: number }) => { - const text: string[] = []; - const healerId = data.args; - const service = await getService(healerId); - - if(service.city_id !== api.player.city_id) { - api.socket.emit('alert', { - type: 'error', - text: `You don't seem to be in the right city...` - }); - return; - } - - const city = await getCityDetails(service.city_id); - - text.push(`

${service.name}

`); - - const cost = api.player.gold <= (healCost * 2) ? 0 : healCost; - - if(api.player.gold < cost) { - text.push(`

${getText('insufficient_money', service, city)}

`) - api.socket.emit('city:service:healer', { - text: text.join("\n") - }); - return; - } - - api.player.hp = maxHp(api.player.constitution, api.player.level); - api.player.gold -= cost; - - await updatePlayer(api.player); - api.socket.emit('updatePlayer', api.player); - - - text.push(`

${getText('heal_successful', service, city)}

`); - text.push('

'); - api.socket.emit('city:service:healer', { - text: text.join("\n") - }); - } -} diff --git a/src/server/api.ts b/src/server/api.ts index c2469cd..71c84fe 100644 --- a/src/server/api.ts +++ b/src/server/api.ts @@ -20,12 +20,14 @@ import {getShopItem, listShopItems } from './shopEquipment'; import {EquippedItemDetails} from '../shared/equipped'; import {ArmourEquipmentSlot, EquipmentSlot} from '../shared/inventory'; import { clearTravelPlan, getAllPaths, getAllServices, getCityDetails, getService, getTravelPlan, travel } from './map'; -import { signup, login } from './auth'; +import { signup, login, authEndpoint } from './auth'; import {db} from './lib/db'; import { getPlayerSkills, getPlayerSkillsAsObject, updatePlayerSkills } from './skills'; import {SkillID, Skills} from '../shared/skills'; import * as EventList from '../events/server'; +import { router as healerRouter } from './locations/healer'; + import { renderPlayerBar } from './views/player-bar' import { renderStore } from './views/stores'; import { renderMap } from './views/map'; @@ -55,6 +57,11 @@ const io = new Server(server); const cache = new Map(); const chatHistory: Message[] = []; +app.use((req, res, next) => { + console.log(req.method, req.url); + next(); +}); + function calcAp(inventoryItem: EquippedItemDetails[], socket: Socket) { const ap: Record = {}; inventoryItem.forEach(item => { @@ -520,16 +527,7 @@ io.on('connection', async socket => { socket.emit('ready'); }); -function authEndpoint(req: Request, res: Response, next: any) { - const authToken = req.headers['x-authtoken']; - if(!authToken) { - logger.log(`Invalid auth token ${authToken}`); - res.sendStatus(400) - } - else { - next() - } -} +app.use(healerRouter); app.get('/player', authEndpoint, async (req: Request, res: Response) => { const authToken = req.headers['x-authtoken'].toString(); @@ -559,20 +557,6 @@ app.get('/player/skills', authEndpoint, async (req: Request, res: Response) => { res.send(renderSkills(skills)); }); -app.get('/city/:id', async (req: Request, res: Response) => { - const id = parseInt(req.params.id); - if(!id || isNaN(id)) { - return res.sendStatus(400); - } - const [city, locations, paths] = await Promise.all([ - getCityDetails(id), - getAllServices(id), - getAllPaths(id) - ]); - - res.send(await renderMap({city, locations, paths})); -}); - app.get('/player/inventory', authEndpoint, async (req: Request, res: Response) => { const authToken = req.headers['x-authtoken'].toString(); const player: Player = await loadPlayer(authToken) @@ -672,37 +656,42 @@ app.post('/player/unequip/:item_id', authEndpoint, async (req: Request, res: Res res.send(renderInventoryPage(inventory, items, item.type) + renderPlayerBar(player, inventory)); }); -app.get('/state', authEndpoint, async (req: Request, res: Response) => { +app.get('/player/explore', authEndpoint, async (req: Request, res: Response) => { const authToken = req.headers['x-authtoken'].toString(); const player: Player = await loadPlayer(authToken) - let closestTown: number = player.city_id; if(!player) { logger.log(`Couldnt find player with id ${authToken}`); return res.sendStatus(400); } - const fight = await loadMonsterFromFight(player.id); - // check if the player is exploring somewhere! - const travelPlan = await getTravelPlan(player.id); + const fight = await loadMonsterFromFight(player.id); + let closestTown = player.city_id; - if(travelPlan) { - closestTown = (travelPlan.current_position / travelPlan.total_distance) > 0.5 ? travelPlan.destination_id : travelPlan.source_id; + if(fight) { + // ok lets display the fight screen! + console.log('in a fight!'); } + else { + const travelPlan = await getTravelPlan(player.id); + if(travelPlan) { + // traveling! + closestTown = (travelPlan.current_position / travelPlan.total_distance) > 0.5 ? travelPlan.destination_id : travelPlan.source_id; + console.log('travel plan'); + } + else { + // display the city info! + const [city, locations, paths] = await Promise.all([ + getCityDetails(player.city_id), + getAllServices(player.city_id), + getAllPaths(player.city_id) + ]); - const state = { - fight: fight || null, - closestTown, - travel: travelPlan ? { - things: [], - closestTown, - walkingText: 'You keep walking...' - } : null - }; - - res.json(state); + res.send(await renderMap({city, locations, paths}, closestTown)); + } + } }); app.post('/player/:player_id/items/:item_id', authEndpoint, async (req: Request, res: Response) => { @@ -823,6 +812,9 @@ app.get('/city/:city_id/location/:location_id', authEndpoint, async (req: Reques } switch(location.type) { + case 'SERVICES': + console.log('loading service', location); + break; case 'STORES': const [shopEquipment, shopItems] = await Promise.all([ listShopItems({location_id: location.id}), diff --git a/src/server/auth.ts b/src/server/auth.ts index 748c79d..d82d4b6 100644 --- a/src/server/auth.ts +++ b/src/server/auth.ts @@ -3,6 +3,7 @@ import bcrypt from 'bcrypt'; import { loadPlayer } from './player'; import { Auth } from '../shared/auth'; import { db } from './lib/db'; +import { Request, Response } from 'express'; export async function signup(playerId: string, username: string, password: string): Promise { const salt = await bcrypt.genSalt(10); @@ -56,3 +57,14 @@ export async function login(username: string, password: string): Promise } } + +export function authEndpoint(req: Request, res: Response, next: any) { + const authToken = req.headers['x-authtoken']; + if(!authToken) { + console.log(`Invalid auth token ${authToken}`); + res.sendStatus(400) + } + else { + next() + } +} diff --git a/src/server/locations/healer/index.ts b/src/server/locations/healer/index.ts new file mode 100644 index 0000000..8cee6aa --- /dev/null +++ b/src/server/locations/healer/index.ts @@ -0,0 +1,171 @@ +import { Request, Response, Router } from "express"; +import { maxHp, Player } from "../../../shared/player"; +import { authEndpoint } from '../../auth'; +import { logger } from "../../lib/logger"; +import { loadPlayer, updatePlayer } from "../../player"; +import { getCityDetails, getService } from '../../map'; +import { sample } from 'lodash'; +import { City, Location } from "../../../shared/map"; +import { renderPlayerBar } from "../../views/player-bar"; +import { getEquippedItems } from "../../inventory"; + +export const router = Router(); + +type TextSegment = 'intro' | 'insufficient_money' | 'heal_successful'; + +type HealText = Record; + +const healCost = 10; + +const defaultTexts: HealText = { + intro: [ + `Welcome traveller, I am {{NAME}}, Healer of {{CITY_NAME}}`, + "Please come in traveller, I am {{NAME}}, Healer of {{CITY_NAME}}", + ], + insufficient_money: [ + "Sorry friend, you don't have enough money..", + "Sorry, that won't be enough..", + "Healing is hard work.. I'm afraid that won't cover it.." + ], + heal_successful: [ + "I hope you feel better now", + "Good luck on your travels!", + "Glad to be of service..." + ] +}; + +// overrides for specific areas +const playerTexts: Record = { + [8]: { + intro: [ + 'Welcome to Midfield traveller, I am Casim - healer in these parts', + 'I am Casim the Healer here... how are you enjoying your stay at Midfield?' + ], + insufficient_money: [ + 'Sorry friend, you don\'t have enough money', + 'Look.. I\'m sorry.. that won\'t be enough...' + ], + heal_successful: [ + 'Glad to help!' + ] + }, + [16]: { + intro: [ + 'Ah, welcome to Wildegard, one of the few safehavens in the Akari Woods. I am Adovras, healer in these parts.', + 'Welcome traveller, I am Adovras - healer in these parts' + ], + insufficient_money: [ + `Sorry friend, you don't have enough money...` + ], + heal_successful: [ + "Hope this small healing will be helpful on your journeys" + ] + + }, + [11]: { + intro: [ + 'Ah, welcome traveler - I am Uthar, healer of Davelfell', + 'Hello, I am Uthar, healer of Davelfell', + 'Sorry I\'m a bit busy today, I am Uthar, healer of Davelfell' + ], + insufficient_money: [ + "Bah, don't bother me if you don't have the money", + "Look, I'm very busy - come back when you have the money" + ], + heal_successful: [ + "*Fizz* *POOF* YOU'RE HEALED!" + ] + } +} + +function getText(type: TextSegment, location: Location, city: City): string { + let selected = sample(defaultTexts[type]); + + if(playerTexts[location.id]) { + if(playerTexts[location.id][type].length) { + selected = sample(playerTexts[location.id][type]); + } + } + + return selected.replace("{{NAME}}", location.name).replace("{{CITY_NAME}}", city.name); + +} + +router.get('/city/services/city:services:healer/:location_id', authEndpoint, async (req: Request, res: Response) => { + const authToken = req.headers['x-authtoken']!.toString(); + const player: Player = await loadPlayer(authToken) + if(!player) { + logger.log(`Couldnt find player with id ${authToken}`); + return res.sendStatus(400); + } + + const service = await getService(parseInt(req.params.location_id)); + const city = await getCityDetails(service.city_id); + + if(!service || service.city_id !== player.city_id) { + logger.log(`Invalid location: [${req.params.location_id}]`); + res.sendStatus(400); + } + + const text: string[] = []; + + text.push(`

${service.name}

`); + text.push(`

"${getText('intro', service, city)}"

`); + + + if(player.hp === maxHp(player.constitution, player.level)) { + text.push(`

You're already at full health?

`); + } + else { + if(player.gold <= (healCost * 2)) { + text.push(`

You don't seem to have too much money... I guess I can do it for free this time...

`); + text.push(`

`); + } + else { + text.push(`

`); + } + + } + + res.send(`
${text.join("\n")}
`); +}); + + + +router.post('/city/services/city:services:healer:heal/:location_id', authEndpoint, async (req: Request, res: Response) => { + const authToken = req.headers['x-authtoken']!.toString(); + const player: Player = await loadPlayer(authToken) + if(!player) { + logger.log(`Couldnt find player with id ${authToken}`); + return res.sendStatus(400); + } + + const service = await getService(parseInt(req.params.location_id)); + const city = await getCityDetails(service.city_id); + + if(!service || service.city_id !== player.city_id) { + logger.log(`Invalid location: [${req.params.location_id}]`); + res.sendStatus(400); + } + + const text: string[] = []; + text.push(`

${service.name}

`); + + const cost = player.gold <= (healCost * 2) ? 0 : healCost; + + if(player.gold < cost) { + text.push(`

${getText('insufficient_money', service, city)}

`) + res.send(`
${text.join("\n")}
`); + } + else { + player.hp = maxHp(player.constitution, player.level); + player.gold -= cost; + + await updatePlayer(player); + const inventory = await getEquippedItems(player.id); + + text.push(`

${getText('heal_successful', service, city)}

`); + text.push('

'); + res.send(`
${text.join("\n")}
` + renderPlayerBar(player, inventory)); + } +}); diff --git a/src/server/views/map.ts b/src/server/views/map.ts index 63c3496..48e4ece 100644 --- a/src/server/views/map.ts +++ b/src/server/views/map.ts @@ -1,6 +1,6 @@ import { LocationType, Location, Path, City } from "../../shared/map"; -export async function renderMap(data: { city: City, locations: Location[], paths: Path[]}): Promise { +export async function renderMap(data: { city: City, locations: Location[], paths: Path[]}, closestTown: number): Promise { if(!data) { console.error('oh no.. this got triggered without any city data'); @@ -13,10 +13,12 @@ export async function renderMap(data: { city: City, locations: Location[], paths }; data.locations.forEach(l => { - servicesParsed[l.type].push(`${l.name}`); + servicesParsed[l.type].push(`${l.name}`); }); - let html = `
+ let html = ` +
+

${data.city.name}

`; @@ -37,6 +39,7 @@ export async function renderMap(data: { city: City, locations: Location[], paths }).join("
")}
+
`; -- 2.25.1