+import { server } from '../../lib/server';
+import { Static, Type } from '@sinclair/typebox';
+import { prisma } from '../../lib/db';
+import { ForbiddenError } from '../../lib/http-errors';
+import {Fight, Player} from '@prisma/client';
+import {maxExp, maxHp} from '../../formulas';
+
+
+const FightRoundInput = Type.Object({
+ params: Type.Object({
+ playerId: Type.String(),
+ fightId: Type.String()
+ }),
+ body: Type.Object({
+ action: Type.String()
+ })
+});
+
+type FightRoundInputType = Static<typeof FightRoundInput>;
+
+export type RoundData = {
+ attacker: string;
+ defender: string;
+ damage: number;
+}
+
+export type FightRoundOutput = {
+ player: Player,
+ monster: Fight,
+ winner: 'monster' | 'player' | 'flee',
+ roundData: RoundData[],
+ reward: any
+}
+
+function round(player: Player, monster: Fight): FightRoundOutput {
+
+ const roundData: RoundData[] = [];
+ if(player.stamina <= 0 || player.hp <= 0) {
+ return {
+ player: player,
+ monster: monster,
+ winner: 'monster',
+ roundData: roundData,
+ reward: {}
+ };
+ }
+
+ const first = player.woosh >= monster.woosh ? player : monster;
+ const second = player.woosh >= monster.woosh ? monster : player;
+
+
+ while(first.hp > 0 && second.hp > 0) {
+ // eventually these will include weapon and skill damage
+ const firstDamage = first.pow;
+ const secondDamage = second.pow;
+
+ second.hp -= firstDamage;
+ roundData.push({
+ attacker: first.id,
+ defender: second.id,
+ damage: firstDamage
+ });
+
+ if(second.hp > 0) {
+ first.hp -= secondDamage;
+ roundData.push({
+ attacker: second.id,
+ defender: first.id,
+ damage: secondDamage
+ })
+ }
+ }
+
+
+ // now we figure out who won!
+ if(player.hp <= 0) {
+ // the player lost
+ player.hp = 0;
+
+ return {
+ player: player,
+ monster: monster,
+ winner: 'monster',
+ roundData: roundData,
+ reward: {}
+ }
+ }
+ else {
+ return {
+ player: player,
+ monster: monster,
+ winner: 'player',
+ roundData: roundData,
+ reward: {
+ exp: monster.exp,
+ currency: monster.currency
+ }
+ }
+ }
+
+}
+
+export const fightRound = server.post<FightRoundInputType, FightRoundOutput>('/v1/accounts/:playerId/fight/:fightId/round', {
+ schema: FightRoundInput,
+ authenicated: true
+}, async req => {
+
+ const [fight, player] = await prisma.$transaction([
+ prisma.fight.findUnique({
+ where: {
+ id: req.params.fightId
+ }
+ }),
+ prisma.player.findUnique({
+ where: {
+ id: req.params.playerId
+ }
+ })
+ ]);
+
+ if(!player) {
+ throw new ForbiddenError('Not a valid fight');
+ }
+
+ if(!fight) {
+ throw new ForbiddenError('Not a real fight');
+ }
+
+ if(fight.playerId !== req.params.playerId) {
+ throw new ForbiddenError('Not a real fight');
+ }
+
+ if(req.body.action === 'Flee') {
+ await prisma.fight.delete({
+ where: {
+ id: req.params.fightId
+ }
+ });
+
+ return {
+ player: player,
+ monster: fight,
+ winner: 'flee',
+ reward: {},
+ roundData: []
+ }
+ }
+
+ if(req.body.action === 'Fight') {
+ const output = round(player, fight);
+ if(output.winner === 'monster') {
+ await prisma.$transaction([
+ prisma.fight.delete({
+ where: {
+ id: fight.id
+ }
+ }),
+ prisma.player.update({
+ where: {
+ id: player.id
+ },
+ data: {
+ hp: 0
+ }
+ })
+ ]);
+ }
+ else if(output.winner === 'player') {
+ player.currency += fight.currency;
+ player.exp += fight.exp;
+ while(player.exp >= maxExp(player.level)) {
+ player.exp -= maxExp(player.level);
+ player.level++;
+ player.statPoints++;
+ player.hp = maxHp(player.level, player.zest);
+ }
+
+ await prisma.$transaction([
+ prisma.fight.delete({
+ where: {
+ id: fight.id
+ }
+ }),
+ prisma.player.update({
+ where: {
+ id: player.id
+ },
+ data: {
+ hp: player.hp,
+ exp: player.exp,
+ level: player.level,
+ currency: player.currency,
+ stamina: player.stamina--,
+ statPoints: player.statPoints
+ }
+ })
+ ]);
+
+ // update the player with the new data
+ output.player = player;
+ }
+
+ return output;
+ }
+
+ throw new ForbiddenError('You can not do that in a fight');
+
+});