Skip to content

Editor improvements: creature levels, help window, wave portal editing and more - #22

Merged
tomluchowski merged 9 commits into
tomluchowski:shaders-improvementfrom
Upabjojr:split/editor-improvements
Sep 2, 2026
Merged

Editor improvements: creature levels, help window, wave portal editing and more#22
tomluchowski merged 9 commits into
tomluchowski:shaders-improvementfrom
Upabjojr:split/editor-improvements

Conversation

@Upabjojr

@Upabjojr Upabjojr commented Aug 1, 2026

Copy link
Copy Markdown

Eight commits improving the map editor:

  • Choose the level of placed creatures: L / shift-L raise and lower the level the editor gives to creatures (shown in the status bar); applies to creatures spawned from then on and to whatever the hand is holding.
  • Shift-C walks the creature classes backwards.
  • A help window listing the editor's keys, and the level being edited shown in the UI.
  • Data-driven menus are emptied before being filled, so re-entering the editor does not duplicate their entries.
  • The debug drawer is left to the render manager that owns it.
  • Wave portals are editable: the editor can change what a wave portal sends.
  • The server decides which tile holds a wave portal. A client is only ever told what its tiles look like, never which room covers them, so the editor now asks about whatever tile is under the cursor and the server answers with the waves, or with an empty room name when there is nothing to edit there.
  • The editor can place a wave portal. Its room buttons come from the skill list and a wave portal is not a skill, so until now the only way to reach the wave window was to open one of the five levels that already ship with one. There is now a button of its own next to the portal, drawn with the portal's glyph in red.

Note for merge order: trivial adjacent-lines conflicts with the user-data-folder PR (ODServer.cpp) and the cross-platform PR (EditorMode.cpp).


Split out of #16 so each topic can be reviewed on its own. Merging all of the split PRs reproduces the tree of #16 exactly.

🤖 Generated with Claude Code

Upabjojr and others added 6 commits August 1, 2026 10:23
The level file format has carried a creature's level since forever, and the game
reads it back, but the editor always wrote 1: it spawned every creature straight
from its definition and offered no way to say otherwise. Levelling a creature in
a map meant editing the file by hand.

L now raises the level the editor gives to creatures, shift L lowers it, both
wrapping around the thirty levels a creature can have, and the status bar shows
it next to the class the way it already shows the fullness, the seat and the
class themselves.

The level applies to the creatures spawned from then on, and also to whatever
the hand is holding when it changes, which is what makes it possible to change a
creature that is already placed: pick it up, set the level, drop it. Only the
server knows what a hand holds, so the client sends the level and lets the server
decide what it lands on. An empty hand is not a mistake, it just means the editor
is setting the level of the creatures to come.

Placing a creature also heals it. Levelling raises the maximum HP without
healing, which is what levelling up in a game should do but not what an author
placing a level 20 hero means, and the difference would have been saved into the
map as a wounded creature.

The status bar field is 20 pixels wider so that the longest class name and a two
digit level still fit within it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit efd537c)
C cycles forward through the creature definitions and wraps around, so reaching
the class just before the current one means pressing it as many times as there
are classes. Shift C now steps back the same way shift L lowers the level.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 4202bd7)
…iting

Shift Y walks the seats backwards, the way shift C and shift L already walk the
creature classes and the creature levels, so GameMap gains previousSeatId next
to nextSeatId.

None of that is discoverable. The status bar names the key next to each thing it
shows, which is how anyone finds T, Y, C and L in the first place, but there is
nowhere it could say that holding shift reverses them, and nothing at all
mentions the camera keys or the point of view hotkeys. Help then Controls now
opens a window listing all of them, grouped by what they act on. The Help menu
had one entry, About, which is not connected to anything.

The editor also never said which level it was editing. A new level is given a
name in the dialog that creates it and then never shown again, and loading one
from the recently used list gives no confirmation of what was opened. The name
now sits under the menu bar. It is read every frame rather than once, because
the client only learns it when the server sends the level over, which happens
after the editor is on screen, and it changes again on saving under a new name.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 9b79128)
Entering the editor a second time in one session kills the game:

    CEGUI::AlreadyExistsException ... Failed to add Element named: Adventurer
    to element at: EDITORGUI/Menubar/Creatures/PopupMenu4 since an Element with
    that name is already attached.

