Finish set bonuses, add more options to the Avali Trader

This commit is contained in:
2025-11-20 16:22:50 +00:00
parent 9162878883
commit b30fcc9563
44 changed files with 1744 additions and 81 deletions
@@ -0,0 +1,23 @@
using Terraria;
using Terraria.DataStructures;
using Terraria.ModLoader;
using ForgottenWawiTech.Content.Projectiles.Protogen;
namespace ForgottenWawiTech.Content.GlobalProjectiles
{
public class ProtogenSentinelGlobalProjectile : GlobalProjectile
{
public bool DisableSentinelSplit { get; set; }
public override bool InstancePerEntity => true;
public override void OnSpawn(Projectile projectile, IEntitySource source)
{
if (projectile.type == ModContent.ProjectileType<ProtogenSentinelFragment>() ||
projectile.type == ModContent.ProjectileType<ProtogenSentinelCoreBeam>())
{
DisableSentinelSplit = true;
}
}
}
}
@@ -5,6 +5,7 @@ using ForgottenWawiTech.Content.Items.Materials;
using ForgottenWawiTech.Content.Rarities;
using ForgottenWawiTech.Content.Tiles.CraftingStations;
using ForgottenWawiTech.Content.Items.Armor.AvaliSkirmisherSet;
using ForgottenWawiTech.Content.Players;
namespace ForgottenWawiTech.Content.Items.Armor.AvaliVanguardSet
{
@@ -44,7 +45,7 @@ namespace ForgottenWawiTech.Content.Items.Armor.AvaliVanguardSet
}
player.endurance += 0.10f;
// TODO: Implement hunting drone projectile system
player.GetModPlayer<AvaliVanguardPlayer>().EnableHuntingDrones();
}
public override void AddRecipes()
@@ -5,6 +5,7 @@ using ForgottenWawiTech.Content.Items.Materials;
using ForgottenWawiTech.Content.Rarities;
using ForgottenWawiTech.Content.Tiles.CraftingStations;
using ForgottenWawiTech.Content.Items.Armor.AvaliSkirmisherSet;
using ForgottenWawiTech.Content.Players;
namespace ForgottenWawiTech.Content.Items.Armor.AvaliVanguardSet
{
@@ -45,7 +46,7 @@ namespace ForgottenWawiTech.Content.Items.Armor.AvaliVanguardSet
}
player.endurance += 0.10f;
// TODO: Implement hunting drone projectile system
player.GetModPlayer<AvaliVanguardPlayer>().EnableHuntingDrones();
}
public override void AddRecipes()
@@ -5,6 +5,7 @@ using ForgottenWawiTech.Content.Items.Materials;
using ForgottenWawiTech.Content.Rarities;
using ForgottenWawiTech.Content.Tiles.CraftingStations;
using ForgottenWawiTech.Content.Items.Armor.AvaliSkirmisherSet;
using ForgottenWawiTech.Content.Players;
namespace ForgottenWawiTech.Content.Items.Armor.AvaliVanguardSet
{
@@ -45,7 +46,7 @@ namespace ForgottenWawiTech.Content.Items.Armor.AvaliVanguardSet
}
player.endurance += 0.10f;
// TODO: Implement hunting drone projectile system
player.GetModPlayer<AvaliVanguardPlayer>().EnableHuntingDrones();
}
public override void AddRecipes()
@@ -5,6 +5,7 @@ using ForgottenWawiTech.Content.Items.Materials;
using ForgottenWawiTech.Content.Rarities;
using ForgottenWawiTech.Content.Tiles.CraftingStations;
using ForgottenWawiTech.Content.Items.Armor.AvaliSkirmisherSet;
using ForgottenWawiTech.Content.Players;
namespace ForgottenWawiTech.Content.Items.Armor.AvaliVanguardSet
{
@@ -44,7 +45,7 @@ namespace ForgottenWawiTech.Content.Items.Armor.AvaliVanguardSet
}
player.endurance += 0.10f;
// TODO: Implement hunting drone projectile system
player.GetModPlayer<AvaliVanguardPlayer>().EnableHuntingDrones();
}
public override void AddRecipes()
@@ -4,6 +4,7 @@ using Terraria.ModLoader;
using ForgottenWawiTech.Content.Items.Materials;
using ForgottenWawiTech.Content.Rarities;
using ForgottenWawiTech.Content.Items.Armor.ProtogenInitiateSet;
using ForgottenWawiTech.Content.Players;
namespace ForgottenWawiTech.Content.Items.Armor.ProtogenOperativeSet
{
@@ -37,7 +38,7 @@ namespace ForgottenWawiTech.Content.Items.Armor.ProtogenOperativeSet
player.setBonus = "Taking damage releases seeking nanites\nNanites heal you for 50% of damage dealt\n+2 max minions and +18% summon damage";
player.maxMinions += 2;
player.GetDamage(DamageClass.Summon) += 0.18f;
// TODO: Implement nanite swarm healing system
player.GetModPlayer<ProtogenOperativePlayer>().EnableNaniteSwarm();
}
public override void AddRecipes()
@@ -4,6 +4,7 @@ using Terraria.ModLoader;
using ForgottenWawiTech.Content.Items.Materials;
using ForgottenWawiTech.Content.Rarities;
using ForgottenWawiTech.Content.Items.Armor.ProtogenInitiateSet;
using ForgottenWawiTech.Content.Players;
namespace ForgottenWawiTech.Content.Items.Armor.ProtogenOperativeSet
{
@@ -38,7 +39,7 @@ namespace ForgottenWawiTech.Content.Items.Armor.ProtogenOperativeSet
player.setBonus = "Taking damage releases seeking nanites\nNanites heal you for 50% of damage dealt\n+12% melee damage and speed";
player.GetDamage(DamageClass.Melee) += 0.12f;
player.GetAttackSpeed(DamageClass.Melee) += 0.12f;
// TODO: Implement nanite swarm healing system
player.GetModPlayer<ProtogenOperativePlayer>().EnableNaniteSwarm();
}
public override void AddRecipes()
@@ -4,6 +4,7 @@ using Terraria.ModLoader;
using ForgottenWawiTech.Content.Items.Materials;
using ForgottenWawiTech.Content.Rarities;
using ForgottenWawiTech.Content.Items.Armor.ProtogenInitiateSet;
using ForgottenWawiTech.Content.Players;
namespace ForgottenWawiTech.Content.Items.Armor.ProtogenOperativeSet
{
@@ -37,7 +38,7 @@ namespace ForgottenWawiTech.Content.Items.Armor.ProtogenOperativeSet
player.setBonus = "Taking damage releases seeking nanites\nNanites heal you for 50% of damage dealt\n+18% magic damage and -18% mana cost";
player.GetDamage(DamageClass.Magic) += 0.18f;
player.manaCost -= 0.18f;
// TODO: Implement nanite swarm healing system
player.GetModPlayer<ProtogenOperativePlayer>().EnableNaniteSwarm();
}
public override void AddRecipes()
@@ -4,6 +4,7 @@ using Terraria.ModLoader;
using ForgottenWawiTech.Content.Items.Materials;
using ForgottenWawiTech.Content.Rarities;
using ForgottenWawiTech.Content.Items.Armor.ProtogenInitiateSet;
using ForgottenWawiTech.Content.Players;
namespace ForgottenWawiTech.Content.Items.Armor.ProtogenOperativeSet
{
@@ -37,7 +38,7 @@ namespace ForgottenWawiTech.Content.Items.Armor.ProtogenOperativeSet
player.setBonus = "Taking damage releases seeking nanites\nNanites heal you for 50% of damage dealt\n+15% ranged damage and critical strike chance";
player.GetDamage(DamageClass.Ranged) += 0.15f;
player.GetCritChance(DamageClass.Ranged) += 15;
// TODO: Implement nanite swarm healing system
player.GetModPlayer<ProtogenOperativePlayer>().EnableNaniteSwarm();
}
public override void AddRecipes()
@@ -5,6 +5,7 @@ using ForgottenWawiTech.Content.Items.Materials;
using ForgottenWawiTech.Content.Rarities;
using ForgottenWawiTech.Content.Tiles.CraftingStations;
using ForgottenWawiTech.Content.Items.Armor.ProtogenOperativeSet;
using ForgottenWawiTech.Content.Players;
namespace ForgottenWawiTech.Content.Items.Armor.ProtogenSentinelSet
{
@@ -47,7 +48,7 @@ namespace ForgottenWawiTech.Content.Items.Armor.ProtogenSentinelSet
player.buffImmune[BuffID.Weak] = true;
player.buffImmune[BuffID.Silenced] = true;
player.buffImmune[BuffID.Cursed] = true;
// TODO: Implement AI core projectile and splitting projectile system
player.GetModPlayer<ProtogenSentinelPlayer>().EnableSentinelCore();
}
public override void AddRecipes()
@@ -5,6 +5,7 @@ using ForgottenWawiTech.Content.Items.Materials;
using ForgottenWawiTech.Content.Rarities;
using ForgottenWawiTech.Content.Tiles.CraftingStations;
using ForgottenWawiTech.Content.Items.Armor.ProtogenOperativeSet;
using ForgottenWawiTech.Content.Players;
namespace ForgottenWawiTech.Content.Items.Armor.ProtogenSentinelSet
{
@@ -48,7 +49,7 @@ namespace ForgottenWawiTech.Content.Items.Armor.ProtogenSentinelSet
player.buffImmune[BuffID.Weak] = true;
player.buffImmune[BuffID.Silenced] = true;
player.buffImmune[BuffID.Cursed] = true;
// TODO: Implement AI core projectile and splitting projectile system
player.GetModPlayer<ProtogenSentinelPlayer>().EnableSentinelCore();
}
public override void AddRecipes()
@@ -5,6 +5,7 @@ using ForgottenWawiTech.Content.Items.Materials;
using ForgottenWawiTech.Content.Rarities;
using ForgottenWawiTech.Content.Tiles.CraftingStations;
using ForgottenWawiTech.Content.Items.Armor.ProtogenOperativeSet;
using ForgottenWawiTech.Content.Players;
namespace ForgottenWawiTech.Content.Items.Armor.ProtogenSentinelSet
{
@@ -47,7 +48,7 @@ namespace ForgottenWawiTech.Content.Items.Armor.ProtogenSentinelSet
player.buffImmune[BuffID.Weak] = true;
player.buffImmune[BuffID.Silenced] = true;
player.buffImmune[BuffID.Cursed] = true;
// TODO: Implement AI core projectile and splitting projectile system
player.GetModPlayer<ProtogenSentinelPlayer>().EnableSentinelCore();
}
public override void AddRecipes()
@@ -5,6 +5,7 @@ using ForgottenWawiTech.Content.Items.Materials;
using ForgottenWawiTech.Content.Rarities;
using ForgottenWawiTech.Content.Tiles.CraftingStations;
using ForgottenWawiTech.Content.Items.Armor.ProtogenOperativeSet;
using ForgottenWawiTech.Content.Players;
namespace ForgottenWawiTech.Content.Items.Armor.ProtogenSentinelSet
{
@@ -47,7 +48,7 @@ namespace ForgottenWawiTech.Content.Items.Armor.ProtogenSentinelSet
player.buffImmune[BuffID.Weak] = true;
player.buffImmune[BuffID.Silenced] = true;
player.buffImmune[BuffID.Cursed] = true;
// TODO: Implement AI core projectile and splitting projectile system
player.GetModPlayer<ProtogenSentinelPlayer>().EnableSentinelCore();
}
public override void AddRecipes()
@@ -4,6 +4,7 @@ using Terraria.ModLoader;
using ForgottenWawiTech.Content.Items.Materials;
using ForgottenWawiTech.Content.Rarities;
using ForgottenWawiTech.Content.Tiles.CraftingStations;
using ForgottenWawiTech.Content.Players;
namespace ForgottenWawiTech.Content.Items.Armor.SingularitySet
{
@@ -40,9 +41,7 @@ namespace ForgottenWawiTech.Content.Items.Armor.SingularitySet
player.GetCritChance(DamageClass.Generic) += 20;
player.endurance += 0.15f;
player.waterWalk = true;
// TODO: Implement singularity orb minion projectile
// For now, grant increased stats as placeholder
player.GetModPlayer<SingularityPlayer>().EnableSingularityOrb();
}
public override void AddRecipes()
@@ -2,6 +2,7 @@ using System;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;
using ForgottenWawiTech.Content.Players;
namespace ForgottenWawiTech.Content.Items.Armor.TranscendentSet
{
@@ -87,7 +88,9 @@ namespace ForgottenWawiTech.Content.Items.Armor.TranscendentSet
player.buffImmune[buffId] = true;
}
// TODO: Implement Ascendant Swarm minions, sentinel beam volley, and nanite shard projectiles.
player.GetModPlayer<TranscendentPlayer>().EnableTranscendentSet();
player.GetModPlayer<ProtogenSentinelPlayer>().EnableSentinelCore();
player.GetModPlayer<SingularityPlayer>().EnableSingularityOrb();
}
}
}
+160 -34
View File
@@ -1,14 +1,69 @@
using System.Collections.Generic;
using System.Reflection;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;
using Terraria.GameContent.Personalities;
using Terraria.Localization;
using ForgottenWawiTech;
namespace ForgottenWawiTech.Content.NPCs.TownNPCs
{
public class AvalianTrader : ModNPC
{
private static readonly Condition HasDefeatedAnyMechBoss = new("Mods.ForgottenWawiTech.Conditions.HasDefeatedAnyMechBoss", () =>
NPC.downedMechBoss1 || NPC.downedMechBoss2 || NPC.downedMechBoss3);
private static readonly Condition HasDefeatedLexy = new("Mods.ForgottenWawiTech.Conditions.HasDefeatedLexy", () =>
Main.hardMode && WorldDefeatedBosses.Lexy);
private static readonly Condition HasDefeatedNiko = new("Mods.ForgottenWawiTech.Conditions.HasDefeatedNiko", () =>
Main.hardMode && WorldDefeatedBosses.Niko);
private static readonly Condition HasDefeatedNeptune = new("Mods.ForgottenWawiTech.Conditions.HasDefeatedNeptune", () =>
Main.hardMode && WorldDefeatedBosses.Neptune);
private static readonly Condition HasDefeatedLyyka = new("Mods.ForgottenWawiTech.Conditions.HasDefeatedLyyka", () =>
Main.hardMode && WorldDefeatedBosses.Lyyka);
private static readonly Condition HasDefeatedCorruptedSwarm = new("Mods.ForgottenWawiTech.Conditions.HasDefeatedCorruptedSwarm", () =>
Main.hardMode && WorldDefeatedBosses.Niko); // TODO: Replace with Corrupted Swarm flag once implemented.
private static readonly Condition HasDefeatedExoMechs = new("Mods.ForgottenWawiTech.Conditions.HasDefeatedExoMechs", CheckExoMechsDowned);
private static readonly Condition HasDefeatedMoonLord = new("Mods.ForgottenWawiTech.Conditions.HasDefeatedMoonLord", () =>
NPC.downedMoonlord);
private static bool CheckExoMechsDowned()
{
if (!ModLoader.TryGetMod("CalamityMod", out Mod calamityMod))
{
return false;
}
var calamityWorldType = calamityMod.Code?.GetType("CalamityMod.World.CalamityWorld");
if (calamityWorldType == null)
{
return false;
}
const BindingFlags flags = BindingFlags.Public | BindingFlags.Static;
var field = calamityWorldType.GetField("downedExoMechs", flags);
if (field?.GetValue(null) is bool fieldValue)
{
return fieldValue;
}
var property = calamityWorldType.GetProperty("downedExoMechs", flags);
if (property?.GetValue(null) is bool propertyValue)
{
return propertyValue;
}
return false;
}
public override void SetStaticDefaults()
{
Main.npcFrameCount[NPC.type] = 25; // The amount of frames the NPC has
@@ -92,48 +147,119 @@ namespace ForgottenWawiTech.Content.NPCs.TownNPCs
public override void AddShops()
{
Item rainbowBrick = new();
rainbowBrick.SetDefaults(ItemID.RainbowBrick);
rainbowBrick.value = Item.buyPrice(silver: 90);
new NPCShop(Type)
.Add<Items.Materials.RGBass>(Condition.DownedMoonLord)
.Add<Items.Materials.VisorShard>(Condition.Hardmode)
.Add(ItemID.RainbowBrick, Condition.Hardmode) // TODO: Force Rainbow Brick to not have No Value.
.Add<Items.Materials.VisorShard>(HasDefeatedLexy)
.Add<Items.Materials.FallenAngelFeather>(HasDefeatedNiko)
.Add<Items.Materials.HalfEatenFish>(HasDefeatedNeptune)
.Add<Items.Materials.LyykaBossDrop>(HasDefeatedLyyka)
.Add<Items.Materials.UnstableMatter>(HasDefeatedCorruptedSwarm)
.Add(rainbowBrick, Condition.Hardmode)
.Add<Items.Materials.AvaliFeather>()
.Add<Items.Materials.RGBAlloy>(HasDefeatedMoonLord)
.Add<Items.Materials.EnergyCore>(HasDefeatedAnyMechBoss)
.Add<Items.Materials.ContainmentUnit>(HasDefeatedExoMechs)
.Add<Items.Materials.ControlledSingularity>(HasDefeatedExoMechs)
.Add<Items.Materials.MiniSingularity>(HasDefeatedExoMechs)
.Add<Items.Materials.SingularityAlloy>(HasDefeatedExoMechs)
.Add<Items.Materials.RGBass>(HasDefeatedMoonLord)
.Register();
}
public override string GetChat()
{
switch (Main.rand.Next(15))
List<string> lines = new()
{
case 0:
return "*avalian chirping*";
case 1:
return "Welcome! I've traveled far from Avalon to bring you the finest technology.";
case 2:
return "These feathers? They're not just for show - they're quite valuable!";
case 3:
return "RGB technology is all the rage back home. Everything needs more colors!";
case 4:
return "The visor shards I sell are fragments from our most advanced equipment.";
case 5:
return "My pack may be small, but I've traversed dimensions to get here.";
case 6:
return "*chirps excitedly* Oh, you're interested in our wares?";
case 7:
return "Avalon sends its regards! And its finest materials.";
case 8:
return "Don't underestimate the power of a well-crafted alloy.";
case 9:
return "The journey here was long, but your world has such fascinating resources!";
case 10:
return "*preens feathers* Presentation is important in any trade.";
case 11:
return "RGBass - it's not just a material, it's a lifestyle.";
case 12:
return "I wonder what the other traders back on Avalon would think of this place...";
case 13:
return "My species may be small, but we're excellent engineers!";
default:
return "Need something? My shop is always open!";
"*avalian chirping*",
"Welcome! I've traveled far from Avalon to bring you the finest technology.",
"My pack may be small, but I've traversed dimensions to get here.",
"*chirps excitedly* Oh, you're interested in our wares?",
"RGBass - it's not just a material, it's a lifestyle.",
"Those rainbow bricks? Ninety silver each. I have transport costs, you know.",
"The journey here was long, but your world has such fascinating resources!",
"Need something? My shop is always open!",
"Avalian markets never sleep; neither does my inventory.",
"If you see a hover sled parked outside, please don't touch the controls.",
"Try not to lick the stock. Some of it bites back.",
"The atmosphere here is thick, but profits make it worth the acclimation.",
"I keep a thermal blanket under the clothes —these nights get cold even for an Avali.",
"Do you have any idea how much customs paperwork interstellar trade requires?",
"If you hear humming, that's just the containment field. Probably."
};
if (HasDefeatedLexy.IsMet())
{
lines.Add("Defeated Lexy, have you? His visor shards always sell out fast.");
lines.Add("Lexy's visor tech makes for excellent souvenirs. Stock up while I have them.");
}
else if (Main.hardMode)
{
lines.Add("Still hunting Lexy? Bring me his visor and I'll unlock some premium stock.");
}
if (HasDefeatedNiko.IsMet())
{
lines.Add("Astramora's Fallen Angel Feathers? Delicate, pricey, worth every coin.");
lines.Add("I've reinforced the shipping crates just for Astramora's volatile feathers.");
}
else if (Main.hardMode)
{
lines.Add("I've heard whispers of a rat angel around these parts of the galaxy...");
}
if (HasDefeatedNeptune.IsMet())
{
lines.Add("Neptune leaves quite the mess. Half-eaten fish, anyone?");
lines.Add("Neptune's half-eaten catches sell faster than I'd like—apparently otter bites add flavor.");
}
else if (Main.hardMode)
{
lines.Add("If you're braving the tides, bring Neptune down and I'll import his catches.");
}
if (HasDefeatedLyyka.IsMet())
{
lines.Add("Avalon sends its regards! Lyyka's gear is highly sought after.");
lines.Add("Lyyka's encore was explosive. Her gear? Even more so.");
}
else if (Main.hardMode)
{
lines.Add("Bring down Lyyka and I'll uncrate her signature alloys.");
}
if (HasDefeatedCorruptedSwarm.IsMet())
{
lines.Add("Unstable Matter? Just delicate enough for my clientele.");
lines.Add("Careful with the Unstable Matter; Avalon still has a watchful eye on it.");
}
else if (Main.hardMode)
{
lines.Add("Rumor says there is a swarm of corrupted tech out there. If you defeat it, I might have something worth the risk.");
}
if (HasDefeatedAnyMechBoss.IsMet())
{
lines.Add("Mechanical husks make great recycling. Fancy some RGB alloys?");
}
if (HasDefeatedMoonLord.IsMet())
{
lines.Add("Moon Lord down? Excellent. His trinkets pair nicely with Avalian engineering.");
}
if (HasDefeatedExoMechs.IsMet())
{
lines.Add("The Exo Mechs are no more? Splendid! I've got singularities ready for purchase.");
lines.Add("You walked away from the Exo Mechs? Take these singularities before they destabilize.");
}
else if (ModLoader.TryGetMod("CalamityMod", out _))
{
lines.Add("Calamity's Exo Mechs still stalk your skies. Ground them and my singularity cache opens.");
}
return lines.Count > 0 ? lines[Main.rand.Next(lines.Count)] : "Need something? My shop is always open!";
}
public override void TownNPCAttackStrength(ref int damage, ref float knockback)
+63
View File
@@ -0,0 +1,63 @@
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ModLoader;
using ForgottenWawiTech.Content.Projectiles.Avali;
namespace ForgottenWawiTech.Content.Players
{
public class AvaliVanguardPlayer : ModPlayer
{
private const int DroneCount = 3;
private const float SpawnRadius = 72f;
public bool HuntingDronesActive { get; private set; }
public static int GetDroneBaseDamage(Player player)
{
const int droneBaseDamage = 240;
return (int)player.GetTotalDamage(DamageClass.Summon).ApplyTo(droneBaseDamage);
}
public override void ResetEffects()
{
HuntingDronesActive = false;
}
public void EnableHuntingDrones()
{
HuntingDronesActive = true;
}
public override void PostUpdate()
{
if (!HuntingDronesActive || Player.whoAmI != Main.myPlayer)
{
return;
}
int droneType = ModContent.ProjectileType<AvaliHuntingDrone>();
int owned = Player.ownedProjectileCounts[droneType];
if (owned >= DroneCount)
{
return;
}
var source = Player.GetSource_Misc("AvaliVanguardSet");
for (int i = owned; i < DroneCount; i++)
{
float rotationOffset = MathHelper.TwoPi * i / DroneCount;
Vector2 spawnPos = Player.Center + rotationOffset.ToRotationVector2() * SpawnRadius;
Projectile projectile = Projectile.NewProjectileDirect(
source,
spawnPos,
Vector2.Zero,
droneType,
0,
0f,
Player.whoAmI,
i,
DroneCount);
projectile.originalDamage = GetDroneBaseDamage(Player);
}
}
}
}
@@ -0,0 +1,71 @@
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ModLoader;
using ForgottenWawiTech.Content.Projectiles.Protogen;
namespace ForgottenWawiTech.Content.Players
{
public class ProtogenOperativePlayer : ModPlayer
{
private const int NaniteProjectileCount = 3;
private const int NaniteCooldownTicks = 90;
private const int NaniteBaseDamage = 90;
private const float NaniteSpawnRadius = 30f;
private int naniteCooldownTimer;
public bool NaniteSwarmActive { get; private set; }
public override void ResetEffects()
{
NaniteSwarmActive = false;
}
public override void UpdateDead()
{
NaniteSwarmActive = false;
naniteCooldownTimer = 0;
}
public override void PostUpdate()
{
if (naniteCooldownTimer > 0)
{
naniteCooldownTimer--;
}
}
public void EnableNaniteSwarm()
{
NaniteSwarmActive = true;
}
public override void OnHurt(Player.HurtInfo info)
{
if (!NaniteSwarmActive || info.Damage <= 0 || naniteCooldownTimer > 0 || Player.whoAmI != Main.myPlayer)
{
return;
}
naniteCooldownTimer = NaniteCooldownTicks;
var source = Player.GetSource_Misc("ProtogenOperativeNanites");
int damage = (int)Player.GetTotalDamage(DamageClass.Generic).ApplyTo(NaniteBaseDamage);
for (int i = 0; i < NaniteProjectileCount; i++)
{
float angle = MathHelper.TwoPi * i / NaniteProjectileCount;
Vector2 spawnOffset = angle.ToRotationVector2() * NaniteSpawnRadius;
Vector2 spawnVelocity = spawnOffset.SafeNormalize(Vector2.UnitY) * 6f + Player.velocity;
Projectile.NewProjectile(
source,
Player.Center + spawnOffset,
spawnVelocity,
ModContent.ProjectileType<ProtogenOperativeNanite>(),
damage,
2f,
Player.whoAmI);
}
}
}
}
+125
View File
@@ -0,0 +1,125 @@
using System;
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ModLoader;
using ForgottenWawiTech.Content.Projectiles.Protogen;
using ForgottenWawiTech.Content.GlobalProjectiles;
namespace ForgottenWawiTech.Content.Players
{
public class ProtogenSentinelPlayer : ModPlayer
{
private const int CoreProjectileCount = 1;
private const int CoreBaseDamage = 320;
private const float CoreSpawnOffset = -68f;
private const float FragmentSplitChance = 0.20f;
public bool SentinelCoreActive { get; private set; }
public bool SentinelProjectileSplitActive { get; private set; }
public override void ResetEffects()
{
SentinelCoreActive = false;
SentinelProjectileSplitActive = false;
}
public void EnableSentinelCore()
{
SentinelCoreActive = true;
SentinelProjectileSplitActive = true;
}
public override void PostUpdate()
{
if (!SentinelCoreActive)
{
return;
}
if (Player.whoAmI != Main.myPlayer)
{
return;
}
int coreType = ModContent.ProjectileType<ProtogenSentinelCore>();
if (Player.ownedProjectileCounts[coreType] >= CoreProjectileCount)
{
return;
}
var source = Player.GetSource_Misc("ProtogenSentinelSet");
Vector2 spawnPos = Player.Center + new Vector2(0f, CoreSpawnOffset);
Projectile.NewProjectile(
source,
spawnPos,
Vector2.Zero,
coreType,
0,
0f,
Player.whoAmI);
}
public static int GetCoreDamage(Player player)
{
return (int)player.GetTotalDamage(DamageClass.Summon).ApplyTo(CoreBaseDamage);
}
public override void OnHitNPCWithProj(Projectile proj, NPC target, NPC.HitInfo hit, int damageDone)
{
if (!SentinelProjectileSplitActive || Player.whoAmI != Main.myPlayer)
{
return;
}
if (!proj.friendly || proj.owner != Player.whoAmI)
{
return;
}
if (proj.GetGlobalProjectile<ProtogenSentinelGlobalProjectile>().DisableSentinelSplit)
{
return;
}
if (proj.type == ModContent.ProjectileType<ProtogenSentinelCoreBeam>() || proj.type == ModContent.ProjectileType<ProtogenSentinelFragment>())
{
return;
}
if (damageDone <= 0)
{
return;
}
if (Main.rand.NextFloat() > FragmentSplitChance)
{
return;
}
SpawnFragments(proj, damageDone);
}
private void SpawnFragments(Projectile sourceProj, int damageDone)
{
int fragmentType = ModContent.ProjectileType<ProtogenSentinelFragment>();
int fragmentCount = 2;
int fragmentDamage = Math.Max(1, damageDone / 2);
var source = Player.GetSource_Misc("ProtogenSentinelSplit");
for (int i = 0; i < fragmentCount; i++)
{
float rotation = MathHelper.ToRadians(Main.rand.NextFloat(-18f, 18f));
Vector2 launch = sourceProj.velocity.SafeNormalize(Vector2.UnitY).RotatedBy(rotation) * 14f;
Projectile fragment = Projectile.NewProjectileDirect(
source,
sourceProj.Center,
launch,
fragmentType,
fragmentDamage,
2f,
Player.whoAmI);
fragment.GetGlobalProjectile<ProtogenSentinelGlobalProjectile>().DisableSentinelSplit = true;
}
}
}
}
+56
View File
@@ -0,0 +1,56 @@
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ModLoader;
using ForgottenWawiTech.Content.Projectiles.Singularity;
namespace ForgottenWawiTech.Content.Players
{
public class SingularityPlayer : ModPlayer
{
private const int OrbProjectileLimit = 1;
private const int OrbBaseDamage = 420;
private const float OrbSpawnOffset = -80f;
public bool SingularityOrbActive { get; private set; }
public override void ResetEffects()
{
SingularityOrbActive = false;
}
public void EnableSingularityOrb()
{
SingularityOrbActive = true;
}
public override void PostUpdate()
{
if (!SingularityOrbActive || Player.whoAmI != Main.myPlayer)
{
return;
}
int orbType = ModContent.ProjectileType<SingularityOrb>();
if (Player.ownedProjectileCounts[orbType] >= OrbProjectileLimit)
{
return;
}
var source = Player.GetSource_Misc("SingularitySet");
Vector2 spawnPos = Player.Center + new Vector2(0f, OrbSpawnOffset);
Projectile.NewProjectile(
source,
spawnPos,
Vector2.Zero,
orbType,
0,
0f,
Player.whoAmI);
}
public static int GetOrbDamage(Player player)
{
return (int)player.GetTotalDamage(DamageClass.Generic).ApplyTo(OrbBaseDamage);
}
}
}
+185 -28
View File
@@ -1,5 +1,7 @@
using System;
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.DataStructures;
using Terraria.ID;
using Terraria.ModLoader;
using ForgottenWawiTech.Content.Projectiles.Transcendent;
@@ -8,60 +10,215 @@ namespace ForgottenWawiTech.Content.Players
{
public class TranscendentPlayer : ModPlayer
{
private const int AccessoryShardInterval = 90;
private const int SetShardInterval = 45;
private const int AccessoryShardBaseDamage = 220;
private const int SetShardBaseDamage = 320;
private const int AscendantDroneCount = 4;
private const int AscendantDroneBaseDamage = 360;
private const int NaniteShardBaseDamage = 260;
public bool SwarmModuleEquipped;
private Item swarmAccessorySourceItem;
private int swarmTimer;
private int naniteShardCooldown;
private bool transcendentSetActive;
public void HandleSwarmAccessory(Item sourceItem)
{
SwarmModuleEquipped = true;
swarmAccessorySourceItem = sourceItem;
}
if (Player.whoAmI != Main.myPlayer)
return;
swarmTimer++;
if (swarmTimer >= 90)
public void EnableTranscendentSet()
{
swarmTimer = 0;
int baseDamage = 220;
int damage = (int)Player.GetDamage(DamageClass.Summon).ApplyTo(baseDamage);
float knockBack = 3f;
Vector2 velocity = Main.rand.NextVector2Circular(6f, 6f);
if (velocity.LengthSquared() < 0.01f)
{
velocity = new Vector2(0f, -6f);
transcendentSetActive = true;
}
Projectile.NewProjectile(
Player.GetSource_Accessory(sourceItem),
Player.Center,
velocity,
ModContent.ProjectileType<TranscendentBladeShard>(),
damage,
knockBack,
Player.whoAmI);
}
}
public bool HasTranscendentSet => transcendentSetActive;
public override void ResetEffects()
{
SwarmModuleEquipped = false;
transcendentSetActive = false;
swarmAccessorySourceItem = null;
}
public override void PostUpdate()
{
if (SwarmModuleEquipped)
bool swarmActive = SwarmModuleEquipped || transcendentSetActive;
if (swarmActive && Player.whoAmI == Main.myPlayer)
{
if (Main.rand.NextBool(6))
int interval = transcendentSetActive ? SetShardInterval : AccessoryShardInterval;
swarmTimer++;
if (swarmTimer >= interval)
{
Dust dust = Dust.NewDustDirect(Player.position, Player.width, Player.height, DustID.RainbowTorch, Player.velocity.X * 0.2f, Player.velocity.Y * 0.2f, 150, default, 1.1f);
dust.noGravity = true;
swarmTimer = 0;
SpawnAscendantShards(transcendentSetActive);
}
}
else if (Player.whoAmI == Main.myPlayer && swarmTimer > 0)
{
swarmTimer = 0;
}
if (naniteShardCooldown > 0)
{
naniteShardCooldown--;
}
if (transcendentSetActive)
{
MaintainAscendantDrones();
}
if (swarmActive)
{
if (Main.rand.NextBool(transcendentSetActive ? 4 : 6))
{
Dust dust = Dust.NewDustDirect(Player.position, Player.width, Player.height, DustID.RainbowTorch, Player.velocity.X * 0.2f, Player.velocity.Y * 0.2f, 150, default, 1.1f);
dust.noGravity = true;
}
}
}
private void SpawnAscendantShards(bool fromSet)
{
int baseDamage = fromSet ? SetShardBaseDamage : AccessoryShardBaseDamage;
int damage = (int)Player.GetDamage(DamageClass.Summon).ApplyTo(baseDamage);
float knockBack = fromSet ? 4f : 3f;
float speed = fromSet ? 8f : 6f;
int shardCount = fromSet ? 2 : 1;
if (!fromSet && swarmAccessorySourceItem == null)
{
swarmAccessorySourceItem = Player.HeldItem;
}
IEntitySource source = fromSet
? Player.GetSource_Misc("TranscendentAscendantSwarm")
: Player.GetSource_Accessory(swarmAccessorySourceItem ?? Player.HeldItem);
for (int i = 0; i < shardCount; i++)
{
Vector2 velocity = Main.rand.NextVector2Circular(speed, speed);
if (velocity.LengthSquared() < 0.01f)
{
velocity = new Vector2(0f, -speed);
}
Projectile shard = Projectile.NewProjectileDirect(
source,
Player.Center,
velocity,
ModContent.ProjectileType<TranscendentBladeShard>(),
damage,
knockBack,
Player.whoAmI);
shard.originalDamage = damage;
}
}
private void MaintainAscendantDrones()
{
if (Player.whoAmI != Main.myPlayer)
{
return;
}
int droneType = ModContent.ProjectileType<TranscendentAscendantDrone>();
int owned = Player.ownedProjectileCounts[droneType];
if (owned >= AscendantDroneCount)
{
return;
}
var source = Player.GetSource_Misc("TranscendentAscendantDrones");
for (int i = owned; i < AscendantDroneCount; i++)
{
Projectile.NewProjectile(
source,
Player.Center,
Vector2.Zero,
droneType,
0,
0f,
Player.whoAmI,
i,
AscendantDroneCount);
}
}
public override void OnHitNPC(NPC target, NPC.HitInfo hit, int damageDone)
{
TrySpawnNaniteShards(target.Center, damageDone);
}
public override void OnHitNPCWithProj(Projectile proj, NPC target, NPC.HitInfo hit, int damageDone)
{
if (proj.owner != Player.whoAmI || !proj.friendly)
{
return;
}
if (proj.type == ModContent.ProjectileType<TranscendentNaniteShard>() ||
proj.type == ModContent.ProjectileType<TranscendentAscendantBolt>() ||
proj.type == ModContent.ProjectileType<TranscendentBladeShard>())
{
return;
}
TrySpawnNaniteShards(target.Center, damageDone);
}
private void TrySpawnNaniteShards(Vector2 position, int damageDone)
{
if (!transcendentSetActive || Player.whoAmI != Main.myPlayer)
{
return;
}
if (damageDone <= 0 || naniteShardCooldown > 0)
{
return;
}
naniteShardCooldown = 8;
int baseDamage = GetNaniteShardDamage(Player);
int computed = Math.Max(baseDamage, damageDone / 2);
float speed = 13f;
var source = Player.GetSource_Misc("TranscendentNaniteShards");
for (int i = 0; i < 2; i++)
{
Vector2 velocity = Main.rand.NextVector2CircularEdge(1f, 1f) * speed;
if (velocity.LengthSquared() < 0.01f)
{
velocity = new Vector2(0f, -speed);
}
Projectile shard = Projectile.NewProjectileDirect(
source,
position,
velocity,
ModContent.ProjectileType<TranscendentNaniteShard>(),
computed,
2f,
Player.whoAmI);
shard.originalDamage = computed;
}
}
public static int GetAscendantDroneDamage(Player player)
{
return (int)player.GetTotalDamage(DamageClass.Summon).ApplyTo(AscendantDroneBaseDamage);
}
public static int GetNaniteShardDamage(Player player)
{
return (int)player.GetTotalDamage(DamageClass.Generic).ApplyTo(NaniteShardBaseDamage);
}
}
}
@@ -0,0 +1,126 @@
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.Audio;
using Terraria.ID;
using Terraria.ModLoader;
using ForgottenWawiTech.Content.Players;
namespace ForgottenWawiTech.Content.Projectiles.Avali
{
public class AvaliHuntingDrone : ModProjectile
{
private const float OrbitSpeed = 0.045f;
private const float OrbitRadius = 96f;
private const int ShootCooldown = 28;
private const float TargetRange = 920f;
private const float ShotSpeed = 19.5f;
public override void SetStaticDefaults()
{
ProjectileID.Sets.MinionSacrificable[Type] = false;
ProjectileID.Sets.MinionTargettingFeature[Type] = true;
}
public override void SetDefaults()
{
Projectile.width = 26;
Projectile.height = 26;
Projectile.friendly = false;
Projectile.minion = true;
Projectile.penetrate = -1;
Projectile.tileCollide = false;
Projectile.ignoreWater = true;
Projectile.DamageType = DamageClass.Summon;
Projectile.netImportant = true;
Projectile.timeLeft = 2;
Projectile.minionSlots = 0f;
Projectile.damage = 0;
}
public override void AI()
{
Player owner = Main.player[Projectile.owner];
AvaliVanguardPlayer modPlayer = owner.GetModPlayer<AvaliVanguardPlayer>();
if (!owner.active || owner.dead || !modPlayer.HuntingDronesActive)
{
Projectile.Kill();
return;
}
Projectile.timeLeft = 2;
float index = Projectile.ai[0];
float total = Projectile.ai[1];
if (total <= 0f)
{
total = 3f;
}
float rotation = (Main.GameUpdateCount * OrbitSpeed) + index * MathHelper.TwoPi / total;
Vector2 offset = rotation.ToRotationVector2() * OrbitRadius;
Projectile.Center = owner.Center + offset;
Projectile.velocity = owner.velocity;
Projectile.rotation = (owner.Center - Projectile.Center).ToRotation() + MathHelper.PiOver2;
Lighting.AddLight(Projectile.Center, 0.35f, 0.6f, 1f);
if (Projectile.owner == Main.myPlayer)
{
Projectile.localAI[0]++;
if (Projectile.localAI[0] >= ShootCooldown)
{
Projectile.localAI[0] = 0f;
NPC target = AcquireTarget(owner, Projectile.Center, TargetRange);
if (target != null)
{
Vector2 velocity = (target.Center - Projectile.Center).SafeNormalize(Vector2.UnitY) * ShotSpeed;
int damage = AvaliVanguardPlayer.GetDroneBaseDamage(owner);
var source = Projectile.GetSource_FromAI();
Projectile.NewProjectile(
source,
Projectile.Center,
velocity,
ModContent.ProjectileType<AvaliHuntingDroneShot>(),
damage,
2f,
owner.whoAmI);
SoundEngine.PlaySound(SoundID.Item12 with { Volume = 0.6f }, Projectile.Center);
Projectile.netUpdate = true;
}
}
}
}
private static NPC AcquireTarget(Player owner, Vector2 center, float range)
{
if (owner.HasMinionAttackTargetNPC)
{
NPC target = Main.npc[owner.MinionAttackTargetNPC];
if (target.CanBeChasedBy())
{
return target;
}
}
NPC closest = null;
float squaredRange = range * range;
for (int i = 0; i < Main.maxNPCs; i++)
{
NPC npc = Main.npc[i];
if (!npc.active || npc.friendly || npc.dontTakeDamage || npc.life <= 0)
{
continue;
}
float distance = Vector2.DistanceSquared(npc.Center, center);
if (distance <= squaredRange && Collision.CanHitLine(center, 0, 0, npc.Center, 0, 0))
{
squaredRange = distance;
closest = npc;
}
}
return closest;
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

@@ -0,0 +1,52 @@
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.Audio;
using Terraria.ID;
using Terraria.ModLoader;
namespace ForgottenWawiTech.Content.Projectiles.Avali
{
public class AvaliHuntingDroneShot : ModProjectile
{
public override void SetStaticDefaults()
{
ProjectileID.Sets.MinionShot[Type] = true;
}
public override void SetDefaults()
{
Projectile.width = 12;
Projectile.height = 12;
Projectile.friendly = true;
Projectile.timeLeft = 120;
Projectile.penetrate = 1;
Projectile.tileCollide = false;
Projectile.ignoreWater = true;
Projectile.DamageType = DamageClass.Summon;
Projectile.usesLocalNPCImmunity = true;
Projectile.localNPCHitCooldown = 10;
Projectile.extraUpdates = 1;
Projectile.alpha = 40;
}
public override void AI()
{
Projectile.rotation = Projectile.velocity.ToRotation();
Lighting.AddLight(Projectile.Center, 0.4f, 0.7f, 1.2f);
if (Main.rand.NextBool(3))
{
Dust dust = Dust.NewDustDirect(Projectile.position, Projectile.width, Projectile.height, DustID.Electric, 0f, 0f, 120, default, 1.1f);
dust.noGravity = true;
}
}
public override void OnHitNPC(NPC target, NPC.HitInfo hit, int damageDone)
{
if (Main.myPlayer == Projectile.owner)
{
SoundEngine.PlaySound(SoundID.Item93 with { Volume = 0.4f }, Projectile.Center);
}
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

@@ -0,0 +1,115 @@
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;
namespace ForgottenWawiTech.Content.Projectiles.Protogen
{
public class ProtogenOperativeNanite : ModProjectile
{
private const float DetectionRange = 640f;
private const float HomingStrength = 0.14f;
private const float DesiredSpeed = 14f;
public override void SetStaticDefaults()
{
ProjectileID.Sets.MinionShot[Type] = true;
ProjectileID.Sets.MinionSacrificable[Type] = false;
}
public override void SetDefaults()
{
Projectile.width = 14;
Projectile.height = 14;
Projectile.friendly = true;
Projectile.penetrate = 1;
Projectile.timeLeft = 360;
Projectile.tileCollide = false;
Projectile.ignoreWater = true;
Projectile.DamageType = DamageClass.Generic;
Projectile.usesLocalNPCImmunity = true;
Projectile.localNPCHitCooldown = 20;
Projectile.extraUpdates = 1;
}
public override void AI()
{
Player owner = Main.player[Projectile.owner];
if (!owner.active || owner.dead)
{
Projectile.Kill();
return;
}
Projectile.rotation += 0.2f * Projectile.direction;
Lighting.AddLight(Projectile.Center, 0.25f, 0.55f, 0.75f);
NPC target = AcquireTarget(owner, Projectile.Center, DetectionRange);
if (target != null)
{
Vector2 desiredVelocity = (target.Center - Projectile.Center).SafeNormalize(Vector2.Zero) * DesiredSpeed;
Projectile.velocity = Vector2.Lerp(Projectile.velocity, desiredVelocity, HomingStrength);
}
else
{
Projectile.velocity *= 0.98f;
}
if (Main.rand.NextBool(4))
{
Dust dust = Dust.NewDustDirect(Projectile.position, Projectile.width, Projectile.height, DustID.Electric, 0f, 0f, 100, default, 1.1f);
dust.noGravity = true;
}
}
public override void OnHitNPC(NPC target, NPC.HitInfo hit, int damageDone)
{
if (Main.myPlayer != Projectile.owner)
{
return;
}
Player owner = Main.player[Projectile.owner];
int healAmount = (int)(damageDone * 0.5f);
if (healAmount <= 0)
{
return;
}
owner.statLife = Utils.Clamp(owner.statLife + healAmount, 0, owner.statLifeMax2);
owner.HealEffect(healAmount);
}
private static NPC AcquireTarget(Player owner, Vector2 center, float range)
{
if (owner.HasMinionAttackTargetNPC)
{
NPC target = Main.npc[owner.MinionAttackTargetNPC];
if (target.CanBeChasedBy())
{
return target;
}
}
NPC closest = null;
float squaredRange = range * range;
for (int i = 0; i < Main.maxNPCs; i++)
{
NPC npc = Main.npc[i];
if (!npc.active || npc.friendly || npc.dontTakeDamage || npc.life <= 0)
{
continue;
}
float distance = Vector2.DistanceSquared(npc.Center, center);
if (distance <= squaredRange && Collision.CanHitLine(center, 0, 0, npc.Center, 0, 0))
{
squaredRange = distance;
closest = npc;
}
}
return closest;
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

@@ -0,0 +1,124 @@
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.Audio;
using Terraria.ID;
using Terraria.ModLoader;
using ForgottenWawiTech.Content.Players;
using ForgottenWawiTech.Content.GlobalProjectiles;
namespace ForgottenWawiTech.Content.Projectiles.Protogen
{
public class ProtogenSentinelCore : ModProjectile
{
private const float HoverRadius = 70f;
private const float HoverSpeed = 0.035f;
private const int FireCooldown = 32;
private const float TargetRange = 950f;
private const float BeamVelocity = 18f;
public override void SetStaticDefaults()
{
ProjectileID.Sets.MinionSacrificable[Type] = false;
ProjectileID.Sets.MinionTargettingFeature[Type] = true;
}
public override void SetDefaults()
{
Projectile.width = 36;
Projectile.height = 36;
Projectile.friendly = false;
Projectile.minion = true;
Projectile.tileCollide = false;
Projectile.ignoreWater = true;
Projectile.penetrate = -1;
Projectile.netImportant = true;
Projectile.timeLeft = 2;
Projectile.damage = 0;
Projectile.minionSlots = 0f;
}
public override void AI()
{
Player owner = Main.player[Projectile.owner];
ProtogenSentinelPlayer modPlayer = owner.GetModPlayer<ProtogenSentinelPlayer>();
if (!owner.active || owner.dead || !modPlayer.SentinelCoreActive)
{
Projectile.Kill();
return;
}
Projectile.timeLeft = 2;
float rotation = ((float)Main.GameUpdateCount * HoverSpeed) % MathHelper.TwoPi;
Vector2 desiredOffset = new Vector2(0f, -HoverRadius).RotatedBy(rotation);
Vector2 desiredPosition = owner.Center + desiredOffset;
Projectile.Center = Vector2.Lerp(Projectile.Center, desiredPosition, 0.2f);
Projectile.velocity = owner.velocity;
Lighting.AddLight(Projectile.Center, 0.2f, 0.6f, 0.9f);
if (Projectile.owner == Main.myPlayer)
{
Projectile.localAI[0]++;
if (Projectile.localAI[0] >= FireCooldown)
{
Projectile.localAI[0] = 0f;
NPC target = AcquireTarget(owner, Projectile.Center, TargetRange);
if (target != null)
{
FireBeam(owner, target);
}
}
}
}
private void FireBeam(Player owner, NPC target)
{
Vector2 velocity = (target.Center - Projectile.Center).SafeNormalize(Vector2.UnitY) * BeamVelocity;
int damage = ProtogenSentinelPlayer.GetCoreDamage(owner);
var source = Projectile.GetSource_FromThis();
Projectile beam = Projectile.NewProjectileDirect(
source,
Projectile.Center,
velocity,
ModContent.ProjectileType<ProtogenSentinelCoreBeam>(),
damage,
3f,
owner.whoAmI);
beam.GetGlobalProjectile<ProtogenSentinelGlobalProjectile>().DisableSentinelSplit = true;
SoundEngine.PlaySound(SoundID.Item33 with { Volume = 0.7f, Pitch = 0.15f }, Projectile.Center);
}
private static NPC AcquireTarget(Player owner, Vector2 center, float range)
{
if (owner.HasMinionAttackTargetNPC)
{
NPC target = Main.npc[owner.MinionAttackTargetNPC];
if (target.CanBeChasedBy())
{
return target;
}
}
NPC closest = null;
float squaredRange = range * range;
for (int i = 0; i < Main.maxNPCs; i++)
{
NPC npc = Main.npc[i];
if (!npc.active || npc.friendly || npc.dontTakeDamage || npc.life <= 0)
{
continue;
}
float distance = Vector2.DistanceSquared(npc.Center, center);
if (distance <= squaredRange && Collision.CanHitLine(center, 0, 0, npc.Center, 0, 0))
{
squaredRange = distance;
closest = npc;
}
}
return closest;
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

@@ -0,0 +1,50 @@
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.DataStructures;
using Terraria.ID;
using Terraria.ModLoader;
using ForgottenWawiTech.Content.GlobalProjectiles;
namespace ForgottenWawiTech.Content.Projectiles.Protogen
{
public class ProtogenSentinelCoreBeam : ModProjectile
{
public override void SetStaticDefaults()
{
ProjectileID.Sets.MinionShot[Type] = true;
}
public override void SetDefaults()
{
Projectile.width = 18;
Projectile.height = 18;
Projectile.friendly = true;
Projectile.timeLeft = 120;
Projectile.penetrate = 2;
Projectile.tileCollide = false;
Projectile.ignoreWater = true;
Projectile.DamageType = DamageClass.Summon;
Projectile.extraUpdates = 1;
Projectile.usesLocalNPCImmunity = true;
Projectile.localNPCHitCooldown = 10;
}
public override void AI()
{
Projectile.rotation = Projectile.velocity.ToRotation();
Lighting.AddLight(Projectile.Center, 0.35f, 0.9f, 1.2f);
if (Main.rand.NextBool(3))
{
Dust dust = Dust.NewDustDirect(Projectile.position, Projectile.width, Projectile.height, DustID.BlueCrystalShard, 0f, 0f, 150, default, 1.1f);
dust.noGravity = true;
dust.velocity = Projectile.velocity * 0.2f;
}
}
public override void OnSpawn(IEntitySource source)
{
Projectile.GetGlobalProjectile<ProtogenSentinelGlobalProjectile>().DisableSentinelSplit = true;
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

@@ -0,0 +1,87 @@
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.DataStructures;
using Terraria.ID;
using Terraria.ModLoader;
using ForgottenWawiTech.Content.GlobalProjectiles;
namespace ForgottenWawiTech.Content.Projectiles.Protogen
{
public class ProtogenSentinelFragment : ModProjectile
{
private const float HomingRange = 560f;
private const float HomingStrength = 0.18f;
private const float DesiredSpeed = 15f;
public override void SetStaticDefaults()
{
ProjectileID.Sets.MinionShot[Type] = true;
}
public override void SetDefaults()
{
Projectile.width = 14;
Projectile.height = 14;
Projectile.friendly = true;
Projectile.penetrate = 1;
Projectile.timeLeft = 240;
Projectile.tileCollide = false;
Projectile.ignoreWater = true;
Projectile.DamageType = DamageClass.Generic;
Projectile.extraUpdates = 1;
Projectile.usesLocalNPCImmunity = true;
Projectile.localNPCHitCooldown = 15;
}
public override void AI()
{
Projectile.rotation = Projectile.velocity.ToRotation();
Lighting.AddLight(Projectile.Center, 0.3f, 0.8f, 1.15f);
NPC target = AcquireTarget(Projectile.Center, HomingRange);
if (target != null)
{
Vector2 desiredVelocity = (target.Center - Projectile.Center).SafeNormalize(Vector2.Zero) * DesiredSpeed;
Projectile.velocity = Vector2.Lerp(Projectile.velocity, desiredVelocity, HomingStrength);
}
else
{
Projectile.velocity *= 0.99f;
}
if (Main.rand.NextBool(4))
{
Dust dust = Dust.NewDustDirect(Projectile.position, Projectile.width, Projectile.height, DustID.Electric, 0f, 0f, 120, default, 1.05f);
dust.noGravity = true;
}
}
public override void OnSpawn(IEntitySource source)
{
Projectile.GetGlobalProjectile<ProtogenSentinelGlobalProjectile>().DisableSentinelSplit = true;
}
private static NPC AcquireTarget(Vector2 center, float range)
{
NPC closest = null;
float squaredRange = range * range;
for (int i = 0; i < Main.maxNPCs; i++)
{
NPC npc = Main.npc[i];
if (!npc.active || npc.friendly || npc.dontTakeDamage || npc.life <= 0)
{
continue;
}
float distance = Vector2.DistanceSquared(npc.Center, center);
if (distance <= squaredRange && Collision.CanHitLine(center, 0, 0, npc.Center, 0, 0))
{
squaredRange = distance;
closest = npc;
}
}
return closest;
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

@@ -0,0 +1,123 @@
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.Audio;
using Terraria.ID;
using Terraria.ModLoader;
using ForgottenWawiTech.Content.Players;
namespace ForgottenWawiTech.Content.Projectiles.Singularity
{
public class SingularityOrb : ModProjectile
{
private const float OrbitRadius = 90f;
private const float OrbitSpeed = 0.04f;
private const int FireCooldown = 22;
private const float TargetRange = 1000f;
private const float BeamSpeed = 21f;
public override void SetStaticDefaults()
{
ProjectileID.Sets.MinionSacrificable[Type] = false;
ProjectileID.Sets.MinionTargettingFeature[Type] = true;
}
public override void SetDefaults()
{
Projectile.width = 32;
Projectile.height = 32;
Projectile.friendly = false;
Projectile.minion = true;
Projectile.DamageType = DamageClass.Generic;
Projectile.penetrate = -1;
Projectile.tileCollide = false;
Projectile.ignoreWater = true;
Projectile.netImportant = true;
Projectile.timeLeft = 2;
Projectile.minionSlots = 0f;
}
public override void AI()
{
Player owner = Main.player[Projectile.owner];
SingularityPlayer modPlayer = owner.GetModPlayer<SingularityPlayer>();
if (!owner.active || owner.dead || !modPlayer.SingularityOrbActive)
{
Projectile.Kill();
return;
}
Projectile.timeLeft = 2;
float rotation = ((float)Main.GameUpdateCount * OrbitSpeed) + Projectile.identity * 0.2f;
Vector2 offset = new Vector2(0f, -OrbitRadius).RotatedBy(rotation);
Vector2 desiredPos = owner.Center + offset;
Projectile.Center = Vector2.Lerp(Projectile.Center, desiredPos, 0.28f);
Projectile.velocity = owner.velocity;
Projectile.rotation += 0.03f * Projectile.direction;
Lighting.AddLight(Projectile.Center, 0.55f, 0.2f, 0.65f);
if (Projectile.owner == Main.myPlayer)
{
Projectile.localAI[0]++;
if (Projectile.localAI[0] >= FireCooldown)
{
Projectile.localAI[0] = 0f;
NPC target = AcquireTarget(owner, Projectile.Center, TargetRange);
if (target != null)
{
FireBeam(owner, target);
}
}
}
}
private void FireBeam(Player owner, NPC target)
{
Vector2 velocity = (target.Center - Projectile.Center).SafeNormalize(Vector2.UnitY) * BeamSpeed;
int damage = SingularityPlayer.GetOrbDamage(owner);
var source = Projectile.GetSource_FromThis();
Projectile.NewProjectile(
source,
Projectile.Center,
velocity,
ModContent.ProjectileType<SingularityOrbBeam>(),
damage,
2f,
owner.whoAmI);
SoundEngine.PlaySound(SoundID.Item91 with { Volume = 0.7f, Pitch = 0.35f }, Projectile.Center);
}
private static NPC AcquireTarget(Player owner, Vector2 center, float range)
{
if (owner.HasMinionAttackTargetNPC)
{
NPC forced = Main.npc[owner.MinionAttackTargetNPC];
if (forced.CanBeChasedBy())
{
return forced;
}
}
NPC closest = null;
float sqrRange = range * range;
for (int i = 0; i < Main.maxNPCs; i++)
{
NPC npc = Main.npc[i];
if (!npc.active || npc.friendly || npc.dontTakeDamage || npc.life <= 0)
{
continue;
}
float dist = Vector2.DistanceSquared(npc.Center, center);
if (dist <= sqrRange && Collision.CanHitLine(center, 0, 0, npc.Center, 0, 0))
{
sqrRange = dist;
closest = npc;
}
}
return closest;
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

@@ -0,0 +1,42 @@
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;
namespace ForgottenWawiTech.Content.Projectiles.Singularity
{
public class SingularityOrbBeam : ModProjectile
{
public override void SetStaticDefaults()
{
ProjectileID.Sets.MinionShot[Type] = true;
}
public override void SetDefaults()
{
Projectile.width = 16;
Projectile.height = 16;
Projectile.friendly = true;
Projectile.timeLeft = 120;
Projectile.penetrate = 3;
Projectile.tileCollide = false;
Projectile.ignoreWater = true;
Projectile.DamageType = DamageClass.Generic;
Projectile.extraUpdates = 1;
Projectile.usesLocalNPCImmunity = true;
Projectile.localNPCHitCooldown = 8;
}
public override void AI()
{
Projectile.rotation = Projectile.velocity.ToRotation();
Lighting.AddLight(Projectile.Center, 0.7f, 0.25f, 0.85f);
if (Main.rand.NextBool(3))
{
Dust dust = Dust.NewDustDirect(Projectile.position, Projectile.width, Projectile.height, DustID.PinkTorch, Projectile.velocity.X * 0.2f, Projectile.velocity.Y * 0.2f, 150, default, 1.2f);
dust.noGravity = true;
}
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

@@ -0,0 +1,76 @@
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;
namespace ForgottenWawiTech.Content.Projectiles.Transcendent
{
public class TranscendentAscendantBolt : ModProjectile
{
private const float HomingRange = 720f;
private const float HomingStrength = 0.2f;
private const float DesiredSpeed = 20f;
/* public override void SetStaticDefaults()
{
ProjectileID.Sets.MinionShot[Type] = true;
} */
public override void SetDefaults()
{
Projectile.width = 18;
Projectile.height = 18;
Projectile.friendly = true;
Projectile.timeLeft = 180;
Projectile.penetrate = 2;
Projectile.tileCollide = false;
Projectile.ignoreWater = true;
Projectile.DamageType = DamageClass.Summon;
Projectile.extraUpdates = 1;
Projectile.usesLocalNPCImmunity = true;
Projectile.localNPCHitCooldown = 10;
}
public override void AI()
{
Projectile.rotation = Projectile.velocity.ToRotation();
Lighting.AddLight(Projectile.Center, 0.6f, 0.55f, 1.2f);
NPC target = AcquireTarget(Projectile.Center, HomingRange);
if (target != null)
{
Vector2 desiredVelocity = (target.Center - Projectile.Center).SafeNormalize(Vector2.Zero) * DesiredSpeed;
Projectile.velocity = Vector2.Lerp(Projectile.velocity, desiredVelocity, HomingStrength);
}
if (Main.rand.NextBool(4))
{
Dust dust = Dust.NewDustDirect(Projectile.position, Projectile.width, Projectile.height, DustID.Enchanted_Pink, Projectile.velocity.X * 0.2f, Projectile.velocity.Y * 0.2f, 150, default, 1.15f);
dust.noGravity = true;
}
}
private static NPC AcquireTarget(Vector2 center, float range)
{
NPC closest = null;
float sqrRange = range * range;
for (int i = 0; i < Main.maxNPCs; i++)
{
NPC npc = Main.npc[i];
if (!npc.active || npc.friendly || npc.dontTakeDamage || npc.life <= 0)
{
continue;
}
float dist = Vector2.DistanceSquared(npc.Center, center);
if (dist <= sqrRange && Collision.CanHitLine(center, 0, 0, npc.Center, 0, 0))
{
sqrRange = dist;
closest = npc;
}
}
return closest;
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

@@ -0,0 +1,130 @@
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.Audio;
using Terraria.ID;
using Terraria.ModLoader;
using ForgottenWawiTech.Content.Players;
namespace ForgottenWawiTech.Content.Projectiles.Transcendent
{
public class TranscendentAscendantDrone : ModProjectile
{
private const float OrbitRadius = 136f;
private const float OrbitSpeed = 0.055f;
private const int FireCooldown = 18;
private const float TargetRange = 1100f;
private const float BoltSpeed = 19f;
public override void SetStaticDefaults()
{
ProjectileID.Sets.MinionSacrificable[Type] = false;
ProjectileID.Sets.MinionTargettingFeature[Type] = true;
//ProjectileID.Sets.Minion[Type] = true;
}
public override void SetDefaults()
{
Projectile.width = 28;
Projectile.height = 28;
Projectile.friendly = false;
Projectile.minion = true;
Projectile.penetrate = -1;
Projectile.tileCollide = false;
Projectile.ignoreWater = true;
Projectile.netImportant = true;
Projectile.timeLeft = 2;
Projectile.minionSlots = 0f;
}
public override void AI()
{
Player owner = Main.player[Projectile.owner];
TranscendentPlayer modPlayer = owner.GetModPlayer<TranscendentPlayer>();
if (!owner.active || owner.dead || !modPlayer.HasTranscendentSet)
{
Projectile.Kill();
return;
}
Projectile.timeLeft = 2;
float index = Projectile.ai[0];
float total = Projectile.ai[1];
if (total <= 0f)
{
total = 4f;
}
float rotation = ((float)Main.GameUpdateCount * OrbitSpeed) + index * MathHelper.TwoPi / total;
Vector2 offset = rotation.ToRotationVector2() * OrbitRadius;
Projectile.Center = owner.Center + offset;
Projectile.velocity = owner.velocity;
Projectile.rotation = (owner.Center - Projectile.Center).ToRotation() + MathHelper.PiOver2;
Lighting.AddLight(Projectile.Center, 0.45f, 0.6f, 1.05f);
if (Projectile.owner == Main.myPlayer)
{
Projectile.localAI[0]++;
if (Projectile.localAI[0] >= FireCooldown)
{
Projectile.localAI[0] = 0f;
NPC target = AcquireTarget(owner, Projectile.Center, TargetRange);
if (target != null)
{
FireBolt(owner, target);
}
}
}
}
private void FireBolt(Player owner, NPC target)
{
Vector2 velocity = (target.Center - Projectile.Center).SafeNormalize(Vector2.UnitY) * BoltSpeed;
int damage = TranscendentPlayer.GetAscendantDroneDamage(owner);
var source = Projectile.GetSource_FromThis();
Projectile.NewProjectile(
source,
Projectile.Center,
velocity,
ModContent.ProjectileType<TranscendentAscendantBolt>(),
damage,
4f,
owner.whoAmI);
SoundEngine.PlaySound(SoundID.Item33 with { Volume = 0.6f, Pitch = 0.2f }, Projectile.Center);
Projectile.netUpdate = true;
}
private static NPC AcquireTarget(Player owner, Vector2 center, float range)
{
if (owner.HasMinionAttackTargetNPC)
{
NPC forced = Main.npc[owner.MinionAttackTargetNPC];
if (forced.CanBeChasedBy())
{
return forced;
}
}
NPC closest = null;
float sqrRange = range * range;
for (int i = 0; i < Main.maxNPCs; i++)
{
NPC npc = Main.npc[i];
if (!npc.active || npc.friendly || npc.dontTakeDamage || npc.life <= 0)
{
continue;
}
float dist = Vector2.DistanceSquared(npc.Center, center);
if (dist <= sqrRange && Collision.CanHitLine(center, 0, 0, npc.Center, 0, 0))
{
sqrRange = dist;
closest = npc;
}
}
return closest;
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

@@ -0,0 +1,81 @@
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;
namespace ForgottenWawiTech.Content.Projectiles.Transcendent
{
public class TranscendentNaniteShard : ModProjectile
{
private const float HomingRange = 680f;
private const float HomingStrength = 0.22f;
private const float DesiredSpeed = 18f;
public override void SetStaticDefaults()
{
ProjectileID.Sets.MinionShot[Type] = true;
}
public override void SetDefaults()
{
Projectile.width = 14;
Projectile.height = 14;
Projectile.friendly = true;
Projectile.penetrate = 1;
Projectile.timeLeft = 220;
Projectile.tileCollide = false;
Projectile.ignoreWater = true;
Projectile.extraUpdates = 1;
Projectile.DamageType = DamageClass.Generic;
Projectile.usesLocalNPCImmunity = true;
Projectile.localNPCHitCooldown = 12;
Projectile.alpha = 60;
}
public override void AI()
{
Projectile.rotation += 0.35f * Projectile.direction;
Lighting.AddLight(Projectile.Center, 0.45f, 0.8f, 1.15f);
NPC target = AcquireTarget(Projectile.Center, HomingRange);
if (target != null)
{
Vector2 desiredVelocity = (target.Center - Projectile.Center).SafeNormalize(Vector2.Zero) * DesiredSpeed;
Projectile.velocity = Vector2.Lerp(Projectile.velocity, desiredVelocity, HomingStrength);
}
else
{
Projectile.velocity *= 0.98f;
}
if (Main.rand.NextBool(5))
{
Dust dust = Dust.NewDustDirect(Projectile.position, Projectile.width, Projectile.height, DustID.Electric, 0f, 0f, 120, default, 1.05f);
dust.noGravity = true;
}
}
private static NPC AcquireTarget(Vector2 center, float range)
{
NPC closest = null;
float sqrRange = range * range;
for (int i = 0; i < Main.maxNPCs; i++)
{
NPC npc = Main.npc[i];
if (!npc.active || npc.friendly || npc.dontTakeDamage || npc.life <= 0)
{
continue;
}
float dist = Vector2.DistanceSquared(npc.Center, center);
if (dist <= sqrRange && Collision.CanHitLine(center, 0, 0, npc.Center, 0, 0))
{
sqrRange = dist;
closest = npc;
}
}
return closest;
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

@@ -896,6 +896,17 @@ Items: {
}
}
Conditions: {
HasDefeatedAnyMechBoss: Defeated a mechanical boss
HasDefeatedExoMechs: Defeated the Exo Mechs
HasDefeatedLexy: Defeated Lexy
HasDefeatedNiko: Defeated Niko
HasDefeatedNeptune: Defeated Neptune
HasDefeatedLyyka: Defeated Lyyka
HasDefeatedCorruptedSwarm: Defeated the Corrupted Swarm
HasDefeatedMoonLord: Defeated the Moon Lord
}
NPCs: {
Lexy.DisplayName: Lexy
@@ -992,6 +1003,17 @@ Projectiles: {
TranscendentNova.DisplayName: Transcendent Nova
TranscendentRailBolt.DisplayName: Transcendent Rail Bolt
AvaliThermalDrone.DisplayName: Avali Thermal Drone
AvaliHuntingDrone.DisplayName: Avali Hunting Drone
AvaliHuntingDroneShot.DisplayName: Avali Hunting Drone Pulse
ProtogenOperativeNanite.DisplayName: Operative Nanite
ProtogenSentinelCore.DisplayName: Sentinel AI Core
ProtogenSentinelCoreBeam.DisplayName: Sentinel Core Beam
ProtogenSentinelFragment.DisplayName: Sentinel Split Fragment
SingularityOrb.DisplayName: Singularity Orb
SingularityOrbBeam.DisplayName: Singularity Orb Beam
TranscendentAscendantDrone.DisplayName: Ascendant Swarm Drone
TranscendentAscendantBolt.DisplayName: Ascendant Swarm Bolt
TranscendentNaniteShard.DisplayName: Nanite Shard
}
Prefixes: {