1 import { server } from '../../lib/server';
2 import { Static, Type } from '@sinclair/typebox';
3 import { prisma } from '../../lib/db';
4 import { ForbiddenError } from '../../lib/http-errors';
5 import {Fight, LootTable, Player, WorldDrop} from '@prisma/client';
6 import {maxExp, maxHp} from '../../formulas';
7 import {weighted} from '../../lib/weighted';
9 const FightRoundInput = Type.Object({
11 playerId: Type.String(),
12 fightId: Type.String()
19 type FightRoundInputType = Static<typeof FightRoundInput>;
21 export type RoundData = {
27 export type FightRoundOutput = {
30 winner: 'monster' | 'player' | 'flee',
31 roundData: RoundData[],
42 function round(player: Player, monster: Fight): FightRoundOutput {
44 const roundData: RoundData[] = [];
45 if(player.stamina <= 0 || player.hp <= 0) {
55 const first = player.woosh >= monster.woosh ? player : monster;
56 const second = player.woosh >= monster.woosh ? monster : player;
59 while(first.hp > 0 && second.hp > 0) {
60 // eventually these will include weapon and skill damage
61 const firstDamage = first.pow;
62 const secondDamage = second.pow;
64 second.hp -= firstDamage;
72 first.hp -= secondDamage;
82 // now we figure out who won!
100 roundData: roundData,
103 currency: monster.currency
110 export const fightRound = server.post<FightRoundInputType, FightRoundOutput>('/v1/accounts/:playerId/fight/:fightId/round', {
111 schema: FightRoundInput,
115 const [fight, player] = await prisma.$transaction([
116 prisma.fight.findUnique({
118 id: req.params.fightId
121 prisma.player.findUnique({
123 id: req.params.playerId
132 throw new ForbiddenError('Not a valid fight');
136 throw new ForbiddenError('Not a real fight');
139 if(fight.playerId !== req.params.playerId) {
140 throw new ForbiddenError('Not a real fight');
143 if(req.body.action === 'Flee') {
144 await prisma.fight.delete({
146 id: req.params.fightId
159 if(req.body.action === 'Fight') {
160 const output = round(player, fight);
161 if(output.winner === 'monster') {
162 await prisma.$transaction([
163 prisma.fight.delete({
168 prisma.player.update({
178 else if(output.winner === 'player') {
179 player.currency += fight.currency;
180 player.exp += fight.exp;
181 while(player.exp >= maxExp(player.level)) {
182 player.exp -= maxExp(player.level);
185 player.hp = maxHp(player.level, player.zest);
188 // we should figure out if you got an item!
190 const [monster, ,] = await prisma.$transaction([
191 prisma.monsterBiome.findUnique({
194 monsterId: fight.monsterId,
195 biome: player.zoneBiome.biome
206 prisma.fight.delete({
211 prisma.player.update({
219 currency: player.currency,
220 stamina: player.stamina--,
221 statPoints: player.statPoints
226 // since the player won, we can give them an item if they've earned it
227 if(monster && monster.lootTable.length > 0) {
228 const item = weighted(monster.lootTable, (i) => i.dropRate.toNumber());
231 // we can give the player this item, so lets place it in the "world-drop-table".
232 // if a player doesn't pick it up, it will remain in the world drop table for
233 // 24 hours.. after that it's gone forever.
234 // if the player does pick it up then it's moved to their inventory
235 const worldDrop = await prisma.worldDrop.create({
238 droppedById: player.id
242 output.reward['worldDrop'] = {
249 // update the player with the new data
250 output.player = player;
256 throw new ForbiddenError('You can not do that in a fight');