1 import {Player} from '@prisma/client';
2 import axios from 'axios';
3 import { Events } from './events';
4 import { LoginOutputType, AccountCreateType, MoveOutputType, FightStartType, FightRoundOutput } from 'src/routes';
5 import {actionLog} from './components';
6 import {$fightButton} from './dom';
8 type ApiResponse<T> = {
9 status: 'ok' | 'error',
14 processingTime: number
19 export class Api extends Events {
21 headers: Record<string, string>;
22 player: Player | null;
24 constructor(root: string) {
28 // we use the headers for auth data
33 setPlayer(player: Player) {
35 this.emit('player', player);
39 async get<T>(endpoint: string, params: Record<string, string> = {}): Promise<ApiResponse<T>> {
40 const res = await axios({
48 if(res.data.status !== 'ok') {
49 throw new Error(res.data.payload.toString());
55 async post<T>(endpoint: string, data: any = {}): Promise<ApiResponse<T>> {
56 const res = await axios({
64 if(res.data.status !== 'ok') {
65 throw new Error(res.data.payload.toString());
71 async signup(username: string, password: string, confirmation: string): Promise<AccountCreateType> {
72 const res = await this.post<AccountCreateType>('/v1/accounts', {
75 confirmation: confirmation
81 async setGameTime(gameTime: number) {
82 const str = gameTime < 10 ? `0${gameTime}` : gameTime.toString();
84 $('body').removeClass().addClass(`sky-gradient-${str}`);
87 async getPlayerInfo() {
89 throw new Error('Not authenticated');
91 const res = await this.get<Player>(`/v1/accounts/${this.player.id}`);
93 this.setPlayer(res.payload);
95 // this also sets the background based on the time!
96 this.setGameTime(res.meta.gameTime);
99 async login(username: string, password: string): Promise<LoginOutputType> {
100 const res = await this.post<LoginOutputType>('/v1/accounts/auth', {
105 this.headers['x-auth-token'] = res.payload.token;
107 this.setPlayer(res.payload.player);
109 this.setGameTime(res.meta.gameTime);
112 setInterval(this.getPlayerInfo.bind(this), 5000);
117 async move(): Promise<MoveOutputType> {
118 if(this.player === null) {
119 throw new Error('Not authenicated');
121 const res = await this.post<MoveOutputType>(`/v1/accounts/${this.player.id}/move`);
123 this.setPlayer(res.payload.player);
125 if(res.payload.generatedMonster !== null) {
126 actionLog(`You see a ${res.payload.generatedMonster.name}`);
127 if(this.player.hp > 0 && this.player.stamina > 0) {
128 $fightButton().prop('disabled', false)
129 .removeClass(['disabled', 'hidden'])
130 .attr('data-fight-id', res.payload.generatedMonster.id);
134 actionLog(res.payload.displayText, true);
135 $fightButton().prop('disabled', true)
136 .addClass(['disabled', 'hidden'])
137 .attr('data-fight-id', 'unset');
143 async increaseStat(stat: string): Promise<Player> {
144 if(this.player === null) {
145 throw new Error('Not authenticated');
147 const res = await this.post<Player>(`/v1/accounts/${this.player.id}/stat`, {
151 this.setPlayer(res.payload);
155 async startFight(fightId: string): Promise<FightStartType> {
156 if(this.player === null) {
157 throw new Error('Not authenticated');
160 const res = await this.post<FightStartType>(`/v1/accounts/${this.player.id}/fight/${fightId}`);
162 if(!res.payload.id) {
163 throw new Error('Invalid fight!');
169 async fight(fightId: string): Promise<FightRoundOutput> {
170 if(this.player === null) {
171 throw new Error('Not authenticated');
174 await this.startFight(fightId);
176 const res = await this.post<FightRoundOutput>(`/v1/accounts/${this.player.id}/fight/${fightId}/round`, {
180 const output = res.payload;
182 this.setPlayer(output.player);
186 const participants = {
187 [output.player.id]: output.player.username,
188 [output.monster.id]: output.monster.name
191 output.roundData.map(round => {
192 const p1 = participants[round.attacker];
193 const p2 = participants[round.defender];
195 const css = round.attacker === output.player.id ? 'text-secondary' : 'text-info';
196 let str = `<span class="${css}">${p1}</span> dealt <span class="text-danger">${round.damage} damage</span> to ${p2}`;
199 }).forEach(a => actionLog(a, false));
201 if(output.winner === 'player') {
202 actionLog(`<span class="text-success">You defeated the ${output.monster.name}</span>`, false);
205 actionLog(`<span class="text-danger">You were defeated by the ${output.monster.name}</span>`, false);
208 Object.keys(output.reward).forEach(rewardType => {
211 actionLog(`You gained <span class="text-info">${output.reward.exp}</span> Exp`, false);
214 actionLog(`You gained <span class="text-warning">${output.reward.currency}</span> Steel`, false);
219 $fightButton().prop('disabled', true)
220 .addClass(['disabled', 'hidden'])
221 .attr('data-fight-id', 'unset');