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,126 @@
/* 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 "ultima/ultima4/core/config.h"
#include "ultima/ultima4/core/settings.h"
namespace Ultima {
namespace Ultima4 {
Config *Config::_instance;
Config::Config() {
_instance = this;
if (!_doc.readConfigFile("data/conf/config.xml"))
error("Failed to read core configuration");
}
Config::~Config() {
_instance = nullptr;
}
ConfigElement Config::getElement(const Common::String &name) const {
Common::String key = Common::String::format("config/%s", name.c_str());
const Shared::XMLNode *node = _doc.getNode(key);
assert(node);
return ConfigElement(node);
}
Std::vector<Common::String> Config::getGames() {
Std::vector<Common::String> result;
result.push_back("Ultima IV");
return result;
}
void Config::setGame(const Common::String &name) {
// No implementation
}
/*-------------------------------------------------------------------*/
ConfigElement::ConfigElement(const Shared::XMLNode *xmlNode) :
_node(xmlNode), _name(xmlNode->id().c_str()) {
}
ConfigElement::ConfigElement(const ConfigElement &e) : _node(e._node), _name(e._name) {
}
ConfigElement::ConfigElement() : _node(nullptr) {
}
ConfigElement::~ConfigElement() {
}
ConfigElement &ConfigElement::operator=(const ConfigElement &e) {
if (&e != this) {
_node = e._node;
_name = e._name;
}
return *this;
}
bool ConfigElement::exists(const Common::String &name) const {
return !(*_node)[name].empty();
}
Common::String ConfigElement::getString(const Common::String &name) const {
return (*_node)[name];
}
int ConfigElement::getInt(const Common::String &name, int defaultValue) const {
Common::String str = (*_node)[name];
return str.empty() ? defaultValue : atol(str.c_str());
}
bool ConfigElement::getBool(const Common::String &name) const {
Common::String str = (*_node)[name];
if (str.empty())
return false;
return toupper(str[0]) == 'T' || str == "1";
}
int ConfigElement::getEnum(const Common::String &name, const char *const enumValues[]) const {
Common::String str = (*_node)[name];
if (str.empty())
return 0;
for (int i = 0; enumValues[i]; ++i) {
if (str.equalsIgnoreCase(enumValues[i]))
return i;
}
error("invalid enum value for %s: %s", name.c_str(), str.c_str());
}
Std::vector<ConfigElement> ConfigElement::getChildren() const {
const Common::Array<Shared::XMLNode *> &children = _node->children();
Std::vector<ConfigElement> result;
for (const auto &c : children)
result.push_back(c);
return result;
}
} // End of namespace Ultima4
} // End of namespace Ultima

View File

