ULTIMA4: Split up controllers into their own files

This commit is contained in:
Paul Gilbert 2020-04-16 13:20:54 -07:00
parent 4e1a461b53
commit e0607343af
39 changed files with 2356 additions and 1653 deletions

View File

@ -134,6 +134,17 @@ MODULE_OBJS := \
ultima1/widgets/urban_widget.o \
ultima1/widgets/wench.o \
ultima1/game.o \
ultima4/controllers/alpha_action_controller.o \
ultima4/controllers/controller.o \
ultima4/controllers/game_controller.o \
ultima4/controllers/key_handler_controller.o \
ultima4/controllers/read_choice_controller.o \
ultima4/controllers/read_dir_controller.o \
ultima4/controllers/read_int_controller.o \
ultima4/controllers/read_player_controller.o \
ultima4/controllers/read_string_controller.o \
ultima4/controllers/wait_controller.o \
ultima4/controllers/ztats_controller.o \
ultima4/conversation/conversation.o \
ultima4/conversation/dialogueloader.o \
ultima4/conversation/dialogueloader_hw.o \
@ -148,7 +159,6 @@ MODULE_OBJS := \
ultima4/core/error.o \
ultima4/core/settings.o \
ultima4/core/utils.o \
ultima4/events/controller.o \
ultima4/events/event.o \
ultima4/events/event_scummvm.o \
ultima4/events/timed_event_mgr.o \

View File

@ -0,0 +1,59 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "ultima/ultima4/controllers/alpha_action_controller.h"
#include "ultima/ultima4/events/event.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 if (key == U4_SPACE || key == U4_ESC || key == U4_ENTER) {
screenMessage("\n");
_value = -1;
doneWaiting();
} else {
screenMessage("\n%s", _prompt.c_str());
g_screen->update();
return KeyHandler::defaultHandler(key, NULL);
}
return true;
}
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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#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.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> {
public:
AlphaActionController(char letter, const Common::String &p) : _lastValidLetter(letter), _prompt(p) {
}
bool keyPressed(int key) override;
static int get(char lastValidLetter, const Common::String &prompt, EventHandler *eh = NULL);
private:
char _lastValidLetter;
Common::String _prompt;
};
} // End of namespace Ultima4
} // End of namespace Ultima
#endif

View File

@ -20,7 +20,7 @@
*
*/
#include "ultima/ultima4/events/controller.h"
#include "ultima/ultima4/controllers/controller.h"
#include "ultima/ultima4/events/event.h"
namespace Ultima {

View File

@ -20,8 +20,8 @@
*
*/
#ifndef ULTIMA4_CONTROLLER_H
#define ULTIMA4_CONTROLLER_H
#ifndef ULTIMA4_CONTROLLERS_CONTROLLER_H
#define ULTIMA4_CONTROLLERS_CONTROLLER_H
#include "ultima/ultima4/meta_engine.h"
@ -113,6 +113,13 @@ private:
bool _exitWhenDone;
};
class TurnCompleter {
public:
virtual ~TurnCompleter() {
}
virtual void finishTurn() = 0;
};
} // End of namespace Ultima4
} // End of namespace Ultima

View File

