Merge remote-tracking branch 'upstream/main'

This commit is contained in:
Ne_Eo
2025-05-07 15:47:35 +02:00
121 changed files with 98 additions and 52 deletions
+1 -1
View File
@@ -128,7 +128,7 @@ class HScript extends Script {
interp.execute(expr);
call("new", []);
}
#if GLOBAL_SCRIPT
funkin.backend.scripting.GlobalScript.call("onScriptSetup", [this, "hscript"]);
#end
@@ -2,12 +2,12 @@ package funkin.backend.scripting.events.gameplay;
final class RatingUpdateEvent extends CancellableEvent {
/**
New combo
New combo (may be null if no ratings were found)
**/
public var rating:ComboRating;
public var rating:Null<ComboRating>;
/**
Old combo (may be null)
**/
public var oldRating:ComboRating;
public var oldRating:Null<ComboRating>;
}
+2 -2
View File
@@ -645,11 +645,11 @@ class CoolUtil
* @param spr Sprite to load the graphic for
* @param path Path to the graphic
*/
public static function loadAnimatedGraphic(spr:FlxSprite, path:String) {
public static function loadAnimatedGraphic(spr:FlxSprite, path:String, fps:Float = 24.0) {
spr.frames = Paths.getFrames(path, true);
if (spr.frames != null && spr.frames.frames != null) {
spr.animation.add("idle", [for(i in 0...spr.frames.frames.length) i], 24, true);
spr.animation.add("idle", [for(i in 0...spr.frames.frames.length) i], fps, true);
spr.animation.play("idle");
}
+92 -46
View File
@@ -288,6 +288,11 @@ class PlayState extends MusicBeatState
* Opponent's icon
*/
public var iconP2:HealthIcon;
/**
* Every active icon that will be updated during gameplay (defaults to `iconP1` and `iconP1` between `create` and `postCreate` in scripts)
*/
public var iconArray:Array<HealthIcon> = [];
/**
* Camera for the HUD (notes, misses).
*/
@@ -540,10 +545,10 @@ class PlayState extends MusicBeatState
*/
public function updateRating() {
var rating = null;
var acc = get_accuracy();
var acc = accuracy; // caching since it has a getter with an operation - Nex
for(e in comboRatings)
if (e.percent <= acc && (rating == null || rating.percent < e.percent))
if (comboRatings != null && comboRatings.length > 0) for (e in comboRatings)
if ((e.percent <= acc && e.maxMisses >= misses) && (rating == null || (rating.percent < e.percent && e.maxMisses >= misses)))
rating = e;
var event = gameAndCharsEvent("onRatingUpdate", EventManager.get(RatingUpdateEvent).recycle(rating, curRating));
@@ -790,9 +795,9 @@ class PlayState extends MusicBeatState
if (Flags.DEFAULT_HEALTH != null) health = Flags.DEFAULT_HEALTH;
else health = maxHealth / 2;
iconP1 = new HealthIcon(boyfriend != null ? boyfriend.getIcon() : Flags.DEFAULT_HEALTH_ICON, true);
iconP2 = new HealthIcon(dad != null ? dad.getIcon() : Flags.DEFAULT_HEALTH_ICON, false);
for(icon in [iconP1, iconP2]) {
iconArray.push(iconP1 = new HealthIcon(boyfriend != null ? boyfriend.getIcon() : Flags.DEFAULT_HEALTH_ICON, true));
iconArray.push(iconP2 = new HealthIcon(dad != null ? dad.getIcon() : Flags.DEFAULT_HEALTH_ICON, false));
for (icon in iconArray) {
icon.y = healthBar.y - (icon.height / 2);
add(icon);
}
@@ -809,7 +814,8 @@ class PlayState extends MusicBeatState
scoreTxt.alignment = RIGHT;
missesTxt.alignment = CENTER;
accuracyTxt.alignment = LEFT;
updateRatingStuff();
if (updateRatingStuff != null)
updateRatingStuff();
for(e in [healthBar, healthBarBG, iconP1, iconP2, scoreTxt, missesTxt, accuracyTxt])
e.cameras = [camHUD];
@@ -1283,7 +1289,7 @@ class PlayState extends MusicBeatState
iconP2.health = 1 - (healthBarPercent / 100);
}
function updateRatingStuff() {
dynamic function updateRatingStuff() {
scoreTxt.text = 'Score:$songScore';
missesTxt.text = '${comboBreaks ? "Combo Breaks" : "Misses"}:$misses';
@@ -1313,7 +1319,8 @@ class PlayState extends MusicBeatState
return;
}
updateRatingStuff();
if (updateRatingStuff != null)
updateRatingStuff();
if (canAccessDebugMenus) {
if (chartingMode && FlxG.keys.justPressed.SEVEN) {
@@ -1327,24 +1334,30 @@ class PlayState extends MusicBeatState
}
if (doIconBop)
for (icon in [iconP1, iconP2])
for (icon in iconArray)
if (icon.updateBump != null)
icon.updateBump();
if (updateIconPositions != null)
updateIconPositions();
if (startingSong)
{
if (startedCountdown)
{
if (startingSong) {
if (startedCountdown) {
Conductor.songPosition += Conductor.songOffset + elapsed * 1000;
if (Conductor.songPosition >= 0)
startSong();
}
} else if (FlxG.sound.music != null) {
var instTime = FlxG.sound.music.time;
var isOffsync = vocals.time != instTime || [for(strumLine in strumLines.members) strumLine.vocals.time != instTime].contains(true);
var isOffsync:Bool = vocals.time != instTime;
if(!isOffsync) {
for(strumLine in strumLines.members) {
if(strumLine.vocals.time != instTime) {
isOffsync = true;
break;
}
}
}
__vocalOffsetViolation = Math.max(0, __vocalOffsetViolation + (isOffsync ? elapsed : -elapsed / 2));
if (__vocalOffsetViolation > Flags.VOCAL_OFFSET_VIOLATION_THRESHOLD) {
resyncVocals();
@@ -1358,11 +1371,17 @@ class PlayState extends MusicBeatState
if (controls.PAUSE && startedCountdown && canPause)
pauseGame();
if (generatedMusic)
moveCamera();
if (generatedMusic && strumLines.members[curCameraTarget] != null) {
var data:CamPosData = getStrumlineCamPos(curCameraTarget);
if (data.amount > 0) {
var event = scripts.event("onCameraMove", EventManager.get(CamMoveEvent).recycle(data.pos, strumLines.members[curCameraTarget], data.amount));
if (!event.cancelled)
camFollow.setPosition(event.position.x, event.position.y);
}
data.put();
}
if (camZooming)
{
if (camZooming) {
FlxG.camera.zoom = lerp(FlxG.camera.zoom, defaultCamZoom, camGameZoomLerp);
camHUD.zoom = lerp(camHUD.zoom, defaultHudZoom, camHUDZoomLerp);
}
@@ -1396,41 +1415,41 @@ class PlayState extends MusicBeatState
scripts.event("postDraw", e);
}
public function moveCamera() if (strumLines.members[curCameraTarget] != null) {
var pos = FlxPoint.weak();
var amount = calculateCamPos(strumLines.members[curCameraTarget].characters, pos);
if (amount > 0) {
var event = gameAndCharsEvent("onCameraMove", EventManager.get(CamMoveEvent).recycle(pos, strumLines.members[curCameraTarget], amount));
if (!event.cancelled)
camFollow.setPosition(pos.x, pos.y);
}
pos.put();
/**
* Returns the camera position of the specified strumline.
* @param strumLine The strumline to get the camera position of.
* @param pos The position to put the camera position in. If `null`, a new FlxPoint will be created.
* @param ignoreInvisible Whenever invisible characters should be ignored.
**/
public inline function getStrumlineCamPos(strumLine:Int, ?pos:FlxPoint = null, ?ignoreInvisible:Bool = true):CamPosData {
return getCharactersCamPos(strumLines.members[strumLine].characters, pos, ignoreInvisible);
}
/**
* Function used to calculate the camera position based on the characters.
*
* This function is dynamic, which means you can do `calculateCamPos = function(chars:Array<Character>, pos:FlxPoint) {}` in scripts.
*/
public dynamic function calculateCamPos(chars:Array<Character>, pos:FlxPoint):Int {
var r:Int = 0;
for (c in chars) {
if (c == null || !c.visible) continue;
* Returns the camera position of the specified characters.
* @param chars The characters to get the camera position of.
* @param pos The position to put the camera position in. If `null`, a new FlxPoint will be created.
* @param ignoreInvisible Whenever invisible characters should be ignored.
**/
public function getCharactersCamPos(chars:Array<Character>, ?pos:FlxPoint = null, ?ignoreInvisible:Bool = true):CamPosData {
if (pos == null) pos = FlxPoint.get();
var amount = 0;
for(c in chars) {
if (c == null || (ignoreInvisible && !c.visible)) continue;
var cpos = c.getCameraPosition();
pos.x += cpos.x;
pos.y += cpos.y;
r++;
amount++;
//cpos.put(); // not actually in the pool, so no need
}
if (r > 0) {
pos.x /= r;
pos.y /= r;
if (amount > 0) {
pos.x /= amount;
pos.y /= amount;
}
return r;
return new CamPosData(pos, amount);
}
public var eventsTween:Map<String, FlxTween> = [];
public function executeEvent(event:ChartEvent) @:privateAccess {
@@ -1988,7 +2007,7 @@ class PlayState extends MusicBeatState
}
if (doIconBop)
for (icon in [iconP1, iconP2])
for (icon in iconArray)
if (icon.bump != null)
icon.bump();
@@ -2127,8 +2146,10 @@ final class ComboRating {
public var percent:Float;
public var rating:String;
public var color:FlxColor;
public var maxMisses:Float; // Float since it could be Math.POSITIVE_INFINITY - Nex
public function new(percent:Float, rating:String, color:FlxColor) {
public function new(?percent:Float, ?rating:String, ?color:FlxColor, ?misses:Float) {
maxMisses = misses == null || Math.isNaN(misses) ? Math.POSITIVE_INFINITY : misses;
this.percent = percent;
this.rating = rating;
this.color = color;
@@ -2143,3 +2164,28 @@ typedef PlayStateTransitionData = {
var camFollowY:Float;
var camZoom:Float;
}
class CamPosData {
/**
* The camera position.
**/
public var pos:FlxPoint;
/**
* The amount of characters that was involved in the calculation.
**/
public var amount:Int;
public function new(pos:FlxPoint, amount:Int) {
this.pos = pos;
this.amount = amount;
}
/**
* Puts the position back into the pool, making it reusable.
**/
public function put() {
if(pos == null) return;
pos.put();
pos = null;
}
}