EditorMode is built anew every time the editor is entered, and its constructor
fills the Creatures menu with one item per creature definition, the recently
used files menu and the Seats menu. The window it fills them into belongs to the
Gui and lives as long as the game does, so the second visit finds all of them
already there. CEGUI refuses a second child of the same name by throwing, and
nothing catches that before it reaches main().

Two of those three menus already had a function to empty them, used when their
contents change while the editor is running, but it was never called on the way
in. The creature list had none, so it moves out of the constructor into the same
install and uninstall pair as the others, and all three are now emptied before
being filled. GameMode has the same kind of list, of the seats in the game, and
already removes it in its destructor, which is why only the editor is affected.

Emptying detaches the old items without destroying them, which is what the
existing functions do: one of them is called from the click handler of an item
it is removing, and destroying a window in the middle of dispatching its own
event would not end well.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 8ec6c20)
Entering the editor a second time now segfaults on its first frame, reading
address 0x10 inside DebugDrawer::build() called from EditorMode::onFrameStarted.

RenderManager makes the debug drawer along with the scene, in createScene(),
which the ODFrameListener constructor calls once for the whole run of the game,
and destroys it in its own destructor. EditorMode's destructor deleted it too,
every time the editor was left. Ogre::Singleton clears its instance pointer as
it goes, so the second editor session found nothing there and drew through it
anyway: getSingleton() returned a null reference and build() read its manual
object, which sits at offset 16 of a DebugDrawer, from address 0x10.

The editor still clears the drawer of what it left behind, which is its own, and
checks first, since at shutdown the render manager may already have taken it.

This was hiding behind the exception fixed in the previous commit, which killed
the game earlier in the same sequence, while EditorMode was still being built.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 893df06)
The waves of a wave portal could only be written by hand in the level file.
They are now edited from the editor: point at a portal, press P, and a window
shows the turns between waves, the strategy, the attack range and the target
teams, along with the list of waves and the creatures of each of them.

A client is never told about the rooms of a map, only about what its tiles look
like, and the waves are not part of what a room sends anyway. So the editor
names the portal to the server by one of its tiles and asks for the waves; the
server answers with them, and sends back what the player applied. The level is
saved from the server side, so that is where the change has to land.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit a548707)
Comment thread source/modes/EditorMode.cpp Outdated

// A client does not get the rooms themselves, only what its tiles look like, so this is
// as much as the editor can tell on its own. The server has the last word.
if(tile->getTileVisual() != RoomPortalWave::mRoomVisual)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason it always enters this branch -- that is a window for "creatures waves " never opens, and at the hand it is written : "Point at a wave portal to edit its waves" regardless the cursor is over the WavePortal or not.....

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 4db4c97 by removing that branch altogether. The check compared the client's tile visual against portalWaveRoom, and as you saw, the client's visuals cannot be trusted for this: a client is only ever told what its tiles look like, never which room covers them. So the editor now always asks the server, whatever the tile, and the server answers with the waves, or with an empty room name when the tile holds no wave portal — only then is the "Point at a wave portal" hint shown. The server checks the real room on the tile (getCoveringRoom()), so pressing P over a portal opens the window and pressing it anywhere else shows the hint, with no guessing on the client.

I also merged the current shaders-improvement into this branch (5737d2a, clean merge), so the PR now builds with the console-test fix and the other merged work; all 10 tests pass here, including the five that run against a live headless server.

Upabjojr and others added 2 commits August 30, 2026 20:57
The editor's P shortcut refused to ask about any tile the client did not
believe looked like a wave portal, and that belief proved wrong: the check
compared against the tile visual, and the window then never opened at all,
with the "Point at a wave portal" hint shown even over a portal.

The client is only ever told what its tiles look like, never which room
covers them, so it has no reliable way to tell. The server does, and it is
also the only side that knows the waves. So the editor now always asks, and
the server answers with the waves, or with an empty room name when the tile
holds no wave portal, in which case the editor shows the hint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JiKh7f4nenYBTjhJfAGa3y
@tomluchowski

Copy link
Copy Markdown
Owner

git cherry-pick ..Upabjojr/split/editor-improvements

[...]
[shaders-improvement 040a6485] Let the server decide what tile holds a wave portal
Author: Francesco Bonazzi franz.bonazzi@gmail.com
Date: Sun Aug 30 20:57:59 2026 +0200
3 files changed, 21 insertions(+), 19 deletions(-)
error: commit 5737d2a is a merge but no -m option was given.
fatal: cherry-pick failed

