Files
sacred_k/src/game/engine/events.cpp
T
khallaandClaude Opus 5.5 6ca96d54b4 Shared chest tabs, rune stacks and rune master, Q pickup, Kettenblitz tooltip and range
- Shared chest: tabs "Truhe"/"Gemeinsam" as a column left of the grid
  (game tab sheet), grids placed every tick, persistent across heroes
- Inventory tab texts capitalized (RegisterText / CapitalizeText)
- Combat-art runes stack like potions (itemstack, renamed from potionstack)
- Rune master: any rune for any number of runes, price by rune count
  (4 runes = 1,000, x10 per missing rune), bought rune goes into the
  backpack, Buy disabled without runes, gold or room, last rune remembered
  per hero (saved), the chosen rune's name below its icon, dropping a
  stack gives up one rune
- Keys: the old key handling no longer takes over after the save screen
  (text field focus only counts while its dialog is shown)
- Esc crash: tooltips forgotten when the game destroys them
- Pick up (Q) sends the pickup order directly; search reach doubled
- Kettenblitz: tooltip shows hits per chain, chains, cast and jump range;
  cast range 900 (longer ray segments), jumps half of that

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 03:40:42 +02:00

90 lines
2.3 KiB
C++

#include "game/engine/events.h"
#include <vector>
#include "re/hook.h"
namespace game {
namespace {
std::vector<EngineTickFn>& TickCallbacks() {
static std::vector<EngineTickFn> callbacks;
return callbacks;
}
std::vector<SaveGameLoadedFn>& LoadedCallbacks() {
static std::vector<SaveGameLoadedFn> callbacks;
return callbacks;
}
std::vector<GameEndFn>& GameEndCallbacks() {
static std::vector<GameEndFn> callbacks;
return callbacks;
}
std::vector<GameStartFn>& GameStartCallbacks() {
static std::vector<GameStartFn> callbacks;
return callbacks;
}
} // namespace
void OnEngineTick(EngineTickFn callback) {
TickCallbacks().push_back(callback);
}
void OnSaveGameLoaded(SaveGameLoadedFn callback) {
LoadedCallbacks().push_back(callback);
}
void OnGameEnd(GameEndFn callback) {
GameEndCallbacks().push_back(callback);
}
void OnGameStart(GameStartFn callback) {
GameStartCallbacks().push_back(callback);
}
void NotifySaveGameLoaded() {
for (SaveGameLoadedFn callback : LoadedCallbacks()) {
callback();
}
}
// cEngine::Tick (0x60f1d0, 10 Hz): processes queued events, then the mouse's
// hold-to-move re-aim orders.
void __fastcall Engine_Tick(void* self, void* edx, void* event);
RE_HOOK(0x0060f1d0, Engine_Tick);
void __fastcall Engine_Tick(void* self, void* edx, void* event) {
Engine_Tick_original(self, edx, event);
if (self) {
for (EngineTickFn callback : TickCallbacks()) {
callback(static_cast<uint8_t*>(self));
}
}
}
// cEngine::cEngine (0x60ac70, ret 4, called once at 0x60d7a3): the save game
// and the hero are loaded inside it.
void* __fastcall Engine_Construct(void* self, void* edx, uint32_t arg);
RE_HOOK(0x0060ac70, Engine_Construct);
void* __fastcall Engine_Construct(void* self, void* edx, uint32_t arg) {
void* result = Engine_Construct_original(self, edx, arg);
for (GameStartFn callback : GameStartCallbacks()) {
callback();
}
return result;
}
// cEngine::~cEngine (0x60d5e0, plain ret).
void __fastcall Engine_Destruct(void* self, void* edx);
RE_HOOK(0x0060d5e0, Engine_Destruct);
void __fastcall Engine_Destruct(void* self, void* edx) {
for (GameEndFn callback : GameEndCallbacks()) {
callback();
}
Engine_Destruct_original(self, edx);
}
} // namespace game