-import {FightRound} from '../shared/fight';
-import { clearFight, loadMonster, getMonsterList, saveFightState, loadMonsterFromFight } from './monster';
-import { Player, expToLevel, maxHp, totalDefence, maxVigor, baseDamage } from '../shared/player';
-import { clearTravelPlan } from './map';
-import { updatePlayer } from './player';
-import { getEquippedItems, updateAp } from './inventory';
-import { EquippedItemDetails } from '../shared/equipped';
-import { EquipmentSlot } 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 * as Alert from './views/alert';
-import { addEvent } from './events';
-import { Professions } from '../shared/profession';
-
-export async function blockPlayerInFight(req: Request, res: Response, next: any) {
+import { FightRound } from "../shared/fight";
+import {
+ clearFight,
+ loadMonster,
+ getMonsterList,
+ saveFightState,
+ loadMonsterFromFight,
+} from "./monster";
+import {
+ Player,
+ expToLevel,
+ maxHp,
+ totalDefence,
+ maxVigor,
+ baseDamage,
+} from "../shared/player";
+import { clearTravelPlan } from "./map";
+import { updatePlayer } from "./player";
+import {
+ DbReturnInventoryExp,
+ getEquippedItems,
+ getInventoryBulk,
+ increaseExp,
+ updateAp,
+} from "./inventory";
+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 * as Alert from "./views/alert";
+import { addEvent } from "./events";
+import { Professions } from "../shared/profession";
+
+export async function blockPlayerInFight(
+ req: Request,
+ res: Response,
+ next: any
+) {
const fight = await loadMonsterFromFight(req.player.id);
- if(!fight) {
+ if (!fight) {
next();
return;
}
- res.send(Alert.ErrorAlert(`You are currently in a fight with a ${fight.name}`));
+ res.send(
+ Alert.ErrorAlert(`You are currently in a fight with a ${fight.name}`)
+ );
}
-function exponentialExp(exp: number, monsterLevel: number, playerLevel: number): number {
+function exponentialExp(
+ exp: number,
+ monsterLevel: number,
+ playerLevel: number
+): number {
let finalExp = exp;
- if((monsterLevel+3) < playerLevel) {
- finalExp = Math.floor(exp * Math.pow(Math.E, ((monsterLevel + 3) - playerLevel)/5));
- }
- else if(monsterLevel > (playerLevel + 3)) {
- finalExp = Math.floor(exp * Math.pow(Math.E, ((playerLevel + 3) - monsterLevel)/5));
+ if (monsterLevel + 3 < playerLevel) {
+ finalExp = Math.floor(
+ exp * Math.pow(Math.E, (monsterLevel + 3 - playerLevel) / 5)
+ );
+ } else if (monsterLevel > playerLevel + 1) {
+ finalExp = Math.floor(
+ exp * Math.pow(Math.E, (playerLevel + 3 - monsterLevel) / 5)
+ );
}
return Math.floor(finalExp);
}
-export async function fightRound(player: Player, monster: Fight, data: {action: 'attack' | 'cast' | 'flee'}) {
+async function increaseEquipmentLevel(
+ player_id: string,
+ items: DbReturnInventoryExp[]
+) {
+ if (items.length < 1) {
+ return;
+ }
+
+ const inventory = await getInventoryBulk(
+ player_id,
+ items.map((i) => i.item_id)
+ );
+
+ const updatedInventory = inventory.map((item, i) => {
+ const newLevel = levelFromExp(items[i].new_exp);
+ const updatedBoosts = {
+ strength: Math.ceil(item.boosts.strength * 1.1),
+ constitution: Math.ceil(item.boosts.constitution * 1.1),
+ dexterity: Math.ceil(item.boosts.dexterity * 1.1),
+ intelligence: Math.ceil(item.boosts.intelligence * 1.1),
+ damage: Math.ceil(item.boosts.damage * 1.1),
+ damage_mitigation: Math.ceil(item.boosts.damage_mitigation * 1.1),
+ defence: Math.ceil(item.boosts.defence * 1.1),
+ };
+ return { ...item, boosts: updatedBoosts, level: newLevel };
+ });
+
+ console.log(updatedInventory);
+}
+
+export async function fightRound(
+ player: Player,
+ monster: Fight,
+ data: { action: "attack" | "cast" | "flee" }
+) {
const playerSkills = await getPlayerSkillsAsObject(player.id);
const roundData: FightRound = {
monster,
player,
- winner: 'in-progress',
+ winner: "in-progress",
fightTrigger: monster.fight_trigger,
roundDetails: [],
rewards: {
exp: 0,
gold: 0,
- levelIncrease: false
- }
+ levelIncrease: false,
+ },
};
const equippedItems = await getEquippedItems(player.id);
- // we only use this if the player successfully defeated the monster
- // they were fighting, then we load the other monsters in this area
+ // we only use this if the player successfully defeated the monster
+ // they were fighting, then we load the other monsters in this area
// so they can "fight again"
let potentialMonsters: MonsterForFight[] = [];
hp: 0,
};
- const equipment: Map<EquipmentSlot, EquippedItemDetails> = new Map<EquipmentSlot, EquippedItemDetails>();
+ const equipment: Map<EquipmentSlot, EquippedItemDetails> = new Map<
+ EquipmentSlot,
+ EquippedItemDetails
+ >();
const weapons: EquippedItemDetails[] = [];
+ const increaseExpToEquipment: Record<string, number> = {};
let anyDamageSpells: boolean = false;
- equippedItems.forEach(item => {
- if(item.type === 'ARMOUR') {
+ equippedItems.forEach((item) => {
+ increaseExpToEquipment[item.item_id] = 1;
+ if (item.type === "ARMOUR") {
equipment.set(item.equipment_slot, item);
- }
- else if(item.type === 'WEAPON') {
+ } else if (item.type === "WEAPON") {
weapons.push(item);
- }
- else if(item.type === 'SPELL') {
- if(item.affectedSkills.includes('destruction_magic')) {
+ } else if (item.type === "SPELL") {
+ if (item.affectedSkills.includes("destruction_magic")) {
anyDamageSpells = true;
}
weapons.push(item);
boost.dexterity += item.boosts.dexterity;
boost.intelligence += item.boosts.intelligence;
- if(item.type === 'SPELL' && item.affectedSkills.includes('restoration_magic')) {
+ if (
+ item.type === "SPELL" &&
+ item.affectedSkills.includes("restoration_magic")
+ ) {
boost.hp += item.boosts.damage;
- }
- else {
+ } else {
boost.damage += item.boosts.damage;
}
});
+ const equipmentExpGains = (
+ await increaseExp(player.id, increaseExpToEquipment)
+ ).filter((obj) => {
+ return levelFromExp(obj.previous_exp) < levelFromExp(obj.new_exp);
+ });
+ // check if any of the equipment levelled up
+ if (equipmentExpGains.length) {
+ // increment random stats
+ await increaseEquipmentLevel(player.id, equipmentExpGains);
+ }
+
// @TODO implement flee based on dex + vigor
- if(data.action === 'flee') {
- roundData.roundDetails.push(`You managed to escape from the ${monster.name}!`)
- roundData.winner = 'monster';
+ if (data.action === "flee") {
+ roundData.roundDetails.push(
+ `You managed to escape from the ${monster.name}!`
+ );
+ roundData.winner = "monster";
await clearFight(player.id);
return { roundData, monsters: [], player };
}
- const attackType = data.action === 'attack' ? 'physical' : 'magical';
- const primaryStat = attackType === 'physical' ? player.strength : player.intelligence;
- const boostStat = attackType === 'physical' ? boost.strength : boost.intelligence;
+ const attackType = data.action === "attack" ? "physical" : "magical";
+ const primaryStat =
+ attackType === "physical" ? player.strength : player.intelligence;
+ const boostStat =
+ attackType === "physical" ? boost.strength : boost.intelligence;
const playerDamage = baseDamage(primaryStat, boostStat, boost.damage);
const skillsUsed: Record<SkillID | any, number> = {};
let hpHealAfterMasteries: number = -1;
let playerDamageAfterMasteries: number = 0;
// apply masteries!
- weapons.forEach(item => {
- item.affectedSkills.forEach(id => {
- if(id === 'restoration_magic') {
- if(hpHealAfterMasteries < 0) {
+ weapons.forEach((item) => {
+ item.affectedSkills.forEach((id) => {
+ if (id === "restoration_magic") {
+ if (hpHealAfterMasteries < 0) {
hpHealAfterMasteries = 0;
}
const skill = Skills.get(id);
- if(skill) {
+ if (skill) {
const playerSkill = playerSkills.get(id);
- if(playerSkill) {
+ if (playerSkill) {
hpHealAfterMasteries += skill.effect(playerSkill);
}
}
- }
- else {
+ } else {
const skill = Skills.get(id);
- if(skill) {
+ if (skill) {
const playerSkill = playerSkills.get(id);
- if(playerSkill) {
- playerDamageAfterMasteries += playerDamage * skill.effect(playerSkill);
+ if (playerSkill) {
+ playerDamageAfterMasteries +=
+ playerDamage * skill.effect(playerSkill);
}
}
}
- if(!skillsUsed[id]) {
+ if (!skillsUsed[id]) {
skillsUsed[id] = 0;
}
skillsUsed[id]++;
});
await updatePlayerSkills(player.id, playerSkills, skillsUsed);
-
- const playerFinalDamage = (data.action === 'cast' && !anyDamageSpells) ? 0 : Math.floor(playerDamage + playerDamageAfterMasteries);
+ const playerFinalDamage =
+ data.action === "cast" && !anyDamageSpells
+ ? 0
+ : Math.floor(playerDamage + playerDamageAfterMasteries);
const playerFinalHeal = Math.floor(boost.hp + hpHealAfterMasteries);
let monsterTakesDamage = playerFinalDamage - monster.defence;
- if(monsterTakesDamage < 0) {
+ if (monsterTakesDamage < 0) {
monsterTakesDamage = 0;
}
- roundData.roundDetails.push(`You dealt ${monsterTakesDamage} damage to the ${monster.name}!`);
+ roundData.roundDetails.push(
+ `You dealt ${monsterTakesDamage} damage to the ${monster.name}!`
+ );
monster.hp -= monsterTakesDamage;
- if(monster.hp <= 0) {
+ if (monster.hp <= 0) {
roundData.monster.hp = 0;
- roundData.winner = 'player';
+ roundData.winner = "player";
- addEvent('MONSTER_KILLED', player.id, {
+ addEvent("MONSTER_KILLED", player.id, {
monster_id: roundData.monster.ref_id,
monster_name: roundData.monster.name,
level: roundData.monster.level,
- fightTrigger: roundData.monster.fight_trigger
+ fightTrigger: roundData.monster.fight_trigger,
});
const expGained = exponentialExp(monster.exp, monster.level, player.level);
player.gold += monster.gold;
player.exp += expGained;
- if(player.exp >= expToLevel(player.level + 1)) {
- player.exp -= expToLevel(player.level + 1)
+ if (player.exp >= expToLevel(player.level + 1)) {
+ player.exp -= expToLevel(player.level + 1);
player.level++;
- addEvent('LEVEL_UP', player.id, {
- from_level: player.level-1,
- to_level: player.level
+ addEvent("LEVEL_UP", player.id, {
+ from_level: player.level - 1,
+ to_level: player.level,
});
roundData.rewards.levelIncrease = true;
const statPointsGained = 1;
const prof = Professions.get(player.profession);
- if(player.level%2) {
- if(prof.levelUpStatIncrease.odd) {
- for(let stat in prof.levelUpStatIncrease.odd) {
+ if (player.level % 2) {
+ if (prof.levelUpStatIncrease.odd) {
+ for (let stat in prof.levelUpStatIncrease.odd) {
player[stat] += prof.levelUpStatIncrease.odd[stat];
- roundData.roundDetails.push(`You gained +${prof.levelUpStatIncrease.odd[stat]} ${stat}`);
+ roundData.roundDetails.push(
+ `You gained +${prof.levelUpStatIncrease.odd[stat]} ${stat}`
+ );
}
}
- }
- else {
- if(prof.levelUpStatIncrease.even) {
- for(let stat in prof.levelUpStatIncrease.even) {
+ } else {
+ if (prof.levelUpStatIncrease.even) {
+ for (let stat in prof.levelUpStatIncrease.even) {
player[stat] += prof.levelUpStatIncrease.even[stat];
- roundData.roundDetails.push(`You gained +${prof.levelUpStatIncrease.even[stat]} ${stat}`);
+ roundData.roundDetails.push(
+ `You gained +${prof.levelUpStatIncrease.even[stat]} ${stat}`
+ );
}
}
}
player.stat_points += statPointsGained;
- roundData.roundDetails.push(`You gained ${statPointsGained} stat points!`);
+ roundData.roundDetails.push(
+ `You gained ${statPointsGained} stat points!`
+ );
player.hp = maxHp(player.constitution, player.level);
player.vigor = maxVigor(player.constitution, player.level);
}
// get the monster location if it was an EXPLORED fight
- if(roundData.fightTrigger === 'explore') {
+ if (roundData.fightTrigger === "explore") {
const rawMonster = await loadMonster(monster.ref_id);
- const monsterList = await getMonsterList(rawMonster.location_id);
- potentialMonsters = monsterList.map(monster => {
+ const monsterList = await getMonsterList(rawMonster.location_id);
+ potentialMonsters = monsterList.map((monster) => {
return {
id: monster.id,
name: monster.name,
maxLevel: monster.maxLevel,
hp: monster.hp,
maxHp: monster.maxHp,
- fight_trigger: 'explore'
- }
+ fight_trigger: "explore",
+ };
});
}
player.vigor -= 1;
- if(player.vigor < 0) {
+ if (player.vigor < 0) {
player.vigor = 0;
}
- const unequippedItems = await updateAp(player.id, 1, equippedItems.map(i => i.item_id));
+ const unequippedItems = await updateAp(
+ player.id,
+ 1,
+ equippedItems.map((i) => i.item_id)
+ );
await clearFight(player.id);
await updatePlayer(player);
- if(unequippedItems.length) {
- unequippedItems.forEach(i => {
- roundData.roundDetails.push(`Your ${i.name} was too damaged and was unequipped!`);
+ if (unequippedItems.length) {
+ unequippedItems.forEach((i) => {
+ roundData.roundDetails.push(
+ `Your ${i.name} was too damaged and was unequipped!`
+ );
});
}
return { roundData, monsters: potentialMonsters, player };
}
- let monsterDamage = (monster.strength*2) - boost.defence;
- if(monsterDamage < 0) {
+ let monsterDamage = monster.strength * 2 - boost.defence;
+ if (monsterDamage < 0) {
monsterDamage = 0;
}
- roundData.roundDetails.push(`The ${monster.name} hit you for ${monsterDamage} damage`);
+ roundData.roundDetails.push(
+ `The ${monster.name} hit you for ${monsterDamage} damage`
+ );
player.hp -= monsterDamage;
- if(playerFinalHeal > 0) {
+ if (playerFinalHeal > 0) {
player.hp += playerFinalHeal;
- if(player.hp > maxHp(player.constitution, player.level)) {
+ if (player.hp > maxHp(player.constitution, player.level)) {
player.hp = maxHp(player.constitution, player.level);
}
roundData.roundDetails.push(`You healed for ${playerFinalHeal} HP`);
}
- if(player.hp <= 0) {
+ if (player.hp <= 0) {
player.hp = 0;
player.vigor = 0;
- roundData.winner = 'monster';
+ roundData.winner = "monster";
roundData.roundDetails.push(`You were killed by the ${monster.name}`);
await clearFight(player.id);
- const unequippedItems = await updateAp(player.id, 5, equippedItems.map(i => i.item_id));
+ const unequippedItems = await updateAp(
+ player.id,
+ 5,
+ equippedItems.map((i) => i.item_id)
+ );
await updatePlayer(player);
await clearTravelPlan(player.id);
- if(unequippedItems.length) {
- unequippedItems.forEach(i => {
- roundData.roundDetails.push(`Your ${i.name} was too damaged and was unequipped!`);
+ if (unequippedItems.length) {
+ unequippedItems.forEach((i) => {
+ roundData.roundDetails.push(
+ `Your ${i.name} was too damaged and was unequipped!`
+ );
});
}
- return { roundData, monsters: [], player};
+ return { roundData, monsters: [], player };
}
await updatePlayer(player);
await saveFightState(player.id, monster);
- return { roundData, monsters: [], player};
-};
+ return { roundData, monsters: [], player };
+}
import { ProgressBar } from "./components/progress-bar";
import { Player } from "../../shared/player";
import { renderStatBoostWithPlayerIncrease } from "./components/stats";
-import { getDurabilityApproximation, expToLevel} from "../../shared/inventory";
+import { getDurabilityApproximation, expToLevel, levelFromExp } from "../../shared/inventory";
import { slugify } from "../../shared/utils";
function icon(icon_name?: string): string {
function renderRequirement(name: string, val: number | string, currentVal?: number): string {
let colorIndicator = '';
- if(currentVal) {
+ 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>`;
}
function renderInventoryItem(player: Player, item: EquippedItemDetails , action: (item: EquippedItemDetails) => string): string {
+ const itemLevel = levelFromExp(item.current_exp);
return `<div class="store-list">
- <div class="inventory-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 class="inventory-icon-wrapper">
+ <div class="inventory-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 class="inventory-actions">
+ ${action(item)}
+ </div>
</div>
<div class="details">
<div class="name">${item.name}</div>
- <div class="requirements">
- ${item.requirements.level ? renderRequirement('LVL', item.requirements.level): ''}
- ${item.requirements.strength ? renderRequirement('STR', item.requirements.strength): ''}
- ${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>
<div class="stat-mods">
${item.boosts.defence ? renderStatBoost('DEF', item.boosts.defence) : ''}
${item.boosts.strength ? renderStatBoost('STR', item.boosts.strength) : ''}
${item.boosts.damage ? renderStatBoostWithPlayerIncrease(player, item.affectedSkills.includes('restoration_magic') ? 'HP' : 'DMG', item) : ''}
${item.boosts.damage_mitigation ? renderStatBoost('MIT', item.boosts.damage_mitigation.toString())+'%' : ''}
</div>
- ${item.hasOwnProperty('id') ? `<div>${item.cost.toLocaleString()}G</div>` : ''}
+ <div class="requirements" title="Requirements">
+ ${item.requirements.level ? renderRequirement('LVL', item.requirements.level): ''}
+ ${item.requirements.strength ? renderRequirement('STR', item.requirements.strength): ''}
+ ${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>
- <div class="inventory-actions">
- ${action(item)}
+ ${ProgressBar(item.current_exp - expToLevel(itemLevel), expToLevel(itemLevel + 1) - expToLevel(itemLevel), `exp-${item.item_id}`, {
+ startingColor: '#7be67b',
+ endingColor: '#7be67b',
+ displayPercent: true,
+ title: `<b>Level ${itemLevel}:</b> `
+ })}
+ ${item.hasOwnProperty('id') ? `<div>${item.cost.toLocaleString()}G</div>` : ''}
</div>
</div>`;
}