websocket communication resolved and auto continue done so chat doesnt have to spam 'a' accept.

pull/760/head
hexblit 2024-05-11 18:41:01 -06:00
parent f78187fe96
commit d251a9d516
8 changed files with 196 additions and 30 deletions

View File

@ -1,2 +1,2 @@
VITE_BYPASS_LOGIN=1
VITE_BYPASS_TUTORIAL=0
VITE_BYPASS_TUTORIAL=1

29
package-lock.json generated
View File

@ -10,12 +10,12 @@
"dependencies": {
"@material/material-color-utilities": "^0.2.7",
"crypto-js": "^4.2.0",
"http": "^0.0.1-security",
"i18next": "^23.11.1",
"i18next-browser-languagedetector": "^7.2.1",
"json-stable-stringify": "^1.1.0",
"phaser": "^3.70.0",
"phaser3-rex-plugins": "^1.1.84",
"ws": "^8.17.0"
"phaser3-rex-plugins": "^1.1.84"
},
"devDependencies": {
"@vitest/coverage-istanbul": "^1.4.0",
@ -1235,6 +1235,17 @@
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"dev": true
},
"node_modules/@types/node": {
"version": "20.12.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz",
"integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@ungap/structured-clone": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
@ -2679,6 +2690,11 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
"node_modules/http": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/http/-/http-0.0.1-security.tgz",
"integrity": "sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g=="
},
"node_modules/http-assert": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz",
@ -4554,6 +4570,14 @@
"integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==",
"dev": true
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true,
"optional": true,
"peer": true
},
"node_modules/universalify": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
@ -5864,6 +5888,7 @@
"version": "8.17.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz",
"integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==",
"dev": true,
"engines": {
"node": ">=10.0.0"
},

View File

