Skip to content

Commit

Permalink
Merge pull request #2747 from CombatExtended-Continued/TurretRecoilAn…
Browse files Browse the repository at this point in the history
…imPrototype

Turret & weapon recoil animation
  • Loading branch information
N7Huntsman authored Oct 23, 2023
2 parents 518142d + b0c8dae commit 341bbfc
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 2 deletions.
90 changes: 90 additions & 0 deletions Source/CombatExtended/CombatExtended/CE_Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,96 @@ public static Thing GetWeaponFromLauncher(Thing launcher)
}
*/

/// <summary>
/// A copy of the same function in Rimworld.EquipmentUtility, except changing requirement to Verb.
/// </summary>
///

private static readonly SimpleCurve RecoilCurveAxisY = new SimpleCurve
{
new CurvePoint(0f, 0f),
new CurvePoint(1f, 0.05f),
new CurvePoint(2f, 0.075f)
};

private static readonly SimpleCurve RecoilCurveRotation = new SimpleCurve
{
new CurvePoint(0f, 0f),
new CurvePoint(1f, 3f),
new CurvePoint(2f, 4f)
};

const float RecoilMagicNumber = 2.6f;
const float MuzzleRiseMagicNumber = 0.1f;

public static void Recoil(ThingDef weaponDef, Verb shootVerb, out Vector3 drawOffset, out float angleOffset, float aimAngle, bool handheld)
{
drawOffset = Vector3.zero;
angleOffset = 0f;
if (shootVerb == null || shootVerb.IsMeleeAttack)
{
return;
}
float recoil = ((VerbPropertiesCE)weaponDef.verbs[0]).recoilAmount;

float recoilRelaxation = weaponDef.verbs[0].burstShotCount > 1 ? weaponDef.verbs[0].ticksBetweenBurstShots : weaponDef.GetStatValueDef(StatDefOf.RangedWeapon_Cooldown) * 20f;

recoil = Math.Min(recoil * recoil, 20) * RecoilMagicNumber * Mathf.Clamp((float)Math.Log10(recoilRelaxation), 0.1f, 10);

//Prevents recoil for something with absurd ROF, it's too fast for any meaningful recoil animation
if (recoilRelaxation < 2)
{
return;
}

Rand.PushState(shootVerb.LastShotTick);
try
{
float muzzleJumpModifier = 10 * (float)Math.Log10(recoil) + 3;
GunDrawExtension recoilAdjustExtension = weaponDef.GetModExtension<GunDrawExtension>();
if (recoilAdjustExtension != null)
{
recoil *= recoilAdjustExtension.recoilModifier;
recoilRelaxation = recoilAdjustExtension.recoilTick > 0 ? recoilAdjustExtension.recoilTick : recoilRelaxation;
recoil = recoilAdjustExtension.recoilScale > 0 ? recoilAdjustExtension.recoilScale : recoil;
muzzleJumpModifier *= recoilAdjustExtension.muzzleJumpModifier > 0 ? recoilAdjustExtension.muzzleJumpModifier : 1;
}

if (recoil <= 0) { return; }

if (handheld)
{
if (weaponDef.weaponTags.Contains("CE_OneHandedWeapon"))
{
recoil /= 3;
muzzleJumpModifier *= 1.5f;
}
else
{
recoil /= 1.3f;
}
if (recoil > 15)
{
recoil = 15;
}
}

int num = Find.TickManager.TicksGame - shootVerb.LastShotTick;
if (num < recoilRelaxation)
{
float num2 = Mathf.Clamp01(num / recoilRelaxation);
float num3 = Mathf.Lerp(recoil, 0f, num2);
drawOffset = new Vector3(0f, 0f, 0f - RecoilCurveAxisY.Evaluate(num2)) * num3;
angleOffset = (handheld ? -1 : Rand.Sign) * RecoilCurveRotation.Evaluate(num2) * num3 * MuzzleRiseMagicNumber * muzzleJumpModifier;
drawOffset = drawOffset.RotatedBy(aimAngle);
aimAngle += angleOffset;
}
}
finally
{
Rand.PopState();
}
}
#endregion Misc

