Skip to content

Commit

Permalink
Refactored grappling hooks to store stats in a less hardcoded manner.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mirsario committed Sep 26, 2024
1 parent 613bd80 commit 9163a96
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 69 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
- For English, changed `Expert`'s difficulty level rename to `Turn Me Undead`. For obvious reasons.
- Removed all unused and functionless legacy content from compilation: `Charcoal`, `Calendars`, `Gramophones`, `Stone Axes`, `Stone Pickaxes`, `Ash Clots`, `Walking Ashes`. These bits will be reintroduced when required.
- Wall flips and wall rolls were made slightly more reliable, with their code rewritten.
- Grappling hooks' code was made more data-driven, with some vanilla hook range values corrected.
### Configuration
- Existing features received the following new options:
**Guns:** `EnableMinigunDynamicFirerate`, `EnableStarCannonDynamicFirerate`.
Expand Down
155 changes: 155 additions & 0 deletions Common/GrapplingHooks/GrapplingHookStats.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
using System;
using System.Collections.Generic;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;
using TerrariaOverhaul.Core.Debugging;

namespace TerrariaOverhaul.Common.GrapplingHooks;

public struct GrapplingHookStats
{
private static readonly Dictionary<int, GrapplingHookStats> stats = [];
private static readonly HashSet<int> grapplingTypesWarnedAbout = [];

public float Range = 300f;
public float PullSpeed = 12.5f;
public float PullVelocity = 0.1f;
public float RaiseSpeed = 5.0f;
public float RaiseVelocity = 0.975f;
public float LowerSpeed = 5.0f;
public float LowerVelocity = 1.0f;

public GrapplingHookStats() { }

public static GrapplingHookStats Get(Player player, Projectile projectile)
{
if (projectile.type <= 0) {
throw new IndexOutOfRangeException($"{projectile.type} <= 0");
}

if (stats.TryGetValue(projectile.type, out var result)) {
return result;
}

if (projectile.type < ProjectileID.Count || projectile.ModProjectile is not ModProjectile modProjectile) {
if (grapplingTypesWarnedAbout.Add(projectile.type)) {
DebugSystem.Logger.Warn($"Vanilla grappling hook '{ContentSamples.ProjectilesByType[projectile.type].Name}' (ID: {projectile.type}) does not have stats assigned.");
}

return new();
}

result = new() {
Range = modProjectile.GrappleRange(),
};

ProjectileLoader.GrapplePullSpeed(projectile, player, ref result.PullSpeed);

return result;
}

public static void Set(int projectileType, GrapplingHookStats value)
=> stats[projectileType] = value;

static GrapplingHookStats()
{
// PHM Singlehooks
Set(ProjectileID.Hook, new() {
Range = 300f,
});
Set(ProjectileID.SquirrelHook, new() {
Range = 300f,
});
Set(ProjectileID.GemHookAmethyst, new() {
Range = 300f,
});
Set(ProjectileID.GemHookTopaz, new() {
Range = 330f,
});
Set(ProjectileID.GemHookSapphire, new() {
Range = 360f,
});
Set(ProjectileID.GemHookEmerald, new() {
Range = 390f,
});
Set(ProjectileID.GemHookRuby, new() {
Range = 420f,
});
Set(ProjectileID.AmberHook, new() {
Range = 420f,
});
Set(ProjectileID.GemHookDiamond, new() {
Range = 466f,
});
// PHM Multihooks
Set(ProjectileID.Web, new() {
Range = 375f,
});
Set(ProjectileID.SkeletronHand, new() {
Range = 350f,
});
Set(ProjectileID.SlimeHook, new() {
Range = 300f,
});
Set(ProjectileID.FishHook, new() {
Range = 400f,
});
Set(ProjectileID.IvyWhip, new() {
Range = 400f,
});
Set(ProjectileID.BatHook, new() {
Range = 500f,
});
Set(ProjectileID.CandyCaneHook, new() {
Range = 400f,
});
// HM Singlehooks
Set(ProjectileID.DualHookBlue, new() {
Range = 440f,
});
Set(ProjectileID.DualHookRed, new() {
Range = 440f,
});
Set(ProjectileID.QueenSlimeHook, new() {
Range = 500f,
});
Set(ProjectileID.StaticHook, new() {
Range = 600f,
});
// HM Multihooks
Set(ProjectileID.TendonHook, new() {
Range = 480f,
});
Set(ProjectileID.ThornHook, new() {
Range = 480f,
});
Set(ProjectileID.IlluminantHook, new() {
Range = 480f,
});
Set(ProjectileID.WormHook, new() {
Range = 480f,
});
Set(ProjectileID.AntiGravityHook, new() {
Range = 500f,
});
Set(ProjectileID.WoodHook, new() {
Range = 550f,
});
Set(ProjectileID.ChristmasHook, new() {
Range = 550f,
});
Set(ProjectileID.LunarHookSolar, new() {
Range = 550f,
});
Set(ProjectileID.LunarHookVortex, new() {
Range = 550f,
});
Set(ProjectileID.LunarHookNebula, new() {
Range = 550f,
});
Set(ProjectileID.LunarHookStardust, new() {
Range = 550f,
});
}
}
72 changes: 3 additions & 69 deletions Common/GrapplingHooks/ProjectileGrapplingHookPhysics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ public class ProjectileGrapplingHookPhysics : GlobalProjectile

