Skip to content

Commit

Permalink
Merge pull request #19 from Nick-NCSU/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
Nick-NCSU authored Nov 2, 2021
2 parents 4124280 + 36f3b0b commit 783a8f8
Show file tree
Hide file tree
Showing 16 changed files with 447 additions and 5 deletions.
32 changes: 32 additions & 0 deletions Buffs/ChlorophyteBuff.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Microsoft.Xna.Framework;
using System;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace DubNation.Buffs
{
public class ChlorophyteBuff : ModBuff
{
public override void SetDefaults()
{
DisplayName.SetDefault("Chlorophyte Minion");
Description.SetDefault("The chlorophyte minion will fight for you");
Main.buffNoSave[Type] = true;
Main.buffNoTimeDisplay[Type] = true;
}

public override void Update(Player player, ref int buffIndex)
{
if (player.ownedProjectileCounts[ModContent.ProjectileType<Projectiles.ChlorophyteMinion>()] > 0)
{
player.buffTime[buffIndex] = 18000;
}
else
{
player.DelBuff(buffIndex);
buffIndex--;
}
}
}
}
Binary file added Buffs/ChlorophyteBuff.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed Items/AdamantiteStaff.png
Binary file not shown.
54 changes: 54 additions & 0 deletions Items/ChlorophyteStaff.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using Microsoft.Xna.Framework;
using System;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace DubNation.Items
{
class ChlorophyteStaff : ModItem
{
public override void SetStaticDefaults()
{
DisplayName.SetDefault("Chlorophyte Staff");
Tooltip.SetDefault("Summons a chlorophyte minion to fight for you.");
ItemID.Sets.GamepadWholeScreenUseRange[item.type] = true;
ItemID.Sets.LockOnIgnoresCollision[item.type] = true;
}

public override void SetDefaults()
{
item.damage = 10;
item.width = 40;
item.height = 40;
item.useTime = 36;
item.useAnimation = 36;
item.useStyle = ItemUseStyleID.SwingThrow;
item.value = 10;
item.rare = ItemRarityID.Blue;
item.UseSound = SoundID.Item1;
item.mana = 10;
item.noMelee = true;
item.summon = true;
item.buffType = ModContent.BuffType<Buffs.ChlorophyteBuff>();
item.shoot = ModContent.ProjectileType<Projectiles.ChlorophyteMinion>();
}

public override bool Shoot(Player player, ref Vector2 position, ref float speedX, ref float speedY, ref int type, ref int damage, ref float knockBack)
{
player.AddBuff(item.buffType, 2);
position = Main.MouseWorld;
return true;
}

public override void AddRecipes()
{
ModRecipe recipe = new ModRecipe(mod);
recipe.AddIngredient(ItemID.ChlorophyteBar, 9);
recipe.AddIngredient(ItemID.TurtleShell, 1);
recipe.AddTile(TileID.MythrilAnvil);
recipe.SetResult(this);
recipe.AddRecipe();
}
}
}
Binary file removed Items/CobaltStaff.png
Binary file not shown.
4 changes: 2 additions & 2 deletions Items/HallowedStaff.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ public override bool Shoot(Player player, ref Vector2 position, ref float speedX
public override void AddRecipes()
{
ModRecipe recipe = new ModRecipe(mod);
recipe.AddIngredient(ItemID.CopperBar, 9);
recipe.AddTile(TileID.Anvils);
recipe.AddIngredient(ItemID.HallowedBar, 8);
recipe.AddTile(TileID.MythrilAnvil);
recipe.SetResult(this);
recipe.AddRecipe();
}
Expand Down
Binary file removed Items/MythrilStaff.png
Binary file not shown.
Binary file removed Items/OrichalcumStaff.png
Binary file not shown.
Binary file removed Items/PalladiumStaff.png
Binary file not shown.
Binary file removed Items/TitaniumStaff.png
Binary file not shown.
248 changes: 248 additions & 0 deletions Projectiles/ChlorophyteMinion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
using Microsoft.Xna.Framework;
using System;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace DubNation.Projectiles
{
public class ChlorophyteMinion : ModProjectile
{
public override void SetStaticDefaults()
{
DisplayName.SetDefault("Chlorophyte");
// Sets the amount of frames this minion has on its spritesheet
Main.projFrames[projectile.type] = 2;
// This is necessary for right-click targeting
ProjectileID.Sets.MinionTargettingFeature[projectile.type] = true;

// These below are needed for a minion
// Denotes that this projectile is a pet or minion
Main.projPet[projectile.type] = true;
// This is needed so your minion can properly spawn when summoned and replaced when other minions are summoned
ProjectileID.Sets.MinionSacrificable[projectile.type] = true;
// Don't mistake this with "if this is true, then it will automatically home". It is just for damage reduction for certain NPCs
ProjectileID.Sets.Homing[projectile.type] = true;
}

public sealed override void SetDefaults()
{
projectile.width = 72;
projectile.height = 44;
// Makes the minion go through tiles freely
projectile.tileCollide = false;

// These below are needed for a minion weapon
// Only controls if it deals damage to enemies on contact (more on that later)
projectile.friendly = true;
// Only determines the damage type
projectile.minion = true;
// Amount of slots this minion occupies from the total minion slots available to the player (more on that later)
projectile.minionSlots = 1f;
// Needed so the minion doesn't despawn on collision with enemies or tiles
projectile.penetrate = -1;
}

// Here you can decide if your minion breaks things like grass or pots
public override bool? CanCutTiles()
{
return false;
}

// This is mandatory if your minion deals contact damage (further related stuff in AI() in the Movement region)
public override bool MinionContactDamage()
{
return false;
}

int delay = 0;
public override void AI()
{
Player player = Main.player[projectile.owner];

#region Active check
// This is the "active check", makes sure the minion is alive while the player is alive, and despawns if not
if (player.dead || !player.active)
{
player.ClearBuff(ModContent.BuffType<Buffs.ChlorophyteBuff>());
}
if (player.HasBuff(ModContent.BuffType<Buffs.ChlorophyteBuff>()))
{
projectile.timeLeft = 2;
}
#endregion

#region General behavior
Vector2 idlePosition = player.Center;
idlePosition.Y -= 48f; // Go up 48 coordinates (three tiles from the center of the player)

// If your minion doesn't aimlessly move around when it's idle, you need to "put" it into the line of other summoned minions
// The index is projectile.minionPos
float minionPositionOffsetX = (10 + projectile.minionPos * 40) * -player.direction;
idlePosition.X += minionPositionOffsetX; // Go behind the player

// All of this code below this line is adapted from Spazmamini code (ID 388, aiStyle 66)

// Teleport to player if distance is too big
Vector2 vectorToIdlePosition = idlePosition - projectile.Center;
float distanceToIdlePosition = vectorToIdlePosition.Length();
if (Main.myPlayer == player.whoAmI && distanceToIdlePosition > 2000f)
{
// Whenever you deal with non-regular events that change the behavior or position drastically, make sure to only run the code on the owner of the projectile,
// and then set netUpdate to true
projectile.position = idlePosition;
projectile.velocity *= 0.1f;
projectile.netUpdate = true;
}

// If your minion is flying, you want to do this independently of any conditions
float overlapVelocity = 0.04f;
for (int i = 0; i < Main.maxProjectiles; i++)
{
// Fix overlap with other minions
Projectile other = Main.projectile[i];
if (i != projectile.whoAmI && other.active && other.owner == projectile.owner && Math.Abs(projectile.position.X - other.position.X) + Math.Abs(projectile.position.Y - other.position.Y) < projectile.width)
{
if (projectile.position.X < other.position.X) projectile.velocity.X -= overlapVelocity;
else projectile.velocity.X += overlapVelocity;

if (projectile.position.Y < other.position.Y) projectile.velocity.Y -= overlapVelocity;
else projectile.velocity.Y += overlapVelocity;
}
}
#endregion

#region Find target
// Starting search distance
float distanceFromTarget = 700f;
Vector2 targetCenter = projectile.position;
Vector2 targetBehind = projectile.position;
bool foundTarget = false;

// This code is required if your minion weapon has the targeting feature
if (player.HasMinionAttackTargetNPC)
{
NPC npc = Main.npc[player.MinionAttackTargetNPC];
float between = Vector2.Distance(npc.Center, projectile.Center);
// Reasonable distance away so it doesn't target across multiple screens
if (between < 2000f)
{
distanceFromTarget = between;
targetCenter = npc.Center;
targetBehind = npc.Center - new Vector2(npc.direction * 50, 30);
foundTarget = true;
}
}
if (!foundTarget)
{
// This code is required either way, used for finding a target
for (int i = 0; i < Main.maxNPCs; i++)
{
NPC npc = Main.npc[i];
if (npc.CanBeChasedBy())
{
float between = Vector2.Distance(npc.Center, projectile.Center);
bool closest = Vector2.Distance(projectile.Center, targetCenter) > between;
bool inRange = between < distanceFromTarget;
bool lineOfSight = Collision.CanHitLine(projectile.position, projectile.width, projectile.height, npc.position, npc.width, npc.height);
// Additional check for this specific minion behavior, otherwise it will stop attacking once it dashed through an enemy while flying though tiles afterwards
// The number depends on various parameters seen in the movement code below. Test different ones out until it works alright
bool closeThroughWall = between < 100f;
if (((closest && inRange) || !foundTarget) && (lineOfSight || closeThroughWall))
{
distanceFromTarget = between;
targetCenter = npc.Center;
targetBehind = npc.Center - new Vector2(npc.direction * 50, 30);
foundTarget = true;
}
}
}
}

// friendly needs to be set to true so the minion can deal contact damage
// friendly needs to be set to false so it doesn't damage things like target dummies while idling
// Both things depend on if it has a target or not, so it's just one assignment here
// You don't need this assignment if your minion is shooting things instead of dealing contact damage
projectile.friendly = foundTarget;
#endregion

#region Attack
float projSpeed2 = 6f;
if (delay == 0)
{
projectile.frame = 0;
if (foundTarget)
{
delay = 120;
projectile.frame = 1;
Vector2 minionToProjectile = projectile.Center - targetCenter;
minionToProjectile.Normalize();
minionToProjectile *= projSpeed2;
Vector2 velocity = -minionToProjectile;
Projectile.NewProjectile(projectile.Center, velocity, ModContent.ProjectileType<ChlorophyteProjectile>(), 60, 8, projectile.owner);
}
}
else
{
projectile.frame = delay < 30 ? 0 : 1;
delay--;
}
#endregion

#region Movement
// Default movement parameters (here for attacking)
float speed = 8f;
float inertia = 20f;

if (foundTarget)
{
// The immediate range around the target (so it doesn't latch onto it when close)
Vector2 direction = targetBehind - projectile.Center;
direction.Normalize();
direction *= speed;
projectile.velocity = (projectile.velocity * (inertia - 1) + direction) / inertia;
}
else
{
// Minion doesn't have a target: return to player and idle
if (distanceToIdlePosition > 600f)
{
// Speed up the minion if it's away from the player
speed = 12f;
inertia = 60f;
}
else
{
// Slow down the minion if closer to the player
speed = 4f;
inertia = 80f;
}
if (distanceToIdlePosition > 20f)
{
// The immediate range around the player (when it passively floats about)

// This is a simple movement formula using the two parameters and its desired direction to create a "homing" movement
vectorToIdlePosition.Normalize();
vectorToIdlePosition *= speed;
projectile.velocity = (projectile.velocity * (inertia - 1) + vectorToIdlePosition) / inertia;
}
else if (projectile.velocity == Vector2.Zero)
{
// If there is a case where it's not moving at all, give it a little "poke"
projectile.velocity.X = -0.15f;
projectile.velocity.Y = -0.05f;
}
}
#endregion

#region Animation and visuals
// So it will lean slightly towards the direction it's moving
projectile.rotation = projectile.velocity.X * 0.05f;

// Some visuals here
Lighting.AddLight(projectile.Center, Color.White.ToVector3() * 0.78f);
#endregion

}
}
}
Binary file added Projectiles/ChlorophyteMinion.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 783a8f8

Please sign in to comment.