Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

5
engines/mm/POTFILES Normal file
View File

@@ -0,0 +1,5 @@
engines/mm/detection.cpp
engines/mm/metaengine.cpp
engines/mm/mm1/metaengine.cpp
engines/mm/mm1/mm1.cpp
engines/mm/xeen/saves.cpp

View File

@@ -0,0 +1,5 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
add_engine mm "Might and Magic" yes "mm1 xeen" "" "midi"
add_engine mm1 "Might and Magic 1" yes
add_engine xeen "Might and Magic Xeen" yes

7
engines/mm/credits.pl Normal file
View File

@@ -0,0 +1,7 @@
begin_section("MM (Xeen)");
add_person("Paul Gilbert", "dreammaster", "");
add_person("Benoit Pierre", "benoit-pierre", "");
add_person("", "TheDrakeRaider", "(M&M1 Gfx Mod)");
add_person("David Goldsmith", "WizardStan", "(Xeen analysis)");
add_person("Matt Taylor", "", "(Xeen analysis)");
end_section();

72
engines/mm/detection.cpp Normal file
View File

@@ -0,0 +1,72 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "base/plugins.h"
#include "engines/advancedDetector.h"
#include "common/translation.h"
#include "mm/detection.h"
#include "mm/mm.h"
static const PlainGameDescriptor MIGHT_AND_MAGIC_GAMES[] = {
{ "mm1", "Might and Magic: Book One - Secret of the Inner Sanctum"},
{ "mm1_enh", "Might and Magic: Book One - Secret of the Inner Sanctum - Enhanced"},
{ "cloudsofxeen", "Might and Magic IV: Clouds of Xeen" },
{ "darksideofxeen", "Might and Magic V: Darkside of Xeen" },
{ "worldofxeen", "Might and Magic: World of Xeen" },
{ "swordsofxeen", "Might and Magic: Swords of Xeen" },
{ 0, 0 }
};
static const DebugChannelDef DEBUG_FLAT_LIST[] = {
{ MM::kDebugPath, "Path", "Pathfinding debug level" },
{ MM::kDebugScripts, "scripts", "Game scripts" },
{ MM::kDebugGraphics, "graphics", "Graphics handling" },
{ MM::kDebugSound, "sound", "Sound processing" },
DEBUG_CHANNEL_END
};
#include "mm/detection_tables.h"
class MMMetaEngineDetection : public AdvancedMetaEngineDetection<MM::MightAndMagicGameDescription> {
public:
MMMetaEngineDetection() : AdvancedMetaEngineDetection(MM::GAME_DESCRIPTIONS,
MIGHT_AND_MAGIC_GAMES) {
_maxScanDepth = 3;
}
const char *getName() const override {
return "mm";
}
const char *getEngineName() const override {
return "Might & Magic";
}
const char *getOriginalCopyright() const override {
return "Might And Magic games (C) 1986-1993 New World Computing, Inc.";
}
const DebugChannelDef *getDebugChannels() const override {
return DEBUG_FLAT_LIST;
}
};
REGISTER_PLUGIN_STATIC(MM_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, MMMetaEngineDetection);

60
engines/mm/detection.h Normal file
View File

@@ -0,0 +1,60 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM_DETECTION_H
#define MM_DETECTION_H
#include "engines/advancedDetector.h"
namespace MM {
enum {
GType_MightAndMagic1,
GType_Clouds,
GType_DarkSide,
GType_WorldOfXeen,
GType_Swords
};
enum GameFeature {
GF_NONE = 0,
GF_ENHANCED = 1,
GF_GFX_PACK = 2
};
struct MightAndMagicGameDescription {
AD_GAME_DESCRIPTION_HELPERS(desc);
ADGameDescription desc;
int gameID;
uint32 features;
};
#define GAMEOPTION_SHOW_ITEM_COSTS GUIO_GAMEOPTIONS1
#define GAMEOPTION_DURABLE_ARMOR GUIO_GAMEOPTIONS2
#define GAMEOPTION_SHOW_HP_SP_BARS GUIO_GAMEOPTIONS3
#define GAMEOPTION_COPY_PROTECTION GUIO_GAMEOPTIONS4
#define GAMEOPTION_TTS GUIO_GAMEOPTIONS5
} // namespace MM
#endif // MM_DETECTION_H

View File

@@ -0,0 +1,411 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "engines/advancedDetector.h"
#include "engines/mm/detection.h"
namespace MM {
#define GUIO_XEEN GUIO5(GAMEOPTION_SHOW_ITEM_COSTS, GAMEOPTION_DURABLE_ARMOR, GAMEOPTION_SHOW_HP_SP_BARS, GAMEOPTION_COPY_PROTECTION, GAMEOPTION_TTS)
static const MightAndMagicGameDescription GAME_DESCRIPTIONS[] = {
{
// Might and Magic 1
{
"mm1",
nullptr,
AD_ENTRY1s("wallpix.dta", "86a7ef03fd5bf434d83012b07fa92680", 123059),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_TTS)
},
GType_MightAndMagic1,
0
},
{
// Might and Magic 1 (Enhanced)
{
"mm1_enh",
nullptr,
AD_ENTRY1s("wallpix.dta", "86a7ef03fd5bf434d83012b07fa92680", 123059),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_TTS)
},
GType_MightAndMagic1,
GF_ENHANCED
},
{
// Might and Magic 1 (Gfx Pack)
// This isn't directly supported, because using it would allow playing
// the game without actually having the original game files
{
"mm1",
nullptr,
AD_ENTRY1s("wallpix.dta", "64af4dad1f86aedf307e8184a9f12ba9", 164220),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_TTS)
},
GType_MightAndMagic1,
GF_GFX_PACK
},
{
// World of Xeen
{
"worldofxeen",
nullptr,
AD_ENTRY2s("xeen.cc", "0cffbab533d9afe140e69ec93096f43e", 13435646,
"dark.cc", "df194483ecea6abc0511637d712ced7c", 11217676),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO_XEEN
},
GType_WorldOfXeen,
0
},
{
// World of Xeen (Monster Spawn Mod v1.0), Bugreport #12714
{
"worldofxeen",
"Monster Spawn Mod v1.0",
AD_ENTRY2s("xeen.cc", "37767811a52fb54a8f2be3b45acc91a9", 13536623,
"dark.cc", "a4dcb0731ad3818fdd908f17230ee773", 11222393),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO_XEEN
},
GType_WorldOfXeen,
0
},
{
// World of Xeen (Bestseller Games Magazine #6 + #8 German)
{
"worldofxeen",
nullptr,
{
{"xeen.cc", 0, "f4e4b3ddc43bd102dbe1637f480f1fa1", 13214150},
{"dark.cc", 0, "84a86bbbc5f2fe96c0b0325485ed8203", 11173657},
{"intro.cc", 0, "e47a7ab0223cf32b2d87eed91d024c35", 8899953},
AD_LISTEND
},
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO_XEEN
},
GType_WorldOfXeen,
0
},
{
// World of Xeen (GOG German)
{
"worldofxeen",
nullptr,
AD_ENTRY2s("xeen.cc", "f4e4b3ddc43bd102dbe1637f480f1fa1", 13214150,
"dark.cc", "84a86bbbc5f2fe96c0b0325485ed8203", 11168312),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO_XEEN
},
GType_WorldOfXeen,
0
},
{
// World of Xeen (2 CD talkie version)
{
"worldofxeen",
"CD",
AD_ENTRY2s("xeen.cc", "964078c53f649937ce9a1a3596ce3d9f", 13438429,
"dark.cc", "7f755ce39ea614fa6adb016f8bfc6e43", 11288403),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_CD,
GUIO_XEEN
},
GType_WorldOfXeen,
0
},
{
// World of Xeen French- French untested
// https://bugs.scummvm.org/ticket/12559
{
"worldofxeen",
nullptr,
AD_ENTRY2s("xeen.cc", "b9bb33bbd3783e7930ddedb189d7ba54", 13203565,
"dark.cc", "437618b6b5e76f174719dcb529edbfbb", 11152923),
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO_XEEN
},
GType_WorldOfXeen,
0
},
{
// World of Xeen, Spanish fan-translation
// https://bugs.scummvm.org/ticket/14976
{
"worldofxeen",
"CD",
AD_ENTRY2s("xeen.cc", "8ca98eba894b104e82963b0f4952e795", 13445166,
"dark.cc", "9f319d7457e822f2484fa1daa26b2687", 11331280),
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_CD,
GUIO_XEEN
},
GType_WorldOfXeen,
0
},
{
// World of Xeen (DOS non-interactive demo)
{
"worldofxeen",
"Non-Interactive Demo",
AD_ENTRY1s("demo.cc", "bcd6775323833dc8dd2d930c3b98da03", 4556916),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_UNSTABLE | ADGF_DEMO,
GUIO0()
},
GType_WorldOfXeen,
0
},
{
// Clouds of Xeen
{
"cloudsofxeen",
nullptr,
AD_ENTRY1s("xeen.cc", "0cffbab533d9afe140e69ec93096f43e", 13435646),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO_XEEN
},
GType_Clouds,
0
},
{
// Clouds of Xeen (GOG, Bestseller Games Magazine #6 German)
{
"cloudsofxeen",
nullptr,
AD_ENTRY1s("xeen.cc", "f4e4b3ddc43bd102dbe1637f480f1fa1", 13214150),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO_XEEN
},
GType_Clouds,
0
},
{
// Clouds of Xeen, Russian fan-translation
{
"cloudsofxeen",
nullptr,
AD_ENTRY1s("xeen.cc", "1dead179f65bbb1ba11dd51d69768489", 13417817),
Common::RU_RUS,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO_XEEN
},
GType_Clouds,
0
},
{
// Clouds of Xeen (Chinese)
{
"cloudsofxeen",
nullptr,
AD_ENTRY1s("xeen.cc", "1fea0587fe139b13f4effa2b5791bd80", 13859999),
Common::ZH_TWN,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO_XEEN
},
GType_Clouds,
0
},
{
// Clouds of Xeen- French untested
// https://bugs.scummvm.org/ticket/12557
{
"cloudsofxeen",
nullptr,
AD_ENTRY1s("xeen.cc", "b9bb33bbd3783e7930ddedb189d7ba54", 13203565),
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO_XEEN
},
GType_Clouds,
0
},
{
// Clouds of Xeen (DOS non-interactive demo)
{
"cloudsofxeen",
"Non-Interactive Demo",
AD_ENTRY1s("logobak.raw", "68425e43f4b0c74f5ff448d1f3c9855c", 64000),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_UNSTABLE | ADGF_DEMO,
GUIO0()
},
GType_Clouds,
0
},
{
// Darkside of Xeen
{
"darksideofxeen",
nullptr,
AD_ENTRY1s("dark.cc", "df194483ecea6abc0511637d712ced7c", 11217676),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO_XEEN
},
GType_DarkSide,
0
},
{
// Darkside of Xeen (GOG German)
{
"darksideofxeen",
nullptr,
AD_ENTRY1s("dark.cc", "84a86bbbc5f2fe96c0b0325485ed8203", 11168312),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO_XEEN
},
GType_DarkSide,
0
},
{
// Darkside of Xeen (Bestseller Games Magazine #8 German)
{
"darksideofxeen",
nullptr,
AD_ENTRY2s("dark.cc", "84a86bbbc5f2fe96c0b0325485ed8203", 11173657,
"intro.cc", "e47a7ab0223cf32b2d87eed91d024c35", 8899953),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO_XEEN
},
GType_DarkSide,
0
},
{
// Darkside of Xeen (Chinese)
{
"darksideofxeen",
nullptr,
AD_ENTRY1s("dark.cc", "4dcbcdb0be885afebc2ab6c9e60c434e", 11137412),
Common::ZH_TWN,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO_XEEN
},
GType_DarkSide,
0
},
{
// Darkside of Xeen- French untested
// https://bugs.scummvm.org/ticket/12558
{
"darksideofxeen",
nullptr,
AD_ENTRY1s("dark.cc", "437618b6b5e76f174719dcb529edbfbb", 11152923),
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO_XEEN
},
GType_DarkSide,
0
},
{
// Darkside of Xeen (DOS non-interactive demo)
{
"darksideofxeen",
"Non-Interactive Demo",
AD_ENTRY1s("demo.cc", "2c320db380a479652a27a604c7c357b6", 5017135),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_UNSTABLE | ADGF_DEMO,
GUIO0()
},
GType_DarkSide,
0
},
{
// Swords of Xeen
{
"swordsofxeen",
nullptr,
AD_ENTRY1s("swrd.cc", "0d51c3457070cc7d1a596da9241924a5", 13026924),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO_XEEN
},
GType_Swords,
0
},
{ AD_TABLE_END_MARKER, 0, 0 }
};
} // End of namespace MM

213
engines/mm/metaengine.cpp Normal file
View File

@@ -0,0 +1,213 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymap.h"
#include "backends/keymapper/standard-actions.h"
#include "base/plugins.h"
#include "engines/advancedDetector.h"
#include "common/savefile.h"
#include "common/system.h"
#include "common/translation.h"
#include "mm/detection.h"
#ifdef ENABLE_MM1
#include "mm/mm1/mm1.h"
#endif
#ifdef ENABLE_XEEN
#include "mm/xeen/xeen.h"
#include "mm/xeen/metaengine.h"
#include "mm/xeen/worldofxeen/worldofxeen.h"
#include "mm/xeen/swordsofxeen/swordsofxeen.h"
#endif
static const ADExtraGuiOptionsMap optionsList[] = {
{
GAMEOPTION_COPY_PROTECTION,
{
_s("Enable copy protection"),
_s("Enable any copy protection that would otherwise be bypassed by default."),
"copy_protection",
false,
0,
0
},
},
{
GAMEOPTION_SHOW_ITEM_COSTS,
{
_s("Show item costs in standard inventory mode"),
_s("Shows item costs in standard inventory mode, allowing the value of items to be compared"),
"ShowItemCosts",
false,
0,
0
}
},
{
GAMEOPTION_DURABLE_ARMOR,
{
_s("More durable armor"),
_s("Armor won't break until character is at -80HP, rather than merely -10HP"),
"DurableArmor",
false,
0,
0
}
},
{
GAMEOPTION_SHOW_HP_SP_BARS,
{
_s("Hitpoint bars"),
_s("Replace a colored gem with bars for hit points and spell points."),
"ShowHpSpBars",
false,
0,
0
}
},
#ifdef USE_TTS
{
GAMEOPTION_TTS,
{
_s("Enable Text to Speech"),
_s("Use TTS to read text in the game (if TTS is available)"),
"tts_enabled",
false,
0,
0
}
},
#endif
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
class MMMetaEngine : public AdvancedMetaEngine<MM::MightAndMagicGameDescription> {
private:
/**
* Gets the game Id given a target string
*/
static Common::String getGameId(const Common::String &target);
/**
* Returns true if the game is a Xeen game
*/
static bool isXeenGame(const Common::String &target);
public:
const char *getName() const override {
return "mm";
}
bool hasFeature(MetaEngineFeature f) const override;
Common::Error createInstance(OSystem *syst, Engine **engine, const MM::MightAndMagicGameDescription *desc) const override;
SaveStateList listSaves(const char *target) const override;
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
Common::KeymapArray initKeymaps(const char *target) const override;
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
return optionsList;
}
};
bool MMMetaEngine::hasFeature(MetaEngineFeature f) const {
return checkExtendedSaves(f) ||
(f == kSupportsLoadingDuringStartup);
}
Common::Error MMMetaEngine::createInstance(OSystem *syst, Engine **engine, const MM::MightAndMagicGameDescription *gd) const {
switch (gd->gameID) {
#ifdef ENABLE_MM1
case MM::GType_MightAndMagic1:
*engine = new MM::MM1::MM1Engine(syst, gd);
break;
#endif
#ifdef ENABLE_XEEN
case MM::GType_Clouds:
case MM::GType_DarkSide:
case MM::GType_WorldOfXeen:
*engine = new MM::Xeen::WorldOfXeen::WorldOfXeenEngine(syst, gd);
break;
case MM::GType_Swords:
*engine = new MM::Xeen::SwordsOfXeen::SwordsOfXeenEngine(syst, gd);
break;
#endif
default:
return Common::kUnsupportedGameidError;
}
return Common::kNoError;
}
SaveStateList MMMetaEngine::listSaves(const char *target) const {
#ifdef ENABLE_XEEN
if (isXeenGame(target))
// Fallback original code for Xeen
return MM::Xeen::XeenMetaEngine::listSaves(this, target);
#endif
return AdvancedMetaEngine::listSaves(target);
}
SaveStateDescriptor MMMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
#ifdef ENABLE_XEEN
if (isXeenGame(target))
// Fallback original code for Xeen
return MM::Xeen::XeenMetaEngine::querySaveMetaInfos(this, target, slot);
#endif
return AdvancedMetaEngine::querySaveMetaInfos(target, slot);
}
Common::KeymapArray MMMetaEngine::initKeymaps(const char *target) const {
#ifdef ENABLE_MM1
const Common::String gameId = getGameId(target);
if (gameId == "mm1" || gameId == "mm1_enh")
return MM::MM1::MetaEngine::initKeymaps();
#endif
return Common::KeymapArray();
}
Common::String MMMetaEngine::getGameId(const Common::String &target) {
// Store a copy of the active domain
Common::String currDomain = ConfMan.getActiveDomainName();
// Switch to the given target domain and get it's game Id
ConfMan.setActiveDomain(target);
Common::String gameId = ConfMan.get("gameid");
// Switch back to the original domain and return the game Id
ConfMan.setActiveDomain(currDomain);
return gameId;
}
bool MMMetaEngine::isXeenGame(const Common::String &target) {
Common::String gameId = getGameId(target);
if (gameId == "mm1" || gameId == "mm1_enh")
return false;
return true;
}
#if PLUGIN_ENABLED_DYNAMIC(MM)
REGISTER_PLUGIN_DYNAMIC(MM, PLUGIN_TYPE_ENGINE, MMMetaEngine);
#else
REGISTER_PLUGIN_STATIC(MM, PLUGIN_TYPE_ENGINE, MMMetaEngine);
#endif

60
engines/mm/mm.cpp Normal file
View File

@@ -0,0 +1,60 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm.h"
namespace MM {
MMEngine *g_engine;
MMEngine::MMEngine(OSystem *syst, const MM::MightAndMagicGameDescription *gameDesc) :
Engine(syst), _gameDescription(gameDesc), _randomSource("MightAndMagic") {
g_engine = this;
}
bool MMEngine::hasFeature(EngineFeature f) const {
return
(f == kSupportsReturnToLauncher) ||
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime);
}
uint32 MMEngine::getGameID() const {
return _gameDescription->gameID;
}
uint32 MMEngine::getFeatures() const {
return _gameDescription->desc.flags;
}
Common::Language MMEngine::getLanguage() const {
return _gameDescription->desc.language;
}
Common::Platform MMEngine::getPlatform() const {
return _gameDescription->desc.platform;
}
bool MMEngine::getIsCD() const {
return getFeatures() & ADGF_CD;
}
} // namespace MM

87
engines/mm/mm.h Normal file
View File

@@ -0,0 +1,87 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM_MM_H
#define MM_MM_H
#include "common/random.h"
#include "mm/detection.h"
namespace MM {
enum MightAndMagicDebugChannels {
kDebugPath = 1,
kDebugScripts,
kDebugGraphics,
kDebugSound,
};
class MMEngine : public Engine {
protected:
const MightAndMagicGameDescription *_gameDescription;
Common::RandomSource _randomSource;
public:
MMEngine(OSystem *syst, const MM::MightAndMagicGameDescription *gameDesc);
~MMEngine() override {}
/**
* Checks for feature flag
*/
bool hasFeature(EngineFeature f) const override;
/**
* Returns the features
*/
uint32 getFeatures() const;
/**
* Returns the game language
*/
Common::Language getLanguage() const;
/**
* Returns the game's platform
*/
Common::Platform getPlatform() const;
/**
* Gets the game Id
*/
uint32 getGameID() const;
/**
* Returns true if the game is the CD version
*/
bool getIsCD() const;
/**
* Get a random number
*/
uint getRandomNumber(int max) {
return _randomSource.getRandomNumber(max);
}
};
extern MMEngine *g_engine;
} // namespace MM
#endif // MM_MM_H

493
engines/mm/mm1/console.cpp Normal file
View File

@@ -0,0 +1,493 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/file.h"
#include "common/savefile.h"
#include "common/system.h"
#include "mm/shared/utils/strings.h"
#include "mm/mm1/console.h"
#include "mm/mm1/globals.h"
#include "mm/mm1/events.h"
#include "mm/mm1/game/spells_party.h"
namespace MM {
namespace MM1 {
Console::Console() : GUI::Debugger() {
registerCmd("dump_map", WRAP_METHOD(Console, cmdDumpMap));
registerCmd("dump_monsters", WRAP_METHOD(Console, cmdDumpMonsters));
registerCmd("dump_items", WRAP_METHOD(Console, cmdDumpItems));
registerCmd("dump_roster", WRAP_METHOD(Console, cmdDumpRoster));
registerCmd("map_string", WRAP_METHOD(Console, cmdMapString));
registerCmd("map", WRAP_METHOD(Console, cmdMap));
registerCmd("pos", WRAP_METHOD(Console, cmdPos));
registerCmd("intangible", WRAP_METHOD(Console, cmdIntangible));
registerCmd("cast", WRAP_METHOD(Console, cmdCast));
registerCmd("spells", WRAP_METHOD(Console, cmdSpellsAll));
registerCmd("encounter", WRAP_METHOD(Console, cmdEncounter));
registerCmd("encounters", WRAP_METHOD(Console, cmdEncounters));
registerCmd("specials", WRAP_METHOD(Console, cmdSpecials));
registerCmd("special", WRAP_METHOD(Console, cmdSpecial));
registerCmd("view", WRAP_METHOD(Console, cmdView));
}
bool Console::cmdDumpMap(int argc, const char **argv) {
Common::File f;
Common::OutSaveFile *dest;
if (argc != 2) {
debugPrintf("%s <roomNum>\n", argv[0]);
} else {
int mapId = strToInt(argv[1]);
Maps::Map *map = g_globals->_maps.getMap(mapId);
// Dump the map data
if (f.open("mazedata.dta")) {
dest = g_system->getSavefileManager()->openForSaving(
Common::String::format("map%.2d-maze.bin", mapId), false);
assert(dest);
byte buffer[512];
f.seek(mapId * 512);
f.read(buffer, 512);
dest->write(buffer, 512);
dest->finalize();
delete dest;
f.close();
}
if (f.open(Common::Path(Common::String::format("%s.ovr", map->getName().c_str())))) {
int magicId = f.readUint16LE();
int codePtr = f.readUint16LE();
int codeSize = f.readUint16LE();
int dataPtr = f.readUint16LE();
int dataSize = f.readUint16LE();
int extrasSize = f.readUint16LE();
int startPtr = f.readUint16LE();
assert(magicId == 0xf2);
assert(startPtr >= codePtr &&
startPtr < (codePtr + codeSize));
dest = g_system->getSavefileManager()->openForSaving(
Common::String::format("map%.2d-code.bin", mapId), false);
byte *code = new byte[codeSize];
f.read(code, codeSize);
for (int i = 0; i < (codePtr % 16); ++i)
dest->writeByte(0);
dest->write(code, codeSize);
dest->flush();
delete dest;
delete[] code;
dest = g_system->getSavefileManager()->openForSaving(
Common::String::format("map%.2d-data.bin", mapId), false);
byte *data = new byte[dataSize];
f.read(data, dataSize);
dest->write(data, dataSize);
dest->flush();
delete dest;
delete[] data;
f.close();
debugPrintf("data: ptr=%xh, size=%xh\n", dataPtr, dataSize);
debugPrintf("code: ptr=%xh, size=%xh start=%xh\n",
codePtr, codeSize, startPtr);
debugPrintf("Extras size=%xh\n", extrasSize);
}
debugPrintf("Done.\n");
}
return true;
}
bool Console::cmdDumpMonsters(int argc, const char **argv) {
Common::File f;
Common::DumpFile df;
if (f.open("mm.exe")) {
if (df.open("monsters.txt")) {
f.seek(0x1b312);
for (int i = 0; i < 195; ++i) {
Common::String line = "\"";
for (int j = 0; j < 15; ++j)
line += f.readByte();
line += '"';
for (int j = 0; j < 16; ++j) {
line += ", ";
int val = (j == 7) ? f.readUint16LE() : f.readByte();
line += Common::String::format("%d", val);
}
df.writeString(line);
df.writeByte('\n');
}
df.close();
f.close();
debugPrintf("Done\n");
return true;
}
}
debugPrintf("Could not create\n");
return true;
}
bool Console::cmdDumpItems(int argc, const char **argv) {
Common::File f;
Common::DumpFile df;
Common::String line;
if (f.open("mm.exe")) {
if (df.open("items.txt")) {
f.seek(0x19b2a);
for (int i = 0; i < 255; ++i) {
if (i == 85) {
// Add the blank unused item line
line = "\" \", 0, 0, 0, 0, 0, 0, 0, 0, 0";
df.writeString(line);
df.writeByte('\n');
}
line = "\"";
for (int j = 0; j < 14; ++j)
line += f.readByte();
line += '"';
for (int j = 0; j < 9; ++j) {
line += ", ";
line += Common::String::format("%d",
(j == 6) ? f.readUint16BE() : f.readByte());
}
df.writeString(line);
df.writeByte('\n');
}
df.close();
f.close();
debugPrintf("Done\n");
return true;
}
}
debugPrintf("Could not create\n");
return true;
}
bool Console::cmdDumpRoster(int argc, const char **argv) {
g_globals->_roster.saveOriginal();
debugPrintf("Dumped roster\n");
return true;
}
bool Console::cmdMapString(int argc, const char **argv) {
Common::File f;
if (argc != 3) {
debugPrintf("%s <map Id> <offset>\n", argv[0]);
} else {
int mapId = strToInt(argv[1]);
Maps::Map *map = g_globals->_maps.getMap(mapId);
int offset = strToInt(Common::String::format(
"%sh", argv[2]).c_str());
if (!f.open(Common::Path(Common::String::format("%s.ovr", map->getName().c_str()))))
error("Failed to open map");
f.readUint16LE();
f.readUint16LE();
int codeSize = f.readUint16LE();
int dataPtr = f.readUint16LE();
f.readUint16LE();
f.readUint16LE();
f.readUint16LE();
f.skip(codeSize);
f.skip(offset - dataPtr);
// Read the string
Common::String s;
char c;
while ((c = f.readByte()) != '\0') {
if (c == '\r') {
s += "\\n";
debugPrintf("%s\n", s.c_str());
s = "";
} else {
s += c;
if (s.size() == 40) {
debugPrintf("%s\n", s.c_str());
s = "";
}
}
}
debugPrintf("%s\n", s.c_str());
f.close();
}
return true;
}
bool Console::cmdMap(int argc, const char **argv) {
Maps::Maps &maps = g_globals->_maps;
if (argc < 2) {
debugPrintf("map <mapId section> | <mapNum [ xp, yp ]>\n");
return true;
} else if (argc == 3) {
int mapId = strToInt(argv[1]);
int section = strToInt(argv[2]);
maps.changeMap(mapId, section);
} else {
int mapNum = strToInt(argv[1]);
Maps::Map &map = *maps.getMap(mapNum);
int x = g_maps->_mapPos.x, y = g_maps->_mapPos.y;
if (argc == 4) {
x = strToInt(argv[2]);
y = strToInt(argv[3]);
}
maps._mapPos.x = x;
maps._mapPos.y = y;
maps.changeMap(map.getId(), map.getDefaultSection());
}
return false;
}
bool Console::cmdPos(int argc, const char **argv) {
if (argc < 3) {
debugPrintf("pos xp, yp\n");
return true;
} else {
Maps::Maps &maps = g_globals->_maps;
maps._mapPos.x = strToInt(argv[1]);
maps._mapPos.y = strToInt(argv[2]);
g_events->send("Game", GameMessage("UPDATE"));
return false;
}
}
bool Console::cmdIntangible(int argc, const char **argv) {
g_globals->_intangible = (argc < 2) || strcmp(argv[1], "off");
debugPrintf("Intangibility is %s\n", g_globals->_intangible ? "on" : "off");
return true;
}
bool Console::cmdCast(int argc, const char **argv) {
if (argc != 3) {
debugPrintf("%s <level> <number>\n", argv[0]);
return true;
} else {
Character *c = g_globals->_currCharacter;
if (!c || (c->_class != CLERIC && c->_class != SORCERER
&& c->_class != ARCHER)) {
uint i;
for (i = 0; i < g_globals->_party.size(); ++i) {
if (g_globals->_party[i]._class == SORCERER) {
c = &g_globals->_party[i];
break;
}
}
if (i == g_globals->_party.size()) {
debugPrintf("Could not find sorcerer in party\n");
return true;
}
}
int spellIndex = getSpellIndex(c, strToInt(argv[1]), strToInt(argv[2]));
setSpell(spellIndex, 0, 0);
Game::SpellsParty::cast(_spellIndex, c);
return false;
}
}
bool Console::cmdSpellsAll(int argc, const char **argv) {
g_globals->_allSpells = (argc == 2) && !strcmp(argv[1], "on");
debugPrintf("All spells is %s\n",
g_globals->_allSpells ? "enabled" : "disabled");
return true;
}
bool Console::cmdEncounter(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("encounter <monster-num> [<level>, <# monsters>, <encounter type>]\n");
return true;
} else {
Game::Encounter &enc = g_globals->_encounters;
int monsterNum = strToInt(argv[1]);
if (monsterNum < 1 || monsterNum > MONSTERS_COUNT) {
debugPrintf("monster-num must be between 1 and %d\n", MONSTERS_COUNT);
return true;
}
int level = (argc == 3) ? strToInt(argv[2]) : 1;
if (level < 1 || level >= 15)
level = 1;
int count = (argc > 3) ? strToInt(argv[3]) : 1;
int encType = (argc > 4) ? strToInt(argv[2]) :
Game::NORMAL_ENCOUNTER;
if (encType != -1 || encType == 0 || encType == 1)
enc._encounterType = (Game::EncounterType)encType;
enc.clearMonsters();
for (int i = 0; i < count; ++i)
enc.addMonster(monsterNum, level);
enc._manual = true;
enc._levelIndex = 80;
bool monstersOn = g_globals->_encountersOn;
g_globals->_encountersOn = true;
enc.execute();
g_globals->_encountersOn = monstersOn;
return false;
}
}
bool Console::cmdEncounters(int argc, const char **argv) {
if (argc == 1) {
g_globals->_encountersOn = !g_globals->_encountersOn;
} else {
g_globals->_encountersOn = !scumm_stricmp(argv[1], "on");
}
debugPrintf("Encounters are %s\n",
g_globals->_encountersOn ? "on" : "off");
return true;
}
bool Console::cmdSpecials(int argc, const char **argv) {
int count = g_maps->_currentMap->dataByte(Maps::MAP_SPECIAL_COUNT);
// List specials that have code attached
for (int i = 0; i < count; ++i) {
int mapOffset = g_maps->_currentMap->dataByte(51 + i);
int x = mapOffset % MAP_W;
int y = mapOffset / MAP_W;
Common::String line = Common::String::format(
"Special #%.2d - %d, %d (", i, x, y);
int dirMask = g_maps->_currentMap->dataByte(51 + i);
if (dirMask & Maps::DIRMASK_N)
line += "N,";
if (dirMask & Maps::DIRMASK_S)
line += "S,";
if (dirMask & Maps::DIRMASK_E)
line += "E,";
if (dirMask & Maps::DIRMASK_W)
line += "W,";
line.deleteLastChar();
line += ')';
debugPrintf("%s\n", line.c_str());
}
// Iterate through the map to find special cells that are codeless
int mapOffset = 0, i;
for (int mapPos = 0; mapPos < (MAP_W * MAP_H); ++mapPos) {
bool isSpecial = (g_maps->_currentMap->_states[mapPos]
& Maps::CELL_SPECIAL) != 0;
if (!isSpecial)
continue;
int x = mapPos % MAP_W;
int y = mapPos / MAP_W;
for (i = 0; i < count; ++i) {
mapOffset = g_maps->_currentMap->dataByte(51 + i);
if (mapOffset == mapPos)
break;
}
// Add row for special if there's no code handling
if (i == count) {
Common::String line = Common::String::format(
"Special #-- - %d, %d", x, y);
debugPrintf("%s\n", line.c_str());
}
}
return true;
}
bool Console::cmdSpecial(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("special <num> to execute special in the current map\n");
return true;
}
Maps::Maps &maps = *g_maps;
Maps::Map &map = *g_maps->_currentMap;
uint count = g_maps->_currentMap->dataByte(Maps::MAP_SPECIAL_COUNT);
uint specialNum = strToInt(argv[1]);
if (specialNum > count) {
debugPrintf("Invalid special number\n");
return true;
}
// Set new position
maps._mapOffset = map[51 + specialNum];
maps._mapPos.x = maps._mapOffset % 16;
maps._mapPos.y = maps._mapOffset / 16;
// Rotate to find a direction that will trigger the special
for (int i = 0; i < 4; ++i) {
if (maps._forwardMask & map[51 + count + specialNum])
break;
maps.turnLeft();
}
// Execute the specials handler for the map
map.special();
return false;
}
bool Console::cmdView(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("view <view name>\n");
return true;
} else {
g_events->addView(argv[1]);
return false;
}
}
} // namespace MM1
} // namespace MM

116
engines/mm/mm1/console.h Normal file
View File