@@ -0,0 +1,159 @@
/* 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 ULTIMA4_CORE_CONFIG_H
#define ULTIMA4_CORE_CONFIG_H
#include "ultima/shared/conf/xml_tree.h"
#include "ultima/shared/conf/xml_node.h"
#include "ultima/shared/std/containers.h"
namespace Ultima {
namespace Ultima4 {
/* info for loading city data from *.ult and *.tlk */
#define CITY_HEIGHT 32
#define CITY_WIDTH 32
#define CITY_MAX_PERSONS 32
/* info for loading area data from *.con */
#define CON_HEIGHT 11
#define CON_WIDTH 11
/* info for loading dungeon map data from *.dng */
#define DNG_HEIGHT 8
#define DNG_WIDTH 8
/* info for loading image data from shapes.ega */
#define N_TILES 256
#define TILE_WIDTH (2 * CHAR_WIDTH)
#define TILE_HEIGHT (2 * CHAR_HEIGHT)
/* info for loading image data from charset.ega */
#define CHAR_WIDTH 8
#define CHAR_HEIGHT 8
/* some character defines */
#define CHARSET_ANKH '\0'
#define CHARSET_REDDOT '\01'
#define CHARSET_SDOOR '\02'
#define CHARSET_WALL '\03'
#define CHARSET_LADDER_UPDOWN '\04'
#define CHARSET_LADDER_DOWN '\05'
#define CHARSET_LADDER_UP '\06'
#define CHARSET_BULLET '\010'
#define CHARSET_COPYRIGHT '\011'
#define CHARSET_REGISTERED '\012'
#define CHARSET_MALE '\013'
#define CHARSET_FEMALE '\014'
#define CHARSET_HORIZBAR '\015'
#define CHARSET_ROOM '\016'
#define CHARSET_ORB '\017'
#define CHARSET_PROMPT '\020'
#define CHARSET_FLOOR '\022'
/* map viewport size (in tiles) */
#define VIEWPORT_W 11
#define VIEWPORT_H 11
/* screen border size (in pixels) */
#define BORDER_WIDTH 8
#define BORDER_HEIGHT 8
/* text area (in character units) */
#define TEXT_AREA_X 24
#define TEXT_AREA_Y 12
#define TEXT_AREA_W 16
#define TEXT_AREA_H 12
/* moons/moongates */
#define MOON_PHASES 24
#define MOON_SECONDS_PER_PHASE 4
#define MOON_CHAR 20
/* wind */
#define WIND_AREA_X 7
#define WIND_AREA_Y 23
#define WIND_AREA_W 10
#define WIND_AREA_H 1
#define WIND_SECONDS_PER_PHASE 1
class ConfigElement;
/**
* Singleton class that manages the XML configuration tree.
*/
class Config {
private:
static Config *_instance;
Shared::XMLTree _doc;
public:
static const Config *getInstance() {
return _instance;
}
public:
Config();
~Config();
ConfigElement getElement(const Common::String &name) const;
static Std::vector<Common::String> getGames();
static void setGame(const Common::String &name);
};
/**
* A single configuration element in the config tree. Right now, a
* thin wrapper around the XML DOM element.
*/
class ConfigElement {
private:
const Shared::XMLNode *_node;
Common::String _name;
public:
ConfigElement(const Shared::XMLNode *xmlNode);
ConfigElement(const ConfigElement &e);
ConfigElement();
~ConfigElement();
ConfigElement &operator=(const ConfigElement &e);
const Common::String getName() const {
return _name;
}
bool exists(const Common::String &name) const;
Common::String getString(const Common::String &name) const;
int getInt(const Common::String &name, int defaultValue = 0) const;
bool getBool(const Common::String &name) const;
int getEnum(const Common::String &name, const char *const enumValues[]) const;
Std::vector<ConfigElement> getChildren() const;
const Shared::XMLNode *getNode() const {
return _node;
}
};
} // End of namespace Ultima4
} // End of namespace Ultima
#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/>.
*
*/
#ifndef ULTIMA4_CORE_COORDS_H
#define ULTIMA4_CORE_COORDS_H
namespace Ultima {
namespace Ultima4 {
/**
* A simple representation of a point in 3D space.
*/
class Coords {
public:
int x, y, z;
Coords(int initx = 0, int inity = 0, int initz = 0) : x(initx), y(inity), z(initz) {}
bool operator==(const Coords &a) const {
return x == a.x && y == a.y && z == a.z;
}
bool operator!=(const Coords &a) const {
return !operator==(a);
}
};
} // End of namespace Ultima4
} // End of namespace Ultima
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,433 @@
/* 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 ULTIMA4_CORE_DEBUGGER_H
#define ULTIMA4_CORE_DEBUGGER_H
#include "ultima/ultima4/core/coords.h"
#include "ultima/ultima4/core/types.h"
#include "ultima/ultima4/core/debugger_actions.h"
#include "gui/debugger.h"
namespace Ultima {
namespace Ultima4 {
/**
* Debugger base class
*/
class Debugger : public GUI::Debugger, public DebuggerActions {
private:
MapTile _horse, _ship, _balloon;
bool _dontEndTurn;
protected:
/**
* Returns true if the debugger is active
*/
bool isDebuggerActive() const override {
return isActive();
}
/**
* Process the given command line.
* Returns true if and only if argv[0] is a known command and was
* handled, false otherwise.
*/
bool handleCommand(int argc, const char **argv, bool &keepRunning) override;
/**
* Prints a message to the console if it's active, or to the
* game screen if not
*/
void print(const char *fmt, ...) override;
/**
* Prints a message to the console if it's active, or to the
* game screen if not, with no newline
*/
void printN(const char *fmt, ...) override;
/**
* Prompts for input, but only if debugger isn't running
*/
void prompt() override;
/**
* Gets the direction for an action
*/
Direction getDirection(int argc, const char **argv);
/**
* Used by methods so that when they're triggered by a keybinding
* action, stops the turn from being finished when they're done
*/
void dontEndTurn() {
_dontEndTurn = true;
}
private:
/**
* Move the avatar in a given direction
*/
bool cmdMove(int argc, const char **argv);
/**
* Attack
*/
bool cmdAttack(int argc, const char **argv);
/**
* Board transport
*/
bool cmdBoard(int argc, const char **argv);
/**
* Cast spell
*/
bool cmdCastSpell(int argc, const char **argv);
/**
* Climb
*/
bool cmdClimb(int argc, const char **argv);
/**
* Descend
*/
bool cmdDescend(int argc, const char **argv);
/**
* Enter location
*/
bool cmdEnter(int argc, const char **argv);
/**
* Exit
*/
bool cmdExit(int argc, const char **argv);
/**
* Fire
*/
bool cmdFire(int argc, const char **argv);
/**
* Get chest
*/
bool cmdGetChest(int argc, const char **argv);
/**
* Hole Up & Camp
*/
bool cmdCamp(int argc, const char **argv);
/**
* Ignite Torch
*/
bool cmdIgnite(int argc, const char **argv);
/**
* Generic interaction
*/
bool cmdInteract(int argc, const char **argv);
/**
* Jimmy lock
*/
bool cmdJimmy(int argc, const char **argv);
/**
* Locate position
*/
bool cmdLocate(int argc, const char **argv);
/**
* Mix reagents
*/
bool cmdMixReagents(int argc, const char **argv);
/**
* Exchanges the position of two players in the party. Prompts the
* user for the player numbers.
*/
bool cmdNewOrder(int argc, const char **argv);
/**
* Open door
*/
bool cmdOpenDoor(int argc, const char **argv);
/**
* Specifies a particular party number
*/
bool cmdParty(int argc, const char **argv);
/**
* Pass turn
*/
bool cmdPass(int argc, const char **argv);
/**
* Peer
*/
bool cmdPeer(int argc, const char **argv);
/**
* Save and quit
*/
bool cmdQuitAndSave(int argc, const char **argv);
/**
* Readies a weapon for a player. Prompts for the player and/or the
* weapon if not provided.
*/
bool cmdReadyWeapon(int argc, const char **argv);
/**
* Search
*/
bool cmdSearch(int argc, const char **argv);
/**
* Speed up, down, or normal
*/
bool cmdSpeed(int argc, const char **argv);
/**
* Combat speed up, down, or normal
*/
bool cmdCombatSpeed(int argc, const char **argv);
/**
* Show character stats
*/
bool cmdStats(int argc, const char **argv);
/**
* Talk
*/
bool cmdTalk(int argc, const char **argv);
/**
* Use
*/
bool cmdUse(int argc, const char **argv);
/**
* Changes a player's armor
*/
bool cmdWearArmor(int argc, const char **argv);
/**
* Yell
*/
bool cmdYell(int argc, const char **argv);
private:
/**
* Collision detection on/off
*/
bool cmd3d(int argc, const char **argv);
/**
* Teleports to the Abyss final altar
*/
bool cmdAbyss(int argc, const char **argv);
/**
* Collision detection on/off
*/
bool cmdCollisions(int argc, const char **argv);
/**
* Have all the companions join the party
*/
bool cmdCompanions(int argc, const char **argv);
/**
* Toggle whether combat occurs
*/
bool cmdCombat(int argc, const char **argv);
/**
* Destroy an object
*/
bool cmdDestroy(int argc, const char **argv);
/**
* Destroy all creatures
*/
bool cmdDestroyCreatures(int argc, const char **argv);
/**
* Jumps to a given dungeon
*/
bool cmdDungeon(int argc, const char **argv);
/**
* Flee from combat
*/
bool cmdFlee(int argc, const char **argv);
/**
* All equipement
*/
bool cmdEquipment(int argc, const char **argv);
/**
* Full stats
*/
bool cmdFullStats(int argc, const char **argv);
/**
* Toggle hunger on or off
*/
bool cmdHunger(int argc, const char **argv);
/**
* Moongate teleportation
*/
bool cmdGate(int argc, const char **argv);
/**
* Go to any specified location by name
*/
bool cmdGoto(int argc, const char **argv);
/**
* Help.. sends the party to Lord British
*/
bool cmdLorddBritish(int argc, const char **argv);
/**
* Grant karma
*/
bool cmdKarma(int argc, const char **argv);
/**
* Give all the items
*/
bool cmdItems(int argc, const char **argv);
/**
* Leave the current location
*/
bool cmdLeave(int argc, const char **argv);
/**
* Displays the current location
*/
bool cmdLocation(int argc, const char **argv);
/**
* Give all the mixtures
*/
bool cmdMixtures(int argc, const char **argv);
/**
* Moon phase
*/
bool cmdMoon(int argc, const char **argv);
/**
* Toggle opacity
*/
bool cmdOpacity(int argc, const char **argv);
/**
* Toggle overhead view
*/
bool cmdOverhead(int argc, const char **argv);
/**
* Give all the reagents
*/
bool cmdReagents(int argc, const char **argv);
/**
* Summons a creature to fight
*/
bool cmdSummon(int argc, const char **argv);
/**
* Returns the torch duration
*/
bool cmdTorch(int argc, const char **argv);
/**
* Creates a given transport
*/
bool cmdTransport(int argc, const char **argv);
/**
* Move up a floor
*/
bool cmdUp(int argc, const char **argv);
/**
* Move down a floor
*/
bool cmdDown(int argc, const char **argv);
/**
* Gives full virtue, or increments a specific virtue
*/
bool cmdVirtue(int argc, const char **argv);
/**
* Set wind direction or locks the direction
*/
bool cmdWind(int argc, const char **argv);
/**
* Lists the triggers in a dungeon room
*/
bool cmdListTriggers(int argc, const char **argv);
public:
bool _collisionOverride;
bool _disableHunger;
bool _disableCombat;
public:
Debugger();
~Debugger() override;
/**
* Gets a chest.
* If the default -2 is used, it bypasses prompting for a
* user. Otherwise, a non-negative player number is expected
*/
void getChest(int player = -2);
/**
* Executes the given command
*/
void executeCommand(const Common::String &cmd);
/**
* Executes the given command
*/
void executeCommand(int argc, const char **argv);
};
extern Debugger *g_debugger;
} // End of namespace Ultima4
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,501 @@
/* 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 "ultima/ultima4/core/debugger_actions.h"
#include "ultima/ultima4/core/config.h"
#include "ultima/ultima4/core/utils.h"
#include "ultima/ultima4/controllers/combat_controller.h"
#include "ultima/ultima4/controllers/read_choice_controller.h"
#include "ultima/ultima4/controllers/read_int_controller.h"
#include "ultima/ultima4/controllers/reagents_menu_controller.h"
#include "ultima/ultima4/conversation/conversation.h"
#include "ultima/ultima4/game/context.h"
#include "ultima/ultima4/game/player.h"
#include "ultima/ultima4/views/stats.h"
#include "ultima/ultima4/gfx/screen.h"
#include "ultima/ultima4/gfx/textcolor.h"
#include "ultima/ultima4/map/annotation.h"
#include "ultima/ultima4/map/city.h"
namespace Ultima {
namespace Ultima4 {
void DebuggerActions::summonCreature(const Common::String &name) {
const Creature *m = nullptr;
Common::String creatureName = name;
creatureName.trim();
if (creatureName.empty()) {
print("\n");
return;
}
/* find the creature by its id and spawn it */
uint id = atoi(creatureName.c_str());
if (id > 0)
m = creatureMgr->getById(id);
if (!m)
m = creatureMgr->getByName(creatureName);
if (m) {
if (gameSpawnCreature(m))
print("\n%s summoned!\n", m->getName().c_str());
else
print("\n\nNo place to put %s!\n\n", m->getName().c_str());
return;
}
print("\n%s not found\n", creatureName.c_str());
}
Direction DebuggerActions::directionFromName(const Common::String &dirStr) {
Common::String dir = dirStr;
dir.toLowercase();
if (dir == "up" || dir == "north")
return DIR_NORTH;
else if (dir == "down" || dir == "south")
return DIR_SOUTH;
else if (dir == "right" || dir == "east")
return DIR_EAST;
else if (dir == "left" || dir == "west")
return DIR_WEST;
return DIR_NONE;
}
bool DebuggerActions::destroyAt(const Coords &coords) {
Object *obj = g_context->_location->_map->objectAt(coords);
if (obj) {
if (isCreature(obj)) {
Creature *c = dynamic_cast<Creature *>(obj);
assert(c);
g_screen->screenMessage("%s Destroyed!\n", c->getName().c_str());
} else {
Tile *t = g_context->_location->_map->_tileSet->get(obj->getTile()._id);
g_screen->screenMessage("%s Destroyed!\n", t->getName().c_str());
}
g_context->_location->_map->removeObject(obj);
g_screen->screenPrompt();
return true;
}
return false;
}
bool DebuggerActions::getChestTrapHandler(int player) {
TileEffect trapType;
int randNum = xu4_random(4);
/* Do we use u4dos's way of trap-determination, or the original intended way? */
int passTest = (settings._enhancements && settings._enhancementsOptions._c64ChestTraps) ?
(xu4_random(2) == 0) : /* xu4-enhanced */
((randNum & 1) == 0); /* u4dos original way (only allows even numbers through, so only acid and poison show) */
/* Chest is trapped! 50/50 chance */
if (passTest) {
/* Figure out which trap the chest has */
switch (randNum & xu4_random(4)) {
case 0:
trapType = EFFECT_FIRE;
break; /* acid trap (56% chance - 9/16) */
case 1:
trapType = EFFECT_SLEEP;
break; /* sleep trap (19% chance - 3/16) */
case 2:
trapType = EFFECT_POISON;
break; /* poison trap (19% chance - 3/16) */
case 3:
trapType = EFFECT_LAVA;
break; /* bomb trap (6% chance - 1/16) */
default:
trapType = EFFECT_FIRE;
break;
}
/* apply the effects from the trap */
if (trapType == EFFECT_FIRE)
g_screen->screenMessage("%cAcid%c Trap!\n", FG_RED, FG_WHITE);
else if (trapType == EFFECT_POISON)
g_screen->screenMessage("%cPoison%c Trap!\n", FG_GREEN, FG_WHITE);
else if (trapType == EFFECT_SLEEP)
g_screen->screenMessage("%cSleep%c Trap!\n", FG_PURPLE, FG_WHITE);
else if (trapType == EFFECT_LAVA)
g_screen->screenMessage("%cBomb%c Trap!\n", FG_RED, FG_WHITE);
// player is < 0 during the 'O'pen spell (immune to traps)
//
// if the chest was opened by a PC, see if the trap was
// evaded by testing the PC's dex
//
if ((player >= 0) &&
(g_ultima->_saveGame->_players[player]._dex + 25 < xu4_random(100))) {
if (trapType == EFFECT_LAVA) /* bomb trap */
g_context->_party->applyEffect(trapType);
else
g_context->_party->member(player)->applyEffect(trapType);
} else {
g_screen->screenMessage("Evaded!\n");
}
return true;
}
return false;
}
bool DebuggerActions::jimmyAt(const Coords &coords) {
MapTile *tile = g_context->_location->_map->tileAt(coords, WITH_OBJECTS);
if (!tile->getTileType()->isLockedDoor())
return false;
if (g_ultima->_saveGame->_keys) {
Tile *door = g_context->_location->_map->_tileSet->getByName("door");
assertMsg(door, "no door tile found in tileset");
g_ultima->_saveGame->_keys--;
g_context->_location->_map->_annotations->add(coords, door->getId());
g_screen->screenMessage("\nUnlocked!\n");
} else
g_screen->screenMessage("%cNo keys left!%c\n", FG_GREY, FG_WHITE);
return true;
}
bool DebuggerActions::mixReagentsForSpellU4(int spell) {
Ingredients ingredients;
g_screen->screenMessage("Reagent: ");
while (1) {
int choice = ReadChoiceController::get("abcdefgh\n\r \033");
// done selecting reagents? mix it up and prompt to mix
// another spell
if (choice == '\n' || choice == '\r' || choice == ' ') {
g_screen->screenMessage("\n\nYou mix the Reagents, and...\n");
if (g_spells->spellMix(spell, &ingredients))
g_screen->screenMessage("Success!\n\n");
else
g_screen->screenMessage("It Fizzles!\n\n");
return false;
}
// escape: put ingredients back and quit mixing
if (choice == -1 || choice == '\033') {
ingredients.revert();
return true;
}
g_screen->screenMessage("\n");
if (!ingredients.addReagent((Reagent)(choice - 'a')))
g_screen->screenMessage("%cNone Left!%c\n", FG_GREY, FG_WHITE);
g_screen->screenMessage("Reagent: ");
}
return true;
}
bool DebuggerActions::mixReagentsForSpellU5(int spell) {
Ingredients ingredients;
g_screen->screenDisableCursor();
g_context->_stats->getReagentsMenu()->reset(); // reset the menu, highlighting the first item
ReagentsMenuController getReagentsController(g_context->_stats->getReagentsMenu(), &ingredients, g_context->_stats->getMainArea());
eventHandler->pushController(&getReagentsController);
getReagentsController.waitFor();
g_context->_stats->getMainArea()->disableCursor();
g_screen->screenEnableCursor();
printN("How many? ");
int howmany = ReadIntController::get(2, TEXT_AREA_X + g_context->_col, TEXT_AREA_Y + g_context->_line);
gameSpellMixHowMany(spell, howmany, &ingredients);
return true;
}
bool DebuggerActions::gameSpellMixHowMany(int spell, int num, Ingredients *ingredients) {
int i;
// Entered 0 mixtures, don't mix anything!
if (num == 0) {
print("\nNone mixed!");
ingredients->revert();
return false;
}
// If they ask for more than will give them 99, only use what they need
if (num > 99 - g_ultima->_saveGame->_mixtures[spell]) {
num = 99 - g_ultima->_saveGame->_mixtures[spell];
print("\n%cOnly need %d!%c", FG_GREY, num, FG_WHITE);
}
print("\nMixing %d...", num);
// See if there's enough reagents to make number of mixtures requested
if (!ingredients->checkMultiple(num)) {
print("\n%cYou don't have enough reagents to mix %d spells!%c", FG_GREY, num, FG_WHITE);
ingredients->revert();
return false;
}
print("\nYou mix the Reagents, and...");
if (g_spells->spellMix(spell, ingredients)) {
print("Success!\n");
// Mix the extra spells
ingredients->multiply(num);
for (i = 0; i < num - 1; i++)
g_spells->spellMix(spell, ingredients);
} else {
print("It Fizzles!\n");
}
return true;
}
bool DebuggerActions::openAt(const Coords &coords) {
const Tile *tile = g_context->_location->_map->tileTypeAt(coords, WITH_OBJECTS);
if (!tile->isDoor() &&
!tile->isLockedDoor())
return false;
if (tile->isLockedDoor()) {
g_screen->screenMessage("%cCan't!%c\n", FG_GREY, FG_WHITE);
return true;
}
Tile *floor = g_context->_location->_map->_tileSet->getByName("brick_floor");
assertMsg(floor, "no floor tile found in tileset");
g_context->_location->_map->_annotations->add(coords, floor->getId(), false, true)->setTTL(4);
g_screen->screenMessage("\nOpened!\n");
return true;
}
void DebuggerActions::gameCastSpell(uint spell, int caster, int param) {
SpellCastError spellError;
Common::String msg;
if (!g_spells->spellCast(spell, caster, param, &spellError, true)) {
msg = g_spells->spellGetErrorMessage(spell, spellError);
if (!msg.empty())
g_screen->screenMessage("%s", msg.c_str());
}
}
bool DebuggerActions::talkAt(const Coords &coords) {
extern int personIsVendor(const Person * person);
City *city;
/* can't have any conversations outside of town */
if (!isCity(g_context->_location->_map)) {
g_screen->screenMessage("Funny, no response!\n");
return true;
}
city = dynamic_cast<City *>(g_context->_location->_map);
assert(city);
Person *talker = city ? city->personAt(coords) : nullptr;
// Make sure we have someone we can talk with
if (!talker || !talker->canConverse())
return false;
/* No response from alerted guards... does any monster both
attack and talk besides Nate the Snake? */
if (talker->getMovementBehavior() == MOVEMENT_ATTACK_AVATAR &&
talker->getId() != PYTHON_ID)
return false;
// If we're talking to Lord British and the avatar is dead, LB resurrects them!
if (talker->getNpcType() == NPC_LORD_BRITISH &&
g_context->_party->member(0)->getStatus() == STAT_DEAD) {
g_screen->screenMessage("%s, Thou shalt live again!\n", g_context->_party->member(0)->getName().c_str());
g_context->_party->member(0)->setStatus(STAT_GOOD);
g_context->_party->member(0)->heal(HT_FULLHEAL);
gameSpellEffect('r', -1, SOUND_LBHEAL);
}
Conversation conv;
conv._script->addProvider("party", g_context->_party);
conv._script->addProvider("context", g_context);
conv._state = Conversation::INTRO;
conv._reply = talker->getConversationText(&conv, "");
conv._playerInput.clear();
talkRunConversation(conv, talker, false);
// Ensure the end of the conversation ends the line
if (g_context->_col != 0)
g_screen->screenMessage("\n");
return true;
}
void DebuggerActions::talkRunConversation(Conversation &conv, Person *talker, bool showPrompt) {
while (conv._state != Conversation::DONE) {
// TODO: instead of calculating linesused again, cache the
// result in person.cpp somewhere.
int linesused = linecount(conv._reply.front(), TEXT_AREA_W);
g_screen->screenMessage("%s", conv._reply.front().c_str());
conv._reply.pop_front();
/* if all chunks haven't been shown, wait for a key and process next chunk*/
int size = conv._reply.size();
if (size > 0) {
#ifdef IOS_ULTIMA4
U4IOS::IOSConversationChoiceHelper continueDialog;
continueDialog.updateChoices(" ");
#endif
ReadChoiceController::get("");
continue;
}
/* otherwise, clear current reply and proceed based on conversation state */
conv._reply.clear();
/* they're attacking you! */
if (conv._state == Conversation::ATTACK) {
conv._state = Conversation::DONE;
talker->setMovementBehavior(MOVEMENT_ATTACK_AVATAR);
}
if (conv._state == Conversation::DONE)
break;
/* When Lord British heals the party */
else if (conv._state == Conversation::FULLHEAL) {
int i;
for (i = 0; i < g_context->_party->size(); i++) {
g_context->_party->member(i)->heal(HT_CURE); // cure the party
g_context->_party->member(i)->heal(HT_FULLHEAL); // heal the party
}
gameSpellEffect('r', -1, SOUND_MAGIC); // same spell effect as 'r'esurrect
conv._state = Conversation::TALK;
}
/* When Lord British checks and advances each party member's level */
else if (conv._state == Conversation::ADVANCELEVELS) {
gameLordBritishCheckLevels();
conv._state = Conversation::TALK;
}
if (showPrompt) {
Common::String prompt = talker->getPrompt(&conv);
if (!prompt.empty()) {
if (linesused + linecount(prompt, TEXT_AREA_W) > TEXT_AREA_H) {
#ifdef IOS_ULTIMA4
U4IOS::IOSConversationChoiceHelper continueDialog;
continueDialog.updateChoices(" ");
#endif
ReadChoiceController::get("");
}
g_screen->screenMessage("%s", prompt.c_str());
}
}
int maxlen;
switch (conv.getInputRequired(&maxlen)) {
case Conversation::INPUT_STRING: {
conv._playerInput = gameGetInput(maxlen);
#ifdef IOS_ULTIMA4
g_screen->screenMessage("%s", conv.playerInput.c_str()); // Since we put this in a different window, we need to show it again.
#endif
conv._reply = talker->getConversationText(&conv, conv._playerInput.c_str());
conv._playerInput.clear();
showPrompt = true;
break;
}
case Conversation::INPUT_CHARACTER: {
char message[2];
#ifdef IOS_ULTIMA4
U4IOS::IOSConversationChoiceHelper yesNoHelper;
yesNoHelper.updateChoices("yn ");
#endif
int choice = ReadChoiceController::get("");
message[0] = choice;
message[1] = '\0';
conv._reply = talker->getConversationText(&conv, message);
conv._playerInput.clear();
showPrompt = true;
break;
}
case Conversation::INPUT_NONE:
conv._state = Conversation::DONE;
break;
}
}
if (conv._reply.size() > 0)
g_screen->screenMessage("%s", conv._reply.front().c_str());
}
void DebuggerActions::gameLordBritishCheckLevels() {
bool advanced = false;
for (int i = 0; i < g_context->_party->size(); i++) {
PartyMember *player = g_context->_party->member(i);
if (player->getRealLevel() <
player->getMaxLevel())
// add an extra space to separate messages
if (!advanced) {
g_screen->screenMessage("\n");
advanced = true;
}
player->advanceLevel();
}
g_screen->screenMessage("\nWhat would thou\nask of me?\n");
}
bool DebuggerActions::isCombat() const {
return dynamic_cast<CombatController *>(eventHandler->getController()) != nullptr;
}
int DebuggerActions::getCombatFocus() const {
CombatController *cc = dynamic_cast<CombatController *>(eventHandler->getController());
assert(cc);
return cc->getFocus();
}
} // End of namespace Ultima4
} // End of namespace Ultima