#region MoteThrower
Expand Down
6 changes: 6 additions & 0 deletions Source/CombatExtended/CombatExtended/Defs/GunDrawExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ public class GunDrawExtension : DefModExtension
public Vector2 DrawSize = Vector2.one;
public Vector2 DrawOffset = Vector2.zero;

public float recoilModifier = 1;
public float recoilScale = -1;
public int recoilTick = -1;
public float muzzleJumpModifier = -1;

public Vector2 CasingOffset = Vector2.zero;
public float CasingAngleOffset = 0;

}
}
5 changes: 5 additions & 0 deletions Source/CombatExtended/CombatExtended/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class Settings : ModSettings, ISettingsCE
private bool autosetup = true;
private bool showCasings = true;
private bool createCasingsFilth = true;
private bool recoilAnim = true;
private bool showTaunts = true;
private bool allowMeleeHunting = false;
private bool smokeEffects = true;
Expand Down Expand Up @@ -130,6 +131,8 @@ public class Settings : ModSettings, ISettingsCE

public bool CreateCasingsFilth => createCasingsFilth;

public bool RecoilAnim => recoilAnim;

#endregion

private bool lastAmmoSystemStatus;
Expand All @@ -141,6 +144,7 @@ public override void ExposeData()
base.ExposeData();
Scribe_Values.Look(ref showCasings, "showCasings", true);
Scribe_Values.Look(ref createCasingsFilth, "createCasingsFilth", true);
Scribe_Values.Look(ref recoilAnim, "recoilAnim", true);
Scribe_Values.Look(ref showTaunts, "showTaunts", true);
Scribe_Values.Look(ref allowMeleeHunting, "allowMeleeHunting", false);
Scribe_Values.Look(ref smokeEffects, "smokeEffects", true);
Expand Down Expand Up @@ -212,6 +216,7 @@ public void DoWindowContents(Rect canvas, ref int offset)
list.CheckboxLabeled("CE_Settings_PartialStats_Title".Translate(), ref partialstats, "CE_Settings_PartialStats_Desc".Translate());
list.CheckboxLabeled("CE_Settings_ShowCasings_Title".Translate(), ref showCasings, "CE_Settings_ShowCasings_Desc".Translate());
list.CheckboxLabeled("CE_Settings_СreateCasingsFilth_Title".Translate(), ref createCasingsFilth, "CE_Settings_СreateCasingsFilth_Desc".Translate());
list.CheckboxLabeled("CE_Settings_RecoilAnim_Title".Translate(), ref recoilAnim, "CE_Settings_RecoilAnim_Desc".Translate());
list.CheckboxLabeled("CE_Settings_ShowTaunts_Title".Translate(), ref showTaunts, "CE_Settings_ShowTaunts_Desc".Translate());
list.CheckboxLabeled("CE_Settings_AllowMeleeHunting_Title".Translate(), ref allowMeleeHunting, "CE_Settings_AllowMeleeHunting_Desc".Translate());
list.CheckboxLabeled("CE_Settings_SmokeEffects_Title".Translate(), ref smokeEffects, "CE_Settings_SmokeEffects_Desc".Translate());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,13 @@ public override string GetInspectString() // Replaced vanilla loaded text

public override void Draw()
{
top.DrawTurret(Vector3.zero, 0f);
Vector3 drawOffset = Vector3.zero;
float angleOffset = 0f;
if (Controller.settings.RecoilAnim)
{
CE_Utility.Recoil(def.building.turretGunDef, AttackVerb, out drawOffset, out angleOffset, top.CurRotation, false);
}
top.DrawTurret(drawOffset, angleOffset);
base.Draw();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1085,6 +1085,7 @@ public override bool TryCastShot()
CompReloadable.UsedOnce();
}
}
lastShotTick = Find.TickManager.TicksGame;
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ public virtual bool TryCastGlobalShot()
CompReloadable.UsedOnce();
}
}
lastShotTick = Find.TickManager.TicksGame;
return true;
}

