Skip to content

Show creature health, needs, experience and attack recovery - #162

Open
Rokk001 wants to merge 31 commits into
tomluchowski:shaders-improvementfrom
Rokk001:pr/creature-health-and-needs
Open

Rokk001 wants to merge 31 commits into
tomluchowski:shaders-improvementfrom
Rokk001:pr/creature-health-and-needs

Conversation

@Rokk001

@Rokk001 Rokk001 commented Sep 27, 2026 •

Copy link
Copy Markdown

Show visible creatures' owner-coloured segmented health rings and alternating level and needs display. Indicators are enabled by default; Alt toggles the chosen state, zoom enlarges the overlay, and depleted segments disappear without obscuring creature details.

The overlay now also shows experience toward the next level and attack-recovery progress. Capability negotiation keeps mismatched clients and servers from reading unsupported progress fields.

Depends on #225 for synchronized creature data. Review the progress follow-up.

Validation: progress and packet checks pass 377 assertions, level-caption rendering passes 557, and Alt handling passes 130. No new full-game, multiplayer or other-platform run is claimed; matching endpoints are required for the new optional fields.

Related to #216 and #6 without claiming either broader issue is fully resolved.


Supersedes #108, closed and force-pushed by mistake; recreated with the same content and cleaned-up commit trailers.

Rokk001 and others added 23 commits September 7, 2026 00:45
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)
Remove depleted fills and borders while preserving surviving health segments and the centre. Update player guidance and add the 200-check texture regression. No release version or protocol change is needed; the existing PR documents this unreleased feature correction.
# Conflicts:
#	source/entities/Creature.cpp
Replace the hardcoded dependency, Python and Visual Studio locations in the
Windows scripts with a shared windows-paths.ps1. The defaults derive from the
current user profile and can be overridden with OD_DEPS_ROOT, OD_PYTHON_ROOT
and OD_VS_PATH.
Replace the auto declarations introduced by this branch with explicit types,
as requested in issue tomluchowski#42 and required by CLAUDE.md. No behavioural change.
@Rokk001

Rokk001 commented Sep 27, 2026

Copy link
Copy Markdown
Author

Comment from the original #108 by @tomluchowski (source):

Creature mood/health icon should be toggle-able with say ALT ( similar to previous functionality )

@Rokk001

Rokk001 commented Sep 27, 2026

Copy link
Copy Markdown
Author

Comment from the original #108 by @tomluchowski (source):

And the health- flower "petal " should be transparent or semi-transparent when empty ... and the same maybe for full one. Should be checked in "washing "

@Rokk001

Rokk001 commented Sep 27, 2026

Copy link
Copy Markdown
Author

Comment from the original #108 by @Rokk001 (source):

Accepted: the previous hold-Alt visibility behavior will be restored instead of keeping the indicators permanently visible.

@Rokk001

Rokk001 commented Sep 27, 2026

Copy link
Copy Markdown
Author

Comment from the original #108 by @Rokk001 (source):

The petals already use alpha blending, but overlapping borders make them nearly opaque; should only empty petals be more transparent, and what does ‘washing’ mean here?

@Rokk001

Rokk001 commented Sep 27, 2026

Copy link
Copy Markdown
Author

Comment from the original #108 by @tomluchowski (source):

In Polish we say that something has been checked or proved in "wash"/"washing". That means it is proven to work in practice. Because chemistry is the theory , "wash"/"washing" a real job.

@Rokk001

Rokk001 commented Sep 27, 2026

Copy link
Copy Markdown
Author

Comment from the original #108 by @tomluchowski (source):

Screenshot_2026-09-19_20-36-34

@Rokk001

Rokk001 commented Sep 27, 2026

Copy link
Copy Markdown
Author

Comment from the original #108 by @tomluchowski (source):

For example , look at the black petals of the Knight, it's sword is not visible, if not only tiny part of it. Black petals here should be more transparent .

@Rokk001

Rokk001 commented Sep 27, 2026

Copy link
Copy Markdown
Author

Review comment on source/modes/SettingsWindow.cpp:599 from the original #108 by @Rokk001 (source):

Reusing what is already there is the goal: Helper::split and Helper::toInt already parse the WIDTHxHEIGHT video mode string, so no new helper was needed for it.

continue;
renderer->setConfigOption(option.first, option.second);
}

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.

strange that FULL_SCREEN and VIDEO_MODE have to be treated separatly , but that's a digression

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.

In this code the two options are simply applied first and explicitly (FULL_SCREEN, then VIDEO_MODE), and the loop after them applies every other saved option while skipping those two. The code does not document why this order is needed; it was settled by testing on the fullscreen/resolution switching cases, so I do not want to claim a deeper reason here.