@ -0,0 +1,842 @@
/* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#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/game/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 {
using namespace std;
GameController *g_game = NULL;
static const MouseArea MOUSE_AREAS[] = {
{ 3, { { 8, 8 }, { 8, 184 }, { 96, 96 } }, MC_WEST, { U4_ENTER, 0, U4_LEFT } },
{ 3, { { 8, 8 }, { 184, 8 }, { 96, 96 } }, MC_NORTH, { U4_ENTER, 0, U4_UP } },
{ 3, { { 184, 8 }, { 184, 184 }, { 96, 96 } }, MC_EAST, { U4_ENTER, 0, U4_RIGHT } },
{ 3, { { 8, 184 }, { 184, 184 }, { 96, 96 } }, MC_SOUTH, { U4_ENTER, 0, U4_DOWN } },
{ 0, { { 0, 0 }, { 0, 0 }, { 0, 0 } }, MC_NORTH, { 0, 0, 0 } }
};
GameController::GameController() : _mapArea(BORDER_WIDTH, BORDER_HEIGHT, VIEWPORT_W, VIEWPORT_H), _paused(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->play();
imageMgr->get(BKGD_BORDERS)->_image->draw(0, 0);
g_context->_stats->update(); /* draw the party stats */
screenMessage("Press Alt-h for help\n");
screenPrompt();
eventHandler->pushMouseAreaSet(MOUSE_AREAS);
eventHandler->setScreenUpdate(&gameUpdateScreen);
}
void GameController::init() {
initScreen();
// initialize the global game context, conversation and game state variables
g_context = new 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 = NULL;
}
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
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);
city->addPeople();
}
}
int GameController::exitToParentMap() {
if (!g_context->_location)
return 0;
if (g_context->_location->_prev != NULL) {
// 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
U4IOS::updateGameControllerContext(c->location->context);
#endif
return 1;
}
return 0;
}
void GameController::finishTurn() {
g_context->_lastCommandTime = g_system->getMillis();
Creature *attacker = NULL;
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);
screenUpdate(&this->_mapArea, true, false);
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());
// 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();
}
/* update map annotations */
g_context->_location->_map->_annotations->passTurn();
if (!g_context->_party->isImmobilized())
break;
if (g_context->_party->isDead()) {
deathStart(0);
return;
} else {
screenMessage("Zzzzzz\n");
screenWait(4);
}
}
if (g_context->_location->_context == CTX_DUNGEON) {
Dungeon *dungeon = dynamic_cast<Dungeon *>(g_context->_location->_map);
if (g_context->_party->getTorchDuration() <= 0)
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()) {
deathStart(0);
return;
}
}
}
/* draw a prompt */
screenPrompt();
//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);
screenTileUpdate(&g_game->_mapArea, coords);
screenWait(frames);
g_context->_location->_map->_annotations->remove(coords, tile);
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);
ASSERT(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.
screenMessage("\n %cThou hast lost\n an eighth!%c\n", FG_YELLOW, FG_WHITE);
break;
case PartyEvent::ADVANCED_LEVEL:
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:
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
dynamic_cast<CombatController *>(eventHandler->getController())->movePartyMember(event);
break;
default:
avatarMoved(event);
break;
}
}
void GameController::keybinder(KeybindingAction action) {
MetaEngine::executeAction(action);
}
bool GameController::keyPressed(int key) {
bool valid = true;
int endTurn = 1;
Object *obj;
MapTile *tile;
/* Translate context-sensitive action key into a useful command */
if (key == U4_ENTER && settings._enhancements && settings._enhancementsOptions._smartEnterKey) {
/* Attempt to guess based on the character's surroundings etc, what
action they want */
/* Do they want to board something? */
if (g_context->_transportContext == TRANSPORT_FOOT) {
obj = g_context->_location->_map->objectAt(g_context->_location->_coords);
if (obj && (obj->getTile().getTileType()->isShip() ||
obj->getTile().getTileType()->isHorse() ||
obj->getTile().getTileType()->isBalloon()))
key = 'b';
}
/* Klimb/Descend Balloon */
else if (g_context->_transportContext == TRANSPORT_BALLOON) {
if (g_context->_party->isFlying())
key = 'd';
else {
#ifdef IOS
U4IOS::IOSSuperButtonHelper superHelper;
key = ReadChoiceController::get("xk \033\n");
#else
key = 'k';
#endif
}
}
/* X-it transport */
else key = 'x';
/* Klimb? */
if ((g_context->_location->_map->portalAt(g_context->_location->_coords, ACTION_KLIMB) != NULL))
key = 'k';
/* Descend? */
else if ((g_context->_location->_map->portalAt(g_context->_location->_coords, ACTION_DESCEND) != NULL))
key = 'd';
if (g_context->_location->_context == CTX_DUNGEON) {
Dungeon *dungeon = static_cast<Dungeon *>(g_context->_location->_map);
bool up = dungeon->ladderUpAt(g_context->_location->_coords);
bool down = dungeon->ladderDownAt(g_context->_location->_coords);
if (up && down) {
#ifdef IOS
U4IOS::IOSClimbHelper climbHelper;
key = ReadChoiceController::get("kd \033\n");
#else
key = 'k'; // This is consistent with the previous code. Ideally, I would have a UI here as well.
#endif
} else if (up) {
key = 'k';
} else {
key = 'd';
}
}
/* Enter? */
if (g_context->_location->_map->portalAt(g_context->_location->_coords, ACTION_ENTER) != NULL)
key = 'e';
/* Get Chest? */
if (!g_context->_party->isFlying()) {
tile = g_context->_location->_map->tileAt(g_context->_location->_coords, WITH_GROUND_OBJECTS);
if (tile->getTileType()->isChest()) key = 'g';
}
/* None of these? Default to search */
if (key == U4_ENTER) key = 's';
}
if ((g_context->_location->_context & CTX_DUNGEON) && strchr("abefjlotxy", key))
screenMessage("%cNot here!%c\n", FG_GREY, FG_WHITE);
if (valid && endTurn) {
if (eventHandler->getController() == g_game)
g_context->_location->_turnCompleter->finishTurn();
} else if (!endTurn) {
/* if our turn did not end, then manually redraw the text prompt */
screenPrompt();
}
return valid || KeyHandler::defaultHandler(key, NULL);
}
void GameController::initMoons() {
int trammelphase = g_ultima->_saveGame->_trammelPhase,
feluccaphase = g_ultima->_saveGame->_feluccaPhase;
ASSERT(g_context != NULL, "Game context doesn't exist!");
ASSERT(g_ultima->_saveGame != NULL, "Savegame doesn't exist!");
//ASSERT(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 = moongateGetGateCoordsForPhase(oldTrammel);
if (gate)
g_context->_location->_map->_annotations->remove(*gate, g_context->_location->_map->translateFromRawTileIndex(0x40));
gate = moongateGetGateCoordsForPhase(g_ultima->_saveGame->_trammelPhase);
if (gate)
g_context->_location->_map->_annotations->add(*gate, g_context->_location->_map->translateFromRawTileIndex(0x40));
} else if (trammelSubphase == 1) {
gate = moongateGetGateCoordsForPhase(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 = moongateGetGateCoordsForPhase(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 = moongateGetGateCoordsForPhase(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 = moongateGetGateCoordsForPhase(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 = moongateGetGateCoordsForPhase(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 = moongateGetGateCoordsForPhase(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 = moongateGetGateCoordsForPhase(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:
screenMessage("%s\n", getDirectionName(event._dir));
break;
case TRANSPORT_SHIP:
if (event._result & MOVE_TURNED)
screenMessage("Turn %s!\n", getDirectionName(event._dir));
else if (event._result & MOVE_SLOWED)
screenMessage("%cSlow progress!%c\n", FG_GREY, FG_WHITE);
else
screenMessage("Sail %s!\n", getDirectionName(event._dir));
break;
case TRANSPORT_BALLOON:
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) != NULL) {
talkAtCoord(newx, newy, 1, NULL);
event.result = MOVE_SUCCEEDED | MOVE_END_TURN;
}*/
}
/* if we're still blocked */
if ((event._result & MOVE_BLOCKED) && !settings._filterMoveMessages) {
soundPlay(SOUND_BLOCKED, false);
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);
screenMessage("%cSlow progress!%c\n", FG_GREY, FG_WHITE);
} else {
soundPlay(SOUND_WALK_NORMAL);
}
}
}
/* exited map */
if (event._result & MOVE_EXIT_TO_PARENT) {
screenMessage("%cLeaving...%c\n", FG_GREY, FG_WHITE);
exitToParentMap();
g_music->play();
}
/* 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) {
Dungeon *dungeon = dynamic_cast<Dungeon *>(g_context->_location->_map);
Direction realDir = dirNormalize((Direction)g_ultima->_saveGame->_orientation, event._dir);
if (!settings._filterMoveMessages) {
if (event._userEvent) {
if (event._result & MOVE_TURNED) {
if (dirRotateCCW((Direction)g_ultima->_saveGame->_orientation) == realDir)
screenMessage("Turn Left\n");
else screenMessage("Turn Right\n");
}
/* show 'Advance' or 'Retreat' in dungeons */
else screenMessage("%s\n", realDir == g_ultima->_saveGame->_orientation ? "Advance" : "Retreat");
}
if (event._result & MOVE_BLOCKED)
screenMessage("%cBlocked!%c\n", FG_GREY, FG_WHITE);
}
/* if we're exiting the map, do this */
if (event._result & MOVE_EXIT_TO_PARENT) {
screenMessage("%cLeaving...%c\n", FG_GREY, FG_WHITE);
exitToParentMap();
g_music->play();
}
/* 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);
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);
screenCycle();
/*
* force pass if no commands within last 20 seconds
*/
Controller *controller = eventHandler->getController();
if (controller != NULL && (eventHandler->getController() == g_game ||
dynamic_cast<CombatController *>(eventHandler->getController()) != NULL) &&
gameTimeSinceLastCommand() > 20) {
/* pass the turn, and redraw the text area so the prompt is shown */
MetaEngine::executeAction(KEYBIND_PASS);
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 (moongateFindActiveGateAt(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 (moongateIsEntryToShrineOfSpirituality(g_ultima->_saveGame->_trammelPhase, g_ultima->_saveGame->_feluccaPhase)) {
Shrine *shrine_spirituality;
shrine_spirituality = dynamic_cast<Shrine *>(mapMgr->get(MAP_SHRINE_SPIRITUALITY));
if (!g_context->_party->canEnterShrine(VIRT_SPIRITUALITY))
return true;
setMap(shrine_spirituality, 1, NULL);
g_music->play();
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
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;
gameSpawnCreature(NULL);
}
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;
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 (i = map->_objects.begin(); i != map->_objects.end(); i++) {
Object *obj = *i;
if (obj->getTile().getTileType()->isBalloon())
return false;
}
const Tile *balloon = map->_tileset->getByName("balloon");
ASSERT(balloon, "no balloon tile found in tileset");
map->addObject(balloon->getId(), balloon->getId(), map->getLabel("balloon"));
return true;
}
} // End of namespace Ultima4
} // End of namespace Ultima

View File

