add beam wand (thanks lexy)
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
using Terraria;
|
||||||
|
using Terraria.ID;
|
||||||
|
using Terraria.ModLoader;
|
||||||
|
using Terraria.GameContent.ItemDropRules;
|
||||||
|
|
||||||
|
namespace ForgottenWawiTech.Content.Items.Global
|
||||||
|
{
|
||||||
|
// Adds BeamAttackWand to Eye of Cthulhu treasure bag loot table.
|
||||||
|
public class EyeOfCthulhuBagLoot : GlobalItem
|
||||||
|
{
|
||||||
|
public override void ModifyItemLoot(Item item, ItemLoot itemLoot)
|
||||||
|
{
|
||||||
|
if (item.type == ItemID.EyeOfCthulhuBossBag)
|
||||||
|
{
|
||||||
|
// 1 in 3 chance (adjust if too common)
|
||||||
|
itemLoot.Add(ItemDropRule.Common(ModContent.ItemType<Weapons.Magic.BeamAttackWand>(), 3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
using ForgottenWawiTech.Content.Projectiles;
|
||||||
|
using ForgottenWawiTech.Content.Players;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
using Terraria;
|
||||||
|
using Terraria.ID;
|
||||||
|
using Terraria.ModLoader;
|
||||||
|
|
||||||
|
namespace ForgottenWawiTech.Content.Items.Weapons.Magic
|
||||||
|
{
|
||||||
|
// A post-Eye of Cthulhu melee/magic hybrid style weapon that swings like a sword and
|
||||||
|
// emits a flurry of short-lived beam projectiles toward the cursor during the swing.
|
||||||
|
// Beams have reduced damage (50% of item base) and very short lifetime.
|
||||||
|
public class BeamAttackWand : ModItem
|
||||||
|
{
|
||||||
|
public override void SetStaticDefaults()
|
||||||
|
{
|
||||||
|
// DisplayName.SetDefault("Beam Attack Wand"); // tML auto localizes; optional if using hjson.
|
||||||
|
// Tooltip.SetDefault("Swings and releases a flurry of brief energy beams");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetDefaults()
|
||||||
|
{
|
||||||
|
Item.width = 32;
|
||||||
|
Item.height = 32;
|
||||||
|
Item.useStyle = ItemUseStyleID.Swing;
|
||||||
|
Item.useAnimation = 28; // frames the swing lasts
|
||||||
|
Item.useTime = 28; // one use per full animation
|
||||||
|
Item.autoReuse = true;
|
||||||
|
Item.UseSound = SoundID.Item1;
|
||||||
|
Item.damage = 25;
|
||||||
|
Item.knockBack = 4.5f;
|
||||||
|
Item.DamageType = DamageClass.Magic; // treat beams as melee for synergy
|
||||||
|
Item.rare = ItemRarityID.Blue; // around early game progression
|
||||||
|
Item.value = Item.buyPrice(silver: 50);
|
||||||
|
Item.shoot = ModContent.ProjectileType<BeamAttackWandBeam>();
|
||||||
|
Item.shootSpeed = 12f; // base speed of beams
|
||||||
|
Item.noMelee = false;
|
||||||
|
Item.mana = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No recipe: obtained from Eye of Cthulhu treasure bag (see GlobalItem loot class)
|
||||||
|
|
||||||
|
public override void HoldItem(Player player)
|
||||||
|
{
|
||||||
|
// Emit beams only while the item is mid-swing
|
||||||
|
if (player.itemAnimation > 0 && player.HeldItem.type == Item.type && player.whoAmI == Main.myPlayer)
|
||||||
|
{
|
||||||
|
var modPlayer = player.GetModPlayer<BeamAttackWandPlayer>();
|
||||||
|
modPlayer.BeamEmissionTimer--;
|
||||||
|
if (modPlayer.BeamEmissionTimer <= 0)
|
||||||
|
{
|
||||||
|
// Kirby-style beam: a sweeping whip that leaves a multi-beam arc.
|
||||||
|
// We'll spawn several beams at once in a tight angular spread around a smoothed central swing rotation.
|
||||||
|
float targetRot = player.itemRotation + (player.direction == -1 ? MathHelper.Pi : 0f);
|
||||||
|
if (modPlayer.LastBeamRot == 0f)
|
||||||
|
modPlayer.LastBeamRot = targetRot;
|
||||||
|
// Smooth aggressively so the arc persists briefly (lower factor = larger persistence)
|
||||||
|
float smoothedRot = modPlayer.LastBeamRot + MathHelper.WrapAngle(targetRot - modPlayer.LastBeamRot) * 0.25f;
|
||||||
|
modPlayer.LastBeamRot = smoothedRot;
|
||||||
|
|
||||||
|
// Determine swing progress (0..1) to slightly widen arc near middle of swing
|
||||||
|
float swingProgress = 1f - (player.itemAnimation / (float)player.itemAnimationMax);
|
||||||
|
// Arc width scales up mid-swing then back down
|
||||||
|
float baseArc = 0.30f; // radians total small arc
|
||||||
|
float arcWidth = baseArc * (float)(1.0 - System.Math.Pow(2 * swingProgress - 1, 2)); // bell curve widen
|
||||||
|
|
||||||
|
int beamsThisBurst = 4; // number of simultaneous beams
|
||||||
|
int beamDamage = (int)(Item.damage * 0.45f); // slight reduction since multiple per tick
|
||||||
|
for (int i = 0; i < beamsThisBurst; i++)
|
||||||
|
{
|
||||||
|
// Evenly distribute within arc centered on smoothedRot
|
||||||
|
float lerp = beamsThisBurst == 1 ? 0f : i / (float)(beamsThisBurst - 1);
|
||||||
|
float offset = MathHelper.Lerp(-arcWidth * 0.5f, arcWidth * 0.5f, lerp);
|
||||||
|
float beamRot = smoothedRot + offset;
|
||||||
|
Vector2 dir = beamRot.ToRotationVector2();
|
||||||
|
if (dir.HasNaNs()) dir = new Vector2(player.direction, 0f);
|
||||||
|
dir = dir.SafeNormalize(new Vector2(player.direction, 0f));
|
||||||
|
|
||||||
|
// Stagger spawn distance to create a whip chain illusion (front beams slightly further)
|
||||||
|
float distance = 14f + 3f * i;
|
||||||
|
Vector2 spawnPos = player.Center + dir * distance + new Vector2(0f, -4f);
|
||||||
|
Vector2 velocity = dir * Item.shootSpeed * (1f + i * 0.05f); // slight incremental speed
|
||||||
|
|
||||||
|
Projectile.NewProjectile(player.GetSource_ItemUse(Item), spawnPos, velocity,
|
||||||
|
Item.shoot, beamDamage, Item.knockBack, player.whoAmI);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit burst every 3 ticks to keep DPS reasonable while visually dense
|
||||||
|
modPlayer.BeamEmissionTimer = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,119 @@
|
|||||||
|
using Terraria;
|
||||||
|
using Terraria.ID;
|
||||||
|
using Terraria.ModLoader;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
|
||||||
|
namespace ForgottenWawiTech.Content.NPCs.Enemies.AvalianInvasion
|
||||||
|
{
|
||||||
|
public class AvalianEliteCommando : ModNPC
|
||||||
|
{
|
||||||
|
private int attackTimer = 0;
|
||||||
|
private int teleportTimer = 0;
|
||||||
|
|
||||||
|
public override void SetStaticDefaults()
|
||||||
|
{
|
||||||
|
Main.npcFrameCount[Type] = 8;
|
||||||
|
NPCID.Sets.NPCBestiaryDrawModifiers value = new NPCID.Sets.NPCBestiaryDrawModifiers(0)
|
||||||
|
{
|
||||||
|
Velocity = 1f
|
||||||
|
};
|
||||||
|
NPCID.Sets.NPCBestiaryDrawOffset.Add(Type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetDefaults()
|
||||||
|
{
|
||||||
|
NPC.width = 38;
|
||||||
|
NPC.height = 48;
|
||||||
|
NPC.damage = 70;
|
||||||
|
NPC.defense = 30;
|
||||||
|
NPC.lifeMax = 800;
|
||||||
|
NPC.HitSound = SoundID.NPCHit1;
|
||||||
|
NPC.DeathSound = SoundID.NPCDeath6;
|
||||||
|
NPC.value = Item.buyPrice(0, 0, 50, 0);
|
||||||
|
NPC.knockBackResist = 0.1f;
|
||||||
|
NPC.aiStyle = NPCAIStyleID.Bat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void AI()
|
||||||
|
{
|
||||||
|
Player target = Main.player[NPC.target];
|
||||||
|
if (target.active && !target.dead)
|
||||||
|
{
|
||||||
|
attackTimer++;
|
||||||
|
teleportTimer++;
|
||||||
|
|
||||||
|
// Fast movement
|
||||||
|
Vector2 toPlayer = Vector2.Normalize(target.Center - NPC.Center);
|
||||||
|
float distanceToPlayer = Vector2.Distance(NPC.Center, target.Center);
|
||||||
|
|
||||||
|
// Teleport mechanic every 180 frames (3 seconds)
|
||||||
|
if (teleportTimer >= 180 && distanceToPlayer > 200f)
|
||||||
|
{
|
||||||
|
teleportTimer = 0;
|
||||||
|
|
||||||
|
// Teleport dust effect
|
||||||
|
for (int i = 0; i < 20; i++)
|
||||||
|
{
|
||||||
|
Dust.NewDust(NPC.position, NPC.width, NPC.height, DustID.Electric);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Teleport near player
|
||||||
|
Vector2 teleportPos = target.Center + new Vector2(Main.rand.Next(-200, 200), -150);
|
||||||
|
NPC.position = teleportPos;
|
||||||
|
|
||||||
|
for (int i = 0; i < 20; i++)
|
||||||
|
{
|
||||||
|
Dust.NewDust(NPC.position, NPC.width, NPC.height, DustID.Electric);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rapid fire attack every 30 frames (0.5 seconds)
|
||||||
|
if (attackTimer >= 30)
|
||||||
|
{
|
||||||
|
attackTimer = 0;
|
||||||
|
|
||||||
|
if (Main.netMode != NetmodeID.MultiplayerClient)
|
||||||
|
{
|
||||||
|
Vector2 shootDirection = Vector2.Normalize(target.Center - NPC.Center);
|
||||||
|
|
||||||
|
// Triple shot spread
|
||||||
|
for (int i = -1; i <= 1; i++)
|
||||||
|
{
|
||||||
|
Vector2 perturbedSpeed = shootDirection.RotatedBy(MathHelper.ToRadians(15 * i));
|
||||||
|
Projectile.NewProjectile(NPC.GetSource_FromAI(), NPC.Center,
|
||||||
|
perturbedSpeed * 14f, ProjectileID.MartianTurretBolt,
|
||||||
|
NPC.damage / 3, 1f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggressive movement
|
||||||
|
if (distanceToPlayer > 150f)
|
||||||
|
{
|
||||||
|
NPC.velocity = toPlayer * 6f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HitEffect(NPC.HitInfo hit)
|
||||||
|
{
|
||||||
|
if (NPC.life <= 0)
|
||||||
|
{
|
||||||
|
// Epic death effects
|
||||||
|
for (int i = 0; i < 20; i++)
|
||||||
|
{
|
||||||
|
Dust.NewDust(NPC.position, NPC.width, NPC.height, DustID.Feather);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 15; i++)
|
||||||
|
{
|
||||||
|
Dust.NewDust(NPC.position, NPC.width, NPC.height, DustID.Electric);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float SpawnChance(NPCSpawnInfo spawnInfo)
|
||||||
|
{
|
||||||
|
return 0f; // Controlled by invasion system
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
using Terraria;
|
||||||
|
using Terraria.ID;
|
||||||
|
using Terraria.ModLoader;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
|
||||||
|
namespace ForgottenWawiTech.Content.NPCs.Enemies.AvalianInvasion
|
||||||
|
{
|
||||||
|
public class AvalianRifleman : ModNPC
|
||||||
|
{
|
||||||
|
private int shootTimer = 0;
|
||||||
|
|
||||||
|
public override void SetStaticDefaults()
|
||||||
|
{
|
||||||
|
Main.npcFrameCount[Type] = 6;
|
||||||
|
NPCID.Sets.NPCBestiaryDrawModifiers value = new NPCID.Sets.NPCBestiaryDrawModifiers(0)
|
||||||
|
{
|
||||||
|
Velocity = 1f
|
||||||
|
};
|
||||||
|
NPCID.Sets.NPCBestiaryDrawOffset.Add(Type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetDefaults()
|
||||||
|
{
|
||||||
|
NPC.width = 34;
|
||||||
|
NPC.height = 42;
|
||||||
|
NPC.damage = 55;
|
||||||
|
NPC.defense = 20;
|
||||||
|
NPC.lifeMax = 400;
|
||||||
|
NPC.HitSound = SoundID.NPCHit1;
|
||||||
|
NPC.DeathSound = SoundID.NPCDeath6;
|
||||||
|
NPC.value = Item.buyPrice(0, 0, 10, 0);
|
||||||
|
NPC.knockBackResist = 0.3f;
|
||||||
|
NPC.aiStyle = NPCAIStyleID.Bat; // Flying ranged enemy
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void AI()
|
||||||
|
{
|
||||||
|
// Ranged attack behavior
|
||||||
|
Player target = Main.player[NPC.target];
|
||||||
|
if (target.active && !target.dead)
|
||||||
|
{
|
||||||
|
shootTimer++;
|
||||||
|
|
||||||
|
// Keep distance from player
|
||||||
|
float distanceToPlayer = Vector2.Distance(NPC.Center, target.Center);
|
||||||
|
if (distanceToPlayer < 300f)
|
||||||
|
{
|
||||||
|
// Move away
|
||||||
|
Vector2 awayDirection = Vector2.Normalize(NPC.Center - target.Center);
|
||||||
|
NPC.velocity = awayDirection * 3f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shoot every 90 frames (1.5 seconds)
|
||||||
|
if (shootTimer >= 90)
|
||||||
|
{
|
||||||
|
shootTimer = 0;
|
||||||
|
|
||||||
|
if (Main.netMode != NetmodeID.MultiplayerClient)
|
||||||
|
{
|
||||||
|
Vector2 shootDirection = Vector2.Normalize(target.Center - NPC.Center);
|
||||||
|
Projectile.NewProjectile(NPC.GetSource_FromAI(), NPC.Center,
|
||||||
|
shootDirection * 10f, ProjectileID.BulletHighVelocity,
|
||||||
|
NPC.damage / 2, 1f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HitEffect(NPC.HitInfo hit)
|
||||||
|
{
|
||||||
|
if (NPC.life <= 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
Dust.NewDust(NPC.position, NPC.width, NPC.height, DustID.Feather);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float SpawnChance(NPCSpawnInfo spawnInfo)
|
||||||
|
{
|
||||||
|
return 0f; // Controlled by invasion system
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
using Terraria;
|
||||||
|
using Terraria.ID;
|
||||||
|
using Terraria.ModLoader;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
|
||||||
|
namespace ForgottenWawiTech.Content.NPCs.Enemies.AvalianInvasion
|
||||||
|
{
|
||||||
|
public class AvalianScout : ModNPC
|
||||||
|
{
|
||||||
|
public override void SetStaticDefaults()
|
||||||
|
{
|
||||||
|
Main.npcFrameCount[Type] = 6; // Animation frames
|
||||||
|
NPCID.Sets.NPCBestiaryDrawModifiers value = new NPCID.Sets.NPCBestiaryDrawModifiers(0)
|
||||||
|
{
|
||||||
|
Velocity = 1f
|
||||||
|
};
|
||||||
|
NPCID.Sets.NPCBestiaryDrawOffset.Add(Type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetDefaults()
|
||||||
|
{
|
||||||
|
NPC.width = 32;
|
||||||
|
NPC.height = 40;
|
||||||
|
NPC.damage = 45;
|
||||||
|
NPC.defense = 15;
|
||||||
|
NPC.lifeMax = 250;
|
||||||
|
NPC.HitSound = SoundID.NPCHit1;
|
||||||
|
NPC.DeathSound = SoundID.NPCDeath6;
|
||||||
|
NPC.value = Item.buyPrice(0, 0, 5, 0);
|
||||||
|
NPC.knockBackResist = 0.5f;
|
||||||
|
NPC.aiStyle = NPCAIStyleID.Fighter; // Basic fighter AI
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void AI()
|
||||||
|
{
|
||||||
|
// Add subtle hover/flight behavior
|
||||||
|
if (NPC.velocity.Y > -2f && NPC.velocity.Y < 2f)
|
||||||
|
{
|
||||||
|
NPC.velocity.Y += Main.rand.NextFloat(-0.1f, 0.1f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HitEffect(NPC.HitInfo hit)
|
||||||
|
{
|
||||||
|
// Feather particles on hit
|
||||||
|
if (NPC.life <= 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
Dust.NewDust(NPC.position, NPC.width, NPC.height, DustID.Feather,
|
||||||
|
Main.rand.NextFloat(-2f, 2f), Main.rand.NextFloat(-2f, 2f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float SpawnChance(NPCSpawnInfo spawnInfo)
|
||||||
|
{
|
||||||
|
// Only spawn during Avalian Invasion event
|
||||||
|
return 0f; // Controlled by invasion system
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
using Terraria;
|
||||||
|
using Terraria.ID;
|
||||||
|
using Terraria.ModLoader;
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
|
||||||
|
namespace ForgottenWawiTech.Content.NPCs.Enemies.AvalianInvasion
|
||||||
|
{
|
||||||
|
public class AvalianTechnician : ModNPC
|
||||||
|
{
|
||||||
|
private int laserTimer = 0;
|
||||||
|
|
||||||
|
public override void SetStaticDefaults()
|
||||||
|
{
|
||||||
|
Main.npcFrameCount[Type] = 6;
|
||||||
|
NPCID.Sets.NPCBestiaryDrawModifiers value = new NPCID.Sets.NPCBestiaryDrawModifiers(0)
|
||||||
|
{
|
||||||
|
Velocity = 1f
|
||||||
|
};
|
||||||
|
NPCID.Sets.NPCBestiaryDrawOffset.Add(Type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetDefaults()
|
||||||
|
{
|
||||||
|
NPC.width = 36;
|
||||||
|
NPC.height = 44;
|
||||||
|
NPC.damage = 40;
|
||||||
|
NPC.defense = 12;
|
||||||
|
NPC.lifeMax = 300;
|
||||||
|
NPC.HitSound = SoundID.NPCHit1;
|
||||||
|
NPC.DeathSound = SoundID.NPCDeath6;
|
||||||
|
NPC.value = Item.buyPrice(0, 0, 15, 0);
|
||||||
|
NPC.knockBackResist = 0.4f;
|
||||||
|
NPC.aiStyle = NPCAIStyleID.Bat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void AI()
|
||||||
|
{
|
||||||
|
Player target = Main.player[NPC.target];
|
||||||
|
if (target.active && !target.dead)
|
||||||
|
{
|
||||||
|
laserTimer++;
|
||||||
|
|
||||||
|
// Circle around player
|
||||||
|
float distanceToPlayer = Vector2.Distance(NPC.Center, target.Center);
|
||||||
|
if (distanceToPlayer > 400f)
|
||||||
|
{
|
||||||
|
// Move closer
|
||||||
|
Vector2 toPlayer = Vector2.Normalize(target.Center - NPC.Center);
|
||||||
|
NPC.velocity = toPlayer * 4f;
|
||||||
|
}
|
||||||
|
else if (distanceToPlayer < 350f)
|
||||||
|
{
|
||||||
|
// Orbit
|
||||||
|
Vector2 toPlayer = target.Center - NPC.Center;
|
||||||
|
Vector2 perpendicular = new Vector2(-toPlayer.Y, toPlayer.X);
|
||||||
|
perpendicular.Normalize();
|
||||||
|
NPC.velocity = perpendicular * 3f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire laser every 60 frames (1 second)
|
||||||
|
if (laserTimer >= 60)
|
||||||
|
{
|
||||||
|
laserTimer = 0;
|
||||||
|
|
||||||
|
if (Main.netMode != NetmodeID.MultiplayerClient)
|
||||||
|
{
|
||||||
|
Vector2 shootDirection = Vector2.Normalize(target.Center - NPC.Center);
|
||||||
|
Projectile.NewProjectile(NPC.GetSource_FromAI(), NPC.Center,
|
||||||
|
shootDirection * 12f, ProjectileID.DeathLaser,
|
||||||
|
NPC.damage / 2, 1f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HitEffect(NPC.HitInfo hit)
|
||||||
|
{
|
||||||
|
if (NPC.life <= 0)
|
||||||
|
{
|
||||||
|
// Tech parts and feathers
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
Dust.NewDust(NPC.position, NPC.width, NPC.height, DustID.Feather);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
Dust.NewDust(NPC.position, NPC.width, NPC.height, DustID.Electric);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float SpawnChance(NPCSpawnInfo spawnInfo)
|
||||||
|
{
|
||||||
|
return 0f; // Controlled by invasion system
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using Terraria.ModLoader;
|
||||||
|
using Terraria;
|
||||||
|
|
||||||
|
namespace ForgottenWawiTech.Content.Players
|
||||||
|
{
|
||||||
|
// Tracks per-tick emission cooldown for BeamAttackWand while swinging.
|
||||||
|
public class BeamAttackWandPlayer : ModPlayer
|
||||||
|
{
|
||||||
|
public int BeamEmissionTimer = 0;
|
||||||
|
public float LastBeamRot; // smoothed rotation used to cluster beams
|
||||||
|
|
||||||
|
public override void ResetEffects()
|
||||||
|
{
|
||||||
|
// If not holding the wand, allow immediate beam spawn next time it's used and reset rotation smoothing.
|
||||||
|
if (Player.HeldItem.type != ModContent.ItemType<Items.Weapons.Magic.BeamAttackWand>())
|
||||||
|
{
|
||||||
|
BeamEmissionTimer = 0;
|
||||||
|
LastBeamRot = 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
|
using Terraria;
|
||||||
|
using Terraria.ID;
|
||||||
|
using Terraria.ModLoader;
|
||||||
|
using Terraria.GameContent;
|
||||||
|
|
||||||
|
namespace ForgottenWawiTech.Content.Projectiles
|
||||||
|
{
|
||||||
|
// Short-lived beam projectile emitted rapidly by BeamAttackWand.
|
||||||
|
// Simple straight projectile with minor dust and fade-out.
|
||||||
|
public class BeamAttackWandBeam : ModProjectile
|
||||||
|
{
|
||||||
|
private const int FrameCount = 3;
|
||||||
|
public override void SetStaticDefaults()
|
||||||
|
{
|
||||||
|
Main.projFrames[Type] = FrameCount; // 3 vertical frames
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetDefaults()
|
||||||
|
{
|
||||||
|
Projectile.width = 28; // frame width
|
||||||
|
Projectile.height = 28; // frame height
|
||||||
|
Projectile.friendly = true;
|
||||||
|
Projectile.penetrate = 1; // single hit per beam
|
||||||
|
Projectile.timeLeft = 15; // very short duration
|
||||||
|
Projectile.DamageType = DamageClass.Melee;
|
||||||
|
Projectile.tileCollide = true;
|
||||||
|
Projectile.light = 0.4f;
|
||||||
|
Projectile.alpha = 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void AI()
|
||||||
|
{
|
||||||
|
// Frame animation: advance every 4 ticks for subtle flicker
|
||||||
|
Projectile.frameCounter++;
|
||||||
|
if (Projectile.frameCounter >= 4)
|
||||||
|
{
|
||||||
|
Projectile.frameCounter = 0;
|
||||||
|
Projectile.frame = (Projectile.frame + 1) % FrameCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slight acceleration forward for a snappy feel
|
||||||
|
Projectile.velocity *= 1.02f;
|
||||||
|
|
||||||
|
// Simple fade: become more transparent near end
|
||||||
|
if (Projectile.timeLeft < 6)
|
||||||
|
Projectile.alpha += 25;
|
||||||
|
|
||||||
|
// Spawn light dust
|
||||||
|
if (Main.rand.NextBool(3))
|
||||||
|
{
|
||||||
|
int d = Dust.NewDust(Projectile.position, Projectile.width, Projectile.height, DustID.PurpleTorch,
|
||||||
|
Projectile.velocity.X * 0.2f, Projectile.velocity.Y * 0.2f, 150, default, 0.9f);
|
||||||
|
Main.dust[d].noGravity = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnTileCollide(Vector2 oldVelocity)
|
||||||
|
{
|
||||||
|
// So beams feel crisp: slight bounce spark then die
|
||||||
|
Projectile.timeLeft = 5;
|
||||||
|
Projectile.velocity *= 0.4f;
|
||||||
|
return false; // don't kill immediately; fade
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool PreDraw(ref Color lightColor)
|
||||||
|
{
|
||||||
|
Texture2D tex = TextureAssets.Projectile[Type].Value;
|
||||||
|
int frameHeight = tex.Height / FrameCount;
|
||||||
|
Rectangle frameRect = new(0, frameHeight * Projectile.frame, tex.Width, frameHeight);
|
||||||
|
Vector2 origin = new(tex.Width / 2f, frameHeight / 2f);
|
||||||
|
float baseScale = 1f;
|
||||||
|
float stretchX = 0.6f + Projectile.velocity.Length() / 30f; // elongate horizontally
|
||||||
|
Color drawColor = Color.White * (1f - Projectile.alpha / 255f);
|
||||||
|
|
||||||
|
// Slight additive trail effect: draw faint previous frame positions
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
Vector2 trailPos = Projectile.Center - Projectile.velocity * i * 0.8f;
|
||||||
|
float trailAlpha = (0.4f - i * 0.12f) * (1f - Projectile.alpha / 255f);
|
||||||
|
Main.EntitySpriteDraw(tex, trailPos - Main.screenPosition, frameRect,
|
||||||
|
drawColor * trailAlpha, Projectile.rotation, origin,
|
||||||
|
new Vector2(stretchX, baseScale), SpriteEffects.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main frame (bright)
|
||||||
|
Main.EntitySpriteDraw(tex, Projectile.Center - Main.screenPosition, frameRect,
|
||||||
|
drawColor, Projectile.rotation, origin,
|
||||||
|
new Vector2(stretchX, baseScale), SpriteEffects.None);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 9.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
@@ -234,6 +234,15 @@ Items: {
|
|||||||
Tooltip: Surprisingly large for a trophy. Especially from an otter.
|
Tooltip: Surprisingly large for a trophy. Especially from an otter.
|
||||||
DisplayName: Neptune Trophy Item
|
DisplayName: Neptune Trophy Item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BeamAttackWand: {
|
||||||
|
Tooltip:
|
||||||
|
'''
|
||||||
|
A wand that fires several balls of energy in quick succession.
|
||||||
|
You'll get em next time! Beam attack!
|
||||||
|
'''
|
||||||
|
DisplayName: Beam Staff
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NPCs: {
|
NPCs: {
|
||||||
@@ -327,6 +336,7 @@ NPCs: {
|
|||||||
Projectiles: {
|
Projectiles: {
|
||||||
ShatteredPrismBeam.DisplayName: Shattered Prism Beam
|
ShatteredPrismBeam.DisplayName: Shattered Prism Beam
|
||||||
ShatteredPrismHoldout.DisplayName: Shattered Prism Holdout
|
ShatteredPrismHoldout.DisplayName: Shattered Prism Holdout
|
||||||
|
BeamAttackWandBeam.DisplayName: Beam Attack Wand Beam
|
||||||
}
|
}
|
||||||
|
|
||||||
Prefixes: {
|
Prefixes: {
|
||||||
|
|||||||
Reference in New Issue
Block a user