Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
503d535
Fix incorrect calculation of reliable packet IDs under backpressure
yungcomputerchair Jun 19, 2026
f62d379
Revert "G9 Fix: Change `MaxOutstandingBytes` to match client UDP sock…
yungcomputerchair Jun 19, 2026
1bf3859
Trim comment
yungcomputerchair Jun 19, 2026
91d0a9a
Add consumables system: transformations, boomboxes, food effects
Sulphural Jun 28, 2026
1c2a466
Remove combat packets, strip non-consumables from ClientPcData
Sulphural Jun 28, 2026
b9a4dc0
Strip comments from consumables code and data
Sulphural Jun 28, 2026
49df8a7
Remove unreferenced packet files
Sulphural Jun 28, 2026
fd95bd8
PR27_review_fixes
Sulphural Jun 28, 2026
7af2add
Refactor cake detection to be data-driven via Consumables.json
Sulphural Jun 28, 2026
8e4b889
Use already-loaded assemblies for provider resolution instead of Asse…
Sulphural Jun 28, 2026
d515774
Merge branch 'pr-17' into pr-27
Sulphural Jun 28, 2026
67634d5
abilitypacket
Sulphural Jun 28, 2026
532508f
Address EDITzDev review: restore removed code comments in BaseZone an…
Sulphural Jun 29, 2026
4741cc1
Refactor consumables: move cooldown ticking to Player.UpdateEverySeco…
Sulphural Jun 29, 2026
9fb7788
Add transform composite effects and reorganize Consumables.jsonc
Sulphural Jun 29, 2026
0e16f90
Fix transform composite effect position
Sulphural Jun 29, 2026
d70ebf1
Make consumable handlers fully data-driven, remove all hardcoded IDs
Sulphural Jun 29, 2026
406c9c4
Boombox: synchronized looping dance
Sulphural Jul 7, 2026
b07962c
Fix DatabaseContext provider-assembly loading at startup
Sulphural Jul 7, 2026
d7d2d23
Fix Consumables.jsonc cake Type parsing (string enum converter)
Sulphural Jul 7, 2026
a8e3350
Boombox: fix dance mappings and make dances prompt + continuous
Sulphural Jul 8, 2026
6f237cf
Merge remote-tracking branch 'origin/main' into HEAD
Sulphural Jul 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions src/Resources/Consumables.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
{
"Boomboxes": [
{ "ItemId": 878, "ModelId": 1936, "EffectId": 16379, "DanceSequence": [3531, 3532, 3533, 3534] }, // Jingle Bell Boombox
{ "ItemId": 76641, "ModelId": 1936, "EffectId": 16379, "DanceSequence": [3531, 3532, 3533, 3534] }, // Jingle Bell Boombox
{ "ItemId": 1180, "ModelId": 1973, "EffectId": 0, "DanceSequence": [3501] }, // Realms Roll Boombox (freestyle_01 loop - avatar has no rroll, only freestyle_01)
{ "ItemId": 1913, "ModelId": 1979, "EffectId": 16452, "DanceSequence": [3568000, 3568007, 3568011] }, // Rainbow Randomizer Boombox (ballet/wackyscifi/8bit medley - rotates for variety)
{ "ItemId": 8185, "ModelId": 2217, "EffectId": 0, "DanceSequence": [3551] }, // Totem Boombox (totem_01 loop - avatar only has totem_01)
{ "ItemId": 35759, "ModelId": 4012, "EffectId": 17042, "DanceSequence": [3568004, 3569004, 3570004, 3571004] }, // Clockwork Boombox
{ "ItemId": 48244, "ModelId": 3893, "EffectId": 0, "DanceSequence": [3568005, 3569005, 3570005, 3571005] }, // Boombox XVD-S
{ "ItemId": 56774, "ModelId": 1661, "EffectId": 0, "DanceSequence": [3568012, 3569012, 3570012, 3571012] }, // Hip-Hop Boombox (shuffle dance)
{ "ItemId": 69825, "ModelId": 1062, "EffectId": 0, "DanceSequence": [3511, 3512, 3513, 3514] }, // Chiller Boombox (zombie - avatar lacks zombie_05)
{ "ItemId": 76927, "ModelId": 4076, "EffectId": 16452, "DanceSequence": [3568010, 3569010, 3570010, 3571010] }, // Pop-Dance Boombox
{ "ItemId": 76928, "ModelId": 2201, "EffectId": 16451, "DanceSequence": [3568006, 3569006, 3570006, 3571006] }, // Tiki Boombox
{ "ItemId": 76930, "ModelId": 4096, "EffectId": 16453, "DanceSequence": [3568001, 3569001, 3570001, 3571001] }, // Big Band Boombox
{ "ItemId": 76933, "ModelId": 4099, "EffectId": 0, "DanceSequence": [3568008, 3569008, 3570008, 3571008] }, // Hootenanny Boombox
{ "ItemId": 76935, "ModelId": 4428, "EffectId": 16928, "DanceSequence": [3568003, 3569003, 3570003, 3571003] }, // Realmshake Boombox (headbanger - full 4-clip shake)
{ "ItemId": 77329, "ModelId": 2059, "EffectId": 16466, "DanceSequence": [3560, 3561, 3562, 3563] }, // Lovelectric Boombox
{ "ItemId": 76934, "ModelId": 4100, "EffectId": 16827, "DanceSequence": [3568009, 3569009, 3570009, 3571009] } // Xylophone
],

"Cakes": [
// Scaredy Cake — spawns a cake NPC that plays a random scare effect on interact
{
"ItemId": 17597, "Type": "ScaredyCake", "CooldownMs": 60000,
"ModelId": 1724, "NameId": 16634, "SpawnPoofEffectId": 21, "LifetimeMs": 60000,
"ScareGroups": [
[5455, 5165, 15923], // Bats: EFX_bats_exp_flyaway, SFX_OS_BatChirps, PFX_halloween_icing_splat_cog
[15909, 15960, 15961], // Ghost: PFX_halloween_ghosts_up_loop, SFX_Halloween_GhostCrys, SFX_Halloween_GhostMoans
[15744, 5118, 15959] // Raven: EFX_birds_pink_med_flyaway, SFX_BirdCrowCaw, SFX_Halloween_Crows
]
}, // Scaredy Cake
{
"ItemId": 69819, "Type": "ScaredyCake", "CooldownMs": 60000,
"ModelId": 1724, "NameId": 16634, "SpawnPoofEffectId": 21, "LifetimeMs": 60000,
"ScareGroups": [
[5455, 5165, 15923],
[15909, 15960, 15961],
[15744, 5118, 15959]
]
}, // Scaredy Cake

// Boss Cake — spawns a cake NPC that randomly applies one of the TransformAbilityIds to the interacting player
{ "ItemId": 69825, "Type": "BossCake", "CooldownMs": 30000, "ModelId": 1724, "NameId": 16635, "SpawnPoofEffectId": 21, "LifetimeMs": 60000, "TransformAbilityIds": [4370, 4371, 4372, 4373] }, // Chiller Boombox
{ "ItemId": 69828, "Type": "BossCake", "CooldownMs": 30000, "ModelId": 1724, "NameId": 16635, "SpawnPoofEffectId": 21, "LifetimeMs": 60000, "TransformAbilityIds": [4370, 4371, 4372, 4373] }, // Jack-O-Lantern
{ "ItemId": 69839, "Type": "BossCake", "CooldownMs": 30000, "ModelId": 1724, "NameId": 16635, "SpawnPoofEffectId": 21, "LifetimeMs": 60000, "TransformAbilityIds": [4370, 4371, 4372, 4373] } // Bat Cupcake
],

"FoodEffects": [
{ "AbilityId": 1956, "CompositeEffectId": 47 }, // Electric Veal Meat
{ "AbilityId": 1957, "CompositeEffectId": 5032 }, // Rocking Road Mix
{ "AbilityId": 1958, "CompositeEffectId": 1 }, // Fiery Fury Candy
{ "AbilityId": 1960, "CompositeEffectId": 5 }, // Mr. Gleam's Granola Bar
{ "AbilityId": 1961, "CompositeEffectId": 5102 }, // Silver Surf 'n Turf
{ "AbilityId": 1962, "CompositeEffectId": 174 }, // Deep Mines Delight
{ "AbilityId": 1963, "CompositeEffectId": 1115 }, // Roaring Rapids Roast
{ "AbilityId": 1965, "CompositeEffectId": 5298 }, // Noxious Pork Chops
{ "AbilityId": 1966, "CompositeEffectId": 12 }, // Firefrog Legs
{ "AbilityId": 1967, "CompositeEffectId": 5370 }, // Hot Sauce Buns
{ "AbilityId": 1968, "CompositeEffectId": 5732 }, // Arbor Buttercake
{ "AbilityId": 1969, "CompositeEffectId": 1107 }, // Pixiedust Pie
{ "AbilityId": 1970, "CompositeEffectId": 630 }, // Frosty Flakes
{ "AbilityId": 1973, "CompositeEffectId": 15173 }, // Scrumptious Soup
{ "AbilityId": 2004, "CompositeEffectId": 4013 }, // Steaming Pretzels
{ "AbilityId": 1964, "CompositeEffectId": 5265 }, // Graveyard Flambe (shadow flames)
{ "AbilityId": 660, "CompositeEffectId": 5343, "QuickChatId": 3340, "EffectDelayMs": 300 } // Can of Beans
],

"Transformations": [
{ "AbilityId": 3042, "ModelId": 169, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3043, "ModelId": 170, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3044, "ModelId": 171, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3045, "ModelId": 172, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3046, "ModelId": 173, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3047, "ModelId": 364, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3056, "ModelId": 348, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3016, "ModelId": 50, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3011, "ModelId": 50, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3012, "ModelId": 50, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3013, "ModelId": 50, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3014, "ModelId": 50, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3015, "ModelId": 50, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 1990, "ModelId": 176, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 2031, "ModelId": 176, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 1998, "ModelId": 175, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3027, "ModelId": 175, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3028, "ModelId": 175, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3029, "ModelId": 175, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3030, "ModelId": 175, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3031, "ModelId": 175, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3032, "ModelId": 175, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3033, "ModelId": 175, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3034, "ModelId": 175, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3035, "ModelId": 175, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3036, "ModelId": 175, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3037, "ModelId": 175, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 1987, "ModelId": 28, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3005, "ModelId": 28, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3017, "ModelId": 28, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3018, "ModelId": 28, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3019, "ModelId": 28, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3020, "ModelId": 28, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3021, "ModelId": 28, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3022, "ModelId": 28, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3023, "ModelId": 28, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3024, "ModelId": 28, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3025, "ModelId": 28, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 1986, "ModelId": 77, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 1994, "ModelId": 77, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3060, "ModelId": 77, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3061, "ModelId": 77, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3062, "ModelId": 77, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3063, "ModelId": 77, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3064, "ModelId": 77, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3065, "ModelId": 77, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3066, "ModelId": 142, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3057, "ModelId": 142, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3067, "ModelId": 142, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3059, "ModelId": 52, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3071, "ModelId": 71, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3072, "ModelId": 71, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3058, "ModelId": 180, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3026, "ModelId": 65, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3001, "ModelId": 70, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3002, "ModelId": 160, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 1979, "ModelId": 136, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3051, "ModelId": 136, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 1983, "ModelId": 139, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3052, "ModelId": 138, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3053, "ModelId": 139, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3055, "ModelId": 188, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 1997, "ModelId": 130, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3038, "ModelId": 130, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 286, "ModelId": 22, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 3070, "ModelId": 177, "DurationMs": 10000, "CooldownMs": 10000, "CompositeEffectId": 21 },
{ "AbilityId": 4370, "ModelId": 2055, "DurationMs": 30000, "CooldownMs": 10000, "CompositeEffectId": 21 }, // Boss Cake - Stone Heart
{ "AbilityId": 4371, "ModelId": 1944, "DurationMs": 30000, "CooldownMs": 10000, "CompositeEffectId": 21 }, // Boss Cake - Abominable Snowman
{ "AbilityId": 4372, "ModelId": 1702, "DurationMs": 30000, "CooldownMs": 10000, "CompositeEffectId": 21 }, // Boss Cake - Pumpkin Prince
{ "AbilityId": 4373, "ModelId": 22, "DurationMs": 30000, "CooldownMs": 10000, "CompositeEffectId": 21 } // Boss Cake - Cakenstein
]
}
5 changes: 4 additions & 1 deletion src/Sanctuary.Database/DatabaseContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Reflection;

