Compare commits
No commits in common. "89164bf83dd2167a324dd0db5aa7f7c4688eaa7c56c99326fa2d83f08efeb825" and "a14be66890fdcddff509a8c1c04e07afcf312db91ae53b71bc22fce6085277f3" have entirely different histories.
89164bf83d
...
a14be66890
5 changed files with 1 additions and 324 deletions
|
|
@ -25,7 +25,6 @@ import {
|
||||||
} from "../../services/game/location.js";
|
} from "../../services/game/location.js";
|
||||||
import { getLevel } from "../../services/game/entity.js";
|
import { getLevel } from "../../services/game/entity.js";
|
||||||
import {
|
import {
|
||||||
fightEntity,
|
|
||||||
getFullItemName,
|
getFullItemName,
|
||||||
getTotalStats,
|
getTotalStats,
|
||||||
type IEntity,
|
type IEntity,
|
||||||
|
|
@ -80,10 +79,6 @@ const registerModuleGame = (
|
||||||
startConditions: [`${gamePrefix} entity `],
|
startConditions: [`${gamePrefix} entity `],
|
||||||
callbackFunc: onEntity,
|
callbackFunc: onEntity,
|
||||||
});
|
});
|
||||||
callbackStore.messageCallbacks.push({
|
|
||||||
startConditions: [`${gamePrefix} fight `],
|
|
||||||
callbackFunc: onFight,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onHelp = (_text: string, roomId: string) => {
|
const onHelp = (_text: string, roomId: string) => {
|
||||||
|
|
@ -102,7 +97,7 @@ const onHelp = (_text: string, roomId: string) => {
|
||||||
<li><b>!game entities</b> - Shows entities at your location</li>
|
<li><b>!game entities</b> - Shows entities at your location</li>
|
||||||
<li><b>!game entity {entity}</b> - Shows information about an entity</li>
|
<li><b>!game entity {entity}</b> - Shows information about an entity</li>
|
||||||
<li><b>(WIP) !game talk {entity}</b> - Talk to an entity</li>
|
<li><b>(WIP) !game talk {entity}</b> - Talk to an entity</li>
|
||||||
<li><b>!game fight {entity}</b> - Fight an entity</li>
|
<li><b>(WIP) !game fight {entity}</b> - Fight an entity</li>
|
||||||
<li><b>(WIP) !game work {action}</b> - Start work</li>
|
<li><b>(WIP) !game work {action}</b> - Start work</li>
|
||||||
</ul>`,
|
</ul>`,
|
||||||
);
|
);
|
||||||
|
|
@ -348,22 +343,4 @@ const onEntity = (text: string, roomId: string, sender: string) => {
|
||||||
onStatusName(roomId, entity.name);
|
onStatusName(roomId, entity.name);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onFight = (text: string, roomId: string, sender: string) => {
|
|
||||||
const player = getPlayer(sender);
|
|
||||||
|
|
||||||
const entityName = text.replace(`${gamePrefix} entity `, "").trim();
|
|
||||||
if (!existsEntity(entityName)) {
|
|
||||||
client.sendTextMessage(roomId, "No such entity exists");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const entity = getEntityByName(entityName);
|
|
||||||
if (!entitiesShareLocation(player, entity)) {
|
|
||||||
client.sendTextMessage(roomId, "No such entity in your vicinity");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fightEntity(client, roomId, player, entity);
|
|
||||||
};
|
|
||||||
|
|
||||||
export { registerModuleGame };
|
export { registerModuleGame };
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,8 @@
|
||||||
import { getUserNameById } from "../../helpers.js";
|
import { getUserNameById } from "../../helpers.js";
|
||||||
import { state } from "../../store/store.js";
|
import { state } from "../../store/store.js";
|
||||||
import {
|
import {
|
||||||
Attack,
|
|
||||||
attackPunch,
|
|
||||||
attacks,
|
|
||||||
npcBecky,
|
npcBecky,
|
||||||
npcs,
|
npcs,
|
||||||
type IAttack,
|
|
||||||
type IEntity,
|
type IEntity,
|
||||||
type INPC,
|
type INPC,
|
||||||
type INPCData,
|
type INPCData,
|
||||||
|
|
@ -20,7 +16,6 @@ import { Race, raceHuman, races, type IRace } from "./structures/races.js";
|
||||||
import type { ILevel } from "./types.js";
|
import type { ILevel } from "./types.js";
|
||||||
|
|
||||||
const createPlayer = (name: string): IPlayer => ({
|
const createPlayer = (name: string): IPlayer => ({
|
||||||
isPlayer: true,
|
|
||||||
name: name,
|
name: name,
|
||||||
description: "",
|
description: "",
|
||||||
race: Race.HUMAN,
|
race: Race.HUMAN,
|
||||||
|
|
@ -40,7 +35,6 @@ const createPlayer = (name: string): IPlayer => ({
|
||||||
stealth: 0,
|
stealth: 0,
|
||||||
charisma: 0,
|
charisma: 0,
|
||||||
lockpicking: 0,
|
lockpicking: 0,
|
||||||
attacks: [Attack.PUNCH, Attack.KICK],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const createNpcData = (npc: INPC): INPCData => ({
|
const createNpcData = (npc: INPC): INPCData => ({
|
||||||
|
|
@ -49,10 +43,6 @@ const createNpcData = (npc: INPC): INPCData => ({
|
||||||
dead: false,
|
dead: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const isPlayer = (entity: IEntity): boolean => {
|
|
||||||
return "isPlayer" in entity;
|
|
||||||
};
|
|
||||||
|
|
||||||
const existsPlayer = (name: string): boolean => {
|
const existsPlayer = (name: string): boolean => {
|
||||||
return (
|
return (
|
||||||
state.game.players.find(
|
state.game.players.find(
|
||||||
|
|
@ -72,12 +62,6 @@ const existsEntity = (name: string): boolean => {
|
||||||
return existsPlayer(name) || existsNpc(name);
|
return existsPlayer(name) || existsNpc(name);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAttack = (id: Attack): IAttack => {
|
|
||||||
const attack = attacks.find((attack) => attack.id === id);
|
|
||||||
|
|
||||||
return attack ? attack : attackPunch;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPlayer = (userId: string): IPlayer => {
|
const getPlayer = (userId: string): IPlayer => {
|
||||||
return getPlayerByName(getUserNameById(userId));
|
return getPlayerByName(getUserNameById(userId));
|
||||||
};
|
};
|
||||||
|
|
@ -192,11 +176,9 @@ const getSpeed = (entity: IEntity) => {
|
||||||
export {
|
export {
|
||||||
createPlayer,
|
createPlayer,
|
||||||
createNpcData,
|
createNpcData,
|
||||||
isPlayer,
|
|
||||||
existsPlayer,
|
existsPlayer,
|
||||||
existsNpc,
|
existsNpc,
|
||||||
existsEntity,
|
existsEntity,
|
||||||
getAttack,
|
|
||||||
getPlayer,
|
getPlayer,
|
||||||
getPlayerByName,
|
getPlayerByName,
|
||||||
getEntityByName,
|
getEntityByName,
|
||||||
|
|
|
||||||
|
|
@ -1,160 +0,0 @@
|
||||||
import type { MatrixClient } from "matrix-js-sdk";
|
|
||||||
import type {
|
|
||||||
Attack,
|
|
||||||
IEntity,
|
|
||||||
INPC,
|
|
||||||
IPlayer,
|
|
||||||
TFullNPC,
|
|
||||||
} from "./structures/entities.js";
|
|
||||||
import { locationFarlands } from "./structures/locations.js";
|
|
||||||
import { getAttack, getMaxHealth, getNpcData, isPlayer } from "./entity.js";
|
|
||||||
import { sleep } from "matrix-js-sdk/lib/utils.js";
|
|
||||||
|
|
||||||
const fightEntity = async (
|
|
||||||
client: MatrixClient,
|
|
||||||
roomId: string,
|
|
||||||
attacker: IPlayer | TFullNPC,
|
|
||||||
defender: IPlayer | TFullNPC,
|
|
||||||
) => {
|
|
||||||
let attackerAttacks: Attack[] = [];
|
|
||||||
let attackerDamage = 0;
|
|
||||||
let attackerDefense = 0;
|
|
||||||
attacker.inventory.items.forEach((item) => {
|
|
||||||
item.attacks.forEach((attack) => {
|
|
||||||
attackerAttacks.push(attack);
|
|
||||||
});
|
|
||||||
item.stats.forEach((stat) => {
|
|
||||||
attackerDamage += stat.damage;
|
|
||||||
attackerDefense += stat.defense;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
attackerDamage *= 1 + attacker.strength / 10;
|
|
||||||
attackerDefense *= 1 + attacker.strength / 10;
|
|
||||||
|
|
||||||
let defenderAttacks: Attack[] = [];
|
|
||||||
let defenderDamage = 0;
|
|
||||||
let defenderDefense = 0;
|
|
||||||
defender.inventory.items.forEach((item) => {
|
|
||||||
item.attacks.forEach((attack) => {
|
|
||||||
defenderAttacks.push(attack);
|
|
||||||
});
|
|
||||||
item.stats.forEach((stat) => {
|
|
||||||
defenderDamage += stat.damage;
|
|
||||||
defenderDefense += stat.defense;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
defenderDamage *= 1 + defender.strength / 10;
|
|
||||||
defenderDefense *= 1 + defender.strength / 10;
|
|
||||||
|
|
||||||
let winner: IPlayer | TFullNPC | undefined = undefined;
|
|
||||||
let loser: IPlayer | TFullNPC | undefined = undefined;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
await sleep(5000);
|
|
||||||
|
|
||||||
const attackerWon = fightRound(
|
|
||||||
client,
|
|
||||||
roomId,
|
|
||||||
attackerAttacks,
|
|
||||||
attackerDamage,
|
|
||||||
defender,
|
|
||||||
defenderDefense,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (attackerWon) {
|
|
||||||
winner = attacker;
|
|
||||||
loser = defender;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const defenderWon = fightRound(
|
|
||||||
client,
|
|
||||||
roomId,
|
|
||||||
defenderAttacks,
|
|
||||||
defenderDamage,
|
|
||||||
attacker,
|
|
||||||
attackerDefense,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (defenderWon) {
|
|
||||||
winner = defender;
|
|
||||||
loser = attacker;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
client.sendTextMessage(
|
|
||||||
roomId,
|
|
||||||
`${attacker.name} has won the fight against ${defender.name}!`,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isPlayer(loser)) {
|
|
||||||
respawnPlayer(client, roomId, loser as IPlayer);
|
|
||||||
} else {
|
|
||||||
const npcData = getNpcData((loser as INPC).id);
|
|
||||||
npcData.dead = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fightRound = (
|
|
||||||
client: MatrixClient,
|
|
||||||
roomId: string,
|
|
||||||
attackerAttacks: Attack[],
|
|
||||||
attackerDamage: number,
|
|
||||||
defender: IPlayer | TFullNPC,
|
|
||||||
defenderDefense: number,
|
|
||||||
): boolean => {
|
|
||||||
const attackerAttack =
|
|
||||||
attackerAttacks[Math.floor(Math.random() * attackerAttacks.length)];
|
|
||||||
|
|
||||||
if (!attackerAttack) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const attackerAttackInfo = getAttack(attackerAttack);
|
|
||||||
|
|
||||||
const attackerAttackDamage =
|
|
||||||
attackerDamage *
|
|
||||||
attackerAttackInfo.damageMultiplier *
|
|
||||||
(0.5 + Math.random() * 0.5);
|
|
||||||
|
|
||||||
defender.health -= attackerAttackDamage;
|
|
||||||
|
|
||||||
if (defender.health <= 0) {
|
|
||||||
const msg = getRandomAttackMessage(
|
|
||||||
defender.health <= -getMaxHealth(defender)
|
|
||||||
? attackerAttackInfo.messagesOverpower
|
|
||||||
: attackerAttackInfo.messagesDead,
|
|
||||||
);
|
|
||||||
client.sendTextMessage(roomId, msg);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const msg = getRandomAttackMessage(attackerAttackInfo.messages);
|
|
||||||
client.sendTextMessage(roomId, msg);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const respawnPlayer = (
|
|
||||||
client: MatrixClient,
|
|
||||||
roomId: string,
|
|
||||||
player: IPlayer,
|
|
||||||
) => {
|
|
||||||
const defaultRespawn = locationFarlands;
|
|
||||||
|
|
||||||
player.location = defaultRespawn.id;
|
|
||||||
player.health = getMaxHealth(player);
|
|
||||||
|
|
||||||
client.sendTextMessage(
|
|
||||||
roomId,
|
|
||||||
`${player.name} has been respawned in ${defaultRespawn.name}`,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getRandomAttackMessage = (messages: string[]): string => {
|
|
||||||
return messages[Math.floor(Math.random() * messages.length)] ?? "";
|
|
||||||
};
|
|
||||||
|
|
||||||
export { fightEntity, fightRound, respawnPlayer, getRandomAttackMessage };
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { Location } from "./locations.js";
|
import { Location } from "./locations.js";
|
||||||
import { Race } from "./races.js";
|
import { Race } from "./races.js";
|
||||||
import type { IInventory } from "../types.js";
|
import type { IInventory } from "../types.js";
|
||||||
import { DamageType } from "./items.js";
|
|
||||||
|
|
||||||
export interface IEntity {
|
export interface IEntity {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -20,11 +19,9 @@ export interface IEntity {
|
||||||
stealth: number;
|
stealth: number;
|
||||||
charisma: number;
|
charisma: number;
|
||||||
lockpicking: number;
|
lockpicking: number;
|
||||||
attacks: Attack[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IPlayer extends IEntity {
|
export interface IPlayer extends IEntity {
|
||||||
isPlayer: true;
|
|
||||||
health: number;
|
health: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,120 +49,6 @@ export enum NpcType {
|
||||||
AGGRESIVE = "AGGRESIVE",
|
AGGRESIVE = "AGGRESIVE",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IAttack {
|
|
||||||
id: Attack;
|
|
||||||
damageType: DamageType;
|
|
||||||
damageMultiplier: number;
|
|
||||||
messages: string[];
|
|
||||||
messagesDead: string[];
|
|
||||||
messagesOverpower: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum Attack {
|
|
||||||
PUNCH = "PUNCH",
|
|
||||||
CUT = "CUT",
|
|
||||||
STAB = "STAB",
|
|
||||||
KICK = "KICK",
|
|
||||||
STOMP = "STOMP",
|
|
||||||
SIT = "SIT",
|
|
||||||
RIP = "RIP",
|
|
||||||
}
|
|
||||||
|
|
||||||
export const attackPunch: IAttack = {
|
|
||||||
id: Attack.PUNCH,
|
|
||||||
damageType: DamageType.PHYSICAL,
|
|
||||||
damageMultiplier: 0.5,
|
|
||||||
messages: ["ATTACKER punches DEFENDER"],
|
|
||||||
messagesDead: [
|
|
||||||
"ATTACKER punches DEFENDER to death",
|
|
||||||
"ATTACKER beats DEFENDER to death",
|
|
||||||
],
|
|
||||||
messagesOverpower: ["ATTACKER punches DEFENDER into mush"],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const attackCut: IAttack = {
|
|
||||||
id: Attack.CUT,
|
|
||||||
damageType: DamageType.PHYSICAL,
|
|
||||||
damageMultiplier: 1,
|
|
||||||
messages: ["ATTACKER cuts DEFENDER"],
|
|
||||||
messagesDead: [
|
|
||||||
"ATTACKER cuts DEFENDER in half",
|
|
||||||
"ATTACKER cuts DEFENDER's head off",
|
|
||||||
"ATTACKER cuts DEFENDER, instantly killing them",
|
|
||||||
],
|
|
||||||
messagesOverpower: ["ATTACKER cuts DEFENDER in half"],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const attackStab: IAttack = {
|
|
||||||
id: Attack.STAB,
|
|
||||||
damageType: DamageType.PHYSICAL,
|
|
||||||
damageMultiplier: 1.5,
|
|
||||||
messages: ["ATTACKER stabs DEFENDER"],
|
|
||||||
messagesDead: [
|
|
||||||
"ATTACKER stabs DEFENDER to death",
|
|
||||||
"ATTACKER stabs DEFENDER, instantly killing them",
|
|
||||||
],
|
|
||||||
messagesOverpower: ["ATTACKER stabs DEFENDER, instantly killing them"],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const attackKick: IAttack = {
|
|
||||||
id: Attack.KICK,
|
|
||||||
damageType: DamageType.PHYSICAL,
|
|
||||||
damageMultiplier: 0.75,
|
|
||||||
messages: [
|
|
||||||
"ATTACKER kick DEFENDER",
|
|
||||||
"ATTACKER kick DEFENDER in their face",
|
|
||||||
],
|
|
||||||
messagesDead: ["ATTACKER kicks DEFENDER to death"],
|
|
||||||
messagesOverpower: ["ATTACKER kicks DEFENDER into mush"],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const attackStomp: IAttack = {
|
|
||||||
id: Attack.STOMP,
|
|
||||||
damageType: DamageType.PHYSICAL,
|
|
||||||
damageMultiplier: 0.75,
|
|
||||||
messages: [
|
|
||||||
"ATTACKER stomps DEFENDER",
|
|
||||||
"ATTACKER stomps DEFENDER in their face",
|
|
||||||
],
|
|
||||||
messagesDead: ["ATTACKER stomps DEFENDER to death"],
|
|
||||||
messagesOverpower: [
|
|
||||||
"ATTACKER stomps DEFENDER into mush",
|
|
||||||
"DEFENDER explodes after ATTACKER stomps them into bloody mush",
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const attackSit: IAttack = {
|
|
||||||
id: Attack.SIT,
|
|
||||||
damageType: DamageType.PHYSICAL,
|
|
||||||
damageMultiplier: 0.65,
|
|
||||||
messages: ["ATTACKER sits on DEFENDER"],
|
|
||||||
messagesDead: ["ATTACKER sits on DEFENDER and crushes them to death"],
|
|
||||||
messagesOverpower: [
|
|
||||||
"ATTACKER sits on DEFENDER and crushes them into mush and goo",
|
|
||||||
"DEFENDER explodes after ATTACKER sits on them with their full weight",
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const attackRip: IAttack = {
|
|
||||||
id: Attack.RIP,
|
|
||||||
damageType: DamageType.PHYSICAL,
|
|
||||||
damageMultiplier: 0.3,
|
|
||||||
messages: ["ATTACKER tries to rip DEFENDER's limbs off"],
|
|
||||||
messagesDead: ["ATTACKER rips DEFENDER's head off"],
|
|
||||||
messagesOverpower: ["ATTACKER effortlessly rips DEFENDER's body in half"],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const attacks = [
|
|
||||||
attackPunch,
|
|
||||||
attackCut,
|
|
||||||
attackStab,
|
|
||||||
attackKick,
|
|
||||||
attackStomp,
|
|
||||||
attackSit,
|
|
||||||
attackRip,
|
|
||||||
];
|
|
||||||
|
|
||||||
export const npcBecky: INPC = {
|
export const npcBecky: INPC = {
|
||||||
id: NPC.BECKY,
|
id: NPC.BECKY,
|
||||||
name: "Becky",
|
name: "Becky",
|
||||||
|
|
@ -187,7 +70,6 @@ export const npcBecky: INPC = {
|
||||||
charisma: 5,
|
charisma: 5,
|
||||||
lockpicking: 50,
|
lockpicking: 50,
|
||||||
type: NpcType.QUEST_GIVER,
|
type: NpcType.QUEST_GIVER,
|
||||||
attacks: [Attack.KICK, Attack.STOMP, Attack.SIT, Attack.RIP],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const npcTato: INPC = {
|
export const npcTato: INPC = {
|
||||||
|
|
@ -211,7 +93,6 @@ export const npcTato: INPC = {
|
||||||
charisma: 20,
|
charisma: 20,
|
||||||
lockpicking: 0,
|
lockpicking: 0,
|
||||||
type: NpcType.QUEST_GIVER,
|
type: NpcType.QUEST_GIVER,
|
||||||
attacks: [Attack.PUNCH],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const npcs = [npcBecky, npcTato];
|
export const npcs = [npcBecky, npcTato];
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { Attack } from "./entities.js";
|
|
||||||
import { Rarity } from "./rarities.js";
|
import { Rarity } from "./rarities.js";
|
||||||
|
|
||||||
export interface IItem {
|
export interface IItem {
|
||||||
|
|
@ -9,7 +8,6 @@ export interface IItem {
|
||||||
type: ItemType;
|
type: ItemType;
|
||||||
rarity: Rarity;
|
rarity: Rarity;
|
||||||
stats: IStat[];
|
stats: IStat[];
|
||||||
attacks: Attack[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IStat {
|
export interface IStat {
|
||||||
|
|
@ -58,5 +56,4 @@ export const itemShortsword: IItem = {
|
||||||
defense: 0,
|
defense: 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
attacks: [Attack.CUT, Attack.STAB],
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue