Skip to content

Bring the merged v83 stack (#497–#514) to master - #517

Merged
neeerp merged 34 commits into
masterfrom
v83/catch-up
Oct 18, 2026
Merged

neeerp merged 34 commits into
masterfrom
v83/catch-up

Conversation

@neeerp

@neeerp neeerp commented Oct 18, 2026

Copy link
Copy Markdown
Owner

The stack #497–#514 was merged layer by layer into each PR's parent branch, not into master: only #496 reached master. The repo doesn't delete head branches on merge, so GitHub never retargeted the upper PRs to master. So master still lacks everything from #497 to #514.

This branch brings those commits to master unchanged, rebased onto current master. Its tree is identical to the merged stack's tip (v83/consumables, 6c8b9d9); git diff 6c8b9d95 v83/catch-up is empty.

What it brings

Each of these was reviewed in its own PR:

Verification

  • Same content as the reviewed stack tip (empty diff).
  • cargo fmt --check and cargo check --workspace --tests pass on this branch.

Merging the stack above

PRs stacked on this one target their parent branch. Head branches aren't deleted on merge here, so GitHub won't retarget them. Please merge bottom-up, and change each PR's base to master before merging it.

neeerp and others added 30 commits October 17, 2026 19:36
Read and write path elements by type as v83 does (FUN_0068a33c decode,
FUN_0068a563 encode): velocity-only types keep the position before them,
type 10 is one byte, every other type ends with stance and duration.
HC sends velocities in px/s and a real start point, and records
element 4 when Teleport is used. The server relays the client's start
point instead of 0 and parses from it, for players and mobs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The user's steps merge into runs and go out about once a second, holding
back air and rope elements until a foothold follows (FUN_0068ab85,
FUN_0068a88d), with the key byte and bounding rect. Other characters
queue whole paths and play them on a Hermite curve, a tenth faster when
a second or more behind, snapping when over six (FUN_0068b371,
FUN_0068adcc).
The controller runs the mob's AI on its own body ahead of what is shown,
steps it until it has a second of movement (FUN_0066b6fc), sends that
path in MOVE_MONSTER and plays it like every other client. A hit shows at
once; the shown path stops at the next foothold and the AI flinches from
there (FUN_0068b16f). Other clients play the paths they get in turn.
…L162)

The server gives one player per field control of its NPCs (0x103, handed
on when they leave) and relays their 0xC5 choices to the whole map as
0x104. HC chooses only for NPCs it controls, waits for the echo, and every
client plays what comes back, as v83 does (FUN_006d2918, 006d2e07). HC
also handles REMOVE_NPC and control release (mode 0 no longer removes).
Server: buff skills give their level's stats plus their own (Booster,
Combo, charges, Hyper Body, Maple Warrior, Dark Sight, Shadow Partner,
...), with MP, cooldown and item costs. GIVE_BUFF carries every stat in
the client's bit order with the exact tail. The stats others see go out
as GIVE/CANCEL_FOREIGN_BUFF (0xC7/0xC8) on use, cancel, expiry and death,
and in SPAWN_PLAYER. Damage caps count attack buffs; Shadow Partner
doubles a shot's lines and Soul Arrow needs no arrows. A player leaving
a field is shown before mob/NPC control passes on.

HC: BuffParser reads the flags in the v83 decode order (FUN_00781d0e)
and the remote encoding (FUN_00788156, two-state blocks). Buffstat::by_bit
replaces the unordered maps. New 199/200 handlers and the spawn block
keep remote buffs on OtherChar.

Harness: strict packet loops skip NPC control (0x103).
Locally a ranged attack with Shadow Partner (not Taunt or Vampire) has
twice the hits, at most 15, and throws its bullets again from 30 px
behind the user, below the first set. Other users' shots read their
Shadow Partner foreign buff: twice the bullets and shown hits, the
client's start choice by mob index, and a miss's ends 30 px short.
USE_SKILL's party mask now reaches the members it names: the directory maps
slot bits to party members on the caster's map, and each applies the buffs
(or Heal's share) when alive and within the skill's lt/rb plus 100 px, with
user effect 2 showing the skill's affected animation. HC plays that effect
on 0xC6 and 0xCE type 2 instead of the use effect.
Server: MobStat flags in the client's decode order, statuses in spawn
packets, USE_SKILL mob list, Threaten/Slow/Seal/Doom/Shadow Web applied
in area with prop rolls, crashes/Dispel cancel mob buffs, expiry resets.
HC: MobStatParser, spawn + 0xF2/0xF3 handlers, Speed into the shoe,
Doom look swap/restore, hold for Stun/Freeze/Web, status effects.
Harness: mob_debuffs (Slow on snails).
Attacks put their skill's status (stuns, Panic's darkness, freezes,
poisons) and a learned Venom on the mobs they hit, rolled per hit that
deals damage. Ninja Ambush joins the USE_SKILL debuffs. Poison, Venom
and Ambush take HP on the field tick each second (fixedDamage if the
mob has one, never below 1 HP), crediting the inflicter and sending
them the HP bar. Poison- and ice-immune mobs (elemAttr S1/I1) shrug off
Poison and Freeze.

HeavenClient shows the numbers each second over the hurt mob as v83's
CMob::Update does (MobHurts).
Server: Poison Mist's attack leaves a mist over the skill's lt/rb box
around the caster (mirrored facing right). It starts 800 ms later,
rolls prop every 2.5 s to poison the live mobs in it, and lifts after
the skill's time. AFFECTED_AREA_CREATED goes to the map and to joiners;
AFFECTED_AREA_REMOVED lifts it.

HeavenClient: MapMists lays the skill's `tile` clouds over the area as
v83's FUN_00432776 does (MistLayout.h, a diamond of random tiles),
draws them above the map's objects, and fades them out over a second
on removal.

Harness: poison_mist.
…v83 30/37/51)

30 sets a tab's slot count and refreshes the item window, 37 answers the
pending skill request, 51 shows the SkillBook glow on the user (front and
back layers) and, for the local player, the Enchant sound and the v83 chat
line.
…ULT 0x33

The server reads a book's info/skill, masterLevel, reqSkillLevel and
success, takes the skill of the reader's job, spends the book and rolls
its success; HeavenClient sends 0x52 for 228/229 items (v83 FUN_00a0a1b2).
HeavenClient answers the pending move and shows v83's reason in chat or a
notice. The server refuses a portal it can't resolve with BLOCKED_MAP
instead of disconnecting, and channel or cash shop refusals with
BLOCKED_SERVER.
Server: GiveFameHandler (level 15, not self), fame_log with the day and
30-day rules, the field routes to the target on the giver's map, the
target changes its fame and answers both sides; a miss is forgotten.
HC: fame buttons per v83 (FUN_00a23eb5: on-map check, 5 minute
cooldown), FameResponseHandler lines, char info fame update.

Co-Authored-By: Claude <noreply@anthropic.com>
Server: USE_SKILL casts a summon at the client's spot (within 250x150 of
the owner), spending MP and itemCon and giving its buff; the field keeps
summons, shows SPAWN/REMOVE/MOVE_SUMMON, checks SUMMON_ATTACK against
mobCount and a 2x damage cap, breaks Puppets at their x HP, and brings
followers along on a map change.

HeavenClient: MapSummons draws and animates summons (v83 CSummoned);
SummonRules holds follow/circle movement, placement and damage; Combat
places the cast, targets and attacks on the owner's 3 s / 1.5 s clock,
and plays others' attacks; chat lines on expiry.

Harness: summons test. Docs: combat.md Summons. Report 158.
A local Puppet's spawn makes each mob this client moves that is chasing
someone chase the Puppet for 3-4 minutes; its removal lets them go
(FUN_00679847). A mob touching an unblinking Puppet rolls 40/(lv+70)
times its attack times 10-85% of it, over 100 (FUN_00793687), shows it,
and sends DAMAGE_SUMMON (0x7a627a).
The client heals its owner with Aura of the Beholder, else buffs one stat
with Hex, each in its skill's time (FUN_007a4080, FUN_007a5ab4,
FUN_007a5c99), and sends BEHOLDER (0xB2). The server checks the skills,
holds each to its pace in the field, heals or gives the Hex buff, and
shows the others SUMMON_SKILL (0xB3).
A summon's hits now carry its skill's status, as a player's attacks do.
Silver Hawk and Golden Eagle stun at the level's prop (50% to 99%).
Elquines and Frostprey always freeze; their data has no prop. Each status
lasts the level's x seconds, inferred from the freezes' x growing from 1
to 3. The client only shows the status it is sent.

The harness has an Arch Mage's Elquines freeze a mob for 3 s. The
Beholder test now finds its own summon on the shared map.
The server had no storage and the client had no storage window, so storage
NPCs only gave their speech. Any NPC with info/trunkPut now opens the
account's storage. Stored items and mesos are shared by the account's
characters. TRUNK_REQUEST takes out, stores, sorts and moves mesos for the
NPC's trunkGet and trunkPut fees, and TRUNK_RESULT answers with the bags that
changed or the v83 error mode. Each change is saved with the player in one
transaction, so an item is never in both places or neither.

HeavenClient gets UIStorage, laid out after v83's CTrunkDlg, with the same
count, fee and meso prompts. Buttons are placed by their top-left, as v83
does, which keeps BtSort's (35, 9) origin from pushing it onto the NPC
portrait.
A Priest's Mystic Door now works end to end. The server checks the cast as
the v83 client does (not in towns, event maps, maps whose fieldLimit forbids
it, or within 50 px of a portal), and the owner's client actor keeps the one
door, closing it when its time runs out, on a new cast, a channel change and
logout. The field shows it to the map with SPAWN_DOOR and REMOVE_DOOR; the
directory tells the owner (SPAWN_PORTAL) and the party on the owner's
channel (party op 0x25 and the party block's door section) where it leads.
USE_DOOR takes the owner and their party from the field to the town's door
portal for the owner's slot, and back to the door through SET_FIELD's chase
position.

HeavenClient casts the door with v83's refusals and messages, draws field
doors opening and standing and town doors at their portals, and walks
through them with Up after the map's own portals. Doors are placed on the
ground at the next update, since SPAWN_DOOR can arrive before the new map's
footholds load, and the chase position is looked up from just above, since
its rounded y can sit below a slope. DoorRules.h holds the rules apart from
the map and has its own tests; the mystic_door harness test covers the
server.
Chakra now heals its user by Cosmic's LUK-based range (LUK with equips),
answered with an HP and MP stat update. Resurrection reaches only dead
party members in its area and raises them with full HP. Time Leap ends
every cooldown but its own, sending COOLDOWN 0 for each, for the caster and
the members it reaches. These skills have neither buff stats nor a mob
debuff, so skills::acts_alone keeps them from being refused as unsupported.

In v83 a party member's Resurrection with no dead member in range plays its
action and effect but is never sent (0x96d567); HeavenClient now does the
same, while alone it is still sent with the 0x80 mask.

The new party_skills harness test resurrects a member killed through the
database and has a Chief Bandit heal with Chakra.
Mobs now use their Mob.nx attacks. The server answers a controller's move
with whether the mob may attack next and its MP, takes each attack's MP
cost, and relays the attack's action and data to the other players. A
TAKE_DAMAGE may name the attack that hit; it is bounded by v83's damage
formula at its largest (game::mob_damage), including the extra damage a
player below the expected W.DEF for their job and level takes. Deadly
attacks leave 1 HP and 1 MP, and mpBurn attacks burn MP.

HeavenClient picks attacks the way v83's FUN_0066a517 does (boxes, aimed
shots, balls, and rows of cells), plays them, replays other controllers'
attacks, and checks each against the local player after attackAfter. The
player's damage from touches and attacks now follows v83's physical and
magic formulas and miss rolls (MobDamage.h), replacing the old touch
procedure, and Achilles keeps its x per mille rather than taking it off.

Tremble, doFirst, element resistance, hits on summons and other mobs,
attack knockback speeds, and mob skills are left for later.
The harness stack's Docker build ran cargo with every core regardless of
the host's job cap. The compose file now forwards CARGO_BUILD_JOBS as a
build argument, and the Dockerfile passes it to cargo as -j when set.
Mobs now cast their MobSkill.img buffs, heals and summons as the v83
client does. The server grants a skill at the end of MOVE_MONSTER_RESPONSE
when a controller's move asks to attack, picking from those the mob can
pay for, that are off cooldown, within their HP limit and, for summons,
under their limit. It checks the cast against the grant, the mob's skill
list and its action, then applies it once the animation has played:
statuses through MOB_STAT_SET, heals as a negative DAMAGE_MONSTER, and
summoned mobs spawned for everyone with their summon effect.

HeavenClient keeps the grant from the ack and casts it before picking an
attack: it plays skillN, shows the level's effect after effectAfter, and
sends the action and skill data. Other clients replay the cast. The spawn
tail now reads the summon option as v83 does and plays the Summon.img
effect, DAMAGE_MONSTER shows heals in blue, and Power Up and Magic Up
scale the mob's hits on the player.

Player diseases (phase C) remain.
Mob skills 120-126, 128 and 132 now give the players in the caster's
lt/rb box a disease by the level's prop, and mob attacks with a disease
give theirs on a damaging hit. A disease is a buff stat whose reason is
the mob skill | level << 16: the player gets GIVE_BUFF, the map sees the
shown ones with the reason as the value, and the server ends it on time
or on death; the player can't cancel it. Poison takes x HP a second but
never the last, and Curse halves kill EXP as Cosmic does. Dispel, on the
caster and its party mask, rolls its prop per target and cures Poison,
Seal, Darkness, Weakness, Curse and Slow.

HeavenClient follows v83 CUserLocal: Stun stops all input, attacks and
skills; Seduce steers the player by its pattern; Confuse reverses the
arrows; Weakness stops jumps; Seal stops every skill but Dispel; Slow
sets the walking speed. Each disease plays its MobSkill `affected`
animation on the character, at the face for pos 1, and the player gets
the v83 grey notice instead of a buff icon. Poison's foreign entry now
keeps its reason.

Darkness's accuracy, Holy Shield, cure items and the exact v83 head
anchor are left open (reports/173-mob-attacks.md).
v83 (FUN_0094289f) flies Energy Orb's ball from 25 px in front of the shot's
origin to the middle of its first mob, then on to the middle of each next one,
1.25 ms per pixel (Spark 2). Each mob is hit when the ball arrives, so the hits
come one hop after another rather than all by horizontal distance. The client
now does this for its own shots and other users', and draws no regular bullet
for these skills.
The v83 client's MDamage (FUN_00791617) reads the mob's temporary stats on
every hit. Disable turns the hit away. Magic Immunity lets only a roll of 0
in 100 through and otherwise shows 1. The EVA and MDR statuses add to the
avoid and magic defense. Magic Guard Up leaves n% after the defense. Hard
Skin stops every hit that is not critical, and only Evans crit spells.
Weakness adds n%. The hit is also scaled by the weapon's percent for the
spell's element (FUN_005d284f): the Elemental Wands and Staffs carry
elemDefault and incRMAI/F/L/S/H, and every other weapon gives 100.

