forked from tModLoader/tModLoader
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathExampleMod.cs
498 lines (441 loc) · 20.7 KB
/
ExampleMod.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
using ExampleMod.NPCs.PuritySpirit;
using ExampleMod.Tiles;
using ExampleMod.UI;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using ReLogic.Graphics;
using System;
using System.Collections.Generic;
using System.IO;
using ExampleMod.Items.ExampleDamageClass;
using Terraria;
using Terraria.GameContent.Dyes;
using Terraria.GameContent.UI;
using Terraria.Graphics.Effects;
using Terraria.Graphics.Shaders;
using Terraria.ID;
using Terraria.Localization;
using Terraria.ModLoader;
using Terraria.UI;
namespace ExampleMod
{
public class ExampleMod : Mod
{
public static ModHotKey RandomBuffHotKey;
public static int FaceCustomCurrencyId;
// With the new fonts in 1.3.5, font files are pretty big now so you need to generate the font file before building the mod.
// You can use https://forums.terraria.org/index.php?threads/dynamicspritefontgenerator-0-4-generate-fonts-without-xna-game-studio.57127/ to make dynamicspritefonts
public static DynamicSpriteFont exampleFont;
private UserInterface _exampleUserInterface;
private UserInterface _exampleResourceBarUserInterface;
internal UserInterface ExamplePersonUserInterface;
internal ExampleUI ExampleUI;
internal ExampleResourceBar ExampleResourceBar;
// Your mod instance has a Logger field, use it.
// OPTIONAL: You can create your own logger this way, recommended is a custom logging class if you do a lot of logging
// You need to reference the log4net library to do this, this can be found in the tModLoader repository
// inside the references folder. You do not have to add this to build.txt as tML has it natively.
// internal ILog Logging = LogManager.GetLogger("ExampleMod");
public ExampleMod() {
// By default, all Autoload properties are True. You only need to change this if you know what you are doing.
//Properties = new ModProperties()
//{
// Autoload = true,
// AutoloadGores = true,
// AutoloadSounds = true,
// AutoloadBackgrounds = true
//};
}
public override void Load() {
// Will show up in client.log under the ExampleMod name
Logger.InfoFormat("{0} example logging", Name);
// In older tModLoader versions we used: ErrorLogger.Log("blabla");
// Replace that with above
// Registers a new hotkey
RandomBuffHotKey = RegisterHotKey("Random Buff", "P"); // See https://docs.microsoft.com/en-us/previous-versions/windows/xna/bb197781(v=xnagamestudio.41) for special keys
// Registers a new custom currency
FaceCustomCurrencyId = CustomCurrencyManager.RegisterCurrency(new ExampleCustomCurrency(ModContent.ItemType<Items.Face>(), 999L));
Mundane.AddHacks();
// All code below runs only if we're not loading on a server
if (!Main.dedServ) {
// Add certain equip textures
AddEquipTexture(null, EquipType.Legs, "ExampleRobe_Legs", "ExampleMod/Items/Armor/ExampleRobe_Legs");
AddEquipTexture(new Items.Armor.BlockyHead(), null, EquipType.Head, "BlockyHead", "ExampleMod/Items/Armor/ExampleCostume_Head");
AddEquipTexture(new Items.Armor.BlockyBody(), null, EquipType.Body, "BlockyBody", "ExampleMod/Items/Armor/ExampleCostume_Body", "ExampleMod/Items/Armor/ExampleCostume_Arms");
AddEquipTexture(new Items.Armor.BlockyLegs(), null, EquipType.Legs, "BlockyLeg", "ExampleMod/Items/Armor/ExampleCostume_Legs");
// Register a new music box
AddMusicBox(GetSoundSlot(SoundType.Music, "Sounds/Music/MarbleGallery"), ItemType("ExampleMusicBox"), TileType("ExampleMusicBox"));
// Change the vanilla loom texture
Main.instance.LoadTiles(TileID.Loom); // First load the tile texture
Main.tileTexture[TileID.Loom] = GetTexture("Tiles/AnimatedLoom"); // Now we change it
//What if....Replace a vanilla item texture and equip texture.
//Main.itemTexture[ItemID.CopperHelmet] = GetTexture("Resprite/CopperHelmet_Item");
//Item copperHelmet = new Item();
//copperHelmet.SetDefaults(ItemID.CopperHelmet);
//Main.armorHeadLoaded[copperHelmet.headSlot] = true;
//Main.armorHeadTexture[copperHelmet.headSlot] = GetTexture("Resprite/CopperHelmet_Head");
// Create new skies and screen filters
Filters.Scene["ExampleMod:PuritySpirit"] = new Filter(new PuritySpiritScreenShaderData("FilterMiniTower").UseColor(0.4f, 0.9f, 0.4f).UseOpacity(0.7f), EffectPriority.VeryHigh);
SkyManager.Instance["ExampleMod:PuritySpirit"] = new PuritySpiritSky();
Filters.Scene["ExampleMod:MonolithVoid"] = new Filter(new ScreenShaderData("FilterMoonLord"), EffectPriority.Medium);
SkyManager.Instance["ExampleMod:MonolithVoid"] = new VoidSky();
GameShaders.Armor.BindShader(ModContent.ItemType<Items.ExampleDye>(), new ArmorShaderData(new Ref<Effect>(GetEffect("Effects/ExampleEffect")), "ExampleDyePass"));
GameShaders.Hair.BindShader(ModContent.ItemType<Items.ExampleHairDye>(), new LegacyHairShaderData().UseLegacyMethod((Player player, Color newColor, ref bool lighting) => Color.Green));
GameShaders.Misc["ExampleMod:DeathAnimation"] = new MiscShaderData(new Ref<Effect>(GetEffect("Effects/ExampleEffectDeath")), "DeathAnimation").UseImage("Images/Misc/Perlin");
if (FontExists("Fonts/ExampleFont"))
exampleFont = GetFont("Fonts/ExampleFont");
// Custom UI
ExampleUI = new ExampleUI();
ExampleUI.Activate();
_exampleUserInterface = new UserInterface();
_exampleUserInterface.SetState(ExampleUI);
// Custom Resource Bar
ExampleResourceBar = new ExampleResourceBar();
_exampleResourceBarUserInterface = new UserInterface();
_exampleResourceBarUserInterface.SetState(ExampleResourceBar);
// UserInterface can only show 1 UIState at a time. If you want different "pages" for a UI, switch between UIStates on the same UserInterface instance.
// We want both the Coin counter and the Example Person UI to be independent and coexist simultaneously, so we have them each in their own UserInterface.
ExamplePersonUserInterface = new UserInterface();
// We will call .SetState later in ExamplePerson.OnChatButtonClicked
}
// Register custom mod translations, lives left is for Spirit of Purity
// See the .lang files in the Localization folder for an easier to manage approach to translations. These few examples are here just to illustrate the concept.
ModTranslation text = CreateTranslation("LivesLeft");
text.SetDefault("{0} has {1} lives left!");
AddTranslation(text);
text = CreateTranslation("LifeLeft");
text.SetDefault("{0} has 1 life left!");
AddTranslation(text);
text = CreateTranslation("NPCTalk");
text.SetDefault("<{0}> {1}");
AddTranslation(text);
text = CreateTranslation("Common.LocalizedLabelDynamic");
text.SetDefault($"[i:{ModContent.ItemType<Items.Weapons.SpectreGun>()}] This dynamic label is added in ExampleMod.Load");
AddTranslation(text);
text = CreateTranslation("BossSpawnInfo.Abomination");
text.SetDefault("Use a [i:" + ModContent.ItemType<Items.Abomination.FoulOrb>() + "] in the underworld after Plantera has been defeated");
AddTranslation(text);
// Volcano warning is for the random volcano tremor
text = CreateTranslation("VolcanoWarning");
text.SetDefault("Did you hear something....A Volcano! Find Cover!");
AddTranslation(text);
}
public override void Unload() {
// All code below runs only if we're not loading on a server
if (!Main.dedServ) {
Main.tileFrame[TileID.Loom] = 0; // Reset the frame of the loom tile
Main.tileSetsLoaded[TileID.Loom] = false; // Causes the loom tile to reload its vanilla texture
}
// Unload static references
// You need to clear static references to assets (Texture2D, SoundEffects, Effects).
// In addition to that, if you want your mod to completely unload during unload, you need to clear static references to anything referencing your Mod class
RandomBuffHotKey = null;
NPCs.ExampleTravelingMerchant.shopItems.Clear();
}
public override void PostSetupContent() {
// Showcases mod support with Boss Checklist without referencing the mod
Mod bossChecklist = ModLoader.GetMod("BossChecklist");
if (bossChecklist != null) {
bossChecklist.Call(
"AddBoss",
10.5f,
new List<int> { ModContent.NPCType<NPCs.Abomination.Abomination>(), ModContent.NPCType<NPCs.Abomination.CaptiveElement2>() },
this, // Mod
"$Mods.ExampleMod.NPCName.Abomination",
(Func<bool>)(() => ExampleWorld.downedAbomination),
ModContent.ItemType<Items.Abomination.FoulOrb>(),
new List<int> { ModContent.ItemType<Items.Armor.AbominationMask>(), ModContent.ItemType<Items.Placeable.AbominationTrophy>() },
new List<int> { ModContent.ItemType<Items.Abomination.SixColorShield>(), ModContent.ItemType<Items.Abomination.MoltenDrill>() },
"$Mods.ExampleMod.BossSpawnInfo.Abomination"
);
bossChecklist.Call(
"AddBoss",
15.5f,
ModContent.NPCType<PuritySpirit>(),
this,
"Purity Spirit",
(Func<bool>)(() => ExampleWorld.downedPuritySpirit),
ItemID.Bunny,
new List<int> { ModContent.ItemType<Items.Armor.PuritySpiritMask>(), ModContent.ItemType<Items.Armor.BunnyMask>(), ModContent.ItemType<Items.Placeable.PuritySpiritTrophy>(), ModContent.ItemType<Items.Placeable.BunnyTrophy>(), ModContent.ItemType<Items.Placeable.TreeTrophy>() },
new List<int> { ModContent.ItemType<Items.PurityShield>(), ItemID.Bunny },
$"Kill a [i:{ItemID.Bunny}] in front of [i:{ModContent.ItemType<Items.Placeable.ElementalPurge>()}]"
);
}
}
public override void AddRecipeGroups() {
// Creates a new recipe group
RecipeGroup group = new RecipeGroup(() => Language.GetTextValue("LegacyMisc.37") + " " + Lang.GetItemNameValue(ItemType("ExampleItem")), new[]
{
ItemType("ExampleItem"),
ItemType("EquipMaterial"),
ItemType("BossItem")
});
// Registers the new recipe group with the specified name
RecipeGroup.RegisterGroup("ExampleMod:ExampleItem", group);
// Modifying a vanilla recipe group. Now we can use Lava Snail to craft Snail Statue
RecipeGroup snailGroup = RecipeGroup.recipeGroups[RecipeGroup.recipeGroupIDs["Snails"]];
snailGroup.ValidItems.Add(ModContent.ItemType<NPCs.ExampleCritterItem>());
// We also add ExampleSand to the Sand group, which is used in the Magic Sand Dropper recipe
RecipeGroup.recipeGroups[RecipeGroup.recipeGroupIDs["Sand"]].ValidItems.Add(ModContent.ItemType<Items.Placeable.ExampleSand>());
}
// Learn how to do Recipes: https://github.com/tModLoader/tModLoader/wiki/Basic-Recipes
public override void AddRecipes() {
// Here is an example of a recipe.
ModRecipe recipe = new ModRecipe(this);
recipe.AddIngredient(ItemType("ExampleItem"));
recipe.SetResult(ItemID.Wood, 999);
recipe.AddRecipe();
// To make ExampleMod more organized, the rest of the recipes are added elsewhere, see the method calls below.
// See RecipeHelper.cs
RecipeHelper.AddExampleRecipes(this);
RecipeHelper.ExampleRecipeEditing(this);
}
public override void UpdateMusic(ref int music, ref MusicPriority priority) {
if (Main.myPlayer == -1 || Main.gameMenu || !Main.LocalPlayer.active) {
return;
}
// Make sure your logic here goes from lowest priority to highest so your intended priority is maintained.
if (Main.LocalPlayer.GetModPlayer<ExamplePlayer>().ZoneExample) {
music = GetSoundSlot(SoundType.Music, "Sounds/Music/MarbleGallery");
priority = MusicPriority.BiomeLow;
}
if (Main.LocalPlayer.HasBuff(BuffType("CarMount"))) {
music = GetSoundSlot(SoundType.Music, "Sounds/Music/DriveMusic");
priority = MusicPriority.Environment;
}
}
public override void ModifySunLightColor(ref Color tileColor, ref Color backgroundColor) {
if (ExampleWorld.exampleTiles <= 0) {
return;
}
float exampleStrength = ExampleWorld.exampleTiles / 200f;
exampleStrength = Math.Min(exampleStrength, 1f);
int sunR = backgroundColor.R;
int sunG = backgroundColor.G;
int sunB = backgroundColor.B;
// Remove some green and more red.
sunR -= (int)(180f * exampleStrength * (backgroundColor.R / 255f));
sunG -= (int)(90f * exampleStrength * (backgroundColor.G / 255f));
sunR = Utils.Clamp(sunR, 15, 255);
sunG = Utils.Clamp(sunG, 15, 255);
sunB = Utils.Clamp(sunB, 15, 255);
backgroundColor.R = (byte)sunR;
backgroundColor.G = (byte)sunG;
backgroundColor.B = (byte)sunB;
}
//const int ShakeLength = 5;
//readonly int ShakeCount = 0;
//readonly float previousRotation = 0;
//readonly float targetRotation = 0;
//readonly float previousOffsetX = 0;
//readonly float previousOffsetY = 0;
//readonly float targetOffsetX = 0;
//readonly float targetOffsetY = 0;
// Volcano Tremor
/* TODO To be fixed later.
public override Matrix ModifyTransformMatrix(Matrix Transform)
{
if (!Main.gameMenu)
{
ExampleWorld world = GetModWorld<ExampleWorld>();
if (world.VolcanoTremorTime > 0)
{
if (world.VolcanoTremorTime % ShakeLength == 0)
{
ShakeCount = 0;
previousRotation = targetRotation;
previousOffsetX = targetOffsetX;
previousOffsetY = targetOffsetY;
targetRotation = (Main.rand.NextFloat() - .5f) * MathHelper.ToRadians(15);
targetOffsetX = Main.rand.Next(60) - 30;
targetOffsetY = Main.rand.Next(40) - 20;
if (world.VolcanoTremorTime == ShakeLength)
{
targetRotation = 0;
targetOffsetX = 0;
targetOffsetY = 0;
}
}
float transX = Main.screenWidth / 2;
float transY = Main.screenHeight / 2;
float lerp = (float)(ShakeCount) / ShakeLength;
float rotation = MathHelper.Lerp(previousRotation, targetRotation, lerp);
float offsetX = MathHelper.Lerp(previousOffsetX, targetOffsetX, lerp);
float offsetY = MathHelper.Lerp(previousOffsetY, targetOffsetY, lerp);
world.VolcanoTremorTime--;
ShakeCount++;
return Transform
* Matrix.CreateTranslation(-transX, -transY, 0f)
* Matrix.CreateRotationZ(rotation)
* Matrix.CreateTranslation(transX, transY, 0f)
* Matrix.CreateTranslation(offsetX, offsetY, 0f);
//Matrix.CreateFromAxisAngle(new Vector3(Main.screenWidth / 2, Main.screenHeight / 2, 0f), .2f);
//Matrix.CreateRotationZ(MathHelper.ToRadians(30));
}
}
return Transform;
}
*/
public override void UpdateUI(GameTime gameTime) {
if (ExampleUI.Visible) {
_exampleUserInterface?.Update(gameTime);
}
_exampleResourceBarUserInterface?.Update(gameTime);
ExamplePersonUserInterface?.Update(gameTime);
}
public override void ModifyInterfaceLayers(List<GameInterfaceLayer> layers) {
int mouseTextIndex = layers.FindIndex(layer => layer.Name.Equals("Vanilla: Mouse Text"));
if (mouseTextIndex != -1) {
layers.Insert(mouseTextIndex, new LegacyGameInterfaceLayer(
"ExampleMod: Coins Per Minute",
delegate {
if (ExampleUI.Visible) {
_exampleUserInterface.Draw(Main.spriteBatch, new GameTime());
}
return true;
},
InterfaceScaleType.UI)
);
}
int inventoryIndex = layers.FindIndex(layer => layer.Name.Equals("Vanilla: Inventory"));
if (inventoryIndex != -1) {
layers.Insert(inventoryIndex, new LegacyGameInterfaceLayer(
"ExampleMod: Example Person UI",
delegate {
// If the current UIState of the UserInterface is null, nothing will draw. We don't need to track a separate .visible value.
ExamplePersonUserInterface.Draw(Main.spriteBatch, new GameTime());
return true;
},
InterfaceScaleType.UI)
);
}
int resourceBarIndex = layers.FindIndex(layer => layer.Name.Equals("Vanilla: Resource Bars"));
if (resourceBarIndex != -1) {
layers.Insert(resourceBarIndex, new LegacyGameInterfaceLayer(
"ExampleMod: Example Resource Bar",
delegate {
_exampleResourceBarUserInterface.Draw(Main.spriteBatch, new GameTime());
return true;
},
InterfaceScaleType.UI)
);
}
}
public static bool NoInvasion(NPCSpawnInfo spawnInfo) => !spawnInfo.invasion && (!Main.pumpkinMoon && !Main.snowMoon || spawnInfo.spawnTileY > Main.worldSurface || Main.dayTime) && (!Main.eclipse || spawnInfo.spawnTileY > Main.worldSurface || !Main.dayTime);
public static bool NoBiome(NPCSpawnInfo spawnInfo) {
Player player = spawnInfo.player;
return !player.ZoneJungle && !player.ZoneDungeon && !player.ZoneCorrupt && !player.ZoneCrimson && !player.ZoneHoly && !player.ZoneSnow && !player.ZoneUndergroundDesert;
}
public static bool NoZoneAllowWater(NPCSpawnInfo spawnInfo) => !spawnInfo.sky && !spawnInfo.player.ZoneMeteor && !spawnInfo.spiderCave;
public static bool NoZone(NPCSpawnInfo spawnInfo) => NoZoneAllowWater(spawnInfo) && !spawnInfo.water;
public static bool NormalSpawn(NPCSpawnInfo spawnInfo) => !spawnInfo.playerInTown && NoInvasion(spawnInfo);
public static bool NoZoneNormalSpawn(NPCSpawnInfo spawnInfo) => NormalSpawn(spawnInfo) && NoZone(spawnInfo);
public static bool NoZoneNormalSpawnAllowWater(NPCSpawnInfo spawnInfo) => NormalSpawn(spawnInfo) && NoZoneAllowWater(spawnInfo);
public static bool NoBiomeNormalSpawn(NPCSpawnInfo spawnInfo) => NormalSpawn(spawnInfo) && NoBiome(spawnInfo) && NoZone(spawnInfo);
public override void HandlePacket(BinaryReader reader, int whoAmI) {
ExampleModMessageType msgType = (ExampleModMessageType)reader.ReadByte();
switch (msgType) {
// This message sent by the server to initialize the Volcano Tremor on clients
case ExampleModMessageType.SetTremorTime:
int tremorTime = reader.ReadInt32();
ExampleWorld world = ModContent.GetInstance<ExampleWorld>();
world.VolcanoTremorTime = tremorTime;
break;
// This message sent by the server to initialize the Volcano Rubble.
case ExampleModMessageType.VolcanicRubbleMultiplayerFix:
int numberProjectiles = reader.ReadInt32();
for (int i = 0; i < numberProjectiles; i++) {
int identity = reader.ReadInt32();
bool found = false;
for (int j = 0; j < 1000; j++) {
if (Main.projectile[j].owner == 255 && Main.projectile[j].identity == identity && Main.projectile[j].active) {
Main.projectile[j].hostile = true;
//Main.projectile[j].name = "Volcanic Rubble";
found = true;
break;
}
}
if (!found) {
Logger.Error("Error: Projectile not found");
}
}
break;
case ExampleModMessageType.PuritySpirit:
if (Main.npc[reader.ReadInt32()].modNPC is PuritySpirit spirit && spirit.npc.active) {
spirit.HandlePacket(reader);
}
break;
case ExampleModMessageType.HeroLives:
Player player = Main.player[reader.ReadInt32()];
int lives = reader.ReadInt32();
player.GetModPlayer<ExamplePlayer>().heroLives = lives;
if (lives > 0) {
NetworkText text;
if (lives == 1) {
text = NetworkText.FromKey("Mods.ExampleMod.LifeLeft", player.name);
}
else {
text = NetworkText.FromKey("Mods.ExampleMod.LivesLeft", player.name, lives);
}
NetMessage.BroadcastChatMessage(text, new Color(255, 25, 25));
}
break;
// This message syncs ExamplePlayer.exampleLifeFruits
case ExampleModMessageType.ExamplePlayerSyncPlayer:
byte playernumber = reader.ReadByte();
ExamplePlayer examplePlayer = Main.player[playernumber].GetModPlayer<ExamplePlayer>();
int exampleLifeFruits = reader.ReadInt32();
examplePlayer.exampleLifeFruits = exampleLifeFruits;
examplePlayer.nonStopParty = reader.ReadBoolean();
// SyncPlayer will be called automatically, so there is no need to forward this data to other clients.
break;
case ExampleModMessageType.NonStopPartyChanged:
playernumber = reader.ReadByte();
examplePlayer = Main.player[playernumber].GetModPlayer<ExamplePlayer>();
examplePlayer.nonStopParty = reader.ReadBoolean();
// Unlike SyncPlayer, here we have to relay/forward these changes to all other connected clients
if (Main.netMode == NetmodeID.Server) {
var packet = GetPacket();
packet.Write((byte)ExampleModMessageType.NonStopPartyChanged);
packet.Write(playernumber);
packet.Write(examplePlayer.nonStopParty);
packet.Send(-1, playernumber);
}
break;
case ExampleModMessageType.ExampleTeleportToStatue:
if (Main.npc[reader.ReadByte()].modNPC is NPCs.ExamplePerson person && person.npc.active) {
person.StatueTeleport();
}
break;
default:
Logger.WarnFormat("ExampleMod: Unknown Message type: {0}", msgType);
break;
}
}
}
internal enum ExampleModMessageType : byte
{
SetTremorTime,
VolcanicRubbleMultiplayerFix,
PuritySpirit,
HeroLives,
ExamplePlayerSyncPlayer,
NonStopPartyChanged,
ExampleTeleportToStatue
}
/*public static class ExampleModExtensions
{
public static int CountItem(this Player player, int type)
{
int count = 0;
for (int i = 0; i < 58; i++)
{
if (type == player.inventory[i].type && player.inventory[i].stack > 0)
{
count += player.inventory[i].stack;
}
}
return count;
}
}*/
}