Characters: campaigns, "Spielen" main menu, story unlocks
- One world per campaign and difficulty in a character file (sections ancaria<d> / underworld<d>; old world<d> renamed when read). Difficulty unlocks by the shared hero's story completions (hero header +0xed..), shared by both campaigns. - Main menu: "Kampagne Ancaria", "Kampagne Underworld" and "Spielstaende" become "Spielen" (mods/mainmenu.cpp). The load window lists all characters, the world dialog offers both campaigns, "Neuer Charakter" asks for the campaign and opens character creation. - Character window: no Underworld import mode (new Underworld heroes, all classes, slots light up), no "Importieren"; load window without "Speichern". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
5bccb0734e
commit
618288e310
7 files changed
+498
-109
No files matched your search
+20
-4
@@ -2,6 +2,7 @@
|
||||
// Character files (docs/characters.md): one file per character with its shared
|
||||
// hero and one world per difficulty, plus reading the game's save files.
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@@ -16,6 +17,11 @@ using Bytes = std::vector<uint8_t>;
|
||||
// Bronze, Silber, Gold, Platin, Niob (g_gameLevel 0..4).
|
||||
constexpr int kDifficultyCount = 5;
|
||||
|
||||
// The campaigns (g_campaign 0x182cdc8): Ancaria 0, Underworld 1.
|
||||
constexpr int kCampaignAncaria = 0;
|
||||
constexpr int kCampaignUnderworld = 1;
|
||||
constexpr int kCampaignCount = 2;
|
||||
|
||||
// What the game's save files (GAME.PAK "AMS", hero files .pax "AMH") say about
|
||||
// their hero: header 0x100 bytes, then a table of 64 chunks {type, offset,
|
||||
// size}; chunk 0xc3 is the hero header.
|
||||
@@ -23,9 +29,12 @@ struct SaveInfo {
|
||||
std::wstring name; // hero header +8, UTF-16
|
||||
uint32_t heroClass = 0; // hero header +4: 1 Seraphim .. 9 Daemonin
|
||||
uint32_t level = 0; // hero header +0
|
||||
// Story completions per difficulty (hero header +0xed.., cStatsManager
|
||||
// +0x5704; the hero file's are the hero's, loadHero sets them).
|
||||
std::array<uint8_t, kDifficultyCount> storyCompleted{};
|
||||
bool world = false; // a save game (AMS), not a hero file
|
||||
int difficulty = 0; // AMS header +0x84 (g_gameLevel)
|
||||
uint32_t mode = 0; // AMS header +0x24: 2 normal, 3 hardcore
|
||||
int campaign = kCampaignAncaria; // AMS header +0x24 (CampaignSaveMode)
|
||||
bool multiplayer = false; // AMS header +0x2c
|
||||
uint64_t savedAt = 0; // AMS header +0x5c SYSTEMTIME, as FILETIME
|
||||
};
|
||||
@@ -37,10 +46,15 @@ std::optional<SaveInfo> ReadSaveInfo(const Bytes& file);
|
||||
constexpr size_t kHeroNameMaxChars = 63;
|
||||
bool SetHeroName(Bytes& file, const std::wstring& name);
|
||||
|
||||
// The save header's campaign field (+0x24): 2 Ancaria, 3 Underworld (written
|
||||
// at 0x61a5c5, read back by cEngine::Load 0x61bafd).
|
||||
uint32_t CampaignSaveMode(int campaign);
|
||||
|
||||
// A small save game the game's load window can list: `world`'s header with its
|
||||
// date, its thumbnail and world time, and `description` (ANSI, up to 63
|
||||
// characters) as the save's name.
|
||||
Bytes BuildPreview(const Bytes& world, const std::string& description);
|
||||
// characters) as the save's name. The load window lists only saves of the
|
||||
// running campaign (0x71e2b9): the preview names `campaign`.
|
||||
Bytes BuildPreview(const Bytes& world, const std::string& description, int campaign);
|
||||
|
||||
// Reading and writing whole files (paths in the game's code page).
|
||||
std::optional<Bytes> ReadFile(const std::string& path);
|
||||
@@ -77,6 +91,8 @@ private:
|
||||
std::vector<Section> sections_;
|
||||
};
|
||||
|
||||
std::string WorldSection(int difficulty);
|
||||
// A world's section: "ancaria<d>" / "underworld<d>", d = g_gameLevel. Sections
|
||||
// are named, not slotted: more difficulties (or campaigns) are more sections.
|
||||
std::string WorldSection(int campaign, int difficulty);
|
||||
|
||||
} // namespace mods::characters
|
||||
Reference in new issue
Block a user