You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This preserved product issue is now part of the fresh MIT replacement program. Final implementation owner: atrinik/client. Legacy C/SDL2, packet, global-state, and file-path details below are historical evidence only.
Replacement implementation contract
Preserve the charge presentation and update behavior. Add typed authoritative charge/capacity/revision fields in Game Protocol 1 and render inventory/quickslot overlays through shared renderer resources; remove classic packet-version and C-struct assumptions.
New implementation and tests are independent MIT work unless an exact contribution by an approved MIT provenance grantor is admitted through the recorded file-level MIT grant. Do not copy or mechanically translate other GPL source/tests. Preserve every player-facing, accessibility, disclosure, and performance design decision below.
Required verification
Add deterministic Rust and/or Go tests at the owning boundary plus cross-language protocol fixtures for new fields.
Exercise malformed/stale/reordered inputs and lifecycle failure without partial state.
Use the shared renderer rather than a client/editor fork.
Validate through a wrapper-managed replacement scenario where the feature is interactive.
Preserved product/design specification and historical implementation notes
Summary
Show the charge level of carried mana crystals directly on their inventory and quickslot icons so players do not have to examine a crystal after every use.
Render a compact mana-blue charge meter over the existing icon. The meter should have an obvious empty track, fill continuously from 0% to 100%, and update immediately when mana enters or leaves the crystal. This should reuse the existing crystal artwork and animation; no new faces are required.
Existing behavior and implementation seams
Power crystals are object type POWER_CRYSTAL / 156.
server/src/types/power_crystal.c stores current charge in object::stats.sp and capacity in object::stats.maxsp, then changes both when the crystal is applied.
server/src/types/player.c:examine() is currently the only player-facing charge presentation. It derives descriptions such as empty, half full, and fully charged from the same fields.
The ordinary item packets in server/src/socket/item.c:add_object_to_packet() send the object type but do not send crystal charge or capacity. UPD_EXTRA already carries type-specific payloads for spells, skills, forces, and poisoning, making it the natural extension point for power-crystal metadata.
The client-side object in client/src/include/item.h consequently has no crystal charge state.
client/src/gui/widgets/inventory.c:object_show_inventory() draws both inventory icons and the objects referenced by client/src/gui/widgets/quickslots.c, so one overlay implementation covers both presentations.
The four existing power_crystal faces are a looping glow animation. Animation phase is not charge state and should remain independent of the new meter.
Proposed implementation
Protocol and authoritative state
Extend the legacy server-to-client item payload for POWER_CRYSTAL objects under UPD_EXTRA:
For a POWER_CRYSTAL record with UPD_EXTRA, append required current_charge and capacity fields in that order as network-byte-order uint16 values. Both values must be in the server fields' non-negative int16 range and current_charge <= capacity before serialization.
Decode them into explicit charge/capacity fields on the client item object.
Reject a truncated type-specific payload and defensively clamp the visual fraction to [0, capacity]. A zero-capacity crystal renders as empty and must never divide by zero.
Include this type-specific extra data in the initial player-inventory item record as well as later updates.
Bump the strict legacy SOCKET_VERSION and document the new field order and widths in doc/ADS/ADS-2; client and server already reject mismatched versions.
The server remains authoritative. Do not infer crystal charge from player mana changes, animation frames, item names, or the number of times the quickslot was used.
Update lifecycle
In server/src/types/power_crystal.c, send UPD_EXTRA after an application actually changes stats.sp.
Audit every in-repository mutation path. In particular, the Old Outpost enhancement script creates the upgraded Gandyld crystal and copies old.sp into new.sp; its resulting item record must contain the preserved charge.
Ensure an inventory replay, reconnect, move between inventory/container, and quickslot reference all resolve to the same current charge.
Do not send redundant updates when applying a full crystal while the player is full or an empty crystal while the player lacks mana.
Icon presentation
In object_show_inventory():
Draw the meter only for TYPE_POWER_CRYSTAL.
Use a slim, high-contrast dark track with mana-blue fill near the bottom edge of the 32x32 icon.
Reserve clear states for 0% and 100%; non-zero charge should always produce at least one visible fill pixel after rounding.
Keep the meter legible with the animated face and existing magical, locked, cursed, applied, selection, and quickslot-key overlays. Adjust draw order or inset placement rather than hiding status markers.
Use the same rendering in the main inventory and quickslots. The below/ground-item view does not need charge metadata or a meter unless its item records are deliberately extended too.
Preserve examination text as the detailed capacity/description view; the icon is an at-a-glance indicator, not a replacement for it.
Alternatives considered
Select a face or animation frame on the server: the existing frames depict a looping glow, not increasing fullness. Reusing them as charge buckets would make animation phase and state ambiguous, while new face sets would multiply content and attribution work.
Append charge to the item name: this adds visual churn, consumes inventory text space, and does not help the icon-only quickslot view.
Client-side prediction: applying a crystal does not tell the client enough to reconstruct the transfer reliably, and server or script changes would desynchronize it.
Tests and validation
Add packet encode/decode coverage for empty, partial, full, and zero-capacity crystals, plus malformed/truncated payload handling.
Verify initial inventory synchronization and immediate ITEM_UPDATE synchronization after charging and draining.
Verify no update is emitted when charge does not change.
Verify Gandyld crystal upgrades preserve both authoritative charge and the displayed fraction when capacity changes from 50 to 100 to 200.
Verify reconnect/inventory replay and moving a crystal through a container retain the correct display.
Visually inspect 0%, a small non-zero charge, about 50%, almost full, and 100% in both inventory and quickslots, including identified magical and inventory-locked crystals.
Build both legacy client and server, run focused server/protocol tests, and update ADS-2 in the implementation change.
Acceptance criteria
Every carried mana crystal shows an at-a-glance charge meter in inventory and quickslots.
The meter updates immediately after mana is transferred in either direction.
Empty, partial, and full states are visually distinct without examining the item.
The display is derived only from server-authoritative current charge and capacity.
Existing crystal animation and item-status overlays remain readable.
Zero-capacity and malformed values cannot cause division by zero, overflow, or out-of-bounds rendering.
The protocol version and ADS-2 specification are updated with automated packet coverage.
Shared item-adornment ownership
Implement the charge meter through the reusable item-adornment/layout helper shared with atrinik/server#6. The helper owns reserved icon regions, clipping, scale-aware geometry, and deterministic composition for charge bars, rarity frames, quantities, selection, applied/unpaid/locked state, magical/cursed/damned/trapped markers, container-chain cues, cooldowns, inventory, and quickslots. atrinik/server#29 consumes the same typed presentation metadata for comparison/tooltips.
Important
This preserved product issue is now part of the fresh MIT replacement program. Final implementation owner:
atrinik/client. Legacy C/SDL2, packet, global-state, and file-path details below are historical evidence only.Replacement implementation contract
Preserve the charge presentation and update behavior. Add typed authoritative charge/capacity/revision fields in Game Protocol 1 and render inventory/quickslot overlays through shared renderer resources; remove classic packet-version and C-struct assumptions.
New implementation and tests are independent MIT work unless an exact contribution by an approved MIT provenance grantor is admitted through the recorded file-level MIT grant. Do not copy or mechanically translate other GPL source/tests. Preserve every player-facing, accessibility, disclosure, and performance design decision below.
Required verification
Preserved product/design specification and historical implementation notes
Summary
Show the charge level of carried mana crystals directly on their inventory and quickslot icons so players do not have to examine a crystal after every use.
Render a compact mana-blue charge meter over the existing icon. The meter should have an obvious empty track, fill continuously from 0% to 100%, and update immediately when mana enters or leaves the crystal. This should reuse the existing crystal artwork and animation; no new faces are required.
Existing behavior and implementation seams
POWER_CRYSTAL/ 156.server/src/types/power_crystal.cstores current charge inobject::stats.spand capacity inobject::stats.maxsp, then changes both when the crystal is applied.server/src/types/player.c:examine()is currently the only player-facing charge presentation. It derives descriptions such as empty, half full, and fully charged from the same fields.server/src/socket/item.c:add_object_to_packet()send the object type but do not send crystal charge or capacity.UPD_EXTRAalready carries type-specific payloads for spells, skills, forces, and poisoning, making it the natural extension point for power-crystal metadata.objectinclient/src/include/item.hconsequently has no crystal charge state.client/src/gui/widgets/inventory.c:object_show_inventory()draws both inventory icons and the objects referenced byclient/src/gui/widgets/quickslots.c, so one overlay implementation covers both presentations.power_crystalfaces are a looping glow animation. Animation phase is not charge state and should remain independent of the new meter.Proposed implementation
Protocol and authoritative state
Extend the legacy server-to-client item payload for
POWER_CRYSTALobjects underUPD_EXTRA:POWER_CRYSTALrecord withUPD_EXTRA, append requiredcurrent_chargeandcapacityfields in that order as network-byte-orderuint16values. Both values must be in the server fields' non-negativeint16range andcurrent_charge <= capacitybefore serialization.[0, capacity]. A zero-capacity crystal renders as empty and must never divide by zero.SOCKET_VERSIONand document the new field order and widths indoc/ADS/ADS-2; client and server already reject mismatched versions.The server remains authoritative. Do not infer crystal charge from player mana changes, animation frames, item names, or the number of times the quickslot was used.
Update lifecycle
server/src/types/power_crystal.c, sendUPD_EXTRAafter an application actually changesstats.sp.old.spintonew.sp; its resulting item record must contain the preserved charge.Icon presentation
In
object_show_inventory():TYPE_POWER_CRYSTAL.Alternatives considered
Tests and validation
ITEM_UPDATEsynchronization after charging and draining.Acceptance criteria
Shared item-adornment ownership
Implement the charge meter through the reusable item-adornment/layout helper shared with atrinik/server#6. The helper owns reserved icon regions, clipping, scale-aware geometry, and deterministic composition for charge bars, rarity frames, quantities, selection, applied/unpaid/locked state, magical/cursed/damned/trapped markers, container-chain cues, cooldowns, inventory, and quickslots. atrinik/server#29 consumes the same typed presentation metadata for comparison/tooltips.
Protocol-epoch coordination
Coordinate this wire change through atrinik/atrinik#168's next classic protocol epoch with atrinik/server#26, atrinik/server#25, atrinik/atrinik#156, #13, #9, #7, and atrinik/server#6 where practical. Do not reserve an isolated numeric version in advance. Land atrinik/atrinik#190's bounded packet primitives first where this payload uses them, then update all current producers, consumers, bots, fixtures, tests, and ADS-2 together.