-
-
Notifications
You must be signed in to change notification settings - Fork 320
Expand file tree
/
Copy pathStoryMenuState.hx
More file actions
389 lines (310 loc) · 13 KB
/
StoryMenuState.hx
File metadata and controls
389 lines (310 loc) · 13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
package funkin.menus;
import flixel.math.FlxPoint;
import flixel.text.FlxText;
import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
import flixel.util.typeLimit.OneOfTwo;
import funkin.backend.FunkinText;
import funkin.backend.scripting.events.CancellableEvent;
import funkin.backend.scripting.events.menu.MenuChangeEvent;
import funkin.backend.scripting.events.menu.storymenu.*;
import funkin.backend.week.*;
import funkin.savedata.FunkinSave;
import haxe.io.Path;
import haxe.xml.Access;
class StoryMenuState extends MusicBeatState {
public var characters:Map<String, WeekData.WeekCharacter> = [];
public var weeks:Array<WeekData> = [];
public var weekList:StoryWeeklist;
// yes it supports parameters - Nex
// To be removed later, when translation branch is merged into public - Neo
public var scoreMessage:String = "WEEK SCORE:{0}";
public var scoreText:FlxText;
public var tracklist:FlxText;
public var weekTitle:FlxText;
public var curDifficulty:Int = 0;
public var curWeek:Int = 0;
public var difficultySprites:Map<String, FlxSprite> = [];
public var leftArrow:FlxSprite;
public var rightArrow:FlxSprite;
public var blackBar:FlxSprite;
public var weekBG:FlxSprite;
public var interpColor:FlxInterpolateColor;
public var lerpScore:Float = 0;
public var intendedScore:Int = 0;
public var canSelect:Bool = true;
public var weekSprites:FlxTypedGroup<MenuItem>;
public var characterSprites:FlxTypedGroup<FunkinSprite>;
//public var charFrames:Map<String, FlxFramesCollection> = [];
public override function create() {
super.create();
loadXMLs();
persistentUpdate = persistentDraw = true;
// WEEK INFO
blackBar = new FlxSprite(0, 0).makeSolid(FlxG.width, 56, 0xFFFFFFFF);
blackBar.color = 0xFF000000;
blackBar.updateHitbox();
scoreText = new FunkinText(10, 10, 0, TU.translate("story.score", ["-"]), 36);
scoreText.setFormat(Paths.font("vcr.ttf"), 32);
weekTitle = new FlxText(10, 10, FlxG.width - 20, "", 32);
weekTitle.setFormat(Paths.font("vcr.ttf"), 32, FlxColor.WHITE, RIGHT);
weekTitle.alpha = 0.7;
weekBG = new FlxSprite(0, 56).makeSolid(FlxG.width, 400, 0xFFFFFFFF);
weekBG.color = weeks.length > 0 ? weeks[0].bgColor : Flags.DEFAULT_WEEK_COLOR;
weekBG.updateHitbox();
weekSprites = new FlxTypedGroup<MenuItem>();
// DUMBASS ARROWS
var assets = Paths.getFrames('menus/storymenu/assets');
var directions = ["left", "right"];
leftArrow = new FlxSprite((FlxG.width + 400) / 2, weekBG.y + weekBG.height + 10 + 10);
rightArrow = new FlxSprite(FlxG.width - 10, weekBG.y + weekBG.height + 10 + 10);
for(k=>arrow in [leftArrow, rightArrow]) {
var dir = directions[k];
arrow.frames = assets;
arrow.animation.addByPrefix('idle', 'arrow $dir');
arrow.animation.addByPrefix('press', 'arrow push $dir', 24, false);
arrow.animation.play('idle');
arrow.antialiasing = true;
add(arrow);
}
rightArrow.x -= rightArrow.width;
tracklist = new FunkinText(16, weekBG.y + weekBG.height + 44, Std.int(((FlxG.width - 400) / 2) - 80), TU.translate("story.tracks"), 32);
tracklist.alignment = CENTER;
tracklist.color = 0xFFE55777;
add(weekSprites);
for (e in [blackBar, scoreText, weekTitle, weekBG, tracklist]) {
e.scrollFactor.set();
add(e);
}
add(characterSprites = new FlxTypedGroup<FunkinSprite>());
for (i=>week in weeks) {
var spr:MenuItem = new MenuItem(0, (i * 120) + 480, 'menus/storymenu/weeks/${week.sprite}');
weekSprites.add(spr);
for (e in week.difficulties) {
var le = e.toLowerCase();
if (difficultySprites[le] == null) {
var diffSprite = new FlxSprite(leftArrow.x + leftArrow.width, leftArrow.y);
diffSprite.loadAnimatedGraphic(Paths.image('menus/storymenu/difficulties/${le}'));
diffSprite.setUnstretchedGraphicSize(Std.int(rightArrow.x - leftArrow.x - leftArrow.width), Std.int(leftArrow.height), false, 1);
diffSprite.antialiasing = true;
diffSprite.scrollFactor.set();
add(diffSprite);
difficultySprites[le] = diffSprite;
}
}
}
interpColor = new FlxInterpolateColor(weekBG.color);
// default difficulty should be the middle difficulty in the array
// to be consistent with base game and whatnot, you know the drill
curDifficulty = Math.floor(weeks[0].difficulties.length * 0.5);
// debug stuff lol
Logs.trace('Middle Difficulty for Week 1 is ${weeks[0].difficulties[curDifficulty]} (ID: $curDifficulty)');
changeWeek(0, true);
DiscordUtil.call("onMenuLoaded", ["Story Menu"]);
CoolUtil.playMenuSong();
}
var __lastDifficultyTween:FlxTween;
public override function update(elapsed:Float) {
super.update(elapsed);
lerpScore = lerp(lerpScore, intendedScore, 0.5);
scoreText.text = TU.translate("story.score", [Math.round(lerpScore)]);
if (canSelect) {
if (leftArrow != null && leftArrow.exists) leftArrow.animation.play(controls.LEFT ? 'press' : 'idle');
if (rightArrow != null && rightArrow.exists) rightArrow.animation.play(controls.RIGHT ? 'press' : 'idle');
if (controls.BACK || FlxG.mouse.justPressedRight) {
goBack();
}
changeDifficulty((controls.LEFT_P ? -1 : 0) + (controls.RIGHT_P ? 1 : 0));
changeWeek((controls.UP_P ? -1 : 0) + (controls.DOWN_P ? 1 : 0) - FlxG.mouse.wheel);
if(FlxG.mouse.justPressed){
if(leftArrow.exists && FlxG.mouse.overlaps(leftArrow)){
leftArrow.animation.play('press');
changeDifficulty(-1);
}else if(rightArrow.exists && FlxG.mouse.overlaps(rightArrow)){
rightArrow.animation.play('press');
changeDifficulty(1);
}else if(FlxG.mouse.overlaps(weekSprites.members[curWeek])){
selectWeek();
}else if(weekSprites.members[curWeek+1] != null && FlxG.mouse.overlaps(weekSprites.members[curWeek+1])){
changeWeek(1);
}
}
if (controls.ACCEPT)
selectWeek();
} else {
for(e in [leftArrow, rightArrow])
if (e != null && e.exists)
e.animation.play('idle');
}
interpColor.fpsLerpTo(weeks[curWeek].bgColor, 0.0625);
weekBG.color = interpColor.color;
}
public override function beatHit(curBeat:Int) {
super.beatHit(curBeat);
if (characterSprites != null) // reason why I wanted to use a MusicBeatGroup, but eh, whatever - Nex
characterSprites.forEachAlive(function(spr) spr.beatHit(curBeat));
}
public function goBack() {
var event = event("onGoBack", new CancellableEvent());
if (!event.cancelled)
FlxG.switchState(new MainMenuState());
}
public function changeWeek(change:Int, force:Bool = false) {
if (change == 0 && !force) return;
var event = event("onChangeWeek", EventManager.get(MenuChangeEvent).recycle(curWeek, FlxMath.wrap(curWeek + change, 0, weeks.length-1), change));
if (event.cancelled) return;
curWeek = event.value;
if (!force) CoolUtil.playMenuSFX();
for(k=>e in weekSprites.members) {
e.targetY = k - curWeek;
e.alpha = k == curWeek ? 1.0 : 0.6;
}
tracklist.text = '${TU.translate("story.tracks")}\n\n${[for(e in weeks[curWeek].songs) if (!e.hide) e.displayName.getDefault(e.name).toUpperCase()].join('\n')}';
weekTitle.text = weeks[curWeek].name.getDefault("");
if (characterSprites != null) for (i in 0...3) {
var char = weeks[curWeek].chars[i];
var curChar:FunkinSprite = null;
var newChar = null;
if (char == null || (newChar = characters[char.name]) == null) modifyCharacterAt(i);
else if ((curChar = cast characterSprites.members[i]) == null || newChar.name != curChar.name) modifyCharacterAt(i, newChar); // forcing the sprites to be FunkinSprite basically - Nex
}
changeDifficulty(0, true);
MemoryUtil.clearMinor();
}
var __oldDiffName = null;
public function changeDifficulty(change:Int, force:Bool = false) {
if (change == 0 && !force) return;
var event = event("onChangeDifficulty", EventManager.get(MenuChangeEvent).recycle(curDifficulty, FlxMath.wrap(curDifficulty + change, 0, weeks[curWeek].difficulties.length-1), change));
if (event.cancelled) return;
curDifficulty = event.value;
if (__oldDiffName != (__oldDiffName = weeks[curWeek].difficulties[curDifficulty].toLowerCase())) {
for(e in difficultySprites) e.visible = false;
var diffSprite = difficultySprites[__oldDiffName];
if (diffSprite != null) {
diffSprite.visible = true;
if (__lastDifficultyTween != null)
__lastDifficultyTween.cancel();
diffSprite.alpha = 0;
diffSprite.y = leftArrow.y - 15;
__lastDifficultyTween = FlxTween.tween(diffSprite, {y: leftArrow.y, alpha: 1}, 0.07);
}
}
intendedScore = FunkinSave.getWeekHighscore(weeks[curWeek].id, weeks[curWeek].difficulties[curDifficulty]).score;
}
public function loadXMLs() {
weekList = StoryWeeklist.get(true, false); // will only load week files AND NOT characters too (we will load them later only if needed)!! - Nex
weeks = weekList.weeks;
for (week in weeks) for (char in week.chars) if (char != null)
addCharacter(char.name);
}
public function addCharacter(char:OneOfTwo<String, WeekData.WeekCharacter>) {
// better to use unsafe casts for going fast - Nex
var charObj:WeekData.WeekCharacter = null;
var charName:String;
charName = char is String ? cast char : (charObj = cast char).name;
if (characters[charName] != null) return; // will load only if it can be saved inside the map - Nex
characters[charName] = charObj == null ? Week.loadWeekCharacter(charName) : charObj;
}
public function modifyCharacterAt(i:Int, ?data:WeekData.WeekCharacter):FunkinSprite {
var curChar:FunkinSprite = null;
if (characterSprites != null) {
var old = characterSprites.members[i];
if (old != null) {
characterSprites.remove(old);
old.destroy();
}
if (data != null) {
curChar = XMLUtil.createSpriteFromXML(data.xml, "", BEAT);
curChar.offset.x += curChar.x; curChar.offset.y += curChar.y;
curChar.setPosition((FlxG.width * 0.25) * (1 + i) - 150, 70);
curChar.playAnim("idle", true, DANCE);
characterSprites.insert(i, curChar);
} else {
//characterSprites.members[i] = null; // cant add nulls to flxgroups, so - Nex
characterSprites.insert(i, new FunkinSprite()).visible = false;
}
}
return curChar;
}
public function selectWeek() {
var event = event("onWeekSelect", EventManager.get(WeekSelectEvent).recycle(weeks[curWeek], weeks[curWeek].difficulties[curDifficulty], curWeek, curDifficulty));
if (event.cancelled) return;
canSelect = false;
CoolUtil.playMenuSFX(CONFIRM);
if (characterSprites != null)
characterSprites.forEachAlive(function(spr) spr.playAnim("confirm", true, LOCK));
PlayState.loadWeek(event.week, event.difficulty);
new FlxTimer().start(1, function(tmr:FlxTimer)
{
FlxG.switchState(new PlayState());
});
weekSprites.members[event.weekID].startFlashing();
}
}
class MenuItem extends FlxSprite
{
public var targetY:Float = 0;
public function new(x:Float, y:Float, path:String)
{
super(x, y);
CoolUtil.loadAnimatedGraphic(this, Paths.image(path, null, true));
screenCenter(X);
antialiasing = true;
}
private var isFlashing:Bool = false;
public function startFlashing():Void
{
isFlashing = true;
}
// if it runs at 60fps, fake framerate will be 6
// if it runs at 144 fps, fake framerate will be like 14, and will update the graphic every 0.016666 * 3 seconds still???
// so it runs basically every so many seconds, not dependant on framerate??
// I'm still learning how math works thanks whoever is reading this lol
// var fakeFramerate:Int = Math.round((1 / FlxG.elapsed) / 10);
// hi ninja muffin
// i have found a more efficient way
// dw, judging by how week 7 looked you prob know how to do maths
// goodbye
var time:Float = 0;
override function update(elapsed:Float)
{
super.update(elapsed);
time += elapsed;
y = CoolUtil.fpsLerp(y, (targetY * 120) + 480, 0.17);
if (isFlashing)
color = (time % 0.1 > 0.05) ? FlxColor.WHITE : 0xFF33ffff;
}
}
class StoryWeeklist {
public var weeks:Array<WeekData> = [];
public function new() {}
public function getWeeksFromSource(source:funkin.backend.assets.AssetSource, useTxt:Bool = true, loadCharactersData:Bool = true) {
var path:String = Paths.txt('weeks/weeks');
var weeksFound:Array<String> = useTxt && Paths.assetsTree.existsSpecific(path, "TEXT", source) ? CoolUtil.coolTextFile(path) :
[for (c in Paths.getFolderContent('data/weeks/weeks/', false, source)) if (Path.extension(c).toLowerCase() == "xml") Path.withoutExtension(c)];
if (weeksFound.length > 0) {
for (w in weeksFound) {
var week = Week.loadWeek(w, loadCharactersData);
if (week != null) weeks.push(week);
}
return false;
}
return true;
}
public static function get(useTxt:Bool = true, loadCharactersData:Bool = true) {
var weekList = new StoryWeeklist();
switch(Flags.WEEKS_LIST_MOD_MODE) {
case 'prepend':
weekList.getWeeksFromSource(MODS, useTxt, loadCharactersData);
weekList.getWeeksFromSource(SOURCE, useTxt, loadCharactersData);
case 'append':
weekList.getWeeksFromSource(SOURCE, useTxt, loadCharactersData);
weekList.getWeeksFromSource(MODS, useTxt, loadCharactersData);
default /*case 'override'*/:
if (weekList.getWeeksFromSource(MODS, useTxt, loadCharactersData))
weekList.getWeeksFromSource(SOURCE, useTxt, loadCharactersData);
}
return weekList;
}
}