/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "ultima/ultima1/u1gfx/view_game.h"
#include "ultima/shared/actions/huh.h"
#include "ultima/shared/actions/pass.h"
#include "ultima/shared/maps/map.h"
#include "ultima/ultima1/game.h"
#include "ultima/ultima1/u1gfx/drawing_support.h"
#include "ultima/ultima1/u1gfx/info.h"
#include "ultima/ultima1/u1gfx/status.h"
#include "ultima/ultima1/u1gfx/viewport_dungeon.h"
#include "ultima/ultima1/u1gfx/viewport_map.h"
#include "ultima/ultima1/actions/map_action.h"
#include "ultima/ultima1/actions/move.h"
#include "ultima/ultima1/actions/attack.h"
#include "ultima/ultima1/actions/quit.h"
#include "ultima/ultima1/actions/ready.h"
#include "ultima/ultima1/actions/stats.h"
#include "ultima/ultima1/core/resources.h"
#include "ultima/shared/gfx/text_cursor.h"
#include "ultima/shared/engine/messages.h"
namespace Ultima {
namespace Ultima1 {
namespace Actions {
MAP_ACTION(Board, 1, board)
MAP_ACTION(Cast, 2, cast)
MAP_ACTION(Drop, 3, drop)
MAP_ACTION_END_TURN(Enter, 4, enter)
MAP_ACTION_END_TURN(Get, 6, get)
MAP_ACTION_END_TURN(HyperJump, 7, hyperjump)
MAP_ACTION_END_TURN(Inform, 8, inform)
MAP_ACTION_END_TURN(Climb, 10, climb)
MAP_ACTION_END_TURN(Open, 14, open)
MAP_ACTION_END_TURN(Steal, 18, steal)
MAP_ACTION(Transact, 19, talk)
MAP_ACTION_END_TURN(Unlock, 20, unlock)
MAP_ACTION_END_TURN(ViewChange, 21, view)
MAP_ACTION_END_TURN(ExitTransport, 23, disembark)
}
namespace U1Gfx {
BEGIN_MESSAGE_MAP(ViewGame, Shared::Gfx::VisualContainer)
ON_MESSAGE(ShowMsg)
ON_MESSAGE(EndOfTurnMsg)
ON_MESSAGE(FrameMsg)
ON_MESSAGE(CharacterInputMsg)
END_MESSAGE_MAP()
ViewGame::ViewGame(TreeItem *parent) : Shared::Gfx::VisualContainer("Game", Rect(0, 0, 320, 200), parent), _frameCtr(0) {
_info = new Info(this);
_status = new Status(this);
_viewportDungeon = new ViewportDungeon(this);
Ultima1Game *game = static_cast(getGame());
_viewportMap = new ViewportMap(this);
_actions.resize(22);
_actions[0] = new Actions::Move(this);
_actions[1] = new Shared::Actions::Huh(this, game->_res->HUH);
_actions[2] = new Actions::Attack(this);
_actions[3] = new Actions::Board(this);
_actions[4] = new Actions::Cast(this);
_actions[5] = new Actions::Drop(this);
_actions[6] = new Actions::Enter(this);
_actions[7] = new Actions::Fire(this);
_actions[8] = new Actions::Get(this);
_actions[9] = new Actions::HyperJump(this);
_actions[10] = new Actions::Inform(this);
_actions[11] = new Actions::Climb(this);
_actions[12] = new Actions::Open(this);
_actions[13] = new Shared::Actions::Pass(this, game->_res->ACTION_NAMES[15]);
_actions[14] = new Actions::Quit(this);
_actions[15] = new Actions::Ready(this);
_actions[16] = new Actions::Steal(this);
_actions[17] = new Actions::Transact(this);
_actions[18] = new Actions::Unlock(this);
_actions[19] = new Actions::ViewChange(this);
_actions[20] = new Actions::ExitTransport(this);
_actions[21] = new Actions::Stats(this);
}
ViewGame::~ViewGame() {
delete _info;
delete _status;
delete _viewportDungeon;
delete _viewportMap;
for (uint idx = 0; idx < _actions.size(); ++idx)
delete _actions[idx];
}
void ViewGame::draw() {
Shared::Gfx::VisualSurface s = getSurface();
if (_isDirty) {
// Draw the overal frame
s.clear();
DrawingSupport ds(s);
ds.drawGameFrame();
drawIndicators();
setDirty();
}
if (_info->isDirty())
_info->draw();
if (_status->isDirty())
_status->draw();
Maps::Ultima1Map *map = static_cast(getGame()->getMap());
switch (map->_mapType) {
case Maps::MAP_DUNGEON:
_viewportDungeon->draw();
break;
default:
_viewportMap->draw();
break;
}
_isDirty = false;
}
void ViewGame::drawIndicators() {
Ultima1Game *game = static_cast(getGame());
Maps::Ultima1Map *map = static_cast(game->getMap());
Shared::Gfx::VisualSurface s = getSurface();
DrawingSupport ds(s);
if (map->_mapType == Maps::MAP_DUNGEON) {
// Draw the dungeon level indicator
ds.drawRightArrow(TextPoint(15, 0));
s.writeString(game->_res->DUNGEON_LEVEL, TextPoint(16, 0));
s.writeString(Common::String::format("%2d", map->getLevel()), TextPoint(23, 0));
ds.drawLeftArrow(TextPoint(26, 0));
// Draw the current direction
const char *dir = game->_res->DIRECTION_NAMES[map->getDirection() - 1];
ds.drawRightArrow(TextPoint(16, 19));
s.writeString(" ", TextPoint(17, 19));
s.writeString(dir, TextPoint(19 - (7 - strlen(dir)) / 2, 19));
ds.drawLeftArrow(TextPoint(24, 19));
}
}
bool ViewGame::ShowMsg(CShowMsg *msg) {
// Set the info area to prompt for a command
Shared::CInfoGetCommandKeypress cmdMsg(this);
cmdMsg.execute(this);
return true;
}
bool ViewGame::EndOfTurnMsg(CEndOfTurnMsg *msg) {
// Set the info area to prompt for the next command
Shared::CInfoGetCommandKeypress cmdMsg(this);
cmdMsg.execute(this);
return false;
}
#define FRAME_REDUCTION_RATE 5
bool ViewGame::FrameMsg(CFrameMsg *msg) {
if (_frameCtr == FRAME_REDUCTION_RATE) {
// Ignore frame message at the start of passing reduced frame rate to child views
return false;
} else if (++_frameCtr == FRAME_REDUCTION_RATE) {
msg->execute(this, nullptr, Shared::MSGFLAG_SCAN);
_frameCtr = 0;
}
return true;
}
/**
* Dispatch action
*/
template
void dispatchKey(ViewGame *game) {
T dMsg;
dMsg.execute(game);
}
#define CHECK(KEYCODE, MSG_CLASS) else if (msg->_keyState.keycode == KEYCODE) { dispatchKey(this); }
bool ViewGame::checkMovement(const Common::KeyState &keyState) {
Shared::Maps::Direction dir = Shared::Maps::MapWidget::directionFromKey(keyState.keycode);
switch (dir) {
case Shared::Maps::DIR_WEST: {
if (keyState.flags & Common::KBD_SHIFT) {
Shared::CAttackMsg attack(Shared::Maps::DIR_LEFT);
attack.execute(this);
} else {
Shared::CMoveMsg move(Shared::Maps::DIR_LEFT);
move.execute(this);
}
break;
}
case Shared::Maps::DIR_EAST: {
if (keyState.flags & Common::KBD_SHIFT) {
Shared::CAttackMsg attack(Shared::Maps::DIR_RIGHT);
attack.execute(this);
} else {
Shared::CMoveMsg move(Shared::Maps::DIR_RIGHT);
move.execute(this);
}
break;
}
case Shared::Maps::DIR_UP: {
if (keyState.flags & Common::KBD_SHIFT) {
Shared::CAttackMsg attack(Shared::Maps::DIR_UP);
attack.execute(this);
} else {
Shared::CMoveMsg move(Shared::Maps::DIR_UP);
move.execute(this);
}
break;
}
case Shared::Maps::DIR_DOWN: {
if (keyState.flags & Common::KBD_SHIFT) {
Shared::CAttackMsg attack(Shared::Maps::DIR_DOWN);
attack.execute(this);
} else {
Shared::CMoveMsg move(Shared::Maps::DIR_DOWN);
move.execute(this);
}
break;
}
default:
return false;
}
return true;
}
bool ViewGame::CharacterInputMsg(CCharacterInputMsg *msg) {
if (checkMovement(msg->_keyState)) {}
CHECK(Common::KEYCODE_a, Shared::CAttackMsg)
CHECK(Common::KEYCODE_b, Shared::CBoardMsg)
CHECK(Common::KEYCODE_c, Shared::CCastMsg)
CHECK(Common::KEYCODE_d, Shared::CDropMsg)
CHECK(Common::KEYCODE_e, Shared::CEnterMsg)
CHECK(Common::KEYCODE_f, Shared::CFireMsg)
CHECK(Common::KEYCODE_g, Shared::CGetMsg)
CHECK(Common::KEYCODE_h, Shared::CHyperJumpMsg)
CHECK(Common::KEYCODE_i, Shared::CInformMsg)
CHECK(Common::KEYCODE_k, Shared::CClimbMsg)
CHECK(Common::KEYCODE_o, Shared::COpenMsg)
CHECK(Common::KEYCODE_q, Shared::CQuitMsg)
CHECK(Common::KEYCODE_r, Shared::CReadyMsg)
CHECK(Common::KEYCODE_s, Shared::CStealMsg)
CHECK(Common::KEYCODE_t, Shared::CTransactMsg)
CHECK(Common::KEYCODE_u, Shared::CUnlockMsg)
CHECK(Common::KEYCODE_v, Shared::CViewChangeMsg)
CHECK(Common::KEYCODE_x, Shared::CExitTransportMsg)
CHECK(Common::KEYCODE_z, Shared::CStatsMsg)
CHECK(Common::KEYCODE_SPACE, Shared::CPassMsg)
else {
// Fallback for unknown key
dispatchKey(this);
}
return true;
}
} // End of namespace U1Gfx
} // End of namespace Shared
} // End of namespace Ultima