"ts-jest": "^29.1.1",
"ts-loader": "^9.4.3",
"ts-node": "^10.9.1",
- "tsconfig-paths": "^3.14.2",
+ "tsconfig-paths": "^3.15.0",
"tsconfig-paths-webpack-plugin": "^4.0.1",
"typescript": "^4.9.5"
}
}
},
"node_modules/tsconfig-paths": {
- "version": "3.14.2",
- "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz",
- "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==",
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
"dev": true,
"dependencies": {
"@types/json5": "^0.0.29",
}
},
"tsconfig-paths": {
- "version": "3.14.2",
- "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz",
- "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==",
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
"dev": true,
"requires": {
"@types/json5": "^0.0.29",
"seed": "npx ts-node ./node_modules/knex/bin/cli.js seed:run",
"seed:prod": "NODE_ENV=production npm run seed",
"dev:client": "npx webpack -w",
- "dev": "npx nodemon src/server/api.ts",
+ "dev": "npx nodemon -r tsconfig-paths/register src/server/api.ts",
"prepare": "husky install",
"release": "npx standard-version && npm run copy-changelog",
"copy-changelog": "cp ./CHANGELOG.md ~/repos/xangelo.ca/static/",
"ts-jest": "^29.1.1",
"ts-loader": "^9.4.3",
"ts-node": "^10.9.1",
- "tsconfig-paths": "^3.14.2",
+ "tsconfig-paths": "^3.15.0",
"tsconfig-paths-webpack-plugin": "^4.0.1",
"typescript": "^4.9.5"
},
-import { Permission } from "../shared/player";
+import { Permission } from "@shared/player";
import { db } from './lib/db';
export async function givePlayerPermission(player_id: string, permission: Permission) {
import http from 'http';
import { Server, Socket } from 'socket.io';
-import * as CONSTANT from '../shared/constants';
+import * as CONSTANT from '@shared/constants';
import { logger } from './lib/logger';
import { loadPlayer, createPlayer, updatePlayer } from './player';
import { random, each } from 'lodash';
-import {broadcastMessage} from '../shared/message';
-import { Player } from '../shared/player';
+import {broadcastMessage} from '@shared/message';
+import { Player } from '@shared/player';
import {createFight, getMonsterList, getMonsterLocation, getRandomMonster, loadMonster, loadMonsterFromFight} from './monster';
import { addInventoryItem } from './inventory';
-import {FightTrigger, Monster} from '../shared/monsters';
+import {FightTrigger, Monster} from '@shared/monsters';
import {getShopEquipment } from './shopEquipment';
import { getAllPaths, getAllServices, getCityDetails, getService, getTravelPlan, getDungeon } from './map';
import { signup, login, authEndpoint } from './auth';
-import { Player } from 'shared/player';
+import { Player } from '@shared/player';
import xss from 'xss';
import bcrypt from 'bcrypt';
import { loadPlayer } from './player';
-import { Auth } from '../shared/auth';
+import { Auth } from '@shared/auth';
import { db } from './lib/db';
import { Request, Response } from 'express';
import { Server, Socket } from 'socket.io';
-import { Player } from '../shared/player';
-import { broadcastMessage } from '../shared/message';
+import { Player } from '@shared/player';
+import { broadcastMessage } from '@shared/message';
import { renderChatMessage } from './views/chat';
import { Commands } from './chat-commands/';
-import { Fight } from "shared/monsters";
-import { Dungeon, DungeonRoom, DungeonPlayer, DungeonState, DungeonStateSummaryVists, DungeonStateSummaryFights } from "../shared/dungeon";
+import { Fight } from "@shared/monsters";
+import { Dungeon, DungeonRoom, DungeonPlayer, DungeonState, DungeonStateSummaryVists, DungeonStateSummaryFights } from "@shared/dungeon";
import { db } from './lib/db';
import { Request, Response } from 'express';
import { ErrorAlert } from "./views/alert";
import {db} from "./lib/db";
-import {EquippedItemDetails} from "../shared/equipped";
-import {EquipmentSlot, InventoryItem} from "../shared/inventory";
+import {EquippedItemDetails} from "@shared/equipped";
+import {EquipmentSlot, InventoryItem} from "@shared/inventory";
export async function getEquippedItems(playerId: string): Promise<EquippedItemDetails[]> {
return db.raw<EquippedItemDetails[]>(`
import { db } from './lib/db';
import { version } from '../../package.json';
-import { CreatedEvent, Event, EventName } from '../shared/event';
+import { CreatedEvent, Event, EventName } from '@shared/event';
import { isEqual } from 'lodash';
import { logger } from './lib/logger';
import { EVENT_FLUSH_INTERVAL } from '../shared/constants';
-import { FightRound } from "../shared/fight";
+import { FightRound } from "@shared/fight";
import {
clearFight,
loadMonster,
increaseExp,
updateAp,
} from "./inventory";
-import { EquippedItemDetails } from "../shared/equipped";
-import { EquipmentSlot, levelFromExp } from "../shared/inventory";
-import { MonsterWithFaction, MonsterForFight, Fight } from "../shared/monsters";
+import { EquippedItemDetails } from "@shared/equipped";
+import { EquipmentSlot, levelFromExp } from "@shared/inventory";
+import { MonsterWithFaction, MonsterForFight, Fight } from "@shared/monsters";
import { getPlayerSkillsAsObject, updatePlayerSkills } from "./skills";
import { SkillID, Skills } from "../shared/skills";
import { Request, Response } from "express";
-import {expToLevel, InventoryItem, levelFromExp, ShopEquipment} from "../shared/inventory";
+import {expToLevel, InventoryItem, levelFromExp, ShopEquipment} from "@shared/inventory";
import { v4 as uuid } from 'uuid';
import { db} from './lib/db';
-import {EquippedItemDetails} from "../shared/equipped";
+import {EquippedItemDetails} from "@shared/equipped";
import { unequipItems } from "./equipment";
export type DbReturnInventoryExp = {
import { db } from './lib/db';
-import {Item, PlayerItem, ShopItem} from '../shared/items';
+import {Item, PlayerItem, ShopItem} from '@shared/items';
export async function getShopItems(location_id: number): Promise<(ShopItem & Item)[]> {
const res = await db.raw(`select si.*, i.* from shop_items si
-import { City, Location, LocationWithCity, Path } from "../shared/map";
-import type { Player } from '../shared/player';
-import type { Travel, TravelWithNames } from '../shared/travel';
+import { City, Location, LocationWithCity, Path } from "@shared/map";
+import type { Player } from "@shared/player";
+import type { Travel, TravelWithNames } from "@shared/travel";
import { db } from './lib/db';
import { random } from 'lodash';
import { db } from './lib/db';
-import { Fight, Monster, MonsterWithFaction, MonsterForList, FightTrigger, MonsterVariant, MonsterVariants } from '../shared/monsters';
-import { TimePeriod, TimeManager } from '../shared/time';
-import { LocationWithCity } from '../shared/map';
+import { Fight, Monster, MonsterWithFaction, MonsterForList, FightTrigger, MonsterVariant, MonsterVariants } from '@shared/monsters';
+import { TimePeriod, TimeManager } from '@shared/time';
+import { LocationWithCity } from '@shared/map';
import { random, sample } from 'lodash';
-import { CHANCE_TO_FIGHT_SPECIAL } from '../shared/constants';
+import { CHANCE_TO_FIGHT_SPECIAL } from '@shared/constants';
const time = new TimeManager();
import { db } from './lib/db';
-import {Player, maxHp, maxVigor} from "../shared/player";
+import {Player, maxHp, maxVigor} from "@shared/player";
import { random } from 'lodash';
-import { Skills } from '../shared/skills';
-import {Profession} from 'shared/profession';
+import { Skills } from '@shared/skills';
+import {Profession} from '@shared/profession';
import {logger} from './lib/logger';
export async function loadPlayer(authToken: string): Promise<Player> {
import { db } from './lib/db';
-import {ShopEquipment} from '../shared/inventory';
+import {ShopEquipment} from '@shared/inventory';
export function listShopItems(where: Partial<ShopEquipment>): Promise<ShopEquipment[]> {
return db.select('*').from<ShopEquipment>('shop_equipment')
-import {Skills, Skill, SkillID} from '../shared/skills';
+import {Skills, Skill, SkillID} from '@shared/skills';
import { db } from './lib/db';
import { each } from 'lodash';
import {Server, Socket} from "socket.io";
-import {Player} from "../shared/player";
+import {Player} from "@shared/player";
export type API = {
socket: Socket;
-import {Message} from "../../shared/message";
+import {Message} from "@shared/message";
export function renderChatMessage(msg: Message): string {
return `<div class="chat-message" title="${new Date(parseInt(msg.sentAt))}" id="${msg.id}">
-import { EquippedItemDetails } from "../../../shared/equipped";
-import { ItemStatBoostAbbr, ShopEquipment } from "../../../shared/inventory";
-import { baseDamage, Player } from "../../../shared/player";
+import { EquippedItemDetails } from "@shared/equipped";
+import { ItemStatBoostAbbr, ShopEquipment } from "@shared/inventory";
+import { baseDamage, Player } from "@shared/player";
export function renderStatBoostWithPlayerIncrease(player: Player, name: ItemStatBoostAbbr, item: ShopEquipment | EquippedItemDetails) {
const val = item.boosts.damage;
const diff = effectiveDamage - val;
// calculate any stat boost from the player
- return `<span class="requirement-title">${name}</span>:
-<span class="requirement-value tooltip ${typeof val === 'number' ? (val > 0 ? "success": "error") : ""}" title="${val}(item) + ${diff}(from stats)">${valSign}${effectiveDamage}</span>`;
+ return `<div class="requirement"><span class="requirement-title">${name}</span>:
+<span class="requirement-value tooltip ${typeof val === 'number' ? (val > 0 ? "success": "error") : ""}" title="${val}(item) + ${diff}(from stats)">${valSign}${effectiveDamage}</span></div>`;
}
-import { DUNGEON_TRAVEL_BLOCK } from '../../../shared/constants';
-import { Dungeon, DungeonPlayer, DungeonRewards, DungeonRoom, DungeonState, DungeonStateSummaryVists } from '../../../shared/dungeon';
+import { DUNGEON_TRAVEL_BLOCK } from '@shared/constants';
+import { Dungeon, DungeonPlayer, DungeonRewards, DungeonRoom, DungeonState, DungeonStateSummaryVists } from '@shared/dungeon';
import { Button, ButtonWithBlock } from '../components/button';
import { Details, Title } from '../components/city';
-import { Dungeon } from "shared/dungeon";
-import { FightRound } from "shared/fight";
-import { LocationWithCity } from "shared/map";
-import { Fight, MonsterForFight } from "../../shared/monsters";
+import { Dungeon } from "@shared/dungeon";
+import { FightRound } from "@shared/fight";
+import { LocationWithCity } from "@shared/map";
+import { Fight, MonsterForFight } from "@shared/monsters";
import { Button, ButtonWithBlock } from "./components/button";
import { Details, Title } from "./components/city";
+import { Player } from "@shared/player";
export function renderRoundDetails(roundData: FightRound): string {
let html: string[] = roundData.roundDetails.map(d => `<div>${d}</div>`);
-import { EquipmentSlot } from "shared/inventory";
-import { EquippedItemDetails } from "../../shared/equipped";
-import { PlayerItem } from "../../shared/items";
+import { EquipmentSlot, meetsRequirements } from "@shared/inventory";
+import { EquippedItemDetails } from "@shared/equipped";
+import { PlayerItem } from "@shared/items";
import { capitalize } from "lodash";
import { ProgressBar } from "./components/progress-bar";
-import { Player } from "../../shared/player";
+import { Player } from "@shared/player";
import { renderStatBoostWithPlayerIncrease } from "./components/stats";
-import { getDurabilityApproximation, expToLevel, levelFromExp } from "../../shared/inventory";
-import { slugify } from "../../shared/utils";
+import { getDurabilityApproximation, expToLevel, levelFromExp } from "@shared/inventory";
+import { slugify } from "@shared/utils";
function icon(icon_name?: string): string {
const placeholder = 'https://placehold.co/64x64/af936c/6d5f4d';
if(currentVal && typeof val === 'number') {
colorIndicator = currentVal >= val ? 'success' : 'error';
}
- return `<span class="requirement-title">${name}</span>: <span class="requirement-value ${colorIndicator}">${val.toLocaleString()}</span>`;
+ return `<div class="requirement"><span class="requirement-title">${name}</span>: <span class="requirement-value ${colorIndicator}">${val.toLocaleString()}</span></div>`;
}
-function renderStatBoost(name: string, val: number | string): string {
+function renderStatBoost(name: string, val: number | string, percent: boolean = false): string {
let valSign: string = '';
if(typeof val === 'number') {
valSign = val > 0 ? '+' : '-';
}
- return `<span class="requirement-title">${name}</span>: <span class="requirement-value ${typeof val === 'number' ? (val > 0 ? "success": "error") : ""}">${valSign}${val}</span>`;
+ return `<div class="requirement"><span class="requirement-title">${name}</span>: <span class="requirement-value ${typeof val === 'number' ? (val > 0 ? "success": "error") : ""}">${valSign}${val}${percent ? '%' : ''}</span></div>`;
}
function renderInventoryItem(player: Player, item: EquippedItemDetails , action: (item: EquippedItemDetails) => string): string {
const itemLevel = levelFromExp(item.current_exp);
+ const requirements = meetsRequirements(item, player);
return `<div class="list-item">
<div class="icon-wrapper">
<div class="icon ${slugify(getDurabilityApproximation(item))}" style="background-image: url('${icon(item.icon)}')" title="Durability: ${item.currentAp}/${item.maxAp} (${Math.round((item.currentAp / item.maxAp) * 100)}%)">
</div>
</div>
<div class="details">
- <div class="name">${item.name}</div>
+ <div class="name">${item.name} <span class="right">${item.profession} (<span class="requirement-hover">REQ</span>)</span></div>
<div class="stat-mods">
${item.boosts.defence ? renderStatBoost('DEF', item.boosts.defence) : ''}
${item.boosts.strength ? renderStatBoost('STR', item.boosts.strength) : ''}
${item.boosts.dexterity ? renderStatBoost('DEX', item.boosts.dexterity) : ''}
${item.boosts.intelligence ? renderStatBoost('INT', item.boosts.intelligence) : ''}
${item.boosts.damage ? renderStatBoostWithPlayerIncrease(player, item.affectedSkills.includes('restoration_magic') ? 'HP' : 'DMG', item) : ''}
- ${item.boosts.damage_mitigation ? renderStatBoost('MIT', item.boosts.damage_mitigation.toString())+'%' : ''}
+ ${item.boosts.damage_mitigation ? renderStatBoost('MIT', item.boosts.damage_mitigation.toString(), true) : ''}
</div>
<div class="requirements" title="Requirements">
${item.requirements.level ? renderRequirement('LVL', item.requirements.level): ''}
${item.requirements.constitution ? renderRequirement('CON', item.requirements.constitution): ''}
${item.requirements.dexterity ? renderRequirement('DEX', item.requirements.dexterity): ''}
${item.requirements.intelligence ? renderRequirement('INT', item.requirements.intelligence): ''}
- ${renderRequirement('PRF', item.profession)}
</div>
${ProgressBar(item.current_exp - expToLevel(itemLevel), expToLevel(itemLevel + 1) - expToLevel(itemLevel), `exp-${item.item_id}`, {
startingColor: '#7be67b',
-import { LocationType, Location, Path, City } from "../../shared/map";
+import { LocationType, Location, Path, City } from "@shared/map";
export async function renderMap(data: { city: City, locations: Location[], paths: Path[]}, closestTown: number): Promise<string> {
-import { max } from "lodash";
-import { LocationWithCity } from "../../shared/map";
-import { Monster, MonsterForFight } from "../../shared/monsters";
+import { LocationWithCity } from "@shared/map";
+import { Monster, MonsterForFight } from "@shared/monsters";
import { BackToTown } from "./components/button";
export function renderOnlyMonsterSelector(monsters: Monster[] | MonsterForFight[], activeMonsterId: number = 0, location?: LocationWithCity): string {
-import { expToLevel, maxHp, maxVigor, Player } from "../../shared/player";
+import { expToLevel, maxHp, maxVigor, Player } from "@shared/player";
import { ProgressBar } from "./components/progress-bar";
function displayLoginSignupForm(): string {
-import { EquippedItemDetails } from "../../shared/equipped";
-import { expToLevel, maxHp, maxVigor, Player, StatDef, StatDisplay, totalDefence } from "../../shared/player";
+import { EquippedItemDetails } from "@shared/equipped";
+import { expToLevel, maxHp, maxVigor, Player, StatDef, StatDisplay, totalDefence } from "@shared/player";
function statPointIncreaser(stat: StatDisplay) {
return `<button class="increase-stat" hx-post="/player/stat/${stat.id}" hx-target="#profile">+</button>`;
import { capitalize } from "lodash";
-import { Player } from "../../shared/player";
-import { repairCost } from "../../shared/inventory";
-import { LocationWithCity } from "../../shared/map";
-import { EquippedItemDetails } from "../../shared/equipped";
+import { Player } from "@shared/player";
+import { repairCost } from "@shared/inventory";
+import { LocationWithCity } from "@shared/map";
+import { EquippedItemDetails } from "@shared/equipped";
import { ProgressBar } from "./components/progress-bar";
import * as City from './components/city';
import { BackToTown } from "./components/button";
function renderRequirement(name: string, val: number | string, currentVal?: number): string {
let colorIndicator = '';
- if(currentVal) {
+ if(currentVal !== undefined && typeof val === 'number') {
colorIndicator = currentVal >= val ? 'success' : 'error';
}
return `<span class="requirement-title">${name}</span>: <span class="requirement-value ${colorIndicator}">${val.toLocaleString()}</span>`;
-import { Skill, Skills } from "../../shared/skills";
+import { Skill, Skills } from "@shared/skills";
export function renderSkills(skills: Skill[]): string {
let html = `<table id="skill-list">
-import { ItemStatBoostAbbr, ShopEquipment } from "../../shared/inventory";
-import { ShopItem, Item } from "../../shared/items";
+import { ItemStatBoostAbbr, ShopEquipment } from "@shared/inventory";
+import { ShopItem, Item } from "@shared/items";
import { capitalize, merge } from "lodash";
-import { Player } from "../../shared/player";
-import { LocationWithCity } from "shared/map";
-import { ProgressBar } from "./components/progress-bar";
+import { Player } from "@shared/player";
+import { LocationWithCity } from "@shared/map";
import { BackToTown } from "./components/button";
import { renderStatBoostWithPlayerIncrease } from "./components/stats";
if(typeof val === 'number') {
valSign = val > 0 ? '+' : '-';
}
- return `<span class="requirement-title" title="${title}">${name}</span>: <span class="requirement-value ${typeof val === 'number' ? (val > 0 ? "success": "error") : ""}">${valSign}${val}</span>`;
+ return `<div class="requirement"><span class="requirement-title" title="${title}">${name}</span>: <span class="requirement-value ${typeof val === 'number' ? (val > 0 ? "success": "error") : ""}">${valSign}${val}</span></div>`;
}
function renderRequirement(name: string, val: number | string, currentVal?: number): string {
if (currentVal && typeof val === 'number') {
colorIndicator = currentVal >= val ? 'success' : 'error';
}
- return `<span class="requirement-title">${name}</span>: <span class="requirement-value ${colorIndicator}">${val.toLocaleString()}</span>`;
+ return `<div class="requirement"><span class="requirement-title">${name}</span>: <span class="requirement-value ${colorIndicator}">${val.toLocaleString()}</span></div>`;
}
function renderShopItem(item: (ShopItem & Item), action: (item: (ShopItem & Item)) => string): string {
function renderShopEquipment(item: ShopEquipment, action: (item: ShopEquipment) => string, player: Player): string {
return `<div class="list-item">
- <div class="store-icon" style="background-image: url('${item.icon ? `/assets/img/icons/equipment/${item.icon}` : 'https://via.placeholder.com/64x64'}')">
- <div class="store-actions">${action(item)}</div>
+ <div class="icon-wrapper">
+ <div class="icon" style="background-image: url('${item.icon ? `/assets/img/icons/equipment/${item.icon}` : 'https://via.placeholder.com/64x64'}')"></div>
+ <div class="actions">${action(item)}</div>
</div>
${renderEquipmentDetails(item, player)}
</div>`;
-import { TravelDTO } from "../../shared/map";
+import { TravelDTO } from "@shared/map";
import { ProgressBar } from './components/progress-bar';
import { ButtonWithBlock } from './components/button';
intelligence = 'intelligence'
};
-export type STAT_ABR = 'STR' | 'CON' | 'DEX' | 'INT';
+export type STAT_ABR = 'STR' | 'CON' | 'DEX' | 'INT';
\ No newline at end of file
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": true,
- "baseUrl": "src",
+ "baseUrl": ".",
"outDir": "dist",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"resolveJsonModule": true,
"typeRoots": ["./src/types"],
+ "paths": {
+ "@shared/*": ["src/shared/*"],
+ "@server/*": ["src/server/*"],
+ "@client/*": ["src/client/*"],
+ "@assets/*": ["public/assets/*"]
+ }
},
"include": ["src/server/api.ts"]
}
extensions: [".ts", ".tsx", ".js"],
plugins: [
new tsconfigPaths()
- ]
+ ],
+ alias: {
+ '@shared': path.resolve(__dirname, 'src/shared/'),
+ '@server': path.resolve(__dirname, 'src/server/'),
+ '@client': path.resolve(__dirname, 'src/client/'),
+ '@assets': path.resolve(__dirname, 'public/assets/')
+ }
},
output: {
filename: "bundle.js",
performance: {
maxEntrypointSize: 512000,
maxAssetSize: 512000
- },
+ },
};