Skip to content

Commit 49e1273

Browse files
better speedrun timer, small update to map transitions. better UX around completion
1 parent d7cb3a1 commit 49e1273

12 files changed

Lines changed: 1141 additions & 276 deletions

File tree

assets/world.ldtk

Lines changed: 960 additions & 261 deletions
Large diffs are not rendered by default.

haxelib.deps

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ newgrounds 2.0.0
1010
flixel git https://github.com/MondayHopscotch/flixel.git lowres2023
1111
flixelutils git https://github.com/bitDecayGames/flixel-utils.git lowrez2023
1212
bitlytics git https://github.com/bitDecayGames/Bitlytics.git
13-
echo 4.2.2
13+
echo git https://github.com/MondayHopscotch/echo.git
1414
echo-flixel 0.0.1
1515
ldtk-haxe-api 1.3.3-rc.1
1616

source/Main.hx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package;
22

3+
import progress.PlayTimePlugin;
34
import io.newgrounds.NG;
45
import newgrounds.Newgrounds;
56
import collision.Color;
@@ -96,6 +97,8 @@ class Main extends Sprite {
9697
shaderUpdater.setShader(pixelShader);
9798
};
9899

100+
FlxG.plugins.add(new PlayTimePlugin());
101+
99102
// call it once on startup
100103
setCameraShader();
101104
// then every time we switch states

source/entities/ColorUpgrade.hx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package entities;
22

3+
import progress.PlayTimePlugin;
34
import entities.particles.UpgradeParticle;
45
import states.CreditsState;
56
import flixel.util.FlxTimer;
@@ -141,6 +142,7 @@ class ColorUpgrade extends ColorCollideSprite {
141142
});
142143
});
143144
} else {
145+
PlayTimePlugin.ME.timerRunning = false;
144146
FlxG.switchState(new CreditsState());
145147
}
146148
}));

source/entities/Player.hx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,9 @@ class Player extends ColorCollideSprite {
343343
#end
344344
}
345345

