-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #19 from Nick-NCSU/development
Development
- Loading branch information
Showing
16 changed files
with
447 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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--; | ||
} | ||
} | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
||
} | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.