From 9fef4d019b2c43ebb02c5608d97c4c4c04bda2ec Mon Sep 17 00:00:00 2001 From: Snirozu <72814880+Snirozu@users.noreply.github.com> Date: Mon, 11 Mar 2024 23:46:33 +0100 Subject: [PATCH] pre 0.5.1 --- README.md | 14 +++- assets/preload/sounds/fap.ogg | Bin 0 -> 50915 bytes source/backend/ClientPrefs.hx | 3 - source/online/Downloader.hx | 32 +++++--- source/online/FunkinPoints.hx | 30 ++++++++ source/online/GameClient.hx | 7 +- source/online/Macros.hx | 2 +- source/online/schema/Player.hx | 6 ++ source/online/states/OptionsState.hx | 15 +++- source/online/states/ResultsScreen.hx | 29 +++++++ source/online/states/Room.hx | 71 ++++++++---------- .../online/states/ServerSettingsSubstate.hx | 8 ++ source/online/states/SkinsState.hx | 2 + source/options/OptionsState.hx | 2 + source/states/FreeplayState.hx | 1 + source/states/PlayState.hx | 46 +++++++++++- source/substates/GameplayChangersSubstate.hx | 2 + 17 files changed, 208 insertions(+), 62 deletions(-) create mode 100644 assets/preload/sounds/fap.ogg create mode 100644 source/online/FunkinPoints.hx diff --git a/README.md b/README.md index f9de8530..6ae01e04 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,16 @@ # Psych Engine - Online -Modified version of Psych Engine that adds multiplayer to it. +Psych Online is a modified Psych Engine with Online Multiplayer functionality! -This engine also has a [wiki!](https://github.com/Snirozu/Funkin-Psych-Online/wiki) +## Features +* Online Multiplayer (Public and LAN) +* Built-In Mod Downloader +* No Port Forwarding needed +* Game Modifiers +* Custom API +* Skins Support -If you want to use 0.6 of Psych Engine then go to [this repository](https://github.com/Snirozu/Funkin-Psych-Online-0.6) +## +This engine also has a [wiki!](https://github.com/Snirozu/Funkin-Psych-Online/wiki) +Check the Source Code of the server [here!](https://github.com/Snirozu/Funkin-Online-Server) ###### Psych Engine Commit: 5d0a66d \ No newline at end of file diff --git a/assets/preload/sounds/fap.ogg b/assets/preload/sounds/fap.ogg new file mode 100644 index 0000000000000000000000000000000000000000..57b749e8228cb80bc4832448c7620bcb33379272 GIT binary patch literal 50915 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 literal 0 HcmV?d00001 diff --git a/source/backend/ClientPrefs.hx b/source/backend/ClientPrefs.hx index 5ee1bed3..de78780d 100644 --- a/source/backend/ClientPrefs.hx +++ b/source/backend/ClientPrefs.hx @@ -72,9 +72,6 @@ class SaveVariables { public var trustedSources:Array = ["https://gamebanana.com/"]; public var comboOffsetOP1:Array = [0, 0, 0, 0]; public var comboOffsetOP2:Array = [0, 0, 0, 0]; - public var gapiRefreshToken:String = null; - public var gapiAccessToken:String = null; - public var gapiAccessExpires:Float = 0; public function new() { diff --git a/source/online/Downloader.hx b/source/online/Downloader.hx index 6892b798..14386d1e 100644 --- a/source/online/Downloader.hx +++ b/source/online/Downloader.hx @@ -106,20 +106,28 @@ class Downloader { isConnected = true; var urlFormat = getURLFormat(url); - try { - socket = !urlFormat.isSSL ? new Socket() : new sys.ssl.Socket(); - socket.connect(new Host(urlFormat.domain), urlFormat.port); - } - catch (exc) { - if (!cancelRequested) { - Waiter.put(() -> { - Alert.alert('Downloading failed!', id + ': ' + exc); - }); + + socket = !urlFormat.isSSL ? new Socket() : new sys.ssl.Socket(); + socket.setTimeout(5); + + var tries = 0; + while (!cancelRequested) { + tries++; + + try { + socket.connect(new Host(urlFormat.domain), urlFormat.port); + break; + } + catch (exc) { + if (tries >= 5) { + Waiter.put(() -> { + Alert.alert('Couldn\'t connect to the server after multiple tries!', id + ': ' + exc); + }); + cancelRequested = true; + break; + } } - doCancel(); - return; } - socket.setTimeout(10); if (cancelRequested) { doCancel(); diff --git a/source/online/FunkinPoints.hx b/source/online/FunkinPoints.hx new file mode 100644 index 00000000..63e30f44 --- /dev/null +++ b/source/online/FunkinPoints.hx @@ -0,0 +1,30 @@ +package online; + +import online.states.ResultsScreen; + +@:build(online.Macros.getSetForwarder()) +class FunkinPoints { + @:forwardField(FlxG.save.data.funkinPointsv1, 0) + public static var funkinPoints(get, null):Float; + + public static function calcFP(accuracy:Float, misses:Float, noteDensity:Float, notesHit:Float, combo:Float, songSpeed:Float):Float { + if (accuracy <= 0) + return 0; + if (notesHit <= 0) + return 0; + + var fp:Float = notesHit / noteDensity; + fp *= 1 + combo / 1000; + fp *= accuracy / (misses * 0.5 + 1) * (1 - noteDensity / 500); + fp *= songSpeed; + return Math.ffloor(fp); + } + + public static function save(accuracy:Float, misses:Float, noteDensity:Float, notesHit:Float, combo:Float, songSpeed:Float) { + var gained:Float = online.FunkinPoints.calcFP(accuracy, misses, noteDensity, notesHit, combo, songSpeed); + funkinPoints += gained; + ResultsScreen.gainedPoints = gained; + FlxG.save.flush(); + GameClient.send("updateFP", funkinPoints); + } +} \ No newline at end of file diff --git a/source/online/GameClient.hx b/source/online/GameClient.hx index a30f8497..8fcb12e0 100644 --- a/source/online/GameClient.hx +++ b/source/online/GameClient.hx @@ -8,6 +8,7 @@ import haxe.Http; import sys.io.File; import sys.FileSystem; import online.states.OnlineState; +//import io.colyseus.error.HttpException; 0.15.3 doesn't work import io.colyseus.error.MatchMakeError; import lime.app.Application; import io.colyseus.events.EventHandler; @@ -134,7 +135,11 @@ class GameClient { } static function getOptions(asHost:Bool):Map { - var options:Map = ["name" => ClientPrefs.data.nickname, "version" => MainMenuState.psychOnlineVersion]; + var options:Map = [ + "name" => ClientPrefs.data.nickname, + "version" => MainMenuState.psychOnlineVersion, + "points" => FunkinPoints.funkinPoints + ]; if (ClientPrefs.data.modSkin != null && ClientPrefs.data.modSkin.length >= 2) { options.set("skinMod", ClientPrefs.data.modSkin[0]); diff --git a/source/online/Macros.hx b/source/online/Macros.hx index a9c96083..9547d9bf 100644 --- a/source/online/Macros.hx +++ b/source/online/Macros.hx @@ -25,7 +25,7 @@ class Macros { access: fieldAccess, kind: FieldType.FFun({ args: [], - expr: macro return ${meta.params[0]} + expr: macro return ${meta.params[0]} ?? ${meta.params[1]} }), pos: pos, }); diff --git a/source/online/schema/Player.hx b/source/online/schema/Player.hx index 3e2aa179..5502efec 100644 --- a/source/online/schema/Player.hx +++ b/source/online/schema/Player.hx @@ -55,4 +55,10 @@ class Player extends Schema { @:type("string") public var skinURL:String = null; + + @:type("number") + public var points:Dynamic = 0; + + @:type("string") + public var status:String = null; } diff --git a/source/online/states/OptionsState.hx b/source/online/states/OptionsState.hx index 44305704..cdebe226 100644 --- a/source/online/states/OptionsState.hx +++ b/source/online/states/OptionsState.hx @@ -32,9 +32,11 @@ class OptionsState extends MusicBeatState { var nicknameOption:InputOption; items.add(nicknameOption = new InputOption("Nickname", "Set your nickname here!", "Boyfriend", true, text -> { + curOption.input.text = curOption.input.text.trim().substr(0, 20); if (curOption.input.text == "") ClientPrefs.data.nickname = "Boyfriend"; - ClientPrefs.data.nickname = curOption.input.text; + else + ClientPrefs.data.nickname = curOption.input.text; ClientPrefs.saveSettings(); })); nicknameOption.input.text = ClientPrefs.data.nickname; @@ -60,6 +62,17 @@ class OptionsState extends MusicBeatState { serverOption.screenCenter(X); serverOption.ID = i++; + // var titleOption:InputOption; + // items.add(titleOption = new InputOption("Title", "This will be shown below your name! (Max 20 characters)", ClientPrefs.data.playerTitle, text -> { + // curOption.input.text = curOption.input.text.trim().substr(0, 20); + // ClientPrefs.data.playerTitle = curOption.input.text; + // ClientPrefs.saveSettings(); + // })); + // titleOption.input.text = ClientPrefs.data.playerTitle; + // titleOption.y = serverOption.y + serverOption.height + 50; + // titleOption.screenCenter(X); + // titleOption.ID = i++; + var skinsOption:InputOption; items.add(skinsOption = new InputOption("Skin", "Choose your skin here!", null, false)); skinsOption.y = serverOption.y + serverOption.height + 50; diff --git a/source/online/states/ResultsScreen.hx b/source/online/states/ResultsScreen.hx index 48f8c9e4..a6c1f6d0 100644 --- a/source/online/states/ResultsScreen.hx +++ b/source/online/states/ResultsScreen.hx @@ -7,6 +7,8 @@ import objects.Character; import online.schema.Player; class ResultsScreen extends MusicBeatState { + public static var gainedPoints:Float = 0; + var disableInput = true; var win:FlxSprite; @@ -16,6 +18,8 @@ class ResultsScreen extends MusicBeatState { var p1Text:Alphabet; var p2Text:Alphabet; + var gainedText:FlxText; + var p1Accuracy:Float; var p2Accuracy:Float; @@ -125,6 +129,13 @@ class ResultsScreen extends MusicBeatState { back.alpha = 0; add(back); + gainedText = new FlxText(0, 0, 0, '+ ${gainedPoints}FP'); + gainedText.setFormat(null, 40, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK); + //gainedText.setPosition(FlxG.width - gainedText.width - 20, FlxG.height - gainedText.height - 20); + gainedText.setPosition(30, FlxG.height - gainedText.height - 25); + gainedText.visible = false; + add(gainedText); + if (!GameClient.isConnected()) { MusicBeatState.switchState(new OnlineState()); return; @@ -220,6 +231,20 @@ class ResultsScreen extends MusicBeatState { FlxTween.tween(lose, {alpha: 1, angle: 3}, 0.5, {ease: FlxEase.quadInOut}); FlxTween.tween(lose, {angle: 0}, 0.2, {ease: FlxEase.quadInOut}); + if (gainedPoints > 0) { + gainedText.visible = true; + FlxG.sound.play(Paths.sound('fap')); + if (ClientPrefs.data.flashing) + FlxFlicker.flicker(gainedText, 0.4, 0.05, true); + FlxTween.tween(gainedText, {alpha: 0.2}, 2, {ease: FlxEase.quadInOut}); + + new FlxTimer().start(2, (t) -> { + FlxTween.tween(gainedText, {x: back.x, y: back.y - 50, size: 25}, 1, {ease: FlxEase.quartOut}); + }); + } + + gainedPoints = 0; + if (ClientPrefs.data.flashing) { flickerLoop(); return; @@ -227,6 +252,8 @@ class ResultsScreen extends MusicBeatState { spotlight.visible = true; }); + + GameClient.send("status", "Viewing results"); } function flickerLoop() { @@ -252,6 +279,8 @@ class ResultsScreen extends MusicBeatState { disableInput = true; back.animation.play('press'); back.offset.set(80, 50); + if (gainedText.visible) + FlxTween.tween(gainedText, {alpha: 0}, 0.2, {ease: FlxEase.quadInOut}); new FlxTimer().start(0.5, (t) -> { GameClient.clearOnMessage(); MusicBeatState.switchState(new Room()); diff --git a/source/online/states/Room.hx b/source/online/states/Room.hx index c7a33490..d526f3a7 100644 --- a/source/online/states/Room.hx +++ b/source/online/states/Room.hx @@ -205,23 +205,23 @@ class Room extends MusicBeatState { // POST STAGE - player1Text = new FlxText(0, 40, 0, "PLAYER 1"); + player1Text = new FlxText(0, 100, 0, "PLAYER 1"); player1Text.setFormat("VCR OSD Mono", 25, FlxColor.WHITE, CENTER, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK); player1Bg = new FlxSprite(-1000); player1Bg.makeGraphic(1, 1, 0xA4000000); player1Bg.updateHitbox(); - player1Bg.y = player1Text.y; + player1Bg.y = player1Text.y - 30; groupHUD.add(player1Bg); groupHUD.add(player1Text); - player2Text = new FlxText(0, 40, 0, "PLAYER 2"); + player2Text = new FlxText(0, 100, 0, "PLAYER 2"); player2Text.setFormat("VCR OSD Mono", 25, FlxColor.WHITE, CENTER, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK); player2Bg = new FlxSprite(-1000); player2Bg.makeGraphic(1, 1, 0xA4000000); player2Bg.updateHitbox(); - player2Bg.y = player2Text.y; + player2Bg.y = player2Text.y - 30; groupHUD.add(player2Bg); groupHUD.add(player2Text); @@ -374,6 +374,8 @@ class Room extends MusicBeatState { FlxG.autoPause = false; verifyDownloadMod(true); + + GameClient.send("status", "In the Lobby"); } function loadCharacter(isP1:Bool, ?enableDownload:Bool = true) { @@ -455,6 +457,12 @@ class Room extends MusicBeatState { super.openSubState(obj); } + override function closeSubState() { + super.closeSubState(); + + GameClient.send("status", "In the Lobby"); + } + var optionShake:FlxTween; var elapsedShit = 3.; @@ -758,21 +766,7 @@ class Room extends MusicBeatState { songNameBg.updateHitbox(); songNameBg.x = songName.x; - player1Text.text = - GameClient.room.state.player1.name + "\n" + - "Ping: " + GameClient.room.state.player1.ping + "ms" + "\n\n" + - "Last Song Summary\n" + - "Score: " + GameClient.room.state.player1.score + "\n" + - "Accuracy: " + GameClient.getPlayerAccuracyPercent(GameClient.room.state.player1) + "%\n" + - "Sicks: " + GameClient.room.state.player1.sicks + "\n" + - "Goods: " + GameClient.room.state.player1.goods + "\n" + - "Bads: " + GameClient.room.state.player1.bads + "\n" + - "Shits: " + GameClient.room.state.player1.shits + "\n" + - "Misses: " + GameClient.room.state.player1.misses + "\n"; - if (GameClient.room.state.player1.isReady) - player1Text.text += "\nREADY"; - else - player1Text.text += "\nNOT READY"; + player1Text.text = returnPlayerText(GameClient.room.state.player1); if (GameClient.room.state.player2 != null && GameClient.room.state.player2.name != "") { player2Text.alpha = 1; @@ -780,24 +774,10 @@ class Room extends MusicBeatState { p2.colorTransform.greenOffset = 0; p2.colorTransform.blueOffset = 0; p2.alpha = 1; - player2Text.text = - GameClient.room.state.player2.name + "\n" + - "Ping: " + GameClient.room.state.player2.ping + "ms" + "\n\n" + - "Last Song Summary\n" + - "Score: " + GameClient.room.state.player2.score + "\n" + - "Accuracy: " + GameClient.getPlayerAccuracyPercent(GameClient.room.state.player2) + "%\n" + - "Sicks: " + GameClient.room.state.player2.sicks + "\n" + - "Goods: " + GameClient.room.state.player2.goods + "\n" + - "Bads: " + GameClient.room.state.player2.bads + "\n" + - "Shits: " + GameClient.room.state.player2.shits + "\n" + - "Misses: " + GameClient.room.state.player2.misses + "\n"; - if (GameClient.room.state.player2.isReady) - player2Text.text += "\nREADY"; - else - player2Text.text += "\nNOT READY"; + player2Text.text = returnPlayerText(GameClient.room.state.player2); } else { - player2Text.text = "WAITING FOR OPPONENT..."; + player2Text.text = "WAITING FOR OPPONENT"; player2Text.alpha = 0.8; p2.colorTransform.redOffset = -255; p2.colorTransform.greenOffset = -255; @@ -827,14 +807,15 @@ class Room extends MusicBeatState { p2.alpha = 0.5; } - player1Bg.x = player1Text.x; - player1Bg.scale.set(player1Text.width, player1Text.height); + player1Bg.scale.set(FlxMath.bound(player1Text.width, 300), player1Text.height + 60); player1Bg.updateHitbox(); - player2Bg.x = player2Text.x; - player2Bg.scale.set(player2Text.width, player2Text.height); + player2Bg.scale.set(FlxMath.bound(player2Text.width, 300), player2Text.height + 60); player2Bg.updateHitbox(); + player1Bg.x = 250 - player1Bg.width / 2; + player2Bg.x = 700 - player2Bg.width / 2; + switch (curSelected) { case 0: itemTip.text = " - SETTINGS - \nOpens server settings.\n\n(Keybind: SHIFT)"; @@ -860,6 +841,18 @@ class Room extends MusicBeatState { itemTipBg.updateHitbox(); } + function returnPlayerText(player:Player) { + return + player.name + "\n\n" + + //player.title + "\n\n" + //completely unsure about that + "Statistics\n" + + "Points: " + player.points + "\n" + + "Ping: " + player.ping + "ms" + "\n\n" + + player.status + "\n" + + (!player.isReady ? "NOT " : "") + "READY" + ; + } + function changeSelection(diffe:Int) { curSelected += diffe; diff --git a/source/online/states/ServerSettingsSubstate.hx b/source/online/states/ServerSettingsSubstate.hx index 36a6cb3a..f98ba7e6 100644 --- a/source/online/states/ServerSettingsSubstate.hx +++ b/source/online/states/ServerSettingsSubstate.hx @@ -117,6 +117,14 @@ class ServerSettingsSubstate extends MusicBeatSubstate { gameOptions.ID = i++; add(items); + + GameClient.send("status", "In the Room Settings"); + } + + override function closeSubState() { + super.closeSubState(); + + GameClient.send("status", "In the Room Settings"); } override function destroy() { diff --git a/source/online/states/SkinsState.hx b/source/online/states/SkinsState.hx index ddd4b882..0b43beb2 100644 --- a/source/online/states/SkinsState.hx +++ b/source/online/states/SkinsState.hx @@ -146,6 +146,8 @@ class SkinsState extends MusicBeatState { super.create(); CustomFadeTransition.nextCamera = hud; // wat + + GameClient.send("status", "Selects their skin"); } var acceptSound:FlxSound; diff --git a/source/options/OptionsState.hx b/source/options/OptionsState.hx index 1995748a..9d779cdc 100644 --- a/source/options/OptionsState.hx +++ b/source/options/OptionsState.hx @@ -66,6 +66,8 @@ class OptionsState extends MusicBeatState ClientPrefs.saveSettings(); super.create(); + + online.GameClient.send("status", "In the Game Options"); } override function closeSubState() { diff --git a/source/states/FreeplayState.hx b/source/states/FreeplayState.hx index a34fe048..90061aff 100644 --- a/source/states/FreeplayState.hx +++ b/source/states/FreeplayState.hx @@ -192,6 +192,7 @@ class FreeplayState extends MusicBeatState if (GameClient.isConnected()) { add(chatBox = new ChatBox(camera)); + GameClient.send("status", "Choosing a Song"); } super.create(); diff --git a/source/states/PlayState.hx b/source/states/PlayState.hx index 367e8054..b2465d84 100644 --- a/source/states/PlayState.hx +++ b/source/states/PlayState.hx @@ -310,12 +310,17 @@ class PlayState extends MusicBeatState if (freakyFlicker?.timer != null) freakyFlicker.stop(); - readyTween = FlxTween.tween(waitReadySpr, {alpha: v ? 1 : 0}, 0.5, {ease: FlxEase.quadIn}); + if (waitReadySpr != null) + readyTween = FlxTween.tween(waitReadySpr, {alpha: v ? 1 : 0}, 0.5, {ease: FlxEase.quadIn}); + + paused = v; return waitReady = v; } var waitReadySpr:Alphabet; + var noteDensity:Float; + override public function create() { registerMessages(); @@ -328,6 +333,8 @@ class PlayState extends MusicBeatState endCallback = endSong; } else { + paused = true; + GameClient.send("status", "In-Game"); startCallback = () -> { waitReady = true; }; @@ -852,6 +859,11 @@ class PlayState extends MusicBeatState } public function addTextToDebug(text:String, color:FlxColor) { + if (!chartingMode) { + Sys.println("SILENT DEBUG PRINT: " + text); + return; + } + #if LUA_ALLOWED var newText:DebugLuaText = luaDebugGroup.recycle(DebugLuaText); newText.text = text; @@ -1287,6 +1299,8 @@ class PlayState extends MusicBeatState } } + static var showFP:Bool = false; + public function updateScore(miss:Bool = false, ?skipRest:Bool = false) { var scoreTextObject = scoreTxt; @@ -1333,6 +1347,8 @@ class PlayState extends MusicBeatState }); } callOnScripts('onUpdateScore', [miss]); + if (showFP) + scoreTextObject.text += ' | FP: ' + online.FunkinPoints.calcFP(ratingPercent, songMisses, noteDensity, totalNotesHit, combo, playbackRate); } public function setSongTime(time:Float) @@ -1446,6 +1462,9 @@ class PlayState extends MusicBeatState makeEvent(event, i); } + var densSum:Float = 0; + var densTotal:Float = 0; + var densLast:Float = -1; for (section in noteData) { for (songNotes in section.sectionNotes) @@ -1461,6 +1480,14 @@ class PlayState extends MusicBeatState gottaHitNote = !section.mustHitSection; } + if (songNotes[2] <= 0 && playsAsBF() ? gottaHitNote : !gottaHitNote) { + if (densLast != -1 && songNotes[0] - densLast > 1 && songNotes[0] - densLast <= 500) { + densSum += songNotes[0] - densLast; + densTotal++; + } + densLast = songNotes[0]; + } + var oldNote:Note; if (unspawnNotes.length > 0) oldNote = unspawnNotes[Std.int(unspawnNotes.length - 1)]; @@ -1550,6 +1577,10 @@ class PlayState extends MusicBeatState makeEvent(event, i); unspawnNotes.sort(sortByTime); + if (densTotal == 0) + noteDensity = 500; + else + noteDensity = densSum / densTotal; generatedMusic = true; } @@ -1783,6 +1814,10 @@ class PlayState extends MusicBeatState override public function update(elapsed:Float) { + if (FlxG.keys.justPressed.F7) { + showFP = !showFP; + } + if (!GameClient.isConnected() && FlxG.keys.justPressed.F8) { opponentMode = !opponentMode; songScore = 0; @@ -1810,6 +1845,12 @@ class PlayState extends MusicBeatState freakyFlicker = FlxFlicker.flicker(waitReadySpr, 0.5, 0.05, true, false, _ -> waitReadySpr.text = "waiting for other player..."); GameClient.send("playerReady"); } + + if (waitReady) { + paused = true; + FlxG.sound.music.pause(); + vocals.pause(); + } } /*if (FlxG.keys.justPressed.NINE) @@ -1879,7 +1920,7 @@ class PlayState extends MusicBeatState if (startedCountdown && !paused) Conductor.songPosition += FlxG.elapsed * 1000 * playbackRate; - if (startingSong) + if (!paused && startingSong) { if (startedCountdown && Conductor.songPosition >= 0) startSong(); @@ -2545,6 +2586,7 @@ class PlayState extends MusicBeatState if (GameClient.isConnected()) { FlxG.sound.playMusic(Paths.music('freakyMenu')); + online.FunkinPoints.save(ratingPercent, songMisses, noteDensity, totalNotesHit, combo, playbackRate); GameClient.clearOnMessage(); MusicBeatState.switchState(new online.states.ResultsScreen()); } diff --git a/source/substates/GameplayChangersSubstate.hx b/source/substates/GameplayChangersSubstate.hx index 8c854904..abbeebee 100644 --- a/source/substates/GameplayChangersSubstate.hx +++ b/source/substates/GameplayChangersSubstate.hx @@ -77,6 +77,8 @@ class GameplayChangersSubstate extends MusicBeatSubstate var option:GameplayOption = new GameplayOption('Play as Opponent', 'opponentplay', 'bool', false); optionsArray.push(option); } + + GameClient.send("status", "In the Game Changers Menu"); } public function getOptionByName(name:String)