Repository navigation
Conversation
Embed per-monitor DPI awareness and maintain the verified OGRE 13.6.5 renderer patch required for context-safe window replacement, runtime GL options and correct fullscreen client geometry. Validation: clean Release build, DPI and 3440x1440 fullscreen probes, three GL3Plus variants and the patch reverse check passed; version 0.7.1 remains unchanged because this is unreleased work and the project has no changelog.
Apply display, input, audio, gameplay, minimap, shadow and negotiated nickname changes to the running session while preserving rollback for failed window or input transitions and keeping the 3440x1440 navigation visible. Validation: clean Release and Debug builds, packet tests, DPI, layout and GL3Plus probes passed; the user confirmed the reported resolution, fullscreen, cursor, flicker and navigation failures are fixed. Version 0.7.1 remains unchanged because this is unreleased work and the project has no changelog.
The spells tab put its ten buttons in a single row of 60 pixel squares running out to x=700. The pane they live in is the window width minus the 200 pixels of minimap, so at the 800 wide minimum window it is 600 pixels across, and the last two spells, Weakness and the Eye of Evil, hung past its right edge where they could be neither seen nor clicked. Only a window at least 900 wide showed the whole row, and nothing said so. The rooms tab solved the same problem long ago: two rows of 40 pixel buttons. The spells tab now uses the same shape, five spells to a row, ending at x=300 with room to spare at any size the game accepts. The cooldown bar inside each button now covers exactly its button too, instead of overhanging 20 pixels on both sides, which with narrower buttons would have bled half way across the neighbours. The rooms tab had the tail of the same bug: the temple and portal buttons the editor uses sat at x=570 to 690, past the same edge. They join the end of the second row instead. (cherry picked from commit 10db1cb)
When an enemy hits a heart that is at or below 11 % of its durability, the human owner receives one event message. No warning on the killing hit, for AI or defeated owners, or in the editor.
When a human player loses the last dungeon heart, the server sends two owner-only messages appended to ServerNotificationType: playerDefeated with the conqueror seat and the heart tile, and levelStatistics with the elapsed time and six per-seat counters (keepers defeated, creatures killed, heroes destroyed, rooms captured, items made, creatures converted). The counters are kept per seat on the server and are not saved. The client stores the statistics and hands playerDefeated to a new GameMode::startDefeatSequence entry point.
On playerDefeated the client hides the interface and pointer, blocks input and cuts the camera to the destroyed heart. A client-only copy of the heart shakes and bursts into particles and a rubble pile, a swirl in the conqueror's colour leaves the heart, and a red tint, two subtitles and a fade to black follow. The timeline runs on a monotonic clock, so repeated frame callbacks do not speed it up. On the black screen a debriefing window shows the player, the outcome, the elapsed time and a per-seat statistics table from levelStatistics. Its confirm button returns to the main menu, which zooms in briefly and opens the skirmish sub-menu.
When a heart's health reaches 0, only the heart object is removed. The room keeps its floor tiles as an inert ruin with no health, so the seat has no temple any more and the existing defeat path runs as before. The ruin cannot be attacked, its floor is never released in game mode, and a saved ruin gets no new heart object on load.
The heart took its health from the durability of its floor tiles, so a 3 by 3 heart fell to a few blows. It now has 10000 health per room tile (90000 for a 3 by 3 heart); the critical warning stays at 11 % of this maximum. Only fighters damage an enemy heart: workers deal no damage to it, and a worker given a non-creature target drops its fight action. Saves write the heart health as "HeartHealth" on the new scale. An older "HeartHP" record is converted to the same share of the new maximum, a value above the maximum is limited to it, an unknown record is rejected and a destroyed heart stays a ruin.
The green ring of the heart badge now shows the remaining heart health of the local player as an arc, and the badge glows magenta for three seconds after the heart was hit. The server sends the new owner-only heartHealth notification (appended last to ServerNotificationType) when the health fraction changed by at least one percentage point, when the heart is destroyed and once when a game starts or is loaded. The client redraws the badge into its existing texture with blitFromMemory, since loadFromMemory would create a new texture that the badge never shows. The badge drawing moves into drawBadgePixels, with the symbol test as a named function instead of a lambda.
The badge icon still said "Your Mana". The heartHealth message now also carries the current and maximum heart HP, the server sends it on every whole HP change, and the client sets the icon tooltip and context line to "Dungeon Heart: current/max" every frame. The badge tooltip in the layout reads "Dungeon Heart Health" until the first message arrives.
…econd The heart maximum no longer depends on the number of covered tiles. A living damaged heart regains 2.5 / turnsPerSecond per turn up to the maximum, a destroyed heart never heals. Saves write HeartHealth10000; the older HeartHealth (per tile scale) and HeartHP records are converted by share, so a full heart stays full and a ruin stays a ruin. Messages that only carry a new whole HP are sent at most once per second; percentage point changes and destruction are still sent at once.
…ght-click to the heart The ring of the heart badge is drawn as six segments separated by spokes. One segment stands for one sixth of the health and a partly covered segment is filled in part. The badge tooltip reads "Dungeon heart health bar" and the info line "Dungeon heart at 17 %. Right-click moves the view to the heart." A right-click on the badge moves the camera to the own heart.
…he six segments are visible
…heart and gold badges
…rivets, emerald health gems, ruby heart and embossed gold coin
… emerald text in the top-left HUD
…ges, lit from height maps with soft 3x3 sampling
…emption and mana transfer on defeat - Mana income is per second: 30 from the heart plus one per claimed tile, the tile part capped at 500 tiles, the tiles of the heart area not counted again (GameMap::updateSeatMana) - Every worker costs 7 mana per second; mana never drops below 0 and is capped at MaxManaPerSeat (now 200000), casting is refused while the cost is not covered - Dropping an own worker onto the tile of a living own heart redeems it: the worker is removed and half of its summoning price at the current worker count is paid back (Player::redemWorkerInHeart) - On a single player defeat the remaining mana of the defeated seat goes to the conqueror, the defeated seat is left with 0 - The mana display shows income and upkeep per second separately - New fixture source/tests/check_heart_mana.py for income, upkeep, redemption and transfer; check_heart_ruin.py keeps passing
…seat has four - While a seat with a living dungeon heart has fewer than four workers, the heart creates one of the seat's worker type at its heart tile every five seconds, until there are four (GameMap::updateSeatAutoWorkers) - A seat with four or more workers, or without a living heart, creates none and its clock resets; a long turn still creates at most up to the target - The existing no-worker chat notice stays and only shows until the first worker appears; summoning a worker by spell is unchanged - The creation clock lives on the seat (mAutoWorkerTimer, server side, not saved with the level: after a load the clock starts over at zero) - New fixture source/tests/check_heart_auto_workers.py for the creation pace, the target cap and the blocked cases; check_heart_mana.py keeps passing
… an alarm call - Each turn the server checks for a living enemy creature within seven tiles of the living heart of the seat; a dead enemy, an allied creature or a destroyed heart gives no defence (GameMap::updateSeatHeartDefense) - While an enemy is in range, the workers and the scouts (CaveHornet, Goblin) leave their work and run to the nearest fighter of the seat (CreatureActionGoDefendHeart, GameMap::getHeartDefenceTargetTile) - When the heart itself takes damage, the runners rush to the heart instead - When no enemy is in range any more, the action pops and everyone returns to normal work - An alarm call sounds once when the defence starts (Keeper/WeAreUnderAttack), not the combat music; a non human owner gets the defence without the call - The defence state lives on the seat (mHeartDefenceActive, mHeartDefenceHeartDamaged, server side, not saved with the level: old maps and saves keep working) - New fixture source/tests/check_heart_defence.py for the six and eight tile reach, the rally to the nearest fighter, the damaged heart, the destroyed heart, the alarm and the runners; the whole heart fixture set keeps passing - check_level_statistics.py: the fake seat of the notification probe gets the mana members the production code needs
The runner action class asks the game map for the tile to defend, so the lookup is a public member next to the other path queries; the per-turn defence check stays private.
A new heart covers 25 tiles: the 3x3 core is the heart and the outer 16 tiles form a treasury ring that stores 1000 gold per tile (16000 total). The ring is part of the heart, so its capacity is fixed and never raised by the treasury skill. A heart's size comes from its covered tiles, so 3x3 hearts in old saves or unchanged maps keep working with no ring. Gold flows through the existing carry virtuals (depositGold, withdrawGold, getTotalGoldStorage, getTotalGoldStored), gold stacks appear on ring tiles, and releasing a ring tile in the editor turns its gold into a treasury object. Split handling moves ring tiles without duplicating gold, and a destroyed heart leaves its 25-tile floor as ruin. Converts the 22 hearts of nine shipped levels to 5x5 around the same centre, absorbing the 13 adjacent treasuries the ring replaces. Hearts that cannot be converted without changing the layout are left unchanged and recorded for a user decision.
Every shipped level now carries a 5x5 heart with a sixteen-tile treasury ring of 1000 gold per tile. - ScreamInTheDark: open seven ring walls (five rock, two dirt) to floor; move Portal5 from (42..44, 53..55) to (34..36, 53..55) off the heart ring of DungeonTemple5 - TheBridge: add the 22 missing ring tiles as dirt floor, 11 per heart - aa and ab: open three gold ring walls each and add the 13 missing ring tiles per heart - DuelToDeath: add the 30 missing ring tiles, 15 per heart - StoneKeep: give the twelve lava ring tiles the heart's seat - TestSingleplayerSmallPassability: add the 2 missing ring tiles; move Portal2 from (1..3, 4..6) to (1..3, 5..7) off the heart ring
The map had an empty [Rooms] section, so a seatless upkeep found zero dungeon temples and reported an instant loss at the start of a new skirmish game. Place the 5x5 heart DungeonTemple1 (centre 4,14, seat 1) on the twenty-five existing dirt floor tiles; no tile, seat or creature data changes.
The probe in check_dungeon_temple_duplication.py declared RoomDungeonTemple::getHP() twice: the 10000 HP stub added for the 5x5 hearts and the older 1.0 stub from the destroyed heart ruin work. A class with two identical member declarations does not compile, so the check could not run. Keep the 10000 HP stub, which matches the current heart health.
Three health tier variants (healthy, damaged, critical) of the dungeon heart, each a mesh with its own two-bone rig and a looping "Pulse" animation of 2.0, 1.25 and 3.5 seconds. Every mesh holds the temple's pedestal with the heart standing on it: a cocoon and heart hybrid with closed vessel stubs, sinew strands and veins; the damaged and critical tiers show injuries and blood. Materials use a colour texture, a tangent-space normal map and a glow texture per tier. assets-src holds the editable Blender source and glTF exports the rigs were taken from.
The temple object now uses the healthy, damaged or critical heart mesh, chosen from the heart's health fraction (switching at 2/3 and 1/3) and rebuilt when the tier changes. The heart plays its "Pulse" animation in a loop. Tile::isBuildableUpon, room object navigation and the defeat sequence lookup recognise the heart by its tier meshes through RoomObjectPath::isDungeonHeartMesh, and the bounds catalog holds the footprint of the pedestal every tier mesh contains. The bounds, navigation and heart construction checks cover all tiers; test_DungeonHeartTier covers the health fraction to tier mapping.
A destroyed heart has no health left, so the client-side copy shown while the temple bursts uses the critical tier mesh. It holds the pedestal too, so the pedestal and the heart shake, glow and burst together.
tools/heart-on-temple builds the heart geometry (heart_shape.py, generate_heart_geometry.py) and packs it with the temple pedestal into the three meshes and skeletons (build_heart_on_temple.cpp). tools/heart-textures paints the colour, normal and glow textures per tier from the mesh's surface points (generate_heart_textures.py).
The pedestal submesh used the temple material Stacheln, whose colour texture has baked-in white highlight blobs and whose spherical environment map layer added a bright grey haze with sparkles. The pedestal now uses its own material Obsidian with a cleaned dark stone texture (generated from the old texture's grain by tools/heart-textures/generate_pedestal_texture.py) and no environment map. The three heart meshes are re-pointed at it; the temple material itself is unchanged.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The dungeon heart gets a new look and three health tiers.
HeartHealthTier). The damaged and critical tiers show injuries (bruises, cracks, scars, torn strands, blood trickles); critical is clearly worse but not black. The temple object is rebuilt when the tier changes.Pulseanimation: 2.0 s (healthy), 1.25 s (damaged), 3.5 s (critical).RoomObjectPath::isDungeonHeartMesh()replaces the oldDungeonTempleObjectmesh name checks inTile::isBuildableUpon, room object navigation and the defeat sequence lookup. The bounds catalog holds the pedestal footprint for the three meshes.How the assets are made
tools/heart-on-temple:heart_shape.pyandgenerate_heart_geometry.pybuild the heart geometry per tier;build_heart_on_temple.cpp(Ogre) packs it with the temple pedestal into the three.meshand.skeletonfiles.tools/heart-textures/generate_heart_textures.pypaints the colour, normal and glow textures (numpy + Pillow, seeded, reproducible).assets-srcholds the Blender source and glTF exports the rigs come from.Stacking
This PR stacks on #203 (
pr/dungeon-heart-h6), which itself stacks on #197 and #198 to #202 (health pool,getHeartHealthFraction()). It also relies on the room object bounds and navigation code that is already part of the #203 branch. Until those are merged, the diff against the base branch also shows their commits; the new work is the last four commits. To see only this step, compare this branch against the head of #203.Validation
check_room_object_bounds.py(1660 checks, 0 failures, includes the three heart meshes),check_room_object_navigation.py(6333 / 0),check_defeat_heart_burst.py(render checks 34 / 0) andcheck_dungeon_heart_targeting.py(10 / 0) pass.check_heart_construction.pycovers all three tiers, but its fixture does not compile on the base branch (Make new dungeon hearts 5x5 with a 16-tile treasury ring #203) either, so it was not run.test_DungeonHeartTier.cppcovers the health fraction to tier mapping; the unit test target is not part of this build, so it was not run.suggestTangentVectorBuildParams/buildTangentVectorsasrrCreateRenderedMovableEntity()does succeeds for all three without an exception. The defeat-sequence copy creates its entity from the critical mesh without building tangents.