{
replacementWindow->setFullscreen(false, description.width, description.height);
replacementWindow->setFullscreen(true, description.width, description.height);
}

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.

Intresting , that setFullscreen has to be done like that , first as 'false' , then as 'true'

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.

Yes, that is what the code does: when the old window was fullscreen and the new configuration is fullscreen too, the replacement window gets setFullscreen(false) followed by setFullscreen(true) with the target size. It is only done in that case (fullscreen to fullscreen); the code does not document the reason for the two steps, so I am not adding a claim about it.

root.destroyRenderTarget(mWindow);
mWindow = mPrimaryWindow;
}
Ogre::WindowEventUtilities::_removeRenderWindow(mPrimaryWindow);

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.

My editor says methods from Ogre::WindowEventUtilities are deprecated .......and I trust it ;)
Doesn't sound optimistic if we want to move up later to higher Ogre ( say 14 ;))

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.

Fair point. The base branch already uses Ogre::WindowEventUtilities (addWindowEventListener/removeWindowEventListener in ODFrameListener.cpp, _addRenderWindow and messagePump in ODApplication.cpp); this PR only reuses the same calls for the replacement window. A move to a newer Ogre would have to replace that windowing/event code as a whole, so it is out of scope for this change.

Comment thread source/modes/GameMode.cpp
Comment thread source/modes/GameMode.cpp
const double rightIntensity = mouseOverGui ? 0.0 : getAutoscrollIntensity(arg.state.X.abs, arg.state.width, false);
const double topIntensity = mouseOverGui ? 0.0 : getAutoscrollIntensity(arg.state.Y.abs, arg.state.height, true);
const double bottomIntensity = mouseOverGui ? 0.0 : getAutoscrollIntensity(arg.state.Y.abs, arg.state.height, false);

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.

OKI this commit is NO NO , because
const double bottomIntensity = mouseOverGui ? 0.0 : getAutoscrollIntensity(arg.state.Y.abs, arg.state.height, false);
This will never trigger bottomIntensity to be above 0, because when pointing downwards in our game , you have always mouse over GUI !!!

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: the bottom edge is no longer blocked by the GUI hit test, so it scrolls again (left, right and top keep the check). Rokk001@da16d6d

Comment thread source/render/Gui.h
bool playButtonClickSound(const CEGUI::EventArgs& e = {});

private:
struct WindowScaleData

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.

I know those are private parts of a class, but a comment or two on what's what would be welcomed.

@Rokk001 Rokk001 Sep 30, 2026 •

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.

Added short comments to the private members and the WindowScaleData struct of Gui: Rokk001@4599865 (follow-up fixing the Doxygen tags: Rokk001@d01fd56)

Comment thread source/render/Gui.cpp
const float LAYOUT_DESIGN_HEIGHT = 768.0f;
const float FONT_DESIGN_WIDTH = 800.0f;
const float FONT_DESIGN_HEIGHT = 600.0f;

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.

So you decided to represent the widgets only by their pixel sizes -- the offset value.
That is when scaling CEGUI::UDim "Scale" is preserved, Offset is scaled.
Strange that CEGUI does not offer that functionality, and we have to code it by hand.

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.

Ahh not only by offset , "SettingsWindow" seems to be a honourable counterexample.....

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.

The Scale part of a UDim is indeed used by the layouts, but the scaling code only ever changes the Offset. scaleDimension multiplies d_offset and leaves d_scale unchanged, and it is applied to the area, min size and max size of every registered window (plus the TabControl tab height). Fonts and images are handled separately in updateResourceScaling via CEGUI's auto-scaling.

SettingsWindow is a mixed case, not an exception: its area is {{0.5,-272.5},{0.5,-250},{0.5,272.5},{0.5,250}}, so the 0.5 Scale part (centering) stays as it is and only the +-272.5 / +-250 pixel offsets are scaled. The window therefore stays centered and its pixel size follows the UI scale.

Comment thread source/render/Gui.cpp
|| name.compare(0, 17, "ODMainMenuButton/") == 0
|| name.compare(0, 7, "ODLogo/") == 0;
}

@tomluchowski tomluchowski Sep 30, 2026 •

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.

and (std::string scaleFormattedImageSizes(const CEGUI::String& ceguiText, float scale)) funtion what does it do ?

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.

It rescales the pixel sizes inside CEGUI formatted-text image tags. In a window text such as [image-size='w:32 h:32'], it finds each w: and h: value, multiplies it by scale, rounds it (minimum 1) and writes it back, so inline images in labels grow and shrink together with the rest of the UI. It returns the modified copy of the text; the original is kept in WindowScaleData::text and re-scaled from there on every scale change.

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.

