private void IL_JCFixedUpdate(ILContext il) { ILCursor c = new ILCursor(il); bool ILFound = c.TryGotoNext( x => x.MatchCallOrCallvirt <UnityEngine.Time>("get_fixedDeltaTime"), x => x.MatchAdd(), x => x.MatchStfld <JetpackController>("stopwatch")); if (ILFound) { c.Index++; c.Emit(OpCodes.Ldarg_0); c.EmitDelegate <Func <float, JetpackController, float> >((origDecr, jpc) => { EmbryoComponent cpt = jpc.GetFieldValue <CharacterBody>("targetBody").GetComponentInChildren <EmbryoComponent>(); if (!cpt || cpt.boostedJetTime <= 0) { return(origDecr); } cpt.boostedJetTime -= origDecr; return(0f); }); } else { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Jetpack (FixedUpdate); target instructions not found"); ILFailed = true; } }
private void IL_ESFireGateway(ILContext il) { ILCursor c = new ILCursor(il); EmbryoComponent cpt = null; c.Emit(OpCodes.Ldarg_0); c.EmitDelegate <Action <EquipmentSlot> >((slot) => { cpt = slot.characterBody?.GetComponentInChildren <EmbryoComponent>(); }); bool ILFound = c.TryGotoNext( x => x.MatchLdstr("Prefabs/NetworkedObjects/Zipline"), x => x.MatchCallOrCallvirt <UnityEngine.Resources>("Load"), x => x.MatchCallOrCallvirt <UnityEngine.Object>("Instantiate")); if (ILFound) { c.Index += 2; c.EmitDelegate <Func <GameObject, GameObject> >((origCtrl) => { if (cpt && cpt.boostedGates > 0) { cpt.boostedGates--; return(boostedGatewayPrefab); } else { return(origCtrl); } }); } else { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Gateway (FireGateway)"); } }
private void IL_ESFixedUpdate(ILContext il) { ILCursor c = new ILCursor(il); EmbryoComponent cpt = null; c.Emit(OpCodes.Ldarg_0); c.EmitDelegate <Action <EquipmentSlot> >((slot) => { cpt = slot.characterBody?.GetComponentInChildren <EmbryoComponent>(); }); bool ILFound; if (subEnable[EquipmentIndex.CommandMissile]) { //Find: loading of 0.125f into EquipmentSlot.missileTimer ILFound = c.TryGotoNext( x => x.OpCode == OpCodes.Ldc_R4, x => x.MatchStfld <EquipmentSlot>("missileTimer")); if (ILFound) { //Replace original missile cooldown (0.125f) with a custom function to check for Embryo-boosted missiles //If boosts exist, halves the missile cooldown to 0.0625f and deducts a boost from CPD; otherwise returns original c.Index++; c.EmitDelegate <Func <float, float> >((origCooldown) => { if (cpt && cpt.boostedMissiles > 0) { cpt.boostedMissiles--; return(origCooldown / 2); } return(origCooldown); }); } else { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: CommandMissile (FixedUpdate)"); } } if (subEnable[EquipmentIndex.BFG]) { //Find: string "Prefabs/Projectiles/BeamSphere" //Then find: CharacterBody.get_damage * 2f (damage argument of FireProjectile) ILFound = c.TryGotoNext( x => x.MatchLdstr("Prefabs/Projectiles/BeamSphere")) && c.TryGotoNext( x => x.MatchCallvirt <CharacterBody>("get_damage"), x => x.OpCode == OpCodes.Ldc_R4, x => x.MatchMul()); if (ILFound) { //Advance cursor to found ldcR4 c.Index += 2; //Replace original FireProjectile damage coefficient (2f) with a custom function to check for Embryo-boosted BFG shots //If boosts exist, doubles the damage coefficient to 4f and deducts a boost from CPD; otherwise returns original c.EmitDelegate <Func <float, float> >((origDamage) => { if (cpt && cpt.boostedBFGs > 0) { cpt.boostedBFGs--; return(origDamage * 2f); } return(origDamage); }); } else { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: BFG (FixedUpdate)"); } } }
private void IL_ESPerformEquipmentAction(ILContext il) { ILCursor c = new ILCursor(il); //Insert a check for Embryo procs at the top of the function bool boost = false; EmbryoComponent cpt = null; c.Emit(OpCodes.Ldarg_0); c.EmitDelegate <Action <EquipmentSlot> >((slot) => { boost = Util.CheckRoll(GetCount(slot.characterBody) * procChance); cpt = slot.characterBody?.GetComponentInChildren <EmbryoComponent>(); }); bool ILFound; ILLabel[] swarr = new ILLabel[] {}; //Load switch case locations ILFound = c.TryGotoNext( x => x.MatchSwitch(out swarr)); if (!ILFound) { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch (ALL EQUIPMENTS): couldn't find switch!"); return; } //CommandMissile: double number of missiles fired in the same timespan if ((int)EquipmentIndex.CommandMissile >= swarr.Length) { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: CommandMissile (PerformEquipmentAction); not in switch"); } else if (subEnable[EquipmentIndex.CommandMissile]) { //Find: default missile increment (+= (int)12) c.GotoLabel(swarr[(int)EquipmentIndex.CommandMissile]); ILFound = c.TryGotoNext( x => x.MatchLdfld <EquipmentSlot>("remainingMissiles"), x => x.OpCode == OpCodes.Ldc_I4_S, x => x.MatchAdd()); if (ILFound) { c.Index += 2; //Replace original increment number with a custom function to check for Embryo proc //If proc happens, doubles number of missiles added and marks the total number of missiles added as boosted; otherwise returns original c.EmitDelegate <Func <int, int> >((int origMissiles) => { if (boost && cpt) { cpt.boostedMissiles += origMissiles * 2; } return((sbyte)(boost ? origMissiles * 2 : origMissiles)); }); } else { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: CommandMissile (PerformEquipmentAction); target instructions not found"); } } //Blackhole: double yoink radius if ((int)EquipmentIndex.CommandMissile >= swarr.Length) { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Blackhole; not in switch"); } else if (subEnable[EquipmentIndex.Blackhole]) { //Find: string "Prefabs/Projectiles/GravSphere", ldloc 15 (Vector3 position) c.GotoLabel(swarr[(int)EquipmentIndex.Blackhole]); ILFound = c.TryGotoNext(MoveType.After, x => x.MatchLdstr("Prefabs/Projectiles/GravSphere"), x => x.MatchCallOrCallvirt <Resources>("Load")); if (ILFound) { //Insert a custom function to check for Embryo proc (captures GravSphere projectile prefab) //If proc happens, radius of prefab's RadialForce component (GravSphere pull range) is doubled c.EmitDelegate <Func <GameObject, GameObject> >((obj) => { var newobj = UnityEngine.Object.Instantiate(obj); if (boost) { newobj.GetComponent <RadialForce>().radius *= 2; } return(newobj); }); } else { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Blackhole; target instructions not found"); } } //CritOnUse: double duration if ((int)EquipmentIndex.CritOnUse >= swarr.Length) { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: CritOnUse; not in switch"); } else if (subEnable[EquipmentIndex.CritOnUse]) { //Find: AddTimedBuff(BuffIndex.FullCrit, 8f) c.GotoLabel(swarr[(int)EquipmentIndex.CritOnUse]); ILFound = c.TryGotoNext( x => x.MatchLdcI4((int)BuffIndex.FullCrit), x => x.OpCode == OpCodes.Ldc_R4, x => x.MatchCallOrCallvirt <CharacterBody>("AddTimedBuff")); if (ILFound) { //Advance cursor to the found ldcR4 (time argument of AddTimedBuff) c.Index += 2; //Replace original buff time with a custom function to check for Embryo proc //If proc happens, doubles the buff time; otherwise returns original c.EmitDelegate <Func <float, float> >((origBuffTime) => { if (cpt) { cpt.lastCOUBoosted = boost; } return(boost ? origBuffTime * 2 : origBuffTime); }); } else { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: CritOnUse; target instructions not found"); } } //Gateway: double speed if ((int)EquipmentIndex.Gateway >= swarr.Length) { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Gateway; not in switch"); } else if (subEnable[EquipmentIndex.Gateway]) { //Find: start of Gateway label c.GotoLabel(swarr[(int)EquipmentIndex.Gateway]); //Insert a custom function to check boost //If proc happens, increments the player's boosted gateway counter; this will be spent once the gateway spawns c.EmitDelegate <Action>(() => { if (boost && cpt) { cpt.boostedGates++; } }); } //Scanner: double duration if ((int)EquipmentIndex.Scanner >= swarr.Length) { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Scanner; not in switch"); } else if (subEnable[EquipmentIndex.Scanner]) { //Find: loading of prefab c.GotoLabel(swarr[(int)EquipmentIndex.Scanner]); ILFound = c.TryGotoNext(MoveType.After, x => x.MatchLdstr("Prefabs/NetworkedObjects/ChestScanner"), x => x.MatchCall <UnityEngine.Resources>("Load")); if (ILFound) { //Insert a custom function to check boost //If proc happens, replace the loaded prefab with a boosted copy (allows proper networking) c.EmitDelegate <Func <GameObject, GameObject> >((origObj) => { if (boost) { return(boostedScannerPrefab); } else { return(origObj); } }); } else { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Scanner; target instructions not found"); } } //BFG: double impact damage if ((int)EquipmentIndex.BFG >= swarr.Length) { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: BFG; not in switch"); } else if (subEnable[EquipmentIndex.BFG]) { //Find: loading of (int)2 into EquipmentSlot.bfgChargeTimer c.GotoLabel(swarr[(int)EquipmentIndex.BFG]); ILFound = c.TryGotoNext(MoveType.After, x => x.OpCode == OpCodes.Ldc_R4, x => x.MatchStfld <EquipmentSlot>("bfgChargeTimer")); if (ILFound) { //Insert a custom function to check boost //If proc happens, increments the player's boosted BFG shot counter; this will be spent once the BFG actually fires c.EmitDelegate <Action>(() => { if (boost && cpt) { cpt.boostedBFGs++; } }); } else { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: BFG (PerformEquipmentAction); target instructions not found"); } } //Jetpack: double duration if ((int)EquipmentIndex.Jetpack >= swarr.Length) { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Jetpack; not in switch"); } else if (subEnable[EquipmentIndex.Jetpack]) { //Find: start of Jetpack label c.GotoLabel(swarr[(int)EquipmentIndex.Jetpack]); //Insert a custom function to check boost //If proc happens, increments the player's boosted jetpack counter; this will be spent during the RPC duration reset c.EmitDelegate <Action>(() => { if (boost && cpt) { cpt.boostedJetTime = 15f; } }); } //FireBallDash: double speed and damage if ((int)EquipmentIndex.FireBallDash >= swarr.Length) { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: FireBallDash; not in switch"); } else if (subEnable[EquipmentIndex.FireBallDash]) { //Find: string "Prefabs/NetworkedObjects/FireballVehicle" //Then find: instantiation of the prefab c.GotoLabel(swarr[(int)EquipmentIndex.FireBallDash]); ILFound = c.TryGotoNext( x => x.MatchLdstr("Prefabs/NetworkedObjects/FireballVehicle")) && c.TryGotoNext( x => x.MatchCallOrCallvirt <UnityEngine.Object>("Instantiate")); if (ILFound) { c.Index++; //Insert a custom function to check boost (captures a dup of the instantiated FireballVehicle) //If proc happens, doubles the instanced vehicle's target speed, acceleration, and blast and collision damage c.Emit(OpCodes.Dup); c.EmitDelegate <Action <GameObject> >((go) => { if (boost) { go.GetComponent <FireballVehicle>().targetSpeed *= 2f; go.GetComponent <FireballVehicle>().acceleration *= 2f; go.GetComponent <FireballVehicle>().blastDamageCoefficient *= 2f; go.GetComponent <FireballVehicle>().overlapDamageCoefficient *= 2f; } }); } else { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: FireBallDash; target instructions not found"); } } //GainArmor: double duration if ((int)EquipmentIndex.GainArmor >= swarr.Length) { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: GainArmor; not in switch"); } else if (subEnable[EquipmentIndex.GainArmor]) { //Find: AddTimedBuff(BuffIndex.ElephantArmorBoost, 5f) c.GotoLabel(swarr[(int)EquipmentIndex.GainArmor]); ILFound = c.TryGotoNext( x => x.MatchLdcI4((int)BuffIndex.ElephantArmorBoost), x => x.OpCode == OpCodes.Ldc_R4, x => x.MatchCallvirt <CharacterBody>("AddTimedBuff")); if (ILFound) { //Advance cursor to the found ldcR4 (time argument of AddTimedBuff) c.Index += 2; //Replace original buff time (5f) with a custom function to check for Embryo proc //If proc happens, doubles the buff time to 10f; otherwise returns original c.EmitDelegate <Func <float, float> >((origBuffTime) => { return(boost?2 * origBuffTime:origBuffTime); }); } else { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: GainArmor; target instructions not found"); } } //Cleanse: double projectile delete radius /*if((int)EquipmentIndex.Cleanse >= swarr.Length) * ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Cleanse; not in switch"); * else if(subEnable[EquipmentIndex.Cleanse]) { * //Find: num3 = 6f; num4 = num3 * num3; * float origRadius = 6f; * c.GotoLabel(swarr[(int)EquipmentIndex.Cleanse]); * ILFound = c.TryGotoNext( * x=>x.MatchCallOrCallvirt<SetStateOnHurt>("Cleanse"), * x=>x.MatchLdcR4(out origRadius), * x=>x.MatchDup(), * x=>x.MatchMul(), * x=>x.OpCode == OpCodes.Stloc_S); * * if(ILFound) { * c.Index+=2; * c.EmitDelegate<Func<float,float>>((ofl)=>{ * return boost?2*ofl:ofl; * }); * } else { * ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Cleanse; target instructions not found"); * } * }*/ //Recycle: double recycle count if ((int)EquipmentIndex.Recycle >= swarr.Length) { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Recycle; not in switch"); } else if (subEnable[EquipmentIndex.Recycle]) { c.GotoLabel(swarr[(int)EquipmentIndex.Recycle]); ILFound = c.TryGotoNext( x => x.MatchLdloc(out _), x => x.MatchLdcI4(1), x => x.MatchCallOrCallvirt <GenericPickupController>("set_NetworkRecycled")); if (ILFound) { c.Index++; c.Emit(OpCodes.Dup); c.Index++; c.EmitDelegate <Func <GenericPickupController, bool, bool> >((pctrl, origRecyc) => { if (boost && pctrl && pctrl.GetComponent <EmbryoRecycleFlag>() == null) { pctrl.gameObject.AddComponent <EmbryoRecycleFlag>(); return(false); } return(true); }); } else { ClassicItemsPlugin._logger.LogError("Failed to apply Beating Embryo IL patch: Recycle; target instructions not found"); } } }