HeavenClient now keeps Disable's byte as a status, names HardSkin and
Weakness, reads the element boosts into EquipData, carries them on the
Attack, and applies each step in Mob::next_magic_damage in the client's
order.
CalcDamage::PDamage (FUN_0078df87) gates a blow on the same mob statuses
MDamage does: Disable makes it miss, Physical Immune caps it at 1 unless
the respect roll pierces it, the EVA and PDR statuses add to the mob's
avoid and weapon defense, Power Guard Up scales it after the crit (except
for Sacrifice), and Hard Skin stops every hit that is not critical.
HeavenClient's weapon damage ignored all of them; it now applies them to
weapon hits, while fixed damage still skips them as in v83. Unlike magic,
PDamage never reads Weakness: its extra percent is Dragon Knight
Berserk's damage.
A granted MobSkill 131 cast now lays a type 0 mist over the mob's
lt/rb box for the level's time, owned by the mob and carrying the
level's elemAttr as its element. As the v83 client does
(FUN_00431783 from CUserLocal's update), HeavenClient hurts the
player x once a second while they stand in a started mob mist without
touching a mob. It shows the number and sends TAKE_DAMAGE from -4 with
the skill and level. The server takes damage only for 131 at a known
level and up to x, then tells the field through DAMAGE_PLAYER, which
drops the mob fields for sources below -1.

HeavenClient also learns the v83 Smokescreen rule (FUN_0043193a): a
started type 2 area owned by the player or a party member keeps mob
attacks, touch and mists off them. The server does not spawn those
yet. Mist layout tests cover the hit and shelter lookups, and the
harness mob_mists test covers the cast, the area, the damage limits
and the expiry.
A mob's Weapon, Magic or full counter puts PCounter (bit 29) and/or
MCounter (bit 30) on it, written after the statuses as each counter's
spread and one shared chance, as the v83 client reads them. Each player
attack (not a summon's) on a countering mob costs the attacker the value
give or take half the spread, magic against MCounter. The client shows
its own roll and sends nothing, so the server takes the HP.

HeavenClient parses the new stats and shows the counter number over the
player for each countering target it hits. Harness mob_counters casts
Lilynouch's 145 and checks the HP lost to one swing.
A mob's Dispel (127) ends every buff but the diseases of each player in
its area, unsummoning summons, and Banish (129) sends up to `count` of
them to the mob's `ban/banMap` portal with its `banMsg` as a type 5
BROADCAST_MSG. Both roll the skill's prop first and reach the mob's
controller when the skill has no area. The warp helper now takes a
portal name.

HeavenClient's BROADCAST_MSG handler now follows the client per type:
"[Notice]" lines in light blue, the popup, the pink type 5 line, and it
reads the extras of types 3, 6 and 7 instead of dropping the packet.

The mob_dispel_banish harness test has an Aqua Road mob dispel a
beginner's Nimble Feet and a subway Shade banish the player to the
ticketing booth. move_mob moved into the harness's hunting helpers.
Mob skills 133-136 now inflict their diseases. Undead shares Puppet's
buff bit 46 and is told apart by its mob skill, on both sides. Stop
Potion, Stop Motion and Fear are bits 75-77, each shown with one int; the
client had read bit 77 as an int and two bytes.

The server refuses potions under Stop Potion and halves recovered HP
(potions, Heal, Chakra) under Undead. Stop Motion's damage arrives as a
mist hit with no skill code, and the server takes up to its value but
never the last HP.

The client gives the notices, refuses item use under Stop Potion or
Seduce, and hurts a player who stands still two seconds under Stop
Motion, with the v83 chat line and packet. Under Fear it blacks out the
screen except Effect/MapEff.img/Viewrange/3 around the player, using a
new cutout shader mode.
Item specs now carry their duration, the stats they raise and the
diseases they cure. USE_ITEM cures those diseases (All Cure and the
single cures) and gives the stats as a buff of id -item_id for the spec's
time, which the client lists with the item's icon. CANCEL_ITEM_EFFECT
(0x49) ends it, and HeavenClient now sends it when an item buff's icon is
right-clicked, as v83 does (FUN_0045b9fc / FUN_00a096af).
@neeerp
neeerp merged commit fb33e2a into master Oct 18, 2026
1 check passed
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