Glasia 森の魔王🌳
The Destroyer
C#:
using Microsoft.Xna.Framework;
using System;
using System.Linq;
using Terraria;
using Terraria.Audio;
using Terraria.ID;
using Terraria.Localization;
using Terraria.ModLoader;
using Terraria.Utilities;
using Terraria.GameContent.Bestiary;
using Terraria.GameContent.ItemDropRules;
using Microsoft.Xna.Framework.Graphics;
using Terraria.GameContent;
using Terraria.GameContent.Personalities;
using System.Collections.Generic;
using Terraria.ModLoader.IO;
namespace GlasiasFirstMod.Content.NPCs
{
[AutoloadHead]
public class GrocerySeller : ModNPC
{
public const string ShopName = "Shop";
public int NumberOfTimesTalkedTo = 0;
private static int ShimmerHeadIndex;
private static Profiles.StackedNPCProfile NPCProfile;
public override void Load()
{
// Adds our Shimmer Head to the NPCHeadLoader.
ShimmerHeadIndex = Mod.AddNPCHeadTexture(Type, Texture + "_Shimmer_Head");
}
public override void SetStaticDefaults()
{
Main.npcFrameCount[Type] = 23; // The total amount of frames the NPC has
NPCID.Sets.ExtraFramesCount[Type] = 9; // Generally for Town NPCs, but this is how the NPC does extra things such as sitting in a chair and talking to other NPCs. This is the remaining frames after the walking frames.
NPCID.Sets.AttackFrameCount[Type] = 4; // The amount of frames in the attacking animation.
NPCID.Sets.DangerDetectRange[Type] = 700; // The amount of pixels away from the center of the NPC that it tries to attack enemies.
NPCID.Sets.AttackType[Type] = 2; // The type of attack the Town NPC performs. 0 = throwing, 1 = shooting, 2 = magic, 3 = melee
NPCID.Sets.AttackTime[Type] = 25; // The amount of time it takes for the NPC's attack animation to be over once it starts.
NPCID.Sets.AttackAverageChance[Type] = 15; // The denominator for the chance for a Town NPC to attack. Lower numbers make the Town NPC appear more aggressive.
NPCID.Sets.HatOffsetY[Type] = 4; // For when a party is active, the party hat spawns at a Y offset.
NPCID.Sets.ShimmerTownTransform[NPC.type] = true; // This set says that the Town NPC has a Shimmered form. Otherwise, the Town NPC will become transparent when touching Shimmer like other enemies.
NPCID.Sets.ShimmerTownTransform[Type] = true; // Allows for this NPC to have a different texture after touching the Shimmer liquid.
// Influences how the NPC looks in the Bestiary
NPCID.Sets.NPCBestiaryDrawModifiers drawModifiers = new NPCID.Sets.NPCBestiaryDrawModifiers(0)
{
Velocity = 1f,
Direction = 1
};
NPCID.Sets.NPCBestiaryDrawOffset.Add(Type, drawModifiers);
// Set Example Person's biome and neighbor preferences with the NPCHappiness hook. You can add happiness text and remarks with localization (See an example in ExampleMod/Localization/en-US.lang).
// NOTE: The following code uses chaining - a style that works due to the fact that the SetXAffection methods return the same NPCHappiness instance they're called on.
NPC.Happiness
.SetBiomeAffection<ForestBiome>(AffectionLevel.Love)
.SetBiomeAffection<JungleBiome>(AffectionLevel.Like)
.SetBiomeAffection<SnowBiome>(AffectionLevel.Like)
.SetBiomeAffection<UndergroundBiome>(AffectionLevel.Dislike)
.SetBiomeAffection<OceanBiome>(AffectionLevel.Love)
.SetNPCAffection(NPCID.Dryad, AffectionLevel.Love)
.SetNPCAffection(NPCID.Guide, AffectionLevel.Like)
.SetNPCAffection(NPCID.Wizard, AffectionLevel.Like)
.SetNPCAffection(NPCID.Merchant, AffectionLevel.Hate)
.SetNPCAffection(NPCID.Demolitionist, AffectionLevel.Hate)
; // < Mind the semicolon!
// This creates a "profile" for the NPC, which allows for different textures during a party and/or while the NPC is shimmered.
NPCProfile = new Profiles.StackedNPCProfile(
new Profiles.DefaultNPCProfile(Texture, NPCHeadLoader.GetHeadSlot(HeadTexture), Texture + "_Party"),
new Profiles.DefaultNPCProfile(Texture + "_Shimmer", ShimmerHeadIndex, Texture + "_Shimmer_Party")
);
}
public override void SetDefaults()
{
//Sets the NPC to a friendly town NPC differentiating from normal enemies
NPC.townNPC = true;
NPC.friendly = true;
//The usual stats
NPC.width = 40;
NPC.height = 58;
NPC.aiStyle = 7;
NPC.damage = 10;
NPC.defense = 15;
NPC.lifeMax = 250;
NPC.HitSound = SoundID.NPCHit1;
NPC.DeathSound = SoundID.NPCDeath1;
NPC.knockBackResist = 0.5f;
//Using the frame amounts from the mechanic.
AnimationType = NPCID.Mechanic;
}
public override void SetBestiary(BestiaryDatabase database, BestiaryEntry bestiaryEntry)
{
bestiaryEntry.Info.AddRange(new IBestiaryInfoElement[] {
BestiaryDatabaseNPCsPopulator.CommonTags.SpawnConditions.Biomes.Surface,
new FlavorTextBestiaryInfoElement("Working hard in the grocery store to provide you the best A+ quality food."),
});
}
public override bool PreDraw(SpriteBatch spriteBatch, Vector2 screenPos, Color drawColor)
{
// This code slowly rotates the NPC in the bestiary
if (NPCID.Sets.NPCBestiaryDrawOffset.TryGetValue(Type, out NPCID.Sets.NPCBestiaryDrawModifiers drawModifiers))
{
drawModifiers.Rotation += 0.001f;
NPCID.Sets.NPCBestiaryDrawOffset.Remove(Type);
NPCID.Sets.NPCBestiaryDrawOffset.Add(Type, drawModifiers);
}
return true;
}
public override void HitEffect(NPC.HitInfo hit)
{
int num = NPC.life > 0 ? 1 : 5;
for (int k = 0; k < num; k++)
{
Dust.NewDust(NPC.position, NPC.width, NPC.height, Dust.NewDust(NPC.position, 4, 4, 0, 0f, 0f, 0, default(Color), 1f));
}
// Create gore when the NPC is killed.
if (Main.netMode != NetmodeID.Server && NPC.life <= 0)
{
Gore.NewGore(NPC.GetSource_Death(), NPC.position, new Vector2(NPC.velocity.X/2, -0.5f), 12);
}
}
public override bool CanTownNPCSpawn(int numTownNPCs)
{ // Requirements for the town NPC to spawn.
for (int k = 0; k < Main.maxPlayers; k++)
{
Player player = Main.player[k];
if (!player.active)
{
continue;
}
// Player has to have 324 or more max HP
if (player.statLifeMax > 324)
{
return true;
}
}
return false;
}
public override ITownNPCProfile TownNPCProfile()
{
return NPCProfile;
}
public override List<string> SetNPCNameList()
{
//Generic short shop names
return new List<string>() {
"RSBA",
"KEDA",
"FGHA",
"RMHC",
"SKWL",
};
}
/*
* Question: How do I retrieve the name of the player currently talking to the NPC to use it
* as a variable in the chat text so that the player directly is addressed?
* For NPC names it is Main.npc[npcname].GivenName, where npcname is the first
* NPC in the 200 NPC slots of the needed type, retreived with the FindFirstNPC(type) method.
*/
public override string GetChat()
{
WeightedRandom<string> chat = new WeightedRandom<string>();
int merchant = NPC.FindFirstNPC(NPCID.Merchant);
if (merchant >= 0 && Main.rand.NextBool(4))
{
chat.Add($"{Main.npc[merchant].GivenName} the merchant tries to steal my customers, I hate this!");
}
// These are things that the NPC has a chance of telling you when you talk to it.
chat.Add($"Hello, how are you?");
chat.Add($"How is your day going?");
NumberOfTimesTalkedTo++;
if (NumberOfTimesTalkedTo >= 10)
{
//This counter is linked to a single instance of the NPC, so if ExamplePerson is killed, the counter will reset.
chat.Add(Language.GetTextValue("You are a frequent customer here. That I appreciate!"));
}
return chat;
}
public override void SetChatButtons(ref string button, ref string button2)
{ // What the chat buttons are when you open up the chat UI
button = Language.GetTextValue("LegacyInterface.28");
}
public override void OnChatButtonClicked(bool firstButton, ref string shop)
{
if (firstButton)
{
shop = ShopName; // Name of the shop tab we want to open.
}
}
// Not completely finished, but below is what the NPC will sell
public override void AddShops()
{
var npcShop = new NPCShop(Type, ShopName)
.Add(ItemID.IronPickaxe); // Item 1 for test
npcShop.Register();
}
public override void ModifyActiveShop(string shopName, Item[] items)
{
foreach (Item item in items)
{
// Skip 'air' items and null items.
if (item == null || item.type == ItemID.None)
{
continue;
}
// If NPC is shimmered then reduce all prices by 30%.
if (NPC.IsShimmerVariant)
{
int value = item.shopCustomPrice ?? item.value;
item.shopCustomPrice = (int?) (value * 0.7f);
}
}
}
public override void ModifyNPCLoot(NPCLoot npcLoot)
{
npcLoot.Add(ItemDropRule.Common(ItemID.RainbowBrick, 1, 15, 15));
}
//Teleports the NPC to the King statue, not to the Queen statue
public override bool CanGoToStatue(bool toKingStatue) => toKingStatue;
// Make something happen when the npc teleports to a statue. Since this method only runs server side, any visual effects like dusts or gores have to be synced across all clients manually.
public override void OnGoToStatue(bool toKingStatue)
{
if (Main.netMode == NetmodeID.Server)
{
ModPacket packet = Mod.GetPacket();
//packet.Write((byte)ExampleMod.MessageType.ExampleTeleportToStatue);
packet.Write((byte)NPC.whoAmI);
packet.Send();
}
else
{
StatueTeleport();
}
}
// Create a square of pixels around the NPC on teleport.
public void StatueTeleport()
{
for (int i = 0; i < 30; i++)
{
Vector2 position = Main.rand.NextVector2Square(-20, 21);
if (Math.Abs(position.X) > Math.Abs(position.Y))
{
position.X = Math.Sign(position.X) * 20;
}
else
{
position.Y = Math.Sign(position.Y) * 20;
}
Dust.NewDustPerfect(NPC.Center + position, Dust.NewDust(NPC.Center+position, 21, 1, 1, 0f, -0.1f), Vector2.Zero).noGravity = true;
}
}
public override void TownNPCAttackStrength(ref int damage, ref float knockback)
{
damage = 22;
knockback = 4f;
}
public override void TownNPCAttackCooldown(ref int cooldown, ref int randExtraCooldown)
{
cooldown = 30;
randExtraCooldown = 30;
}
public override void TownNPCAttackProj(ref int projType, ref int attackDelay)
{
projType = ProjectileID.DemonScythe;
attackDelay = 1;
}
public override void TownNPCAttackProjSpeed(ref float multiplier, ref float gravityCorrection, ref float randomOffset)
{
multiplier = 12f;
randomOffset = 2f;
}
public override void LoadData(TagCompound tag)
{
NumberOfTimesTalkedTo = tag.GetInt("numberOfTimesTalkedTo");
}
public override void SaveData(TagCompound tag)
{
tag["numberOfTimesTalkedTo"] = NumberOfTimesTalkedTo;
}
}
}
To the public override string GetChat() method I have a question: How do I use the player's name in the NPC dialogue texts? How do I use the world name in the NPC dialogue texts?