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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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