@ -30,12 +30,12 @@
"dependencies": {
"@material/material-color-utilities": "^0.2.7",
"crypto-js": "^4.2.0",
"http": "^0.0.1-security",
"i18next": "^23.11.1",
"i18next-browser-languagedetector": "^7.2.1",
"json-stable-stringify": "^1.1.0",
"phaser": "^3.70.0",
"phaser3-rex-plugins": "^1.1.84",
"ws": "^8.17.0"
"phaser3-rex-plugins": "^1.1.84"
},
"engines": {
"node": ">=18.0.0"

View File

@ -187,9 +187,9 @@ export default class BattleScene extends SceneBase {
this.phaseQueue = [];
this.phaseQueuePrepend = [];
this.phaseQueuePrependSpliceIndex = -1;
this.nextCommandPhaseQueue = [];
this.nextCommandPhaseQueue = [];
}
loadPokemonAtlas(key: string, atlasPath: string, experimental?: boolean) {
if (experimental === undefined)
experimental = this.experimentalSprites;
@ -1505,6 +1505,7 @@ export default class BattleScene extends SceneBase {
this.populatePhaseQueue();
this.currentPhase = this.phaseQueue.shift();
this.currentPhase.start();
}
overridePhase(phase: Phase): boolean {

View File

@ -1,5 +1,4 @@
import Phaser, {Time} from "phaser";
import WebSocket, {WebSocketServer} from 'ws';
import * as Utils from "./utils";
import {initTouchControls} from './touch-controls';
import pad_generic from "./configs/pad_generic";
@ -7,6 +6,8 @@ import pad_unlicensedSNES from "./configs/pad_unlicensedSNES";
import pad_xbox360 from "./configs/pad_xbox360";
import pad_dualshock from "./configs/pad_dualshock";
import {Button} from "./enums/buttons";
import BattleScene from "./battle-scene";
import { AttemptCapturePhase, EncounterPhase, LearnMovePhase, MessagePhase } from "./phases";
export interface GamepadMapping {
[key: string]: number;
@ -28,7 +29,9 @@ export class InputsController {
private buttonKeys: Phaser.Input.Keyboard.Key[][];
private gamepads: Array<string> = new Array();
private scene: Phaser.Scene;
private wss = new WebSocketServer({ port: 9876});
private ws: WebSocket;
// buttonLock ensures only a single movement key is firing repeated inputs
// (i.e. by holding down a button) at a time
@ -81,17 +84,123 @@ export class InputsController {
this.scene.input.gamepad.on('up', this.gamepadButtonUp, this);
}
// Keyboard
this.setupKeyboardControls();
this.ws = new WebSocket("ws://127.0.0.1:5050/");
this.ws.onopen = this.onOpen.bind(this);
this.ws.onmessage = this.onMessage.bind(this);
this.ws.onerror = this.onError.bind(this);
this.ws.onclose = this.onClose.bind(this);
// setup WebSocket
this.wss.on('connection', function connection(ws: WebSocket) {
ws.on('message', function incoming(message: string) {
console.log('received: %s', message);
}
private onOpen(): void {
console.log("WebSocket connection established");
var getActions = {
"request": "GetEvents",
"id": "pokerogue"
}
var subscribe = {
"request": "Subscribe",
"id": "pokerogue",
"events": {
"raw": [
"ActionCompleted"
]
},
}
// console.log(JSON.stringify(subscribe));
this.ws.send(JSON.stringify(subscribe));
}
private onMessage(event: MessageEvent): void {
var data = JSON.parse(event.data);
// console.log("Received message: ", data.data);
if ( data.data.name == "twitch-plays-input-decide") {
var chatAction = data.data.arguments.chatAction
if (chatAction != "NoOp") {
this.processInputCommad(chatAction);
} else {
});
});
let battleScene = this.scene as BattleScene;
console.log(battleScene);
if (battleScene != null) {
let currentPhase = battleScene.getCurrentPhase();
switch (currentPhase.constructor.name){ // AttemptCapturePhase
case 'ExpPhase':
case 'EvolutionPhase':
case 'ModifierRewardPhase':
case 'LevelUpPhase':
this.processInputCommad('accept')
break;
case 'MessagePhase':
console.log(battleScene);
let messagePhase = currentPhase as MessagePhase;
if(messagePhase.getText().includes("fainted!")
|| messagePhase.getText().includes("fled!")
|| messagePhase.getText().includes("You picked up")) {
this.processInputCommad('accept')
}
break;
case 'LearnMovePhase':
console.log(battleScene);
let learnMovePhase = currentPhase as LearnMovePhase;
if(learnMovePhase.openMovesRemaining > 0) { // only auto continue if move slot is free
this.processInputCommad('accept')
} else {
switch(learnMovePhase.learnMovesStep) {
case 'battle:learnMovePrompt':
case 'battle:learnMoveLimitReached':
case 'battle:learnMoveNotLearned':
case 'battle:learnMoveForgetQuestion':
case 'battle:learnMovePoof':
case 'battle:learnMoveAnd':
case 'battle:learnMoveForgetSuccess':
this.processInputCommad('accept')
break;
}
}
break;
case 'AttemptCapturePhase':
let attemptCapturePhase = currentPhase as AttemptCapturePhase;
switch(attemptCapturePhase.attemptCaptureStep) {
case 'battle:pokemonCaught':
this.processInputCommad('accept')
break;
}
break;
case 'EncounterPhase':
let encounterPhase = currentPhase as EncounterPhase;
switch( encounterPhase.encounterStep) {
case 'battle:encounterMessage':
this.processInputCommad('accept');
break;
}
break;
}
}
}
}
// Process the received message here
}
private onError(error: Event): void {
console.error("WebSocket error: ", error);
}
private onClose(event: CloseEvent): void {
console.log("WebSocket connection closed");
}
loseFocus(): void {
@ -178,10 +287,11 @@ export class InputsController {
this.delLastProcessedMovementTime(buttonUp);
}
}
/*
setupWebSocketControls(): void {
}
*/
setupKeyboardControls(): void {
const keyCodes = Phaser.Input.Keyboard.KeyCodes;
@ -239,6 +349,7 @@ export class InputsController {
}
});
}
processInputCommad(input: string): void {
const command = input.toLowerCase();
@ -247,26 +358,26 @@ export class InputsController {
'down' : Button.DOWN,
'left' : Button.LEFT,
'right' : Button.RIGHT,
'submit' : Button.SUBMIT,
'accept' : Button.SUBMIT,
'select' : Button.ACTION,
'cancel' : Button.CANCEL,
'menu' : Button.MENU,
'stats' : Button.STATS
};
if (commandMapping[command]) {
this.triggerButtonPress(commandMapping[command]);
}
};
this.triggerButtonPress(commandMapping[command]);
}
triggerButtonPress(button: Button): void {
this.events.emit('input_down', {
controller_type: 'command',
button: button,
});
this.setLastProcessedMovementTime(button);
this.events.emit('input_up', {
controller_type: 'command',
button: button,
});
this.setLastProcessedMovementTime(button);
this.delLastProcessedMovementTime(button);
}

View File

@ -646,7 +646,7 @@ export abstract class PartyMemberPokemonPhase extends FieldPhase {
}
}
export abstract class PlayerPartyMemberPokemonPhase extends PartyMemberPokemonPhase {
export abstract class PlayerPartyMemberPokemonPhase extends PartyMemberPokemonPhase {
constructor(scene: BattleScene, partyMemberIndex: integer) {
super(scene, partyMemberIndex, true);
}
@ -668,6 +668,7 @@ export abstract class EnemyPartyMemberPokemonPhase extends PartyMemberPokemonPha
export class EncounterPhase extends BattlePhase {
private loaded: boolean;
public encounterStep: string;
constructor(scene: BattleScene, loaded?: boolean) {
super(scene);
@ -876,6 +877,7 @@ export class EncounterPhase extends BattlePhase {
const showDialogueAndSummon = () => {
let message: string;
this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex);
this.encounterStep = 'battle:encounterMessage';
this.scene.ui.showDialogue(message, trainer.getName(), null, () => {
this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => doSummon()));
});
@ -2999,6 +3001,10 @@ export class MessagePhase extends Phase {
this.scene.ui.showText(this.text, null, () => this.end(), this.callbackDelay || (this.prompt ? 0 : 1500), this.prompt, this.promptDelay);
}
getText() {
return this.text;
}
end() {
if (this.scene.abilityBar.shown)
this.scene.abilityBar.hide();
@ -3799,7 +3805,8 @@ export class LevelUpPhase extends PlayerPartyMemberPokemonPhase {
export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
private moveId: Moves;
public openMovesRemaining: number;
public learnMovesStep : string;
constructor(scene: BattleScene, partyMemberIndex: integer, moveId: Moves) {
super(scene, partyMemberIndex);
@ -3813,6 +3820,7 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
const move = allMoves[this.moveId];
const existingMoveIndex = pokemon.getMoveset().findIndex(m => m?.moveId === move.id);
if (existingMoveIndex > -1)
return this.end();
@ -3821,6 +3829,8 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
? pokemon.getMoveset().length
: pokemon.getMoveset().findIndex(m => m === null);
this.openMovesRemaining = emptyMoveIndex+1;
const messageMode = this.scene.ui.getHandler() instanceof EvolutionSceneHandler
? Mode.EVOLUTION_SCENE
: Mode.MESSAGE;
@ -3830,6 +3840,7 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
initMoveAnim(this.scene, this.moveId).then(() => {
loadMoveAnimAssets(this.scene, [ this.moveId ], true)
.then(() => {
this.learnMovesStep = 'battle:learnMove';
this.scene.ui.setMode(messageMode).then(() => {
this.scene.playSound('level_up_fanfare');
this.scene.ui.showText(i18next.t('battle:learnMove', { pokemonName: pokemon.name, moveName: move.name }), null, () => {
@ -3841,16 +3852,21 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
});
} else {
this.scene.ui.setMode(messageMode).then(() => {
this.learnMovesStep = 'battle:learnMovePrompt';
this.scene.ui.showText(i18next.t('battle:learnMovePrompt', { pokemonName: pokemon.name, moveName: move.name }), null, () => {
this.learnMovesStep = 'battle:learnMoveLimitReached';
this.scene.ui.showText(i18next.t('battle:learnMoveLimitReached', { pokemonName: pokemon.name }), null, () => {
this.learnMovesStep = 'battle:learnMoveReplaceQuestion';
this.scene.ui.showText(i18next.t('battle:learnMoveReplaceQuestion', { moveName: move.name }), null, () => {
const noHandler = () => {
this.scene.ui.setMode(messageMode).then(() => {
this.learnMovesStep = 'battle:learnMoveStopTeaching';
this.scene.ui.showText(i18next.t('battle:learnMoveStopTeaching', { moveName: move.name }), null, () => {
this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => {
this.scene.ui.setMode(messageMode);
this.learnMovesStep = 'battle:learnMoveNotLearned';
this.scene.ui.showText(i18next.t('battle:learnMoveNotLearned', { pokemonName: pokemon.name, moveName: move.name }), null, () => this.end(), null, true);
}, () => {
}, () => {
this.scene.ui.setMode(messageMode);
this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId));
this.end();
@ -3858,17 +3874,23 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
});
});
};
this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => {
this.scene.ui.setMode(messageMode);
this.learnMovesStep = 'battle:learnMoveForgetQuestion';
this.scene.ui.showText(i18next.t('battle:learnMoveForgetQuestion'), null, () => {
this.learnMovesStep = 'battle:replaceMove';
this.scene.ui.setModeWithoutClear(Mode.SUMMARY, this.getPokemon(), SummaryUiMode.LEARN_MOVE, move, (moveIndex: integer) => {
if (moveIndex === 4) {
noHandler();
return;
}
this.scene.ui.setMode(messageMode).then(() => {
this.learnMovesStep = 'battle:learnMovePoof';
this.scene.ui.setMode(messageMode).then(() => {
this.scene.ui.showText('@d{32}1, @d{15}2, and@d{15}… @d{15}… @d{15}… @d{15}@s{pb_bounce_1}Poof!', null, () => {
this.learnMovesStep = 'battle:learnMoveForgetSuccess';
this.scene.ui.showText(i18next.t('battle:learnMoveForgetSuccess', { pokemonName: pokemon.name, moveName: pokemon.moveset[moveIndex].getName() }), null, () => {
this.learnMovesStep = 'battle:learnMoveAnd';
this.scene.ui.showText('And…', null, () => {
pokemon.setMove(moveIndex, Moves.NONE);
this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId));
@ -4008,6 +4030,7 @@ export class AttemptCapturePhase extends PokemonPhase {
private pokeballType: PokeballType;
private pokeball: Phaser.GameObjects.Sprite;
private originalY: number;
public attemptCaptureStep: string;
constructor(scene: BattleScene, targetIndex: integer, pokeballType: PokeballType) {
super(scene, BattlerIndex.ENEMY + targetIndex);
@ -4185,7 +4208,7 @@ export class AttemptCapturePhase extends PokemonPhase {
this.scene.pokemonInfoContainer.show(pokemon, true);
this.scene.gameData.updateSpeciesDexIvs(pokemon.species.getRootSpeciesId(true), pokemon.ivs);
this.attemptCaptureStep = 'battle:pokemonCaught';
this.scene.ui.showText(i18next.t('battle:pokemonCaught', { pokemonName: pokemon.name }), null, () => {
const end = () => {
this.scene.pokemonInfoContainer.hide();
@ -4217,6 +4240,7 @@ export class AttemptCapturePhase extends PokemonPhase {
Promise.all([ pokemon.hideInfo(), this.scene.gameData.setPokemonCaught(pokemon) ]).then(() => {
if (this.scene.getParty().length === 6) {
const promptRelease = () => {
this.attemptCaptureStep = 'battle:pokemonCaughtMakeRoom';
this.scene.ui.showText(`Your party is full.\nRelease a Pokémon to make room for ${pokemon.name}?`, null, () => {
this.scene.ui.setMode(Mode.CONFIRM, () => {
this.scene.ui.setMode(Mode.PARTY, PartyUiMode.RELEASE, this.fieldIndex, (slotIndex: integer, _option: PartyOption) => {

View File

@ -27,8 +27,13 @@ export class UiInputs {
this.listenInputs();
}
processInput(input: string): void {
this.inputsController.processInputCommad(input);
}
listenInputs(): void {
this.events.on('input_down', (event) => {
console.log(event);
const actions = this.getActionsKeyDown();
if (!actions.hasOwnProperty(event.button)) return;
actions[event.button]();

View File

@ -3,7 +3,7 @@ import { defineConfig } from 'vite';
export default defineConfig(({ mode }) => {
return {
plugins: [/*fs()*/],
plugins: [],
server: { host: '0.0.0.0', port: 8000 },
clearScreen: false,
build: {