Initial commit

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

View File

@@ -0,0 +1,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

View 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

View 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

View File

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

File diff suppressed because it is too large Load Diff

View 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

View File

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

View 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

View 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

View 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

View 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

View File

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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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

View 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

View File

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

View File

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

View 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

View 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

View File

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

View File

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

View File

@@ -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

View File

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

View 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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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