diff options
| -rw-r--r-- | client/dist/assets/demo-videos/recording.mpg | bin | 0 -> 18624512 bytes | |||
| -rw-r--r-- | client/dist/assets/pokemon/RGB_Red_Back.webp | bin | 0 -> 242 bytes | |||
| -rw-r--r-- | client/dist/assets/pokemon/Spr_Y_Brock.webp | bin | 0 -> 336 bytes | |||
| -rw-r--r-- | client/dist/assets/pokemon/opponentFront.png | bin | 0 -> 854 bytes | |||
| -rw-r--r-- | client/dist/assets/pokemon/playerBack.png | bin | 0 -> 651 bytes | |||
| -rw-r--r-- | client/src/akkamon/client/Client.ts | 4 | ||||
| -rw-r--r-- | client/src/akkamon/client/IncomingEvents.ts | 22 | ||||
| -rw-r--r-- | client/src/akkamon/render/BattleEngine.ts | 51 | ||||
| -rw-r--r-- | client/src/akkamon/render/battleUI.ts | 230 | ||||
| -rw-r--r-- | client/src/akkamon/scenes/BattleScene.ts | 71 | ||||
| -rw-r--r-- | client/src/akkamon/scenes/BootScene.ts | 4 | ||||
| -rw-r--r-- | client/src/akkamon/scenes/UIElement.ts | 9 | ||||
| -rw-r--r-- | domain/src/main/java/akkamon/domain/model/akkamon/MonStats.java | 30 | ||||
| -rw-r--r-- | domain/src/main/java/akkamon/domain/model/akkamon/Stat.java | 11 |
14 files changed, 378 insertions, 54 deletions
diff --git a/client/dist/assets/demo-videos/recording.mpg b/client/dist/assets/demo-videos/recording.mpg Binary files differnew file mode 100644 index 0000000..6f3f5ad --- /dev/null +++ b/client/dist/assets/demo-videos/recording.mpg diff --git a/client/dist/assets/pokemon/RGB_Red_Back.webp b/client/dist/assets/pokemon/RGB_Red_Back.webp Binary files differnew file mode 100644 index 0000000..fcf0ecd --- /dev/null +++ b/client/dist/assets/pokemon/RGB_Red_Back.webp diff --git a/client/dist/assets/pokemon/Spr_Y_Brock.webp b/client/dist/assets/pokemon/Spr_Y_Brock.webp Binary files differnew file mode 100644 index 0000000..f12b6a5 --- /dev/null +++ b/client/dist/assets/pokemon/Spr_Y_Brock.webp diff --git a/client/dist/assets/pokemon/opponentFront.png b/client/dist/assets/pokemon/opponentFront.png Binary files differnew file mode 100644 index 0000000..2e2fda3 --- /dev/null +++ b/client/dist/assets/pokemon/opponentFront.png diff --git a/client/dist/assets/pokemon/playerBack.png b/client/dist/assets/pokemon/playerBack.png Binary files differnew file mode 100644 index 0000000..b76851c --- /dev/null +++ b/client/dist/assets/pokemon/playerBack.png diff --git a/client/src/akkamon/client/Client.ts b/client/src/akkamon/client/Client.ts index 3d44dd5..c6b0f24 100644 --- a/client/src/akkamon/client/Client.ts +++ b/client/src/akkamon/client/Client.ts @@ -172,6 +172,10 @@ export class Client implements AkkamonClient this.scene = scene; + if (this.BattleEngine) { + this.BattleEngine = undefined; + } + let playerSprite = new PlayerSprite({ scene: scene, tilePos: new Phaser.Math.Vector2(scene.spawnPointTilePos!), diff --git a/client/src/akkamon/client/IncomingEvents.ts b/client/src/akkamon/client/IncomingEvents.ts index bc2c731..aa515f0 100644 --- a/client/src/akkamon/client/IncomingEvents.ts +++ b/client/src/akkamon/client/IncomingEvents.ts @@ -23,17 +23,23 @@ export type BattleState = { } } +export type Stat = { + base: number, + effective: number +} + export type Mon = { name: string, stats: { - HP: number, - Attack: number, - Defence: number, - SpecialAttack: number, - SpecialDefence: number, - Speed: number, - accuracy: number, - evasion: number, + level: number, + HP: Stat, + Attack: Stat, + Defence: Stat, + SpecialAttack: Stat, + SpecialDefence: Stat, + Speed: Stat, + accuracy: Stat, + evasion: Stat, }, status: {} } diff --git a/client/src/akkamon/render/BattleEngine.ts b/client/src/akkamon/render/BattleEngine.ts index f775046..a6524c4 100644 --- a/client/src/akkamon/render/BattleEngine.ts +++ b/client/src/akkamon/render/BattleEngine.ts @@ -9,6 +9,13 @@ import { import { client } from '../../app'; +function delay(ms: number) { + return new Promise(resolve => { + setTimeout(resolve, ms); + }); +} + + export enum BattleEventType { INTRODUCTION = "INTRODUCTION" } @@ -38,12 +45,19 @@ export class BattleEngine extends AkkamonEngine { private uiEventTriggers = baseQueue<() => void>(); + private playerName: string + private opponentName: string + constructor( message: BattleMessage, ) { super(); this.state = message.state; this.eventsToPlay.pushArray(message.eventsToPlay); + + this.playerName = client.getTrainerID()!.id; + this.opponentName = this.getOpponentName(); + } getOpponentName(): string { @@ -78,20 +92,35 @@ export class BattleEngine extends AkkamonEngine { [BattleEventType.INTRODUCTION](eventToPlay: BattleEvent) { let scene = this.scene!; - let opponentName = this.getOpponentName(); - // scene.showPlayerSprites(); scene.pushEvents( - [ - new DialogueUIEvent(opponentName + " wants to fight!", () => {scene.busy = false;}), - new DialogueUIEvent(opponentName + " sent out " + this.state.teams[opponentName].activeMon.name + "!", () => {scene.busy = false;}) - ], - () => { - scene.pushMenu(new BattleOptions( - scene - )); - }); + [ + new DialogueUIEvent(this.opponentName + " wants to fight!", async () => { + scene.removePlayerSprites(); + scene.busy = false; + }), + new DialogueUIEvent(this.opponentName + " sent out " + this.state.teams[this.opponentName].activeMon.name + "!", async () => { + scene.showOpponentMonInterface(this.opponentName, this.state.teams[this.opponentName].activeMon); + scene.busy = false; + }), + new DialogueUIEvent("Go! " + this.state.teams[this.playerName].activeMon.name + "!", async () => { + scene.showPlayerMonInterface(this.playerName, this.state.teams[this.playerName].activeMon); + scene.busy = false; + }), + ], + () => { + scene.clearDialogue(); + scene.pushMenu(new BattleOptions( + scene + )); + }, + new InstantUIEvent(async () => { + scene.showPlayerSpritesAndBalls(); + await delay(1000); + scene.busy = false; + }) + ); } diff --git a/client/src/akkamon/render/battleUI.ts b/client/src/akkamon/render/battleUI.ts index bc5d876..2f204e3 100644 --- a/client/src/akkamon/render/battleUI.ts +++ b/client/src/akkamon/render/battleUI.ts @@ -1,3 +1,4 @@ +import type { Mon, Stat } from "../client/IncomingEvents"; import { baseQueue, Queue } from "../DataWrappers"; import type BattleScene from "../scenes/BattleScene"; import { AkkamonMenu, Dialogue, MenuText, Picker } from "../scenes/UIElement"; @@ -38,10 +39,11 @@ export class BattleDialogue extends Dialogue { this.confirmNextBehavior(); } else { console.log("currentlyWriting removing timed printer!"); + this.timeEvent!.destroy(); this.timeEvent!.remove(); + this.timeCallback!(); this.displayedText.text = this.currentlyWriting!; this.currentlyWriting = undefined; - this.timeCallback!(); } } @@ -55,35 +57,46 @@ export class BattleDialogue extends Dialogue { this.endBehaviour(); } else { console.log("Pushing ui trigger to client update handling!"); - let trigger = () => { - console.log("The trigger is fired!"); - this.playNextEvent(); - } - console.log(trigger); + this.pushTriggerWhenNotBusy(() => { this.playNextEvent(); }); + } + } - // this.scene!.pushMen this.playNextEvent(); - this.battleScene!.pushUIEvent( - trigger - ); + pushTriggerWhenNotBusy(trigger: () => void) { + // let trigger = () => { + // console.log("The trigger is fired!"); + // callback(); + // } + // console.log(trigger); - } + // this.scene!.pushMen this.playNextEvent(); + this.battleScene!.pushUIEvent( + trigger + ); } pushEvents(events: DialogueUIEvent[], endBehaviour: () => void) { this.endBehaviour = endBehaviour; this.eventQueue.pushArray(events); - this.playNextEvent(); + this.pushTriggerWhenNotBusy(() => { this.playNextEvent(); } ); } playNextEvent() { + this.battleScene.busy = true; this.displayedText.text = ''; if (!this.eventQueue.isEmpty()) { let uiEvent = this.eventQueue.pop()!; - this.timeEvent = this.typewriteText(uiEvent.dialogue, uiEvent.callback); + console.log("Playing event now:"); + console.log(uiEvent); + this.timeEvent = this.typewriteText(uiEvent.dialogue, uiEvent.callback, () => {this.currentlyWriting = undefined;}); this.timeCallback = uiEvent.callback; + this.currentlyWriting = uiEvent.dialogue; } } + clearText() { + this.displayedText.text = ''; + } + } enum BattleOptionsButtons { @@ -118,7 +131,7 @@ export class BattleOptions extends Phaser.GameObjects.Image implements AkkamonMe this.paddingX = 120; this.paddingY = 60; - this.buttonSpacing = 30; + this.buttonSpacing = 45; this.battleScene = scene; this.group = new Phaser.GameObjects.Group(scene); @@ -285,3 +298,192 @@ export class BattleOptions extends Phaser.GameObjects.Image implements AkkamonMe setMenuVisible() { } } + +class HPBar extends Phaser.GameObjects.Rectangle { + constructor( + scene: BattleScene, + group: Phaser.GameObjects.Group, + groupDepth: number, + pos: {x: number, y: number}, + size: {width: number, height: number}, + ori: {x: number, y: number}, + ) { + super(scene, pos.x, pos.y, size.width, size.height, 0x00ff00); + this.setOrigin(ori.x, ori.y); + scene.add.existing(this); + group.add(this); + this.setDepth(groupDepth); + } + + setHP(hp: Stat) { + } +} + +export class MonInterface extends Phaser.GameObjects.Image { + + battleScene: BattleScene; + group: Phaser.GameObjects.Group; + groupDepth: number; + + mon: Mon + + monName?: MenuText; + level?: MenuText; + + hpBar?: HPBar; + baseHP?: MenuText; + effectiveHP?: MenuText; + + constructor( + scene: BattleScene, + mon: Mon, + imageKey: string, + pos: {x: number, y: number}, + ori: {x: number, y: number}, + aspect: number + ) { + super( + scene, + pos.x,pos.y, + imageKey + ); + let width = scene.cameras.main.displayWidth / 2.2; + this.setDisplaySize(width, aspect * width); + this.setOrigin(ori.x, ori.y); + scene.add.existing(this); + this.group = new Phaser.GameObjects.Group(scene); + this.groupDepth = 10; + this.group.setDepth(this.groupDepth); + this.battleScene = scene; + + this.mon = mon; + } + +} + +export class PlayerInterface extends MonInterface { + constructor(scene: BattleScene, + mon: Mon, + imageKey: string, + pos: {x: number, y: number}, + ori: {x: number, y: number}, + aspect: number) { + super(scene, mon, imageKey, pos, ori, aspect); + this.setMonName(mon.name); + this.setLevel(mon.stats.level); + this.setHP(mon.stats.HP); + this.setSprite(mon.name); + } + + setSprite(name: string) { + console.log("Setting back sprite for " + name); + let image = this.scene.add.image(this.x - this.displayWidth - 200, this.y, name.toLowerCase() + "-back") + .setDisplaySize(200,200) + .setOrigin(0.5, 0.5) + + this.group.add(image); + } + + setMonName(name: string) { + this.monName = new MenuText( + this.battleScene, + this.group, + this.groupDepth, + this.x - this.displayWidth + 20, this.y - this.displayHeight / 2 - 20, + name + ); + } + + setLevel(level: number) { + this.level = new MenuText( + this.battleScene, + this.group, + this.groupDepth, + this.x - this.displayWidth * (1 - 490 / 790), this.y - this.displayHeight / 2, + level.toString() + ); + } + + setHP(hp: Stat) { + this.baseHP = new MenuText( + this.battleScene, + this.group, + this.groupDepth, + this.x - this.displayWidth * (1 - 500 / 790), this.y + this.displayHeight * ((220 - 178) / 314), + hp.base.toString() + ); + + this.effectiveHP = new MenuText( + this.battleScene, + this.group, + this.groupDepth, + this.x - this.displayWidth * (1 - 320 / 790), this.y + this.displayHeight * ((220 - 178) / 314), + hp.base.toString() + ); + + this.hpBar = new HPBar( + this.battleScene, + this.group, + this.groupDepth, + {x: this.x - this.displayWidth * (1 - 245 / 790), y: this.y - this.displayHeight / 2 * (103 / 314)}, + {width: this.displayWidth * ((731 - 237) / 790), height: this.displayHeight * ((129 - 105) / 314)}, + {x: 0, y: 0} + ); + } +} + +export class OpponentInterface extends MonInterface { + constructor(scene: BattleScene, + mon: Mon, + imageKey: string, + pos: {x: number, y: number}, + ori: {x: number, y: number}, + aspect: number) { + super(scene, mon, imageKey, pos, ori, aspect); + this.setMonName(mon.name); + this.setLevel(mon.stats.level); + this.setHP(mon.stats.HP); + this.setSprite(mon.name); + } + + setSprite(name: string) { + console.log("Setting front sprite for " + name); + let image = this.scene.add.image(this.x + this.displayWidth + 200, this.y + this.displayHeight / 2 + 60, name.toLowerCase() + "-front") + .setDisplaySize(200,200) + .setOrigin(0.5, 0.5) + + this.group.add(image); + } + + setMonName(name: string) { + this.monName = new MenuText( + this.battleScene, + this.group, + this.groupDepth, + this.x + 10, this.y - 20, + name + ); + } + + setLevel(level: number) { + this.level = new MenuText( + this.battleScene, + this.group, + this.groupDepth, + this.x + this.displayWidth * (289 / 788), this.y + this.displayHeight * (24 / 223) - 10, + level.toString() + ); + } + + setHP(hp: Stat) { + this.hpBar = new HPBar( + this.battleScene, + this.group, + this.groupDepth, + {x: this.x + this.displayWidth * (217 / 788), y: this.y + this.displayHeight * (106 / 223)}, + {width: this.displayWidth * ((705.14 - 215.66) / 788), height: this.displayHeight * ((130.5 - 104.2) / 223)}, + {x: 0, y: 0} + ); + } + +} diff --git a/client/src/akkamon/scenes/BattleScene.ts b/client/src/akkamon/scenes/BattleScene.ts index c2746d8..8ff430f 100644 --- a/client/src/akkamon/scenes/BattleScene.ts +++ b/client/src/akkamon/scenes/BattleScene.ts @@ -1,8 +1,9 @@ import { client } from '../../app'; +import type { Mon } from '../client/IncomingEvents'; import { baseStack, Queue, queueFromArray } from '../DataWrappers'; import type { BasePhaserScene, GConstructor } from '../PhaserTypes'; -import type { BattleUIEvent, DialogueUIEvent } from '../render/BattleEngine'; -import { BattleDialogue } from '../render/battleUI'; +import type { BattleUIEvent, DialogueUIEvent, InstantUIEvent } from '../render/BattleEngine'; +import { BattleDialogue, MonInterface, OpponentInterface, PlayerInterface } from '../render/battleUI'; import { Direction } from '../render/Direction'; import type { AkkamonMenu, Dialogue, Menu, MenuText, Picker } from './UIElement'; import type { WorldScene } from './WorldScene'; @@ -14,6 +15,9 @@ export default class BattleScene extends Phaser.Scene { dialogueBox?: BattleDialogue; busy = false; + playerSprites: Phaser.GameObjects.Image[] = []; + monInterfaces: Map<string, MonInterface> = new Map(); + constructor() { super('BattleScene') } @@ -61,10 +65,12 @@ export default class BattleScene extends Phaser.Scene { client.setBattleControls(input, menu); } - pushEvents(events: DialogueUIEvent[], endBehavior: () => void, before?: BattleUIEvent) { + pushEvents(events: DialogueUIEvent[], endBehavior: () => void, before?: InstantUIEvent) { this.toggleVisible(false); this.menuTakesUIControl(this.input, this.dialogueBox!); if (before) { + this.busy = true; + before.callback(); } this.dialogueBox!.pushEvents( events, @@ -85,4 +91,63 @@ export default class BattleScene extends Phaser.Scene { isBusy() { return this.busy; } + + showPlayerSpritesAndBalls() { + this.showPlayerSprites(); + this.showBalls(); + } + + showPlayerSprites() { + let playerBack = this.add.image(0,0, "playerBack") + .setOrigin(0, 1) + .setPosition(0 + 100, (1 - 0.28) * this.cameras.main.displayHeight - 20) + .setDisplaySize(200, 200) + + let opponentFront = this.add.image(0,0, "opponentFront") + .setOrigin(1, 0.5) + .setPosition(this.cameras.main.displayWidth - 100, 150) + .setDisplaySize(200, 200) + + this.playerSprites.push(playerBack); + this.playerSprites.push(opponentFront); + } + + showBalls() { + + } + + removePlayerSprites() { + for (let sprite of this.playerSprites) { + sprite.destroy(); + } + } + + showPlayerMonInterface(trainer: string, monObj: Mon) { + this.monInterfaces.set(trainer, + new PlayerInterface( + this, + monObj, + "hp-player", + {x: this.cameras.main.displayWidth - 20, y: (1 - 0.28) * this.cameras.main.displayHeight - 400 * 0.4 / 1.5}, + {x: 1, y: 0.5}, + 0.4 + )); + } + + showOpponentMonInterface(trainer: string, monObj: Mon) { + this.monInterfaces.set(trainer, + new OpponentInterface( + this, + monObj, + "hp-opponent", + {x: 30, y: 30}, + {x: 0, y: 0}, + 0.28 + )); + + } + + clearDialogue() { + this.dialogueBox!.clearText(); + } } diff --git a/client/src/akkamon/scenes/BootScene.ts b/client/src/akkamon/scenes/BootScene.ts index d3d8dba..98be8b6 100644 --- a/client/src/akkamon/scenes/BootScene.ts +++ b/client/src/akkamon/scenes/BootScene.ts @@ -31,6 +31,10 @@ function createBootScene<PhaserScene extends BasePhaserScene>(base: PhaserScene, this.load.pack("general-interface", "assets/images/general-ui.json") this.load.pack("battle-interface", "assets/images/battle-ui.json") + + this.load.image("playerBack", "assets/pokemon/playerBack.png") + this.load.image("opponentFront", "assets/pokemon/opponentFront.png") + } create(): void { diff --git a/client/src/akkamon/scenes/UIElement.ts b/client/src/akkamon/scenes/UIElement.ts index 104bb7c..d9447f7 100644 --- a/client/src/akkamon/scenes/UIElement.ts +++ b/client/src/akkamon/scenes/UIElement.ts @@ -435,20 +435,21 @@ export class Dialogue extends Phaser.GameObjects.Image implements AkkamonMenu { } } - typewriteText(text: string, uiEventCallback?: () => void) { + typewriteText(text: string, uiEventCallback?: () => void, endDialogueCallback?: () => void) { const length = text.length let i = 0 - if (uiEventCallback) { + if (uiEventCallback && endDialogueCallback) { let timeEvent = this.scene.time.addEvent({ callback: () => { this.displayedText.text += text[i] ++i if (i === length - 1) { uiEventCallback(); + endDialogueCallback(); } }, repeat: length - 1, - delay: 20 + delay: 50 }) return timeEvent; } else { @@ -458,7 +459,7 @@ export class Dialogue extends Phaser.GameObjects.Image implements AkkamonMenu { ++i }, repeat: length - 1, - delay: 20 + delay: 50 }) return timeEvent; } diff --git a/domain/src/main/java/akkamon/domain/model/akkamon/MonStats.java b/domain/src/main/java/akkamon/domain/model/akkamon/MonStats.java index ee4eee3..2e8ab68 100644 --- a/domain/src/main/java/akkamon/domain/model/akkamon/MonStats.java +++ b/domain/src/main/java/akkamon/domain/model/akkamon/MonStats.java @@ -1,22 +1,24 @@ package akkamon.domain.model.akkamon; public class MonStats { - public final int HP; - public final int Attack; - public final int Defence; - public final int SpecialAttack; - public final int SpecialDefence; - public final int Speed; - public final int accuracy = 1; - public final int evasion = 1; + public final int level; + public final Stat HP; + public final Stat Attack; + public final Stat Defence; + public final Stat SpecialAttack; + public final Stat SpecialDefence; + public final Stat Speed; + public final Stat accuracy = new Stat(1); + public final Stat evasion = new Stat(1); public MonStats(int[] base, int[] evs, int[] ivs, double[] natureMultiplier, int level) { - this.HP = setHP(base[0], evs[0], ivs[0], level); - this.Attack = setStat(base[1], evs[1], ivs[1], natureMultiplier[1], level); - this.Defence = setStat(base[2], evs[2], ivs[2], natureMultiplier[2], level); - this.SpecialAttack = setStat(base[3], evs[3], ivs[3], natureMultiplier[3], level); - this.SpecialDefence = setStat(base[4], evs[4], ivs[4], natureMultiplier[4], level); - this.Speed = setStat(base[5], evs[5], ivs[5], natureMultiplier[5], level); + this.level = level; + this.HP = new Stat(setHP(base[0], evs[0], ivs[0], level)); + this.Attack = new Stat(setStat(base[1], evs[1], ivs[1], natureMultiplier[1], level)); + this.Defence = new Stat(setStat(base[2], evs[2], ivs[2], natureMultiplier[2], level)); + this.SpecialAttack = new Stat(setStat(base[3], evs[3], ivs[3], natureMultiplier[3], level)); + this.SpecialDefence = new Stat(setStat(base[4], evs[4], ivs[4], natureMultiplier[4], level)); + this.Speed = new Stat(setStat(base[5], evs[5], ivs[5], natureMultiplier[5], level)); } private int setStat(int base, int ev, int iv, double nature, int level) { diff --git a/domain/src/main/java/akkamon/domain/model/akkamon/Stat.java b/domain/src/main/java/akkamon/domain/model/akkamon/Stat.java new file mode 100644 index 0000000..92bc76e --- /dev/null +++ b/domain/src/main/java/akkamon/domain/model/akkamon/Stat.java @@ -0,0 +1,11 @@ +package akkamon.domain.model.akkamon; + +public class Stat { + public final int base; + public int effective; + + public Stat(int base) { + this.base = base; + this.effective = base; + } +} |
