$('nav a').first().click();
}
+document.body.addEventListener('htmx:configRequest', function(evt) {
+ //@ts-ignore
+ evt.detail.headers['x-authtoken'] = authToken();
+});
+++ /dev/null
-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);
-});
+++ /dev/null
-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<TextSegment, string[]>;
-
-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<number, HealText> = {
- [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(`<p><b>${service.name}</b></p>`);
- text.push(`<p>"${getText('intro', service, city)}"</p>`);
-
-
- if(api.player.hp === maxHp(api.player.constitution, api.player.level)) {
- text.push(`<p>You're already at full health?</p>`);
- }
- else {
- if(api.player.gold <= (healCost * 2)) {
- text.push(`<p>You don't seem to have too much money... I guess I can do it for free this time...</p>`);
- text.push(`<p><button type="button" class="city-emit-event" data-event="city:services:healer:heal" data-args="${service.id}">Heal for free!</button></p>`);
- }
- else {
- text.push(`<p><button type="button" class="city-emit-event" data-event="city:services:healer:heal" data-args="${service.id}">Heal for ${healCost}g!</button></p>`);
- }
-
- }
-
- 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(`<p><b>${service.name}</b></p>`);
-
- const cost = api.player.gold <= (healCost * 2) ? 0 : healCost;
-
- if(api.player.gold < cost) {
- text.push(`<p>${getText('insufficient_money', service, city)}</p>`)
- 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(`<p>${getText('heal_successful', service, city)}</p>`);
- text.push('<p><button class="emit-event-internal" data-event="tab:explore">Back to Town</button></p>');
- api.socket.emit('city:service:healer', {
- text: text.join("\n")
- });
- }
-}
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';
const cache = new Map<string, any>();
const chatHistory: Message[] = [];
+app.use((req, res, next) => {
+ console.log(req.method, req.url);
+ next();
+});
+
function calcAp(inventoryItem: EquippedItemDetails[], socket: Socket) {
const ap: Record<any | EquipmentSlot, {currentAp: number, maxAp: number}> = {};
inventoryItem.forEach(item => {
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();
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)
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) => {
}
switch(location.type) {
+ case 'SERVICES':
+ console.log('loading service', location);
+ break;
case 'STORES':
const [shopEquipment, shopItems] = await Promise.all([
listShopItems({location_id: location.id}),
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<void> {
const salt = await bcrypt.genSalt(10);
}
}
+
+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()
+ }
+}
--- /dev/null
+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<TextSegment, string[]>;
+
+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<number, HealText> = {
+ [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(`<p><b>${service.name}</b></p>`);
+ text.push(`<p>"${getText('intro', service, city)}"</p>`);
+
+
+ if(player.hp === maxHp(player.constitution, player.level)) {
+ text.push(`<p>You're already at full health?</p>`);
+ }
+ else {
+ if(player.gold <= (healCost * 2)) {
+ text.push(`<p>You don't seem to have too much money... I guess I can do it for free this time...</p>`);
+ text.push(`<p><button type="button" class="city-emit-event" data-event="city:services:healer:heal" data-args="${service.id}">Heal for free!</button></p>`);
+ }
+ else {
+ text.push(`<p><button type="button" hx-post="/city/services/city:services:healer:heal/${service.id}">Heal for ${healCost}g!</button></p>`);
+ }
+
+ }
+
+ res.send(`<div id="map" hx-target="#map" hx-swap-oob="true">${text.join("\n")}</div>`);
+});
+
+
+
+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(`<p><b>${service.name}</b></p>`);
+
+ const cost = player.gold <= (healCost * 2) ? 0 : healCost;
+
+ if(player.gold < cost) {
+ text.push(`<p>${getText('insufficient_money', service, city)}</p>`)
+ res.send(`<div id="map" hx-target="#map" hx-swap-oob="true">${text.join("\n")}</div>`);
+ }
+ else {
+ player.hp = maxHp(player.constitution, player.level);
+ player.gold -= cost;
+
+ await updatePlayer(player);
+ const inventory = await getEquippedItems(player.id);
+
+ text.push(`<p>${getText('heal_successful', service, city)}</p>`);
+ text.push('<p><button hx-get="/player/explore" hx-target="#explore">Back to Town</button></p>');
+ res.send(`<div id="map" hx-target="#map" hx-swap-oob="true">${text.join("\n")}</div>` + renderPlayerBar(player, inventory));
+ }
+});
import { LocationType, Location, Path, City } from "../../shared/map";
-export async function renderMap(data: { city: City, locations: Location[], paths: Path[]}): Promise<string> {
+export async function renderMap(data: { city: City, locations: Location[], paths: Path[]}, closestTown: number): Promise<string> {
if(!data) {
console.error('oh no.. this got triggered without any city data');
};
data.locations.forEach(l => {
- servicesParsed[l.type].push(`<a href="#" hx-get="/city/${data.city.id}/location/${l.id}/" hx-trigger="click" id="location-${l.id}">${l.name}</a>`);
+ servicesParsed[l.type].push(`<a href="#" hx-get="/city/${l.type.toLowerCase()}/${l.event_name}/${l.id}" hx-trigger="click" id="location-${l.id}">${l.name}</a>`);
});
- let html = `<div hx-target="#map">
+ let html = `
+<section id="explore" class="tab active" style="background-image: linear-gradient(to left top, rgba(255,255,255,0) 0%,rgb(255,255,255) 100%), linear-gradient(to left, rgba(255, 255, 255, 0) 0%, rgb(255, 255, 255) 100%), url('/assets/img/map/${closestTown}.jpeg')">
+<div id="map">
<h1>${data.city.name}</h1>
<div class="city-details">`;
}).join("<br>")}
</div>
</div>
+</div>
</div>
`;