ng release and blank controls
This commit is contained in:
parent
3d0acca834
commit
e63dc749b8
|
@ -1,2 +1,3 @@
|
|||
export/
|
||||
.vscode/
|
||||
.vscode/
|
||||
APIStuff.hx
|
|
@ -1,14 +1,16 @@
|
|||
# Changelog
|
||||
All notable changes will be documented in this file.
|
||||
|
||||
## [Unreleased]
|
||||
## [1.1.0]
|
||||
### Added
|
||||
- 32bit support
|
||||
- Controller (dancepads) support
|
||||
- Pause screen
|
||||
- Main Menu overhaul
|
||||
- Cool intro screen thing
|
||||
- Uh lots of bullshit
|
||||
- Remind me to actually add this later lmaooo
|
||||
|
||||
## [0.1.0] - 2020-10-05
|
||||
## [1.0.0] - 2020-10-05
|
||||
### Added
|
||||
- Uh, everything. This the game's initial gamejam release. We put it out
|
|
@ -97,5 +97,6 @@
|
|||
|
||||
<!--Place custom nodes like icons here (higher priority to override the HaxeFlixel icon)-->
|
||||
<icon path="art/icon.png" />
|
||||
<haxedef name="SKIP_TO_PLAYSTATE" if="debug" />
|
||||
<!-- <haxedef name="SKIP_TO_PLAYSTATE" if="debug" /> -->
|
||||
<haxedef name="NG_LOGIN" />
|
||||
</project>
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,11 @@
|
|||
@echo off
|
||||
color 0a
|
||||
cd ..
|
||||
@echo on
|
||||
echo BUILDING GAME
|
||||
lime build html5 -release
|
||||
echo UPLOADING TO ITCH
|
||||
butler push ./export/release/html5/bin ninja-muffin24/funkin:html5
|
||||
butler status ninja-muffin24/funkin:html5
|
||||
echo ITCH SHIT UPDATED LMAOOOOO
|
||||
pause
|
|
@ -3,12 +3,12 @@ color 0a
|
|||
cd ..
|
||||
@echo on
|
||||
echo BUILDING GAME
|
||||
lime build hl -debug
|
||||
lime build windows -release
|
||||
echo UPLOADING 64 BIT VERSION TO ITCH
|
||||
butler push ./export/debug/hl/bin ninja-muffin24/funkin:windows-64bit
|
||||
lime build hl -debug -32
|
||||
butler push ./export/release/windows/bin ninja-muffin24/funkin:windows-64bit
|
||||
lime build windows -release -32
|
||||
echo UPLOADING 32 BIT VERSION TO ITCH
|
||||
butler push ./export/debug/hl/bin ninja-muffin24/funkin:windows-32bit
|
||||
butler push ./export/release/windows/bin ninja-muffin24/funkin:windows-32bit
|
||||
butler status ninja-muffin24/funkin:windows-32bit
|
||||
butler status ninja-muffin24/funkin:windows-64bit
|
||||
echo ITCH SHIT UPDATED LMAOOOOO
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 132 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextureAtlas imagePath="backspace.png">
|
||||
<!-- Created with Adobe Animate version 20.0.0.17400 -->
|
||||
<!-- http://www.adobe.com/products/animate.html -->
|
||||
<SubTexture name="backspace PRESSED0000" x="0" y="0" width="288" height="160" frameX="-14" frameY="-6" frameWidth="314" frameHeight="174"/>
|
||||
<SubTexture name="backspace PRESSED0001" x="0" y="0" width="288" height="160" frameX="-14" frameY="-6" frameWidth="314" frameHeight="174"/>
|
||||
<SubTexture name="backspace PRESSED0002" x="298" y="0" width="314" height="174"/>
|
||||
<SubTexture name="backspace to exit0000" x="622" y="0" width="280" height="106"/>
|
||||
<SubTexture name="backspace to exit white0000" x="0" y="184" width="280" height="106"/>
|
||||
</TextureAtlas>
|
Binary file not shown.
After Width: | Height: | Size: 1.2 MiB |
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextureAtlas imagePath="gfDanceTitle.png">
|
||||
<!-- Created with Adobe Animate version 20.0.0.17400 -->
|
||||
<!-- http://www.adobe.com/products/animate.html -->
|
||||
<SubTexture name="gfDance0000" x="0" y="0" width="717" height="648" frameX="-2" frameY="-14" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0001" x="727" y="0" width="721" height="648" frameX="0" frameY="-14" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0002" x="1458" y="0" width="721" height="646" frameX="0" frameY="-16" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0003" x="2189" y="0" width="717" height="646" frameX="-2" frameY="-16" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0004" x="2916" y="0" width="717" height="649" frameX="-2" frameY="-13" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0005" x="0" y="659" width="717" height="649" frameX="-2" frameY="-13" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0006" x="727" y="659" width="717" height="650" frameX="-2" frameY="-12" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0007" x="1454" y="659" width="717" height="661" frameX="-2" frameY="-1" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0008" x="1454" y="659" width="717" height="661" frameX="-2" frameY="-1" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0009" x="1454" y="659" width="717" height="661" frameX="-2" frameY="-1" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0010" x="2181" y="659" width="717" height="662" frameX="-2" frameY="0" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0011" x="2181" y="659" width="717" height="662" frameX="-2" frameY="0" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0012" x="2181" y="659" width="717" height="662" frameX="-2" frameY="0" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0013" x="2908" y="659" width="717" height="661" frameX="-2" frameY="-1" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0014" x="2908" y="659" width="717" height="661" frameX="-2" frameY="-1" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0015" x="0" y="1331" width="717" height="650" frameX="-2" frameY="-12" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0016" x="727" y="1331" width="721" height="650" frameX="0" frameY="-12" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0017" x="1458" y="1331" width="721" height="650" frameX="0" frameY="-12" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0018" x="2189" y="1331" width="717" height="650" frameX="-2" frameY="-12" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0019" x="2916" y="1331" width="717" height="651" frameX="-2" frameY="-11" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0020" x="0" y="1992" width="717" height="651" frameX="-2" frameY="-11" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0021" x="727" y="1992" width="717" height="652" frameX="-2" frameY="-10" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0022" x="1454" y="1992" width="717" height="657" frameX="-2" frameY="-5" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0023" x="1454" y="1992" width="717" height="657" frameX="-2" frameY="-5" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0024" x="1454" y="1992" width="717" height="657" frameX="-2" frameY="-5" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0025" x="2181" y="1992" width="717" height="656" frameX="-2" frameY="-6" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0026" x="2181" y="1992" width="717" height="656" frameX="-2" frameY="-6" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0027" x="2181" y="1992" width="717" height="656" frameX="-2" frameY="-6" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0028" x="2908" y="1992" width="717" height="656" frameX="-2" frameY="-6" frameWidth="721" frameHeight="662"/>
|
||||
<SubTexture name="gfDance0029" x="2908" y="1992" width="717" height="656" frameX="-2" frameY="-6" frameWidth="721" frameHeight="662"/>
|
||||
</TextureAtlas>
|
Binary file not shown.
After Width: | Height: | Size: 579 KiB |
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextureAtlas imagePath="logoBumpin.png">
|
||||
<!-- Created with Adobe Animate version 20.0.0.17400 -->
|
||||
<!-- http://www.adobe.com/products/animate.html -->
|
||||
<SubTexture name="logo bumpin0000" x="0" y="0" width="894" height="670" frameX="-22" frameY="-16" frameWidth="939" frameHeight="703"/>
|
||||
<SubTexture name="logo bumpin0001" x="904" y="0" width="939" height="703"/>
|
||||
<SubTexture name="logo bumpin0002" x="904" y="0" width="939" height="703"/>
|
||||
<SubTexture name="logo bumpin0003" x="0" y="713" width="911" height="683" frameX="-14" frameY="-10" frameWidth="939" frameHeight="703"/>
|
||||
<SubTexture name="logo bumpin0004" x="0" y="713" width="911" height="683" frameX="-14" frameY="-10" frameWidth="939" frameHeight="703"/>
|
||||
<SubTexture name="logo bumpin0005" x="921" y="713" width="905" height="679" frameX="-17" frameY="-12" frameWidth="939" frameHeight="703"/>
|
||||
<SubTexture name="logo bumpin0006" x="921" y="713" width="905" height="679" frameX="-17" frameY="-12" frameWidth="939" frameHeight="703"/>
|
||||
<SubTexture name="logo bumpin0007" x="921" y="713" width="905" height="679" frameX="-17" frameY="-12" frameWidth="939" frameHeight="703"/>
|
||||
<SubTexture name="logo bumpin0008" x="921" y="713" width="905" height="679" frameX="-17" frameY="-12" frameWidth="939" frameHeight="703"/>
|
||||
<SubTexture name="logo bumpin0009" x="921" y="713" width="905" height="679" frameX="-17" frameY="-12" frameWidth="939" frameHeight="703"/>
|
||||
<SubTexture name="logo bumpin0010" x="921" y="713" width="905" height="679" frameX="-17" frameY="-12" frameWidth="939" frameHeight="703"/>
|
||||
<SubTexture name="logo bumpin0011" x="921" y="713" width="905" height="679" frameX="-17" frameY="-12" frameWidth="939" frameHeight="703"/>
|
||||
<SubTexture name="logo bumpin0012" x="921" y="713" width="905" height="679" frameX="-17" frameY="-12" frameWidth="939" frameHeight="703"/>
|
||||
<SubTexture name="logo bumpin0013" x="921" y="713" width="905" height="679" frameX="-17" frameY="-12" frameWidth="939" frameHeight="703"/>
|
||||
<SubTexture name="logo bumpin0014" x="921" y="713" width="905" height="679" frameX="-17" frameY="-12" frameWidth="939" frameHeight="703"/>
|
||||
</TextureAtlas>
|
Binary file not shown.
After Width: | Height: | Size: 600 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 MiB |
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextureAtlas imagePath="titleEnter.png">
|
||||
<!-- Created with Adobe Animate version 20.0.0.17400 -->
|
||||
<!-- http://www.adobe.com/products/animate.html -->
|
||||
<SubTexture name="ENTER PRESSED0000" x="0" y="0" width="1477" height="79"/>
|
||||
<SubTexture name="ENTER PRESSED0001" x="1487" y="0" width="1477" height="79"/>
|
||||
<SubTexture name="ENTER PRESSED0002" x="0" y="0" width="1477" height="79"/>
|
||||
<SubTexture name="ENTER PRESSED0003" x="1487" y="0" width="1477" height="79"/>
|
||||
<SubTexture name="ENTER PRESSED0004" x="0" y="0" width="1477" height="79"/>
|
||||
<SubTexture name="ENTER PRESSED0005" x="1487" y="0" width="1477" height="79"/>
|
||||
<SubTexture name="ENTER PRESSED0006" x="1487" y="0" width="1477" height="79"/>
|
||||
<SubTexture name="ENTER PRESSED0007" x="0" y="0" width="1477" height="79"/>
|
||||
<SubTexture name="ENTER PRESSED0008" x="1487" y="0" width="1477" height="79"/>
|
||||
<SubTexture name="Press Enter to Begin0000" x="0" y="89" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0001" x="1505" y="89" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0002" x="0" y="195" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0003" x="1505" y="195" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0004" x="0" y="301" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0005" x="1505" y="301" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0006" x="0" y="407" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0007" x="1505" y="407" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0008" x="0" y="513" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0009" x="1505" y="513" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0010" x="0" y="619" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0011" x="1505" y="619" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0012" x="0" y="725" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0013" x="1505" y="725" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0014" x="0" y="831" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0015" x="1505" y="831" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0016" x="0" y="937" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0017" x="1505" y="937" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0018" x="0" y="1043" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0019" x="1505" y="1043" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0020" x="0" y="1149" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0021" x="1505" y="1149" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0022" x="0" y="1255" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0023" x="0" y="1255" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0024" x="1505" y="1255" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0025" x="0" y="1149" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0026" x="0" y="1361" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0027" x="1505" y="1361" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0028" x="0" y="1467" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0029" x="1505" y="1467" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0030" x="0" y="1573" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0031" x="1505" y="1573" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0032" x="0" y="1679" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0033" x="1505" y="1679" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0034" x="0" y="1785" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0035" x="1505" y="1785" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0036" x="0" y="1891" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0037" x="1505" y="1891" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0038" x="0" y="1997" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0039" x="1505" y="1997" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0040" x="0" y="2103" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0041" x="1505" y="2103" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0042" x="0" y="2209" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0043" x="1505" y="2209" width="1495" height="96"/>
|
||||
<SubTexture name="Press Enter to Begin0044" x="0" y="89" width="1495" height="96"/>
|
||||
</TextureAtlas>
|
|
@ -17,6 +17,10 @@ class Alphabet extends FlxSpriteGroup
|
|||
public var delay:Float = 0.05;
|
||||
public var paused:Bool = false;
|
||||
|
||||
// for menu shit
|
||||
public var targetY:Float = 0;
|
||||
public var isMenuItem:Bool = false;
|
||||
|
||||
public var text:String = "";
|
||||
|
||||
var _finalText:String = "";
|
||||
|
@ -207,6 +211,14 @@ class Alphabet extends FlxSpriteGroup
|
|||
|
||||
override function update(elapsed:Float)
|
||||
{
|
||||
if (isMenuItem)
|
||||
{
|
||||
var scaledY = FlxMath.remapToRange(targetY, 0, 1, 0, 1.3);
|
||||
|
||||
y = FlxMath.lerp(y, (scaledY * 120) + (FlxG.height * 0.48), 0.16);
|
||||
x = FlxMath.lerp(x, (targetY * 20) + 90, 0.16);
|
||||
}
|
||||
|
||||
super.update(elapsed);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,47 +3,59 @@ package;
|
|||
import flixel.FlxG;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.addons.display.FlxGridOverlay;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.text.FlxText;
|
||||
|
||||
#if switch
|
||||
import openfl.events.GameInputEvent;
|
||||
import openfl.ui.GameInput;
|
||||
import openfl.ui.GameInputDevice;
|
||||
import openfl.ui.GameInputControl;
|
||||
#end
|
||||
|
||||
class FreeplayState extends MusicBeatState
|
||||
{
|
||||
var songs:Array<String> = ["Bopeebo", "Dadbattle", "Fresh", "Tutorial", "Spookeez", "South", "Monster"];
|
||||
var songs:Array<String> = ["Bopeebo", "Dadbattle", "Fresh", "Tutorial"];
|
||||
|
||||
var selector:FlxText;
|
||||
var curSelected:Int = 0;
|
||||
|
||||
private var grpSongs:FlxTypedGroup<Alphabet>;
|
||||
|
||||
override function create()
|
||||
{
|
||||
if (!FlxG.sound.music.playing)
|
||||
FlxG.sound.playMusic('assets/music/freakyMenu' + TitleState.soundExt);
|
||||
|
||||
if (StoryMenuState.weekUnlocked[1])
|
||||
{
|
||||
songs.push('Spookeez');
|
||||
songs.push('South');
|
||||
}
|
||||
|
||||
// LOAD MUSIC
|
||||
|
||||
// LOAD CHARACTERS
|
||||
|
||||
var bg:FlxSprite = FlxGridOverlay.create(20, 20);
|
||||
var bg:FlxSprite = new FlxSprite().loadGraphic(AssetPaths.menuBGBlue__png);
|
||||
add(bg);
|
||||
|
||||
grpSongs = new FlxTypedGroup<Alphabet>();
|
||||
add(grpSongs);
|
||||
|
||||
for (i in 0...songs.length)
|
||||
{
|
||||
var songText:Alphabet = new Alphabet(0, (70 * i) + 30, songs[i], true, false);
|
||||
add(songText);
|
||||
songText.x += 40;
|
||||
songText.isMenuItem = true;
|
||||
songText.targetY = i;
|
||||
grpSongs.add(songText);
|
||||
// songText.x += 40;
|
||||
// DONT PUT X IN THE FIRST PARAMETER OF new ALPHABET() !!
|
||||
// songText.screenCenter(X);
|
||||
}
|
||||
|
||||
FlxG.sound.playMusic('assets/music/title' + TitleState.soundExt, 0);
|
||||
FlxG.sound.music.fadeIn(2, 0, 0.8);
|
||||
changeSelection();
|
||||
|
||||
// FlxG.sound.playMusic('assets/music/title' + TitleState.soundExt, 0);
|
||||
// FlxG.sound.music.fadeIn(2, 0, 0.8);
|
||||
selector = new FlxText();
|
||||
|
||||
selector.size = 40;
|
||||
selector.text = ">";
|
||||
add(selector);
|
||||
// add(selector);
|
||||
|
||||
var swag:Alphabet = new Alphabet(1, 0, "swag");
|
||||
|
||||
|
@ -59,47 +71,53 @@ class FreeplayState extends MusicBeatState
|
|||
|
||||
if (upP)
|
||||
{
|
||||
curSelected -= 1;
|
||||
changeSelection(-1);
|
||||
}
|
||||
if (downP)
|
||||
{
|
||||
curSelected += 1;
|
||||
changeSelection(1);
|
||||
}
|
||||
|
||||
#if switch
|
||||
if (gamepad.anyJustPressed(["UP", "DPAD_UP", "LEFT_STICK_DIGITAL_UP"]))
|
||||
{
|
||||
curSelected -= 1;
|
||||
}
|
||||
if (gamepad.anyJustPressed(["DOWN", "DPAD_DOWN", "LEFT_STICK_DIGITAL_DOWN"]))
|
||||
{
|
||||
curSelected += 1;
|
||||
}
|
||||
#end
|
||||
if (controls.BACK)
|
||||
{
|
||||
FlxG.switchState(new MainMenuState());
|
||||
}
|
||||
|
||||
if (accepted)
|
||||
{
|
||||
PlayState.SONG = Song.loadFromJson(songs[curSelected].toLowerCase(), songs[curSelected].toLowerCase());
|
||||
PlayState.isStoryMode = false;
|
||||
FlxG.switchState(new PlayState());
|
||||
FlxG.sound.music.stop();
|
||||
}
|
||||
}
|
||||
|
||||
function changeSelection(change:Int = 0)
|
||||
{
|
||||
curSelected += change;
|
||||
|
||||
if (curSelected < 0)
|
||||
curSelected = songs.length - 1;
|
||||
if (curSelected >= songs.length)
|
||||
curSelected = 0;
|
||||
|
||||
selector.y = (70 * curSelected) + 30;
|
||||
// selector.y = (70 * curSelected) + 30;
|
||||
|
||||
if (accepted)
|
||||
var bullShit:Int = 0;
|
||||
|
||||
for (item in grpSongs.members)
|
||||
{
|
||||
PlayState.SONG = Song.loadFromJson(songs[curSelected].toLowerCase());
|
||||
PlayState.isStoryMode = false;
|
||||
FlxG.switchState(new PlayState());
|
||||
FlxG.sound.music.stop();
|
||||
}
|
||||
item.targetY = bullShit - curSelected;
|
||||
bullShit++;
|
||||
|
||||
#if switch
|
||||
if (gamepad.anyJustPressed(["B"])) //"B" is swapped with "A" on Switch
|
||||
item.alpha = 0.6;
|
||||
// item.setGraphicSize(Std.int(item.width * 0.8));
|
||||
|
||||
if (item.targetY == 0)
|
||||
{
|
||||
PlayState.SONG = Song.loadFromJson(songs[curSelected].toLowerCase());
|
||||
PlayState.isStoryMode = false;
|
||||
FlxG.switchState(new PlayState());
|
||||
FlxG.sound.music.stop();
|
||||
item.alpha = 1;
|
||||
// item.setGraphicSize(Std.int(item.width));
|
||||
}
|
||||
#end
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ class GameOverSubstate extends MusicBeatSubstate
|
|||
var bf:Boyfriend;
|
||||
var camFollow:FlxObject;
|
||||
|
||||
// var
|
||||
|
||||
public function new(x:Float, y:Float)
|
||||
{
|
||||
super();
|
||||
|
@ -39,11 +41,21 @@ class GameOverSubstate extends MusicBeatSubstate
|
|||
{
|
||||
super.update(elapsed);
|
||||
|
||||
if (FlxG.keys.justPressed.ENTER)
|
||||
if (controls.ACCEPT)
|
||||
{
|
||||
endBullshit();
|
||||
}
|
||||
|
||||
if (controls.BACK)
|
||||
{
|
||||
FlxG.sound.music.stop();
|
||||
|
||||
if (PlayState.isStoryMode)
|
||||
FlxG.switchState(new StoryMenuState());
|
||||
else
|
||||
FlxG.switchState(new FreeplayState());
|
||||
}
|
||||
|
||||
if (bf.animation.curAnim.name == 'firstDeath' && bf.animation.curAnim.curFrame == 12)
|
||||
{
|
||||
FlxG.camera.follow(camFollow, LOCKON, 0.01);
|
||||
|
|
|
@ -73,70 +73,76 @@ class MainMenuState extends MusicBeatState
|
|||
super.create();
|
||||
}
|
||||
|
||||
var selectedSomethin:Bool = false;
|
||||
|
||||
override function update(elapsed:Float)
|
||||
{
|
||||
if (controls.UP_P)
|
||||
if (!selectedSomethin)
|
||||
{
|
||||
FlxG.sound.play('assets/sounds/scrollMenu' + TitleState.soundExt);
|
||||
changeItem(-1);
|
||||
}
|
||||
if (controls.UP_P)
|
||||
{
|
||||
FlxG.sound.play('assets/sounds/scrollMenu' + TitleState.soundExt);
|
||||
changeItem(-1);
|
||||
}
|
||||
|
||||
if (controls.DOWN_P)
|
||||
{
|
||||
FlxG.sound.play('assets/sounds/scrollMenu' + TitleState.soundExt);
|
||||
changeItem(1);
|
||||
}
|
||||
if (controls.DOWN_P)
|
||||
{
|
||||
FlxG.sound.play('assets/sounds/scrollMenu' + TitleState.soundExt);
|
||||
changeItem(1);
|
||||
}
|
||||
|
||||
if (controls.BACK)
|
||||
{
|
||||
FlxG.switchState(new TitleState());
|
||||
if (controls.BACK)
|
||||
{
|
||||
FlxG.switchState(new TitleState());
|
||||
}
|
||||
|
||||
if (controls.ACCEPT)
|
||||
{
|
||||
if (optionShit[curSelected] == 'donate')
|
||||
{
|
||||
FlxG.openURL('https://ninja-muffin24.itch.io/funkin');
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedSomethin = true;
|
||||
FlxG.sound.play('assets/sounds/confirmMenu' + TitleState.soundExt);
|
||||
|
||||
FlxFlicker.flicker(magenta, 1.1, 0.15, false);
|
||||
|
||||
menuItems.forEach(function(spr:FlxSprite)
|
||||
{
|
||||
if (curSelected != spr.ID)
|
||||
{
|
||||
FlxTween.tween(spr, {alpha: 0}, 0.4, {
|
||||
ease: FlxEase.quadOut,
|
||||
onComplete: function(twn:FlxTween)
|
||||
{
|
||||
spr.kill();
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxFlicker.flicker(spr, 1, 0.06, false, false, function(flick:FlxFlicker)
|
||||
{
|
||||
var daChoice:String = optionShit[curSelected];
|
||||
|
||||
switch (daChoice)
|
||||
{
|
||||
case 'story mode':
|
||||
FlxG.switchState(new StoryMenuState());
|
||||
case 'freeplay':
|
||||
FlxG.switchState(new FreeplayState());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.update(elapsed);
|
||||
|
||||
if (controls.ACCEPT)
|
||||
{
|
||||
if (optionShit[curSelected] == 'donate')
|
||||
{
|
||||
FlxG.openURL('https://ninja-muffin24.itch.io/funkin');
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxG.sound.play('assets/sounds/confirmMenu' + TitleState.soundExt);
|
||||
|
||||
FlxFlicker.flicker(magenta, 1.1, 0.15, false);
|
||||
|
||||
menuItems.forEach(function(spr:FlxSprite)
|
||||
{
|
||||
if (curSelected != spr.ID)
|
||||
{
|
||||
FlxTween.tween(spr, {alpha: 0}, 0.4, {
|
||||
ease: FlxEase.quadOut,
|
||||
onComplete: function(twn:FlxTween)
|
||||
{
|
||||
spr.kill();
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
FlxFlicker.flicker(spr, 1, 0.06, false, false, function(flick:FlxFlicker)
|
||||
{
|
||||
var daChoice:String = optionShit[curSelected];
|
||||
|
||||
switch (daChoice)
|
||||
{
|
||||
case 'story mode':
|
||||
FlxG.switchState(new StoryMenuState());
|
||||
case 'freeplay':
|
||||
FlxG.switchState(new FreeplayState());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
menuItems.forEach(function(spr:FlxSprite)
|
||||
{
|
||||
spr.screenCenter(X);
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
package;
|
||||
|
||||
import flixel.FlxG;
|
||||
import flixel.util.FlxSignal;
|
||||
import io.newgrounds.NG;
|
||||
import io.newgrounds.components.ScoreBoardComponent.Period;
|
||||
import io.newgrounds.objects.Medal;
|
||||
import io.newgrounds.objects.Score;
|
||||
import io.newgrounds.objects.ScoreBoard;
|
||||
import openfl.display.Stage;
|
||||
|
||||
/**
|
||||
* MADE BY GEOKURELI THE LEGENED GOD HERO MVP
|
||||
*/
|
||||
class NGio
|
||||
{
|
||||
public static var isLoggedIn:Bool = false;
|
||||
public static var scoreboardsLoaded:Bool = false;
|
||||
|
||||
public static var scoreboardArray:Array<Score> = [];
|
||||
|
||||
public static var ngDataLoaded(default, null):FlxSignal = new FlxSignal();
|
||||
public static var ngScoresLoaded(default, null):FlxSignal = new FlxSignal();
|
||||
|
||||
public function new(api:String, encKey:String, ?sessionId:String)
|
||||
{
|
||||
trace("connecting to newgrounds");
|
||||
|
||||
NG.createAndCheckSession(api, sessionId);
|
||||
|
||||
NG.core.verbose = true;
|
||||
// Set the encryption cipher/format to RC4/Base64. AES128 and Hex are not implemented yet
|
||||
NG.core.initEncryption(encKey); // Found in you NG project view
|
||||
|
||||
trace(NG.core.attemptingLogin);
|
||||
|
||||
if (NG.core.attemptingLogin)
|
||||
{
|
||||
/* a session_id was found in the loadervars, this means the user is playing on newgrounds.com
|
||||
* and we should login shortly. lets wait for that to happen
|
||||
*/
|
||||
trace("attempting login");
|
||||
NG.core.onLogin.add(onNGLogin);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* They are NOT playing on newgrounds.com, no session id was found. We must start one manually, if we want to.
|
||||
* Note: This will cause a new browser window to pop up where they can log in to newgrounds
|
||||
*/
|
||||
NG.core.requestLogin(onNGLogin);
|
||||
}
|
||||
}
|
||||
|
||||
function onNGLogin():Void
|
||||
{
|
||||
trace('logged in! user:${NG.core.user.name}');
|
||||
isLoggedIn = true;
|
||||
FlxG.save.data.sessionId = NG.core.sessionId;
|
||||
// FlxG.save.flush();
|
||||
// Load medals then call onNGMedalFetch()
|
||||
NG.core.requestMedals(onNGMedalFetch);
|
||||
|
||||
// Load Scoreboards hten call onNGBoardsFetch()
|
||||
NG.core.requestScoreBoards(onNGBoardsFetch);
|
||||
|
||||
ngDataLoaded.dispatch();
|
||||
}
|
||||
|
||||
// --- MEDALS
|
||||
function onNGMedalFetch():Void
|
||||
{
|
||||
/*
|
||||
// Reading medal info
|
||||
for (id in NG.core.medals.keys())
|
||||
{
|
||||
var medal = NG.core.medals.get(id);
|
||||
trace('loaded medal id:$id, name:${medal.name}, description:${medal.description}');
|
||||
}
|
||||
|
||||
// Unlocking medals
|
||||
var unlockingMedal = NG.core.medals.get(54352);// medal ids are listed in your NG project viewer
|
||||
if (!unlockingMedal.unlocked)
|
||||
unlockingMedal.sendUnlock();
|
||||
*/
|
||||
}
|
||||
|
||||
// --- SCOREBOARDS
|
||||
function onNGBoardsFetch():Void
|
||||
{
|
||||
/*
|
||||
// Reading medal info
|
||||
for (id in NG.core.scoreBoards.keys())
|
||||
{
|
||||
var board = NG.core.scoreBoards.get(id);
|
||||
trace('loaded scoreboard id:$id, name:${board.name}');
|
||||
}
|
||||
*/
|
||||
// var board = NG.core.scoreBoards.get(8004);// ID found in NG project view
|
||||
|
||||
// Posting a score thats OVER 9000!
|
||||
// board.postScore(FlxG.random.int(0, 1000));
|
||||
|
||||
// --- To view the scores you first need to select the range of scores you want to see ---
|
||||
|
||||
// add an update listener so we know when we get the new scores
|
||||
// board.onUpdate.add(onNGScoresFetch);
|
||||
trace("shoulda got score by NOW!");
|
||||
// board.requestScores(20);// get the best 10 scores ever logged
|
||||
// more info on scores --- http://www.newgrounds.io/help/components/#scoreboard-getscores
|
||||
}
|
||||
|
||||
inline static public function postScore(score:Int = 0, song:String)
|
||||
{
|
||||
if (isLoggedIn)
|
||||
{
|
||||
for (id in NG.core.scoreBoards.keys())
|
||||
{
|
||||
var board = NG.core.scoreBoards.get(id);
|
||||
|
||||
if (song == board.name)
|
||||
{
|
||||
board.postScore(score, "Uhh meow?");
|
||||
}
|
||||
|
||||
// trace('loaded scoreboard id:$id, name:${board.name}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onNGScoresFetch():Void
|
||||
{
|
||||
scoreboardsLoaded = true;
|
||||
|
||||
ngScoresLoaded.dispatch();
|
||||
/*
|
||||
for (score in NG.core.scoreBoards.get(8737).scores)
|
||||
{
|
||||
trace('score loaded user:${score.user.name}, score:${score.formatted_value}');
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
// var board = NG.core.scoreBoards.get(8004);// ID found in NG project view
|
||||
// board.postScore(HighScore.score);
|
||||
|
||||
// NGio.scoreboardArray = NG.core.scoreBoards.get(8004).scores;
|
||||
}
|
||||
|
||||
inline static public function unlockMedal(id:Int)
|
||||
{
|
||||
if (isLoggedIn)
|
||||
{
|
||||
var medal = NG.core.medals.get(id);
|
||||
if (!medal.unlocked)
|
||||
medal.sendUnlock();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -80,6 +80,7 @@ class PlayState extends MusicBeatState
|
|||
var halloweenBG:FlxSprite;
|
||||
|
||||
var talking:Bool = true;
|
||||
var songScore:Int = 0;
|
||||
|
||||
override public function create()
|
||||
{
|
||||
|
@ -593,7 +594,7 @@ class PlayState extends MusicBeatState
|
|||
|
||||
if (FlxG.keys.justPressed.ESCAPE)
|
||||
{
|
||||
FlxG.switchState(new ChartingState());
|
||||
// FlxG.switchState(new ChartingState());
|
||||
}
|
||||
|
||||
// FlxG.watch.addQuick('VOL', vocals.amplitudeLeft);
|
||||
|
@ -602,6 +603,9 @@ class PlayState extends MusicBeatState
|
|||
healthHeads.setGraphicSize(Std.int(FlxMath.lerp(100, healthHeads.width, 0.98)));
|
||||
healthHeads.x = healthBar.x + (healthBar.width * (FlxMath.remapToRange(healthBar.percent, 0, 100, 100, 0) * 0.01)) - (healthHeads.width / 2);
|
||||
|
||||
if (health > 2)
|
||||
health = 2;
|
||||
|
||||
if (healthBar.percent < 20)
|
||||
healthHeads.animation.play('unhealthy');
|
||||
else
|
||||
|
@ -609,8 +613,8 @@ class PlayState extends MusicBeatState
|
|||
|
||||
/* if (FlxG.keys.justPressed.NINE)
|
||||
FlxG.switchState(new Charting()); */
|
||||
if (FlxG.keys.justPressed.EIGHT)
|
||||
FlxG.switchState(new AnimationDebug(SONG.player2));
|
||||
// if (FlxG.keys.justPressed.EIGHT)
|
||||
// FlxG.switchState(new AnimationDebug(SONG.player2));
|
||||
|
||||
if (startingSong)
|
||||
{
|
||||
|
@ -805,15 +809,24 @@ class PlayState extends MusicBeatState
|
|||
{
|
||||
trace('SONG DONE' + isStoryMode);
|
||||
|
||||
NGio.postScore(songScore, SONG.song);
|
||||
|
||||
if (isStoryMode)
|
||||
{
|
||||
storyPlaylist.remove(storyPlaylist[0]);
|
||||
|
||||
if (storyPlaylist.length <= 0)
|
||||
{
|
||||
FlxG.switchState(new TitleState());
|
||||
FlxG.sound.playMusic('assets/music/freakyMenu' + TitleState.soundExt);
|
||||
|
||||
FlxG.switchState(new StoryMenuState());
|
||||
|
||||
StoryMenuState.weekUnlocked[1] = true;
|
||||
|
||||
NGio.unlockMedal(60961);
|
||||
|
||||
FlxG.save.data.weekUnlocked = StoryMenuState.weekUnlocked;
|
||||
FlxG.save.flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -851,22 +864,28 @@ class PlayState extends MusicBeatState
|
|||
//
|
||||
|
||||
var rating:FlxSprite = new FlxSprite();
|
||||
var score:Int = 350;
|
||||
|
||||
var daRating:String = "sick";
|
||||
|
||||
if (noteDiff > Conductor.safeZoneOffset * 0.9)
|
||||
{
|
||||
daRating = 'shit';
|
||||
score = 50;
|
||||
}
|
||||
else if (noteDiff > Conductor.safeZoneOffset * 0.75)
|
||||
{
|
||||
daRating = 'bad';
|
||||
score = 100;
|
||||
}
|
||||
else if (noteDiff > Conductor.safeZoneOffset * 0.2)
|
||||
{
|
||||
daRating = 'good';
|
||||
score = 200;
|
||||
}
|
||||
|
||||
songScore += score;
|
||||
|
||||
/* if (combo > 60)
|
||||
daRating = 'sick';
|
||||
else if (combo > 12)
|
||||
|
@ -916,7 +935,9 @@ class PlayState extends MusicBeatState
|
|||
numScore.acceleration.y = FlxG.random.int(200, 300);
|
||||
numScore.velocity.y -= FlxG.random.int(140, 160);
|
||||
numScore.velocity.x = FlxG.random.float(-5, 5);
|
||||
add(numScore);
|
||||
|
||||
if (combo >= 10 || combo == 0)
|
||||
add(numScore);
|
||||
|
||||
FlxTween.tween(numScore, {alpha: 0}, 0.2, {
|
||||
onComplete: function(tween:FlxTween)
|
||||
|
@ -1102,6 +1123,8 @@ class PlayState extends MusicBeatState
|
|||
}
|
||||
combo = 0;
|
||||
|
||||
songScore -= 10;
|
||||
|
||||
FlxG.sound.play('assets/sounds/missnote' + FlxG.random.int(1, 3) + TitleState.soundExt, FlxG.random.float(0.1, 0.2));
|
||||
// FlxG.sound.play('assets/sounds/missnote1' + TitleState.soundExt, 1, false);
|
||||
// FlxG.log.add('played imss note');
|
||||
|
@ -1283,7 +1306,7 @@ class PlayState extends MusicBeatState
|
|||
if (!boyfriend.animation.curAnim.name.startsWith("sing"))
|
||||
boyfriend.playAnim('idle');
|
||||
|
||||
if (totalBeats % 8 == 6)
|
||||
if (totalBeats % 8 == 7 && curSong == 'Bopeebo')
|
||||
{
|
||||
boyfriend.playAnim('hey', true);
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ class StoryMenuState extends MusicBeatState
|
|||
{
|
||||
var scoreText:FlxText;
|
||||
|
||||
var weekData:Array<Dynamic> = [['Tutorial', 'Bopeebo', 'Fresh', 'Dadbattle'], ['Spookeez', 'South', 'Monster']];
|
||||
var weekData:Array<Dynamic> = [['Tutorial', 'Bopeebo', 'Fresh', 'Dadbattle'], ['Spookeez', 'South']];
|
||||
var curDifficulty:Int = 1;
|
||||
|
||||
public static var weekUnlocked:Array<Bool> = [true, false];
|
||||
|
@ -38,6 +38,9 @@ class StoryMenuState extends MusicBeatState
|
|||
|
||||
override function create()
|
||||
{
|
||||
if (!FlxG.sound.music.playing)
|
||||
FlxG.sound.playMusic('assets/music/freakyMenu' + TitleState.soundExt);
|
||||
|
||||
persistentUpdate = persistentDraw = true;
|
||||
|
||||
scoreText = new FlxText(10, 10, 0, "SCORE: 49324858", 36);
|
||||
|
@ -102,6 +105,7 @@ class StoryMenuState extends MusicBeatState
|
|||
case 'bf':
|
||||
weekCharacterThing.setGraphicSize(Std.int(weekCharacterThing.width * 0.9));
|
||||
weekCharacterThing.updateHitbox();
|
||||
weekCharacterThing.x -= 80;
|
||||
case 'gf':
|
||||
weekCharacterThing.setGraphicSize(Std.int(weekCharacterThing.width * 0.5));
|
||||
weekCharacterThing.updateHitbox();
|
||||
|
@ -145,8 +149,8 @@ class StoryMenuState extends MusicBeatState
|
|||
txtTracklist.font = rankText.font;
|
||||
txtTracklist.color = 0xFFe55777;
|
||||
add(txtTracklist);
|
||||
add(rankText);
|
||||
add(scoreText);
|
||||
// add(rankText);
|
||||
// add(scoreText);
|
||||
|
||||
updateText();
|
||||
|
||||
|
@ -243,7 +247,8 @@ class StoryMenuState extends MusicBeatState
|
|||
PlayState.SONG = Song.loadFromJson(PlayState.storyPlaylist[0].toLowerCase() + diffic, PlayState.storyPlaylist[0].toLowerCase());
|
||||
new FlxTimer().start(1, function(tmr:FlxTimer)
|
||||
{
|
||||
FlxG.sound.music.stop();
|
||||
if (FlxG.sound.music != null)
|
||||
FlxG.sound.music.stop();
|
||||
FlxG.switchState(new PlayState());
|
||||
});
|
||||
}
|
||||
|
@ -274,9 +279,11 @@ class StoryMenuState extends MusicBeatState
|
|||
}
|
||||
|
||||
sprDifficulty.alpha = 0;
|
||||
sprDifficulty.y -= 15;
|
||||
|
||||
FlxTween.tween(sprDifficulty, {y: sprDifficulty.y + 15, alpha: 1}, 0.07);
|
||||
// USING THESE WEIRD VALUES SO THAT IT DOESNT FLOAT UP
|
||||
sprDifficulty.y = leftArrow.y - 15;
|
||||
|
||||
FlxTween.tween(sprDifficulty, {y: leftArrow.y + 15, alpha: 1}, 0.07);
|
||||
}
|
||||
|
||||
function changeWeek(change:Int = 0):Void
|
||||
|
|
|
@ -8,6 +8,7 @@ import flixel.addons.transition.FlxTransitionSprite.GraphicTransTileDiamond;
|
|||
import flixel.addons.transition.FlxTransitionableState;
|
||||
import flixel.addons.transition.TransitionData;
|
||||
import flixel.graphics.FlxGraphic;
|
||||
import flixel.graphics.frames.FlxAtlasFrames;
|
||||
import flixel.group.FlxGroup;
|
||||
import flixel.input.gamepad.FlxGamepad;
|
||||
import flixel.math.FlxPoint;
|
||||
|
@ -53,6 +54,10 @@ class TitleState extends MusicBeatState
|
|||
|
||||
super.create();
|
||||
|
||||
#if (!debug && NG_LOGIN)
|
||||
var ng:NGio = new NGio(APIStuff.API, APIStuff.EncKey);
|
||||
#end
|
||||
|
||||
#if SKIP_TO_PLAYSTATE
|
||||
FlxG.switchState(new StoryMenuState());
|
||||
#else
|
||||
|
@ -60,6 +65,11 @@ class TitleState extends MusicBeatState
|
|||
#end
|
||||
}
|
||||
|
||||
var logoBl:FlxSprite;
|
||||
var gfDance:FlxSprite;
|
||||
var danceLeft:Bool = false;
|
||||
var titleText:FlxSprite;
|
||||
|
||||
function startIntro()
|
||||
{
|
||||
if (!initialized)
|
||||
|
@ -82,28 +92,58 @@ class TitleState extends MusicBeatState
|
|||
FlxG.sound.playMusic('assets/music/freakyMenu' + TitleState.soundExt, 0);
|
||||
|
||||
FlxG.sound.music.fadeIn(4, 0, 0.7);
|
||||
|
||||
FlxG.save.bind('funkin', 'ninjamuffin99');
|
||||
|
||||
if (FlxG.save.data.weekUnlocked != null)
|
||||
{
|
||||
StoryMenuState.weekUnlocked = FlxG.save.data.weekUnlocked;
|
||||
}
|
||||
}
|
||||
|
||||
Conductor.changeBPM(102);
|
||||
persistentUpdate = true;
|
||||
|
||||
var bg:FlxSprite = new FlxSprite().loadGraphic(AssetPaths.stageback__png);
|
||||
bg.antialiasing = true;
|
||||
bg.setGraphicSize(Std.int(bg.width * 0.6));
|
||||
bg.updateHitbox();
|
||||
var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK);
|
||||
// bg.antialiasing = true;
|
||||
// bg.setGraphicSize(Std.int(bg.width * 0.6));
|
||||
// bg.updateHitbox();
|
||||
add(bg);
|
||||
|
||||
var logoBl:FlxSprite = new FlxSprite().loadGraphic(AssetPaths.logo__png);
|
||||
logoBl.screenCenter();
|
||||
logoBl.color = FlxColor.BLACK;
|
||||
logoBl = new FlxSprite(-150, -100);
|
||||
logoBl.frames = FlxAtlasFrames.fromSparrow(AssetPaths.logoBumpin__png, AssetPaths.logoBumpin__xml);
|
||||
logoBl.antialiasing = true;
|
||||
logoBl.animation.addByPrefix('bump', 'logo bumpin', 24);
|
||||
logoBl.animation.play('bump');
|
||||
logoBl.updateHitbox();
|
||||
// logoBl.screenCenter();
|
||||
// logoBl.color = FlxColor.BLACK;
|
||||
|
||||
gfDance = new FlxSprite(FlxG.width * 0.4, FlxG.height * 0.07);
|
||||
gfDance.frames = FlxAtlasFrames.fromSparrow(AssetPaths.gfDanceTitle__png, AssetPaths.gfDanceTitle__xml);
|
||||
gfDance.animation.addByIndices('danceLeft', 'gfDance', [30, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], "", 24, false);
|
||||
gfDance.animation.addByIndices('danceRight', 'gfDance', [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], "", 24, false);
|
||||
gfDance.antialiasing = true;
|
||||
add(gfDance);
|
||||
add(logoBl);
|
||||
|
||||
titleText = new FlxSprite(100, FlxG.height * 0.8);
|
||||
titleText.frames = FlxAtlasFrames.fromSparrow(AssetPaths.titleEnter__png, AssetPaths.titleEnter__xml);
|
||||
titleText.animation.addByPrefix('idle', "Press Enter to Begin", 24);
|
||||
titleText.animation.addByPrefix('press', "ENTER PRESSED", 24);
|
||||
titleText.antialiasing = true;
|
||||
titleText.animation.play('idle');
|
||||
titleText.updateHitbox();
|
||||
// titleText.screenCenter(X);
|
||||
add(titleText);
|
||||
|
||||
var logo:FlxSprite = new FlxSprite().loadGraphic(AssetPaths.logo__png);
|
||||
logo.screenCenter();
|
||||
logo.antialiasing = true;
|
||||
add(logo);
|
||||
// add(logo);
|
||||
|
||||
FlxTween.tween(logoBl, {y: logoBl.y + 50}, 0.6, {ease: FlxEase.quadInOut, type: PINGPONG});
|
||||
FlxTween.tween(logo, {y: logoBl.y + 50}, 0.6, {ease: FlxEase.quadInOut, type: PINGPONG, startDelay: 0.1});
|
||||
// FlxTween.tween(logoBl, {y: logoBl.y + 50}, 0.6, {ease: FlxEase.quadInOut, type: PINGPONG});
|
||||
// FlxTween.tween(logo, {y: logoBl.y + 50}, 0.6, {ease: FlxEase.quadInOut, type: PINGPONG, startDelay: 0.1});
|
||||
|
||||
credGroup = new FlxGroup();
|
||||
add(credGroup);
|
||||
|
@ -129,6 +169,8 @@ class TitleState extends MusicBeatState
|
|||
|
||||
FlxTween.tween(credTextShit, {y: credTextShit.y + 20}, 2.9, {ease: FlxEase.quadInOut, type: PINGPONG});
|
||||
|
||||
FlxG.mouse.visible = false;
|
||||
|
||||
if (initialized)
|
||||
skipIntro();
|
||||
else
|
||||
|
@ -155,6 +197,9 @@ class TitleState extends MusicBeatState
|
|||
|
||||
if (pressedEnter && !transitioning && skippedIntro)
|
||||
{
|
||||
NGio.unlockMedal(60960);
|
||||
titleText.animation.play('press');
|
||||
|
||||
FlxG.camera.flash(FlxColor.WHITE, 1);
|
||||
FlxG.sound.play('assets/sounds/confirmMenu' + TitleState.soundExt, 0.7);
|
||||
|
||||
|
@ -210,6 +255,14 @@ class TitleState extends MusicBeatState
|
|||
{
|
||||
super.beatHit();
|
||||
|
||||
logoBl.animation.play('bump');
|
||||
danceLeft = !danceLeft;
|
||||
|
||||
if (danceLeft)
|
||||
gfDance.animation.play('danceRight');
|
||||
else
|
||||
gfDance.animation.play('danceLeft');
|
||||
|
||||
FlxG.log.add(curBeat);
|
||||
|
||||
switch (curBeat)
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
package io.newgrounds;
|
||||
|
||||
import io.newgrounds.utils.Dispatcher;
|
||||
import io.newgrounds.utils.AsyncHttp;
|
||||
import io.newgrounds.objects.Error;
|
||||
import io.newgrounds.objects.events.Result;
|
||||
import io.newgrounds.objects.events.Result.ResultBase;
|
||||
import io.newgrounds.objects.events.Response;
|
||||
|
||||
import haxe.ds.StringMap;
|
||||
import haxe.Json;
|
||||
|
||||
/** A generic way to handle calls agnostic to their type */
|
||||
interface ICallable {
|
||||
|
||||
public var component(default, null):String;
|
||||
|
||||
public function send():Void;
|
||||
public function queue():Void;
|
||||
public function destroy():Void;
|
||||
}
|
||||
|
||||
class Call<T:ResultBase>
|
||||
implements ICallable {
|
||||
|
||||
public var component(default, null):String;
|
||||
|
||||
var _core:NGLite;
|
||||
var _properties:StringMap<Dynamic>;
|
||||
var _parameters:StringMap<Dynamic>;
|
||||
var _requireSession:Bool;
|
||||
var _isSecure:Bool;
|
||||
|
||||
// --- BASICALLY SIGNALS
|
||||
var _dataHandlers:TypedDispatcher<Response<T>>;
|
||||
var _successHandlers:Dispatcher;
|
||||
var _httpErrorHandlers:TypedDispatcher<Error>;
|
||||
var _statusHandlers:TypedDispatcher<Int>;
|
||||
|
||||
public function new (core:NGLite, component:String, requireSession:Bool = false, isSecure:Bool = false) {
|
||||
|
||||
_core = core;
|
||||
this.component = component;
|
||||
_requireSession = requireSession;
|
||||
_isSecure = isSecure && core.encryptionHandler != null;
|
||||
}
|
||||
|
||||
/** adds a property to the input's object. **/
|
||||
public function addProperty(name:String, value:Dynamic):Call<T> {
|
||||
|
||||
if (_properties == null)
|
||||
_properties = new StringMap<Dynamic>();
|
||||
|
||||
_properties.set(name, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/** adds a parameter to the call's component object. **/
|
||||
public function addComponentParameter(name:String, value:Dynamic, defaultValue:Dynamic = null):Call<T> {
|
||||
|
||||
if (value == defaultValue)//TODO?: allow sending null value
|
||||
return this;
|
||||
|
||||
if (_parameters == null)
|
||||
_parameters = new StringMap<Dynamic>();
|
||||
|
||||
_parameters.set(name, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Handy callback setter for chained call modifiers. Called when ng.io replies successfully */
|
||||
public function addDataHandler(handler:Response<T>->Void):Call<T> {
|
||||
|
||||
if (_dataHandlers == null)
|
||||
_dataHandlers = new TypedDispatcher<Response<T>>();
|
||||
|
||||
_dataHandlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Handy callback setter for chained call modifiers. Called when ng.io replies successfully */
|
||||
public function addSuccessHandler(handler:Void->Void):Call<T> {
|
||||
|
||||
if (_successHandlers == null)
|
||||
_successHandlers = new Dispatcher();
|
||||
|
||||
_successHandlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Handy callback setter for chained call modifiers. Called when ng.io does not reply for any reason */
|
||||
public function addErrorHandler(handler:Error->Void):Call<T> {
|
||||
|
||||
if (_httpErrorHandlers == null)
|
||||
_httpErrorHandlers = new TypedDispatcher<Error>();
|
||||
|
||||
_httpErrorHandlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Handy callback setter for chained call modifiers. No idea when this is called; */
|
||||
public function addStatusHandler(handler:Int->Void):Call<T> {//TODO:learn what this is for
|
||||
|
||||
if (_statusHandlers == null)
|
||||
_statusHandlers = new TypedDispatcher<Int>();
|
||||
|
||||
_statusHandlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the call to the server, do not modify this object after calling this
|
||||
* @param secure If encryption is enabled, it will encrypt the call.
|
||||
**/
|
||||
public function send():Void {
|
||||
|
||||
var data:Dynamic = {};
|
||||
data.app_id = _core.appId;
|
||||
data.call = {};
|
||||
data.call.component = component;
|
||||
|
||||
if (_core.debug)
|
||||
addProperty("debug", true);
|
||||
|
||||
if (_properties == null || !_properties.exists("session_id")) {
|
||||
// --- HAS NO SESSION ID
|
||||
|
||||
if (_core.sessionId != null) {
|
||||
// --- AUTO ADD SESSION ID
|
||||
|
||||
addProperty("session_id", _core.sessionId);
|
||||
|
||||
} else if (_requireSession){
|
||||
|
||||
_core.logError(new Error('cannot send "$component" call without a sessionId'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_properties != null) {
|
||||
|
||||
for (field in _properties.keys())
|
||||
Reflect.setField(data, field, _properties.get(field));
|
||||
}
|
||||
|
||||
if (_parameters != null) {
|
||||
|
||||
data.call.parameters = {};
|
||||
|
||||
for (field in _parameters.keys())
|
||||
Reflect.setField(data.call.parameters, field, _parameters.get(field));
|
||||
}
|
||||
|
||||
_core.logVerbose('Post - ${Json.stringify(data)}');
|
||||
|
||||
if (_isSecure) {
|
||||
|
||||
var secureData = _core.encryptionHandler(Json.stringify(data.call));
|
||||
data.call = {};
|
||||
data.call.secure = secureData;
|
||||
|
||||
_core.logVerbose(' secure - $secureData');
|
||||
}
|
||||
|
||||
_core.markCallPending(this);
|
||||
|
||||
AsyncHttp.send(_core, Json.stringify(data), onData, onHttpError, onStatus);
|
||||
}
|
||||
|
||||
/** Adds the call to the queue */
|
||||
public function queue():Void {
|
||||
|
||||
_core.queueCall(this);
|
||||
}
|
||||
|
||||
function onData(reply:String):Void {
|
||||
|
||||
_core.logVerbose('Reply - $reply');
|
||||
|
||||
if (_dataHandlers == null && _successHandlers == null)
|
||||
return;
|
||||
|
||||
var response = new Response<T>(_core, reply);
|
||||
|
||||
if (_dataHandlers != null)
|
||||
_dataHandlers.dispatch(response);
|
||||
|
||||
if (response.success && response.result.success && _successHandlers != null)
|
||||
_successHandlers.dispatch();
|
||||
|
||||
destroy();
|
||||
}
|
||||
|
||||
function onHttpError(message:String):Void {
|
||||
|
||||
_core.logError(message);
|
||||
|
||||
if (_httpErrorHandlers == null)
|
||||
return;
|
||||
|
||||
var error = new Error(message);
|
||||
_httpErrorHandlers.dispatch(error);
|
||||
}
|
||||
|
||||
function onStatus(status:Int):Void {
|
||||
|
||||
if (_statusHandlers == null)
|
||||
return;
|
||||
|
||||
_statusHandlers.dispatch(status);
|
||||
}
|
||||
|
||||
public function destroy():Void {
|
||||
|
||||
_core = null;
|
||||
|
||||
_properties = null;
|
||||
_parameters = null;
|
||||
|
||||
_dataHandlers = null;
|
||||
_successHandlers = null;
|
||||
_httpErrorHandlers = null;
|
||||
_statusHandlers = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,475 @@
|
|||
package io.newgrounds;
|
||||
|
||||
#if ng_lite
|
||||
typedef NG = NGLite; //TODO: test and make lite UI
|
||||
#else
|
||||
import io.newgrounds.utils.Dispatcher;
|
||||
import io.newgrounds.objects.Error;
|
||||
import io.newgrounds.objects.events.Result.SessionResult;
|
||||
import io.newgrounds.objects.events.Result.MedalListResult;
|
||||
import io.newgrounds.objects.events.Result.ScoreBoardResult;
|
||||
import io.newgrounds.objects.events.Response;
|
||||
import io.newgrounds.objects.User;
|
||||
import io.newgrounds.objects.Medal;
|
||||
import io.newgrounds.objects.Session;
|
||||
import io.newgrounds.objects.ScoreBoard;
|
||||
|
||||
import haxe.ds.IntMap;
|
||||
import haxe.Timer;
|
||||
|
||||
/**
|
||||
* The Newgrounds API for Haxe.
|
||||
* Contains many things ripped from MSGhero
|
||||
* - https://github.com/MSGhero/NG.hx
|
||||
* @author GeoKureli
|
||||
*/
|
||||
class NG extends NGLite {
|
||||
|
||||
static public var core(default, null):NG;
|
||||
static public var onCoreReady(default, null):Dispatcher = new Dispatcher();
|
||||
|
||||
// --- DATA
|
||||
|
||||
/** The logged in user */
|
||||
public var user(get, never):User;
|
||||
function get_user():User {
|
||||
|
||||
if (_session == null)
|
||||
return null;
|
||||
|
||||
return _session.user;
|
||||
}
|
||||
public var passportUrl(get, never):String;
|
||||
function get_passportUrl():String {
|
||||
|
||||
if (_session == null || _session.status != SessionStatus.REQUEST_LOGIN)
|
||||
return null;
|
||||
|
||||
return _session.passportUrl;
|
||||
}
|
||||
public var medals(default, null):IntMap<Medal>;
|
||||
public var scoreBoards(default, null):IntMap<ScoreBoard>;
|
||||
|
||||
// --- EVENTS
|
||||
|
||||
public var onLogin(default, null):Dispatcher;
|
||||
public var onLogOut(default, null):Dispatcher;
|
||||
public var onMedalsLoaded(default, null):Dispatcher;
|
||||
public var onScoreBoardsLoaded(default, null):Dispatcher;
|
||||
|
||||
// --- MISC
|
||||
|
||||
public var loggedIn(default, null):Bool;
|
||||
public var attemptingLogin(default, null):Bool;
|
||||
|
||||
var _loginCancelled:Bool;
|
||||
var _passportCallback:Void->Void;
|
||||
|
||||
var _session:Session;
|
||||
|
||||
/**
|
||||
* Iniitializes the API, call before utilizing any other component
|
||||
* @param appId The unique ID of your app as found in the 'API Tools' tab of your Newgrounds.com project.
|
||||
* @param sessionId A unique session id used to identify the active user.
|
||||
**/
|
||||
public function new(appId = "test", sessionId:String = null, ?onSessionFail:Error->Void) {
|
||||
|
||||
_session = new Session(this);
|
||||
onLogin = new Dispatcher();
|
||||
onLogOut = new Dispatcher();
|
||||
onMedalsLoaded = new Dispatcher();
|
||||
onScoreBoardsLoaded = new Dispatcher();
|
||||
|
||||
attemptingLogin = sessionId != null;
|
||||
|
||||
super(appId, sessionId, onSessionFail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates NG.core, the heart and soul of the API. This is not the only way to create an instance,
|
||||
* nor is NG a forced singleton, but it's the only way to set the static NG.core.
|
||||
**/
|
||||
static public function create(appId = "test", sessionId:String = null, ?onSessionFail:Error->Void):Void {
|
||||
|
||||
core = new NG(appId, sessionId, onSessionFail);
|
||||
|
||||
onCoreReady.dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates NG.core, and tries to create a session. This is not the only way to create an instance,
|
||||
* nor is NG a forced singleton, but it's the only way to set the static NG.core.
|
||||
**/
|
||||
static public function createAndCheckSession
|
||||
( appId = "test"
|
||||
, backupSession:String = null
|
||||
, ?onSessionFail:Error->Void
|
||||
):Void {
|
||||
|
||||
var session = NGLite.getSessionId();
|
||||
if (session == null)
|
||||
session = backupSession;
|
||||
|
||||
create(appId, session, onSessionFail);
|
||||
|
||||
core.host = getHost();
|
||||
if (core.sessionId != null)
|
||||
core.attemptingLogin = true;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// APP
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
override function checkInitialSession(failHandler:Error->Void, response:Response<SessionResult>):Void {
|
||||
|
||||
onSessionReceive(response, null, null, failHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins the login process
|
||||
*
|
||||
* @param onSuccess Called when the login is a success
|
||||
* @param onPending Called when the passportUrl has been identified, call NG.core.openPassportLink
|
||||
* to open the link continue the process. Leave as null to open the url automatically
|
||||
* NOTE: Browser games must open links on click events or else it will be blocked by
|
||||
* the popup blocker.
|
||||
* @param onFail
|
||||
* @param onCancel Called when the user denies the passport connection.
|
||||
*/
|
||||
public function requestLogin
|
||||
( onSuccess:Void->Void = null
|
||||
, onPending:Void->Void = null
|
||||
, onFail :Error->Void = null
|
||||
, onCancel :Void->Void = null
|
||||
):Void {
|
||||
|
||||
if (attemptingLogin) {
|
||||
|
||||
logError("cannot request another login until the previous attempt is complete");
|
||||
return;
|
||||
}
|
||||
|
||||
if (loggedIn) {
|
||||
|
||||
logError("cannot log in, already logged in");
|
||||
return;
|
||||
}
|
||||
|
||||
attemptingLogin = true;
|
||||
_loginCancelled = false;
|
||||
_passportCallback = null;
|
||||
|
||||
var call = calls.app.startSession(true)
|
||||
.addDataHandler(onSessionReceive.bind(_, onSuccess, onPending, onFail, onCancel));
|
||||
|
||||
if (onFail != null)
|
||||
call.addErrorHandler(onFail);
|
||||
|
||||
call.send();
|
||||
}
|
||||
|
||||
function onSessionReceive
|
||||
( response :Response<SessionResult>
|
||||
, onSuccess:Void->Void = null
|
||||
, onPending:Void->Void = null
|
||||
, onFail :Error->Void = null
|
||||
, onCancel :Void->Void = null
|
||||
):Void {
|
||||
|
||||
if (!response.success || !response.result.success) {
|
||||
|
||||
sessionId = null;
|
||||
endLoginAndCall(null);
|
||||
|
||||
if (onFail != null)
|
||||
onFail(!response.success ? response.error : response.result.error);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_session.parse(response.result.data.session);
|
||||
sessionId = _session.id;
|
||||
|
||||
logVerbose('session started - status: ${_session.status}');
|
||||
|
||||
if (_session.status == SessionStatus.REQUEST_LOGIN) {
|
||||
|
||||
_passportCallback = checkSession.bind(null, onSuccess, onCancel);
|
||||
if (onPending != null)
|
||||
onPending();
|
||||
else
|
||||
openPassportUrl();
|
||||
|
||||
} else
|
||||
checkSession(null, onSuccess, onCancel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this once the passport link is established and it will load the passport URL and
|
||||
* start checking for session connect periodically
|
||||
*/
|
||||
public function openPassportUrl():Void {
|
||||
|
||||
if (passportUrl != null) {
|
||||
|
||||
logVerbose('loading passport: ${passportUrl}');
|
||||
openPassportHelper(passportUrl);
|
||||
onPassportUrlOpen();
|
||||
|
||||
} else
|
||||
logError("Cannot open passport");
|
||||
}
|
||||
|
||||
|
||||
static function openPassportHelper(url:String):Void {
|
||||
var window = "_blank";
|
||||
|
||||
#if flash
|
||||
flash.Lib.getURL(new flash.net.URLRequest(url), window);
|
||||
#elseif (js && html5)
|
||||
js.Browser.window.open(url, window);
|
||||
#elseif desktop
|
||||
|
||||
#if (sys && windows)
|
||||
Sys.command("start", ["", url]);
|
||||
#elseif mac
|
||||
Sys.command("/usr/bin/open", [url]);
|
||||
#elseif linux
|
||||
Sys.command("/usr/bin/xdg-open", [path, "&"]);
|
||||
#end
|
||||
|
||||
#elseif android
|
||||
JNI.createStaticMethod
|
||||
( "org/haxe/lime/GameActivity"
|
||||
, "openURL"
|
||||
, "(Ljava/lang/String;Ljava/lang/String;)V"
|
||||
) (url, window);
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this once the passport link is established and it will start checking for session connect periodically
|
||||
*/
|
||||
public function onPassportUrlOpen():Void {
|
||||
|
||||
if (_passportCallback != null)
|
||||
_passportCallback();
|
||||
|
||||
_passportCallback = null;
|
||||
}
|
||||
|
||||
function checkSession(response:Response<SessionResult>, onSucceess:Void->Void, onCancel:Void->Void):Void {
|
||||
|
||||
if (response != null) {
|
||||
|
||||
if (!response.success || !response.result.success) {
|
||||
|
||||
log("login cancelled via passport");
|
||||
|
||||
endLoginAndCall(onCancel);
|
||||
return;
|
||||
}
|
||||
|
||||
_session.parse(response.result.data.session);
|
||||
}
|
||||
|
||||
if (_session.status == SessionStatus.USER_LOADED) {
|
||||
|
||||
loggedIn = true;
|
||||
endLoginAndCall(onSucceess);
|
||||
onLogin.dispatch();
|
||||
|
||||
} else if (_session.status == SessionStatus.REQUEST_LOGIN){
|
||||
|
||||
var call = calls.app.checkSession()
|
||||
.addDataHandler(checkSession.bind(_, onSucceess, onCancel));
|
||||
|
||||
// Wait 3 seconds and try again
|
||||
timer(3.0,
|
||||
function():Void {
|
||||
|
||||
// Check if cancelLoginRequest was called
|
||||
if (!_loginCancelled)
|
||||
call.send();
|
||||
else {
|
||||
|
||||
log("login cancelled via cancelLoginRequest");
|
||||
endLoginAndCall(onCancel);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
} else
|
||||
// The user cancelled the passport
|
||||
endLoginAndCall(onCancel);
|
||||
}
|
||||
|
||||
public function cancelLoginRequest():Void {
|
||||
|
||||
if (attemptingLogin)
|
||||
_loginCancelled = true;
|
||||
}
|
||||
|
||||
function endLoginAndCall(callback:Void->Void):Void {
|
||||
|
||||
attemptingLogin = false;
|
||||
_loginCancelled = false;
|
||||
|
||||
if (callback != null)
|
||||
callback();
|
||||
}
|
||||
|
||||
public function logOut(onComplete:Void->Void = null):Void {
|
||||
|
||||
var call = calls.app.endSession()
|
||||
.addSuccessHandler(onLogOutSuccessful);
|
||||
|
||||
if (onComplete != null)
|
||||
call.addSuccessHandler(onComplete);
|
||||
|
||||
call.addSuccessHandler(onLogOut.dispatch)
|
||||
.send();
|
||||
}
|
||||
|
||||
function onLogOutSuccessful():Void {
|
||||
|
||||
_session.expire();
|
||||
sessionId = null;
|
||||
loggedIn = false;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// MEDALS
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
public function requestMedals(onSuccess:Void->Void = null, onFail:Error->Void = null):Void {
|
||||
|
||||
var call = calls.medal.getList()
|
||||
.addDataHandler(onMedalsReceived);
|
||||
|
||||
if (onSuccess != null)
|
||||
call.addSuccessHandler(onSuccess);
|
||||
|
||||
if (onFail != null)
|
||||
call.addErrorHandler(onFail);
|
||||
|
||||
call.send();
|
||||
}
|
||||
|
||||
function onMedalsReceived(response:Response<MedalListResult>):Void {
|
||||
|
||||
if (!response.success || !response.result.success)
|
||||
return;
|
||||
|
||||
var idList:Array<Int> = new Array<Int>();
|
||||
|
||||
if (medals == null) {
|
||||
|
||||
medals = new IntMap<Medal>();
|
||||
|
||||
for (medalData in response.result.data.medals) {
|
||||
|
||||
var medal = new Medal(this, medalData);
|
||||
medals.set(medal.id, medal);
|
||||
idList.push(medal.id);
|
||||
}
|
||||
} else {
|
||||
|
||||
for (medalData in response.result.data.medals) {
|
||||
|
||||
medals.get(medalData.id).parse(medalData);
|
||||
idList.push(medalData.id);
|
||||
}
|
||||
}
|
||||
|
||||
logVerbose('${response.result.data.medals.length} Medals received [${idList.join(", ")}]');
|
||||
|
||||
onMedalsLoaded.dispatch();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// SCOREBOARDS
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
public function requestScoreBoards(onSuccess:Void->Void = null, onFail:Error->Void = null):Void {
|
||||
|
||||
if (scoreBoards != null) {
|
||||
|
||||
log("aborting scoreboard request, all scoreboards are loaded");
|
||||
|
||||
if (onSuccess != null)
|
||||
onSuccess();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var call = calls.scoreBoard.getBoards()
|
||||
.addDataHandler(onBoardsReceived);
|
||||
|
||||
if (onSuccess != null)
|
||||
call.addSuccessHandler(onSuccess);
|
||||
|
||||
if (onFail != null)
|
||||
call.addErrorHandler(onFail);
|
||||
|
||||
call.send();
|
||||
}
|
||||
|
||||
function onBoardsReceived(response:Response<ScoreBoardResult>):Void {
|
||||
|
||||
if (!response.success || !response.result.success)
|
||||
return;
|
||||
|
||||
var idList:Array<Int> = new Array<Int>();
|
||||
|
||||
if (scoreBoards == null) {
|
||||
|
||||
scoreBoards = new IntMap<ScoreBoard>();
|
||||
|
||||
for (boardData in response.result.data.scoreboards) {
|
||||
|
||||
var board = new ScoreBoard(this, boardData);
|
||||
scoreBoards.set(board.id, board);
|
||||
idList.push(board.id);
|
||||
}
|
||||
}
|
||||
|
||||
logVerbose('${response.result.data.scoreboards.length} ScoreBoards received [${idList.join(", ")}]');
|
||||
|
||||
onScoreBoardsLoaded.dispatch();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// HELPERS
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
function timer(delay:Float, callback:Void->Void):Void {
|
||||
|
||||
var timer = new Timer(Std.int(delay * 1000));
|
||||
timer.run = function func():Void {
|
||||
|
||||
timer.stop();
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
static var urlParser:EReg = ~/^(?:http[s]?:\/\/)?([^:\/\s]+)(:[0-9]+)?((?:\/\w+)*\/)([\w\-\.]+[^#?\s]+)([^#\s]*)?(#[\w\-]+)?$/i;//TODO:trim
|
||||
/** Used to get the current web host of your game. */
|
||||
static public function getHost():String {
|
||||
|
||||
var url = NGLite.getUrl();
|
||||
|
||||
if (url == null || url == "")
|
||||
return "<AppView>";
|
||||
|
||||
if (url.indexOf("file") == 0)
|
||||
return "<LocalHost>";
|
||||
|
||||
if (urlParser.match(url))
|
||||
return urlParser.matched(1);
|
||||
|
||||
return "<Unknown>";
|
||||
}
|
||||
}
|
||||
#end
|
|
@ -0,0 +1,287 @@
|
|||
package io.newgrounds;
|
||||
|
||||
import haxe.crypto.Base64;
|
||||
import haxe.io.Bytes;
|
||||
import haxe.PosInfos;
|
||||
|
||||
import io.newgrounds.Call.ICallable;
|
||||
import io.newgrounds.components.ComponentList;
|
||||
import io.newgrounds.crypto.EncryptionFormat;
|
||||
import io.newgrounds.crypto.Cipher;
|
||||
import io.newgrounds.crypto.Rc4;
|
||||
import io.newgrounds.objects.Error;
|
||||
import io.newgrounds.objects.events.Response;
|
||||
import io.newgrounds.objects.events.Result.ResultBase;
|
||||
import io.newgrounds.objects.events.Result.SessionResult;
|
||||
import io.newgrounds.utils.Dispatcher;
|
||||
|
||||
#if !(html5 || flash || desktop || neko)
|
||||
#error "Target not supported, use: Flash, JS/HTML5, cpp or maybe neko";
|
||||
#end
|
||||
|
||||
/**
|
||||
* The barebones NG.io API. Allows API calls with code completion
|
||||
* and retrieves server data via strongly typed Objects
|
||||
*
|
||||
* Contains many things ripped from MSGhero's repo
|
||||
* - https://github.com/MSGhero/NG.hx
|
||||
*
|
||||
* @author GeoKureli
|
||||
*/
|
||||
class NGLite {
|
||||
|
||||
static public var core(default, null):NGLite;
|
||||
static public var onCoreReady(default, null):Dispatcher = new Dispatcher();
|
||||
|
||||
/** Enables verbose logging */
|
||||
public var verbose:Bool;
|
||||
public var debug:Bool;
|
||||
/** The unique ID of your app as found in the 'API Tools' tab of your Newgrounds.com project. */
|
||||
public var appId(default, null):String;
|
||||
/** The name of the host the game is being played on */
|
||||
public var host:String;
|
||||
|
||||
@:isVar
|
||||
public var sessionId(default, set):String;
|
||||
function set_sessionId(value:String):String {
|
||||
|
||||
return this.sessionId = value == "" ? null : value;
|
||||
}
|
||||
|
||||
/** Components used to call the NG server directly */
|
||||
public var calls(default, null):ComponentList;
|
||||
|
||||
/**
|
||||
* Converts an object to an encrypted string that can be decrypted by the server.
|
||||
* Set your preffered encrypter here,
|
||||
* or just call setDefaultEcryptionHandler with your app's encryption settings
|
||||
**/
|
||||
public var encryptionHandler:String->String;
|
||||
|
||||
/**
|
||||
* Iniitializes the API, call before utilizing any other component
|
||||
* @param appId The unique ID of your app as found in the 'API Tools' tab of your Newgrounds.com project.
|
||||
* @param sessionId A unique session id used to identify the active user.
|
||||
**/
|
||||
public function new(appId = "test", sessionId:String = null, ?onSessionFail:Error->Void) {
|
||||
|
||||
this.appId = appId;
|
||||
this.sessionId = sessionId;
|
||||
|
||||
calls = new ComponentList(this);
|
||||
|
||||
if (this.sessionId != null) {
|
||||
|
||||
calls.app.checkSession()
|
||||
.addDataHandler(checkInitialSession.bind(onSessionFail))
|
||||
.addErrorHandler(initialSessionFail.bind(onSessionFail))
|
||||
.send();
|
||||
}
|
||||
}
|
||||
|
||||
function checkInitialSession(onFail:Error->Void, response:Response<SessionResult>):Void {
|
||||
|
||||
if (!response.success || !response.result.success || response.result.data.session.expired) {
|
||||
|
||||
initialSessionFail(onFail, response.success ? response.result.error : response.error);
|
||||
}
|
||||
}
|
||||
|
||||
function initialSessionFail(onFail:Error->Void, error:Error):Void {
|
||||
|
||||
sessionId = null;
|
||||
|
||||
if (onFail != null)
|
||||
onFail(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates NG.core, the heart and soul of the API. This is not the only way to create an instance,
|
||||
* nor is NG a forced singleton, but it's the only way to set the static NG.core.
|
||||
**/
|
||||
static public function create(appId = "test", sessionId:String = null, ?onSessionFail:Error->Void):Void {
|
||||
|
||||
core = new NGLite(appId, sessionId, onSessionFail);
|
||||
|
||||
onCoreReady.dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates NG.core, and tries to create a session. This is not the only way to create an instance,
|
||||
* nor is NG a forced singleton, but it's the only way to set the static NG.core.
|
||||
**/
|
||||
static public function createAndCheckSession
|
||||
( appId = "test"
|
||||
, backupSession:String = null
|
||||
, ?onSessionFail:Error->Void
|
||||
):Void {
|
||||
|
||||
var session = getSessionId();
|
||||
if (session == null)
|
||||
session = backupSession;
|
||||
|
||||
create(appId, session, onSessionFail);
|
||||
}
|
||||
|
||||
inline static public function getUrl():String {
|
||||
|
||||
#if html5
|
||||
return js.Browser.document.location.href;
|
||||
#elseif flash
|
||||
return flash.Lib.current.stage.loaderInfo != null
|
||||
? flash.Lib.current.stage.loaderInfo.url
|
||||
: null;
|
||||
#else
|
||||
return null;
|
||||
#end
|
||||
}
|
||||
|
||||
static public function getSessionId():String {
|
||||
|
||||
#if html5
|
||||
|
||||
var url = getUrl();
|
||||
|
||||
// Check for URL params
|
||||
var index = url.indexOf("?");
|
||||
if (index != -1) {
|
||||
|
||||
// Check for session ID in params
|
||||
for (param in url.substr(index + 1).split("&")) {
|
||||
|
||||
index = param.indexOf("=");
|
||||
if (index != -1 && param.substr(0, index) == "ngio_session_id")
|
||||
return param.substr(index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
#elseif flash
|
||||
|
||||
if (flash.Lib.current.stage.loaderInfo != null
|
||||
&& Reflect.hasField(flash.Lib.current.stage.loaderInfo.parameters, "ngio_session_id"))
|
||||
return Reflect.field(flash.Lib.current.stage.loaderInfo.parameters, "ngio_session_id");
|
||||
|
||||
#end
|
||||
|
||||
return null;
|
||||
|
||||
// --- EXAMPLE LOADER PARAMS
|
||||
//{ "1517703669" : ""
|
||||
//, "ng_username" : "GeoKureli"
|
||||
//, "NewgroundsAPI_SessionID" : "F1LusbG6P8Qf91w7zeUE37c1752563f366688ac6153996d12eeb111a2f60w2xn"
|
||||
//, "NewgroundsAPI_PublisherID" : 1
|
||||
//, "NewgroundsAPI_UserID" : 488329
|
||||
//, "NewgroundsAPI_SandboxID" : "5a76520e4ae1e"
|
||||
//, "ngio_session_id" : "0c6c4e02567a5116734ba1a0cd841dac28a42e79302290"
|
||||
//, "NewgroundsAPI_UserName" : "GeoKureli"
|
||||
//}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// CALLS
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
var _queuedCalls:Array<ICallable> = new Array<ICallable>();
|
||||
var _pendingCalls:Array<ICallable> = new Array<ICallable>();
|
||||
|
||||
@:allow(io.newgrounds.Call)
|
||||
@:generic
|
||||
function queueCall<T:ResultBase>(call:Call<T>):Void {
|
||||
|
||||
logVerbose('queued - ${call.component}');
|
||||
|
||||
_queuedCalls.push(call);
|
||||
checkQueue();
|
||||
}
|
||||
|
||||
@:allow(io.newgrounds.Call)
|
||||
@:generic
|
||||
function markCallPending<T:ResultBase>(call:Call<T>):Void {
|
||||
|
||||
_pendingCalls.push(call);
|
||||
|
||||
call.addDataHandler(function (_):Void { onCallComplete(call); });
|
||||
call.addErrorHandler(function (_):Void { onCallComplete(call); });
|
||||
}
|
||||
|
||||
function onCallComplete(call:ICallable):Void {
|
||||
|
||||
_pendingCalls.remove(call);
|
||||
checkQueue();
|
||||
}
|
||||
|
||||
function checkQueue():Void {
|
||||
|
||||
if (_pendingCalls.length == 0 && _queuedCalls.length > 0)
|
||||
_queuedCalls.shift().send();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// LOGGING / ERRORS
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
/** Called internally, set this to your preferred logging method */
|
||||
dynamic public function log(any:Dynamic, ?pos:PosInfos):Void {//TODO: limit access via @:allow
|
||||
|
||||
haxe.Log.trace('[Newgrounds API] :: ${any}', pos);
|
||||
}
|
||||
|
||||
/** used internally, logs if verbose is true */
|
||||
inline public function logVerbose(any:Dynamic, ?pos:PosInfos):Void {//TODO: limit access via @:allow
|
||||
|
||||
if (verbose)
|
||||
log(any, pos);
|
||||
}
|
||||
|
||||
/** Used internally. Logs by default, set this to your preferred error handling method */
|
||||
dynamic public function logError(any:Dynamic, ?pos:PosInfos):Void {//TODO: limit access via @:allow
|
||||
|
||||
log('Error: $any', pos);
|
||||
}
|
||||
|
||||
/** used internally, calls log error if the condition is false. EX: if (assert(data != null, "null data")) */
|
||||
inline public function assert(condition:Bool, msg:Dynamic, ?pos:PosInfos):Bool {//TODO: limit access via @:allow
|
||||
if (!condition)
|
||||
logError(msg, pos);
|
||||
|
||||
return condition;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// ENCRYPTION
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
/** Sets */
|
||||
public function initEncryption
|
||||
( key :String
|
||||
, cipher:Cipher = Cipher.RC4
|
||||
, format:EncryptionFormat = EncryptionFormat.BASE_64
|
||||
):Void {
|
||||
|
||||
if (cipher == Cipher.NONE)
|
||||
encryptionHandler = null;
|
||||
else if (cipher == Cipher.RC4)
|
||||
encryptionHandler = encryptRc4.bind(key, format);
|
||||
else
|
||||
throw "aes not yet implemented";
|
||||
}
|
||||
|
||||
function encryptRc4(key:String, format:EncryptionFormat, data:String):String {
|
||||
|
||||
if (format == EncryptionFormat.HEX)
|
||||
throw "hex format not yet implemented";
|
||||
|
||||
var keyBytes:Bytes;
|
||||
if (format == EncryptionFormat.BASE_64)
|
||||
keyBytes = Base64.decode(key);
|
||||
else
|
||||
keyBytes = null;//TODO
|
||||
|
||||
var dataBytes = new Rc4(keyBytes).crypt(Bytes.ofString(data));
|
||||
|
||||
if (format == EncryptionFormat.BASE_64)
|
||||
return Base64.encode(dataBytes);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package io.newgrounds.components;
|
||||
|
||||
import io.newgrounds.objects.events.Result;
|
||||
import io.newgrounds.objects.events.Result.SessionResult;
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
class AppComponent extends Component {
|
||||
|
||||
public function new (core:NGLite) { super(core); }
|
||||
|
||||
public function startSession(force:Bool = false):Call<SessionResult> {
|
||||
|
||||
return new Call<SessionResult>(_core, "App.startSession")
|
||||
.addComponentParameter("force", force, false);
|
||||
}
|
||||
|
||||
public function checkSession():Call<SessionResult> {
|
||||
|
||||
return new Call<SessionResult>(_core, "App.checkSession", true);
|
||||
}
|
||||
|
||||
public function endSession():Call<SessionResult> {
|
||||
|
||||
return new Call<SessionResult>(_core, "App.endSession", true);
|
||||
}
|
||||
|
||||
public function getCurrentVersion(version:String):Call<GetCurrentVersionResult> {
|
||||
|
||||
return new Call<GetCurrentVersionResult>(_core, "App.getCurrentVersion")
|
||||
.addComponentParameter("version", version);
|
||||
}
|
||||
|
||||
public function getHostLicense():Call<GetHostResult> {
|
||||
|
||||
return new Call<GetHostResult>(_core, "App.getHostLicense")
|
||||
.addComponentParameter("host", _core.host);
|
||||
}
|
||||
|
||||
public function logView():Call<ResultBase> {
|
||||
|
||||
return new Call<ResultBase>(_core, "App.logView")
|
||||
.addComponentParameter("host", _core.host);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package io.newgrounds.components;
|
||||
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
class Component {
|
||||
|
||||
var _core:NGLite;
|
||||
|
||||
public function new(core:NGLite) {
|
||||
|
||||
this._core = core;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package io.newgrounds.components;
|
||||
class ComponentList {
|
||||
|
||||
var _core:NGLite;
|
||||
|
||||
// --- COMPONENTS
|
||||
public var medal : MedalComponent;
|
||||
public var app : AppComponent;
|
||||
public var event : EventComponent;
|
||||
public var scoreBoard: ScoreBoardComponent;
|
||||
public var loader : LoaderComponent;
|
||||
public var gateway : GatewayComponent;
|
||||
|
||||
public function new(core:NGLite) {
|
||||
|
||||
_core = core;
|
||||
|
||||
medal = new MedalComponent (_core);
|
||||
app = new AppComponent (_core);
|
||||
event = new EventComponent (_core);
|
||||
scoreBoard = new ScoreBoardComponent(_core);
|
||||
loader = new LoaderComponent (_core);
|
||||
gateway = new GatewayComponent (_core);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package io.newgrounds.components;
|
||||
|
||||
import io.newgrounds.objects.events.Result.LogEventResult;
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
class EventComponent extends Component {
|
||||
|
||||
public function new (core:NGLite){ super(core); }
|
||||
|
||||
public function logEvent(eventName:String):Call<LogEventResult> {
|
||||
|
||||
return new Call<LogEventResult>(_core, "Event.logEvent")
|
||||
.addComponentParameter("event_name", eventName)
|
||||
.addComponentParameter("host", _core.host);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package io.newgrounds.components;
|
||||
|
||||
import io.newgrounds.objects.events.Result;
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
class GatewayComponent extends Component {
|
||||
|
||||
public function new (core:NGLite){ super(core); }
|
||||
|
||||
public function getDatetime():Call<GetDateTimeResult> {
|
||||
|
||||
return new Call<GetDateTimeResult>(_core, "Gateway.getDatetime");
|
||||
}
|
||||
|
||||
public function getVersion():Call<GetVersionResult> {
|
||||
|
||||
return new Call<GetVersionResult>(_core, "Gateway.getVersion");
|
||||
}
|
||||
|
||||
public function ping():Call<PingResult> {
|
||||
|
||||
return new Call<PingResult>(_core, "Gateway.ping");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package io.newgrounds.components;
|
||||
|
||||
import io.newgrounds.objects.events.Result;
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
class LoaderComponent extends Component {
|
||||
|
||||
public function new (core:NGLite){ super(core); }
|
||||
|
||||
public function loadAuthorUrl(redirect:Bool = false):Call<ResultBase> {
|
||||
|
||||
return new Call<ResultBase>(_core, "Loader.loadAuthorUrl")
|
||||
.addComponentParameter("host", _core.host)
|
||||
.addComponentParameter("redirect", redirect, true);
|
||||
}
|
||||
|
||||
public function loadMoreGames(redirect:Bool = false):Call<ResultBase> {
|
||||
|
||||
return new Call<ResultBase>(_core, "Loader.loadMoreGames")
|
||||
.addComponentParameter("host", _core.host)
|
||||
.addComponentParameter("redirect", redirect, true);
|
||||
}
|
||||
|
||||
public function loadNewgrounds(redirect:Bool = false):Call<ResultBase> {
|
||||
|
||||
return new Call<ResultBase>(_core, "Loader.loadNewgrounds")
|
||||
.addComponentParameter("host", _core.host)
|
||||
.addComponentParameter("redirect", redirect, true);
|
||||
}
|
||||
|
||||
public function loadOfficialUrl(redirect:Bool = false):Call<ResultBase> {
|
||||
|
||||
return new Call<ResultBase>(_core, "Loader.loadOfficialUrl")
|
||||
.addComponentParameter("host", _core.host)
|
||||
.addComponentParameter("redirect", redirect, true);
|
||||
}
|
||||
|
||||
public function loadReferral(redirect:Bool = false):Call<ResultBase> {
|
||||
|
||||
return new Call<ResultBase>(_core, "Loader.loadReferral")
|
||||
.addComponentParameter("host", _core.host)
|
||||
.addComponentParameter("redirect", redirect, true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package io.newgrounds.components;
|
||||
|
||||
import io.newgrounds.objects.events.Result;
|
||||
import io.newgrounds.Call;
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
class MedalComponent extends Component {
|
||||
|
||||
public function new(core:NGLite):Void { super(core); }
|
||||
|
||||
public function unlock(id:Int):Call<MedalUnlockResult> {
|
||||
|
||||
return new Call<MedalUnlockResult>(_core, "Medal.unlock", true, true)
|
||||
.addComponentParameter("id", id);
|
||||
}
|
||||
|
||||
public function getList():Call<MedalListResult> {
|
||||
|
||||
return new Call<MedalListResult>(_core, "Medal.getList");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
package io.newgrounds.components;
|
||||
|
||||
import io.newgrounds.objects.User;
|
||||
import io.newgrounds.objects.events.Response;
|
||||
import io.newgrounds.objects.events.Result;
|
||||
import io.newgrounds.objects.events.Result.ScoreBoardResult;
|
||||
import io.newgrounds.objects.events.Result.ScoreResult;
|
||||
import io.newgrounds.NGLite;
|
||||
import io.newgrounds.objects.ScoreBoard;
|
||||
|
||||
import haxe.ds.IntMap;
|
||||
|
||||
class ScoreBoardComponent extends Component {
|
||||
|
||||
public var allById:IntMap<ScoreBoard>;
|
||||
|
||||
public function new (core:NGLite){ super(core); }
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// GET SCORES
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
public function getBoards():Call<ScoreBoardResult> {
|
||||
|
||||
return new Call<ScoreBoardResult>(_core, "ScoreBoard.getBoards");
|
||||
}
|
||||
|
||||
/*function onBoardsReceive(response:Response<ScoreBoardResult>):Void {
|
||||
|
||||
if (!response.result.success)
|
||||
return;
|
||||
|
||||
allById = new IntMap<ScoreBoard>();
|
||||
|
||||
for (boardData in response.result.scoreboards)
|
||||
createBoard(boardData);
|
||||
|
||||
_core.log('${response.result.scoreboards.length} ScoreBoards loaded');
|
||||
}*/
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// GET SCORES
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
public function getScores
|
||||
( id :Int
|
||||
, limit :Int = 10
|
||||
, skip :Int = 0
|
||||
, period:Period = Period.DAY
|
||||
, social:Bool = false
|
||||
, tag :String = null
|
||||
, user :Dynamic = null
|
||||
):Call<ScoreResult> {
|
||||
|
||||
if (user != null && !Std.is(user, String) && !Std.is(user, Int))
|
||||
user = user.id;
|
||||
|
||||
return new Call<ScoreResult>(_core, "ScoreBoard.getScores")
|
||||
.addComponentParameter("id" , id )
|
||||
.addComponentParameter("limit" , limit , 10)
|
||||
.addComponentParameter("skip" , skip , 0)
|
||||
.addComponentParameter("period", period, Period.DAY)
|
||||
.addComponentParameter("social", social, false)
|
||||
.addComponentParameter("tag" , tag , null)
|
||||
.addComponentParameter("user" , user , null);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// POST SCORE
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
public function postScore(id:Int, value:Int, tag:String = null):Call<PostScoreResult> {
|
||||
|
||||
return new Call<PostScoreResult>(_core, "ScoreBoard.postScore", true, true)
|
||||
.addComponentParameter("id" , id)
|
||||
.addComponentParameter("value", value)
|
||||
.addComponentParameter("tag" , tag , null);
|
||||
}
|
||||
|
||||
/*function onScorePosted(response:Response<ResultBase>):Void {
|
||||
|
||||
if (!response.result.success)
|
||||
return;
|
||||
|
||||
allById = new IntMap<ScoreBoard>();
|
||||
|
||||
//createBoard(data.data.scoreBoard).parseScores(data.data.scores);
|
||||
}*/
|
||||
|
||||
inline function createBoard(data:Dynamic):ScoreBoard {
|
||||
|
||||
var board = new ScoreBoard(_core, data);
|
||||
_core.logVerbose('created $board');
|
||||
|
||||
allById.set(board.id, board);
|
||||
|
||||
return board;
|
||||
}
|
||||
}
|
||||
|
||||
@:enum
|
||||
abstract Period(String) to String from String{
|
||||
|
||||
/** Indicates scores are from the current day. */
|
||||
var DAY = "D";
|
||||
/** Indicates scores are from the current week. */
|
||||
var WEEK = "W";
|
||||
/** Indicates scores are from the current month. */
|
||||
var MONTH = "M";
|
||||
/** Indicates scores are from the current year. */
|
||||
var YEAR = "Y";
|
||||
/** Indicates scores are from all-time. */
|
||||
var ALL = "A";
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package io.newgrounds.crypto;
|
||||
|
||||
@:enum
|
||||
abstract Cipher(String) to String{
|
||||
var NONE = "none";
|
||||
var AES_128 = "aes128";
|
||||
var RC4 = "rc4";
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package io.newgrounds.crypto;
|
||||
|
||||
@:enum
|
||||
abstract EncryptionFormat(String) to String {
|
||||
var BASE_64 = "base64";
|
||||
var HEX = "hex";
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package io.newgrounds.crypto;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
|
||||
/**
|
||||
* The following was straight-up ganked from https://github.com/iskolbin/rc4hx
|
||||
*
|
||||
* You da real MVP iskolbin...
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 iskolbin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
**/
|
||||
class Rc4 {
|
||||
var perm = Bytes.alloc( 256 );
|
||||
var index1: Int = 0;
|
||||
var index2: Int = 0;
|
||||
|
||||
public function new( key: Bytes ) {
|
||||
for ( i in 0...256 ) {
|
||||
perm.set( i, i );
|
||||
}
|
||||
|
||||
var j: Int = 0;
|
||||
for ( i in 0...256 ) {
|
||||
j = ( j + perm.get( i ) + key.get( i % key.length )) % 256;
|
||||
swap( i, j );
|
||||
}
|
||||
}
|
||||
|
||||
inline function swap( i: Int, j: Int ): Void {
|
||||
var temp = perm.get( i );
|
||||
perm.set( i, perm.get( j ));
|
||||
perm.set( j, temp );
|
||||
}
|
||||
|
||||
public function crypt( input: Bytes ): Bytes {
|
||||
var output = Bytes.alloc( input.length );
|
||||
|
||||
for ( i in 0...input.length ) {
|
||||
index1 = ( index1 + 1 ) % 256;
|
||||
index2 = ( index2 + perm.get( index1 )) % 256;
|
||||
swap( index1, index2 );
|
||||
var j = ( perm.get( index1 ) + perm.get( index2 )) % 256;
|
||||
output.set( i, input.get( i ) ^ perm.get( j ));
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package io.newgrounds.objects;
|
||||
class Error {
|
||||
|
||||
public var code(default, null):Int;
|
||||
public var message(default, null):String;
|
||||
|
||||
public function new (message:String, code:Int = 0) {
|
||||
|
||||
this.message = message;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
|
||||
if (code > 0)
|
||||
return '#$code - $message';
|
||||
|
||||
return message;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
package io.newgrounds.objects;
|
||||
|
||||
import io.newgrounds.objects.events.Response;
|
||||
import io.newgrounds.objects.events.Result.MedalUnlockResult;
|
||||
import io.newgrounds.utils.Dispatcher;
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
class Medal extends Object {
|
||||
|
||||
inline static public var EASY :Int = 1;
|
||||
inline static public var MODERATE :Int = 2;
|
||||
inline static public var CHALLENGING:Int = 3;
|
||||
inline static public var DIFFICULT :Int = 4;
|
||||
inline static public var BRUTAL :Int = 5;
|
||||
|
||||
static var difficultyNames:Array<String> =
|
||||
[ "Easy"
|
||||
, "Moderate"
|
||||
, "Challenging"
|
||||
, "Difficult"
|
||||
, "Brutal"
|
||||
];
|
||||
// --- FROM SERVER
|
||||
public var id (default, null):Int;
|
||||
public var name (default, null):String;
|
||||
public var description(default, null):String;
|
||||
public var icon (default, null):String;
|
||||
public var value (default, null):Int;
|
||||
public var difficulty (default, null):Int;
|
||||
public var secret (default, null):Bool;
|
||||
public var unlocked (default, null):Bool;
|
||||
// --- HELPERS
|
||||
public var difficultyName(get, never):String;
|
||||
|
||||
public var onUnlock:Dispatcher;
|
||||
|
||||
public function new(core:NGLite, data:Dynamic = null):Void {
|
||||
|
||||
onUnlock = new Dispatcher();
|
||||
|
||||
super(core, data);
|
||||
}
|
||||
|
||||
@:allow(io.newgrounds.NG)
|
||||
override function parse(data:Dynamic):Void {
|
||||
|
||||
var wasLocked = !unlocked;
|
||||
|
||||
id = data.id;
|
||||
name = data.name;
|
||||
description = data.description;
|
||||
icon = data.icon;
|
||||
value = data.value;
|
||||
difficulty = data.difficulty;
|
||||
secret = data.secret == 1;
|
||||
unlocked = data.unlocked;
|
||||
|
||||
super.parse(data);
|
||||
|
||||
if (wasLocked && unlocked)
|
||||
onUnlock.dispatch();
|
||||
|
||||
}
|
||||
|
||||
public function sendUnlock():Void {
|
||||
|
||||
if (_core.sessionId == null) {
|
||||
// --- Unlock regardless, show medal popup to encourage NG signup
|
||||
unlocked = true;
|
||||
onUnlock.dispatch();
|
||||
//TODO: save unlock in local save
|
||||
}
|
||||
|
||||
_core.calls.medal.unlock(id)
|
||||
.addDataHandler(onUnlockResponse)
|
||||
.send();
|
||||
}
|
||||
|
||||
function onUnlockResponse(response:Response<MedalUnlockResult>):Void {
|
||||
|
||||
if (response.success && response.result.success) {
|
||||
|
||||
parse(response.result.data.medal);
|
||||
|
||||
// --- Unlock response doesn't include unlock=true, so parse won't change it.
|
||||
if (!unlocked) {
|
||||
|
||||
unlocked = true;
|
||||
onUnlock.dispatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Locks the medal on the client and sends an unlock request, Server responds the same either way. */
|
||||
public function sendDebugUnlock():Void {
|
||||
|
||||
if (NG.core.sessionId == null) {
|
||||
|
||||
onUnlock.dispatch();
|
||||
|
||||
} else {
|
||||
|
||||
unlocked = false;
|
||||
|
||||
sendUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
public function get_difficultyName():String {
|
||||
|
||||
return difficultyNames[difficulty - 1];
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
|
||||
return 'Medal: $id@$name (${unlocked ? "unlocked" : "locked"}, $value pts, $difficultyName).';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package io.newgrounds.objects;
|
||||
|
||||
import io.newgrounds.utils.Dispatcher;
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
class Object {
|
||||
|
||||
var _core:NGLite;
|
||||
|
||||
public var onUpdate(default, null):Dispatcher;
|
||||
|
||||
public function new(core:NGLite, data:Dynamic = null) {
|
||||
|
||||
this._core = core;
|
||||
|
||||
onUpdate = new Dispatcher();
|
||||
|
||||
if (data != null)
|
||||
parse(data);
|
||||
}
|
||||
|
||||
@:allow(io.newgrounds.NGLite)
|
||||
function parse(data:Dynamic):Void {
|
||||
|
||||
onUpdate.dispatch();
|
||||
}
|
||||
|
||||
|
||||
public function destroy():Void {
|
||||
|
||||
_core = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package io.newgrounds.objects;
|
||||
|
||||
/** We don't want to serialize scores since there's a bajillion of them. */
|
||||
typedef Score = {
|
||||
|
||||
/** The value value in the format selected in your scoreboard settings. */
|
||||
var formatted_value:String;
|
||||
|
||||
/** The tag attached to this value (if any). */
|
||||
var tag:String;
|
||||
|
||||
/** The user who earned value. If this property is absent, the value belongs to the active user. */
|
||||
var user:User;
|
||||
|
||||
/** The integer value of the value. */
|
||||
var value:Int;
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package io.newgrounds.objects;
|
||||
|
||||
import io.newgrounds.components.ScoreBoardComponent.Period;
|
||||
import io.newgrounds.objects.events.Response;
|
||||
import io.newgrounds.objects.events.Result;
|
||||
import io.newgrounds.objects.events.Result.ScoreResult;
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
class ScoreBoard extends Object {
|
||||
|
||||
public var scores(default, null):Array<Score>;
|
||||
|
||||
/** The numeric ID of the scoreboard.*/
|
||||
public var id(default, null):Int;
|
||||
|
||||
/** The name of the scoreboard. */
|
||||
public var name(default, null):String;
|
||||
|
||||
public function new(core:NGLite, data:Dynamic):Void {super(core, data); }
|
||||
|
||||
override function parse(data:Dynamic):Void {
|
||||
|
||||
id = data.id;
|
||||
name = data.name;
|
||||
|
||||
super.parse(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches score data from the server, this removes all of the existing scores cached
|
||||
*
|
||||
* We don't unify the old and new scores because a user's rank or score may change between requests
|
||||
*/
|
||||
public function requestScores
|
||||
( limit :Int = 10
|
||||
, skip :Int = 0
|
||||
, period:Period = Period.ALL
|
||||
, social:Bool = false
|
||||
, tag :String = null
|
||||
, user :Dynamic = null
|
||||
):Void {
|
||||
|
||||
_core.calls.scoreBoard.getScores(id, limit, skip, period, social, tag, user)
|
||||
.addDataHandler(onScoresReceived)
|
||||
.send();
|
||||
}
|
||||
|
||||
function onScoresReceived(response:Response<ScoreResult>):Void {
|
||||
|
||||
if (!response.success || !response.result.success)
|
||||
return;
|
||||
|
||||
scores = response.result.data.scores;
|
||||
_core.logVerbose('received ${scores.length} scores');
|
||||
|
||||
onUpdate.dispatch();
|
||||
}
|
||||
|
||||
public function postScore(value :Int, tag:String = null):Void {
|
||||
|
||||
_core.calls.scoreBoard.postScore(id, value, tag)
|
||||
.addDataHandler(onScorePosted)
|
||||
.send();
|
||||
}
|
||||
|
||||
function onScorePosted(response:Response<PostScoreResult>):Void {
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
|
||||
return 'ScoreBoard: $id@$name';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package io.newgrounds.objects;
|
||||
|
||||
class Session extends Object {
|
||||
|
||||
/** If true, the session_id is expired. Use App.startSession to get a new one.*/
|
||||
public var expired(default, null):Bool;
|
||||
|
||||
/** A unique session identifier */
|
||||
public var id(default, null):String;
|
||||
|
||||
/** If the session has no associated user but is not expired, this property will provide a URL that can be used to sign the user in. */
|
||||
public var passportUrl(default, null):String;
|
||||
|
||||
/** If true, the user would like you to remember their session id. */
|
||||
public var remember(default, null):Bool;
|
||||
|
||||
/** If the user has not signed in, or granted access to your app, this will be null */
|
||||
public var user(default, null):User;
|
||||
|
||||
//TODO:desciption
|
||||
public var status(get, never):SessionStatus;
|
||||
|
||||
public function new(core:NGLite, data:Dynamic = null) { super(core, data); }
|
||||
|
||||
override public function parse(data:Dynamic):Void {
|
||||
|
||||
id = data.id;
|
||||
expired = data.expired;
|
||||
passportUrl = data.passport_url;
|
||||
remember = data.remember;
|
||||
|
||||
// --- KEEP THE SAME INSTANCE
|
||||
if (user == null)
|
||||
user = data.user;
|
||||
// TODO?: update original user instance with new data. (probly not)
|
||||
|
||||
super.parse(data);
|
||||
}
|
||||
|
||||
public function get_status():SessionStatus {
|
||||
|
||||
if (expired || id == null || id == "")
|
||||
return SessionStatus.SESSION_EXPIRED;
|
||||
|
||||
if (user != null && user.name != null && user.name != "")
|
||||
return SessionStatus.USER_LOADED;
|
||||
|
||||
return SessionStatus.REQUEST_LOGIN;
|
||||
}
|
||||
|
||||
public function expire():Void {
|
||||
|
||||
expired = true;
|
||||
id = null;
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
|
||||
@:enum
|
||||
abstract SessionStatus(String) {
|
||||
|
||||
var SESSION_EXPIRED = "session-expired";
|
||||
var REQUEST_LOGIN = "request-login";
|
||||
var USER_LOADED = "user-loaded";
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package io.newgrounds.objects;
|
||||
|
||||
typedef User = {
|
||||
|
||||
/** The user's icon images. */
|
||||
var icons:UserIcons;
|
||||
|
||||
/** The user's numeric ID. */
|
||||
var id:Int;
|
||||
|
||||
/** The user's textual name. */
|
||||
var name:String;
|
||||
|
||||
/** Returns true if the user has a Newgrounds Supporter upgrade. */
|
||||
var supporter:Bool;
|
||||
|
||||
/** The user's NG profile url. */
|
||||
var url:String;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package io.newgrounds.objects;
|
||||
|
||||
typedef UserIcons = {
|
||||
|
||||
/**The URL of the user's large icon. */
|
||||
var large:String;
|
||||
|
||||
/** The URL of the user's medium icon. */
|
||||
var medium:String;
|
||||
|
||||
/** The URL of the user's small icon. */
|
||||
var small:String;
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package io.newgrounds.objects.events;
|
||||
|
||||
import io.newgrounds.objects.events.Result.ResultBase;
|
||||
import haxe.Json;
|
||||
import io.newgrounds.objects.Error;
|
||||
|
||||
typedef DebugResponse = {
|
||||
|
||||
var exec_time:Int;
|
||||
var input:Dynamic;
|
||||
}
|
||||
|
||||
class Response<T:ResultBase> {
|
||||
|
||||
public var success(default, null):Bool;
|
||||
public var error(default, null):Error;
|
||||
public var debug(default, null):DebugResponse;
|
||||
public var result(default, null):Result<T>;
|
||||
|
||||
public function new (core:NGLite, reply:String) {
|
||||
|
||||
var data:Dynamic;
|
||||
|
||||
try {
|
||||
data = Json.parse(reply);
|
||||
|
||||
} catch (e:Dynamic) {
|
||||
|
||||
data = Json.parse('{"success":false,"error":{"message":"${Std.string(reply)}","code":0}}');
|
||||
}
|
||||
|
||||
success = data.success;
|
||||
debug = data.debug;
|
||||
|
||||
if (!success) {
|
||||
error = new Error(data.error.message, data.error.code);
|
||||
core.logError('Call unseccessful: $error');
|
||||
return;
|
||||
}
|
||||
|
||||
result = new Result<T>(core, data.result);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
package io.newgrounds.objects.events;
|
||||
|
||||
class Result<T:ResultBase> {
|
||||
|
||||
public var echo(default, null):String;
|
||||
public var component(default, null):String;
|
||||
|
||||
public var data(default, null):T;
|
||||
public var success(default, null):Bool;
|
||||
public var debug(default, null):Bool;
|
||||
public var error(default, null):Error;
|
||||
|
||||
public function new(core:NGLite, data:Dynamic) {
|
||||
|
||||
echo = data.echo;
|
||||
component = data.component;
|
||||
|
||||
data = data.data;
|
||||
success = data.success;
|
||||
debug = data.debug;
|
||||
|
||||
if(!data.success) {
|
||||
|
||||
error = new Error(data.error.message, data.error.code);
|
||||
core.logError('$component fail: $error');
|
||||
|
||||
} else
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
typedef ResultBase = { };
|
||||
|
||||
typedef SessionResult = {
|
||||
> ResultBase,
|
||||
|
||||
var session:Dynamic;
|
||||
}
|
||||
|
||||
typedef GetHostResult = {
|
||||
> ResultBase,
|
||||
|
||||
var host_approved:Bool;
|
||||
}
|
||||
|
||||
typedef GetCurrentVersionResult = {
|
||||
> ResultBase,
|
||||
|
||||
var current_version:String;
|
||||
var client_deprecated:Bool;
|
||||
}
|
||||
|
||||
typedef LogEventResult = {
|
||||
> ResultBase,
|
||||
|
||||
var event_name:String;
|
||||
}
|
||||
|
||||
typedef GetDateTimeResult = {
|
||||
> ResultBase,
|
||||
|
||||
var datetime:String;
|
||||
}
|
||||
|
||||
typedef GetVersionResult = {
|
||||
> ResultBase,
|
||||
|
||||
var version:String;
|
||||
}
|
||||
|
||||
typedef PingResult = {
|
||||
> ResultBase,
|
||||
|
||||
var pong:String;
|
||||
}
|
||||
|
||||
typedef MedalListResult = {
|
||||
> ResultBase,
|
||||
|
||||
var medals:Array<Dynamic>;
|
||||
}
|
||||
|
||||
typedef MedalUnlockResult = {
|
||||
> ResultBase,
|
||||
|
||||
var medal_score:String;
|
||||
var medal:Dynamic;
|
||||
}
|
||||
|
||||
typedef ScoreBoardResult = {
|
||||
> ResultBase,
|
||||
|
||||
var scoreboards:Array<Dynamic>;
|
||||
}
|
||||
|
||||
typedef ScoreResult = {
|
||||
> ResultBase,
|
||||
|
||||
var scores:Array<Score>;
|
||||
var scoreboard:Dynamic;
|
||||
}
|
||||
|
||||
typedef PostScoreResult = {
|
||||
> ResultBase,
|
||||
|
||||
var tag:String;
|
||||
var scoreboard:Dynamic;
|
||||
var score:Score;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package io.newgrounds.swf;
|
||||
|
||||
import openfl.display.MovieClip;
|
||||
|
||||
class LoadingBar extends MovieClip {
|
||||
|
||||
public var bar(default, null):MovieClip;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
|
||||
setProgress(0.0);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value The ratio of bytes loaded to bytes total
|
||||
*/
|
||||
public function setProgress(value:Float):Void {
|
||||
|
||||
bar.gotoAndStop(1 + Std.int(value * (bar.totalFrames - 1)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
package io.newgrounds.swf;
|
||||
|
||||
import io.newgrounds.swf.common.BaseAsset;
|
||||
import io.newgrounds.objects.Medal;
|
||||
|
||||
import openfl.text.TextFieldAutoSize;
|
||||
import openfl.text.TextField;
|
||||
import openfl.display.DisplayObject;
|
||||
import openfl.display.Loader;
|
||||
import openfl.display.MovieClip;
|
||||
import openfl.net.URLRequest;
|
||||
import openfl.events.Event;
|
||||
|
||||
class MedalPopup extends BaseAsset {
|
||||
|
||||
static inline var FRAME_HIDDEN:String = "hidden";
|
||||
static inline var FRAME_MEDAL_UNLOCKED:String = "medalUnlocked";
|
||||
static inline var FRAME_INTRO_COMPLETE:String = "introComplete";
|
||||
static inline var FRAME_UNLOCK_COMPLETE:String = "unlockComplete";
|
||||
static inline var MIN_TEXT_SIZE:Int = 12;
|
||||
|
||||
public var medalIcon(default, null):MovieClip;
|
||||
public var medalName(default, null):MovieClip;
|
||||
public var medalPoints(default, null):MovieClip;
|
||||
|
||||
public var alwaysOnTop:Bool;
|
||||
#if !ng_lite
|
||||
public var requiresSession:Bool;
|
||||
#end
|
||||
|
||||
var _animQueue = new Array<Void->Void>();
|
||||
var _scrollSpeed:Float;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
|
||||
mouseEnabled = false;
|
||||
mouseChildren = false;
|
||||
|
||||
hide();
|
||||
addFrameScript(totalFrames - 1, onUnlockAnimComplete);
|
||||
}
|
||||
|
||||
function hide():Void {
|
||||
|
||||
visible = false;
|
||||
gotoAndStop(FRAME_HIDDEN);
|
||||
}
|
||||
|
||||
#if !ng_lite
|
||||
override function onReady():Void {
|
||||
super.onReady();
|
||||
|
||||
if (NG.core.medals != null)
|
||||
onMedalsLoaded();
|
||||
else
|
||||
NG.core.onLogin.addOnce(NG.core.requestMedals.bind(onMedalsLoaded));
|
||||
}
|
||||
|
||||
function onMedalsLoaded():Void {
|
||||
|
||||
for (medal in NG.core.medals)
|
||||
medal.onUnlock.add(onMedalOnlock.bind(medal));
|
||||
}
|
||||
|
||||
function onMedalOnlock(medal:Medal):Void {
|
||||
|
||||
if (requiresSession && !NG.core.loggedIn)
|
||||
return;
|
||||
|
||||
var loader = new Loader();
|
||||
loader.load(new URLRequest(medal.icon));
|
||||
|
||||
playAnim(loader, medal.name, medal.value);
|
||||
}
|
||||
|
||||
#end
|
||||
|
||||
public function playAnim(icon:DisplayObject, name:String, value:Int):Void {
|
||||
|
||||
if (currentLabel == FRAME_HIDDEN)
|
||||
playNextAnim(icon, name, value);
|
||||
else
|
||||
_animQueue.push(playNextAnim.bind(icon, name, value));
|
||||
}
|
||||
|
||||
function playNextAnim(icon:DisplayObject, name:String, value:Int):Void {
|
||||
|
||||
visible = true;
|
||||
gotoAndPlay(FRAME_MEDAL_UNLOCKED);
|
||||
|
||||
if (alwaysOnTop && parent != null) {
|
||||
|
||||
parent.setChildIndex(this, parent.numChildren - 1);
|
||||
}
|
||||
|
||||
while(medalIcon.numChildren > 0)
|
||||
medalIcon.removeChildAt(0);
|
||||
|
||||
cast(medalPoints.getChildByName("field"), TextField).text = Std.string(value);
|
||||
|
||||
var field:TextField = cast medalName.getChildByName("field");
|
||||
field.autoSize = TextFieldAutoSize.LEFT;
|
||||
field.x = 0;
|
||||
field.text = "";
|
||||
var oldWidth = medalName.width;
|
||||
field.text = name;
|
||||
|
||||
_scrollSpeed = 0;
|
||||
if (field.width > oldWidth + 4) {
|
||||
|
||||
field.x = oldWidth + 4;
|
||||
initScroll(field);
|
||||
}
|
||||
|
||||
medalIcon.addChild(icon);
|
||||
}
|
||||
|
||||
function initScroll(field:TextField):Void {
|
||||
//TODO: Find out why scrollrect didn't work
|
||||
|
||||
var animDuration = 0;
|
||||
|
||||
for (frame in currentLabels){
|
||||
|
||||
if (frame.name == FRAME_INTRO_COMPLETE )
|
||||
animDuration -= frame.frame;
|
||||
else if (frame.name == FRAME_UNLOCK_COMPLETE)
|
||||
animDuration += frame.frame;
|
||||
}
|
||||
|
||||
_scrollSpeed = (field.width + field.x + 4) / animDuration;
|
||||
field.addEventListener(Event.ENTER_FRAME, updateScroll);
|
||||
}
|
||||
|
||||
function updateScroll(e:Event):Void{
|
||||
|
||||
if (currentLabel == FRAME_INTRO_COMPLETE)
|
||||
cast (e.currentTarget, TextField).x -= _scrollSpeed;
|
||||
}
|
||||
|
||||
function onUnlockAnimComplete():Void {
|
||||
|
||||
cast (medalName.getChildByName("field"), TextField).removeEventListener(Event.ENTER_FRAME, updateScroll);
|
||||
|
||||
if (_animQueue.length == 0)
|
||||
hide();
|
||||
else
|
||||
(_animQueue.shift())();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,250 @@
|
|||
package io.newgrounds.swf;
|
||||
|
||||
import openfl.events.Event;
|
||||
import io.newgrounds.swf.common.DropDown;
|
||||
import io.newgrounds.objects.Score;
|
||||
import io.newgrounds.objects.events.Result.ScoreBoardResult;
|
||||
import io.newgrounds.objects.events.Result.ScoreResult;
|
||||
import io.newgrounds.objects.events.Response;
|
||||
import io.newgrounds.swf.common.BaseAsset;
|
||||
import io.newgrounds.swf.common.Button;
|
||||
import io.newgrounds.components.ScoreBoardComponent.Period;
|
||||
|
||||
import openfl.display.MovieClip;
|
||||
import openfl.text.TextField;
|
||||
|
||||
class ScoreBrowser extends BaseAsset {
|
||||
|
||||
public var prevButton (default, null):MovieClip;
|
||||
public var nextButton (default, null):MovieClip;
|
||||
public var reloadButton (default, null):MovieClip;
|
||||
public var listBox (default, null):MovieClip;
|
||||
public var loadingIcon (default, null):MovieClip;
|
||||
public var errorIcon (default, null):MovieClip;
|
||||
public var scoreContainer(default, null):MovieClip;
|
||||
public var titleField (default, null):TextField;
|
||||
public var pageField (default, null):TextField;
|
||||
|
||||
public var period(get, set):Period;
|
||||
function get_period():Period { return _periodDropDown.value; }
|
||||
function set_period(value:Period):Period { return _periodDropDown.value = value; }
|
||||
|
||||
public var title(get, set):String;
|
||||
function get_title():String { return titleField.text; }
|
||||
function set_title(value:String):String { return titleField.text = value; }
|
||||
|
||||
public var tag(default, set):String;
|
||||
function set_tag(value:String):String {
|
||||
|
||||
if (this.tag != value) {
|
||||
|
||||
this.tag = value;
|
||||
delayReload();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public var social(default, set):Bool;
|
||||
function set_social(value:Bool):Bool {
|
||||
|
||||
if (this.social != value) {
|
||||
|
||||
this.social = value;
|
||||
delayReload();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public var boardId(default, set):Int;
|
||||
function set_boardId(value:Int):Int {
|
||||
|
||||
_boardIDSet = true;
|
||||
|
||||
if (this.boardId != value) {
|
||||
|
||||
this.boardId = value;
|
||||
delayReload();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public var page(default, set):Int;
|
||||
function set_page(value:Int):Int {
|
||||
|
||||
if (this.page != value) {
|
||||
|
||||
this.page = value;
|
||||
delayReload();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
var _scores:Array<MovieClip>;
|
||||
var _limit:Int = 0;
|
||||
var _periodDropDown:DropDown;
|
||||
var _boardIDSet:Bool;
|
||||
|
||||
public function new() { super(); }
|
||||
|
||||
override function setDefaults():Void {
|
||||
super.setDefaults();
|
||||
|
||||
boardId = -1;
|
||||
_boardIDSet = false;
|
||||
|
||||
scoreContainer.visible = false;
|
||||
loadingIcon.visible = false;
|
||||
reloadButton.visible = false;
|
||||
errorIcon.visible = false;
|
||||
errorIcon.addFrameScript(errorIcon.totalFrames - 1, errorIcon.stop);
|
||||
|
||||
//TODO: prevent memory leaks?
|
||||
new Button(prevButton, onPrevClick);
|
||||
new Button(nextButton, onNextClick);
|
||||
new Button(reloadButton, reload);
|
||||
_periodDropDown = new DropDown(listBox, delayReload);
|
||||
_periodDropDown.addItem("Current day" , Period.DAY );
|
||||
_periodDropDown.addItem("Current week" , Period.WEEK );
|
||||
_periodDropDown.addItem("Current month", Period.MONTH);
|
||||
_periodDropDown.addItem("Current year" , Period.YEAR );
|
||||
_periodDropDown.addItem("All time" , Period.ALL );
|
||||
_periodDropDown.value = Period.ALL;
|
||||
|
||||
_scores = new Array<MovieClip>();
|
||||
while(true) {
|
||||
|
||||
var score:MovieClip = cast scoreContainer.getChildByName('score${_scores.length}');
|
||||
if (score == null)
|
||||
break;
|
||||
|
||||
new Button(score);
|
||||
_scores.push(score);
|
||||
}
|
||||
|
||||
_limit = _scores.length;
|
||||
}
|
||||
|
||||
override function onReady():Void {
|
||||
super.onReady();
|
||||
|
||||
if (boardId == -1 && !_boardIDSet) {
|
||||
|
||||
#if ng_lite
|
||||
NG.core.calls.scoreBoard.getBoards()
|
||||
.addDataHandler(onBoardsRecieved)
|
||||
.queue();
|
||||
#else
|
||||
if (NG.core.scoreBoards != null)
|
||||
onBoardsLoaded();
|
||||
else
|
||||
NG.core.requestScoreBoards(onBoardsLoaded);
|
||||
#end
|
||||
}
|
||||
|
||||
reload();
|
||||
}
|
||||
|
||||
#if ng_lite
|
||||
function onBoardsRecieved(response:Response<ScoreBoardResult>):Void {
|
||||
|
||||
if (response.success && response.result.success) {
|
||||
|
||||
for (board in response.result.data.scoreboards) {
|
||||
|
||||
NG.core.log('No boardId specified defaulting to ${board.name}');
|
||||
boardId = board.id;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
function onBoardsLoaded():Void {
|
||||
|
||||
for (board in NG.core.scoreBoards) {
|
||||
|
||||
NG.core.log('No boardId specified defaulting to ${board.name}');
|
||||
boardId = board.id;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
/** Used internally to avoid multiple server requests from various property changes in a small time-frame. **/
|
||||
function delayReload():Void {
|
||||
|
||||
addEventListener(Event.EXIT_FRAME, onDelayComplete);
|
||||
}
|
||||
|
||||
function onDelayComplete(e:Event):Void { reload(); }
|
||||
|
||||
public function reload():Void {
|
||||
removeEventListener(Event.EXIT_FRAME, onDelayComplete);
|
||||
|
||||
errorIcon.visible = false;
|
||||
scoreContainer.visible = false;
|
||||
pageField.text = 'page ${page + 1}';
|
||||
|
||||
if (_coreReady && boardId != -1 && _limit > 0 && period != null) {
|
||||
|
||||
loadingIcon.visible = true;
|
||||
|
||||
NG.core.calls.scoreBoard.getScores(boardId, _limit, _limit * page, period, social, tag)
|
||||
.addDataHandler(onScoresReceive)
|
||||
.send();
|
||||
}
|
||||
}
|
||||
|
||||
function onScoresReceive(response:Response<ScoreResult>):Void {
|
||||
|
||||
loadingIcon.visible = false;
|
||||
|
||||
if (response.success && response.result.success) {
|
||||
|
||||
scoreContainer.visible = true;
|
||||
|
||||
var i = _limit;
|
||||
while(i > 0) {
|
||||
i--;
|
||||
|
||||
if (i < response.result.data.scores.length)
|
||||
drawScore(i, response.result.data.scores[i], _scores[i]);
|
||||
else
|
||||
drawScore(i, null, _scores[i]);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
errorIcon.visible = true;
|
||||
errorIcon.gotoAndPlay(1);
|
||||
reloadButton.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
inline function drawScore(rank:Int, score:Score, asset:MovieClip):Void {
|
||||
|
||||
if (score == null)
|
||||
asset.visible = false;
|
||||
else {
|
||||
|
||||
asset.visible = true;
|
||||
cast (asset.getChildByName("nameField" ), TextField).text = score.user.name;
|
||||
cast (asset.getChildByName("scoreField"), TextField).text = score.formatted_value;
|
||||
cast (asset.getChildByName("rankField" ), TextField).text = Std.string(rank + 1);
|
||||
}
|
||||
}
|
||||
|
||||
function onPrevClick():Void {
|
||||
|
||||
if (page > 0)
|
||||
page--;
|
||||
}
|
||||
|
||||
function onNextClick():Void {
|
||||
|
||||
page++;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package io.newgrounds.swf.common;
|
||||
|
||||
import openfl.events.Event;
|
||||
import openfl.display.MovieClip;
|
||||
|
||||
class BaseAsset extends MovieClip {
|
||||
|
||||
var _coreReady:Bool = false;
|
||||
|
||||
public function new() {
|
||||
super();
|
||||
|
||||
setDefaults();
|
||||
|
||||
if (stage != null)
|
||||
onAdded(null);
|
||||
else
|
||||
addEventListener(Event.ADDED_TO_STAGE, onAdded);
|
||||
}
|
||||
|
||||
function setDefaults():Void { }
|
||||
|
||||
function onAdded(e:Event):Void {
|
||||
|
||||
if (NG.core != null)
|
||||
onReady();
|
||||
else
|
||||
NG.onCoreReady.add(onReady);
|
||||
}
|
||||
|
||||
function onReady():Void {
|
||||
|
||||
_coreReady = true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
package io.newgrounds.swf.common;
|
||||
|
||||
import openfl.display.Stage;
|
||||
import openfl.events.Event;
|
||||
import openfl.events.MouseEvent;
|
||||
import openfl.display.MovieClip;
|
||||
|
||||
class Button {
|
||||
|
||||
var _enabled:Bool;
|
||||
public var enabled(get, set):Bool;
|
||||
function get_enabled():Bool { return _enabled; }
|
||||
function set_enabled(value:Bool):Bool {
|
||||
|
||||
if (value != _enabled) {
|
||||
|
||||
_enabled = value;
|
||||
updateEnabled();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public var onClick:Void->Void;
|
||||
public var onOver:Void->Void;
|
||||
public var onOut:Void->Void;
|
||||
|
||||
var _target:MovieClip;
|
||||
var _down:Bool;
|
||||
var _over:Bool;
|
||||
var _foundLabels:Array<String>;
|
||||
|
||||
public function new(target:MovieClip, onClick:Void->Void = null, onOver:Void->Void = null, onOut:Void->Void = null) {
|
||||
|
||||
_target = target;
|
||||
this.onClick = onClick;
|
||||
this.onOver = onOver;
|
||||
this.onOut = onOut;
|
||||
|
||||
_foundLabels = new Array<String>();
|
||||
for (label in _target.currentLabels)
|
||||
_foundLabels.push(label.name);
|
||||
|
||||
_target.stop();
|
||||
_target.addEventListener(Event.ADDED_TO_STAGE, onAdded);
|
||||
if (target.stage != null)
|
||||
onAdded(null);
|
||||
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
function onAdded(e:Event):Void {
|
||||
|
||||
var stage = _target.stage;
|
||||
stage.addEventListener(MouseEvent.MOUSE_UP, mouseHandler);
|
||||
_target.addEventListener(MouseEvent.MOUSE_OVER, mouseHandler);
|
||||
_target.addEventListener(MouseEvent.MOUSE_OUT, mouseHandler);
|
||||
_target.addEventListener(MouseEvent.MOUSE_DOWN, mouseHandler);
|
||||
_target.addEventListener(MouseEvent.CLICK, mouseHandler);
|
||||
|
||||
function selfRemoveEvent(e:Event):Void {
|
||||
|
||||
_target.removeEventListener(Event.REMOVED_FROM_STAGE, selfRemoveEvent);
|
||||
onRemove(e, stage);
|
||||
}
|
||||
_target.addEventListener(Event.REMOVED_FROM_STAGE, selfRemoveEvent);
|
||||
}
|
||||
|
||||
function onRemove(e:Event, stage:Stage):Void {
|
||||
|
||||
stage.removeEventListener(MouseEvent.MOUSE_UP, mouseHandler);
|
||||
_target.removeEventListener(MouseEvent.MOUSE_OVER, mouseHandler);
|
||||
_target.removeEventListener(MouseEvent.MOUSE_OUT, mouseHandler);
|
||||
_target.removeEventListener(MouseEvent.MOUSE_DOWN, mouseHandler);
|
||||
_target.removeEventListener(MouseEvent.CLICK, mouseHandler);
|
||||
}
|
||||
|
||||
function mouseHandler(event:MouseEvent):Void {
|
||||
|
||||
switch(event.type) {
|
||||
|
||||
case MouseEvent.MOUSE_OVER:
|
||||
|
||||
_over = true;
|
||||
|
||||
if (onOver != null)
|
||||
onOver();
|
||||
|
||||
case MouseEvent.MOUSE_OUT:
|
||||
|
||||
_over = false;
|
||||
|
||||
if (onOut != null)
|
||||
onOut();
|
||||
|
||||
case MouseEvent.MOUSE_DOWN:
|
||||
|
||||
_down = true;
|
||||
|
||||
case MouseEvent.MOUSE_UP:
|
||||
|
||||
_down = false;
|
||||
|
||||
case MouseEvent.CLICK:
|
||||
|
||||
if (enabled && onClick != null)
|
||||
onClick();
|
||||
}
|
||||
updateState();
|
||||
}
|
||||
|
||||
function updateEnabled():Void {
|
||||
|
||||
updateState();
|
||||
|
||||
_target.useHandCursor = enabled;
|
||||
_target.buttonMode = enabled;
|
||||
}
|
||||
|
||||
function updateState():Void {
|
||||
|
||||
var state = determineState();
|
||||
|
||||
if (_target.currentLabel != state && _foundLabels.indexOf(state) != -1)
|
||||
_target.gotoAndStop(state);
|
||||
}
|
||||
|
||||
function determineState():String {
|
||||
|
||||
if (enabled) {
|
||||
|
||||
if (_over)
|
||||
return _down ? "down" : "over";
|
||||
|
||||
return "up";
|
||||
|
||||
}
|
||||
return "disabled";
|
||||
}
|
||||
|
||||
public function destroy():Void {
|
||||
|
||||
_target.removeEventListener(Event.ADDED_TO_STAGE, onAdded);
|
||||
|
||||
_target = null;
|
||||
onClick = null;
|
||||
onOver = null;
|
||||
onOut = null;
|
||||
_foundLabels = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package io.newgrounds.swf.common;
|
||||
|
||||
|
||||
import haxe.ds.StringMap;
|
||||
|
||||
import openfl.display.MovieClip;
|
||||
import openfl.display.Sprite;
|
||||
import openfl.text.TextField;
|
||||
|
||||
class DropDown {
|
||||
|
||||
public var value(default, set):String;
|
||||
function set_value(v:String):String {
|
||||
|
||||
if (this.value == v)
|
||||
return v;
|
||||
|
||||
this.value = v;
|
||||
_selectedLabel.text = _values.get(v);
|
||||
|
||||
if (_onChange != null)
|
||||
_onChange();
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
var _choiceContainer:Sprite;
|
||||
var _selectedLabel:TextField;
|
||||
var _onChange:Void->Void;
|
||||
var _values:StringMap<String>;
|
||||
var _unusedChoices:Array<MovieClip>;
|
||||
|
||||
public function new(target:MovieClip, label:String = "", onChange:Void->Void = null) {
|
||||
|
||||
_onChange = onChange;
|
||||
|
||||
_selectedLabel = cast cast(target.getChildByName("currentItem"), MovieClip).getChildByName("label");
|
||||
_selectedLabel.text = label;
|
||||
|
||||
_values = new StringMap<String>();
|
||||
|
||||
new Button(cast target.getChildByName("button"), onClickExpand);
|
||||
new Button(cast target.getChildByName("currentItem"), onClickExpand);
|
||||
_choiceContainer = new Sprite();
|
||||
_choiceContainer.visible = false;
|
||||
target.addChild(_choiceContainer);
|
||||
|
||||
_unusedChoices = new Array<MovieClip>();
|
||||
while(true) {
|
||||
|
||||
var item:MovieClip = cast target.getChildByName('item${_unusedChoices.length}');
|
||||
if (item == null)
|
||||
break;
|
||||
|
||||
target.removeChild(item);
|
||||
_unusedChoices.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
public function addItem(name:String, value:String):Void {
|
||||
|
||||
_values.set(value, name);
|
||||
|
||||
if (_unusedChoices.length == 0) {
|
||||
|
||||
NG.core.logError('cannot create another dropBox item max=${_choiceContainer.numChildren}');
|
||||
return;
|
||||
}
|
||||
|
||||
var button = _unusedChoices.shift();
|
||||
cast(button.getChildByName("label"), TextField).text = name;
|
||||
_choiceContainer.addChild(button);
|
||||
|
||||
new Button(button, onChoiceClick.bind(value));
|
||||
}
|
||||
|
||||
function onClickExpand():Void {
|
||||
|
||||
_choiceContainer.visible = !_choiceContainer.visible;
|
||||
}
|
||||
|
||||
function onChoiceClick(name:String):Void {
|
||||
|
||||
value = name;
|
||||
|
||||
_choiceContainer.visible = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
package io.newgrounds.utils;
|
||||
|
||||
import io.newgrounds.NGLite;
|
||||
|
||||
import haxe.Http;
|
||||
import haxe.Timer;
|
||||
|
||||
#if neko
|
||||
import neko.vm.Thread;
|
||||
#elseif java
|
||||
import java.vm.Thread;
|
||||
#elseif cpp
|
||||
import cpp.vm.Thread;
|
||||
#end
|
||||
|
||||
/**
|
||||
* Uses Threading to turn hxcpp's synchronous http requests into asynchronous processes
|
||||
*
|
||||
* @author GeoKureli
|
||||
*/
|
||||
class AsyncHttp {
|
||||
|
||||
inline static var PATH:String = "https://newgrounds.io/gateway_v3.php";
|
||||
|
||||
static public function send
|
||||
( core:NGLite
|
||||
, data:String
|
||||
, onData:String->Void
|
||||
, onError:String->Void
|
||||
, onStatus:Int->Void
|
||||
) {
|
||||
|
||||
core.logVerbose('sending: $data');
|
||||
|
||||
#if (neko || java || cpp)
|
||||
sendAsync(core, data, onData, onError, onStatus);
|
||||
#else
|
||||
sendSync(core, data, onData, onError, onStatus);
|
||||
#end
|
||||
}
|
||||
|
||||
static function sendSync
|
||||
( core:NGLite
|
||||
, data:String
|
||||
, onData:String->Void
|
||||
, onError:String->Void
|
||||
, onStatus:Int->Void
|
||||
):Void {
|
||||
|
||||
var http = new Http(PATH);
|
||||
http.setParameter("input", data);
|
||||
http.onData = onData;
|
||||
http.onError = onError;
|
||||
http.onStatus = onStatus;
|
||||
// #if js http.async = async; #end
|
||||
http.request(true);
|
||||
}
|
||||
|
||||
#if (neko || java || cpp)
|
||||
static var _deadPool:Array<AsyncHttp> = [];
|
||||
static var _livePool:Array<AsyncHttp> = [];
|
||||
static var _map:Map<Int, AsyncHttp> = new Map();
|
||||
static var _timer:Timer;
|
||||
|
||||
static var _count:Int = 0;
|
||||
|
||||
var _core:NGLite;
|
||||
var _key:Int;
|
||||
var _onData:String->Void;
|
||||
var _onError:String->Void;
|
||||
var _onStatus:Int->Void;
|
||||
var _worker:Thread;
|
||||
|
||||
public function new (core:NGLite) {
|
||||
|
||||
_core = core;
|
||||
_worker = Thread.create(sendThreaded);
|
||||
_key = _count++;
|
||||
_map[_key] = this;
|
||||
_core.logVerbose('async http created: $_key');
|
||||
}
|
||||
|
||||
function start(data:String, onData:String->Void, onError:String->Void, onStatus:Int->Void) {
|
||||
|
||||
_core.logVerbose('async http started: $_key');
|
||||
|
||||
if (_livePool.length == 0)
|
||||
startTimer();
|
||||
|
||||
_deadPool.remove(this);
|
||||
_livePool.push(this);
|
||||
|
||||
_onData = onData;
|
||||
_onError = onError;
|
||||
_onStatus = onStatus;
|
||||
_worker.sendMessage({ source:Thread.current(), args:data, key:_key, core:_core });
|
||||
}
|
||||
|
||||
function handleMessage(data:ReplyData):Void {
|
||||
|
||||
_core.logVerbose('handling message: $_key');
|
||||
|
||||
if (data.status != null) {
|
||||
|
||||
_core.logVerbose('\t- status: ${data.status}');
|
||||
_onStatus(cast data.status);
|
||||
return;
|
||||
}
|
||||
|
||||
var tempFunc:Void->Void;
|
||||
if (data.data != null) {
|
||||
|
||||
_core.logVerbose('\t- data');
|
||||
tempFunc = _onData.bind(data.data);
|
||||
|
||||
} else {
|
||||
|
||||
_core.logVerbose('\t- error');
|
||||
tempFunc = _onError.bind(data.error);
|
||||
}
|
||||
|
||||
cleanUp();
|
||||
// Delay the call until destroy so that we're more likely to use a single
|
||||
// thread on daisy-chained calls
|
||||
tempFunc();
|
||||
}
|
||||
|
||||
inline function cleanUp():Void {
|
||||
|
||||
_onData = null;
|
||||
_onError = null;
|
||||
|
||||
_deadPool.push(this);
|
||||
_livePool.remove(this);
|
||||
|
||||
if (_livePool.length == 0)
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
static function sendAsync
|
||||
( core:NGLite
|
||||
, data:String
|
||||
, onData:String->Void
|
||||
, onError:String->Void
|
||||
, onStatus:Int->Void
|
||||
):Void {
|
||||
|
||||
var http:AsyncHttp;
|
||||
if (_deadPool.length == 0)
|
||||
http = new AsyncHttp(core);
|
||||
else
|
||||
http = _deadPool[0];
|
||||
|
||||
http.start(data, onData, onError, onStatus);
|
||||
}
|
||||
|
||||
static function startTimer():Void {
|
||||
|
||||
if (_timer != null)
|
||||
return;
|
||||
|
||||
_timer = new Timer(1000 / 60.0);
|
||||
_timer.run = update;
|
||||
}
|
||||
|
||||
static function stopTimer():Void {
|
||||
|
||||
_timer.stop();
|
||||
_timer = null;
|
||||
}
|
||||
|
||||
static public function update():Void {
|
||||
|
||||
var message:ReplyData = cast Thread.readMessage(false);
|
||||
if (message != null)
|
||||
_map[message.key].handleMessage(message);
|
||||
}
|
||||
|
||||
static function sendThreaded():Void {
|
||||
|
||||
while(true) {
|
||||
|
||||
var data:LoaderData = cast Thread.readMessage(true);
|
||||
data.core.logVerbose('start message received: ${data.key}');
|
||||
|
||||
sendSync
|
||||
( data.core
|
||||
, data.args
|
||||
, function(reply ) { data.source.sendMessage({ key:data.key, data :reply }); }
|
||||
, function(error ) { data.source.sendMessage({ key:data.key, error :error }); }
|
||||
, function(status) { data.source.sendMessage({ key:data.key, status:status }); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#end
|
||||
}
|
||||
|
||||
|
||||
#if (neko || java || cpp)
|
||||
typedef LoaderData = { source:Thread, key:Int, args:String, core:NGLite };
|
||||
typedef ReplyData = { key:Int, ?data:String, ?error:String, ?status:Null<Int> };
|
||||
#end
|
|
@ -0,0 +1,118 @@
|
|||
package io.newgrounds.utils;
|
||||
|
||||
/**
|
||||
* Basically shitty signals, but I didn't want to have external references.
|
||||
**/
|
||||
class Dispatcher {
|
||||
|
||||
var _list:Array<Void->Void>;
|
||||
var _once:Array<Void->Void>;
|
||||
|
||||
public function new() {
|
||||
|
||||
_list = new Array<Void->Void>();
|
||||
_once = new Array<Void->Void>();
|
||||
}
|
||||
|
||||
public function add(handler:Void->Void, once:Bool = false):Bool {
|
||||
|
||||
if (_list.indexOf(handler) != -1) {
|
||||
|
||||
// ---- REMOVE ONCE
|
||||
if (!once && _once.indexOf(handler) != -1)
|
||||
_once.remove(handler);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_list.unshift(handler);
|
||||
if (once)
|
||||
_once.unshift(handler);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline public function addOnce(handler:Void->Void):Bool {
|
||||
|
||||
return add(handler, true);
|
||||
}
|
||||
|
||||
public function remove(handler:Void->Void):Bool {
|
||||
|
||||
_once.remove(handler);
|
||||
return _list.remove(handler);
|
||||
}
|
||||
|
||||
public function dispatch():Void {
|
||||
|
||||
var i = _list.length - 1;
|
||||
while(i >= 0) {
|
||||
|
||||
var handler = _list[i];
|
||||
|
||||
if (_once.remove(handler))
|
||||
_list.remove(handler);
|
||||
|
||||
handler();
|
||||
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TypedDispatcher<T> {
|
||||
|
||||
var _list:Array<T->Void>;
|
||||
var _once:Array<T->Void>;
|
||||
|
||||
public function new() {
|
||||
|
||||
_list = new Array<T->Void>();
|
||||
_once = new Array<T->Void>();
|
||||
}
|
||||
|
||||
public function add(handler:T->Void, once:Bool = false):Bool {
|
||||
|
||||
if (_list.indexOf(handler) != -1) {
|
||||
|
||||
// ---- REMOVE ONCE
|
||||
if (!once && _once.indexOf(handler) != -1)
|
||||
_once.remove(handler);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_list.unshift(handler);
|
||||
if (once)
|
||||
_once.unshift(handler);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline public function addOnce(handler:T->Void):Bool {
|
||||
|
||||
return add(handler, true);
|
||||
}
|
||||
|
||||
public function remove(handler:T->Void):Bool {
|
||||
|
||||
_once.remove(handler);
|
||||
return _list.remove(handler);
|
||||
}
|
||||
|
||||
public function dispatch(arg:T):Void {
|
||||
|
||||
var i = _list.length - 1;
|
||||
while(i >= 0) {
|
||||
|
||||
var handler = _list[i];
|
||||
|
||||
if (_once.remove(handler))
|
||||
_list.remove(handler);
|
||||
|
||||
handler(arg);
|
||||
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue