Health survives a run now, so a character sitting on the select screen can be nearly dead -- and the screen was still printing only the ceiling. "level 11, 2000 hp" for a character on 160 is a lie told at the exact moment it matters most, because the next thing that happens is the player walks it into a dungeon under permadeath. The roster message carries current hit points alongside the maximum, and the screen shows a fraction only when it means something: a healthy character still reads "1300 hp" rather than "1300/1300". The field is inserted mid-record, which shifts every field after it, so the protocol version goes to 10 -- a mismatched client would read the active flag out of the middle of a health value. tools/screenshot.gd had no capture for this screen, which is how a UI change to it could have shipped unlooked-at. It now shows a made-up roster of three characters -- whole, badly hurt, nearly dead -- for the same reason the upgrade screen is fed a made-up offer: the point is the layout, not a dungeon run. Getting there needs the player walked off the quartermaster first, or the game scene re-opens the upgrade screen over the top of it every frame. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4aeb6d229d
commit
a6084c1a8d
5 files changed
+110
-7
No files matched your search
@@ -651,3 +651,30 @@ func test_an_unknown_event_type_stops_the_stream_rather_than_desyncing_it() -> v
|
||||
assert_eq(got.size(), 1,
|
||||
"an event whose length is unknown unaligns everything after it, " +
|
||||
"so the only safe answer is to stop")
|
||||
|
||||
|
||||
func test_the_roster_carries_current_health_as_well_as_the_ceiling() -> void:
|
||||
# Health survives a run now, so a character on the select screen can be
|
||||
# badly hurt. Sending only the maximum would advertise it as whole, which
|
||||
# under permadeath is the worst thing on that screen to be wrong about.
|
||||
var rng := RandomNumberGenerator.new()
|
||||
var whole := Character.create("Whole", rng)
|
||||
var hurt := Character.create("Hurt", rng)
|
||||
hurt.hp = 42
|
||||
var got: Array = NetCodec.decode_characters(
|
||||
NetCodec.encode_characters([whole, hurt] as Array[Character], ""))["characters"]
|
||||
assert_eq(got.size(), 2)
|
||||
assert_eq(int(got[0]["hp"]), int(got[0]["max_hp"]),
|
||||
"a character that has never been hurt reads as full")
|
||||
assert_eq(int(got[1]["hp"]), 42)
|
||||
assert_gt(int(got[1]["max_hp"]), 42, "and the ceiling is still its own number")
|
||||
|
||||
|
||||
func test_roster_health_is_never_sent_as_zero() -> void:
|
||||
# A zero on this screen reads as a dead character the player is being
|
||||
# offered, which cannot happen: the roster is living characters only.
|
||||
var c := Character.create("Edge", RandomNumberGenerator.new())
|
||||
c.hp = 0
|
||||
var got: Array = NetCodec.decode_characters(
|
||||
NetCodec.encode_characters([c] as Array[Character], ""))["characters"]
|
||||
assert_gte(int(got[0]["hp"]), 1)
|
||||
Reference in new issue
Block a user