Skip to content

Server batch: experience_update, bot autonomy stack, legacy magic types, loot/shop audit - #1

Open
barrelin-oss wants to merge 11 commits into
Helbreath:masterfrom
barrelin-oss:master
Open

Server batch: experience_update, bot autonomy stack, legacy magic types, loot/shop audit#1
barrelin-oss wants to merge 11 commits into
Helbreath:masterfrom
barrelin-oss:master

Conversation

@barrelin-oss

Copy link
Copy Markdown

Adds a dedicated experience_update server->client message sent whenever a player gains XP (solo/party NPC kill XP, crusade rewards, login reward delivery). Carries experience_gained, new total experience and level; on level-up also levels_gained, new max_hp/max_mp/max_sp and unspent stat_points.

Implemented via an experience-gain callback on player_system (fired from add_experience only when XP actually changes), wired in game_handlers so every call site is covered. Includes protocol docs (docs/protocol/player.md, docs/JSON_PROTOCOL.md, docs/protocol/items.md), PROGRESS.md entry, and a new unit test. Also fixes pre-existing MSVC build breaks (NOMINMAX include order, unguarded POSIX time functions).

Full test suite passes: 2521 tests OK.

🤖 Generated with Claude Code

barrelin-oss and others added 4 commits August 30, 2026 11:22
Adds a dedicated experience_update server->client message sent whenever
a player gains XP (solo/party NPC kill XP, crusade rewards, login reward
delivery). Carries experience_gained, new total experience and level;
on level-up also levels_gained, new max_hp/max_mp/max_sp and unspent
stat_points.

Implemented as an experience_gain callback on player_system (fired from
add_experience only when XP actually changes; silent at max level),
wired in game_handlers so every add_experience call site is covered.

Also fixes pre-existing MSVC build breaks: missing NOMINMAX include
order in three bridge translation units and unguarded POSIX time
functions (timegm/gmtime_r/localtime_r) in auth_system and a crusade
test.

Documented in docs/protocol/player.md, docs/JSON_PROTOCOL.md and
docs/protocol/items.md; PROGRESS.md updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…y, headless bot client

- JSON party messages (invite/accept/leave/update) + handlers and docs
- Fix loot gold credited to ECS entity id instead of player_id
- npc_registry YAML: parse gold_min/gold_max; npcs.yaml exp/gold keys fixed
- Perf: O(1) player lookup in find_aggro_target; sees_all counter gates
  the far-admin scan in get_players_who_can_see
- Case-insensitive .amd loading (ARESDEN/ELVINE were silently skipped),
  lowercase map names; characters start in their nation town
- Registration rate limit configurable (auth.max_registration_attempts)
- mapdata configs for default/aresden/elvine (spawners, merchants,
  initial points validated by tools/bot/scan-map.mjs)
- tools/bot: headless bot client (hunt/loot/shop/party), gen-bots, scanner
- posix_time_compat shim (pre-existing local build fix, included as-is)

WIP checkpoint requested by worktree session relaxed-bell-f49dbe for merge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The modernized magic_type enum was missing six values used by the HBX
Magic.cfg data (14, 19, 21, 25, 26, 27), so 15 spells were skipped at
load as invalid magic_type - including all high-circle attack magic
(Blizzard, Meteor-Strike, Lightning-Strike, Bloody-Shock-Wave).

- Add create_dynamic(14), damage_linear(19), damage_area_no_center(21),
  damage_area_sp_down(25), armor_break(26), ice_linear(27) to the enum
- Implement line targeting (find_line_targets): Bresenham trace from
  caster toward target up to 12 tiles, faction and safe-zone filtered
- damage_area_sp_down drains SP from player targets (effect2 dice,
  parsed into new sp_drain field)
- armor_break deals pure damage (ignores_defense now honored via
  deal_pure_damage)
- Cancellation becomes an offensive dispel (debuff category, removes
  the target effects) instead of hitting the utility stub
- Fix 4 placeholder rows in magic.yaml copied from Lightning-Strike in
  the original cfg: Cancellation type 28, Resurrection 32,
  Illusion-Movement and Mass-Illusion-Movement 16
- create_dynamic spells (Spike-Field, Ice-Storm, Cloud-Kill) stay
  skipped with an info log until the dynamic ground-object subsystem
  exists (tracked in PROGRESS.md)
- New registry test covering the legacy types; 2522 tests pass

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
loot_tables.yaml and shops.yaml were authored against a different item
numbering than items.yaml - 22 referenced IDs did not exist (375+
validation warnings per boot) and several existing IDs pointed at the
wrong item entirely (bosses dropping Tomato/Hoe/Garlic/Carrot as
placeholders for Ice/Merien gear; potion shops selling a MagicNecklace;
blacksmiths selling Dagger variants labeled ShortSword/MainGauche).

Audited every reference against items.yaml using the intent recorded in
the line comments:
- Remapped 16 references whose intended item exists under another ID
  (BlackShadowSword 926, The_Devastator 923, BarbarianHammer 928,
  KlonessAxe 929, StormBringer 924, GiantSword 46, Flameberge+1 55,
  MagicWand(MS20) 256, KnecklaceOfStoneGolem 647, SapphireRing 336 ...)
- Removed 32 pool entries whose intended item does not exist in this
  item set (AncientTablets, CritCandy, SSS/E.S.W/I.M.C manuals,
  XelimaCap/Hat/Helm, NecklaceOfXelima, DragonWand MS40, HolyBlade,
  GiantBattleHammer) plus the vegetable placeholders
