From 1c62dc127410373018355c455100fe5c895d3044 Mon Sep 17 00:00:00 2001
From: Eric Myllyoja <ericmyllyoja@gmail.com>
Date: Wed, 1 Jun 2022 22:07:42 -0400
Subject: [PATCH] First revision of henchmen killing.

---
 source/funkin/Conductor.hx         |  4 +++
 source/funkin/play/stage/Bopper.hx | 58 ++++++++++++++++++++++++++++--
 source/funkin/play/stage/Stage.hx  |  2 ++
 3 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/source/funkin/Conductor.hx b/source/funkin/Conductor.hx
index 331dd46f7..f21afeaac 100644
--- a/source/funkin/Conductor.hx
+++ b/source/funkin/Conductor.hx
@@ -40,7 +40,11 @@ class Conductor
 		return crochet / 4;
 	}
 
+	/**
+	 * The current position in the song in milliseconds.
+	 */
 	public static var songPosition:Float;
+
 	public static var lastSongPos:Float;
 	public static var offset:Float = 0;
 
diff --git a/source/funkin/play/stage/Bopper.hx b/source/funkin/play/stage/Bopper.hx
index 95b9cfea2..c98437071 100644
--- a/source/funkin/play/stage/Bopper.hx
+++ b/source/funkin/play/stage/Bopper.hx
@@ -1,6 +1,8 @@
 package funkin.play.stage;
 
 import flixel.FlxSprite;
+import flixel.math.FlxPoint;
+import flixel.util.FlxTimer;
 import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
 import funkin.modding.events.ScriptEvent;
 
@@ -42,7 +44,7 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
 	 */
 	public var shouldBop:Bool = true;
 
-	public var finishCallbackMap:Map<String, Void->Void> = new Map<String, Void->Void>();
+	private var finishCallbackMap:Map<String, Void->Void> = new Map<String, Void->Void>();
 
 	function set_idleSuffix(value:String):String
 	{
@@ -54,10 +56,28 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
 	/**
 	 * The offset of the character relative to the position specified by the stage.
 	 */
-	public var globalOffsets(default, null):Array<Float> = [0, 0];
+	public var globalOffsets(default, set):Array<Float> = [0, 0];
+
+	function set_globalOffsets(value:Array<Float>)
+	{
+		if (globalOffsets == null)
+			globalOffsets = [0, 0];
+		if (globalOffsets == value)
+			return value;
+
+		var xDiff = globalOffsets[0] - value[0];
+		var yDiff = globalOffsets[1] - value[1];
+
+		this.x += xDiff;
+		this.y += yDiff;
+
+		return animOffsets = value;
+	}
 
 	private var animOffsets(default, set):Array<Float> = [0, 0];
 
+	public var originalPosition:FlxPoint = new FlxPoint(0, 0);
+
 	function set_animOffsets(value:Array<Float>)
 	{
 		if (animOffsets == null)
@@ -91,6 +111,15 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
 		};
 	}
 
+	/**
+	 * If this Bopper was defined by the stage, return the prop to its original position.
+	 */
+	public function resetPosition()
+	{
+		this.x = originalPosition.x + animOffsets[0];
+		this.y = originalPosition.y + animOffsets[1];
+	}
+
 	function update_shouldAlternate():Void
 	{
 		if (hasAnimation('danceLeft'))
@@ -219,6 +248,31 @@ class Bopper extends FlxSprite implements IPlayStateScriptedClass
 		applyAnimationOffsets(correctName);
 	}
 
+	var forceAnimationTimer:FlxTimer = new FlxTimer();
+
+	/**
+	 * @param name The animation to play.
+	 * @param duration The duration in which other (non-forced) animations will be skipped, in seconds.
+	 */
+	public function forceAnimationForDuration(name:String, duration:Float):Void
+	{
+		if (this.animation == null)
+			return;
+
+		var correctName = correctAnimationName(name);
+		if (correctName == null)
+			return;
+
+		this.animation.play(correctName, false, false);
+		applyAnimationOffsets(correctName);
+
+		canPlayOtherAnims = false;
+		forceAnimationTimer.start(duration, (timer) ->
+		{
+			canPlayOtherAnims = true;
+		}, 1);
+	}
+
 	function applyAnimationOffsets(name:String)
 	{
 		var offsets = animationOffsets.get(name);
diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx
index 768938608..172b0dc9c 100644
--- a/source/funkin/play/stage/Stage.hx
+++ b/source/funkin/play/stage/Stage.hx
@@ -166,6 +166,8 @@ class Stage extends FlxSpriteGroup implements IHook implements IPlayStateScripte
 				{
 					cast(propSprite, Bopper).setAnimationOffsets(propAnim.name, propAnim.offsets[0], propAnim.offsets[1]);
 				}
+				cast(propSprite, Bopper).originalPosition.x = dataProp.position[0];
+				cast(propSprite, Bopper).originalPosition.y = dataProp.position[1];
 			}
 
 			if (dataProp.startingAnimation != null)