Expand Down
44 changes: 43 additions & 1 deletion Source/CombatExtended/Harmony/Harmony_PawnRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -403,6 +404,10 @@ internal static class Harmony_PawnRenderer_DrawEquipmentAiming

private static Pawn pawn;

private static Vector3 recoilOffset = new Vector3();

private static float muzzleJump = 0;

private static Vector3 casingDrawPos;

private static readonly Matrix4x4 TBot5 = Matrix4x4.Translate(new Vector3(0, -0.006f, 0));
Expand All @@ -416,6 +421,16 @@ public static void Prefix(PawnRenderer __instance, Thing eq, Vector3 drawLoc)
casingDrawPos = drawLoc;
}

private static void RecoilCE(Thing eq, Vector3 position, float aimAngle, float num, CompEquippable compEquippable)
{
if (Controller.settings.RecoilAnim && compEquippable.PrimaryVerb.verbProps is VerbPropertiesCE)
{
CE_Utility.Recoil(eq.def, compEquippable.PrimaryVerb, out var drawOffset, out var angleOffset, aimAngle, true);
recoilOffset = drawOffset;
muzzleJump = angleOffset;
}
}

private static void DrawMesh(Mesh mesh, Matrix4x4 matrix, Material mat, int layer, Thing eq, Vector3 position, float aimAngle)
{
GunDrawExtension drawData = eq.def.GetModExtension<GunDrawExtension>() ?? new GunDrawExtension() { DrawSize = eq.def.graphicData.drawSize };
Expand All @@ -426,9 +441,10 @@ private static void DrawMesh(Mesh mesh, Matrix4x4 matrix, Material mat, int laye
if (aimAngle > 200 && aimAngle < 340)
{
posVec.x *= -1;
muzzleJump = -muzzleJump;
casingOffset.x *= -1;
}
matrix.SetTRS(position + posVec.RotatedBy(matrix.rotation.eulerAngles.y), matrix.rotation, scale);
matrix.SetTRS(position + posVec.RotatedBy(matrix.rotation.eulerAngles.y) + recoilOffset, Quaternion.AngleAxis(matrix.rotation.eulerAngles.y + muzzleJump, Vector3.up), scale);
CompEquippable compEquippable = eq.TryGetComp<CompEquippable>();
if (compEquippable != null && compEquippable.PrimaryVerb is Verb_ShootCE verbCE)
{
Expand All @@ -444,12 +460,38 @@ private static void DrawMesh(Mesh mesh, Matrix4x4 matrix, Material mat, int laye
}
}


/*
* This replace the last DrawMesh in
*/
internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var recoil_opcodes = new CodeInstruction[]
{
new CodeInstruction(OpCodes.Ldarg_1),
new CodeInstruction(OpCodes.Ldarg_2),
new CodeInstruction(OpCodes.Ldarg_3),
new CodeInstruction(OpCodes.Ldloc_1),
new CodeInstruction(OpCodes.Ldloc_2),
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Harmony_PawnRenderer_DrawEquipmentAiming), nameof(RecoilCE)))
};
bool foundRecoil = false;
int index = 0;
for (int i = 0; i < codes.Count; i++)
{
CodeInstruction code = codes[i];
if (foundRecoil && code.opcode == OpCodes.Stloc_1)
{
index = i + 1;
break;
}
else if (code.opcode == OpCodes.Call && ReferenceEquals(code.operand, typeof(EquipmentUtility).GetMethod("Recoil")))
{
foundRecoil = true;
}
}
codes.InsertRange(index, recoil_opcodes);
codes[codes.Count - 2].operand =
AccessTools.Method(typeof(Harmony_PawnRenderer_DrawEquipmentAiming), nameof(DrawMesh));
codes.InsertRange(codes.Count - 2, new[]
Expand Down

0 comments on commit 341bbfc

Please sign in to comment.