From 401321e310c362362d47edb5dc1e7f411ba00090 Mon Sep 17 00:00:00 2001
From: James R <justsomejames2@gmail.com>
Date: Sat, 25 Nov 2023 04:54:59 -0800
Subject: [PATCH] Hardcode Ivo Balls

---
 src/k_objects.h            |   8 ++
 src/objects/CMakeLists.txt |   1 +
 src/objects/ivoball.cpp    | 162 +++++++++++++++++++++++++++++++++++++
 src/p_inter.c              |  12 +++
 src/p_map.c                |  12 +++
 src/p_mobj.c               |  22 +++++
 6 files changed, 217 insertions(+)
 create mode 100644 src/objects/ivoball.cpp

diff --git a/src/k_objects.h b/src/k_objects.h
index eb5bd2b796..51e7eae96b 100644
--- a/src/k_objects.h
+++ b/src/k_objects.h
@@ -313,6 +313,14 @@ void Obj_IceCubeBurst(player_t *player);
 void Obj_SidewaysFreezeThrusterInit(mobj_t *mobj);
 void Obj_SidewaysFreezeThrusterThink(mobj_t *mobj);
 
+/* Ivo Balls */
+void Obj_IvoBallInit(mobj_t *mo);
+void Obj_IvoBallThink(mobj_t *mo);
+void Obj_IvoBallTouch(mobj_t *special, mobj_t *toucher);
+void Obj_PatrolIvoBallInit(mobj_t *mo);
+void Obj_PatrolIvoBallThink(mobj_t *mo);
+void Obj_PatrolIvoBallTouch(mobj_t *special, mobj_t *toucher);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt
index 0dae6ae06f..6a420e1cf1 100644
--- a/src/objects/CMakeLists.txt
+++ b/src/objects/CMakeLists.txt
@@ -43,6 +43,7 @@ target_sources(SRB2SDL2 PRIVATE
 	charge.c
 	mega-barrier.cpp
 	frost-thrower.cpp
+	ivoball.cpp
 )
 
 add_subdirectory(versus)
diff --git a/src/objects/ivoball.cpp b/src/objects/ivoball.cpp
new file mode 100644
index 0000000000..fbcecede96
--- /dev/null
+++ b/src/objects/ivoball.cpp
@@ -0,0 +1,162 @@
+// DR. ROBOTNIK'S RING RACERS
+//-----------------------------------------------------------------------------
+// Copyright (C) 2023 by Kart Krew.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+
+//
+// CREDITS
+// Original Lua script by Callmore
+// Edits by Ivo, Angular and Sal
+// Hardcoded by jartha
+//
+
+#include <algorithm>
+
+#include "../math/fixed.hpp"
+#include "../math/vec.hpp"
+#include "../mobj.hpp"
+
+#include "../d_player.h"
+#include "../doomdef.h"
+#include "../doomstat.h"
+#include "../k_objects.h"
+#include "../p_local.h"
+#include "../r_defs.h"
+#include "../s_sound.h"
+#include "../sounds.h"
+#include "../tables.h"
+
+using srb2::Mobj;
+using srb2::math::Fixed;
+using srb2::math::Vec2;
+
+namespace
+{
+
+Vec2<Fixed> angle_vector(angle_t x)
+{
+	return Vec2<Fixed> {FCOS(x), FSIN(x)};
+}
+
+struct IvoBall : Mobj
+{
+	static constexpr tic_t kCooldown = TICRATE*2;
+	static constexpr tic_t kFlashTime = TICRATE/2;
+	static constexpr Fixed kRippleFactor = 128*FRACUNIT/3;
+	static constexpr Fixed kBobHeight = 8*FRACUNIT;
+	static constexpr tic_t kBobTime = kFlashTime * 16;
+	static constexpr int kFloat = 24;
+
+	void extravalue1() = delete;
+	tic_t timer() const { return mobj_t::extravalue1; }
+	void timer(tic_t n) { mobj_t::extravalue1 = n; }
+
+	void extravalue2() = delete;
+	fixed_t offset() const { return mobj_t::extravalue2; }
+	void offset(fixed_t n) { mobj_t::extravalue2 = n; }
+
+	void init()
+	{
+		Fixed wave{(x / mapobjectscale) + (y / mapobjectscale)};
+		offset(wave / kRippleFactor);
+		color = SKINCOLOR_TANGERINE;
+		sprzoff = kFloat * mapobjectscale;
+	}
+
+	void think()
+	{
+		if (timer())
+		{
+			timer(timer() - 1);
+
+			if (timer() == 0)
+			{
+				renderflags &= ~RF_DONTDRAW;
+			}
+		}
+
+		fixed_t ballTimer = leveltime + offset();
+		Fixed bob = kBobHeight * Fixed {FSIN((M_TAU_FIXED * kBobTime) * ballTimer)};
+		spriteyoffset = bob;
+
+		colorized = !((ballTimer / kFlashTime) & 1);
+	}
+
+	void touch(Mobj* toucher)
+	{
+		if (timer())
+		{
+			return;
+		}
+
+		renderflags |= RF_DONTDRAW;
+		timer(kCooldown);
+
+		toucher->player->ringboost += 30;
+
+		if (P_IsDisplayPlayer(toucher->player))
+		{
+			S_StartSoundAtVolume(nullptr, sfx_ivobal, 160);
+		}
+	}
+};
+
+struct PatrolIvoBall : IvoBall
+{
+	void init()
+	{
+		Vec2<Fixed> v = angle_vector(angle) * Fixed {info->speed} * Fixed {mapobjectscale};
+		momx = -v.x;
+		momy = v.y;
+
+		IvoBall::init();
+	}
+
+	void think()
+	{
+		if (!P_TryMove(this, x + momx, y + momy, true, nullptr))
+		{
+			angle += ANGLE_180;
+			momx = -momx;
+			momy = -momy;
+		}
+
+		IvoBall::think();
+	}
+};
+
+}; // namespace
+
+void Obj_IvoBallInit(mobj_t* mobj)
+{
+	static_cast<IvoBall*>(mobj)->init();
+}
+
+void Obj_IvoBallThink(mobj_t* mobj)
+{
+	static_cast<IvoBall*>(mobj)->think();
+}
+
+void Obj_IvoBallTouch(mobj_t* special, mobj_t* toucher)
+{
+	static_cast<IvoBall*>(special)->touch(static_cast<Mobj*>(toucher));
+}
+
+void Obj_PatrolIvoBallInit(mobj_t* mobj)
+{
+	static_cast<PatrolIvoBall*>(mobj)->init();
+}
+
+void Obj_PatrolIvoBallThink(mobj_t* mobj)
+{
+	static_cast<PatrolIvoBall*>(mobj)->think();
+}
+
+void Obj_PatrolIvoBallTouch(mobj_t* special, mobj_t* toucher)
+{
+	static_cast<PatrolIvoBall*>(special)->touch(static_cast<Mobj*>(toucher));
+}
diff --git a/src/p_inter.c b/src/p_inter.c
index 0a44e56bd3..bcb2aade49 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -974,6 +974,18 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			return;
 		}
 
