Initial commit
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
/* 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/controllers/alpha_action_controller.h"
|
||||
#include "ultima/ultima4/events/event_handler.h"
|
||||
#include "ultima/ultima4/gfx/screen.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
bool AlphaActionController::keyPressed(int key) {
|
||||
if (Common::isLower(key))
|
||||
key = toupper(key);
|
||||
|
||||
if (key >= 'A' && key <= toupper(_lastValidLetter)) {
|
||||
_value = key - 'A';
|
||||
doneWaiting();
|
||||
} else {
|
||||
g_screen->screenMessage("\n%s", _prompt.c_str());
|
||||
g_screen->update();
|
||||
return KeyHandler::defaultHandler(key, nullptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AlphaActionController::keybinder(KeybindingAction action) {
|
||||
if (action == KEYBIND_ESCAPE) {
|
||||
g_screen->screenMessage("\n");
|
||||
_value = -1;
|
||||
doneWaiting();
|
||||
}
|
||||
}
|
||||
|
||||
int AlphaActionController::get(char lastValidLetter, const Common::String &prompt, EventHandler *eh) {
|
||||
if (!eh)
|
||||
eh = eventHandler;
|
||||
|
||||
AlphaActionController ctrl(lastValidLetter, prompt);
|
||||
eh->pushController(&ctrl);
|
||||
return ctrl.waitFor();
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
52
engines/ultima/ultima4/controllers/alpha_action_controller.h
Normal file
52
engines/ultima/ultima4/controllers/alpha_action_controller.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* 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_CONTROLLERS_ALPHA_ACTION_CONTROLLER_H
|
||||
#define ULTIMA4_CONTROLLERS_ALPHA_ACTION_CONTROLLER_H
|
||||
|
||||
#include "ultima/ultima4/controllers/controller.h"
|
||||
#include "ultima/ultima4/events/event_handler.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* A controller to handle input for commands requiring a letter
|
||||
* argument in the range 'a' - lastValidLetter.
|
||||
*/
|
||||
class AlphaActionController : public WaitableController<int> {
|
||||
private:
|
||||
char _lastValidLetter;
|
||||
Common::String _prompt;
|
||||
public:
|
||||
AlphaActionController(char letter, const Common::String &p) :
|
||||
WaitableController<int>(-1), _lastValidLetter(letter), _prompt(p) {}
|
||||
|
||||
bool keyPressed(int key) override;
|
||||
void keybinder(KeybindingAction action) override;
|
||||
|
||||
static int get(char lastValidLetter, const Common::String &prompt, EventHandler *eh = nullptr);
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
123
engines/ultima/ultima4/controllers/camp_controller.cpp
Normal file
123
engines/ultima/ultima4/controllers/camp_controller.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
/* 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/controllers/camp_controller.h"
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/map/mapmgr.h"
|
||||
#include "ultima/ultima4/ultima4.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
CampController::CampController() {
|
||||
MapId id;
|
||||
|
||||
/* setup camp (possible, but not for-sure combat situation */
|
||||
if (g_context->_location->_context & CTX_DUNGEON)
|
||||
id = MAP_CAMP_DNG;
|
||||
else
|
||||
id = MAP_CAMP_CON;
|
||||
|
||||
_map = getCombatMap(mapMgr->get(id));
|
||||
g_game->setMap(_map, true, nullptr, this);
|
||||
}
|
||||
|
||||
void CampController::init(Creature *m) {
|
||||
CombatController::init(m);
|
||||
_camping = true;
|
||||
}
|
||||
|
||||
void CampController::begin() {
|
||||
// make sure everyone's asleep
|
||||
for (int i = 0; i < g_context->_party->size(); i++)
|
||||
g_context->_party->member(i)->putToSleep();
|
||||
|
||||
CombatController::begin();
|
||||
|
||||
g_music->camp();
|
||||
|
||||
g_screen->screenMessage("Resting...\n");
|
||||
g_screen->screenDisableCursor();
|
||||
|
||||
EventHandler::wait_msecs(settings._campTime * 1000);
|
||||
|
||||
g_screen->screenEnableCursor();
|
||||
|
||||
/* Is the party ambushed during their rest? */
|
||||
if (settings._campingAlwaysCombat || (xu4_random(8) == 0)) {
|
||||
const Creature *m = creatureMgr->randomAmbushing();
|
||||
|
||||
g_music->playMapMusic();
|
||||
g_screen->screenMessage("Ambushed!\n");
|
||||
|
||||
/* create an ambushing creature (so it leaves a chest) */
|
||||
setCreature(g_context->_location->_prev->_map->addCreature(m, g_context->_location->_prev->_coords));
|
||||
|
||||
/* fill the creature table with creatures and place them */
|
||||
fillCreatureTable(m);
|
||||
placeCreatures();
|
||||
|
||||
/* creatures go first! */
|
||||
finishTurn();
|
||||
} else {
|
||||
/* Wake everyone up! */
|
||||
for (int i = 0; i < g_context->_party->size(); i++)
|
||||
g_context->_party->member(i)->wakeUp();
|
||||
|
||||
/* Make sure we've waited long enough for camping to be effective */
|
||||
bool healed = false;
|
||||
if (((g_ultima->_saveGame->_moves / CAMP_HEAL_INTERVAL) >= 0x10000) ||
|
||||
(((g_ultima->_saveGame->_moves / CAMP_HEAL_INTERVAL) & 0xffff) != g_ultima->_saveGame->_lastCamp))
|
||||
healed = heal();
|
||||
|
||||
g_screen->screenMessage(healed ? "Party Healed!\n" : "No effect.\n");
|
||||
g_ultima->_saveGame->_lastCamp = (g_ultima->_saveGame->_moves / CAMP_HEAL_INTERVAL) & 0xffff;
|
||||
|
||||
eventHandler->popController();
|
||||
g_game->exitToParentMap();
|
||||
g_music->fadeIn(CAMP_FADE_IN_TIME, true);
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void CampController::end(bool adjustKarma) {
|
||||
// wake everyone up!
|
||||
for (int i = 0; i < g_context->_party->size(); i++)
|
||||
g_context->_party->member(i)->wakeUp();
|
||||
CombatController::end(adjustKarma);
|
||||
}
|
||||
|
||||
bool CampController::heal() {
|
||||
// restore each party member to max mp, and restore some hp
|
||||
bool healed = false;
|
||||
for (int i = 0; i < g_context->_party->size(); i++) {
|
||||
PartyMember *m = g_context->_party->member(i);
|
||||
m->setMp(m->getMaxMp());
|
||||
if ((m->getHp() < m->getMaxHp()) && m->heal(HT_CAMPHEAL))
|
||||
healed = true;
|
||||
}
|
||||
|
||||
return healed;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
46
engines/ultima/ultima4/controllers/camp_controller.h
Normal file
46
engines/ultima/ultima4/controllers/camp_controller.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_CONTROLLERS_CAMP_CONTROLLER_H
|
||||
#define ULTIMA4_CONTROLLERS_CAMP_CONTROLLER_H
|
||||
|
||||
#include "ultima/ultima4/controllers/combat_controller.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
#define CAMP_HEAL_INTERVAL 100 /* Number of moves before camping will heal the party */
|
||||
|
||||
class CampController : public CombatController {
|
||||
public:
|
||||
CampController();
|
||||
void init(Creature *m) override;
|
||||
void begin() override;
|
||||
void end(bool adjustKarma) override;
|
||||
|
||||
private:
|
||||
bool heal();
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
1076
engines/ultima/ultima4/controllers/combat_controller.cpp
Normal file
1076
engines/ultima/ultima4/controllers/combat_controller.cpp
Normal file
File diff suppressed because it is too large
Load Diff
286
engines/ultima/ultima4/controllers/combat_controller.h
Normal file
286
engines/ultima/ultima4/controllers/combat_controller.h
Normal file
@@ -0,0 +1,286 @@
|
||||
/* 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_CONTROLLERS_COMBAT_CONTROLLER_H
|
||||
#define ULTIMA4_CONTROLLERS_COMBAT_CONTROLLER_H
|
||||
|
||||
#include "ultima/ultima4/map/direction.h"
|
||||
#include "ultima/ultima4/map/map.h"
|
||||
#include "ultima/ultima4/controllers/controller.h"
|
||||
#include "ultima/ultima4/core/observer.h"
|
||||
#include "ultima/ultima4/core/types.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/game/creature.h"
|
||||
#include "ultima/ultima4/game/game.h"
|
||||
#include "ultima/ultima4/game/object.h"
|
||||
#include "ultima/ultima4/game/player.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
#define AREA_CREATURES 16
|
||||
#define AREA_PLAYERS 8
|
||||
|
||||
class CombatMap;
|
||||
class Creature;
|
||||
class MoveEvent;
|
||||
class Weapon;
|
||||
|
||||
typedef enum {
|
||||
CA_ATTACK,
|
||||
CA_CAST_SLEEP,
|
||||
CA_ADVANCE,
|
||||
CA_RANGED,
|
||||
CA_FLEE,
|
||||
CA_TELEPORT
|
||||
} CombatAction;
|
||||
|
||||
/**
|
||||
* CombatController class
|
||||
*/
|
||||
class CombatController : public Controller, public Observer<Party *, PartyEvent &>, public TurnCompleter {
|
||||
protected:
|
||||
CombatController();
|
||||
public:
|
||||
CombatController(CombatMap *m);
|
||||
CombatController(MapId id);
|
||||
virtual ~CombatController();
|
||||
|
||||
// Accessor Methods
|
||||
bool isCombatController() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a controller is made active
|
||||
*/
|
||||
void setActive() override;
|
||||
|
||||
bool isCamping() const;
|
||||
bool isWinOrLose() const;
|
||||
Direction getExitDir() const;
|
||||
byte getFocus() const;
|
||||
CombatMap *getMap() const;
|
||||
Creature *getCreature() const;
|
||||
PartyMemberVector *getParty();
|
||||
PartyMember *getCurrentPlayer();
|
||||
|
||||
void setExitDir(Direction d);
|
||||
void setCreature(Creature *);
|
||||
void setWinOrLose(bool worl = true);
|
||||
void showCombatMessage(bool show = true);
|
||||
|
||||
// Methods
|
||||
/**
|
||||
* Initializes the combat controller with combat information
|
||||
*/
|
||||
virtual void init(Creature *m);
|
||||
|
||||
/**
|
||||
* Initializes dungeon room combat
|
||||
*/
|
||||
void initDungeonRoom(int room, Direction from);
|
||||
|
||||
/**
|
||||
* Apply tile effects to all creatures depending on what they're standing on
|
||||
*/
|
||||
void applyCreatureTileEffects();
|
||||
|
||||
/**
|
||||
* Begin combat
|
||||
*/
|
||||
virtual void begin();
|
||||
virtual void end(bool adjustKarma);
|
||||
|
||||
/**
|
||||
* Fills the combat creature table with the creatures that the party will be facing.
|
||||
* The creature table only contains *which* creatures will be encountered and
|
||||
* *where* they are placed (by position in the table). Information like
|
||||
* hit points and creature status will be created when the creature is actually placed
|
||||
*/
|
||||
void fillCreatureTable(const Creature *creature);
|
||||
|
||||
/**
|
||||
* Generate the number of creatures in a group.
|
||||
*/
|
||||
int initialNumberOfCreatures(const Creature *creature) const;
|
||||
|
||||
/**
|
||||
* Returns true if the player has won.
|
||||
*/
|
||||
bool isWon() const;
|
||||
|
||||
/**
|
||||
* Returns true if the player has lost.
|
||||
*/
|
||||
bool isLost() const;
|
||||
|
||||
/**
|
||||
* Performs all of the creature's actions
|
||||
*/
|
||||
void moveCreatures();
|
||||
|
||||
/**
|
||||
* Places creatures on the map from the creature table and from the creature_start coords
|
||||
*/
|
||||
void placeCreatures();
|
||||
|
||||
/**
|
||||
* Places the party members on the map
|
||||
*/
|
||||
void placePartyMembers();
|
||||
|
||||
/**
|
||||
* Sets the active player for combat, showing which weapon they're weilding, etc.
|
||||
*/
|
||||
bool setActivePlayer(int player);
|
||||
bool attackHit(Creature *attacker, Creature *defender);
|
||||
virtual void awardLoot();
|
||||
|
||||
// attack functions
|
||||
void attack(Direction dir = DIR_NONE, int distance = 0);
|
||||
bool attackAt(const Coords &coords, PartyMember *attacker, int dir, int range, int distance);
|
||||
bool rangedAttack(const Coords &coords, Creature *attacker);
|
||||
void rangedMiss(const Coords &coords, Creature *attacker);
|
||||
bool returnWeaponToOwner(const Coords &coords, int distance, int dir, const Weapon *weapon);
|
||||
|
||||
/**
|
||||
* Static member functions
|
||||
*/
|
||||
static void attackFlash(const Coords &coords, MapTile tile, int timeFactor);
|
||||
static void attackFlash(const Coords &coords, const Common::String &tilename, int timeFactor);
|
||||
static void doScreenAnimationsWhilePausing(int timeFactor);
|
||||
|
||||
void keybinder(KeybindingAction action) override;
|
||||
|
||||
void finishTurn() override;
|
||||
|
||||
/**
|
||||
* Move a party member during combat and display the appropriate messages
|
||||
*/
|
||||
void movePartyMember(MoveEvent &event);
|
||||
void update(Party *party, PartyEvent &event) override;
|
||||
|
||||
// Properties
|
||||
protected:
|
||||
CombatMap *_map;
|
||||
|
||||
PartyMemberVector _party;
|
||||
byte _focus;
|
||||
|
||||
const Creature *_creatureTable[AREA_CREATURES];
|
||||
Creature *_creature;
|
||||
|
||||
bool _camping;
|
||||
bool _forceStandardEncounterSize;
|
||||
bool _placePartyOnMap;
|
||||
bool _placeCreaturesOnMap;
|
||||
bool _winOrLose;
|
||||
bool _showMessage;
|
||||
Direction _exitDir;
|
||||
|
||||
private:
|
||||
CombatController(const CombatController &);
|
||||
const CombatController &operator=(const CombatController &);
|
||||
|
||||
void init();
|
||||
};
|
||||
|
||||
extern CombatController *g_combat;
|
||||
|
||||
/**
|
||||
* CombatMap class
|
||||
*/
|
||||
class CombatMap : public Map {
|
||||
public:
|
||||
CombatMap();
|
||||
~CombatMap() override {}
|
||||
|
||||
/**
|
||||
* Returns a vector containing all of the creatures on the map
|
||||
*/
|
||||
CreatureVector getCreatures();
|
||||
|
||||
/**
|
||||
* Returns a vector containing all of the party members on the map
|
||||
*/
|
||||
PartyMemberVector getPartyMembers();
|
||||
|
||||
/**
|
||||
* Returns the party member at the given coords, if there is one,
|
||||
* nullptr if otherwise.
|
||||
*/
|
||||
PartyMember *partyMemberAt(Coords coords);
|
||||
|
||||
/**
|
||||
* Returns the creature at the given coords, if there is one,
|
||||
* nullptr if otherwise.
|
||||
*/
|
||||
Creature *creatureAt(Coords coords);
|
||||
|
||||
/**
|
||||
* Returns a valid combat map given the provided information
|
||||
*/
|
||||
static MapId mapForTile(const Tile *ground, const Tile *transport, Object *obj);
|
||||
|
||||
// Getters
|
||||
bool isDungeonRoom() const {
|
||||
return _dungeonRoom;
|
||||
}
|
||||
bool isAltarRoom() const {
|
||||
return _altarRoom != VIRT_NONE;
|
||||
}
|
||||
bool isContextual() const {
|
||||
return _contextual;
|
||||
}
|
||||
BaseVirtue getAltarRoom() const {
|
||||
return _altarRoom;
|
||||
}
|
||||
|
||||
// Setters
|
||||
void setAltarRoom(BaseVirtue ar) {
|
||||
_altarRoom = ar;
|
||||
}
|
||||
void setDungeonRoom(bool d) {
|
||||
_dungeonRoom = d;
|
||||
}
|
||||
void setContextual(bool c) {
|
||||
_contextual = c;
|
||||
}
|
||||
|
||||
// Properties
|
||||
protected:
|
||||
bool _dungeonRoom;
|
||||
BaseVirtue _altarRoom;
|
||||
bool _contextual;
|
||||
|
||||
public:
|
||||
Coords creature_start[AREA_CREATURES];
|
||||
Coords player_start[AREA_PLAYERS];
|
||||
};
|
||||
|
||||
bool isCombatMap(Map *punknown);
|
||||
CombatMap *getCombatMap(Map *punknown = nullptr);
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
82
engines/ultima/ultima4/controllers/controller.cpp
Normal file
82
engines/ultima/ultima4/controllers/controller.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/controllers/controller.h"
|
||||
#include "ultima/ultima4/events/event_handler.h"
|
||||
#include "engines/engine.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
Controller::Controller(int timerInterval) {
|
||||
this->_timerInterval = timerInterval;
|
||||
}
|
||||
|
||||
Controller::~Controller() {
|
||||
}
|
||||
|
||||
bool Controller::notifyKeyPressed(int key) {
|
||||
bool processed = KeyHandler::globalHandler(key);
|
||||
if (!processed)
|
||||
processed = keyPressed(key);
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
bool Controller::notifyMousePress(const Common::Point &mousePos) {
|
||||
return mousePressed(mousePos);
|
||||
}
|
||||
|
||||
|
||||
int Controller::getTimerInterval() {
|
||||
return _timerInterval;
|
||||
}
|
||||
|
||||
void Controller::setActive() {
|
||||
// By default, only the Escape action is turned on for controllers,
|
||||
// to allow the different sorts of input prompts to be aborted
|
||||
MetaEngine::setKeybindingMode(KBMODE_MINIMAL);
|
||||
}
|
||||
|
||||
void Controller::timerFired() {
|
||||
}
|
||||
|
||||
void Controller::timerCallback(void *data) {
|
||||
Controller *controller = static_cast<Controller *>(data);
|
||||
controller->timerFired();
|
||||
}
|
||||
|
||||
bool Controller::shouldQuit() const {
|
||||
return g_engine->shouldQuit();
|
||||
}
|
||||
|
||||
void Controller_startWait() {
|
||||
eventHandler->run();
|
||||
eventHandler->setControllerDone(false);
|
||||
eventHandler->popController();
|
||||
}
|
||||
|
||||
void Controller_endWait() {
|
||||
eventHandler->setControllerDone();
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
167
engines/ultima/ultima4/controllers/controller.h
Normal file
167
engines/ultima/ultima4/controllers/controller.h
Normal file
@@ -0,0 +1,167 @@
|
||||
/* 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_CONTROLLERS_CONTROLLER_H
|
||||
#define ULTIMA4_CONTROLLERS_CONTROLLER_H
|
||||
|
||||
#include "ultima/ultima4/metaengine.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* A generic controller base class. Controllers are classes that
|
||||
* contain the logic for responding to external events (e.g. keyboard,
|
||||
* mouse, timers).
|
||||
*/
|
||||
class Controller {
|
||||
public:
|
||||
Controller(int timerInterval = 1);
|
||||
virtual ~Controller();
|
||||
|
||||
/* methods for interacting with event manager */
|
||||
virtual bool isCombatController() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The event manager will call this method to notify the active
|
||||
* controller that a key has been pressed. The key will be passed on
|
||||
* to the virtual keyPressed method.
|
||||
*/
|
||||
bool notifyKeyPressed(int key);
|
||||
|
||||
/**
|
||||
* The event manager will call this method to notify that
|
||||
* the left button was clicked
|
||||
*/
|
||||
bool notifyMousePress(const Common::Point &mousePos);
|
||||
|
||||
int getTimerInterval();
|
||||
|
||||
/**
|
||||
* A simple adapter to make a timer callback into a controller method
|
||||
* call.
|
||||
*/
|
||||
static void timerCallback(void *data);
|
||||
|
||||
/** control methods subclasses may want to override */
|
||||
|
||||
/**
|
||||
* Called when a controller is made active
|
||||
*/
|
||||
virtual void setActive();
|
||||
|
||||
/**
|
||||
* Key was pressed
|
||||
*/
|
||||
virtual bool keyPressed(int key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mouse button was pressed
|
||||
*/
|
||||
virtual bool mousePressed(const Common::Point &mousePos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles keybinder actions
|
||||
*/
|
||||
virtual void keybinder(KeybindingAction action) {}
|
||||
|
||||
/**
|
||||
* The default timerFired handler for a controller. By default,
|
||||
* timers are ignored, but subclasses can override this method and it
|
||||
* will be called every <interval> 1/4 seconds.
|
||||
*/
|
||||
virtual void timerFired();
|
||||
|
||||
/**
|
||||
* Returns true if game should quit
|
||||
*/
|
||||
bool shouldQuit() const;
|
||||
private:
|
||||
int _timerInterval;
|
||||
};
|
||||
|
||||
// helper functions for the waitable controller; they just avoid
|
||||
// having eventhandler dependencies in this header file
|
||||
void Controller_startWait();
|
||||
void Controller_endWait();
|
||||
|
||||
/**
|
||||
* Class template for controllers that can be "waited for".
|
||||
* Subclasses should set the value variable and call doneWaiting when
|
||||
* the controller has completed.
|
||||
*/
|
||||
template<class T>
|
||||
class WaitableController : public Controller {
|
||||
private:
|
||||
bool _exitWhenDone;
|
||||
T _defaultValue;
|
||||
protected:
|
||||
T _value;
|
||||
void doneWaiting() {
|
||||
if (_exitWhenDone)
|
||||
Controller_endWait();
|
||||
}
|
||||
public:
|
||||
WaitableController(T defaultValue) : _defaultValue(defaultValue),
|
||||
_value(defaultValue), _exitWhenDone(false) {}
|
||||
|
||||
virtual T getValue() {
|
||||
return shouldQuit() ? _defaultValue : _value;
|
||||
}
|
||||
|
||||
virtual T waitFor() {
|
||||
_exitWhenDone = true;
|
||||
Controller_startWait();
|
||||
return getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mouse button was pressed
|
||||
*/
|
||||
virtual bool mousePressed(const Common::Point &mousePos) {
|
||||
// Treat mouse clicks as an abort
|
||||
doneWaiting();
|
||||
_value = _defaultValue;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class TurnCompleter {
|
||||
public:
|
||||
virtual ~TurnCompleter() {
|
||||
}
|
||||
virtual void finishTurn() = 0;
|
||||
|
||||
virtual void finishTurnAfterCombatEnds() {
|
||||
finishTurn();
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
862
engines/ultima/ultima4/controllers/game_controller.cpp
Normal file
862
engines/ultima/ultima4/controllers/game_controller.cpp
Normal file
@@ -0,0 +1,862 @@
|
||||
/* 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/controllers/game_controller.h"
|
||||
#include "ultima/ultima4/core/config.h"
|
||||
#include "ultima/ultima4/core/debugger.h"
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/game/game.h"
|
||||
#include "ultima/ultima4/game/context.h"
|
||||
#include "ultima/ultima4/game/death.h"
|
||||
#include "ultima/ultima4/game/moongate.h"
|
||||
#include "ultima/ultima4/views/stats.h"
|
||||
#include "ultima/ultima4/gfx/imagemgr.h"
|
||||
#include "ultima/ultima4/gfx/screen.h"
|
||||
#include "ultima/ultima4/map/annotation.h"
|
||||
#include "ultima/ultima4/map/city.h"
|
||||
#include "ultima/ultima4/map/dungeon.h"
|
||||
#include "ultima/ultima4/map/mapmgr.h"
|
||||
#include "ultima/ultima4/map/shrine.h"
|
||||
#include "ultima/ultima4/ultima4.h"
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
GameController *g_game = nullptr;
|
||||
|
||||
static const MouseArea MOUSE_AREAS[] = {
|
||||
{ 3, { { 8, 8 }, { 8, 184 }, { 96, 96 } }, MC_WEST, DIR_WEST },
|
||||
{ 3, { { 8, 8 }, { 184, 8 }, { 96, 96 } }, MC_NORTH, DIR_NORTH },
|
||||
{ 3, { { 184, 8 }, { 184, 184 }, { 96, 96 } }, MC_EAST, DIR_EAST },
|
||||
{ 3, { { 8, 184 }, { 184, 184 }, { 96, 96 } }, MC_SOUTH, DIR_SOUTH },
|
||||
{ 0, { { 0, 0 }, { 0, 0 }, { 0, 0 } }, MC_NORTH, DIR_NONE }
|
||||
};
|
||||
|
||||
GameController::GameController() :
|
||||
_mapArea(BORDER_WIDTH, BORDER_HEIGHT, VIEWPORT_W, VIEWPORT_H),
|
||||
_paused(false), _combatFinished(false), _pausedTimer(0) {
|
||||
g_game = this;
|
||||
}
|
||||
|
||||
void GameController::initScreen() {
|
||||
Image *screen = imageMgr->get("screen")->_image;
|
||||
|
||||
screen->fillRect(0, 0, screen->width(), screen->height(), 0, 0, 0);
|
||||
g_screen->update();
|
||||
}
|
||||
|
||||
void GameController::initScreenWithoutReloadingState() {
|
||||
g_music->playMapMusic();
|
||||
imageMgr->get(BKGD_BORDERS)->_image->draw(0, 0);
|
||||
g_context->_stats->update(); /* draw the party stats */
|
||||
|
||||
g_screen->screenMessage("Press Alt-h for help\n");
|
||||
g_screen->screenPrompt();
|
||||
|
||||
eventHandler->pushMouseAreaSet(MOUSE_AREAS);
|
||||
|
||||
eventHandler->setScreenUpdate(&gameUpdateScreen);
|
||||
}
|
||||
|
||||
void GameController::init() {
|
||||
initScreen();
|
||||
|
||||
// initialize the state of the global game context
|
||||
g_context->_line = TEXT_AREA_H - 1;
|
||||
g_context->_col = 0;
|
||||
g_context->_stats = new StatsArea();
|
||||
g_context->_moonPhase = 0;
|
||||
g_context->_windDirection = DIR_NORTH;
|
||||
g_context->_windCounter = 0;
|
||||
g_context->_windLock = false;
|
||||
g_context->_aura = new Aura();
|
||||
g_context->_horseSpeed = 0;
|
||||
g_context->_opacity = 1;
|
||||
g_context->_lastCommandTime = g_system->getMillis();
|
||||
g_context->_lastShip = nullptr;
|
||||
}
|
||||
|
||||
void GameController::setMap(Map *map, bool saveLocation, const Portal *portal, TurnCompleter *turnCompleter) {
|
||||
int viewMode;
|
||||
LocationContext context;
|
||||
int activePlayer = g_context->_party->getActivePlayer();
|
||||
MapCoords coords;
|
||||
|
||||
if (!turnCompleter)
|
||||
turnCompleter = this;
|
||||
|
||||
if (portal)
|
||||
coords = portal->_start;
|
||||
else
|
||||
coords = MapCoords(map->_width / 2, map->_height / 2);
|
||||
|
||||
/* If we don't want to save the location, then just return to the previous location,
|
||||
as there may still be ones in the stack we want to keep */
|
||||
if (!saveLocation)
|
||||
exitToParentMap();
|
||||
|
||||
switch (map->_type) {
|
||||
case Map::WORLD:
|
||||
context = CTX_WORLDMAP;
|
||||
viewMode = VIEW_NORMAL;
|
||||
break;
|
||||
case Map::DUNGEON:
|
||||
context = CTX_DUNGEON;
|
||||
viewMode = VIEW_DUNGEON;
|
||||
if (portal)
|
||||
g_ultima->_saveGame->_orientation = DIR_EAST;
|
||||
break;
|
||||
case Map::COMBAT:
|
||||
coords = MapCoords(-1, -1); /* set these to -1 just to be safe; we don't need them */
|
||||
context = CTX_COMBAT;
|
||||
viewMode = VIEW_NORMAL;
|
||||
activePlayer = -1; /* different active player for combat, defaults to 'None' */
|
||||
break;
|
||||
case Map::SHRINE:
|
||||
context = CTX_SHRINE;
|
||||
viewMode = VIEW_NORMAL;
|
||||
break;
|
||||
case Map::CITY:
|
||||
default:
|
||||
context = CTX_CITY;
|
||||
viewMode = VIEW_NORMAL;
|
||||
break;
|
||||
}
|
||||
g_context->_location = new Location(coords, map, viewMode, context, turnCompleter, g_context->_location);
|
||||
g_context->_location->addObserver(this);
|
||||
g_context->_party->setActivePlayer(activePlayer);
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::updateGameControllerContext(c->location->context);
|
||||
#endif
|
||||
|
||||
/* now, actually set our new tileset */
|
||||
_mapArea.setTileset(map->_tileSet);
|
||||
|
||||
if (isCity(map)) {
|
||||
City *city = dynamic_cast<City *>(map);
|
||||
assert(city);
|
||||
city->addPeople();
|
||||
}
|
||||
}
|
||||
|
||||
int GameController::exitToParentMap() {
|
||||
if (!g_context->_location)
|
||||
return 0;
|
||||
|
||||
if (g_context->_location->_prev != nullptr) {
|
||||
// Create the balloon for Hythloth
|
||||
if (g_context->_location->_map->_id == MAP_HYTHLOTH)
|
||||
createBalloon(g_context->_location->_prev->_map);
|
||||
|
||||
// free map info only if previous location was on a different map
|
||||
if (g_context->_location->_prev->_map != g_context->_location->_map) {
|
||||
g_context->_location->_map->_annotations->clear();
|
||||
g_context->_location->_map->clearObjects();
|
||||
|
||||
/* quench the torch of we're on the world map */
|
||||
if (g_context->_location->_prev->_map->isWorldMap())
|
||||
g_context->_party->quenchTorch();
|
||||
}
|
||||
locationFree(&g_context->_location);
|
||||
|
||||
// restore the tileset to the one the current map uses
|
||||
_mapArea.setTileset(g_context->_location->_map->_tileSet);
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::updateGameControllerContext(c->location->context);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GameController::finishTurn() {
|
||||
g_context->_lastCommandTime = g_system->getMillis();
|
||||
Creature *attacker = nullptr;
|
||||
|
||||
while (1) {
|
||||
|
||||
/* adjust food and moves */
|
||||
g_context->_party->endTurn();
|
||||
|
||||
/* count down the aura, if there is one */
|
||||
g_context->_aura->passTurn();
|
||||
|
||||
gameCheckHullIntegrity();
|
||||
|
||||
/* update party stats */
|
||||
//c->stats->setView(STATS_PARTY_OVERVIEW);
|
||||
|
||||
g_screen->screenUpdate(&this->_mapArea, true, false);
|
||||
g_screen->screenWait(1);
|
||||
|
||||
/* Creatures cannot spawn, move or attack while the avatar is on the balloon */
|
||||
if (!g_context->_party->isFlying()) {
|
||||
|
||||
// apply effects from tile avatar is standing on
|
||||
g_context->_party->applyEffect(g_context->_location->_map->tileTypeAt(g_context->_location->_coords, WITH_GROUND_OBJECTS)->getEffect());
|
||||
|
||||
// WORKAROUND: This fixes infinite combat loop at the Shrine of Humility.
|
||||
// I presume the original had code to show the game screen after combat
|
||||
// without doing all the end turn logic
|
||||
if (!_combatFinished) {
|
||||
// Move creatures and see if something is attacking the avatar
|
||||
attacker = g_context->_location->_map->moveObjects(g_context->_location->_coords);
|
||||
|
||||
// Something's attacking! Start combat!
|
||||
if (attacker) {
|
||||
gameCreatureAttack(attacker);
|
||||
return;
|
||||
}
|
||||
|
||||
// cleanup old creatures and spawn new ones
|
||||
creatureCleanup();
|
||||
checkRandomCreatures();
|
||||
checkBridgeTrolls();
|
||||
} else {
|
||||
_combatFinished = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* update map annotations */
|
||||
g_context->_location->_map->_annotations->passTurn();
|
||||
|
||||
if (!g_context->_party->isImmobilized())
|
||||
break;
|
||||
|
||||
if (g_context->_party->isDead()) {
|
||||
g_death->start(0);
|
||||
return;
|
||||
} else {
|
||||
g_screen->screenMessage("Zzzzzz\n");
|
||||
g_screen->screenWait(4);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_context->_location->_context == CTX_DUNGEON) {
|
||||
Dungeon *dungeon = dynamic_cast<Dungeon *>(g_context->_location->_map);
|
||||
assert(dungeon);
|
||||
|
||||
if (g_context->_party->getTorchDuration() <= 0)
|
||||
g_screen->screenMessage("It's Dark!\n");
|
||||
else
|
||||
g_context->_party->burnTorch();
|
||||
|
||||
/* handle dungeon traps */
|
||||
if (dungeon->currentToken() == DUNGEON_TRAP) {
|
||||
dungeonHandleTrap((TrapType)dungeon->currentSubToken());
|
||||
// a little kludgey to have a second test for this
|
||||
// right here. But without it you can survive an
|
||||
// extra turn after party death and do some things
|
||||
// that could cause a crash, like Hole up and Camp.
|
||||
if (g_context->_party->isDead()) {
|
||||
g_death->start(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* draw a prompt */
|
||||
g_screen->screenPrompt();
|
||||
//g_screen->screenRedrawTextArea(TEXT_AREA_X, TEXT_AREA_Y, TEXT_AREA_W, TEXT_AREA_H);
|
||||
}
|
||||
|
||||
void GameController::flashTile(const Coords &coords, MapTile tile, int frames) {
|
||||
g_context->_location->_map->_annotations->add(coords, tile, true);
|
||||
|
||||
g_screen->screenTileUpdate(&g_game->_mapArea, coords);
|
||||
|
||||
g_screen->screenWait(frames);
|
||||
g_context->_location->_map->_annotations->remove(coords, tile);
|
||||
|
||||
g_screen->screenTileUpdate(&g_game->_mapArea, coords, false);
|
||||
}
|
||||
|
||||
void GameController::flashTile(const Coords &coords, const Common::String &tilename, int timeFactor) {
|
||||
Tile *tile = g_context->_location->_map->_tileSet->getByName(tilename);
|
||||
assertMsg(tile, "no tile named '%s' found in tileset", tilename.c_str());
|
||||
flashTile(coords, tile->getId(), timeFactor);
|
||||
}
|
||||
|
||||
void GameController::update(Party *party, PartyEvent &event) {
|
||||
int i;
|
||||
|
||||
switch (event._type) {
|
||||
case PartyEvent::LOST_EIGHTH:
|
||||
// inform a player he has lost zero or more eighths of avatarhood.
|
||||
g_screen->screenMessage("\n %cThou hast lost\n an eighth!%c\n", FG_YELLOW, FG_WHITE);
|
||||
break;
|
||||
case PartyEvent::ADVANCED_LEVEL:
|
||||
g_screen->screenMessage("\n%c%s\nThou art now Level %d%c\n", FG_YELLOW, event._player->getName().c_str(), event._player->getRealLevel(), FG_WHITE);
|
||||
gameSpellEffect('r', -1, SOUND_MAGIC); // Same as resurrect spell
|
||||
break;
|
||||
case PartyEvent::STARVING:
|
||||
g_screen->screenMessage("\n%cStarving!!!%c\n", FG_YELLOW, FG_WHITE);
|
||||
/* FIXME: add sound effect here */
|
||||
|
||||
// 2 damage to each party member for starving!
|
||||
for (i = 0; i < g_ultima->_saveGame->_members; i++)
|
||||
g_context->_party->member(i)->applyDamage(2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GameController::update(Location *location, MoveEvent &event) {
|
||||
switch (location->_map->_type) {
|
||||
case Map::DUNGEON:
|
||||
avatarMovedInDungeon(event);
|
||||
break;
|
||||
case Map::COMBAT: {
|
||||
// FIXME: let the combat controller handle it
|
||||
CombatController *ctl = dynamic_cast<CombatController *>(eventHandler->getController());
|
||||
assert(ctl);
|
||||
ctl->movePartyMember(event);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
avatarMoved(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GameController::setActive() {
|
||||
// The game controller has the keybindings enabled
|
||||
MetaEngine::setKeybindingMode(KBMODE_NORMAL);
|
||||
}
|
||||
|
||||
void GameController::keybinder(KeybindingAction action) {
|
||||
MetaEngine::executeAction(action);
|
||||
}
|
||||
|
||||
bool GameController::mousePressed(const Common::Point &mousePos) {
|
||||
const MouseArea *area = eventHandler->mouseAreaForPoint(mousePos.x, mousePos.y);
|
||||
|
||||
if (area) {
|
||||
keybinder(KEYBIND_INTERACT);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GameController::initMoons() {
|
||||
int trammelphase = g_ultima->_saveGame->_trammelPhase,
|
||||
feluccaphase = g_ultima->_saveGame->_feluccaPhase;
|
||||
|
||||
assertMsg(g_context != nullptr, "Game context doesn't exist!");
|
||||
assertMsg(g_ultima->_saveGame != nullptr, "Savegame doesn't exist!");
|
||||
//assertMsg(mapIsWorldMap(c->location->map) && c->location->viewMode == VIEW_NORMAL, "Can only call gameInitMoons() from the world map!");
|
||||
|
||||
g_ultima->_saveGame->_trammelPhase = g_ultima->_saveGame->_feluccaPhase = 0;
|
||||
g_context->_moonPhase = 0;
|
||||
|
||||
while ((g_ultima->_saveGame->_trammelPhase != trammelphase) ||
|
||||
(g_ultima->_saveGame->_feluccaPhase != feluccaphase))
|
||||
updateMoons(false);
|
||||
}
|
||||
|
||||
void GameController::updateMoons(bool showmoongates) {
|
||||
int realMoonPhase,
|
||||
oldTrammel,
|
||||
trammelSubphase;
|
||||
const Coords *gate;
|
||||
|
||||
if (g_context->_location->_map->isWorldMap()) {
|
||||
oldTrammel = g_ultima->_saveGame->_trammelPhase;
|
||||
|
||||
if (++g_context->_moonPhase >= MOON_PHASES * MOON_SECONDS_PER_PHASE * 4)
|
||||
g_context->_moonPhase = 0;
|
||||
|
||||
trammelSubphase = g_context->_moonPhase % (MOON_SECONDS_PER_PHASE * 4 * 3);
|
||||
realMoonPhase = (g_context->_moonPhase / (4 * MOON_SECONDS_PER_PHASE));
|
||||
|
||||
g_ultima->_saveGame->_trammelPhase = realMoonPhase / 3;
|
||||
g_ultima->_saveGame->_feluccaPhase = realMoonPhase % 8;
|
||||
|
||||
if (g_ultima->_saveGame->_trammelPhase > 7)
|
||||
g_ultima->_saveGame->_trammelPhase = 7;
|
||||
|
||||
if (showmoongates) {
|
||||
/* update the moongates if trammel changed */
|
||||
if (trammelSubphase == 0) {
|
||||
gate = g_moongates->getGateCoordsForPhase(oldTrammel);
|
||||
if (gate)
|
||||
g_context->_location->_map->_annotations->remove(*gate, g_context->_location->_map->translateFromRawTileIndex(0x40));
|
||||
gate = g_moongates->getGateCoordsForPhase(g_ultima->_saveGame->_trammelPhase);
|
||||
if (gate)
|
||||
g_context->_location->_map->_annotations->add(*gate, g_context->_location->_map->translateFromRawTileIndex(0x40));
|
||||
} else if (trammelSubphase == 1) {
|
||||
gate = g_moongates->getGateCoordsForPhase(g_ultima->_saveGame->_trammelPhase);
|
||||
if (gate) {
|
||||
g_context->_location->_map->_annotations->remove(*gate, g_context->_location->_map->translateFromRawTileIndex(0x40));
|
||||
g_context->_location->_map->_annotations->add(*gate, g_context->_location->_map->translateFromRawTileIndex(0x41));
|
||||
}
|
||||
} else if (trammelSubphase == 2) {
|
||||
gate = g_moongates->getGateCoordsForPhase(g_ultima->_saveGame->_trammelPhase);
|
||||
if (gate) {
|
||||
g_context->_location->_map->_annotations->remove(*gate, g_context->_location->_map->translateFromRawTileIndex(0x41));
|
||||
g_context->_location->_map->_annotations->add(*gate, g_context->_location->_map->translateFromRawTileIndex(0x42));
|
||||
}
|
||||
} else if (trammelSubphase == 3) {
|
||||
gate = g_moongates->getGateCoordsForPhase(g_ultima->_saveGame->_trammelPhase);
|
||||
if (gate) {
|
||||
g_context->_location->_map->_annotations->remove(*gate, g_context->_location->_map->translateFromRawTileIndex(0x42));
|
||||
g_context->_location->_map->_annotations->add(*gate, g_context->_location->_map->translateFromRawTileIndex(0x43));
|
||||
}
|
||||
} else if ((trammelSubphase > 3) && (trammelSubphase < (MOON_SECONDS_PER_PHASE * 4 * 3) - 3)) {
|
||||
gate = g_moongates->getGateCoordsForPhase(g_ultima->_saveGame->_trammelPhase);
|
||||
if (gate) {
|
||||
g_context->_location->_map->_annotations->remove(*gate, g_context->_location->_map->translateFromRawTileIndex(0x43));
|
||||
g_context->_location->_map->_annotations->add(*gate, g_context->_location->_map->translateFromRawTileIndex(0x43));
|
||||
}
|
||||
} else if (trammelSubphase == (MOON_SECONDS_PER_PHASE * 4 * 3) - 3) {
|
||||
gate = g_moongates->getGateCoordsForPhase(g_ultima->_saveGame->_trammelPhase);
|
||||
if (gate) {
|
||||
g_context->_location->_map->_annotations->remove(*gate, g_context->_location->_map->translateFromRawTileIndex(0x43));
|
||||
g_context->_location->_map->_annotations->add(*gate, g_context->_location->_map->translateFromRawTileIndex(0x42));
|
||||
}
|
||||
} else if (trammelSubphase == (MOON_SECONDS_PER_PHASE * 4 * 3) - 2) {
|
||||
gate = g_moongates->getGateCoordsForPhase(g_ultima->_saveGame->_trammelPhase);
|
||||
if (gate) {
|
||||
g_context->_location->_map->_annotations->remove(*gate, g_context->_location->_map->translateFromRawTileIndex(0x42));
|
||||
g_context->_location->_map->_annotations->add(*gate, g_context->_location->_map->translateFromRawTileIndex(0x41));
|
||||
}
|
||||
} else if (trammelSubphase == (MOON_SECONDS_PER_PHASE * 4 * 3) - 1) {
|
||||
gate = g_moongates->getGateCoordsForPhase(g_ultima->_saveGame->_trammelPhase);
|
||||
if (gate) {
|
||||
g_context->_location->_map->_annotations->remove(*gate, g_context->_location->_map->translateFromRawTileIndex(0x41));
|
||||
g_context->_location->_map->_annotations->add(*gate, g_context->_location->_map->translateFromRawTileIndex(0x40));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameController::avatarMoved(MoveEvent &event) {
|
||||
if (event._userEvent) {
|
||||
|
||||
// is filterMoveMessages even used? it doesn't look like the option is hooked up in the configuration menu
|
||||
if (!settings._filterMoveMessages) {
|
||||
switch (g_context->_transportContext) {
|
||||
case TRANSPORT_FOOT:
|
||||
case TRANSPORT_HORSE:
|
||||
g_screen->screenMessage("%s\n", getDirectionName(event._dir));
|
||||
break;
|
||||
case TRANSPORT_SHIP:
|
||||
if (event._result & MOVE_TURNED)
|
||||
g_screen->screenMessage("Turn %s!\n", getDirectionName(event._dir));
|
||||
else if (event._result & MOVE_SLOWED)
|
||||
g_screen->screenMessage("%cSlow progress!%c\n", FG_GREY, FG_WHITE);
|
||||
else
|
||||
g_screen->screenMessage("Sail %s!\n", getDirectionName(event._dir));
|
||||
break;
|
||||
case TRANSPORT_BALLOON:
|
||||
g_screen->screenMessage("%cDrift Only!%c\n", FG_GREY, FG_WHITE);
|
||||
break;
|
||||
default:
|
||||
error("bad transportContext %d in avatarMoved()", g_context->_transportContext);
|
||||
}
|
||||
}
|
||||
|
||||
/* movement was blocked */
|
||||
if (event._result & MOVE_BLOCKED) {
|
||||
|
||||
/* if shortcuts are enabled, try them! */
|
||||
if (settings._shortcutCommands) {
|
||||
MapCoords new_coords = g_context->_location->_coords;
|
||||
MapTile *tile;
|
||||
|
||||
new_coords.move(event._dir, g_context->_location->_map);
|
||||
tile = g_context->_location->_map->tileAt(new_coords, WITH_OBJECTS);
|
||||
|
||||
if (tile->getTileType()->isDoor()) {
|
||||
g_debugger->openAt(new_coords);
|
||||
event._result = (MoveResult)(MOVE_SUCCEEDED | MOVE_END_TURN);
|
||||
} else if (tile->getTileType()->isLockedDoor()) {
|
||||
g_debugger->jimmyAt(new_coords);
|
||||
event._result = (MoveResult)(MOVE_SUCCEEDED | MOVE_END_TURN);
|
||||
} /*else if (mapPersonAt(c->location->map, new_coords) != nullptr) {
|
||||
talkAtCoord(newx, newy, 1, nullptr);
|
||||
event.result = MOVE_SUCCEEDED | MOVE_END_TURN;
|
||||
}*/
|
||||
}
|
||||
|
||||
/* if we're still blocked */
|
||||
if ((event._result & MOVE_BLOCKED) && !settings._filterMoveMessages) {
|
||||
soundPlay(SOUND_BLOCKED, false);
|
||||
g_screen->screenMessage("%cBlocked!%c\n", FG_GREY, FG_WHITE);
|
||||
}
|
||||
} else if (g_context->_transportContext == TRANSPORT_FOOT || g_context->_transportContext == TRANSPORT_HORSE) {
|
||||
/* movement was slowed */
|
||||
if (event._result & MOVE_SLOWED) {
|
||||
soundPlay(SOUND_WALK_SLOWED);
|
||||
g_screen->screenMessage("%cSlow progress!%c\n", FG_GREY, FG_WHITE);
|
||||
} else {
|
||||
soundPlay(SOUND_WALK_NORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* exited map */
|
||||
if (event._result & MOVE_EXIT_TO_PARENT) {
|
||||
g_screen->screenMessage("%cLeaving...%c\n", FG_GREY, FG_WHITE);
|
||||
exitToParentMap();
|
||||
g_music->playMapMusic();
|
||||
}
|
||||
|
||||
/* things that happen while not on board the balloon */
|
||||
if (g_context->_transportContext & ~TRANSPORT_BALLOON)
|
||||
checkSpecialCreatures(event._dir);
|
||||
/* things that happen while on foot or horseback */
|
||||
if ((g_context->_transportContext & TRANSPORT_FOOT_OR_HORSE) &&
|
||||
!(event._result & (MOVE_SLOWED | MOVE_BLOCKED))) {
|
||||
if (checkMoongates())
|
||||
event._result = (MoveResult)(MOVE_MAP_CHANGE | MOVE_END_TURN);
|
||||
}
|
||||
}
|
||||
|
||||
void GameController::avatarMovedInDungeon(MoveEvent &event) {
|
||||
Direction realDir = dirNormalize((Direction)g_ultima->_saveGame->_orientation, event._dir);
|
||||
Dungeon *dungeon = dynamic_cast<Dungeon *>(g_context->_location->_map);
|
||||
assert(dungeon);
|
||||
|
||||
if (!settings._filterMoveMessages) {
|
||||
if (event._userEvent) {
|
||||
if (event._result & MOVE_TURNED) {
|
||||
if (dirRotateCCW((Direction)g_ultima->_saveGame->_orientation) == realDir)
|
||||
g_screen->screenMessage("Turn Left\n");
|
||||
else
|
||||
g_screen->screenMessage("Turn Right\n");
|
||||
} else {
|
||||
// Show 'Advance' or 'Retreat' in dungeons
|
||||
g_screen->screenMessage("%s\n", realDir == g_ultima->_saveGame->_orientation ? "Advance" : "Retreat");
|
||||
}
|
||||
}
|
||||
|
||||
if (event._result & MOVE_BLOCKED)
|
||||
g_screen->screenMessage("%cBlocked!%c\n", FG_GREY, FG_WHITE);
|
||||
}
|
||||
|
||||
// If we're exiting the map, do this
|
||||
if (event._result & MOVE_EXIT_TO_PARENT) {
|
||||
g_screen->screenMessage("%cLeaving...%c\n", FG_GREY, FG_WHITE);
|
||||
exitToParentMap();
|
||||
g_music->playMapMusic();
|
||||
}
|
||||
|
||||
// Check to see if we're entering a dungeon room
|
||||
if (event._result & MOVE_SUCCEEDED) {
|
||||
if (dungeon->currentToken() == DUNGEON_ROOM) {
|
||||
int room = (int)dungeon->currentSubToken(); // Get room number
|
||||
|
||||
/**
|
||||
* recalculate room for the abyss -- there are 16 rooms for every 2 levels,
|
||||
* each room marked with 0xD* where (* == room number 0-15).
|
||||
* for levels 1 and 2, there are 16 rooms, levels 3 and 4 there are 16 rooms, etc.
|
||||
*/
|
||||
if (g_context->_location->_map->_id == MAP_ABYSS)
|
||||
room = (0x10 * (g_context->_location->_coords.z / 2)) + room;
|
||||
|
||||
Dungeon *dng = dynamic_cast<Dungeon *>(g_context->_location->_map);
|
||||
assert(dng);
|
||||
dng->_currentRoom = room;
|
||||
|
||||
// Set the map and start combat!
|
||||
CombatController *cc = new CombatController(dng->_roomMaps[room]);
|
||||
cc->initDungeonRoom(room, dirReverse(realDir));
|
||||
cc->begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameController::timerFired() {
|
||||
if (_pausedTimer > 0) {
|
||||
_pausedTimer--;
|
||||
if (_pausedTimer <= 0) {
|
||||
_pausedTimer = 0;
|
||||
_paused = false; /* unpause the game */
|
||||
}
|
||||
}
|
||||
|
||||
if (!_paused && !_pausedTimer) {
|
||||
if (++g_context->_windCounter >= MOON_SECONDS_PER_PHASE * 4) {
|
||||
if (xu4_random(4) == 1 && !g_context->_windLock)
|
||||
g_context->_windDirection = dirRandomDir(MASK_DIR_ALL);
|
||||
g_context->_windCounter = 0;
|
||||
}
|
||||
|
||||
/* balloon moves about 4 times per second */
|
||||
if ((g_context->_transportContext == TRANSPORT_BALLOON) &&
|
||||
g_context->_party->isFlying()) {
|
||||
g_context->_location->move(dirReverse((Direction) g_context->_windDirection), false);
|
||||
}
|
||||
|
||||
updateMoons(true);
|
||||
|
||||
g_screen->screenCycle();
|
||||
|
||||
// Check for any right-button mouse movement
|
||||
KeybindingAction action = eventHandler->getAction();
|
||||
if (action != KEYBIND_NONE)
|
||||
keybinder(action);
|
||||
|
||||
// Do game udpates to the screen, like tile animations
|
||||
gameUpdateScreen();
|
||||
|
||||
/*
|
||||
* force pass if no commands within last 20 seconds
|
||||
*/
|
||||
Controller *controller = eventHandler->getController();
|
||||
if (controller != nullptr && (eventHandler->getController() == g_game ||
|
||||
dynamic_cast<CombatController *>(eventHandler->getController()) != nullptr) &&
|
||||
gameTimeSinceLastCommand() > 20) {
|
||||
|
||||
/* pass the turn, and redraw the text area so the prompt is shown */
|
||||
MetaEngine::executeAction(KEYBIND_PASS);
|
||||
g_screen->screenRedrawTextArea(TEXT_AREA_X, TEXT_AREA_Y, TEXT_AREA_W, TEXT_AREA_H);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GameController::checkSpecialCreatures(Direction dir) {
|
||||
int i;
|
||||
Object *obj;
|
||||
static const struct {
|
||||
int x, y;
|
||||
Direction dir;
|
||||
} pirateInfo[] = {
|
||||
{ 224, 220, DIR_EAST }, /* N'M" O'A" */
|
||||
{ 224, 228, DIR_EAST }, /* O'E" O'A" */
|
||||
{ 226, 220, DIR_EAST }, /* O'E" O'C" */
|
||||
{ 227, 228, DIR_EAST }, /* O'E" O'D" */
|
||||
{ 228, 227, DIR_SOUTH }, /* O'D" O'E" */
|
||||
{ 229, 225, DIR_SOUTH }, /* O'B" O'F" */
|
||||
{ 229, 223, DIR_NORTH }, /* N'P" O'F" */
|
||||
{ 228, 222, DIR_NORTH } /* N'O" O'E" */
|
||||
};
|
||||
|
||||
/*
|
||||
* if heading east into pirates cove (O'A" N'N"), generate pirate
|
||||
* ships
|
||||
*/
|
||||
if (dir == DIR_EAST &&
|
||||
g_context->_location->_coords.x == 0xdd &&
|
||||
g_context->_location->_coords.y == 0xe0) {
|
||||
for (i = 0; i < 8; i++) {
|
||||
obj = g_context->_location->_map->addCreature(creatureMgr->getById(PIRATE_ID), MapCoords(pirateInfo[i].x, pirateInfo[i].y));
|
||||
obj->setDirection(pirateInfo[i].dir);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* if heading south towards the shrine of humility, generate
|
||||
* daemons unless horn has been blown
|
||||
*/
|
||||
if (dir == DIR_SOUTH &&
|
||||
g_context->_location->_coords.x >= 229 &&
|
||||
g_context->_location->_coords.x < 234 &&
|
||||
g_context->_location->_coords.y >= 212 &&
|
||||
g_context->_location->_coords.y < 217 &&
|
||||
*g_context->_aura != Aura::HORN) {
|
||||
for (i = 0; i < 8; i++)
|
||||
g_context->_location->_map->addCreature(creatureMgr->getById(DAEMON_ID), MapCoords(231, g_context->_location->_coords.y + 1, g_context->_location->_coords.z));
|
||||
}
|
||||
}
|
||||
|
||||
bool GameController::checkMoongates() {
|
||||
Coords dest;
|
||||
|
||||
if (g_moongates->findActiveGateAt(g_ultima->_saveGame->_trammelPhase, g_ultima->_saveGame->_feluccaPhase, g_context->_location->_coords, dest)) {
|
||||
|
||||
gameSpellEffect(-1, -1, SOUND_MOONGATE); // Default spell effect (screen inversion without 'spell' sound effects)
|
||||
|
||||
if (g_context->_location->_coords != dest) {
|
||||
g_context->_location->_coords = dest;
|
||||
gameSpellEffect(-1, -1, SOUND_MOONGATE); // Again, after arriving
|
||||
}
|
||||
|
||||
if (g_moongates->isEntryToShrineOfSpirituality(g_ultima->_saveGame->_trammelPhase, g_ultima->_saveGame->_feluccaPhase)) {
|
||||
Shrine *shrine_spirituality;
|
||||
|
||||
shrine_spirituality = dynamic_cast<Shrine *>(mapMgr->get(MAP_SHRINE_SPIRITUALITY));
|
||||
assert(shrine_spirituality);
|
||||
|
||||
if (!g_context->_party->canEnterShrine(VIRT_SPIRITUALITY))
|
||||
return true;
|
||||
|
||||
setMap(shrine_spirituality, 1, nullptr);
|
||||
g_music->playMapMusic();
|
||||
|
||||
shrine_spirituality->enter();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GameController::creatureCleanup() {
|
||||
ObjectDeque::iterator i;
|
||||
Map *map = g_context->_location->_map;
|
||||
|
||||
for (i = map->_objects.begin(); i != map->_objects.end();) {
|
||||
Object *obj = *i;
|
||||
MapCoords o_coords = obj->getCoords();
|
||||
|
||||
if ((obj->getType() == Object::CREATURE) && (o_coords.z == g_context->_location->_coords.z) &&
|
||||
o_coords.distance(g_context->_location->_coords, g_context->_location->_map) > MAX_CREATURE_DISTANCE) {
|
||||
|
||||
/* delete the object and remove it from the map */
|
||||
i = map->removeObject(i);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameController::checkRandomCreatures() {
|
||||
int canSpawnHere = g_context->_location->_map->isWorldMap() || g_context->_location->_context & CTX_DUNGEON;
|
||||
#ifdef IOS_ULTIMA4
|
||||
int spawnDivisor = c->location->context & CTX_DUNGEON ? (53 - (c->location->coords.z << 2)) : 53;
|
||||
#else
|
||||
int spawnDivisor = g_context->_location->_context & CTX_DUNGEON ? (32 - (g_context->_location->_coords.z << 2)) : 32;
|
||||
#endif
|
||||
|
||||
/* If there are too many creatures already,
|
||||
or we're not on the world map, don't worry about it! */
|
||||
if (!canSpawnHere ||
|
||||
g_context->_location->_map->getNumberOfCreatures() >= MAX_CREATURES_ON_MAP ||
|
||||
xu4_random(spawnDivisor) != 0)
|
||||
return;
|
||||
|
||||
// If combat is turned off, then don't spawn any creator
|
||||
if (g_debugger->_disableCombat)
|
||||
return;
|
||||
|
||||
gameSpawnCreature(nullptr);
|
||||
}
|
||||
|
||||
void GameController::checkBridgeTrolls() {
|
||||
const Tile *bridge = g_context->_location->_map->_tileSet->getByName("bridge");
|
||||
if (!bridge)
|
||||
return;
|
||||
|
||||
// TODO: CHEST: Make a user option to not make chests block bridge trolls
|
||||
if (!g_context->_location->_map->isWorldMap() ||
|
||||
g_context->_location->_map->tileAt(g_context->_location->_coords, WITH_OBJECTS)->_id != bridge->getId() ||
|
||||
xu4_random(8) != 0)
|
||||
return;
|
||||
|
||||
g_screen->screenMessage("\nBridge Trolls!\n");
|
||||
|
||||
Creature *m = g_context->_location->_map->addCreature(creatureMgr->getById(TROLL_ID), g_context->_location->_coords);
|
||||
CombatController *cc = new CombatController(MAP_BRIDGE_CON);
|
||||
cc->init(m);
|
||||
cc->begin();
|
||||
}
|
||||
|
||||
bool GameController::createBalloon(Map *map) {
|
||||
ObjectDeque::iterator i;
|
||||
|
||||
/* see if the balloon has already been created (and not destroyed) */
|
||||
for (auto *obj : map->_objects) {
|
||||
if (obj->getTile().getTileType()->isBalloon())
|
||||
return false;
|
||||
}
|
||||
|
||||
const Tile *balloon = map->_tileSet->getByName("balloon");
|
||||
assertMsg(balloon, "no balloon tile found in tileset");
|
||||
map->addObject(balloon->getId(), balloon->getId(), map->getLabel("balloon"));
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameController::attack(Direction dir) {
|
||||
g_screen->screenMessage("Attack: ");
|
||||
if (g_context->_party->isFlying()) {
|
||||
g_screen->screenMessage("\n%cDrift only!%c\n", FG_GREY, FG_WHITE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dir == DIR_NONE)
|
||||
dir = gameGetDirection();
|
||||
|
||||
if (dir == DIR_NONE) {
|
||||
g_screen->screenMessage("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Std::vector<Coords> path = gameGetDirectionalActionPath(
|
||||
MASK_DIR(dir), MASK_DIR_ALL, g_context->_location->_coords,
|
||||
1, 1, nullptr, true);
|
||||
for (const auto &coords : path) {
|
||||
if (attackAt(coords))
|
||||
return;
|
||||
}
|
||||
|
||||
g_screen->screenMessage("%cNothing to Attack!%c\n", FG_GREY, FG_WHITE);
|
||||
}
|
||||
|
||||
bool GameController::attackAt(const Coords &coords) {
|
||||
Object *under;
|
||||
const Tile *ground;
|
||||
Creature *m;
|
||||
|
||||
m = dynamic_cast<Creature *>(g_context->_location->_map->objectAt(coords));
|
||||
// Nothing attackable: move on to next tile
|
||||
if (m == nullptr || !m->isAttackable())
|
||||
return false;
|
||||
|
||||
// Attack successful
|
||||
/// TODO: CHEST: Make a user option to not make chests change battlefield
|
||||
/// map (1 of 2)
|
||||
ground = g_context->_location->_map->tileTypeAt(g_context->_location->_coords, WITH_GROUND_OBJECTS);
|
||||
if (!ground->isChest()) {
|
||||
ground = g_context->_location->_map->tileTypeAt(g_context->_location->_coords, WITHOUT_OBJECTS);
|
||||
if ((under = g_context->_location->_map->objectAt(g_context->_location->_coords)) &&
|
||||
under->getTile().getTileType()->isShip())
|
||||
ground = under->getTile().getTileType();
|
||||
}
|
||||
|
||||
// You're attacking a townsperson! Alert the guards!
|
||||
if ((m->getType() == Object::PERSON) && (m->getMovementBehavior() != MOVEMENT_ATTACK_AVATAR))
|
||||
g_context->_location->_map->alertGuards();
|
||||
|
||||
// Not good karma to be killing the innocent. Bad avatar!
|
||||
if (m->isGood() || /* attacking a good creature */
|
||||
/* attacking a docile (although possibly evil) person in town */
|
||||
((m->getType() == Object::PERSON) && (m->getMovementBehavior() != MOVEMENT_ATTACK_AVATAR)))
|
||||
g_context->_party->adjustKarma(KA_ATTACKED_GOOD);
|
||||
|
||||
CombatController *cc = new CombatController(CombatMap::mapForTile(ground, g_context->_party->getTransport().getTileType(), m));
|
||||
cc->init(m);
|
||||
cc->begin();
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
195
engines/ultima/ultima4/controllers/game_controller.h
Normal file
195
engines/ultima/ultima4/controllers/game_controller.h
Normal file
@@ -0,0 +1,195 @@
|
||||
/* 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_CONTROLLERS_GAME_CONTROLLER_H
|
||||
#define ULTIMA4_CONTROLLERS_GAME_CONTROLLER_H
|
||||
|
||||
#include "ultima/ultima4/controllers/controller.h"
|
||||
#include "ultima/ultima4/core/coords.h"
|
||||
#include "ultima/ultima4/core/observer.h"
|
||||
#include "ultima/ultima4/game/portal.h"
|
||||
#include "ultima/ultima4/game/player.h"
|
||||
#include "ultima/ultima4/map/location.h"
|
||||
#include "ultima/ultima4/views/tileview.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* The main game controller that handles basic game flow and keypresses.
|
||||
*
|
||||
* @todo
|
||||
* <ul>
|
||||
* <li>separate the dungeon specific stuff into another class (subclass?)</li>
|
||||
* </ul>
|
||||
*/
|
||||
class GameController : public Controller, public Observer<Party *, PartyEvent &>, public Observer<Location *, MoveEvent &>,
|
||||
public TurnCompleter {
|
||||
private:
|
||||
/**
|
||||
* Handles feedback after avatar moved during normal 3rd-person view.
|
||||
*/
|
||||
void avatarMoved(MoveEvent &event);
|
||||
|
||||
/**
|
||||
* Handles feedback after moving the avatar in the 3-d dungeon view.
|
||||
*/
|
||||
void avatarMovedInDungeon(MoveEvent &event);
|
||||
|
||||
/**
|
||||
* Removes creatures from the current map if they are too far away from the avatar
|
||||
*/
|
||||
void creatureCleanup();
|
||||
|
||||
/**
|
||||
* Handles trolls under bridges
|
||||
*/
|
||||
void checkBridgeTrolls();
|
||||
|
||||
/**
|
||||
* Checks creature conditions and spawns new creatures if necessary
|
||||
*/
|
||||
void checkRandomCreatures();
|
||||
|
||||
/**
|
||||
* Checks for valid conditions and handles
|
||||
* special creatures guarding the entrance to the
|
||||
* abyss and to the shrine of spirituality
|
||||
*/
|
||||
void checkSpecialCreatures(Direction dir);
|
||||
|
||||
/**
|
||||
* Checks for and handles when the avatar steps on a moongate
|
||||
*/
|
||||
bool checkMoongates();
|
||||
|
||||
/**
|
||||
* Creates the balloon near Hythloth, but only if the balloon doesn't already exists somewhere
|
||||
*/
|
||||
bool createBalloon(Map *map);
|
||||
|
||||
/**
|
||||
* Attempts to attack a creature at map coordinates x,y. If no
|
||||
* creature is present at that point, zero is returned.
|
||||
*/
|
||||
bool attackAt(const Coords &coords);
|
||||
public:
|
||||
/**
|
||||
* Show an attack flash at x, y on the current map.
|
||||
* This is used for 'being hit' or 'being missed'
|
||||
* by weapons, cannon fire, spells, etc.
|
||||
*/
|
||||
static void flashTile(const Coords &coords, MapTile tile, int timeFactor);
|
||||
|
||||
static void flashTile(const Coords &coords, const Common::String &tilename, int timeFactor);
|
||||
static void doScreenAnimationsWhilePausing(int timeFactor);
|
||||
public:
|
||||
TileView _mapArea;
|
||||
bool _paused;
|
||||
int _pausedTimer;
|
||||
bool _combatFinished;
|
||||
public:
|
||||
GameController();
|
||||
|
||||
/* controller functions */
|
||||
|
||||
/**
|
||||
* Called when a controller is made active
|
||||
*/
|
||||
void setActive() override;
|
||||
|
||||
/**
|
||||
* Keybinder actions
|
||||
*/
|
||||
void keybinder(KeybindingAction action) override;
|
||||
|
||||
/**
|
||||
* Mouse button was pressed
|
||||
*/
|
||||
bool mousePressed(const Common::Point &mousePos) override;
|
||||
|
||||
/**
|
||||
* This function is called every quarter second.
|
||||
*/
|
||||
void timerFired() override;
|
||||
|
||||
/* main game functions */
|
||||
void init();
|
||||
void initScreen();
|
||||
void initScreenWithoutReloadingState();
|
||||
void setMap(Map *map, bool saveLocation, const Portal *portal, TurnCompleter *turnCompleter = nullptr);
|
||||
|
||||
/**
|
||||
* Exits the current map and location and returns to its parent location
|
||||
* This restores all relevant information from the previous location,
|
||||
* such as the map, map position, etc. (such as exiting a city)
|
||||
**/
|
||||
int exitToParentMap();
|
||||
|
||||
/**
|
||||
* Finishes the game turn after combat ends. It suppresses monster
|
||||
* movement to prevent issues, such as infinite combat at the
|
||||
* Shrine of Humility
|
||||
*/
|
||||
void finishTurnAfterCombatEnds() override {
|
||||
_combatFinished = true;
|
||||
finishTurn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates a game turn. This performs the post-turn housekeeping
|
||||
* tasks like adjusting the party's food, incrementing the number of
|
||||
* moves, etc.
|
||||
*/
|
||||
void finishTurn() override;
|
||||
|
||||
/**
|
||||
* Provide feedback to user after a party event happens.
|
||||
*/
|
||||
void update(Party *party, PartyEvent &event) override;
|
||||
|
||||
/**
|
||||
* Provide feedback to user after a movement event happens.
|
||||
*/
|
||||
void update(Location *location, MoveEvent &event) override;
|
||||
|
||||
/**
|
||||
* Initializes the moon state according to the savegame file. This method of
|
||||
* initializing the moons (rather than just setting them directly) is necessary
|
||||
* to make sure trammel and felucca stay in sync
|
||||
*/
|
||||
void initMoons();
|
||||
|
||||
/**
|
||||
* Updates the phases of the moons and shows
|
||||
* the visual moongates on the map, if desired
|
||||
*/
|
||||
void updateMoons(bool showmoongates);
|
||||
|
||||
void attack(Direction dir = DIR_NONE);
|
||||
};
|
||||
|
||||
extern GameController *g_game;
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
175
engines/ultima/ultima4/controllers/inn_controller.cpp
Normal file
175
engines/ultima/ultima4/controllers/inn_controller.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
/* 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/controllers/inn_controller.h"
|
||||
#include "ultima/ultima4/conversation/conversation.h"
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
#include "ultima/ultima4/map/city.h"
|
||||
#include "ultima/ultima4/map/mapmgr.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
InnController::InnController() {
|
||||
_map = nullptr;
|
||||
/*
|
||||
* Normally in cities, only one opponent per encounter; inn's
|
||||
* override this to get the regular encounter size.
|
||||
*/
|
||||
_forceStandardEncounterSize = true;
|
||||
}
|
||||
|
||||
void InnController::begin() {
|
||||
/* first, show the avatar before sleeping */
|
||||
gameUpdateScreen();
|
||||
|
||||
/* in the original, the vendor music plays straight through sleeping */
|
||||
if (settings._enhancements)
|
||||
g_music->fadeOut(INN_FADE_OUT_TIME); /* Fade volume out to ease into rest */
|
||||
|
||||
EventHandler::wait_msecs(INN_FADE_OUT_TIME);
|
||||
|
||||
/* show the sleeping avatar */
|
||||
g_context->_party->setTransport(g_context->_location->_map->_tileSet->getByName("corpse")->getId());
|
||||
gameUpdateScreen();
|
||||
|
||||
g_screen->screenDisableCursor();
|
||||
|
||||
EventHandler::wait_msecs(settings._innTime * 1000);
|
||||
|
||||
g_screen->screenEnableCursor();
|
||||
|
||||
/* restore the avatar to normal */
|
||||
g_context->_party->setTransport(g_context->_location->_map->_tileSet->getByName("avatar")->getId());
|
||||
gameUpdateScreen();
|
||||
|
||||
/* the party is always healed */
|
||||
heal();
|
||||
|
||||
/* Is there a special encounter during your stay? */
|
||||
// mwinterrowd suggested code, based on u4dos
|
||||
if (g_context->_party->member(0)->isDead()) {
|
||||
maybeMeetIsaac();
|
||||
} else {
|
||||
if (xu4_random(8) != 0) {
|
||||
maybeMeetIsaac();
|
||||
} else {
|
||||
maybeAmbush();
|
||||
}
|
||||
}
|
||||
|
||||
g_screen->screenMessage("\nMorning!\n");
|
||||
g_screen->screenPrompt();
|
||||
|
||||
g_music->fadeIn(INN_FADE_IN_TIME, true);
|
||||
}
|
||||
|
||||
bool InnController::heal() {
|
||||
// restore each party member to max mp, and restore some hp
|
||||
bool healed = false;
|
||||
for (int i = 0; i < g_context->_party->size(); i++) {
|
||||
PartyMember *m = g_context->_party->member(i);
|
||||
m->setMp(m->getMaxMp());
|
||||
if ((m->getHp() < m->getMaxHp()) && m->heal(HT_INNHEAL))
|
||||
healed = true;
|
||||
}
|
||||
|
||||
return healed;
|
||||
}
|
||||
|
||||
|
||||
void InnController::maybeMeetIsaac() {
|
||||
// Does Isaac the Ghost pay a visit to the Avatar?
|
||||
// if ((location == skara_brae) && (random(4) = 0) {
|
||||
// // create Isaac the Ghost
|
||||
// }
|
||||
if ((g_context->_location->_map->_id == 11) && (xu4_random(4) == 0)) {
|
||||
City *city = dynamic_cast<City *>(g_context->_location->_map);
|
||||
assert(city);
|
||||
|
||||
if (city->_extraDialogues.size() == 1 &&
|
||||
city->_extraDialogues[0]->getName() == "Isaac") {
|
||||
|
||||
Coords coords(27, xu4_random(3) + 10, g_context->_location->_coords.z);
|
||||
|
||||
// If Isaac is already around, just bring him back to the inn
|
||||
for (ObjectDeque::iterator i = g_context->_location->_map->_objects.begin();
|
||||
i != g_context->_location->_map->_objects.end();
|
||||
i++) {
|
||||
Person *p = dynamic_cast<Person *>(*i);
|
||||
if (p && p->getName() == "Isaac") {
|
||||
p->setCoords(coords);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, we need to create Isaac
|
||||
Person *isaac = new Person(creatureMgr->getById(GHOST_ID)->getTile());
|
||||
|
||||
isaac->setMovementBehavior(MOVEMENT_WANDER);
|
||||
|
||||
isaac->setDialogue(city->_extraDialogues[0]);
|
||||
isaac->getStart() = coords;
|
||||
isaac->setPrevTile(isaac->getTile());
|
||||
|
||||
// Add Isaac near the Avatar
|
||||
city->addPerson(isaac);
|
||||
|
||||
delete isaac;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InnController::maybeAmbush() {
|
||||
if (settings._innAlwaysCombat || (xu4_random(8) == 0)) {
|
||||
MapId mapid;
|
||||
Creature *creature;
|
||||
bool showMessage = true;
|
||||
|
||||
/* Rats seem much more rare than meeting rogues in the streets */
|
||||
if (xu4_random(4) == 0) {
|
||||
/* Rats! */
|
||||
mapid = MAP_BRICK_CON;
|
||||
creature = g_context->_location->_map->addCreature(creatureMgr->getById(RAT_ID), g_context->_location->_coords);
|
||||
} else {
|
||||
/* While strolling down the street, attacked by rogues! */
|
||||
mapid = MAP_INN_CON;
|
||||
creature = g_context->_location->_map->addCreature(creatureMgr->getById(ROGUE_ID), g_context->_location->_coords);
|
||||
g_screen->screenMessage("\nIn the middle of the night while out on a stroll...\n\n");
|
||||
showMessage = false;
|
||||
}
|
||||
|
||||
|
||||
_map = getCombatMap(mapMgr->get(mapid));
|
||||
g_game->setMap(_map, true, nullptr, this);
|
||||
|
||||
init(creature);
|
||||
showCombatMessage(showMessage);
|
||||
CombatController::begin();
|
||||
}
|
||||
}
|
||||
|
||||
void InnController::awardLoot() {
|
||||
// never get a chest from inn combat
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
46
engines/ultima/ultima4/controllers/inn_controller.h
Normal file
46
engines/ultima/ultima4/controllers/inn_controller.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_CONTROLLERS_INN_CONTROLLER_H
|
||||
#define ULTIMA4_CONTROLLERS_INN_CONTROLLER_H
|
||||
|
||||
#include "ultima/ultima4/controllers/combat_controller.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class InnController : public CombatController {
|
||||
public:
|
||||
InnController();
|
||||
|
||||
void begin() override;
|
||||
void awardLoot() override;
|
||||
|
||||
private:
|
||||
bool heal();
|
||||
void maybeMeetIsaac();
|
||||
void maybeAmbush();
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
1942
engines/ultima/ultima4/controllers/intro_controller.cpp
Normal file
1942
engines/ultima/ultima4/controllers/intro_controller.cpp
Normal file
File diff suppressed because it is too large
Load Diff
436
engines/ultima/ultima4/controllers/intro_controller.h
Normal file
436
engines/ultima/ultima4/controllers/intro_controller.h
Normal file
@@ -0,0 +1,436 @@
|
||||
/* 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_CONTROLLERS_INTRO_CONTROLLER_H
|
||||
#define ULTIMA4_CONTROLLERS_INTRO_CONTROLLER_H
|
||||
|
||||
#include "ultima/ultima4/controllers/controller.h"
|
||||
#include "ultima/ultima4/core/observer.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/views/menu.h"
|
||||
#include "ultima/ultima4/views/textview.h"
|
||||
#include "ultima/ultima4/views/imageview.h"
|
||||
#include "ultima/ultima4/views/tileview.h"
|
||||
#include "common/file.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class IntroObjectState;
|
||||
class Tile;
|
||||
|
||||
/**
|
||||
* Binary data loaded from the U4DOS title.exe file.
|
||||
*/
|
||||
class IntroBinData {
|
||||
private:
|
||||
// disallow assignments, copy construction
|
||||
IntroBinData(const IntroBinData &);
|
||||
const IntroBinData &operator=(const IntroBinData &);
|
||||
void openFile(Common::File &f, const Common::String &name);
|
||||
public:
|
||||
IntroBinData();
|
||||
~IntroBinData();
|
||||
|
||||
bool load();
|
||||
|
||||
Std::vector<MapTile> _introMap;
|
||||
byte *_sigData;
|
||||
byte *_scriptTable;
|
||||
Tile **_baseTileTable;
|
||||
byte *_beastie1FrameTable;
|
||||
byte *_beastie2FrameTable;
|
||||
Std::vector<Common::String> _introText;
|
||||
Std::vector<Common::String> _introQuestions;
|
||||
Std::vector<Common::String> _introGypsy;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Controls the title animation sequences, including the traditional
|
||||
* plotted "Lord British" signature, the pixelized fade-in of the
|
||||
* "Ultima IV" game title, as well as the other more simple animated
|
||||
* features, followed by the traditional animated map and "Journey
|
||||
* Onward" menu, plus the xU4-specific configuration menu.
|
||||
*
|
||||
* @todo
|
||||
* <ul>
|
||||
* <li>make initial menu a Menu too</li>
|
||||
* <li>get rid of mode and switch(mode) statements</li>
|
||||
* <li>get rid global intro instance -- should only need to be accessed from u4.cpp</li>
|
||||
* </ul>
|
||||
*/
|
||||
class IntroController : public Controller, public Observer<Menu *, MenuEvent &> {
|
||||
public:
|
||||
IntroController();
|
||||
~IntroController() override;
|
||||
|
||||
/**
|
||||
* Initializes intro state and loads in introduction graphics, text
|
||||
* and map data from title.exe.
|
||||
*/
|
||||
bool init();
|
||||
bool hasInitiatedNewGame();
|
||||
|
||||
/**
|
||||
* Frees up data not needed after introduction.
|
||||
*/
|
||||
void deleteIntro();
|
||||
|
||||
/**
|
||||
* Handles keystrokes during the introduction.
|
||||
*/
|
||||
bool keyPressed(int key) override;
|
||||
|
||||
/**
|
||||
* Mouse button was pressed
|
||||
*/
|
||||
bool mousePressed(const Common::Point &mousePos) override;
|
||||
|
||||
byte *getSigData();
|
||||
|
||||
/**
|
||||
* Paints the screen.
|
||||
*/
|
||||
void updateScreen();
|
||||
|
||||
/**
|
||||
* Timer callback for the intro sequence. Handles animating the intro
|
||||
* map, the beasties, etc..
|
||||
*/
|
||||
void timerFired() override;
|
||||
|
||||
/**
|
||||
* Preload map tiles
|
||||
*/
|
||||
void preloadMap();
|
||||
|
||||
/**
|
||||
* Update the screen when an observed menu is reset or has an item
|
||||
* activated.
|
||||
* TODO: Reduce duped code.
|
||||
*/
|
||||
void update(Menu *menu, MenuEvent &event) override;
|
||||
void updateConfMenu(MenuEvent &event);
|
||||
void updateVideoMenu(MenuEvent &event);
|
||||
void updateGfxMenu(MenuEvent &event);
|
||||
void updateSoundMenu(MenuEvent &event);
|
||||
void updateInputMenu(MenuEvent &event);
|
||||
void updateSpeedMenu(MenuEvent &event);
|
||||
void updateGameplayMenu(MenuEvent &event);
|
||||
void updateInterfaceMenu(MenuEvent &event);
|
||||
|
||||
//
|
||||
// Title methods
|
||||
//
|
||||
/**
|
||||
* Initialize the title elements
|
||||
*/
|
||||
void initTitles();
|
||||
|
||||
/**
|
||||
* Update the title element, drawing the appropriate frame of animation
|
||||
*/
|
||||
bool updateTitle();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Draws the small map on the intro screen.
|
||||
*/
|
||||
void drawMap();
|
||||
void drawMapStatic();
|
||||
void drawMapAnimated();
|
||||
|
||||
/**
|
||||
* Draws the animated beasts in the upper corners of the screen.
|
||||
*/
|
||||
void drawBeasties();
|
||||
|
||||
/**
|
||||
* Animates the "beasties". The animate intro image is made up frames
|
||||
* for the two creatures in the top left and top right corners of the
|
||||
* screen. This function draws the frame for the given beastie on the
|
||||
* screen. vertoffset is used lower the creatures down from the top
|
||||
* of the screen.
|
||||
*/
|
||||
void drawBeastie(int beast, int vertoffset, int frame);
|
||||
|
||||
/**
|
||||
* Animates the moongate in the tree intro image. There are two
|
||||
* overlays in the part of the image normally covered by the text. If
|
||||
* the frame parameter is "moongate", the moongate overlay is painted
|
||||
* over the image. If frame is "items", the second overlay is
|
||||
* painted: the circle without the moongate, but with a small white
|
||||
* dot representing the anhk and history book.
|
||||
*/
|
||||
void animateTree(const Common::String &frame);
|
||||
|
||||
/**
|
||||
* Draws the cards in the character creation sequence with the gypsy.
|
||||
*/
|
||||
void drawCard(int pos, int card);
|
||||
|
||||
/**
|
||||
* Draws the beads in the abacus during the character creation sequence
|
||||
*/
|
||||
void drawAbacusBeads(int row, int selectedVirtue, int rejectedVirtue);
|
||||
|
||||
/**
|
||||
* Initializes the question tree. The tree starts off with the first
|
||||
* eight entries set to the numbers 0-7 in a random order.
|
||||
*/
|
||||
void initQuestionTree();
|
||||
|
||||
/**
|
||||
* Updates the question tree with the given answer, and advances to
|
||||
* the next round.
|
||||
* @return true if all questions have been answered, false otherwise
|
||||
*/
|
||||
bool doQuestion(int answer);
|
||||
|
||||
/**
|
||||
* Build the initial avatar player record from the answers to the
|
||||
* gypsy's questions.
|
||||
*/
|
||||
void initPlayers(SaveGame *saveGame);
|
||||
|
||||
/**
|
||||
* Get the text for the question giving a choice between virtue v1 and
|
||||
* virtue v2 (zero based virtue index, starting at honesty).
|
||||
*/
|
||||
Common::String getQuestion(int v1, int v2);
|
||||
#ifdef IOS_ULTIMA4
|
||||
public:
|
||||
/**
|
||||
* Try to put the intro music back at just the correct moment on iOS;
|
||||
* don't play it at the very beginning.
|
||||
*/
|
||||
void tryTriggerIntroMusic();
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initiate a new savegame by reading the name, sex, then presenting a
|
||||
* series of questions to determine the class of the new character.
|
||||
*/
|
||||
void initiateNewGame();
|
||||
void finishInitiateGame(const Common::String &nameBuffer, SexType sex);
|
||||
|
||||
/**
|
||||
* Starts the gypsys questioning that eventually determines the new
|
||||
* characters class.
|
||||
*/
|
||||
void startQuestions();
|
||||
void showStory();
|
||||
|
||||
/**
|
||||
* Starts the game.
|
||||
*/
|
||||
void journeyOnward();
|
||||
|
||||
/**
|
||||
* Shows an about box.
|
||||
*/
|
||||
void about();
|
||||
#ifdef IOS_ULTIMA4
|
||||
private:
|
||||
#endif
|
||||
/**
|
||||
* Shows text in the question area.
|
||||
*/
|
||||
void showText(const Common::String &text);
|
||||
|
||||
/**
|
||||
* Run a menu and return when the menu has been closed. Screen
|
||||
* updates are handled by observing the menu.
|
||||
*/
|
||||
void runMenu(Menu *menu, TextView *view, bool withBeasties);
|
||||
|
||||
/**
|
||||
* The states of the intro.
|
||||
*/
|
||||
enum {
|
||||
INTRO_TITLES, // displaying the animated intro titles
|
||||
INTRO_MAP, // displaying the animated intro map
|
||||
INTRO_MENU, // displaying the main menu: journey onward, etc.
|
||||
INTRO_ABOUT
|
||||
} _mode;
|
||||
|
||||
enum MenuConstants {
|
||||
MI_CONF_VIDEO,
|
||||
MI_CONF_SOUND,
|
||||
MI_CONF_INPUT,
|
||||
MI_CONF_SPEED,
|
||||
MI_CONF_GAMEPLAY,
|
||||
MI_CONF_INTERFACE,
|
||||
MI_CONF_01,
|
||||
MI_VIDEO_CONF_GFX,
|
||||
MI_VIDEO_02,
|
||||
MI_VIDEO_03,
|
||||
MI_VIDEO_07,
|
||||
MI_VIDEO_08,
|
||||
MI_GFX_SCHEME,
|
||||
MI_GFX_TILE_TRANSPARENCY,
|
||||
MI_GFX_TILE_TRANSPARENCY_SHADOW_SIZE,
|
||||
MI_GFX_TILE_TRANSPARENCY_SHADOW_OPACITY,
|
||||
MI_GFX_RETURN,
|
||||
MI_SOUND_03,
|
||||
MI_INPUT_03,
|
||||
MI_SPEED_01,
|
||||
MI_SPEED_02,
|
||||
MI_SPEED_03,
|
||||
MI_SPEED_04,
|
||||
MI_SPEED_05,
|
||||
MI_SPEED_06,
|
||||
MI_SPEED_07,
|
||||
MI_GAMEPLAY_01,
|
||||
MI_GAMEPLAY_02,
|
||||
MI_GAMEPLAY_03,
|
||||
MI_GAMEPLAY_04,
|
||||
MI_GAMEPLAY_05,
|
||||
MI_GAMEPLAY_06,
|
||||
MI_INTERFACE_01,
|
||||
MI_INTERFACE_02,
|
||||
MI_INTERFACE_03,
|
||||
MI_INTERFACE_04,
|
||||
MI_INTERFACE_05,
|
||||
MI_INTERFACE_06,
|
||||
USE_SETTINGS = 0xFE,
|
||||
CANCEL = 0xFF
|
||||
};
|
||||
|
||||
ImageView _backgroundArea;
|
||||
TextView _menuArea;
|
||||
TextView _extendedMenuArea;
|
||||
TextView _questionArea;
|
||||
TileView _mapArea;
|
||||
Image *_mapScreen;
|
||||
|
||||
// menus
|
||||
Menu _mainMenu;
|
||||
Menu _confMenu;
|
||||
Menu _videoMenu;
|
||||
Menu _gfxMenu;
|
||||
Menu _soundMenu;
|
||||
Menu _inputMenu;
|
||||
Menu _speedMenu;
|
||||
Menu _gameplayMenu;
|
||||
Menu _interfaceMenu;
|
||||
|
||||
// data loaded in from title.exe
|
||||
IntroBinData *_binData;
|
||||
|
||||
// additional introduction state data
|
||||
Common::String _errorMessage;
|
||||
int _answerInd;
|
||||
int _questionRound;
|
||||
int _questionTree[15];
|
||||
int _beastie1Cycle;
|
||||
int _beastie2Cycle;
|
||||
int _beastieOffset;
|
||||
bool _beastiesVisible;
|
||||
int _sleepCycles;
|
||||
int _scrPos; /* current position in the script table */
|
||||
IntroObjectState *_objectStateTable;
|
||||
|
||||
bool _justInitiatedNewGame;
|
||||
Common::String _profileName;
|
||||
bool _useProfile;
|
||||
|
||||
//
|
||||
// Title defs, structs, methods, and data members
|
||||
//
|
||||
enum AnimType {
|
||||
SIGNATURE,
|
||||
AND,
|
||||
BAR,
|
||||
ORIGIN,
|
||||
PRESENT,
|
||||
TITLE,
|
||||
SUBTITLE,
|
||||
MAP
|
||||
};
|
||||
|
||||
struct AnimPlot {
|
||||
uint8 x, y;
|
||||
uint8 r, g, b, a;
|
||||
};
|
||||
|
||||
struct AnimElement {
|
||||
void shufflePlotData();
|
||||
|
||||
int _rx, _ry; // screen/source x and y
|
||||
int _rw, _rh; // source width and height
|
||||
AnimType _method; // render method
|
||||
int _animStep; // tracks animation position
|
||||
int _animStepMax;
|
||||
int _timeBase; // initial animation time
|
||||
uint32 _timeDelay; // delay before rendering begins
|
||||
int _timeDuration; // total animation time
|
||||
Image *_srcImage; // storage for the source image
|
||||
Image *_destImage; // storage for the animation frame
|
||||
Std::vector <AnimPlot> _plotData; // plot data
|
||||
bool _prescaled;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the intro element to the element list
|
||||
*/
|
||||
void addTitle(int x, int y, int w, int h, AnimType method, uint32 delay, int duration);
|
||||
|
||||
/**
|
||||
* The title element has finished drawing all frames, so delete, remove,
|
||||
* or free data that is no longer needed
|
||||
*/
|
||||
void compactTitle();
|
||||
|
||||
/**
|
||||
* Scale the animation canvas, then draw it to the screen
|
||||
*/
|
||||
void drawTitle();
|
||||
|
||||
/**
|
||||
* Get the source data for title elements that have already been initialized
|
||||
*/
|
||||
void getTitleSourceData();
|
||||
|
||||
/**
|
||||
* skip the remaining titles
|
||||
*/
|
||||
void skipTitles();
|
||||
|
||||
Std::vector<AnimElement> _titles; // list of title elements
|
||||
Std::vector<AnimElement>::iterator _title; // current title element
|
||||
|
||||
int _transparentIndex; // palette index for transparency
|
||||
RGBA _transparentColor; // palette color for transparency
|
||||
|
||||
bool _bSkipTitles;
|
||||
|
||||
// Temporary place-holder for settings changes
|
||||
SettingsData _settingsChanged;
|
||||
};
|
||||
|
||||
extern IntroController *g_intro;
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
120
engines/ultima/ultima4/controllers/key_handler_controller.cpp
Normal file
120
engines/ultima/ultima4/controllers/key_handler_controller.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
/* 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/controllers/key_handler_controller.h"
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
#include "ultima/ultima4/events/event_handler.h"
|
||||
#include "ultima/ultima4/game/context.h"
|
||||
#include "ultima/ultima4/ultima4.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
|
||||
KeyHandler::KeyHandler(Callback func, void *d, bool asyncronous) :
|
||||
_handler(func),
|
||||
_async(asyncronous),
|
||||
_data(d) {
|
||||
}
|
||||
|
||||
bool KeyHandler::globalHandler(int key) {
|
||||
if (key == Common::KEYCODE_F5) {
|
||||
(void)g_ultima->saveGameDialog();
|
||||
} else if (key == Common::KEYCODE_F7) {
|
||||
(void)g_ultima->loadGameDialog();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KeyHandler::defaultHandler(int key, void *data) {
|
||||
bool valid = true;
|
||||
|
||||
switch (key) {
|
||||
case '`':
|
||||
if (g_context && g_context->_location)
|
||||
debug(1, "x = %d, y = %d, level = %d, tile = %d (%s)\n", g_context->_location->_coords.x, g_context->_location->_coords.y, g_context->_location->_coords.z, g_context->_location->_map->translateToRawTileIndex(*g_context->_location->_map->tileAt(g_context->_location->_coords, WITH_OBJECTS)), g_context->_location->_map->tileTypeAt(g_context->_location->_coords, WITH_OBJECTS)->getName().c_str());
|
||||
break;
|
||||
default:
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool KeyHandler::ignoreKeys(int key, void *data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KeyHandler::handle(int key) {
|
||||
bool processed = false;
|
||||
if (!isKeyIgnored(key)) {
|
||||
processed = globalHandler(key);
|
||||
if (!processed)
|
||||
processed = _handler(key, _data);
|
||||
}
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
bool KeyHandler::isKeyIgnored(int key) {
|
||||
switch (key) {
|
||||
case Common::KEYCODE_RSHIFT:
|
||||
case Common::KEYCODE_LSHIFT:
|
||||
case Common::KEYCODE_RCTRL:
|
||||
case Common::KEYCODE_LCTRL:
|
||||
case Common::KEYCODE_RALT:
|
||||
case Common::KEYCODE_LALT:
|
||||
case Common::KEYCODE_RMETA:
|
||||
case Common::KEYCODE_LMETA:
|
||||
case Common::KEYCODE_TAB:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool KeyHandler::operator==(Callback cb) const {
|
||||
return (_handler == cb) ? true : false;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
KeyHandlerController::KeyHandlerController(KeyHandler *handler) {
|
||||
this->_handler = handler;
|
||||
}
|
||||
|
||||
KeyHandlerController::~KeyHandlerController() {
|
||||
delete _handler;
|
||||
}
|
||||
|
||||
bool KeyHandlerController::keyPressed(int key) {
|
||||
assertMsg(_handler != nullptr, "key handler must be initialized");
|
||||
return _handler->handle(key);
|
||||
}
|
||||
|
||||
KeyHandler *KeyHandlerController::getKeyHandler() {
|
||||
return _handler;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
119
engines/ultima/ultima4/controllers/key_handler_controller.h
Normal file
119
engines/ultima/ultima4/controllers/key_handler_controller.h
Normal file
@@ -0,0 +1,119 @@
|
||||
/* 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_CONTROLLERS_KEY_HANDLER_CONTROLLER_H
|
||||
#define ULTIMA4_CONTROLLERS_KEY_HANDLER_CONTROLLER_H
|
||||
|
||||
#include "ultima/ultima4/controllers/controller.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* A class for handling keystrokes.
|
||||
*/
|
||||
class KeyHandler {
|
||||
public:
|
||||
virtual ~KeyHandler() {}
|
||||
|
||||
/* Typedefs */
|
||||
typedef bool (*Callback)(int, void *);
|
||||
|
||||
/** Additional information to be passed as data param for read buffer key handler */
|
||||
typedef struct ReadBuffer {
|
||||
int (*_handleBuffer)(Common::String *);
|
||||
Common::String *_buffer;
|
||||
int _bufferLen;
|
||||
int _screenX, _screenY;
|
||||
} ReadBuffer;
|
||||
|
||||
/** Additional information to be passed as data param for get choice key handler */
|
||||
typedef struct GetChoice {
|
||||
Common::String _choices;
|
||||
int (*_handleChoice)(int);
|
||||
} GetChoice;
|
||||
|
||||
/* Constructors */
|
||||
KeyHandler(Callback func, void *data = nullptr, bool asyncronous = true);
|
||||
|
||||
/**
|
||||
* Handles any and all keystrokes.
|
||||
* Generally used to exit the application, switch applications,
|
||||
* minimize, maximize, etc.
|
||||
*/
|
||||
static bool globalHandler(int key);
|
||||
|
||||
/* Static default key handler functions */
|
||||
/**
|
||||
* A default key handler that should be valid everywhere
|
||||
*/
|
||||
static bool defaultHandler(int key, void *data);
|
||||
|
||||
/**
|
||||
* A key handler that ignores keypresses
|
||||
*/
|
||||
static bool ignoreKeys(int key, void *data);
|
||||
|
||||
/* Operators */
|
||||
bool operator==(Callback cb) const;
|
||||
|
||||
/* Member functions */
|
||||
/**
|
||||
* Handles a keypress.
|
||||
* First it makes sure the key combination is not ignored
|
||||
* by the current key handler. Then, it passes the keypress
|
||||
* through the global key handler. If the global handler
|
||||
* does not process the keystroke, then the key handler
|
||||
* handles it itself by calling its handler callback function.
|
||||
*/
|
||||
bool handle(int key);
|
||||
|
||||
/**
|
||||
* Returns true if the key or key combination is always ignored by xu4
|
||||
*/
|
||||
virtual bool isKeyIgnored(int key);
|
||||
|
||||
protected:
|
||||
Callback _handler;
|
||||
bool _async;
|
||||
void *_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* A controller that wraps a keyhander function. Keyhandlers are
|
||||
* deprecated -- please use a controller instead.
|
||||
*/
|
||||
class KeyHandlerController : public Controller {
|
||||
public:
|
||||
KeyHandlerController(KeyHandler *handler);
|
||||
~KeyHandlerController();
|
||||
|
||||
bool keyPressed(int key) override;
|
||||
KeyHandler *getKeyHandler();
|
||||
|
||||
private:
|
||||
KeyHandler *_handler;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
121
engines/ultima/ultima4/controllers/menu_controller.cpp
Normal file
121
engines/ultima/ultima4/controllers/menu_controller.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
/* 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/controllers/menu_controller.h"
|
||||
#include "ultima/ultima4/events/event_handler.h"
|
||||
#include "ultima/ultima4/views/menu.h"
|
||||
#include "ultima/ultima4/views/textview.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
MenuController::MenuController(Menu *menu, TextView *view) :
|
||||
WaitableController<void *>(nullptr) {
|
||||
this->_menu = menu;
|
||||
this->_view = view;
|
||||
}
|
||||
|
||||
void MenuController::setActive() {
|
||||
MetaEngine::setKeybindingMode(KBMODE_MENU);
|
||||
}
|
||||
|
||||
void MenuController::keybinder(KeybindingAction action) {
|
||||
bool cursorOn = _view->getCursorEnabled();
|
||||
|
||||
if (cursorOn)
|
||||
_view->disableCursor();
|
||||
|
||||
switch (action) {
|
||||
case KEYBIND_UP:
|
||||
_menu->prev();
|
||||
break;
|
||||
case KEYBIND_DOWN:
|
||||
_menu->next();
|
||||
break;
|
||||
case KEYBIND_LEFT:
|
||||
case KEYBIND_RIGHT:
|
||||
case KEYBIND_INTERACT: {
|
||||
MenuEvent::Type menuAction = MenuEvent::ACTIVATE;
|
||||
|
||||
if (action == KEYBIND_LEFT)
|
||||
menuAction = MenuEvent::DECREMENT;
|
||||
else if (action == KEYBIND_RIGHT)
|
||||
menuAction = MenuEvent::INCREMENT;
|
||||
_menu->activateItem(-1, menuAction);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_menu->show(_view);
|
||||
|
||||
if (cursorOn)
|
||||
_view->enableCursor();
|
||||
_view->update();
|
||||
|
||||
if (_menu->getClosed())
|
||||
doneWaiting();
|
||||
}
|
||||
|
||||
bool MenuController::keyPressed(int key) {
|
||||
bool handled = true;
|
||||
bool cursorOn = _view->getCursorEnabled();
|
||||
|
||||
if (cursorOn)
|
||||
_view->disableCursor();
|
||||
|
||||
handled = _menu->activateItemByShortcut(key, MenuEvent::ACTIVATE);
|
||||
|
||||
_menu->show(_view);
|
||||
|
||||
if (cursorOn)
|
||||
_view->enableCursor();
|
||||
_view->update();
|
||||
|
||||
if (_menu->getClosed())
|
||||
doneWaiting();
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
bool MenuController::mousePressed(const Common::Point &mousePos) {
|
||||
bool cursorOn = _view->getCursorEnabled();
|
||||
|
||||
if (cursorOn)
|
||||
_view->disableCursor();
|
||||
|
||||
_menu->activateItemAtPos(_view, mousePos);
|
||||
|
||||
_menu->show(_view);
|
||||
|
||||
if (cursorOn)
|
||||
_view->enableCursor();
|
||||
_view->update();
|
||||
|
||||
if (_menu->getClosed())
|
||||
doneWaiting();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
68
engines/ultima/ultima4/controllers/menu_controller.h
Normal file
68
engines/ultima/ultima4/controllers/menu_controller.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* 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_CONTROLLERS_MENU_CONTROLLER_H
|
||||
#define ULTIMA4_CONTROLLERS_MENU_CONTROLLER_H
|
||||
|
||||
#include "ultima/ultima4/controllers/controller.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class Menu;
|
||||
class TextView;
|
||||
|
||||
/**
|
||||
* This class controls a menu. The value field of WaitableController
|
||||
* isn't used.
|
||||
*/
|
||||
class MenuController : public WaitableController<void *> {
|
||||
public:
|
||||
MenuController(Menu *menu, TextView *view);
|
||||
|
||||
/**
|
||||
* Called when a controller is made active
|
||||
*/
|
||||
void setActive() override;
|
||||
|
||||
/**
|
||||
* Handles keybinder actions
|
||||
*/
|
||||
void keybinder(KeybindingAction action) override;
|
||||
|
||||
/**
|
||||
* Key was pressed
|
||||
*/
|
||||
bool keyPressed(int key) override;
|
||||
|
||||
/**
|
||||
* Mouse button was pressed
|
||||
*/
|
||||
bool mousePressed(const Common::Point &mousePos) override;
|
||||
protected:
|
||||
Menu *_menu;
|
||||
TextView *_view;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,72 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/controllers/read_choice_controller.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
|
||||
ReadChoiceController::ReadChoiceController(const Common::String &choices) :
|
||||
WaitableController<int>(-1) {
|
||||
_choices = choices;
|
||||
}
|
||||
|
||||
bool ReadChoiceController::keyPressed(int key) {
|
||||
// Common::isUpper() accepts 1-byte characters, yet the modifier keys
|
||||
// (ALT, SHIFT, ETC) produce values beyond 255
|
||||
if ((key <= 0x7F) && (Common::isUpper(key)))
|
||||
key = tolower(key);
|
||||
|
||||
_value = key;
|
||||
|
||||
if (_choices.empty() || _choices.findFirstOf(_value) < _choices.size()) {
|
||||
// If the value is printable, display it
|
||||
if (!Common::isSpace(key))
|
||||
g_screen->screenMessage("%c", toupper(key));
|
||||
doneWaiting();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ReadChoiceController::keybinder(KeybindingAction action) {
|
||||
if (action == KEYBIND_ESCAPE && _choices.contains('\x1B')) {
|
||||
_value = 27;
|
||||
doneWaiting();
|
||||
} else {
|
||||
WaitableController<int>::keybinder(action);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char ReadChoiceController::get(const Common::String &choices, EventHandler *eh) {
|
||||
if (!eh)
|
||||
eh = eventHandler;
|
||||
|
||||
ReadChoiceController ctrl(choices);
|
||||
eh->pushController(&ctrl);
|
||||
return ctrl.waitFor();
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
49
engines/ultima/ultima4/controllers/read_choice_controller.h
Normal file
49
engines/ultima/ultima4/controllers/read_choice_controller.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_CONTROLLERS_READ_CHOICE_CONTROLLER_H
|
||||
#define ULTIMA4_CONTROLLERS_READ_CHOICE_CONTROLLER_H
|
||||
|
||||
#include "ultima/ultima4/controllers/controller.h"
|
||||
#include "ultima/ultima4/events/event_handler.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* A controller to read a single key from a provided list.
|
||||
*/
|
||||
class ReadChoiceController : public WaitableController<int> {
|
||||
public:
|
||||
ReadChoiceController(const Common::String &choices);
|
||||
bool keyPressed(int key) override;
|
||||
void keybinder(KeybindingAction action) override;
|
||||
|
||||
static char get(const Common::String &choices, EventHandler *eh = nullptr);
|
||||
|
||||
protected:
|
||||
Common::String _choices;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
64
engines/ultima/ultima4/controllers/read_dir_controller.cpp
Normal file
64
engines/ultima/ultima4/controllers/read_dir_controller.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
/* 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/controllers/read_dir_controller.h"
|
||||
#include "ultima/ultima4/map/direction.h"
|
||||
#include "ultima/ultima4/metaengine.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
|
||||
ReadDirController::ReadDirController() : WaitableController<Direction>(DIR_NONE) {
|
||||
}
|
||||
|
||||
void ReadDirController::setActive() {
|
||||
// Special mode for inputing directions
|
||||
MetaEngine::setKeybindingMode(KBMODE_DIRECTION);
|
||||
}
|
||||
|
||||
void ReadDirController::keybinder(KeybindingAction action) {
|
||||
switch (action) {
|
||||
case KEYBIND_UP:
|
||||
_value = DIR_NORTH;
|
||||
break;
|
||||
case KEYBIND_DOWN:
|
||||
_value = DIR_SOUTH;
|
||||
break;
|
||||
case KEYBIND_LEFT:
|
||||
_value = DIR_WEST;
|
||||
break;
|
||||
case KEYBIND_RIGHT:
|
||||
_value = DIR_EAST;
|
||||
break;
|
||||
case KEYBIND_ESCAPE:
|
||||
_value = DIR_NONE;
|
||||
doneWaiting();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
doneWaiting();
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
52
engines/ultima/ultima4/controllers/read_dir_controller.h
Normal file
52
engines/ultima/ultima4/controllers/read_dir_controller.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* 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_CONTROLLERS_READ_DIR_CONTROLLER_H
|
||||
#define ULTIMA4_CONTROLLERS_READ_DIR_CONTROLLER_H
|
||||
|
||||
#include "ultima/ultima4/controllers/controller.h"
|
||||
#include "ultima/ultima4/map/direction.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* A controller to read a direction enter with the arrow keys.
|
||||
*/
|
||||
class ReadDirController : public WaitableController<Direction> {
|
||||
public:
|
||||
ReadDirController();
|
||||
|
||||
/**
|
||||
* Called when a controller is made active
|
||||
*/
|
||||
void setActive() override;
|
||||
|
||||
/**
|
||||
* Handles keybinder actions
|
||||
*/
|
||||
void keybinder(KeybindingAction action) override;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
45
engines/ultima/ultima4/controllers/read_int_controller.cpp
Normal file
45
engines/ultima/ultima4/controllers/read_int_controller.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/controllers/read_int_controller.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
ReadIntController::ReadIntController(int maxlen, int screenX, int screenY) :
|
||||
ReadStringController(maxlen, screenX, screenY, "0123456789 \n\r\010") {}
|
||||
|
||||
int ReadIntController::get(int maxlen, int screenX, int screenY, EventHandler *eh) {
|
||||
if (!eh)
|
||||
eh = eventHandler;
|
||||
|
||||
ReadIntController ctrl(maxlen, screenX, screenY);
|
||||
eh->pushController(&ctrl);
|
||||
ctrl.waitFor();
|
||||
return ctrl.getInt();
|
||||
}
|
||||
|
||||
int ReadIntController::getInt() const {
|
||||
return static_cast<int>(strtol(_value.c_str(), nullptr, 10));
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
45
engines/ultima/ultima4/controllers/read_int_controller.h
Normal file
45
engines/ultima/ultima4/controllers/read_int_controller.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_CONTROLLERS_READ_INT_CONTROLLER_H
|
||||
#define ULTIMA4_CONTROLLERS_READ_INT_CONTROLLER_H
|
||||
|
||||
#include "ultima/ultima4/controllers/read_string_controller.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* A controller to read a integer, terminated by the enter key.
|
||||
* Non-numeric keys are ignored.
|
||||
*/
|
||||
class ReadIntController : public ReadStringController {
|
||||
public:
|
||||
ReadIntController(int maxlen, int screenX, int screenY);
|
||||
|
||||
static int get(int maxlen, int screenX, int screenY, EventHandler *eh = nullptr);
|
||||
int getInt() const;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,63 @@
|
||||
/* 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/controllers/read_player_controller.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/ultima4.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
ReadPlayerController::ReadPlayerController() : ReadChoiceController("12345678 \033\n") {
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::beginCharacterChoiceDialog();
|
||||
#endif
|
||||
}
|
||||
|
||||
ReadPlayerController::~ReadPlayerController() {
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::endCharacterChoiceDialog();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ReadPlayerController::keyPressed(int key) {
|
||||
bool valid = ReadChoiceController::keyPressed(key);
|
||||
if (valid) {
|
||||
if (_value < '1' ||
|
||||
_value > ('0' + g_ultima->_saveGame->_members))
|
||||
_value = '0';
|
||||
} else {
|
||||
_value = '0';
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
int ReadPlayerController::getPlayer() {
|
||||
return _value == 27 ? -1 : _value - '1';
|
||||
}
|
||||
|
||||
int ReadPlayerController::waitFor() {
|
||||
ReadChoiceController::waitFor();
|
||||
return getPlayer();
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
46
engines/ultima/ultima4/controllers/read_player_controller.h
Normal file
46
engines/ultima/ultima4/controllers/read_player_controller.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_CONTROLLERS_READ_PLAYER_CONTROLLER_H
|
||||
#define ULTIMA4_CONTROLLERS_READ_PLAYER_CONTROLLER_H
|
||||
|
||||
#include "ultima/ultima4/controllers/read_choice_controller.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* A controller to read a player number.
|
||||
*/
|
||||
class ReadPlayerController : public ReadChoiceController {
|
||||
public:
|
||||
ReadPlayerController();
|
||||
~ReadPlayerController();
|
||||
bool keyPressed(int key) override;
|
||||
|
||||
int getPlayer();
|
||||
int waitFor() override;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
112
engines/ultima/ultima4/controllers/read_string_controller.cpp
Normal file
112
engines/ultima/ultima4/controllers/read_string_controller.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
/* 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/controllers/read_string_controller.h"
|
||||
#include "ultima/ultima4/game/context.h"
|
||||
#include "ultima/ultima4/gfx/screen.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
ReadStringController::ReadStringController(int maxlen, int screenX, int screenY,
|
||||
const Common::String &accepted_chars) : WaitableController<Common::String>("") {
|
||||
_maxLen = maxlen;
|
||||
_screenX = screenX;
|
||||
_screenY = screenY;
|
||||
_view = nullptr;
|
||||
_accepted = accepted_chars;
|
||||
}
|
||||
|
||||
ReadStringController::ReadStringController(int maxlen, TextView *view,
|
||||
const Common::String &accepted_chars) : WaitableController<Common::String>("") {
|
||||
_maxLen = maxlen;
|
||||
_screenX = view->getCursorX();
|
||||
_screenY = view->getCursorY();
|
||||
_view = view;
|
||||
_accepted = accepted_chars;
|
||||
}
|
||||
|
||||
bool ReadStringController::keyPressed(int key) {
|
||||
int valid = true, len = _value.size();
|
||||
size_t pos = Common::String::npos;
|
||||
|
||||
if (key < 0x80)
|
||||
pos = _accepted.findFirstOf(key);
|
||||
|
||||
if (pos != Common::String::npos) {
|
||||
if (key == Common::KEYCODE_BACKSPACE) {
|
||||
if (len > 0) {
|
||||
/* remove the last character */
|
||||
_value.erase(len - 1, 1);
|
||||
|
||||
if (_view) {
|
||||
_view->textAt(_screenX + len - 1, _screenY, " ");
|
||||
_view->setCursorPos(_screenX + len - 1, _screenY, true);
|
||||
} else {
|
||||
g_screen->screenHideCursor();
|
||||
g_screen->screenTextAt(_screenX + len - 1, _screenY, " ");
|
||||
g_screen->screenSetCursorPos(_screenX + len - 1, _screenY);
|
||||
g_screen->screenShowCursor();
|
||||
}
|
||||
}
|
||||
} else if (key == '\n' || key == '\r') {
|
||||
doneWaiting();
|
||||
} else if (len < _maxLen) {
|
||||
/* add a character to the end */
|
||||
_value += key;
|
||||
|
||||
if (_view) {
|
||||
_view->textAt(_screenX + len, _screenY, "%c", key);
|
||||
} else {
|
||||
g_screen->screenHideCursor();
|
||||
g_screen->screenTextAt(_screenX + len, _screenY, "%c", key);
|
||||
g_screen->screenSetCursorPos(_screenX + len + 1, _screenY);
|
||||
g_context->_col = len + 1;
|
||||
g_screen->screenShowCursor();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
return valid || KeyHandler::defaultHandler(key, nullptr);
|
||||
}
|
||||
|
||||
Common::String ReadStringController::get(int maxlen, int screenX, int screenY, EventHandler *eh) {
|
||||
if (!eh)
|
||||
eh = eventHandler;
|
||||
|
||||
ReadStringController ctrl(maxlen, screenX, screenY);
|
||||
eh->pushController(&ctrl);
|
||||
return ctrl.waitFor();
|
||||
}
|
||||
|
||||
Common::String ReadStringController::get(int maxlen, TextView *view, EventHandler *eh) {
|
||||
if (!eh)
|
||||
eh = eventHandler;
|
||||
|
||||
ReadStringController ctrl(maxlen, view);
|
||||
eh->pushController(&ctrl);
|
||||
return ctrl.waitFor();
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
64
engines/ultima/ultima4/controllers/read_string_controller.h
Normal file
64
engines/ultima/ultima4/controllers/read_string_controller.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/* 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_CONTROLLERS_READ_STRING_CONTROLLER_H
|
||||
#define ULTIMA4_CONTROLLERS_READ_STRING_CONTROLLER_H
|
||||
|
||||
#include "ultima/ultima4/controllers/controller.h"
|
||||
#include "ultima/ultima4/events/event_handler.h"
|
||||
#include "ultima/ultima4/views/textview.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* A controller to read a Common::String, terminated by the enter key.
|
||||
*/
|
||||
class ReadStringController : public WaitableController<Common::String> {
|
||||
public:
|
||||
/**
|
||||
* @param maxlen the maximum length of the Common::String
|
||||
* @param screenX the screen column where to begin input
|
||||
* @param screenY the screen row where to begin input
|
||||
* @param accepted_chars a Common::String characters to be accepted for input
|
||||
*/
|
||||
ReadStringController(int maxlen, int screenX, int screenY, const Common::String &accepted_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 \n\r\010");
|
||||
ReadStringController(int maxlen, TextView *view, const Common::String &accepted_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 \n\r\010");
|
||||
bool keyPressed(int key) override;
|
||||
|
||||
static Common::String get(int maxlen, int screenX, int screenY, EventHandler *eh = nullptr);
|
||||
static Common::String get(int maxlen, TextView *view, EventHandler *eh = nullptr);
|
||||
#ifdef IOS_ULTIMA4
|
||||
void setValue(const Common::String &utf8StringValue) {
|
||||
value = utf8StringValue;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
int _maxLen, _screenX, _screenY;
|
||||
TextView *_view;
|
||||
Common::String _accepted;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,96 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/controllers/reagents_menu_controller.h"
|
||||
#include "ultima/ultima4/views/menu.h"
|
||||
#include "ultima/ultima4/game/spell.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
bool ReagentsMenuController::keyPressed(int key) {
|
||||
switch (key) {
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'e':
|
||||
case 'f':
|
||||
case 'g':
|
||||
case 'h': {
|
||||
// Select the corresponding reagent (if visible)
|
||||
Menu::MenuItemList::iterator mi = _menu->getById(key - 'a');
|
||||
if ((*mi)->isVisible()) {
|
||||
_menu->setCurrent(_menu->getById(key - 'a'));
|
||||
keyPressed(Common::KEYCODE_SPACE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return MenuController::keyPressed(key);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReagentsMenuController::keybinder(KeybindingAction action) {
|
||||
switch (action) {
|
||||
case KEYBIND_ESCAPE:
|
||||
_ingredients->revert();
|
||||
eventHandler->setControllerDone();
|
||||
break;
|
||||
|
||||
case KEYBIND_UP:
|
||||
_menu->prev();
|
||||
break;
|
||||
|
||||
case KEYBIND_DOWN:
|
||||
_menu->next();
|
||||
break;
|
||||
|
||||
case KEYBIND_LEFT:
|
||||
case KEYBIND_RIGHT:
|
||||
if (_menu->isVisible()) {
|
||||
MenuItem *item = *_menu->getCurrent();
|
||||
|
||||
// change whether or not it's selected
|
||||
item->setSelected(!item->isSelected());
|
||||
|
||||
if (item->isSelected())
|
||||
_ingredients->addReagent((Reagent)item->getId());
|
||||
else
|
||||
_ingredients->removeReagent((Reagent)item->getId());
|
||||
}
|
||||
break;
|
||||
|
||||
case KEYBIND_INTERACT:
|
||||
eventHandler->setControllerDone();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
@@ -0,0 +1,54 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_CONTROLLERS_REAGENTS_MENU_CONTROLLER_H
|
||||
#define ULTIMA4_CONTROLLERS_REAGENTS_MENU_CONTROLLER_H
|
||||
|
||||
#include "ultima/ultima4/controllers/menu_controller.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class Ingredients;
|
||||
|
||||
/**
|
||||
* Controller for the reagents menu used when mixing spells. Fills
|
||||
* the passed in Ingredients with the selected reagents.
|
||||
*/
|
||||
class ReagentsMenuController : public MenuController {
|
||||
public:
|
||||
ReagentsMenuController(Menu *menu, Ingredients *i, TextView *view) : MenuController(menu, view), _ingredients(i) { }
|
||||
|
||||
/**
|
||||
* Handles spell mixing for the Ultima V-style menu-system
|
||||
*/
|
||||
bool keyPressed(int key) override;
|
||||
|
||||
void keybinder(KeybindingAction action) override;
|
||||
|
||||
private:
|
||||
Ingredients *_ingredients;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
51
engines/ultima/ultima4/controllers/wait_controller.cpp
Normal file
51
engines/ultima/ultima4/controllers/wait_controller.cpp
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/controllers/wait_controller.h"
|
||||
#include "ultima/ultima4/events/event_handler.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
WaitController::WaitController(uint c) : Controller(), _cycles(c), _current(0) {
|
||||
}
|
||||
|
||||
void WaitController::timerFired() {
|
||||
if (++_current >= _cycles) {
|
||||
_current = 0;
|
||||
eventHandler->setControllerDone(true);
|
||||
}
|
||||
}
|
||||
|
||||
bool WaitController::keyPressed(int key) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void WaitController::wait() {
|
||||
Controller_startWait();
|
||||
}
|
||||
|
||||
void WaitController::setCycles(int c) {
|
||||
_cycles = c;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
51
engines/ultima/ultima4/controllers/wait_controller.h
Normal file
51
engines/ultima/ultima4/controllers/wait_controller.h
Normal 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 ULTIMA4_CONTROLLERS_WAIT_CONTROLLER_H
|
||||
#define ULTIMA4_CONTROLLERS_WAIT_CONTROLLER_H
|
||||
|
||||
#include "ultima/ultima4/controllers/controller.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* A controller to pause for a given length of time, ignoring all
|
||||
* keyboard input.
|
||||
*/
|
||||
class WaitController : public Controller {
|
||||
public:
|
||||
WaitController(uint cycles);
|
||||
bool keyPressed(int key) override;
|
||||
void timerFired() override;
|
||||
|
||||
void wait();
|
||||
void setCycles(int c);
|
||||
|
||||
private:
|
||||
uint _cycles;
|
||||
uint _current;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
71
engines/ultima/ultima4/controllers/ztats_controller.cpp
Normal file
71
engines/ultima/ultima4/controllers/ztats_controller.cpp
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/controllers/ztats_controller.h"
|
||||
#include "ultima/ultima4/events/event_handler.h"
|
||||
#include "ultima/ultima4/game/context.h"
|
||||
#include "ultima/ultima4/views/stats.h"
|
||||
#include "ultima/ultima4/ultima4.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
bool ZtatsController::keyPressed(int key) {
|
||||
switch (key) {
|
||||
case Common::KEYCODE_RETURN:
|
||||
keybinder(KEYBIND_ESCAPE);
|
||||
return true;
|
||||
case Common::KEYCODE_UP:
|
||||
case Common::KEYCODE_LEFT:
|
||||
g_context->_stats->prevItem();
|
||||
return true;
|
||||
case Common::KEYCODE_DOWN:
|
||||
case Common::KEYCODE_RIGHT:
|
||||
g_context->_stats->nextItem();
|
||||
return true;
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
if (g_ultima->_saveGame->_members >= key - '0')
|
||||
g_context->_stats->setView(StatsView(STATS_CHAR1 + key - '1'));
|
||||
return true;
|
||||
case '0':
|
||||
g_context->_stats->setView(StatsView(STATS_WEAPONS));
|
||||
return true;
|
||||
default:
|
||||
return KeyHandler::defaultHandler(key, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void ZtatsController::keybinder(KeybindingAction action) {
|
||||
if (action == KEYBIND_ESCAPE) {
|
||||
g_context->_stats->setView(StatsView(STATS_PARTY_OVERVIEW));
|
||||
doneWaiting();
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
44
engines/ultima/ultima4/controllers/ztats_controller.h
Normal file
44
engines/ultima/ultima4/controllers/ztats_controller.h
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_CONTROLLERS_ZTATS_CONTROLLER_H
|
||||
#define ULTIMA4_CONTROLLERS_ZTATS_CONTROLLER_H
|
||||
|
||||
#include "ultima/ultima4/controllers/controller.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* Controls interaction while Ztats are being displayed.
|
||||
*/
|
||||
class ZtatsController : public WaitableController<void *> {
|
||||
public:
|
||||
ZtatsController() : WaitableController<void *>(nullptr) {}
|
||||
|
||||
bool keyPressed(int key) override;
|
||||
void keybinder(KeybindingAction action) override;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
303
engines/ultima/ultima4/conversation/conversation.cpp
Normal file
303
engines/ultima/ultima4/conversation/conversation.cpp
Normal file
@@ -0,0 +1,303 @@
|
||||
/* 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/conversation/conversation.h"
|
||||
#include "ultima/ultima4/game/person.h"
|
||||
#include "ultima/ultima4/game/script.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
const uint Conversation::BUFFERLEN = 16;
|
||||
ResponseParts *g_responseParts;
|
||||
|
||||
ResponseParts::ResponseParts() :
|
||||
NONE("<NONE>", "", true),
|
||||
ASK("<ASK>", "", true),
|
||||
END("<END>", "", true),
|
||||
ATTACK("<ATTACK>", "", true),
|
||||
BRAGGED("<BRAGGED>", "", true),
|
||||
HUMBLE("<HUMBLE>", "", true),
|
||||
ADVANCELEVELS("<ADVANCELEVELS>", "", true),
|
||||
HEALCONFIRM("<HEALCONFIRM>", "", true),
|
||||
STARTMUSIC_LB("<STARTMUSIC_LB>", "", true),
|
||||
STARTMUSIC_HW("<STARTMUSIC_HW>", "", true),
|
||||
STOPMUSIC("<STOPMUSIC>", "", true),
|
||||
HAWKWIND("<HAWKWIND>", "", true) {
|
||||
g_responseParts = this;
|
||||
}
|
||||
|
||||
ResponseParts::~ResponseParts() {
|
||||
g_responseParts = nullptr;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
Response::Response(const Common::String &response) : _references(0) {
|
||||
add(response);
|
||||
}
|
||||
|
||||
void Response::add(const ResponsePart &part) {
|
||||
_parts.push_back(part);
|
||||
}
|
||||
|
||||
const Std::vector<ResponsePart> &Response::getParts() const {
|
||||
return _parts;
|
||||
}
|
||||
|
||||
Response::operator Common::String() const {
|
||||
Common::String result;
|
||||
for (const auto &i : _parts) {
|
||||
result += i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Response *Response::addref() {
|
||||
_references++;
|
||||
return this;
|
||||
}
|
||||
|
||||
void Response::release() {
|
||||
_references--;
|
||||
if (_references <= 0)
|
||||
delete this;
|
||||
}
|
||||
|
||||
ResponsePart::ResponsePart(const Common::String &value, const Common::String &arg, bool command) {
|
||||
_value = value;
|
||||
_arg = arg;
|
||||
_command = command;
|
||||
}
|
||||
|
||||
ResponsePart::operator Common::String() const {
|
||||
return _value;
|
||||
}
|
||||
|
||||
bool ResponsePart::operator==(const ResponsePart &rhs) const {
|
||||
return _value == rhs._value;
|
||||
}
|
||||
|
||||
bool ResponsePart::isCommand() const {
|
||||
return _command;
|
||||
}
|
||||
|
||||
DynamicResponse::DynamicResponse(Response * (*generator)(const DynamicResponse *), const Common::String ¶m) :
|
||||
Response(""), _param(param) {
|
||||
_generator = generator;
|
||||
_currentResponse = nullptr;
|
||||
}
|
||||
|
||||
DynamicResponse::~DynamicResponse() {
|
||||
if (_currentResponse)
|
||||
delete _currentResponse;
|
||||
}
|
||||
|
||||
const Std::vector<ResponsePart> &DynamicResponse::getParts() const {
|
||||
// blah, must cast away constness
|
||||
const_cast<DynamicResponse *>(this)->_currentResponse = (*_generator)(this);
|
||||
return _currentResponse->getParts();
|
||||
}
|
||||
|
||||
/*
|
||||
* Dialogue::Question class
|
||||
*/
|
||||
Dialogue::Question::Question(const Common::String &txt, Response *yes, Response *no) :
|
||||
_text(txt), _yesResp(yes->addref()), _noResp(no->addref()) {}
|
||||
|
||||
Common::String Dialogue::Question::getText() {
|
||||
return _text;
|
||||
}
|
||||
|
||||
Response *Dialogue::Question::getResponse(bool yes) {
|
||||
if (yes)
|
||||
return _yesResp;
|
||||
return _noResp;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Dialogue::Keyword class
|
||||
*/
|
||||
Dialogue::Keyword::Keyword(const Common::String &kw, Response *resp) :
|
||||
_keyword(kw), _response(resp->addref()) {
|
||||
trim(_keyword);
|
||||
lowercase(_keyword);
|
||||
}
|
||||
|
||||
Dialogue::Keyword::Keyword(const Common::String &kw, const Common::String &resp) :
|
||||
_keyword(kw), _response((new Response(resp))->addref()) {
|
||||
trim(_keyword);
|
||||
lowercase(_keyword);
|
||||
}
|
||||
|
||||
Dialogue::Keyword::~Keyword() {
|
||||
_response->release();
|
||||
}
|
||||
|
||||
bool Dialogue::Keyword::operator==(const Common::String &kw) const {
|
||||
// minimum 4-character "guessing"
|
||||
int testLen = (_keyword.size() < 4) ? _keyword.size() : 4;
|
||||
|
||||
// exception: empty keyword only matches empty Common::String (alias for 'bye')
|
||||
if (testLen == 0 && kw.size() > 0)
|
||||
return false;
|
||||
|
||||
if (scumm_strnicmp(kw.c_str(), _keyword.c_str(), testLen) == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dialogue class
|
||||
*/
|
||||
|
||||
Dialogue::Dialogue()
|
||||
: _intro(nullptr)
|
||||
, _longIntro(nullptr)
|
||||
, _defaultAnswer(nullptr)
|
||||
, _question(nullptr) {
|
||||
}
|
||||
|
||||
Dialogue::~Dialogue() {
|
||||
for (auto &i : _keywords) {
|
||||
delete i._value;
|
||||
}
|
||||
}
|
||||
|
||||
void Dialogue::addKeyword(const Common::String &kw, Response *response) {
|
||||
if (_keywords.find(kw) != _keywords.end())
|
||||
delete _keywords[kw];
|
||||
|
||||
_keywords[kw] = new Keyword(kw, response);
|
||||
}
|
||||
|
||||
Dialogue::Keyword *Dialogue::operator[](const Common::String &kw) {
|
||||
KeywordMap::iterator i = _keywords.find(kw);
|
||||
|
||||
// If they entered the keyword verbatim, return it!
|
||||
if (i != _keywords.end())
|
||||
return i->_value;
|
||||
// Otherwise, go find one that fits the description.
|
||||
else {
|
||||
for (i = _keywords.begin(); i != _keywords.end(); i++) {
|
||||
if ((*i->_value) == kw)
|
||||
return i->_value;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ResponsePart &Dialogue::getAction() const {
|
||||
int prob = xu4_random(0x100);
|
||||
|
||||
/* Does the person turn away from/attack you? */
|
||||
if (prob >= _turnAwayProb)
|
||||
return g_responseParts->NONE;
|
||||
else {
|
||||
if (_attackProb - prob < 0x40)
|
||||
return g_responseParts->END;
|
||||
else
|
||||
return g_responseParts->ATTACK;
|
||||
}
|
||||
}
|
||||
|
||||
Common::String Dialogue::dump(const Common::String &arg) {
|
||||
Common::String result;
|
||||
if (arg == "") {
|
||||
result = "keywords:\n";
|
||||
for (const auto &i : _keywords) {
|
||||
result += i._key + "\n";
|
||||
}
|
||||
} else {
|
||||
if (_keywords.find(arg) != _keywords.end())
|
||||
result = static_cast<Common::String>(*_keywords[arg]->getResponse());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Conversation class
|
||||
*/
|
||||
|
||||
Conversation::Conversation() : _state(INTRO), _script(new Script()),
|
||||
_question(nullptr), _quant(0), _player(0), _price(0) {
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::incrementConversationCount();
|
||||
#endif
|
||||
}
|
||||
|
||||
Conversation::~Conversation() {
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::decrementConversationCount();
|
||||
#endif
|
||||
delete _script;
|
||||
}
|
||||
|
||||
Conversation::InputType Conversation::getInputRequired(int *bufferlen) {
|
||||
switch (_state) {
|
||||
case BUY_QUANTITY:
|
||||
case SELL_QUANTITY: {
|
||||
*bufferlen = 2;
|
||||
return INPUT_STRING;
|
||||
}
|
||||
|
||||
case TALK:
|
||||
case BUY_PRICE:
|
||||
case TOPIC: {
|
||||
*bufferlen = BUFFERLEN;
|
||||
return INPUT_STRING;
|
||||
}
|
||||
|
||||
case GIVEBEGGAR: {
|
||||
*bufferlen = 2;
|
||||
return INPUT_STRING;
|
||||
}
|
||||
|
||||
case ASK:
|
||||
case ASKYESNO: {
|
||||
*bufferlen = 3;
|
||||
return INPUT_STRING;
|
||||
}
|
||||
|
||||
case VENDORQUESTION:
|
||||
case BUY_ITEM:
|
||||
case SELL_ITEM:
|
||||
case CONFIRMATION:
|
||||
case CONTINUEQUESTION:
|
||||
case PLAYER:
|
||||
return INPUT_CHARACTER;
|
||||
|
||||
case ATTACK:
|
||||
case DONE:
|
||||
case INTRO:
|
||||
case FULLHEAL:
|
||||
case ADVANCELEVELS:
|
||||
return INPUT_NONE;
|
||||
}
|
||||
|
||||
error("invalid state: %d", _state);
|
||||
return INPUT_NONE;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
321
engines/ultima/ultima4/conversation/conversation.h
Normal file
321
engines/ultima/ultima4/conversation/conversation.h
Normal file
@@ -0,0 +1,321 @@
|
||||
/* 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_CONVERSATION_CONVERSATION_H
|
||||
#define ULTIMA4_CONVERSATION_CONVERSATION_H
|
||||
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class Debug;
|
||||
class Person;
|
||||
class Script;
|
||||
|
||||
/**
|
||||
* A response part can be text or a "command" that triggers an
|
||||
* action.
|
||||
*/
|
||||
class ResponsePart {
|
||||
public:
|
||||
ResponsePart(const Common::String &value, const Common::String &arg = "", bool command = false);
|
||||
|
||||
operator Common::String() const;
|
||||
bool operator==(const ResponsePart &rhs) const;
|
||||
bool isCommand() const;
|
||||
|
||||
private:
|
||||
Common::String _value, _arg;
|
||||
bool _command;
|
||||
};
|
||||
|
||||
/**
|
||||
* A list of valid command response parts
|
||||
*/
|
||||
class ResponseParts {
|
||||
public:
|
||||
const ResponsePart NONE;
|
||||
const ResponsePart ASK;
|
||||
const ResponsePart END;
|
||||
const ResponsePart ATTACK;
|
||||
const ResponsePart BRAGGED;
|
||||
const ResponsePart HUMBLE;
|
||||
const ResponsePart ADVANCELEVELS;
|
||||
const ResponsePart HEALCONFIRM;
|
||||
const ResponsePart STARTMUSIC_LB;
|
||||
const ResponsePart STARTMUSIC_HW;
|
||||
const ResponsePart STOPMUSIC;
|
||||
const ResponsePart HAWKWIND;
|
||||
|
||||
ResponseParts();
|
||||
~ResponseParts();
|
||||
};
|
||||
|
||||
extern ResponseParts *g_responseParts;
|
||||
|
||||
/**
|
||||
* A static response. Each response can be made up of any number of
|
||||
* ResponseParts, which are either text fragments or commands.
|
||||
*/
|
||||
class Response {
|
||||
public:
|
||||
Response(const Common::String &response);
|
||||
virtual ~Response() {}
|
||||
|
||||
void add(const ResponsePart &part);
|
||||
|
||||
virtual const Std::vector<ResponsePart> &getParts() const;
|
||||
|
||||
operator Common::String() const;
|
||||
|
||||
Response *addref();
|
||||
void release();
|
||||
|
||||
private:
|
||||
int _references;
|
||||
Std::vector<ResponsePart> _parts;
|
||||
};
|
||||
|
||||
/**
|
||||
* A dynamically generated response. This class allows the response
|
||||
* to be generated dynamically at the time of the conversation instead
|
||||
* of when the conversation data is loaded.
|
||||
*/
|
||||
class DynamicResponse : public Response {
|
||||
public:
|
||||
DynamicResponse(Response * (*generator)(const DynamicResponse *), const Common::String ¶m = "");
|
||||
virtual ~DynamicResponse();
|
||||
|
||||
const Std::vector<ResponsePart> &getParts() const override;
|
||||
|
||||
const Common::String &getParam() const {
|
||||
return _param;
|
||||
}
|
||||
|
||||
private:
|
||||
Response *(*_generator)(const DynamicResponse *);
|
||||
Response *_currentResponse;
|
||||
Common::String _param;
|
||||
};
|
||||
|
||||
/**
|
||||
* The dialogue class, which holds conversation information for
|
||||
* townspeople and others who may talk to you. It includes information
|
||||
* like pronouns, keywords, actual conversation text (of course),
|
||||
* questions, and what happens when you answer these questions.
|
||||
*/
|
||||
class Dialogue {
|
||||
public:
|
||||
/**
|
||||
* A question-response to a keyword.
|
||||
*/
|
||||
class Question {
|
||||
public:
|
||||
Question(const Common::String &txt, Response *yes, Response *no);
|
||||
|
||||
Common::String getText();
|
||||
Response *getResponse(bool yes);
|
||||
|
||||
private:
|
||||
Common::String _text;
|
||||
Response *_yesResp, *_noResp;
|
||||
};
|
||||
|
||||
/**
|
||||
* A dialogue keyword.
|
||||
* It contains all the keywords that the talker will respond to, as
|
||||
* well as the responses to those keywords.
|
||||
*/
|
||||
class Keyword {
|
||||
public:
|
||||
Keyword(const Common::String &kw, Response *resp);
|
||||
Keyword(const Common::String &kw, const Common::String &resp);
|
||||
~Keyword();
|
||||
|
||||
bool operator==(const Common::String &kw) const;
|
||||
|
||||
/*
|
||||
* Accessor methods
|
||||
*/
|
||||
const Common::String &getKeyword() {
|
||||
return _keyword;
|
||||
}
|
||||
Response *getResponse() {
|
||||
return _response;
|
||||
}
|
||||
|
||||
private:
|
||||
Common::String _keyword;
|
||||
Response *_response;
|
||||
};
|
||||
|
||||
/**
|
||||
* A mapping of keywords to the Keyword object that represents them
|
||||
*/
|
||||
typedef Common::HashMap<Common::String, Keyword *> KeywordMap;
|
||||
|
||||
/*
|
||||
* Constructors/Destructors
|
||||
*/
|
||||
Dialogue();
|
||||
virtual ~Dialogue();
|
||||
|
||||
/*
|
||||
* Accessor methods
|
||||
*/
|
||||
const Common::String &getName() const {
|
||||
return _name;
|
||||
}
|
||||
const Common::String &getPronoun() const {
|
||||
return _pronoun;
|
||||
}
|
||||
const Common::String &getPrompt() const {
|
||||
return _prompt;
|
||||
}
|
||||
Response *getIntro(bool familiar = false) {
|
||||
return _intro;
|
||||
}
|
||||
Response *getLongIntro(bool familiar = false) {
|
||||
return _longIntro;
|
||||
}
|
||||
Response *getDefaultAnswer() {
|
||||
return _defaultAnswer;
|
||||
}
|
||||
Dialogue::Question *getQuestion() {
|
||||
return _question;
|
||||
}
|
||||
|
||||
/*
|
||||
* Getters
|
||||
*/
|
||||
void setName(const Common::String &n) {
|
||||
_name = n;
|
||||
}
|
||||
void setPronoun(const Common::String &pn) {
|
||||
_pronoun = pn;
|
||||
}
|
||||
void setPrompt(const Common::String &prompt) {
|
||||
this->_prompt = prompt;
|
||||
}
|
||||
void setIntro(Response *i) {
|
||||
_intro = i;
|
||||
}
|
||||
void setLongIntro(Response *i) {
|
||||
_longIntro = i;
|
||||
}
|
||||
void setDefaultAnswer(Response *a) {
|
||||
_defaultAnswer = a;
|
||||
}
|
||||
void setTurnAwayProb(int prob) {
|
||||
_turnAwayProb = prob;
|
||||
}
|
||||
void setQuestion(Question *q) {
|
||||
_question = q;
|
||||
}
|
||||
void addKeyword(const Common::String &kw, Response *response);
|
||||
|
||||
const ResponsePart &getAction() const;
|
||||
Common::String dump(const Common::String &arg);
|
||||
|
||||
/*
|
||||
* Operators
|
||||
*/
|
||||
Keyword *operator[](const Common::String &kw);
|
||||
|
||||
private:
|
||||
Common::String _name;
|
||||
Common::String _pronoun;
|
||||
Common::String _prompt;
|
||||
Response *_intro;
|
||||
Response *_longIntro;
|
||||
Response *_defaultAnswer;
|
||||
KeywordMap _keywords;
|
||||
union {
|
||||
int _turnAwayProb;
|
||||
int _attackProb;
|
||||
};
|
||||
Question *_question;
|
||||
};
|
||||
|
||||
/**
|
||||
* The conversation class, which handles the flow of text from the
|
||||
* player to the talker and vice-versa. It is responsible for beginning
|
||||
* and termination conversations and handing state changes during.
|
||||
*/
|
||||
class Conversation {
|
||||
public:
|
||||
/** Different states the conversation may be in */
|
||||
enum State {
|
||||
INTRO, /**< The initial state of the conversation, before anything is said */
|
||||
TALK, /**< The "default" state of the conversation */
|
||||
ASK, /**< The talker is asking the player a question */
|
||||
ASKYESNO, /**< The talker is asking the player a yes/no question */
|
||||
VENDORQUESTION, /**< A vendor is asking the player a question */
|
||||
BUY_ITEM, /**< Asked which item to buy */
|
||||
SELL_ITEM, /**< Asked which item to sell */
|
||||
BUY_QUANTITY, /**< Asked how many items to buy */
|
||||
SELL_QUANTITY, /**< Asked how many items to sell */
|
||||
BUY_PRICE, /**< Asked how much money to give someone */
|
||||
CONFIRMATION, /**< Asked by a vendor to confirm something */
|
||||
CONTINUEQUESTION, /**< Asked whether or not to continue */
|
||||
TOPIC, /**< Asked a topic to speak about */
|
||||
PLAYER, /**< Input for which player is required */
|
||||
FULLHEAL, /**< Heal the entire party before continuing conversation */
|
||||
ADVANCELEVELS, /**< Check and advance the party's levels before continuing */
|
||||
GIVEBEGGAR, /**< Asked how much to give a beggar */
|
||||
ATTACK, /**< The conversation ends with the talker attacking you */
|
||||
DONE /**< The conversation is over */
|
||||
};
|
||||
|
||||
/** Different types of conversation input required */
|
||||
enum InputType {
|
||||
INPUT_STRING,
|
||||
INPUT_CHARACTER,
|
||||
INPUT_NONE
|
||||
};
|
||||
|
||||
/* Constructor/Destructors */
|
||||
Conversation();
|
||||
~Conversation();
|
||||
|
||||
/* Member functions */
|
||||
InputType getInputRequired(int *bufferLen);
|
||||
|
||||
/* Static variables */
|
||||
static const uint BUFFERLEN; /**< The default maxixum length of input */
|
||||
|
||||
public:
|
||||
State _state; /**< The state of the conversation */
|
||||
Common::String _playerInput; /**< A Common::String holding the text the player inputs */
|
||||
Common::List<Common::String> _reply; /**< What the talker says */
|
||||
class Script *_script; /**< A script that this person follows during the conversation (may be nullptr) */
|
||||
Dialogue::Question *_question; /**< The current question the player is being asked */
|
||||
int _quant; /**< For vendor transactions */
|
||||
int _player; /**< For vendor transactions */
|
||||
int _price; /**< For vendor transactions */
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
51
engines/ultima/ultima4/conversation/dialogueloader.cpp
Normal file
51
engines/ultima/ultima4/conversation/dialogueloader.cpp
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/conversation/conversation.h"
|
||||
#include "ultima/ultima4/conversation/dialogueloader.h"
|
||||
#include "ultima/ultima4/conversation/dialogueloader_hw.h"
|
||||
#include "ultima/ultima4/conversation/dialogueloader_lb.h"
|
||||
#include "ultima/ultima4/conversation/dialogueloader_tlk.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
static DialogueLoaders *g_loaders;
|
||||
|
||||
DialogueLoaders::DialogueLoaders() {
|
||||
g_loaders = this;
|
||||
registerLoader(new U4HWDialogueLoader, "application/x-u4hwtlk");
|
||||
registerLoader(new U4LBDialogueLoader, "application/x-u4lbtlk");
|
||||
registerLoader(new U4TlkDialogueLoader(), "application/x-u4tlk");
|
||||
}
|
||||
|
||||
DialogueLoaders::~DialogueLoaders() {
|
||||
for (auto &l : _loaders)
|
||||
delete l._value;
|
||||
g_loaders = nullptr;
|
||||
}
|
||||
|
||||
DialogueLoader *DialogueLoaders::getLoader(const Common::String &mimeType) {
|
||||
return (*g_loaders)[mimeType];
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
71
engines/ultima/ultima4/conversation/dialogueloader.h
Normal file
71
engines/ultima/ultima4/conversation/dialogueloader.h
Normal 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 ULTIMA4_CONVERSATION_DIALOGUELOADER_H
|
||||
#define ULTIMA4_CONVERSATION_DIALOGUELOADER_H
|
||||
|
||||
#include "common/hashmap.h"
|
||||
#include "common/str.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class Dialogue;
|
||||
|
||||
/**
|
||||
* The generic dialogue loader interface. Different dialogue
|
||||
* loaders should override the load method to load dialogues from
|
||||
* different sources (.tlk files, xml config elements, etc.). They
|
||||
* must also register themselves with registerLoader for one or more
|
||||
* source types. By convention, the source type of load() and
|
||||
* registerLoader() is an xu4-specific mime type.
|
||||
* The two main types used are application/x-u4tlk and text/x-u4cfg.
|
||||
*/
|
||||
class DialogueLoader {
|
||||
public:
|
||||
virtual ~DialogueLoader() {}
|
||||
|
||||
virtual Dialogue *load(Common::SeekableReadStream *source) = 0;
|
||||
};
|
||||
|
||||
class DialogueLoaders {
|
||||
private:
|
||||
Common::HashMap<Common::String, DialogueLoader *> _loaders;
|
||||
public:
|
||||
static DialogueLoader *getLoader(const Common::String &mimeType);
|
||||
public:
|
||||
DialogueLoaders();
|
||||
~DialogueLoaders();
|
||||
|
||||
void registerLoader(DialogueLoader *loader, const Common::String &mimeType) {
|
||||
_loaders[mimeType] = loader;
|
||||
}
|
||||
|
||||
DialogueLoader *operator[](const Common::String &mimeType) {
|
||||
return _loaders[mimeType];
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
143
engines/ultima/ultima4/conversation/dialogueloader_hw.cpp
Normal file
143
engines/ultima/ultima4/conversation/dialogueloader_hw.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
/* 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/game/context.h"
|
||||
#include "ultima/ultima4/conversation/conversation.h"
|
||||
#include "ultima/ultima4/conversation/dialogueloader_hw.h"
|
||||
#include "ultima/ultima4/game/player.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/filesys/u4file.h"
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
#include "ultima/ultima4/ultima4.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
Response *hawkwindGetAdvice(const DynamicResponse *kw);
|
||||
Response *hawkwindGetIntro(const DynamicResponse *dynResp);
|
||||
|
||||
/* Hawkwind text indexes */
|
||||
#define HW_SPEAKONLYWITH 40
|
||||
#define HW_RETURNWHEN 41
|
||||
#define HW_ISREVIVED 42
|
||||
#define HW_WELCOME 43
|
||||
#define HW_GREETING1 44
|
||||
#define HW_GREETING2 45
|
||||
#define HW_PROMPT 46
|
||||
#define HW_DEFAULT 49
|
||||
#define HW_ALREADYAVATAR 50
|
||||
#define HW_GOTOSHRINE 51
|
||||
#define HW_BYE 52
|
||||
|
||||
/**
|
||||
* A special case dialogue loader for Hawkwind.
|
||||
*/
|
||||
Dialogue *U4HWDialogueLoader::load(Common::SeekableReadStream *source) {
|
||||
Std::vector<Common::String> &hawkwindText = g_ultima->_hawkwindText;
|
||||
hawkwindText = u4read_stringtable("hawkwind");
|
||||
|
||||
Dialogue *dlg = new Dialogue();
|
||||
dlg->setTurnAwayProb(0);
|
||||
|
||||
dlg->setName("Hawkwind");
|
||||
dlg->setPronoun("He");
|
||||
dlg->setPrompt(hawkwindText[HW_PROMPT]);
|
||||
Response *intro = new DynamicResponse(&hawkwindGetIntro);
|
||||
dlg->setIntro(intro);
|
||||
dlg->setLongIntro(intro);
|
||||
dlg->setDefaultAnswer(new Response(Common::String("\n" + hawkwindText[HW_DEFAULT])));
|
||||
|
||||
for (int v = 0; v < VIRT_MAX; v++) {
|
||||
Common::String virtue(getVirtueName((Virtue) v));
|
||||
lowercase(virtue);
|
||||
virtue = virtue.substr(0, 4);
|
||||
dlg->addKeyword(virtue, new DynamicResponse(&hawkwindGetAdvice, virtue));
|
||||
}
|
||||
|
||||
Response *bye = new Response(hawkwindText[HW_BYE]);
|
||||
bye->add(g_responseParts->STOPMUSIC);
|
||||
bye->add(g_responseParts->END);
|
||||
dlg->addKeyword("bye", bye);
|
||||
dlg->addKeyword("", bye);
|
||||
|
||||
return dlg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the appropriate response when the player asks Lord British
|
||||
* for help. The help text depends on the current party status; when
|
||||
* one quest item is complete, Lord British provides some direction to
|
||||
* the next one.
|
||||
*/
|
||||
Response *hawkwindGetAdvice(const DynamicResponse *dynResp) {
|
||||
Common::String text;
|
||||
int virtue = -1, virtueLevel = -1;
|
||||
Std::vector<Common::String> &hawkwindText = g_ultima->_hawkwindText;
|
||||
|
||||
/* check if asking about a virtue */
|
||||
for (int v = 0; v < VIRT_MAX; v++) {
|
||||
if (scumm_strnicmp(dynResp->getParam().c_str(), getVirtueName((Virtue) v), 4) == 0) {
|
||||
virtue = v;
|
||||
virtueLevel = g_ultima->_saveGame->_karma[v];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (virtue != -1) {
|
||||
text = "\n\n";
|
||||
if (virtueLevel == 0)
|
||||
text += hawkwindText[HW_ALREADYAVATAR] + "\n";
|
||||
else if (virtueLevel < 80)
|
||||
text += hawkwindText[(virtueLevel / 20) * 8 + virtue];
|
||||
else if (virtueLevel < 99)
|
||||
text += hawkwindText[3 * 8 + virtue];
|
||||
else /* virtueLevel >= 99 */
|
||||
text = hawkwindText[4 * 8 + virtue] + hawkwindText[HW_GOTOSHRINE];
|
||||
} else {
|
||||
text = Common::String("\n") + hawkwindText[HW_DEFAULT];
|
||||
}
|
||||
|
||||
return new Response(text);
|
||||
}
|
||||
|
||||
Response *hawkwindGetIntro(const DynamicResponse *dynResp) {
|
||||
Response *intro = new Response("");
|
||||
Std::vector<Common::String> &hawkwindText = g_ultima->_hawkwindText;
|
||||
|
||||
if (g_context->_party->member(0)->getStatus() == STAT_SLEEPING ||
|
||||
g_context->_party->member(0)->getStatus() == STAT_DEAD) {
|
||||
intro->add(hawkwindText[HW_SPEAKONLYWITH] + g_context->_party->member(0)->getName() +
|
||||
hawkwindText[HW_RETURNWHEN] + g_context->_party->member(0)->getName() +
|
||||
hawkwindText[HW_ISREVIVED]);
|
||||
intro->add(g_responseParts->END);
|
||||
} else {
|
||||
intro->add(g_responseParts->STARTMUSIC_HW);
|
||||
intro->add(g_responseParts->HAWKWIND);
|
||||
|
||||
intro->add(hawkwindText[HW_WELCOME] + g_context->_party->member(0)->getName() +
|
||||
hawkwindText[HW_GREETING1] + hawkwindText[HW_GREETING2]);
|
||||
}
|
||||
|
||||
return intro;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
41
engines/ultima/ultima4/conversation/dialogueloader_hw.h
Normal file
41
engines/ultima/ultima4/conversation/dialogueloader_hw.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* 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_CONVERSATION_DIALOGUELOADER_HW_H
|
||||
#define ULTIMA4_CONVERSATION_DIALOGUELOADER_HW_H
|
||||
|
||||
#include "ultima/ultima4/conversation/dialogueloader.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* The dialogue loader for Hawkwind.
|
||||
*/
|
||||
class U4HWDialogueLoader : public DialogueLoader {
|
||||
public:
|
||||
Dialogue *load(Common::SeekableReadStream *source) override;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
196
engines/ultima/ultima4/conversation/dialogueloader_lb.cpp
Normal file
196
engines/ultima/ultima4/conversation/dialogueloader_lb.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
/* 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/game/context.h"
|
||||
#include "ultima/ultima4/conversation/conversation.h"
|
||||
#include "ultima/ultima4/conversation/dialogueloader_lb.h"
|
||||
#include "ultima/ultima4/game/player.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/filesys/u4file.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
Response *lordBritishGetHelp(const DynamicResponse *resp);
|
||||
Response *lordBritishGetIntro(const DynamicResponse *resp);
|
||||
|
||||
/**
|
||||
* A special case dialogue loader for Lord British. Loads most of the
|
||||
* keyword/responses from a string table originally extracted from the
|
||||
* game executable. The "help" response is a special case that changes
|
||||
* based on the current party status.
|
||||
*/
|
||||
Dialogue *U4LBDialogueLoader::load(Common::SeekableReadStream *source) {
|
||||
Std::vector<Common::String> lbKeywords = u4read_stringtable("lb_keywords");
|
||||
Std::vector<Common::String> lbText = u4read_stringtable("lb_text");
|
||||
|
||||
Dialogue *dlg = new Dialogue();
|
||||
dlg->setTurnAwayProb(0);
|
||||
|
||||
dlg->setName("Lord British");
|
||||
dlg->setPronoun("He");
|
||||
dlg->setPrompt("What else?\n");
|
||||
Response *intro = new DynamicResponse(&lordBritishGetIntro);
|
||||
dlg->setIntro(intro);
|
||||
dlg->setLongIntro(intro);
|
||||
dlg->setDefaultAnswer(new Response("\nHe says: I\ncannot help thee\nwith that.\n"));
|
||||
|
||||
for (unsigned i = 0; i < lbKeywords.size(); i++) {
|
||||
dlg->addKeyword(lbKeywords[i], new Response(lbText[i]));
|
||||
}
|
||||
|
||||
Response *heal = new Response("\n\n\n\n\n\nHe says: I am\nwell, thank ye.");
|
||||
heal->add(g_responseParts->HEALCONFIRM);
|
||||
dlg->addKeyword("heal", heal);
|
||||
|
||||
Response *bye;
|
||||
if (g_context->_party->size() > 1)
|
||||
bye = new Response("Lord British says: Fare thee well my friends!");
|
||||
else
|
||||
bye = new Response("Lord British says: Fare thee well my friend!");
|
||||
bye->add(g_responseParts->STOPMUSIC);
|
||||
bye->add(g_responseParts->END);
|
||||
dlg->addKeyword("bye", bye);
|
||||
dlg->addKeyword("", bye);
|
||||
|
||||
dlg->addKeyword("help", new DynamicResponse(&lordBritishGetHelp));
|
||||
|
||||
return dlg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the appropriate response when the player asks Lord British
|
||||
* for help. The help text depends on the current party status; when
|
||||
* one quest item is complete, Lord British provides some direction to
|
||||
* the next one.
|
||||
*/
|
||||
Response *lordBritishGetHelp(const DynamicResponse *resp) {
|
||||
int v;
|
||||
bool fullAvatar, partialAvatar;
|
||||
Common::String text;
|
||||
|
||||
/*
|
||||
* check whether player is full avatar (in all virtues) or partial
|
||||
* avatar (in at least one virtue)
|
||||
*/
|
||||
fullAvatar = true;
|
||||
partialAvatar = false;
|
||||
for (v = 0; v < VIRT_MAX; v++) {
|
||||
fullAvatar &= (g_ultima->_saveGame->_karma[v] == 0);
|
||||
partialAvatar |= (g_ultima->_saveGame->_karma[v] == 0);
|
||||
}
|
||||
|
||||
if (g_ultima->_saveGame->_moves <= 1000) {
|
||||
text = "To survive in this hostile land thou must first know thyself! Seek ye to master thy weapons and thy magical ability!\n"
|
||||
"\nTake great care in these thy first travels in Britannia.\n"
|
||||
"\nUntil thou dost well know thyself, travel not far from the safety of the townes!\n";
|
||||
}
|
||||
|
||||
else if (g_ultima->_saveGame->_members == 1) {
|
||||
text = "Travel not the open lands alone. There are many worthy people in the diverse townes whom it would be wise to ask to Join thee!\n"
|
||||
"\nBuild thy party unto eight travellers, for only a true leader can win the Quest!\n";
|
||||
}
|
||||
|
||||
else if (g_ultima->_saveGame->_runes == 0) {
|
||||
text = "Learn ye the paths of virtue. Seek to gain entry unto the eight shrines!\n"
|
||||
"\nFind ye the Runes, needed for entry into each shrine, and learn each chant or \"Mantra\" used to focus thy meditations.\n"
|
||||
"\nWithin the Shrines thou shalt learn of the deeds which show thy inner virtue or vice!\n"
|
||||
"\nChoose thy path wisely for all thy deeds of good and evil are remembered and can return to hinder thee!\n";
|
||||
}
|
||||
|
||||
else if (!partialAvatar) {
|
||||
text = "Visit the Seer Hawkwind often and use his wisdom to help thee prove thy virtue.\n"
|
||||
"\nWhen thou art ready, Hawkwind will advise thee to seek the Elevation unto partial Avatarhood in a virtue.\n"
|
||||
"\nSeek ye to become a partial Avatar in all eight virtues, for only then shalt thou be ready to seek the codex!\n";
|
||||
}
|
||||
|
||||
else if (g_ultima->_saveGame->_stones == 0) {
|
||||
text = "Go ye now into the depths of the dungeons. Therein recover the 8 colored stones from the altar pedestals in the halls of the dungeons.\n"
|
||||
"\nFind the uses of these stones for they can help thee in the Abyss!\n";
|
||||
}
|
||||
|
||||
else if (!fullAvatar) {
|
||||
text = "Thou art doing very well indeed on the path to Avatarhood! Strive ye to achieve the Elevation in all eight virtues!\n";
|
||||
}
|
||||
|
||||
else if ((g_ultima->_saveGame->_items & ITEM_BELL) == 0 ||
|
||||
(g_ultima->_saveGame->_items & ITEM_BOOK) == 0 ||
|
||||
(g_ultima->_saveGame->_items & ITEM_CANDLE) == 0) {
|
||||
text = "Find ye the Bell, Book and Candle! With these three things, one may enter the Great Stygian Abyss!\n";
|
||||
}
|
||||
|
||||
else if ((g_ultima->_saveGame->_items & ITEM_KEY_C) == 0 ||
|
||||
(g_ultima->_saveGame->_items & ITEM_KEY_L) == 0 ||
|
||||
(g_ultima->_saveGame->_items & ITEM_KEY_T) == 0) {
|
||||
text = "Before thou dost enter the Abyss thou shalt need the Key of Three Parts, and the Word of Passage.\n"
|
||||
"\nThen might thou enter the Chamber of the Codex of Ultimate Wisdom!\n";
|
||||
}
|
||||
|
||||
else {
|
||||
text = "Thou dost now seem ready to make the final journey into the dark Abyss! Go only with a party of eight!\n"
|
||||
"\nGood Luck, and may the powers of good watch over thee on this thy most perilous endeavor!\n"
|
||||
"\nThe hearts and souls of all Britannia go with thee now. Take care, my friend.\n";
|
||||
}
|
||||
|
||||
return new Response(Common::String("He says: ") + text);
|
||||
}
|
||||
|
||||
Response *lordBritishGetIntro(const DynamicResponse *resp) {
|
||||
Response *intro = new Response("");
|
||||
intro->add(g_responseParts->STARTMUSIC_LB);
|
||||
|
||||
if (g_ultima->_saveGame->_lbIntro) {
|
||||
if (g_ultima->_saveGame->_members == 1) {
|
||||
intro->add(Common::String("\n\n\nLord British\nsays: Welcome\n") +
|
||||
g_context->_party->member(0)->getName() + "!\n\n");
|
||||
} else if (g_ultima->_saveGame->_members == 2) {
|
||||
intro->add(Common::String("\n\nLord British\nsays: Welcome\n") +
|
||||
g_context->_party->member(0)->getName() +
|
||||
" and thee also " +
|
||||
g_context->_party->member(1)->getName() +
|
||||
"!\n\n");
|
||||
} else {
|
||||
intro->add(Common::String("\n\n\nLord British\nsays: Welcome\n") +
|
||||
g_context->_party->member(0)->getName() +
|
||||
" and thy\nworthy\nAdventurers!\n\n");
|
||||
}
|
||||
|
||||
// Lord British automatically adds "What would thou ask of me?"
|
||||
|
||||
// Check levels here, just like the original!
|
||||
intro->add(g_responseParts->ADVANCELEVELS);
|
||||
}
|
||||
|
||||
else {
|
||||
intro->add(Common::String("\n\n\nLord British rises and says: At long last!\n") +
|
||||
g_context->_party->member(0)->getName() +
|
||||
" thou hast come! We have waited such a long, long time...\n"
|
||||
"\n\nLord British sits and says: A new age is upon Britannia. The great evil Lords are gone but our people lack direction and purpose in their lives...\n\n\n"
|
||||
"A champion of virtue is called for. Thou may be this champion, but only time shall tell. I will aid thee any way that I can!\n\n"
|
||||
"How may I help thee?\n");
|
||||
g_ultima->_saveGame->_lbIntro = 1;
|
||||
}
|
||||
|
||||
return intro;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
41
engines/ultima/ultima4/conversation/dialogueloader_lb.h
Normal file
41
engines/ultima/ultima4/conversation/dialogueloader_lb.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* 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_CONVERSATION_DIALOGUELOADER_LB_H
|
||||
#define ULTIMA4_CONVERSATION_DIALOGUELOADER_LB_H
|
||||
|
||||
#include "ultima/ultima4/conversation/dialogueloader.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* The dialogue loader for Lord British
|
||||
*/
|
||||
class U4LBDialogueLoader : public DialogueLoader {
|
||||
public:
|
||||
Dialogue *load(Common::SeekableReadStream *source) override;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
162
engines/ultima/ultima4/conversation/dialogueloader_tlk.cpp
Normal file
162
engines/ultima/ultima4/conversation/dialogueloader_tlk.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
/* 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/conversation/conversation.h"
|
||||
#include "ultima/ultima4/conversation/dialogueloader_tlk.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* A dialogue loader for standard u4dos .tlk files.
|
||||
*/
|
||||
Dialogue *U4TlkDialogueLoader::load(Common::SeekableReadStream *source) {
|
||||
enum QTrigger {
|
||||
NONE = 0,
|
||||
JOB = 3,
|
||||
HEALTH = 4,
|
||||
KEYWORD1 = 5,
|
||||
KEYWORD2 = 6
|
||||
};
|
||||
|
||||
/* there's no dialogues left in the file */
|
||||
char tlk_buffer[288];
|
||||
if (source->read(tlk_buffer, sizeof(tlk_buffer)) != sizeof(tlk_buffer))
|
||||
return nullptr;
|
||||
|
||||
char *ptr = &tlk_buffer[3];
|
||||
Std::vector<Common::String> strings;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
strings.push_back(ptr);
|
||||
ptr += strlen(ptr) + 1;
|
||||
}
|
||||
|
||||
Dialogue *dlg = new Dialogue();
|
||||
byte prob = tlk_buffer[2];
|
||||
QTrigger qtrigger = QTrigger(tlk_buffer[0]);
|
||||
bool humilityTestQuestion = tlk_buffer[1] == 1;
|
||||
|
||||
dlg->setTurnAwayProb(prob);
|
||||
|
||||
dlg->setName(strings[0]);
|
||||
dlg->setPronoun(strings[1]);
|
||||
dlg->setPrompt("\nYour Interest:\n");
|
||||
|
||||
// Fix the actor description Common::String, converting the first character
|
||||
// to lower-case.
|
||||
strings[2].setChar(tolower(strings[2][0]), 0);
|
||||
|
||||
// ... then replace any newlines in the Common::String with spaces
|
||||
size_t index = strings[2].find("\n");
|
||||
while (index != Common::String::npos) {
|
||||
strings[2].setChar(' ', index);
|
||||
index = strings[2].find("\n");
|
||||
}
|
||||
|
||||
// ... then append a period to the end of the Common::String if one does
|
||||
// not already exist
|
||||
if (!Common::isPunct(strings[2][strings[2].size() - 1]))
|
||||
strings[2] = strings[2] + Common::String(".");
|
||||
|
||||
// ... and finally, a few characters in the game have descriptions
|
||||
// that do not begin with a definite (the) or indefinite (a/an)
|
||||
// article. On those characters, insert the appropriate article.
|
||||
if ((strings[0] == "Iolo")
|
||||
|| (strings[0] == "Tracie")
|
||||
|| (strings[0] == "Dupre")
|
||||
|| (strings[0] == "Traveling Dan"))
|
||||
strings[2] = Common::String("a ") + strings[2];
|
||||
|
||||
Common::String introBase = Common::String("\nYou meet ") + strings[2] + "\n";
|
||||
|
||||
dlg->setIntro(new Response(introBase + dlg->getPrompt()));
|
||||
dlg->setLongIntro(new Response(introBase +
|
||||
"\n" + dlg->getPronoun() + " says: I am " + dlg->getName() + "\n"
|
||||
+ dlg->getPrompt()));
|
||||
dlg->setDefaultAnswer(new Response("That I cannot\nhelp thee with."));
|
||||
|
||||
Response *yes = new Response(strings[8]);
|
||||
Response *no = new Response(strings[9]);
|
||||
if (humilityTestQuestion) {
|
||||
yes->add(g_responseParts->BRAGGED);
|
||||
no->add(g_responseParts->HUMBLE);
|
||||
}
|
||||
dlg->setQuestion(new Dialogue::Question(strings[7], yes, no));
|
||||
|
||||
// one of the following four keywords triggers the speaker's question
|
||||
Response *job = new Response(Common::String("\n") + strings[3]);
|
||||
Response *health = new Response(Common::String("\n") + strings[4]);
|
||||
Response *kw1 = new Response(Common::String("\n") + strings[5]);
|
||||
Response *kw2 = new Response(Common::String("\n") + strings[6]);
|
||||
|
||||
switch (qtrigger) {
|
||||
case JOB:
|
||||
job->add(g_responseParts->ASK);
|
||||
break;
|
||||
case HEALTH:
|
||||
health->add(g_responseParts->ASK);
|
||||
break;
|
||||
case KEYWORD1:
|
||||
kw1->add(g_responseParts->ASK);
|
||||
break;
|
||||
case KEYWORD2:
|
||||
kw2->add(g_responseParts->ASK);
|
||||
break;
|
||||
case NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dlg->addKeyword("job", job);
|
||||
dlg->addKeyword("heal", health);
|
||||
dlg->addKeyword(strings[10], kw1);
|
||||
dlg->addKeyword(strings[11], kw2);
|
||||
|
||||
// NOTE: We let the talker's custom keywords override the standard
|
||||
// keywords like HEAL and LOOK. This behavior differs from u4dos,
|
||||
// but fixes a couple conversation files which have keywords that
|
||||
// conflict with the standard ones (e.g. Calabrini in Moonglow has
|
||||
// HEAL for healer, which is unreachable in u4dos, but clearly
|
||||
// more useful than "Fine." for health).
|
||||
Common::String look = Common::String("\nYou see ") + strings[2];
|
||||
dlg->addKeyword("look", new Response(look));
|
||||
dlg->addKeyword("name", new Response(Common::String("\n") + dlg->getPronoun() + " says: I am " + dlg->getName()));
|
||||
dlg->addKeyword("give", new Response(Common::String("\n") + dlg->getPronoun() + " says: I do not need thy gold. Keep it!"));
|
||||
dlg->addKeyword("join", new Response(Common::String("\n") + dlg->getPronoun() + " says: I cannot join thee."));
|
||||
|
||||
Response *bye = new Response("\nBye.");
|
||||
bye->add(g_responseParts->END);
|
||||
dlg->addKeyword("bye", bye);
|
||||
dlg->addKeyword("", bye);
|
||||
|
||||
/*
|
||||
* This little easter egg appeared in the Amiga version of Ultima IV.
|
||||
* I've never figured out what the number means.
|
||||
* "Banjo" Bob Hardy was the programmer for the Amiga version.
|
||||
*/
|
||||
dlg->addKeyword("ojna", new Response("\nHi Banjo Bob!\nYour secret\nnumber is\n4F4A4E0A"));
|
||||
|
||||
return dlg;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
41
engines/ultima/ultima4/conversation/dialogueloader_tlk.h
Normal file
41
engines/ultima/ultima4/conversation/dialogueloader_tlk.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* 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_CONVERSATION_DIALOGUELOADER_TLK_H
|
||||
#define ULTIMA4_CONVERSATION_DIALOGUELOADER_TLK_H
|
||||
|
||||
#include "ultima/ultima4/conversation/dialogueloader.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* The dialogue loader for u4dos .tlk files
|
||||
*/
|
||||
class U4TlkDialogueLoader : public DialogueLoader {
|
||||
public:
|
||||
Dialogue *load(Common::SeekableReadStream *source) override;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
126
engines/ultima/ultima4/core/config.cpp
Normal file
126
engines/ultima/ultima4/core/config.cpp
Normal 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
|
||||
159
engines/ultima/ultima4/core/config.h
Normal file
159
engines/ultima/ultima4/core/config.h
Normal 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
|
||||
48
engines/ultima/ultima4/core/coords.h
Normal file
48
engines/ultima/ultima4/core/coords.h
Normal 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
|
||||
2002
engines/ultima/ultima4/core/debugger.cpp
Normal file
2002
engines/ultima/ultima4/core/debugger.cpp
Normal file
File diff suppressed because it is too large
Load Diff
433
engines/ultima/ultima4/core/debugger.h
Normal file
433
engines/ultima/ultima4/core/debugger.h
Normal 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
|
||||
501
engines/ultima/ultima4/core/debugger_actions.cpp
Normal file
501
engines/ultima/ultima4/core/debugger_actions.cpp
Normal 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
|
||||
144
engines/ultima/ultima4/core/debugger_actions.h
Normal file
144
engines/ultima/ultima4/core/debugger_actions.h
Normal 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
|
||||
83
engines/ultima/ultima4/core/lzw/hash.cpp
Normal file
83
engines/ultima/ultima4/core/lzw/hash.cpp
Normal 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
|
||||
39
engines/ultima/ultima4/core/lzw/hash.h
Normal file
39
engines/ultima/ultima4/core/lzw/hash.h
Normal 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
|
||||
315
engines/ultima/ultima4/core/lzw/lzw.cpp
Normal file
315
engines/ultima/ultima4/core/lzw/lzw.cpp
Normal 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
|
||||
38
engines/ultima/ultima4/core/lzw/lzw.h
Normal file
38
engines/ultima/ultima4/core/lzw/lzw.h
Normal 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
|
||||
155
engines/ultima/ultima4/core/lzw/u4decode.cpp
Normal file
155
engines/ultima/ultima4/core/lzw/u4decode.cpp
Normal 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
|
||||
40
engines/ultima/ultima4/core/lzw/u4decode.h
Normal file
40
engines/ultima/ultima4/core/lzw/u4decode.h
Normal 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
|
||||
107
engines/ultima/ultima4/core/observable.h
Normal file
107
engines/ultima/ultima4/core/observable.h
Normal 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
|
||||
67
engines/ultima/ultima4/core/observer.h
Normal file
67
engines/ultima/ultima4/core/observer.h
Normal 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
|
||||
190
engines/ultima/ultima4/core/settings.cpp
Normal file
190
engines/ultima/ultima4/core/settings.cpp
Normal 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
|
||||
186
engines/ultima/ultima4/core/settings.h
Normal file
186
engines/ultima/ultima4/core/settings.h
Normal 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
|
||||
83
engines/ultima/ultima4/core/types.h
Normal file
83
engines/ultima/ultima4/core/types.h
Normal 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
|
||||
103
engines/ultima/ultima4/core/utils.cpp
Normal file
103
engines/ultima/ultima4/core/utils.cpp
Normal 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
|
||||
124
engines/ultima/ultima4/core/utils.h
Normal file
124
engines/ultima/ultima4/core/utils.h
Normal 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
|
||||
378
engines/ultima/ultima4/events/event_handler.cpp
Normal file
378
engines/ultima/ultima4/events/event_handler.cpp
Normal file
@@ -0,0 +1,378 @@
|
||||
/* 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/events/event_handler.h"
|
||||
#include "ultima/ultima4/controllers/wait_controller.h"
|
||||
#include "ultima/ultima4/core/settings.h"
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/game/context.h"
|
||||
#include "ultima/ultima4/views/textview.h"
|
||||
#include "ultima/ultima4/gfx/screen.h"
|
||||
#include "ultima/ultima4/map/location.h"
|
||||
#include "common/events.h"
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
bool EventHandler::_controllerDone = false;
|
||||
bool EventHandler::_ended = false;
|
||||
|
||||
EventHandler *EventHandler::_instance = nullptr;
|
||||
|
||||
EventHandler::EventHandler() : _timer(settings._eventTimerGranularity),
|
||||
_updateScreen(nullptr), _isRightButtonDown(false) {
|
||||
}
|
||||
|
||||
EventHandler *EventHandler::getInstance() {
|
||||
if (_instance == nullptr)
|
||||
_instance = new EventHandler();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
void EventHandler::wait_msecs(uint msecs) {
|
||||
int msecs_per_cycle = (1000 / settings._gameCyclesPerSecond);
|
||||
int cycles = msecs / msecs_per_cycle;
|
||||
|
||||
if (cycles > 0) {
|
||||
WaitController waitCtrl(cycles);
|
||||
getInstance()->pushController(&waitCtrl);
|
||||
waitCtrl.wait();
|
||||
}
|
||||
// Sleep the rest of the msecs we can't wait for
|
||||
EventHandler::sleep(msecs % msecs_per_cycle);
|
||||
}
|
||||
|
||||
void EventHandler::sleep(uint msec) {
|
||||
g_system->delayMillis(msec);
|
||||
}
|
||||
|
||||
void EventHandler::wait_cycles(uint cycles) {
|
||||
WaitController waitCtrl(cycles);
|
||||
getInstance()->pushController(&waitCtrl);
|
||||
waitCtrl.wait();
|
||||
}
|
||||
|
||||
void EventHandler::setControllerDone(bool done) {
|
||||
_controllerDone = done;
|
||||
#if defined(IOS_ULTIMA4)
|
||||
if (done)
|
||||
controllerStopped_helper();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool EventHandler::getControllerDone() {
|
||||
return _controllerDone;
|
||||
}
|
||||
|
||||
void EventHandler::end() {
|
||||
// End all event processing
|
||||
_ended = true;
|
||||
}
|
||||
|
||||
TimedEventMgr *EventHandler::getTimer() {
|
||||
return &_timer;
|
||||
}
|
||||
|
||||
Controller *EventHandler::pushController(Controller *c) {
|
||||
c->setActive();
|
||||
_controllers.push_back(c);
|
||||
getTimer()->add(&Controller::timerCallback, c->getTimerInterval(), c);
|
||||
return c;
|
||||
}
|
||||
|
||||
Controller *EventHandler::popController() {
|
||||
if (_controllers.empty())
|
||||
return nullptr;
|
||||
|
||||
Controller *controller = _controllers.back();
|
||||
getTimer()->remove(&Controller::timerCallback, controller);
|
||||
_controllers.pop_back();
|
||||
|
||||
controller = getController();
|
||||
if (controller)
|
||||
controller->setActive();
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
Controller *EventHandler::getController() const {
|
||||
if (_controllers.empty())
|
||||
return nullptr;
|
||||
|
||||
return _controllers.back();
|
||||
}
|
||||
|
||||
void EventHandler::setController(Controller *c) {
|
||||
while (popController() != nullptr) {}
|
||||
pushController(c);
|
||||
}
|
||||
|
||||
void EventHandler::pushMouseAreaSet(const MouseArea *mouseAreas) {
|
||||
_mouseAreaSets.push_front(mouseAreas);
|
||||
}
|
||||
|
||||
void EventHandler::popMouseAreaSet() {
|
||||
if (_mouseAreaSets.size())
|
||||
_mouseAreaSets.pop_front();
|
||||
}
|
||||
|
||||
const MouseArea *EventHandler::getMouseAreaSet() const {
|
||||
if (_mouseAreaSets.size())
|
||||
return _mouseAreaSets.front();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EventHandler::run() {
|
||||
if (_updateScreen)
|
||||
(*_updateScreen)();
|
||||
g_screen->update();
|
||||
|
||||
while (!_ended && !_controllerDone && !g_ultima->shouldQuit()) {
|
||||
Common::Event event;
|
||||
if (g_system->getEventManager()->pollEvent(event)) {
|
||||
switch (event.type) {
|
||||
case Common::EVENT_KEYDOWN:
|
||||
handleKeyDownEvent(event, getController(), _updateScreen);
|
||||
break;
|
||||
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
case Common::EVENT_MBUTTONDOWN:
|
||||
handleMouseButtonDownEvent(event, getController(), _updateScreen);
|
||||
break;
|
||||
|
||||
case Common::EVENT_LBUTTONUP:
|
||||
case Common::EVENT_RBUTTONUP:
|
||||
case Common::EVENT_MBUTTONUP:
|
||||
handleMouseButtonUpEvent(event, getController(), _updateScreen);
|
||||
break;
|
||||
|
||||
case Common::EVENT_MOUSEMOVE:
|
||||
handleMouseMotionEvent(event);
|
||||
continue;
|
||||
|
||||
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
|
||||
getController()->keybinder((KeybindingAction)event.customType);
|
||||
break;
|
||||
|
||||
case Common::EVENT_QUIT:
|
||||
_ended = true;
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Brief delay
|
||||
g_system->delayMillis(10);
|
||||
|
||||
// Poll the timer manager
|
||||
_timer.poll();
|
||||
|
||||
// Update the screen
|
||||
g_screen->screenFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void EventHandler::setScreenUpdate(void (*updateScreen)(void)) {
|
||||
_updateScreen = updateScreen;
|
||||
}
|
||||
|
||||
void EventHandler::pushKeyHandler(KeyHandler kh) {
|
||||
KeyHandler *new_kh = new KeyHandler(kh);
|
||||
KeyHandlerController *khc = new KeyHandlerController(new_kh);
|
||||
pushController(khc);
|
||||
}
|
||||
|
||||
void EventHandler::popKeyHandler() {
|
||||
if (_controllers.empty())
|
||||
return;
|
||||
|
||||
popController();
|
||||
}
|
||||
|
||||
KeyHandler *EventHandler::getKeyHandler() const {
|
||||
if (_controllers.empty())
|
||||
return nullptr;
|
||||
|
||||
KeyHandlerController *khc = dynamic_cast<KeyHandlerController *>(_controllers.back());
|
||||
assertMsg(khc != nullptr, "EventHandler::getKeyHandler called when controller wasn't a keyhandler");
|
||||
if (khc == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return khc->getKeyHandler();
|
||||
}
|
||||
|
||||
void EventHandler::setKeyHandler(KeyHandler kh) {
|
||||
while (popController() != nullptr) {
|
||||
}
|
||||
pushKeyHandler(kh);
|
||||
}
|
||||
|
||||
const MouseArea *EventHandler::mouseAreaForPoint(int x, int y) {
|
||||
int i;
|
||||
const MouseArea *areas = getMouseAreaSet();
|
||||
|
||||
if (!areas)
|
||||
return nullptr;
|
||||
|
||||
for (i = 0; areas[i]._nPoints != 0; i++) {
|
||||
if (g_screen->screenPointInMouseArea(x, y, &(areas[i]))) {
|
||||
return &(areas[i]);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EventHandler::handleMouseMotionEvent(const Common::Event &event) {
|
||||
if (!settings._mouseOptions._enabled)
|
||||
return;
|
||||
|
||||
const MouseArea *area;
|
||||
area = eventHandler->mouseAreaForPoint(event.mouse.x, event.mouse.y);
|
||||
if (area) {
|
||||
g_screen->setMouseCursor(area->_cursor);
|
||||
|
||||
if (_isRightButtonDown) {
|
||||
int xd = (event.mouse.x / settings._scale) - 96,
|
||||
yd = (event.mouse.y / settings._scale) - 96;
|
||||
double dist = sqrt((double)(xd * xd + yd * yd));
|
||||
_walk.setDelta(area->_direction, (int)dist);
|
||||
}
|
||||
} else {
|
||||
g_screen->setMouseCursor(MC_DEFAULT);
|
||||
if (_isRightButtonDown)
|
||||
_walk.setDelta(DIR_NONE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void EventHandler::handleMouseButtonDownEvent(const Common::Event &event, Controller *controller, updateScreenCallback updateScreen) {
|
||||
if (!settings._mouseOptions._enabled)
|
||||
return;
|
||||
|
||||
if (event.type == Common::EVENT_LBUTTONDOWN) {
|
||||
// handle the keypress
|
||||
bool processed = controller->notifyMousePress(event.mouse);
|
||||
|
||||
if (processed) {
|
||||
if (updateScreen)
|
||||
(*updateScreen)();
|
||||
g_screen->update();
|
||||
}
|
||||
|
||||
} else if (event.type == Common::EVENT_RBUTTONDOWN) {
|
||||
_isRightButtonDown = true;
|
||||
handleMouseMotionEvent(event);
|
||||
}
|
||||
|
||||
if (updateScreen)
|
||||
(*updateScreen)();
|
||||
g_screen->update();
|
||||
}
|
||||
|
||||
void EventHandler::handleMouseButtonUpEvent(const Common::Event &event, Controller *controller, updateScreenCallback updateScreen) {
|
||||
if (!settings._mouseOptions._enabled)
|
||||
return;
|
||||
|
||||
if (event.type == Common::EVENT_RBUTTONUP)
|
||||
_isRightButtonDown = false;
|
||||
}
|
||||
|
||||
void EventHandler::handleKeyDownEvent(const Common::Event &event, Controller *controller, updateScreenCallback updateScreen) {
|
||||
int key;
|
||||
bool processed;
|
||||
|
||||
key = (event.kbd.ascii != 0 && event.kbd.ascii < 128) ?
|
||||
event.kbd.ascii : (int)event.kbd.keycode;
|
||||
|
||||
key += (event.kbd.flags & (Common::KBD_CTRL |
|
||||
Common::KBD_ALT | Common::KBD_META)) << 16;
|
||||
|
||||
debug(1, "key event: sym = %d, mod = %d; translated = %d",
|
||||
event.kbd.keycode, event.kbd.flags, key);
|
||||
|
||||
// handle the keypress
|
||||
processed = controller->notifyKeyPressed(key);
|
||||
|
||||
if (processed) {
|
||||
if (updateScreen)
|
||||
(*updateScreen)();
|
||||
g_screen->update();
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
void WalkTrigger::reset() {
|
||||
_action = KEYBIND_NONE;
|
||||
_ticksCtr = 0;
|
||||
}
|
||||
|
||||
void WalkTrigger::setDelta(Direction dir, int distance) {
|
||||
if (distance > 96) {
|
||||
distance = 0;
|
||||
dir = DIR_NONE;
|
||||
}
|
||||
|
||||
KeybindingAction action;
|
||||
switch (dir) {
|
||||
case DIR_NORTH:
|
||||
action = KEYBIND_UP;
|
||||
break;
|
||||
case DIR_SOUTH:
|
||||
action = KEYBIND_DOWN;
|
||||
break;
|
||||
case DIR_WEST:
|
||||
action = KEYBIND_LEFT;
|
||||
break;
|
||||
case DIR_EAST:
|
||||
action = KEYBIND_RIGHT;
|
||||
break;
|
||||
default:
|
||||
action = KEYBIND_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (action != _action) {
|
||||
// Walk quadrant changed
|
||||
_action = action;
|
||||
_ticksCtr = 0;
|
||||
}
|
||||
|
||||
_ticksPerWalk = 4 - (distance / 25);
|
||||
}
|
||||
|
||||
KeybindingAction WalkTrigger::getAction() {
|
||||
if (--_ticksCtr <= 0) {
|
||||
_ticksCtr = _ticksPerWalk;
|
||||
return _action;
|
||||
}
|
||||
|
||||
return KEYBIND_NONE;
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
209
engines/ultima/ultima4/events/event_handler.h
Normal file
209
engines/ultima/ultima4/events/event_handler.h
Normal file
@@ -0,0 +1,209 @@
|
||||
/* 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_EVENTS_EVENT_HANDLER_H
|
||||
#define ULTIMA4_EVENTS_EVENT_HANDLER_H
|
||||
|
||||
#include "ultima/ultima4/events/timed_event_mgr.h"
|
||||
#include "ultima/ultima4/controllers/key_handler_controller.h"
|
||||
#include "ultima/ultima4/core/types.h"
|
||||
#include "ultima/ultima4/gfx/screen.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
#include "common/events.h"
|
||||
#include "common/list.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
#define eventHandler (EventHandler::getInstance())
|
||||
|
||||
#if defined(IOS_ULTIMA4)
|
||||
#ifndef __OBJC__
|
||||
typedef void *TimedManagerHelper;
|
||||
typedef void *UIEvent;
|
||||
#else
|
||||
@class TimedManagerHelper;
|
||||
@class UIEvent;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef void(*updateScreenCallback)();
|
||||
|
||||
/**
|
||||
* Encapsulates the logic for deciding how frequently to walk
|
||||
* when holding down the right moues button in enhanced mode
|
||||
*/
|
||||
class WalkTrigger {
|
||||
private:
|
||||
int _ticksCtr, _ticksPerWalk;
|
||||
KeybindingAction _action;
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
WalkTrigger() : _ticksCtr(0), _ticksPerWalk(0), _action(KEYBIND_NONE) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the walker
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Sets the delta from the center of the map
|
||||
*/
|
||||
void setDelta(Direction dir, int distance);
|
||||
|
||||
/**
|
||||
* Checks for whether to walk, and if so, returns the direction
|
||||
*/
|
||||
KeybindingAction getAction();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A class for handling game events.
|
||||
*/
|
||||
class EventHandler {
|
||||
typedef Common::List<const MouseArea *> MouseAreaList;
|
||||
private:
|
||||
static EventHandler *_instance;
|
||||
WalkTrigger _walk;
|
||||
bool _isRightButtonDown;
|
||||
private:
|
||||
void handleMouseMotionEvent(const Common::Event &event);
|
||||
void handleMouseButtonDownEvent(const Common::Event &event, Controller *controller, updateScreenCallback updateScreen);
|
||||
void handleMouseButtonUpEvent(const Common::Event &event, Controller *controller, updateScreenCallback updateScreen);
|
||||
void handleKeyDownEvent(const Common::Event &event, Controller *controller, updateScreenCallback updateScreen);
|
||||
protected:
|
||||
static bool _controllerDone;
|
||||
static bool _ended;
|
||||
TimedEventMgr _timer;
|
||||
Std::vector<Controller *> _controllers;
|
||||
MouseAreaList _mouseAreaSets;
|
||||
updateScreenCallback _updateScreen;
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
EventHandler();
|
||||
|
||||
/* Static functions */
|
||||
static EventHandler *getInstance();
|
||||
|
||||
/**
|
||||
* Delays program execution for the specified number of milliseconds.
|
||||
* This doesn't actually stop events, but it stops the user from interacting
|
||||
* While some important event happens (e.g., getting hit by a cannon ball or a spell effect).
|
||||
*/
|
||||
static void sleep(uint usec);
|
||||
|
||||
/**
|
||||
* Waits a given number of milliseconds before continuing
|
||||
*/
|
||||
static void wait_msecs(uint msecs);
|
||||
|
||||
/**
|
||||
* Waits a given number of game cycles before continuing
|
||||
*/
|
||||
static void wait_cycles(uint cycles);
|
||||
|
||||
static void setControllerDone(bool exit = true);
|
||||
|
||||
/**
|
||||
* Returns the current value of the global exit flag
|
||||
*/
|
||||
static bool getControllerDone();
|
||||
static void end();
|
||||
|
||||
/* Member functions */
|
||||
TimedEventMgr *getTimer();
|
||||
|
||||
/* Event functions */
|
||||
void run();
|
||||
void setScreenUpdate(void (*updateScreen)(void));
|
||||
#if defined(IOS_ULTIMA4)
|
||||
void handleEvent(UIEvent *);
|
||||
static void controllerStopped_helper();
|
||||
updateScreenCallback screenCallback() {
|
||||
return updateScreen;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Controller functions */
|
||||
Controller *pushController(Controller *c);
|
||||
Controller *popController();
|
||||
Controller *getController() const;
|
||||
void setController(Controller *c);
|
||||
|
||||
/* Key handler functions */
|
||||
/**
|
||||
* Adds a key handler to the stack.
|
||||
*/
|
||||
void pushKeyHandler(KeyHandler kh);
|
||||
|
||||
/**
|
||||
* Pops a key handler off the stack.
|
||||
* Returns a pointer to the resulting key handler after
|
||||
* the current handler is popped.
|
||||
*/
|
||||
void popKeyHandler();
|
||||
|
||||
/**
|
||||
* Returns a pointer to the current key handler.
|
||||
* Returns nullptr if there is no key handler.
|
||||
*/
|
||||
KeyHandler *getKeyHandler() const;
|
||||
|
||||
/**
|
||||
* Eliminates all key handlers and begins stack with new handler.
|
||||
* This pops all key handlers off the stack and adds
|
||||
* the key handler provided to the stack, making it the
|
||||
* only key handler left. Use this function only if you
|
||||
* are sure the key handlers in the stack are disposable.
|
||||
*/
|
||||
void setKeyHandler(KeyHandler kh);
|
||||
|
||||
/* Mouse area functions */
|
||||
void pushMouseAreaSet(const MouseArea *mouseAreas);
|
||||
void popMouseAreaSet();
|
||||
|
||||
/**
|
||||
* Get the currently active mouse area set off the top of the stack.
|
||||
*/
|
||||
const MouseArea *getMouseAreaSet() const;
|
||||
|
||||
const MouseArea *mouseAreaForPoint(int x, int y);
|
||||
|
||||
/**
|
||||
* Checks for whether to walk, and if so, returns the direction action
|
||||
*/
|
||||
KeybindingAction getAction() {
|
||||
return _isRightButtonDown ? _walk.getAction() : KEYBIND_NONE;
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
133
engines/ultima/ultima4/events/timed_event_mgr.cpp
Normal file
133
engines/ultima/ultima4/events/timed_event_mgr.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
/* 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/events/timed_event_mgr.h"
|
||||
#include "ultima/ultima4/gfx/screen.h"
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
TimedEventMgr::TimedEventMgr(int baseInterval) :
|
||||
_baseInterval(baseInterval), _lastTickTime(0), _locked(false) {
|
||||
}
|
||||
|
||||
void TimedEventMgr::poll() {
|
||||
uint32 time = g_system->getMillis();
|
||||
if (time >= (_lastTickTime + _baseInterval)) {
|
||||
_lastTickTime = time;
|
||||
tick();
|
||||
|
||||
g_screen->update();
|
||||
}
|
||||
}
|
||||
|
||||
void TimedEventMgr::reset(uint interval) {
|
||||
_baseInterval = interval;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
TimedEvent::TimedEvent(TimedEvent::Callback cb, int i, void *d) :
|
||||
_callback(cb),
|
||||
_data(d),
|
||||
_interval(i),
|
||||
_current(0) {
|
||||
}
|
||||
|
||||
TimedEvent::Callback TimedEvent::getCallback() const {
|
||||
return _callback;
|
||||
}
|
||||
|
||||
void *TimedEvent::getData() {
|
||||
return _data;
|
||||
}
|
||||
|
||||
void TimedEvent::tick() {
|
||||
if (++_current >= _interval) {
|
||||
(*_callback)(_data);
|
||||
_current = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool TimedEventMgr::isLocked() const {
|
||||
return _locked;
|
||||
}
|
||||
|
||||
void TimedEventMgr::add(TimedEvent::Callback theCallback, int interval, void *data) {
|
||||
_events.push_back(new TimedEvent(theCallback, interval, data));
|
||||
}
|
||||
|
||||
TimedEventMgr::List::iterator TimedEventMgr::remove(List::iterator i) {
|
||||
if (isLocked()) {
|
||||
_deferredRemovals.push_back(*i);
|
||||
return i;
|
||||
} else {
|
||||
delete *i;
|
||||
return _events.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
void TimedEventMgr::remove(TimedEvent *event) {
|
||||
List::iterator i;
|
||||
for (i = _events.begin(); i != _events.end(); i++) {
|
||||
if ((*i) == event) {
|
||||
remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimedEventMgr::remove(TimedEvent::Callback theCallback, void *data) {
|
||||
List::iterator i;
|
||||
for (i = _events.begin(); i != _events.end(); i++) {
|
||||
if ((*i)->getCallback() == theCallback && (*i)->getData() == data) {
|
||||
remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimedEventMgr::tick() {
|
||||
List::iterator i;
|
||||
lock();
|
||||
|
||||
for (i = _events.begin(); i != _events.end(); i++)
|
||||
(*i)->tick();
|
||||
|
||||
unlock();
|
||||
|
||||
// Remove events that have been deferred for removal
|
||||
for (i = _deferredRemovals.begin(); i != _deferredRemovals.end(); i++)
|
||||
_events.remove(*i);
|
||||
}
|
||||
|
||||
void TimedEventMgr::lock() {
|
||||
_locked = true;
|
||||
}
|
||||
|
||||
void TimedEventMgr::unlock() {
|
||||
_locked = false;
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
140
engines/ultima/ultima4/events/timed_event_mgr.h
Normal file
140
engines/ultima/ultima4/events/timed_event_mgr.h
Normal 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 ULTIMA4_EVENTS_TIMED_EVENT_MGR_H
|
||||
#define ULTIMA4_EVENTS_TIMED_EVENT_MGR_H
|
||||
|
||||
#include "common/list.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* A class for handling timed events.
|
||||
*/
|
||||
class TimedEvent {
|
||||
public:
|
||||
/* Typedefs */
|
||||
typedef Common::List<TimedEvent *> List;
|
||||
typedef void (*Callback)(void *);
|
||||
|
||||
/* Constructors */
|
||||
TimedEvent(Callback callback, int interval, void *data = nullptr);
|
||||
|
||||
/* Member functions */
|
||||
Callback getCallback() const;
|
||||
void *getData();
|
||||
|
||||
/**
|
||||
* Advances the timed event forward a tick.
|
||||
* When (current >= interval), then it executes its callback function.
|
||||
*/
|
||||
void tick();
|
||||
|
||||
/* Properties */
|
||||
protected:
|
||||
Callback _callback;
|
||||
void *_data;
|
||||
int _interval;
|
||||
int _current;
|
||||
};
|
||||
|
||||
/**
|
||||
* A class for managing timed events
|
||||
*/
|
||||
class TimedEventMgr {
|
||||
public:
|
||||
/* Typedefs */
|
||||
typedef TimedEvent::List List;
|
||||
|
||||
/* Constructors */
|
||||
/**
|
||||
* Constructs a timed event manager object.
|
||||
* Adds a timer callback to the SDL subsystem, which
|
||||
* will drive all of the timed events that this object
|
||||
* controls.
|
||||
*/
|
||||
TimedEventMgr(int baseInterval);
|
||||
|
||||
/**
|
||||
* Destructs a timed event manager object.
|
||||
* It removes the callback timer and un-initializes the
|
||||
* SDL subsystem if there are no other active TimedEventMgr
|
||||
* objects.
|
||||
*/
|
||||
~TimedEventMgr() {}
|
||||
|
||||
/**
|
||||
* Checks whether the frame time has expired, and if so,
|
||||
* triggers a tick
|
||||
*/
|
||||
void poll();
|
||||
|
||||
/**
|
||||
* Returns true if the event queue is locked (in use)
|
||||
*/
|
||||
bool isLocked() const;
|
||||
|
||||
/**
|
||||
* Adds a timed event to the event queue.
|
||||
*/
|
||||
void add(TimedEvent::Callback theCallback, int interval, void *data = nullptr);
|
||||
|
||||
/**
|
||||
* Removes a timed event from the event queue.
|
||||
*/
|
||||
List::iterator remove(List::iterator i);
|
||||
void remove(TimedEvent *event);
|
||||
void remove(TimedEvent::Callback theCallback, void *data = nullptr);
|
||||
|
||||
/**
|
||||
* Runs each of the callback functions of the TimedEvents associated with this manager.
|
||||
*/
|
||||
void tick();
|
||||
|
||||
/**
|
||||
* Re-initializes the timer manager to a new timer granularity
|
||||
*/
|
||||
void reset(uint interval); /**< Re-initializes the event manager to a new base interval */
|
||||
#if defined(IOS_ULTIMA4)
|
||||
bool hasActiveTimer() const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
void lock(); /**< Locks the event list */
|
||||
void unlock(); /**< Unlocks the event list */
|
||||
|
||||
/* Properties */
|
||||
protected:
|
||||
uint32 _lastTickTime;
|
||||
uint32 _baseInterval;
|
||||
bool _locked;
|
||||
List _events;
|
||||
List _deferredRemovals;
|
||||
#if defined(IOS_ULTIMA4)
|
||||
TimedManagerHelper *m_helper;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
127
engines/ultima/ultima4/filesys/rle.cpp
Normal file
127
engines/ultima/ultima4/filesys/rle.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
/* 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/filesys/rle.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* Decompress an RLE encoded file.
|
||||
*/
|
||||
long rleDecompressFile(Common::ReadStream *in, long inlen, void **out) {
|
||||
void *indata;
|
||||
long outlen;
|
||||
|
||||
/* input file should be longer than 0 bytes */
|
||||
if (inlen <= 0)
|
||||
return -1;
|
||||
|
||||
/* load compressed file into memory */
|
||||
indata = malloc(inlen);
|
||||
in->read(indata, inlen);
|
||||
|
||||
outlen = rleDecompressMemory(indata, inlen, out);
|
||||
|
||||
free(indata);
|
||||
|
||||
return outlen;
|
||||
}
|
||||
|
||||
long rleDecompressMemory(void *in, long inlen, void **out) {
|
||||
byte *indata, *outdata;
|
||||
long outlen;
|
||||
|
||||
/* input should be longer than 0 bytes */
|
||||
if (inlen <= 0)
|
||||
return -1;
|
||||
|
||||
indata = (byte *)in;
|
||||
|
||||
/* determine decompressed file size */
|
||||
outlen = rleGetDecompressedSize(indata, inlen);
|
||||
|
||||
if (outlen <= 0)
|
||||
return -1;
|
||||
|
||||
/* decompress file from inlen to outlen */
|
||||
outdata = (byte *) malloc(outlen);
|
||||
rleDecompress(indata, inlen, outdata, outlen);
|
||||
|
||||
*out = outdata;
|
||||
|
||||
return outlen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the uncompressed size of RLE compressed data.
|
||||
*/
|
||||
long rleGetDecompressedSize(byte *indata, long inlen) {
|
||||
byte *p;
|
||||
byte ch, count;
|
||||
long len = 0;
|
||||
|
||||
p = indata;
|
||||
while ((p - indata) < inlen) {
|
||||
ch = *p++;
|
||||
if (ch == RLE_RUNSTART) {
|
||||
count = *p++;
|
||||
p++;
|
||||
len += count;
|
||||
} else
|
||||
len++;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompress a block of RLE encoded memory.
|
||||
*/
|
||||
long rleDecompress(byte *indata, long inlen, byte *outdata, long outlen) {
|
||||
int i;
|
||||
byte *p, *q;
|
||||
byte ch, count, val;
|
||||
|
||||
p = indata;
|
||||
q = outdata;
|
||||
while ((p - indata) < inlen) {
|
||||
ch = *p++;
|
||||
if (ch == RLE_RUNSTART) {
|
||||
count = *p++;
|
||||
val = *p++;
|
||||
for (i = 0; i < count; i++) {
|
||||
*q++ = val;
|
||||
if ((q - outdata) >= outlen)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
*q++ = ch;
|
||||
if ((q - outdata) >= outlen)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return q - outdata;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
40
engines/ultima/ultima4/filesys/rle.h
Normal file
40
engines/ultima/ultima4/filesys/rle.h
Normal 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_FILESYS_RLE_H
|
||||
#define ULTIMA4_FILESYS_RLE_H
|
||||
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
#define RLE_RUNSTART 02
|
||||
|
||||
long rleDecompressFile(Common::ReadStream *in, long inlen, void **out);
|
||||
long rleDecompressMemory(void *in, long inlen, void **out);
|
||||
long rleGetDecompressedSize(byte *indata, long inlen);
|
||||
long rleDecompress(byte *indata, long inlen, byte *outdata, long outlen);
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
362
engines/ultima/ultima4/filesys/savegame.cpp
Normal file
362
engines/ultima/ultima4/filesys/savegame.cpp
Normal file
@@ -0,0 +1,362 @@
|
||||
/* 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/filesys/savegame.h"
|
||||
#include "ultima/ultima4/core/types.h"
|
||||
#include "ultima/ultima4/game/context.h"
|
||||
#include "ultima/ultima4/game/game.h"
|
||||
#include "ultima/ultima4/game/item.h"
|
||||
#include "ultima/ultima4/game/object.h"
|
||||
#include "ultima/ultima4/game/player.h"
|
||||
#include "ultima/ultima4/game/spell.h"
|
||||
#include "ultima/ultima4/views/stats.h"
|
||||
#include "ultima/ultima4/map/location.h"
|
||||
#include "ultima/ultima4/map/mapmgr.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
void SaveGame::save(Common::WriteStream *stream) {
|
||||
Common::Serializer ser(nullptr, stream);
|
||||
assert(g_context && g_context->_location);
|
||||
|
||||
_positions.load();
|
||||
synchronize(ser);
|
||||
|
||||
/*
|
||||
* Save monsters
|
||||
*/
|
||||
|
||||
// fix creature animations. This was done for compatibility with u4dos,
|
||||
// so may be redundant now
|
||||
g_context->_location->_map->resetObjectAnimations();
|
||||
g_context->_location->_map->fillMonsterTable();
|
||||
|
||||
SaveGameMonsterRecord::synchronize(g_context->_location->_map->_monsterTable, ser);
|
||||
|
||||
/**
|
||||
* Write dungeon info
|
||||
*/
|
||||
if (g_context->_location && g_context->_location->_prev) {
|
||||
/**
|
||||
* Write out monsters
|
||||
*/
|
||||
|
||||
// fix creature animations so they are compatible with u4dos.
|
||||
// This may be redundant now for ScummVM
|
||||
g_context->_location->_prev->_map->resetObjectAnimations();
|
||||
g_context->_location->_prev->_map->fillMonsterTable(); /* fill the monster table so we can save it */
|
||||
|
||||
SaveGameMonsterRecord::synchronize(g_context->_location->_prev->_map->_monsterTable, ser);
|
||||
}
|
||||
}
|
||||
|
||||
void SaveGame::load(Common::SeekableReadStream *stream) {
|
||||
Common::Serializer *ser = nullptr;
|
||||
assert(g_context);
|
||||
|
||||
if (stream) {
|
||||
ser = new Common::Serializer(stream, nullptr);
|
||||
synchronize(*ser);
|
||||
}
|
||||
|
||||
// initialize our party
|
||||
if (g_context->_party) {
|
||||
g_context->_party->deleteObserver(g_game);
|
||||
delete g_context->_party;
|
||||
}
|
||||
g_context->_party = new Party(this);
|
||||
g_context->_party->addObserver(g_game);
|
||||
|
||||
// Delete any prior map
|
||||
while (g_context->_location)
|
||||
locationFree(&g_context->_location);
|
||||
|
||||
// set the map to the world map
|
||||
Map *map = mapMgr->get(MAP_WORLD);
|
||||
g_game->setMap(map, 0, nullptr);
|
||||
assert(g_context->_location && g_context->_location->_map);
|
||||
g_context->_location->_map->clearObjects();
|
||||
|
||||
// initialize the moons (must be done from the world map)
|
||||
g_game->initMoons();
|
||||
|
||||
// initialize overworld position and any secondary map we're in
|
||||
g_context->_location->_coords = _positions[0];
|
||||
|
||||
for (uint idx = 1; idx < _positions.size(); ++idx) {
|
||||
map = mapMgr->get(_positions[idx]._map);
|
||||
g_game->setMap(map, 1, nullptr);
|
||||
g_context->_location->_coords = _positions[idx];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix the coordinates if they're out of bounds. This happens every
|
||||
* time on the world map because (z == -1) is no longer valid.
|
||||
* To maintain compatibility with u4dos, this value gets translated
|
||||
* when the game is saved and loaded
|
||||
*/
|
||||
if (MAP_IS_OOB(g_context->_location->_map, g_context->_location->_coords))
|
||||
g_context->_location->_coords.putInBounds(g_context->_location->_map);
|
||||
|
||||
// load in creatures
|
||||
if (ser)
|
||||
SaveGameMonsterRecord::synchronize(g_context->_location->_map->_monsterTable, *ser);
|
||||
gameFixupObjects(g_context->_location->_map);
|
||||
|
||||
/* we have previous creature information as well, load it! */
|
||||
if (g_context->_location->_prev) {
|
||||
if (ser)
|
||||
SaveGameMonsterRecord::synchronize(g_context->_location->_prev->_map->_monsterTable, *ser);
|
||||
gameFixupObjects(g_context->_location->_prev->_map);
|
||||
}
|
||||
|
||||
g_spells->spellSetEffectCallback(&gameSpellEffect);
|
||||
g_items->setDestroyAllCreaturesCallback(&gameDestroyAllCreatures);
|
||||
|
||||
g_context->_stats->resetReagentsMenu();
|
||||
|
||||
/* add some observers */
|
||||
g_context->_aura->addObserver(g_context->_stats);
|
||||
g_context->_party->addObserver(g_context->_stats);
|
||||
|
||||
g_game->initScreenWithoutReloadingState();
|
||||
|
||||
delete ser;
|
||||
}
|
||||
|
||||
void SaveGame::newGame() {
|
||||
// Most default state has already been set up by the IntroController.
|
||||
// Call the load method with no stream to handle pre-game setup
|
||||
load(nullptr);
|
||||
}
|
||||
|
||||
void SaveGame::synchronize(Common::Serializer &s) {
|
||||
int i;
|
||||
|
||||
s.syncAsUint32LE(_unknown1);
|
||||
s.syncAsUint32LE(_moves);
|
||||
|
||||
for (i = 0; i < 8; ++i)
|
||||
_players[i].synchronize(s);
|
||||
|
||||
s.syncAsUint32LE(_food);
|
||||
s.syncAsUint16LE(_gold);
|
||||
|
||||
for (i = 0; i < 8; ++i)
|
||||
s.syncAsUint16LE(_karma[i]);
|
||||
|
||||
s.syncAsUint16LE(_torches);
|
||||
s.syncAsUint16LE(_gems);
|
||||
s.syncAsUint16LE(_keys);
|
||||
s.syncAsUint16LE(_sextants);
|
||||
|
||||
for (i = 0; i < ARMR_MAX; ++i)
|
||||
s.syncAsUint16LE(_armor[i]);
|
||||
|
||||
for (i = 0; i < WEAP_MAX; ++i)
|
||||
s.syncAsUint16LE(_weapons[i]);
|
||||
|
||||
for (i = 0; i < REAG_MAX; ++i)
|
||||
s.syncAsUint16LE(_reagents[i]);
|
||||
|
||||
for (i = 0; i < SPELL_MAX; ++i)
|
||||
s.syncAsUint16LE(_mixtures[i]);
|
||||
|
||||
_positions.synchronize(s);
|
||||
s.syncAsUint16LE(_orientation);
|
||||
|
||||
s.syncAsUint16LE(_items);
|
||||
s.syncAsByte(_stones);
|
||||
s.syncAsByte(_runes);
|
||||
s.syncAsUint16LE(_members);
|
||||
s.syncAsUint16LE(_transport);
|
||||
s.syncAsUint16LE(_balloonState);
|
||||
s.syncAsUint16LE(_trammelPhase);
|
||||
s.syncAsUint16LE(_feluccaPhase);
|
||||
s.syncAsUint16LE(_shipHull);
|
||||
s.syncAsUint16LE(_lbIntro);
|
||||
s.syncAsUint16LE(_lastCamp);
|
||||
s.syncAsUint16LE(_lastReagent);
|
||||
s.syncAsUint16LE(_lastMeditation);
|
||||
s.syncAsUint16LE(_lastVirtue);
|
||||
}
|
||||
|
||||
void SaveGame::init(const SaveGamePlayerRecord *avatarInfo) {
|
||||
int i;
|
||||
|
||||
_unknown1 = 0;
|
||||
_moves = 0;
|
||||
|
||||
_players[0] = *avatarInfo;
|
||||
for (i = 1; i < 8; ++i)
|
||||
_players[i].init();
|
||||
|
||||
_food = 0;
|
||||
_gold = 0;
|
||||
|
||||
for (i = 0; i < 8; ++i)
|
||||
_karma[i] = 20;
|
||||
|
||||
_torches = 0;
|
||||
_gems = 0;
|
||||
_keys = 0;
|
||||
_sextants = 0;
|
||||
|
||||
for (i = 0; i < ARMR_MAX; ++i)
|
||||
_armor[i] = 0;
|
||||
|
||||
for (i = 0; i < WEAP_MAX; ++i)
|
||||
_weapons[i] = 0;
|
||||
|
||||
for (i = 0; i < REAG_MAX; ++i)
|
||||
_reagents[i] = 0;
|
||||
|
||||
for (i = 0; i < SPELL_MAX; ++i)
|
||||
_mixtures[i] = 0;
|
||||
|
||||
_items = 0;
|
||||
_stones = 0;
|
||||
_runes = 0;
|
||||
_members = 1;
|
||||
_transport = 0x1f;
|
||||
_balloonState = 0;
|
||||
_trammelPhase = 0;
|
||||
_feluccaPhase = 0;
|
||||
_shipHull = 50;
|
||||
_lbIntro = 0;
|
||||
_lastCamp = 0;
|
||||
_lastReagent = 0;
|
||||
_lastMeditation = 0;
|
||||
_lastVirtue = 0;
|
||||
_orientation = 0;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
void SaveGamePlayerRecord::synchronize(Common::Serializer &s) {
|
||||
s.syncAsUint16LE(_hp);
|
||||
s.syncAsUint16LE(_hpMax);
|
||||
s.syncAsUint16LE(_xp);
|
||||
s.syncAsUint16LE(_str);
|
||||
s.syncAsUint16LE(_dex);
|
||||
s.syncAsUint16LE(_intel);
|
||||
s.syncAsUint16LE(_mp);
|
||||
s.syncAsUint16LE(_unknown);
|
||||
s.syncAsUint16LE(_weapon);
|
||||
s.syncAsUint16LE(_armor);
|
||||
s.syncBytes((byte *)_name, 16);
|
||||
s.syncAsByte(_sex);
|
||||
s.syncAsByte(_class);
|
||||
s.syncAsByte(_status);
|
||||
}
|
||||
|
||||
void SaveGamePlayerRecord::init() {
|
||||
int i;
|
||||
|
||||
_hp = 0;
|
||||
_hpMax = 0;
|
||||
_xp = 0;
|
||||
_str = 0;
|
||||
_dex = 0;
|
||||
_intel = 0;
|
||||
_mp = 0;
|
||||
_unknown = 0;
|
||||
_weapon = WEAP_HANDS;
|
||||
_armor = ARMR_NONE;
|
||||
|
||||
for (i = 0; i < 16; ++i)
|
||||
_name[i] = '\0';
|
||||
|
||||
_sex = SEX_MALE;
|
||||
_class = CLASS_MAGE;
|
||||
_status = STAT_GOOD;
|
||||
}
|
||||
|
||||
void SaveGameMonsterRecord::synchronize(SaveGameMonsterRecord *monsterTable, Common::Serializer &s) {
|
||||
int i;
|
||||
const uint32 IDENT = MKTAG('M', 'O', 'N', 'S');
|
||||
uint32 val = IDENT;
|
||||
|
||||
s.syncAsUint32BE(val);
|
||||
if (s.isLoading() && val != IDENT)
|
||||
error("Invalid savegame");
|
||||
|
||||
if (s.isSaving() && !monsterTable) {
|
||||
int dataSize = MONSTERTABLE_SIZE * 8;
|
||||
byte b = 0;
|
||||
while (dataSize-- > 0)
|
||||
s.syncAsByte(b);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < MONSTERTABLE_SIZE; ++i)
|
||||
s.syncAsByte(monsterTable[i]._tile);
|
||||
for (i = 0; i < MONSTERTABLE_SIZE; ++i)
|
||||
s.syncAsByte(monsterTable[i]._x);
|
||||
for (i = 0; i < MONSTERTABLE_SIZE; ++i)
|
||||
s.syncAsByte(monsterTable[i]._y);
|
||||
for (i = 0; i < MONSTERTABLE_SIZE; ++i)
|
||||
s.syncAsByte(monsterTable[i]._prevTile);
|
||||
for (i = 0; i < MONSTERTABLE_SIZE; ++i)
|
||||
s.syncAsByte(monsterTable[i]._prevX);
|
||||
for (i = 0; i < MONSTERTABLE_SIZE; ++i)
|
||||
s.syncAsByte(monsterTable[i]._prevY);
|
||||
for (i = 0; i < MONSTERTABLE_SIZE; ++i)
|
||||
s.syncAsByte(monsterTable[i]._unused1);
|
||||
for (i = 0; i < MONSTERTABLE_SIZE; ++i)
|
||||
s.syncAsByte(monsterTable[i]._unused2);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
void LocationCoordsArray::load() {
|
||||
clear();
|
||||
|
||||
for (Location *l = g_context->_location; l; l = l->_prev)
|
||||
insert_at(0, LocationCoords(l->_map->_id, l->_coords));
|
||||
}
|
||||
|
||||
void LocationCoords::synchronize(Common::Serializer &s) {
|
||||
s.syncAsByte(x);
|
||||
s.syncAsByte(y);
|
||||
s.syncAsByte(z);
|
||||
s.syncAsByte(_map);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
void LocationCoordsArray::synchronize(Common::Serializer &s) {
|
||||
byte count = size();
|
||||
s.syncAsByte(count);
|
||||
|
||||
if (s.isLoading())
|
||||
resize(count);
|
||||
|
||||
for (uint idx = 0; idx < count; ++idx)
|
||||
(*this)[idx].synchronize(s);
|
||||
|
||||
assert(!empty() && (*this)[0]._map == MAP_WORLD);
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
336
engines/ultima/ultima4/filesys/savegame.h
Normal file
336
engines/ultima/ultima4/filesys/savegame.h
Normal file
@@ -0,0 +1,336 @@
|
||||
/* 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_FILESYS_SAVEGAME_H
|
||||
#define ULTIMA4_FILESYS_SAVEGAME_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/serializer.h"
|
||||
#include "ultima/ultima4/core/coords.h"
|
||||
#include "ultima/ultima4/core/types.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
#define PARTY_SAV_BASE_FILENAME "party.sav"
|
||||
#define MONSTERS_SAV_BASE_FILENAME "monsters.sav"
|
||||
#define OUTMONST_SAV_BASE_FILENAME "outmonst.sav"
|
||||
|
||||
#define MONSTERTABLE_SIZE 32
|
||||
#define MONSTERTABLE_CREATURES_SIZE 8
|
||||
#define MONSTERTABLE_OBJECTS_SIZE (MONSTERTABLE_SIZE - MONSTERTABLE_CREATURES_SIZE)
|
||||
|
||||
class Object;
|
||||
|
||||
/**
|
||||
* The list of all weapons. These values are used in both the
|
||||
* inventory fields and character records of the savegame.
|
||||
*/
|
||||
enum WeaponType {
|
||||
WEAP_HANDS,
|
||||
WEAP_STAFF,
|
||||
WEAP_DAGGER,
|
||||
WEAP_SLING,
|
||||
WEAP_MACE,
|
||||
WEAP_AXE,
|
||||
WEAP_SWORD,
|
||||
WEAP_BOW,
|
||||
WEAP_CROSSBOW,
|
||||
WEAP_OIL,
|
||||
WEAP_HALBERD,
|
||||
WEAP_MAGICAXE,
|
||||
WEAP_MAGICSWORD,
|
||||
WEAP_MAGICBOW,
|
||||
WEAP_MAGICWAND,
|
||||
WEAP_MYSTICSWORD,
|
||||
WEAP_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* The list of all armor types. These values are used in both the
|
||||
* inventory fields and character records of the savegame.
|
||||
*/
|
||||
enum ArmorType {
|
||||
ARMR_NONE,
|
||||
ARMR_CLOTH,
|
||||
ARMR_LEATHER,
|
||||
ARMR_CHAIN,
|
||||
ARMR_PLATE,
|
||||
ARMR_MAGICCHAIN,
|
||||
ARMR_MAGICPLATE,
|
||||
ARMR_MYSTICROBES,
|
||||
ARMR_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* The list of sex values for the savegame character records. The
|
||||
* values match the male and female symbols in the character set.
|
||||
*/
|
||||
enum SexType {
|
||||
SEX_MALE = 0xb,
|
||||
SEX_FEMALE = 0xc
|
||||
};
|
||||
|
||||
/**
|
||||
* The list of class types for the savegame character records.
|
||||
*/
|
||||
enum ClassType {
|
||||
CLASS_MAGE,
|
||||
CLASS_BARD,
|
||||
CLASS_FIGHTER,
|
||||
CLASS_DRUID,
|
||||
CLASS_TINKER,
|
||||
CLASS_PALADIN,
|
||||
CLASS_RANGER,
|
||||
CLASS_SHEPHERD
|
||||
};
|
||||
|
||||
/**
|
||||
* The list of status values for the savegame character records. The
|
||||
* values match the letter that's appear in the ztats area.
|
||||
*/
|
||||
enum StatusType {
|
||||
STAT_GOOD = 'G',
|
||||
STAT_POISONED = 'P',
|
||||
STAT_SLEEPING = 'S',
|
||||
STAT_DEAD = 'D'
|
||||
};
|
||||
|
||||
enum Virtue {
|
||||
VIRT_HONESTY,
|
||||
VIRT_COMPASSION,
|
||||
VIRT_VALOR,
|
||||
VIRT_JUSTICE,
|
||||
VIRT_SACRIFICE,
|
||||
VIRT_HONOR,
|
||||
VIRT_SPIRITUALITY,
|
||||
VIRT_HUMILITY,
|
||||
VIRT_MAX
|
||||
};
|
||||
|
||||
enum BaseVirtue {
|
||||
VIRT_NONE = 0x00,
|
||||
VIRT_TRUTH = 0x01,
|
||||
VIRT_LOVE = 0x02,
|
||||
VIRT_COURAGE = 0x04
|
||||
};
|
||||
|
||||
enum Reagent {
|
||||
REAG_ASH,
|
||||
REAG_GINSENG,
|
||||
REAG_GARLIC,
|
||||
REAG_SILK,
|
||||
REAG_MOSS,
|
||||
REAG_PEARL,
|
||||
REAG_NIGHTSHADE,
|
||||
REAG_MANDRAKE,
|
||||
REAG_MAX
|
||||
};
|
||||
|
||||
#define SPELL_MAX 26
|
||||
|
||||
enum Item {
|
||||
ITEM_SKULL = 0x01,
|
||||
ITEM_SKULL_DESTROYED = 0x02,
|
||||
ITEM_CANDLE = 0x04,
|
||||
ITEM_BOOK = 0x08,
|
||||
ITEM_BELL = 0x10,
|
||||
ITEM_KEY_C = 0x20,
|
||||
ITEM_KEY_L = 0x40,
|
||||
ITEM_KEY_T = 0x80,
|
||||
ITEM_HORN = 0x100,
|
||||
ITEM_WHEEL = 0x200,
|
||||
ITEM_CANDLE_USED = 0x400,
|
||||
ITEM_BOOK_USED = 0x800,
|
||||
ITEM_BELL_USED = 0x1000
|
||||
};
|
||||
|
||||
enum Stone {
|
||||
STONE_BLUE = 0x01,
|
||||
STONE_YELLOW = 0x02,
|
||||
STONE_RED = 0x04,
|
||||
STONE_GREEN = 0x08,
|
||||
STONE_ORANGE = 0x10,
|
||||
STONE_PURPLE = 0x20,
|
||||
STONE_WHITE = 0x40,
|
||||
STONE_BLACK = 0x80
|
||||
};
|
||||
|
||||
enum Rune {
|
||||
RUNE_HONESTY = 0x01,
|
||||
RUNE_COMPASSION = 0x02,
|
||||
RUNE_VALOR = 0x04,
|
||||
RUNE_JUSTICE = 0x08,
|
||||
RUNE_SACRIFICE = 0x10,
|
||||
RUNE_HONOR = 0x20,
|
||||
RUNE_SPIRITUALITY = 0x40,
|
||||
RUNE_HUMILITY = 0x80
|
||||
};
|
||||
|
||||
/**
|
||||
* The Ultima IV savegame player record data.
|
||||
*/
|
||||
struct SaveGamePlayerRecord {
|
||||
void synchronize(Common::Serializer &s);
|
||||
void init();
|
||||
|
||||
unsigned short _hp;
|
||||
unsigned short _hpMax;
|
||||
unsigned short _xp;
|
||||
unsigned short _str, _dex, _intel;
|
||||
unsigned short _mp;
|
||||
unsigned short _unknown;
|
||||
WeaponType _weapon;
|
||||
ArmorType _armor;
|
||||
char _name[16];
|
||||
SexType _sex;
|
||||
ClassType _class;
|
||||
StatusType _status;
|
||||
};
|
||||
|
||||
/**
|
||||
* How Ultima IV stores monster information
|
||||
*/
|
||||
struct SaveGameMonsterRecord {
|
||||
byte _tile;
|
||||
byte _x;
|
||||
byte _y;
|
||||
byte _prevTile;
|
||||
byte _prevX;
|
||||
byte _prevY;
|
||||
byte _unused1;
|
||||
byte _unused2;
|
||||
|
||||
SaveGameMonsterRecord() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
_tile = _x = _y = 0;
|
||||
_prevTile = _prevX = _prevY = 0;
|
||||
_unused1 = _unused2 = 0;
|
||||
}
|
||||
|
||||
static void synchronize(SaveGameMonsterRecord *monsterTable, Common::Serializer &s);
|
||||
};
|
||||
|
||||
class LocationCoords : public Coords {
|
||||
public:
|
||||
MapId _map;
|
||||
|
||||
LocationCoords() : Coords(), _map(0xff) {}
|
||||
LocationCoords(MapId map, int x_, int y_, int z_) :
|
||||
Coords(x_, y_, z_), _map(map) {}
|
||||
LocationCoords(MapId map, const Coords &pos) :
|
||||
Coords(pos), _map(map) {}
|
||||
|
||||
/**
|
||||
* Synchronize to/from a savegame
|
||||
*/
|
||||
void synchronize(Common::Serializer &s);
|
||||
};
|
||||
|
||||
class LocationCoordsArray : public Common::Array<LocationCoords> {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Loads the list of map & coordinates from the game context
|
||||
*/
|
||||
void load();
|
||||
|
||||
/**
|
||||
* Synchronize to/from a savegame
|
||||
*/
|
||||
void synchronize(Common::Serializer &s);
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the on-disk contents of PARTY.SAV.
|
||||
*/
|
||||
struct SaveGame {
|
||||
/**
|
||||
* Initialize a new savegame structure
|
||||
*/
|
||||
void init(const SaveGamePlayerRecord *avatarInfo);
|
||||
|
||||
/**
|
||||
* Load an entire savegame, including monsters
|
||||
*/
|
||||
void save(Common::WriteStream *stream);
|
||||
|
||||
/**
|
||||
* Save an entire savegame, including monsters
|
||||
*/
|
||||
void load(Common::SeekableReadStream *stream);
|
||||
|
||||
/**
|
||||
* Called when a new game is started without loading an existing
|
||||
* savegame from launcher.
|
||||
*/
|
||||
void newGame();
|
||||
|
||||
/**
|
||||
* Synchronizes data for the savegame structure
|
||||
*/
|
||||
void synchronize(Common::Serializer &s);
|
||||
|
||||
uint _unknown1;
|
||||
uint _moves;
|
||||
SaveGamePlayerRecord _players[8];
|
||||
int _food;
|
||||
short _gold;
|
||||
short _karma[VIRT_MAX];
|
||||
short _torches;
|
||||
short _gems;
|
||||
short _keys;
|
||||
short _sextants;
|
||||
short _armor[ARMR_MAX];
|
||||
short _weapons[WEAP_MAX];
|
||||
short _reagents[REAG_MAX];
|
||||
short _mixtures[SPELL_MAX];
|
||||
unsigned short _items;
|
||||
LocationCoordsArray _positions;
|
||||
unsigned short _orientation;
|
||||
|
||||
byte _stones;
|
||||
byte _runes;
|
||||
unsigned short _members;
|
||||
unsigned short _transport;
|
||||
union {
|
||||
unsigned short _balloonState;
|
||||
unsigned short _torchDuration;
|
||||
};
|
||||
unsigned short _trammelPhase;
|
||||
unsigned short _feluccaPhase;
|
||||
unsigned short _shipHull;
|
||||
unsigned short _lbIntro;
|
||||
unsigned short _lastCamp;
|
||||
unsigned short _lastReagent;
|
||||
unsigned short _lastMeditation;
|
||||
unsigned short _lastVirtue;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
44
engines/ultima/ultima4/filesys/u4file.cpp
Normal file
44
engines/ultima/ultima4/filesys/u4file.cpp
Normal 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 "ultima/ultima4/filesys/u4file.h"
|
||||
#include "common/file.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
Std::vector<Common::String> u4read_stringtable(const Common::String &filename) {
|
||||
Common::File f;
|
||||
if (!f.open(Common::Path(Common::String::format("data/text/%s.dat", filename.c_str()))))
|
||||
error("Could not open string table '%s'", filename.c_str());
|
||||
|
||||
Std::vector<Common::String> strs;
|
||||
Common::String line;
|
||||
int64 filesize = f.size();
|
||||
|
||||
while (f.pos() < filesize)
|
||||
strs.push_back(f.readString());
|
||||
|
||||
return strs;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
41
engines/ultima/ultima4/filesys/u4file.h
Normal file
41
engines/ultima/ultima4/filesys/u4file.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* 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_FILE_H
|
||||
#define ULTIMA4_FILE_H
|
||||
|
||||
#include "common/str.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* Read a series of zero terminated strings from a file. The strings
|
||||
* are read from the given offset, or the current file position if
|
||||
* offset is -1.
|
||||
*/
|
||||
extern Std::vector<Common::String> u4read_stringtable(const Common::String &filename);
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
112
engines/ultima/ultima4/game/armor.cpp
Normal file
112
engines/ultima/ultima4/game/armor.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
/* 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/game/armor.h"
|
||||
#include "ultima/ultima4/core/config.h"
|
||||
#include "ultima/ultima4/game/names.h"
|
||||
#include "ultima/ultima4/map/tile.h"
|
||||
#include "common/algorithm.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
Armors *g_armors;
|
||||
|
||||
Armors::Armors() : _confLoaded(false) {
|
||||
g_armors = this;
|
||||
}
|
||||
|
||||
Armors::~Armors() {
|
||||
g_armors = nullptr;
|
||||
}
|
||||
|
||||
const Armor *Armors::get(ArmorType a) {
|
||||
// Load in XML if it hasn't been already
|
||||
loadConf();
|
||||
|
||||
if (static_cast<unsigned>(a) >= size())
|
||||
return nullptr;
|
||||
return (*this)[a];
|
||||
}
|
||||
|
||||
const Armor *Armors::get(const Common::String &name) {
|
||||
// Load in XML if it hasn't been already
|
||||
loadConf();
|
||||
|
||||
for (unsigned i = 0; i < size(); i++) {
|
||||
if (scumm_stricmp(name.c_str(), (*this)[i]->_name.c_str()) == 0)
|
||||
return (*this)[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void Armors::loadConf() {
|
||||
if (!_confLoaded)
|
||||
_confLoaded = true;
|
||||
else
|
||||
return;
|
||||
|
||||
const Config *config = Config::getInstance();
|
||||
|
||||
Std::vector<ConfigElement> armorConfs = config->getElement("armors").getChildren();
|
||||
for (const auto &i : armorConfs) {
|
||||
if (i.getName() != "armor")
|
||||
continue;
|
||||
|
||||
ArmorType armorType = static_cast<ArmorType>(size());
|
||||
push_back(new Armor(armorType, i));
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
Armor::Armor(ArmorType armorType, const ConfigElement &conf) :
|
||||
_type(armorType), _canUse(0xff) /*, _mask(0) */ {
|
||||
_name = conf.getString("name");
|
||||
_defense = conf.getInt("defense");
|
||||
|
||||
Std::vector<ConfigElement> contraintConfs = conf.getChildren();
|
||||
for (const auto &i : contraintConfs) {
|
||||
byte useMask = 0;
|
||||
|
||||
if (i.getName() != "constraint")
|
||||
continue;
|
||||
|
||||
for (int cl = 0; cl < 8; cl++) {
|
||||
if (scumm_stricmp(i.getString("class").c_str(), getClassName(static_cast<ClassType>(cl))) == 0)
|
||||
useMask = (1 << cl);
|
||||
}
|
||||
if (useMask == 0 && scumm_stricmp(i.getString("class").c_str(), "all") == 0)
|
||||
useMask = 0xFF;
|
||||
if (useMask == 0) {
|
||||
error("malformed armor.xml file: constraint has unknown class %s",
|
||||
i.getString("class").c_str());
|
||||
}
|
||||
if (i.getBool("canuse"))
|
||||
_canUse |= useMask;
|
||||
else
|
||||
_canUse &= ~useMask;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
94
engines/ultima/ultima4/game/armor.h
Normal file
94
engines/ultima/ultima4/game/armor.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_GAME_ARMOR_H
|
||||
#define ULTIMA4_GAME_ARMOR_H
|
||||
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
#include "ultima/shared/std/string.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class ConfigElement;
|
||||
class Armors;
|
||||
|
||||
class Armor {
|
||||
friend class Armors;
|
||||
public:
|
||||
// Getters
|
||||
ArmorType getType() const {
|
||||
return _type; /**< Returns the ArmorType of the armor */
|
||||
}
|
||||
const Common::String &getName() const {
|
||||
return _name; /**< Returns the name of the armor */
|
||||
}
|
||||
int getDefense() const {
|
||||
return _defense; /**< Returns the defense value of the armor */
|
||||
}
|
||||
/** Returns true if the class given can wear the armor */
|
||||
bool canWear(ClassType klass) const {
|
||||
return _canUse & (1 << klass);
|
||||
}
|
||||
|
||||
private:
|
||||
Armor(ArmorType armorType, const ConfigElement &conf);
|
||||
|
||||
ArmorType _type;
|
||||
Common::String _name;
|
||||
byte _canUse;
|
||||
int _defense;
|
||||
//unsigned short _mask;
|
||||
};
|
||||
|
||||
class Armors : public Std::vector<Armor *> {
|
||||
private:
|
||||
void loadConf();
|
||||
bool _confLoaded;
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Armors();
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Armors();
|
||||
|
||||
/**
|
||||
* Returns armor by ArmorType.
|
||||
*/
|
||||
const Armor *get(ArmorType a);
|
||||
|
||||
/**
|
||||
* Returns armor that has the given name
|
||||
*/
|
||||
const Armor *get(const Common::String &name);
|
||||
};
|
||||
|
||||
extern Armors *g_armors;
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
62
engines/ultima/ultima4/game/aura.cpp
Normal file
62
engines/ultima/ultima4/game/aura.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/game/aura.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
Aura::Aura() : _type(NONE), _duration(0) {}
|
||||
|
||||
void Aura::setDuration(int d) {
|
||||
_duration = d;
|
||||
setChanged();
|
||||
notifyObservers(nullptr);
|
||||
}
|
||||
|
||||
void Aura::set(Type t, int d) {
|
||||
_type = t;
|
||||
_duration = d;
|
||||
setChanged();
|
||||
notifyObservers(nullptr);
|
||||
}
|
||||
|
||||
void Aura::setType(Type t) {
|
||||
_type = t;
|
||||
setChanged();
|
||||
notifyObservers(nullptr);
|
||||
}
|
||||
|
||||
void Aura::passTurn() {
|
||||
if (_duration > 0) {
|
||||
_duration--;
|
||||
|
||||
if (_duration == 0) {
|
||||
_type = NONE;
|
||||
|
||||
setChanged();
|
||||
notifyObservers(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
77
engines/ultima/ultima4/game/aura.h
Normal file
77
engines/ultima/ultima4/game/aura.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/* 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_GAME_AURA_H
|
||||
#define ULTIMA4_GAME_AURA_H
|
||||
|
||||
#include "ultima/ultima4/core/observable.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* Aura class
|
||||
*/
|
||||
class Aura : public Observable<Aura *> {
|
||||
public:
|
||||
enum Type {
|
||||
NONE,
|
||||
HORN,
|
||||
JINX,
|
||||
NEGATE,
|
||||
PROTECTION,
|
||||
QUICKNESS
|
||||
};
|
||||
|
||||
Aura();
|
||||
|
||||
int getDuration() const {
|
||||
return _duration;
|
||||
}
|
||||
Aura::Type getType() const {
|
||||
return _type;
|
||||
}
|
||||
bool isActive() const {
|
||||
return _duration > 0;
|
||||
}
|
||||
|
||||
void setDuration(int d);
|
||||
void set(Type = NONE, int d = 0);
|
||||
void setType(Type t);
|
||||
|
||||
bool operator==(const Type &t) const {
|
||||
return _type == t;
|
||||
}
|
||||
bool operator!=(const Type &t) const {
|
||||
return !operator==(t);
|
||||
}
|
||||
|
||||
void passTurn();
|
||||
|
||||
private:
|
||||
Type _type;
|
||||
int _duration;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
435
engines/ultima/ultima4/game/codex.cpp
Normal file
435
engines/ultima/ultima4/game/codex.cpp
Normal file
@@ -0,0 +1,435 @@
|
||||
/* 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/game/codex.h"
|
||||
#include "ultima/ultima4/game/context.h"
|
||||
#include "ultima/ultima4/events/event_handler.h"
|
||||
#include "ultima/ultima4/game/game.h"
|
||||
#include "ultima/ultima4/game/item.h"
|
||||
#include "ultima/ultima4/gfx/imagemgr.h"
|
||||
#include "ultima/ultima4/game/names.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/gfx/screen.h"
|
||||
#include "ultima/ultima4/views/stats.h"
|
||||
#include "ultima/ultima4/ultima4.h"
|
||||
#include "ultima/ultima4/filesys/u4file.h"
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
Codex *g_codex;
|
||||
|
||||
Codex::Codex() {
|
||||
g_codex = this;
|
||||
}
|
||||
|
||||
Codex::~Codex() {
|
||||
g_codex = nullptr;
|
||||
}
|
||||
|
||||
int Codex::init() {
|
||||
_virtueQuestions = u4read_stringtable("virtue");
|
||||
_endgameText1 = u4read_stringtable("endgame1");
|
||||
_endgameText2 = u4read_stringtable("endgame2");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Codex::deinit() {
|
||||
_virtueQuestions.clear();
|
||||
_endgameText1.clear();
|
||||
_endgameText2.clear();
|
||||
}
|
||||
|
||||
void Codex::start() {
|
||||
init();
|
||||
|
||||
// Disable the whirlpool cursor and black out the screen
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSHideGameControllerHelper hideControllsHelper;
|
||||
#endif
|
||||
g_screen->screenDisableCursor();
|
||||
g_screen->screenUpdate(&g_game->_mapArea, false, true);
|
||||
|
||||
// Make the avatar alone
|
||||
|
||||
g_context->_stats->setView(STATS_PARTY_OVERVIEW);
|
||||
g_context->_stats->update(true); // show just the avatar
|
||||
g_screen->update();
|
||||
|
||||
// Change the view mode so the dungeon doesn't get shown
|
||||
|
||||
gameSetViewMode(VIEW_CODEX);
|
||||
|
||||
g_screen->screenMessage("\n\n\n\nThere is a sudden darkness, and you find yourself alone in an empty chamber.\n");
|
||||
EventHandler::sleep(4000);
|
||||
|
||||
// Check to see if you have the 3-part key
|
||||
if ((g_ultima->_saveGame->_items & (ITEM_KEY_C | ITEM_KEY_L | ITEM_KEY_T)) != (ITEM_KEY_C | ITEM_KEY_L | ITEM_KEY_T)) {
|
||||
eject(CODEX_EJECT_NO_3_PART_KEY);
|
||||
return;
|
||||
}
|
||||
|
||||
g_screen->screenDrawImageInMapArea(BKGD_KEY);
|
||||
g_screen->screenRedrawMapArea();
|
||||
|
||||
g_screen->screenMessage("\nYou use your key of Three Parts.\n");
|
||||
EventHandler::sleep(3000);
|
||||
|
||||
g_screen->screenMessage("\nA voice rings out:\n\"What is the Word of Passage?\"\n\n");
|
||||
|
||||
// Get the Word of Passage
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSConversationHelper::setIntroString("What is the Word of Passage?");
|
||||
#endif
|
||||
handleWOP(gameGetInput());
|
||||
}
|
||||
|
||||
void Codex::eject(CodexEjectCode code) {
|
||||
const struct {
|
||||
int x, y;
|
||||
} startLocations[] = {
|
||||
{ 231, 136 },
|
||||
{ 83, 105 },
|
||||
{ 35, 221 },
|
||||
{ 59, 44 },
|
||||
{ 158, 21 },
|
||||
{ 105, 183 },
|
||||
{ 23, 129 },
|
||||
{ 186, 171 }
|
||||
};
|
||||
|
||||
switch (code) {
|
||||
case CODEX_EJECT_NO_3_PART_KEY:
|
||||
g_screen->screenMessage("\nThou dost not have the Key of Three Parts.\n\n");
|
||||
break;
|
||||
case CODEX_EJECT_NO_FULL_PARTY:
|
||||
g_screen->screenMessage("\nThou hast not proved thy leadership in all eight virtues.\n\n");
|
||||
EventHandler::sleep(2000);
|
||||
g_screen->screenMessage("\nPassage is not granted.\n\n");
|
||||
break;
|
||||
case CODEX_EJECT_NO_FULL_AVATAR:
|
||||
g_screen->screenMessage("\nThou art not ready.\n");
|
||||
EventHandler::sleep(2000);
|
||||
g_screen->screenMessage("\nPassage is not granted.\n\n");
|
||||
break;
|
||||
case CODEX_EJECT_BAD_WOP:
|
||||
g_screen->screenMessage("\nPassage is not granted.\n\n");
|
||||
break;
|
||||
case CODEX_EJECT_HONESTY:
|
||||
case CODEX_EJECT_COMPASSION:
|
||||
case CODEX_EJECT_VALOR:
|
||||
case CODEX_EJECT_JUSTICE:
|
||||
case CODEX_EJECT_SACRIFICE:
|
||||
case CODEX_EJECT_HONOR:
|
||||
case CODEX_EJECT_SPIRITUALITY:
|
||||
case CODEX_EJECT_HUMILITY:
|
||||
case CODEX_EJECT_TRUTH:
|
||||
case CODEX_EJECT_LOVE:
|
||||
case CODEX_EJECT_COURAGE:
|
||||
g_screen->screenMessage("\nThy quest is not yet complete.\n\n");
|
||||
break;
|
||||
case CODEX_EJECT_BAD_INFINITY:
|
||||
g_screen->screenMessage("\nThou dost not know the true nature of the Universe.\n\n");
|
||||
break;
|
||||
default:
|
||||
g_screen->screenMessage("\nOops, you just got too close to beating the game.\nBAD AVATAR!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
EventHandler::sleep(2000);
|
||||
|
||||
// Free memory associated with the Codex
|
||||
deinit();
|
||||
|
||||
// Re-enable the cursor and show it
|
||||
g_screen->screenEnableCursor();
|
||||
g_screen->screenShowCursor();
|
||||
|
||||
// Return view to normal and exit the Abyss
|
||||
gameSetViewMode(VIEW_NORMAL);
|
||||
g_game->exitToParentMap();
|
||||
g_music->playMapMusic();
|
||||
|
||||
/*
|
||||
* if being ejected because of a missed virtue question,
|
||||
* then teleport the party to the starting location for
|
||||
* that virtue.
|
||||
*/
|
||||
if (code >= CODEX_EJECT_HONESTY && code <= CODEX_EJECT_HUMILITY) {
|
||||
int virtue = code - CODEX_EJECT_HONESTY;
|
||||
g_context->_location->_coords.x = startLocations[virtue].x;
|
||||
g_context->_location->_coords.y = startLocations[virtue].y;
|
||||
}
|
||||
|
||||
// finally, finish the turn
|
||||
g_context->_location->_turnCompleter->finishTurn();
|
||||
eventHandler->setController(g_game);
|
||||
}
|
||||
|
||||
void Codex::handleWOP(const Common::String &word) {
|
||||
static int tries = 1;
|
||||
int i;
|
||||
|
||||
eventHandler->popKeyHandler();
|
||||
|
||||
// slight pause before continuing
|
||||
g_screen->screenMessage("\n");
|
||||
g_screen->screenDisableCursor();
|
||||
EventHandler::sleep(1000);
|
||||
|
||||
// entered correctly
|
||||
if (scumm_stricmp(word.c_str(), "veramocor") == 0) {
|
||||
tries = 1; // reset 'tries' in case we need to enter this again later
|
||||
|
||||
// eject them if they don't have all 8 party members
|
||||
if (g_ultima->_saveGame->_members != 8) {
|
||||
eject(CODEX_EJECT_NO_FULL_PARTY);
|
||||
return;
|
||||
}
|
||||
|
||||
// eject them if they're not a full avatar at this point
|
||||
for (i = 0; i < VIRT_MAX; i++) {
|
||||
if (g_ultima->_saveGame->_karma[i] != 0) {
|
||||
eject(CODEX_EJECT_NO_FULL_AVATAR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
g_screen->screenMessage("\nPassage is granted.\n");
|
||||
EventHandler::sleep(4000);
|
||||
|
||||
g_screen->screenEraseMapArea();
|
||||
g_screen->screenRedrawMapArea();
|
||||
|
||||
// Ask the Virtue questions
|
||||
g_screen->screenMessage("\n\nThe voice asks:\n");
|
||||
EventHandler::sleep(2000);
|
||||
g_screen->screenMessage("\n%s\n\n", _virtueQuestions[0].c_str());
|
||||
|
||||
handleVirtues(gameGetInput());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// entered incorrectly - give 3 tries before ejecting
|
||||
else if (tries++ < 3) {
|
||||
impureThoughts();
|
||||
g_screen->screenMessage("\"What is the Word of Passage?\"\n\n");
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSConversationHelper::setIntroString("Which virtue?");
|
||||
#endif
|
||||
handleWOP(gameGetInput());
|
||||
}
|
||||
|
||||
// 3 tries are up... eject!
|
||||
else {
|
||||
tries = 1;
|
||||
eject(CODEX_EJECT_BAD_WOP);
|
||||
}
|
||||
}
|
||||
|
||||
void Codex::handleVirtues(const Common::String &virtue) {
|
||||
static const char *const codexImageNames[] = {
|
||||
BKGD_HONESTY, BKGD_COMPASSN, BKGD_VALOR, BKGD_JUSTICE,
|
||||
BKGD_SACRIFIC, BKGD_HONOR, BKGD_SPIRIT, BKGD_HUMILITY,
|
||||
BKGD_TRUTH, BKGD_LOVE, BKGD_COURAGE
|
||||
};
|
||||
|
||||
static int current = 0;
|
||||
static int tries = 1;
|
||||
|
||||
eventHandler->popKeyHandler();
|
||||
|
||||
// slight pause before continuing
|
||||
g_screen->screenMessage("\n");
|
||||
g_screen->screenDisableCursor();
|
||||
EventHandler::sleep(1000);
|
||||
|
||||
// answered with the correct one of eight virtues
|
||||
if ((current < VIRT_MAX) &&
|
||||
(scumm_stricmp(virtue.c_str(), getVirtueName(static_cast<Virtue>(current))) == 0)) {
|
||||
|
||||
g_screen->screenDrawImageInMapArea(codexImageNames[current]);
|
||||
g_screen->screenRedrawMapArea();
|
||||
|
||||
current++;
|
||||
tries = 1;
|
||||
|
||||
EventHandler::sleep(2000);
|
||||
|
||||
if (current == VIRT_MAX) {
|
||||
g_screen->screenMessage("\nThou art well versed in the virtues of the Avatar.\n");
|
||||
EventHandler::sleep(5000);
|
||||
}
|
||||
|
||||
g_screen->screenMessage("\n\nThe voice asks:\n");
|
||||
EventHandler::sleep(2000);
|
||||
g_screen->screenMessage("\n%s\n\n", _virtueQuestions[current].c_str());
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSConversationHelper::setIntroString((current != VIRT_MAX) ? "Which virtue?" : "Which principle?");
|
||||
#endif
|
||||
handleVirtues(gameGetInput());
|
||||
}
|
||||
|
||||
// answered with the correct base virtue (truth, love, courage)
|
||||
else if ((current >= VIRT_MAX) &&
|
||||
(scumm_stricmp(virtue.c_str(), getBaseVirtueName(static_cast<BaseVirtue>(1 << (current - VIRT_MAX)))) == 0)) {
|
||||
|
||||
g_screen->screenDrawImageInMapArea(codexImageNames[current]);
|
||||
g_screen->screenRedrawMapArea();
|
||||
|
||||
current++;
|
||||
tries = 1;
|
||||
|
||||
if (current < VIRT_MAX + 3) {
|
||||
g_screen->screenMessage("\n\nThe voice asks:\n");
|
||||
EventHandler::sleep(2000);
|
||||
g_screen->screenMessage("\n%s\n\n", _virtueQuestions[current].c_str());
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSConversationHelper::setIntroString("Which principle?");
|
||||
#endif
|
||||
handleVirtues(gameGetInput());
|
||||
} else {
|
||||
g_screen->screenMessage("\nThe ground rumbles beneath your feet.\n");
|
||||
EventHandler::sleep(1000);
|
||||
g_screen->screenShake(10);
|
||||
|
||||
EventHandler::sleep(3000);
|
||||
g_screen->screenEnableCursor();
|
||||
g_screen->screenMessage("\nAbove the din, the voice asks:\n\nIf all eight virtues of the Avatar combine into and are derived from the Three Principles of Truth, Love and Courage...");
|
||||
#ifdef IOS_ULTIMA4
|
||||
// Ugh, we now enter happy callback land, so I know how to do these things manually. Good thing I kept these separate functions.
|
||||
U4IOS::beginChoiceConversation();
|
||||
U4IOS::updateChoicesInDialog(" ", "", -1);
|
||||
#endif
|
||||
eventHandler->pushKeyHandler(&handleInfinityAnyKey);
|
||||
}
|
||||
}
|
||||
|
||||
// give them 3 tries to enter the correct virtue, then eject them!
|
||||
else if (tries++ < 3) {
|
||||
impureThoughts();
|
||||
g_screen->screenMessage("%s\n\n", _virtueQuestions[current].c_str());
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSConversationHelper::setIntroString("Which virtue?");
|
||||
#endif
|
||||
handleVirtues(gameGetInput());
|
||||
}
|
||||
|
||||
// failed 3 times... eject!
|
||||
else {
|
||||
eject(static_cast<CodexEjectCode>(CODEX_EJECT_HONESTY + current));
|
||||
|
||||
tries = 1;
|
||||
current = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool Codex::handleInfinityAnyKey(int key, void *data) {
|
||||
eventHandler->popKeyHandler();
|
||||
|
||||
g_screen->screenMessage("\n\nThen what is the one thing which encompasses and is the whole of all undeniable Truth, unending Love, and unyielding Courage?\n\n");
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::endChoiceConversation();
|
||||
U4IOS::IOSConversationHelper::setIntroString("What is the whole of all undeniable Truth, unending Love, and unyielding Courage?");
|
||||
#endif
|
||||
g_codex->handleInfinity(gameGetInput());
|
||||
return true;
|
||||
}
|
||||
|
||||
void Codex::handleInfinity(const Common::String &answer) {
|
||||
static int tries = 1;
|
||||
|
||||
eventHandler->popKeyHandler();
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSHideGameControllerHelper hideControllsHelper;
|
||||
#endif
|
||||
// slight pause before continuing
|
||||
g_screen->screenMessage("\n");
|
||||
g_screen->screenDisableCursor();
|
||||
EventHandler::sleep(1000);
|
||||
|
||||
if (scumm_stricmp(answer.c_str(), "infinity") == 0) {
|
||||
EventHandler::sleep(2000);
|
||||
g_screen->screenShake(10);
|
||||
|
||||
g_screen->screenEnableCursor();
|
||||
g_screen->screenMessage("\n%s", _endgameText1[0].c_str());
|
||||
#ifdef IOS_ULTIMA4
|
||||
// Ugh, we now enter happy callback land, so I know how to do these things manually. Good thing I kept these separate functions.
|
||||
U4IOS::hideGameButtons();
|
||||
U4IOS::beginChoiceConversation();
|
||||
U4IOS::updateChoicesInDialog(" ", "", -1);
|
||||
U4IOS::testFlightPassCheckPoint("Game won!");
|
||||
#endif
|
||||
eventHandler->pushKeyHandler(&handleEndgameAnyKey);
|
||||
} else if (tries++ < 3) {
|
||||
impureThoughts();
|
||||
g_screen->screenMessage("\nAbove the din, the voice asks:\n\nIf all eight virtues of the Avatar combine into and are derived from the Three Principles of Truth, Love and Courage...");
|
||||
eventHandler->pushKeyHandler(&handleInfinityAnyKey);
|
||||
} else eject(CODEX_EJECT_BAD_INFINITY);
|
||||
}
|
||||
|
||||
bool Codex::handleEndgameAnyKey(int key, void *data) {
|
||||
static int index = 1;
|
||||
|
||||
eventHandler->popKeyHandler();
|
||||
|
||||
if (index < 10) {
|
||||
|
||||
if (index < 7) {
|
||||
if (index == 6) {
|
||||
g_screen->screenEraseMapArea();
|
||||
g_screen->screenRedrawMapArea();
|
||||
}
|
||||
g_screen->screenMessage("%s", g_codex->_endgameText1[index].c_str());
|
||||
} else if (index == 7) {
|
||||
g_screen->screenDrawImageInMapArea(BKGD_STONCRCL);
|
||||
g_screen->screenRedrawMapArea();
|
||||
g_screen->screenMessage("\n\n%s", g_codex->_endgameText2[index - 7].c_str());
|
||||
} else if (index > 7)
|
||||
g_screen->screenMessage("%s", g_codex->_endgameText2[index - 7].c_str());
|
||||
|
||||
index++;
|
||||
eventHandler->pushKeyHandler(&g_codex->handleEndgameAnyKey);
|
||||
} else {
|
||||
// CONGRATULATIONS!... you have completed the game in x turns
|
||||
g_screen->screenDisableCursor();
|
||||
g_screen->screenMessage("%s%d%s", g_codex->_endgameText2[index - 7].c_str(),
|
||||
g_ultima->_saveGame->_moves, g_codex->_endgameText2[index - 6].c_str());
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::endChoiceConversation();
|
||||
#endif
|
||||
eventHandler->pushKeyHandler(&KeyHandler::ignoreKeys);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Codex::impureThoughts() {
|
||||
g_screen->screenMessage("\nThy thoughts are not pure.\nI ask again.\n");
|
||||
EventHandler::sleep(2000);
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
115
engines/ultima/ultima4/game/codex.h
Normal file
115
engines/ultima/ultima4/game/codex.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/* 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_GAME_CODEX_H
|
||||
#define ULTIMA4_GAME_CODEX_H
|
||||
|
||||
#include "common/array.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
enum CodexEjectCode {
|
||||
CODEX_EJECT_NO_3_PART_KEY,
|
||||
CODEX_EJECT_BAD_WOP,
|
||||
CODEX_EJECT_NO_FULL_PARTY,
|
||||
CODEX_EJECT_NO_FULL_AVATAR,
|
||||
CODEX_EJECT_HONESTY,
|
||||
CODEX_EJECT_COMPASSION,
|
||||
CODEX_EJECT_VALOR,
|
||||
CODEX_EJECT_JUSTICE,
|
||||
CODEX_EJECT_SACRIFICE,
|
||||
CODEX_EJECT_HONOR,
|
||||
CODEX_EJECT_SPIRITUALITY,
|
||||
CODEX_EJECT_HUMILITY,
|
||||
CODEX_EJECT_TRUTH,
|
||||
CODEX_EJECT_LOVE,
|
||||
CODEX_EJECT_COURAGE,
|
||||
CODEX_EJECT_BAD_INFINITY
|
||||
};
|
||||
|
||||
class Codex {
|
||||
private:
|
||||
Common::Array<Common::String> _virtueQuestions;
|
||||
Common::Array<Common::String> _endgameText1;
|
||||
Common::Array<Common::String> _endgameText2;
|
||||
private:
|
||||
/**
|
||||
* Initializes the Chamber of the Codex sequence (runs from codexStart())
|
||||
*/
|
||||
int init();
|
||||
|
||||
/**
|
||||
* Frees all memory associated with the Codex sequence
|
||||
*/
|
||||
void deinit();
|
||||
|
||||
/**
|
||||
* Ejects you from the chamber of the codex (and the Abyss, for that matter)
|
||||
* with the correct message.
|
||||
*/
|
||||
void eject(CodexEjectCode code);
|
||||
|
||||
/**
|
||||
* Handles entering the Word of Passage
|
||||
*/
|
||||
void handleWOP(const Common::String &word);
|
||||
|
||||
/**
|
||||
* Handles naming of virtues in the Chamber of the Codex
|
||||
*/
|
||||
void handleVirtues(const Common::String &virtue);
|
||||
|
||||
void handleInfinity(const Common::String &answer);
|
||||
|
||||
/**
|
||||
* Pretty self-explanatory
|
||||
*/
|
||||
void impureThoughts();
|
||||
|
||||
/**
|
||||
* Key handlers
|
||||
*/
|
||||
static bool handleInfinityAnyKey(int key, void *data);
|
||||
static bool handleEndgameAnyKey(int key, void *data);
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Codex();
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Codex();
|
||||
|
||||
/**
|
||||
* Begins the Chamber of the Codex sequence
|
||||
*/
|
||||
void start();
|
||||
};
|
||||
|
||||
extern Codex *g_codex;
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
70
engines/ultima/ultima4/game/context.cpp
Normal file
70
engines/ultima/ultima4/game/context.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/game/context.h"
|
||||
#include "ultima/ultima4/game/player.h"
|
||||
#include "ultima/ultima4/views/stats.h"
|
||||
#include "ultima/ultima4/map/location.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
Context *g_context;
|
||||
|
||||
Context::Context() : _stats(nullptr), _aura(nullptr),
|
||||
_party(nullptr), _location(nullptr) {
|
||||
g_context = this;
|
||||
reset();
|
||||
}
|
||||
|
||||
Context::~Context() {
|
||||
g_context = nullptr;
|
||||
reset();
|
||||
}
|
||||
|
||||
void Context::reset() {
|
||||
delete _stats;
|
||||
delete _aura;
|
||||
delete _party;
|
||||
|
||||
while (_location)
|
||||
locationFree(&_location);
|
||||
|
||||
_stats = nullptr;
|
||||
_aura = nullptr;
|
||||
_party = nullptr;
|
||||
_location = nullptr;
|
||||
|
||||
_lastShip = nullptr;
|
||||
_line = 9999;
|
||||
_col = 0;
|
||||
_moonPhase = 0;
|
||||
_windDirection = 0;
|
||||
_windCounter = 0;
|
||||
_windLock = false;
|
||||
_horseSpeed = 0;
|
||||
_opacity = 0;
|
||||
_lastCommandTime = 0;
|
||||
_transportContext = TRANSPORT_ANY;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
97
engines/ultima/ultima4/game/context.h
Normal file
97
engines/ultima/ultima4/game/context.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_GAME_CONTEXT_H
|
||||
#define ULTIMA4_GAME_CONTEXT_H
|
||||
|
||||
#include "ultima/ultima4/map/location.h"
|
||||
#include "ultima/ultima4/game/aura.h"
|
||||
#include "ultima/ultima4/game/names.h"
|
||||
#include "ultima/ultima4/game/person.h"
|
||||
#include "ultima/ultima4/game/script.h"
|
||||
#include "ultima/ultima4/core/types.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class Object;
|
||||
class Party;
|
||||
class Person;
|
||||
class Script;
|
||||
class StatsArea;
|
||||
|
||||
enum TransportContext {
|
||||
TRANSPORT_FOOT = 0x1,
|
||||
TRANSPORT_HORSE = 0x2,
|
||||
TRANSPORT_SHIP = 0x4,
|
||||
TRANSPORT_BALLOON = 0x8,
|
||||
TRANSPORT_FOOT_OR_HORSE = TRANSPORT_FOOT | TRANSPORT_HORSE,
|
||||
TRANSPORT_ANY = 0xffff
|
||||
};
|
||||
|
||||
/**
|
||||
* Context class
|
||||
*/
|
||||
class Context : public Script::Provider {
|
||||
public:
|
||||
Context();
|
||||
~Context();
|
||||
|
||||
/**
|
||||
* Reset the context
|
||||
*/
|
||||
void reset();
|
||||
|
||||
StatsArea *_stats;
|
||||
Aura *_aura;
|
||||
Party *_party;
|
||||
Location *_location;
|
||||
int _line, _col;
|
||||
int _moonPhase;
|
||||
int _windDirection;
|
||||
int _windCounter;
|
||||
bool _windLock;
|
||||
int _horseSpeed;
|
||||
int _opacity;
|
||||
TransportContext _transportContext;
|
||||
uint32 _lastCommandTime;
|
||||
Object *_lastShip;
|
||||
public:
|
||||
/**
|
||||
* Provides scripts with information
|
||||
*/
|
||||
Common::String translate(Std::vector<Common::String> &parts) override {
|
||||
if (parts.size() == 1) {
|
||||
if (parts[0] == "wind")
|
||||
return getDirectionName(static_cast<Direction>(_windDirection));
|
||||
}
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
extern Context *g_context;
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
1013
engines/ultima/ultima4/game/creature.cpp
Normal file
1013
engines/ultima/ultima4/game/creature.cpp
Normal file
File diff suppressed because it is too large
Load Diff
449
engines/ultima/ultima4/game/creature.h
Normal file
449
engines/ultima/ultima4/game/creature.h
Normal file
@@ -0,0 +1,449 @@
|
||||
/* 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_GAME_CREATURE_H
|
||||
#define ULTIMA4_GAME_CREATURE_H
|
||||
|
||||
#include "ultima/ultima4/game/object.h"
|
||||
#include "ultima/ultima4/map/movement.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/core/types.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class CombatController;
|
||||
class ConfigElement;
|
||||
class Tile;
|
||||
|
||||
typedef unsigned short CreatureId;
|
||||
typedef Common::HashMap<CreatureId, class Creature *> CreatureMap;
|
||||
typedef Std::vector<class Creature *> CreatureVector;
|
||||
|
||||
#define MAX_CREATURES 128
|
||||
|
||||
/* Creatures on world map */
|
||||
|
||||
#define MAX_CREATURES_ON_MAP 4
|
||||
#define MAX_CREATURE_DISTANCE 16
|
||||
|
||||
/* Creature ids */
|
||||
|
||||
typedef enum {
|
||||
HORSE1_ID = 0,
|
||||
HORSE2_ID = 1,
|
||||
|
||||
MAGE_ID = 2,
|
||||
BARD_ID = 3,
|
||||
FIGHTER_ID = 4,
|
||||
DRUID_ID = 5,
|
||||
TINKER_ID = 6,
|
||||
PALADIN_ID = 7,
|
||||
RANGER_ID = 8,
|
||||
SHEPHERD_ID = 9,
|
||||
|
||||
GUARD_ID = 10,
|
||||
VILLAGER_ID = 11,
|
||||
SINGINGBARD_ID = 12,
|
||||
JESTER_ID = 13,
|
||||
BEGGAR_ID = 14,
|
||||
CHILD_ID = 15,
|
||||
BULL_ID = 16,
|
||||
LORDBRITISH_ID = 17,
|
||||
|
||||
PIRATE_ID = 18,
|
||||
NIXIE_ID = 19,
|
||||
GIANT_SQUID_ID = 20,
|
||||
SEA_SERPENT_ID = 21,
|
||||
SEAHORSE_ID = 22,
|
||||
WHIRLPOOL_ID = 23,
|
||||
STORM_ID = 24,
|
||||
RAT_ID = 25,
|
||||
BAT_ID = 26,
|
||||
GIANT_SPIDER_ID = 27,
|
||||
GHOST_ID = 28,
|
||||
SLIME_ID = 29,
|
||||
TROLL_ID = 30,
|
||||
GREMLIN_ID = 31,
|
||||
MIMIC_ID = 32,
|
||||
REAPER_ID = 33,
|
||||
INSECT_SWARM_ID = 34,
|
||||
GAZER_ID = 35,
|
||||
PHANTOM_ID = 36,
|
||||
ORC_ID = 37,
|
||||
SKELETON_ID = 38,
|
||||
ROGUE_ID = 39,
|
||||
PYTHON_ID = 40,
|
||||
ETTIN_ID = 41,
|
||||
HEADLESS_ID = 42,
|
||||
CYCLOPS_ID = 43,
|
||||
WISP_ID = 44,
|
||||
EVILMAGE_ID = 45,
|
||||
LICH_ID = 46,
|
||||
LAVA_LIZARD_ID = 47,
|
||||
ZORN_ID = 48,
|
||||
DAEMON_ID = 49,
|
||||
HYDRA_ID = 50,
|
||||
DRAGON_ID = 51,
|
||||
BALRON_ID = 52
|
||||
} CreatureType;
|
||||
|
||||
typedef enum {
|
||||
MATTR_STEALFOOD = 0x1,
|
||||
MATTR_STEALGOLD = 0x2,
|
||||
MATTR_CASTS_SLEEP = 0x4,
|
||||
MATTR_UNDEAD = 0x8,
|
||||
MATTR_GOOD = 0x10,
|
||||
MATTR_WATER = 0x20,
|
||||
MATTR_NONATTACKABLE = 0x40,
|
||||
MATTR_NEGATE = 0x80,
|
||||
MATTR_CAMOUFLAGE = 0x100,
|
||||
MATTR_NOATTACK = 0x200,
|
||||
MATTR_AMBUSHES = 0x400,
|
||||
MATTR_RANDOMRANGED = 0x800,
|
||||
MATTR_INCORPOREAL = 0x1000,
|
||||
MATTR_NOCHEST = 0x2000,
|
||||
MATTR_DIVIDES = 0x4000,
|
||||
MATTR_SPAWNSONDEATH = 0x8000,
|
||||
MATTR_FORCE_OF_NATURE = 0x10000
|
||||
} CreatureAttrib;
|
||||
|
||||
typedef enum {
|
||||
MATTR_STATIONARY = 0x1,
|
||||
MATTR_WANDERS = 0x2,
|
||||
MATTR_SWIMS = 0x4,
|
||||
MATTR_SAILS = 0x8,
|
||||
MATTR_FLIES = 0x10,
|
||||
MATTR_TELEPORT = 0x20,
|
||||
MATTR_CANMOVECREATURES = 0x40,
|
||||
MATTR_CANMOVEAVATAR = 0x80
|
||||
} CreatureMovementAttrib;
|
||||
|
||||
typedef enum {
|
||||
MSTAT_DEAD,
|
||||
MSTAT_FLEEING,
|
||||
MSTAT_CRITICAL,
|
||||
MSTAT_HEAVILYWOUNDED,
|
||||
MSTAT_LIGHTLYWOUNDED,
|
||||
MSTAT_BARELYWOUNDED
|
||||
} CreatureStatus;
|
||||
|
||||
/**
|
||||
* Creature Class Definition
|
||||
* @todo
|
||||
* <ul>
|
||||
* <li>split into a CreatureType (all the settings for a
|
||||
* particular creature e.g. orc) and Creature (a specific
|
||||
* creature instance)</li>
|
||||
* <li>creatures can be looked up by name, ids can probably go away</li>
|
||||
* </ul>
|
||||
*/
|
||||
class Creature : public Object {
|
||||
typedef Common::List<StatusType> StatusList;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creature class implementation
|
||||
*/
|
||||
Creature(MapTile tile = MapTile(0));
|
||||
|
||||
void load(const ConfigElement &conf);
|
||||
|
||||
// Accessor methods
|
||||
virtual Common::String getName() const {
|
||||
return _name;
|
||||
}
|
||||
virtual const Common::String &getHitTile() const {
|
||||
return _rangedHitTile;
|
||||
}
|
||||
virtual const Common::String &getMissTile() const {
|
||||
return _rangedMissTile;
|
||||
}
|
||||
CreatureId getId() const {
|
||||
return _id;
|
||||
}
|
||||
CreatureId getLeader() const {
|
||||
return _leader;
|
||||
}
|
||||
virtual int getHp() const {
|
||||
return _hp;
|
||||
}
|
||||
virtual int getXp() const {
|
||||
return _xp;
|
||||
}
|
||||
virtual const Common::String &getWorldrangedtile() const {
|
||||
return _worldRangedTile;
|
||||
}
|
||||
SlowedType getSlowedType() const {
|
||||
return _slowedType;
|
||||
}
|
||||
int getEncounterSize() const {
|
||||
return _encounterSize;
|
||||
}
|
||||
byte getResists() const {
|
||||
return _resists;
|
||||
}
|
||||
|
||||
// Setters
|
||||
void setName(Common::String s) {
|
||||
_name = s;
|
||||
}
|
||||
void setHitTile(const Common::String &t) {
|
||||
_rangedHitTile = t;
|
||||
}
|
||||
void setMissTile(const Common::String &t) {
|
||||
_rangedMissTile = t;
|
||||
}
|
||||
virtual void setHp(int points) {
|
||||
_hp = points;
|
||||
}
|
||||
|
||||
// Query methods
|
||||
bool isGood() const {
|
||||
return _mAttr & MATTR_GOOD;
|
||||
}
|
||||
bool isEvil() const {
|
||||
return !isGood();
|
||||
}
|
||||
bool isUndead() const {
|
||||
return _mAttr & MATTR_UNDEAD;
|
||||
}
|
||||
bool leavesChest() const {
|
||||
return !isAquatic() && !(_mAttr & MATTR_NOCHEST);
|
||||
}
|
||||
bool isAquatic() const {
|
||||
return _mAttr & MATTR_WATER;
|
||||
}
|
||||
bool wanders() const {
|
||||
return _movementAttr & MATTR_WANDERS;
|
||||
}
|
||||
bool isStationary() const {
|
||||
return _movementAttr & MATTR_STATIONARY;
|
||||
}
|
||||
bool flies() const {
|
||||
return _movementAttr & MATTR_FLIES;
|
||||
}
|
||||
bool teleports() const {
|
||||
return _movementAttr & MATTR_TELEPORT;
|
||||
}
|
||||
bool swims() const {
|
||||
return _movementAttr & MATTR_SWIMS;
|
||||
}
|
||||
bool sails() const {
|
||||
return _movementAttr & MATTR_SAILS;
|
||||
}
|
||||
bool walks() const {
|
||||
return !(flies() || swims() || sails());
|
||||
}
|
||||
bool divides() const {
|
||||
return _mAttr & MATTR_DIVIDES;
|
||||
}
|
||||
bool spawnsOnDeath() const {
|
||||
return _mAttr & MATTR_SPAWNSONDEATH;
|
||||
}
|
||||
bool canMoveOntoCreatures() const {
|
||||
return _movementAttr & MATTR_CANMOVECREATURES;
|
||||
}
|
||||
bool canMoveOntoPlayer() const {
|
||||
return _movementAttr & MATTR_CANMOVEAVATAR;
|
||||
}
|
||||
bool isAttackable() const;
|
||||
bool willAttack() const {
|
||||
return !(_mAttr & MATTR_NOATTACK);
|
||||
}
|
||||
bool stealsGold() const {
|
||||
return _mAttr & MATTR_STEALGOLD;
|
||||
}
|
||||
bool stealsFood() const {
|
||||
return _mAttr & MATTR_STEALFOOD;
|
||||
}
|
||||
bool negates() const {
|
||||
return _mAttr & MATTR_NEGATE;
|
||||
}
|
||||
bool camouflages() const {
|
||||
return _mAttr & MATTR_CAMOUFLAGE;
|
||||
}
|
||||
bool ambushes() const {
|
||||
return _mAttr & MATTR_AMBUSHES;
|
||||
}
|
||||
bool isIncorporeal() const {
|
||||
return _mAttr & MATTR_INCORPOREAL;
|
||||
}
|
||||
bool hasRandomRanged() const {
|
||||
return _mAttr & MATTR_RANDOMRANGED;
|
||||
}
|
||||
bool leavesTile() const {
|
||||
return _leavesTile;
|
||||
}
|
||||
bool castsSleep() const {
|
||||
return _mAttr & MATTR_CASTS_SLEEP;
|
||||
}
|
||||
bool isForceOfNature() const {
|
||||
return _mAttr & MATTR_FORCE_OF_NATURE;
|
||||
}
|
||||
int getDamage() const;
|
||||
const Common::String &getCamouflageTile() const {
|
||||
return _camouflageTile;
|
||||
}
|
||||
void setRandomRanged();
|
||||
int setInitialHp(int hp = -1);
|
||||
|
||||
/**
|
||||
* Performs a special action for the creature
|
||||
* Returns true if the action takes up the creatures
|
||||
* whole turn (i.e. it can't move afterwards)
|
||||
*/
|
||||
bool specialAction();
|
||||
|
||||
/**
|
||||
* Performs a special effect for the creature
|
||||
* Returns true if something special happened,
|
||||
* or false if nothing happened
|
||||
*/
|
||||
bool specialEffect();
|
||||
|
||||
/* combat methods */
|
||||
void act(CombatController *controller);
|
||||
|
||||
/**
|
||||
* Add status effects to the creature, in order of importance
|
||||
*/
|
||||
virtual void addStatus(StatusType status);
|
||||
void applyTileEffect(TileEffect effect);
|
||||
virtual int getAttackBonus() const;
|
||||
virtual int getDefense() const;
|
||||
bool divide();
|
||||
bool spawnOnDeath();
|
||||
virtual CreatureStatus getState() const;
|
||||
StatusType getStatus() const;
|
||||
bool isAsleep() const;
|
||||
|
||||
/**
|
||||
* Hides or shows a camouflaged creature, depending on its distance from
|
||||
* the nearest opponent
|
||||
*/
|
||||
bool hideOrShow();
|
||||
|
||||
Creature *nearestOpponent(int *dist, bool ranged);
|
||||
virtual void putToSleep();
|
||||
virtual void removeStatus(StatusType status);
|
||||
virtual void setStatus(StatusType status);
|
||||
virtual void wakeUp();
|
||||
|
||||
/**
|
||||
* Applies damage to the creature.
|
||||
* Returns true if the creature still exists after the damage has been applied
|
||||
* or false, if the creature was destroyed
|
||||
*
|
||||
* If byplayer is false (when a monster is killed by walking through
|
||||
* fire or poison, or as a result of jinx) we don't report experience
|
||||
* on death
|
||||
*/
|
||||
virtual bool applyDamage(int damage, bool byplayer = true);
|
||||
virtual bool dealDamage(Creature *m, int damage);
|
||||
|
||||
// Properties
|
||||
protected:
|
||||
Common::String _name;
|
||||
Common::String _rangedHitTile;
|
||||
Common::String _rangedMissTile;
|
||||
CreatureId _id;
|
||||
Common::String _camouflageTile;
|
||||
CreatureId _leader;
|
||||
int _baseHp;
|
||||
int _hp;
|
||||
StatusList _status;
|
||||
int _xp;
|
||||
byte _ranged;
|
||||
Common::String _worldRangedTile;
|
||||
bool _leavesTile;
|
||||
CreatureAttrib _mAttr;
|
||||
CreatureMovementAttrib _movementAttr;
|
||||
SlowedType _slowedType;
|
||||
int _encounterSize;
|
||||
byte _resists;
|
||||
CreatureId _spawn;
|
||||
};
|
||||
|
||||
/**
|
||||
* CreatureMgr Class Definition
|
||||
*/
|
||||
class CreatureMgr {
|
||||
public:
|
||||
static CreatureMgr *getInstance();
|
||||
|
||||
void loadAll();
|
||||
|
||||
/**
|
||||
* Returns a creature using a tile to find which one to create
|
||||
* or nullptr if a creature with that tile cannot be found
|
||||
*/
|
||||
Creature *getByTile(MapTile tile);
|
||||
|
||||
/**
|
||||
* Returns the creature that has the corresponding id
|
||||
* or returns nullptr if no creature with that id could
|
||||
* be found.
|
||||
*/
|
||||
Creature *getById(CreatureId id);
|
||||
|
||||
/**
|
||||
* Returns the creature that has the corresponding name
|
||||
* or returns nullptr if no creature can be found with
|
||||
* that name (case insensitive)
|
||||
*/
|
||||
Creature *getByName(Common::String name);
|
||||
|
||||
/**
|
||||
* Creates a random creature based on the tile given
|
||||
*/
|
||||
Creature *randomForTile(const Tile *tile);
|
||||
|
||||
/**
|
||||
* Creates a random creature based on the dungeon level given
|
||||
*/
|
||||
Creature *randomForDungeon(int dnglevel);
|
||||
|
||||
/**
|
||||
* Creates a random ambushing creature
|
||||
*/
|
||||
Creature *randomAmbushing();
|
||||
|
||||
private:
|
||||
CreatureMgr() {}
|
||||
|
||||
// disallow assignments, copy construction
|
||||
CreatureMgr(const CreatureMgr &);
|
||||
const CreatureMgr &operator=(const CreatureMgr &);
|
||||
|
||||
static CreatureMgr *_instance;
|
||||
|
||||
CreatureMap _creatures;
|
||||
};
|
||||
|
||||
bool isCreature(Object *punknown);
|
||||
|
||||
#define creatureMgr (CreatureMgr::getInstance())
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
148
engines/ultima/ultima4/game/death.cpp
Normal file
148
engines/ultima/ultima4/game/death.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/game/death.h"
|
||||
#include "ultima/ultima4/game/context.h"
|
||||
#include "ultima/ultima4/game/game.h"
|
||||
#include "ultima/ultima4/game/player.h"
|
||||
#include "ultima/ultima4/game/portal.h"
|
||||
#include "ultima/ultima4/views/stats.h"
|
||||
#include "ultima/ultima4/controllers/wait_controller.h"
|
||||
#include "ultima/ultima4/core/settings.h"
|
||||
#include "ultima/ultima4/events/event_handler.h"
|
||||
#include "ultima/ultima4/gfx/screen.h"
|
||||
#include "ultima/ultima4/map/map.h"
|
||||
#include "ultima/ultima4/map/annotation.h"
|
||||
#include "ultima/ultima4/map/city.h"
|
||||
#include "ultima/ultima4/map/location.h"
|
||||
#include "ultima/ultima4/map/mapmgr.h"
|
||||
#include "ultima/ultima4/sound/music.h"
|
||||
#include "ultima/ultima4/ultima4.h"
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
Death *g_death;
|
||||
|
||||
#define REVIVE_WORLD_X 86
|
||||
#define REVIVE_WORLD_Y 107
|
||||
#define REVIVE_CASTLE_X 19
|
||||
#define REVIVE_CASTLE_Y 8
|
||||
|
||||
const struct {
|
||||
int _timeout; ///< pause in seconds
|
||||
const char *_text; ///< text of message
|
||||
} DEATH_MSGS[] = {
|
||||
{ 5, "\n\n\nAll is Dark...\n" },
|
||||
{ 5, "\nBut wait...\n" },
|
||||
{ 5, "Where am I?...\n" },
|
||||
{ 5, "Am I dead?...\n" },
|
||||
{ 5, "Afterlife?...\n" },
|
||||
{ 5, "You hear:\n %s\n" },
|
||||
{ 5, "I feel motion...\n" },
|
||||
{ 5, "\nLord British says: I have pulled thy spirit and some possessions from the void. Be more careful in the future!\n\n\020" }
|
||||
};
|
||||
|
||||
#define N_MSGS (sizeof(DEATH_MSGS) / sizeof(DEATH_MSGS[0]))
|
||||
|
||||
Death::Death() : timerCount(0), timerMsg(0), deathSequenceRunning(false) {
|
||||
g_death = this;
|
||||
}
|
||||
|
||||
Death::~Death() {
|
||||
g_death = nullptr;
|
||||
}
|
||||
|
||||
void Death::start(int delay) {
|
||||
if (deathSequenceRunning)
|
||||
return;
|
||||
|
||||
// stop playing music
|
||||
g_music->fadeOut(1000);
|
||||
|
||||
deathSequenceRunning = 1;
|
||||
timerCount = 0;
|
||||
timerMsg = 0;
|
||||
|
||||
WaitController waitCtrl(delay * settings._gameCyclesPerSecond);
|
||||
eventHandler->pushController(&waitCtrl);
|
||||
waitCtrl.wait();
|
||||
|
||||
gameSetViewMode(VIEW_DEAD);
|
||||
|
||||
eventHandler->pushKeyHandler(&KeyHandler::ignoreKeys);
|
||||
g_screen->screenDisableCursor();
|
||||
|
||||
eventHandler->getTimer()->add(&deathTimer, settings._gameCyclesPerSecond);
|
||||
}
|
||||
|
||||
void Death::deathTimer(void *data) {
|
||||
g_death->timerCount++;
|
||||
if ((g_death->timerMsg < N_MSGS) && (g_death->timerCount > DEATH_MSGS[g_death->timerMsg]._timeout)) {
|
||||
|
||||
g_screen->screenMessage(DEATH_MSGS[g_death->timerMsg]._text, g_context->_party->member(0)->getName().c_str());
|
||||
g_screen->screenHideCursor();
|
||||
|
||||
g_death->timerCount = 0;
|
||||
g_death->timerMsg++;
|
||||
|
||||
if (g_death->timerMsg >= N_MSGS) {
|
||||
eventHandler->getTimer()->remove(&deathTimer);
|
||||
g_death->revive();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Death::revive() {
|
||||
while (!g_context->_location->_map->isWorldMap() && g_context->_location->_prev != nullptr) {
|
||||
g_game->exitToParentMap();
|
||||
}
|
||||
|
||||
eventHandler->setController(g_game);
|
||||
|
||||
deathSequenceRunning = false;
|
||||
gameSetViewMode(VIEW_NORMAL);
|
||||
|
||||
// Move our world map location to Lord British's Castle
|
||||
g_context->_location->_coords = g_context->_location->_map->_portals[0]->_coords;
|
||||
|
||||
// Now, move the avatar into the castle and put him in front of Lord British
|
||||
g_game->setMap(mapMgr->get(100), 1, nullptr);
|
||||
g_context->_location->_coords.x = REVIVE_CASTLE_X;
|
||||
g_context->_location->_coords.y = REVIVE_CASTLE_Y;
|
||||
g_context->_location->_coords.z = 0;
|
||||
|
||||
g_context->_aura->set();
|
||||
g_context->_horseSpeed = 0;
|
||||
g_context->_lastCommandTime = g_system->getMillis();
|
||||
g_music->playMapMusic();
|
||||
|
||||
g_context->_party->reviveParty();
|
||||
|
||||
g_screen->screenEnableCursor();
|
||||
g_screen->screenShowCursor();
|
||||
g_context->_stats->setView(STATS_PARTY_OVERVIEW);
|
||||
g_screen->update();
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
61
engines/ultima/ultima4/game/death.h
Normal file
61
engines/ultima/ultima4/game/death.h
Normal 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 ULTIMA4_GAME_DEATH_H
|
||||
#define ULTIMA4_GAME_DEATH_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class Death {
|
||||
private:
|
||||
int timerCount;
|
||||
uint timerMsg;
|
||||
bool deathSequenceRunning;
|
||||
private:
|
||||
/**
|
||||
* Timer
|
||||
*/
|
||||
static void deathTimer(void *data);
|
||||
|
||||
void revive();
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Death();
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Death();
|
||||
|
||||
void start(int delay);
|
||||
};
|
||||
|
||||
extern Death *g_death;
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
831
engines/ultima/ultima4/game/game.cpp
Normal file
831
engines/ultima/ultima4/game/game.cpp
Normal file
@@ -0,0 +1,831 @@
|
||||
/* 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/ultima4.h"
|
||||
#include "ultima/ultima4/controllers/camp_controller.h"
|
||||
#include "ultima/ultima4/controllers/combat_controller.h"
|
||||
#include "ultima/ultima4/controllers/intro_controller.h"
|
||||
#include "ultima/ultima4/controllers/read_choice_controller.h"
|
||||
#include "ultima/ultima4/controllers/read_dir_controller.h"
|
||||
#include "ultima/ultima4/controllers/read_int_controller.h"
|
||||
#include "ultima/ultima4/controllers/read_player_controller.h"
|
||||
#include "ultima/ultima4/controllers/read_string_controller.h"
|
||||
#include "ultima/ultima4/conversation/conversation.h"
|
||||
#include "ultima/ultima4/core/config.h"
|
||||
#include "ultima/ultima4/core/debugger.h"
|
||||
#include "ultima/ultima4/core/settings.h"
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
#include "ultima/ultima4/events/event_handler.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/game/game.h"
|
||||
#include "ultima/ultima4/game/armor.h"
|
||||
#include "ultima/ultima4/game/context.h"
|
||||
#include "ultima/ultima4/game/death.h"
|
||||
#include "ultima/ultima4/game/item.h"
|
||||
#include "ultima/ultima4/views/menu.h"
|
||||
#include "ultima/ultima4/game/creature.h"
|
||||
#include "ultima/ultima4/game/moongate.h"
|
||||
#include "ultima/ultima4/game/names.h"
|
||||
#include "ultima/ultima4/game/person.h"
|
||||
#include "ultima/ultima4/game/player.h"
|
||||
#include "ultima/ultima4/game/portal.h"
|
||||
#include "ultima/ultima4/game/spell.h"
|
||||
#include "ultima/ultima4/views/stats.h"
|
||||
#include "ultima/ultima4/game/script.h"
|
||||
#include "ultima/ultima4/game/weapon.h"
|
||||
#include "ultima/ultima4/gfx/imagemgr.h"
|
||||
#include "ultima/ultima4/gfx/screen.h"
|
||||
#include "ultima/ultima4/map/city.h"
|
||||
#include "ultima/ultima4/map/annotation.h"
|
||||
#include "ultima/ultima4/map/dungeon.h"
|
||||
#include "ultima/ultima4/map/direction.h"
|
||||
#include "ultima/ultima4/map/location.h"
|
||||
#include "ultima/ultima4/map/mapmgr.h"
|
||||
#include "ultima/ultima4/map/movement.h"
|
||||
#include "ultima/ultima4/map/shrine.h"
|
||||
#include "ultima/ultima4/map/tilemap.h"
|
||||
#include "ultima/ultima4/map/tileset.h"
|
||||
#include "ultima/ultima4/sound/music.h"
|
||||
#include "ultima/ultima4/sound/sound.h"
|
||||
#include "ultima/ultima4/views/dungeonview.h"
|
||||
#include "ultima/ultima4/metaengine.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/*-----------------*/
|
||||
/* Functions BEGIN */
|
||||
|
||||
/* main game functions */
|
||||
void gameAdvanceLevel(PartyMember *player);
|
||||
void gameInnHandler();
|
||||
void gameLostEighth(Virtue virtue);
|
||||
void gamePartyStarving();
|
||||
|
||||
void mixReagentsSuper();
|
||||
|
||||
/* action functions */
|
||||
void wearArmor(int player = -1);
|
||||
|
||||
/* Functions END */
|
||||
/*---------------*/
|
||||
|
||||
void gameSetViewMode(ViewMode newMode) {
|
||||
g_context->_location->_viewMode = newMode;
|
||||
}
|
||||
|
||||
void gameUpdateScreen() {
|
||||
switch (g_context->_location->_viewMode) {
|
||||
case VIEW_NORMAL:
|
||||
g_screen->screenUpdate(&g_game->_mapArea, true, false);
|
||||
break;
|
||||
case VIEW_GEM:
|
||||
g_screen->screenGemUpdate();
|
||||
break;
|
||||
case VIEW_RUNE:
|
||||
g_screen->screenUpdate(&g_game->_mapArea, false, false);
|
||||
break;
|
||||
case VIEW_DUNGEON:
|
||||
g_screen->screenUpdate(&g_game->_mapArea, true, false);
|
||||
break;
|
||||
case VIEW_DEAD:
|
||||
g_screen->screenUpdate(&g_game->_mapArea, true, true);
|
||||
break;
|
||||
case VIEW_CODEX: /* the screen updates will be handled elsewhere */
|
||||
break;
|
||||
case VIEW_MIXTURES: /* still testing */
|
||||
break;
|
||||
default:
|
||||
error("invalid view mode: %d", g_context->_location->_viewMode);
|
||||
}
|
||||
}
|
||||
|
||||
void gameSpellEffect(int spell, int player, Sound sound) {
|
||||
int time;
|
||||
Spell::SpecialEffects effect = Spell::SFX_INVERT;
|
||||
|
||||
if (player >= 0)
|
||||
g_context->_stats->highlightPlayer(player);
|
||||
|
||||
time = settings._spellEffectSpeed * 800 / settings._gameCyclesPerSecond;
|
||||
soundPlay(sound, false, time);
|
||||
|
||||
///The following effect multipliers are not accurate
|
||||
switch (spell) {
|
||||
case 'g': /* gate */
|
||||
case 'r': /* resurrection */
|
||||
break;
|
||||
case 't': /* tremor */
|
||||
effect = Spell::SFX_TREMOR;
|
||||
break;
|
||||
default:
|
||||
/* default spell effect */
|
||||
break;
|
||||
}
|
||||
|
||||
switch (effect) {
|
||||
case Spell::SFX_TREMOR:
|
||||
case Spell::SFX_INVERT:
|
||||
gameUpdateScreen();
|
||||
g_game->_mapArea.highlight(0, 0, VIEWPORT_W * TILE_WIDTH, VIEWPORT_H * TILE_HEIGHT);
|
||||
g_screen->update();
|
||||
EventHandler::sleep(time);
|
||||
g_game->_mapArea.unhighlight();
|
||||
g_screen->update();
|
||||
|
||||
if (effect == Spell::SFX_TREMOR) {
|
||||
gameUpdateScreen();
|
||||
g_screen->update();
|
||||
soundPlay(SOUND_RUMBLE, false);
|
||||
g_screen->screenShake(8);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Common::String gameGetInput(int maxlen) {
|
||||
g_screen->screenEnableCursor();
|
||||
g_screen->screenShowCursor();
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSConversationHelper helper;
|
||||
helper.beginConversation(U4IOS::UIKeyboardTypeDefault);
|
||||
#endif
|
||||
|
||||
return ReadStringController::get(maxlen, TEXT_AREA_X + g_context->_col, TEXT_AREA_Y + g_context->_line);
|
||||
}
|
||||
|
||||
int gameGetPlayer(bool canBeDisabled, bool canBeActivePlayer) {
|
||||
int player;
|
||||
if (g_ultima->_saveGame->_members <= 1) {
|
||||
player = 0;
|
||||
} else {
|
||||
if (canBeActivePlayer && (g_context->_party->getActivePlayer() >= 0)) {
|
||||
player = g_context->_party->getActivePlayer();
|
||||
} else {
|
||||
ReadPlayerController readPlayerController;
|
||||
eventHandler->pushController(&readPlayerController);
|
||||
player = readPlayerController.waitFor();
|
||||
}
|
||||
|
||||
if (player == -1) {
|
||||
g_screen->screenMessage("None\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
g_context->_col--;// display the selected character name, in place of the number
|
||||
if ((player >= 0) && (player < 8)) {
|
||||
g_screen->screenMessage("%s\n", g_ultima->_saveGame->_players[player]._name); //Write player's name after prompt
|
||||
}
|
||||
|
||||
if (!canBeDisabled && g_context->_party->member(player)->isDisabled()) {
|
||||
g_screen->screenMessage("%cDisabled!%c\n", FG_GREY, FG_WHITE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
assertMsg(player < g_context->_party->size(), "player %d, but only %d members\n", player, g_context->_party->size());
|
||||
return player;
|
||||
}
|
||||
|
||||
Direction gameGetDirection() {
|
||||
ReadDirController dirController;
|
||||
|
||||
g_screen->screenMessage("Dir?");
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSDirectionHelper directionPopup;
|
||||
#endif
|
||||
|
||||
eventHandler->pushController(&dirController);
|
||||
Direction dir = dirController.waitFor();
|
||||
|
||||
g_screen->screenMessage("\b\b\b\b");
|
||||
|
||||
if (dir == DIR_NONE) {
|
||||
g_screen->screenMessage(" \n");
|
||||
return dir;
|
||||
} else {
|
||||
g_screen->screenMessage("%s\n", getDirectionName(dir));
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
|
||||
bool fireAt(const Coords &coords, bool originAvatar) {
|
||||
bool validObject = false;
|
||||
bool hitsAvatar = false;
|
||||
bool objectHit = false;
|
||||
|
||||
Object *obj = nullptr;
|
||||
|
||||
|
||||
MapTile tile(g_context->_location->_map->_tileSet->getByName("miss_flash")->getId());
|
||||
GameController::flashTile(coords, tile, 1);
|
||||
|
||||
obj = g_context->_location->_map->objectAt(coords);
|
||||
Creature *m = dynamic_cast<Creature *>(obj);
|
||||
|
||||
if (obj && obj->getType() == Object::CREATURE && m && m->isAttackable())
|
||||
validObject = true;
|
||||
/* See if it's an object to be destroyed (the avatar cannot destroy the balloon) */
|
||||
else if (obj &&
|
||||
(obj->getType() == Object::UNKNOWN) &&
|
||||
!(obj->getTile().getTileType()->isBalloon() && originAvatar))
|
||||
validObject = true;
|
||||
|
||||
/* Does the cannon hit the avatar? */
|
||||
if (coords == g_context->_location->_coords) {
|
||||
validObject = true;
|
||||
hitsAvatar = true;
|
||||
}
|
||||
|
||||
if (validObject) {
|
||||
/* always displays as a 'hit' though the object may not be destroyed */
|
||||
|
||||
/* Is is a pirate ship firing at US? */
|
||||
if (hitsAvatar) {
|
||||
GameController::flashTile(coords, "hit_flash", 4);
|
||||
|
||||
if (g_context->_transportContext == TRANSPORT_SHIP)
|
||||
gameDamageShip(-1, 10);
|
||||
else gameDamageParty(10, 25); /* party gets hurt between 10-25 damage */
|
||||
}
|
||||
/* inanimate objects get destroyed instantly, while creatures get a chance */
|
||||
else if (obj->getType() == Object::UNKNOWN) {
|
||||
GameController::flashTile(coords, "hit_flash", 4);
|
||||
g_context->_location->_map->removeObject(obj);
|
||||
}
|
||||
|
||||
/* only the avatar can hurt other creatures with cannon fire */
|
||||
else if (originAvatar) {
|
||||
GameController::flashTile(coords, "hit_flash", 4);
|
||||
if (xu4_random(4) == 0) /* reverse-engineered from u4dos */
|
||||
g_context->_location->_map->removeObject(obj);
|
||||
}
|
||||
|
||||
objectHit = true;
|
||||
}
|
||||
|
||||
return objectHit;
|
||||
}
|
||||
|
||||
bool gamePeerCity(int city, void *data) {
|
||||
Map *peerMap;
|
||||
|
||||
peerMap = mapMgr->get((MapId)(city + 1));
|
||||
|
||||
if (peerMap != nullptr) {
|
||||
g_game->setMap(peerMap, 1, nullptr);
|
||||
g_context->_location->_viewMode = VIEW_GEM;
|
||||
g_game->_paused = true;
|
||||
g_game->_pausedTimer = 0;
|
||||
|
||||
g_screen->screenDisableCursor();
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSConversationChoiceHelper continueHelper;
|
||||
continueHelper.updateChoices(" ");
|
||||
continueHelper.fullSizeChoicePanel();
|
||||
#endif
|
||||
ReadChoiceController::get("\015 \033");
|
||||
|
||||
g_game->exitToParentMap();
|
||||
g_screen->screenEnableCursor();
|
||||
g_game->_paused = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void peer(bool useGem) {
|
||||
if (useGem) {
|
||||
if (g_ultima->_saveGame->_gems <= 0) {
|
||||
g_screen->screenMessage("%cPeer at What?%c\n", FG_GREY, FG_WHITE);
|
||||
return;
|
||||
}
|
||||
|
||||
g_ultima->_saveGame->_gems--;
|
||||
g_screen->screenMessage("Peer at a Gem!\n");
|
||||
}
|
||||
|
||||
g_game->_paused = true;
|
||||
g_game->_pausedTimer = 0;
|
||||
g_screen->screenDisableCursor();
|
||||
|
||||
g_context->_location->_viewMode = VIEW_GEM;
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSConversationChoiceHelper continueHelper;
|
||||
continueHelper.updateChoices(" ");
|
||||
continueHelper.fullSizeChoicePanel();
|
||||
#endif
|
||||
ReadChoiceController::get("\015 \033");
|
||||
|
||||
g_screen->screenEnableCursor();
|
||||
g_context->_location->_viewMode = VIEW_NORMAL;
|
||||
g_game->_paused = false;
|
||||
}
|
||||
|
||||
void gameCheckHullIntegrity() {
|
||||
int i;
|
||||
|
||||
bool killAll = false;
|
||||
/* see if the ship has sunk */
|
||||
if ((g_context->_transportContext == TRANSPORT_SHIP) && g_ultima->_saveGame->_shipHull <= 0) {
|
||||
g_screen->screenMessage("\nThy ship sinks!\n\n");
|
||||
killAll = true;
|
||||
}
|
||||
|
||||
|
||||
if (!g_debugger->_collisionOverride && g_context->_transportContext == TRANSPORT_FOOT &&
|
||||
g_context->_location->_map->tileTypeAt(g_context->_location->_coords, WITHOUT_OBJECTS)->isSailable() &&
|
||||
!g_context->_location->_map->tileTypeAt(g_context->_location->_coords, WITH_GROUND_OBJECTS)->isShip() &&
|
||||
!g_context->_location->_map->getValidMoves(g_context->_location->_coords, g_context->_party->getTransport())) {
|
||||
g_screen->screenMessage("\nTrapped at sea without thy ship, thou dost drown!\n\n");
|
||||
killAll = true;
|
||||
}
|
||||
|
||||
if (killAll) {
|
||||
for (i = 0; i < g_context->_party->size(); i++) {
|
||||
g_context->_party->member(i)->setHp(0);
|
||||
g_context->_party->member(i)->setStatus(STAT_DEAD);
|
||||
}
|
||||
|
||||
g_screen->update();
|
||||
g_death->start(5);
|
||||
}
|
||||
}
|
||||
|
||||
void gameFixupObjects(Map *map) {
|
||||
int i;
|
||||
Object *obj;
|
||||
|
||||
/* add stuff from the monster table to the map */
|
||||
for (i = 0; i < MONSTERTABLE_SIZE; i++) {
|
||||
SaveGameMonsterRecord *monster = &map->_monsterTable[i];
|
||||
if (monster->_prevTile != 0) {
|
||||
Coords coords(monster->_x, monster->_y);
|
||||
|
||||
// tile values stored in monsters.sav hardcoded to index into base tilemap
|
||||
MapTile tile = g_tileMaps->get("base")->translate(monster->_tile),
|
||||
oldTile = g_tileMaps->get("base")->translate(monster->_prevTile);
|
||||
|
||||
if (i < MONSTERTABLE_CREATURES_SIZE) {
|
||||
const Creature *creature = creatureMgr->getByTile(tile);
|
||||
/* make sure we really have a creature */
|
||||
if (creature)
|
||||
obj = map->addCreature(creature, coords);
|
||||
else {
|
||||
warning("A non-creature object was found in the creature section of the monster table. (Tile: %s)\n", tile.getTileType()->getName().c_str());
|
||||
obj = map->addObject(tile, oldTile, coords);
|
||||
}
|
||||
} else {
|
||||
obj = map->addObject(tile, oldTile, coords);
|
||||
}
|
||||
|
||||
/* set the map for our object */
|
||||
obj->setMap(map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32 gameTimeSinceLastCommand() {
|
||||
return (g_system->getMillis() - g_context->_lastCommandTime) / 1000;
|
||||
}
|
||||
|
||||
void gameCreatureAttack(Creature *m) {
|
||||
Object *under;
|
||||
const Tile *ground;
|
||||
|
||||
g_screen->screenMessage("\nAttacked by %s\n", m->getName().c_str());
|
||||
|
||||
/// TODO: CHEST: Make a user option to not make chests change battlefield
|
||||
/// map (2 of 2)
|
||||
ground = g_context->_location->_map->tileTypeAt(g_context->_location->_coords, WITH_GROUND_OBJECTS);
|
||||
if (!ground->isChest()) {
|
||||
ground = g_context->_location->_map->tileTypeAt(g_context->_location->_coords, WITHOUT_OBJECTS);
|
||||
if ((under = g_context->_location->_map->objectAt(g_context->_location->_coords)) &&
|
||||
under->getTile().getTileType()->isShip())
|
||||
ground = under->getTile().getTileType();
|
||||
}
|
||||
|
||||
CombatController *cc = new CombatController(CombatMap::mapForTile(ground, g_context->_party->getTransport().getTileType(), m));
|
||||
cc->init(m);
|
||||
cc->begin();
|
||||
}
|
||||
|
||||
bool creatureRangeAttack(const Coords &coords, Creature *m) {
|
||||
// int attackdelay = MAX_BATTLE_SPEED - settings.battleSpeed;
|
||||
|
||||
// Figure out what the ranged attack should look like
|
||||
MapTile tile(g_context->_location->_map->_tileSet->getByName((m && !m->getWorldrangedtile().empty()) ?
|
||||
m->getWorldrangedtile() :
|
||||
"hit_flash")->getId());
|
||||
|
||||
GameController::flashTile(coords, tile, 1);
|
||||
|
||||
// See if the attack hits the avatar
|
||||
Object *obj = g_context->_location->_map->objectAt(coords);
|
||||
m = dynamic_cast<Creature *>(obj);
|
||||
|
||||
// Does the attack hit the avatar?
|
||||
if (coords == g_context->_location->_coords) {
|
||||
/* always displays as a 'hit' */
|
||||
GameController::flashTile(coords, tile, 3);
|
||||
|
||||
/* FIXME: check actual damage from u4dos -- values here are guessed */
|
||||
if (g_context->_transportContext == TRANSPORT_SHIP)
|
||||
gameDamageShip(-1, 10);
|
||||
else
|
||||
gameDamageParty(10, 25);
|
||||
|
||||
return true;
|
||||
}
|
||||
// Destroy objects that were hit
|
||||
else if (obj) {
|
||||
if ((obj->getType() == Object::CREATURE && m && m->isAttackable()) ||
|
||||
obj->getType() == Object::UNKNOWN) {
|
||||
|
||||
GameController::flashTile(coords, tile, 3);
|
||||
g_context->_location->_map->removeObject(obj);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Std::vector<Coords> gameGetDirectionalActionPath(int dirmask, int validDirections, const Coords &origin, int minDistance, int maxDistance, bool (*blockedPredicate)(const Tile *tile), bool includeBlocked) {
|
||||
Std::vector<Coords> path;
|
||||
Direction dirx = DIR_NONE,
|
||||
diry = DIR_NONE;
|
||||
|
||||
/* Figure out which direction the action is going */
|
||||
if (DIR_IN_MASK(DIR_WEST, dirmask))
|
||||
dirx = DIR_WEST;
|
||||
else if (DIR_IN_MASK(DIR_EAST, dirmask))
|
||||
dirx = DIR_EAST;
|
||||
if (DIR_IN_MASK(DIR_NORTH, dirmask))
|
||||
diry = DIR_NORTH;
|
||||
else if (DIR_IN_MASK(DIR_SOUTH, dirmask))
|
||||
diry = DIR_SOUTH;
|
||||
|
||||
/*
|
||||
* try every tile in the given direction, up to the given range.
|
||||
* Stop when the range is exceeded, or the action is blocked.
|
||||
*/
|
||||
|
||||
MapCoords t_c(origin);
|
||||
if ((dirx <= 0 || DIR_IN_MASK(dirx, validDirections)) &&
|
||||
(diry <= 0 || DIR_IN_MASK(diry, validDirections))) {
|
||||
for (int distance = 0; distance <= maxDistance;
|
||||
distance++, t_c.move(dirx, g_context->_location->_map), t_c.move(diry, g_context->_location->_map)) {
|
||||
|
||||
if (distance >= minDistance) {
|
||||
/* make sure our action isn't taking us off the map */
|
||||
if (MAP_IS_OOB(g_context->_location->_map, t_c))
|
||||
break;
|
||||
|
||||
const Tile *tile = g_context->_location->_map->tileTypeAt(t_c, WITH_GROUND_OBJECTS);
|
||||
|
||||
/* should we see if the action is blocked before trying it? */
|
||||
if (!includeBlocked && blockedPredicate &&
|
||||
!(*(blockedPredicate))(tile))
|
||||
break;
|
||||
|
||||
path.push_back(t_c);
|
||||
|
||||
/* see if the action was blocked only if it did not succeed */
|
||||
if (includeBlocked && blockedPredicate &&
|
||||
!(*(blockedPredicate))(tile))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
void gameDamageParty(int minDamage, int maxDamage) {
|
||||
int i;
|
||||
int damage;
|
||||
int lastdmged = -1;
|
||||
|
||||
for (i = 0; i < g_context->_party->size(); i++) {
|
||||
if (xu4_random(2) == 0) {
|
||||
damage = ((minDamage >= 0) && (minDamage < maxDamage)) ?
|
||||
xu4_random((maxDamage + 1) - minDamage) + minDamage :
|
||||
maxDamage;
|
||||
g_context->_party->member(i)->applyDamage(damage);
|
||||
g_context->_stats->highlightPlayer(i);
|
||||
lastdmged = i;
|
||||
EventHandler::sleep(50);
|
||||
}
|
||||
}
|
||||
|
||||
g_screen->screenShake(1);
|
||||
|
||||
// Un-highlight the last player
|
||||
if (lastdmged != -1) g_context->_stats->highlightPlayer(lastdmged);
|
||||
}
|
||||
|
||||
void gameDamageShip(int minDamage, int maxDamage) {
|
||||
int damage;
|
||||
|
||||
if (g_context->_transportContext == TRANSPORT_SHIP) {
|
||||
damage = ((minDamage >= 0) && (minDamage < maxDamage)) ?
|
||||
xu4_random((maxDamage + 1) - minDamage) + minDamage :
|
||||
maxDamage;
|
||||
|
||||
g_screen->screenShake(1);
|
||||
|
||||
g_context->_party->damageShip(damage);
|
||||
gameCheckHullIntegrity();
|
||||
}
|
||||
}
|
||||
|
||||
void gameSetActivePlayer(int player) {
|
||||
if (player == -1) {
|
||||
g_context->_party->setActivePlayer(-1);
|
||||
g_screen->screenMessage("Set Active Player: None!\n");
|
||||
} else if (player < g_context->_party->size()) {
|
||||
g_screen->screenMessage("Set Active Player: %s!\n", g_context->_party->member(player)->getName().c_str());
|
||||
if (g_context->_party->member(player)->isDisabled())
|
||||
g_screen->screenMessage("Disabled!\n");
|
||||
else
|
||||
g_context->_party->setActivePlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
bool gameSpawnCreature(const Creature *m) {
|
||||
int t, i;
|
||||
const Creature *creature;
|
||||
MapCoords coords = g_context->_location->_coords;
|
||||
|
||||
if (g_context->_location->_context & CTX_DUNGEON) {
|
||||
/* FIXME: for some reason dungeon monsters aren't spawning correctly */
|
||||
|
||||
bool found = false;
|
||||
MapCoords new_coords;
|
||||
|
||||
for (i = 0; i < 0x20; i++) {
|
||||
new_coords = MapCoords(xu4_random(g_context->_location->_map->_width), xu4_random(g_context->_location->_map->_height), coords.z);
|
||||
const Tile *tile = g_context->_location->_map->tileTypeAt(new_coords, WITH_OBJECTS);
|
||||
if (tile->isCreatureWalkable()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return false;
|
||||
|
||||
coords = new_coords;
|
||||
} else {
|
||||
int dx = 0,
|
||||
dy = 0;
|
||||
bool ok = false;
|
||||
int tries = 0;
|
||||
static const int MAX_TRIES = 10;
|
||||
|
||||
while (!ok && (tries < MAX_TRIES)) {
|
||||
dx = 7;
|
||||
dy = xu4_random(7);
|
||||
|
||||
if (xu4_random(2))
|
||||
dx = -dx;
|
||||
if (xu4_random(2))
|
||||
dy = -dy;
|
||||
if (xu4_random(2)) {
|
||||
t = dx;
|
||||
dx = dy;
|
||||
dy = t;
|
||||
}
|
||||
|
||||
/* make sure we can spawn the creature there */
|
||||
if (m) {
|
||||
MapCoords new_coords = coords;
|
||||
new_coords.move(dx, dy, g_context->_location->_map);
|
||||
|
||||
const Tile *tile = g_context->_location->_map->tileTypeAt(new_coords, WITHOUT_OBJECTS);
|
||||
if ((m->sails() && tile->isSailable()) ||
|
||||
(m->swims() && tile->isSwimable()) ||
|
||||
(m->walks() && tile->isCreatureWalkable()) ||
|
||||
(m->flies() && tile->isFlyable()))
|
||||
ok = true;
|
||||
else
|
||||
tries++;
|
||||
} else {
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok)
|
||||
coords.move(dx, dy, g_context->_location->_map);
|
||||
}
|
||||
|
||||
/* can't spawn creatures on top of the player */
|
||||
if (coords == g_context->_location->_coords)
|
||||
return false;
|
||||
|
||||
/* figure out what creature to spawn */
|
||||
if (m)
|
||||
creature = m;
|
||||
else if (g_context->_location->_context & CTX_DUNGEON)
|
||||
creature = creatureMgr->randomForDungeon(g_context->_location->_coords.z);
|
||||
else
|
||||
creature = creatureMgr->randomForTile(g_context->_location->_map->tileTypeAt(coords, WITHOUT_OBJECTS));
|
||||
|
||||
if (creature)
|
||||
g_context->_location->_map->addCreature(creature, coords);
|
||||
return true;
|
||||
}
|
||||
|
||||
void gameDestroyAllCreatures(void) {
|
||||
int i;
|
||||
|
||||
gameSpellEffect('t', -1, SOUND_MAGIC); /* same effect as tremor */
|
||||
|
||||
if (g_context->_location->_context & CTX_COMBAT) {
|
||||
// Destroy all creatures in combat
|
||||
for (i = 0; i < AREA_CREATURES; i++) {
|
||||
CombatMap *cm = getCombatMap();
|
||||
CreatureVector creatures = cm->getCreatures();
|
||||
for (const auto *obj : creatures) {
|
||||
if (obj->getId() != LORDBRITISH_ID)
|
||||
cm->removeObject(obj);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Destroy all creatures on the map
|
||||
ObjectDeque::iterator current;
|
||||
Map *map = g_context->_location->_map;
|
||||
|
||||
for (current = map->_objects.begin(); current != map->_objects.end();) {
|
||||
Creature *m = dynamic_cast<Creature *>(*current);
|
||||
|
||||
if (m) {
|
||||
// The skull does not destroy Lord British
|
||||
if (m->getId() != LORDBRITISH_ID)
|
||||
current = map->removeObject(current);
|
||||
else
|
||||
current++;
|
||||
} else {
|
||||
current++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Alert the guards! Really, the only one left should be LB himself :)
|
||||
g_context->_location->_map->alertGuards();
|
||||
}
|
||||
|
||||
// Colors assigned to reagents based on my best reading of them
|
||||
// from the book of wisdom. Maybe we could use BOLD to distinguish
|
||||
// the two grey and the two red reagents.
|
||||
const int colors[] = {
|
||||
FG_YELLOW, FG_GREY, FG_BLUE, FG_WHITE, FG_RED, FG_GREY, FG_GREEN, FG_RED
|
||||
};
|
||||
|
||||
void showMixturesSuper(int page = 0) {
|
||||
g_screen->screenTextColor(FG_WHITE);
|
||||
for (int i = 0; i < 13; i++) {
|
||||
|
||||
const Spell *s = g_spells->getSpell(i + 13 * page);
|
||||
int line = i + 8;
|
||||
g_screen->screenTextAt(2, line, "%s", s->_name);
|
||||
|
||||
g_screen->screenTextAt(6, line, "%s", Common::String::format("%3d", g_ultima->_saveGame->_mixtures[i + 13 * page]).c_str());
|
||||
|
||||
g_screen->screenShowChar(32, 9, line);
|
||||
int comp = s->_components;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
g_screen->screenTextColor(colors[j]);
|
||||
g_screen->screenShowChar(comp & (1 << j) ? CHARSET_BULLET : ' ', 10 + j, line);
|
||||
}
|
||||
g_screen->screenTextColor(FG_WHITE);
|
||||
|
||||
g_screen->screenTextAt(19, line, "%s", Common::String::format("%2d", s->_mp).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void mixReagentsSuper() {
|
||||
g_screen->screenMessage("Mix reagents\n");
|
||||
|
||||
static int page = 0;
|
||||
|
||||
struct ReagentShop {
|
||||
const char *name;
|
||||
int price[6];
|
||||
};
|
||||
ReagentShop shops[] = {
|
||||
{ "BuccDen", {6, 7, 9, 9, 9, 1} },
|
||||
{ "Moonglo", {2, 5, 6, 3, 6, 9} },
|
||||
{ "Paws", {3, 4, 2, 8, 6, 7} },
|
||||
{ "SkaraBr", {2, 4, 9, 6, 4, 8} },
|
||||
};
|
||||
const int shopcount = sizeof(shops) / sizeof(shops[0]);
|
||||
|
||||
int oldlocation = g_context->_location->_viewMode;
|
||||
g_context->_location->_viewMode = VIEW_MIXTURES;
|
||||
g_screen->screenUpdate(&g_game->_mapArea, true, true);
|
||||
|
||||
g_screen->screenTextAt(16, 2, "%s", "<-Shops");
|
||||
|
||||
g_context->_stats->setView(StatsView(STATS_REAGENTS));
|
||||
g_screen->screenTextColor(FG_PURPLE);
|
||||
g_screen->screenTextAt(2, 7, "%s", "SPELL # Reagents MP");
|
||||
|
||||
for (int i = 0; i < shopcount; i++) {
|
||||
int line = i + 1;
|
||||
ReagentShop *s = &shops[i];
|
||||
g_screen->screenTextColor(FG_WHITE);
|
||||
g_screen->screenTextAt(2, line, "%s", s->name);
|
||||
for (int j = 0; j < 6; j++) {
|
||||
g_screen->screenTextColor(colors[j]);
|
||||
g_screen->screenShowChar('0' + s->price[j], 10 + j, line);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
g_screen->screenTextColor(colors[i]);
|
||||
g_screen->screenShowChar('A' + i, 10 + i, 6);
|
||||
}
|
||||
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
showMixturesSuper(page);
|
||||
g_screen->screenMessage("For Spell: ");
|
||||
|
||||
int spell = ReadChoiceController::get("abcdefghijklmnopqrstuvwxyz \033\n\r");
|
||||
if (spell < 'a' || spell > 'z') {
|
||||
g_screen->screenMessage("\nDone.\n");
|
||||
done = true;
|
||||
} else {
|
||||
spell -= 'a';
|
||||
const Spell *s = g_spells->getSpell(spell);
|
||||
g_screen->screenMessage("%s\n", s->_name);
|
||||
page = (spell >= 13);
|
||||
showMixturesSuper(page);
|
||||
|
||||
// how many can we mix?
|
||||
int mixQty = 99 - g_ultima->_saveGame->_mixtures[spell];
|
||||
int ingQty = 99;
|
||||
int comp = s->_components;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (comp & 1 << i) {
|
||||
int reagentQty = g_ultima->_saveGame->_reagents[i];
|
||||
if (reagentQty < ingQty)
|
||||
ingQty = reagentQty;
|
||||
}
|
||||
}
|
||||
g_screen->screenMessage("You can make %d.\n", (mixQty > ingQty) ? ingQty : mixQty);
|
||||
g_screen->screenMessage("How many? ");
|
||||
|
||||
int howmany = ReadIntController::get(2, TEXT_AREA_X + g_context->_col, TEXT_AREA_Y + g_context->_line);
|
||||
|
||||
if (howmany == 0) {
|
||||
g_screen->screenMessage("\nNone mixed!\n");
|
||||
} else if (howmany > mixQty) {
|
||||
g_screen->screenMessage("\n%cYou cannot mix that much more of that spell!%c\n", FG_GREY, FG_WHITE);
|
||||
} else if (howmany > ingQty) {
|
||||
g_screen->screenMessage("\n%cYou don't have enough reagents to mix %d spells!%c\n", FG_GREY, howmany, FG_WHITE);
|
||||
} else {
|
||||
g_ultima->_saveGame->_mixtures[spell] += howmany;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (comp & 1 << i) {
|
||||
g_ultima->_saveGame->_reagents[i] -= howmany;
|
||||
}
|
||||
}
|
||||
g_screen->screenMessage("\nSuccess!\n\n");
|
||||
}
|
||||
}
|
||||
g_context->_stats->setView(StatsView(STATS_REAGENTS));
|
||||
}
|
||||
|
||||
g_context->_location->_viewMode = oldlocation;
|
||||
return;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
153
engines/ultima/ultima4/game/game.h
Normal file
153
engines/ultima/ultima4/game/game.h
Normal file
@@ -0,0 +1,153 @@
|
||||
/* 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_GAME_GAME_H
|
||||
#define ULTIMA4_GAME_GAME_H
|
||||
|
||||
#include "ultima/ultima4/events/event_handler.h"
|
||||
#include "ultima/ultima4/controllers/game_controller.h"
|
||||
#include "ultima/ultima4/core/observer.h"
|
||||
#include "ultima/ultima4/core/types.h"
|
||||
#include "ultima/ultima4/map/map.h"
|
||||
#include "ultima/ultima4/views/tileview.h"
|
||||
#include "ultima/ultima4/sound/sound.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class Map;
|
||||
struct Portal;
|
||||
class Creature;
|
||||
class Location;
|
||||
class MoveEvent;
|
||||
class Party;
|
||||
class PartyEvent;
|
||||
class PartyMember;
|
||||
|
||||
typedef enum {
|
||||
VIEW_NORMAL,
|
||||
VIEW_GEM,
|
||||
VIEW_RUNE,
|
||||
VIEW_DUNGEON,
|
||||
VIEW_DEAD,
|
||||
VIEW_CODEX,
|
||||
VIEW_MIXTURES
|
||||
} ViewMode;
|
||||
|
||||
/* map and screen functions */
|
||||
|
||||
/**
|
||||
* Sets the view mode.
|
||||
*/
|
||||
void gameSetViewMode(ViewMode newMode);
|
||||
void gameUpdateScreen();
|
||||
|
||||
/* spell functions */
|
||||
void gameSpellEffect(int spell, int player, Sound sound);
|
||||
|
||||
/* action functions */
|
||||
/**
|
||||
* Peers at a city from A-P (Lycaeum telescope) and functions like a gem
|
||||
*/
|
||||
bool gamePeerCity(int city, void *data);
|
||||
|
||||
/**
|
||||
* Peers at a gem
|
||||
*/
|
||||
void peer(bool useGem = true);
|
||||
bool fireAt(const Coords &coords, bool originAvatar);
|
||||
Direction gameGetDirection();
|
||||
uint32 gameTimeSinceLastCommand();
|
||||
|
||||
/* checking functions */
|
||||
/**
|
||||
* Checks the hull integrity of the ship and handles
|
||||
* the ship sinking, if necessary
|
||||
*/
|
||||
void gameCheckHullIntegrity();
|
||||
|
||||
/* creature functions */
|
||||
/**
|
||||
* Performs a ranged attack for the creature at x,y on the world map
|
||||
*/
|
||||
bool creatureRangeAttack(const Coords &coords, Creature *m);
|
||||
void gameCreatureCleanup();
|
||||
|
||||
/**
|
||||
* Spawns a creature (m) just offscreen of the avatar.
|
||||
* If (m==nullptr) then it finds its own creature to spawn and spawns it.
|
||||
*/
|
||||
bool gameSpawnCreature(const class Creature *m);
|
||||
|
||||
/**
|
||||
* Fixes objects initially loaded by saveGameMonstersRead,
|
||||
* and alters movement behavior accordingly to match the creature
|
||||
*/
|
||||
void gameFixupObjects(Map *map);
|
||||
|
||||
/**
|
||||
* Destroys all creatures on the current map.
|
||||
*/
|
||||
void gameDestroyAllCreatures();
|
||||
|
||||
/**
|
||||
* Handles what happens when a creature attacks you
|
||||
*/
|
||||
void gameCreatureAttack(Creature *obj);
|
||||
|
||||
/* etc */
|
||||
Common::String gameGetInput(int maxlen = 32);
|
||||
int gameGetPlayer(bool canBeDisabled, bool canBeActivePlayer);
|
||||
void gameGetPlayerForCommand(bool (*commandFn)(int player), bool canBeDisabled, bool canBeActivePlayer);
|
||||
|
||||
/**
|
||||
* Deals an amount of damage between 'minDamage' and 'maxDamage'
|
||||
* to each party member, with a 50% chance for each member to
|
||||
* avoid the damage. If (minDamage == -1) or (minDamage >= maxDamage),
|
||||
* deals 'maxDamage' damage to each member.
|
||||
*/
|
||||
void gameDamageParty(int minDamage, int maxDamage);
|
||||
|
||||
/**
|
||||
* Deals an amount of damage between 'minDamage' and 'maxDamage'
|
||||
* to the ship. If (minDamage == -1) or (minDamage >= maxDamage),
|
||||
* deals 'maxDamage' damage to the ship.
|
||||
*/
|
||||
void gameDamageShip(int minDamage, int maxDamage);
|
||||
|
||||
/**
|
||||
* Sets (or unsets) the active player
|
||||
*/
|
||||
void gameSetActivePlayer(int player);
|
||||
|
||||
/**
|
||||
* Gets the path of coordinates for an action. Each tile in the
|
||||
* direction specified by dirmask, between the minimum and maximum
|
||||
* distances given, is included in the path, until blockedPredicate
|
||||
* fails. If a tile is blocked, that tile is included in the path
|
||||
* only if includeBlocked is true.
|
||||
*/
|
||||
Std::vector<Coords> gameGetDirectionalActionPath(int dirmask, int validDirections, const Coords &origin, int minDistance, int maxDistance, bool (*blockedPredicate)(const Tile *tile), bool includeBlocked);
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
761
engines/ultima/ultima4/game/item.cpp
Normal file
761
engines/ultima/ultima4/game/item.cpp
Normal file
@@ -0,0 +1,761 @@
|
||||
/* 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/game/item.h"
|
||||
#include "ultima/ultima4/game/codex.h"
|
||||
#include "ultima/ultima4/game/game.h"
|
||||
#include "ultima/ultima4/game/names.h"
|
||||
#include "ultima/ultima4/game/player.h"
|
||||
#include "ultima/ultima4/game/portal.h"
|
||||
#include "ultima/ultima4/game/context.h"
|
||||
#include "ultima/ultima4/game/weapon.h"
|
||||
#include "ultima/ultima4/controllers/alpha_action_controller.h"
|
||||
#include "ultima/ultima4/controllers/combat_controller.h"
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/gfx/screen.h"
|
||||
#include "ultima/ultima4/map/annotation.h"
|
||||
#include "ultima/ultima4/map/dungeon.h"
|
||||
#include "ultima/ultima4/map/location.h"
|
||||
#include "ultima/ultima4/map/map.h"
|
||||
#include "ultima/ultima4/map/mapmgr.h"
|
||||
#include "ultima/ultima4/map/tileset.h"
|
||||
#include "ultima/ultima4/ultima4.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
Items *g_items;
|
||||
|
||||
const ItemLocation Items::ITEMS[N_ITEMS] = {
|
||||
{
|
||||
"Mandrake Root", nullptr, "mandrake1",
|
||||
&Items::isReagentInInventory, &Items::putReagentInInventory, nullptr, REAG_MANDRAKE, SC_NEWMOONS | SC_REAGENTDELAY
|
||||
},
|
||||
{
|
||||
"Mandrake Root", nullptr, "mandrake2",
|
||||
&Items::isReagentInInventory, &Items::putReagentInInventory, nullptr, REAG_MANDRAKE, SC_NEWMOONS | SC_REAGENTDELAY
|
||||
},
|
||||
{
|
||||
"Nightshade", nullptr, "nightshade1",
|
||||
&Items::isReagentInInventory, &Items::putReagentInInventory, nullptr, REAG_NIGHTSHADE, SC_NEWMOONS | SC_REAGENTDELAY
|
||||
},
|
||||
{
|
||||
"Nightshade", nullptr, "nightshade2",
|
||||
&Items::isReagentInInventory, &Items::putReagentInInventory, nullptr, REAG_NIGHTSHADE, SC_NEWMOONS | SC_REAGENTDELAY
|
||||
},
|
||||
{
|
||||
"the Bell of Courage", "bell", "bell",
|
||||
&Items::isItemInInventory, &Items::putItemInInventory, &Items::useBBC, ITEM_BELL, 0
|
||||
},
|
||||
{
|
||||
"the Book of Truth", "book", "book",
|
||||
&Items::isItemInInventory, &Items::putItemInInventory, &Items::useBBC, ITEM_BOOK, 0
|
||||
},
|
||||
{
|
||||
"the Candle of Love", "candle", "candle",
|
||||
&Items::isItemInInventory, &Items::putItemInInventory, &Items::useBBC, ITEM_CANDLE, 0
|
||||
},
|
||||
{
|
||||
"A Silver Horn", "horn", "horn",
|
||||
&Items::isItemInInventory, &Items::putItemInInventory, &Items::useHorn, ITEM_HORN, 0
|
||||
},
|
||||
{
|
||||
"the Wheel from the H.M.S. Cape", "wheel", "wheel",
|
||||
&Items::isItemInInventory, &Items::putItemInInventory, &Items::useWheel, ITEM_WHEEL, 0
|
||||
},
|
||||
{
|
||||
"the Skull of Modain the Wizard", "skull", "skull",
|
||||
&Items::isSkullInInventory, &Items::putItemInInventory, &Items::useSkull, ITEM_SKULL, SC_NEWMOONS
|
||||
},
|
||||
{
|
||||
"the Red Stone", "red", "redstone",
|
||||
&Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_RED, 0
|
||||
},
|
||||
{
|
||||
"the Orange Stone", "orange", "orangestone",
|
||||
&Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_ORANGE, 0
|
||||
},
|
||||
{
|
||||
"the Yellow Stone", "yellow", "yellowstone",
|
||||
&Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_YELLOW, 0
|
||||
},
|
||||
{
|
||||
"the Green Stone", "green", "greenstone",
|
||||
&Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_GREEN, 0
|
||||
},
|
||||
{
|
||||
"the Blue Stone", "blue", "bluestone",
|
||||
&Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_BLUE, 0
|
||||
},
|
||||
{
|
||||
"the Purple Stone", "purple", "purplestone",
|
||||
&Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_PURPLE, 0
|
||||
},
|
||||
{
|
||||
"the Black Stone", "black", "blackstone",
|
||||
&Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_BLACK, SC_NEWMOONS
|
||||
},
|
||||
{
|
||||
"the White Stone", "white", "whitestone",
|
||||
&Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_WHITE, 0
|
||||
},
|
||||
|
||||
/* handlers for using generic objects */
|
||||
{ nullptr, "stone", nullptr, &Items::isStoneInInventory, nullptr, &Items::useStone, -1, 0 },
|
||||
{ nullptr, "stones", nullptr, &Items::isStoneInInventory, nullptr, &Items::useStone, -1, 0 },
|
||||
{ nullptr, "key", nullptr, &Items::isItemInInventory, nullptr, &Items::useKey, (ITEM_KEY_C | ITEM_KEY_L | ITEM_KEY_T), 0 },
|
||||
{ nullptr, "keys", nullptr, &Items::isItemInInventory, nullptr, &Items::useKey, (ITEM_KEY_C | ITEM_KEY_L | ITEM_KEY_T), 0 },
|
||||
|
||||
/* Lycaeum telescope */
|
||||
{ nullptr, nullptr, "telescope", nullptr, &Items::useTelescope, nullptr, 0, 0 },
|
||||
|
||||
{
|
||||
"Mystic Armor", nullptr, "mysticarmor",
|
||||
&Items::isMysticInInventory, &Items::putMysticInInventory, nullptr, ARMR_MYSTICROBES, SC_FULLAVATAR
|
||||
},
|
||||
{
|
||||
"Mystic Swords", nullptr, "mysticswords",
|
||||
&Items::isMysticInInventory, &Items::putMysticInInventory, nullptr, WEAP_MYSTICSWORD, SC_FULLAVATAR
|
||||
},
|
||||
{
|
||||
"the sulfury remains of an ancient Sosarian Laser Gun. It turns to ash in your fingers", nullptr, "lasergun", // lol, where'd that come from?
|
||||
//Looks like someone was experimenting with "maps.xml". It effectively increments sulfur ash by one due to '16' being an invalid weapon index.
|
||||
&Items::isWeaponInInventory, &Items::putWeaponInInventory, nullptr, 16, 0
|
||||
},
|
||||
{
|
||||
"the rune of Honesty", nullptr, "honestyrune",
|
||||
&Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_HONESTY, 0
|
||||
},
|
||||
{
|
||||
"the rune of Compassion", nullptr, "compassionrune",
|
||||
&Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_COMPASSION, 0
|
||||
},
|
||||
{
|
||||
"the rune of Valor", nullptr, "valorrune",
|
||||
&Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_VALOR, 0
|
||||
},
|
||||
{
|
||||
"the rune of Justice", nullptr, "justicerune",
|
||||
&Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_JUSTICE, 0
|
||||
},
|
||||
{
|
||||
"the rune of Sacrifice", nullptr, "sacrificerune",
|
||||
&Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_SACRIFICE, 0
|
||||
},
|
||||
{
|
||||
"the rune of Honor", nullptr, "honorrune",
|
||||
&Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_HONOR, 0
|
||||
},
|
||||
{
|
||||
"the rune of Spirituality", nullptr, "spiritualityrune",
|
||||
&Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_SPIRITUALITY, 0
|
||||
},
|
||||
{
|
||||
"the rune of Humility", nullptr, "humilityrune",
|
||||
&Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_HUMILITY, 0
|
||||
}
|
||||
};
|
||||
|
||||
Items::Items() : destroyAllCreaturesCallback(nullptr),
|
||||
needStoneNames(0), stoneMask(0) {
|
||||
g_items = this;
|
||||
}
|
||||
|
||||
Items::~Items() {
|
||||
g_items = nullptr;
|
||||
}
|
||||
|
||||
void Items::setDestroyAllCreaturesCallback(DestroyAllCreaturesCallback callback) {
|
||||
destroyAllCreaturesCallback = callback;
|
||||
}
|
||||
|
||||
bool Items::isRuneInInventory(int virt) {
|
||||
return g_ultima->_saveGame->_runes & virt;
|
||||
}
|
||||
|
||||
void Items::putRuneInInventory(int virt) {
|
||||
g_context->_party->member(0)->awardXp(100);
|
||||
g_context->_party->adjustKarma(KA_FOUND_ITEM);
|
||||
g_ultima->_saveGame->_runes |= virt;
|
||||
#ifdef IOS_ULTIMA4
|
||||
Common::String virtueName;
|
||||
switch (virt) {
|
||||
default:
|
||||
case RUNE_HONESTY:
|
||||
virtueName = "Honesty";
|
||||
break;
|
||||
case RUNE_HONOR:
|
||||
virtueName = "Honor";
|
||||
break;
|
||||
case RUNE_HUMILITY:
|
||||
virtueName = "Humility";
|
||||
break;
|
||||
case RUNE_JUSTICE:
|
||||
virtueName = "Justice";
|
||||
break;
|
||||
case RUNE_SACRIFICE:
|
||||
virtueName = "Sacrifice";
|
||||
break;
|
||||
case RUNE_SPIRITUALITY:
|
||||
virtueName = "Spirituality";
|
||||
break;
|
||||
case RUNE_VALOR:
|
||||
virtueName = "Valor";
|
||||
break;
|
||||
case RUNE_COMPASSION:
|
||||
virtueName = "Compassion";
|
||||
break;
|
||||
}
|
||||
U4IOS::testFlightPassCheckPoint("Player got stone: " + virtueName);
|
||||
#endif
|
||||
g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0;
|
||||
}
|
||||
|
||||
bool Items::isStoneInInventory(int virt) {
|
||||
/* generic test: does the party have any stones yet? */
|
||||
if (virt == -1)
|
||||
return (g_ultima->_saveGame->_stones > 0);
|
||||
/* specific test: does the party have a specific stone? */
|
||||
else
|
||||
return g_ultima->_saveGame->_stones & virt;
|
||||
}
|
||||
|
||||
void Items::putStoneInInventory(int virt) {
|
||||
g_context->_party->member(0)->awardXp(200);
|
||||
g_context->_party->adjustKarma(KA_FOUND_ITEM);
|
||||
g_ultima->_saveGame->_stones |= virt;
|
||||
#ifdef IOS_ULTIMA4
|
||||
Common::String stoneName;
|
||||
switch (virt) {
|
||||
default:
|
||||
case STONE_BLACK:
|
||||
stoneName = "Black";
|
||||
break;
|
||||
case STONE_BLUE:
|
||||
stoneName = "Blue";
|
||||
break;
|
||||
case STONE_GREEN:
|
||||
stoneName = "Green";
|
||||
break;
|
||||
case STONE_ORANGE:
|
||||
stoneName = "Orange";
|
||||
break;
|
||||
case STONE_PURPLE:
|
||||
stoneName = "Purple";
|
||||
break;
|
||||
case STONE_RED:
|
||||
stoneName = "Red";
|
||||
break;
|
||||
case STONE_WHITE:
|
||||
stoneName = "White";
|
||||
break;
|
||||
case STONE_YELLOW:
|
||||
stoneName = "Yellow";
|
||||
break;
|
||||
}
|
||||
U4IOS::testFlightPassCheckPoint("Player got rune: " + stoneName);
|
||||
#endif
|
||||
g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0;
|
||||
}
|
||||
|
||||
bool Items::isItemInInventory(int item) {
|
||||
return g_ultima->_saveGame->_items & item;
|
||||
}
|
||||
|
||||
bool Items::isSkullInInventory(int unused) {
|
||||
return (g_ultima->_saveGame->_items & (ITEM_SKULL | ITEM_SKULL_DESTROYED));
|
||||
}
|
||||
|
||||
void Items::putItemInInventory(int item) {
|
||||
g_context->_party->member(0)->awardXp(400);
|
||||
g_context->_party->adjustKarma(KA_FOUND_ITEM);
|
||||
g_ultima->_saveGame->_items |= item;
|
||||
#ifdef IOS_ULTIMA4
|
||||
Common::String itemName;
|
||||
switch (item) {
|
||||
default:
|
||||
case ITEM_BELL:
|
||||
itemName = "Bell";
|
||||
break;
|
||||
case ITEM_BOOK:
|
||||
itemName = "Book";
|
||||
break;
|
||||
case ITEM_CANDLE:
|
||||
itemName = "Candle";
|
||||
break;
|
||||
case ITEM_HORN:
|
||||
itemName = "Horn";
|
||||
break;
|
||||
case ITEM_KEY_C:
|
||||
itemName = "Key Courage";
|
||||
break;
|
||||
case ITEM_KEY_L:
|
||||
itemName = "Key Love";
|
||||
break;
|
||||
case ITEM_KEY_T:
|
||||
itemName = "Key Truth";
|
||||
break;
|
||||
case ITEM_SKULL:
|
||||
itemName = "Skull";
|
||||
break;
|
||||
case ITEM_WHEEL:
|
||||
itemName = "Wheel";
|
||||
break;
|
||||
|
||||
}
|
||||
U4IOS::testFlightPassCheckPoint("Player got rune: " + itemName);
|
||||
#endif
|
||||
g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0;
|
||||
}
|
||||
|
||||
void Items::useBBC(int item) {
|
||||
Coords abyssEntrance(0xe9, 0xe9);
|
||||
/* on top of the Abyss entrance */
|
||||
if (g_context->_location->_coords == abyssEntrance) {
|
||||
/* must use bell first */
|
||||
if (item == ITEM_BELL) {
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::testFlightPassCheckPoint("The Bell rings on and on!");
|
||||
#endif
|
||||
g_screen->screenMessage("\nThe Bell rings on and on!\n");
|
||||
g_ultima->_saveGame->_items |= ITEM_BELL_USED;
|
||||
}
|
||||
/* then the book */
|
||||
else if ((item == ITEM_BOOK) && (g_ultima->_saveGame->_items & ITEM_BELL_USED)) {
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::testFlightPassCheckPoint("The words resonate with the ringing!");
|
||||
#endif
|
||||
g_screen->screenMessage("\nThe words resonate with the ringing!\n");
|
||||
g_ultima->_saveGame->_items |= ITEM_BOOK_USED;
|
||||
}
|
||||
/* then the candle */
|
||||
else if ((item == ITEM_CANDLE) && (g_ultima->_saveGame->_items & ITEM_BOOK_USED)) {
|
||||
g_screen->screenMessage("\nAs you light the Candle the Earth Trembles!\n");
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::testFlightPassCheckPoint("As you light the Candle the Earth Trembles!");
|
||||
#endif
|
||||
g_ultima->_saveGame->_items |= ITEM_CANDLE_USED;
|
||||
} else {
|
||||
g_screen->screenMessage("\nHmm...No effect!\n");
|
||||
}
|
||||
}
|
||||
/* somewhere else */
|
||||
else {
|
||||
g_screen->screenMessage("\nHmm...No effect!\n");
|
||||
}
|
||||
}
|
||||
|
||||
void Items::useHorn(int item) {
|
||||
g_screen->screenMessage("\nThe Horn sounds an eerie tone!\n");
|
||||
g_context->_aura->set(Aura::HORN, 10);
|
||||
}
|
||||
|
||||
void Items::useWheel(int item) {
|
||||
if ((g_context->_transportContext == TRANSPORT_SHIP) && (g_ultima->_saveGame->_shipHull == 50)) {
|
||||
g_screen->screenMessage("\nOnce mounted, the Wheel glows with a blue light!\n");
|
||||
g_context->_party->setShipHull(99);
|
||||
} else {
|
||||
g_screen->screenMessage("\nHmm...No effect!\n");
|
||||
}
|
||||
}
|
||||
|
||||
void Items::useSkull(int item) {
|
||||
/* FIXME: check to see if the abyss must be opened first
|
||||
for the skull to be *able* to be destroyed */
|
||||
|
||||
/* We do the check here instead of in the table, because we need to distinguish between a
|
||||
never-found skull and a destroyed skull. */
|
||||
if (g_ultima->_saveGame->_items & ITEM_SKULL_DESTROYED) {
|
||||
g_screen->screenMessage("\nNone owned!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* destroy the skull! pat yourself on the back */
|
||||
if (g_context->_location->_coords.x == 0xe9 && g_context->_location->_coords.y == 0xe9) {
|
||||
g_screen->screenMessage("\n\nYou cast the Skull of Mondain into the Abyss!\n");
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::testFlightPassCheckPoint("You cast the Skull of Mondain into the Abyss!");
|
||||
#endif
|
||||
|
||||
g_ultima->_saveGame->_items = (g_ultima->_saveGame->_items & ~ITEM_SKULL) | ITEM_SKULL_DESTROYED;
|
||||
g_context->_party->adjustKarma(KA_DESTROYED_SKULL);
|
||||
}
|
||||
|
||||
/* use the skull... bad, very bad */
|
||||
else {
|
||||
g_screen->screenMessage("\n\nYou hold the evil Skull of Mondain the Wizard aloft...\n");
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::testFlightPassCheckPoint("You hold the evil Skull of Mondain the Wizard aloft...");
|
||||
#endif
|
||||
|
||||
/* destroy all creatures */
|
||||
(*destroyAllCreaturesCallback)();
|
||||
|
||||
/* we don't lose the skull until we toss it into the abyss */
|
||||
//c->saveGame->_items = (c->saveGame->_items & ~ITEM_SKULL);
|
||||
g_context->_party->adjustKarma(KA_USED_SKULL);
|
||||
}
|
||||
}
|
||||
|
||||
void Items::useStone(int item) {
|
||||
MapCoords coords;
|
||||
byte stone = static_cast<byte>(item);
|
||||
|
||||
static const byte truth = STONE_WHITE | STONE_PURPLE | STONE_GREEN | STONE_BLUE;
|
||||
static const byte love = STONE_WHITE | STONE_YELLOW | STONE_GREEN | STONE_ORANGE;
|
||||
static const byte courage = STONE_WHITE | STONE_RED | STONE_PURPLE | STONE_ORANGE;
|
||||
static const byte *attr = nullptr;
|
||||
|
||||
g_context->_location->getCurrentPosition(&coords);
|
||||
|
||||
/**
|
||||
* Named a specific stone (after using "stone" or "stones")
|
||||
*/
|
||||
if (item != -1) {
|
||||
CombatMap *cm = getCombatMap();
|
||||
|
||||
if (needStoneNames) {
|
||||
/* named a stone while in a dungeon altar room */
|
||||
if (g_context->_location->_context & CTX_ALTAR_ROOM) {
|
||||
needStoneNames--;
|
||||
|
||||
switch (cm->getAltarRoom()) {
|
||||
case VIRT_TRUTH:
|
||||
attr = &truth;
|
||||
break;
|
||||
case VIRT_LOVE:
|
||||
attr = &love;
|
||||
break;
|
||||
case VIRT_COURAGE:
|
||||
attr = &courage;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* make sure we're in an altar room */
|
||||
if (attr) {
|
||||
/* we need to use the stone, and we haven't used it yet */
|
||||
if ((*attr & stone) && (stone & ~stoneMask))
|
||||
stoneMask |= stone;
|
||||
/* we already used that stone! */
|
||||
else if (stone & stoneMask) {
|
||||
g_screen->screenMessage("\nAlready used!\n");
|
||||
needStoneNames = 0;
|
||||
stoneMask = 0; /* reset the mask so you can try again */
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
error("Not in an altar room!");
|
||||
}
|
||||
/* see if we have all the stones, if not, get more names! */
|
||||
if (attr && needStoneNames) {
|
||||
g_screen->screenMessage("\n%c:", 'E' - needStoneNames);
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSConversationHelper::setIntroString("Which Color?");
|
||||
#endif
|
||||
itemHandleStones(gameGetInput());
|
||||
}
|
||||
/* all the stones have been entered, verify them! */
|
||||
else {
|
||||
unsigned short key = 0xFFFF;
|
||||
switch (cm->getAltarRoom()) {
|
||||
case VIRT_TRUTH:
|
||||
key = ITEM_KEY_T;
|
||||
break;
|
||||
case VIRT_LOVE:
|
||||
key = ITEM_KEY_L;
|
||||
break;
|
||||
case VIRT_COURAGE:
|
||||
key = ITEM_KEY_C;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* in an altar room, named all of the stones, and don't have the key yet... */
|
||||
if (attr && (stoneMask == *attr) && !(g_ultima->_saveGame->_items & key)) {
|
||||
#ifdef IOS_ULTIMA4
|
||||
Common::String keyName;
|
||||
switch (key) {
|
||||
case ITEM_KEY_C:
|
||||
keyName = "Key Courage";
|
||||
break;
|
||||
case ITEM_KEY_L:
|
||||
keyName = "Key Love";
|
||||
break;
|
||||
case ITEM_KEY_T:
|
||||
keyName = "Key Truth";
|
||||
break;
|
||||
}
|
||||
U4IOS::testFlightPassCheckPoint("Receive a key: " + keyName);
|
||||
#endif
|
||||
g_screen->screenMessage("\nThou doth find one third of the Three Part Key!\n");
|
||||
g_ultima->_saveGame->_items |= key;
|
||||
} else {
|
||||
g_screen->screenMessage("\nHmm...No effect!\n");
|
||||
}
|
||||
|
||||
stoneMask = 0; /* reset the mask so you can try again */
|
||||
}
|
||||
} else {
|
||||
/* Otherwise, we're asking for a stone while in the abyss on top of an altar */
|
||||
/* see if they entered the correct stone */
|
||||
if (stone == (1 << g_context->_location->_coords.z)) {
|
||||
if (g_context->_location->_coords.z < 7) {
|
||||
/* replace the altar with a down-ladder */
|
||||
MapCoords pos;
|
||||
g_screen->screenMessage("\n\nThe altar changes before thyne eyes!\n");
|
||||
g_context->_location->getCurrentPosition(&pos);
|
||||
g_context->_location->_map->_annotations->add(pos, g_context->_location->_map->_tileSet->getByName("down_ladder")->getId());
|
||||
} else {
|
||||
// Start chamber of the codex sequence...
|
||||
g_codex->start();
|
||||
}
|
||||
} else {
|
||||
g_screen->screenMessage("\nHmm...No effect!\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
g_screen->screenMessage("\nNot a Usable Item!\n");
|
||||
stoneMask = 0; /* reset the mask so you can try again */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* in the abyss, on an altar to place the stones
|
||||
*/
|
||||
else if ((g_context->_location->_map->_id == MAP_ABYSS) &&
|
||||
(g_context->_location->_context & CTX_DUNGEON) &&
|
||||
(static_cast<Dungeon *>(g_context->_location->_map)->currentToken() == DUNGEON_ALTAR)) {
|
||||
|
||||
int virtueMask = getBaseVirtues((Virtue)g_context->_location->_coords.z);
|
||||
if (virtueMask > 0)
|
||||
g_screen->screenMessage("\n\nAs thou doth approach, a voice rings out: What virtue dost stem from %s?\n\n", getBaseVirtueName(virtueMask));
|
||||
else
|
||||
g_screen->screenMessage("\n\nA voice rings out: What virtue exists independently of Truth, Love, and Courage?\n\n");
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSConversationHelper::setIntroString("Which virtue?");
|
||||
#endif
|
||||
Common::String virtue = gameGetInput();
|
||||
|
||||
if (scumm_strnicmp(virtue.c_str(), getVirtueName((Virtue)g_context->_location->_coords.z), 6) == 0) {
|
||||
/* now ask for stone */
|
||||
g_screen->screenMessage("\n\nThe Voice says: Use thy Stone.\n\nColor:\n");
|
||||
needStoneNames = 1;
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSConversationHelper::setIntroString("Which color?");
|
||||
#endif
|
||||
itemHandleStones(gameGetInput());
|
||||
} else {
|
||||
g_screen->screenMessage("\nHmm...No effect!\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* in a dungeon altar room, on the altar
|
||||
*/
|
||||
else if ((g_context->_location->_context & CTX_ALTAR_ROOM) &&
|
||||
coords.x == 5 && coords.y == 5) {
|
||||
needStoneNames = 4;
|
||||
g_screen->screenMessage("\n\nThere are holes for 4 stones.\nWhat colors:\nA:");
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSConversationHelper::setIntroString("Which color?");
|
||||
#endif
|
||||
itemHandleStones(gameGetInput());
|
||||
} else {
|
||||
g_screen->screenMessage("\nNo place to Use them!\n");
|
||||
// This used to say "\nNo place to Use them!\nHmm...No effect!\n"
|
||||
// That doesn't match U4DOS; does it match another?
|
||||
}
|
||||
}
|
||||
|
||||
void Items::useKey(int item) {
|
||||
g_screen->screenMessage("\nNo place to Use them!\n");
|
||||
}
|
||||
|
||||
bool Items::isMysticInInventory(int mystic) {
|
||||
/* FIXME: you could feasibly get more mystic weapons and armor if you
|
||||
have 8 party members and equip them all with everything,
|
||||
then search for Mystic Weapons/Armor again
|
||||
|
||||
or, you could just sell them all and search again. What an easy
|
||||
way to make some cash!
|
||||
|
||||
This would be a good candidate for an xu4 "extended" savegame
|
||||
format.
|
||||
*/
|
||||
if (mystic == WEAP_MYSTICSWORD)
|
||||
return g_ultima->_saveGame->_weapons[WEAP_MYSTICSWORD] > 0;
|
||||
else if (mystic == ARMR_MYSTICROBES)
|
||||
return g_ultima->_saveGame->_armor[ARMR_MYSTICROBES] > 0;
|
||||
else
|
||||
error("Invalid mystic item was tested in isMysticInInventory()");
|
||||
return false;
|
||||
}
|
||||
|
||||
void Items::putMysticInInventory(int mystic) {
|
||||
g_context->_party->member(0)->awardXp(400);
|
||||
g_context->_party->adjustKarma(KA_FOUND_ITEM);
|
||||
if (mystic == WEAP_MYSTICSWORD)
|
||||
g_ultima->_saveGame->_weapons[WEAP_MYSTICSWORD] += 8;
|
||||
else if (mystic == ARMR_MYSTICROBES)
|
||||
g_ultima->_saveGame->_armor[ARMR_MYSTICROBES] += 8;
|
||||
else
|
||||
error("Invalid mystic item was added in putMysticInInventory()");
|
||||
g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0;
|
||||
}
|
||||
|
||||
bool Items::isWeaponInInventory(int weapon) {
|
||||
if (g_ultima->_saveGame->_weapons[weapon])
|
||||
return true;
|
||||
else {
|
||||
for (int i = 0; i < g_context->_party->size(); i++) {
|
||||
if (g_context->_party->member(i)->getWeapon()->getType() == weapon)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Items::putWeaponInInventory(int weapon) {
|
||||
g_ultima->_saveGame->_weapons[weapon]++;
|
||||
}
|
||||
|
||||
void Items::useTelescope(int notused) {
|
||||
g_screen->screenMessage("You see a knob\non the telescope\nmarked A-P\nYou Select:");
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSConversationChoiceHelper telescopeHelper;
|
||||
telescopeHelper.updateChoices("abcdefghijklmnop ");
|
||||
#endif
|
||||
int choice = AlphaActionController::get('p', "You Select:");
|
||||
|
||||
if (choice == -1)
|
||||
return;
|
||||
|
||||
gamePeerCity(choice, nullptr);
|
||||
}
|
||||
|
||||
bool Items::isReagentInInventory(int reag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Items::putReagentInInventory(int reag) {
|
||||
g_context->_party->adjustKarma(KA_FOUND_ITEM);
|
||||
g_ultima->_saveGame->_reagents[reag] += xu4_random(8) + 2;
|
||||
g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0;
|
||||
|
||||
if (g_ultima->_saveGame->_reagents[reag] > 99) {
|
||||
g_ultima->_saveGame->_reagents[reag] = 99;
|
||||
g_screen->screenMessage("Dropped some!\n");
|
||||
}
|
||||
}
|
||||
|
||||
bool Items::itemConditionsMet(byte conditions) {
|
||||
if ((conditions & SC_NEWMOONS) &&
|
||||
!(g_ultima->_saveGame->_trammelPhase == 0 && g_ultima->_saveGame->_feluccaPhase == 0))
|
||||
return false;
|
||||
|
||||
if (conditions & SC_FULLAVATAR) {
|
||||
for (int i = 0; i < VIRT_MAX; i++) {
|
||||
if (g_ultima->_saveGame->_karma[i] != 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((conditions & SC_REAGENTDELAY) &&
|
||||
(g_ultima->_saveGame->_moves & 0xF0) == g_ultima->_saveGame->_lastReagent)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const ItemLocation *Items::itemAtLocation(const Map *map, const Coords &coords) {
|
||||
for (uint i = 0; i < N_ITEMS; i++) {
|
||||
if (!ITEMS[i]._locationLabel)
|
||||
continue;
|
||||
if (map->getLabel(ITEMS[i]._locationLabel) == coords &&
|
||||
itemConditionsMet(ITEMS[i]._conditions))
|
||||
return &(ITEMS[i]);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Items::itemUse(const Common::String &shortName) {
|
||||
const ItemLocation *item = nullptr;
|
||||
|
||||
for (uint i = 0; i < N_ITEMS; i++) {
|
||||
if (ITEMS[i]._shortName &&
|
||||
scumm_stricmp(ITEMS[i]._shortName, shortName.c_str()) == 0) {
|
||||
|
||||
item = &ITEMS[i];
|
||||
|
||||
/* item name found, see if we have that item in our inventory */
|
||||
if (!ITEMS[i]._isItemInInventory || (this->*(ITEMS[i]._isItemInInventory))(ITEMS[i]._data)) {
|
||||
|
||||
/* use the item, if we can! */
|
||||
if (!item || !item->_useItem)
|
||||
g_screen->screenMessage("\nNot a Usable item!\n");
|
||||
else
|
||||
(this->*(item->_useItem))(ITEMS[i]._data);
|
||||
} else
|
||||
g_screen->screenMessage("\nNone owned!\n");
|
||||
|
||||
/* we found the item, no need to keep searching */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* item was not found */
|
||||
if (!item)
|
||||
g_screen->screenMessage("\nNot a Usable item!\n");
|
||||
}
|
||||
|
||||
void Items::itemHandleStones(const Common::String &color) {
|
||||
bool found = false;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (scumm_stricmp(color.c_str(), getStoneName((Virtue)i)) == 0 &&
|
||||
isStoneInInventory(1 << i)) {
|
||||
found = true;
|
||||
itemUse(color.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
g_screen->screenMessage("\nNone owned!\n");
|
||||
stoneMask = 0; /* make sure stone mask is reset */
|
||||
}
|
||||
}
|
||||
|
||||
bool Items::isAbyssOpened(const Portal *p) {
|
||||
/* make sure the bell, book and candle have all been used */
|
||||
int items = g_ultima->_saveGame->_items;
|
||||
int isopened = (items & ITEM_BELL_USED) && (items & ITEM_BOOK_USED) && (items & ITEM_CANDLE_USED);
|
||||
|
||||
if (!isopened)
|
||||
g_screen->screenMessage("Enter Can't!\n");
|
||||
return isopened;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
150
engines/ultima/ultima4/game/item.h
Normal file
150
engines/ultima/ultima4/game/item.h
Normal file
@@ -0,0 +1,150 @@
|
||||
/* 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_GAME_ITEM_H
|
||||
#define ULTIMA4_GAME_ITEM_H
|
||||
|
||||
#include "ultima/ultima4/core/types.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class Coords;
|
||||
class Map;
|
||||
struct Portal;
|
||||
|
||||
enum SearchCondition {
|
||||
SC_NONE = 0x00,
|
||||
SC_NEWMOONS = 0x01,
|
||||
SC_FULLAVATAR = 0x02,
|
||||
SC_REAGENTDELAY = 0x04
|
||||
};
|
||||
|
||||
class Items;
|
||||
typedef bool (Items::*IsInInventoryProc)(int item);
|
||||
typedef void (Items::*InventoryActionProc)(int item);
|
||||
|
||||
struct
|
||||
#ifndef NO_CXX11_ALIGNAS
|
||||
alignas(8)
|
||||
#endif
|
||||
ItemLocation {
|
||||
const char *_name;
|
||||
const char *_shortName;
|
||||
const char *_locationLabel;
|
||||
IsInInventoryProc _isItemInInventory;
|
||||
InventoryActionProc _putItemInInventory;
|
||||
InventoryActionProc _useItem;
|
||||
int _data;
|
||||
byte _conditions;
|
||||
};
|
||||
|
||||
typedef void (*DestroyAllCreaturesCallback)();
|
||||
#define N_ITEMS 34
|
||||
|
||||
class Items {
|
||||
private:
|
||||
|
||||
static const ItemLocation ITEMS[N_ITEMS];
|
||||
DestroyAllCreaturesCallback destroyAllCreaturesCallback;
|
||||
int needStoneNames;
|
||||
byte stoneMask;
|
||||
private:
|
||||
bool isRuneInInventory(int virt);
|
||||
void putRuneInInventory(int virt);
|
||||
bool isStoneInInventory(int virt);
|
||||
void putStoneInInventory(int virt);
|
||||
bool isItemInInventory(int item);
|
||||
bool isSkullInInventory(int item);
|
||||
void putItemInInventory(int item);
|
||||
|
||||
/**
|
||||
* Use bell, book, or candle on the entrance to the Abyss
|
||||
*/
|
||||
void useBBC(int item);
|
||||
|
||||
/**
|
||||
* Uses the silver horn
|
||||
*/
|
||||
void useHorn(int item);
|
||||
|
||||
/**
|
||||
* Uses the wheel (if on board a ship)
|
||||
*/
|
||||
void useWheel(int item);
|
||||
|
||||
/**
|
||||
* Uses or destroys the skull of Mondain
|
||||
*/
|
||||
void useSkull(int item);
|
||||
|
||||
/**
|
||||
* Handles using the virtue stones in dungeon altar rooms and on dungeon altars
|
||||
*/
|
||||
void useStone(int item);
|
||||
void useKey(int item);
|
||||
bool isMysticInInventory(int mystic);
|
||||
void putMysticInInventory(int mystic);
|
||||
bool isWeaponInInventory(int weapon);
|
||||
void putWeaponInInventory(int weapon);
|
||||
void useTelescope(int notused);
|
||||
bool isReagentInInventory(int reag);
|
||||
void putReagentInInventory(int reag);
|
||||
|
||||
/**
|
||||
* Handles naming of stones when used
|
||||
*/
|
||||
void itemHandleStones(const Common::String &color);
|
||||
|
||||
/**
|
||||
* Returns true if the specified conditions are met to be able to get the item
|
||||
*/
|
||||
bool itemConditionsMet(byte conditions);
|
||||
public:
|
||||
Items();
|
||||
~Items();
|
||||
|
||||
void setDestroyAllCreaturesCallback(DestroyAllCreaturesCallback callback);
|
||||
|
||||
/**
|
||||
* Returns an item location record if a searchable object exists at
|
||||
* the given location. nullptr is returned if nothing is there.
|
||||
*/
|
||||
const ItemLocation *itemAtLocation(const Map *map, const Coords &coords);
|
||||
|
||||
/**
|
||||
* Uses the item indicated by 'shortname'
|
||||
*/
|
||||
void itemUse(const Common::String &shortName);
|
||||
|
||||
/**
|
||||
* Checks to see if the abyss was opened
|
||||
*/
|
||||
static bool isAbyssOpened(const Portal *p);
|
||||
};
|
||||
|
||||
extern Items *g_items;
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
77
engines/ultima/ultima4/game/moongate.cpp
Normal file
77
engines/ultima/ultima4/game/moongate.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/* 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/coords.h"
|
||||
#include "ultima/ultima4/game/moongate.h"
|
||||
#include "ultima/ultima4/core/types.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
Moongates *g_moongates;
|
||||
|
||||
Moongates::Moongates() {
|
||||
g_moongates = this;
|
||||
}
|
||||
|
||||
Moongates::~Moongates() {
|
||||
g_moongates = nullptr;
|
||||
}
|
||||
|
||||
void Moongates::add(int phase, const Coords &coords) {
|
||||
if (contains(phase))
|
||||
error("Error: A moongate for phase %d already exists", phase);
|
||||
|
||||
(*this)[phase] = coords;
|
||||
}
|
||||
|
||||
const Coords *Moongates::getGateCoordsForPhase(int phase) {
|
||||
iterator moongate;
|
||||
|
||||
moongate = find(phase);
|
||||
if (moongate != end())
|
||||
return &moongate->_value;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Moongates::findActiveGateAt(int trammel, int felucca, const Coords &src, Coords &dest) {
|
||||
const Coords *moongate_coords;
|
||||
|
||||
moongate_coords = getGateCoordsForPhase(trammel);
|
||||
if (moongate_coords && (src == *moongate_coords)) {
|
||||
moongate_coords = getGateCoordsForPhase(felucca);
|
||||
if (moongate_coords) {
|
||||
dest = *moongate_coords;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Moongates::isEntryToShrineOfSpirituality(int trammel, int felucca) {
|
||||
return (trammel == 4 && felucca == 4) ? true : false;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
54
engines/ultima/ultima4/game/moongate.h
Normal file
54
engines/ultima/ultima4/game/moongate.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_GAME_MOONGATE_H
|
||||
#define ULTIMA4_GAME_MOONGATE_H
|
||||
|
||||
#include "ultima/ultima4/core/coords.h"
|
||||
#include "common/hashmap.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class Moongates : public Common::HashMap<int, Coords> {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Moongates();
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Moongates();
|
||||
|
||||
void add(int phase, const Coords &coords);
|
||||
const Coords *getGateCoordsForPhase(int phase);
|
||||
bool findActiveGateAt(int trammel, int felucca, const Coords &src, Coords &dest);
|
||||
bool isEntryToShrineOfSpirituality(int trammel, int felucca);
|
||||
};
|
||||
|
||||
extern Moongates *g_moongates;
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
178
engines/ultima/ultima4/game/names.cpp
Normal file
178
engines/ultima/ultima4/game/names.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
/* 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/game/names.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
const char *getClassName(ClassType klass) {
|
||||
switch (klass) {
|
||||
case CLASS_MAGE:
|
||||
return "Mage";
|
||||
case CLASS_BARD:
|
||||
return "Bard";
|
||||
case CLASS_FIGHTER:
|
||||
return "Fighter";
|
||||
case CLASS_DRUID:
|
||||
return "Druid";
|
||||
case CLASS_TINKER:
|
||||
return "Tinker";
|
||||
case CLASS_PALADIN:
|
||||
return "Paladin";
|
||||
case CLASS_RANGER:
|
||||
return "Ranger";
|
||||
case CLASS_SHEPHERD:
|
||||
return "Shepherd";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
const char *getReagentName(Reagent reagent) {
|
||||
static const char *const reagentNames[] = {
|
||||
"Sulfur Ash", "Ginseng", "Garlic",
|
||||
"Spider Silk", "Blood Moss", "Black Pearl",
|
||||
"Nightshade", "Mandrake"
|
||||
};
|
||||
|
||||
if (reagent < REAG_MAX)
|
||||
return reagentNames[reagent - REAG_ASH];
|
||||
else
|
||||
return "???";
|
||||
}
|
||||
|
||||
const char *getVirtueName(Virtue virtue) {
|
||||
static const char *const virtueNames[] = {
|
||||
"Honesty", "Compassion", "Valor",
|
||||
"Justice", "Sacrifice", "Honor",
|
||||
"Spirituality", "Humility"
|
||||
};
|
||||
|
||||
if (virtue < 8)
|
||||
return virtueNames[virtue - VIRT_HONESTY];
|
||||
else
|
||||
return "???";
|
||||
}
|
||||
|
||||
const char *getBaseVirtueName(int virtueMask) {
|
||||
if (virtueMask == VIRT_TRUTH) return "Truth";
|
||||
else if (virtueMask == VIRT_LOVE) return "Love";
|
||||
else if (virtueMask == VIRT_COURAGE) return "Courage";
|
||||
else if (virtueMask == (VIRT_TRUTH | VIRT_LOVE)) return "Truth and Love";
|
||||
else if (virtueMask == (VIRT_LOVE | VIRT_COURAGE)) return "Love and Courage";
|
||||
else if (virtueMask == (VIRT_COURAGE | VIRT_TRUTH)) return "Courage and Truth";
|
||||
else if (virtueMask == (VIRT_TRUTH | VIRT_LOVE | VIRT_COURAGE)) return "Truth, Love and Courage";
|
||||
else return "???";
|
||||
}
|
||||
|
||||
int getBaseVirtues(Virtue virtue) {
|
||||
switch (virtue) {
|
||||
case VIRT_HONESTY:
|
||||
return VIRT_TRUTH;
|
||||
case VIRT_COMPASSION:
|
||||
return VIRT_LOVE;
|
||||
case VIRT_VALOR:
|
||||
return VIRT_COURAGE;
|
||||
case VIRT_JUSTICE:
|
||||
return VIRT_TRUTH | VIRT_LOVE;
|
||||
case VIRT_SACRIFICE:
|
||||
return VIRT_LOVE | VIRT_COURAGE;
|
||||
case VIRT_HONOR:
|
||||
return VIRT_COURAGE | VIRT_TRUTH;
|
||||
case VIRT_SPIRITUALITY:
|
||||
return VIRT_TRUTH | VIRT_LOVE | VIRT_COURAGE;
|
||||
case VIRT_HUMILITY:
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const char *getVirtueAdjective(Virtue virtue) {
|
||||
static const char *const virtueAdjectives[] = {
|
||||
"honest",
|
||||
"compassionate",
|
||||
"valiant",
|
||||
"just",
|
||||
"sacrificial",
|
||||
"honorable",
|
||||
"spiritual",
|
||||
"humble"
|
||||
};
|
||||
|
||||
if (virtue < 8)
|
||||
return virtueAdjectives[virtue - VIRT_HONESTY];
|
||||
else
|
||||
return "???";
|
||||
}
|
||||
|
||||
const char *getStoneName(Virtue virtue) {
|
||||
static const char *const virtueNames[] = {
|
||||
"Blue", "Yellow", "Red",
|
||||
"Green", "Orange", "Purple",
|
||||
"White", "Black"
|
||||
};
|
||||
|
||||
if (virtue < VIRT_MAX)
|
||||
return virtueNames[virtue - VIRT_HONESTY];
|
||||
else
|
||||
return "???";
|
||||
}
|
||||
|
||||
const char *getItemName(Item item) {
|
||||
switch (item) {
|
||||
case ITEM_SKULL:
|
||||
return "Skull";
|
||||
case ITEM_CANDLE:
|
||||
return "Candle";
|
||||
case ITEM_BOOK:
|
||||
return "Book";
|
||||
case ITEM_BELL:
|
||||
return "Bell";
|
||||
case ITEM_KEY_C:
|
||||
return "Courage";
|
||||
case ITEM_KEY_L:
|
||||
return "Love";
|
||||
case ITEM_KEY_T:
|
||||
return "Truth";
|
||||
case ITEM_HORN:
|
||||
return "Horn";
|
||||
case ITEM_WHEEL:
|
||||
return "Wheel";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
const char *getDirectionName(Direction dir) {
|
||||
static const char *const directionNames[] = {
|
||||
"West", "North", "East", "South"
|
||||
};
|
||||
|
||||
if (dir >= DIR_WEST && dir <= DIR_SOUTH)
|
||||
return directionNames[dir - DIR_WEST];
|
||||
else
|
||||
return "???";
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
48
engines/ultima/ultima4/game/names.h
Normal file
48
engines/ultima/ultima4/game/names.h
Normal 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_GAME_NAMES_H
|
||||
#define ULTIMA4_GAME_NAMES_H
|
||||
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/map/direction.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/*
|
||||
* These routines convert the various enumerations for classes, reagents,
|
||||
* etc. into the textual representations used in the game.
|
||||
*/
|
||||
const char *getClassName(ClassType klass);
|
||||
const char *getReagentName(Reagent reagent);
|
||||
const char *getVirtueName(Virtue virtue);
|
||||
const char *getBaseVirtueName(int virtueMask);
|
||||
int getBaseVirtues(Virtue virtue);
|
||||
const char *getVirtueAdjective(Virtue virtue);
|
||||
const char *getStoneName(Virtue virtue);
|
||||
const char *getItemName(Item item);
|
||||
const char *getDirectionName(Direction dir);
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
66
engines/ultima/ultima4/game/object.cpp
Normal file
66
engines/ultima/ultima4/game/object.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
/* 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/game/object.h"
|
||||
#include "ultima/ultima4/map/map.h"
|
||||
#include "ultima/ultima4/gfx/screen.h"
|
||||
#include "ultima/ultima4/game/game.h"
|
||||
#include "common/algorithm.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
bool Object::setDirection(Direction d) {
|
||||
return _tile.setDirection(d);
|
||||
}
|
||||
|
||||
void Object::setMap(class Map *m) {
|
||||
if (Common::find(_maps.begin(), _maps.end(), m) == _maps.end())
|
||||
_maps.push_back(m);
|
||||
}
|
||||
|
||||
Map *Object::getMap() {
|
||||
if (_maps.empty())
|
||||
return nullptr;
|
||||
return _maps.back();
|
||||
}
|
||||
|
||||
void Object::remove() {
|
||||
uint size = _maps.size();
|
||||
uint i = 0;
|
||||
|
||||
for (auto *map : _maps) {
|
||||
if (i == size - 1)
|
||||
map->removeObject(this);
|
||||
else map->removeObject(this, false);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void Object::animateMovement() {
|
||||
//TODO abstract movement - also make screen.h and game.h not required
|
||||
g_screen->screenTileUpdate(&g_game->_mapArea, _prevCoords, false);
|
||||
if (g_screen->screenTileUpdate(&g_game->_mapArea, _coords, false))
|
||||
g_screen->screenWait(1);
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
147
engines/ultima/ultima4/game/object.h
Normal file
147
engines/ultima/ultima4/game/object.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/* 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_GAME_OBJECT_H
|
||||
#define ULTIMA4_GAME_OBJECT_H
|
||||
|
||||
#include "ultima/ultima4/core/coords.h"
|
||||
#include "ultima/ultima4/map/map_tile.h"
|
||||
#include "ultima/ultima4/core/types.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class Object;
|
||||
|
||||
typedef Common::List<Object *> ObjectDeque;
|
||||
|
||||
enum ObjectMovementBehavior {
|
||||
MOVEMENT_FIXED,
|
||||
MOVEMENT_WANDER,
|
||||
MOVEMENT_FOLLOW_AVATAR,
|
||||
MOVEMENT_ATTACK_AVATAR
|
||||
};
|
||||
|
||||
class Object {
|
||||
public:
|
||||
enum Type {
|
||||
UNKNOWN,
|
||||
CREATURE,
|
||||
PERSON
|
||||
};
|
||||
|
||||
Object(Type type = UNKNOWN) :
|
||||
_tile(0),
|
||||
_prevTile(0),
|
||||
_movementBehavior(MOVEMENT_FIXED),
|
||||
_objType(type),
|
||||
_focused(false),
|
||||
_visible(true),
|
||||
_animated(true) {
|
||||
}
|
||||
|
||||
virtual ~Object() {}
|
||||
|
||||
// Methods
|
||||
MapTile &getTile() {
|
||||
return _tile;
|
||||
}
|
||||
MapTile &getPrevTile() {
|
||||
return _prevTile;
|
||||
}
|
||||
const Coords &getCoords() const {
|
||||
return _coords;
|
||||
}
|
||||
const Coords &getPrevCoords() const {
|
||||
return _prevCoords;
|
||||
}
|
||||
ObjectMovementBehavior getMovementBehavior() const {
|
||||
return _movementBehavior;
|
||||
}
|
||||
Type getType() const {
|
||||
return _objType;
|
||||
}
|
||||
bool hasFocus() const {
|
||||
return _focused;
|
||||
}
|
||||
bool isVisible() const {
|
||||
return _visible;
|
||||
}
|
||||
bool isAnimated() const {
|
||||
return _animated;
|
||||
}
|
||||
|
||||
void setTile(MapTile t) {
|
||||
_tile = t;
|
||||
}
|
||||
void setTile(Tile *t) {
|
||||
_tile = t->getId();
|
||||
}
|
||||
void setPrevTile(MapTile t) {
|
||||
_prevTile = t;
|
||||
}
|
||||
void setCoords(Coords c) {
|
||||
_prevCoords = _coords;
|
||||
_coords = c;
|
||||
}
|
||||
void setPrevCoords(Coords c) {
|
||||
_prevCoords = c;
|
||||
}
|
||||
void setMovementBehavior(ObjectMovementBehavior b) {
|
||||
_movementBehavior = b;
|
||||
}
|
||||
void setType(Type t) {
|
||||
_objType = t;
|
||||
}
|
||||
void setFocus(bool f = true) {
|
||||
_focused = f;
|
||||
}
|
||||
void setVisible(bool v = true) {
|
||||
_visible = v;
|
||||
}
|
||||
void setAnimated(bool a = true) {
|
||||
_animated = a;
|
||||
}
|
||||
|
||||
void setMap(class Map *m);
|
||||
Map *getMap();
|
||||
void remove(); /**< Removes itself from any maps that it is a part of */
|
||||
|
||||
bool setDirection(Direction d);
|
||||
|
||||
void animateMovement();
|
||||
|
||||
// Properties
|
||||
protected:
|
||||
MapTile _tile, _prevTile;
|
||||
Coords _coords, _prevCoords;
|
||||
ObjectMovementBehavior _movementBehavior;
|
||||
Type _objType;
|
||||
Common::List<class Map *> _maps; /**< A list of maps this object is a part of */
|
||||
|
||||
bool _focused;
|
||||
bool _visible;
|
||||
bool _animated;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
613
engines/ultima/ultima4/game/person.cpp
Normal file
613
engines/ultima/ultima4/game/person.cpp
Normal file
@@ -0,0 +1,613 @@
|
||||
/* 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/game/person.h"
|
||||
#include "ultima/ultima4/game/names.h"
|
||||
#include "ultima/ultima4/game/player.h"
|
||||
#include "ultima/ultima4/views/stats.h"
|
||||
#include "ultima/ultima4/game/context.h"
|
||||
#include "ultima/ultima4/game/script.h"
|
||||
#include "ultima/ultima4/controllers/read_choice_controller.h"
|
||||
#include "ultima/ultima4/controllers/read_int_controller.h"
|
||||
#include "ultima/ultima4/controllers/read_player_controller.h"
|
||||
#include "ultima/ultima4/conversation/conversation.h"
|
||||
#include "ultima/ultima4/core/config.h"
|
||||
#include "ultima/ultima4/core/settings.h"
|
||||
#include "ultima/ultima4/core/types.h"
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
#include "ultima/ultima4/events/event_handler.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/map/city.h"
|
||||
#include "ultima/ultima4/map/location.h"
|
||||
#include "ultima/ultima4/sound/music.h"
|
||||
#include "ultima/ultima4/ultima4.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
int chars_needed(const char *s, int columnmax, int linesdesired, int *real_lines);
|
||||
|
||||
/**
|
||||
* Returns true of the object that 'punknown' points
|
||||
* to is a person object
|
||||
*/
|
||||
bool isPerson(Object *punknown) {
|
||||
Person *p;
|
||||
if ((p = dynamic_cast<Person *>(punknown)) != nullptr)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a piece of response text into screen-sized chunks.
|
||||
*/
|
||||
Common::List<Common::String> replySplit(const Common::String &text) {
|
||||
Common::String str = text;
|
||||
int pos, real_lines;
|
||||
Common::List<Common::String> reply;
|
||||
|
||||
/* skip over any initial newlines */
|
||||
if ((pos = str.find("\n\n")) == 0)
|
||||
str = str.substr(pos + 1);
|
||||
|
||||
uint num_chars = chars_needed(str.c_str(), TEXT_AREA_W, TEXT_AREA_H, &real_lines);
|
||||
|
||||
/* we only have one chunk, no need to split it up */
|
||||
uint len = str.size();
|
||||
if (num_chars == len)
|
||||
reply.push_back(str);
|
||||
else {
|
||||
Common::String pre = str.substr(0, num_chars);
|
||||
|
||||
/* add the first chunk to the list */
|
||||
reply.push_back(pre);
|
||||
/* skip over any initial newlines */
|
||||
if ((pos = str.find("\n\n")) == 0)
|
||||
str = str.substr(pos + 1);
|
||||
|
||||
while (num_chars != str.size()) {
|
||||
/* go to the rest of the text */
|
||||
str = str.substr(num_chars);
|
||||
/* skip over any initial newlines */
|
||||
if ((pos = str.find("\n\n")) == 0)
|
||||
str = str.substr(pos + 1);
|
||||
|
||||
/* find the next chunk and add it */
|
||||
num_chars = chars_needed(str.c_str(), TEXT_AREA_W, TEXT_AREA_H, &real_lines);
|
||||
pre = str.substr(0, num_chars);
|
||||
|
||||
reply.push_back(pre);
|
||||
}
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
Person::Person(MapTile tile) : Creature(tile), _start(0, 0) {
|
||||
setType(Object::PERSON);
|
||||
_dialogue = nullptr;
|
||||
_npcType = NPC_EMPTY;
|
||||
}
|
||||
|
||||
Person::Person(const Person *p) : Creature(p->_tile) {
|
||||
*this = *p;
|
||||
}
|
||||
|
||||
bool Person::canConverse() const {
|
||||
return isVendor() || _dialogue != nullptr;
|
||||
}
|
||||
|
||||
bool Person::isVendor() const {
|
||||
return
|
||||
_npcType >= NPC_VENDOR_WEAPONS &&
|
||||
_npcType <= NPC_VENDOR_STABLE;
|
||||
}
|
||||
|
||||
Common::String Person::getName() const {
|
||||
if (_dialogue)
|
||||
return _dialogue->getName();
|
||||
else if (_npcType == NPC_EMPTY)
|
||||
return Creature::getName();
|
||||
else
|
||||
return "(unnamed person)";
|
||||
}
|
||||
|
||||
void Person::goToStartLocation() {
|
||||
setCoords(_start);
|
||||
}
|
||||
|
||||
void Person::setDialogue(Dialogue *d) {
|
||||
_dialogue = d;
|
||||
if (_tile.getTileType()->getName() == "beggar")
|
||||
_npcType = NPC_TALKER_BEGGAR;
|
||||
else if (_tile.getTileType()->getName() == "guard")
|
||||
_npcType = NPC_TALKER_GUARD;
|
||||
else
|
||||
_npcType = NPC_TALKER;
|
||||
}
|
||||
|
||||
void Person::setNpcType(PersonNpcType t) {
|
||||
_npcType = t;
|
||||
assertMsg(!isVendor() || _dialogue == nullptr, "vendor has dialogue");
|
||||
}
|
||||
|
||||
Common::List<Common::String> Person::getConversationText(Conversation *cnv, const char *inquiry) {
|
||||
Common::String text;
|
||||
|
||||
/*
|
||||
* a convsation with a vendor
|
||||
*/
|
||||
if (isVendor()) {
|
||||
static const Common::String ids[] = {
|
||||
"Weapons", "Armor", "Food", "Tavern", "Reagents", "Healer", "Inn", "Guild", "Stable"
|
||||
};
|
||||
Script *script = cnv->_script;
|
||||
|
||||
/**
|
||||
* We aren't currently running a script, load the appropriate one!
|
||||
*/
|
||||
if (cnv->_state == Conversation::INTRO) {
|
||||
// unload the previous script if it wasn't already unloaded
|
||||
if (script->getState() != Script::STATE_UNLOADED)
|
||||
script->unload();
|
||||
script->load("vendorScript.xml", ids[_npcType - NPC_VENDOR_WEAPONS], "vendor", g_context->_location->_map->getName());
|
||||
script->run("intro");
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSConversationChoiceHelper choiceDialog;
|
||||
#endif
|
||||
while (script->getState() != Script::STATE_DONE) {
|
||||
// Gather input for the script
|
||||
if (script->getState() == Script::STATE_INPUT) {
|
||||
switch (script->getInputType()) {
|
||||
case Script::INPUT_CHOICE: {
|
||||
const Common::String &choices = script->getChoices();
|
||||
// Get choice
|
||||
#ifdef IOS_ULTIMA4
|
||||
choiceDialog.updateChoices(choices, script->getTarget(), npcType);
|
||||
#endif
|
||||
char val = ReadChoiceController::get(choices);
|
||||
if (Common::isSpace(val) || val == '\033')
|
||||
script->unsetVar(script->getInputName());
|
||||
else {
|
||||
Common::String s_val;
|
||||
s_val = val;
|
||||
script->setVar(script->getInputName(), s_val);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Script::INPUT_KEYPRESS:
|
||||
ReadChoiceController::get(" \015\033");
|
||||
break;
|
||||
|
||||
case Script::INPUT_NUMBER: {
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSConversationHelper ipadNumberInput;
|
||||
ipadNumberInput.beginConversation(U4IOS::UIKeyboardTypeNumberPad, "Amount?");
|
||||
#endif
|
||||
int val = ReadIntController::get(script->getInputMaxLen(), TEXT_AREA_X + g_context->_col, TEXT_AREA_Y + g_context->_line);
|
||||
script->setVar(script->getInputName(), val);
|
||||
}
|
||||
break;
|
||||
|
||||
case Script::INPUT_STRING: {
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::IOSConversationHelper ipadNumberInput;
|
||||
ipadNumberInput.beginConversation(U4IOS::UIKeyboardTypeDefault);
|
||||
#endif
|
||||
Common::String str = ReadStringController::get(script->getInputMaxLen(), TEXT_AREA_X + g_context->_col, TEXT_AREA_Y + g_context->_line);
|
||||
if (str.size()) {
|
||||
lowercase(str);
|
||||
script->setVar(script->getInputName(), str);
|
||||
} else script->unsetVar(script->getInputName());
|
||||
}
|
||||
break;
|
||||
|
||||
case Script::INPUT_PLAYER: {
|
||||
ReadPlayerController getPlayerCtrl;
|
||||
eventHandler->pushController(&getPlayerCtrl);
|
||||
int player = getPlayerCtrl.waitFor();
|
||||
if (player != -1) {
|
||||
Common::String player_str = xu4_to_string(player + 1);
|
||||
script->setVar(script->getInputName(), player_str);
|
||||
} else script->unsetVar(script->getInputName());
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
} // } switch
|
||||
|
||||
// Continue running the script!
|
||||
g_context->_line++;
|
||||
script->_continue();
|
||||
} // } if
|
||||
} // } while
|
||||
}
|
||||
|
||||
// Unload the script
|
||||
script->unload();
|
||||
cnv->_state = Conversation::DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* a conversation with a non-vendor
|
||||
*/
|
||||
else {
|
||||
text = "\n\n\n";
|
||||
|
||||
switch (cnv->_state) {
|
||||
case Conversation::INTRO:
|
||||
text = getIntro(cnv);
|
||||
break;
|
||||
|
||||
case Conversation::TALK:
|
||||
text += getResponse(cnv, inquiry) + "\n";
|
||||
break;
|
||||
|
||||
case Conversation::CONFIRMATION:
|
||||
assertMsg(_npcType == NPC_LORD_BRITISH, "invalid state: %d", cnv->_state);
|
||||
text += lordBritishGetQuestionResponse(cnv, inquiry);
|
||||
break;
|
||||
|
||||
case Conversation::ASK:
|
||||
case Conversation::ASKYESNO:
|
||||
assertMsg(_npcType != NPC_HAWKWIND, "invalid state for hawkwind conversation");
|
||||
text += talkerGetQuestionResponse(cnv, inquiry) + "\n";
|
||||
break;
|
||||
|
||||
case Conversation::GIVEBEGGAR:
|
||||
assertMsg(_npcType == NPC_TALKER_BEGGAR, "invalid npc type: %d", _npcType);
|
||||
text = beggarGetQuantityResponse(cnv, inquiry);
|
||||
break;
|
||||
|
||||
case Conversation::FULLHEAL:
|
||||
case Conversation::ADVANCELEVELS:
|
||||
/* handled elsewhere */
|
||||
break;
|
||||
|
||||
default:
|
||||
error("invalid state: %d", cnv->_state);
|
||||
}
|
||||
}
|
||||
|
||||
return replySplit(text);
|
||||
}
|
||||
|
||||
Common::String Person::getPrompt(Conversation *cnv) {
|
||||
if (isVendor())
|
||||
return "";
|
||||
|
||||
Common::String prompt;
|
||||
if (cnv->_state == Conversation::ASK)
|
||||
prompt = getQuestion(cnv);
|
||||
else if (cnv->_state == Conversation::GIVEBEGGAR)
|
||||
prompt = "How much? ";
|
||||
else if (cnv->_state == Conversation::CONFIRMATION)
|
||||
prompt = "\n\nHe asks: Art thou well?";
|
||||
else if (cnv->_state != Conversation::ASKYESNO)
|
||||
prompt = _dialogue->getPrompt();
|
||||
|
||||
return prompt;
|
||||
}
|
||||
|
||||
const char *Person::getChoices(Conversation *cnv) {
|
||||
if (isVendor())
|
||||
return cnv->_script->getChoices().c_str();
|
||||
|
||||
switch (cnv->_state) {
|
||||
case Conversation::CONFIRMATION:
|
||||
case Conversation::CONTINUEQUESTION:
|
||||
return "ny\015 \033";
|
||||
|
||||
case Conversation::PLAYER:
|
||||
return "012345678\015 \033";
|
||||
|
||||
default:
|
||||
error("invalid state: %d", cnv->_state);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::String Person::getIntro(Conversation *cnv) {
|
||||
if (_npcType == NPC_EMPTY) {
|
||||
cnv->_state = Conversation::DONE;
|
||||
return Common::String("Funny, no\nresponse!\n");
|
||||
}
|
||||
|
||||
// As far as I can tell, about 50% of the time they tell you their
|
||||
// name in the introduction
|
||||
Response *intro;
|
||||
if (xu4_random(2) == 0)
|
||||
intro = _dialogue->getIntro();
|
||||
else
|
||||
intro = _dialogue->getLongIntro();
|
||||
|
||||
cnv->_state = Conversation::TALK;
|
||||
Common::String text = processResponse(cnv, intro);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
Common::String Person::processResponse(Conversation *cnv, Response *response) {
|
||||
Common::String text;
|
||||
const Std::vector<ResponsePart> &parts = response->getParts();
|
||||
for (const auto &i : parts) {
|
||||
|
||||
// check for command triggers
|
||||
if (i.isCommand())
|
||||
runCommand(cnv, i);
|
||||
|
||||
// otherwise, append response part to reply
|
||||
else
|
||||
text += i;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
void Person::runCommand(Conversation *cnv, const ResponsePart &command) {
|
||||
if (command == g_responseParts->ASK) {
|
||||
cnv->_question = _dialogue->getQuestion();
|
||||
cnv->_state = Conversation::ASK;
|
||||
} else if (command == g_responseParts->END) {
|
||||
cnv->_state = Conversation::DONE;
|
||||
} else if (command == g_responseParts->ATTACK) {
|
||||
cnv->_state = Conversation::ATTACK;
|
||||
} else if (command == g_responseParts->BRAGGED) {
|
||||
g_context->_party->adjustKarma(KA_BRAGGED);
|
||||
} else if (command == g_responseParts->HUMBLE) {
|
||||
g_context->_party->adjustKarma(KA_HUMBLE);
|
||||
} else if (command == g_responseParts->ADVANCELEVELS) {
|
||||
cnv->_state = Conversation::ADVANCELEVELS;
|
||||
} else if (command == g_responseParts->HEALCONFIRM) {
|
||||
cnv->_state = Conversation::CONFIRMATION;
|
||||
} else if (command == g_responseParts->STARTMUSIC_LB) {
|
||||
g_music->lordBritish();
|
||||
} else if (command == g_responseParts->STARTMUSIC_HW) {
|
||||
g_music->hawkwind();
|
||||
} else if (command == g_responseParts->STOPMUSIC) {
|
||||
g_music->playMapMusic();
|
||||
} else if (command == g_responseParts->HAWKWIND) {
|
||||
g_context->_party->adjustKarma(KA_HAWKWIND);
|
||||
} else {
|
||||
error("unknown command trigger in dialogue response: %s\n", Common::String(command).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
Common::String Person::getResponse(Conversation *cnv, const char *inquiry) {
|
||||
Common::String reply;
|
||||
Virtue v;
|
||||
const ResponsePart &action = _dialogue->getAction();
|
||||
|
||||
reply = "\n";
|
||||
|
||||
/* Does the person take action during the conversation? */
|
||||
if (action == g_responseParts->END) {
|
||||
runCommand(cnv, action);
|
||||
return _dialogue->getPronoun() + " turns away!\n";
|
||||
} else if (action == g_responseParts->ATTACK) {
|
||||
runCommand(cnv, action);
|
||||
return Common::String("\n") + getName() + " says: On guard! Fool!";
|
||||
}
|
||||
|
||||
if (_npcType == NPC_TALKER_BEGGAR && scumm_strnicmp(inquiry, "give", 4) == 0) {
|
||||
reply.clear();
|
||||
cnv->_state = Conversation::GIVEBEGGAR;
|
||||
}
|
||||
|
||||
else if (scumm_strnicmp(inquiry, "join", 4) == 0 &&
|
||||
g_context->_party->canPersonJoin(getName(), &v)) {
|
||||
CannotJoinError join = g_context->_party->join(getName());
|
||||
|
||||
if (join == JOIN_SUCCEEDED) {
|
||||
reply += "I am honored to join thee!";
|
||||
g_context->_location->_map->removeObject(this);
|
||||
cnv->_state = Conversation::DONE;
|
||||
} else {
|
||||
reply += "Thou art not ";
|
||||
reply += (join == JOIN_NOT_VIRTUOUS) ? getVirtueAdjective(v) : "experienced";
|
||||
reply += " enough for me to join thee.";
|
||||
}
|
||||
}
|
||||
|
||||
else if ((*_dialogue)[inquiry]) {
|
||||
Dialogue::Keyword *kw = (*_dialogue)[inquiry];
|
||||
|
||||
reply = processResponse(cnv, kw->getResponse());
|
||||
}
|
||||
|
||||
else if (settings._debug && scumm_strnicmp(inquiry, "dump", 4) == 0) {
|
||||
Std::vector<Common::String> words = split(inquiry, " \t");
|
||||
if (words.size() <= 1)
|
||||
reply = _dialogue->dump("");
|
||||
else
|
||||
reply = _dialogue->dump(words[1]);
|
||||
}
|
||||
|
||||
else
|
||||
reply += processResponse(cnv, _dialogue->getDefaultAnswer());
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
Common::String Person::talkerGetQuestionResponse(Conversation *cnv, const char *answer) {
|
||||
bool valid = false;
|
||||
bool yes = false;
|
||||
char ans = tolower(answer[0]);
|
||||
|
||||
if (ans == 'y' || ans == 'n') {
|
||||
valid = true;
|
||||
yes = ans == 'y';
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
cnv->_state = Conversation::ASKYESNO;
|
||||
return "Yes or no!";
|
||||
}
|
||||
|
||||
cnv->_state = Conversation::TALK;
|
||||
return "\n" + processResponse(cnv, cnv->_question->getResponse(yes));
|
||||
}
|
||||
|
||||
Common::String Person::beggarGetQuantityResponse(Conversation *cnv, const char *response) {
|
||||
Common::String reply;
|
||||
|
||||
cnv->_quant = (int) strtol(response, nullptr, 10);
|
||||
cnv->_state = Conversation::TALK;
|
||||
|
||||
if (cnv->_quant > 0) {
|
||||
if (g_context->_party->donate(cnv->_quant)) {
|
||||
reply = "\n";
|
||||
reply += _dialogue->getPronoun();
|
||||
reply += " says: Oh Thank thee! I shall never forget thy kindness!\n";
|
||||
}
|
||||
|
||||
else
|
||||
reply = "\n\nThou hast not that much gold!\n";
|
||||
} else
|
||||
reply = "\n";
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
Common::String Person::lordBritishGetQuestionResponse(Conversation *cnv, const char *answer) {
|
||||
Common::String reply;
|
||||
|
||||
cnv->_state = Conversation::TALK;
|
||||
|
||||
if (tolower(answer[0]) == 'y') {
|
||||
reply = "Y\n\nHe says: That is good.\n";
|
||||
}
|
||||
|
||||
else if (tolower(answer[0]) == 'n') {
|
||||
reply = "N\n\nHe says: Let me heal thy wounds!\n";
|
||||
cnv->_state = Conversation::FULLHEAL;
|
||||
}
|
||||
|
||||
else
|
||||
reply = "\n\nThat I cannot\nhelp thee with.\n";
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
Common::String Person::getQuestion(Conversation *cnv) {
|
||||
return "\n" + cnv->_question->getText() + "\n\nYou say: ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of characters needed to get to
|
||||
* the next line of text (based on column width).
|
||||
*/
|
||||
int chars_to_next_line(const char *s, int columnmax) {
|
||||
int chars = -1;
|
||||
|
||||
if (strlen(s) > 0) {
|
||||
int lastbreak = columnmax;
|
||||
chars = 0;
|
||||
for (const char *str = s; *str; str++) {
|
||||
if (*str == '\n')
|
||||
return (str - s);
|
||||
else if (*str == ' ')
|
||||
lastbreak = (str - s);
|
||||
else if (++chars >= columnmax)
|
||||
return lastbreak;
|
||||
}
|
||||
}
|
||||
|
||||
return chars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of lines (of the maximum width given by
|
||||
* columnmax) in the Common::String.
|
||||
*/
|
||||
int linecount(const Common::String &s, int columnmax) {
|
||||
int lines = 0;
|
||||
unsigned ch = 0;
|
||||
while (ch < s.size()) {
|
||||
ch += chars_to_next_line(s.c_str() + ch, columnmax);
|
||||
if (ch < s.size())
|
||||
ch++;
|
||||
lines++;
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of characters needed to produce a
|
||||
* valid screen of text (given a column width and row height)
|
||||
*/
|
||||
int chars_needed(const char *s, int columnmax, int linesdesired, int *real_lines) {
|
||||
int chars = 0,
|
||||
totalChars = 0;
|
||||
|
||||
Common::String new_str = s;
|
||||
const char *str = new_str.c_str();
|
||||
|
||||
// try breaking text into paragraphs first
|
||||
Common::String text = s;
|
||||
Common::String paragraphs;
|
||||
uint pos;
|
||||
int lines = 0;
|
||||
while ((pos = text.find("\n\n")) < text.size()) {
|
||||
Common::String p = text.substr(0, pos);
|
||||
lines += linecount(p.c_str(), columnmax);
|
||||
if (lines <= linesdesired)
|
||||
paragraphs += p + "\n";
|
||||
else
|
||||
break;
|
||||
text = text.substr(pos + 1);
|
||||
}
|
||||
// Seems to be some sort of clang compilation bug in this code, that causes this addition
|
||||
// to not work correctly.
|
||||
int totalPossibleLines = lines + linecount(text.c_str(), columnmax);
|
||||
if (totalPossibleLines <= linesdesired)
|
||||
paragraphs += text;
|
||||
|
||||
if (!paragraphs.empty()) {
|
||||
*real_lines = lines;
|
||||
return paragraphs.size();
|
||||
} else {
|
||||
// reset variables and try another way
|
||||
lines = 1;
|
||||
}
|
||||
// gather all the line breaks
|
||||
while ((chars = chars_to_next_line(str, columnmax)) >= 0) {
|
||||
if (++lines >= linesdesired)
|
||||
break;
|
||||
|
||||
int num_to_move = chars;
|
||||
if (*(str + num_to_move) == '\n')
|
||||
num_to_move++;
|
||||
|
||||
totalChars += num_to_move;
|
||||
str += num_to_move;
|
||||
}
|
||||
|
||||
*real_lines = lines;
|
||||
return totalChars;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
109
engines/ultima/ultima4/game/person.h
Normal file
109
engines/ultima/ultima4/game/person.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/* 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_GAME_PERSON_H
|
||||
#define ULTIMA4_GAME_PERSON_H
|
||||
|
||||
#include "ultima/ultima4/game/creature.h"
|
||||
#include "ultima/ultima4/core/types.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class Conversation;
|
||||
class Dialogue;
|
||||
class Response;
|
||||
class ResponsePart;
|
||||
|
||||
typedef enum {
|
||||
NPC_EMPTY,
|
||||
NPC_TALKER,
|
||||
NPC_TALKER_BEGGAR,
|
||||
NPC_TALKER_GUARD,
|
||||
NPC_TALKER_COMPANION,
|
||||
NPC_VENDOR_WEAPONS,
|
||||
NPC_VENDOR_ARMOR,
|
||||
NPC_VENDOR_FOOD,
|
||||
NPC_VENDOR_TAVERN,
|
||||
NPC_VENDOR_REAGENTS,
|
||||
NPC_VENDOR_HEALER,
|
||||
NPC_VENDOR_INN,
|
||||
NPC_VENDOR_GUILD,
|
||||
NPC_VENDOR_STABLE,
|
||||
NPC_LORD_BRITISH,
|
||||
NPC_HAWKWIND,
|
||||
NPC_MAX
|
||||
} PersonNpcType;
|
||||
|
||||
class Person : public Creature {
|
||||
public:
|
||||
Person(MapTile tile);
|
||||
Person(const Person *p);
|
||||
|
||||
bool canConverse() const;
|
||||
bool isVendor() const;
|
||||
Common::String getName() const override;
|
||||
void goToStartLocation();
|
||||
void setDialogue(Dialogue *d);
|
||||
MapCoords &getStart() {
|
||||
return _start;
|
||||
}
|
||||
PersonNpcType getNpcType() const {
|
||||
return _npcType;
|
||||
}
|
||||
void setNpcType(PersonNpcType t);
|
||||
|
||||
Common::List<Common::String> getConversationText(Conversation *cnv, const char *inquiry);
|
||||
|
||||
/**
|
||||
* Get the prompt shown after each reply.
|
||||
*/
|
||||
Common::String getPrompt(Conversation *cnv);
|
||||
|
||||
/**
|
||||
* Returns the valid keyboard choices for a given conversation.
|
||||
*/
|
||||
const char *getChoices(Conversation *cnv);
|
||||
|
||||
Common::String getIntro(Conversation *cnv);
|
||||
Common::String processResponse(Conversation *cnv, Response *response);
|
||||
void runCommand(Conversation *cnv, const ResponsePart &command);
|
||||
Common::String getResponse(Conversation *cnv, const char *inquiry);
|
||||
Common::String talkerGetQuestionResponse(Conversation *cnv, const char *inquiry);
|
||||
Common::String beggarGetQuantityResponse(Conversation *cnv, const char *response);
|
||||
Common::String lordBritishGetQuestionResponse(Conversation *cnv, const char *answer);
|
||||
Common::String getQuestion(Conversation *cnv);
|
||||
|
||||
private:
|
||||
Dialogue *_dialogue;
|
||||
MapCoords _start;
|
||||
PersonNpcType _npcType;
|
||||
};
|
||||
|
||||
bool isPerson(Object *punknown);
|
||||
|
||||
Common::List<Common::String> replySplit(const Common::String &text);
|
||||
int linecount(const Common::String &s, int columnmax);
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
1172
engines/ultima/ultima4/game/player.cpp
Normal file
1172
engines/ultima/ultima4/game/player.cpp
Normal file
File diff suppressed because it is too large
Load Diff
449
engines/ultima/ultima4/game/player.h
Normal file
449
engines/ultima/ultima4/game/player.h
Normal file
@@ -0,0 +1,449 @@
|
||||
/* 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_GAME_PLAYER_H
|
||||
#define ULTIMA4_GAME_PLAYER_H
|
||||
|
||||
#include "ultima/ultima4/game/creature.h"
|
||||
#include "ultima/ultima4/map/direction.h"
|
||||
#include "ultima/ultima4/core/observable.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/game/script.h"
|
||||
#include "ultima/ultima4/map/tile.h"
|
||||
#include "ultima/ultima4/core/types.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class Armor;
|
||||
class Party;
|
||||
class Weapon;
|
||||
|
||||
typedef Std::vector<class PartyMember *> PartyMemberVector;
|
||||
|
||||
#define ALL_PLAYERS -1
|
||||
|
||||
enum KarmaAction {
|
||||
KA_FOUND_ITEM,
|
||||
KA_STOLE_CHEST,
|
||||
KA_GAVE_TO_BEGGAR,
|
||||
KA_GAVE_ALL_TO_BEGGAR,
|
||||
KA_BRAGGED,
|
||||
KA_HUMBLE,
|
||||
KA_HAWKWIND,
|
||||
KA_MEDITATION,
|
||||
KA_BAD_MANTRA,
|
||||
KA_ATTACKED_GOOD,
|
||||
KA_FLED_EVIL,
|
||||
KA_FLED_GOOD,
|
||||
KA_HEALTHY_FLED_EVIL,
|
||||
KA_KILLED_EVIL,
|
||||
KA_SPARED_GOOD,
|
||||
KA_DONATED_BLOOD,
|
||||
KA_DIDNT_DONATE_BLOOD,
|
||||
KA_CHEAT_REAGENTS,
|
||||
KA_DIDNT_CHEAT_REAGENTS,
|
||||
KA_USED_SKULL,
|
||||
KA_DESTROYED_SKULL
|
||||
};
|
||||
|
||||
enum HealType {
|
||||
HT_NONE,
|
||||
HT_CURE,
|
||||
HT_FULLHEAL,
|
||||
HT_RESURRECT,
|
||||
HT_HEAL,
|
||||
HT_CAMPHEAL,
|
||||
HT_INNHEAL
|
||||
};
|
||||
|
||||
enum InventoryItem {
|
||||
INV_NONE,
|
||||
INV_WEAPON,
|
||||
INV_ARMOR,
|
||||
INV_FOOD,
|
||||
INV_REAGENT,
|
||||
INV_GUILDITEM,
|
||||
INV_HORSE
|
||||
};
|
||||
|
||||
enum CannotJoinError {
|
||||
JOIN_SUCCEEDED,
|
||||
JOIN_NOT_EXPERIENCED,
|
||||
JOIN_NOT_VIRTUOUS
|
||||
};
|
||||
|
||||
enum EquipError {
|
||||
EQUIP_SUCCEEDED,
|
||||
EQUIP_NONE_LEFT,
|
||||
EQUIP_CLASS_RESTRICTED
|
||||
};
|
||||
|
||||
/**
|
||||
* PartyMember class
|
||||
*/
|
||||
class PartyMember : public Creature, public Script::Provider {
|
||||
public:
|
||||
PartyMember(Party *p, SaveGamePlayerRecord *pr);
|
||||
virtual ~PartyMember();
|
||||
|
||||
/**
|
||||
* Notify the party that this player has changed somehow
|
||||
*/
|
||||
void notifyOfChange();
|
||||
|
||||
/**
|
||||
* Used to translate script values into something useful
|
||||
*/
|
||||
Common::String translate(Std::vector<Common::String> &parts) override;
|
||||
|
||||
// Accessor methods
|
||||
int getHp() const override;
|
||||
int getMaxHp() const {
|
||||
return _player->_hpMax;
|
||||
}
|
||||
int getExp() const {
|
||||
return _player->_xp;
|
||||
}
|
||||
int getStr() const {
|
||||
return _player->_str;
|
||||
}
|
||||
int getDex() const {
|
||||
return _player->_dex;
|
||||
}
|
||||
int getInt() const {
|
||||
return _player->_intel;
|
||||
}
|
||||
int getMp() const {
|
||||
return _player->_mp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the most magic points a character could have
|
||||
* given his class and intelligence.
|
||||
*/
|
||||
int getMaxMp() const;
|
||||
|
||||
const Weapon *getWeapon() const;
|
||||
const Armor *getArmor() const;
|
||||
Common::String getName() const override;
|
||||
SexType getSex() const;
|
||||
ClassType getClass() const;
|
||||
CreatureStatus getState() const override;
|
||||
|
||||
/**
|
||||
* Determine what level a character has.
|
||||
*/
|
||||
int getRealLevel() const;
|
||||
|
||||
/**
|
||||
* Determine the highest level a character could have with the number
|
||||
* of experience points he has.
|
||||
*/
|
||||
int getMaxLevel() const;
|
||||
|
||||
/**
|
||||
* Adds a status effect to the player
|
||||
*/
|
||||
void addStatus(StatusType status) override;
|
||||
|
||||
/**
|
||||
* Adjusts the player's mp by 'pts'
|
||||
*/
|
||||
void adjustMp(int pts);
|
||||
|
||||
/**
|
||||
* Advances the player to the next level if they have enough experience
|
||||
*/
|
||||
void advanceLevel();
|
||||
|
||||
/**
|
||||
* Apply an effect to the party member
|
||||
*/
|
||||
void applyEffect(TileEffect effect);
|
||||
|
||||
/**
|
||||
* Award a player experience points. Maxs out the players xp at 9999.
|
||||
*/
|
||||
void awardXp(int xp);
|
||||
|
||||
/**
|
||||
* Perform a certain type of healing on the party member
|
||||
*/
|
||||
bool heal(HealType type);
|
||||
|
||||
/**
|
||||
* Remove status effects from the party member
|
||||
*/
|
||||
void removeStatus(StatusType status) override;
|
||||
|
||||
void setHp(int hp) override;
|
||||
void setMp(int mp);
|
||||
EquipError setArmor(const Armor *a);
|
||||
EquipError setWeapon(const Weapon *w);
|
||||
|
||||
/**
|
||||
* Applies damage to a player, and changes status to dead if hit
|
||||
* points drop below zero.
|
||||
*
|
||||
* Byplayer is ignored for now, since it should always be false for U4. (Is
|
||||
* there anything special about being killed by a party member in U5?) Also
|
||||
* keeps interface consistent for virtual base function Creature::applydamage()
|
||||
*/
|
||||
bool applyDamage(int damage, bool byplayer = false) override;
|
||||
int getAttackBonus() const override;
|
||||
int getDefense() const override;
|
||||
bool dealDamage(Creature *m, int damage) override;
|
||||
|
||||
/**
|
||||
* Calculate damage for an attack.
|
||||
*/
|
||||
int getDamage();
|
||||
|
||||
/**
|
||||
* Returns the tile that will be displayed when the party
|
||||
* member's attack hits
|
||||
*/
|
||||
const Common::String &getHitTile() const override;
|
||||
|
||||
/**
|
||||
* Returns the tile that will be displayed when the party
|
||||
* member's attack fails
|
||||
*/
|
||||
const Common::String &getMissTile() const override;
|
||||
bool isDead();
|
||||
bool isDisabled();
|
||||
|
||||
/**
|
||||
* Lose the equipped weapon for the player (flaming oil, ranged daggers, etc.)
|
||||
* Returns the number of weapons left of that type, including the one in
|
||||
* the players hand
|
||||
*/
|
||||
int loseWeapon();
|
||||
|
||||
/**
|
||||
* Put the party member to sleep
|
||||
*/
|
||||
void putToSleep() override;
|
||||
|
||||
/**
|
||||
* Wakes up the party member
|
||||
*/
|
||||
void wakeUp() override;
|
||||
|
||||
protected:
|
||||
static MapTile tileForClass(int klass);
|
||||
|
||||
SaveGamePlayerRecord *_player;
|
||||
class Party *_party;
|
||||
};
|
||||
|
||||
/**
|
||||
* Party class
|
||||
*/
|
||||
class PartyEvent {
|
||||
public:
|
||||
enum Type {
|
||||
GENERIC,
|
||||
LOST_EIGHTH,
|
||||
ADVANCED_LEVEL,
|
||||
STARVING,
|
||||
TRANSPORT_CHANGED,
|
||||
PLAYER_KILLED,
|
||||
ACTIVE_PLAYER_CHANGED,
|
||||
MEMBER_JOINED,
|
||||
PARTY_REVIVED,
|
||||
INVENTORY_ADDED
|
||||
};
|
||||
|
||||
PartyEvent(Type type, PartyMember *partyMember) : _type(type), _player(partyMember) { }
|
||||
|
||||
Type _type;
|
||||
PartyMember *_player;
|
||||
};
|
||||
|
||||
typedef Std::vector<PartyMember *> PartyMemberVector;
|
||||
|
||||
class Party : public Observable<Party *, PartyEvent &>, public Script::Provider {
|
||||
friend class PartyMember;
|
||||
public:
|
||||
Party(SaveGame *saveGame);
|
||||
virtual ~Party();
|
||||
|
||||
/**
|
||||
* Notify the party that something about it has changed
|
||||
*/
|
||||
void notifyOfChange(PartyMember *partyMember = 0, PartyEvent::Type = PartyEvent::GENERIC);
|
||||
|
||||
// Used to translate script values into something useful
|
||||
Common::String translate(Std::vector<Common::String> &parts) override;
|
||||
|
||||
void adjustFood(int food);
|
||||
void adjustGold(int gold);
|
||||
|
||||
/**
|
||||
* Adjusts the avatar's karma level for the given action. Notify
|
||||
* observers with a lost eighth event if the player has lost
|
||||
* avatarhood.
|
||||
*/
|
||||
void adjustKarma(KarmaAction action);
|
||||
|
||||
/**
|
||||
* Apply effects to the entire party
|
||||
*/
|
||||
void applyEffect(TileEffect effect);
|
||||
|
||||
/**
|
||||
* Attempt to elevate in the given virtue
|
||||
*/
|
||||
bool attemptElevation(Virtue virtue);
|
||||
|
||||
/**
|
||||
* Burns a torch's duration down a certain number of turns
|
||||
*/
|
||||
void burnTorch(int turns = 1);
|
||||
|
||||
/**
|
||||
* Returns true if the party can enter the shrine
|
||||
*/
|
||||
bool canEnterShrine(Virtue virtue);
|
||||
|
||||
/**
|
||||
* Returns true if the person can join the party
|
||||
*/
|
||||
bool canPersonJoin(Common::String name, Virtue *v);
|
||||
|
||||
/**
|
||||
* Damages the party's ship
|
||||
*/
|
||||
void damageShip(uint pts);
|
||||
|
||||
/**
|
||||
* Donates 'quantity' gold. Returns true if the donation succeeded,
|
||||
* or false if there was not enough gold to make the donation
|
||||
*/
|
||||
bool donate(int quantity);
|
||||
|
||||
/**
|
||||
* Ends the party's turn
|
||||
*/
|
||||
void endTurn();
|
||||
|
||||
/**
|
||||
* Adds a chest worth of gold to the party's inventory
|
||||
*/
|
||||
int getChest();
|
||||
|
||||
/**
|
||||
* Returns the number of turns a currently lit torch will last (or 0 if no torch lit)
|
||||
*/
|
||||
int getTorchDuration() const;
|
||||
|
||||
/**
|
||||
* Heals the ship's hull strength by 'pts' points
|
||||
*/
|
||||
void healShip(uint pts);
|
||||
|
||||
/**
|
||||
* Returns true if the balloon is currently in the air
|
||||
*/
|
||||
bool isFlying() const;
|
||||
|
||||
/**
|
||||
* Whether or not the party can make an action.
|
||||
*/
|
||||
bool isImmobilized();
|
||||
|
||||
/**
|
||||
* Whether or not all the party members are dead.
|
||||
*/
|
||||
bool isDead();
|
||||
|
||||
/**
|
||||
* Returns true if the person with that name
|
||||
* is already in the party
|
||||
*/
|
||||
bool isPersonJoined(Common::String name);
|
||||
|
||||
/**
|
||||
* Attempts to add the person to the party.
|
||||
* Returns JOIN_SUCCEEDED if successful.
|
||||
*/
|
||||
CannotJoinError join(Common::String name);
|
||||
|
||||
/**
|
||||
* Lights a torch with a default duration of 100
|
||||
*/
|
||||
bool lightTorch(int duration = 100, bool loseTorch = true);
|
||||
|
||||
/**
|
||||
* Extinguishes a torch
|
||||
*/
|
||||
void quenchTorch();
|
||||
|
||||
/**
|
||||
* Revives the party after the entire party has been killed
|
||||
*/
|
||||
void reviveParty();
|
||||
MapTile getTransport() const;
|
||||
void setTransport(MapTile transport);
|
||||
void setShipHull(int str);
|
||||
|
||||
Direction getDirection() const;
|
||||
void setDirection(Direction dir);
|
||||
|
||||
void adjustReagent(int reagent, int amt);
|
||||
int getReagent(int reagent) const;
|
||||
short *getReagentPtr(int reagent) const;
|
||||
|
||||
void setActivePlayer(int p);
|
||||
int getActivePlayer() const;
|
||||
|
||||
void swapPlayers(int p1, int p2);
|
||||
|
||||
/**
|
||||
* Returns the size of the party
|
||||
*/
|
||||
int size() const;
|
||||
|
||||
/**
|
||||
* Returns a pointer to the party member indicated
|
||||
*/
|
||||
PartyMember *member(int index) const;
|
||||
|
||||
private:
|
||||
void syncMembers();
|
||||
PartyMemberVector _members;
|
||||
SaveGame *_saveGame;
|
||||
MapTile _transport;
|
||||
int _torchDuration;
|
||||
int _activePlayer;
|
||||
#ifdef IOS_ULTIMA4
|
||||
friend void U4IOS::syncPartyMembersWithSaveGame();
|
||||
#endif
|
||||
};
|
||||
|
||||
bool isPartyMember(Object *punknown);
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user