@ -0,0 +1,174 @@
/* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#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/map/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 {
public:
GameController();
/* controller functions */
/**
* Keybinder actions
*/
void keybinder(KeybindingAction action) override;
/**
* The main key handler for the game. Interpretes each key as a
* command - 'a' for attack, 't' for talk, etc.
*/
bool keyPressed(int key) 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 = NULL);
/**
* 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();
/**
* 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);
/**
* 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);
TileView _mapArea;
bool _paused;
int _pausedTimer;
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);
};
extern GameController *g_game;
} // End of namespace Ultima4
} // End of namespace Ultima
#endif

View File

@ -0,0 +1,140 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "ultima/ultima4/controllers/key_handler_controller.h"
#include "ultima/ultima4/core/utils.h"
#include "ultima/ultima4/events/event.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) {
}
/**
* Sets the key-repeat characteristics of the keyboard.
*/
int KeyHandler::setKeyRepeat(int delay, int interval) {
#ifdef TODO
return SDL_EnableKeyRepeat(delay, interval);
#else
return 0;
#endif
}
bool KeyHandler::globalHandler(int key) {
switch (key) {
#if defined(MACOSX)
case U4_META + 'q': /* Cmd+q */
case U4_META + 'x': /* Cmd+x */
#endif
case U4_ALT + 'x': /* Alt+x */
#if defined(WIN32)
case U4_ALT + U4_FKEY + 3:
#endif
g_ultima->quitGame();
EventHandler::end();
return true;
default:
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 U4_RIGHT_SHIFT:
case U4_LEFT_SHIFT:
case U4_RIGHT_CTRL:
case U4_LEFT_CTRL:
case U4_RIGHT_ALT:
case U4_LEFT_ALT:
case U4_RIGHT_META:
case U4_LEFT_META:
case U4_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) {
ASSERT(_handler != NULL, "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,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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#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 = NULL, bool asyncronous = true);
/* Static functions */
static int setKeyRepeat(int delay, int interval);
/**
* 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,62 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "ultima/ultima4/controllers/read_choice_controller.h"
namespace Ultima {
namespace Ultima4 {
ReadChoiceController::ReadChoiceController(const Common::String &choices) {
_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))
screenMessage("%c", toupper(key));
doneWaiting();
return true;
}
return false;
}
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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#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.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;
static char get(const Common::String &choices, EventHandler *eh = NULL);
protected:
Common::String _choices;
};
} // End of namespace Ultima4
} // End of namespace Ultima
#endif

View File

@ -0,0 +1,76 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "ultima/ultima4/controllers/read_dir_controller.h"
#include "ultima/ultima4/map/direction.h"
namespace Ultima {
namespace Ultima4 {
ReadDirController::ReadDirController() {
_value = DIR_NONE;
}
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_PASS:
_value = DIR_NONE;
doneWaiting();
break;
default:
return;
}
doneWaiting();
}
bool ReadDirController::keyPressed(int key) {
switch (key) {
case Common::KEYCODE_ESCAPE:
case Common::KEYCODE_SPACE:
case Common::KEYCODE_RETURN:
_value = DIR_NONE;
doneWaiting();
return true;
default:
break;
}
return false;
}
} // End of namespace Ultima4
} // End of namespace Ultima

View File

@ -0,0 +1,53 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#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();
/**
* Key was pressed
*/
bool keyPressed(int key) override;
/**
* Handles keybinder actions
*/
void keybinder(KeybindingAction action) override;
};
} // End of namespace Ultima4
} // End of namespace Ultima
#endif

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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#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(), NULL, 10));
}
} // 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#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 = NULL);
int getInt() const;
};
} // 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#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
U4IOS::beginCharacterChoiceDialog();
#endif
}
ReadPlayerController::~ReadPlayerController() {
#ifdef IOS
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 - '1';
}
int ReadPlayerController::waitFor() {
ReadChoiceController::waitFor();
return getPlayer();
}
} // End of namespace Ultima4
} // End of namespace Ultima

View File

