first commit, even though I've been "done" with the mod for like ever

This commit is contained in:
ForeverPyrite
2025-08-12 00:01:48 -05:00
commit 46bc9baab8
32 changed files with 1145 additions and 0 deletions

85
Buffs/Parasitism.cs Normal file
View File

@@ -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<Parasitism>();
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
}
}
}
}
}
}

BIN
Buffs/Parasitism.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

19
Items/OutbreakCatalyst.cs Normal file
View File

@@ -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;
}
}
}

BIN
Items/OutbreakCatalyst.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -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

212
Projectiles/SIVANanite.cs Normal file
View File

@@ -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<SIVANanite>();
// 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
}
}
}

BIN
Projectiles/SIVANanite.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 921 B

View File

@@ -0,0 +1,16 @@
{
"profiles": {
"Terraria": {
"commandName": "Executable",
"executablePath": "$(DotNetName)",
"commandLineArgs": "$(tMLPath)",
"workingDirectory": "$(tMLSteamPath)"
},
"TerrariaServer": {
"commandName": "Executable",
"executablePath": "$(DotNetName)",
"commandLineArgs": "$(tMLServerPath)",
"workingDirectory": "$(tMLSteamPath)"
}
}
}

0
README.md Normal file
View File

261
Siva.cs Normal file
View File

@@ -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<DropRateInfo> 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<IItemDropRuleChainAttempt> ChainedRules => new List<IItemDropRuleChainAttempt>();
}
// 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));
}
}
}
}
}

10
Siva.csproj Normal file
View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\tModLoader.targets" />
<PropertyGroup>
<AssemblyName>Siva</AssemblyName>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
</ItemGroup>
</Project>

25
Siva.sln Normal file
View File

@@ -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

BIN
Sounds/Pew.wav Normal file

Binary file not shown.

View File

@@ -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<MasterworkedOutbreak>();
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<Outbreak>()
.AddIngredient<OutbreakCatalyst>()
.AddTile(TileID.MythrilAnvil)
.Register();
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

115
Weapons/Outbreak.cs Normal file
View File

@@ -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<Outbreak>();
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<OutbreakCatalyst>()) && 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<OutbreakCatalyst>());
}
}
}
}
}
// 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<MasterworkedOutbreak>() & projectile.type != ModContent.ProjectileType<SIVANanite>())
{
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);
}
}
}
}
}*/

BIN
Weapons/Outbreak.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

3
build.txt Normal file
View File

@@ -0,0 +1,3 @@
displayName = Outbreak Perfected
author = ForeverPyrite
version = 0.9.8

7
description.txt Normal file
View File

@@ -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.

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]

View File

@@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
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.

View File

@@ -0,0 +1 @@
47b2cfa54c9c9d42493b9726e8ede67436d64078f36f1068b1efa251fb950172

View File

@@ -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 =

Binary file not shown.

Binary file not shown.

View File

@@ -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"
}
}
}
}
}

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\ForeverPyrite\.nuget\packages\</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.13.0</NuGetToolVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="C:\Users\ForeverPyrite\.nuget\packages\" />
</ItemGroup>
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<PkgtModLoader_CodeAssist Condition=" '$(PkgtModLoader_CodeAssist)' == '' ">C:\Users\ForeverPyrite\.nuget\packages\tmodloader.codeassist\0.1.5</PkgtModLoader_CodeAssist>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />

115
obj/project.assets.json Normal file
View File

@@ -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"
}
}
}
}

13
obj/project.nuget.cache Normal file
View File

@@ -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": []
}