Skills, auto-attack, aura and chain lightning rework, Esc, cache fix
- Skill bar: no art selection; slot 0 is the right-click art, keys 0-6 cast slots directly; cooldown labels in exact seconds; slot 0 set apart. - Arts regenerate on their own (casting no longer refills the group). - Auto-attack after targeting or casting at a hostile creature, at the speed of a held button. - Auras can be recast while active (renew, no second visual). - Blicke wie Klingen hits every enemy in range, physical, with life leech. - Blitzschlag steers the game's own chain: 3 + level/5 hits, 1 + level/10 chains, re-hits a lone enemy. - Weapon sets: click selects, drag only from the selected set; the leftmost set is clickable (signed hit tests). - Esc closes open windows before opening the game menu. - HUD: standalone buttons and texts are centred. - Record cache sized for widened views (town lag while walking). - Shared game code: orders, fx events, text drawing, cooldowns, effects. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
f721d2a486
commit
1fd89524d3
41 files changed
+2347
-214
No files matched your search
@@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
// Creature orders (cEvent_creature) and the engine fields around them
|
||||
// (docs/wasd.md, docs/auto-attack.md).
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "game/engine/objects.h"
|
||||
|
||||
namespace game {
|
||||
|
||||
constexpr uint32_t kEventCreatureVtable = 0x0088e95c;
|
||||
|
||||
// cEngine fields.
|
||||
constexpr size_t kEngineFlags = 0x54; // 0x10 mouse button held (hold-to-move), 0x4000 input blocked
|
||||
constexpr size_t kEngineControlledId = 0x13c; // object id of the controlled creature
|
||||
|
||||
// Order commands.
|
||||
constexpr uint32_t kOrderWalk = 4;
|
||||
constexpr uint32_t kOrderAttack = 7; // left click on a creature: one attack
|
||||
constexpr uint32_t kOrderUseArt = 0x105;
|
||||
|
||||
// cEvent_creature (0x44 bytes). The meaning of +0x10..+0x20 depends on the
|
||||
// command:
|
||||
// walk (4): +0x14 2 = ground point at +0x18 / +0x1c (0: stop),
|
||||
// +0x20 1 = re-aim while a mouse button is held
|
||||
// attack (7): +0x18 target object id, +0x1c / +0x20 its position
|
||||
// use art (105): +0x10 target object id (0: ground / self), +0x18 / +0x1c
|
||||
// ground point, +0x30 art id
|
||||
// hit (5): +8 sender, +0xc / +0x10 target, +0x1c attacker id (the
|
||||
// damage code credits it: life leech, ...; aura hits leave
|
||||
// it 0), +0x20 four u16 damage slots (physical, ...),
|
||||
// +0x30 float damage factor
|
||||
struct CreatureOrder {
|
||||
uint32_t vtable = kEventCreatureVtable;
|
||||
uint32_t command = 0;
|
||||
uint32_t unknown08[2] = {};
|
||||
uint32_t target = 0; // +0x10
|
||||
uint32_t groundTarget = 0; // +0x14
|
||||
int32_t a = 0; // +0x18
|
||||
int32_t b = 0; // +0x1c
|
||||
int32_t c = 0; // +0x20
|
||||
uint32_t unknown24[3] = {};
|
||||
uint32_t art = 0; // +0x30
|
||||
uint32_t unknown34[4] = {};
|
||||
};
|
||||
static_assert(sizeof(CreatureOrder) == 0x44);
|
||||
static_assert(offsetof(CreatureOrder, art) == 0x30);
|
||||
|
||||
// cEngine::SendCreatureOrder (0x6170d0): snaps ground targets, then
|
||||
// creature->vf18 (cCreature::ReceiveOrder 0x52e400).
|
||||
inline void SendCreatureOrder(uint8_t* engine, void* creature, CreatureOrder& order) {
|
||||
using Fn = void(__fastcall*)(void* engine, void* edx, void* creature, void* event);
|
||||
reinterpret_cast<Fn>(0x006170d0)(engine, nullptr, creature, &order);
|
||||
}
|
||||
|
||||
inline void* ControlledCreature(uint8_t* engine) {
|
||||
return GetCreature(*reinterpret_cast<uint32_t*>(engine + kEngineControlledId));
|
||||
}
|
||||
|
||||
// Called before cCreature::ReceiveOrder (0x52e400) handles an order (any
|
||||
// creature; `caller` is the code that sent it). Register at startup.
|
||||
using CreatureOrderFn = void (*)(void* creature, const CreatureOrder& order, void* caller);
|
||||
void OnCreatureOrder(CreatureOrderFn callback);
|
||||
|
||||
// Creature fields.
|
||||
constexpr size_t kCreatureId = 0xc;
|
||||
constexpr size_t kCreatureX = 0x1c;
|
||||
constexpr size_t kCreatureY = 0x20;
|
||||
// +0x150: low word the current action (0 none, 1 art, 3 attack, 8 ...), high
|
||||
// word its phase; 0x10000 when standing idle.
|
||||
constexpr size_t kCreatureAction = 0x150;
|
||||
|
||||
// cCreature::IsAlive (0x548ef0): not dying (+0x150 == 6), not dead (+0xfc ==
|
||||
// 9), health (+0x4d8) left.
|
||||
inline bool IsAlive(void* creature) {
|
||||
using Fn = char(__fastcall*)(void*, void*);
|
||||
return reinterpret_cast<Fn>(0x00548ef0)(creature, nullptr) != 0;
|
||||
}
|
||||
|
||||
// cCreature::IsHostileTo (0x548dd0, (creature)): `other` is alive and its
|
||||
// faction is hostile to this creature's (0x4226f0 / 0x423450). NPCs such as
|
||||
// traders are not hostile (and not allies either).
|
||||
inline bool IsHostileTo(void* creature, void* other) {
|
||||
using Fn = char(__fastcall*)(void*, void*, void*);
|
||||
return other && reinterpret_cast<Fn>(0x00548dd0)(creature, nullptr, other) != 0;
|
||||
}
|
||||
|
||||
// cCreature::DistanceTo (0x536f20, (object id)).
|
||||
inline uint32_t DistanceTo(void* creature, uint32_t id) {
|
||||
using Fn = uint32_t(__fastcall*)(void*, void*, uint32_t);
|
||||
return reinterpret_cast<Fn>(0x00536f20)(creature, nullptr, id);
|
||||
}
|
||||
|
||||
inline int32_t CreatureField(const void* creature, size_t offset) {
|
||||
return *reinterpret_cast<const int32_t*>(static_cast<const uint8_t*>(creature) + offset);
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
Reference in new issue
Block a user