@ -0,0 +1,47 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#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,109 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#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) {
_maxLen = maxlen;
_screenX = screenX;
_screenY = screenY;
_view = NULL;
_accepted = accepted_chars;
}
ReadStringController::ReadStringController(int maxlen, TextView *view, const Common::String &accepted_chars) {
_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 < U4_ALT)
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 {
screenHideCursor();
screenTextAt(_screenX + len - 1, _screenY, " ");
screenSetCursorPos(_screenX + len - 1, _screenY);
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 {
screenHideCursor();
screenTextAt(_screenX + len, _screenY, "%c", key);
screenSetCursorPos(_screenX + len + 1, _screenY);
g_context->col = len + 1;
screenShowCursor();
}
}
} else valid = false;
return valid || KeyHandler::defaultHandler(key, NULL);
}
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,65 @@
/* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#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.h"
#include "ultima/ultima4/game/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 = NULL);
static Common::String get(int maxlen, TextView *view, EventHandler *eh = NULL);
#ifdef IOS
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,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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "ultima/ultima4/controllers/wait_controller.h"
#include "ultima/ultima4/events/event.h"
namespace Ultima {
namespace Ultima4 {
WaitController::WaitController(unsigned int 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,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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#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(unsigned int cycles);
bool keyPressed(int key) override;
void timerFired() override;
void wait();
void setCycles(int c);
private:
unsigned int _cycles;
unsigned int _current;
};
} // End of namespace Ultima4
} // End of namespace Ultima
#endif

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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "ultima/ultima4/controllers/ztats_controller.h"
#include "ultima/ultima4/events/event.h"
#include "ultima/ultima4/game/context.h"
#include "ultima/ultima4/game/stats.h"
#include "ultima/ultima4/ultima4.h"
namespace Ultima {
namespace Ultima4 {
bool ZtatsController::keyPressed(int key) {
switch (key) {
case U4_UP:
case U4_LEFT:
g_context->_stats->prevItem();
return true;
case U4_DOWN:
case U4_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;
case U4_ESC:
case U4_SPACE:
case U4_ENTER:
g_context->_stats->setView(StatsView(STATS_PARTY_OVERVIEW));
doneWaiting();
return true;
default:
return KeyHandler::defaultHandler(key, NULL);
}
}
} // End of namespace Ultima4
} // End of namespace Ultima

View File

@ -0,0 +1,42 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#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:
bool keyPressed(int key) override;
};
} // End of namespace Ultima4
} // End of namespace Ultima
#endif

View File

@ -22,6 +22,10 @@
#include "ultima/ultima4/core/debugger.h"
#include "ultima/ultima4/core/utils.h"
#include "ultima/ultima4/controllers/alpha_action_controller.h"
#include "ultima/ultima4/controllers/read_choice_controller.h"
#include "ultima/ultima4/controllers/read_dir_controller.h"
#include "ultima/ultima4/controllers/ztats_controller.h"
#include "ultima/ultima4/game/armor.h"
#include "ultima/ultima4/game/context.h"
#include "ultima/ultima4/game/game.h"

View File

@ -23,6 +23,8 @@
#include "ultima/ultima4/core/debugger_actions.h"
#include "ultima/ultima4/core/config.h"
#include "ultima/ultima4/core/utils.h"
#include "ultima/ultima4/controllers/read_choice_controller.h"
#include "ultima/ultima4/controllers/read_int_controller.h"
#include "ultima/ultima4/conversation/conversation.h"
#include "ultima/ultima4/game/context.h"
#include "ultima/ultima4/game/player.h"

View File

@ -21,12 +21,13 @@
*/
#include "ultima/ultima4/events/event.h"
#include "ultima/ultima4/game/context.h"
#include "ultima/ultima4/map/location.h"
#include "ultima/ultima4/filesys/savegame.h"
#include "ultima/ultima4/gfx/screen.h"
#include "ultima/ultima4/controllers/wait_controller.h"
#include "ultima/ultima4/core/settings.h"
#include "ultima/ultima4/filesys/savegame.h"
#include "ultima/ultima4/game/context.h"
#include "ultima/ultima4/game/textview.h"
#include "ultima/ultima4/gfx/screen.h"
#include "ultima/ultima4/map/location.h"
#include "common/events.h"
namespace Ultima {
@ -128,205 +129,5 @@ const MouseArea *EventHandler::getMouseAreaSet() const {
return NULL;
}
/*-------------------------------------------------------------------*/
ReadStringController::ReadStringController(int maxlen, int screenX, int screenY, const Common::String &accepted_chars) {
_maxLen = maxlen;
_screenX = screenX;
_screenY = screenY;
_view = NULL;
_accepted = accepted_chars;
}
ReadStringController::ReadStringController(int maxlen, TextView *view, const Common::String &accepted_chars) {
_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 < U4_ALT)
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 {
screenHideCursor();
screenTextAt(_screenX + len - 1, _screenY, " ");
screenSetCursorPos(_screenX + len - 1, _screenY);
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 {
screenHideCursor();
screenTextAt(_screenX + len, _screenY, "%c", key);
screenSetCursorPos(_screenX + len + 1, _screenY);
g_context->col = len + 1;
screenShowCursor();
}
}
} else valid = false;
return valid || KeyHandler::defaultHandler(key, NULL);
}
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();
}
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(), NULL, 10));
}
/*-------------------------------------------------------------------*/
ReadChoiceController::ReadChoiceController(const Common::String &choices) {
_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))
screenMessage("%c", toupper(key));
doneWaiting();
return true;
}
return false;
}
char ReadChoiceController::get(const Common::String &choices, EventHandler *eh) {
if (!eh)
eh = eventHandler;
ReadChoiceController ctrl(choices);
eh->pushController(&ctrl);
return ctrl.waitFor();
}
/*-------------------------------------------------------------------*/
ReadDirController::ReadDirController() {
_value = DIR_NONE;
}
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_PASS:
_value = DIR_NONE;
doneWaiting();
break;
default:
return;
}
doneWaiting();
}
bool ReadDirController::keyPressed(int key) {
switch (key) {
case Common::KEYCODE_ESCAPE:
case Common::KEYCODE_SPACE:
case Common::KEYCODE_RETURN:
_value = DIR_NONE;
doneWaiting();
return true;
default:
break;
}
return false;
}
/*-------------------------------------------------------------------*/
WaitController::WaitController(unsigned int 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

@ -23,13 +23,15 @@
#ifndef ULTIMA4_EVENT_H
#define ULTIMA4_EVENT_H
#include "ultima/ultima4/events/event.h"
#include "ultima/ultima4/events/timed_event_mgr.h"
#include "ultima/ultima4/controllers/key_handler_controller.h"
#include "ultima/ultima4/core/types.h"
#include "ultima/ultima4/gfx/screen.h"
#include "ultima/shared/std/containers.h"
#include "common/events.h"
#include "common/list.h"
#include "common/str.h"
#include "ultima/ultima4/events/controller.h"
#include "ultima/ultima4/events/timed_event_mgr.h"
#include "ultima/ultima4/core/types.h"
#include "ultima/shared/std/containers.h"
namespace Ultima {
namespace Ultima4 {
@ -58,190 +60,6 @@ namespace Ultima4 {
#define U4_RIGHT_META Common::KEYCODE_RMETA
#define U4_LEFT_META Common::KEYCODE_LMETA
struct MouseArea;
class EventHandler;
class TextView;
/**
* 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 = NULL, bool asyncronous = true);
/* Static functions */
static int setKeyRepeat(int delay, int interval);
/**
* 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;
};
/**
* 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 = NULL);
static Common::String get(int maxlen, TextView *view, EventHandler *eh = NULL);
#ifdef IOS
void setValue(const Common::String &utf8StringValue) {
value = utf8StringValue;
}
#endif
protected:
int _maxLen, _screenX, _screenY;
TextView *_view;
Common::String _accepted;
};
/**
* 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 = NULL);
int getInt() const;
};
/**
* 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;
static char get(const Common::String &choices, EventHandler *eh = NULL);
protected:
Common::String _choices;
};
/**
* A controller to read a direction enter with the arrow keys.
*/
class ReadDirController : public WaitableController<Direction> {
public:
ReadDirController();
/**
* Key was pressed
*/
bool keyPressed(int key) override;
/**
* Handles keybinder actions
*/
void keybinder(KeybindingAction action) override;
};
/**
* A controller to pause for a given length of time, ignoring all
* keyboard input.
*/
class WaitController : public Controller {
public:
WaitController(unsigned int cycles);
bool keyPressed(int key) override;
void timerFired() override;
void wait();
void setCycles(int c);
private:
unsigned int _cycles;
unsigned int _current;
};
#if defined(IOS)
#ifndef __OBJC__
typedef void *TimedManagerHelper;

View File

@ -34,112 +34,6 @@
namespace Ultima {
namespace Ultima4 {
KeyHandler::KeyHandler(Callback func, void *d, bool asyncronous) :
_handler(func),
_async(asyncronous),
_data(d) {
}
/**
* Sets the key-repeat characteristics of the keyboard.
*/
int KeyHandler::setKeyRepeat(int delay, int interval) {
#ifdef TODO
return SDL_EnableKeyRepeat(delay, interval);
#else
return 0;
#endif
}
bool KeyHandler::globalHandler(int key) {
switch (key) {
#if defined(MACOSX)
case U4_META + 'q': /* Cmd+q */
case U4_META + 'x': /* Cmd+x */
#endif
case U4_ALT + 'x': /* Alt+x */
#if defined(WIN32)
case U4_ALT + U4_FKEY + 3:
#endif
g_ultima->quitGame();
EventHandler::end();
return true;
default:
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 U4_RIGHT_SHIFT:
case U4_LEFT_SHIFT:
case U4_RIGHT_CTRL:
case U4_LEFT_CTRL:
case U4_RIGHT_ALT:
case U4_LEFT_ALT:
case U4_RIGHT_META:
case U4_LEFT_META:
case U4_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) {
ASSERT(_handler != NULL, "key handler must be initialized");
return _handler->handle(key);
}
KeyHandler *KeyHandlerController::getKeyHandler() {
return _handler;
}
/*-------------------------------------------------------------------*/
EventHandler::EventHandler() : _timer(settings._eventTimerGranularity), _updateScreen(NULL) {
}

View File

@ -20,22 +20,23 @@
*
*/
#include "ultima/ultima4/ultima4.h"
#include "ultima/ultima4/game/death.h"
#include "ultima/ultima4/game/context.h"
#include "ultima/ultima4/game/game.h"
#include "ultima/ultima4/game/player.h"
#include "ultima/ultima4/game/portal.h"
#include "ultima/ultima4/game/stats.h"
#include "ultima/ultima4/controllers/wait_controller.h"
#include "ultima/ultima4/core/settings.h"
#include "ultima/ultima4/events/event.h"
#include "ultima/ultima4/gfx/screen.h"
#include "ultima/ultima4/map/map.h"
#include "ultima/ultima4/map/annotation.h"
#include "ultima/ultima4/map/city.h"
#include "ultima/ultima4/game/context.h"
#include "ultima/ultima4/events/event.h"
#include "ultima/ultima4/game/game.h"
#include "ultima/ultima4/map/location.h"
#include "ultima/ultima4/map/mapmgr.h"
#include "ultima/ultima4/sound/music.h"
#include "ultima/ultima4/game/player.h"
#include "ultima/ultima4/game/portal.h"
#include "ultima/ultima4/gfx/screen.h"
#include "ultima/ultima4/core/settings.h"
#include "ultima/ultima4/game/stats.h"
#include "ultima/ultima4/ultima4.h"
#include "common/system.h"
namespace Ultima {

View File

@ -21,6 +21,11 @@
*/
#include "ultima/ultima4/ultima4.h"
#include "ultima/ultima4/controllers/read_choice_controller.h"
#include "ultima/ultima4/controllers/read_dir_controller.h"
#include "ultima/ultima4/controllers/read_int_controller.h"
#include "ultima/ultima4/controllers/read_player_controller.h"
#include "ultima/ultima4/controllers/read_string_controller.h"
#include "ultima/ultima4/conversation/conversation.h"
#include "ultima/ultima4/core/config.h"
#include "ultima/ultima4/core/debugger.h"
@ -72,8 +77,6 @@ namespace Ultima4 {
using namespace std;
GameController *g_game = NULL;
/*-----------------*/
/* Functions BEGIN */
@ -82,132 +85,15 @@ void gameAdvanceLevel(PartyMember *player);
void gameInnHandler(void);
void gameLostEighth(Virtue virtue);
void gamePartyStarving(void);
uint32 gameTimeSinceLastCommand(void);
void mixReagentsSuper();
/* action functions */
void wearArmor(int player = -1);
/* creature functions */
void gameCreatureAttack(Creature *obj);
/* Functions END */
/*---------------*/
static const MouseArea MOUSE_AREAS[] = {
{ 3, { { 8, 8 }, { 8, 184 }, { 96, 96 } }, MC_WEST, { U4_ENTER, 0, U4_LEFT } },
{ 3, { { 8, 8 }, { 184, 8 }, { 96, 96 } }, MC_NORTH, { U4_ENTER, 0, U4_UP } },
{ 3, { { 184, 8 }, { 184, 184 }, { 96, 96 } }, MC_EAST, { U4_ENTER, 0, U4_RIGHT } },
{ 3, { { 8, 184 }, { 184, 184 }, { 96, 96 } }, MC_SOUTH, { U4_ENTER, 0, U4_DOWN } },
{ 0, { { 0, 0 }, { 0, 0 }, { 0, 0 } }, MC_NORTH, { 0, 0, 0 } }
};
ReadPlayerController::ReadPlayerController() : ReadChoiceController("12345678 \033\n") {
#ifdef IOS
U4IOS::beginCharacterChoiceDialog();
#endif
}
ReadPlayerController::~ReadPlayerController() {
#ifdef IOS
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 - '1';
}
int ReadPlayerController::waitFor() {
ReadChoiceController::waitFor();
return getPlayer();
}
bool AlphaActionController::keyPressed(int key) {
if (Common::isLower(key))
key = toupper(key);
if (key >= 'A' && key <= toupper(_lastValidLetter)) {
_value = key - 'A';
doneWaiting();
} else if (key == U4_SPACE || key == U4_ESC || key == U4_ENTER) {
screenMessage("\n");
_value = -1;
doneWaiting();
} else {
screenMessage("\n%s", _prompt.c_str());
g_screen->update();
return KeyHandler::defaultHandler(key, NULL);
}
return true;
}
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();
}
GameController::GameController() : _mapArea(BORDER_WIDTH, BORDER_HEIGHT, VIEWPORT_W, VIEWPORT_H), _paused(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->play();
imageMgr->get(BKGD_BORDERS)->_image->draw(0, 0);
g_context->_stats->update(); /* draw the party stats */
screenMessage("Press Alt-h for help\n");
screenPrompt();
eventHandler->pushMouseAreaSet(MOUSE_AREAS);
eventHandler->setScreenUpdate(&gameUpdateScreen);
}
void GameController::init() {
initScreen();
// initialize the global game context, conversation and game state variables
g_context = new 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 = NULL;
}
/**
* Sets the view mode.
*/
@ -241,238 +127,6 @@ void gameUpdateScreen() {
}
}
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
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);
city->addPeople();
}
}
int GameController::exitToParentMap() {
if (!g_context->_location)
return 0;
if (g_context->_location->_prev != NULL) {
// 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
U4IOS::updateGameControllerContext(c->location->context);
#endif
return 1;
}
return 0;
}
void GameController::finishTurn() {
g_context->_lastCommandTime = g_system->getMillis();
Creature *attacker = NULL;
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);
screenUpdate(&this->_mapArea, true, false);
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());
// 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();
}
/* update map annotations */
g_context->_location->_map->_annotations->passTurn();
if (!g_context->_party->isImmobilized())
break;
if (g_context->_party->isDead()) {
deathStart(0);
return;
} else {
screenMessage("Zzzzzz\n");
screenWait(4);
}
}
if (g_context->_location->_context == CTX_DUNGEON) {
Dungeon *dungeon = dynamic_cast<Dungeon *>(g_context->_location->_map);
if (g_context->_party->getTorchDuration() <= 0)
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()) {
deathStart(0);
return;
}
}
}
/* draw a prompt */
screenPrompt();
//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);
screenTileUpdate(&g_game->_mapArea, coords);
screenWait(frames);
g_context->_location->_map->_annotations->remove(coords, tile);
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);
ASSERT(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.
screenMessage("\n %cThou hast lost\n an eighth!%c\n", FG_YELLOW, FG_WHITE);
break;
case PartyEvent::ADVANCED_LEVEL:
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:
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
dynamic_cast<CombatController *>(eventHandler->getController())->movePartyMember(event);
break;
default:
avatarMoved(event);
break;
}
}
void gameSpellEffect(int spell, int player, Sound sound) {
int time;
@ -519,99 +173,6 @@ void gameSpellEffect(int spell, int player, Sound sound) {
}
}
void GameController::keybinder(KeybindingAction action) {
MetaEngine::executeAction(action);
}
bool GameController::keyPressed(int key) {
bool valid = true;
int endTurn = 1;
Object *obj;
MapTile *tile;
/* Translate context-sensitive action key into a useful command */
if (key == U4_ENTER && settings._enhancements && settings._enhancementsOptions._smartEnterKey) {
/* Attempt to guess based on the character's surroundings etc, what
action they want */
/* Do they want to board something? */
if (g_context->_transportContext == TRANSPORT_FOOT) {
obj = g_context->_location->_map->objectAt(g_context->_location->_coords);
if (obj && (obj->getTile().getTileType()->isShip() ||
obj->getTile().getTileType()->isHorse() ||
obj->getTile().getTileType()->isBalloon()))
key = 'b';
}
/* Klimb/Descend Balloon */
else if (g_context->_transportContext == TRANSPORT_BALLOON) {
if (g_context->_party->isFlying())
key = 'd';
else {
#ifdef IOS
U4IOS::IOSSuperButtonHelper superHelper;
key = ReadChoiceController::get("xk \033\n");
#else
key = 'k';
#endif
}
}
/* X-it transport */
else key = 'x';
/* Klimb? */
if ((g_context->_location->_map->portalAt(g_context->_location->_coords, ACTION_KLIMB) != NULL))
key = 'k';
/* Descend? */
else if ((g_context->_location->_map->portalAt(g_context->_location->_coords, ACTION_DESCEND) != NULL))
key = 'd';
if (g_context->_location->_context == CTX_DUNGEON) {
Dungeon *dungeon = static_cast<Dungeon *>(g_context->_location->_map);
bool up = dungeon->ladderUpAt(g_context->_location->_coords);
bool down = dungeon->ladderDownAt(g_context->_location->_coords);
if (up && down) {
#ifdef IOS
U4IOS::IOSClimbHelper climbHelper;
key = ReadChoiceController::get("kd \033\n");
#else
key = 'k'; // This is consistent with the previous code. Ideally, I would have a UI here as well.
#endif
} else if (up) {
key = 'k';
} else {
key = 'd';
}
}
/* Enter? */
if (g_context->_location->_map->portalAt(g_context->_location->_coords, ACTION_ENTER) != NULL)
key = 'e';
/* Get Chest? */
if (!g_context->_party->isFlying()) {
tile = g_context->_location->_map->tileAt(g_context->_location->_coords, WITH_GROUND_OBJECTS);
if (tile->getTileType()->isChest()) key = 'g';
}
/* None of these? Default to search */
if (key == U4_ENTER) key = 's';
}
if ((g_context->_location->_context & CTX_DUNGEON) && strchr("abefjlotxy", key))
screenMessage("%cNot here!%c\n", FG_GREY, FG_WHITE);
if (valid && endTurn) {
if (eventHandler->getController() == g_game)
g_context->_location->_turnCompleter->finishTurn();
} else if (!endTurn) {
/* if our turn did not end, then manually redraw the text prompt */
screenPrompt();
}
return valid || KeyHandler::defaultHandler(key, NULL);
}
Common::String gameGetInput(int maxlen) {
screenEnableCursor();
screenShowCursor();
@ -678,42 +239,6 @@ Direction gameGetDirection() {
}
}
bool ZtatsController::keyPressed(int key) {
switch (key) {
case U4_UP:
case U4_LEFT:
g_context->_stats->prevItem();
return true;
case U4_DOWN:
case U4_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;
case U4_ESC:
case U4_SPACE:
case U4_ENTER:
g_context->_stats->setView(StatsView(STATS_PARTY_OVERVIEW));
doneWaiting();
return true;
default:
return KeyHandler::defaultHandler(key, NULL);
}
}
bool fireAt(const Coords &coords, bool originAvatar) {
bool validObject = false;
bool hitsAvatar = false;
@ -772,233 +297,6 @@ bool fireAt(const Coords &coords, bool originAvatar) {
return objectHit;
}
void GameController::initMoons() {
int trammelphase = g_ultima->_saveGame->_trammelPhase,
feluccaphase = g_ultima->_saveGame->_feluccaPhase;
ASSERT(g_context != NULL, "Game context doesn't exist!");
ASSERT(g_ultima->_saveGame != NULL, "Savegame doesn't exist!");
//ASSERT(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 = moongateGetGateCoordsForPhase(oldTrammel);
if (gate)
g_context->_location->_map->_annotations->remove(*gate, g_context->_location->_map->translateFromRawTileIndex(0x40));
gate = moongateGetGateCoordsForPhase(g_ultima->_saveGame->_trammelPhase);
if (gate)
g_context->_location->_map->_annotations->add(*gate, g_context->_location->_map->translateFromRawTileIndex(0x40));
} else if (trammelSubphase == 1) {
gate = moongateGetGateCoordsForPhase(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 = moongateGetGateCoordsForPhase(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 = moongateGetGateCoordsForPhase(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 = moongateGetGateCoordsForPhase(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 = moongateGetGateCoordsForPhase(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 = moongateGetGateCoordsForPhase(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 = moongateGetGateCoordsForPhase(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:
screenMessage("%s\n", getDirectionName(event._dir));
break;
case TRANSPORT_SHIP:
if (event._result & MOVE_TURNED)
screenMessage("Turn %s!\n", getDirectionName(event._dir));
else if (event._result & MOVE_SLOWED)
screenMessage("%cSlow progress!%c\n", FG_GREY, FG_WHITE);
else
screenMessage("Sail %s!\n", getDirectionName(event._dir));
break;
case TRANSPORT_BALLOON:
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) != NULL) {
talkAtCoord(newx, newy, 1, NULL);
event.result = MOVE_SUCCEEDED | MOVE_END_TURN;
}*/
}
/* if we're still blocked */
if ((event._result & MOVE_BLOCKED) && !settings._filterMoveMessages) {
soundPlay(SOUND_BLOCKED, false);
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);
screenMessage("%cSlow progress!%c\n", FG_GREY, FG_WHITE);
} else {
soundPlay(SOUND_WALK_NORMAL);
}
}
}
/* exited map */
if (event._result & MOVE_EXIT_TO_PARENT) {
screenMessage("%cLeaving...%c\n", FG_GREY, FG_WHITE);
exitToParentMap();
g_music->play();
}
/* 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) {
Dungeon *dungeon = dynamic_cast<Dungeon *>(g_context->_location->_map);
Direction realDir = dirNormalize((Direction)g_ultima->_saveGame->_orientation, event._dir);
if (!settings._filterMoveMessages) {
if (event._userEvent) {
if (event._result & MOVE_TURNED) {
if (dirRotateCCW((Direction)g_ultima->_saveGame->_orientation) == realDir)
screenMessage("Turn Left\n");
else screenMessage("Turn Right\n");
}
/* show 'Advance' or 'Retreat' in dungeons */
else screenMessage("%s\n", realDir == g_ultima->_saveGame->_orientation ? "Advance" : "Retreat");
}
if (event._result & MOVE_BLOCKED)
screenMessage("%cBlocked!%c\n", FG_GREY, FG_WHITE);
}
/* if we're exiting the map, do this */
if (event._result & MOVE_EXIT_TO_PARENT) {
screenMessage("%cLeaving...%c\n", FG_GREY, FG_WHITE);
exitToParentMap();
g_music->play();
}
/* 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);
dng->_currentRoom = room;
/* set the map and start combat! */
CombatController *cc = new CombatController(dng->_roomMaps[room]);
cc->initDungeonRoom(room, dirReverse(realDir));
cc->begin();
}
}
}
/**
* Peers at a city from A-P (Lycaeum telescope) and functions like a gem
*/
@ -1062,48 +360,6 @@ void peer(bool useGem) {
g_game->_paused = false;
}
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);
screenCycle();
/*
* force pass if no commands within last 20 seconds
*/
Controller *controller = eventHandler->getController();
if (controller != NULL && (eventHandler->getController() == g_game ||
dynamic_cast<CombatController *>(eventHandler->getController()) != NULL) &&
gameTimeSinceLastCommand() > 20) {
/* pass the turn, and redraw the text area so the prompt is shown */
MetaEngine::executeAction(KEYBIND_PASS);
screenRedrawTextArea(TEXT_AREA_X, TEXT_AREA_Y, TEXT_AREA_W, TEXT_AREA_H);
}
}
}
/**
* Checks the hull integrity of the ship and handles
* the ship sinking, if necessary
@ -1138,83 +394,6 @@ void gameCheckHullIntegrity() {
}
}
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 (moongateFindActiveGateAt(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 (moongateIsEntryToShrineOfSpirituality(g_ultima->_saveGame->_trammelPhase, g_ultima->_saveGame->_feluccaPhase)) {
Shrine *shrine_spirituality;
shrine_spirituality = dynamic_cast<Shrine *>(mapMgr->get(MAP_SHRINE_SPIRITUALITY));
if (!g_context->_party->canEnterShrine(VIRT_SPIRITUALITY))
return true;
setMap(shrine_spirituality, 1, NULL);
g_music->play();
shrine_spirituality->enter();
}
return true;
}
return false;
}
/**
* Fixes objects initially loaded by saveGameMonstersRead,
* and alters movement behavior accordingly to match the creature
@ -1446,60 +625,6 @@ void gameSetActivePlayer(int player) {
}
}
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
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;
gameSpawnCreature(NULL);
}
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;
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();
}
/**
* Spawns a creature (m) just offscreen of the avatar.
* If (m==NULL) then it finds its own creature to spawn and spawns it.
@ -1626,22 +751,6 @@ void gameDestroyAllCreatures(void) {
g_context->_location->_map->alertGuards();
}
bool GameController::createBalloon(Map *map) {
ObjectDeque::iterator i;
/* see if the balloon has already been created (and not destroyed) */
for (i = map->_objects.begin(); i != map->_objects.end(); i++) {
Object *obj = *i;
if (obj->getTile().getTileType()->isBalloon())
return false;
}
const Tile *balloon = map->_tileset->getByName("balloon");
ASSERT(balloon, "no balloon tile found in tileset");
map->addObject(balloon->getId(), balloon->getId(), map->getLabel("balloon"));
return true;
}
// Colors assigned to reagents based on my best reading of them
// from the book of wisdom. Maybe we could use BOLD to distinguish
// the two grey and the two red reagents.

