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
{{ message }}
Repository navigation
Show monster XP suitability for a selected combat skill #9
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 distinct danger-versus-character and XP-suitability-versus-selected-skill cues. Go computes authorized typed classifications, Game Protocol 1 transports them, and Rust maps them to accessible presentation without client-side XP-rule reconstruction.
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 monster danger and skill-XP suitability as two distinct signals. Keep the existing character-relative monster color as the danger cue, and add a server-authoritative XP band for a selected combat skill so a high-level character can identify useful enemies for a lower-level secondary skill.
Problem
Visible living names and the target widget currently use get_living_level_color() in server/src/socket/request.c. That helper compares op->level with level_color[pl->level], so gray/green/blue/yellow/orange/red/purple always describe the monster relative to the character's overall level.
Actual skill XP follows a different comparison:
calc_skill_exp() in server/src/server/skill_util.c calls calc_level_difference(skill_level, monster_level) and returns zero for targets below the selected skill's gray cutoff or with no base XP.
award_kill_exp() in server/src/server/attack.c calculates a full award separately for every participating damage skill, then weights it by that skill's share of total damage before party sharing.
A monster can therefore appear gray to a high-level character while being yellow, orange, or otherwise valuable for a low-level secondary skill. The UI currently gives the player no way to discover that before fighting it.
The target packet already carries the monster level, skill objects already carry their levels and XP to the client, and visible living objects already carry a character-relative name color. However, the 201-entry authoritative level_color table and calc_skill_exp() live only on the server. Reimplementing those rules in the client would create a second gameplay authority and would still miss zero-base-XP monsters and server-side reward changes.
Proposed player experience
Preserve danger; add XP suitability
Do not replace the existing name/target color with a skill-relative color. A level-30 monster must not look harmless merely because it is being compared with a level-5 secondary skill.
Keep the monster name color as Danger vs character level.
Add a compact, separately labeled XP for <skill> band beside visible monster names and in the target widget.
In the target widget, show the selected skill name and level, the named band, and the potential full-credit skill XP, for example: XP for Wizardry 6: yellow — up to 1,120.
Describe that number as potential/full-credit XP: the actual award can be lower when damage is split across skills or party members. Do not present it as a guaranteed kill reward.
Hide the XP badge/line for players, friendly NPCs, monsters with no base XP, unavailable skills, or any target for which calc_skill_exp() returns zero. A zero-XP hostile may instead use an explicit No XP state where that is clearer.
Pair colors with text, symbols, or shapes (No XP, Low, Good, Bonus, or the named band) so this feature does not depend on color perception. The target widget must always spell out the meaning.
Allow the map badge to be hidden in settings if it creates too much annotation clutter; the target widget remains the detailed source.
Select the comparison skill
Default to the skill that the current attack source would credit: equipped melee weapon/unarmed skill, launcher skill, directly cast spell's skill, thrown skill, or fired skill object.
Provide a small selector/cycler in the target or skills UI to compare any learned XP-bearing combat skill. Allow the player to pin that choice for the session; provide a clear Auto choice to return to attack-source tracking.
Resolve Auto through the same server-side source/skill rules used by damage and XP credit, rather than trusting a possibly stale chosen_skill pointer or reproducing weapon/spell rules in the client.
Centralize the duplicated level-band classification in one server helper that accepts viewer/skill level and target level and returns a bounded enum such as:
get_living_level_color(), target serialization, and the XP comparison feature should use that helper. The client may map the enum to presentation colors and localized labels, but it must not own the threshold table or XP formula.
Suggested classic protocol changes:
Add session comparison state on the server: Auto or a validated learned skill ID.
Add a bounded client-to-server command/field for changing that state. Reject unknown, non-learned, and non-XP-bearing skill IDs without changing the current selection.
Extend living-name MAP2 data with a one-byte skill-XP band. Keep the existing character-danger presentation separate.
Extend CLIENT_CMD_TARGET with explicit danger band, comparison skill identity/level, XP band, and a bounded potential full-credit XP value. The existing target name, target level, relationship, and combat state remain independently meaningful.
Bump SOCKET_VERSION and update the current client/server contract coherently; no permanent compatibility path is needed.
Update common/toolkit/map_protocol.c to validate new enum values and truncation before applying any map state.
Update doc/ADS/ADS-2, the C client parser/render state, and tools/atrinik_bot/ constants, map model/parser, target model/parser, and tests in the same change.
The server map cache must include the computed danger and XP bands (or an equivalent comparison-generation key). Changing the selected skill, leveling that skill, changing attack source while in Auto, or changing character level must force prompt deltas for unchanged visible monsters and refresh the current target. Today the map cache equality test does not include the non-player name color, so merely changing viewer level is not sufficient to repaint an otherwise unchanged monster; this proposal should fix that invalidation rather than requiring movement or a full map clear.
Acceptance criteria
A monster that is gray relative to overall character level but yellow relative to a selected lower-level skill displays both facts simultaneously and unambiguously.
The original character-relative danger signal remains visible and is not relabeled as XP suitability.
Auto selects the skill that the current attack source would credit; a learned combat skill can also be pinned explicitly and restored to Auto.
Switching/pinning a skill, changing attack source in Auto, or gaining a skill level refreshes the current target and all visible monster XP indicators without requiring the monster or player to move.
Character level changes likewise refresh visible danger indicators for otherwise unchanged monsters.
The target widget shows the selected skill and level, a named/color-independent XP band, and potential full-credit XP with a clear damage/party-sharing caveat.
Zero-base-XP and zero-calculated-XP targets never appear profitable; non-monster living objects do not receive misleading XP badges.
Band boundaries match the server's level_color/calc_level_difference() rules at gray/green/blue/yellow/orange/red/purple transitions; the client contains no duplicate 201-entry threshold table.
Unknown enum values, invalid skill IDs, truncated MAP/TARGET payloads, and trailing malformed fields are rejected without partially changing comparison or render state.
SOCKET_VERSION, shared constants/validator, server producers and map cache, C client parsers/UI, Python bot consumers, tests, and ADS-2 are updated together.
Focused server tests cover band boundaries, no-XP targets, selection validation, potential XP, and cache invalidation; parser tests cover every new field and malformed payload; both legacy C targets and the relevant bot tests pass.
Live validation covers at least one high-character/low-skill comparison, an Auto attack-source change, a pinned-skill change, and a skill level-up while monsters remain visible.
Out of scope
Rebalancing level_color, calc_level_difference(), XP caps, party sharing, or damage-participation awards.
Replacing character-relative danger with skill-relative danger.
Predicting the exact final award before the fight's per-skill damage shares and party shares are known.
server/src/server/attack.c: per-skill damage participation and kill-XP weighting
client/src/client/commands.c: MAP and TARGET parsing
client/src/gui/widgets/map.c and client/src/gui/widgets/target.c: visible-name and target rendering
common/toolkit/socket.h and common/toolkit/map_protocol.c: legacy packet contract and validation
tools/atrinik_bot/client.py and tools/atrinik_bot/model.py: current non-C protocol consumer
This proposal turns the playtest observation in GAMEPLAY_PLAYTEST_NOTES.md into a concrete server-authoritative UI and protocol change.
Shared semantic presentation
#6 owns the shared semantic-color tokens, contrast requirements, and mandatory non-color cues. This issue owns danger/XP gameplay categories and server authority; it must send categorical values rather than theme colors and consume the shared client presentation contract.
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 distinct danger-versus-character and XP-suitability-versus-selected-skill cues. Go computes authorized typed classifications, Game Protocol 1 transports them, and Rust maps them to accessible presentation without client-side XP-rule reconstruction.
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 monster danger and skill-XP suitability as two distinct signals. Keep the existing character-relative monster color as the danger cue, and add a server-authoritative XP band for a selected combat skill so a high-level character can identify useful enemies for a lower-level secondary skill.
Problem
Visible living names and the target widget currently use
get_living_level_color()inserver/src/socket/request.c. That helper comparesop->levelwithlevel_color[pl->level], so gray/green/blue/yellow/orange/red/purple always describe the monster relative to the character's overall level.Actual skill XP follows a different comparison:
calc_skill_exp()inserver/src/server/skill_util.ccallscalc_level_difference(skill_level, monster_level)and returns zero for targets below the selected skill's gray cutoff or with no base XP.award_kill_exp()inserver/src/server/attack.ccalculates a full award separately for every participating damage skill, then weights it by that skill's share of total damage before party sharing.The target packet already carries the monster level, skill objects already carry their levels and XP to the client, and visible living objects already carry a character-relative name color. However, the 201-entry authoritative
level_colortable andcalc_skill_exp()live only on the server. Reimplementing those rules in the client would create a second gameplay authority and would still miss zero-base-XP monsters and server-side reward changes.Proposed player experience
Preserve danger; add XP suitability
Do not replace the existing name/target color with a skill-relative color. A level-30 monster must not look harmless merely because it is being compared with a level-5 secondary skill.
<skill>band beside visible monster names and in the target widget.XP for Wizardry 6: yellow — up to 1,120.calc_skill_exp()returns zero. A zero-XP hostile may instead use an explicitNo XPstate where that is clearer.No XP,Low,Good,Bonus, or the named band) so this feature does not depend on color perception. The target widget must always spell out the meaning.Select the comparison skill
Autochoice to return to attack-source tracking.Autothrough the same server-side source/skill rules used by damage and XP credit, rather than trusting a possibly stalechosen_skillpointer or reproducing weapon/spell rules in the client.Server and protocol design
Centralize the duplicated level-band classification in one server helper that accepts viewer/skill level and target level and returns a bounded enum such as:
get_living_level_color(), target serialization, and the XP comparison feature should use that helper. The client may map the enum to presentation colors and localized labels, but it must not own the threshold table or XP formula.Suggested classic protocol changes:
Autoor a validated learned skill ID.CLIENT_CMD_TARGETwith explicit danger band, comparison skill identity/level, XP band, and a bounded potential full-credit XP value. The existing target name, target level, relationship, and combat state remain independently meaningful.SOCKET_VERSIONand update the current client/server contract coherently; no permanent compatibility path is needed.common/toolkit/map_protocol.cto validate new enum values and truncation before applying any map state.doc/ADS/ADS-2, the C client parser/render state, andtools/atrinik_bot/constants, map model/parser, target model/parser, and tests in the same change.The server map cache must include the computed danger and XP bands (or an equivalent comparison-generation key). Changing the selected skill, leveling that skill, changing attack source while in
Auto, or changing character level must force prompt deltas for unchanged visible monsters and refresh the current target. Today the map cache equality test does not include the non-player name color, so merely changing viewer level is not sufficient to repaint an otherwise unchanged monster; this proposal should fix that invalidation rather than requiring movement or a full map clear.Acceptance criteria
Autoselects the skill that the current attack source would credit; a learned combat skill can also be pinned explicitly and restored toAuto.Auto, or gaining a skill level refreshes the current target and all visible monster XP indicators without requiring the monster or player to move.level_color/calc_level_difference()rules at gray/green/blue/yellow/orange/red/purple transitions; the client contains no duplicate 201-entry threshold table.SOCKET_VERSION, shared constants/validator, server producers and map cache, C client parsers/UI, Python bot consumers, tests, and ADS-2 are updated together.Autoattack-source change, a pinned-skill change, and a skill level-up while monsters remain visible.Out of scope
level_color,calc_level_difference(), XP caps, party sharing, or damage-participation awards.Relevant code
server/src/socket/request.c:get_living_level_color(), MAP2 living-name serialization/cache,send_target_command()server/src/server/skill_util.c:calc_skill_exp()server/src/server/exp.c:level_color[],calc_level_difference()server/src/server/attack.c: per-skill damage participation and kill-XP weightingclient/src/client/commands.c: MAP and TARGET parsingclient/src/gui/widgets/map.candclient/src/gui/widgets/target.c: visible-name and target renderingcommon/toolkit/socket.handcommon/toolkit/map_protocol.c: legacy packet contract and validationtools/atrinik_bot/client.pyandtools/atrinik_bot/model.py: current non-C protocol consumerThis proposal turns the playtest observation in
GAMEPLAY_PLAYTEST_NOTES.mdinto a concrete server-authoritative UI and protocol change.Shared semantic presentation
#6 owns the shared semantic-color tokens, contrast requirements, and mandatory non-color cues. This issue owns danger/XP gameplay categories and server authority; it must send categorical values rather than theme colors and consume the shared client presentation contract.
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.
Supersedes
This issue supersedes atrinik/atrinik#149. It retains atrinik/atrinik#149's server-authoritative target-widget XP suitability and adds explicit Auto/pinned selection, visible-map indicators, potential full-credit XP, cache invalidation, and complete malformed-packet behavior.