using System; using System.Timers; using System.Collections.Generic; using MonoMod.RuntimeDetour; using Terraria; using Terraria.ModLoader; using Terraria.GameContent.ItemDropRules; using Terraria.ID; using Terraria.ModLoader.Default; using Terraria.ModLoader.UI; using Terraria.DataStructures; using Siva; using Siva.Weapons; using Siva.Buffs; using Siva.Projectiles; // only thing that really needs to be active mod wide is the drop chance and reduced crit, so here it is namespace Siva { public class Siva : Mod { public class ReducedCritMulti : GlobalNPC // Modifies the crit multiplier on NPCs so it doesn't do insane damage with it's high crit chance { // Alters the base behavior of what happens when an NPC is hit by a projectile. public override void ModifyHitByProjectile(NPC npc, Projectile projectile, ref NPC.HitModifiers modifiers) { if(LogicPerfected.HeldIsNorm() && LogicPerfected.Bullet(projectile)) { modifiers.CritDamage = new(1.6f, 1f); // Modifies Outbreak's bullets crit damage to 60% to match in game } else if(LogicPerfected.HeldIsMaster() && LogicPerfected.Bullet(projectile)) { modifiers.CritDamage = new(1.55f, 1f); // Modifies Outbreak's bullets crit damage to 55% as a little terraria bonus } } } public class OutbreakDrop : GlobalNPC { public override void ModifyNPCLoot(NPC npc, NPCLoot npcLoot) { if (npc.type == NPCID.MossHornet) { npcLoot.Add(new ConditionalNormalvsExpertDropRule(Outbreak.ItemType, 350, 250, new Conditions.DownedAllMechBosses())); } } } } public class LogicPerfected // I got so fed up with typing the same long ass hard to read conditionals, so I made this class. { public static bool HeldIsNorm() { return Main.LocalPlayer.HeldItem.type == Outbreak.ItemType; } public static bool HeldIsMaster() { return Main.LocalPlayer.HeldItem.type == MasterworkedOutbreak.ItemType; } public static bool Bullet(Projectile projectile) // Really it just checks that it's a projectile that acts like a bullet while you are holding Outbreak, best way I could find to do it :/. This means hypothetically someone can shoot bullets from any other gun then quickswap. Probably not useful but I'll see.. { return projectile.aiStyle == 1; } public static bool Nanite(Projectile projectile) { return projectile.type == SIVANanite.ProjectileType; } public static bool NormBullet(Projectile projectile) { return HeldIsNorm() && Bullet(projectile); } public static bool MasterBulletOrNanite(Projectile projectile) { return HeldIsMaster() && (Bullet(projectile) || Nanite(projectile)); } public static bool ParasitismOnTarget(NPC npc) { return npc.FindBuffIndex(Parasitism.BuffType) != -1; // FindBuffIndex returns -1 if the buff isn't found } } } // none of the base game drops have a condition for the state of the game you are in and also have different chances based on expert vs master public class ConditionalNormalvsExpertDropRule : IItemDropRule { private int itemId; private int normalModeChance; private int expertModeChance; private IItemDropRuleCondition condition; public ConditionalNormalvsExpertDropRule(int itemId, int normalModeChance, int expertModeChance, IItemDropRuleCondition condition) { this.itemId = itemId; this.normalModeChance = normalModeChance; this.expertModeChance = expertModeChance; this.condition = condition; } public bool CanDrop(DropAttemptInfo info) { return condition.CanDrop(info); } public ItemDropAttemptResult TryDroppingItem(DropAttemptInfo info) { ItemDropAttemptResult result = new ItemDropAttemptResult(); if (CanDrop(info)) { int chance = info.IsExpertMode ? expertModeChance : normalModeChance; if (info.player.RollLuck(chance) == 0) { CommonCode.DropItem(info, itemId, 1, true); result.State = ItemDropAttemptResultState.Success; } else { result.State = ItemDropAttemptResultState.FailedRandomRoll; } } else { result.State = ItemDropAttemptResultState.DoesntFillConditions; } return result; } public void ReportDroprates(List drops, DropRateInfoChainFeed ratesInfo) { DropRateInfoChainFeed ratesInfo2 = ratesInfo.With(1f); ratesInfo2.AddCondition(condition); float chance = ratesInfo2.parentDroprateChance != 0 && ratesInfo2.parentDroprateChance != 1f ? ratesInfo2.parentDroprateChance : (ratesInfo2.parentDroprateChance == 1f ? expertModeChance : normalModeChance); float dropRate = 1f / chance * ratesInfo2.parentDroprateChance; drops.Add(new DropRateInfo(itemId, 1, 1, dropRate, ratesInfo2.conditions)); } public List ChainedRules => new List(); } // A timer to make sure the nanite hits are consecutive public class RapidHitTimer { private Timer hitTimer; public RapidHitTimer() { hitTimer = new Timer { AutoReset = false, }; hitTimer.Elapsed += NaniteCreation.ResetHits; } public void Start() { hitTimer.Interval = 2000; hitTimer.Enabled = true; } public void Stop() { hitTimer.Enabled = false; } } /* Nanite creation about to go crazy I used to have one class in each of them that was copied and pasted, and I think it might still make more sense to put them in there Deriving them off of each other isn't too important, I just wanted to try more programming things. I also just wanted it to be a lot more readable, hence all the extra functions like OutbreakBullet and OutbreakNanite, as well as overriding OutbreakBullet instead of making a new class just to try it. */ public class NaniteCreation : GlobalProjectile // Creates Nanites on sustained fire and critical kills. { // Alters the base behavior of what happens when a projectile hits an NPC protected static int outbreakHits = 0; protected static RapidHitTimer hitTimer = new RapidHitTimer(); protected static void ResetHits() { outbreakHits = 0; hitTimer.Stop(); } public static void ResetHits(Object ph, ElapsedEventArgs ph2) // Have to cater to Timer.Elapsed so I made an overload for it { outbreakHits = 0; hitTimer.Stop(); } public override void OnHitNPC(Projectile projectile, NPC target, NPC.HitInfo hit, int damage) { // Checks to make sure the player is holding Outbreak and the projectile was a bullet, as well as making sure that the life of the target is more than 5 (ie not magic or a critter). if (LogicPerfected.NormBullet(projectile) && target.lifeMax > 5) { outbreakHits++; hitTimer.Start(); // Main.NewText($"{outbreakHits}"); //debug text if(outbreakHits >= 12) { int naniteCount = new Random().Next(2, 5); // It's just how ranges work in this function, I didn't make it, go yell at Microsoft SIVANanite.Create(naniteCount, target, (int)(hit.SourceDamage*2.21)); ResetHits(); } if(hit.Crit & target.life <= 0) // On critical kills make 9 nanites { // Main.NewText("Critical Kill!"); // debug text SIVANanite.Create(9, target, (int)(hit.SourceDamage*1.38)); } } } } public class MasterworkedNaniteCreation : NaniteCreation { private new static int outbreakHits = 0; public override void OnHitNPC(Projectile projectile, NPC target, NPC.HitInfo hit, int damage) { // Checks to make sure the player is holding MasterworkedOutbreak, and that the targets max life is more than 5 (ie. not magic/critters), as well as making sure the projectile is either a bullet or a siva nanite. if (LogicPerfected.MasterBulletOrNanite(projectile) && target.lifeMax > 5) { // Checks to make sure that it was a bullet and not a nanite or anything if(LogicPerfected.Bullet(projectile)) { outbreakHits++; hitTimer.Start(); if(outbreakHits >= 12) { int naniteCount = new Random().Next(2, 5); // It's just how ranges work in this function, I didn't make it, go yell at Microsoft SIVANanite.Create(naniteCount, target, (int)(hit.SourceDamage*2.21)); outbreakHits = 0; ResetHits(); } } if(target.life <= 0) // On kill { if(target.FindBuffIndex(Parasitism.BuffType) != -1) // If parasitism is in the buff list of the target { SIVANanite.Create(4, target, (int)(damage*0.6)); // It's actually 15% (46.8 vs 7) of the normal ones in game according to the Destiny Data Compendium spreedsheets and I forgot to check it myself. Maybe it was pvp or something. Idk. But they also scale damage so that might be why. } if(hit.Crit) // If the final blow was a crit { SIVANanite.Create(9, target, (int)(hit.SourceDamage*1.38)); } } } } }