commit 46bc9baab86a9b0e283fd060d26c26e8d16d4d98 Author: ForeverPyrite Date: Tue Aug 12 00:01:48 2025 -0500 first commit, even though I've been "done" with the mod for like ever diff --git a/Buffs/Parasitism.cs b/Buffs/Parasitism.cs new file mode 100644 index 0000000..ce1e3ef --- /dev/null +++ b/Buffs/Parasitism.cs @@ -0,0 +1,85 @@ +using Siva.Projectiles; +using Terraria; +using Terraria.ModLoader; + + +namespace Siva.Buffs +{ + public class Parasitism : ModBuff + { + public static readonly int BuffType = ModContent.BuffType(); + + public override void SetStaticDefaults() + { + Main.debuff[Type] = true; // Indicates this is a negative effect. + Main.buffNoSave[Type] = true; // Indicates that the buff won't save when exiting the game. + Main.buffNoTimeDisplay[Type] = false; // Shows the duration on the buff icon. + } + + // What happens while the debuff is a applied + public override void Update(NPC npc, ref int buffIndex) + { + // Do I make them glow red or anything? + } + + public static int AttachedNaniteCount(NPC npc) + { + int attachedNanites = 0; + foreach (var p in Main.ActiveProjectiles) + { + if (p.type == SIVANanite.ProjectileType && p.ai[0] == 1f && p.ai[1] == npc.whoAmI) { + attachedNanites++; + // Main.NewText($"{attachedNanites}"); // Debug text + } + } + return attachedNanites; + } + + public static float GetParasitismScaleFactor(int attachedNanites) + { + if(attachedNanites <= 5) + { + return attachedNanites*0.3f; + } + else if(attachedNanites > 5 && attachedNanites < 100) + { + return (attachedNanites-5)*0.021f+1.5f; + } + else if(attachedNanites >= 100) + { + return 3.5f; + } + else + { + return 0f; + } + } + + public class ParasitismScaling : GlobalNPC + { + public override bool InstancePerEntity => true; + + public override void ModifyHitByProjectile(NPC npc, Projectile projectile, ref NPC.HitModifiers modifiers) + { + if(LogicPerfected.ParasitismOnTarget(npc) && projectile.owner == Main.myPlayer) // Check to make sure the projectile is the local players rather than any other projectils from any other gun. Trying to prevent hypothetical exploits. + { + if(LogicPerfected.NormBullet(projectile)) + { + float scaleFactor = GetParasitismScaleFactor(AttachedNaniteCount(npc)); + AddableFloat addableScaleFactor = AddableFloat.Zero + scaleFactor; + modifiers.ScalingBonusDamage = addableScaleFactor; + Main.NewText($"norm {scaleFactor}"); // debug text + } + else if(LogicPerfected.MasterBulletOrNanite(projectile)) + { + float scaleFactor = GetParasitismScaleFactor(AttachedNaniteCount(npc)); + AddableFloat addableScaleFactor = AddableFloat.Zero + scaleFactor; + modifiers.ScalingBonusDamage = addableScaleFactor; + Main.NewText($"master {scaleFactor}"); // debug text + } + } + } + } + } +} + diff --git a/Buffs/Parasitism.png b/Buffs/Parasitism.png new file mode 100644 index 0000000..b2893a0 Binary files /dev/null and b/Buffs/Parasitism.png differ diff --git a/Items/OutbreakCatalyst.cs b/Items/OutbreakCatalyst.cs new file mode 100644 index 0000000..431c98c --- /dev/null +++ b/Items/OutbreakCatalyst.cs @@ -0,0 +1,19 @@ +using Terraria; +using Terraria.ID; +using Terraria.ModLoader; + +namespace Siva.Items +{ + public class OutbreakCatalyst : ModItem + { + public override void SetDefaults() + { + Item.height = 24; + Item.width = 24; + Item.buyPrice(platinum: 5); + Item.sellPrice(0); + Item.maxStack = 1; + Item.rare = ItemRarityID.Yellow; + } + } +} \ No newline at end of file diff --git a/Items/OutbreakCatalyst.png b/Items/OutbreakCatalyst.png new file mode 100644 index 0000000..9854d59 Binary files /dev/null and b/Items/OutbreakCatalyst.png differ diff --git a/Localization/en-US_Mods.Siva.hjson b/Localization/en-US_Mods.Siva.hjson new file mode 100644 index 0000000..d4888df --- /dev/null +++ b/Localization/en-US_Mods.Siva.hjson @@ -0,0 +1,46 @@ +Buffs: { + Parasitism: { + DisplayName: Parasitism + Description: SIVA Nanites are attached to you! + } +} + +Items: { + Outbreak: { + DisplayName: Outbreak Perfected + Tooltip: + ''' + Critical kills with this weapon have a chance to drop it's Catalyst. + Combine this weapon with it's Catalyst at a Mythril/Orichalcum anvil in order to Masterwork it which will unlock new perks and slight stat bonuses. + Rapid hits and critical kills with this weapon will create SIVA nanites swarms. + SIVA nanites seek out and damage targets. + This weapon's damage increases against targets based on the number of SIVA nanites that are attached to them. + [c/FF0000:~directive = KILL while enemies = PRESENT: execute(directive)~] + ''' + } + + OutbreakCatalyst: { + DisplayName: Outbreak Catalyst + Tooltip: + ''' + Combine with Outbreak Perfected at an anvil to Masterwork it. + *Be glad I didn't make you get another 700 kills on top of what it already took* + ''' + } + + MasterworkedOutbreak: { + DisplayName: Outbreak Perfected + Tooltip: + ''' + [c/FFF014:Masterworked] + [c/FFF014:Increased nanite damage.] + [c/FFF014:Enemies that die with nanites attached to them generate additional nanites.] + Rapid hits and critical kills with this weapon will create SIVA nanites swarms. + SIVA nanites seek out and damage targets. + This weapon's damage increases against targets based on the number of SIVA nanites that are attached to them. + [c/FF0000:~directive = KILL while enemies = PRESENT: execute(directive)~] + ''' + } +} + +Projectiles.SIVANanite.DisplayName: SIVA Nanite diff --git a/Projectiles/SIVANanite.cs b/Projectiles/SIVANanite.cs new file mode 100644 index 0000000..263c1c9 --- /dev/null +++ b/Projectiles/SIVANanite.cs @@ -0,0 +1,212 @@ +using Microsoft.Xna.Framework; +using Siva.Buffs; +using Terraria; +using Terraria.DataStructures; +using Terraria.ID; +using Terraria.ModLoader; +using System; +using Microsoft.Xna.Framework.Graphics; + +namespace Siva.Projectiles +{ + // This Example show how to implement simple homing projectile + public class SIVANanite : ModProjectile + { + public static Texture2D glowMaskTexture; + public static readonly int ProjectileType = ModContent.ProjectileType(); + + // These properties wrap the usual ai arrays for cleaner and easier to understand code. + // Are we sticking to a target? + public bool IsStickingToTarget { + get => Projectile.ai[0] == 1f; + set => Projectile.ai[0] = value ? 1f : 0f; + } + + // Index of the current target + public int TargetWhoAmI { + get => (int)Projectile.ai[1]; + set => Projectile.ai[1] = value; + } + + public float TimeAttached { + get => Projectile.localAI[0]; + set => Projectile.localAI[0] = value; + } + + public float Damage + { + get => Projectile.localAI[1]; + set => Projectile.localAI[1] = value; + } + + public override void SetStaticDefaults() { + ProjectileID.Sets.CultistIsResistantTo[Projectile.type] = true; // Make the cultist resistant to this projectile, as it's resistant to all homing projectiles. + Main.projFrames[Projectile.type] = 4; + } + + // Setting the default parameters of the projectile + // You can check most of Fields and Properties here https://github.com/tModLoader/tModLoader/wiki/Projectile-Class-Documentation + public override void SetDefaults() { + + Projectile.width = 16; // The width of projectile hitbox + Projectile.height = 16; // The height of projectile hitbox + Projectile.scale = .8f; + Projectile.aiStyle = 0; // The ai style of the projectile (0 means custom AI). For more please reference the source code of Terraria + Projectile.DamageType = DamageClass.Ranged; // What type of damage does this projectile affect? + Projectile.friendly = true; // Can the projectile deal damage to enemies? + Projectile.hostile = false; // Can the projectile deal damage to the player? + Projectile.ignoreWater = true; // Does the projectile's speed be influenced by water? + Projectile.tileCollide = false; // Can the projectile collide with tiles? + Projectile.timeLeft = 180; // The live time for the projectile (60 = 1 second, so 420 is 7 seconds) + Projectile.penetrate = -1; + Projectile.CritChance = 0; + } + + public override void PostDraw(Color lightColor) + { + Texture2D NaniteGlow = new Texture2D() + Main.EntitySpriteDraw() + base.PostDraw(lightColor); + } + public override void AI() + { + Projectile.ai[2] += 1f; + if (++Projectile.frameCounter >= 5) { + Projectile.frameCounter = 0; + // Or more compactly Projectile.frame = ++Projectile.frame % Main.projFrames[Projectile.type]; + if (++Projectile.frame >= Main.projFrames[Projectile.type]) + Projectile.frame = 0; + } + Lighting.AddLight(Projectile.position, Color.Red.ToVector3() * 0.7f); + // Run either the Sticky AI or Normal AI + // Separating into different methods helps keeps your AI clean + // Main.NewText($"IsStickingToTarget = {IsStickingToTarget}"); // + if (IsStickingToTarget) + { + // Main.NewText("Calling StickyAI()"); // debug text + StickyAI(); + } + else { + NormalAI(); + } + } + + public void NormalAI() + { + float maxDetectRadius = 400f; // The maximum radius at which a projectile can detect a target + float projSpeed = 7f; // The speed at which the projectile moves towards the target + Projectile.damage = (int)Damage; // Sets the damage of the projectile to the damage it was created with. + // Trying to find NPC closest to the projectile + NPC closestNPC = FindClosestNPC(maxDetectRadius); + if (closestNPC != null) + { + // If a target is found, change the velocity of the projectile and rotate it in the direction of the target + Projectile.velocity = (closestNPC.Center - Projectile.Center).SafeNormalize(Vector2.Zero) * projSpeed; + Projectile.rotation = Projectile.velocity.ToRotation(); + } + else + { + Projectile.velocity = Vector2.Zero; + } + } + + private const int MaxTimeAttached = 540; + public void StickyAI() + { + TimeAttached += 1f; + // Main.NewText($"TimeAttached: {TimeAttached}"); // debug text + int npcTarget = TargetWhoAmI; + if (TimeAttached >= MaxTimeAttached) { // If the nanite has stuck for 9 seconds (or more) + Projectile.Kill(); + } + else if (Main.npc[npcTarget].active && !Main.npc[npcTarget].dontTakeDamage) { + // If the target is active and can take damage + // Set the projectile's position relative to the target's center + Projectile.Center = Main.npc[npcTarget].Center - Projectile.velocity * 2f; + Projectile.gfxOffY = Main.npc[npcTarget].gfxOffY; + } + else + { + Projectile.Kill(); + } + /* Very fun but a bit OP + else { // Otherwise, reset the timer to 3 seconds and unstick it + Projectile.timeLeft = 180; + IsStickingToTarget = false; + } + */ + + + } + + public NPC FindClosestNPC(float maxDetectDistance) + { + NPC closestNPC = null; + float sqrMaxDetectDistance = maxDetectDistance * maxDetectDistance; + + foreach (NPC target in Main.npc) + { + if (target.CanBeChasedBy()) + { + float sqrDistanceToTarget = Vector2.DistanceSquared(target.Center, Projectile.Center); + if (sqrDistanceToTarget < sqrMaxDetectDistance) + { + sqrMaxDetectDistance = sqrDistanceToTarget; + closestNPC = target; + } + } + } + return closestNPC; + } + + public override void OnSpawn(IEntitySource source) + { + Damage = Projectile.damage; + } + + public const int maxNanites = 100; + private readonly Point[] attachedNanites = new Point[maxNanites]; + public override void OnHitNPC(NPC target, NPC.HitInfo hit, int damageDone) + { + // Main.NewText($"{damageDone}"); // debug text + IsStickingToTarget = true; // we are sticking to a target + Projectile.damage = 0; // Makes sure it can't deal damage, this is reset when NormalAI() is called again. + TargetWhoAmI = target.whoAmI; // Set the target whoAmI + Projectile.velocity = (target.Center - Projectile.Center) * 0.75f; // Change velocity based on delta center of targets (difference between entity centers) + Projectile.netUpdate = true; // netUpdate this javelin + int na = 0; + foreach (var p in Main.ActiveProjectiles) // Refreses the duration of all of the nanites on the npc. + { + if (p.type == ProjectileType && p.ai[0] == 1f && p.ai[1] == TargetWhoAmI) + { + // Main.NewText("Refreshing duration of nanite"); // debug text + p.timeLeft = 540; + na++; + } + } + // Main.NewText(na); // debug text + // ExampleJavelinBuff handles the damage over time (DoT) + target.AddBuff(Parasitism.BuffType, 540); + } + + public static void Create(int naniteCount, NPC target, int projDamage) + { + // Main.NewText($"naniteCount = {naniteCount}"); //debug text + int i = 0; + while(i < naniteCount) // I'm proud of this basic function, draws the amount of nanites in a parabola above the target + { + float k = naniteCount/2; + // Main.NewText($"k = {k}"); //debug text + int x = (i - ((int)Math.Floor(k)))*4; + float y = 1.0f / 6.0f * x * x; + y -= target.height; + x *= 4; + y *= 2; + Projectile.NewProjectile(target.GetSource_FromThis(), Vector2.Add(target.Center, new Vector2(x, y)), Vector2.Zero, ProjectileType, projDamage, 0f, Main.myPlayer); // Might want to use Target.Top and make it not as high above them, big bosses have nanites spawn on them I think + // Main.NewText($"({x},{y})"); //debug text + i += 1; + } + // Main.NewText($"made {i} nanites off of {naniteCount}"); //debug text + } + } +} diff --git a/Projectiles/SIVANanite.png b/Projectiles/SIVANanite.png new file mode 100644 index 0000000..590b93d Binary files /dev/null and b/Projectiles/SIVANanite.png differ diff --git a/Projectiles/SIVANanite_Glow.png b/Projectiles/SIVANanite_Glow.png new file mode 100644 index 0000000..1d5f7c0 Binary files /dev/null and b/Projectiles/SIVANanite_Glow.png differ diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json new file mode 100644 index 0000000..ab8c14e --- /dev/null +++ b/Properties/launchSettings.json @@ -0,0 +1,16 @@ +{ + "profiles": { + "Terraria": { + "commandName": "Executable", + "executablePath": "$(DotNetName)", + "commandLineArgs": "$(tMLPath)", + "workingDirectory": "$(tMLSteamPath)" + }, + "TerrariaServer": { + "commandName": "Executable", + "executablePath": "$(DotNetName)", + "commandLineArgs": "$(tMLServerPath)", + "workingDirectory": "$(tMLSteamPath)" + } + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/Siva.cs b/Siva.cs new file mode 100644 index 0000000..092a0c1 --- /dev/null +++ b/Siva.cs @@ -0,0 +1,261 @@ +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)); + } + } + } + } +} \ No newline at end of file diff --git a/Siva.csproj b/Siva.csproj new file mode 100644 index 0000000..7853017 --- /dev/null +++ b/Siva.csproj @@ -0,0 +1,10 @@ + + + + + Siva + latest + + + + \ No newline at end of file diff --git a/Siva.sln b/Siva.sln new file mode 100644 index 0000000..02eb6a2 --- /dev/null +++ b/Siva.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Siva", "Siva.csproj", "{94357A29-B3C2-4A40-A301-4C495F984AA8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {94357A29-B3C2-4A40-A301-4C495F984AA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94357A29-B3C2-4A40-A301-4C495F984AA8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94357A29-B3C2-4A40-A301-4C495F984AA8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94357A29-B3C2-4A40-A301-4C495F984AA8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {13B26FFE-7FBA-4AD1-8598-C8797B82D8C1} + EndGlobalSection +EndGlobal diff --git a/Sounds/Pew.wav b/Sounds/Pew.wav new file mode 100644 index 0000000..0289b78 Binary files /dev/null and b/Sounds/Pew.wav differ diff --git a/Weapons/MasterworkedOutbreak.cs b/Weapons/MasterworkedOutbreak.cs new file mode 100644 index 0000000..0b3224d --- /dev/null +++ b/Weapons/MasterworkedOutbreak.cs @@ -0,0 +1,69 @@ +using Microsoft.Xna.Framework; +using Siva.Items; +using Terraria; +using Terraria.Audio; +using Terraria.ID; +using Terraria.ModLoader; + +namespace Siva.Weapons +{ + public class MasterworkedOutbreak : ModItem + { + public static readonly int ItemType = ModContent.ItemType(); + + public override void SetDefaults() + { + // Modders can use Item.DefaultToRangedWeapon to quickly set many common properties, such as: useTime, useAnimation, useStyle, autoReuse, DamageType, shoot, shootSpeed, useAmmo, and noMelee. These are all shown individually here for teaching purposes. + + // Common Properties + Item.width = 22; // Hitbox width of the item. + Item.height = 10; // Hitbox height of the item. + Item.scale = 0.75f; + Item.rare = ItemRarityID.Yellow; // The color that the item's name will be in-game. + + + // Use Properties + Item.useTime = 4; // one third of useAnimation for three burst effect I think + Item.useAnimation = 12; // The length of the item's use animation in ticks + Item.reuseDelay = 12; // The delay before you can shoot again, crazy I know + Item.useStyle = ItemUseStyleID.Shoot; // How you use the item (swinging, holding out, etc.) + Item.autoReuse = true; // Whether or not you can hold click to automatically use it again + + // Weapon Properties + Item.consumeAmmoOnLastShotOnly = true; + Item.DamageType = DamageClass.Ranged; // Sets the damage type to ranged. + Item.damage = 22; // Sets the item's damage. Note that projectiles shot by this weapon will use its and the used ammunition's damage added together. + Item.knockBack = 5f; // Sets the item's knockback. Note that projectiles shot by this weapon will use its and the used ammunition's knockback added together. + Item.noMelee = true; // So the item's animation doesn't do damage. + Item.crit = 34; // Sets the item's crit chance. Note that the player's base crit chance is 4% and will be added to the item's crit chance. + + // Gun Properties + Item.shoot = ProjectileID.PurificationPowder; // For some reason, all the guns in the vanilla source have this. + Item.shootSpeed = 20f; // The speed of the projectile (measured in pixels per frame.) + Item.useAmmo = AmmoID.Bullet; // The "ammo Id" of the ammo item that this weapon uses. Ammo IDs are magic numbers that usually correspond to the item id of one item that most commonly represent the ammo type. + + // The sound that this item plays when used. + Item.UseSound = new SoundStyle($"{nameof(Siva)}/Sounds/Pew") { + Volume = 1f, + PitchVariance = 0, + MaxInstances = 3, + }; + + } + + // This method lets you adjust position of the gun in the player's hands. Play with these values until it looks good with your graphics. + public override Vector2? HoldoutOffset() { + return new Vector2(2f, 0f); + } + + public override void AddRecipes() + { + CreateRecipe() + .AddIngredient() + .AddIngredient() + .AddTile(TileID.MythrilAnvil) + .Register(); + } + + } +} \ No newline at end of file diff --git a/Weapons/MasterworkedOutbreak.png b/Weapons/MasterworkedOutbreak.png new file mode 100644 index 0000000..206b758 Binary files /dev/null and b/Weapons/MasterworkedOutbreak.png differ diff --git a/Weapons/Outbreak.cs b/Weapons/Outbreak.cs new file mode 100644 index 0000000..0ab042a --- /dev/null +++ b/Weapons/Outbreak.cs @@ -0,0 +1,115 @@ +using System; +using Microsoft.Xna.Framework; +using Terraria; +using Terraria.Audio; +using Terraria.ID; +using Terraria.ModLoader; +using Siva.Items; + +namespace Siva.Weapons +{ + public class Outbreak : ModItem + { + public static readonly int ItemType = ModContent.ItemType(); + + public override void SetDefaults() + { + // Modders can use Item.DefaultToRangedWeapon to quickly set many common properties, such as: useTime, useAnimation, useStyle, autoReuse, DamageType, shoot, shootSpeed, useAmmo, and noMelee. These are all shown individually here for teaching purposes. + + // Common Properties + Item.width = 22; // Hitbox width of the item. + Item.height = 10; // Hitbox height of the item. + Item.scale = 0.75f; + Item.rare = ItemRarityID.Red; // The color that the item's name will be in-game. + + + // Use Properties + Item.useTime = 4; // one third of useAnimation for three burst effect I think + Item.useAnimation = 12; // The length of the item's use animation in ticks + Item.reuseDelay = 12; // The delay before you can shoot again, crazy I know + Item.useStyle = ItemUseStyleID.Shoot; // How you use the item (swinging, holding out, etc.) + Item.autoReuse = true; // Whether or not you can hold click to automatically use it again + + // Weapon Properties + Item.consumeAmmoOnLastShotOnly = true; + Item.DamageType = DamageClass.Ranged; // Sets the damage type to ranged. + Item.damage = 22; // Sets the item's damage. Note that projectiles shot by this weapon will use its and the used ammunition's damage added together. + Item.knockBack = 3f; // Sets the item's knockback. Note that projectiles shot by this weapon will use its and the used ammunition's knockback added together. + Item.noMelee = true; // So the item's animation doesn't do damage. + Item.crit = 29; // Sets the item's crit chance. Note that the player's base crit chance is 4% and will be added to the item's crit chance. + + // Gun Properties + Item.shoot = ProjectileID.PurificationPowder; // For some reason, all the guns in the vanilla source have this. + Item.shootSpeed = 20f; // The speed of the projectile (measured in pixels per frame.) + Item.useAmmo = AmmoID.Bullet; // The "ammo Id" of the ammo item that this weapon uses. Ammo IDs are magic numbers that usually correspond to the item id of one item that most commonly represent the ammo type. + + // The sound that this item plays when used. + Item.UseSound = new SoundStyle($"{nameof(Siva)}/Sounds/Pew") { + Volume = 1f, + PitchVariance = 0, + MaxInstances = 3, + }; + + } + + // This method lets you adjust position of the gun in the player's hands. Play with these values until it looks good with your graphics. + public override Vector2? HoldoutOffset() { + return new Vector2(2f, 0f); + } + } + + public class CatalystDropChance : GlobalNPC + { + private static readonly Random Random = new Random(); + public override void OnHitByProjectile(NPC npc, Projectile projectile, NPC.HitInfo hit, int damageDone) + { + Player player = Main.player[projectile.owner]; + if(!player.HasItemInAnyInventory(ModContent.ItemType()) && player.HeldItem.type == Outbreak.ItemType && projectile.aiStyle == 1 && hit.Crit && npc.life <= 0 && !npc.SpawnedFromStatue && npc.lifeMax > 5) + { + int randomNumber = Random.Next(350); + + // Main.NewText($"Rolled Catty drop chance, randint is {randomNumber}"); // debug text + if(randomNumber == 77) + { + player.QuickSpawnItem(player.GetSource_FromThis(), ModContent.ItemType()); + } + } + } + } +} + + // Old nanite creation method. + /* + public class NaniteCreation : GlobalProjectile // Creates 1-3 Nanites on critical hits + { + // Alters the base behavior of what happens when a projectile hits an NPC + public override void OnHitNPC(Projectile projectile, NPC target, NPC.HitInfo hit, int damage) + { + // Accessing the player who shot the projectile + Player player = Main.player[projectile.owner]; + // Checks to make sure the player is holding MasterworkedOutbreak and the projectile was not a SIVA Nanite so they don't spawn more nanites. + if (player.HeldItem.type == ModContent.ItemType() & projectile.type != ModContent.ProjectileType()) + { + if(hit.Crit) // On critical hits + { + int randomNumber = new Random().Next(101); // Random number 0-10 + if (randomNumber <= 60) // 60% chance to make a nanite + { + SIVANanite.Create(1, projectile, target, damage/2); + } + else if (60 < randomNumber & randomNumber <= 90) // 30% chance to make a nanite twice + { + SIVANanite.Create(2, projectile, target, damage/2); + } + else if (90 < randomNumber & randomNumber <= 100) // 10% chance to make a nanite thrice + { + SIVANanite.Create(3, projectile, target, damage/2); + } + + } + } + } + + }*/ + + diff --git a/Weapons/Outbreak.png b/Weapons/Outbreak.png new file mode 100644 index 0000000..7b81d68 Binary files /dev/null and b/Weapons/Outbreak.png differ diff --git a/build.txt b/build.txt new file mode 100644 index 0000000..64fda52 --- /dev/null +++ b/build.txt @@ -0,0 +1,3 @@ +displayName = Outbreak Perfected +author = ForeverPyrite +version = 0.9.8 \ No newline at end of file diff --git a/description.txt b/description.txt new file mode 100644 index 0000000..4861aca --- /dev/null +++ b/description.txt @@ -0,0 +1,7 @@ +This mod introduces Outbreak Perfected, the iconic pulse rifle from the Destiny francise, into Terraria. + +This mod includes: + - Outbreak Perfected (Obtainable by defeating Moss Hornets in the Jungle, might change this, but I was inspired by the Uzi or something...) + - Outbreak Perfected Catalyst (Rarely found from getting Critical Kills. Since it's already rare I didn't add an extra 700 kills to match Destiny lol) + +This one is very accurate to the game, yeah. Not much to say. Just adds the one weapon. \ No newline at end of file diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..2f85fe3 Binary files /dev/null and b/icon.png differ diff --git a/obj/Debug/net8.0/.NETCoreApp,Version=v8.0.AssemblyAttributes.cs b/obj/Debug/net8.0/.NETCoreApp,Version=v8.0.AssemblyAttributes.cs new file mode 100644 index 0000000..678fc5f --- /dev/null +++ b/obj/Debug/net8.0/.NETCoreApp,Version=v8.0.AssemblyAttributes.cs @@ -0,0 +1,4 @@ +// +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")] diff --git a/obj/Debug/net8.0/Siva.AssemblyInfo.cs b/obj/Debug/net8.0/Siva.AssemblyInfo.cs new file mode 100644 index 0000000..d7878ce --- /dev/null +++ b/obj/Debug/net8.0/Siva.AssemblyInfo.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using System; +using System.Reflection; + +[assembly: System.Reflection.AssemblyCompanyAttribute("Siva")] +[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] +[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")] +[assembly: System.Reflection.AssemblyProductAttribute("Siva")] +[assembly: System.Reflection.AssemblyTitleAttribute("Siva")] +[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] + +// Generated by the MSBuild WriteCodeFragment class. + diff --git a/obj/Debug/net8.0/Siva.AssemblyInfoInputs.cache b/obj/Debug/net8.0/Siva.AssemblyInfoInputs.cache new file mode 100644 index 0000000..eba20e0 --- /dev/null +++ b/obj/Debug/net8.0/Siva.AssemblyInfoInputs.cache @@ -0,0 +1 @@ +47b2cfa54c9c9d42493b9726e8ede67436d64078f36f1068b1efa251fb950172 diff --git a/obj/Debug/net8.0/Siva.GeneratedMSBuildEditorConfig.editorconfig b/obj/Debug/net8.0/Siva.GeneratedMSBuildEditorConfig.editorconfig new file mode 100644 index 0000000..98ca99c --- /dev/null +++ b/obj/Debug/net8.0/Siva.GeneratedMSBuildEditorConfig.editorconfig @@ -0,0 +1,15 @@ +is_global = true +build_property.TargetFramework = net8.0 +build_property.TargetPlatformMinVersion = +build_property.UsingMicrosoftNETSdkWeb = +build_property.ProjectTypeGuids = +build_property.InvariantGlobalization = +build_property.PlatformNeutralAssembly = +build_property.EnforceExtendedAnalyzerRules = +build_property._SupportedPlatformList = Linux,macOS,Windows +build_property.RootNamespace = Siva +build_property.ProjectDir = C:\Users\ForeverPyrite\Documents\My Games\Terraria\tModLoader\ModSources\Siva\ +build_property.EnableComHosting = +build_property.EnableGeneratedComInterfaceComImportInterop = +build_property.EffectiveAnalysisLevelStyle = 8.0 +build_property.EnableCodeStyleSeverity = diff --git a/obj/Debug/net8.0/Siva.assets.cache b/obj/Debug/net8.0/Siva.assets.cache new file mode 100644 index 0000000..6cb9993 Binary files /dev/null and b/obj/Debug/net8.0/Siva.assets.cache differ diff --git a/obj/Debug/net8.0/Siva.csproj.AssemblyReference.cache b/obj/Debug/net8.0/Siva.csproj.AssemblyReference.cache new file mode 100644 index 0000000..d0c1e27 Binary files /dev/null and b/obj/Debug/net8.0/Siva.csproj.AssemblyReference.cache differ diff --git a/obj/Siva.csproj.nuget.dgspec.json b/obj/Siva.csproj.nuget.dgspec.json new file mode 100644 index 0000000..b46049d --- /dev/null +++ b/obj/Siva.csproj.nuget.dgspec.json @@ -0,0 +1,87 @@ +{ + "format": 1, + "restore": { + "C:\\Users\\ForeverPyrite\\Documents\\My Games\\Terraria\\tModLoader\\ModSources\\Siva\\Siva.csproj": {} + }, + "projects": { + "C:\\Users\\ForeverPyrite\\Documents\\My Games\\Terraria\\tModLoader\\ModSources\\Siva\\Siva.csproj": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "C:\\Users\\ForeverPyrite\\Documents\\My Games\\Terraria\\tModLoader\\ModSources\\Siva\\Siva.csproj", + "projectName": "Siva", + "projectPath": "C:\\Users\\ForeverPyrite\\Documents\\My Games\\Terraria\\tModLoader\\ModSources\\Siva\\Siva.csproj", + "packagesPath": "C:\\Users\\ForeverPyrite\\.nuget\\packages\\", + "outputPath": "C:\\Users\\ForeverPyrite\\Documents\\My Games\\Terraria\\tModLoader\\ModSources\\Siva\\obj\\", + "projectStyle": "PackageReference", + "configFilePaths": [ + "C:\\Users\\ForeverPyrite\\AppData\\Roaming\\NuGet\\NuGet.Config" + ], + "originalTargetFrameworks": [ + "net8.0" + ], + "sources": { + "https://api.nuget.org/v3/index.json": {} + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "projectReferences": {} + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + }, + "restoreAuditProperties": { + "enableAudit": "true", + "auditLevel": "low", + "auditMode": "direct" + }, + "SdkAnalysisLevel": "9.0.200" + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "dependencies": { + "tModLoader.CodeAssist": { + "target": "Package", + "version": "[0.1.*, )" + } + }, + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48", + "net481" + ], + "assetTargetFallback": true, + "warn": true, + "downloadDependencies": [ + { + "name": "Microsoft.AspNetCore.App.Ref", + "version": "[8.0.13, 8.0.13]" + }, + { + "name": "Microsoft.NETCore.App.Ref", + "version": "[8.0.13, 8.0.13]" + }, + { + "name": "Microsoft.WindowsDesktop.App.Ref", + "version": "[8.0.13, 8.0.13]" + } + ], + "frameworkReferences": { + "Microsoft.NETCore.App": { + "privateAssets": "all" + } + }, + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.200/PortableRuntimeIdentifierGraph.json" + } + } + } + } +} \ No newline at end of file diff --git a/obj/Siva.csproj.nuget.g.props b/obj/Siva.csproj.nuget.g.props new file mode 100644 index 0000000..55e8fe7 --- /dev/null +++ b/obj/Siva.csproj.nuget.g.props @@ -0,0 +1,18 @@ + + + + True + NuGet + $(MSBuildThisFileDirectory)project.assets.json + $(UserProfile)\.nuget\packages\ + C:\Users\ForeverPyrite\.nuget\packages\ + PackageReference + 6.13.0 + + + + + + C:\Users\ForeverPyrite\.nuget\packages\tmodloader.codeassist\0.1.5 + + \ No newline at end of file diff --git a/obj/Siva.csproj.nuget.g.targets b/obj/Siva.csproj.nuget.g.targets new file mode 100644 index 0000000..35a7576 --- /dev/null +++ b/obj/Siva.csproj.nuget.g.targets @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/obj/project.assets.json b/obj/project.assets.json new file mode 100644 index 0000000..55c9e0b --- /dev/null +++ b/obj/project.assets.json @@ -0,0 +1,115 @@ +{ + "version": 3, + "targets": { + "net8.0": { + "tModLoader.CodeAssist/0.1.5": { + "type": "package" + } + } + }, + "libraries": { + "tModLoader.CodeAssist/0.1.5": { + "sha512": "Sg/jKEg/z5eN+Cdi3/9CU4JfJwvXHVH+UDJf4Lipo26uTa+guLLJXH1XDJNPShTFwKK8jMUYbthINP4QhsgHSA==", + "type": "package", + "path": "tmodloader.codeassist/0.1.5", + "hasTools": true, + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "NugetIcon.png", + "analyzers/dotnet/cs/tModLoader.CodeAssist.dll", + "tmodloader.codeassist.0.1.5.nupkg.sha512", + "tmodloader.codeassist.nuspec", + "tools/install.ps1", + "tools/uninstall.ps1" + ] + } + }, + "projectFileDependencyGroups": { + "net8.0": [ + "tModLoader.CodeAssist >= 0.1.*" + ] + }, + "packageFolders": { + "C:\\Users\\ForeverPyrite\\.nuget\\packages\\": {} + }, + "project": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "C:\\Users\\ForeverPyrite\\Documents\\My Games\\Terraria\\tModLoader\\ModSources\\Siva\\Siva.csproj", + "projectName": "Siva", + "projectPath": "C:\\Users\\ForeverPyrite\\Documents\\My Games\\Terraria\\tModLoader\\ModSources\\Siva\\Siva.csproj", + "packagesPath": "C:\\Users\\ForeverPyrite\\.nuget\\packages\\", + "outputPath": "C:\\Users\\ForeverPyrite\\Documents\\My Games\\Terraria\\tModLoader\\ModSources\\Siva\\obj\\", + "projectStyle": "PackageReference", + "configFilePaths": [ + "C:\\Users\\ForeverPyrite\\AppData\\Roaming\\NuGet\\NuGet.Config" + ], + "originalTargetFrameworks": [ + "net8.0" + ], + "sources": { + "https://api.nuget.org/v3/index.json": {} + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "projectReferences": {} + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + }, + "restoreAuditProperties": { + "enableAudit": "true", + "auditLevel": "low", + "auditMode": "direct" + }, + "SdkAnalysisLevel": "9.0.200" + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "dependencies": { + "tModLoader.CodeAssist": { + "target": "Package", + "version": "[0.1.*, )" + } + }, + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48", + "net481" + ], + "assetTargetFallback": true, + "warn": true, + "downloadDependencies": [ + { + "name": "Microsoft.AspNetCore.App.Ref", + "version": "[8.0.13, 8.0.13]" + }, + { + "name": "Microsoft.NETCore.App.Ref", + "version": "[8.0.13, 8.0.13]" + }, + { + "name": "Microsoft.WindowsDesktop.App.Ref", + "version": "[8.0.13, 8.0.13]" + } + ], + "frameworkReferences": { + "Microsoft.NETCore.App": { + "privateAssets": "all" + } + }, + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.200/PortableRuntimeIdentifierGraph.json" + } + } + } +} \ No newline at end of file diff --git a/obj/project.nuget.cache b/obj/project.nuget.cache new file mode 100644 index 0000000..6ca90db --- /dev/null +++ b/obj/project.nuget.cache @@ -0,0 +1,13 @@ +{ + "version": 2, + "dgSpecHash": "xoCv9EvyOl0=", + "success": true, + "projectFilePath": "C:\\Users\\ForeverPyrite\\Documents\\My Games\\Terraria\\tModLoader\\ModSources\\Siva\\Siva.csproj", + "expectedPackageFiles": [ + "C:\\Users\\ForeverPyrite\\.nuget\\packages\\tmodloader.codeassist\\0.1.5\\tmodloader.codeassist.0.1.5.nupkg.sha512", + "C:\\Users\\ForeverPyrite\\.nuget\\packages\\microsoft.netcore.app.ref\\8.0.13\\microsoft.netcore.app.ref.8.0.13.nupkg.sha512", + "C:\\Users\\ForeverPyrite\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\8.0.13\\microsoft.windowsdesktop.app.ref.8.0.13.nupkg.sha512", + "C:\\Users\\ForeverPyrite\\.nuget\\packages\\microsoft.aspnetcore.app.ref\\8.0.13\\microsoft.aspnetcore.app.ref.8.0.13.nupkg.sha512" + ], + "logs": [] +} \ No newline at end of file