using Microsoft.EntityFrameworkCore;
Expand Down Expand Up @@ -32,6 +33,8 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
// optionsBuilder.EnableDetailedErrors();
// optionsBuilder.EnableSensitiveDataLogging();
#endif
optionsBuilder.ConfigureWarnings(warnings =>
warnings.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning));
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
Expand Down Expand Up @@ -62,7 +65,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
{
assembly = EF.IsDesignTime
? Assembly.Load(providerAssembly)
: Assembly.LoadFrom($"{providerAssembly}.dll");
: Assembly.LoadFrom(Path.Combine(AppContext.BaseDirectory, $"{providerAssembly}.dll"));
}
catch
{
Expand Down
9 changes: 3 additions & 6 deletions src/Sanctuary.Game/Entities/Npc.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Collections.Concurrent;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
Expand Down Expand Up @@ -45,6 +45,7 @@ public class Npc : IEntity
/// 2 - Ally
/// </summary>
public int Disposition { get; set; } = 1;
public System.Action<Player>? InteractAction { get; set; }
Comment thread
EDITzDev marked this conversation as resolved.

public int Animation { get; set; } = 1;

Expand Down Expand Up @@ -76,6 +77,7 @@ public Npc(IZone zone)

public void OnInteract(Player player)
{
InteractAction?.Invoke(player);
}

public virtual void OnAddVisibleNpcs(params IEnumerable<Npc> npcs)
Expand Down Expand Up @@ -207,7 +209,6 @@ public virtual PlayerUpdatePacketAddNpc GetAddNpcPacket()
Unknown36 = default, // AnimationEvent
TemporaryAppearance = default,

// playerUpdatePacketAddNpc.EffectTags = TODO

Unknown38 = default,
Unknown39 = default,
Expand All @@ -217,7 +218,6 @@ public virtual PlayerUpdatePacketAddNpc GetAddNpcPacket()

HasTilt = default,

// playerUpdatePacketAddNpc.Customization = TODO

Tilt = default,

Expand Down Expand Up @@ -247,9 +247,6 @@ public virtual PlayerUpdatePacketAddNpc GetAddNpcPacket()
Unknown57 = default,
Unknown58 = default,

// playerUpdatePacketAddNpc.Head = TODO
// playerUpdatePacketAddNpc.Hair = TODO
// playerUpdatePacketAddNpc.ModelCustomization = TODO

ReplaceTerrainObject = default,

Expand Down
Loading