View File

@ -23,13 +23,13 @@
#ifndef ULTIMA4_GAME_H
#define ULTIMA4_GAME_H
#include "ultima/ultima4/events/controller.h"
#include "ultima/ultima4/events/event.h"
#include "ultima/ultima4/map/map.h"
#include "ultima/ultima4/controllers/game_controller.h"
#include "ultima/ultima4/core/observer.h"
#include "ultima/ultima4/sound/sound.h"
#include "ultima/ultima4/map/tileview.h"
#include "ultima/ultima4/core/types.h"
#include "ultima/ultima4/map/map.h"
#include "ultima/ultima4/map/tileview.h"
#include "ultima/ultima4/sound/sound.h"
namespace Ultima {
namespace Ultima4 {
@ -53,186 +53,9 @@ typedef enum {
VIEW_MIXTURES
} ViewMode;
/**
* A controller to read a player number.
*/
class ReadPlayerController : public ReadChoiceController {
public:
ReadPlayerController();
~ReadPlayerController();
bool keyPressed(int key) override;
int getPlayer();
int waitFor() override;
};
/**
* A controller to handle input for commands requiring a letter
* argument in the range 'a' - lastValidLetter.
*/
class AlphaActionController : public WaitableController<int> {
public:
AlphaActionController(char letter, const Common::String &p) : _lastValidLetter(letter), _prompt(p) {}
bool keyPressed(int key) override;
static int get(char lastValidLetter, const Common::String &prompt, EventHandler *eh = NULL);
private:
char _lastValidLetter;
Common::String _prompt;
};
/**
* Controls interaction while Ztats are being displayed.
*/
class ZtatsController : public WaitableController<void *> {
public:
bool keyPressed(int key) override;
};
class TurnCompleter {
public:
virtual ~TurnCompleter() {}
virtual void finishTurn() = 0;
};
/**
* 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 {
public:
GameController();
/* controller functions */
/**
* Keybinder actions
*/
void keybinder(KeybindingAction action) override;
/**
* The main key handler for the game. Interpretes each key as a
* command - 'a' for attack, 't' for talk, etc.
*/
bool keyPressed(int key) 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 = NULL);
/**
* 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();
/**
* 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);
/**
* 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);
TileView _mapArea;
bool _paused;
int _pausedTimer;
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);
};
extern GameController *g_game;
/* map and screen functions */
void gameSetViewMode(ViewMode newMode);
void gameUpdateScreen(void);
void gameUpdateScreen();
/* spell functions */
void gameSpellEffect(int spell, int player, Sound sound);
@ -242,16 +65,18 @@ bool gamePeerCity(int city, void *data);
void peer(bool useGem = true);
bool fireAt(const Coords &coords, bool originAvatar);
Direction gameGetDirection();
uint32 gameTimeSinceLastCommand();
/* checking functions */
void gameCheckHullIntegrity(void);
void gameCheckHullIntegrity();
/* creature functions */
bool creatureRangeAttack(const Coords &coords, Creature *m);
void gameCreatureCleanup(void);
void gameCreatureCleanup();
bool gameSpawnCreature(const class Creature *m);
void gameFixupObjects(Map *map);
void gameDestroyAllCreatures(void);
void gameDestroyAllCreatures();
void gameCreatureAttack(Creature *obj);
/* etc */
Common::String gameGetInput(int maxlen = 32);

View File

@ -20,24 +20,26 @@
*
*/
#include "ultima/ultima4/ultima4.h"
#include "ultima/ultima4/core/config.h"
#include "ultima/ultima4/game/intro.h"
#include "ultima/ultima4/core/error.h"
#include "ultima/ultima4/events/event.h"
#include "ultima/ultima4/gfx/imagemgr.h"
#include "ultima/ultima4/game/menu.h"
#include "ultima/ultima4/sound/music.h"
#include "ultima/ultima4/sound/sound.h"
#include "ultima/ultima4/game/player.h"
#include "ultima/ultima4/filesys/savegame.h"
#include "ultima/ultima4/gfx/screen.h"
#include "ultima/ultima4/game/menu.h"
#include "ultima/ultima4/controllers/read_string_controller.h"
#include "ultima/ultima4/controllers/read_choice_controller.h"
#include "ultima/ultima4/core/config.h"
#include "ultima/ultima4/core/utils.h"
#include "ultima/ultima4/core/error.h"
#include "ultima/ultima4/core/settings.h"
#include "ultima/ultima4/events/event.h"
#include "ultima/ultima4/filesys/savegame.h"
#include "ultima/ultima4/filesys/u4file.h"
#include "ultima/ultima4/gfx/imagemgr.h"
#include "ultima/ultima4/gfx/screen.h"
#include "ultima/ultima4/map/shrine.h"
#include "ultima/ultima4/map/tileset.h"
#include "ultima/ultima4/map/tilemap.h"
#include "ultima/ultima4/filesys/u4file.h"
#include "ultima/ultima4/core/utils.h"
#include "ultima/ultima4/sound/music.h"
#include "ultima/ultima4/sound/sound.h"
#include "ultima/ultima4/ultima4.h"
#include "common/savefile.h"
#include "common/system.h"

View File

@ -23,12 +23,12 @@
#ifndef ULTIMA4_INTRO_H
#define ULTIMA4_INTRO_H
#include "ultima/ultima4/events/controller.h"
#include "ultima/ultima4/game/menu.h"
#include "ultima/ultima4/controllers/controller.h"
#include "ultima/ultima4/core/observer.h"
#include "ultima/ultima4/filesys/savegame.h"
#include "ultima/ultima4/gfx/imageview.h"
#include "ultima/ultima4/game/menu.h"
#include "ultima/ultima4/game/textview.h"
#include "ultima/ultima4/gfx/imageview.h"
#include "ultima/ultima4/map/tileview.h"
namespace Ultima {

View File

@ -21,24 +21,25 @@
*/
#include "ultima/ultima4/game/item.h"
#include "ultima/ultima4/map/annotation.h"
#include "ultima/ultima4/game/codex.h"
#include "ultima/ultima4/map/combat.h"
#include "ultima/ultima4/game/context.h"
#include "ultima/ultima4/map/dungeon.h"
#include "ultima/ultima4/game/game.h"
#include "ultima/ultima4/map/location.h"
#include "ultima/ultima4/map/map.h"
#include "ultima/ultima4/map/mapmgr.h"
#include "ultima/ultima4/game/names.h"
#include "ultima/ultima4/game/player.h"
#include "ultima/ultima4/game/portal.h"
#include "ultima/ultima4/game/context.h"
#include "ultima/ultima4/game/weapon.h"
#include "ultima/ultima4/controllers/alpha_action_controller.h"
#include "ultima/ultima4/core/utils.h"
#include "ultima/ultima4/filesys/savegame.h"
#include "ultima/ultima4/gfx/screen.h"
#include "ultima/ultima4/map/annotation.h"
#include "ultima/ultima4/map/combat.h"
#include "ultima/ultima4/map/dungeon.h"
#include "ultima/ultima4/map/location.h"
#include "ultima/ultima4/map/map.h"
#include "ultima/ultima4/map/mapmgr.h"
#include "ultima/ultima4/map/tileset.h"
#include "ultima/ultima4/ultima4.h"
#include "ultima/ultima4/core/utils.h"
#include "ultima/ultima4/game/weapon.h"
namespace Ultima {
namespace Ultima4 {

View File

@ -20,25 +20,27 @@
*
*/
#include "ultima/ultima4/ultima4.h"
#include "ultima/ultima4/core/config.h"
#include "ultima/ultima4/game/person.h"
#include "ultima/ultima4/map/city.h"
#include "ultima/ultima4/game/context.h"
#include "ultima/ultima4/conversation/conversation.h"
#include "ultima/ultima4/events/event.h"
#include "ultima/ultima4/game/game.h" // Included for ReadPlayerController
#include "ultima/ultima4/map/location.h"
#include "ultima/ultima4/sound/music.h"
#include "ultima/ultima4/game/names.h"
#include "ultima/ultima4/game/player.h"
#include "ultima/ultima4/filesys/savegame.h"
#include "ultima/ultima4/core/settings.h"
#include "ultima/ultima4/game/stats.h"
#include "ultima/ultima4/core/types.h"
#include "ultima/ultima4/filesys/u4file.h"
#include "ultima/ultima4/core/utils.h"
#include "ultima/ultima4/game/context.h"
#include "ultima/ultima4/game/script.h"
#include "ultima/ultima4/controllers/read_choice_controller.h"
#include "ultima/ultima4/controllers/read_int_controller.h"
#include "ultima/ultima4/controllers/read_player_controller.h"
#include "ultima/ultima4/conversation/conversation.h"
#include "ultima/ultima4/core/config.h"
#include "ultima/ultima4/core/settings.h"
#include "ultima/ultima4/core/types.h"
#include "ultima/ultima4/core/utils.h"
#include "ultima/ultima4/events/event.h"
#include "ultima/ultima4/filesys/savegame.h"
#include "ultima/ultima4/filesys/u4file.h"
#include "ultima/ultima4/map/city.h"
#include "ultima/ultima4/map/location.h"
#include "ultima/ultima4/sound/music.h"
#include "ultima/ultima4/ultima4.h"
namespace Ultima {
namespace Ultima4 {

View File

@ -20,7 +20,16 @@
*
*/
#include "ultima/ultima4/ultima4.h"
#include "ultima/ultima4/map/combat.h"
#include "ultima/ultima4/map/annotation.h"
#include "ultima/ultima4/map/dungeon.h"
#include "ultima/ultima4/map/location.h"
#include "ultima/ultima4/map/mapmgr.h"
#include "ultima/ultima4/map/movement.h"
#include "ultima/ultima4/map/tileset.h"
#include "ultima/ultima4/controllers/read_choice_controller.h"
#include "ultima/ultima4/controllers/read_dir_controller.h"
#include "ultima/ultima4/controllers/ztats_controller.h"
#include "ultima/ultima4/core/debugger.h"
#include "ultima/ultima4/core/settings.h"
#include "ultima/ultima4/core/utils.h"
@ -38,14 +47,8 @@
#include "ultima/ultima4/game/stats.h"
#include "ultima/ultima4/game/weapon.h"
#include "ultima/ultima4/gfx/screen.h"
#include "ultima/ultima4/map/combat.h"
#include "ultima/ultima4/map/annotation.h"
#include "ultima/ultima4/map/dungeon.h"
#include "ultima/ultima4/map/location.h"
#include "ultima/ultima4/map/mapmgr.h"
#include "ultima/ultima4/map/movement.h"
#include "ultima/ultima4/map/tileset.h"
#include "ultima/shared/std/containers.h"
#include "ultima/ultima4/ultima4.h"
#include "common/system.h"
namespace Ultima {

View File

@ -25,14 +25,14 @@
#include "ultima/ultima4/map/direction.h"
#include "ultima/ultima4/map/map.h"
#include "ultima/ultima4/events/controller.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/core/observer.h"
#include "ultima/ultima4/game/player.h"
#include "ultima/ultima4/filesys/savegame.h"
#include "ultima/ultima4/core/types.h"
namespace Ultima {
namespace Ultima4 {

View File

@ -20,25 +20,28 @@
*
*/
#include "ultima/ultima4/ultima4.h"
#include "ultima/ultima4/core/config.h"
#include "ultima/ultima4/map/shrine.h"
#include "ultima/ultima4/map/annotation.h"
#include "ultima/ultima4/game/context.h"
#include "ultima/ultima4/events/event.h"
#include "ultima/ultima4/game/game.h"
#include "ultima/ultima4/gfx/imagemgr.h"
#include "ultima/ultima4/map/location.h"
#include "ultima/ultima4/map/mapmgr.h"
#include "ultima/ultima4/map/shrine.h"
#include "ultima/ultima4/controllers/read_choice_controller.h"
#include "ultima/ultima4/controllers/read_string_controller.h"
#include "ultima/ultima4/controllers/wait_controller.h"
#include "ultima/ultima4/core/config.h"
#include "ultima/ultima4/core/settings.h"
#include "ultima/ultima4/core/types.h"
#include "ultima/ultima4/events/event.h"
#include "ultima/ultima4/game/context.h"
#include "ultima/ultima4/game/game.h"
#include "ultima/ultima4/game/creature.h"
#include "ultima/ultima4/sound/music.h"
#include "ultima/ultima4/game/names.h"
#include "ultima/ultima4/game/player.h"
#include "ultima/ultima4/game/portal.h"
#include "ultima/ultima4/gfx/imagemgr.h"
#include "ultima/ultima4/gfx/screen.h"
#include "ultima/ultima4/core/settings.h"
#include "ultima/ultima4/map/tileset.h"
#include "ultima/ultima4/core/types.h"
#include "ultima/ultima4/sound/music.h"
#include "ultima/ultima4/ultima4.h"
namespace Ultima {
namespace Ultima4 {

View File

@ -108,7 +108,7 @@ static const KeybindingRecord CHEAT_KEYS[] = {
{ KEYBIND_CHEAT_UP, "CHEAT-UP", "Up Level", "up", "A+UP", nullptr },
{ KEYBIND_CHEAT_DOWN, "CHEAT-DOWN", "Down Level", "down", "A+DOWN", nullptr },
{ KEYBIND_CHEAT_VIRTUE, "CHEAT-VIRTUE", "Grant Virtue", "virtue", "A+v", nullptr },
{ KEYBIND_CHEAT_WIND, "CHEAT-WIND", "Change Wind", "wind", "A+w", nullptr },
{ KEYBIND_CHEAT_WIND, "CHEAT-WIND", "Change Wind", "wind", "A+w", nullptr },
{ KEYBIND_NONE, nullptr, nullptr, nullptr, nullptr, nullptr }
};