Follow-up: the doc comment above scaleFormattedImageSizes is now in Rokk001@5f26921

Comment thread source/render/Gui.cpp
++valueEnd;

float value = 0.0f;
std::istringstream valueParser(text.substr(valueStart, valueEnd - valueStart));

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.

atof is passe these days ? or actually it should be array to double .....
#include

double atof( const char * str ); but I haven't wrote a parser long time , so maybe I am mistaken :)
Anyway: one can read out what does that function do , but please write above it what does it do...

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.

atof would work for the parse itself, but on invalid input it silently returns 0 (no error), whereas the istringstream extraction lets us skip a malformed value and leave the tag untouched, so I kept it. Added a comment above the function describing what it does: Rokk001@5f26921

Comment thread source/render/Gui.cpp
++font;
}

const CEGUI::Sizef imageNativeResolution(LAYOUT_DESIGN_WIDTH / mUserScale,

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.

Strange that here we divide , not multiply by mUserScale .... ?

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.

Widget offsets are multiplied by the scale (applyScale), but fonts and images are scaled by CEGUI itself: with ASM_Min auto-scaling the rendered size is displaySize / nativeResolution. To make fonts and images grow by mUserScale, the native resolution therefore has to shrink by the same factor, so FONT_DESIGN_WIDTH / mUserScale is the matching operation. Multiplying would make the fonts and images smaller as the user scale increases, while the widgets get larger.

Comment thread source/render/CreaturePortrait.cpp Outdated
{
struct PortraitScene
{
Ogre::SceneManager* scene = Ogre::Root::getSingleton().createSceneManager("DefaultSceneManager");

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.

Shouldn't that be in ctor, as God commanded ?

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.

Agreed, the scene manager is now created in the PortraitScene constructor's initializer list instead of a default member initializer: Rokk001@f94d8d0ef

mCarriedEntity (nullptr),
mMoodCooldownTurns (0),
mMoodValue (CreatureMoodLevel::Neutral),
mMoodValue (gameMap->isServerGameMap() ? CreatureMoodLevel::Neutral : CreatureMoodLevel::Unknown),

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.

So why two diffrent moods for Creature , one neutral when starting on server , and one unkown when on local side of the game ?

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.

On the server map the mood is real and starts at Neutral until the mood logic updates it. A client copy only knows a mood if the server sent one: importMoodFromPacket resets it to Unknown, and enemy creatures are exported as Unknown. So Unknown on the client means "not known here" rather than a wrong Neutral.


void Creature::exportMoodToPacket(ODPacket& os, const Seat* seat) const
{
if(!ODServer::getSingleton().supportsCreatureMood(seat->getPlayer()))

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.

I could increase the OD version , and assume that from now on every version supports CreatureMoods. THat's the option, what do you think ?

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.

The flags are optional trailing fields in the nick/start-game packets: a client that does not send them is treated as not supporting them, and the server then omits the mood data for it, so older clients and servers keep working. If you prefer to drop the flags, I can bump the version and assume mood support from then on - just say so.

Comment thread source/entities/Creature.cpp Outdated
if(!mActions.empty())
activity.action = mActions.back()->getType();

for(auto it = mActions.rbegin(); it != mActions.rend(); ++it)

@tomluchowski tomluchowski Oct 3, 2026 •

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.

Ahh thats a reverse iterator, so activity.action is what creature currently does and activity.task is the first ( from back :) ) meaningful action apart from walkToTile and parkToTile. Naahh there is "break; " ... Can you in comments of CreatureActivity explain what are those two fields ?

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.

[Yeah somehow the ](std::vector<std::unique_ptr> mActions;) back is front , front is back. So the insertion removal goes by the end of this vector. I just noticed.

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.

Documented the CreatureActivity fields (action, task, back-to-front processing of mActions); this also covers the follow-up 4172677835: Rokk001@f777975

}
return activity;
}

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.

So it seems that this function method can end up with activity.task == activity.action ??? ( when creature does something meaningful ... )

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.

Yes. If the top action is not walkToTile/parkToTile (for example useRoom or an attack), action and task hold the same type. They only differ while the creature is walking or parking towards something. The field comments I am adding to CreatureActivity.h will state this.

Explain action, task and the back-to-front processing of mActions.
Comment thread source/game/CreaturePanelData.cpp Outdated
switch(criterion)
{
case CreaturePanelCriterion::Idle: return idle;
case CreaturePanelCriterion::Working:

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.

What a strange way of grouping bool flags, either all formulas after every return OR create another boolean flag 'working' :)

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.

Good point, thanks: the Working condition is now a named const bool next to the other flags, behaviour unchanged. Rokk001@bf5b732

This branch has not been deployed

No deployments
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.

3 participants