I am such a lamer, why does the cherry-pick fails here and what is this -m option I should somehow use ?

@tomluchowski

tomluchowski commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Besides that : something fundamentally flawed : as I cannot get the window with portal creatures to be displayed.
The mentioned before situation still occurs :
I keep pushing 'P' but only get that :

Screenshot_2026-08-31_10-28-25

@tomluchowski

tomluchowski commented Aug 31, 2026

Copy link
Copy Markdown
Owner

The bug seems to be located in here :

` //! \brief Finds the wave portal the editor is talking about. The editor names it by one of
//! its tiles: a client has no rooms of its own, only the tiles it was told about.
RoomPortalWave* getWavePortalOnTile(Tile* tile)
{
if(tile == nullptr)
return nullptr;

    Room* room = tile->getCoveringRoom();
    if(room == nullptr)
        return nullptr;

    if(room->getType() != RoomType::portalWave)
        return nullptr;

    return static_cast<RoomPortalWave*>(room);
}`

getType() seems to return RoomType::portal , not portalWave :(

The editor offers two rooms of its own, a dungeon temple and a portal, so a
wave portal could only ever be edited in a level that already had one. Add a
third button for it, next to the portal, drawn with the portal's glyph in red
so the two cannot be confused.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JiKh7f4nenYBTjhJfAGa3y
@Upabjojr

Copy link
Copy Markdown
Author

Nothing lame about it — git cherry-pick simply refuses to guess on merges.

A normal commit has one parent, so "the changes this commit made" is well defined. A merge has two, so the same phrase means nothing until you say changes with respect to which side. That is what -m picks: -m 1 means "relative to the first parent", i.e. take everything the merge brought in from the branch that was merged.

Here you do not want it at all. The range you gave ended at 5737d2a, which is me merging your shaders-improvement into my branch to get the console-test fix. Replaying it on top of shaders-improvement would only try to re-apply your own commits. The commit before it, Let the server decide what tile holds a wave portal, is the only real change and it already landed as 040a6485, so:

git cherry-pick --quit

and you are done — nothing is missing.

If you would rather not deal with the merge commit at all, git merge Upabjojr/split/editor-improvements takes the branch in one go and keeps the history honest about where it came from. And for future ranges of mine, picking the individual commits (git cherry-pick <sha>) avoids the question entirely.

@Upabjojr

Copy link
Copy Markdown
Author

getType() is telling you the truth: that tile really is covered by a plain portal, not a wave portal.

Your screenshot is Forgotten treasures, with the cursor at x:85, y:65. In levels/skirmish/ForgottenTreasures.level those coordinates belong to:

4	Portal_6	1	9
84	64
84	65
84	66
85	64
85	65      <- the tile under your cursor
85	66
...

4 is RoomType::portal; RoomType::portalWave is 10. That level has no wave portal anywhere in it — only five levels ship with one (StoneKeep, DuelToDeath, FallingKeeper, RuinsOfTheConfluent and TestSingleplayerSmall). So the hint was correct, and getWavePortalOnTile returning nullptr was the right answer.

Which leaves the real problem, and it is a fair one: the editor could not create a wave portal in the first place. The room buttons the editor shows come from the skill list, and a wave portal is not a skill, so it was only ever shown through the two hand-written buttons for a dungeon temple and a portal. There was no third one. The only way to reach the wave window was to open one of those five levels and point at a portal that was already there — which is not something anyone should have to know.

4a44d71 adds the missing button, next to the portal in the Rooms tab. It draws the portal's own glyph in red so the two are not mistaken for each other, and its tooltip says what it builds.

Place it like the portal (it claims its tiles for the selected seat), then press P over it: the window opens on an empty wave list, ready to be filled in.

If you would rather test on an existing level, StoneKeep has six wave portals — PortalWave1 sits on tiles 105-107 x 199-201.

@tomluchowski

tomluchowski commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Great, great I have look over that and don't see any errors so far and at least I know for now that a special PortalWave exisits in the game :). I now have a lot's of work in my real life but in a few days I will come back to the issue. The only remark is : we start to create a glue language to describe what the waves of creatures/monsters should be. But the glue language of choice for ODP is Python ;) .... Think about that and join our discord channel to exchange the opinions.

@tomluchowski
tomluchowski merged commit 033ad8a into tomluchowski:shaders-improvement Sep 2, 2026
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.

2 participants