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

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