View File

@@ -0,0 +1,144 @@
/* 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 ULTIMA4_CORE_DEBUGGER_ACTIONS_H
#define ULTIMA4_CORE_DEBUGGER_ACTIONS_H
#include "ultima/ultima4/core/coords.h"
#include "ultima/ultima4/game/spell.h"
#include "ultima/ultima4/core/types.h"
namespace Ultima {
namespace Ultima4 {
/**
* This is a secondary class inherited by the Debugger class
* that contains various support methods for implementing the
* different actions players can take in the game
*/
class DebuggerActions {
private:
/**
* Executes the current conversation until it is done.
*/
void talkRunConversation(Conversation &conv, Person *talker, bool showPrompt);
/**
* Check the levels of each party member while talking to Lord British
*/
void gameLordBritishCheckLevels();
protected:
/**
* Returns true if the debugger is active
*/
virtual bool isDebuggerActive() const = 0;
/**
* Prints a message to the console if it's active, or to the
* game screen if not
*/
virtual void print(const char *fmt, ...) = 0;
/**
* Prints a message to the console if it's active, or to the
* game screen if not
*/
virtual void printN(const char *fmt, ...) = 0;
/**
* Prompts for input, but only if debugger isn't running
*/
virtual void prompt() = 0;
/**
* Returns true if combat is currently active
*/
bool isCombat() const;
/**
* Returns currently focused character in combat mode
*/
int getCombatFocus() const;
public:
virtual ~DebuggerActions() {}
/**
* Summons a creature given by 'creatureName'. This can either be given
* as the creature's name, or the creature's id. Once it finds the
* creature to be summoned, it calls gameSpawnCreature() to spawn it.
*/
void summonCreature(const Common::String &name);
/**
* Destroy object at a given co-ordinate
*/
bool destroyAt(const Coords &coords);
/**
* Returns a direction from a given string
*/
Direction directionFromName(const Common::String &dirStr);
/**
* Called by getChest() to handle possible traps on chests
**/
bool getChestTrapHandler(int player);
/**
* Attempts to jimmy a locked door at map coordinates x,y. The locked
* door is replaced by a permanent annotation of an unlocked door
* tile.
*/
bool jimmyAt(const Coords &coords);
/**
* Prompts for spell reagents to mix in the traditional Ultima IV
* style.
*/
bool mixReagentsForSpellU4(int spell);
/**
* Prompts for spell reagents to mix with an Ultima V-like menu.
*/
bool mixReagentsForSpellU5(int spell);
bool gameSpellMixHowMany(int spell, int num, Ingredients *ingredients);
/**
* Attempts to open a door at map coordinates x,y. The door is
* replaced by a temporary annotation of a floor tile for 4 turns.
*/
bool openAt(const Coords &coords);
void gameCastSpell(uint spell, int caster, int param);
/**
* Begins a conversation with the NPC at map coordinates x,y. If no
* NPC is present at that point, zero is returned.
*/
bool talkAt(const Coords &coords);
};
} // End of namespace Ultima4
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,83 @@
/* 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 "ultima/ultima4/core/lzw/hash.h"
namespace Ultima {
namespace Ultima4 {
namespace LZW {
int probe1(byte root, int codeword) {
int newHashCode = ((root << 4) ^ codeword) & 0xfff;
return (newHashCode);
}
/* The secondary probe uses some assembler instructions that aren't easily translated to C. */
int probe2(byte root, int codeword) {
/* registers[0] == AX, registers[1] == DX */
long registers[2], temp;
long carry, oldCarry;
int i, j;
/* the pre-mul part */
registers[1] = 0;
registers[0] = ((root << 1) + codeword) | 0x800;
/* the mul part (simulated mul instruction) */
/* DX:AX = AX * AX */
temp = (registers[0] & 0xff) * (registers[0] & 0xff);
temp += 2 * (registers[0] & 0xff) * (registers[0] >> 8) * 0x100;
registers[1] = (temp >> 16) + (registers[0] >> 8) * (registers[0] >> 8);
registers[0] = temp & 0xffff;
/* if DX != 0, the mul instruction sets the carry flag */
if (registers[1] == 00) {
carry = 0;
} else {
carry = 1;
}
/* the rcl part */
for (i = 0; i < 2; i++) { /* 2 rcl's */
for (j = 0; j < 2; j++) { /* rotate through 2 registers */
oldCarry = carry;
carry = (registers[j] >> 15) & 1;
registers[j] = (registers[j] << 1) | oldCarry;
registers[j] = registers[j] & 0xffff; /* make sure register stays 16 bit */
}
}
/* final touches */
registers[0] = ((registers[0] >> 8) | (registers[1] << 8)) & 0xfff;
return ((int)registers[0]);
}
int probe3(int hashCode) {
const long probeOffset = 0x1fd; /* I think 0x1fd is prime */
long newHashCode = (hashCode + probeOffset) & 0xfff;
return ((int)newHashCode);
}
} // End of namespace LZW
} // End of namespace Ultima4
} // End of namespace Ultima

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/>.
*
*/
#ifndef ULTIMA4_CORE_LZW_HASH_H
#define ULTIMA4_CORE_LZW_HASH_H
#include "common/scummsys.h"
namespace Ultima {
namespace Ultima4 {
namespace LZW {
int probe1(byte root, int codeword);
int probe2(byte root, int codeword);
int probe3(int hashCode);
} // End of namespace LZW
} // End of namespace Ultima4
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,315 @@
/* 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/>.
*
*/
/*
* A few files from Ultima 4 (PC version) have been compressed with the LZW algorithm.
* There are two things that make the U4 implementation of the LZW decoding algorithm special:
* 1) It uses a fixed codeword length of 12 bits.
* The advantages over variable-length codewords are faster decompression and simpler code.
* 2) The dictionary is implemented as a hash table.
* While the dictionary is supposed to implemented as a hash table in the LZW *en*coder (to speed up
* string searches), there is no reason not to implement it as a simple array in the decoder.
* But since U4 uses a hash table in the decoder, this C version must do the same (or it won't be
* able to decode the U4 files).
*
* An explanation on LZW data (de)compression can be found here:
* https://web.archive.org/web/20191231131544/https://marknelson.us/posts/1989/10/01/lzw-data-compression.html
* https://web.archive.org/web/20191231131532/https://marknelson.us/posts/2011/11/08/lzw-revisited.html
*/
#include "ultima/ultima4/core/lzw/lzw.h"
#include "ultima/ultima4/core/lzw/hash.h"
namespace Ultima {
namespace Ultima4 {
namespace LZW {
typedef void (*WRITE_DECOMP)(byte root, byte *destination, long *position);
struct lzwDictionaryEntry {
byte root;
int codeword;
byte occupied;
};
long generalizedDecompress(WRITE_DECOMP outFunc, byte *compressedMem, byte *decompressedMem, long compressedSize);
int getNextCodeword(long *bitsRead, byte *compressedMem);
void discardRoot(byte root, byte *destination, long *position);
void outputRoot(byte root, byte *destination, long *position);
void getString(int codeword, lzwDictionaryEntry *lzwDictionary, byte *stack, int *elementsInStack);
int getNewHashCode(byte root, int codeword, lzwDictionaryEntry *dictionary);
byte hashPosFound(int hashCode, byte root, int codeword, lzwDictionaryEntry *dictionary);
/*
* This function returns the decompressed size of a block of compressed data.
* It doesn't decompress the data.
* Use this function if you want to decompress a block of data, but don't know the decompressed size
* in advance.
*
* There is some error checking to detect if the compressed data is corrupt, but it's only rudimentary.
* Returns:
* No errors: (long) decompressed size
* Error: (long) -1
*/
long lzwGetDecompressedSize(byte *compressedMem, long compressedSize) {
return (generalizedDecompress(&discardRoot, compressedMem, nullptr, compressedSize));
}
/*
* Decompresses a block of compressed data from memory to memory.
* Use this function if you already know the decompressed size.
*
* This function assumes that *decompressed_mem is already allocated, and that the decompressed data
* will fit into *decompressed_mem.
* There is some error checking to detect if the compressed data is corrupt, but it's only rudimentary.
* Returns:
* No errors: (long) decompressed size
* Error: (long) -1
*/
long lzwDecompress(byte *compressedMem, byte *decompressedMem, long compressedSize) {
return (generalizedDecompress(&outputRoot, compressedMem, decompressedMem, compressedSize));
}
/* --------------------------------------------------------------------------------------
Functions used only inside lzw.c
-------------------------------------------------------------------------------------- */
/*
* This function does the actual decompression work.
* Parameters:
* perform_decompression: FALSE ==> return decompressed size, but discard decompressed data
* compressed_mem: compressed data
* decompressed_mem: this is where the compressed data will be decompressed to
* compressed_size: size of the compressed data (in bytes)
*/
long generalizedDecompress(WRITE_DECOMP outFunc, byte *compressedMem, byte *decompressedMem, long compressedSize) {
int i;
/* re-initialize the dictionary when there are more than 0xccc entries */
const int maxDictEntries = 0xccc;
const int lzwStackSize = 0x8000;
const int lzwDictionarySize = 0x1000;
int old_code;
int new_code;
byte character;
long bitsRead = 0;
long bytesWritten = 0;
/* newpos: position in the dictionary where new codeword was added */
/* must be equal to current codeword (if it isn't, the compressed data must be corrupt) */
/* unknownCodeword: is the current codeword in the dictionary? */
int newpos;
byte unknownCodeword;
/* initialize the dictionary and the stack */
lzwDictionaryEntry *lzwDictionary = (lzwDictionaryEntry *) malloc(sizeof(lzwDictionaryEntry) * lzwDictionarySize);
int codewordsInDictionary = 0;
byte *lzwStack = (byte *) malloc(sizeof(byte) * lzwStackSize);
int elementsInStack = 0;
/* clear the dictionary */
memset(lzwDictionary, 0, sizeof(lzwDictionaryEntry) * lzwDictionarySize);
for (i = 0; i < 0x100; i++) {
lzwDictionary[i].occupied = 1;
}
if (bitsRead + 12 <= compressedSize * 8) {
/* read OLD_CODE */
old_code = getNextCodeword(&bitsRead, compressedMem);
/* CHARACTER = OLD_CODE */
character = (byte)old_code;
/* output OLD_CODE */
outFunc(character, decompressedMem, &bytesWritten);
while (bitsRead + 12 <= compressedSize * 8) { /* WHILE there are still input characters DO */
/* read NEW_CODE */
new_code = getNextCodeword(&bitsRead, compressedMem);
if (lzwDictionary[new_code].occupied) { /* is the codeword in the dictionary? */
/* codeword is present in the dictionary */
/* it must either be a root or a non-root that has already been added to the dicionary */
unknownCodeword = 0;
/* STRING = get translation of NEW_CODE */
getString(new_code, lzwDictionary, lzwStack, &elementsInStack);
} else {
/* codeword is yet to be defined */
unknownCodeword = 1;
/* STRING = get translation of OLD_CODE */
/* STRING = STRING+CHARACTER */
lzwStack[elementsInStack] = character; /* push character on the stack */
elementsInStack++;
getString(old_code, lzwDictionary, lzwStack, &elementsInStack);
}
/* CHARACTER = first character in STRING */
character = lzwStack[elementsInStack - 1]; /* element at top of stack */
/* output STRING */
while (elementsInStack > 0) {
outFunc(lzwStack[elementsInStack - 1], decompressedMem, &bytesWritten);
elementsInStack--;
}
/* add OLD_CODE + CHARACTER to the translation table */
newpos = getNewHashCode(character, old_code, lzwDictionary);
lzwDictionary[newpos].root = character;
lzwDictionary[newpos].codeword = old_code;
lzwDictionary[newpos].occupied = 1;
codewordsInDictionary++;
/* check for errors */
if (unknownCodeword && (newpos != new_code)) {
/* clean up */
free(lzwStack);
free(lzwDictionary);
return (-1);
}
if (codewordsInDictionary > maxDictEntries) {
/* wipe dictionary */
codewordsInDictionary = 0;
memset(lzwDictionary, 0, sizeof(lzwDictionaryEntry) * lzwDictionarySize);
for (i = 0; i < 0x100; i++) {
lzwDictionary[i].occupied = 1;
}
if (bitsRead + 12 <= compressedSize * 8) {
new_code = getNextCodeword(&bitsRead, compressedMem);
character = (byte)new_code;
outFunc(character, decompressedMem, &bytesWritten);
} else {
/* clean up */
free(lzwStack);
free(lzwDictionary);
return (bytesWritten);
}
}
/* OLD_CODE = NEW_CODE */
old_code = new_code;
}
}
/* clean up */
free(lzwStack);
free(lzwDictionary);
return (bytesWritten);
}
/* read the next 12-bit codeword from the compressed data */
int getNextCodeword(long *bitsRead, byte *compressedMem) {
int codeword = (compressedMem[(*bitsRead) / 8] << 8) + compressedMem[(*bitsRead) / 8 + 1];
codeword = codeword >> (4 - ((*bitsRead) % 8));
codeword = codeword & 0xfff;
(*bitsRead) += 12;
return (codeword);
}
/* increment position pointer, but do not write root to memory */
void discardRoot(byte root, byte *destination, long *position) {
(*position)++;
}
/* output a root to memory */
void outputRoot(byte root, byte *destination, long *position) {
destination[*position] = root;
(*position)++;
}
/* --------------------------------------------------------------------------------------
Dictionary-related functions
-------------------------------------------------------------------------------------- */
/* pushes the string associated with codeword onto the stack */
void getString(int codeword, lzwDictionaryEntry *dictionary, byte *stack, int *elementsInStack) {
byte root;
int currentCodeword = codeword;
while (currentCodeword > 0xff) {
root = dictionary[currentCodeword].root;
currentCodeword = dictionary[currentCodeword].codeword;
stack[*elementsInStack] = root;
(*elementsInStack)++;
}
/* push the root at the leaf */
stack[*elementsInStack] = (byte)currentCodeword;
(*elementsInStack)++;
}
int getNewHashCode(byte root, int codeword, lzwDictionaryEntry *dictionary) {
int hashCode;
/* probe 1 */
hashCode = probe1(root, codeword);
if (hashPosFound(hashCode, root, codeword, dictionary)) {
return (hashCode);
}
/* probe 2 */
hashCode = probe2(root, codeword);
if (hashPosFound(hashCode, root, codeword, dictionary)) {
return (hashCode);
}
/* probe 3 */
do {
hashCode = probe3(hashCode);
} while (! hashPosFound(hashCode, root, codeword, dictionary));
return (hashCode);
}
byte hashPosFound(int hashCode, byte root, int codeword, lzwDictionaryEntry *dictionary) {
if (hashCode > 0xff) {
// hash codes must not be roots
byte c1 = 0, c2 = 0, c3 = 0;
if (dictionary[hashCode].occupied) {
// hash table position is occupied
c1 = 1;
// is our (root,codeword) pair already in the hash table?
c2 = dictionary[hashCode].root == root;
c3 = dictionary[hashCode].codeword == codeword;
} else {
// hash table position is free
c1 = 0;
}
return (!c1) || (c1 && c2 && c3);
} else {
return 0;
}
}
} // End of namespace LZW
} // End of namespace Ultima4
} // End of namespace Ultima

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 ULTIMA4_CORE_LZW_LZW_H
#define ULTIMA4_CORE_LZW_LZW_H
#include "common/scummsys.h"
namespace Ultima {
namespace Ultima4 {
namespace LZW {
long lzwGetDecompressedSize(byte *compressedMem, long compressedSize);
long lzwDecompress(byte *compressedMem, byte *decompressedMem, long compressedSize);
} // End of namespace LZW
} // End of namespace Ultima4
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,155 @@
/* 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 "ultima/ultima4/core/lzw/lzw.h"
#include "ultima/ultima4/core/lzw/u4decode.h"
namespace Ultima {
namespace Ultima4 {
namespace LZW {
/*
* Loads a file, decompresses it (from memory to memory), and writes the decompressed data to another file
* Returns:
* -1 if there was an error
* the decompressed file length, on success
*/
long decompress_u4_file(Common::SeekableReadStream *in, long filesize, void **out) {
byte *compressed_mem, *decompressed_mem;
long compressed_filesize, decompressed_filesize;
long errorCode;
/* size of the compressed input file */
compressed_filesize = filesize;
/* input file should be longer than 0 bytes */
if (compressed_filesize == 0)
return (-1);
/* check if the input file is _not_ a valid LZW-compressed file */
if (!mightBeValidCompressedFile(in))
return (-1);
/* load compressed file into compressed_mem[] */
compressed_mem = (byte *) malloc(compressed_filesize);
in->read(compressed_mem, compressed_filesize);
/*
* determine decompressed file size
* if lzw_get_decompressed_size() can't determine the decompressed size (i.e. the compressed
* data is corrupt), it returns -1
*/
decompressed_filesize = lzwGetDecompressedSize(compressed_mem, compressed_filesize);
if (decompressed_filesize <= 0) {
return (-1);
}
/* decompress file from compressed_mem[] into decompressed_mem[] */
decompressed_mem = (byte *) malloc(decompressed_filesize);
/* testing: clear destination mem */
memset(decompressed_mem, 0, decompressed_filesize);
errorCode = lzwDecompress(compressed_mem, decompressed_mem, compressed_filesize);
free(compressed_mem);
*out = decompressed_mem;
return (errorCode);
}
long decompress_u4_memory(void *in, long inlen, void **out) {
byte *compressed_mem, *decompressed_mem;
long compressed_filesize, decompressed_filesize;
long errorCode;
/* size of the compressed input */
compressed_filesize = inlen;
/* input file should be longer than 0 bytes */
if (compressed_filesize == 0)
return (-1);
compressed_mem = (byte *) in;
/*
* determine decompressed data size
* if lzw_get_decompressed_size() can't determine the decompressed size (i.e. the compressed
* data is corrupt), it returns -1
*/
decompressed_filesize = lzwGetDecompressedSize(compressed_mem, compressed_filesize);
if (decompressed_filesize <= 0) {
return (-1);
}
/* decompress file from compressed_mem[] into decompressed_mem[] */
decompressed_mem = (byte *) malloc(decompressed_filesize);
/* testing: clear destination mem */
memset(decompressed_mem, 0, decompressed_filesize);
errorCode = lzwDecompress(compressed_mem, decompressed_mem, compressed_filesize);
*out = decompressed_mem;
return (errorCode);
}
/*
* Returns the size of a file, and moves the file pointer to the beginning.
* The file must already be open when this function is called.
*/
long getFilesize(Common::SeekableReadStream *input_file) {
return input_file->size();
}
/*
* If the input file is a valid LZW-compressed file, the upper 4 bits of
* the first byte must be 0, because the first codeword is always a root.
*/
byte mightBeValidCompressedFile(Common::SeekableReadStream *input_file) {
byte firstByte;
byte c1, c2, c3; /* booleans */
long input_filesize;
/* check if the input file has a valid size */
/* the compressed file is made up of 12-bit codewords, */
/* so there are either 0 or 4 bits of wasted space */
input_filesize = getFilesize(input_file);
c1 = (input_filesize * 8) % 12 == 0;
c2 = (input_filesize * 8 - 4) % 12 == 0;
// read first byte, and then reset back file pointer
input_file->seek(0);
firstByte = input_file->readByte();
input_file->seek(0);
c3 = (firstByte >> 4) == 0;
// check if upper 4 bits are 0
return ((c1 || c2) && c3);
}
} // End of namespace LZW
} // End of namespace Ultima4
} // End of namespace Ultima

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 ULTIMA4_CORE_LZW_U4DECODE_H
#define ULTIMA4_CORE_LZW_U4DECODE_H
#include "common/stream.h"
namespace Ultima {
namespace Ultima4 {
namespace LZW {
long decompress_u4_file(Common::SeekableReadStream *in, long filesize, void **out);
long getFilesize(Common::SeekableReadStream *input_file);
byte mightBeValidCompressedFile(Common::SeekableReadStream *compressed_file);
long decompress_u4_memory(void *in, long inlen, void **out);
} // End of namespace LZW
} // End of namespace Ultima4
} // End of namespace Ultima
#endif

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 ULTIMA4_CORE_OBSERVABLE_H
#define ULTIMA4_CORE_OBSERVABLE_H
#include "ultima/ultima4/core/observer.h"
#include "ultima/shared/std/containers.h"
namespace Ultima {
namespace Ultima4 {
/**
* Classes can report updates to a list of decoupled Observers by
* extending this class.
*
* The O class parameter should be a pointer to the class of the
* observable itself, so it can be passed in a typesafe manner to the
* observers update method.
*
* The A class can be any additional information to pass to observers.
* Observables that don't need to pass an argument when they update
* observers should use the default "NoArg" class for the second
* template parameter and pass nullptr to notifyObservers.
*/
template <class O, class A = NoArg *>
class Observable {
public:
Observable() : _changed(false) {}
void addObserver(Observer<O, A> *o) {
typename Std::vector< Observer<O, A> *>::iterator i;
i = Common::find(_observers.begin(), _observers.end(), o);
if (i == _observers.end())
_observers.push_back(o);
}
int countObservers() const {
return _observers.size();
}
void deleteObserver(Observer<O, A> *o) {
typename Std::vector< Observer<O, A> *>::iterator i;
i = Common::find(_observers.begin(), _observers.end(), o);
if (i != _observers.end())
_observers.erase(i);
}
void deleteObservers() {
_observers.clear();
}
bool hasChanged() const {
return _changed;
}
void notifyObservers(A arg) {
if (!_changed)
return;
// vector iterators are invalidated if erase is called, so a copy
// is used to prevent problems if the observer removes itself (or
// otherwise changes the observer list)
typename Std::vector< Observer<O, A> *> tmp = _observers;
clearChanged();
for (auto *observer : tmp) {
observer->update(static_cast<O>(this), arg);
}
}
protected:
void clearChanged() {
_changed = false;
}
void setChanged() {
_changed = true;
}
private:
bool _changed;
Std::vector< Observer<O, A> *> _observers;
};
} // End of namespace Ultima4
} // End of namespace Ultima
#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/>.
*
*/
#ifndef ULTIMA4_CORE_OBSERVER_H
#define ULTIMA4_CORE_OBSERVER_H
namespace Ultima {
namespace Ultima4 {
template<class O, class A>
class Observable;
class NoArg;
/**
* This is the interface a class must implement to watch an
* Observable.
*/
template<class O, class A = NoArg *>
class Observer {
public:
virtual void update(O observable, A arg) = 0;
virtual ~Observer() {}
};
/**
* A specialized observer for watching observables that don't use the
* "arg" parameter to update.
*/
template<class O>
class Observer<O, NoArg *> {
public:
virtual void update(O observable, NoArg *arg) {
update(observable);
}
virtual void update(O observable) = 0;
virtual ~Observer() {}
};
/**
* Just an empty marker class to identify observers that take no args
* on update.
*/
class NoArg {
};
} // End of namespace Ultima4
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,190 @@
/* 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 "ultima/ultima4/core/settings.h"
#include "ultima/ultima4/events/event_handler.h"
#include "ultima/ultima4/core/utils.h"
#include "common/file.h"
namespace Ultima {
namespace Ultima4 {
/*
* Initialize static members
*/
Settings *Settings::_instance = nullptr;
bool SettingsEnhancementOptions::operator==(const SettingsEnhancementOptions &s) const {
return _activePlayer == s._activePlayer
&& _u5SpellMixing == s._u5SpellMixing
&& _u5Shrines == s._u5Shrines
&& _u5Combat == s._u5Combat
&& _slimeDivides == s._slimeDivides
&& _gazerSpawnsInsects == s._gazerSpawnsInsects
&& _textColorization == s._textColorization
&& _c64ChestTraps == s._c64ChestTraps
&& _smartEnterKey == s._smartEnterKey
&& _peerShowsObjects == s._peerShowsObjects
&& _u4TileTransparencyHack == s._u4TileTransparencyHack
&& _u4TileTransparencyHackPixelShadowOpacity == s._u4TileTransparencyHackPixelShadowOpacity
&& _u4TrileTransparencyHackShadowBreadth == s._u4TrileTransparencyHackShadowBreadth;
}
/*-------------------------------------------------------------------*/
bool SettingsData::operator==(const SettingsData &s) const {
return _videoType == s._videoType
&& _battleSpeed == s._battleSpeed
&& _campingAlwaysCombat == s._campingAlwaysCombat
&& _campTime == s._campTime
&& _debug == s._debug
&& _enhancements == s._enhancements
&& _enhancementsOptions == s._enhancementsOptions
&& _filterMoveMessages == s._filterMoveMessages
&& _gameCyclesPerSecond == s._gameCyclesPerSecond
&& _screenAnimationFramesPerSecond == s._screenAnimationFramesPerSecond
&& _innAlwaysCombat == s._innAlwaysCombat
&& _innTime == s._innTime
&& _mouseOptions == s._mouseOptions
&& _screenShakes == s._screenShakes
&& _gamma == s._gamma
&& _shakeInterval == s._shakeInterval
&& _shortcutCommands == s._shortcutCommands
&& _shrineTime == s._shrineTime
&& _spellEffectSpeed == s._spellEffectSpeed
&& _validateXml == s._validateXml
&& _volumeFades == s._volumeFades
&& _titleSpeedRandom == s._titleSpeedRandom
&& _titleSpeedOther == s._titleSpeedOther;
}
bool SettingsData::operator!=(const SettingsData &s) const {
return !operator==(s);
}
/*-------------------------------------------------------------------*/
Settings::Settings() {
// Synchronize settings
Shared::ConfSerializer s(false);
synchronize(s);
// Set up various other constants that aren't configurable
_game = "Ultima IV";
_debug = gDebugLevel > 0;
_innAlwaysCombat = 0;
_campingAlwaysCombat = 0;
_screenAnimationFramesPerSecond = DEFAULT_ANIMATION_FRAMES_PER_SECOND;
bool isEnhanced = _videoType != "EGA";
_scale = isEnhanced ? 2 : 1;
_filter = isEnhanced ? "Scale2x" : "point";
_battleDiffs.push_back("Normal");
_battleDiffs.push_back("Hard");
_battleDiffs.push_back("Expert");
_eventTimerGranularity = (1000 / _gameCyclesPerSecond);
}
Settings &Settings::getInstance() {
if (_instance == nullptr)
_instance = new Settings();
return *_instance;
}
void Settings::setData(const SettingsData &data) {
// bitwise copy is safe
*(SettingsData *)this = data;
bool isEnhanced = _videoType != "EGA";
_scale = isEnhanced ? 2 : 1;
_filter = isEnhanced ? "Scale2x" : "point";
}
bool Settings::write() {
Shared::ConfSerializer s(true);
synchronize(s);
setChanged();
notifyObservers(nullptr);
return true;
}
void Settings::synchronize(Shared::ConfSerializer &s) {
// General settings
bool isEnhanced = g_ultima->isEnhanced();
s.syncAsString("video", _videoType, isEnhanced ? "new" : "EGA");
s.syncAsString("gemLayout", _gemLayout, DEFAULT_GEM_LAYOUT);
s.syncAsString("lineOfSight", _lineOfSight, DEFAULT_LINEOFSIGHT);
s.syncAsBool("screenShakes", _screenShakes, DEFAULT_SCREEN_SHAKES);
s.syncAsInt("gamma", _gamma, DEFAULT_GAMMA);
s.syncAsBool("volumeFades", _volumeFades, DEFAULT_VOLUME_FADES);
s.syncAsBool("shortcutCommands", _shortcutCommands, DEFAULT_SHORTCUT_COMMANDS);
s.syncAsBool("filterMoveMessages", _filterMoveMessages, DEFAULT_FILTER_MOVE_MESSAGES);
s.syncAsInt("battlespeed", _battleSpeed, DEFAULT_BATTLE_SPEED);
s.syncAsBool("enhancements", _enhancements, DEFAULT_ENHANCEMENTS);
s.syncAsInt("gameCyclesPerSecond", _gameCyclesPerSecond, DEFAULT_CYCLES_PER_SECOND);
s.syncAsString("battleDiff", _battleDiff, DEFAULT_BATTLE_DIFFICULTY);
s.syncAsBool("validateXml", _validateXml, DEFAULT_VALIDATE_XML);
s.syncAsInt("spellEffectSpeed", _spellEffectSpeed, DEFAULT_SPELL_EFFECT_SPEED);
s.syncAsInt("campTime", _campTime, DEFAULT_CAMP_TIME);
s.syncAsInt("innTime", _innTime, DEFAULT_INN_TIME);
s.syncAsInt("shrineTime", _shrineTime, DEFAULT_SHRINE_TIME);
s.syncAsInt("shakeInterval", _shakeInterval, DEFAULT_SHAKE_INTERVAL);
s.syncAsInt("titleSpeedRandom", _titleSpeedRandom, DEFAULT_TITLE_SPEED_RANDOM);
s.syncAsInt("titleSpeedOther", _titleSpeedOther, DEFAULT_TITLE_SPEED_OTHER);
s.syncAsBool("innAlwaysCombat", _innAlwaysCombat, false);
s.syncAsBool("campingAlwaysCombat", _campingAlwaysCombat, false);
s.syncAsBool("u5spellMixing", _enhancementsOptions._u5SpellMixing, isEnhanced);
// all specific minor enhancements default to "on", any major enhancements default to "off"
// minor enhancement options
s.syncAsBool("activePlayer", _enhancementsOptions._activePlayer, true);
s.syncAsBool("u5shrines", _enhancementsOptions._u5Shrines, true);
s.syncAsBool("slimeDivides", _enhancementsOptions._slimeDivides, true);
s.syncAsBool("gazerSpawnsInsects", _enhancementsOptions._gazerSpawnsInsects, true);
s.syncAsBool("textColorization", _enhancementsOptions._textColorization, false);
s.syncAsBool("c64chestTraps", _enhancementsOptions._c64ChestTraps, true);
s.syncAsBool("smartEnterKey", _enhancementsOptions._smartEnterKey, true);
// major enhancement options
s.syncAsBool("peerShowsObjects", _enhancementsOptions._peerShowsObjects, false);
s.syncAsBool("u5combat", _enhancementsOptions._u5Combat, false);
// graphics enhancements options
s.syncAsBool("renderTileTransparency", _enhancementsOptions._u4TileTransparencyHack, true);
s.syncAsInt("transparentTilePixelShadowOpacity", _enhancementsOptions._u4TileTransparencyHackPixelShadowOpacity, DEFAULT_SHADOW_PIXEL_OPACITY);
s.syncAsInt("transparentTileShadowSize", _enhancementsOptions._u4TrileTransparencyHackShadowBreadth, DEFAULT_SHADOW_PIXEL_SIZE);
// mouse options
s.syncAsBool("mouseEnabled", _mouseOptions._enabled, true);
s.syncAsString("logging", _logging, DEFAULT_LOGGING);
}
const Std::vector<Common::String> &Settings::getBattleDiffs() {
return _battleDiffs;
}
} // End of namespace Ultima4
} // End of namespace Ultima

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/>.
*
*/
#ifndef ULTIMA4_CORE_SETTINGS_H
#define ULTIMA4_CORE_SETTINGS_H
#include "ultima/ultima4/core/observable.h"
#include "ultima/ultima4/core/types.h"
#include "ultima/shared/conf/conf_serializer.h"
#include "common/hash-str.h"
namespace Ultima {
namespace Ultima4 {
#define MIN_SHAKE_INTERVAL 50
#define MAX_BATTLE_SPEED 10
#define MAX_KEY_DELAY 1000
#define MAX_KEY_INTERVAL 100
#define MAX_CYCLES_PER_SECOND 20
#define MAX_SPELL_EFFECT_SPEED 10
#define MAX_CAMP_TIME 10
#define MAX_INN_TIME 10
#define MAX_SHRINE_TIME 20
#define MAX_SHAKE_INTERVAL 200
#define MAX_VOLUME 10
#define DEFAULT_GEM_LAYOUT "Standard"
#define DEFAULT_LINEOFSIGHT "DOS"
#define DEFAULT_SCREEN_SHAKES 1
#define DEFAULT_GAMMA 100
#define DEFAULT_VOLUME_FADES 1
#define DEFAULT_SHORTCUT_COMMANDS 0
#define DEFAULT_KEY_DELAY 500
#define DEFAULT_KEY_INTERVAL 30
#define DEFAULT_FILTER_MOVE_MESSAGES 0
#define DEFAULT_BATTLE_SPEED 5
#define DEFAULT_ENHANCEMENTS 1
#define DEFAULT_CYCLES_PER_SECOND 4
#define DEFAULT_ANIMATION_FRAMES_PER_SECOND 24
#define DEFAULT_DEBUG 0
#define DEFAULT_VALIDATE_XML 1
#define DEFAULT_SPELL_EFFECT_SPEED 10
#define DEFAULT_CAMP_TIME 10
#define DEFAULT_INN_TIME 8
#define DEFAULT_SHRINE_TIME 16
#define DEFAULT_SHAKE_INTERVAL 100
#define DEFAULT_BATTLE_DIFFICULTY "Normal"
#define DEFAULT_LOGGING ""
#define DEFAULT_TITLE_SPEED_RANDOM 150
#define DEFAULT_TITLE_SPEED_OTHER 30
//--Tile transparency stuff
#define DEFAULT_SHADOW_PIXEL_OPACITY 64
#define DEFAULT_SHADOW_PIXEL_SIZE 2
struct SettingsEnhancementOptions {
bool _activePlayer;
bool _u5SpellMixing;
bool _u5Shrines;
bool _u5Combat;
bool _slimeDivides;
bool _gazerSpawnsInsects;
bool _textColorization;
bool _c64ChestTraps;
bool _smartEnterKey;
bool _peerShowsObjects;
bool _u4TileTransparencyHack;
int _u4TileTransparencyHackPixelShadowOpacity;
int _u4TrileTransparencyHackShadowBreadth;
bool operator==(const SettingsEnhancementOptions &s) const;
};
struct MouseOptions {
bool _enabled;
bool operator==(const MouseOptions &s) const {
return _enabled == s._enabled;
}
};
/**
* SettingsData stores all the settings information.
*/
class SettingsData {
public:
bool operator==(const SettingsData &) const;
bool operator!=(const SettingsData &) const;
int _battleSpeed;
bool _campingAlwaysCombat;
int _campTime;
bool _debug;
bool _enhancements;
SettingsEnhancementOptions _enhancementsOptions;
bool _filterMoveMessages;
int _gameCyclesPerSecond;
int _screenAnimationFramesPerSecond;
bool _innAlwaysCombat;
int _innTime;
MouseOptions _mouseOptions;
uint _scale;
bool _screenShakes;
int _gamma;
int _shakeInterval;
bool _shortcutCommands;
int _shrineTime;
int _spellEffectSpeed;
bool _validateXml;
bool _volumeFades;
int _titleSpeedRandom;
int _titleSpeedOther;
int _eventTimerGranularity;
Common::String _filter;
Common::String _gemLayout;
Common::String _lineOfSight;
Common::String _videoType;
Common::String _battleDiff;
Common::String _logging;
Common::String _game;
};
/**
* The settings class is a singleton that holds all the settings
* information. It is dynamically initialized when first accessed.
*/
class Settings : public SettingsData, public Observable<Settings *> {
typedef Common::HashMap<Common::String, Common::String> SettingsMap;
private:
static Settings *_instance;
Std::vector<Common::String> _battleDiffs;
private:
/**
* Default contructor. Settings is a singleton so this is private.
*/
Settings();
/**
* Synchronize settings with ConfMan
*/
void synchronize(Shared::ConfSerializer &s);
public:
/* Methods */
/**
* Return the global instance of settings.
*/
static Settings &getInstance();
void setData(const SettingsData &data);
/**
* Write the settings out into a human readable file. This also
* notifies observers that changes have been committed.
*/
bool write();
const Std::vector<Common::String> &getBattleDiffs();
};
/* the global settings */
#define settings (Settings::getInstance())
} // End of namespace Ultima4
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,83 @@
/* 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 ULTIMA4_CORE_TYPES_H
#define ULTIMA4_CORE_TYPES_H
#include "ultima/ultima4/map/direction.h"
#include "common/scummsys.h"
namespace Ultima {
namespace Ultima4 {
class Tile;
typedef uint TileId;
typedef byte MapId;
enum TileSpeed {
FAST,
SLOW,
VSLOW,
VVSLOW
};
enum TileEffect {
EFFECT_NONE,
EFFECT_FIRE,
EFFECT_SLEEP,
EFFECT_POISON,
EFFECT_POISONFIELD,
EFFECT_ELECTRICITY,
EFFECT_LAVA
};
enum TileAnimationStyle {
ANIM_NONE,
ANIM_SCROLL,
ANIM_CAMPFIRE,
ANIM_CITYFLAG,
ANIM_CASTLEFLAG,
ANIM_SHIPFLAG,
ANIM_LCBFLAG,
ANIM_FRAMES
};
/**
* An Uncopyable has no default copy constructor of operator=. A subclass may derive from
* Uncopyable at any level of visibility, even private, and subclasses will not have a default copy
* constructor or operator=. See also, boost::noncopyable Uncopyable (from the Boost project) and
* Item 6 from Scott Meyers Effective C++.
*/
class Uncopyable {
protected:
Uncopyable() {}
~Uncopyable() {}
private:
Uncopyable(const Uncopyable &);
const Uncopyable &operator=(const Uncopyable &);
};
} // End of namespace Ultima4
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,103 @@
/* 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 "ultima/ultima4/core/utils.h"
namespace Ultima {
namespace Ultima4 {
void assertMsg(bool exp, const char *desc, ...) {
if (!exp) {
va_list args;
va_start(args, desc);
Common::String msg = Common::String::vformat(desc, args);
va_end(args);
error("Assertion failed: %s", msg.c_str());
}
}
void xu4_srandom() {
// srand((uint)time(nullptr));
}
int xu4_random(int upperRange) {
if (upperRange == 0) {
warning("No upper range specified");
return 0;
}
return g_ultima->getRandomNumber(upperRange - 1);
}
Common::String &trim(Common::String &val, const Common::String &chars_to_trim) {
Common::String::iterator i;
if (val.size()) {
size_t pos;
for (i = val.begin(); (i != val.end()) && (pos = chars_to_trim.find(*i)) != Common::String::npos;)
i = val.erase(i);
for (i = val.end() - 1; (i != val.begin()) && (pos = chars_to_trim.find(*i)) != Common::String::npos;)
i = val.erase(i) - 1;
}
return val;
}
Common::String &lowercase(Common::String &val) {
Common::String::iterator i;
for (i = val.begin(); i != val.end(); i++)
*i = tolower(*i);
return val;
}
Common::String &uppercase(Common::String &val) {
Common::String::iterator i;
for (i = val.begin(); i != val.end(); i++)
*i = toupper(*i);
return val;
}
Common::String xu4_to_string(int val) {
char buffer[16];
Common::sprintf_s(buffer, "%d", val);
return buffer;
}
Std::vector<Common::String> split(const Common::String &s, const Common::String &separators) {
Std::vector<Common::String> result;
Common::String current;
for (unsigned i = 0; i < s.size(); i++) {
if (separators.find(s[i]) != Common::String::npos) {
if (current.size() > 0)
result.push_back(current);
current.clear();
} else
current += s[i];
}
if (current.size() > 0)
result.push_back(current);
return result;
}
} // End of namespace Ultima4
} // End of namespace Ultima

View File

@@ -0,0 +1,124 @@
/* 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 ULTIMA4_CORE_UTILS_H
#define ULTIMA4_CORE_UTILS_H
#include "ultima/ultima4/ultima4.h"
#include "ultima/shared/std/containers.h"
#include "common/savefile.h"
#include "common/hash-str.h"
namespace Ultima {
namespace Ultima4 {
extern void assertMsg(bool exp, const char *desc, ...);
/* The AdjustValue functions used to be #define'd macros, but these are
* evil for several reasons, *especially* when they contain multiple
* statements, and have if statements in them. The macros did both.
* See https://isocpp.org/wiki/faq/inline-functions#inline-vs-macros
* for more information.
*/
inline void AdjustValueMax(int &v, int val, int max) {
v += val;
if (v > max) v = max;
}
inline void AdjustValueMin(int &v, int val, int min) {
v += val;
if (v < min) v = min;
}
inline void AdjustValue(int &v, int val, int max, int min) {
v += val;
if (v > max) v = max;
if (v < min) v = min;
}
inline void AdjustValueMax(short &v, int val, int max) {
v += val;
if (v > max) v = max;
}
inline void AdjustValueMin(short &v, int val, int min) {
v += val;
if (v < min) v = min;
}
inline void AdjustValue(short &v, int val, int max, int min) {
v += val;
if (v > max) v = max;
if (v < min) v = min;
}
inline void AdjustValueMax(unsigned short &v, int val, int max) {
v += val;
if (v > max) v = max;
}
inline void AdjustValueMin(unsigned short &v, int val, int min) {
v += val;
if (v < min) v = min;
}
inline void AdjustValue(unsigned short &v, int val, int max, int min) {
v += val;
if (v > max) v = max;
if (v < min) v = min;
}
/**
* Seed the random number generator.
*/
void xu4_srandom();
/**
* Generate a random number between 0 and (upperRange - 1)
*/
int xu4_random(int upperval);
/**
* Trims whitespace from a Common::String
* @param val The Common::String you are trimming
* @param chars_to_trim A list of characters that will be trimmed
*/
Common::String &trim(Common::String &val, const Common::String &chars_to_trim = "\t\013\014 \n\r");
/**
* Converts the Common::String to lowercase
*/
Common::String &lowercase(Common::String &val);
/**
* Converts the Common::String to uppercase
*/
Common::String &uppercase(Common::String &val);
/**
* Converts an integer value to a Common::String
*/
Common::String xu4_to_string(int val);
/**
* Splits a Common::String into substrings, divided by the charactars in
* separators. Multiple adjacent separators are treated as one.
*/
Std::vector<Common::String> split(const Common::String &s, const Common::String &separators);
} // End of namespace Ultima4
} // End of namespace Ultima
#endif