@@ -0,0 +1,116 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM_MM1_CONSOLE_H
#define MM_MM1_CONSOLE_H
#include "gui/debugger.h"
#include "mm/mm1/game/spell_casting.h"
namespace MM {
namespace MM1 {
class Console : public GUI::Debugger, public MM1::Game::SpellCasting {
protected:
/**
* Used to dump a map's code and data
*/
bool cmdDumpMap(int argc, const char **argv);
/**
* Dumps the monster list
*/
bool cmdDumpMonsters(int argc, const char **argv);
/**
* Dumps the item list
*/
bool cmdDumpItems(int argc, const char **argv);
/**
* Prints a string from within a map's data segment
*/
bool cmdMapString(int argc, const char **argv);
/**
* Toggles intangible mode, allowing walking through walls
*/
bool cmdIntangible(int argc, const char **argv);
/**
* Jumps to a given map, and optionally a given position
*/
bool cmdMap(int argc, const char **argv);
/**
* Changes the party's position in the current map
*/
bool cmdPos(int argc, const char **argv);
/**
* Casts a spell
*/
bool cmdCast(int argc, const char **argv);
/**
* Enables/disables casting any spell
*/
bool cmdSpellsAll(int argc, const char **argv);
/**
* Trigger an encounter
*/
bool cmdEncounter(int argc, const char **argv);
/**
* Turns encounters on or off
*/
bool cmdEncounters(int argc, const char **argv);
/**
* List the special cells in the current map
*/
bool cmdSpecials(int argc, const char **argv);
/**
* Trigger a special in the current map
*/
bool cmdSpecial(int argc, const char **argv);
/**
* Add a specific view
*/
bool cmdView(int argc, const char **argv);
/**
* Dump the roster in the original roster.dat format
*/
bool cmdDumpRoster(int argc, const char **argv);
public:
Console();
~Console() override {}
};
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,36 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/data/active_spells.h"
namespace MM {
namespace MM1 {
void ActiveSpells::clear() {
Common::fill(&_arr[0], &_arr[ACTIVE_SPELLS_COUNT], 0);
}
void ActiveSpells::synchronize(Common::Serializer &s) {
s.syncBytes(_arr, ACTIVE_SPELLS_COUNT);
}
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,73 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_DATA_ACTIVE_SPELLS_H
#define MM1_DATA_ACTIVE_SPELLS_H
#include "mm/mm1/data/character.h"
namespace MM {
namespace MM1 {
#define ACTIVE_SPELLS_COUNT 18
struct ActiveSpellsStruct {
byte fear;
byte cold;
byte fire;
byte poison;
byte acid;
byte electricity;
byte magic;
byte light;
byte leather_skin;
byte levitate;
byte walk_on_water;
byte guard_dog;
byte psychic_protection;
byte bless;
byte invisbility;
byte shield;
byte power_shield;
byte cursed;
};
union ActiveSpells {
ActiveSpellsStruct _s;
byte _arr[ACTIVE_SPELLS_COUNT];
ActiveSpells() { clear(); }
/**
* Clear the spells
*/
void clear();
/**
* Synchronize spell data to/from savegames
*/
void synchronize(Common::Serializer &s);
};
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,754 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/algorithm.h"
#include "mm/mm1/data/character.h"
#include "mm/shared/utils/strings.h"
#include "mm/mm1/mm1.h"
namespace MM {
namespace MM1 {
static const int CLASS_HP_PER_LEVEL[6] = {
12, 10, 10, 8, 6, 8
};
Resistances::Resistances() {
for (int i = 0; i < 8; ++i)
_arr[i].clear();
}
void Resistances::synchronize(Common::Serializer &s) {
for (int i = 0; i < 8; ++i)
_arr[i].synchronize(s);
}
size_t Resistances::getPerformanceTotal() const {
size_t total = 0;
for (int i = 0; i < 8; ++i)
total += _arr[i].getPerformanceTotal();
return total;
}
void Inventory::clear() {
_items.clear();
_items.resize(INVENTORY_COUNT);
}
void Inventory::synchronize(Common::Serializer &s, bool ids) {
for (int i = 0; i < INVENTORY_COUNT; ++i)
s.syncAsByte(ids ? _items[i]._id : _items[i]._charges);
}
bool Inventory::empty() const {
for (uint i = 0; i < INVENTORY_COUNT; ++i) {
if (_items[i])
return false;
}
return true;
}
bool Inventory::full() const {
for (uint i = 0; i < INVENTORY_COUNT; ++i) {
if (!_items[i])
return false;
}
return true;
}
uint Inventory::size() const {
for (int i = INVENTORY_COUNT - 1; i >= 0; --i) {
if (_items[i])
return i + 1;
}
return 0;
}
uint Inventory::add(byte id, byte charges) {
uint idx = getFreeSlot();
_items[idx]._id = id;
_items[idx]._charges = charges;
return idx;
}
int Inventory::getFreeSlot() const {
for (uint i = 0; i < INVENTORY_COUNT; ++i) {
if (!_items[i])
return i;
}
error("Inventory is full");
return -1;
}
void Inventory::removeAt(uint idx) {
_items.remove_at(idx);
_items.push_back(Entry());
}
void Inventory::remove(Entry *e) {
int index = indexOf(e);
assert(index >= 0);
removeAt(index);
}
int Inventory::indexOf(Entry *e) const {
for (uint i = 0; i < INVENTORY_COUNT; ++i) {
if (e == &_items[i])
return i;
}
return -1;
}
int Inventory::indexOf(byte itemId) const {
for (uint i = 0; i < INVENTORY_COUNT; ++i) {
if (_items[i]._id == itemId)
return i;
}
return -1;
}
bool Inventory::hasCategory(CategoryFn fn) const {
for (uint i = 0; i < INVENTORY_COUNT; ++i) {
if (fn(_items[i]._id))
return true;
}
return false;
}
void Inventory::removeCharge(Entry *e) {
if (e->_charges) {
if (--e->_charges == 0)
remove(e);
}
}
size_t Inventory::getPerformanceTotal() const {
size_t total = 0;
for (uint i = 0; i < size(); ++i)
total += (size_t)(*this)[i]._id + (size_t)(*this)[i]._charges;
return total;
}
/*------------------------------------------------------------------------*/
Character::Character() : PrimaryAttributes() {
Common::fill(&_flags[0], &_flags[14], 0);
}
void Character::synchronize(Common::Serializer &s, int portraitNum) {
char name[16];
if (s.isSaving()) {
// Save the name in uppercase to match original
Common::strlcpy(name, uppercase(_name).c_str(), 16);
s.syncBytes((byte *)name, 16);
} else {
s.syncBytes((byte *)name, 16);
name[15] = '\0';
if (g_engine->isEnhanced())
Common::strlcpy(_name, camelCase(name).c_str(), 16);
else
Common::strlcpy(_name, uppercase(name).c_str(), 16);
}
s.syncAsByte(_sex);
s.syncAsByte(_alignmentInitial);
s.syncAsByte(_alignment);
s.syncAsByte(_race);
s.syncAsByte(_class);
_intelligence.synchronize(s);
_might.synchronize(s);
_personality.synchronize(s);
_endurance.synchronize(s);
_speed.synchronize(s);
_accuracy.synchronize(s);
_luck.synchronize(s);
_level.synchronize(s);
s.syncAsByte(_age);
s.syncAsByte(_ageDayCtr);
s.syncAsUint32LE(_exp);
s.syncAsUint16LE(_sp._current);
s.syncAsUint16LE(_sp._base);
_spellLevel.synchronize(s);
s.syncAsUint16LE(_gems);
s.syncAsUint16LE(_hpCurrent);
s.syncAsUint16LE(_hp);
s.syncAsUint16LE(_hpMax);
// Gold field is annoying by being 3 bytes
uint goldLo = _gold & 0xffff;
uint goldHi = _gold >> 16;
s.syncAsUint16LE(goldLo);
s.syncAsByte(goldHi);
if (s.isLoading())
_gold = goldLo | (goldHi << 16);
_ac.synchronize(s);
s.syncAsByte(_food);
s.syncAsByte(_condition);
_equipped.synchronize(s, true);
_backpack.synchronize(s, true);
_equipped.synchronize(s, false);
_backpack.synchronize(s, false);
_resistances.synchronize(s);
_physicalAttr.synchronize(s);
_missileAttr.synchronize(s);
s.syncAsByte(_trapCtr);
s.syncAsByte(_quest);
s.syncAsByte(_worthiness);
s.syncAsByte(_alignmentCtr);
s.syncBytes(_flags, 14);
s.syncAsByte(_portrait);
if (s.isLoading()) {
if (portraitNum != -1)
_portrait = portraitNum;
if (_portrait >= NUM_PORTRAITS)
// Ensure portrait number is valid
_portrait = 0;
}
if (s.isLoading())
loadFaceSprites();
}
void Character::loadFaceSprites() {
if (_portrait != 0xff && g_engine->isEnhanced()) {
Common::Path cname(Common::String::format("char%02d.fac",
_portrait * 2 + (_sex == MALE ? 0 : 1) + 1));
_faceSprites.load(cname);
}
}
void Character::clear() {
Common::fill(_name, _name + 16, 0);
_sex = (Sex)0;
_alignmentInitial = (Alignment)0;
_alignment = (Alignment)0;
_race = (Race)0;
_class = (CharacterClass)0;
_intelligence = _might = _personality = _endurance = 0;
_speed = _accuracy = _luck = 0;
_level = 1;
_age = _ageDayCtr = 0;
_exp = 0;
_sp = 0;
_spellLevel = 0;
_gems = 0;
_hpCurrent = _hp = _hpMax = 0;
_gold = 0;
_ac = 0;
_food = 0;
_condition = 0;
_quest = 0;
_equipped.clear();
_backpack.clear();
_alignmentInitial = GOOD;
_alignment = GOOD;
_resistances._s._magic.clear();
_resistances._s._fear.clear();
_resistances._s._poison.clear();
_resistances._s._psychic.clear();
_trapCtr = _alignmentCtr = 0;
Common::fill(&_flags[0], &_flags[8], 0);
}
void Character::gatherGold() {
uint total = 0;
for (uint i = 0; i < g_globals->_party.size(); ++i) {
total += g_globals->_party[i]._gold;
g_globals->_party[i]._gold = 0;
}
_gold = total;
}
Character::TradeResult Character::trade(int whoTo, int itemIndex) {
Character &dest = g_globals->_party[whoTo];
if (&dest == this)
return TRADE_SUCCESS;
if (dest._backpack.full())
return TRADE_FULL;
if (!_backpack[itemIndex])
return TRADE_NO_ITEM;
Inventory::Entry e = _backpack[itemIndex];
_backpack.removeAt(itemIndex);
dest._backpack.add(e._id, e._charges);
return TRADE_SUCCESS;
}
Character::LevelIncrease Character::increaseLevel() {
++_level;
++_age;
if (_age > 220)
_age = 220;
_trapCtr += 2;
int classNum = _class == NONE ? ROBBER : _class;
int newHP = g_engine->getRandomNumber(CLASS_HP_PER_LEVEL[classNum - 1]);
if (_endurance._base >= 40)
newHP += 10;
else if (_endurance._base >= 35)
newHP += 9;
else if (_endurance._base >= 30)
newHP += 8;
else if (_endurance._base >= 27)
newHP += 7;
else if (_endurance._base >= 24)
newHP += 6;
else if (_endurance._base >= 21)
newHP += 5;
else if (_endurance._base >= 19)
newHP += 4;
else if (_endurance._base >= 17)
newHP += 3;
else if (_endurance._base >= 15)
newHP += 2;
else if (_endurance._base >= 13)
newHP += 1;
else if (_endurance._base >= 9)
newHP += 0;
else if (_endurance._base >= 7)
newHP = MAX(newHP - 1, 1);
else if (_endurance._base >= 5)
newHP = MAX(newHP - 2, 1);
else
newHP = MAX(newHP - 3, 1);
_hpCurrent += newHP;
_hp = _hpMax = _hpCurrent;
int gainedSpells = 0;
if (classNum < ARCHER) {
if (_level._base < 7)
gainedSpells = 0;
else if (_level._base == 7)
gainedSpells = 1;
else if (_level._base == 9)
gainedSpells = 2;
else if (_level._base == 11)
gainedSpells = 3;
else if (_level._base == 13)
gainedSpells = 4;
} else if (classNum < SORCERER) {
if (_level._base == 3)
gainedSpells = 2;
else if (_level._base == 5)
gainedSpells = 3;
else if (_level._base == 7)
gainedSpells = 4;
else if (_level._base == 9)
gainedSpells = 5;
else if (_level._base == 11)
gainedSpells = 6;
else if (_level._base == 13)
gainedSpells = 7;
}
LevelIncrease result;
result._numHP = newHP;
result._numSpells = gainedSpells;
return result;
}
Character::BuyResult Character::buyItem(byte itemId) {
// Check if backpack is full
if (_backpack.full())
return BUY_BACKPACK_FULL;
// Check character has enough gold
g_globals->_items.getItem(itemId);
Item &item = g_globals->_currItem;
if (_gold < item._cost)
return BUY_NOT_ENOUGH_GOLD;
// Add the item
_gold -= item._cost;
_backpack.add(itemId, item._maxCharges);
return BUY_SUCCESS;
}
void Character::updateAttributes() {
_intelligence.reset();
_might.reset();
_personality.reset();
_endurance.reset();
_speed.reset();
_personality.reset();
_endurance.reset();
_speed.reset();
_accuracy.reset();
_luck.reset();
_level.reset();
_spellLevel.reset();
}
void Character::updateAC() {
int ac = _ac._base;
if (_speed >= 40)
ac += 9;
else if (_speed >= 35)
ac += 8;
else if (_speed >= 30)
ac += 7;
else if (_speed >= 25)
ac += 6;
else if (_speed >= 21)
ac += 5;
else if (_speed >= 19)
ac += 4;
else if (_speed >= 17)
ac += 3;
else if (_speed >= 15)
ac += 2;
else if (_speed >= 13)
ac += 1;
else if (_speed >= 9)
ac += 0;
else if (_speed >= 7)
ac = MAX(ac - 1, 0);
else if (_speed >= 5)
ac = MAX(ac - 2, 0);
else
ac = MAX(ac - 3, 0);
_ac._current = ac;
}
void Character::updateSP() {
int intelligence = _intelligence._current;
int personality = _personality._current;
int level = _level._current;
int index = 3;
AttributePair newSP;
// Spell points only relevant for spell casters
if (_spellLevel._current) {
int threshold = -1;
if (_class == CLERIC)
threshold = personality;
else if (_class == SORCERER)
threshold = intelligence;
else if (level < 7)
threshold = -1;
else {
level -= 6;
threshold = (_class == PALADIN) ?
personality : intelligence;
}
if (threshold >= 40)
index += 10;
else if (threshold >= 35)
index += 9;
else if (threshold >= 30)
index += 8;
else if (threshold >= 27)
index += 7;
else if (threshold >= 24)
index += 6;
else if (threshold >= 21)
index += 5;
else if (threshold >= 19)
index += 4;
else if (threshold >= 17)
index += 3;
else if (threshold >= 15)
index += 2;
else if (threshold >= 13)
index += 1;
else if (threshold < 5)
index -= 3;
else if (threshold < 7)
index -= 2;
else if (threshold < 9)
index -= 1;
// Calculate the SP
newSP._base += index * level;
newSP._current = newSP._base;
}
// Set the character's new SP
_sp = newSP;
}
void Character::updateResistances() {
for (int i = 0; i < 8; ++i)
_resistances._arr[i]._current = _resistances._arr[i]._base;
}
Common::String Character::getConditionString() const {
Common::String result;
int cond = _condition;
if (cond == 0) {
result += STRING["stats.conditions.good"];
} else if (cond == ERADICATED) {
result += STRING["stats.conditions.eradicated"];
} else {
if (cond & BAD_CONDITION) {
// Fatal conditions
if (cond & DEAD)
result += STRING["stats.conditions.dead"] + ",";
if (cond & STONE)
result += STRING["stats.conditions.stone"] + ",";
} else {
if (cond & UNCONSCIOUS)
result += STRING["stats.conditions.unconscious"] + ",";
if (cond & PARALYZED)
result += STRING["stats.conditions.paralyzed"] + ",";
if (cond & POISONED)
result += STRING["stats.conditions.poisoned"] + ",";
if (cond & DISEASED)
result += STRING["stats.conditions.diseased"] + ",";
if (cond & SILENCED)
result += STRING["stats.conditions.silenced"] + ",";
if (cond & BLINDED)
result += STRING["stats.conditions.blinded"] + ",";
if (cond & ASLEEP)
result += STRING["stats.conditions.asleep"] + ",";
}
result.deleteLastChar();
}
return result;
}
void Character::rest() {
// Characters with a bad condition like
// being stoned can't rest
if (_condition & BAD_CONDITION)
return;
updateSP();
updateAttributes();
updateAC();
updateResistances();
_condition &= ~(ASLEEP | BLINDED | SILENCED |
PARALYZED | UNCONSCIOUS);
if (_hpCurrent == 0)
_hpCurrent = 1;
// Increment the day counter. When it overflows,
// it's time to increment the character's age by a year
if (_ageDayCtr++ > 255) {
_ageDayCtr = 0;
if (_age < 255)
++_age;
}
if ((g_engine->getRandomNumber(70) + 80) < _age) {
// Older characters have a chance of falling unconscious
_condition = UNCONSCIOUS | BAD_CONDITION;
return;
}
// Fun fact: in the original if any of the attributes reach zero,
// then it jumps to an instruction that jumps to itself, freezing the game.
// For ScummVM, I just limit the minimum to 1 instead
if (_age >= 60) {
_might._current = MAX(_might._current - 1, 1);
_endurance._current = MAX(_endurance._current - 1, 1);
_speed._current = MAX(_speed._current - 1, 1);
}
if (_age >= 70) {
_might._current = MAX(_might._current - 1, 1);
_endurance._current = MAX(_endurance._current - 1, 1);
_speed._current = MAX(_speed._current - 1, 1);
}
if (_age >= 80) {
_might._current = MAX((int)_might._current - 2, 1);
}
if (_food > 0) {
--_food;
if (_condition & POISONED) {
_hpMax /= 2;
} else {
_hpMax = _hp;
}
if (!(_condition & DISEASED)) {
_hpCurrent = _hpMax;
_sp._current = _sp._base;
}
}
}
bool Character::hasItem(byte itemId) const {
return _backpack.indexOf(itemId) != -1 ||
_equipped.indexOf(itemId) != -1;
}
#define PERF16(x) ((x & 0xff) + ((x >> 8) & 0xff))
#define PERF32(x) ((x & 0xff) + ((x >> 8) & 0xff) + \
((x >> 16) & 0xff) + ((x >> 24) & 0xff))
size_t Character::getPerformanceTotal() const {
size_t totalFlags = 0;
for (int i = 0; i < 14; ++i)
totalFlags += _flags[i];
return (int)_sex
+ _alignmentInitial
+ _alignment
+ _race
+ _class
+ _intelligence.getPerformanceTotal()
+ _might.getPerformanceTotal()
+ _personality.getPerformanceTotal()
+ _endurance.getPerformanceTotal()
+ _speed.getPerformanceTotal()
+ _accuracy.getPerformanceTotal()
+ _luck.getPerformanceTotal()
+ _level.getPerformanceTotal()
+ (int)_age + (int)_ageDayCtr
+ PERF32(_exp)
+ _sp.getPerformanceTotal()
+ _spellLevel.getPerformanceTotal()
+ PERF16(_gems)
+ PERF16(_hpCurrent)
+ PERF16(_hp)
+ PERF16(_hpMax)
+ PERF32(_gold)
+ _ac
+ _food
+ _condition
+ _equipped.getPerformanceTotal()
+ _backpack.getPerformanceTotal()
+ _resistances.getPerformanceTotal()
+ _physicalAttr.getPerformanceTotal()
+ _missileAttr.getPerformanceTotal()
+ _trapCtr
+ _quest
+ _worthiness
+ _alignmentCtr
+ totalFlags;
}
byte Character::statColor(int amount, int threshold) const {
if (amount < 1)
return 6;
else if (amount > threshold)
return 2;
else if (amount == threshold)
return 15;
else if (amount >= (threshold / 4))
return 9;
else
return 32;
}
byte Character::conditionColor() const {
if (_condition == ERADICATED)
return 32;
else if (_condition == FINE)
return 15;
else if (_condition & BAD_CONDITION)
return 6;
else
return 9;
}
ConditionEnum Character::worstCondition() const {
if (_condition == ERADICATED) {
return C_ERADICATED;
} else if (_condition & BAD_CONDITION) {
if (_condition & DEAD)
return C_DEAD;
if (_condition & STONE)
return C_STONE;
if (_condition & UNCONSCIOUS)
return C_UNCONSCIOUS;
} else {
if (_condition & PARALYZED)
return C_PARALYZED;
if (_condition & POISONED)
return C_POISONED;
if (_condition & DISEASED)
return C_DISEASED;
if (_condition & SILENCED)
return C_SILENCED;
if (_condition & BLINDED)
return C_BLINDED;
if (_condition & ASLEEP)
return C_ASLEEP;
}
return C_GOOD;
}
Common::String Character::getConditionString(ConditionEnum cond) {
switch (cond) {
case C_ERADICATED: return STRING["stats.conditions.eradicated"];
case C_DEAD: return STRING["stats.conditions.dead"];
case C_STONE: return STRING["stats.conditions.stone"];
case C_UNCONSCIOUS: return STRING["stats.conditions.unconscious"];
case C_PARALYZED: return STRING["stats.conditions.paralyzed"];
case C_POISONED: return STRING["stats.conditions.poisoned"];
case C_DISEASED: return STRING["stats.conditions.diseased"];
case C_SILENCED: return STRING["stats.conditions.silenced"];
case C_BLINDED: return STRING["stats.conditions.blinded"];
case C_ASLEEP: return STRING["stats.conditions.asleep"];
default: return STRING["stats.conditions.good"];
}
}
int Character::spellNumber() const {
return g_events->isInCombat() ? _combatSpell : _nonCombatSpell;
}
void Character::setSpellNumber(int spellNum) {
if (g_events->isInCombat())
_combatSpell = spellNum;
else
_nonCombatSpell = spellNum;
}
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,609 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_DATA_CHAR_H
#define MM1_DATA_CHAR_H
#include "common/array.h"
#include "common/serializer.h"
#include "mm/mm1/data/items.h"
#include "mm/shared/xeen/sprites.h"
namespace MM {
namespace MM1 {
#define INVENTORY_COUNT 6
#define MAX_LEVEL 200
#define NUM_PORTRAITS 12
enum CharacterClass {
KNIGHT = 1, PALADIN = 2, ARCHER = 3, CLERIC = 4,
SORCERER = 5, ROBBER = 6, NONE = 0
};
enum Race {
HUMAN = 1, ELF = 2, DWARF = 3, GNOME = 4, HALF_ORC = 5
};
enum Alignment {
GOOD = 1, NEUTRAL = 2, EVIL = 3
};
enum Sex {
MALE = 1, FEMALE = 2, YES_PLEASE = 3
};
enum Condition {
FINE = 0, BAD_CONDITION = 0x80, ERADICATED = 0xff,
DEAD = 0x40, STONE = 0x20,
UNCONSCIOUS = 0x40, PARALYZED = 0x20, POISONED = 0x10,
DISEASED = 8, SILENCED = 4, BLINDED = 2, ASLEEP = 1
};
enum ConditionEnum {
HEART_BROKEN = 1,
C_BLINDED = 2, // WEAK condition in Xeen
C_POISONED = 3,
C_DISEASED = 4,
C_ASLEEP = 8,
DEPRESSED = 9,
C_SILENCED = 10, // CONFUSED condition in Xeen
C_PARALYZED = 11,
C_UNCONSCIOUS = 12,
C_DEAD = 13,
C_STONE = 14,
C_ERADICATED = 15,
C_GOOD = 16
};
enum Resistance {
RESISTANCE_MAGIC = 0, RESISTANCE_FIRE = 1, RESISTANCE_COLD = 2,
RESISTANCE_ELECTRICITY = 3, RESISTANCE_ACID = 4,
RESISTANCE_FEAR = 5, RESISTANCE_POISON = 6,
RESISTANCE_PSYCHIC = 7, RESISTANCE_15 = 15
};
enum CharFlags0 {
CHARFLAG0_COURIER1 = 1,
CHARFLAG0_COURIER2 = 2,
CHARFLAG0_COURIER3 = 4,
CHARFLAG0_ZOM_CLUE = 8,
CHARFLAG0_ZAM_CLUE = 0x10,
CHARFLAG0_FOUND_CHEST = 0x20,
CHARFLAG0_40 = 0x40,
CHARFLAG0_DOG_STATUE = 0x80
};
enum CharFlags1 {
CHARFLAG1_1 = 1,
CHARFLAG1_2 = 2,
CHARFLAG1_4 = 4,
CHARFLAG1_8 = 8,
CHARFLAG1_10 = 0x10,
CHARFLAG1_20 = 0x20,
CHARFLAG1_40 = 0x40,
CHARFLAG1_WORTHY = 0x80
};
enum CharFlags2 {
CHARFLAG2_1 = 1,
CHARFLAG2_2 = 2,
CHARFLAG2_4 = 4,
CHARFLAG2_8 = 8,
CHARFLAG2_10 = 0x10,
CHARFLAG2_20 = 0x20,
CHARFLAG2_40 = 0x40,
CHARFLAG2_80 = 0x80
};
enum CharFlags4 {
CHARFLAG4_ASSIGNED = 8,
CHARFLAG4_SIGN = 7,
CHARFLAG4_COLOR = 0xf,
CHARFLAG4_80 = 0x80
};
enum CharFlags5 {
CHARFLAG5_1 = 1,
CHARFLAG5_2 = 2,
CHARFLAG5_4 = 4,
CHARFLAG5_8 = 8,
CHARFLAG5_10 = 0x10,
CHARFLAG5_20 = 0x20,
CHARFLAG5_40 = 0x40,
CHARFLAG5_80 = 0x80
};
enum CharFlags6 {
CHARFLAG6_1 = 1,
CHARFLAG6_2 = 2,
CHARFLAG6_4 = 4,
CHARFLAG6_8 = 8,
CHARFLAG6_10 = 0x10,
CHARFLAG6_20 = 0x20,
CHARFLAG6_40 = 0x40,
CHARFLAG6_80 = 0x80
};
enum CharFlags7 {
CHARFLAG7_1 = 1,
CHARFLAG7_2 = 2,
CHARFLAG7_4 = 4,
CHARFLAG7_8 = 8,
CHARFLAG7_10 = 0x10,
CHARFLAG7_20 = 0x20,
CHARFLAG7_40 = 0x40,
CHARFLAG7_80 = 0x80
};
enum CharFlags8 {
CHARFLAG8_1 = 1,
CHARFLAG8_2 = 2,
CHARFLAG8_4 = 4,
CHARFLAG8_8 = 8,
CHARFLAG8_10 = 0x10,
CHARFLAG8_20 = 0x20,
CHARFLAG8_40 = 0x40,
CHARFLAG8_80 = 0x80
};
enum CharFlags9 {
CHARFLAG9_1 = 1,
CHARFLAG9_2 = 2,
CHARFLAG9_4 = 4,
CHARFLAG9_8 = 8,
CHARFLAG9_10 = 0x10,
CHARFLAG9_20 = 0x20,
CHARFLAG9_40 = 0x40,
CHARFLAG9_80 = 0x80
};
enum CharFlags10 {
CHARFLAG10_1 = 1,
CHARFLAG10_2 = 2,
CHARFLAG10_4 = 4,
CHARFLAG10_8 = 8,
CHARFLAG10_10 = 0x10,
CHARFLAG10_20 = 0x20,
CHARFLAG10_40 = 0x40,
CHARFLAG10_80 = 0x80
};
enum CharFlags11 {
CHARFLAG11_GOT_ENDURANCE = 1,
CHARFLAG11_PERSONALITY = 2,
CHARFLAG11_GOT_INTELLIGENCE = 4,
CHARFLAG11_GOT_MIGHT = 8,
CHARFLAG11_GOT_ACCURACY = 0x10,
CHARFLAG11_GOT_SPEED = 0x20,
CHARFLAG11_GOT_LUCK = 0x40,
CHARFLAG11_CLERICS = 0x80
};
enum CharFlags12 {
CHARFLAG12_1 = 1,
CHARFLAG12_2 = 2,
CHARFLAG12_4 = 4,
CHARFLAG12_8 = 8,
CHARFLAG12_10 = 0x10,
CHARFLAG12_20 = 0x20,
CHARFLAG12_40 = 0x40,
CHARFLAG12_80 = 0x80
};
enum CharFlags13 {
CHARFLAG13_1 = 1,
CHARFLAG13_2 = 2,
CHARFLAG13_4 = 4,
CHARFLAG13_8 = 8,
CHARFLAG13_10 = 0x10,
CHARFLAG13_20 = 0x20,
CHARFLAG13_ALAMAR = 0x40,
CHARFLAG13_80 = 0x80
};
class Inventory {
public:
struct Entry {
byte _id = 0;
byte _charges = 0;
operator bool() const { return _id != 0; }
// bool operator!() const { return !_id; }
// operator byte() const { return _id; }
};
private:
Common::Array<Entry> _items;
/**
* Used to test if the inventory has a category of item
*/
typedef bool (*CategoryFn)(byte id);
bool hasCategory(CategoryFn fn) const;
/**
* Returns the index of a free slot
*/
int getFreeSlot() const;
public:
Inventory() {
clear();
}
Entry &operator[](uint idx) {
assert(idx < INVENTORY_COUNT);
return _items[idx];
}
const Entry &operator[](uint idx) const {
assert(idx < INVENTORY_COUNT);
return _items[idx];
}
/**
* Saves or loads the inventory data
*/
void synchronize(Common::Serializer &s, bool ids);
/**
* Clears the inventory
*/
void clear();
/**
* Returns true if the inventory is empty
*/
bool empty() const;
/**
* Returns true if the inventory is full
*/
bool full() const;
/**
* Returns the size of the backpack that's filled in
*/
uint size() const;
/**
* Adds an item to the inventory
*/
uint add(byte id, byte charges);
/**
* Removes an index from the inventory
*/
void removeAt(uint idx);
/**
* Remove an entry from the inventory
*/
void remove(Entry *e);
/**
* Returns the index of a given entry
*/
int indexOf(Entry *e) const;
/**
* Returns the index of an entry with a given id
*/
int indexOf(byte itemId) const;
/**
* Decreases the charge on a magic item, and removes
* it if the charges have run out
*/
void removeCharge(Entry *e);
/**
* The following methods return true if any of
* the contained items are of the given category
*/
bool hasWeapon() const { return hasCategory(isWeapon); }
bool hasMissile() const { return hasCategory(isMissile); }
bool hasTwoHanded() const { return hasCategory(isTwoHanded); }
bool hasArmor() const { return hasCategory(isArmor); }
bool hasShield() const { return hasCategory(isShield); }
size_t getPerformanceTotal() const;
};
/**
* Attribute pair representing it's base value and the
* current temporary value
*/
struct AttributePair {
uint8 _current = 0;
uint8 _base = 0;
operator uint8() const { return _current; }
AttributePair &operator=(byte v) {
_base = _current = v;
return *this;
}
AttributePair &operator++() {
if (_base < 255)
_current = ++_base;
return *this;
}
AttributePair &operator--() {
if (_base > 0)
_current = --_base;
return *this;
}
void clear() { _current = _base = 0; }
void reset() { _current = _base; }
void synchronize(Common::Serializer &s) {
s.syncAsByte(_base);
s.syncAsByte(_current);
}
size_t getPerformanceTotal() const {
return (size_t)_base + (size_t)_current;
}
};
struct AttributePair16 {
uint16 _current = 0;
uint16 _base = 0;
void clear() { _current = _base = 0; }
AttributePair16 &operator=(byte v) {
_base = _current = v;
return *this;
}
operator uint16() const {
return _current;
}
void synchronize(Common::Serializer &s) {
s.syncAsUint16LE(_base);
s.syncAsUint16LE(_current);
}
size_t getPerformanceTotal() const {
return (_base & 0xff) + (_base >> 8) +
(_current & 0xff) + (_current >> 8);
}
};
struct ResistanceFields {
AttributePair _magic;
AttributePair _fire;
AttributePair _cold;
AttributePair _electricity;
AttributePair _acid;
AttributePair _fear;
AttributePair _poison;
AttributePair _psychic;
};
union Resistances {
ResistanceFields _s;
AttributePair _arr[8];
Resistances();
/**
* Handles save/loading resistances
*/
void synchronize(Common::Serializer &s);
size_t getPerformanceTotal() const;
};
struct PrimaryAttributes {
public:
AttributePair _intelligence;
AttributePair _might;
AttributePair _personality;
AttributePair _endurance;
AttributePair _speed;
AttributePair _accuracy;
AttributePair _luck;
AttributePair _level;
AttributePair &getAttribute(uint i) {
return *_attributes[i];
}
private:
AttributePair *_attributes[8] = {
&_intelligence, &_might, &_personality, &_endurance,
&_speed, &_accuracy, &_luck, &_level
};
};
struct Character : public PrimaryAttributes {
char _name[16] = { 0 };
Sex _sex = MALE;
Alignment _alignmentInitial = GOOD;
Alignment _alignment = GOOD;
Race _race = HUMAN;
CharacterClass _class = NONE;
byte _age = 0;
int _ageDayCtr = 0;
AttributePair16 _sp;
AttributePair _spellLevel;
AttributePair _ac;
uint32 _exp = 0;
uint16 _gems = 0;
uint16 _hpCurrent = 0, _hp = 0, _hpMax = 0;
uint32 _gold = 0;
uint8 _food = 0;
uint8 _condition = 0;
Inventory _equipped;
Inventory _backpack;
Resistances _resistances;
AttributePair _physicalAttr, _missileAttr;
byte _trapCtr = 0;
byte _quest = 0;
byte _worthiness = 0;
byte _alignmentCtr = 0;
byte _flags[14];
byte _portrait = 0;
Shared::Xeen::SpriteResource _faceSprites;
// Non persistent fields
byte _numDrinks = 0;
// Combat fields
bool _checked = false;
bool _canAttack = false;
int _nonCombatSpell = -1;
int _combatSpell = -1;
/**
* Get the selected combat/noncombat spell number
*/
int spellNumber() const;
/**
* Sets the selected spell
*/
void setSpellNumber(int spellNum);
Character();
/**
* Handles save/loading a character
* @param portraitNum Override for portrait to use for
* a character being loaded from the game defaults
*/
void synchronize(Common::Serializer &s, int portraitNum = -1);
/**
* Equality test
*/
bool operator==(const Character &rhs) const {
return !strcmp(_name, rhs._name);
}
/**
* Clearing the character
*/
void clear();
/**
* Gathers the party gold into the character
*/
void gatherGold();
/**
* Trade an item to another
*/
enum TradeResult { TRADE_SUCCESS, TRADE_NO_ITEM, TRADE_FULL };
TradeResult trade(int whoTo, int itemIndex);
/**
* Increase the character's level by 1 at a trainer
*/
struct LevelIncrease {
int _numHP;
int _numSpells;
};
LevelIncrease increaseLevel();
/**
* Buy an item
*/
enum BuyResult { BUY_SUCCESS, BUY_NOT_ENOUGH_GOLD, BUY_BACKPACK_FULL };
BuyResult buyItem(byte itemId);
/**
* Updates the current attribute levels to match
* their base values
*/
void updateAttributes();
/**
* Updates the character's AC
*/
void updateAC();
/**
* Updates the character's SP
*/
void updateSP();
void updateResistances();
/**
* Gets a character's condition string
*/
Common::String getConditionString() const;
/**
* Rest the character
*/
void rest();
/**
* Returns true if the character has a given item
*/
bool hasItem(byte itemId) const;
/**
* Gets the numeric value of every property a character
* has and totals it up to give a stupid 'performance'
* value for the party at the end of the game
*/
size_t getPerformanceTotal() const;
/**
* Loads the face sprites for the character
*/
void loadFaceSprites();
/**
* Returns the color to use in enhanced mode to
* represent the color of a character attribute
*/
byte statColor(int amount, int threshold) const;
/**
* Returns the condition color for display
*/
byte conditionColor() const;
/**
* Returns the worst condition, if any, a character
* currently has.
*/
ConditionEnum worstCondition() const;
/**
* Returns a string for a given condition
*/
static Common::String getConditionString(ConditionEnum cond);
/**
* Returns true if the character has a fatal condition
*/
bool hasBadCondition() const {
return (_condition & BAD_CONDITION) != 0;
}
};
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,33 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/algorithm.h"
#include "mm/mm1/data/game_state.h"
namespace MM {
namespace MM1 {
GameState::GameState() {
Common::fill(&_activeSpells._arr[0], &_activeSpells._arr[ACTIVE_SPELLS_COUNT], 0);
}
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,51 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_DATA_GAME_STATE_H
#define MM1_DATA_GAME_STATE_H
#include "common/array.h"
#include "common/rect.h"
#include "common/serializer.h"
#include "mm/mm1/data/character.h"
#include "mm/mm1/data/party.h"
#include "mm/mm1/data/active_spells.h"
#include "mm/mm1/data/spells_state.h"
namespace MM {
namespace MM1 {
/**
* This acts as a container for everything in the game
* that is persisted to savegames
*/
struct GameState {
Party _party;
ActiveSpells _activeSpells;
SpellsState _spellsState;
GameState();
};
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,71 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_DATA_INT_ARRAY_H
#define MM1_DATA_INT_ARRAY_H
#include "common/array.h"
namespace MM {
namespace MM1 {
class IntArray : public Common::Array<uint> {
public:
IntArray() : Common::Array<uint>() {}
int indexOf(uint val) const {
for (uint i = 0; i < size(); ++i) {
if ((*this)[i] == val)
return i;
}
return -1;
}
/**
* Returns true if the array contains the value
*/
bool contains(uint val) {
return indexOf(val) != -1;
}
/**
* Removes a given item from the array
*/
void remove(uint val) {
int idx = indexOf(val);
if (idx != -1)
remove_at(idx);
}
/**
* Adds an item to the array
*/
void push_back(uint val) {
assert(!contains(val));
Common::Array<uint>::push_back(val);
}
};
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,93 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/data/items.h"
#include "mm/mm1/globals.h"
namespace MM {
namespace MM1 {
uint Item::getSellCost() const {
uint cost = _cost;
if (_maxCharges)
cost /= 2;
cost /= 2;
return cost;
}
bool ItemsArray::load() {
Common::File f;
if (!f.open("items.txt"))
return false;
resize(255);
for (int lineNum = 0; lineNum < 255; ++lineNum) {
Item &item = (*this)[lineNum];
Common::String line = f.readLine();
assert(line.size() > 20 && line[0] == '"' && line[15] == '"');
item._name = Common::String(line.c_str() + 1, line.c_str() + 15);
line = Common::String(line.c_str() + 16);
while (item._name.lastChar() == ' ')
item._name.deleteLastChar();
item._disablements = getNextValue(line);
item._constBonus_id = getNextValue(line);
item._constBonus_value = getNextValue(line);
item._tempBonus_id = getNextValue(line);
if (item._tempBonus_id != 0xff) {item._tempBonus_value = getNextValue(line);}
else {item._spellId = getNextValue(line);}
item._maxCharges = getNextValue(line);
item._cost = getNextValue(line);
item._damage = getNextValue(line);
item._AC_Dmg = getNextValue(line);
}
return true;
}
Item *ItemsArray::getItem(byte index) const {
assert(index > 0);
g_globals->_currItem = (*this)[index - 1];
return &g_globals->_currItem;
}
ItemCategory getItemCategory(byte itemId) {
if (isWeapon(itemId))
return ITEMCAT_WEAPON;
if (isMissile(itemId))
return ITEMCAT_MISSILE;
if (isTwoHanded(itemId))
return ITEMCAT_TWO_HANDED;
if (isArmor(itemId))
return ITEMCAT_ARMOR;
if (isShield(itemId))
return ITEMCAT_SHIELD;
return ITEMCAT_NONE;
}
} // namespace MM1
} // namespace MM

142
engines/mm/mm1/data/items.h Normal file
View File

@@ -0,0 +1,142 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_DATA_ITEMS_H
#define MM1_DATA_ITEMS_H
#include "common/array.h"
#include "common/stream.h"
#include "mm/mm1/data/text_parser.h"
namespace MM {
namespace MM1 {
enum ItemId {
GARLIC_ID = 175,
WOLFSBANE_ID = 176,
BELLADONNA_ID = 177,
VELLUM_SCROLL_ID = 231,
RUBY_WHISTLE_ID = 232,
KINGS_PASS_ID = 233,
MERCHANTS_PASS_ID = 234,
CRYSTAL_KEY_ID = 235,
CORAL_KEY_ID = 236,
BRONZE_KEY_ID = 237,
SILVER_KEY_ID = 238,
GOLD_KEY_ID = 239,
DIAMOND_KEY_ID = 240,
CACTUS_NECTAR_ID = 241,
MAP_OF_DESERT_ID = 242,
LASER_BLASTER_ID = 243,
DRAGONS_TOOTH_ID = 244,
WYVERN_EYE_ID = 245,
MEDUSA_HEAD_ID = 246,
RING_OF_OKRIM_ID = 247,
B_QUEEN_IDOL_ID = 248,
W_QUEEN_IDOL_ID = 249,
PIRATES_MAP_A_ID = 250,
PIRATES_MAP_B_ID = 251,
THUNDRANIUM_ID = 252,
KEY_CARD_ID = 253,
EYE_OF_GOROS_ID = 254,
USELESS_ITEM_ID = 255
};
enum EnablementBit {
KNIGHT_BIT = 0x20, PALADIN_BIT = 0x10, ARCHER_BIT = 8,
CLERIC_BIT = 4, SORCERER_BIT = 2, ROBBER_BIT = 1,
GOOD_BIT = 0x80, EVIL_BIT = 0x40,
NEUTRAL_BIT = GOOD_BIT | EVIL_BIT
};
enum ItemCategory {
ITEMCAT_NONE, ITEMCAT_WEAPON, ITEMCAT_MISSILE,
ITEMCAT_TWO_HANDED, ITEMCAT_ARMOR, ITEMCAT_SHIELD
};
enum EquipMode {
NO_EQUIP_BONUS = 0, IS_EQUIPPABLE = 1,
EQUIP_CURSED = 0xff
};
enum TransferKind {
TK_GEMS = 1, TK_GOLD = 2, TK_FOOD = 3, TK_ITEM = 4
};
struct ItemData {
byte _disablements = 0;
byte _constBonus_id = 0; // id equals to character characteristic id, except special "EquipMode" values
byte _constBonus_value = 0; // value to be added to character characteristic
byte _tempBonus_id = 0; // id equals to character characteristic id, except 0xff
byte _tempBonus_value = 0; // value to be added to character characteristic
byte _spellId = 0;
byte _maxCharges = 0; // for spells and tempBonus
uint16 _cost = 0;
byte _damage = 0;
byte _AC_Dmg = 0; //it is AC for armor and additional damage for weapon
};
struct Item : public ItemData {
Common::String _name;
/**
* Get the sell value
*/
uint getSellCost() const;
};
struct ItemsArray : public Common::Array<Item>, public TextParser {
ItemsArray() {}
/**
* Loads the items array
*/
bool load();
/**
* Gets an item
*/
Item *getItem(byte index) const;
};
inline bool isWeapon(byte id) {
return id >= 1 && id <= 60;
}
inline bool isMissile(byte id) {
return id >= 61 && id <= 85;
}
inline bool isTwoHanded(byte id) {
return id >= 86 && id <= 120;
}
inline bool isArmor(byte id) {
return id >= 121 && id <= 155;
}
inline bool isShield(byte id) {
return id >= 156 && id <= 170;
}
extern ItemCategory getItemCategory(byte itemId);
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,33 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/data/locations.h"
namespace MM {
namespace MM1 {
const byte TownData::TOWN_MAP_ID1[5] = { 4, 3, 3, 2, 0x1A };
const byte TownData::TOWN_MAP_ID2[5] = { 6, 0xC, 2, 8, 0xB };
const byte TownData::TOWN_MAP_X[5] = { 8, 1, 11, 12, 4 };
const byte TownData::TOWN_MAP_Y[5] = { 5, 12, 13, 8, 6 };
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,106 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_DATA_LOCATIONS_H
#define MM1_DATA_LOCATIONS_H
#include "common/serializer.h"
namespace MM {
namespace MM1 {
struct BlacksmithData {
const byte BLACKSMITH_CLASS_USAGE[6] = { 0x20, 0x10, 8, 4, 2, 1 };
};
struct BuyWeaponData {
const byte WEAPONS_TOWN1[6] = { 2, 3, 5, 61, 62, 86 };
const byte WEAPONS_TOWN2[6] = { 4, 6, 8, 63, 87, 88 };
const byte WEAPONS_TOWN3[6] = { 9, 10, 62, 64, 89, 91 };
const byte WEAPONS_TOWN4[6] = { 23, 67, 69, 93, 97, 99 };
const byte WEAPONS_TOWN5[6] = { 7, 11, 64, 65, 90, 92 };
const byte *WEAPONS[5] = {
WEAPONS_TOWN1, WEAPONS_TOWN2, WEAPONS_TOWN3,
WEAPONS_TOWN4, WEAPONS_TOWN5
};
};
struct BuyArmorData {
const byte ARMOR_TOWN1[6] = { 156, 121, 122, 123, 124, 125 };
const byte ARMOR_TOWN2[6] = { 156, 157, 121, 122, 123, 124 };
const byte ARMOR_TOWN3[6] = { 157, 121, 124, 125, 126, 127 };
const byte ARMOR_TOWN4[6] = { 160, 128, 131, 132, 133, 134 };
const byte ARMOR_TOWN5[6] = { 157, 123, 124, 125, 126, 127 };
const byte *ARMOR[5] = {
ARMOR_TOWN1, ARMOR_TOWN2, ARMOR_TOWN3,
ARMOR_TOWN4, ARMOR_TOWN5
};
};
struct BuyMiscData {
const byte MISC_TOWN1[6] = { 172, 171, 175, 178, 185, 192 };
const byte MISC_TOWN2[6] = { 172, 171, 174, 183, 188, 195 };
const byte MISC_TOWN3[6] = { 173, 175, 176, 179, 184, 195 };
const byte MISC_TOWN4[6] = { 180, 196, 211, 215, 219, 223 };
const byte MISC_TOWN5[6] = { 171, 173, 177, 185, 186, 192 };
const byte *MISC[5] = {
MISC_TOWN1, MISC_TOWN2, MISC_TOWN3,
MISC_TOWN4, MISC_TOWN5
};
};
#define MAX_FOOD 40
struct MarketData {
const byte FOOD_COST[5] = { 5, 10, 20, 200, 50 };
};
struct TempleData {
const uint16 HEAL_COST1[5] = { 2000, 5000, 5000, 2000, 8000 };
const uint16 HEAL_COST2[5] = { 200, 500, 500, 200, 1000 };
const uint16 HEAL_COST3[5] = { 25, 50, 50, 25, 100 };
const uint16 UNCURSE_COST[5] = { 500, 1000, 1000, 1012, 1500 };
const uint16 ALIGNMENT_COST[5] = { 250, 200, 200, 200, 250 };
const uint16 DONATE_COST[5] = { 100, 100, 100, 25, 200 };
const byte ALIGNMENT_VALS[3] = { 8, 0x10, 0x18 };
const byte DONATE_VALS[5] = { 1, 2, 4, 8, 0x10 };
};
struct TrainingData {
const int TRAINING_COSTS1[7] = {
25, 50, 100, 200, 400, 800, 1500
};
const int TRAINING_COSTS2[7] = {
40, 75, 150, 300, 600, 1200, 2500
};
};
struct TownData {
static const byte TOWN_MAP_ID1[5];
static const byte TOWN_MAP_ID2[5];
static const byte TOWN_MAP_X[5];
static const byte TOWN_MAP_Y[5];
};
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,106 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/file.h"
#include "mm/mm1/data/monsters.h"
#include "mm/mm1/gfx/dta.h"
#include "mm/mm1/gfx/screen_decoder.h"
#include "mm/mm1/mm1.h"
namespace MM {
namespace MM1 {
static const uint16 PALETTE[76] = {
0xf470, 0xf420, 0xfe20, 0xf630, 0xf420, 0xf620, 0xf460, 0xf6e0,
0xf510, 0xfe40, 0xf420, 0xf410, 0xfd50, 0xfc90, 0xf430, 0xfc30,
0xf770, 0xfc30, 0xf420, 0xf430, 0xf420, 0xf490, 0xf110, 0xf4e0,
0xf430, 0xfd60, 0xf430, 0xfc20, 0xf2a0, 0xf470, 0xf4e0, 0xf250,
0xf430, 0xf320, 0xfee0, 0xf420, 0xf220, 0xf420, 0xfdd0, 0xf420,
0xf620, 0xfc20, 0xfc10, 0xf520, 0xf420, 0xf220, 0xf420, 0xfa50,
0xfe20, 0xf620, 0xf470, 0xf420, 0xfe10, 0xf4e0, 0xfe40, 0xf140,
0xf290, 0xf410, 0xf520, 0xf410, 0xfc10, 0xf120, 0xf420, 0xfe10,
0xf520, 0xf4a0, 0xfe60, 0xfe60, 0xf620, 0xf620, 0xfce0, 0xf420,
0xfc20, 0xfc20, 0xfd90, 0xf420
};
Monsters::Monsters() : _monPix(MONPIX_DTA) {
}
bool Monsters::load() {
Common::File f;
if (!f.open("monsters.txt"))
return false;
for (int lineNum = 0; lineNum < MONSTERS_COUNT; ++lineNum) {
Monster &mon = _monsters[lineNum];
Common::String line = f.readLine();
assert(line.size() > 20 && line[0] == '"' && line[16] == '"');
mon._name = Common::String(line.c_str() + 1, line.c_str() + 15);
while (mon._name.hasSuffix(" "))
mon._name.deleteLastChar();
line = Common::String(line.c_str() + 17);
mon._count = getNextValue(line);
mon._fleeThreshold = getNextValue(line);
mon._defaultHP = getNextValue(line);
mon._defaultAC = getNextValue(line);
mon._maxDamage = getNextValue(line);
mon._numberOfAttacks = getNextValue(line);
mon._speed = getNextValue(line);
mon._experience = getNextValue(line);
mon._loot = getNextValue(line);
mon._resistUndead = getNextValue(line);
mon._resistances = getNextValue(line);
mon._bonusOnTouch = getNextValue(line);
mon._specialAbility = getNextValue(line);
mon._specialThreshold = getNextValue(line);
mon._counterFlags = getNextValue(line);
mon._imgNum = getNextValue(line);
}
return true;
}
Graphics::ManagedSurface Monsters::getMonsterImage(int imgNum) {
Common::SeekableReadStream *entry = _monPix.load(imgNum);
entry->skip(2);
// Decode the image
Graphics::ManagedSurface img;
Gfx::ScreenDecoder decoder;
uint pal = PALETTE[imgNum];
decoder._indexes[0] = pal & 0xf;
decoder._indexes[1] = (pal >> 4) & 0xf;
decoder._indexes[2] = (pal >> 8) & 0xf;
decoder._indexes[3] = (pal >> 12) & 0xf;
if (!decoder.loadStream(*entry, 104, 96))
error("Failed decoding monster image");
img.copyFrom(*decoder.getSurface());
return img;
}
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,140 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_DATA_MONSTERS_H
#define MM1_DATA_MONSTERS_H
#include "mm/mm1/gfx/dta.h"
#include "mm/mm1/data/text_parser.h"
#include "common/str.h"
#include "graphics/managed_surface.h"
namespace MM {
namespace MM1 {
#define MONSTERS_COUNT 195
enum MonsterStatus {
MON_PARALYZED = 0, MON_WEBBED = 1, MON_HELD = 2,
MON_ASLEEP = 3, MON_MINDLESS = 4, MON_SILENCED = 5,
MON_BLINDED = 6, MON_AFRAID = 7, MON_DEAD = 8
};
enum MonsterStatusFlag {
MONFLAG_AFRAID = 1, MONFLAG_BLIND = 2, MONFLAG_SILENCED = 4,
MONFLAG_MINDLESS = 8, MONFLAG_ASLEEP = 0x10,
MONFLAG_HELD = 0x20, MONFLAG_WEBBED = 0x40,
MONFLAG_PARALYZED = 0x80, MONFLAG_DEAD = 0xff
};
enum MonsterLoot {
DROPS_GEMS = 0x1, // if 1 - a monster can drop gems
GOLD_DROP = 0xfe>>1 // _loot>>1 - gold value that a monster drops
};
enum MonsterResistUndead {
MAGIC_RESISTANCE = 0x7f,
IS_UNDEAD = 0x80
};
enum MonsterBonusOnTouch {
TOUCH_BONUS_VALUE = 0x7f,
HAS_BONUS = 0x80
};
enum MonsterResistances {
MONRES_ASLEEP = 1, MONRES_FEAR = 2, MONRES_PARALYSIS = 4,
MONRES_ENERGY = 8, MONRES_COLD = 0x10,
MONRES_ELECTRICITY = 0x20, MONRES_FIRE = 0x40,
MONRES_PHYSICAL_ATTACK = 0x80,
};
enum MonsterSpecialAbility {
HAS_RANGED_ATTACK = 0x80, // 1 if has ranged attack, 0 if has special attack
ATTACK_VALUE = 0x7f // damage for ranged attack. Special attack id for special attack
};
enum MonsterCounter {
COUNTER_BITS = 0xf,
COUNTER_THRESHOLD1 = 0x10, COUNTER_THRESHOLD2 = 0x20,
COUNTER_REGENERATE = 0x40, COUNTER_ADVANCES = 0x80
};
struct Monster {
Common::String _name; // char _name[15];
byte _count;
byte _fleeThreshold;
byte _defaultHP;
byte _defaultAC;
byte _maxDamage;
byte _numberOfAttacks;
byte _speed;
uint16 _experience;
byte _loot;
byte _resistUndead;
byte _resistances;
byte _bonusOnTouch;
byte _specialAbility;
byte _specialThreshold; // % of luck of special attack
byte _counterFlags;
byte _imgNum;
// Runtime combat fields
byte _level = 0;
bool _checked = false;
byte _status = 0;
byte _hp = 0;
byte _ac = 0;
Common::String getDisplayName() const {
return _name;
}
};
class Monsters : public TextParser {
private:
Monster _monsters[MONSTERS_COUNT];
Gfx::DTA _monPix;
public:
Monsters();
/**
* Load the monster list
*/
bool load();
/**
* Square brackets operator
*/
const Monster &operator[](uint i) {
assert(i >= 1 && i <= MONSTERS_COUNT);
return _monsters[i - 1];
}
/**
* Get a monster image
*/
Graphics::ManagedSurface getMonsterImage(int imgNum);
};
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,185 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/data/party.h"
#include "mm/mm1/globals.h"
namespace MM {
namespace MM1 {
#define SHARE_FIELD(FIELD) \
for (uint i = 0; i < party.size(); ++i) \
total += party[i].FIELD; \
avg = total / party.size(); \
party[0].FIELD = avg + (total % party.size()); \
for (uint i = 1; i < party.size(); ++i) \
party[i].FIELD = avg;
void Party::share(TransferKind shareType) {
auto &party = g_globals->_party;
int total = 0, avg;
switch (shareType) {
case TK_GEMS:
SHARE_FIELD(_gems);
break;
case TK_GOLD:
SHARE_FIELD(_gold);
break;
case TK_FOOD:
SHARE_FIELD(_food);
break;
default:
break;
}
}
uint Party::getPartyGold() const {
uint total = 0;
for (uint i = 0; i < size(); ++i)
total += (*this)[i]._gold;
return total;
}
void Party::clearPartyGold() {
for (uint i = 0; i < size(); ++i)
(*this)[i]._gold = 0;
}
void Party::clearPartyGems() {
for (uint i = 0; i < size(); ++i)
(*this)[i]._gems = 0;
}
void Party::clearPartyFood() {
for (uint i = 0; i < size(); ++i)
(*this)[i]._food = 0;
}
void Party::updateAC() {
for (uint i = 0; i < size(); ++i)
(*this)[i].updateAC();
}
void Party::combatDone() {
for (uint i = 0; i < size(); ++i) {
Character &c = (*this)[i];
c.updateAttributes();
c.updateResistances();
if (!(c._condition & BAD_CONDITION))
c._condition &= ~(ASLEEP | SILENCED);
}
}
bool Party::hasItem(byte itemId) const {
for (uint i = 0; i < size(); ++i) {
const Character &c = (*this)[i];
if (c._equipped.indexOf(itemId) != -1 ||
c._backpack.indexOf(itemId) != -1)
return true;
}
return false;
}
bool Party::isPartyDead() const {
for (uint i = 0; i < size(); ++i) {
const Character &c = (*this)[i];
if (!(c._condition & (ASLEEP | PARALYZED | UNCONSCIOUS | BAD_CONDITION)))
return false;
}
return true;
}
bool Party::checkPartyDead() const {
if (isPartyDead()) {
// At this point, there's no good characters.
// So redirect to the death screen
g_events->replaceView("Dead", true);
return true;
} else {
return false;
}
}
bool Party::checkPartyIncapacitated() const {
bool isActive = false;
for (uint i = 0; i < size() && !isActive; ++i) {
const Character &c = (*this)[i];
isActive = !(c._condition & (BAD_CONDITION | UNCONSCIOUS));
}
if (isActive) {
return false;
} else {
g_events->replaceView("Dead", true);
return true;
}
}
void Party::synchronize(Common::Serializer &s) {
int partySize = size();
s.syncAsByte(partySize);
if (s.isLoading())
resize(partySize);
for (int i = 0; i < partySize; ++i) {
// Sync the common properties
Character &c = (*this)[i];
c.synchronize(s);
// Sync extra properties
s.syncAsSByte(c._combatSpell);
s.syncAsSByte(c._nonCombatSpell);
}
if (s.isLoading())
g_globals->_currCharacter = &front();
}
void Party::rearrange(const Common::Array<Character *> &party) {
assert(party.size() == size());
for (uint i = 0; i < size(); ++i) {
for (uint j = i; j < size(); ++j) {
if (party[i] == &(*this)[j]) {
if (j != i)
insert_at(i, remove_at(j));
break;
}
}
}
}
int Party::indexOf(const Character *c) {
for (uint i = 0; i < size(); ++i) {
if (&(*this)[i] == c)
return i;
}
return -1;
}
} // namespace MM1
} // namespace MM

110
engines/mm/mm1/data/party.h Normal file
View File

@@ -0,0 +1,110 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_DATA_PARTY_H
#define MM1_DATA_PARTY_H
#include "common/array.h"
#include "mm/mm1/data/character.h"
namespace MM {
namespace MM1 {
#define MAX_PARTY_SIZE 6
struct Party : public Common::Array<Character> {
/**
* Share food, gold, gems between entire party
*/
static void share(TransferKind shareType);
/**
* Get the party gold combined
*/
uint getPartyGold() const;
/**
* Reset entire party's gold to zero
*/
void clearPartyGold();
/**
* Reset entire party's gems to zero
*/
void clearPartyGems();
/**
* Reset entire party's food to zero
*/
void clearPartyFood();
/**
* Update the entire party AC
*/
void updateAC();
/**
* Called to update the party after combat is done
*/
void combatDone();
/**
* Returns true if any of the party has an item
*/
bool hasItem(byte itemId) const;
/**
* Returns true if the party is dead or out of action
*/
bool isPartyDead() const;
/**
* Checks whether the party is dead or out of action,
* and if so, switches to the death screen
*/
bool checkPartyDead() const;
/**
* Checks whether the party is incapitated, and if so,
* switches to the death screen
*/
bool checkPartyIncapacitated() const;
/**
* Reorder the party based on a passed array of character pointers
*/
void rearrange(const Common::Array<Character *> &party);
/**
* Synchronizes the party to/from savegames
*/
void synchronize(Common::Serializer &s);
/**
* Return the index of a given character
*/
int indexOf(const Character *c);
};
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,177 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/file.h"
#include "common/memstream.h"
#include "common/savefile.h"
#include "common/system.h"
#include "mm/mm1/data/roster.h"
#include "mm/mm1/mm1.h"
namespace MM {
namespace MM1 {
static byte DEFAULT_PORTRAITS[6] = { 0, 11, 9, 7, 4, 3 };
void Roster::synchronize(Common::Serializer &s, bool isLoadingDefaults) {
for (int i = 0; i < ROSTER_COUNT; ++i)
_items[i].synchronize(s, s.isLoading() && isLoadingDefaults ?
(i < 6 ? DEFAULT_PORTRAITS[i] : 0xff) : -1
);
for (int i = 0; i < ROSTER_COUNT; ++i)
s.syncAsByte(_towns[i]);
}
void Roster::load() {
Common::InSaveFile *sf = g_system->getSavefileManager()->openForLoading(
rosterSaveName());
if (sf) {
Common::Serializer s(sf, nullptr);
synchronize(s, false);
while (!sf->eos()) {
uint32 chunk = sf->readUint32BE();
if (!sf->eos() && chunk == MKTAG('M', 'A', 'P', 'S')) {
sf->skip(4); // Skip chunk size
g_maps->synchronize(s);
}
}
} else {
sf = g_system->getSavefileManager()->openForLoading("roster.dta");
if (sf) {
Common::Serializer s(sf, nullptr);
synchronize(s, true);
} else {
Common::File f;
if (!f.open("roster.dta"))
error("Could not open roster.dta");
Common::Serializer s(&f, nullptr);
synchronize(s, true);
}
}
}
void Roster::update(const IntArray &charNums) {
int fallbackIndex = ROSTER_COUNT - 1;
for (int i = (int)g_globals->_party.size() - 1; i >= 0; --i) {
const Character &c = g_globals->_party[i];
int destIndex;
if (charNums.size() == g_globals->_party.size() &&
charNums[i] < ROSTER_COUNT &&
!strcmp(_items[charNums[i]]._name, c._name)) {
// Started game from title screen and set up party,
// so we known the correct roster index already
destIndex = charNums[i];
} else {
for (destIndex = 0; destIndex < ROSTER_COUNT; ++destIndex) {
if (!strcmp(_items[destIndex]._name, c._name))
break;
}
if (destIndex == ROSTER_COUNT) {
// Couldn't find a matching name in roster to update
for (destIndex = 0; destIndex < ROSTER_COUNT; ++destIndex) {
if (!_towns[destIndex])
break;
}
if (destIndex == ROSTER_COUNT)
// Replace entries at the end of the roster
destIndex = fallbackIndex--;
}
}
// Copy the entry into the roster
_items[destIndex] = c;
_towns[destIndex] = (Maps::TownId)g_maps->_currentMap->dataByte(Maps::MAP_ID);
}
}
void Roster::save() {
Common::OutSaveFile *sf = g_system->getSavefileManager()->openForSaving(
rosterSaveName());
Common::Serializer s(nullptr, sf);
synchronize(s, false);
// Get automap data to save
Common::MemoryWriteStreamDynamic mapData(DisposeAfterUse::YES);
Common::Serializer s2(nullptr, &mapData);
g_maps->synchronize(s2);
// Write out the map data
sf->writeUint32BE(MKTAG('M', 'A', 'P', 'S'));
sf->writeUint32LE(mapData.size());
sf->write(mapData.getData(), mapData.size());
sf->finalize();
delete sf;
}
void Roster::saveOriginal() {
Common::OutSaveFile *sf = g_system->getSavefileManager()->openForSaving(
"roster.dta", false);
Common::Serializer s(nullptr, sf);
synchronize(s, false);
sf->finalize();
delete sf;
}
Common::String Roster::rosterSaveName() const {
return Common::String::format("%s-roster.dta",
g_engine->getTargetName().c_str());
}
void Roster::remove(Character *entry) {
entry->clear();
size_t idx = entry - _items;
_towns[idx] = Maps::NO_TOWN;
}
bool Roster::empty() const {
for (uint i = 0; i < ROSTER_COUNT; ++i) {
if (_towns[i])
return false;
}
return true;
}
bool Roster::full() const {
for (uint i = 0; i < ROSTER_COUNT; ++i) {
if (!_towns[i])
return false;
}
return true;
}
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,93 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_DATA_ROSTER_H
#define MM1_DATA_ROSTER_H
#include "common/serializer.h"
#include "mm/mm1/data/character.h"
#include "mm/mm1/data/int_array.h"
#include "mm/mm1/maps/maps.h"
namespace MM {
namespace MM1 {
#define ROSTER_COUNT 18
struct Roster {
private:
Common::String rosterSaveName() const;
public:
Character _items[ROSTER_COUNT];
Maps::TownId _towns[ROSTER_COUNT] = { Maps::NO_TOWN };
Character &operator[](uint idx) {
assert(idx < ROSTER_COUNT);
return _items[idx];
}
/**
* Synchronizes the contents of the roster
* @param isLoadingDefaults True if we're loading the
* default roster of the game
*/
void synchronize(Common::Serializer &s, bool isLoadingDefaults);
/**
* Load the roster
*/
void load();
/**
* Updates the roster from the party
*/
void update(const IntArray &charNums);
/**
* Save the roster
*/
void save();
/**
* Save the roster in the original format
*/
void saveOriginal();
/**
* Deletes a character
*/
void remove(Character *entry);
/**
* Returns true if the roster is empty
*/
bool empty() const;
/**
* Returns true if the roster is full
*/
bool full() const;
};
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,61 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_DATA_SPELLS_H
#define MM1_DATA_SPELLS_H
#include "common/serializer.h"
#include "mm/mm1/data/character.h"
namespace MM {
namespace MM1 {
struct SpellsState {
byte _mmVal1 = 0;
byte _resistanceIndex = 0;
byte _mmVal5 = 0;
byte _mmVal7 = 0;
// This can hold both a resistance type, or count of monsters to affect
byte _resistanceTypeOrTargetCount = RESISTANCE_MAGIC;
// TODO: Is this variable different in different contexts?
// In some places it's used to hold a new condition,
// but others, like moonRay, uses it to hold Hp
byte _damage = 0;
/**
* Synchronize data to/from savegames
*/
void synchronize(Common::Serializer &s) {
s.syncAsByte(_mmVal1);
s.syncAsByte(_resistanceIndex);
s.syncAsByte(_mmVal5);
s.syncAsByte(_mmVal7);
s.syncAsByte(_resistanceTypeOrTargetCount);
s.syncAsByte(_damage);
}
};
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,44 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/data/text_parser.h"
#include "common/util.h"
namespace MM {
namespace MM1 {
uint TextParser::getNextValue(Common::String &line) {
// Verify the next comma
if (!line.hasPrefix(", "))
return 0;
line.deleteChar(0);
line.deleteChar(0);
// Get the value
int result = atoi(line.c_str());
while (!line.empty() && Common::isDigit(line.firstChar()))
line.deleteChar(0);
return result;
}
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,40 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_DATA_TEXT_PARSER_H
#define MM1_DATA_TEXT_PARSER_H
#include "common/str.h"
namespace MM {
namespace MM1 {
/**
* Get the next value from a read line
*/
struct TextParser {
static uint getNextValue(Common::String &line);
};
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,141 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/data/trap.h"
#include "mm/mm1/globals.h"
namespace MM {
namespace MM1 {
int8 TrapData::RESISTANCE_INDEXES[11] = {
-1, -1, -1, -1, -1, -1,
1, // Fire
3, // Electricity
4, // Acid
2, // Cold
0 // Magic
};
byte TrapData::CONDITIONS1[11] = {
16, 32, 16, 16, 2, 1, 2, 32, 2, 2, 2
};
byte TrapData::CONDITIONS2[11] = {
16, 16, 16, 32, 2, 16, 64, 64, 2, 32, 32
};
byte TrapData::CONDITIONS3[11] = {
64, 64, 64, 64, 64, 192, 192, 192, 255, 64, 224
};
byte TrapData::DAMAGE_TYPE[7] = {
3, 0, 2, 5, 4, 1, 6
};
void TrapData::trap() {
_trapType = getRandomNumber(11) - 1;
int maxVal = 4;
for (int i = (int)g_globals->_treasure._container - 1; i > 0; i -= 2) {
maxVal <<= 1;
}
maxVal += getRandomNumber(maxVal);
_resistanceIndex = RESISTANCE_INDEXES[_trapType];
if (g_globals->_treasure._container < WOODEN_BOX)
_condition = 0;
else if (g_globals->_treasure._container < SILVER_BOX)
_condition = CONDITIONS1[_trapType];
else if (g_globals->_treasure._container < BLACK_BOX)
_condition = CONDITIONS2[_trapType];
else
_condition = CONDITIONS3[_trapType];
int idx = _trapType;
if (idx >= 7)
idx -= 5;
else if (_condition == POISONED)
idx = 0;
else if (_condition == PARALYZED)
idx = 1;
else
idx = -1;
int val4 = 0;
_reduced = 0;
if (idx >= 0) {
int spellCount = g_globals->_activeSpells._arr[DAMAGE_TYPE[idx]];
if (spellCount > 0 && getRandomNumber(100) < spellCount) {
_reduced = val4 = 1;
maxVal = 1;
}
}
for (uint i = 0; i < g_globals->_party.size(); ++i, _reduced = val4) {
_hpInitial = maxVal;
damageChar(i);
}
}
void TrapData::damageChar(uint partyIndex) {
Character &c = g_globals->_party[partyIndex];
if (&c != g_globals->_currCharacter)
_hpInitial >>= 1;
if (_resistanceIndex != -1 &&
c._resistances._arr[_resistanceIndex] != 0 &&
getRandomNumber(100) < c._resistances._arr[_resistanceIndex]) {
_hpInitial >>= 1;
++_reduced;
}
int luckLevel1 = c._luck + c._level;
int luckLevel2 = getRandomNumber(luckLevel1 + 20);
if (getRandomNumber(luckLevel2) < luckLevel1) {
_hpInitial >>= 1;
++_reduced;
}
if (c._condition & BAD_CONDITION) {
c._hpCurrent = 0;
} else if (c._condition & UNCONSCIOUS) {
c._condition = BAD_CONDITION | DEAD;
c._hpCurrent = 0;
} else {
c._hpCurrent = MAX((int)c._hpCurrent - _hpInitial, 0);
if (c._hpCurrent == 0) {
c._condition |= UNCONSCIOUS;
} else if (!_reduced && _condition &&
getRandomNumber(luckLevel1 + 20) >= luckLevel1) {
if (_condition >= UNCONSCIOUS)
c._hpCurrent = 0;
if (!(c._condition & BAD_CONDITION))
c._condition = _condition;
}
}
}
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,59 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_DATA_TRAP_H
#define MM1_DATA_TRAP_H
#include "common/scummsys.h"
#include "mm/mm1/game/game_logic.h"
namespace MM {
namespace MM1 {
class TrapData : public Game::GameLogic {
private:
/**
* Damages a party member from the trap
*/
void damageChar(uint partyIndex);
protected:
static int8 RESISTANCE_INDEXES[11];
static byte CONDITIONS1[11];
static byte CONDITIONS2[11];
static byte CONDITIONS3[11];
static byte DAMAGE_TYPE[7];
int _trapType = 0;
int _hpInitial = 0;
int _reduced = 0;
int _resistanceIndex = 0;
byte _condition = 0;
virtual void trap();
public:
virtual ~TrapData() {}
};
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,80 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/algorithm.h"
#include "mm/mm1/data/treasure.h"
namespace MM {
namespace MM1 {
#define TREASURE_COUNT 6
#define ARRAY_COUNT (TREASURE_COUNT + 3)
void Treasure::clear() {
Common::fill(&_data[0], &_data[ARRAY_COUNT], 0);
}
void Treasure::clear0() {
Common::fill(&_data[1], &_data[ARRAY_COUNT], 0);
}
byte &Treasure::operator[](uint i) {
assert(i < ARRAY_COUNT);
return _data[i];
}
bool Treasure::present() const {
// Checks for items, treasure, and/or gems
for (int i = 0; i < TREASURE_COUNT; ++i) {
if (_data[i + 3])
return true;
}
return false;
}
bool Treasure::hasItems() const {
for (int i = 0; i < 3; ++i) {
if (_items[i])
return true;
}
return false;
}
byte Treasure::removeItem() {
for (int i = 0; i < 3; ++i) {
if (_items[i]) {
byte result = _items[i];
_items[i] = 0;
return result;
}
}
return 0;
}
void Treasure::synchronize(Common::Serializer &s) {
s.syncBytes(_data, 9);
}
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,114 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_DATA_TREASURE_H
#define MM1_DATA_TREASURE_H
#include "common/scummsys.h"
#include "common/serializer.h"
namespace MM {
namespace MM1 {
enum ContainerType {
CLOTH_SACK = 0, LEATHER_SACK = 1, WOODEN_BOX = 2,
WOODEN_CHEST = 3, IRON_BOX = 4, IRON_CHEST = 5,
SILVER_BOX = 6, SILVER_CHEST = 7, GOLD_BOX = 8,
GOLD_CHEST = 9, BLACK_BOX = 10
};
class Treasure {
private:
byte _data[9];
public:
byte &_trapType = _data[1];
byte &_container = _data[2];
byte *const _items = &_data[3];
Treasure() {
clear();
}
byte &operator[](uint i);
/**
* Clears the treasure list
*/
void clear();
/**
* Clears everything except byte 0
*/
void clear0();
/**
* Returns true if any treasure has been assigned
*/
bool present() const;
/**
* Returns true if any items are present
*/
bool hasItems() const;
/**
* Synchronize savegame data
*/
void synchronize(Common::Serializer &s);
/**
* Return the gold value for the treasure
*/
uint16 getGold() const {
return READ_LE_UINT16(&_data[6]);
}
/**
* Sets the gold value for the treasure
*/
void setGold(uint16 amount) {
WRITE_LE_UINT16(&_data[6], amount);
}
/**
* Return the gems amount
*/
byte getGems() const {
return _data[8];
}
/**
* Set the gems amount
*/
void setGems(byte amount) {
_data[8] = amount;
}
/**
* Get any item, and remove it from the treasure
*/
byte removeItem();
};
} // namespace MM1
} // namespace MM
#endif

366
engines/mm/mm1/events.cpp Normal file
View File

@@ -0,0 +1,366 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/system.h"
#include "graphics/screen.h"
#include "mm/mm1/events.h"
#include "mm/mm1/mm1.h"
#include "mm/mm1/gfx/gfx.h"
#include "mm/mm1/views/dialogs.h"
#include "mm/mm1/views_enh/dialogs.h"
namespace MM {
namespace MM1 {
Events *g_events;
Events::Events(bool enhancedMode) : UIElement("Root", nullptr),
_enhancedMode(enhancedMode) {
g_events = this;
}
Events::~Events() {
g_events = nullptr;
}
void Events::runGame() {
ViewsBase *allViews = _enhancedMode ?
(ViewsBase *)new ViewsEnh::Dialogs() :
(ViewsBase *)new Views::Dialogs();
uint currTime, nextFrameTime = 0;
_screen = new Graphics::Screen();
// Run the game
int saveSlot = ConfMan.getInt("save_slot");
if (saveSlot == -1 ||
g_engine->loadGameState(saveSlot).getCode() != Common::kNoError) {
addView("Title");
}
Common::Event e;
bool quitFlag = false;
while (!quitFlag) {
while (g_system->getEventManager()->pollEvent(e)) {
if (e.type == Common::EVENT_QUIT ||
e.type == Common::EVENT_RETURN_TO_LAUNCHER) {
quitFlag = true;
break;
} else {
processEvent(e);
}
}
g_system->delayMillis(10);
if ((currTime = g_system->getMillis()) >= nextFrameTime) {
nextFrameTime = currTime + FRAME_DELAY;
tick();
drawElements();
_screen->update();
}
quitFlag |= shouldQuit();
}
delete _screen;
delete allViews;
}
void Events::processEvent(Common::Event &ev) {
switch (ev.type) {
case Common::EVENT_KEYDOWN:
if (ev.kbd.keycode < Common::KEYCODE_NUMLOCK)
msgKeypress(KeypressMessage(ev.kbd));
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
msgAction(ActionMessage((KeybindingAction)ev.customType));
break;
case Common::EVENT_LBUTTONDOWN:
case Common::EVENT_RBUTTONDOWN:
//case Common::EVENT_MBUTTONDOWN:
msgMouseDown(MouseDownMessage(ev.type, ev.mouse));
break;
case Common::EVENT_LBUTTONUP:
case Common::EVENT_RBUTTONUP:
//case Common::EVENT_MBUTTONUP:
msgMouseUp(MouseUpMessage(ev.type, ev.mouse));
break;
default:
break;
}
}
void Events::replaceView(UIElement *ui, bool replaceAllViews) {
assert(ui);
UIElement *priorView = focusedView();
if (replaceAllViews) {
clearViews();
} else if (!_views.empty()) {
priorView->msgUnfocus(UnfocusMessage());
_views.pop();
}
_views.push(ui);
ui->redraw();
ui->msgFocus(FocusMessage(priorView));
}
void Events::replaceView(const Common::String &name, bool replaceAllViews) {
replaceView(findView(name));
}
void Events::addView(UIElement *ui) {
assert(ui);
UIElement *priorView = focusedView();
if (!_views.empty())
priorView->msgUnfocus(UnfocusMessage());
_views.push(ui);
ui->redraw();
ui->msgFocus(FocusMessage(priorView));
}
void Events::addView(const Common::String &name) {
addView(findView(name));
}
void Events::popView() {
UIElement *priorView = focusedView();
priorView->msgUnfocus(UnfocusMessage());
_views.pop();
for (int i = 0; i < (int)_views.size() - 1; ++i) {
_views[i]->redraw();
_views[i]->draw();
}
if (!_views.empty()) {
UIElement *view = focusedView();
view->msgFocus(FocusMessage(priorView));
view->redraw();
view->draw();
}
}
void Events::redrawViews() {
for (uint i = 0; i < _views.size(); ++i) {
_views[i]->redraw();
_views[i]->draw();
}
}
bool Events::isPresent(const Common::String &name) const {
for (uint i = 0; i < _views.size(); ++i) {
if (_views[i]->_name == name)
return true;
}
return false;
}
void Events::clearViews() {
if (!_views.empty())
focusedView()->msgUnfocus(UnfocusMessage());
_views.clear();
}
void Events::addKeypress(const Common::KeyCode kc) {
Common::KeyState ks;
ks.keycode = kc;
if (kc >= Common::KEYCODE_SPACE && kc <= Common::KEYCODE_TILDE)
ks.ascii = kc;
focusedView()->msgKeypress(KeypressMessage(ks));
}
void Events::addAction(KeybindingAction action) {
focusedView()->msgAction(ActionMessage(action));
}
bool Events::isKeypressPending() const {
// TODO: Currently the engine doesn't cache keypresses, but rather
// processes them immediately after each is pulled from the
// SDL event queue. So for this to work, we'd need to rework
// the event handler code
return false;
}
/*------------------------------------------------------------------------*/
Bounds::Bounds(Common::Rect &innerBounds) :
_bounds(0, 0, 320, 200),
_innerBounds(innerBounds),
left(_bounds.left), top(_bounds.top),
right(_bounds.right), bottom(_bounds.bottom) {
}
Bounds &Bounds::operator=(const Common::Rect &r) {
_bounds = r;
_innerBounds = r;
_innerBounds.grow(-_borderSize);
return *this;
}
void Bounds::setBorderSize(size_t borderSize) {
_borderSize = borderSize;
_innerBounds = *this;
_innerBounds.grow(-_borderSize);
}
/*------------------------------------------------------------------------*/
UIElement::UIElement(const Common::String &name, UIElement *uiParent) :
_name(name), _parent(uiParent),
_bounds(_innerBounds) {
if (_parent)
_parent->_children.push_back(this);
}
void UIElement::redraw() {
_needsRedraw = true;
for (size_t i = 0; i < _children.size(); ++i)
_children[i]->redraw();
}
void UIElement::drawElements() {
if (_needsRedraw) {
draw();
_needsRedraw = false;
}
for (size_t i = 0; i < _children.size(); ++i)
_children[i]->drawElements();
}
UIElement *UIElement::findViewGlobally(const Common::String &name) {
return g_events->findView(name);
}
void UIElement::focus() {
g_engine->replaceView(this);
}
void UIElement::close() {
assert(g_engine->focusedView() == this);
g_engine->popView();
}
bool UIElement::isFocused() const {
return g_events->focusedView() == this;
}
void UIElement::clearSurface() {
Graphics::ManagedSurface s = getSurface();
s.fillRect(Common::Rect(s.w, s.h), 0);
}
void UIElement::draw() {
for (size_t i = 0; i < _children.size(); ++i) {
_children[i]->draw();
}
}
bool UIElement::tick() {
if (_timeoutCtr && --_timeoutCtr == 0) {
timeout();
}
for (size_t i = 0; i < _children.size(); ++i) {
if (_children[i]->tick())
return true;
}
return false;
}
UIElement *UIElement::findView(const Common::String &name) {
if (_name.equalsIgnoreCase(name))
return this;
UIElement *result;
for (size_t i = 0; i < _children.size(); ++i) {
if ((result = _children[i]->findView(name)) != nullptr)
return result;
}
return nullptr;
}
void UIElement::replaceView(UIElement *ui, bool replaceAllViews) {
g_events->replaceView(ui, replaceAllViews);
}
void UIElement::replaceView(const Common::String &name, bool replaceAllViews) {
g_events->replaceView(name, replaceAllViews);
}
void UIElement::addView(UIElement *ui) {
g_events->addView(ui);
}
void UIElement::addView(const Common::String &name) {
g_events->addView(name);
}
void UIElement::addView() {
g_events->addView(this);
}
Graphics::ManagedSurface UIElement::getSurface() const {
return Graphics::ManagedSurface(*g_events->getScreen(), _bounds);
}
int UIElement::getRandomNumber(int minNumber, int maxNumber) {
return g_engine->getRandomNumber(maxNumber - minNumber + 1) + minNumber;
}
int UIElement::getRandomNumber(int maxNumber) {
return g_engine->getRandomNumber(maxNumber);
}
void UIElement::delaySeconds(uint seconds) {
_timeoutCtr = seconds * FRAME_RATE;
}
void UIElement::delayFrames(uint frames) {
_timeoutCtr = frames;
}
bool UIElement::endDelay() {
if (_timeoutCtr) {
_timeoutCtr = 0;
timeout();
return true;
} else {
return false;
}
}
void UIElement::timeout() {
redraw();
}
} // namespace MM1
} // namespace MM

407
engines/mm/mm1/events.h Normal file
View File

@@ -0,0 +1,407 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_EVENTS_H
#define MM1_EVENTS_H
#include "common/array.h"
#include "common/stack.h"
#include "graphics/screen.h"
#include "mm/mm1/messages.h"
#include "mm/mm1/utils/mouse.h"
namespace MM {
namespace MM1 {
#define FRAME_RATE 20
#define FRAME_DELAY (1000 / FRAME_RATE)
class Events;
/**
* Implements a thunk layer around an element's
* bounds, allowing access to it as if it were
* a simple Common::Rect, but any changes to it
* will also be applied to a linked inner bounds
*/
struct Bounds {
private:
Common::Rect _bounds;
Common::Rect &_innerBounds;
int _borderSize = 0;
public:
const int16 &left;
const int16 &top;
const int16 &right;
const int16 &bottom;
public:
Bounds(Common::Rect &innerBounds);
operator const Common::Rect &() const { return _bounds; }
Bounds &operator=(const Common::Rect &r);
void setBorderSize(size_t borderSize);
size_t borderSize() const { return _borderSize; }
int16 width() const { return _bounds.width(); }
int16 height() const { return _bounds.height(); }
};
/**
* User interface element
*/
class UIElement {
friend class Events;
private:
int _timeoutCtr = 0;
protected:
UIElement *_parent;
Common::Array<UIElement *> _children;
Common::Rect _innerBounds;
Bounds _bounds;
bool _needsRedraw = true;
Common::String _name;
protected:
Common::Rect getLineBounds(int line1, int line2) const {
return Common::Rect(0, line1 * 8, 320, (line2 + 1) * 8);
}
/**
* Set a delay countdown in seconds
*/
void delaySeconds(uint seconds);
/**
* Set a delay countdown in frames
*/
void delayFrames(uint frames);
/**
* Returns true if a delay is active
*/
bool isDelayActive() const {
return _timeoutCtr != 0;
}
/**
* Cancels any active delay
*/
void cancelDelay() {
_timeoutCtr = 0;
}
/**
* Ends an active delay and calls timeout
*/
bool endDelay();
/**
* Called when an active timeout countdown expired
*/
virtual void timeout();
private:
/**
* Outer method for doing drawing
*
*/
void drawElements();
/**
* Finds a view globally
*/
static UIElement *findViewGlobally(const Common::String &name);
public:
UIElement(const Common::String &name, UIElement *uiParent);
virtual ~UIElement() {}
/**
* Returns true if the elements needs to be redrawn
*/
bool needsRedraw() const { return _needsRedraw; }
/**
* Sets that the element needs to be redrawn
*/
void redraw();
/**
* Focuses the element as the current view
*/
void focus();
/**
* Closes the current view. The view must have been added
* via addView, so there's a remaining view afterwards
*/
virtual void close();
/*
* Returns true if the view is focused
*/
bool isFocused() const;
/**
* Sets the focus to a new view
*/
void replaceView(UIElement *ui, bool replaceAllViews = false);
void replaceView(const Common::String &name, bool replaceAllViews = false);
/**
* Adds a focused view to the view stack without replacing current one
*/
void addView(UIElement *ui);
void addView(const Common::String &name);
void addView();
void open() { addView(); }
/**
* Returns a random number
*/
int getRandomNumber(int minNumber, int maxNumber);
int getRandomNumber(int maxNumber);
/**
* Gets the element's name
*/
Common::String getName() const { return _name; }
/**
* Sets the element's bounds
*/
virtual void setBounds(const Common::Rect &r) {
_bounds = r;
}
/**
* Gets the element's bounds
*/
Common::Rect getBounds() const {
return _bounds;
}
/**
* Returns a surface for drawing the element
*/
Graphics::ManagedSurface getSurface() const;
/**
* Clear the surface
*/
virtual void clearSurface();
/**
* Draws the element
*/
virtual void draw();
/**
* Called for game frame ticks
*/
virtual bool tick();
/**
* Find a view by name
*/
virtual UIElement *findView(const Common::String &name);
/**
* Handles events
*/
#define MESSAGE(NAME) \
protected: \
virtual bool msg##NAME(const NAME##Message &e) { \
for (Common::Array<UIElement *>::iterator it = _children.begin(); \
it != _children.end(); ++it) { \
if ((*it)->msg##NAME(e)) return true; \
} \
return false; \
} \
public: \
bool send(const Common::String &viewName, const NAME##Message &msg) { \
UIElement *view = UIElement::findViewGlobally(viewName); \
assert(view); \
return view->msg##NAME(msg); \
} \
bool send(const NAME##Message &msg) { \
return send("Root", msg); \
} \
MESSAGE(Focus);
MESSAGE(Unfocus);
MESSAGE(Keypress);
MESSAGE(MouseDown);
MESSAGE(MouseUp);
MESSAGE(Action);
MESSAGE(Game);
MESSAGE(Header);
MESSAGE(Info);
MESSAGE(DrawGraphic);
#undef MESSAGE
};
class ViewsBase {
public:
ViewsBase() {}
virtual ~ViewsBase() {}
};
/**
* Main events and view manager
*/
class Events : public UIElement, public Mouse {
private:
Graphics::Screen *_screen = nullptr;
Common::Stack<UIElement *> _views;
bool _enhancedMode;
protected:
/**
* Process an event
*/
void processEvent(Common::Event &ev);
/**
* Returns true if the game should quit
*/
virtual bool shouldQuit() const = 0;
/**
* Overrides events we want to only go to the focused view
*/
#define MESSAGE(NAME) \
bool msg##NAME(const NAME##Message &e) override { \
return !_views.empty() ? focusedView()->msg##NAME(e) : false; \
}
MESSAGE(Action);
MESSAGE(Focus);
MESSAGE(Unfocus);
MESSAGE(Keypress);
MESSAGE(MouseDown);
MESSAGE(MouseUp);
MESSAGE(DrawGraphic);
#undef MESSAGE
public:
Events(bool enhancedMode);
virtual ~Events();
/**
* Main game loop
*/
void runGame();
/**
* Sets the focus to a new view
*/
void replaceView(UIElement *ui, bool replaceAllViews = false);
void replaceView(const Common::String &name, bool replaceAllViews = false);
/**
* Adds a focused view to the view stack without replacing current one
*/
void addView(UIElement *ui);
void addView(const Common::String &name);
/**
* Clears the view list
*/
void clearViews();
/**
* Pops a view from the view stack
*/
void popView();
/**
* Redraws the views in order. This is used in rare cases
* where a view draws outside it's defined area, and needs
* to restore whether the background was before
*/
void redrawViews();
/**
* Returns the currently focused view, if any
*/
UIElement *focusedView() const {
return _views.empty() ? nullptr : _views.top();
}
/**
* Returns the view prior to the current view, if any
*/
UIElement *priorView() const {
return _views.size() < 2 ? nullptr :
_views[_views.size() - 2];
}
/**
* Returns true if a view of a given name is present
* at all in the visible view stack
*/
bool isPresent(const Common::String &name) const;
/**
* Returns true if combat is active
*/
bool isInCombat() const {
return isPresent("Combat");
}
Graphics::Screen *getScreen() const {
return _screen;
}
void drawElements() {
if (!_views.empty())
focusedView()->drawElements();
}
/**
* Add a keypress to the event queue
*/
void addKeypress(const Common::KeyCode kc);
/**
* Add a action to the event queue
*/
void addAction(KeybindingAction action);
/**
* Checks whether a keypress is pending
*/
bool isKeypressPending() const;
void draw() override {}
bool tick() override {
return !_views.empty() ? focusedView()->tick() : false;
}
/**
* Calling the close method for g_events closes the active window
*/
void close() override {
focusedView()->close();
}
};
extern Events *g_events;
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,50 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/game/arenko.h"
#include "mm/mm1/globals.h"
namespace MM {
namespace MM1 {
namespace Game {
void Arenko::giveGold() {
g_globals->_treasure.setGold((getRandomNumber(8) + 8) * 256);
g_maps->clearSpecial();
g_events->addAction(KEYBIND_SEARCH);
}
void Arenko::giveGems() {
g_globals->_treasure.setGems(getRandomNumber(50) + 200);
g_maps->clearSpecial();
g_events->addAction(KEYBIND_SEARCH);
}
void Arenko::giveItem() {
g_globals->_treasure._items[2] = getRandomNumber(22) + 196;
g_maps->clearSpecial();
g_events->addAction(KEYBIND_SEARCH);
}
} // namespace Game
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,42 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAME_ARENKO_H
#define MM1_GAME_ARENKO_H
#include "mm/mm1/game/game_logic.h"
namespace MM {
namespace MM1 {
namespace Game {
class Arenko : public GameLogic {
public:
void giveGold();
void giveGems();
void giveItem();
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,82 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/game/arrested.h"
#include "mm/mm1/maps/map04.h"
#include "mm/mm1/globals.h"
namespace MM {
namespace MM1 {
namespace Game {
void Arrested::attack() {
Game::Encounter &enc = g_globals->_encounters;
int monsterCount = getRandomNumber(5);
g_events->close();
enc.clearMonsters();
for (int i = 0; i < monsterCount; ++i)
enc.addMonster(6, 10);
enc._manual = true;
enc._levelIndex = 64;
enc._encounterType = Game::FORCE_SURPRISED;
enc.execute();
}
void Arrested::bribe() {
if (getRandomNumber(10) == 10) {
// Success
g_events->close();
} else {
// 8 year sentence for attempted bribery
surrender(8);
}
}
void Arrested::run() {
g_events->close();
g_globals->_currCharacter = &g_globals->_party[
getRandomNumber(g_globals->_party.size()) - 1
];
g_globals->_currCharacter->_condition = ERADICATED;
static_cast<MM1::Maps::Map04 *>(g_maps->_currentMap)->special08();
}
void Arrested::surrender(int numYears) {
g_events->close();
// Characters are aged by their prision sentence, and their gold halved
for (uint i = 0; i < g_globals->_party.size(); ++i) {
Character &c = g_globals->_party[i];
if ((int)c._age + numYears < 256)
c._age += numYears;
c._gold /= 2;
}
(*g_maps->_currentMap)[MAP04_TREASURE_STOLEN] = 0;
}
} // namespace Game
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,45 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAMES_ARRESTED_H
#define MM1_GAMES_ARRESTED_H
#include "mm/mm1/game/game_logic.h"
namespace MM {
namespace MM1 {
namespace Game {
class Arrested : public GameLogic {
protected:
void attack();
void bribe();
void run();
void surrender(int numYears = 2);
Arrested() {}
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,351 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAME_COMBAT_H
#define MM1_GAME_COMBAT_H
#include "common/array.h"
#include "mm/mm1/data/character.h"
#include "mm/mm1/data/party.h"
#include "mm/mm1/game/game_logic.h"
#include "mm/mm1/game/encounter.h"
#include "mm/mm1/game/monster_touch.h"
namespace MM {
namespace MM1 {
namespace Game {
class Combat : public MonsterTouch {
protected:
Common::Array<Line> _message;
int _monstersCount = 0;
Monster *_monsterP;
bool _treasureFlags[MAX_PARTY_SIZE];
int _val1;
int _roundNum;
int _monsterIndex, _currentChar;
bool _allowFight, _allowShoot, _allowCast, _allowAttack;
byte _val6, _val7;
int _partyIndex, _val9, _monsterShootingCtr;
int _activeMonsterNum;
int _destCharCtr;
int _destAC;
int _numberOfTimes;
int _attackerLevel;
int _handicapThreshold, _handicapParty;
int _handicapMonsters, _handicapDelta;
int _attackersCount;
int _totalExperience;
Common::String _monsterName;
bool _monstersResistSpells;
bool _monstersRegenerate;
AttributePair _attackAttr1, _attackAttr2;
int _timesHit;
bool _isShooting;
Common::String _attackMessage;
enum Handicap {
HANDICAP_EVEN = 0, HANDICAP_PARTY = 1,
HANDICAP_MONSTER = 2
};
Handicap _handicap = HANDICAP_EVEN;
enum Mode {
SELECT_OPTION, FIGHT_WHICH, DEFEATED_MONSTERS,
NEXT_ROUND, MONSTER_ADVANCES, MONSTERS_AFFECTED,
MONSTER_FLEES, MONSTER_WANDERS, MONSTER_SPELL,
CHAR_ATTACKS, MONSTER_ATTACK, INFILTRATION,
WAITS_FOR_OPENING, SPELL_RESULT, NO_EFFECT
};
Mode _mode = SELECT_OPTION;
int _destMonsterNum = 0;
int _monstersDestroyedCtr = 0;
bool _turnUndeadUsed = false;
bool _divineInterventionUsed = false;
size_t _spellMonsterCount = 0;
int _monsterAttackStyle = -1;
/**
* Constructor
*/
Combat();
/**
* Destructor
*/
virtual ~Combat();
/**
* Sets the combat display mode
*/
virtual void setMode(Mode newMode) = 0;
/**
* Does final cleanup when combat is done
*/
virtual void combatDone();
/**
* Subtracts the damage from the character, making
* them unconscious or die if needed
*/
Common::String subtractDamageFromChar() override;
/**
* Clear all the combat variables
*/
void clear();
void loadMonsters();
/**
* Sets the _monsterIndex to the index of
* _monsterP in the monster list
*/
void monsterIndexOf();
/**
* Sets _monsterP to point to a specified monster
*/
void monsterSetPtr(int monsterNum);
/**
* Sets up the flags for whether each character
* in the party can attack from their position.
*/
void setupCanAttacks();
/**
* Chooses the starting character to
*/
void setupAttackersCount();
/**
* Checks whether the third party member
* is blocked by a left wall
*/
void checkLeftWall();
/**
* Checks whether the fourth party member
* is blocked by a right wall
*/
void checkRightWall();
/**
* Sets up the handicap for the encounter
*/
void setupHandicap();
/*------- Inherited virtual methods ------*/
/**
* Get the monster index
*/
int getMonsterIndex() const override {
return _monsterIndex;
}
/**
* Returns true if a monster can cast certain spells
*/
bool canMonsterCast() const override;
/**
* Dispels any effects on the party
*/
void dispelParty() override;
void removeMonster() override;
/*------- combat execution ------*/
/**
* Main combat loop that selects the next party
* member or monster to take their turn
*/
void combatLoop(bool checkMonstersFirst = false);
/**
* Select treasure for a defeated monster
*/
void selectTreasure();
void selectTreasure2(int index);
/**
* Moves to the next round
*/
void nextRound();
void nextRound2();
void nextRound3();
/**
* Update the _highestLevel to the remaining
* active members of the party
*/
void updateHighestLevel();
/**
* Calculate a monster action
*/
void monsterAction();
/**
* Check if monster is mindless and wandering
*/
bool checkMonsterSpells();
/**
* Check other monster actions
*/
void checkMonsterActions();
void defeatedMonsters();
void setTreasure();
void clearArrays();
bool moveMonsters();
void monsterAdvances(uint index);
bool monsterChanges();
void proc2();
void checkParty();
/**
* Attack a monster
*/
void fightMonster(int monsterNum);
void shootMonster(int monsterNum);
void attackMonsterPhysical();
void attackMonsterShooting();
void attackMonster(int monsterNum);
/**
* Adds attack damage message for character hitting monster
*/
void addAttackDamage();
/**
* Updates a monster's status
*/
void updateMonsterStatus();
/**
* Handles a monster touch action, if any
*/
bool monsterTouch(Common::String &line);
/**
* Handles monster atttack logic
*/
void monsterAttackRandom();
void monsterAttackInner();
/**
* Handles monster shooting a character
*/
void monsterAttackShooting();
/**
* Selects monster to attack
*/
void selectMonsterTarget();
/**
* Attack option
*/
void attack();
/**
* Block option
*/
void block();
/**
* Cast option
*/
void cast();
/**
* Exchange current character with another
*/
void exchangeWith(int charNum);
/**
* Use option
*/
void use();
/**
* Retreat option
*/
void retreat();
/**
* Called to remove any dead monsters
*/
void removeDeadMonsters();
private:
void spellFailed();
void destroyMonster();
bool monsterLevelThreshold() const;
void iterateMonsters1Inner();
void iterateMonsters2Inner();
void characterDone();
void summonLightning2();
void fireball2();
void levelAdjust();
public:
/**
* Display a combat spell's result
*/
virtual void displaySpellResult(const InfoMessage &msg) = 0;
void iterateMonsters1();
void iterateMonsters2();
void resetDestMonster();
// Cleric spells that need access to internal fields
void turnUndead();
void summonLightning();
void paralyze();
bool divineIntervention();
void holyWord();
// Wizard spells that need access to internal fields
void identifyMonster();
void fireball();
void lightningBolt();
void makeRoom();
void slow();
void weaken();
bool web();
bool acidRain();
void fingerOfDeath();
void disintegration();
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,67 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/game/detect_magic.h"
#include "mm/mm1/globals.h"
namespace MM {
namespace MM1 {
namespace Game {
void DetectMagic::getMagicStrings() {
Inventory &inv = g_globals->_currCharacter->_backpack;
for (uint i = 0; i < inv.size(); ++i) {
int itemId = inv[i]._id;
bool flag = false;
if (itemId < 12)
flag = false;
else if (itemId < 61)
flag = true;
else if (itemId < 66)
flag = false;
else if (itemId < 86)
flag = true;
else if (itemId < 93)
flag = false;
else if (itemId < 121)
flag = true;
else if (itemId < 128)
flag = false;
else if (itemId < 156)
flag = true;
else if (itemId < 158)
flag = false;
else if (itemId < 255)
flag = true;
else
flag = false;
if (flag) {
_strings[i] = Common::String::format("Y (%d)", inv[i]._charges);
} else {
_strings[i] = "N";
}
}
}
} // namespace Game
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,46 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAME_DETECT_MAGIC_H
#define MM1_GAME_DETECT_MAGIC_H
#include "common/rect.h"
#include "mm/mm1/data/character.h"
namespace MM {
namespace MM1 {
namespace Game {
class DetectMagic {
protected:
Common::String _strings[INVENTORY_COUNT];
/**
* Gets the magic flags
*/
void getMagicStrings();
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,53 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/game/duplication.h"
#include "mm/mm1/globals.h"
namespace MM {
namespace MM1 {
namespace Game {
bool Duplication::duplicate(Character &c, Inventory &inv, int itemIndex) {
if (c._backpack.full())
// No space to duplicate
return false;
if (getRandomNumber(100) == 100) {
// OMG: The original seriously had this fringe
// case that happens so rarely
inv.removeAt(itemIndex); // Break item
return false;
} else if (inv[itemIndex]._id >= 230) {
// Item range that can't be duplicated
return false;
} else {
// Add a copy of the item
c._backpack.add(inv[itemIndex]._id, inv[itemIndex]._charges);
return true;
}
}
} // namespace Game
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,46 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAME_DUPLICATION_H
#define MM1_GAME_DUPLICATION_H
#include "mm/mm1/game/game_logic.h"
#include "mm/mm1/data/character.h"
#include "mm/mm1/data/items.h"
namespace MM {
namespace MM1 {
namespace Game {
class Duplication : public GameLogic {
protected:
/**
* Charge a given item
* @returns Returns true if the spell succeeded
*/
bool duplicate(Character &c, Inventory &inv, int itemIndex);
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,186 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/game/encounter.h"
#include "mm/mm1/data/monsters.h"
#include "mm/mm1/maps/maps.h"
#include "mm/mm1/events.h"
#include "mm/mm1/globals.h"
#include "mm/mm1/mm1.h"
namespace MM {
namespace MM1 {
namespace Game {
void Encounter::execute() {
if (!g_globals->_encountersOn)
return;
Maps::Map &map = *g_maps->_currentMap;
int comp, maxRand, maxVal;
const Monster *monsterP;
_bribeFleeCtr = _bribeAlignmentCtr = 0;
_alignmentsChanged = 0;
// In manual mode, the scripts have already set up
// a list of monsters to use
if (!_manual) {
_monsterSummaries.clear();
_levelIndex = 0;
}
_totalLevels = _highestLevel = 0;
_levelOffset = _monsterImgNum = _maxLevelForImg = _fleeThreshold = 0;
for (uint i = 0; i < g_globals->_party.size(); ++i) {
const Character &c = g_globals->_party[i];
_highestLevel = MAX(_highestLevel, (int)c._level._current);
if (!(c._condition & (BAD_CONDITION | PARALYZED | UNCONSCIOUS)))
_totalLevels = MIN(_totalLevels + c._level._current, 255);
}
_totalLevels /= 2;
_highestLevel /= 2;
bool firstLoop = !_manual;
_manual = false;
while (firstLoop || _levelIndex < _totalLevels) {
randomAdjust();
firstLoop = false;
maxRand = _levelOffset + _highestLevel;
if (maxRand >= 2) {
int highestRand = map[Maps::MAP_33];
maxRand = MIN(maxRand, highestRand);
comp = getRandomNumber(maxRand);
} else {
comp = 1;
}
maxVal = map[Maps::MAP_47];
if (comp < maxVal) {
comp = MIN(maxVal, 10);
}
assert(_monsterSummaries.size() < MAX_COMBAT_MONSTERS);
_monsterNum = getRandomNumber(16);
_monsterSummaries.push_back(MonsterSummary(_monsterNum, comp));
_monsterLevel = comp;
_levelIndex += comp;
if (_monsterSummaries.size() < MAX_COMBAT_MONSTERS) {
if (_monsterSummaries.size() >= map[Maps::MAP_MAX_MONSTERS])
goto exit_loop;
monsterP = getMonster();
maxVal = getRandomNumber(monsterP->_count);
for (int i = 0; i < maxVal; ++i) {
assert(!_monsterSummaries.empty());
_monsterSummaries.push_back(_monsterSummaries.back());
_levelIndex += _monsterSummaries.back()._level;
if (_monsterSummaries.size() >= MAX_COMBAT_MONSTERS)
goto exit_loop;
if (_monsterSummaries.size() >= map[Maps::MAP_MAX_MONSTERS])
goto exit_loop;
}
} else {
goto exit_loop;
}
}
exit_loop:
_monsterList.clear();
for (uint i = 0; i < _monsterSummaries.size(); ++i) {
maxVal = (_monsterSummaries[i]._level - 1) * 16 +
_monsterSummaries[i]._num;
if (_monsterSummaries[i]._level < 1 || _monsterSummaries[i]._level > 12
|| maxVal >= 196) {
_monsterSummaries[i]._level = 10;
_monsterSummaries[i]._num = getRandomNumber(15);
}
// Add monster details to list
_monsterLevel = _monsterSummaries[i]._level;
const Monster &srcMons = (*g_globals->_monsters)[_monsterSummaries[i]._num];
_monsterList.push_back(srcMons);
Monster &mons = _monsterList.back();
mons._level = _monsterSummaries[i]._level;
if (_monsterLevel > _maxLevelForImg) {
_maxLevelForImg = _monsterLevel;
_fleeThreshold = mons._fleeThreshold;
_monsterImgNum = mons._imgNum;
}
}
g_events->addView("Encounter");
}
void Encounter::randomAdjust() {
int rval = getRandomNumber(100);
_levelOffset = 0;
if (rval < 51) {
} else if (rval < 71)
_levelOffset += 1;
else if (rval < 86)
_levelOffset += 2;
else if (rval < 96)
_levelOffset += 3;
else
_levelOffset += 4;
}
const Monster *Encounter::getMonster() {
assert(_monsterNum > 0 && _monsterLevel > 0);
return &(*g_globals->_monsters)[_monsterNum + ((_monsterLevel - 1) * 16)];
}
bool Encounter::checkSurroundParty() const {
return getRandomNumber(100) > _fleeThreshold;
}
void Encounter::changeCharAlignment(Alignment align) {
if (g_globals->_currCharacter->_alignment != align) {
g_globals->_currCharacter->_alignment = align;
++_alignmentsChanged;
}
}
void Encounter::clearMonsters() {
_monsterSummaries.clear();
}
void Encounter::addMonster(byte id, byte level) {
_monsterSummaries.push_back(MonsterSummary(id, level));
}
} // namespace Game
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,96 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAME_ENCOUNTER_H
#define MM1_GAME_ENCOUNTER_H
#include "common/array.h"
#include "mm/mm1/data/character.h"
#include "mm/mm1/data/monsters.h"
#include "mm/mm1/game/game_logic.h"
namespace MM {
namespace MM1 {
namespace Game {
#define MAX_COMBAT_MONSTERS 15
enum EncounterType {
FORCE_SURPRISED = -1, NORMAL_SURPRISED = 0, NORMAL_ENCOUNTER = 1
};
class Encounter : public GameLogic {
private:
int _levelOffset = 0;
int _maxLevelForImg = 0;
int _monsterLevel = 0;
int _totalLevels = 0;
int _monsterNum = 0;
struct MonsterSummary {
byte _num;
byte _level;
MonsterSummary() : _num(0), _level(0) {}
MonsterSummary(byte num, byte level) : _num(num), _level(level) {}
};
Common::Array<MonsterSummary> _monsterSummaries;
void randomAdjust();
const Monster *getMonster();
public:
Common::Array<Monster> _monsterList;
int _bribeAlignmentCtr = 0, _bribeFleeCtr = 0;
int _alignmentsChanged = 0;
int _monsterImgNum = 0;
int _highestLevel = 0;
EncounterType _encounterType = NORMAL_SURPRISED;
byte _fleeThreshold = 0;
bool _manual = false;
int _levelIndex = 0;
public:
/**
* Start an encounter
*/
void execute();
/**
* Chooses whether an encounter can be fled
*/
bool checkSurroundParty() const;
void changeCharAlignment(Alignment align);
/**
* Clears the monster list
*/
void clearMonsters();
/**
* Adds a monster to the monster list
*/
void addMonster(byte id, byte level);
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,259 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/game/equip_remove.h"
#include "mm/mm1/globals.h"
namespace MM {
namespace MM1 {
namespace Game {
bool EquipRemove::equipItem(int index, Common::Point &textPos, Common::String &equipError) {
Character &c = *g_globals->_currCharacter;
uint itemId = c._backpack[index]._id;
uint charges = c._backpack[index]._charges;
int classBit = 0;
textPos.x = 0;
switch (c._class) {
case KNIGHT:
classBit = KNIGHT_BIT;
break;
case PALADIN:
classBit = PALADIN_BIT;
break;
case ARCHER:
classBit = ARCHER_BIT;
break;
case CLERIC:
classBit = CLERIC_BIT;
break;
case SORCERER:
classBit = SORCERER_BIT;
break;
case ROBBER:
classBit = ROBBER_BIT;
break;
default:
equipError = STRING["dialogs.character.wrong_class"];
break;
}
g_globals->_items.getItem(itemId);
const Item &item = g_globals->_currItem;
if (equipError.empty() && (item._disablements & classBit))
equipError = STRING["dialogs.character.wrong_class"];
if (equipError.empty()) {
int alignBit = 0;
switch (c._alignment) {
case GOOD:
alignBit = GOOD_BIT;
break;
case NEUTRAL:
alignBit = NEUTRAL_BIT;
break;
case EVIL:
alignBit = EVIL_BIT;
break;
default:
equipError = STRING["dialogs.character.wrong_alignment"];
break;
}
if ((item._disablements & alignBit) && alignBit != NEUTRAL_BIT)
equipError = STRING["dialogs.character.wrong_alignment"];
}
if (equipError.empty()) {
if (item._constBonus_id == IS_EQUIPPABLE) {
equipError = STRING["dialogs.character.not_equipped"];
textPos.x = 10;
}
}
if (equipError.empty()) {
if (isWeapon(itemId)) {
if (c._equipped.hasWeapon() || c._equipped.hasTwoHanded())
equipError = STRING["dialogs.character.have_weapon"];
} else if (isMissile(itemId)) {
if (c._equipped.hasMissile()) {
equipError = STRING["dialogs.character.have_missile"];
textPos.x = 3;
}
} else if (isTwoHanded(itemId)) {
if (c._equipped.hasShield()) {
equipError = STRING["dialogs.character.cannot_with_shield"];
textPos.x = 7;
} else if (c._equipped.hasWeapon()) {
equipError = STRING["dialogs.character.have_weapon"];
}
} else if (isArmor(itemId)) {
if (c._equipped.hasArmor()) {
equipError = STRING["dialogs.character.have_armor"];
textPos.x = 5;
}
} else if (isShield(itemId)) {
if (c._equipped.hasTwoHanded()) {
equipError = STRING["dialogs.character.cannot_two_handed"];
textPos.x = 1;
}
} else if (itemId == 255) {
equipError = STRING["dialogs.character.not_equipped"];
textPos.x = 10;
}
}
if (equipError.empty() && c._equipped.full()) {
equipError = STRING["dialogs.character.full"];
textPos.x = 14;
}
if (equipError.empty()) {
// All checks passed, can equip item
c._backpack.removeAt(index);
uint freeIndex = c._equipped.add(itemId, charges);
if (item._constBonus_id != NO_EQUIP_BONUS) {
if (item._constBonus_id == IS_EQUIPPABLE) {
equipError = STRING["dialogs.character.not_equipped"];
textPos.x = 10;
} else if (item._constBonus_id == EQUIP_CURSED) {
c._equipped[freeIndex]._charges += item._constBonus_value;
}
}
}
if (!equipError.empty())
return false;
//add const equip bonus to character parameters
applyEquipBonus(item._constBonus_id, item._constBonus_value);
switch (getItemCategory(itemId)) {
case ITEMCAT_WEAPON:
case ITEMCAT_TWO_HANDED:
c._physicalAttr._base = item._damage;
c._physicalAttr._current = item._AC_Dmg;
break;
case ITEMCAT_MISSILE:
c._missileAttr._base = item._damage;
c._missileAttr._current = item._AC_Dmg;
break;
case ITEMCAT_ARMOR:
case ITEMCAT_SHIELD:
c._ac._base += item._AC_Dmg;
break;
default:
break;
}
c.updateResistances();
c.updateAttributes();
c.updateAC();
return true;
}
bool EquipRemove::removeItem(int index, Common::Point &textPos, Common::String &removeError) {
Character &c = *g_globals->_currCharacter;
uint itemId = c._equipped[index]._id;
uint charges = c._equipped[index]._charges;
g_globals->_items.getItem(itemId);
const Item &item = g_globals->_currItem;
if (item._constBonus_id == EQUIP_CURSED) {
removeError = STRING["dialogs.character.cursed"];
textPos.x = 13;
} else if (c._backpack.full()) {
removeError = STRING["dialogs.character.full"];
textPos.x = 14;
}
if (!removeError.empty())
return false;
// Shift item to backpack
c._equipped.removeAt(index);
c._backpack.add(itemId, charges);
if (item._constBonus_value) {
// TODO: _equipMode is used as a character offset. Need to
// find an example that calls it so I know what area of
// the character updates are being done to
//error("TODO: item flag in remove item");
//subtract const equip bonus from character parameters
applyEquipBonus(item._constBonus_id, -item._constBonus_value);
}
switch (getItemCategory(itemId)) {
case ITEMCAT_WEAPON:
case ITEMCAT_TWO_HANDED:
c._physicalAttr.clear();
break;
case ITEMCAT_MISSILE:
c._missileAttr.clear();
break;
case ITEMCAT_ARMOR:
case ITEMCAT_SHIELD:
c._ac._base = MAX((int)c._ac._base - (int)item._AC_Dmg, 0);
break;
default:
break;
}
return true;
}
void EquipRemove::applyEquipBonus(int id, int value){
if ((id<2)||(id>=0xff)) return;
Character &c = *g_globals->_currCharacter;
// TODO: check strange cases (decimal id numbers): 16, 19
switch (id) {
//case 16: c.sex = NONE; break; //UNOBTAINIUM
// case 19: c._luck._race = NONE; break; //JADE AMULET
case 21: c._intelligence._base += value; break;
case 23: c._might._base += value; break;
case 25: c._personality._base += value; break;
case 29: c._speed._base += value; break;
case 31: c._accuracy._base += value; break;
case 33: c._luck._base += value; break;
case 37: c._age += value; break;
case 60: c._ac._base += value; break;
case 88: c._resistances._s._magic._base += value; break;
case 90: c._resistances._s._fire._base += value; break;
case 92: c._resistances._s._cold._base += value; break;
case 94: c._resistances._s._electricity._base += value; break;
case 96: c._resistances._s._acid._base += value; break;
case 98: c._resistances._s._fear._base += value; break;
case 100: c._resistances._s._poison._base += value; break;
case 102: c._resistances._s._psychic._base += value; break; //resistance to sleep
case 108: c._trapCtr += value; break;
}
}
} // namespace Game
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,53 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAME_EQUIP_REMOVE_H
#define MM1_GAME_EQUIP_REMOVE_H
#include "common/rect.h"
namespace MM {
namespace MM1 {
namespace Game {
struct EquipRemove {
/**
* Equip an item
*/
bool equipItem(int index, Common::Point &textPos, Common::String &equipError);
/**
* Remove an item
*/
bool removeItem(int index, Common::Point &textPos, Common::String &removeError);
/**
* apply an equip bonus on a current character
*/
void applyEquipBonus(int id, int value);
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,70 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/game/fly.h"
#include "mm/mm1/globals.h"
namespace MM {
namespace MM1 {
namespace Game {
static const byte FLY_MAP_ID1[20] = {
1, 0, 4, 5, 0x12,
2, 3, 0x11, 5, 6,
2, 1, 4, 6, 0x1A,
3, 3, 4, 1, 0x1B
};
static const byte FLY_MAP_ID2[20] = {
0xF, 0xA, 3, 5, 1,
5, 7, 0xA, 0xB, 7,
0xB, 1, 9, 1, 0xB,
1, 0xD, 0xF, 8, 1
};
static const byte FLY_MAP_X[20] = {
15, 8, 11, 0, 9,
15, 3, 10, 4, 11,
15, 3, 3, 7, 12,
14, 11, 5, 7, 15
};
static const byte FLY_MAP_Y[20] = {
7, 10, 0, 8, 11,
7, 2, 10, 0, 0,
15, 3, 9, 0, 6,
14, 15, 15, 7, 15
};
void Fly::fly(int mapIndex) {
if (mapIndex != -1) {
Maps::Maps &maps = *g_maps;
int id = FLY_MAP_ID1[mapIndex] | ((int)FLY_MAP_ID2[mapIndex] << 8);
maps._mapPos.x = FLY_MAP_X[mapIndex];
maps._mapPos.y = FLY_MAP_Y[mapIndex];
maps.changeMap(id, 2);
}
}
} // namespace Game
} // namespace MM1
} // namespace MM

43
engines/mm/mm1/game/fly.h Normal file
View File

@@ -0,0 +1,43 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAME_FLY_H
#define MM1_GAME_FLY_H
#include "common/rect.h"
namespace MM {
namespace MM1 {
namespace Game {
class Fly {
protected:
/**
* Handles teleporting to the given map
*/
void fly(int mapIndex);
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,39 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/game/game_logic.h"
#include "mm/mm1/mm1.h"
namespace MM {
namespace MM1 {
namespace Game {
int GameLogic::getRandomNumber(int minNumber, int maxNumber) {
return g_engine->getRandomNumber(maxNumber - minNumber + 1) + minNumber;
}
int GameLogic::getRandomNumber(int maxNumber) {
return g_engine->getRandomNumber(maxNumber);
}
} // namespace Game
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,42 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAME_GAME_LOGIC_H
#define MM1_GAME_GAME_LOGIC_H
namespace MM {
namespace MM1 {
namespace Game {
class GameLogic {
public:
/**
* Returns a random number
*/
static int getRandomNumber(int minNumber, int maxNumber);
static int getRandomNumber(int maxNumber);
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,67 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/game/leprechaun.h"
#include "mm/mm1/maps/map00.h"
#include "mm/mm1/globals.h"
#define TOWN_NUM 0x2fe
#define LEPRECHAUN_MAP_ID1 0x2ff
#define LEPRECHAUN_MAP_ID2 0x304
#define LEPRECHAUN_MAP_X 0x309
#define LEPRECHAUN_MAP_Y 0x30E
namespace MM {
namespace MM1 {
namespace Game {
void Leprechaun::teleportToTown(char townNum) {
Maps::Maps &maps = *g_maps;
Maps::Map00 &map = *static_cast<Maps::Map00 *>(g_maps->_currentMap);
map[TOWN_NUM] = townNum;
// Scan the party for someone with any gems
for (uint i = 0; i < g_globals->_party.size(); ++i) {
Character &c = g_globals->_party[i];
if (c._gems) {
c._gems--;
int townIndex = map[TOWN_NUM] - Common::KEYCODE_1;
maps._mapPos.x = map[LEPRECHAUN_MAP_X + townIndex];
maps._mapPos.y = map[LEPRECHAUN_MAP_Y + townIndex];
maps.changeMap(
map[LEPRECHAUN_MAP_ID1 + townIndex] |
(map[LEPRECHAUN_MAP_ID2 + townIndex] << 8),
1);
g_events->redraw();
return;
}
}
maps._mapPos = Common::Point(8, 5);
map.updateGame();
}
} // namespace Game
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,38 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAME_LEPRECHAUN_H
#define MM1_GAME_LEPRECHAUN_H
namespace MM {
namespace MM1 {
namespace Game {
class Leprechaun {
public:
void teleportToTown(char townNum);
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,388 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/game/monster_touch.h"
#include "mm/mm1/maps/maps.h"
#include "mm/mm1/globals.h"
#include "mm/mm1/sound.h"
namespace MM {
namespace MM1 {
namespace Game {
const MonsterTouchAction MonsterTouch::ACTIONS[25] = {
&MonsterTouch::action00,
&MonsterTouch::action01,
&MonsterTouch::action02,
&MonsterTouch::action03,
&MonsterTouch::action04,
&MonsterTouch::action05,
&MonsterTouch::action06,
&MonsterTouch::action07,
&MonsterTouch::action08,
&MonsterTouch::action09,
&MonsterTouch::action10,
&MonsterTouch::action11,
&MonsterTouch::action12,
&MonsterTouch::action13,
&MonsterTouch::action14,
&MonsterTouch::action15,
&MonsterTouch::action16,
&MonsterTouch::action17,
&MonsterTouch::action18,
&MonsterTouch::action19,
&MonsterTouch::action20,
&MonsterTouch::action21,
&MonsterTouch::action22,
&MonsterTouch::action23,
&MonsterTouch::action24
};
#define LINE(ACTION) line = Common::String::format(" %s%s", \
STRING["monster_actions.and"].c_str(), STRING[ACTION].c_str())
bool MonsterTouch::monsterTouch(uint index, Common::String &line) {
line = "";
return (this->*ACTIONS[index])(line);
}
bool MonsterTouch::canPerform(int level) const {
return getRandomNumber(level) == level;
}
bool MonsterTouch::action00(Common::String &line) {
if (canPerform(3)) {
g_globals->_currCharacter->_food = 0;
LINE("monster_actions.takes_food");
return true;
} else {
return false;
}
}
bool MonsterTouch::action01(Common::String &line) {
if (canPerform(20)) {
setCondition(DISEASED);
LINE("monster_actions.inflicts_disease");
return true;
}
return true;
}
bool MonsterTouch::action02(Common::String &line) {
if (canPerform(20)) {
return action07(line);
} else {
return false;
}
}
bool MonsterTouch::action03(Common::String &line) {
if (canPerform(20)) {
return action11(line);
} else {
return false;
}
}
bool MonsterTouch::action04(Common::String &line) {
Character &c = *g_globals->_currCharacter;
if (canPerform(2) && c._gems != 0) {
c._gems /= 2;
LINE("monster_actions.steals_gems");
return true;
} else {
return false;
}
}
bool MonsterTouch::action05(Common::String &line) {
Character &c = *g_globals->_currCharacter;
if (c._endurance._current == 0 || --c._endurance._current == 0)
setCondition(BAD_CONDITION | DEAD);
_damage += 3;
LINE("monster_actions.reduces_endurance");
return true;
}
bool MonsterTouch::action06(Common::String &line) {
if (damageType7()) {
setCondition(ASLEEP);
LINE("monster_actions.induces_sleep");
return true;
}
return false;
}
bool MonsterTouch::action07(Common::String &line) {
if (damageType5()) {
setCondition(3);
LINE("monster_actions.cures_paralysis");
return true;
}
return false;
}
bool MonsterTouch::action08(Common::String &line) {
if (canPerform(4)) {
setCondition(DISEASED);
LINE("monster_actions.inflicts_disease");
return true;
} else {
return false;
}
}
bool MonsterTouch::action09(Common::String &line) {
Character &c = *g_globals->_currCharacter;
if (canPerform(2) && c._gold != 0) {
c._gold /= 2;
LINE("monster_actions.steals_gold");
return true;
} else {
return false;
}
return true;
}
bool MonsterTouch::action10(Common::String &line) {
Character &c = *g_globals->_currCharacter;
if (canPerform(2) && !c._backpack.empty()) {
c._backpack.removeAt(c._backpack.size() - 1);
LINE("monster_actions.steals_something");
return true;
} else {
return false;
}
}
bool MonsterTouch::action11(Common::String &line) {
if (damageType6()) {
setCondition(16);
LINE("monster_actions.induces_poison");
return true;
}
return false;
}
bool MonsterTouch::action12(Common::String &line) {
if (canPerform(3)) {
setCondition(BLINDED);
LINE("monster_actions.causes_blindness");
return true;
} else {
return false;
}
}
bool MonsterTouch::action13(Common::String &line) {
Character &c = *g_globals->_currCharacter;
if (c._level._current == 0 || --c._level._current == 0)
setCondition(BAD_CONDITION | DEAD);
_damage += 10;
LINE("monster_actions.drains_lifeforce");
return true;
}
bool MonsterTouch::action14(Common::String &line) {
Character &c = *g_globals->_currCharacter;
if (canPerform(3) && isCharAffected()) {
setCondition(BAD_CONDITION | STONE);
line = Common::String::format("%s %s",
c._name,
STRING["monster_actions.turned_to_stone"].c_str()
);
return true;
} else {
return false;
}
}
bool MonsterTouch::action15(Common::String &line) {
Character &c = *g_globals->_currCharacter;
if (isCharAffected()) {
int age = c._age + 10;
c._age = age;
if (age > 255) {
c._age = 200;
setCondition(ERADICATED);
}
LINE("monster_actions.causes_aging");
return true;
} else {
return false;
}
}
bool MonsterTouch::action16(Common::String &line) {
Character &c = *g_globals->_currCharacter;
if (c._level < 3) {
c._level._current = 0;
setCondition(BAD_CONDITION | DEAD);
} else {
c._level._current -= 2;
}
_damage += 20;
LINE("monster_actions.drains_lifeforce");
return true;
}
bool MonsterTouch::action17(Common::String &line) {
Character &c = *g_globals->_currCharacter;
if (canPerform(3) && isCharAffected()) {
setCondition(BAD_CONDITION | DEAD);
line = Common::String::format("%s %s",
c._name,
STRING["monster_actions.is_killed"].c_str()
);
return true;
} else {
return false;
}
}
bool MonsterTouch::action18(Common::String &line) {
if (canPerform(3) && isCharAffected()) {
setCondition(UNCONSCIOUS);
LINE("monster_actions.induces_unconsciousness");
return true;
} else {
return false;
}
}
bool MonsterTouch::action19(Common::String &line) {
Character &c = *g_globals->_currCharacter;
if (c._might._current < 4) {
c._might._current = 0;
setCondition(BAD_CONDITION | DEAD);
} else {
c._might._current -= 3;
}
LINE("monster_actions.drains_might");
return true;
}
bool MonsterTouch::action20(Common::String &line) {
Character &c = *g_globals->_currCharacter;
for (int i = 0; i < 7; ++i) {
AttributePair &attr = c.getAttribute(i);
if (attr._current < 3) {
attr._current = 0;
setCondition(BAD_CONDITION | DEAD);
} else {
attr._current -= 2;
}
}
LINE("monster_actions.drains_abilities");
return true;
}
bool MonsterTouch::action21(Common::String &line) {
Character &c = *g_globals->_currCharacter;
if (canPerform(2)) {
c._backpack.clear();
LINE("monster_actions.steals_backpack");
return true;
} else {
return false;
}
}
bool MonsterTouch::action22(Common::String &line) {
Character &c = *g_globals->_currCharacter;
if (canPerform(2)) {
c._gold = 0;
c._gems = 0;
LINE("monster_actions.steals_gold_and_gems");
return true;
} else {
return false;
}
}
bool MonsterTouch::action23(Common::String &line) {
Character &c = *g_globals->_currCharacter;
setCondition(ERADICATED);
line = Common::String::format("%s %s",
c._name,
STRING["monster_actions.is_eradicated"].c_str()
);
return true;
}
bool MonsterTouch::action24(Common::String &line) {
Character &c = *g_globals->_currCharacter;
c._sp._current = 0;
LINE("monster_actions.drains_sp");
return true;
}
} // namespace Game
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,76 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAME_MONSTER_TOUCH_H
#define MM1_GAME_MONSTER_TOUCH_H
#include "mm/mm1/game/spells_monsters.h"
namespace MM {
namespace MM1 {
namespace Game {
class MonsterTouch;
typedef bool (MonsterTouch::*MonsterTouchAction)(Common::String &line);
class MonsterTouch : public SpellsMonsters {
private:
static const MonsterTouchAction ACTIONS[25];
bool canPerform(int level) const;
private:
bool action00(Common::String &line);
bool action01(Common::String &line);
bool action02(Common::String &line);
bool action03(Common::String &line);
bool action04(Common::String &line);
bool action05(Common::String &line);
bool action06(Common::String &line);
bool action07(Common::String &line);
bool action08(Common::String &line);
bool action09(Common::String &line);
bool action10(Common::String &line);
bool action11(Common::String &line);
bool action12(Common::String &line);
bool action13(Common::String &line);
bool action14(Common::String &line);
bool action15(Common::String &line);
bool action16(Common::String &line);
bool action17(Common::String &line);
bool action18(Common::String &line);
bool action19(Common::String &line);
bool action20(Common::String &line);
bool action21(Common::String &line);
bool action22(Common::String &line);
bool action23(Common::String &line);
bool action24(Common::String &line);
public:
virtual ~MonsterTouch() {}
bool monsterTouch(uint index, Common::String &line);
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,48 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/game/recharge_item.h"
#include "mm/mm1/data/character.h"
#include "mm/mm1/globals.h"
namespace MM {
namespace MM1 {
namespace Game {
bool RechargeItem::charge(Inventory &inv, int itemIndex) {
const Item &item = *g_globals->_items.getItem(inv[itemIndex]._id);
if (getRandomNumber(100) == 100) {
// OMG: The original seriously had this fringe
// case that happens so rarely
inv.removeAt(itemIndex); // Break item
return false;
} else {
inv[itemIndex]._charges = MIN(inv[itemIndex]._charges + getRandomNumber(4),
(int)item._maxCharges);
return true;
}
}
} // namespace Game
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,46 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAME_RECHARGE_ITEM_H
#define MM1_GAME_RECHARGE_ITEM_H
#include "mm/mm1/game/game_logic.h"
#include "mm/mm1/data/character.h"
#include "mm/mm1/data/items.h"
namespace MM {
namespace MM1 {
namespace Game {
class RechargeItem : public GameLogic {
protected:
/**
* Charge a given item
* @returns Returns true if the spell succeeded
*/
bool charge(Inventory &inv, int itemIndex);
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,72 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/events.h"
#include "mm/mm1/mm1.h"
#include "mm/mm1/game/rest.h"
#include "mm/mm1/game/encounter.h"
#include "mm/mm1/maps/maps.h"
namespace MM {
namespace MM1 {
namespace Game {
void Rest::check() {
int dangerRate = g_maps->_currentMap->dataByte(44);
if (!dangerRate ||
g_engine->getRandomNumber(dangerRate) != dangerRate) {
// Rest allowed
execute();
} else {
// Choose a random character, and make everyone
// but them be asleep
uint awakeIndex = g_engine->getRandomNumber(
g_globals->_party.size() - 1);
for (uint i = 0; i < g_globals->_party.size(); ++i) {
Character &c = g_globals->_party[i];
if (!(c._condition & BAD_CONDITION) && i != awakeIndex)
c._condition |= ASLEEP;
}
// Start an encounter
g_globals->_encounters.execute();
}
}
void Rest::execute() {
// Reset active spells
Common::fill(&g_globals->_activeSpells._arr[0],
&g_globals->_activeSpells._arr[ACTIVE_SPELLS_COUNT], 0);
// Rest the characters of the party
for (uint i = 0; i < g_globals->_party.size(); ++i) {
Character &c = g_globals->_party[i];
c.rest();
}
g_events->send(InfoMessage(
STRING["dialogs.game.rest.rest_complete"]
));
}
} // namespace Game
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,46 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAME_REST_H
#define MM1_GAME_REST_H
namespace MM {
namespace MM1 {
namespace Game {
struct Rest {
/**
* Checks the current map to see whether the
* party is able to rest, or an encounter is triggered
*/
static void check();
/**
* Handles the rest
*/
static void execute();
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,192 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/game/spell_casting.h"
#include "mm/mm1/maps/maps.h"
#include "mm/mm1/views/combat.h"
#include "mm/mm1/globals.h"
namespace MM {
namespace MM1 {
namespace Game {
static const int8 SPELLS_SP_GEMS[47 * 2] = {
0, 0, 0, 0, 0, -1, 0, 0,
0, 1, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 2, 0, 2, 2,
0, 0, 0, 3, 3,
4, 4, 4, 4, 4,
10, 5, 5, 5, 5,
0, 0, -1, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0,
-1, 0, 1, -1, 0, 0, 1, 0,
0, 0, 2, 0, 0, 2, 2, 2,
0, 0, 3, 3, 3,
4, 4, 4, 4, 4,
5, 100, 5, 5, 5
};
static const byte SPELL_FLAGS[47 * 2] = {
1, 1, 9, 4, 2, 4, 0, 1,
4, 5, 9, 0, 0, 0, 9, 9,
2, 4, 4, 2, 9, 9, 2, 2,
6, 6, 0, 0, 6, 25, 5, 2,
17, 0, 1, 4, 4,
17, 4, 6, 4, 2,
1, 1, 0, 6, 25,
1, 2, 9, 9, 0, 2, 2, 9,
9, 9, 9, 2, 2, 5, 5, 9,
9, 18, 1, 9, 1, 1, 1, 9,
9, 9, 9, 9, 2, 0, 1, 1,
17, 0, 9, 2, 2,
1, 9, 2, 0, 2,
2, 2, 17, 1, 1
};
enum SpellFlag {
SF_COMBAT_ONLY = 1,
SF_NONCOMBAT_ONLY = 2,
SF_CAST_ON = 4,
SF_OUTDOORS_ONLY = 0x10
};
int SpellCasting::getSpellIndex(const Character *chr, int lvl, int num) {
int lvlNum;
int setNum = chr->_class == ARCHER || chr->_class == SORCERER ? 1 : 0;
int spellNum = num - 1;
for (lvlNum = 2; lvlNum <= MIN(lvl, 5); ++lvlNum)
spellNum += 8;
for (lvlNum = 5; lvlNum < lvl; ++lvlNum)
spellNum += 5;
int spellIndex = setNum * 47 + spellNum;
return spellIndex;
}
void SpellCasting::getSpellLevelNum(int spellIndex, int &lvl, int &num) {
int idx = spellIndex % 47;
for (lvl = 1; lvl < 8; ++lvl) {
int numSpells = (lvl >= 5) ? 5 : 8;
if (idx < numSpells) {
num = idx + 1;
return;
}
idx -= numSpells;
}
num = -1;
}
void SpellCasting::setSpell(const Character *chr, int lvl, int num) {
_spellState = SS_OK;
// Figure the offset in the spell list
int spellIndex = getSpellIndex(chr, lvl, num);
// The required SP matches the spell level
int requiredSp = lvl;
if (SPELLS_SP_GEMS[spellIndex] < 0)
// required SP increases with character's level
requiredSp = chr->_level;
if (SPELLS_SP_GEMS[spellIndex] < 0 && chr->_sp._current < chr->_level._current)
_spellState = SS_NOT_ENOUGH_SP;
else if (requiredSp > chr->_sp._current)
_spellState = SS_NOT_ENOUGH_SP;
int requiredGems = ABS(SPELLS_SP_GEMS[spellIndex]);
if (_spellState == SS_OK)
setSpell(spellIndex, requiredSp, requiredGems);
// Supporting debugger command that enables all spells
if (g_globals->_allSpells && _spellState != SS_COMBAT_ONLY &&
_spellState != SS_NONCOMBAT_ONLY && _spellState != SS_OUTDOORS_ONLY) {
_requiredSp = _requiredGems = 0;
_spellState = SS_OK;
}
}
void SpellCasting::setSpell(int spellIndex, int requiredSp, int requiredGems) {
_spellIndex = spellIndex;
_requiredSp = requiredSp;
_requiredGems = requiredGems;
_spellState = SS_OK;
Maps::Map &map = *g_maps->_currentMap;
if (!isInCombat() && SPELL_FLAGS[spellIndex] & SF_COMBAT_ONLY)
_spellState = SS_COMBAT_ONLY;
else if (isInCombat() && SPELL_FLAGS[spellIndex] & SF_NONCOMBAT_ONLY)
_spellState = SS_NONCOMBAT_ONLY;
else if ((SPELL_FLAGS[spellIndex] & SF_OUTDOORS_ONLY) &&
!(map[Maps::MAP_ID] & 0x80))
_spellState = SS_OUTDOORS_ONLY;
}
bool SpellCasting::hasCharTarget() const {
return (SPELL_FLAGS[_spellIndex] & SF_CAST_ON) != 0;
}
bool SpellCasting::isMagicAllowed() const {
return !(g_maps->_currentState & Maps::SFLAG_SPELLS_DISALLOWED);
}
Common::String SpellCasting::getSpellError() const {
Common::String msg;
switch (_spellState) {
case SS_NOT_ENOUGH_SP:
msg = STRING["dialogs.misc.not_enough_sp"];
break;
case SS_NOT_ENOUGH_GEMS:
msg = STRING["dialogs.misc.not_enough_gems"];
break;
case SS_COMBAT_ONLY:
msg = STRING["spells.combat_only"];
break;
case SS_NONCOMBAT_ONLY:
msg = STRING["spells.noncombat_only"];
break;
case SS_OUTDOORS_ONLY:
msg = STRING["spells.outdoors_only"];
break;
default:
msg = STRING["spells.done"];
break;
}
if (!isInCombat())
msg = Common::String::format("*** %s ***", msg.c_str());
return msg;
}
bool SpellCasting::isInCombat() const {
return g_events->isPresent("Combat");
}
} // namespace Game
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,107 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAME_SPELL_CASTING_H
#define MM1_GAME_SPELL_CASTING_H
#include "mm/mm1/data/character.h"
namespace MM {
namespace MM1 {
namespace Game {
enum SpellState {
SS_OK, SS_NOT_ENOUGH_SP, SS_NOT_ENOUGH_GEMS,
SS_COMBAT_ONLY, SS_NONCOMBAT_ONLY, SS_DOESNT_WORK,
SS_OUTDOORS_ONLY
};
/**
* Support class for handling spell casting logic
*/
class SpellCasting {
protected:
int _spellIndex = 0;
int _requiredSp = 0, _requiredGems = 0;
SpellState _spellState = SS_OK;
/**
* Returns true if combat is in progress
*/
bool isInCombat() const;
public:
/**
* Sets the current spell
*/
void setSpell(const Character *chr, int lvl, int num);
/**
* Get the index in the spell array for a given spell
*/
static int getSpellIndex(const Character *chr, int lvl, int num);
/**
* Get the spell level and number from spell index
*/
static void getSpellLevelNum(int spellIndex, int &lvl, int &num);
/**
* Sets a spell directly by index
*/
void setSpell(int spellIndex, int requiredSp, int requiredGems);
/**
* Returns true if the spell requires a target
*/
bool hasCharTarget() const;
/**
* Returns true if a spell can be cast, with the exception
* of magic not being allowed at the current location.
*/
bool canCast() const {
return _spellState == SS_OK;
}
/**
* Returns if magic is allowed at the current location
*/
bool isMagicAllowed() const;
/**
* Returns the spell error
*/
SpellState getSpellState() const {
return _spellState;
}
/**
* Returns the error message
*/
Common::String getSpellError() const;
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,699 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/game/spells_monsters.h"
#include "mm/mm1/maps/maps.h"
#include "mm/mm1/globals.h"
#include "mm/mm1/sound.h"
namespace MM {
namespace MM1 {
namespace Game {
const SpellMonstersSpell SpellsMonsters::SPELLS[MONSTER_SPELLS_COUNT] = {
&SpellsMonsters::spell01_curse,
&SpellsMonsters::spell02_energyBlast,
&SpellsMonsters::spell03_fire,
&SpellsMonsters::spell04_blindness,
&SpellsMonsters::spell05_sprayPoison,
&SpellsMonsters::spell06_sprayAcid,
&SpellsMonsters::spell07_sleep,
&SpellsMonsters::spell08_paralyze,
&SpellsMonsters::spell09_dispel,
&SpellsMonsters::spell10_lightningBolt,
&SpellsMonsters::spell11_strangeGas,
&SpellsMonsters::spell12_explode,
&SpellsMonsters::spell13_fireball,
&SpellsMonsters::spell14_fireBreath,
&SpellsMonsters::spell15_gazes,
&SpellsMonsters::spell16_acidArrow,
&SpellsMonsters::spell17_elements,
&SpellsMonsters::spell18_coldBeam,
&SpellsMonsters::spell19_dancingSword,
&SpellsMonsters::spell20_magicDrain,
&SpellsMonsters::spell21_fingerOfDeath,
&SpellsMonsters::spell22_sunRay,
&SpellsMonsters::spell23_disintegration,
&SpellsMonsters::spell24_commandsEnergy,
&SpellsMonsters::spell25_poison,
&SpellsMonsters::spell26_lightning,
&SpellsMonsters::spell27_frost,
&SpellsMonsters::spell28_spikes,
&SpellsMonsters::spell29_acid,
&SpellsMonsters::spell30_fire,
&SpellsMonsters::spell31_energy,
&SpellsMonsters::spell32_swarm
};
SpellsMonsters::SpellsMonsters() {
}
void SpellsMonsters::castMonsterSpell(const Common::String &monsterName, int spellNum) {
g_globals->_spellsState._mmVal1 = 0;
g_globals->_spellsState._resistanceIndex = 0;
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_MAGIC;
g_globals->_spellsState._damage = 0;
// All spell messages start with the monster who casts it
_lines.clear();
_lines.push_back(Line(monsterName));
(this->*SPELLS[spellNum - 1])();
}
void SpellsMonsters::spell01_curse() {
if (casts()) {
add(STRING["monster_spells.a_curse"]);
g_globals->_activeSpells._s.cursed = MIN(255,
(int)g_globals->_activeSpells._s.cursed + 1);
}
}
void SpellsMonsters::spell02_energyBlast() {
if (casts()) {
add(STRING["monster_spells.energy_blast"]);
++g_globals->_spellsState._mmVal1;
g_globals->_spellsState._damage = getRandomNumber(16) + 4;
damageRandomChar();
}
}
void SpellsMonsters::spell03_fire() {
add(Common::String::format("%s %s",
STRING["monster_spells.breathes"].c_str(),
STRING["monster_spells.fire"].c_str()));
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_FIRE;
int count = _remainingMonsters[getMonsterIndex()]->_level;
g_globals->_spellsState._damage += count * 6;
damageRandomChar();
}
void SpellsMonsters::spell04_blindness() {
if (casts()) {
add(STRING["monster_spells.blindness"]);
++g_globals->_spellsState._mmVal1;
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._damage = 2;
handlePartyEffects();
}
}
void SpellsMonsters::spell05_sprayPoison() {
add(STRING["monster_spells.sprays_poison"]);
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_POISON;
g_globals->_spellsState._damage = POISONED;
handlePartyEffects();
}
void SpellsMonsters::spell06_sprayAcid() {
add(STRING["monster_spells.sprays_acid"]);
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_ACID;
g_globals->_spellsState._damage = getRandomNumber((int)POISONED);
add(':');
handlePartyDamage();
}
void SpellsMonsters::spell07_sleep() {
if (casts()) {
add(STRING["monster_spells.sleep"]);
++g_globals->_spellsState._mmVal1;
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_PSYCHIC;
g_globals->_spellsState._damage = ASLEEP;
handlePartyEffects();
}
}
void SpellsMonsters::spell08_paralyze() {
if (casts()) {
add(STRING["monster_spells.paralyze"]);
++g_globals->_spellsState._mmVal1;
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_FEAR;
g_globals->_spellsState._damage = PARALYZED;
if (_remainingMonsters[getMonsterIndex()]->_level >= 5) {
handlePartyEffects();
} else {
chooseCharacter();
writeConditionEffect();
}
}
}
void SpellsMonsters::spell09_dispel() {
if (casts()) {
add(STRING["monster_spells.dispel"]);
dispelParty();
}
}
void SpellsMonsters::spell10_lightningBolt() {
if (casts()) {
add(STRING["monster_spells.lightning_bolt"]);
++g_globals->_spellsState._mmVal1;
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_ELECTRICITY;
g_globals->_spellsState._damage = getRandomNumber(37) + 5;
damageRandomChar();
}
}
void SpellsMonsters::spell11_strangeGas() {
add(Common::String::format("%s %s",
STRING["monster_spells.breathes"].c_str(),
STRING["monster_spells.strange_gas"].c_str()));
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_POISON;
g_globals->_spellsState._damage = BAD_CONDITION | STONE;
handlePartyEffects();
}
void SpellsMonsters::spell12_explode() {
add(STRING["monster_spells.explode"]);
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_POISON;
g_globals->_spellsState._damage = getRandomNumber(
_remainingMonsters[getMonsterIndex()]->_level);
_remainingMonsters[getMonsterIndex()]->_level = 0;
_remainingMonsters[getMonsterIndex()]->_status = MONFLAG_DEAD;
removeMonster();
add(':');
handlePartyDamage();
}
void SpellsMonsters::spell13_fireball() {
if (casts()) {
add(STRING["monster_spells.fireball"]);
++g_globals->_spellsState._mmVal1;
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_FIRE;
// This whole condition choice makes no sense
g_globals->_spellsState._damage += 6 *
_remainingMonsters[getMonsterIndex()]->_level;
g_globals->_spellsState._damage = getRandomNumber(g_globals->_spellsState._damage) + 4;
add(':');
handlePartyDamage();
}
}
void SpellsMonsters::spell14_fireBreath() {
add(Common::String::format("%s %s",
STRING["monster_spells.breathes"].c_str(),
STRING["monster_spells.fire"].c_str()));
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_FIRE;
// This whole condition choice makes no sense
g_globals->_spellsState._damage += 8 *
_remainingMonsters[getMonsterIndex()]->_level;
g_globals->_spellsState._damage = getRandomNumber(g_globals->_spellsState._damage);
add(':');
handlePartyDamage();
}
void SpellsMonsters::spell15_gazes() {
add(STRING["monster_spells.gazes"]);
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._damage = BAD_CONDITION | STONE;
chooseCharacter();
writeConditionEffect();
}
void SpellsMonsters::spell16_acidArrow() {
add(STRING["monster_spells.acid_arrow"]);
++g_globals->_spellsState._mmVal1;
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_ACID;
g_globals->_spellsState._damage = getRandomNumber(31) + 9;
damageRandomChar();
}
void SpellsMonsters::spell17_elements() {
add(STRING["monster_spells.call_elements"]);
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._damage = getRandomNumber(21) + 39;
damageRandomChar();
}
void SpellsMonsters::spell18_coldBeam() {
if (casts()) {
add(STRING["monster_spells.cold_beam"]);
++g_globals->_spellsState._mmVal1;
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_COLD;
g_globals->_spellsState._damage = getRandomNumber(41) + 9;
damageRandomChar();
}
}
void SpellsMonsters::spell19_dancingSword() {
if (casts()) {
add(STRING["monster_spells.dancing_sword"]);
++g_globals->_spellsState._mmVal1;
g_globals->_spellsState._damage = getRandomNumber(14) + 16;
add(':');
handlePartyDamage();
}
}
void SpellsMonsters::spell20_magicDrain() {
add(STRING["monster_spells.magic_drain"]);
for (uint i = 0; i < g_globals->_party.size(); ++i) {
Character &c = g_globals->_party[i];
g_globals->_currCharacter = &c;
c._sp._current = 0;
}
dispelParty();
}
void SpellsMonsters::spell21_fingerOfDeath() {
if (casts()) {
add(STRING["monster_spells.finger_of_death"]);
++g_globals->_spellsState._mmVal1;
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._damage = BAD_CONDITION | DEAD;
chooseCharacter();
writeConditionEffect();
}
}
void SpellsMonsters::spell22_sunRay() {
if (casts()) {
add(STRING["monster_spells.sun_ray"]);
++g_globals->_spellsState._mmVal1;
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._damage = getRandomNumber(51) + 49;
damageRandomChar();
}
}
void SpellsMonsters::spell23_disintegration() {
if (casts()) {
add(STRING["monster_spells.disintegration"]);
++g_globals->_spellsState._mmVal1;
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._damage = ERADICATED;
chooseCharacter();
writeConditionEffect();
}
}
void SpellsMonsters::spell24_commandsEnergy() {
add(STRING["monster_spells.commands_energy"]);
g_globals->_spellsState._damage = SILENCED | PARALYZED | UNCONSCIOUS;
damageRandomChar();
}
void SpellsMonsters::spell25_poison() {
add(Common::String::format("%s %s",
STRING["monster_spells.breathes"].c_str(),
STRING["monster_spells.poison"].c_str()));
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_POISON;
g_globals->_spellsState._damage = _remainingMonsters[getMonsterIndex()]->_hp;
++g_globals->_spellsState._resistanceIndex;
add(':');
handlePartyDamage();
}
void SpellsMonsters::spell26_lightning() {
add(Common::String::format("%s %s",
STRING["monster_spells.breathes"].c_str(),
STRING["monster_spells.lightning"].c_str()));
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_ELECTRICITY;
g_globals->_spellsState._damage = _remainingMonsters[getMonsterIndex()]->_hp;
++g_globals->_spellsState._resistanceIndex;
add(':');
handlePartyDamage();
}
void SpellsMonsters::spell27_frost() {
add(Common::String::format("%s %s",
STRING["monster_spells.breathes"].c_str(),
STRING["monster_spells.frost"].c_str()));
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_COLD;
g_globals->_spellsState._damage = _remainingMonsters[getMonsterIndex()]->_hp;
++g_globals->_spellsState._resistanceIndex;
add(':');
handlePartyDamage();
}
void SpellsMonsters::spell28_spikes() {
add(Common::String::format("%s %s",
STRING["monster_spells.breathes"].c_str(),
STRING["monster_spells.spikes"].c_str()));
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_ELECTRICITY;
g_globals->_spellsState._damage = _remainingMonsters[getMonsterIndex()]->_hp;
++g_globals->_spellsState._resistanceIndex;
add(':');
handlePartyDamage();
}
void SpellsMonsters::spell29_acid() {
add(Common::String::format("%s %s",
STRING["monster_spells.breathes"].c_str(),
STRING["monster_spells.acid"].c_str()));
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_ACID;
g_globals->_spellsState._damage = _remainingMonsters[getMonsterIndex()]->_hp;
++g_globals->_spellsState._resistanceIndex;
add(':');
handlePartyDamage();
}
void SpellsMonsters::spell30_fire() {
add(Common::String::format("%s %s",
STRING["monster_spells.breathes"].c_str(),
STRING["monster_spells.fire"].c_str()));
g_globals->_spellsState._resistanceTypeOrTargetCount = RESISTANCE_FIRE;
g_globals->_spellsState._damage = _remainingMonsters[getMonsterIndex()]->_hp;
++g_globals->_spellsState._resistanceIndex;
add(':');
handlePartyDamage();
}
void SpellsMonsters::spell31_energy() {
add(STRING["monster_spells.energy"]);
g_globals->_spellsState._damage = _remainingMonsters[getMonsterIndex()]->_hp;
++g_globals->_spellsState._resistanceIndex;
add(':');
handlePartyDamage();
}
void SpellsMonsters::spell32_swarm() {
add(STRING["monster_spells.swarm"]);
++g_globals->_spellsState._resistanceIndex;
g_globals->_spellsState._damage = getRandomNumber(12);
add(':');
handlePartyDamage();
}
bool SpellsMonsters::casts() {
if (canMonsterCast()) {
add(STRING["monster_spells.casts"]);
return true;
} else {
add(STRING["monster_spells.fails_to_cast"]);
return false;
}
}
void SpellsMonsters::addCharName() {
add(g_globals->_currCharacter->_name);
}
void SpellsMonsters::damageRandomChar() {
chooseCharacter();
handleDamage();
}
void SpellsMonsters::chooseCharacter() {
add(':');
// Choose a random character
g_globals->_currCharacter = &g_globals->_party[
getRandomNumber(g_globals->_party.size()) - 1
];
if (g_globals->_currCharacter->_condition & (BAD_CONDITION | UNCONSCIOUS)) {
// Can't use character, so sequently scan party
// to find a character that can be used
for (uint i = 0; i < g_globals->_party.size(); ++i) {
g_globals->_currCharacter = &g_globals->_party[i];
if (g_globals->_currCharacter->_condition & (BAD_CONDITION | UNCONSCIOUS))
break;
}
}
}
bool SpellsMonsters::isCharAffected() const {
int val = g_globals->_currCharacter->_resistances._s._magic._current +
g_globals->_activeSpells._s.magic;
return randomThreshold(val);
}
void SpellsMonsters::handleDamage() {
g_globals->_spellsState._mmVal5 = 1;
_damage = g_globals->_spellsState._damage;
if (charAffected()) {
if (isEffective()) {
if (testElementalResistance()) {
if (g_globals->_activeSpells._s.power_shield)
_damage = 1;
writeDamage();
Common::String str = subtractDamageFromChar();
if (!str.empty())
_lines.push_back(Line(0, _lines.back().y + 1, str));
}
}
}
}
bool SpellsMonsters::charAffected() {
_lines.push_back(Line(0, 2, ""));
addCharName();
add(' ');
if (g_globals->_spellsState._mmVal1 && !isCharAffected()) {
_lines.back()._text += STRING["monster_spells.not_affected"];
return false;
}
return true;
}
bool SpellsMonsters::isEffective() {
if (g_globals->_spellsState._resistanceIndex) {
proc9();
if (g_globals->_spellsState._mmVal7) {
if (g_globals->_spellsState._mmVal5) {
_damage >>= 1;
} else {
_lines.back()._text += STRING["monster_spells.not_affected"];
return false;
}
}
}
return true;
}
bool SpellsMonsters::testElementalResistance() {
bool result = false;
switch (g_globals->_spellsState._resistanceTypeOrTargetCount) {
case RESISTANCE_FIRE:
result = damageType1(); break;
case RESISTANCE_COLD:
result = damageType2(); break;
case RESISTANCE_ELECTRICITY:
result = damageType3(); break;
case RESISTANCE_ACID:
result = damageType4(); break;
case RESISTANCE_FEAR:
result = damageType5(); break;
case RESISTANCE_POISON:
result = damageType6(); break;
case RESISTANCE_PSYCHIC:
result = damageType7(); break;
default:
break;
}
if (!result) {
if (g_globals->_spellsState._mmVal5) {
_damage >>= 2;
} else {
add(STRING["monster_spells.not_affected"]);
return false;
}
}
return true;
}
void SpellsMonsters::writeDamage() {
add(STRING["monster_spells.takes"]);
add(Common::String::format("%d ", _damage));
add(STRING[_damage > 1 ? "monster_spells.points" : "monster_spells.point"]);
add(' ');
if (_lines.back()._text.size() >= 30)
add('!');
else
add(STRING["monster_spells.of_damage"]);
}
void SpellsMonsters::proc9() {
const Character &c = *g_globals->_currCharacter;
int val = c._level._current * 4 + c._luck._current;
if (c._class == PALADIN)
val += 20;
int randVal = getRandomNumber(100);
g_globals->_spellsState._mmVal7 = randVal < 99 && randVal <= val ? 1 : 0;
}
bool SpellsMonsters::damageType1() {
int threshold = g_globals->_currCharacter->_resistances._s._fire +
g_globals->_activeSpells._s.fire;
return randomThreshold(threshold);
}
bool SpellsMonsters::damageType2() {
int threshold = g_globals->_currCharacter->_resistances._s._cold +
g_globals->_activeSpells._s.cold;
return randomThreshold(threshold);
}
bool SpellsMonsters::damageType3() {
int threshold = g_globals->_currCharacter->_resistances._s._electricity +
g_globals->_activeSpells._s.electricity;
return randomThreshold(threshold);
}
bool SpellsMonsters::damageType4() {
int threshold = g_globals->_currCharacter->_resistances._s._acid +
g_globals->_activeSpells._s.acid;
return randomThreshold(threshold);
}
bool SpellsMonsters::damageType5() {
if (g_globals->_activeSpells._s.psychic_protection) {
return false;
} else {
int threshold = g_globals->_currCharacter->_resistances._s._fear +
g_globals->_activeSpells._s.fear;
return randomThreshold(threshold);
}
}
bool SpellsMonsters::damageType6() {
int threshold = g_globals->_currCharacter->_resistances._s._poison +
g_globals->_activeSpells._s.poison;
return randomThreshold(threshold);
}
bool SpellsMonsters::damageType7() {
if (g_globals->_activeSpells._s.psychic_protection) {
return false;
} else {
int threshold = g_globals->_currCharacter->_resistances._s._psychic;
return randomThreshold(threshold);
}
}
void SpellsMonsters::writeConditionEffect() {
g_globals->_spellsState._mmVal5 = 0;
int effectNum;
if (!charAffected() || !isEffective() || !testElementalResistance())
return;
if (g_globals->_spellsState._damage == 0) {
effectNum = 10;
} else if (g_globals->_spellsState._damage & BAD_CONDITION) {
if (!(g_globals->_spellsState._damage & (BAD_CONDITION | DEAD)))
effectNum = 7;
else if (!(g_globals->_spellsState._damage & (BAD_CONDITION | STONE)))
effectNum = 8;
else if (g_globals->_spellsState._damage == ERADICATED)
effectNum = 9;
else
effectNum = 10;
} else {
effectNum = 0;
for (byte bitset = g_globals->_spellsState._damage; bitset & 1;
++effectNum, bitset >>= 1) {
}
}
add(STRING[Common::String::format("spells.char_effects.%d",
effectNum)]);
add('!');
}
void SpellsMonsters::setCondition(byte newCondition) {
Character &c = *g_globals->_currCharacter;
if (!(c._condition & BAD_CONDITION)) {
c._condition |= newCondition;
} else if (newCondition & BAD_CONDITION) {
c._condition = newCondition;
}
}
void SpellsMonsters::handlePartyEffects() {
add(':');
for (uint i = 0; i < g_globals->_party.size(); ++i) {
g_globals->_currCharacter = &g_globals->_party[i];
writeConditionEffect();
}
}
void SpellsMonsters::handlePartyDamage() {
Character *tmp = g_globals->_currCharacter;
for (uint i = 0; i < g_globals->_party.size(); ++i) {
g_globals->_currCharacter = &g_globals->_party[i];
handleDamage();
}
g_globals->_currCharacter = tmp;
}
} // namespace Game
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,213 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAME_SPELLS_MONSTERS
#define MM1_GAME_SPELLS_MONSTERS
#include "mm/mm1/game/game_logic.h"
#include "mm/mm1/data/character.h"
#include "mm/mm1/data/monsters.h"
#include "mm/mm1/messages.h"
#include "common/str.h"
namespace MM {
namespace MM1 {
namespace Game {
#define MAX_COMBAT_MONSTERS 15
#define MONSTER_SPELLS_COUNT 32
class SpellsMonsters;
typedef void (SpellsMonsters::*SpellMonstersSpell)();
class SpellsMonsters : public GameLogic {
private:
static const SpellMonstersSpell SPELLS[MONSTER_SPELLS_COUNT];
void spell01_curse();
void spell02_energyBlast();
void spell03_fire();
void spell04_blindness();
void spell05_sprayPoison();
void spell06_sprayAcid();
void spell07_sleep();
void spell08_paralyze();
void spell09_dispel();
void spell10_lightningBolt();
void spell11_strangeGas();
void spell12_explode();
void spell13_fireball();
void spell14_fireBreath();
void spell15_gazes();
void spell16_acidArrow();
void spell17_elements();
void spell18_coldBeam();
void spell19_dancingSword();
void spell20_magicDrain();
void spell21_fingerOfDeath();
void spell22_sunRay();
void spell23_disintegration();
void spell24_commandsEnergy();
void spell25_poison();
void spell26_lightning();
void spell27_frost();
void spell28_spikes();
void spell29_acid();
void spell30_fire();
void spell31_energy();
void spell32_swarm();
/**
* Called to determine whether the spell can be cast
*/
bool casts();
/**
* Selects a random character and applies the damage to them
*/
void damageRandomChar();
/**
* Randomly chooses a character in the party
*/
void chooseCharacter();
/**
* Checks whether the character is affected by the spell,
* and if so writes out the damage and whether the
* character is knocked unconscious or dies
*/
void handleDamage();
/**
* Checks if character is affected by spell,
* and adds to the message if not
*/
bool charAffected();
/**
* Checks random range
*/
bool randomThreshold(int threshold) const {
int v = getRandomNumber(120);
return v < 3 || v >= threshold;
}
bool isEffective();
/**
* Writes out how much damage the character suffers
*/
void writeDamage();
bool testElementalResistance();
/**
* Adds different spell effects to the lines
*/
void writeConditionEffect();
/**
* Adds text for condition effects on the party
*/
void handlePartyEffects();
protected:
Common::Array<Monster *> _remainingMonsters;
LineArray _lines;
int _damage = 0, _displayedDamage = 0;
virtual bool canMonsterCast() const = 0;
virtual int getMonsterIndex() const = 0;
virtual void dispelParty() = 0;
virtual void removeMonster() = 0;
/**
* Subtracts the damage from the character, making
* them unconscious or die if needed
*/
virtual Common::String subtractDamageFromChar() = 0;
/**
* Adds text for damage effects on the party
*/
void handlePartyDamage();
/**
* Sets the condition to apply
*/
void setCondition(byte newCondition);
/**
* Returns true if character is affected so spell
*/
bool isCharAffected() const;
/**
* Test whether character resists different damage types
*/
bool damageType1();
bool damageType2();
bool damageType3();
bool damageType4();
bool damageType5();
bool damageType6();
bool damageType7();
void proc9();
/**
* Adds text to the current line
*/
void add(const Common::String &msg) {
_lines.back()._text += msg;
}
void add(char c) {
_lines.back()._text += c;
}
/**
* Adds current character's name
*/
void addCharName();
public:
SpellsMonsters();
virtual ~SpellsMonsters() {}
/**
* Monster casts a spell
*/
void castMonsterSpell(const Common::String &monsterName, int spellNum);
/**
* Gets a spell message
*/
const LineArray &getMonsterSpellMessage() const {
return _lines;
}
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,188 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAME_SPELLS_H
#define MM1_GAME_SPELLS_H
#include "mm/mm1/data/character.h"
#include "mm/mm1/game/game_logic.h"
#include "mm/mm1/messages.h"
namespace MM {
namespace MM1 {
namespace Game {
#define CATEGORY_SPELLS_COUNT 47
#define SPELLS_COUNT (CATEGORY_SPELLS_COUNT * 2)
enum SpellResult {
SR_SUCCESS_DONE, SR_SUCCESS_SILENT, SR_FAILED
};
extern byte FLY_MAP_ID1[20];
extern byte FLY_MAP_ID2[20];
extern byte FLY_MAP_X[20];
extern byte FLY_MAP_Y[20];
class SpellsParty : public GameLogic {
typedef SpellResult(*SpellFn)();
private:
static Character *_destChar;
private:
/**
* Returns true if in combat
*/
static bool isInCombat();
/**
* Restores an amount of Hp
*/
static void restoreHp(Character &c, uint16 hp);
static void restoreHp(uint16 hp);
/**
* Increases light duration
*/
static void addLight(int amount);
/**
* Display a message
*/
static void display(const InfoMessage &msg);
private:
static SpellResult cleric11_awaken();
static SpellResult cleric12_bless();
static SpellResult cleric13_blind();
static SpellResult cleric14_firstAid();
static SpellResult cleric15_light();
static SpellResult cleric16_powerCure();
static SpellResult cleric17_protectionFromFear();
static SpellResult cleric18_turnUndead();
static SpellResult cleric21_cureWounds();
static SpellResult cleric22_heroism();
static SpellResult cleric23_pain();
static SpellResult cleric24_protectionFromCold();
static SpellResult cleric25_protectionFromIce();
static SpellResult cleric26_protectionFromPoison();
static SpellResult cleric27_silence();
static SpellResult cleric28_suggestion();
static SpellResult cleric31_createFood();
static SpellResult cleric32_cureBlindness();
static SpellResult cleric33_cureParalysis();
static SpellResult cleric34_lastingLight();
static SpellResult cleric35_produceFlame();
static SpellResult cleric36_produceFrost();
static SpellResult cleric37_removeQuest();
static SpellResult cleric38_walkOnWater();
static SpellResult cleric41_cureDisease();
static SpellResult cleric42_neutralizePoison();
static SpellResult cleric43_protectionFromAcid();
static SpellResult cleric44_protectionFromElectricity();
static SpellResult cleric45_restoreAlignment();
static SpellResult cleric46_summonLightning();
static SpellResult cleric47_superHeroism();
static SpellResult cleric48_surface();
static SpellResult cleric51_deadlySwarm();
static SpellResult cleric52_dispelMagic();
static SpellResult cleric53_paralyze();
static SpellResult cleric54_removeCondition();
static SpellResult cleric55_restoreEnergy();
static SpellResult cleric61_moonRay();
static SpellResult cleric62_raiseDead();
static SpellResult cleric63_rejuvinate();
static SpellResult cleric64_stoneToFlesh();
static SpellResult cleric65_townPortal();
static SpellResult cleric71_divineIntervention();
static SpellResult cleric72_holyWord();
static SpellResult cleric73_protectionFromElements();
static SpellResult cleric74_resurrection();
static SpellResult cleric75_sunRay();
static SpellResult wizard11_awaken() {
return cleric11_awaken();
}
static SpellResult wizard12_detectMagic();
static SpellResult wizard13_energyBlast();
static SpellResult wizard14_flameArrow();
static SpellResult wizard15_leatherSkin();
static SpellResult wizard16_light() {
return cleric15_light();
}
static SpellResult wizard17_location();
static SpellResult wizard18_sleep();
static SpellResult wizard21_electricArrow();
static SpellResult wizard22_hypnotize() {
return cleric28_suggestion();
}
static SpellResult wizard23_identifyMonster();
static SpellResult wizard24_jump();
static SpellResult wizard25_levitate();
static SpellResult wizard26_power();
static SpellResult wizard27_quickness();
static SpellResult wizard28_scare();
static SpellResult wizard31_fireball();
static SpellResult wizard32_fly();
static SpellResult wizard33_invisibility();
static SpellResult wizard34_lightningBolt();
static SpellResult wizard35_makeRoom();
static SpellResult wizard36_slow();
static SpellResult wizard37_weaken();
static SpellResult wizard38_web();
static SpellResult wizard41_acidArrow();
static SpellResult wizard42_coldBeam();
static SpellResult wizard43_feebleMind();
static SpellResult wizard44_freeze();
static SpellResult wizard45_guardDog();
static SpellResult wizard46_psychicProtection();
static SpellResult wizard47_shield();
static SpellResult wizard48_timeDistortion();
static SpellResult wizard51_acidRain();
static SpellResult wizard52_dispelMagic() {
return cleric52_dispelMagic();
}
static SpellResult wizard53_fingerOfDeath();
static SpellResult wizard54_shelter();
static SpellResult wizard55_teleport();
static SpellResult wizard61_dancingSword();
static SpellResult wizard62_disintegration();
static SpellResult wizard63_etherialize();
static SpellResult wizard64_protectionFromMagic();
static SpellResult wizard65_rechargeItem();
static SpellResult wizard71_astralSpell();
static SpellResult wizard72_duplication();
static SpellResult wizard73_meteorShower();
static SpellResult wizard74_powerShield();
static SpellResult wizard75_prismaticLight();
static SpellFn SPELLS[SPELLS_COUNT];
public:
/**
* Casts a spell
*/
static SpellResult cast(uint spell, Character *destChar);
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,146 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/game/use_item.h"
#include "mm/mm1/game/equip_remove.h"
#include "mm/mm1/game/combat.h"
#include "mm/mm1/game/spells_party.h"
#include "mm/mm1/globals.h"
namespace MM {
namespace MM1 {
namespace Game {
Common::String UseItem::combatUseItem(Inventory &inv, Inventory::Entry &invEntry,
bool isEquipped) {
Common::String msg;
Item *item = g_globals->_items.getItem(invEntry._id);
if (!item->_tempBonus_id) {
msg = STRING["dialogs.character.use_combat.no_special_power"];
} else if (item->_constBonus_id == IS_EQUIPPABLE || isEquipped) {
if (invEntry._charges) {
g_globals->_combatEffectCtr++;
inv.removeCharge(&invEntry);
if (item->_tempBonus_id == 0xff) {
setSpell(item->_spellId, 0, 0);
Game::SpellsParty::cast(_spellIndex, g_globals->_currCharacter);
} else {
// TODO: find out area of Character _effectId is used as an offset for
//error("TODO: _effectId used as a character offset to increase attribute?");
//add temporary equip bonus to character parameters
applyItemBonus(item->_tempBonus_id, item->_tempBonus_value);
if (g_globals->_combatEffectCtr)
(isEquipped ? &g_globals->_currCharacter->_equipped :
&g_globals->_currCharacter->_backpack)->removeCharge(&invEntry);
g_globals->_party.updateAC();
msg = STRING["dialogs.character.use_combat.done"];
return "";
}
} else {
msg = STRING["dialogs.character.use_combat.no_charges_left"];
}
} else {
msg = STRING["dialogs.character.use_combat.not_equipped"];
}
g_events->send("Combat", GameMessage("DISABLE_ATTACKS"));
return msg;
}
Common::String UseItem::nonCombatUseItem(Inventory &inv, Inventory::Entry &invEntry,
bool isEquipped) {
Common::String msg;
Item *item = g_globals->_items.getItem(invEntry._id);
if (!item->_tempBonus_id) {
msg = STRING["dialogs.character.use_noncombat.no_special_power"];
} else if (item->_constBonus_id == IS_EQUIPPABLE || isEquipped) {
if (invEntry._charges) {
g_globals->_nonCombatEffectCtr++;
inv.removeCharge(&invEntry);
if (item->_tempBonus_id== 0xff) {
setSpell(item->_spellId, 0, 0);
SpellResult result = Game::SpellsParty::cast(_spellIndex, g_globals->_currCharacter);
switch (result) {
case SR_SUCCESS_DONE:
msg = STRING["spells.done"];
break;
case SR_FAILED:
msg = STRING["spells.failed"];
break;
case SR_SUCCESS_SILENT:
break;
}
} else {
// Add temorary equip bonus to character parameters
applyItemBonus (item->_tempBonus_id, item->_tempBonus_value);
if (g_globals->_nonCombatEffectCtr)
(isEquipped ? &g_globals->_currCharacter->_equipped :
&g_globals->_currCharacter->_backpack)->removeCharge(&invEntry);
g_globals->_party.updateAC();
msg = STRING["spells.done"];
}
} else {
msg = STRING["dialogs.character.use_noncombat.no_charges_left"];
}
} else {
msg = STRING["dialogs.character.use_noncombat.not_equipped"];
}
return msg;
}
void UseItem::applyItemBonus(int id, int value){
if ((id<2)||(id>=0xff)) return;
Character &c = *g_globals->_currCharacter;
switch (id) {
case 24: c._might._current += value; break;
case 30: c._speed._current += value; break;
case 32: c._accuracy._current += value; break;
case 34: c._luck._current += value; break;
case 36: c._level._current += value; break;
case 37: c._age += value; break;
case 43: c._sp._current += value; break;
case 48: c._spellLevel._current += value; break;
case 49: c._gems += value; break;
case 58: c._gold += 255*value; break;
case 62: c._food += value; break;
case 89: c._resistances._s._magic._current += value; break;
case 99: c._resistances._s._fear._current += value; break;
}
}
} // namespace Game
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,54 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAME_USE_ITEM_H
#define MM1_GAME_USE_ITEM_H
#include "mm/mm1/data/character.h"
#include "mm/mm1/game/spell_casting.h"
namespace MM {
namespace MM1 {
namespace Game {
class UseItem : public MM1::Game::SpellCasting {
public:
/**
* Using an item during combat
*/
Common::String combatUseItem(Inventory &inv, Inventory::Entry &invEntry, bool isEquipped);
/**
* Using an item outside of combat
*/
Common::String nonCombatUseItem(Inventory &inv, Inventory::Entry &invEntry, bool isEquipped);
/**
* Apply item to a character parameter
*/
void applyItemBonus(int id, int value);
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,250 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/util.h"
#include "mm/mm1/game/view_base.h"
#include "mm/mm1/globals.h"
#include "mm/mm1/mm1.h"
#include "mm/mm1/mm1.h"
#include "mm/mm1/sound.h"
namespace MM {
namespace MM1 {
namespace Game {
ViewBase::ViewBase(UIElement *owner) : Views::TextView("View", owner) {
}
void ViewBase::update() {
Maps::Maps &maps = g_globals->_maps;
Maps::Map &map = *maps._currentMap;
maps._mapOffset = maps._mapPos.y * MAP_W + maps._mapPos.x;
maps._currentWalls = map._walls[maps._mapOffset];
maps._currentState = map._states[maps._mapOffset];
_isDark = false;
if (maps._currentState & Maps::CELL_DARK) {
if (g_globals->_activeSpells._s.light) {
g_globals->_activeSpells._s.light--;
} else {
goto darkness;
}
}
if ((map[Maps::MAP_FLAGS] & 1) && !g_globals->_activeSpells._s.light) {
darkness:
_isDark = true;
}
// Refresh the view immediately, so things like the minimap will
// update before special actions run
g_events->redraw();
g_events->drawElements();
// Encounter checks
g_globals->_encounters._encounterType = NORMAL_SURPRISED;
if (maps._currentState & Maps::CELL_SPECIAL) {
map.visitedSpecial();
draw();
map.special();
return;
} else if (_stepRandom) {
g_globals->_encounters._encounterType = NORMAL_ENCOUNTER;
_stepRandom = false;
g_globals->_encounters.execute();
} else {
g_globals->_party.checkPartyDead();
}
}
bool ViewBase::msgAction(const ActionMessage &msg) {
switch (msg._action) {
case KEYBIND_FORWARDS:
case KEYBIND_STRAFE_LEFT:
case KEYBIND_STRAFE_RIGHT:
forward(msg._action);
break;
case KEYBIND_BACKWARDS:
backwards();
break;
case KEYBIND_TURN_LEFT:
turnLeft();
break;
case KEYBIND_TURN_RIGHT:
turnRight();
break;
default:
return TextView::msgAction(msg);
}
return true;
}
bool ViewBase::msgFocus(const FocusMessage &msg) {
return false;
}
bool ViewBase::msgGame(const GameMessage &msg) {
if (msg._name == "DISPLAY") {
replaceView("Game");
return true;
} else if (msg._name == "UPDATE") {
replaceView("Game");
update();
return true;
} else if (msg._name == "LOCATION") {
_descriptionLine = STRING[Common::String::format(
"dialogs.location.titles.%d", msg._value)];
draw();
return true;
}
return TextView::msgGame(msg);
}
bool ViewBase::msgHeader(const HeaderMessage &msg) {
_descriptionLine = msg._name;
draw();
return true;
}
void ViewBase::turnLeft() {
g_globals->_maps.turnLeft();
update();
}
void ViewBase::turnRight() {
g_globals->_maps.turnRight();
update();
}
void ViewBase::forward(KeybindingAction action) {
Maps::Maps &maps = g_globals->_maps;
Maps::Map &map = *g_globals->_maps._currentMap;
// Figure out direction mask
byte mask = maps._forwardMask;
if (action == KEYBIND_STRAFE_LEFT) {
mask = maps._leftMask;
} else if (action == KEYBIND_STRAFE_RIGHT) {
mask = maps._rightMask;
}
// Get the delta X/Y from the direction
Common::Point delta = g_maps->getMoveDelta(mask);
// Check for obstructions
if (!g_globals->_intangible) {
if (maps._currentWalls & mask) {
if (maps._currentState & 0x55 & mask) {
obstructed(mask);
redraw();
return;
}
int offset;
if (!(maps._currentWalls & mask & 0x55))
offset = 1;
else if (maps._currentWalls & mask & 0xaa)
offset = 2;
else
offset = 0;
if (map.dataByte(Maps::MAP_30 + offset) == 4 &&
!g_globals->_activeSpells._s.walk_on_water) {
Sound::sound(SOUND_1);
_dialogMessage = STRING["movement.obstructed.cant_swim"];
redraw();
return;
}
} else {
if (maps._currentState & 0x55 & mask) {
barrier();
redraw();
return;
}
}
}
g_globals->_treasure.clear();
int maxVal = map[Maps::MAP_29];
if (g_engine->getRandomNumber(maxVal) == maxVal)
_stepRandom = true;
g_globals->_maps.step(delta);
update();
}
void ViewBase::backwards() {
Maps::Maps &maps = g_globals->_maps;
Maps::Map &map = *g_globals->_maps._currentMap;
Common::Point delta = g_maps->getMoveDelta(maps._backwardsMask);
if (!g_globals->_intangible) {
if (maps._currentWalls & maps._backwardsMask) {
Sound::sound(SOUND_1);
g_globals->_party.checkPartyDead();
return;
}
if (maps._currentState & 0x55 & maps._backwardsMask) {
Sound::sound(SOUND_1);
g_globals->_party.checkPartyDead();
return;
}
}
g_globals->_treasure.clear();
int maxVal = map[Maps::MAP_29];
if (g_engine->getRandomNumber(maxVal) == maxVal)
_stepRandom = true;
g_globals->_maps.step(delta);
update();
}
void ViewBase::obstructed(byte mask) {
Maps::Maps &maps = g_globals->_maps;
Maps::Map &map = *maps._currentMap;
Sound::sound(SOUND_1);
int index = 32;
if (!(maps._currentWalls & mask & 0x55))
index = 31;
else if (!(maps._currentWalls & mask & 0xaa))
index = 30;
_dialogMessage = STRING[Common::String::format(
"movement.obstructed.%d", map.dataByte(index))];
}
void ViewBase::barrier() {
_dialogMessage = STRING["movement.obstructed.barrier"];
Sound::sound(SOUND_1);
}
} // namespace Game
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,92 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAME_VIEW_BASE_H
#define MM1_GAME_VIEW_BASE_H
#include "mm/mm1/views/text_view.h"
namespace MM {
namespace MM1 {
namespace Game {
/**
* This acts as the base class for the 3d view
* of the surroundings. It contains the all
* the gameplay logic except for actual drawing.
*/
class ViewBase : public Views::TextView {
protected:
bool _isDark = false;
Common::String _dialogMessage;
bool _stepRandom = false;
Common::String _descriptionLine;
private:
/**
* Turn left
*/
void turnLeft();
/**
* Turn right
*/
void turnRight();
/**
* Move forwards
*/
void forward(KeybindingAction action);
/**
* Move backwards
*/
void backwards();
/**
* Forward movement is obstructed
*/
void obstructed(byte mask);
/**
* Barrier blocking forward movement
*/
void barrier();
public:
ViewBase(UIElement *owner);
virtual ~ViewBase() {}
bool msgFocus(const FocusMessage &msg) override;
bool msgHeader(const HeaderMessage &msg) override;
bool msgGame(const GameMessage &msg) override;
bool msgAction(const ActionMessage &msg) override;
/**
* Updates game state
*/
void update();
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,97 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/game/wheel_spin.h"
#include "mm/mm1/maps/map16.h"
#include "mm/mm1/globals.h"
namespace MM {
namespace MM1 {
namespace Game {
#define VAL2 84
#define VAL3 85
#define VAL4 87
void WheelSpin::spin() {
Maps::Map16 &map = *static_cast<Maps::Map16 *>(g_maps->_currentMap);
_results.clear();
for (uint i = 0; i < g_globals->_party.size(); ++i) {
Character &c = g_globals->_party[i];
g_globals->_currCharacter = &c;
map[VAL2] = i;
// Count set flags
map[VAL4] = 0;
byte v = c._flags[2];
for (int j = 0; j < 4; ++j, v >>= 1) {
if (v & 1)
map[VAL4]++;
}
Common::String line;
if (map[VAL4] == 0) {
line = STRING["maps.map16.loser"];
} else {
c._flags[2] |= CHARFLAG2_80;
int val;
switch (getRandomNumber(6)) {
case 1:
val = 2000 << map[VAL4];
WRITE_LE_UINT16(&map[VAL3], val);
c._exp += val;
line = Common::String::format("+%d %s", val,
STRING["maps.map16.exp"].c_str());
break;
case 2:
val = 500 << map[VAL4];
WRITE_LE_UINT16(&map[VAL3], val);
c._gold += val;
line = Common::String::format("+%d %s", val,
STRING["maps.map16.gold"].c_str());
break;
case 3:
val = 15 << map[VAL4];
WRITE_LE_UINT16(&map[VAL3], val);
c._gems += val;
line = Common::String::format("+%d %s", val,
STRING["maps.map16.gems"].c_str());
break;
default:
line = STRING["maps.map16.loser"];
break;
}
}
_results.push_back(line);
}
}
} // namespace Game
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,47 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GAME_WHEEL_SPIN_H
#define MM1_GAME_WHEEL_SPIN_H
#include "common/str-array.h"
#include "mm/mm1/game/game_logic.h"
namespace MM {
namespace MM1 {
namespace Game {
class WheelSpin : public GameLogic {
protected:
Common::StringArray _results;
public:
/**
* Spins the wheel for the party, and loads _results with
* the result for each character
*/
void spin();
};
} // namespace Game
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,47 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/shared/utils/bitmap_font.h"
namespace MM {
namespace MM1 {
namespace Gfx {
/**
* The Might and Magic 1 font only has uppercase. So we
* override the base BitmapFont class to uppercase characters
*/
class BitmapFont : public ::MM::BitmapFont {
public:
void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override {
chr = (chr & 0x80) | toupper(chr & 0x7f);
MM::BitmapFont::drawChar(dst, chr, x, y, color);
}
void drawChar(Graphics::ManagedSurface *dst, uint32 chr, int x, int y, uint32 color) const override {
chr = (chr & 0x80) | toupper(chr & 0x7f);
MM::BitmapFont::drawChar(dst, chr, x, y, color);
}
};
} // namespace Gfx
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,49 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/file.h"
#include "mm/mm1/gfx/dta.h"
#include "mm/mm1/gfx/screen_decoder.h"
namespace MM {
namespace MM1 {
namespace Gfx {
Common::SeekableReadStream *DTA::load(uint entryIndex) {
Common::File f;
if (!f.open(_fname))
error("Could not open - %s", _fname.toString().c_str());
uint indexSize = f.readUint16LE();
assert(entryIndex < (indexSize / 4));
f.seek(entryIndex * 4, SEEK_CUR);
size_t entryOffset = f.readUint32LE();
size_t nextOffset = (entryIndex == (indexSize / 4 - 1)) ?
f.size() : f.readUint32LE();
f.seek(2 + indexSize + entryOffset);
return f.readStream(nextOffset - entryOffset);
}
} // namespace Gfx
} // namespace MM1
} // namespace MM

51
engines/mm/mm1/gfx/dta.h Normal file
View File

@@ -0,0 +1,51 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GFX_DTA_H
#define MM1_GFX_DTA_H
#include "graphics/managed_surface.h"
namespace MM {
namespace MM1 {
namespace Gfx {
#define WALLPIX_DTA (g_engine->isEnhanced() ? "gfx/wallpix.dta" : "wallpix.dta")
#define MONPIX_DTA (g_engine->isEnhanced() ? "gfx/monpix.dta" : "monpix.dta")
class DTA {
public:
Common::Path _fname;
public:
DTA(const Common::Path &fname) : _fname(fname) {
}
/**
* Returns a read stream for an entry
*/
Common::SeekableReadStream *load(uint entryIndex);
};
} // namespace Gfx
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,86 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/system.h"
#include "common/debug.h"
#include "graphics/paletteman.h"
#include "graphics/screen.h"
#include "mm/mm1/gfx/gfx.h"
namespace MM {
namespace MM1 {
namespace Gfx {
byte EGA_INDEXES[EGA_PALETTE_COUNT];
static const uint32 EGA_PALETTE[16] = {
0x000000, 0x0000aa, 0x00aa00, 0x00aaaa,
0xaa0000, 0xaa00aa, 0xaa5500, 0xaaaaaa,
0x555555, 0x5555ff, 0x55ff55, 0x55ffff,
0xff5555, 0xff55ff, 0xffff55, 0xffffff
};
void GFX::setEgaPalette() {
byte pal[16 * 3];
byte *pDest = pal;
for (int i = 0; i < EGA_PALETTE_COUNT; ++i) {
*pDest++ = (EGA_PALETTE[i] >> 16) & 0xff;
*pDest++ = (EGA_PALETTE[i] >> 8) & 0xff;
*pDest++ = EGA_PALETTE[i] & 0xff;
EGA_INDEXES[i] = i;
}
g_system->getPaletteManager()->setPalette(pal, 0, 16);
uint32 c = 0xffffffff;
g_system->getPaletteManager()->setPalette((const byte *)&c, 255, 1);
// Set the EGA palette indexes to be themselves
}
void GFX::findPalette(const byte palette[256 * 3]) {
for (int col = 0; col < 16; ++col) {
byte r = (EGA_PALETTE[col] >> 16) & 0xff;
byte g = (EGA_PALETTE[col] >> 8) & 0xff;
byte b = EGA_PALETTE[col] & 0xff;
int closestDiff = 0x7fffffff;
byte closest = 0;
const byte *pal = &palette[0];
for (int i = 0; i < 256; ++i, pal += 3) {
int diff = ABS((int)r - (int)pal[0]) +
ABS((int)g - (int)pal[1]) +
ABS((int)b - (int)pal[2]);
if (diff < closestDiff) {
closestDiff = diff;
closest = i;
}
}
EGA_INDEXES[col] = closest;
}
}
} // namespace Gfx
} // namespace MM1
} // namespace MM

53
engines/mm/mm1/gfx/gfx.h Normal file
View File

@@ -0,0 +1,53 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GFX_H
#define MM1_GFX_H
namespace MM {
namespace MM1 {
namespace Gfx {
#define SCREEN_W 320
#define SCREEN_H 200
#define EGA_PALETTE_COUNT 16
extern byte EGA_INDEXES[EGA_PALETTE_COUNT];
class GFX {
public:
/**
* Sets the EGA palette
*/
static void setEgaPalette();
/**
* Called after the Xeen palette has been loaded, to determine
* which palette indexes most closely match the EGA colors
*/
static void findPalette(const byte palette[256 * 3]);
};
} // namespace Gfx
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,102 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/file.h"
#include "common/system.h"
#include "graphics/screen.h"
#include "mm/mm1/gfx/gfx.h"
#include "mm/mm1/gfx/screen_decoder.h"
namespace MM {
namespace MM1 {
namespace Gfx {
#define IMAGE_SIZE 16000
ScreenDecoder::~ScreenDecoder() {
destroy();
}
void ScreenDecoder::destroy() {
_surface.free();
}
bool ScreenDecoder::loadFile(const Common::Path &fname,
int16 w, int16 h) {
Common::File f;
if (!f.open(fname))
return false;
f.skip(2); // Skip size word
return loadStream(f, w, h);
}
bool ScreenDecoder::loadStream(Common::SeekableReadStream &stream,
int16 w, int16 h) {
byte bytes[IMAGE_SIZE];
byte v;
int len;
const byte *srcP;
byte *destP = &bytes[0];
int index = 0;
int imgSize = w * h / 4;
// Decompress the image bytes
int x = 0;
while (x < (w / 4) && !stream.eos()) {
v = stream.readByte();
if (v != 0x7B) {
len = 1;
} else {
len = stream.readByte() + 1;
v = stream.readByte();
}
for (; len > 0; --len) {
destP[index] = v;
index += (w / 4);
if (index >= imgSize) {
index = 0;
++destP;
++x;
}
}
}
// Create surface from splitting up the pairs of bits
_surface.free();
_surface.create(w, h, Graphics::PixelFormat::createFormatCLUT8());
srcP = &bytes[0];
destP = (byte *)_surface.getPixels();
for (int i = 0; i < w * h / 4; ++i, ++srcP) {
v = *srcP;
for (int j = 0; j < 4; ++j, v <<= 2)
*destP++ = EGA_INDEXES[_indexes[v >> 6]];
}
return true;
}
} // namespace Gfx
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,62 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GFX_SCREEN_DECODER_H
#define MM1_GFX_SCREEN_DECODER_H
#include "image/image_decoder.h"
#include "graphics/managed_surface.h"
#include "graphics/palette.h"
namespace MM {
namespace MM1 {
namespace Gfx {
class ScreenDecoder : public Image::ImageDecoder {
private:
Graphics::Surface _surface;
Graphics::Palette _palette;
public:
byte _indexes[4] = { 0 }; // EGA palete indexes used
public:
ScreenDecoder() : _palette(0) {}
~ScreenDecoder() override;
void destroy() override;
bool loadFile(const Common::Path &fname,
int16 w = 320, int16 h = 200);
bool loadStream(Common::SeekableReadStream &stream, int16 w, int16 h);
bool loadStream(Common::SeekableReadStream &stream) override {
return loadStream(stream, 320, 200);
}
const Graphics::Surface *getSurface() const override {
return &_surface;
}
const Graphics::Palette &getPalette() const override { return _palette; }
void clear() { _surface.free(); }
};
} // namespace Gfx
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,69 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/file.h"
#include "image/bmp.h"
#include "image/png.h"
#include "mm/mm1/gfx/symbols.h"
namespace MM {
namespace MM1 {
namespace Gfx {
void Symbols::load() {
Common::File f;
if (!f.open("symbols.bin"))
error("Could not load symbols.bin");
for (uint i = 0; i < MAX_SYMBOLS; ++i) {
const Common::String fname1 = Common::String::format("gfx//symbols.bin//image%.2d.bmp", i);
const Common::String fname2 = Common::String::format("gfx//symbols.bin//image%.2d.bmp", i);
Common::File fOverride1, fOverride2;
Image::BitmapDecoder bmpDecoder;
Image::PNGDecoder pngDecoder;
if (fOverride1.open(fname1.c_str()) && bmpDecoder.loadStream(fOverride1)) {
// Load the bitmap
_data[i].copyFrom(*bmpDecoder.getSurface());
f.skip(SYMBOL_WIDTH * SYMBOL_HEIGHT);
} else if (fOverride2.open(fname2.c_str()) && pngDecoder.loadStream(fOverride2)) {
// Load the png
_data[i].copyFrom(*pngDecoder.getSurface());
f.skip(SYMBOL_WIDTH * SYMBOL_HEIGHT);
} else {
// Fall back on the default
_data[i].create(SYMBOL_WIDTH, SYMBOL_HEIGHT);
f.read((byte *)_data[i].getPixels(), SYMBOL_WIDTH * SYMBOL_HEIGHT);
}
}
f.close();
}
void Symbols::draw(Graphics::ManagedSurface &dest, const Common::Point &destPos, int symbolNum) {
assert(symbolNum < MAX_SYMBOLS);
dest.blitFrom(_data[symbolNum], destPos);
}
} // namespace Gfx
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,49 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GFX_SYMBOLS_H
#define MM1_GFX_SYMBOLS_H
#include "graphics/managed_surface.h"
namespace MM {
namespace MM1 {
namespace Gfx {
#define MAX_SYMBOLS 20
#define SYMBOL_WIDTH 8
#define SYMBOL_HEIGHT 8
class Symbols {
private:
Graphics::ManagedSurface _data[20];
public:
void load();
void draw(Graphics::ManagedSurface &dest, const Common::Point &destPos, int symbolNum);
};
} // namespace Gfx
} // namespace MM1
} // namespace MM
#endif

180
engines/mm/mm1/globals.cpp Normal file
View File

@@ -0,0 +1,180 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/engine_data.h"
#include "engines/engine.h"
#include "graphics/fonts/ttf.h"
#include "image/bmp.h"
#include "mm/mm1/globals.h"
#include "mm/mm1/mm1.h"
#include "mm/shared/utils/strings.h"
#include "graphics/fontman.h"
namespace MM {
namespace MM1 {
Globals *g_globals;
Globals::Globals() {
g_globals = this;
_treasure.clear();
}
Globals::~Globals() {
delete _monsters;
g_globals = nullptr;
}
void Globals::createBlankButton() {
// Get the Escape glyph we'll use as a base
Shared::Xeen::SpriteResource escSprite;
escSprite.load("esc.icn");
_blankButton.create(20, 20);
_blankButton.clear(255);
_blankButton.setTransparentColor(255);
escSprite.draw(&_blankButton, 0, Common::Point(0, 0));
_blankButton.fillRect(Common::Rect(2, 2, 18, 18), 0x9f);
}
bool Globals::load(bool isEnhanced) {
// Initialise engine data for the game
Common::U32String errMsg;
if (!Common::load_engine_data("mm.dat", "mm1", 1, 0, errMsg)) {
GUIErrorMessage(errMsg);
return false;
}
if (!_strings.load("strings_en.yml"))
return false;
if (!_font.load("font.bmp"))
return false;
_monsters = new Monsters();
if (!_monsters->load() || !_items.load())
return false;
// Load roster
_roster.load();
if (isEnhanced) {
_mainIcons.load("main.icn");
_confirmIcons.load("confirm.icn");
_globalSprites.load("global.icn");
_tileSprites.load("town.til");
_escSprites.load("esc.icn");
createBlankButton();
{
// Load the Xeen game screen background
Common::File f;
Image::BitmapDecoder decoder;
if (f.open("gfx/back.bmp") && decoder.loadStream(f))
_gameBackground.copyFrom(*decoder.getSurface());
else if (f.open("back.raw")) {
_gameBackground.create(320, 200);
f.read(_gameBackground.getPixels(), 320 * 200);
} else {
error("Could not load background");
}
}
_symbols.load();
{
Common::File f;
if (!f.open("fnt"))
error("Could not open font");
_fontNormal.load(&f, 0, 0x1000);
_fontReduced.load(&f, 0x800, 0x1080);
f.close();
}
{
Common::File f;
if (!f.open("text_colors.bin"))
error("Could not open text colors");
XeenFont::loadColors(&f);
XeenFont::setColors(0);
f.close();
}
// Show the mouse cursor
g_events->loadCursors();
g_events->setCursor(0);
g_events->showCursor();
}
return true;
}
Common::String Globals::operator[](const Common::String &name) const {
bool isMapStr = name.hasPrefix("maps.map");
if (g_engine->isEnhanced()) {
if (isMapStr) {
// Map strings support having alternate versions in Enhanced version
Common::String altName = Common::String::format("maps.emap%s",
name.c_str() + 8);
if (_strings.contains(altName))
return _strings[altName];
}
if (name.hasPrefix("dialogs.")) {
Common::String altName = Common::String::format("enh%s", name.c_str());
if (_strings.contains(altName))
return _strings[altName];
}
}
assert(_strings.contains(name));
Common::String result = _strings[name];
if (g_engine->isEnhanced() && name.hasPrefix("maps."))
result = searchAndReplace(result, "\n", " ");
return result;
}
void Globals::synchronize(Common::Serializer &s) {
s.syncAsByte(_startingTown);
s.syncAsByte(_minimapOn);
// Sync the state information
_party.synchronize(s);
_activeSpells.synchronize(s);
_spellsState.synchronize(s);
_treasure.synchronize(s);
// Sync map data and visited tiles
_maps.synchronize(s);
_maps.synchronizeCurrent(s);
if (s.isLoading()) {
_currCharacter = nullptr;
_heardRumor = false;
_nonCombatEffectCtr = _combatEffectCtr = 0;
}
}
} // namespace MM1
} // namespace MM

113
engines/mm/mm1/globals.h Normal file
View File

@@ -0,0 +1,113 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_GLOBALS_H
#define MM1_GLOBALS_H
#include "common/serializer.h"
#include "graphics/font.h"
#include "mm/shared/utils/strings_data.h"
#include "mm/shared/utils/xeen_font.h"
#include "mm/xeen/sprites.h"
#include "mm/mm1/data/game_state.h"
#include "mm/mm1/data/items.h"
#include "mm/mm1/data/monsters.h"
#include "mm/mm1/data/roster.h"
#include "mm/mm1/data/treasure.h"
#include "mm/mm1/gfx/bitmap_font.h"
#include "mm/mm1/gfx/symbols.h"
#include "mm/mm1/game/combat.h"
#include "mm/mm1/game/encounter.h"
#include "mm/mm1/maps/maps.h"
namespace MM {
namespace MM1 {
class Globals : public GameState {
public:
StringsData _strings;
Roster _roster;
Common::Array<Character *> _combatParty;
Character *_currCharacter = nullptr;
Item _currItem;
ItemsArray _items;
Maps::TownId _startingTown = Maps::SORPIGAL;
Maps::Maps _maps;
Gfx::BitmapFont _font;
bool _heardRumor = false;
Game::Encounter _encounters;
Game::Combat *_combat = nullptr;
Monsters *_monsters = nullptr;
Treasure _treasure;
byte _delay = 5;
int _nonCombatEffectCtr = 0, _combatEffectCtr = 0;
bool _minimapOn = false;
// Console flags
bool _intangible = false;
bool _encountersOn = true;
bool _allSpells = false;
/**
* Creates a blank button with no inner glyph
*/
void createBlankButton();
public:
// Enhanced mode globals
Shared::Xeen::SpriteResource _mainIcons;
Shared::Xeen::SpriteResource _confirmIcons;
Shared::Xeen::SpriteResource _globalSprites;
Shared::Xeen::SpriteResource _tileSprites;
Shared::Xeen::SpriteResource _escSprites;
Graphics::ManagedSurface _blankButton;
Graphics::ManagedSurface _gameBackground;
Gfx::Symbols _symbols;
XeenFont _fontNormal;
XeenFont _fontReduced;
public:
Globals();
virtual ~Globals();
/**
* Loads data for the globals
*/
bool load(bool isEnhanced);
/**
* Returns a string
*/
Common::String operator[](const Common::String &name) const;
/**
* Saves global data to/from savegames
*/
void synchronize(Common::Serializer &s);
};
extern Globals *g_globals;
#define STRING (*g_globals)
} // namespace MM1
} // namespace MM
#endif

148
engines/mm/mm1/maps/map.cpp Normal file
View File

@@ -0,0 +1,148 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/file.h"
#include "mm/mm1/maps/map.h"
#include "mm/mm1/maps/maps.h"
#include "mm/mm1/globals.h"
#include "mm/mm1/events.h"
namespace MM {
namespace MM1 {
namespace Maps {
Map::Map(uint index, const Common::String &name, uint16 id,
byte defaultSection, const char *desc) :
_mapIndex(index), _name(name), _id(id), _defaultSection(defaultSection) {
_description = desc ? Common::String(desc) : _name;
_description.setChar(toupper(_description[0]), 0);
if (_description.hasPrefix("Area")) {
_description.setChar(toupper(_description[4]), 4);
_description.insertChar(' ', 4);
}
Common::fill((byte *)&_walls[0], (byte *)&_walls[MAP_SIZE], 0);
Common::fill(&_states[0], (byte *)&_states[MAP_SIZE], 0);
Common::fill(&_visited[0], &_visited[MAP_SIZE], 0);
}
void Map::load() {
loadMazeData();
loadOverlay();
g_globals->_heardRumor = false;
}
void Map::loadMazeData() {
Common::File f;
if (!f.open("mazedata.dta"))
error("Could not open mazedata.dta");
f.seek(512 * _mapIndex);
f.read((byte *)_walls, MAP_SIZE);
f.read(_states, MAP_SIZE);
f.close();
}
void Map::loadOverlay() {
Common::File f;
if (!f.open(Common::Path(Common::String::format("%s.ovr", _name.c_str()))))
error("Could not open %s.ovr overlay", _name.c_str());
int magicId = f.readUint16LE();
int codePtr = f.readUint16LE();
int codeSize = f.readUint16LE();
f.readUint16LE(); // dataPtr
int dataSize = f.readUint16LE();
f.readUint16LE(); // extras size
f.readUint16LE(); // code entry-point
if (magicId != 0xF2 || (codePtr != 0xF48F && codePtr != 0xF47C))
error("Invalid map overlay header");
// Skip over code segment, since each map's
// code is going to be reimplemented in C++
f.skip(codeSize);
// Read in the data segment
_data.resize(dataSize);
f.read(&_data[0], dataSize);
}
bool Map::checkPartyDead() {
return g_globals->_party.checkPartyDead();
}
uint16 Map::dataWord(uint16 ofs) const {
return READ_LE_UINT16(&_data[ofs]);
}
void Map::dataWord(uint16 ofs, uint16 val) {
WRITE_LE_UINT16(&_data[ofs], val);
}
void Map::reduceHP() {
for (uint i = 0; i < g_globals->_party.size(); ++i)
g_globals->_party[i]._hpCurrent /= 2;
}
void Map::updateGame() {
g_events->send("Game", GameMessage("UPDATE"));
}
void Map::redrawGame() {
g_events->send("Game", GameMessage("REDRAW"));
}
void Map::encounter(const byte *id1, const byte *id2) {
Game::Encounter &enc = g_globals->_encounters;
g_maps->clearSpecial();
enc.clearMonsters();
for (int i = 0; i < 14 && *id1; ++i, ++id1, ++id2)
enc.addMonster(*id1, *id2);
enc._manual = true;
enc._levelIndex = 64;
enc.execute();
}
void Map::unlockDoor() {
g_maps->_currentState = _states[g_maps->_mapOffset] ^=
g_maps->_forwardMask & 0x55;
}
void Map::visitedSpecial() {
if (!_visited[g_maps->_mapOffset])
_visited[g_maps->_mapOffset] = VISITED_SPECIAL;
}
void Map::visitedExit() {
_visited[g_maps->_mapOffset] = VISITED_EXIT;
}
void Map::visitedBusiness() {
_visited[g_maps->_mapOffset] = VISITED_BUSINESS;
}
} // namespace Maps
} // namespace MM1
} // namespace MM

260
engines/mm/mm1/maps/map.h Normal file
View File

@@ -0,0 +1,260 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_MAPS_MAP_H
#define MM1_MAPS_MAP_H
#include "common/array.h"
#include "common/str.h"
#include "mm/mm1/events.h"
#include "mm/mm1/game/game_logic.h"
namespace MM {
namespace MM1 {
namespace Maps {
#define MAP_W 16
#define MAP_H 16
#define MAP_SIZE (MAP_W * MAP_H)
class Maps;
enum DataOffset {
MAP_ID = 0,
MAP_1 = 1,
MAP_2 = 2,
MAP_4 = 4,
MAP_6 = 6,
MAP_NORTH_EXIT_ID = 8,
MAP_NORTH_EXIT_SECTION = 10,
MAP_EAST_EXIT_ID = 11,
MAP_EAST_EXIT_SECTION = 13,
MAP_SOUTH_EXIT_ID = 14,
MAP_SOUTH_EXIT_SECTION = 16,
MAP_WEST_EXIT_ID = 17,
MAP_WEST_EXIT_SECTION = 19,
MAP_20 = 20,
MAP_21 = 21,
MAP_FLEE_THRESHOLD = 22,
MAP_FLEE_X = 23,
MAP_FLEE_Y = 24,
MAP_SURRENDER_THRESHOLD = 25,
MAP_SURRENDER_X = 26,
MAP_SURRENDER_Y = 27,
MAP_BRIBE_THRESHOLD = 28,
MAP_29 = 29,
MAP_30 = 30,
MAP_31 = 31,
MAP_32 = 32,
MAP_33 = 33,
MAP_MAX_MONSTERS = 34,
MAP_SECTOR1 = 35,
MAP_SECTOR2 = 36,
MAP_TYPE = 37,
MAP_DISPEL_THRESHOLD = 38,
MAP_SURFACE_ID = 39,
MAP_SURFACE_SECTION = 41,
MAP_SURFACE_X = 42,
MAP_SURFACE_Y = 43,
MAP_44 = 44,
MAP_45 = 45,
MAP_FLAGS = 46,
MAP_47 = 47,
MAP_TRAP_THRESHOLD = 48,
MAP_49 = 49,
MAP_SPECIAL_COUNT = 50
};
enum WallType {
WALL_NONE = 0, WALL_NORMAL = 1, WALL_DOOR = 2,
WALL_TORCH = 3
};
enum CellState {
CELL_SPECIAL = 0x80, CELL_DARK = 0x20
};
class Maps;
class Map : public Game::GameLogic {
protected:
Common::String _name;
Common::String _description;
uint16 _id;
uint _mapIndex;
byte _defaultSection;
Common::Array<byte> _data;
private:
/**
* Loads the map's maze data
*/
void loadMazeData();
/**
* Load the map's overlay file
*/
void loadOverlay();
public:
/**
* Divides all the party's Hp in half
*/
static void reduceHP();
/**
* Many special methods return a 160 key, but it
* doesn't look like the game uses. My current
* assumption is that this is just a convenient
* 'do nothing' return value
*/
static void none160() {}
/**
* Generates an encounter based on the passed id arrays
*/
void encounter(const byte *id1, const byte *id2);
public:
byte _walls[MAP_SIZE];
byte _states[MAP_SIZE];
uint8 _visited[MAP_SIZE];
public:
Map(uint index, const Common::String &name, uint16 id,
byte defaultSection, const char *desc = nullptr);
virtual ~Map() {}
/**
* Loads the map
*/
virtual void load();
/**
* Handles all special stuff that happens on the map
*/
virtual void special() = 0;
/**
* Returns true if mapping is allowed in enhanced mode.
* This is to prevent places like the desert where the
* players shouldn't be able to see where they are
*/
virtual bool mappingAllowed() const {
return true;
}
/**
* Gets the map name
*/
Common::String getName() const { return _name; }
/**
* Gets the map description for the map display
*/
Common::String getDescription() const { return _description; }
/**
* Returns the map Id
*/
uint16 getId() const { return _id; }
/**
* Returns the map default section
*/
byte getDefaultSection() const {
return _defaultSection;
}
/**
* Accesses the map data
*/
const byte &operator[](uint ofs) const {
return _data[ofs];
}
byte &operator[](uint ofs) {
return _data[ofs];
}
byte dataByte(uint ofs) const {
return _data[ofs];
}
uint16 dataWord(uint16 ofs) const;
void dataWord(uint16 ofs, uint16 val);
/**
* Checks whether the party is dead or out of action,
* and if so, switches to the death screen
*/
bool checkPartyDead();
/**
* Send a message to a UI element
*/
template<class T>
bool send(const T &msg) {
return g_events->send(msg);
}
template<class T>
bool send(const Common::String &name, const T &msg) {
return g_events->send(name, msg);
}
/**
* Returns the map's index
*/
uint getMapIndex() const {
return _mapIndex;
}
/**
* Updates the game display
*/
static void updateGame();
/**
* Redraw the game display
*/
static void redrawGame();
/**
* Unlocks door
*/
void unlockDoor();
/**
* Visited a special cell
*/
void visitedSpecial();
/**
* Visited an exit cell
*/
void visitedExit();
/**
* Visited a business cell
*/
void visitedBusiness();
};
} // namespace Maps
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,235 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/maps/map00.h"
#include "mm/mm1/maps/maps.h"
#include "mm/mm1/events.h"
#include "mm/mm1/globals.h"
#include "mm/mm1/sound.h"
namespace MM {
namespace MM1 {
namespace Maps {
#define STATUE_VAL 0x412
void Map00::special() {
// Scan for special actions on the map cell
for (uint i = 0; i < 24; ++i) {
if (g_maps->_mapOffset == _data[51 + i]) {
// Found a specially handled cell, but it
// only triggers in designated direction(s)
if (g_maps->_forwardMask & _data[75 + i]) {
(this->*SPECIAL_FN[i])();
} else {
checkPartyDead();
}
return;
}
}
// All other cells on the map are encounters
g_maps->clearSpecial();
g_globals->_encounters.execute();
}
void Map00::special00() {
inn();
}
void Map00::special01() {
Common::String line2;
int x = 5;
switch (g_maps->_forwardMask) {
case DIRMASK_E:
line2 = STRING["maps.map00.market"];
x = 6;
break;
case DIRMASK_W:
line2 = STRING["maps.map00.blacksmith"];
break;
default:
line2 = STRING["maps.map00.inn"];
break;
}
send(SoundMessage(
2, 0, STRING["maps.sign"],
x, 1, line2
));
}
void Map00::special02() {
blacksmith();
}
void Map00::special03() {
market();
}
void Map00::special04() {
visitedExit();
send(SoundMessage(
STRING["maps.passage_outside1"],
[]() {
g_maps->_mapPos = Common::Point(10, 10);
g_maps->changeMap(0xa11, 2);
}
));
}
void Map00::special05() {
tavern();
}
void Map00::special06() {
temple();
}
void Map00::special07() {
training();
}
void Map00::special08() {
g_events->addView("Leprechaun");
}
void Map00::special09() {
visitedExit();
send(SoundMessage(
STRING["maps.stairs_down"],
[]() {
g_maps->changeMap(0xa11, 1);
}
));
}
void Map00::special10() {
Map &map = *g_maps->_currentMap;
map[STATUE_VAL] = 0;
searchStatue();
}
void Map00::special11() {
Map &map = *g_maps->_currentMap;
map[STATUE_VAL] = 1;
searchStatue();
}
void Map00::special12() {
Map &map = *g_maps->_currentMap;
map[STATUE_VAL] = 2;
searchStatue();
}
void Map00::special13() {
Map &map = *g_maps->_currentMap;
map[STATUE_VAL] = 3;
searchStatue();
}
void Map00::special14() {
Map &map = *g_maps->_currentMap;
map[STATUE_VAL] = 4;
searchStatue();
}
void Map00::special15() {
Map &map = *g_maps->_currentMap;
map[STATUE_VAL] = 5;
searchStatue();
}
void Map00::special16() {
Map &map = *g_maps->_currentMap;
map[STATUE_VAL] = 6;
searchStatue();
}
void Map00::special17() {
Map &map = *g_maps->_currentMap;
map[STATUE_VAL] = 7;
searchStatue();
}
void Map00::special18() {
send(SoundMessage(
2, 0, STRING["maps.sign"],
6, 1, STRING["maps.map00.temple"]
));
}
void Map00::special19() {
send(SoundMessage(
2, 0, STRING["maps.sign"],
6, 1, STRING["maps.map00.jail"]
));
}
void Map00::special20() {
send(SoundMessage(
2, 0, STRING["maps.sign"],
6, 1, STRING["maps.map00.tavern"]
));
}
void Map00::special21() {
send(SoundMessage(
2, 0, STRING["maps.sign"],
6, 1, STRING["maps.map00.training"]
));
}
void Map00::special22() {
Map &map = *g_maps->_currentMap;
map[MM1::Maps::MAP_47] = 3;
map[MM1::Maps::MAP_33] = 6;
g_maps->clearSpecial();
}
void Map00::special23() {
visitedExit();
Common::String msg = STRING["maps.map00.trapdoor"];
if (g_globals->_activeSpells._s.levitate)
msg += STRING["maps.map00.levitate"];
send(SoundMessage(msg,
[](const Common::KeyState &keyState) {
g_events->focusedView()->close();
if (!g_globals->_activeSpells._s.levitate)
g_maps->changeMap(0xa11, 1);
}
));
}
void Map00::searchStatue() {
send(SoundMessage(
STRING["maps.map00.statue"],
[]() {
Map &map = *g_maps->_currentMap;
g_events->send("Statue", GameMessage("STATUE", map[STATUE_VAL]));
}
));
}
} // namespace Maps
} // namespace MM1
} // namespace MM

100
engines/mm/mm1/maps/map00.h Normal file
View File

@@ -0,0 +1,100 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_MAPS_MAP00_H
#define MM1_MAPS_MAP00_H
#include "mm/mm1/maps/map_town.h"
namespace MM {
namespace MM1 {
namespace Maps {
class Map00 : public MapTown {
typedef void (Map00:: *SpecialFn)();
private:
void searchStatue();
void special00();
void special01();
void special02();
void special03();
void special04();
void special05();
void special06();
void special07();
void special08();
void special09();
void special10();
void special11();
void special12();
void special13();
void special14();
void special15();
void special16();
void special17();
void special18();
void special19();
void special20();
void special21();
void special22();
void special23();
const SpecialFn SPECIAL_FN[24] = {
&Map00::special00,
&Map00::special01,
&Map00::special02,
&Map00::special03,
&Map00::special04,
&Map00::special05,
&Map00::special06,
&Map00::special07,
&Map00::special08,
&Map00::special09,
&Map00::special10,
&Map00::special11,
&Map00::special12,
&Map00::special13,
&Map00::special14,
&Map00::special15,
&Map00::special16,
&Map00::special17,
&Map00::special18,
&Map00::special19,
&Map00::special20,
&Map00::special21,
&Map00::special22,
&Map00::special23
};
public:
Map00() : MapTown(0, "sorpigal", 0x604, 1) {}
/**
* Handles all special stuff that happens on the map
*/
virtual void special();
};
} // namespace Maps
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,184 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/maps/map01.h"
#include "mm/mm1/maps/maps.h"
#include "mm/mm1/events.h"
#include "mm/mm1/globals.h"
#include "mm/mm1/sound.h"
namespace MM {
namespace MM1 {
namespace Maps {
void Map01::special() {
// Scan for special actions on the map cell
for (uint i = 0; i < 17; ++i) {
if (g_maps->_mapOffset == _data[51 + i]) {
// Found a specially handled cell, but it
// only triggers in designated direction(s)
if (g_maps->_forwardMask & _data[68 + i]) {
(this->*SPECIAL_FN[i])();
} else {
checkPartyDead();
}
return;
}
}
// All other cells on the map are encounters
g_maps->clearSpecial();
g_globals->_encounters.execute();
}
void Map01::special00() {
inn();
}
void Map01::special01() {
Common::String line1 = STRING["maps.map01.zam1"];
Common::String line2 = STRING["maps.map01.zam3"];
bool hasCourier = false;
for (uint i = 0; i < g_globals->_party.size() && !hasCourier; ++i) {
hasCourier = (g_globals->_party[i]._flags[0] & CHARFLAG0_COURIER3) != 0;
}
if (hasCourier) {
for (uint i = 0; i < g_globals->_party.size(); ++i) {
Character &c = g_globals->_party[i];
c._flags[0] |= CHARFLAG0_COURIER3 | CHARFLAG0_ZAM_CLUE;
}
line2 = STRING["maps.map01.zam2"];
}
send(InfoMessage(
0, 1, line1,
0, 3, line2
));
}
void Map01::special02() {
blacksmith();
}
void Map01::special03() {
market();
}
void Map01::special04() {
visitedExit();
send(SoundMessage(
STRING["maps.passage_outside2"],
[]() {
g_maps->_mapPos = Common::Point(3, 3);
g_maps->changeMap(0x101, 2);
}
));
}
void Map01::special05() {
tavern();
}
void Map01::special06() {
temple();
}
void Map01::special07() {
training();
}
void Map01::special08() {
// Paralyze all the men
for (uint i = 0; i < g_globals->_party.size(); ++i) {
Character &c = g_globals->_party[i];
if (c._sex == MALE && !(c._condition & BAD_CONDITION))
c._condition = PARALYZED;
}
redrawGame();
// Show the message and wait for a keypress
send(SoundMessage(
STRING["maps.map01.secret"],
[](const Common::KeyState &) {
g_events->close();
Game::Encounter &enc = g_globals->_encounters;
enc.clearMonsters();
uint count = g_events->getRandomNumber(4) + 4;
enc.addMonster(6, 12);
for (uint i = 0; i < count; ++i)
enc.addMonster(4, 9);
enc._manual = true;
enc._levelIndex = 80;
enc.execute();
}
));
}
void Map01::special09() {
visitedExit();
send(SoundMessage(
STRING["maps.stairs_down"],
[]() {
g_maps->_mapPos = Common::Point(8, 0);
g_maps->changeMap(0xc01, 1);
}
));
}
void Map01::special10() {
showSign(STRING["maps.map01.market"]);
}
void Map01::special11() {
showSign(STRING["maps.map01.blacksmith"]);
}
void Map01::special12() {
showSign(STRING["maps.map01.inn"]);
}
void Map01::special13() {
showSign(STRING["maps.map01.temple"]);
}
void Map01::special14() {
send(SoundMessage(
0, 1, STRING["maps.map01.zam0"]
));
}
void Map01::special15() {
showSign(STRING["maps.map01.tavern"]);
}
void Map01::special16() {
showSign(STRING["maps.map01.training"]);
}
} // namespace Maps
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,84 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_MAPS_MAP01_H
#define MM1_MAPS_MAP01_H
#include "mm/mm1/maps/map_town.h"
namespace MM {
namespace MM1 {
namespace Maps {
class Map01 : public MapTown {
typedef void (Map01:: *SpecialFn)();
private:
void special00();
void special01();
void special02();
void special03();
void special04();
void special05();
void special06();
void special07();
void special08();
void special09();
void special10();
void special11();
void special12();
void special13();
void special14();
void special15();
void special16();
const SpecialFn SPECIAL_FN[17] = {
&Map01::special00,
&Map01::special01,
&Map01::special02,
&Map01::special03,
&Map01::special04,
&Map01::special05,
&Map01::special06,
&Map01::special07,
&Map01::special08,
&Map01::special09,
&Map01::special10,
&Map01::special11,
&Map01::special12,
&Map01::special13,
&Map01::special14,
&Map01::special15,
&Map01::special16
};
public:
Map01() : MapTown(1, "portsmit", 0xc03, 1, "Portsmith") {}
/**
* Handles all special stuff that happens on the map
*/
void special() override;
};
} // namespace Maps
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,181 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/maps/map02.h"
#include "mm/mm1/maps/maps.h"
#include "mm/mm1/events.h"
#include "mm/mm1/globals.h"
#include "mm/mm1/sound.h"
namespace MM {
namespace MM1 {
namespace Maps {
static const byte PORTAL_DEST_X[4] = { 8, 15, 0, 5 };
static const byte PORTAL_DEST_Y[4] = { 4, 0, 0, 14 };
static const uint16 PORTAL_DEST_ID[4] = { 0xc03, 0xa00, 0xf01, 0x604 };
static const byte PORTAL_DEST_SECTION[4] = { 3, 0, 1, 4 };
void Map02::special() {
// Scan for special actions on the map cell
for (uint i = 0; i < 22; ++i) {
if (g_maps->_mapOffset == _data[51 + i]) {
// Found a specially handled cell, but it
// only triggers in designated direction(s)
if (g_maps->_forwardMask & _data[73 + i]) {
(this->*SPECIAL_FN[i])();
} else {
checkPartyDead();
}
return;
}
}
// All other cells on the map are encounters
g_maps->clearSpecial();
g_globals->_encounters.execute();
}
void Map02::special00() {
inn();
}
void Map02::special01() {
Common::String line1 = STRING["maps.map02.zom1"];
Common::String line2 = STRING["maps.map02.zom3"];
bool hasCourier = false;
for (uint i = 0; i < g_globals->_party.size() && !hasCourier; ++i) {
hasCourier = (g_globals->_party[i]._flags[0] & CHARFLAG0_COURIER3) != 0;
}
if (hasCourier) {
for (uint i = 0; i < g_globals->_party.size(); ++i) {
Character &c = g_globals->_party[i];
c._flags[0] |= CHARFLAG0_COURIER3 | CHARFLAG0_ZOM_CLUE;
}
line2 = STRING["maps.map02.zom2"];
}
send(InfoMessage(
0, 1, line1,
0, 3, line2
));
}
void Map02::special02() {
blacksmith();
}
void Map02::special03() {
market();
}
void Map02::special04() {
visitedExit();
send(SoundMessage(
STRING["maps.passage_outside2"],
[]() {
g_maps->_mapPos = Common::Point(7, 7);
g_maps->changeMap(0x801, 2);
}
));
}
void Map02::special05() {
tavern();
}
void Map02::special06() {
temple();
}
void Map02::special07() {
training();
}
void Map02::special08() {
g_events->addView("Resistances");
}
void Map02::special09() {
showSign(STRING["maps.map02.docks"]);
}
void Map02::special10() {
showSign(STRING["maps.map02.market"]);
}
void Map02::special11() {
showSign(STRING["maps.map02.blacksmith"]);
}
void Map02::special12() {
showSign(STRING["maps.map02.inn"]);
}
void Map02::special13() {
send(SoundMessage(0, 1, STRING["maps.map02.zom0"]));
}
void Map02::special14() {
send(SoundMessage(
STRING["maps.map02.pit"],
[]() {
g_maps->_mapPos.x++;
g_globals->_encounters.execute();
}
));
}
void Map02::special15() {
visitedExit();
send(SoundMessage(
STRING["maps.map02.portal"],
[]() {
int index = g_maps->_mapPos.x - 9;
g_maps->_mapPos.x = PORTAL_DEST_X[index];
g_maps->_mapPos.y = PORTAL_DEST_Y[index];
uint16 id = PORTAL_DEST_ID[index];
byte section = PORTAL_DEST_SECTION[index];
g_maps->changeMap(id, section);
}
));
}
void Map02::special18() {
showSign(STRING["maps.map02.temple"]);
}
void Map02::special20() {
showSign(STRING["maps.map02.tavern"]);
}
void Map02::special21() {
showSign(STRING["maps.map02.training"]);
}
} // namespace Maps
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,91 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_MAPS_MAP02_H
#define MM1_MAPS_MAP02_H
#include "mm/mm1/maps/map_town.h"
namespace MM {
namespace MM1 {
namespace Maps {
class Map02 : public MapTown {
typedef void (Map02:: *SpecialFn)();
private:
void special00();
void special01();
void special02();
void special03();
void special04();
void special05();
void special06();
void special07();
void special08();
void special09();
void special10();
void special11();
void special12();
void special13();
void special14();
void special15();
void special18();
void special20();
void special21();
const SpecialFn SPECIAL_FN[22] = {
&Map02::special00,
&Map02::special01,
&Map02::special02,
&Map02::special03,
&Map02::special04,
&Map02::special05,
&Map02::special06,
&Map02::special07,
&Map02::special08,
&Map02::special09,
&Map02::special10,
&Map02::special11,
&Map02::special12,
&Map02::special13,
&Map02::special14,
&Map02::special15,
&Map02::special15,
&Map02::special15,
&Map02::special18,
&Map02::special15,
&Map02::special20,
&Map02::special21
};
public:
Map02() : MapTown(2, "algary", 0x203, 1) {}
/**
* Handles all special stuff that happens on the map
*/
void special() override;
};
} // namespace Maps
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,228 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/maps/map03.h"
#include "mm/mm1/maps/maps.h"
#include "mm/mm1/events.h"
#include "mm/mm1/globals.h"
#include "mm/mm1/sound.h"
namespace MM {
namespace MM1 {
namespace Maps {
static const byte MONSTER_ID1[8] = { 5, 10, 12, 2, 11, 16, 6, 12 };
static const byte MONSTER_ID2[8] = { 2, 7, 7, 7, 5, 4, 3, 8 };
void Map03::special() {
// Scan for special actions on the map cell
for (uint i = 0; i < 29; ++i) {
if (g_maps->_mapOffset == _data[51 + i]) {
// Found a specially handled cell, but it
// only triggers in designated direction(s)
if (g_maps->_forwardMask & _data[80 + i]) {
(this->*SPECIAL_FN[i])();
} else {
checkPartyDead();
}
return;
}
}
// All other cells on the map are encounters
g_maps->clearSpecial();
g_globals->_encounters.execute();
}
void Map03::special00() {
inn();
}
void Map03::special01() {
bool hasCourier = false;
for (uint i = 0; i < g_globals->_party.size() && !hasCourier; ++i) {
hasCourier = (g_globals->_party[i]._flags[0] & CHARFLAG0_COURIER3) != 0;
}
bool hasScroll = false;
for (uint i = 0; i < g_globals->_party.size(); ++i) {
Character &c = g_globals->_party[i];
if (c.hasItem(VELLUM_SCROLL_ID)) {
hasScroll = true;
c._gold += 1500;
}
}
if (hasCourier && hasScroll) {
for (uint i = 0; i < g_globals->_party.size(); ++i) {
Character &c = g_globals->_party[i];
g_globals->_currCharacter = &c;
c._exp += 2500;
int idx = c._equipped.indexOf(VELLUM_SCROLL_ID);
if (idx != -1)
c._equipped.removeAt(idx);
idx = c._backpack.indexOf(VELLUM_SCROLL_ID);
if (idx != -1)
c._backpack.removeAt(idx);
}
InfoMessage info1(
0, 0, STRING["maps.map03.telgoran1"],
0, 1, STRING["maps.map03.telgoran2"],
[](const Common::KeyState &) {
InfoMessage info2(
0, 1, STRING["maps.map03.telgoran3"],
[](const Common::KeyState &) {
g_events->close();
}
);
info2._largeMessage = true;
g_events->send(info2);
}
);
info1._largeMessage = true;
g_events->send(info1);
} else {
send(InfoMessage(
0, 0, STRING["maps.map03.telgoran1"],
0, 1, STRING["maps.map03.telgoran4"]
));
}
none160();
}
void Map03::special02() {
blacksmith();
}
void Map03::special03() {
market();
}
void Map03::special04() {
visitedExit();
send(SoundMessage(
STRING["maps.passage_outside1"],
[]() {
g_maps->_mapPos = Common::Point(9, 11);
g_maps->changeMap(0x112, 2);
}
));
}
void Map03::special05() {
tavern();
}
void Map03::special06() {
temple();
}
void Map03::special07() {
training();
}
void Map03::special08() {
g_globals->_treasure._container = GOLD_CHEST;
g_globals->_treasure._items[2] = 200;
g_globals->_treasure.setGems(200);
g_events->addAction(KEYBIND_SEARCH);
}
void Map03::special09() {
visitedExit();
send(SoundMessage(
STRING["maps.stairs_down"],
[]() {
g_maps->_mapPos = Common::Point(14, 0);
g_maps->changeMap(5, 1);
}
));
}
void Map03::special10() {
showSign(STRING["maps.map03.market"]);
}
void Map03::special11() {
showSign(STRING["maps.map03.blacksmith"]);
}
void Map03::special12() {
showSign(STRING["maps.map03.inn"]);
}
void Map03::special13() {
_data[0x1d] = 80;
_data[0x2e] = 3;
_data[0x2f] = 3;
}
void Map03::special14() {
_data[0x1d] = 150;
_data[0x2e] = 2;
_data[0x2f] = 2;
}
void Map03::special15() {
assert(g_maps->_mapPos.x < 8);
g_maps->clearSpecial();
int monsterCount = (g_maps->_mapPos.x < 3) ? 1 :
getRandomNumber(8);
Game::Encounter &enc = g_globals->_encounters;
enc._levelIndex = 80;
enc._manual = true;
enc.clearMonsters();
for (int i = 0; i < monsterCount; ++i)
enc.addMonster(MONSTER_ID1[i], MONSTER_ID2[i]);
enc.execute();
}
void Map03::special18() {
showSign(STRING["maps.map03.temple"]);
}
void Map03::special20() {
showSign(STRING["maps.map03.tavern"]);
}
void Map03::special21() {
showSign(STRING["maps.map03.training"]);
}
void Map03::special27() {
showSign(STRING["maps.map03.forbidden_crypt"]);
}
void Map03::special28() {
showSign(STRING["maps.map03.eternal_rest"]);
}
} // namespace Maps
} // namespace MM1
} // namespace MM

100
engines/mm/mm1/maps/map03.h Normal file
View File

@@ -0,0 +1,100 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_MAPS_MAP03_H
#define MM1_MAPS_MAP03_H
#include "mm/mm1/maps/map_town.h"
namespace MM {
namespace MM1 {
namespace Maps {
class Map03 : public MapTown {
typedef void (Map03:: *SpecialFn)();
private:
void special00();
void special01();
void special02();
void special03();
void special04();
void special05();
void special06();
void special07();
void special08();
void special09();
void special10();
void special11();
void special12();
void special13();
void special14();
void special15();
void special18();
void special20();
void special21();
void special27();
void special28();
const SpecialFn SPECIAL_FN[29] = {
&Map03::special00,
&Map03::special01,
&Map03::special02,
&Map03::special03,
&Map03::special04,
&Map03::special05,
&Map03::special06,
&Map03::special07,
&Map03::special08,
&Map03::special09,
&Map03::special10,
&Map03::special11,
&Map03::special12,
&Map03::special13,
&Map03::special14,
&Map03::special15,
&Map03::special15,
&Map03::special15,
&Map03::special18,
&Map03::special15,
&Map03::special20,
&Map03::special21,
&Map03::special15,
&Map03::special15,
&Map03::special15,
&Map03::special15,
&Map03::special15,
&Map03::special27,
&Map03::special28
};
public:
Map03() : MapTown(3, "dusk", 0x802, 1) {}
/**
* Handles all special stuff that happens on the map
*/
void special() override;
};
} // namespace Maps
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,200 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/maps/map04.h"
#include "mm/mm1/maps/maps.h"
#include "mm/mm1/events.h"
#include "mm/mm1/globals.h"
#include "mm/mm1/sound.h"
namespace MM {
namespace MM1 {
namespace Maps {
void Map04::special() {
// Scan for special actions on the map cell
for (uint i = 0; i < 22; ++i) {
if (g_maps->_mapOffset == _data[51 + i]) {
// Found a specially handled cell, but it
// only triggers in designated direction(s)
if (g_maps->_forwardMask & _data[73 + i]) {
(this->*SPECIAL_FN[i])();
} else {
checkPartyDead();
}
return;
}
}
// Stealable treasure
send(SoundMessage(
STRING["maps.map04.treasure"],
[]() {
g_maps->clearSpecial();
if ((*g_maps->_currentMap)[MAP04_TREASURE_STOLEN] < 255)
(*g_maps->_currentMap)[MAP04_TREASURE_STOLEN]++;
g_globals->_treasure.setGold(600);
g_globals->_treasure.setGems(10);
g_events->addAction(KEYBIND_SEARCH);
}
));
}
void Map04::special00() {
inn();
}
void Map04::special01() {
bool hasCourier = false;
for (uint i = 0; i < g_globals->_party.size() && !hasCourier; ++i) {
hasCourier = (g_globals->_party[i]._flags[0] & CHARFLAG0_COURIER1) != 0;
}
bool hasScroll = g_globals->_party.hasItem(VELLUM_SCROLL_ID);
auto callback = [](const Common::KeyState &) {
g_events->close();
g_maps->_mapPos.y = 6;
g_maps->_currentMap->updateGame();
};
if (hasCourier && hasScroll) {
for (uint i = 0; i < g_globals->_party.size(); ++i) {
Character &c = g_globals->_party[i];
g_globals->_currCharacter = &c;
c._exp += 1000;
c._flags[0] &= (c._flags[0] & CHARFLAG0_COURIER1) | CHARFLAG0_COURIER2;
}
send(InfoMessage(
0, 0, STRING["maps.map04.agar1"],
0, 1, STRING["maps.map04.agar2"],
callback
));
} else {
send(InfoMessage(
0, 0, STRING["maps.map04.agar1"],
0, 1, STRING["maps.map04.agar3"],
callback
));
}
}
void Map04::special02() {
blacksmith();
}
void Map04::special03() {
market();
}
void Map04::special04() {
visitedExit();
if (_data[MAP04_PASSAGE_OVERRIDE] || _data[MAP04_TREASURE_STOLEN] == 0) {
send(SoundMessage(
STRING["maps.passage_outside2"],
[]() {
g_maps->_mapPos.x = 13;
g_maps->_mapPos.y = 1;
g_maps->changeMap(0xa00, 2);
}
));
} else {
// Sucks to be you
_data[MAP04_PASSAGE_OVERRIDE]++;
g_events->addView("Arrested");
}
}
void Map04::special05() {
tavern();
}
void Map04::special06() {
temple();
}
void Map04::special07() {
training();
}
void Map04::special08() {
g_maps->_mapPos.x = getRandomNumber(15);
g_maps->_mapPos.y = getRandomNumber(15);
redrawGame();
send(InfoMessage(0, 1, STRING["maps.poof"]));
}
void Map04::special09() {
visitedExit();
if (_data[MAP04_STAIRS_OVERRIDE] || _data[MAP04_TREASURE_STOLEN] == 0) {
send(SoundMessage(
STRING["maps.stairs_down"],
[]() {
g_maps->_mapPos.x = 0;
g_maps->_mapPos.y = 7;
g_maps->changeMap(0x202, 1);
}
));
} else {
// Sucks to be you
_data[MAP04_STAIRS_OVERRIDE]++;
g_events->addView("Arrested");
}
}
void Map04::special10() {
showSign(STRING["maps.map04.market"]);
}
void Map04::special11() {
showSign(STRING["maps.map04.blacksmith"]);
}
void Map04::special12() {
showSign(STRING["maps.map04.inn"]);
}
void Map04::special13() {
if (_data[MAP04_TREASURE_STOLEN]) {
g_maps->clearSpecial();
g_events->addView("Arrested");
} else {
none160();
}
}
void Map04::special18() {
showSign(STRING["maps.map04.temple"]);
}
void Map04::special20() {
showSign(STRING["maps.map04.tavern"]);
}
void Map04::special21() {
showSign(STRING["maps.map04.training"]);
}
} // namespace Maps
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,94 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_MAPS_MAP04_H
#define MM1_MAPS_MAP04_H
#include "mm/mm1/maps/map_town.h"
namespace MM {
namespace MM1 {
namespace Maps {
#define MAP04_PASSAGE_OVERRIDE 169
#define MAP04_STAIRS_OVERRIDE 196
#define MAP04_TREASURE_STOLEN 971
class Map04 : public MapTown {
typedef void (Map04:: *SpecialFn)();
private:
void special00();
void special01();
void special02();
void special03();
void special04();
void special05();
void special06();
void special07();
void special09();
void special10();
void special11();
void special12();
void special13();
void special18();
void special20();
void special21();
const SpecialFn SPECIAL_FN[22] = {
&Map04::special00,
&Map04::special01,
&Map04::special02,
&Map04::special03,
&Map04::special04,
&Map04::special05,
&Map04::special06,
&Map04::special07,
&Map04::special08,
&Map04::special09,
&Map04::special10,
&Map04::special11,
&Map04::special12,
&Map04::special13,
&Map04::special13,
&Map04::special13,
&Map04::special13,
&Map04::special13,
&Map04::special18,
&Map04::special13,
&Map04::special20,
&Map04::special21
};
public:
Map04() : MapTown(4, "erliquin", 0xb1a, 3) {}
/**
* Handles all special stuff that happens on the map
*/
void special() override;
void special08();
};
} // namespace Maps
} // namespace MM1
} // namespace MM
#endif

View File

@@ -0,0 +1,225 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/mm1/maps/map05.h"
#include "mm/mm1/maps/maps.h"
#include "mm/mm1/events.h"
#include "mm/mm1/globals.h"
#include "mm/mm1/sound.h"
namespace MM {
namespace MM1 {
namespace Maps {
#define MONSTER_ID 169
#define VAL1 574
#define VAL2 575
void Map05::special() {
// Scan for special actions on the map cell
for (uint i = 0; i < 21; ++i) {
if (g_maps->_mapOffset == _data[51 + i]) {
// Found a specially handled cell, but it
// only triggers in designated direction(s)
if (g_maps->_forwardMask & _data[72 + i]) {
(this->*SPECIAL_FN[i])();
} else {
checkPartyDead();
}
return;
}
}
// All other cells on the map are encounters
g_maps->clearSpecial();
g_globals->_encounters.execute();
}
void Map05::special00() {
visitedExit();
send(SoundMessage(
STRING["maps.stairs_up"],
[]() {
g_maps->changeMap(0x604, 1);
}
));
}
void Map05::special01() {
encounter(1);
}
void Map05::special02() {
encounter(9);
}
void Map05::special03() {
encounter(14);
}
void Map05::special04() {
encounter(20);
}
void Map05::special05() {
Game::Encounter &enc = g_globals->_encounters;
g_maps->clearSpecial();
enc.clearMonsters();
for (int i = 0; i < 10; ++i)
enc.addMonster(14, 1);
enc._levelIndex = 80;
enc._manual = true;
enc.execute();
}
void Map05::special06() {
if (!g_globals->_party.hasItem(VELLUM_SCROLL_ID)) {
send(SoundMessage(
0, 1, STRING[!hasFlag() ? "maps.map05.man1" : "maps.map05.man2"],
[]() {
Map05 &map = *static_cast<Map05 *>(g_maps->_currentMap);
if (map.addScroll()) {
map.addFlag();
g_events->send(InfoMessage(0, 1, STRING["maps.map05.man3"]));
}
}
));
}
}
void Map05::special07() {
visitedExit();
send(SoundMessage(
STRING["maps.map05.portal"],
[]() {
g_maps->_mapPos.x = 8;
g_maps->_mapPos.y = 8;
g_maps->changeMap(1, 1);
}
));
}
void Map05::special08() {
send(SoundMessage(
2, 1, STRING["maps.sign"],
10, 2, STRING["maps.map05.arena"]
));
}
void Map05::special09() {
send(SoundMessage(
0, 1, STRING["maps.map05.arena_inside"],
[]() {
Map05 &map = *static_cast<Map05 *>(g_maps->_currentMap);
map[MAP_47] = 2;
map[MAP_33] = 6;
map[MAP_MAX_MONSTERS] = 15;
g_globals->_encounters._encounterType = Game::FORCE_SURPRISED;
g_globals->_encounters.execute();
},
[]() {
Map05 &map = *static_cast<Map05 *>(g_maps->_currentMap);
map[MAP_47] = 1;
map[MAP_33] = 4;
map[MAP_MAX_MONSTERS] = 10;
g_globals->_treasure.clear();
}
));
}
void Map05::special10() {
showMessage(STRING["maps.map05.message2"]);
}
void Map05::special11() {
if (g_maps->_forwardMask == DIRMASK_S) {
g_maps->clearSpecial();
g_globals->_encounters.execute();
} else {
showMessage(STRING["maps.map05.message3"]);
}
}
void Map05::special14() {
g_maps->clearSpecial();
if (getRandomNumber(2) == 2)
g_globals->_encounters.execute();
}
void Map05::encounter(int monsterId) {
Game::Encounter &enc = g_globals->_encounters;
_data[MONSTER_ID] = monsterId;
g_maps->clearSpecial();
int monsterCount = getRandomNumber(5);
enc.clearMonsters();
for (int i = 0; i < monsterCount; ++i)
enc.addMonster(monsterId, 1);
enc._levelIndex = 80;
enc._manual = true;
enc.execute();
}
void Map05::showMessage(const Common::String &msg) {
send(SoundMessage(
0, 1, STRING["maps.map05.message1"],
0, 2, msg
));
}
bool Map05::addScroll() {
for (uint i = 0; i < g_globals->_party.size(); ++i) {
Character &c = g_globals->_party[i];
if (!c._backpack.full()) {
// Add item
c._backpack.add(VELLUM_SCROLL_ID, 0);
g_globals->_items.getItem(VELLUM_SCROLL_ID);
return true;
}
}
g_events->send(SoundMessage(8, 2, STRING["maps.map05.backpacks_full"]));
return false;
}
bool Map05::hasFlag() {
for (uint i = 0; i < g_globals->_party.size(); ++i) {
const Character &c = g_globals->_party[i];
if (c._flags[0] & CHARFLAG0_COURIER1)
return true;
}
return false;
}
void Map05::addFlag() {
for (uint i = 0; i < g_globals->_party.size(); ++i) {
Character &c = g_globals->_party[i];
c._flags[0] |= CHARFLAG0_COURIER1;
}
}
} // namespace Maps
} // namespace MM1
} // namespace MM

View File

@@ -0,0 +1,89 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MM1_MAPS_MAP05_H
#define MM1_MAPS_MAP05_H
#include "mm/mm1/maps/map.h"
namespace MM {
namespace MM1 {
namespace Maps {
class Map05 : public Map {
typedef void (Map05:: *SpecialFn)();
private:
void special00();
void special01();
void special02();
void special03();
void special04();
void special05();
void special06();
void special07();
void special08();
void special09();
void special10();
void special11();
void special14();
void encounter(int monsterId);
void showMessage(const Common::String &msg);
static bool addScroll();
static bool hasFlag();
static void addFlag();
const SpecialFn SPECIAL_FN[21] = {
&Map05::special00,
&Map05::special01,
&Map05::special02,
&Map05::special03,
&Map05::special04,
&Map05::special05,
&Map05::special06,
&Map05::special07,
&Map05::special08,
&Map05::special09,
&Map05::special10,
&Map05::special11,
&Map05::special11,
&Map05::special11,
&Map05::special14,
&Map05::special14,
&Map05::special14,
&Map05::special14,
&Map05::special14,
&Map05::special14,
&Map05::special08
};
public:
Map05() : Map(5, "cave1", 0xa11, 1) {}
/**
* Handles all special stuff that happens on the map
*/
void special() override;
};
} // namespace Maps
} // namespace MM1
} // namespace MM
#endif

Some files were not shown because too many files have changed in this diff Show More