public const int GrapplingHookAIStyle = ProjAIStyleID.Hook;

private static HashSet<int>? grapplingTypesWarnedAbout;
private static Dictionary<int, float>? vanillaHookRangesInPixels;

private float maxDist;
private bool noPulling;

Expand Down Expand Up @@ -59,76 +56,13 @@ public override void Load()

physics.ProjectileGrappleMovement(player, projectile);
};

// Vanilla's data for this is hardcoded and not accessible. These stats are from the wiki.
vanillaHookRangesInPixels = new Dictionary<int, float> {
// PHM Singlehooks
{ ProjectileID.Hook, 300f }, // ID 13
{ ProjectileID.SquirrelHook, 300f }, // ID 865
{ ProjectileID.GemHookAmethyst, 300f }, // ID 230
{ ProjectileID.GemHookTopaz, 330f }, // ID 231
{ ProjectileID.GemHookSapphire, 360f }, // ID 232
{ ProjectileID.GemHookEmerald, 390f }, // ID 233
{ ProjectileID.GemHookRuby, 420f }, // ID 234
{ ProjectileID.AmberHook, 420f }, // ID 753
{ ProjectileID.GemHookDiamond, 466f }, // ID 235
// PHM Multihooks
{ ProjectileID.Web, 375f }, // ID 165
{ ProjectileID.SkeletronHand, 350f }, // ID 256
{ ProjectileID.SlimeHook, 300f }, // ID 396
{ ProjectileID.FishHook, 400f }, // ID 372
{ ProjectileID.IvyWhip, 400f }, // ID 32
{ ProjectileID.BatHook, 500f }, // ID 315
{ ProjectileID.CandyCaneHook, 400f }, // ID 331
// HM Singlehooks
{ ProjectileID.DualHookBlue, 440f }, // ID 73
{ ProjectileID.DualHookRed, 440f }, // ID 74
{ ProjectileID.QueenSlimeHook, 500f }, // ID 935
{ ProjectileID.StaticHook, 600f }, // ID 652
// HM Multihooks
{ ProjectileID.TendonHook, 480f }, // ID 486
{ ProjectileID.ThornHook, 480f }, // ID 487
{ ProjectileID.IlluminantHook, 480f }, // ID 488
{ ProjectileID.WormHook, 480f }, // ID 489
{ ProjectileID.AntiGravityHook, 500f }, // ID 446
{ ProjectileID.WoodHook, 550f }, // ID 322
{ ProjectileID.ChristmasHook, 550f }, // ID 332
{ ProjectileID.LunarHookSolar, 550f }, // ID 646
{ ProjectileID.LunarHookVortex, 550f }, // ID 647
{ ProjectileID.LunarHookNebula, 550f }, // ID 648
{ ProjectileID.LunarHookStardust, 550f }, // ID 649
};
}

public override void Unload()
{
if (vanillaHookRangesInPixels != null) {
vanillaHookRangesInPixels.Clear();

vanillaHookRangesInPixels = null;
}
}

public void ProjectileGrappleMovement(Player player, Projectile proj)
{
if (vanillaHookRangesInPixels == null) {
throw new InvalidOperationException($"'{nameof(ProjectileGrappleMovement)}' called before '{nameof(Load)}'.");
}

var playerCenter = player.Center;
var projCenter = proj.Center;
float hookRange;

if (proj.ModProjectile != null) {
hookRange = proj.ModProjectile.GrappleRange();
} else if (!vanillaHookRangesInPixels.TryGetValue(proj.type, out hookRange)) {
// Fallback, not intended to be ran.
hookRange = 512f;

if ((grapplingTypesWarnedAbout ??= new()).Add(proj.type)) {
DebugSystem.Logger.Warn($"Vanilla grappling hook '{proj.Name}' (ID: {proj.type}) does not have a hook range assigned. Please report this.");
}
}
var hookStats = GrapplingHookStats.Get(player, proj);

var mountedCenter = player.MountedCenter;
var mountedOffset = mountedCenter - projCenter;
Expand All @@ -149,10 +83,10 @@ public void ProjectileGrappleMovement(Player player, Projectile proj)

// Prevent hooks from going farther than normal
float ClampDistance(float distance)
=> MathHelper.Clamp(distance, 0f, hookRange - 1f);
=> MathHelper.Clamp(distance, 0f, hookStats.Range - 1f);

float dist = ClampDistance(Vector2.Distance(playerCenter, projCenter));
bool down = player.controlDown && maxDist < hookRange;
bool down = player.controlDown && maxDist < hookStats.Range;
bool up = player.controlUp;

const float PullSpeed = 12.5f;
Expand Down

0 comments on commit 9163a96

Please sign in to comment.