exploring/fighting is functional
[sketchy-heroes.git] / src / routes / fight / fight.ts
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, Player} from '@prisma/client';
6 import {maxExp, maxHp} from '../../formulas';
7
8
9 const FightRoundInput = Type.Object({
10   params: Type.Object({
11     playerId: Type.String(),
12     fightId: Type.String()
13   }),
14   body: Type.Object({
15     action: Type.String()
16   })
17 });
18
19 type FightRoundInputType = Static<typeof FightRoundInput>;
20
21 export type RoundData = {
22   attacker: string;
23   defender: string;
24   damage: number;
25 }
26
27 export type FightRoundOutput = {
28   player: Player,
29   monster: Fight,
30   winner: 'monster' | 'player' | 'flee',
31   roundData: RoundData[],
32   reward: any
33 }
34
35 function round(player: Player, monster: Fight): FightRoundOutput {
36
37   const roundData: RoundData[] = [];
38   if(player.stamina <= 0 || player.hp <= 0) {
39     return {
40       player: player,
41       monster: monster,
42       winner: 'monster',
43       roundData: roundData,
44       reward: {}
45     };
46   }
47
48   const first = player.woosh >= monster.woosh ? player : monster;
49   const second = player.woosh >= monster.woosh ? monster : player;
50
51
52   while(first.hp > 0 && second.hp > 0) {
53     // eventually these will include weapon and skill damage
54     const firstDamage = first.pow;
55     const secondDamage = second.pow;
56
57     second.hp -= firstDamage;
58     roundData.push({
59       attacker: first.id,
60       defender: second.id,
61       damage: firstDamage
62     });
63
64     if(second.hp > 0) {
65       first.hp -= secondDamage;
66       roundData.push({
67         attacker: second.id,
68         defender: first.id,
69         damage: secondDamage
70       })
71     }
72   }
73
74
75   // now we figure out who won!
76   if(player.hp <= 0) {
77     // the player lost
78     player.hp = 0;
79
80     return {
81       player: player,
82       monster: monster,
83       winner: 'monster',
84       roundData: roundData,
85       reward: {}
86     }
87   }
88   else {
89     return {
90       player: player,
91       monster: monster,
92       winner: 'player',
93       roundData: roundData,
94       reward: {
95         exp: monster.exp,
96         currency: monster.currency
97       }
98     }
99   }
100
101 }
102
103 export const fightRound = server.post<FightRoundInputType, FightRoundOutput>('/v1/accounts/:playerId/fight/:fightId/round', {
104   schema: FightRoundInput,
105   authenicated: true
106 }, async req => {
107
108   const [fight, player] = await prisma.$transaction([
109     prisma.fight.findUnique({
110       where: {
111         id: req.params.fightId
112       }
113     }),
114     prisma.player.findUnique({
115       where: {
116         id: req.params.playerId
117       }
118     })
119   ]);
120
121   if(!player) {
122     throw new ForbiddenError('Not a valid fight');
123   }
124
125   if(!fight) {
126     throw new ForbiddenError('Not a real fight');
127   }
128
129   if(fight.playerId !== req.params.playerId) {
130     throw new ForbiddenError('Not a real fight');
131   }
132
133   if(req.body.action === 'Flee') {
134     await prisma.fight.delete({
135       where: {
136         id: req.params.fightId
137       }
138     });
139
140     return {
141       player: player,
142       monster: fight,
143       winner: 'flee',
144       reward: {},
145       roundData: []
146     }
147   }
148
149   if(req.body.action === 'Fight') {
150       const output = round(player, fight);
151       if(output.winner === 'monster') {
152         await prisma.$transaction([
153           prisma.fight.delete({
154             where: {
155               id: fight.id
156             }
157           }),
158           prisma.player.update({
159             where: {
160               id: player.id
161             },
162             data: {
163               hp: 0
164             }
165           })
166         ]);
167       }
168       else if(output.winner === 'player') {
169         player.currency += fight.currency;
170         player.exp += fight.exp;
171         while(player.exp >= maxExp(player.level)) {
172           player.exp -= maxExp(player.level);
173           player.level++;
174           player.statPoints++;
175           player.hp = maxHp(player.level, player.zest);
176         }
177
178         await prisma.$transaction([
179           prisma.fight.delete({
180             where: {
181               id: fight.id
182             }
183           }),
184           prisma.player.update({
185             where: {
186               id: player.id
187             },
188             data: {
189               hp: player.hp,
190               exp: player.exp,
191               level: player.level,
192               currency: player.currency,
193               stamina: player.stamina--,
194               statPoints: player.statPoints
195             }
196           })
197         ]);
198
199         // update the player with the new data
200         output.player = player;
201       }
202
203       return output;
204   }
205
206   throw new ForbiddenError('You can not do that in a fight');
207
208 });