346+
var lineA = Line.get(0, 0);
347+
var lineB = Line.get(0, 0);
348+
346349
function handleInput(delta:Float) {
347350
var inputDir = InputCalcuator.getInputCardinal(playerNum);
348351
if (inputDir != NONE) {
@@ -495,6 +498,27 @@ class Player extends ColorCollideSprite {
495498
addColorIfUnlocked(YELLOW);
496499
}
497500
} else {
501+
if (!topShape.solid) {
502+
var world = FlxEcho.instance.world;
503+
var bodies = PlayState.ME.terrainGroup.get_group_bodies();
504+
505+
lineA.start.set(bottomShape.left, bottomShape.top);
506+
lineA.end.set(topShape.left, topShape.top);
507+
lineB.start.set(bottomShape.right, bottomShape.top);
508+
lineB.end.set(topShape.right, topShape.top);
509+
510+
// For some reason doing the linecast like this causes bodies to disappear...
511+
// var resultA = lineA.linecast(bodies, world, false);
512+
// var resultB = lineB.linecast(bodies, world, false);
513+
// So instead, we'll do linecast_all as we were already doing it for lasers and it doesn't break things.
514+
var resultA = lineA.linecast_all(FlxEcho.get_group_bodies(PlayState.ME.terrainGroup));
515+
var resultB = lineB.linecast_all(FlxEcho.get_group_bodies(PlayState.ME.terrainGroup));
516+
if (resultA.length > 0) {
517+
body.y += lineA.length - resultA[0].closest.distance;
518+
} else if (resultB.length > 0) {
519+
body.y += lineB.length - resultB[0].closest.distance;
520+
}
521+
}
498522
topShape.solid = true;
499523
body.drag.x = decel;
500524
if (mixColors) {

source/progress/Collected.hx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ class Collected {
9595
return FlxG.save.data.game.checkpoint.safeReturn;
9696
}
9797

98+
public static function isGameComplete():Bool {
99+
return FlxG.save.data.game.gameCompleted;
100+
}
98101

99102
public static function gameComplete() {
100103
clearCheckpoint();
@@ -146,6 +149,11 @@ class Collected {
146149
return FlxG.save.data.game.checkpoint.deaths;
147150
}
148151

152+
public static function setTime(t:Float) {
153+
FlxG.save.data.game.checkpoint.time = t;
154+
FlxG.save.flush();
155+
}
156+
149157
public static function addTime(t:Float) {
150158
FlxG.save.data.game.checkpoint.time += t;
151159
FlxG.save.flush();

source/progress/PlayTimePlugin.hx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package progress;
2+
3+
import flixel.FlxG;
4+
import flixel.system.FlxAssets.FlxShader;
5+
import flixel.FlxBasic;
6+
7+
/*
8+
* This is here to help time our gameplay consistently
9+
*/
10+
class PlayTimePlugin extends FlxBasic {
11+
public static var ME:PlayTimePlugin;
12+
13+
public var accumulated:Float = 0.0;
14+
public var timerRunning:Bool = false;
15+
16+
public function new() {
17+
super();
18+
ME = this;
19+
20+
FlxG.signals.preStateSwitch.add(() -> {
21+
FlxG.watch.add(this, "accumulated", "Timer: ");
22+
});
23+
}
24+
25+
override function update(elapsed:Float) {
26+
super.update(elapsed);
27+
28+
if (timerRunning) {
29+
accumulated += elapsed;
30+
}
31+
}
32+
}

source/states/ClickToFocusState.hx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package states;
22

3+
import progress.Collected;
34
import io.newgrounds.NG;
45
import config.Newgrounds;
56
import com.bitdecay.analytics.Bitlytics;
@@ -148,7 +149,11 @@ var clickHere:FlxSprite = null;
148149
state = "go_to_splash";
149150
}
150151
case "go_to_splash":
151-
FlxG.switchState(new SplashScreenState());
152+
if (Collected.getCheckpointLevel() != null) {
153+
FlxG.switchState(new MainMenuState());
154+
} else {
155+
FlxG.switchState(new SplashScreenState());
156+
}
152157
}
153158
}
154159
}

source/states/CreditsState.hx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package states;
22

3+
import progress.PlayTimePlugin;
34
import input.SimpleController;
45
import config.Newgrounds;
56
import flixel.util.FlxStringUtil;
@@ -50,10 +51,14 @@ class CreditsState extends FlxUIState {
5051
bgColor = backgroundColor;
5152
camera.pixelPerfectRender = true;
5253

53-
Collected.addTime(PlayState.ME.levelTime);
54+
// Collected.addTime(PlayState.ME.levelTime);
55+
56+
var finalTime = PlayTimePlugin.ME.accumulated;
57+
Collected.setTime(finalTime);
5458

5559
// Report for the leaderboard
56-
Newgrounds.reportScore(Collected.getTime());
60+
Newgrounds.reportScore(finalTime);
61+
Collected.setTime(0);
5762

5863
// Credits
5964
_allCreditElements = new Array<FlxSprite>();
@@ -113,7 +118,7 @@ class CreditsState extends FlxUIState {
113118
add(_txtThankYou);
114119
_allCreditElements.push(_txtThankYou);
115120

116-
var _txtTime = FlxTextFactory.make('Time: ${getFormattedTime()}', FlxG.width / 2, creditsVerticalOffset + FlxG.height * .75, 36, FlxTextAlign.CENTER);
121+
var _txtTime = FlxTextFactory.make('Time: ${getFormattedTime(finalTime)}', FlxG.width / 2, creditsVerticalOffset + FlxG.height * .75, 36, FlxTextAlign.CENTER);
117122
_txtTime.color = FlxColor.GRAY;
118123
center(_txtTime);
119124
_txtTime.x -= _txtTime.x % 4;
@@ -130,9 +135,8 @@ class CreditsState extends FlxUIState {
130135
Collected.gameComplete();
131136
}
132137

133-
private function getFormattedTime():String {
134-
var rawTime = Collected.getTime();
135-
var ngConsistentTime = Math.round(rawTime * 1000);
138+
private function getFormattedTime(t:Float):String {
139+
var ngConsistentTime = Math.round(t * 1000);
136140
return formatTime(ngConsistentTime, true);
137141
}
138142

source/states/PlayState.hx

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package states;
22

3+
import progress.PlayTimePlugin;
4+
import ui.font.BitmapText;
35
import flixel.FlxSprite;
46
import ldtk.Level;
57
import states.substate.UpgradeCutscene;
@@ -52,6 +54,9 @@ class PlayState extends FlxTransitionableState {
5254

5355
public var player:Player;
5456

57+
public var timerCam = new FlxCamera();
58+
public var timerTxt = new BitmapText("0:00");
59+
5560
public var baseTerrainCam:FlxCamera;
5661
public var colorCams:Map<Color, FlxCamera> = [];
5762
public var objectCam:FlxCamera;
@@ -75,6 +80,9 @@ class PlayState extends FlxTransitionableState {
7580
var deltaMod = 1.0;
7681
var deathDeltaMod = 0.1;
7782

83+
// This is our cumulative time in-game
84+
var baseTime = 0.0;
85+
// This is our time on this specific level
7886
public var levelTime = 0.0;
7987

8088
var resetQueued = false;
@@ -109,6 +117,22 @@ class PlayState extends FlxTransitionableState {
109117
objectCam = makeShaderCamera(EMPTY);
110118
FlxG.cameras.add(objectCam, false);
111119

120+
timerCam.bgColor = FlxColor.TRANSPARENT;
121+
FlxG.cameras.add(timerCam, false);
122+
timerTxt.scrollFactor.set();
123+
timerTxt.camera = timerCam;
124+
timerTxt.autoSize = false;
125+
timerTxt.width = FlxG.width;
126+
timerTxt.alignment = CENTER;
127+
timerTxt.updateHitbox();
128+
timerTxt.screenCenter(X);
129+
timerTxt.setBorderStyle(OUTLINE, FlxColor.BLACK);
130+
131+
if (Collected.isGameComplete()) {
132+
// Only show timer once they've completed the game
133+
add(timerTxt);
134+
}
135+
112136
// XXX: need the substate to use the right cameras... but it's not set yet, so we go to this
113137
// variable to get it
114138
if (_requestedSubState != null) {
@@ -150,6 +174,7 @@ class PlayState extends FlxTransitionableState {
150174
var checkpointRoom = Collected.getCheckpointLevel();
151175
var checkpointEntity = Collected.getCheckpointEntity();
152176
if (checkpointRoom != null && checkpointEntity != null) {
177+
PlayTimePlugin.ME.accumulated = Collected.getTime();
153178
loadLevel(checkpointRoom, checkpointEntity);
154179
} else {
155180
var spawnLevel = levels.ldtk.Level.project.all_worlds.Default.levels.filter((l) -> return l.l_Objects.all_Spawn.length > 0);
@@ -162,7 +187,7 @@ class PlayState extends FlxTransitionableState {
162187
}
163188
#end
164189

165-
FlxG.watch.add(this, "levelTime", "Timer: ");
190+
PlayTimePlugin.ME.timerRunning = true;
166191
}
167192

168193
override function draw() {
@@ -220,8 +245,8 @@ class PlayState extends FlxTransitionableState {
220245
lastLevel = levelID;
221246
lastSpawnEntity = entityID;
222247

223-
Collected.addTime(levelTime);
224-
levelTime = 0;
248+
// Collected.addTime(levelTime);
249+
Collected.setTime(PlayTimePlugin.ME.accumulated);
225250

226251
Collected.setLastCheckpoint(levelID, entityID);
227252

@@ -541,9 +566,10 @@ class PlayState extends FlxTransitionableState {
541566

542567
super.update(elapsed);
543568

544-
// if (player.inControl) {
545-
levelTime += originalDelta;
546-
// }
569+
// Time travels at normal speed as far as timing goes
570+
// levelTime += originalDelta;
571+
572+
updateTimer();
547573

548574
for (o in pendingObjects) {
549575
o.add_to_group(objects);
@@ -570,6 +596,12 @@ class PlayState extends FlxTransitionableState {
570596
#end
571597
}
572598

599+
public function updateTimer() {
600+
var rawTime = PlayTimePlugin.ME.accumulated;
601+
var ngConsistentTime = Math.round(rawTime * 1000);
602+
timerTxt.text = CreditsState.formatTime(ngConsistentTime, false);
603+
}
604+
573605
var tmpScreenPoint = FlxPoint.get();
574606
function camSeesWorldPoint(cam:FlxCamera, x:Float, y:Float):Bool {
575607
tmpScreenPoint = objectCam.project(FlxPoint.weak(x, y));

0 commit comments

Comments
 (0)