Initial commit
This commit is contained in:
600
engines/tsage/user_interface.cpp
Normal file
600
engines/tsage/user_interface.cpp
Normal file
@@ -0,0 +1,600 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tsage/user_interface.h"
|
||||
#include "tsage/core.h"
|
||||
#include "tsage/tsage.h"
|
||||
#include "tsage/blue_force/blueforce_dialogs.h"
|
||||
#include "tsage/blue_force/blueforce_logic.h"
|
||||
#include "tsage/ringworld2/ringworld2_logic.h"
|
||||
|
||||
namespace TsAGE {
|
||||
|
||||
void StripProxy::process(Event &event) {
|
||||
if (_action)
|
||||
_action->process(event);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
void UIElement::synchronize(Serializer &s) {
|
||||
BackgroundSceneObject::synchronize(s);
|
||||
if (s.getVersion() < 15) {
|
||||
int useless = 0;
|
||||
s.syncAsSint16LE(useless);
|
||||
}
|
||||
s.syncAsSint16LE(_enabled);
|
||||
s.syncAsSint16LE(_frameNum);
|
||||
}
|
||||
|
||||
void UIElement::setup(int visage, int stripNum, int frameNum, int posX, int posY, int priority) {
|
||||
_frameNum = frameNum;
|
||||
_enabled = true;
|
||||
|
||||
SceneObject::setup(visage, stripNum, frameNum, posX, posY, priority);
|
||||
}
|
||||
|
||||
void UIElement::setEnabled(bool flag) {
|
||||
if (_enabled != flag) {
|
||||
_enabled = flag;
|
||||
setFrame(_enabled ? _frameNum : _frameNum + 2);
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
void UIQuestion::process(Event &event) {
|
||||
if (event.eventType == EVENT_BUTTON_DOWN) {
|
||||
CursorType currentCursor = GLOBALS._events.getCursor();
|
||||
GLOBALS._events.hideCursor();
|
||||
showDescription(currentCursor);
|
||||
|
||||
event.handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void UIQuestion::showDescription(CursorType cursor) {
|
||||
switch (g_vm->getGameID()) {
|
||||
case GType_BlueForce:
|
||||
if (cursor == INV_FOREST_RAP) {
|
||||
// Forest rap item has a graphical display
|
||||
showItem(5, 1, 1);
|
||||
} else {
|
||||
// Display object description
|
||||
SceneItem::display2(9001, (int)cursor);
|
||||
}
|
||||
break;
|
||||
case GType_Ringworld2:
|
||||
if ((cursor == R2_COM_SCANNER) || (cursor == R2_COM_SCANNER_2)) {
|
||||
// Show communicator
|
||||
Ringworld2::SceneExt *scene = static_cast<Ringworld2::SceneExt *>
|
||||
(R2_GLOBALS._sceneManager._scene);
|
||||
if (!scene->_sceneAreas.contains(R2_GLOBALS._scannerDialog))
|
||||
R2_GLOBALS._scannerDialog->setup2(4, 1, 1, 160, 125);
|
||||
} else {
|
||||
// Show object description
|
||||
SceneItem::display2(3, (int)cursor);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void UIQuestion::setEnabled(bool flag) {
|
||||
if (_enabled != flag) {
|
||||
UIElement::setEnabled(flag);
|
||||
T2_GLOBALS._uiElements.draw();
|
||||
}
|
||||
}
|
||||
|
||||
void UIQuestion::showItem(int resNum, int rlbNum, int frameNum) {
|
||||
GfxDialog::setPalette();
|
||||
|
||||
// Get the item to display
|
||||
GfxSurface objImage = surfaceFromRes(resNum, rlbNum, frameNum);
|
||||
Rect imgRect;
|
||||
imgRect.resize(objImage, 0, 0, 100);
|
||||
imgRect.center(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
|
||||
|
||||
// Save the area behind where the image will be displayed
|
||||
GfxSurface *savedArea = surfaceGetArea(GLOBALS.gfxManager().getSurface(), imgRect);
|
||||
|
||||
// Draw the image
|
||||
GLOBALS.gfxManager().copyFrom(objImage, imgRect);
|
||||
|
||||
// Wait for a press
|
||||
GLOBALS._events.waitForPress();
|
||||
|
||||
// Restore the old area
|
||||
GLOBALS.gfxManager().copyFrom(*savedArea, imgRect);
|
||||
delete savedArea;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
void UIScore::postInit(SceneObjectList *OwnerList) {
|
||||
int xp = 266;
|
||||
_digit3.setup(1, 6, 1, xp, 180, 255);
|
||||
_digit3.reposition();
|
||||
xp += 7;
|
||||
_digit2.setup(1, 6, 1, xp, 180, 255);
|
||||
_digit2.reposition();
|
||||
xp += 7;
|
||||
_digit1.setup(1, 6, 1, xp, 180, 255);
|
||||
_digit1.reposition();
|
||||
xp += 7;
|
||||
_digit0.setup(1, 6, 1, xp, 180, 255);
|
||||
_digit0.reposition();
|
||||
}
|
||||
|
||||
void UIScore::draw() {
|
||||
_digit3.draw();
|
||||
_digit2.draw();
|
||||
_digit1.draw();
|
||||
_digit0.draw();
|
||||
}
|
||||
|
||||
void UIScore::updateScore() {
|
||||
int score = T2_GLOBALS._uiElements._scoreValue;
|
||||
|
||||
_digit3.setFrame(score / 1000 + 1); score %= 1000;
|
||||
_digit2.setFrame(score / 100 + 1); score %= 100;
|
||||
_digit1.setFrame(score / 10 + 1); score %= 10;
|
||||
_digit0.setFrame(score + 1);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
UIInventorySlot::UIInventorySlot(): UIElement() {
|
||||
_objIndex = 0;
|
||||
_object = NULL;
|
||||
}
|
||||
|
||||
void UIInventorySlot::synchronize(Serializer &s) {
|
||||
UIElement::synchronize(s);
|
||||
s.syncAsSint16LE(_objIndex);
|
||||
SYNC_POINTER(_object);
|
||||
}
|
||||
|
||||
void UIInventorySlot::process(Event &event) {
|
||||
if (event.eventType == EVENT_BUTTON_DOWN) {
|
||||
event.handled = true;
|
||||
|
||||
// Check if game has a select item handler, and if so, give it a chance to check
|
||||
// whether something special happens when the item is selected
|
||||
if (!T2_GLOBALS._onSelectItem || !T2_GLOBALS._onSelectItem((CursorType)_objIndex))
|
||||
_object->setCursor();
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
UIInventoryScroll::UIInventoryScroll() {
|
||||
_isLeft = false;
|
||||
}
|
||||
|
||||
void UIInventoryScroll::synchronize(Serializer &s) {
|
||||
UIElement::synchronize(s);
|
||||
s.syncAsSint16LE(_isLeft);
|
||||
}
|
||||
|
||||
void UIInventoryScroll::process(Event &event) {
|
||||
switch (event.eventType) {
|
||||
case EVENT_BUTTON_DOWN:
|
||||
// Draw the button as selected
|
||||
toggle(true);
|
||||
|
||||
// Wait for the mouse to be released
|
||||
g_globals->_events.waitForPress(EVENT_BUTTON_UP);
|
||||
|
||||
// Restore unselected version
|
||||
toggle(false);
|
||||
|
||||
// Scroll the inventory as necessary
|
||||
T2_GLOBALS._uiElements.scrollInventory(_isLeft);
|
||||
event.handled = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void UIInventoryScroll::toggle(bool pressed) {
|
||||
if (_enabled) {
|
||||
setFrame(pressed ? (_frameNum + 1) : _frameNum);
|
||||
T2_GLOBALS._uiElements.draw();
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
UICollection::UICollection(): EventHandler() {
|
||||
_clearScreen = false;
|
||||
_visible = false;
|
||||
_cursorChanged = false;
|
||||
}
|
||||
|
||||
void UICollection::setup(const Common::Point &pt) {
|
||||
_position = pt;
|
||||
_bounds.left = _bounds.right = pt.x;
|
||||
_bounds.top = _bounds.bottom = pt.y;
|
||||
}
|
||||
|
||||
void UICollection::hide() {
|
||||
erase();
|
||||
_visible = false;
|
||||
}
|
||||
|
||||
void UICollection::show() {
|
||||
_visible = true;
|
||||
draw();
|
||||
}
|
||||
|
||||
void UICollection::erase() {
|
||||
if (_clearScreen) {
|
||||
Rect tempRect(0, UI_INTERFACE_Y, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
GLOBALS._screen.fillRect(tempRect, 0);
|
||||
GLOBALS._sceneManager._scene->_backSurface.fillRect(tempRect, 0);
|
||||
_clearScreen = false;
|
||||
}
|
||||
}
|
||||
|
||||
void UICollection::resetClear() {
|
||||
_clearScreen = false;
|
||||
}
|
||||
|
||||
void UICollection::draw() {
|
||||
if (_visible) {
|
||||
// Temporarily reset the sceneBounds when drawing UI elements to force them on-screen
|
||||
Rect savedBounds = g_globals->_sceneManager._scene->_sceneBounds;
|
||||
g_globals->_sceneManager._scene->_sceneBounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
|
||||
// Draw the elements onto the background
|
||||
for (uint idx = 0; idx < _objList.size(); ++idx)
|
||||
_objList[idx]->draw();
|
||||
|
||||
// Draw the resulting UI onto the screen
|
||||
GLOBALS._screen.copyFrom(GLOBALS._sceneManager._scene->_backSurface,
|
||||
Rect(0, UI_INTERFACE_Y, SCREEN_WIDTH, SCREEN_HEIGHT),
|
||||
Rect(0, UI_INTERFACE_Y, SCREEN_WIDTH, SCREEN_HEIGHT));
|
||||
|
||||
if (g_vm->getGameID() == GType_Ringworld2)
|
||||
r2rDrawFrame();
|
||||
|
||||
_clearScreen = 1;
|
||||
g_globals->_sceneManager._scene->_sceneBounds = savedBounds;
|
||||
}
|
||||
}
|
||||
|
||||
void UICollection::r2rDrawFrame() {
|
||||
Visage visage;
|
||||
visage.setVisage(2, 1);
|
||||
GfxSurface vertLineLeft = visage.getFrame(1);
|
||||
GfxSurface vertLineRight = visage.getFrame(3);
|
||||
GfxSurface horizLine = visage.getFrame(2);
|
||||
|
||||
GLOBALS._screen.copyFrom(horizLine, 0, 0);
|
||||
GLOBALS._screen.copyFrom(vertLineLeft, 0, 3);
|
||||
GLOBALS._screen.copyFrom(vertLineRight, SCREEN_WIDTH - 4, 3);
|
||||
|
||||
// Restrict drawing area to exclude the borders at the edge of the screen
|
||||
R2_GLOBALS._screen._clipRect = Rect(4, 3, SCREEN_WIDTH - 4,
|
||||
SCREEN_HEIGHT - 3);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
UIElements::UIElements(): UICollection() {
|
||||
if (g_vm->getGameID() == GType_Ringworld2)
|
||||
_cursorVisage.setVisage(5, 1);
|
||||
else
|
||||
_cursorVisage.setVisage(1, 5);
|
||||
g_saver->addLoadNotifier(&UIElements::loadNotifierProc);
|
||||
|
||||
_slotStart = 0;
|
||||
_scoreValue = 0;
|
||||
_active = false;
|
||||
}
|
||||
|
||||
void UIElements::synchronize(Serializer &s) {
|
||||
UICollection::synchronize(s);
|
||||
|
||||
s.syncAsSint16LE(_slotStart);
|
||||
s.syncAsSint16LE(_scoreValue);
|
||||
s.syncAsByte(_active);
|
||||
|
||||
int count = _itemList.size();
|
||||
s.syncAsSint16LE(count);
|
||||
if (s.isLoading()) {
|
||||
// Load in item list
|
||||
_itemList.clear();
|
||||
|
||||
for (int idx = 0; idx < count; ++idx) {
|
||||
int itemId = 0;
|
||||
s.syncAsSint16LE(itemId);
|
||||
_itemList.push_back(itemId);
|
||||
}
|
||||
} else {
|
||||
// Save item list
|
||||
for (int idx = 0; idx < count; ++idx) {
|
||||
int itemId = _itemList[idx];
|
||||
s.syncAsSint16LE(itemId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UIElements::process(Event &event) {
|
||||
if (_clearScreen && GLOBALS._player._enabled &&
|
||||
((g_vm->getGameID() != GType_BlueForce) || (GLOBALS._sceneManager._sceneNumber != 50))) {
|
||||
if (_bounds.contains(event.mousePos)) {
|
||||
// Cursor inside UI area
|
||||
if (!_cursorChanged) {
|
||||
if (GLOBALS._events.isInventoryIcon()) {
|
||||
// Inventory icon being displayed, so leave alone
|
||||
} else {
|
||||
// Change to the inventory use cursor
|
||||
int cursorId = (g_vm->getGameID() == GType_Ringworld2) ? 11 : 6;
|
||||
GfxSurface surface = _cursorVisage.getFrame(cursorId);
|
||||
GLOBALS._events.setCursor(surface);
|
||||
}
|
||||
_cursorChanged = true;
|
||||
}
|
||||
|
||||
// Pass event to any element that the cursor falls on
|
||||
for (int idx = (int)_objList.size() - 1; idx >= 0; --idx) {
|
||||
if (_objList[idx]->_bounds.contains(event.mousePos) && _objList[idx]->_enabled) {
|
||||
_objList[idx]->process(event);
|
||||
if (event.handled)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.eventType == EVENT_BUTTON_DOWN)
|
||||
event.handled = true;
|
||||
|
||||
} else if (_cursorChanged) {
|
||||
// Cursor outside UI area, so reset as necessary
|
||||
GLOBALS._events.setCursor(GLOBALS._events.getCursor());
|
||||
_cursorChanged = false;
|
||||
/*
|
||||
SceneExt *scene = (SceneExt *)GLOBALS._sceneManager._scene;
|
||||
if (scene->_focusObject) {
|
||||
GfxSurface surface = _cursorVisage.getFrame(7);
|
||||
GLOBALS._events.setCursor(surface);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UIElements::setup(const Common::Point &pt) {
|
||||
_slotStart = 0;
|
||||
_itemList.clear();
|
||||
_scoreValue = 0;
|
||||
_active = true;
|
||||
UICollection::setup(pt);
|
||||
hide();
|
||||
|
||||
_background.setup(1, 3, 1, 0, 0, 255);
|
||||
add(&_background);
|
||||
|
||||
// Set up the inventory slots
|
||||
int xp = 0;
|
||||
for (int idx = 0; idx < 4; ++idx) {
|
||||
UIElement *item = NULL;
|
||||
switch (idx) {
|
||||
case 0:
|
||||
item = &_slot1;
|
||||
break;
|
||||
case 1:
|
||||
item = &_slot2;
|
||||
break;
|
||||
case 2:
|
||||
item = &_slot3;
|
||||
break;
|
||||
case 3:
|
||||
item = &_slot4;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
xp = idx * 63 + 2;
|
||||
if (g_vm->getGameID() == GType_BlueForce) {
|
||||
item->setup(9, 1, idx, xp, 4, 255);
|
||||
} else {
|
||||
item->setup(7, 1, idx, xp, 4, 255);
|
||||
}
|
||||
add(item);
|
||||
}
|
||||
|
||||
// Setup bottom-right hand buttons
|
||||
xp = (g_vm->getGameID() == GType_Ringworld2) ? 255 : 253;
|
||||
int yp = (g_vm->getGameID() == GType_BlueForce) ? 16 : 17;
|
||||
_question.setup(1, 4, 7, xp, yp, 255);
|
||||
_question.setEnabled(false);
|
||||
add(&_question);
|
||||
|
||||
xp += 21;
|
||||
_scrollLeft.setup(1, 4, 1, xp, yp, 255);
|
||||
add(&_scrollLeft);
|
||||
_scrollLeft._isLeft = true;
|
||||
|
||||
xp += (g_vm->getGameID() == GType_Ringworld2) ? 21 : 22;
|
||||
_scrollRight.setup(1, 4, 4, xp, yp, 255);
|
||||
add(&_scrollRight);
|
||||
_scrollRight._isLeft = false;
|
||||
|
||||
switch (g_vm->getGameID()) {
|
||||
case GType_BlueForce:
|
||||
// Set up the score
|
||||
_score.postInit();
|
||||
add(&_score);
|
||||
break;
|
||||
case GType_Ringworld2:
|
||||
// Set up the character display
|
||||
_character.setup(1, 5, R2_GLOBALS._player._characterIndex, 285, 11, 255);
|
||||
add(&_character);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Set interface area
|
||||
_bounds = Rect(0, UI_INTERFACE_Y - 1, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
|
||||
updateInventory();
|
||||
}
|
||||
|
||||
void UIElements::add(UIElement *obj) {
|
||||
// Add object
|
||||
assert(_objList.size() < 12);
|
||||
_objList.push_back(obj);
|
||||
|
||||
obj->setPosition(Common::Point(_bounds.left + obj->_position.x, _bounds.top + obj->_position.y));
|
||||
obj->reposition();
|
||||
|
||||
GfxSurface s = obj->getFrame();
|
||||
s.draw(obj->_position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles updating the visual inventory in the user interface
|
||||
*/
|
||||
void UIElements::updateInventory(int objectNumber) {
|
||||
switch (g_vm->getGameID()) {
|
||||
case GType_BlueForce:
|
||||
// Update the score
|
||||
_score.updateScore();
|
||||
break;
|
||||
case GType_Ringworld2:
|
||||
_character.setFrame(R2_GLOBALS._player._characterIndex);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
updateInvList();
|
||||
|
||||
// Enable scroll buttons if the player has more than four items
|
||||
if (_itemList.size() > 4) {
|
||||
_scrollLeft.setEnabled(true);
|
||||
_scrollRight.setEnabled(true);
|
||||
} else {
|
||||
_scrollLeft.setEnabled(false);
|
||||
_scrollRight.setEnabled(false);
|
||||
}
|
||||
|
||||
// Handle cropping the slots start within inventory
|
||||
int lastPage = (_itemList.size() - 1) / 4 + 1;
|
||||
if (_slotStart < 0)
|
||||
_slotStart = lastPage - 1;
|
||||
else if (_slotStart > (lastPage - 1))
|
||||
_slotStart = 0;
|
||||
|
||||
// Handle changing the page, if necessary, to ensure an optionally supplied
|
||||
// object number will be on-screen
|
||||
if (objectNumber != 0) {
|
||||
for (uint idx = 0; idx < _itemList.size(); ++idx) {
|
||||
if (_itemList[idx] == objectNumber) {
|
||||
_slotStart = idx / 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle refreshing slot graphics
|
||||
UIInventorySlot *slotList[4] = { &_slot1, &_slot2, &_slot3, &_slot4 };
|
||||
|
||||
// Loop through the inventory objects
|
||||
SynchronizedList<InvObject *>::iterator i;
|
||||
int objIndex = 0;
|
||||
for (i = GLOBALS._inventory->_itemList.begin(); i != GLOBALS._inventory->_itemList.end(); ++i, ++objIndex) {
|
||||
InvObject *obj = *i;
|
||||
|
||||
// Check whether the object is in any of the four inventory slots
|
||||
for (int slotIndex = 0; slotIndex < 4; ++slotIndex) {
|
||||
int idx = _slotStart * 4 + slotIndex;
|
||||
int objectIdx = (idx < (int)_itemList.size()) ? _itemList[idx] : 0;
|
||||
|
||||
if (objectIdx == objIndex) {
|
||||
UIInventorySlot *slot = slotList[slotIndex];
|
||||
|
||||
slot->_objIndex = objIndex;
|
||||
slot->_object = obj;
|
||||
slot->setVisage(obj->_visage);
|
||||
slot->setStrip(obj->_strip);
|
||||
slot->setFrame(obj->_frame);
|
||||
|
||||
slot->reposition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh the display if necessary
|
||||
if (_active)
|
||||
draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the list of the indexes of items in the player's inventory
|
||||
*/
|
||||
void UIElements::updateInvList() {
|
||||
// Update the index list of items in the player's inventory
|
||||
_itemList.clear();
|
||||
|
||||
SynchronizedList<InvObject *>::iterator i;
|
||||
int itemIndex = 0;
|
||||
for (i = GLOBALS._inventory->_itemList.begin(); i != GLOBALS._inventory->_itemList.end(); ++i, ++itemIndex) {
|
||||
InvObject *invObject = *i;
|
||||
if (invObject->inInventory())
|
||||
_itemList.push_back(itemIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the game score
|
||||
*/
|
||||
void UIElements::addScore(int amount) {
|
||||
_scoreValue += amount;
|
||||
T2_GLOBALS._inventorySound.play(0);
|
||||
updateInventory();
|
||||
}
|
||||
|
||||
/*
|
||||
* Scroll the inventory slots
|
||||
*/
|
||||
void UIElements::scrollInventory(bool isLeft) {
|
||||
if (isLeft)
|
||||
--_slotStart;
|
||||
else
|
||||
++_slotStart;
|
||||
|
||||
updateInventory();
|
||||
}
|
||||
|
||||
void UIElements::loadNotifierProc(bool postFlag) {
|
||||
if (postFlag && T2_GLOBALS._uiElements._active)
|
||||
T2_GLOBALS._uiElements.show();
|
||||
}
|
||||
|
||||
} // End of namespace TsAGE
|
||||
Reference in New Issue
Block a user