- Rewrote ~50 stale comments to the real item names (no behavior change)
- shops.yaml: ShopKeeper-E/W sell RedPotion/BluePotion/GreenPotion
  (91/93/95); Gandlf/William sell Dagger/ShortSword/MainGauche (1/8/12)

All references now resolve; no in-pool duplicates; 2522 tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@barrelin-oss barrelin-oss changed the title Send experience_update to client on XP gain and level-up Server batch: experience_update, bot autonomy stack, legacy magic types, loot/shop audit Aug 30, 2026
barrelin-oss and others added 7 commits August 30, 2026 18:27
Entity ids from the shared entity_manager are not player ids; casting one
to the other made get_player() return nullptr (or the wrong player) and
silently skipped guarded blocks. Standard fix: get_player_by_entity().

- magic_system: 21 lookup sites (mana/HP/SP costs, silenced/level/stat
  checks, range and safe-zone checks, damage/heal scaling, SP drain,
  resurrection, debuff resist); AOE/line target finders now push the real
  p.ecs_entity instead of fabricating entity{player_id.value, 0}
- application: periodic heal/mana_drain/mana_restore effect ticks never
  applied to players
- game_handlers_combat: spell-cast broadcast target resolution; respawn
  invulnerability was keyed on a fabricated entity and never matched
- player_system::remove_player: effect cleanup used entity{id.value},
  leaking active effects on logout
- wave4 legacy handlers: attack/cast fabricated caster entities from
  player_id, so spell knowledge/cooldowns could never match the JSON path
- tests: fixtures now resolve the real ecs_entity (old fabricated form
  only passed because fixtures spawn no NPCs, keeping counters aligned)

Legitimate uses kept: admin API player_id from the wire, inventory/trade
entity_id{pid.value} keying (consistent), spatial entity_id{index}.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ng spells

- magic_system.cpp: 20x get_player(player_id{entity.id}) -> get_player_by_entity;
  mana cost, range checks, INT scaling and player-target effects were silently
  skipped (ECS entity ids are not player ids)
- auth_handlers: grant qualifying spells on first login (magic_data '[]')
- tools/bot: mage role (Magic-Missile at range, self-Heal, mana potions),
  combat-aware potion use, resting state that waits for natural regen

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tick_ms + hp/mp/sp_interval_ms wired through server_config into
player_system_config (set_config was never called before — defaults only).
Amounts keep the legacy formulas; intervals control how fast a full roll lands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New world::dynamic_object_system managing temporary tile objects,
modernized from the legacy CDynamicObject system. The three create_
dynamic (type 14) spells now load and cast:

- Spike-Field: 25 spike traps in a 5x5 area; 2d4 physical damage when
  an entity steps on a trap tile (movement-triggered, owner-immune)
- Ice-Storm: single field ticking 3d3+5 ice damage in 5x5 every ~1s,
  applies freeze (20s) to players
- Cloud-Kill: single cloud ticking 1d8 poison damage in 3x3 (power 40
  from cfg), applies poison DoT to players

Field data is parsed from magic.yaml effect3 {object type, rx, ry}
(legacy Magic.cfg effect10/11/12 columns). Spawn validation: walkable
tile, not a safe zone, one object per tile; area ticks skip players in
safe zones. Damage is attributed to the caster so kill credit flows
through the normal combat/death pipeline.

Protocol: new dynamic_object_spawn / dynamic_object_removed broadcasts
to visible players, plus re-send of visible fields on enter_game and
teleport. Documented in docs/protocol/combat.md and JSON_PROTOCOL.md.

Not ported (documented in PROGRESS.md): weather shortening fire
duration, fire-ice mutual duration reduction, coal fire spreading,
NPC-move spike triggers.

8 new tests; registry test updated for type-14 loading; 2530 tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Closes the magic type cycle: sp_down_area(5), sp_up_area(7),
create(10), possession(15) and tremor(22) join the enum, removing the
last invalid magic_type skips at boot (66/66 spells loadable).

- Staminar-Drain: direct area SP drain (effect1 average); the
  sp_down_spot single-target variant now drains SP too instead of
  applying an empty effect. Celebrating-Light (zero dice) is a
  harmless visual cast.
- Staminar-Recovery / Great-Staminar-Recov.: area ally SP restore;
  healing path branches on sp_up_spot/sp_up_area to restore SP
  instead of HP (sp_up_spot previously healed HP by mistake)
- Create-Food: drops a random basic food (Baguette/Meat/Fish) at the
  caster via item_ops::drop_loot + ground item broadcast
- Tremor: area earthquake damage 3d4+3 (legacy knockback not ported,
  documented)
- Possession: accepted as no-op (legacy ground-item ownership does
  not exist in the modern server)
- Fixed two more entity-id-as-player-id lookups found while wiring:
  on_spell_cast caster resolution (game_handlers_combat.cpp) and the
  healing apply_heal pid (magic_system.cpp)

New registry test covering the five types; 2531 tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
recipes.yaml and fishing.yaml referenced potions from a different item
set (same class as the loot/shop audit). Remapped by function, results
are name-referenced: HealthPotion -> RedPotion, ManaPotion ->
BluePotion, RevitalizingPotion -> GreenPotion (and Big variants);
fishing rare catch SuperPowerGreenPotion -> SuperGreenPotion (391).

Eliminates the last 7 registry warnings at startup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Overlap with helbreathx-52 (20 magic_system lookup sites, on_spell_cast
caster, healing apply_heal pid) resolved in favor of master. Remaining
delta: AOE/line target fabrication, damage/heal scaling lookups, SP
drain, debuff resists, application.cpp effect ticks, respawn
invulnerability, logout effect cleanup, wave4 legacy handlers, test
fixtures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant