Item::RemoveFromInventory ended with `delete this`, so every caller that still used the item afterwards (SetCount(0)
returning into its caller, loops that remove several items, proxies purged while their parent is handled) touched
freed memory. The inventory now takes the removed item and frees it at the inventory component's next update, or
with the inventory.
Check in game: sell, drop, delete, trade, mail and use up stacks (including the last of a stack); unequip and
remove an item set piece and a proxy-bearing item (rocket, modular car); donate items; nothing crashes and the
inventory shows the right counts after relogging.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client's TacArcBehavior::DoHit (0x00fb10c0) writes the closest max-targets ids from a set, so ascending and
each once, then the action data per id in that same order; DoUnserializeBS (0x00fb26a0) reads them the same way and
skips empty ids. The server handled the targets in list order and skipped ids whose object it could not find
without reading their action data, so every later target read the wrong bits (several pirates under a Doom Slicer).
Handle now reads the action for every listed id in ascending order, and the server's own casts write ids and actions
in ascending order after picking the closest targets.
TacArcBehavior::Cast (0x00fb2d10): a picked target that passes the filter gets the action with no TacArc data (the
server's own cast now calculates that action instead of handling it); otherwise the target is dropped, and an arc
measured from the target's position writes nothing.
Check in game: Doom Slicer and multi-target katanas on groups of pirates/admirals damage each of them; apes still
take damage during their stun; enemies with arc attacks (apes, Maelstrom horsemen) still hit players.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The live boss script's rain of fire takes every target of the first ROF target group and ROFImpactCnt (2) different
random targets of each other group. The server took one per group, so the rain was sparser than live. The impacts are
now picked like the live script (without repeats inside a group).
Check in game: AG Spider Queen stage 3; the rain of fire lands on the centre ring and on two spots in each outer ring.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The live boss script tracks which arena zone volume (Zone1Vol..Zone8Vol, TeleVol for the default Zone3Vol) each
player last entered, and its rapid fire picks a random player, takes the three RFS target groups around that zone,
sorts each by the targets' CWOrder (CWOrder2 when the sweep crosses between zones 8 and 1), clockwise or
counter-clockwise at random, drops the first and last target of the middle group (shared with its neighbours),
turns with skill 1480 at the fourth target and fires 1394 at every target in turn, playing attack-shoot-right or
attack-shoot-left. The server shot a single random target with the single-shot animation, and the AG property zone
never subscribed the boss to the zone volumes. The zone now registers the volumes (retrying until they are spawned)
and the boss builds the sweep like the live script.
Check in game: AG Spider Queen stage 2; the rapid fire is an arc of many shots sweeping across the arena near the
player, left or right, and follows the player to other parts of the arena; after teleporting it starts from the
default side.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every hit started both the rapid fire shooter and the rain of fire timers, from stage 1 on, so the two specials
ran in every stage and on top of each other, each turning the boss's AI off and on again under the other's
animations. They also wrote the "stoppedFlag" that the no-players-around attack stop uses, which could leave her
stopped for good. As the live script: after she comes back down, a skill manager fires the rapid fire shooter in
stage 2 and the rain of fire in stage 3 only, again 10 to 15 s after each ends; for 3.1 s after her melee smash
(skill 322) a due special waits and fires when the smash ends. The rain of fire keeps her from attacking until
its last impact.
Check in game: Spider Queen fight: no specials before the first spiderling wave; stage 2 only rapid fire, stage 3
only rain of fire; she never freezes in the smash animation and keeps attacking after each special.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The zone script never handed the boss her landing target and scream emitter: ZoneAgProperty::ProcessGroupObjects
was empty and nothing answered the boss client script's "QueryZoneScript" event. As the live scripts: the boss asks
the zone ("RetrieveZoneData"), the zone stores the first object of Land_Target and Spider_Scream on her as
LandingTarget and ScreamEmitter (looking again every 0.3 s until they are spawned), and each spiderling death sends
NotifyClientObject "EmitScream" with the emitter, which the boss's client script plays as the scream. The landing
skill and camera shake now come from the landing target, not the boss.
Check in game: AG Spider Queen (property or instance): kill a spiderling: the scream plays from the mountain; when
she comes back down, the landing blast hits around the landing spot and the camera shakes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client's AreaOfEffectBehavior::Cast (0x004ec590) writes every target id, then runs and writes the action once
per unique target in ascending id order. The server ran it for every listed target in list order, so a target
listed twice (the caster with a magnet, Everlasting items' refill) was handled twice, and later targets' data was
read against the wrong target. Handle and the server's own Calculate now go through the unique ids in ascending
order. Check: Thumpin' Bass / Flowin' MC refill once; Shinobi charge with a magnet gives imagination once; area
attacks on several enemies still hit each of them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WBL_Frog_Bridge had no script. As the live script: when a player who finished mission 946 comes within 25 units,
the four FrogBridge pieces move out to form the bridge; after 10 seconds they go back one a second from the tip,
and 7 seconds later the frog looks for players again. Check: in Portabello, with mission 946 done, walk up to the
frog: the tongue bridge comes out and goes back; without the mission it doesn't. Depends on moving platforms
following their paths.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WBL_Starflies had no script. As the live script: when the mission given at that object is handed in, every object of
the Starflies group starts its path. Check: in Portabello, hand in the starflies mission: the starflies fly off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
L_ACT_HORSEMEN_1 (one path spawner of Maelstrom Cavalry, LOT 7816, in Forbidden Valley) had no script. It does what
L_FV_MAELSTROM_CAVALRY does (tell the group's turret it spawned, report a Brick Fury kill to the horsemen trigger),
so it uses that port. Check: the cavalry on that path wakes the ninja turret and Brick Fury kills count.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
L_TURRET (LOTs 12286 and 13093, e.g. the Crux Prime auto turrets) had no script, so these turrets could be stunned,
knocked back and pulled. They now push the same immunities the live script does. Check: stun and knock back a
Crux Prime auto turret: nothing happens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The turret (LOT 9303-9305, 12618) had no script: it fought as soon as it was spawned and lasted the generic 60
seconds. As TURRET.lua: its combat AI is off until it is built, it can't be stunned, interrupted, knocked back or
pulled, and it dies 30 seconds after it was placed once nobody is building it. Kill credit going to the builder is
not done. Check: summon the turret with Engineer gear: it doesn't shoot until built, then fights and goes away
after about 30 seconds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Master stops with a message naming client_location, its value and the folder it resolves to when that folder
doesn't exist, instead of failing later on missing client files; a missing dump_folder gets a warning. Check: set
client_location to a folder that doesn't exist: master says so and stops.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The WSL steps used software-properties-common, lsb-release and the deprecated apt-key. They now follow Kitware's
current instructions: the key as a keyring referenced with signed-by, the release codename from /etc/os-release.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RepositionPlayer added the offsets in world X/Z, set the height to 0 and built the keyboard turn around the position
vector. As l_ns_concert_instrument_qb.lua: the offset is in the instrument's own frame, the instrument's height is
kept, the player faces the instrument's way, and at the keyboard turns -0.8 further. Check: build and play all four
instruments on the Nimbus Station stage: the player stands at each one facing it, not below the stage.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When staff approve or reject a character name while the player is online, the player gets a private announcement
popup ("Name approved" / "Name not approved") as well as the chat line. Players who are offline still aren't told
at their next login. Check: approve and reject a pending name while its player is online.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The port turned the in-use flag off on a second use instead of refusing it, so a torch could be used again at
once; it now stays in use until the interact animation is done, as L_GF_TORCH does, and the cooldown is that
animation's length (4 seconds when it has none) instead of a flat 4. Check: use a tiki torch twice quickly: the
second use does nothing until the torch is done.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
L_ACT_CANDLE also blows a candle out on the "waterspray" skill event; the port only did it on a hit. The 169
candles in Forbidden Valley now go out (and count for missions 850, 1431, 1529, 1566 and 1603) when sprayed. Check:
spray a candle with the water gun: it goes out, smokes, relights after 5 seconds, and the candle missions count it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Venture vision from an equipped item was sent while the character loaded, before the client's UI existed, so after a
login the minimap stayed empty until the item was equipped again (a world transfer keeps the UI, so it worked
there). The player's active venture vision effects are sent again once the player has loaded. Check: wear the
Venture Vision helmet, log out and back in: the minimap shows the icons.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
L_BASE_ENEMY_SPIDERLING was excluded, so the 7 spiderlings in Avant Gardens and 3 in Venture Explorer had none of the
immunities its onStartup pushes: stun immunity to attack, interrupt, move, turn, use item, equip and interact, and
status immunity to pull-to-point and knockback. BaseEnemySpiderling does the same. Check: stun and knock back a
spiderling: nothing happens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dashboard snapshot counts guilds whose name waits for moderation; the moderation counts (WebSocket and
/api/moderation/counts) include it, the Review Queue badge adds it for staff with guilds_manage, and the Guilds menu
entry has its own badge. Check: create a guild with a name off the allow list: both badges go up; approve it: they
go down.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The table named commits by their hashes from before earlier rebases; each is now the current hash of the commit
with the same subject.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The zone file tests' sample zones now use generic world IDs, file names
and path names instead of ones taken from real files. What they test is
unchanged.
Check in game: nothing (test only).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LDF_FROM_STRING (1.10.64, 0x010fa220) splits an object's config at
every comma and line break, leaves out empty entries and entries with
no '=', and sets each key=value it finds. The reader split at line
breaks only and parsed every piece, so an object with no config got an
empty entry of unknown type, and a value with a comma would have stayed
whole.
Live scene files are unchanged; objects in older client files with an
empty config no longer get the empty entry.
Check in game: every world spawns its objects as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ClientZoneFilesTests now
- reads sd0 files instead of skipping them: some clients' loose sd0
files hold what followed the file in its pack, and the file is the
first sd0 stream, up to the first chunk that does not fit;
- reads the terrain (.raw) of every zone of version 30 or newer, once
per terrain file shipped by several clients;
- skips a damaged file (by the MD5 of its bytes) only when it does not
read, so only the files the client cannot read either are left out:
a scene file that claims 81 objects and ends after the first, and six
version 30 terrain files with a width x width scene map per chunk
where the client reads one byte (RAWReadSceneMap, 1.10.64) and so
misreads every chunk after the first.
Every other zone, scene and terrain file of every client on disk reads,
in about 80 seconds.
Check in game: nothing (test only).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The heights of a terrain chunk (millions of floats in the big zones)
and the mesh index lists were read one value at a time; they are now
read in one read each, as the client does (ReadBytesTimes4 /
RAWReadU16 with a count). The values are the same.
Check in game: nothing (terrain reads the same, only faster).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For a zone file older than version 30 the client reads the terrain in
an older format (OpenTerrain, 1.10.64: mapVersion < 30), and the game
client never reads that format's scene map (only the editor does), so
scene loading goes by the zone's scenes alone. The world now does the
same instead of reading the terrain file as the newer format.
Check in game: nothing to check (all live zones are version 36+).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client reads a terrain chunk of a file older than version 32
(1.10.64):
- its color map as width x width BGRA pixels, keeping the
(width - 1) x (width - 1) before the last row and column, as RGBA
(RAWReadColorandLightMaps); the reader kept all the pixels as
written;
- its texture blend map's pixels as BGRA, kept as RGBA (0x0103aaf0);
- before version 31 no scene map, only a byte: the client's scene map is
all scene 0 (RAWReadSceneMap); the reader had none.
A chunk of width or height 0, which the client reads (no heights, no
color map), no longer fails the whole file.
Live terrain files are version 32, so they read as before.
Check in game: nothing to check (no live terrain changes).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ReadLvlObjectData (1.10.64, 0x0103ba20) loads every scene object except
those of LOT 1 (the player). The world spawned them; now it skips them.
No live scene has one; one older client scene file has two.
Check in game: nothing to check (no live scene has LOT 1 objects).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ReadLvlObjectData (1.10.64, 0x0103ba20) changes an object's config as it
loads it:
- before version 47 a respawn time is in milliseconds: a float over 100
or any u32 becomes that many seconds, as a float;
- a model spawner (LOT 176 with spawntemplate 14) gets its subkey as its
blueprintid unless it has one, DisableModelBehaviors true unless set,
and preventRenderWrapping true;
- before version 44 an object with sceneIDOverrideEnabled gets
sceneLayerIDOverride 0.
LevelFile now makes the same changes. On live worlds 1906 spawners in
114 scene files of versions 45-46 have a u32 respawn time; the world
already divided those by 1000 but as an integer, so they now keep their
fraction (a 2500 ms respawn is 2.5 s, not 2 s). No live scene has a
model spawner; 62 objects of version 43 scenes get sceneLayerIDOverride.
Check in game: enemies and smashables respawn after the same time as
before on Forbidden Valley, Avant Gardens and Gnarled Forest.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SceneLoader::ReadLvlFile (1.10.64):
- chunked files: the file info chunk starts the file and gives where
the environment, object and particle chunks start, 0 for none
(DoLvlChunk, ReadLvlChunk1000); the reader walked the chunks one after
another by their sizes instead;
- older files: the editor settings are a u32 size and that many bytes
(SceneLoader::ReadEditorSettings, skipped by size); the reader took
them for a u32 and then a count of 12 byte points;
- older files before version 3 have no objects for the client ("Level
file is unsupported").
LevelFile now does the same. Every scene file on disk reads the same
objects as before.
Check in game: every world spawns its objects as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ReadLvlObjectData (1.10.64, 0x0103ba20) reads a scene object's last
field, from version 7 on, as a render technique count, and when it is
not 0 a 64 byte block and 133 bytes per technique (a 64 byte name, a
u32, a u8 and 16 floats) follow. The reader took the count for an
unknown u32 and read the next object from inside the techniques, and
read the u32 for every version. It also left the node type and glom ID
uninitialized when a file has none (or, for the node type, one outside
0-10); the client uses 1 for both.
The count is now SceneObject::renderTechniqueCount, the techniques are
skipped, and the defaults are the client's. Every scene file on disk
has no render techniques, so what they read is unchanged.
Check in game: every world spawns its objects as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LevelPath::FromBuffer (1.10.64) changes two values as it reads a path:
- a property path's reputation multiplier outside 0-1000 becomes 0;
- a camera waypoint's FOV in a path older than version 11 is divided
by 1.25.
The reader kept both as written. Only camera paths of older client
files (versions 8-10) change; no live zone has either case.
Check in game: nothing to check (no live zone changes).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client reads a spawner path's fields (LOT, respawn time, counts,
object ID) and a camera path's next path and rotate-player flag only
when the path is newer than version 3 (LevelPath::FromBuffer, 1.10.64:
pathVersion > 3). The reader read them for every version, which would
misread an older spawner or camera path from there on.
No zone file on disk has a spawner or camera path older than version 4,
so what they read is unchanged.
Check in game: nothing to check (no live zone changes).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client reads a zone file's paths as a chunk of its own
(LuzFile::ReadLUZFile copies the u32-sized chunk, ReadLUZPaths reads
it, 1.10.64) and drops every path when it refuses any of it:
- a chunk version of 2 or more, or 10000 paths or more (ReadLUZPaths);
- a path of version 19 or more, 10000 waypoints or more, or a
waypoint with more than 99 name/value pairs (LevelPath::FromBuffer).
The reader read the paths straight from the file, trusting the chunk to
be well formed. It now reads the chunk by its size, reads the paths
from it with the client's limits, and when one is refused the zone has
no paths (logged), while the rest of the zone file still reads.
Every zone file on disk reads the same paths as before.
The unit tests' sample zones now give the path chunk its real length.
Check in game: moving platforms, NPC patrols and spawners work as
before on every world.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Scene transitions of versions 34-38 have five points. The client links
the scenes of the first and the last one (ZoneLoader::ReadZoneFile,
1.10.64: points[0] and points[count - 1], for every version); the scene
graph used the first two, so in those zones it linked a scene with the
one of the transition's second point instead.
Of the 130 five-point transitions on disk, six (all in one older client
zone) have a different scene at the second and last points; the rest
link the same scenes as before.
Live zones are unchanged.
Check in game: nothing to check (no live zone changes).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LuzReader::ReadScenes (1.10.64) keeps a zone's scenes by scene ID and
layer, so a later scene with the same ID and layer replaces an earlier
one. ZoneLoader::ReadZoneFile then goes through them in scene ID order
and
- leaves out every layer of a scene ID that has no layer 0 (General)
scene;
- before version 41 loads only the layer 0 scene of each ID; from 41 on
the other layers (Audio, FX) are loaded with it.
ZoneFile now keeps the scenes the same way, so the world loads the same
scenes as the client. No zone file on disk has a duplicate, a scene
without a General layer, or a non-General layer before version 41, and
their scenes are already in ID order: what every file reads is
unchanged.
Check in game: nothing to check (no live zone changes).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Zone files before version 33 have no scene IDs. The client gives each
scene its index in the file as its ID, -1 past 255, with layer 0
(LuzReader::ReadScenes, 1.10.64, via the LWOSceneID from the loop
index); the reader left every such scene at ID 0, so a zone with several
scenes had them all collide on one ID.
The version 30 zones on disk have one scene each, so what they read is
unchanged.
Check in game: nothing to check on live worlds (all are version 36+).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Zone files older than version 30 were refused. The client reads them
(1.10.64):
- LuzFile::ReadLUZFile reads a version below 20 as 20;
- LuzReader::ReadScenes reads a scene of a version 20-29 file as a bare
u32 SceneTable ID, with no file name;
- ZoneLoader::ReadZoneFile takes those scenes in SceneTable ID order,
looks each one up in the CDClient SceneTable and, when there is a
row, makes it a scene with the next index (0, 1, ...; -1 past 255) as
its ID and the row's sceneName as its file; a scene with no row is
left out.
ZoneFile now reads these files (FileFormatVersion::Oldest = 20 for
anything older), keeps the SceneTable ID on ZoneScene::sceneTableID,
and ZoneFile::ResolveSceneTable does the lookup the client does; the
world looks the names up in the CDClient SceneTable.
The only such file on disk (version 20) now reads.
Check in game: nothing to check on live worlds (all are version 36+).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>