+		case MT_IVOBALL:
+		case MT_AIRIVOBALL:
+		{
+			Obj_IvoBallTouch(special, toucher);
+			return;
+		}
+		case MT_PATROLIVOBALL:
+		{
+			Obj_PatrolIvoBallTouch(special, toucher);
+			return;
+		}
+
 		default: // SOC or script pickup
 			P_SetTarget(&special->target, toucher);
 			break;
diff --git a/src/p_map.c b/src/p_map.c
index 954aca720b..216d7c1695 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -758,6 +758,18 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
 		return BMIT_CONTINUE;
 	}
 
+	if (tm.thing->type == MT_PATROLIVOBALL)
+	{
+		if (!thing->player)
+			return BMIT_CONTINUE;
+		if (tm.thing->z > thing->z + thing->height)
+			return BMIT_CONTINUE; // overhead
+		if (tm.thing->z + tm.thing->height < thing->z)
+			return BMIT_CONTINUE; // underneath
+		Obj_PatrolIvoBallTouch(tm.thing, thing);
+		return BMIT_CONTINUE;
+	}
+
 	if (thing->type == MT_BATTLEUFO)
 	{
 		if (tm.thing->type != MT_PLAYER)
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 6e303ce81b..2578f13aff 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -6825,6 +6825,17 @@ static void P_MobjSceneryThink(mobj_t *mobj)
 		}
 		break;
 	}
+	case MT_IVOBALL:
+	case MT_AIRIVOBALL:
+	{
+		Obj_IvoBallThink(mobj);
+		return;
+	}
+	case MT_PATROLIVOBALL:
+	{
+		Obj_PatrolIvoBallThink(mobj);
+		return;
+	}
 	case MT_VWREF:
 	case MT_VWREB:
 	{
@@ -14454,6 +14465,17 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj)
 		Obj_SidewaysFreezeThrusterInit(mobj);
 		break;
 	}
+	case MT_IVOBALL:
+	case MT_AIRIVOBALL:
+	{
+		Obj_IvoBallInit(mobj);
+		break;
+	}
+	case MT_PATROLIVOBALL:
+	{
+		Obj_PatrolIvoBallInit(mobj);
+		break;
+	}
 	default:
 		break;
 	}
-- 
GitLab