Initial commit
This commit is contained in:
448
engines/ultima/ultima4/views/dungeonview.cpp
Normal file
448
engines/ultima/ultima4/views/dungeonview.cpp
Normal file
@@ -0,0 +1,448 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/core/config.h"
|
||||
#include "ultima/ultima4/core/settings.h"
|
||||
#include "ultima/ultima4/gfx/image.h"
|
||||
#include "ultima/ultima4/gfx/imagemgr.h"
|
||||
#include "ultima/ultima4/gfx/screen.h"
|
||||
#include "ultima/ultima4/map/tileanim.h"
|
||||
#include "ultima/ultima4/map/tileset.h"
|
||||
#include "ultima/ultima4/views/dungeonview.h"
|
||||
#include "ultima/ultima4/ultima4.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
DungeonView *DungeonView::_instance = nullptr;
|
||||
|
||||
DungeonView::DungeonView(int x, int y, int columns, int rows) : TileView(x, y, rows, columns)
|
||||
, _screen3dDungeonViewEnabled(true) {
|
||||
}
|
||||
|
||||
DungeonView *DungeonView::getInstance() {
|
||||
if (!_instance) {
|
||||
_instance = new DungeonView(BORDER_WIDTH, BORDER_HEIGHT, VIEWPORT_W, VIEWPORT_H);
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
|
||||
void DungeonView::display(Context *c, TileView *view) {
|
||||
int x, y;
|
||||
|
||||
// 1st-person perspective
|
||||
if (_screen3dDungeonViewEnabled) {
|
||||
// Note: This shouldn't go above 4, unless we check opaque tiles each step of the way.
|
||||
const int farthest_non_wall_tile_visibility = 4;
|
||||
|
||||
Std::vector<MapTile> tiles;
|
||||
|
||||
g_screen->screenEraseMapArea();
|
||||
if (c->_party->getTorchDuration() > 0) {
|
||||
for (y = 3; y >= 0; y--) {
|
||||
DungeonGraphicType type;
|
||||
|
||||
// FIXME: Maybe this should be in a loop
|
||||
tiles = getTiles(y, -1);
|
||||
type = tilesToGraphic(tiles);
|
||||
drawWall(-1, y, (Direction)g_ultima->_saveGame->_orientation, type);
|
||||
|
||||
tiles = getTiles(y, 1);
|
||||
type = tilesToGraphic(tiles);
|
||||
drawWall(1, y, (Direction)g_ultima->_saveGame->_orientation, type);
|
||||
|
||||
tiles = getTiles(y, 0);
|
||||
type = tilesToGraphic(tiles);
|
||||
drawWall(0, y, (Direction)g_ultima->_saveGame->_orientation, type);
|
||||
|
||||
// This only checks that the tile at y==3 is opaque
|
||||
if (y == 3 && !tiles.front().getTileType()->isOpaque()) {
|
||||
for (int y_obj = farthest_non_wall_tile_visibility; y_obj > y; y_obj--) {
|
||||
Std::vector<MapTile> distant_tiles = getTiles(y_obj , 0);
|
||||
DungeonGraphicType distant_type = tilesToGraphic(distant_tiles);
|
||||
|
||||
if ((distant_type == DNGGRAPHIC_DNGTILE) || (distant_type == DNGGRAPHIC_BASETILE))
|
||||
drawTile(c->_location->_map->_tileSet->get(distant_tiles.front().getId()), 0, y_obj, Direction(g_ultima->_saveGame->_orientation));
|
||||
}
|
||||
}
|
||||
if ((type == DNGGRAPHIC_DNGTILE) || (type == DNGGRAPHIC_BASETILE))
|
||||
drawTile(c->_location->_map->_tileSet->get(tiles.front().getId()), 0, y, Direction(g_ultima->_saveGame->_orientation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3rd-person perspective
|
||||
else {
|
||||
Std::vector<MapTile> tiles;
|
||||
|
||||
static MapTile black = c->_location->_map->_tileSet->getByName("black")->getId();
|
||||
static MapTile avatar = c->_location->_map->_tileSet->getByName("avatar")->getId();
|
||||
|
||||
for (y = 0; y < VIEWPORT_H; y++) {
|
||||
for (x = 0; x < VIEWPORT_W; x++) {
|
||||
tiles = getTiles((VIEWPORT_H / 2) - y, x - (VIEWPORT_W / 2));
|
||||
|
||||
// Only show blackness if there is no light
|
||||
if (c->_party->getTorchDuration() <= 0)
|
||||
view->drawTile(black, false, x, y);
|
||||
else if (x == VIEWPORT_W / 2 && y == VIEWPORT_H / 2)
|
||||
view->drawTile(avatar, false, x, y);
|
||||
else
|
||||
view->drawTile(tiles, false, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DungeonView::drawInDungeon(Tile *tile, int x_offset, int distance, Direction orientation, bool tiledWall) {
|
||||
Image *scaled;
|
||||
|
||||
const static int nscale_vga[] = { 12, 8, 4, 2, 1};
|
||||
const static int nscale_ega[] = { 8, 4, 2, 1, 0};
|
||||
|
||||
const int lscale_vga[] = { 22, 18, 10, 4, 1};
|
||||
const int lscale_ega[] = { 22, 14, 6, 3, 1};
|
||||
|
||||
const int *lscale;
|
||||
const int *nscale;
|
||||
int offset_multiplier = 0;
|
||||
int offset_adj = 0;
|
||||
if (settings._videoType != "EGA") {
|
||||
lscale = & lscale_vga[0];
|
||||
nscale = & nscale_vga[0];
|
||||
offset_multiplier = 1;
|
||||
offset_adj = 2;
|
||||
} else {
|
||||
lscale = & lscale_ega[0];
|
||||
nscale = & nscale_ega[0];
|
||||
offset_adj = 1;
|
||||
offset_multiplier = 4;
|
||||
}
|
||||
|
||||
const int *dscale = tiledWall ? lscale : nscale;
|
||||
|
||||
// Clear scratchpad and set a background color
|
||||
_animated->initializeToBackgroundColor();
|
||||
// Put tile on animated scratchpad
|
||||
if (tile->getAnim()) {
|
||||
MapTile mt = tile->getId();
|
||||
tile->getAnim()->draw(_animated, tile, mt, orientation);
|
||||
} else {
|
||||
tile->getImage()->drawOn(_animated, 0, 0);
|
||||
}
|
||||
_animated->makeBackgroundColorTransparent();
|
||||
// This process involving the background color is only required for drawing in the dungeon.
|
||||
// It will not play well with semi-transparent graphics.
|
||||
|
||||
/* scale is based on distance; 1 means half size, 2 regular, 4 means scale by 2x, etc. */
|
||||
if (dscale[distance] == 0)
|
||||
return;
|
||||
else if (dscale[distance] == 1)
|
||||
scaled = g_screen->screenScaleDown(_animated, 2);
|
||||
else {
|
||||
scaled = g_screen->screenScale(_animated, dscale[distance] / 2, 1, 0);
|
||||
}
|
||||
|
||||
if (tiledWall) {
|
||||
int i_x = SCALED((VIEWPORT_W * _tileWidth / 2) + _bounds.left) - (scaled->width() / 2);
|
||||
int i_y = SCALED((VIEWPORT_H * _tileHeight / 2) + _bounds.top) - (scaled->height() / 2);
|
||||
int f_x = i_x + scaled->width();
|
||||
int f_y = i_y + scaled->height();
|
||||
int d_x = _animated->width();
|
||||
int d_y = _animated->height();
|
||||
|
||||
for (int x = i_x; x < f_x; x += d_x)
|
||||
for (int y = i_y; y < f_y; y += d_y)
|
||||
_animated->drawSubRectOn(this->_screen,
|
||||
x, y, 0, 0, f_x - x, f_y - y);
|
||||
} else {
|
||||
int y_offset = MAX(0, (dscale[distance] - offset_adj) * offset_multiplier);
|
||||
int x = SCALED((VIEWPORT_W * _tileWidth / 2) + _bounds.left) - (scaled->width() / 2);
|
||||
int y = SCALED((VIEWPORT_H * _tileHeight / 2) + _bounds.top + y_offset) - (scaled->height() / 8);
|
||||
|
||||
scaled->drawSubRectOn(this->_screen, x, y, 0, 0,
|
||||
SCALED(_tileWidth * VIEWPORT_W + _bounds.left) - x,
|
||||
SCALED(_tileHeight * VIEWPORT_H + _bounds.top) - y);
|
||||
}
|
||||
|
||||
delete scaled;
|
||||
}
|
||||
|
||||
int DungeonView::graphicIndex(int xoffset, int distance, Direction orientation, DungeonGraphicType type) {
|
||||
int index;
|
||||
|
||||
index = 0;
|
||||
|
||||
if (type == DNGGRAPHIC_LADDERUP && xoffset == 0)
|
||||
return 48 +
|
||||
(distance * 2) +
|
||||
(DIR_IN_MASK(orientation, MASK_DIR_SOUTH | MASK_DIR_NORTH) ? 1 : 0);
|
||||
|
||||
if (type == DNGGRAPHIC_LADDERDOWN && xoffset == 0)
|
||||
return 56 +
|
||||
(distance * 2) +
|
||||
(DIR_IN_MASK(orientation, MASK_DIR_SOUTH | MASK_DIR_NORTH) ? 1 : 0);
|
||||
|
||||
if (type == DNGGRAPHIC_LADDERUPDOWN && xoffset == 0)
|
||||
return 64 +
|
||||
(distance * 2) +
|
||||
(DIR_IN_MASK(orientation, MASK_DIR_SOUTH | MASK_DIR_NORTH) ? 1 : 0);
|
||||
|
||||
// FIXME
|
||||
if (type != DNGGRAPHIC_WALL && type != DNGGRAPHIC_DOOR)
|
||||
return -1;
|
||||
|
||||
if (type == DNGGRAPHIC_DOOR)
|
||||
index += 24;
|
||||
|
||||
index += (xoffset + 1) * 2;
|
||||
|
||||
index += distance * 6;
|
||||
|
||||
if (DIR_IN_MASK(orientation, MASK_DIR_SOUTH | MASK_DIR_NORTH))
|
||||
index++;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void DungeonView::drawTile(Tile *tile, int x_offset, int distance, Direction orientation) {
|
||||
// Draw the tile to the screen
|
||||
DungeonViewer.drawInDungeon(tile, x_offset, distance, orientation, tile->isTiledInDungeon());
|
||||
}
|
||||
|
||||
Std::vector<MapTile> DungeonView::getTiles(int fwd, int side) {
|
||||
MapCoords coords = g_context->_location->_coords;
|
||||
|
||||
switch (g_ultima->_saveGame->_orientation) {
|
||||
case DIR_WEST:
|
||||
coords.x -= fwd;
|
||||
coords.y -= side;
|
||||
break;
|
||||
|
||||
case DIR_NORTH:
|
||||
coords.x += side;
|
||||
coords.y -= fwd;
|
||||
break;
|
||||
|
||||
case DIR_EAST:
|
||||
coords.x += fwd;
|
||||
coords.y += side;
|
||||
break;
|
||||
|
||||
case DIR_SOUTH:
|
||||
coords.x -= side;
|
||||
coords.y += fwd;
|
||||
break;
|
||||
|
||||
case DIR_ADVANCE:
|
||||
case DIR_RETREAT:
|
||||
default:
|
||||
error("Invalid dungeon orientation");
|
||||
}
|
||||
|
||||
// Wrap the coordinates if necessary
|
||||
coords.wrap(g_context->_location->_map);
|
||||
|
||||
bool focus;
|
||||
return g_context->_location->tilesAt(coords, focus);
|
||||
}
|
||||
|
||||
DungeonGraphicType DungeonView::tilesToGraphic(const Std::vector<MapTile> &tiles) {
|
||||
MapTile tile = tiles.front();
|
||||
|
||||
if (!_tiles._loaded) {
|
||||
_tiles._corridor = g_context->_location->_map->_tileSet->getByName("brick_floor")->getId();
|
||||
_tiles._upLadder = g_context->_location->_map->_tileSet->getByName("up_ladder")->getId();
|
||||
_tiles._downLadder = g_context->_location->_map->_tileSet->getByName("down_ladder")->getId();
|
||||
_tiles._upDownLadder = g_context->_location->_map->_tileSet->getByName("up_down_ladder")->getId();
|
||||
_tiles._loaded = true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* check if the dungeon tile has an annotation or object on top
|
||||
* (always displayed as a tile, unless a ladder)
|
||||
*/
|
||||
if (tiles.size() > 1) {
|
||||
if (tile._id == _tiles._upLadder._id)
|
||||
return DNGGRAPHIC_LADDERUP;
|
||||
else if (tile._id == _tiles._downLadder._id)
|
||||
return DNGGRAPHIC_LADDERDOWN;
|
||||
else if (tile._id == _tiles._upDownLadder._id)
|
||||
return DNGGRAPHIC_LADDERUPDOWN;
|
||||
else if (tile._id == _tiles._corridor._id)
|
||||
return DNGGRAPHIC_NONE;
|
||||
else
|
||||
return DNGGRAPHIC_BASETILE;
|
||||
}
|
||||
|
||||
/*
|
||||
* if not an annotation or object, then the tile is a dungeon
|
||||
* token
|
||||
*/
|
||||
Dungeon *dungeon = dynamic_cast<Dungeon *>(g_context->_location->_map);
|
||||
assert(dungeon);
|
||||
DungeonToken token = dungeon->tokenForTile(tile);
|
||||
|
||||
switch (token) {
|
||||
case DUNGEON_TRAP:
|
||||
case DUNGEON_CORRIDOR:
|
||||
return DNGGRAPHIC_NONE;
|
||||
case DUNGEON_WALL:
|
||||
case DUNGEON_SECRET_DOOR:
|
||||
return DNGGRAPHIC_WALL;
|
||||
case DUNGEON_ROOM:
|
||||
case DUNGEON_DOOR:
|
||||
return DNGGRAPHIC_DOOR;
|
||||
case DUNGEON_LADDER_UP:
|
||||
return DNGGRAPHIC_LADDERUP;
|
||||
case DUNGEON_LADDER_DOWN:
|
||||
return DNGGRAPHIC_LADDERDOWN;
|
||||
case DUNGEON_LADDER_UPDOWN:
|
||||
return DNGGRAPHIC_LADDERUPDOWN;
|
||||
|
||||
default:
|
||||
return DNGGRAPHIC_DNGTILE;
|
||||
}
|
||||
}
|
||||
|
||||
const struct {
|
||||
const char *subimage;
|
||||
int ega_x2, ega_y2;
|
||||
int vga_x2, vga_y2;
|
||||
const char *subimage2;
|
||||
} DNG_GRAPHIC_INFO[] = {
|
||||
{ "dung0_lft_ew", -1, -1, -1, -1, nullptr },
|
||||
{ "dung0_lft_ns", -1, -1, -1, -1, nullptr },
|
||||
{ "dung0_mid_ew", -1, -1, -1, -1, nullptr },
|
||||
{ "dung0_mid_ns", -1, -1, -1, -1, nullptr },
|
||||
{ "dung0_rgt_ew", -1, -1, -1, -1, nullptr },
|
||||
{ "dung0_rgt_ns", -1, -1, -1, -1, nullptr },
|
||||
|
||||
{ "dung1_lft_ew", 0, 32, 0, 8, "dung1_xxx_ew" },
|
||||
{ "dung1_lft_ns", 0, 32, 0, 8, "dung1_xxx_ns" },
|
||||
{ "dung1_mid_ew", -1, -1, -1, -1, nullptr },
|
||||
{ "dung1_mid_ns", -1, -1, -1, -1, nullptr },
|
||||
{ "dung1_rgt_ew", 144, 32, 160, 8, "dung1_xxx_ew" },
|
||||
{ "dung1_rgt_ns", 144, 32, 160, 8, "dung1_xxx_ns" },
|
||||
|
||||
{ "dung2_lft_ew", 0, 64, 0, 48, "dung2_xxx_ew" },
|
||||
{ "dung2_lft_ns", 0, 64, 0, 48, "dung2_xxx_ns" },
|
||||
{ "dung2_mid_ew", -1, -1, -1, -1, nullptr },
|
||||
{ "dung2_mid_ns", -1, -1, -1, -1, nullptr },
|
||||
{ "dung2_rgt_ew", 112, 64, 128, 48, "dung2_xxx_ew" },
|
||||
{ "dung2_rgt_ns", 112, 64, 128, 48, "dung2_xxx_ns" },
|
||||
|
||||
{ "dung3_lft_ew", 0, 80, 48, 72, "dung3_xxx_ew" },
|
||||
{ "dung3_lft_ns", 0, 80, 48, 72, "dung3_xxx_ns" },
|
||||
{ "dung3_mid_ew", -1, -1, -1, -1, nullptr },
|
||||
{ "dung3_mid_ns", -1, -1, -1, -1, nullptr },
|
||||
{ "dung3_rgt_ew", 96, 80, 104, 72, "dung3_xxx_ew" },
|
||||
{ "dung3_rgt_ns", 96, 80, 104, 72, "dung3_xxx_ns" },
|
||||
|
||||
{ "dung0_lft_ew_door", -1, -1, -1, -1, nullptr },
|
||||
{ "dung0_lft_ns_door", -1, -1, -1, -1, nullptr },
|
||||
{ "dung0_mid_ew_door", -1, -1, -1, -1, nullptr },
|
||||
{ "dung0_mid_ns_door", -1, -1, -1, -1, nullptr },
|
||||
{ "dung0_rgt_ew_door", -1, -1, -1, -1, nullptr },
|
||||
{ "dung0_rgt_ns_door", -1, -1, -1, -1, nullptr },
|
||||
|
||||
{ "dung1_lft_ew_door", 0, 32, 0, 8, "dung1_xxx_ew" },
|
||||
{ "dung1_lft_ns_door", 0, 32, 0, 8, "dung1_xxx_ns" },
|
||||
{ "dung1_mid_ew_door", -1, -1, -1, -1, nullptr },
|
||||
{ "dung1_mid_ns_door", -1, -1, -1, -1, nullptr },
|
||||
{ "dung1_rgt_ew_door", 144, 32, 160, 8, "dung1_xxx_ew" },
|
||||
{ "dung1_rgt_ns_door", 144, 32, 160, 8, "dung1_xxx_ns" },
|
||||
|
||||
{ "dung2_lft_ew_door", 0, 64, 0, 48, "dung2_xxx_ew" },
|
||||
{ "dung2_lft_ns_door", 0, 64, 0, 48, "dung2_xxx_ns" },
|
||||
{ "dung2_mid_ew_door", -1, -1, -1, -1, nullptr },
|
||||
{ "dung2_mid_ns_door", -1, -1, -1, -1, nullptr },
|
||||
{ "dung2_rgt_ew_door", 112, 64, 128, 48, "dung2_xxx_ew" },
|
||||
{ "dung2_rgt_ns_door", 112, 64, 128, 48, "dung2_xxx_ns" },
|
||||
|
||||
{ "dung3_lft_ew_door", 0, 80, 48, 72, "dung3_xxx_ew" },
|
||||
{ "dung3_lft_ns_door", 0, 80, 48, 72, "dung3_xxx_ns" },
|
||||
{ "dung3_mid_ew_door", -1, -1, -1, -1, nullptr },
|
||||
{ "dung3_mid_ns_door", -1, -1, -1, -1, nullptr },
|
||||
{ "dung3_rgt_ew_door", 96, 80, 104, 72, "dung3_xxx_ew" },
|
||||
{ "dung3_rgt_ns_door", 96, 80, 104, 72, "dung3_xxx_ns" },
|
||||
|
||||
{ "dung0_ladderup", -1, -1, -1, -1, nullptr },
|
||||
{ "dung0_ladderup_side", -1, -1, -1, -1, nullptr },
|
||||
{ "dung1_ladderup", -1, -1, -1, -1, nullptr },
|
||||
{ "dung1_ladderup_side", -1, -1, -1, -1, nullptr },
|
||||
{ "dung2_ladderup", -1, -1, -1, -1, nullptr },
|
||||
{ "dung2_ladderup_side", -1, -1, -1, -1, nullptr },
|
||||
{ "dung3_ladderup", -1, -1, -1, -1, nullptr },
|
||||
{ "dung3_ladderup_side", -1, -1, -1, -1, nullptr },
|
||||
|
||||
{ "dung0_ladderdown", -1, -1, -1, -1, nullptr },
|
||||
{ "dung0_ladderdown_side", -1, -1, -1, -1, nullptr },
|
||||
{ "dung1_ladderdown", -1, -1, -1, -1, nullptr },
|
||||
{ "dung1_ladderdown_side", -1, -1, -1, -1, nullptr },
|
||||
{ "dung2_ladderdown", -1, -1, -1, -1, nullptr },
|
||||
{ "dung2_ladderdown_side", -1, -1, -1, -1, nullptr },
|
||||
{ "dung3_ladderdown", -1, -1, -1, -1, nullptr },
|
||||
{ "dung3_ladderdown_side", -1, -1, -1, -1, nullptr },
|
||||
|
||||
{ "dung0_ladderupdown", -1, -1, -1, -1, nullptr },
|
||||
{ "dung0_ladderupdown_side", -1, -1, -1, -1, nullptr },
|
||||
{ "dung1_ladderupdown", -1, -1, -1, -1, nullptr },
|
||||
{ "dung1_ladderupdown_side", -1, -1, -1, -1, nullptr },
|
||||
{ "dung2_ladderupdown", -1, -1, -1, -1, nullptr },
|
||||
{ "dung2_ladderupdown_side", -1, -1, -1, -1, nullptr },
|
||||
{ "dung3_ladderupdown", -1, -1, -1, -1, nullptr },
|
||||
{ "dung3_ladderupdown_side", -1, -1, -1, -1, nullptr },
|
||||
};
|
||||
|
||||
void DungeonView::drawWall(int xoffset, int distance, Direction orientation, DungeonGraphicType type) {
|
||||
int index;
|
||||
|
||||
index = graphicIndex(xoffset, distance, orientation, type);
|
||||
if (index == -1 || distance >= 4)
|
||||
return;
|
||||
|
||||
int x = 0, y = 0;
|
||||
SubImage *subimage = imageMgr->getSubImage(DNG_GRAPHIC_INFO[index].subimage);
|
||||
if (subimage) {
|
||||
x = subimage->left;
|
||||
y = subimage->top;
|
||||
}
|
||||
|
||||
g_screen->screenDrawImage(DNG_GRAPHIC_INFO[index].subimage, (BORDER_WIDTH + x) * settings._scale,
|
||||
(BORDER_HEIGHT + y) * settings._scale);
|
||||
|
||||
if (DNG_GRAPHIC_INFO[index].subimage2 != nullptr) {
|
||||
// FIXME: subimage2 is a horrible hack, needs to be cleaned up
|
||||
if (settings._videoType == "EGA")
|
||||
g_screen->screenDrawImage(DNG_GRAPHIC_INFO[index].subimage2,
|
||||
(8 + DNG_GRAPHIC_INFO[index].ega_x2) * settings._scale,
|
||||
(8 + DNG_GRAPHIC_INFO[index].ega_y2) * settings._scale);
|
||||
else
|
||||
g_screen->screenDrawImage(DNG_GRAPHIC_INFO[index].subimage2,
|
||||
(8 + DNG_GRAPHIC_INFO[index].vga_x2) * settings._scale,
|
||||
(8 + DNG_GRAPHIC_INFO[index].vga_y2) * settings._scale);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
97
engines/ultima/ultima4/views/dungeonview.h
Normal file
97
engines/ultima/ultima4/views/dungeonview.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_VIEWS_DUNGEONVIEW_H
|
||||
#define ULTIMA4_VIEWS_DUNGEONVIEW_H
|
||||
|
||||
#include "ultima/ultima4/game/context.h"
|
||||
#include "ultima/ultima4/map/dungeon.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/map/tileset.h"
|
||||
#include "ultima/ultima4/map/direction.h"
|
||||
#include "ultima/ultima4/views/tileview.h"
|
||||
#include "ultima/ultima4/core/types.h"
|
||||
#include "ultima/ultima4/map/location.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
typedef enum {
|
||||
DNGGRAPHIC_NONE,
|
||||
DNGGRAPHIC_WALL,
|
||||
DNGGRAPHIC_LADDERUP,
|
||||
DNGGRAPHIC_LADDERDOWN,
|
||||
DNGGRAPHIC_LADDERUPDOWN,
|
||||
DNGGRAPHIC_DOOR,
|
||||
DNGGRAPHIC_DNGTILE,
|
||||
DNGGRAPHIC_BASETILE
|
||||
} DungeonGraphicType;
|
||||
|
||||
Std::vector<MapTile> dungeonViewGetTiles(int fwd, int side);
|
||||
DungeonGraphicType dungeonViewTilesToGraphic(const Std::vector<MapTile> &tiles);
|
||||
|
||||
#define DungeonViewer (*DungeonView::getInstance())
|
||||
|
||||
/**
|
||||
* @todo
|
||||
* <ul>
|
||||
* <li>move the rest of the dungeon drawing logic here from screen_sdl</li>
|
||||
* </ul>
|
||||
*/
|
||||
class DungeonView : public TileView {
|
||||
struct MapTiles {
|
||||
MapTile _corridor;
|
||||
MapTile _upLadder;
|
||||
MapTile _downLadder;
|
||||
MapTile _upDownLadder;
|
||||
bool _loaded;
|
||||
|
||||
MapTiles() : _loaded(false) {
|
||||
}
|
||||
};
|
||||
private:
|
||||
bool _screen3dDungeonViewEnabled;
|
||||
MapTiles _tiles;
|
||||
private:
|
||||
DungeonView(int x, int y, int columns, int rows);
|
||||
public:
|
||||
static DungeonView *_instance;
|
||||
static DungeonView *getInstance();
|
||||
|
||||
void drawInDungeon(Tile *tile, int x_offset, int distance, Direction orientation, bool tiled);
|
||||
int graphicIndex(int xoffset, int distance, Direction orientation, DungeonGraphicType type);
|
||||
void drawTile(Tile *tile, int x_offset, int distance, Direction orientation);
|
||||
void drawWall(int xoffset, int distance, Direction orientation, DungeonGraphicType type);
|
||||
|
||||
void display(Context *c, TileView *view);
|
||||
DungeonGraphicType tilesToGraphic(const Std::vector<MapTile> &tiles);
|
||||
|
||||
bool toggle3DDungeonView() {
|
||||
return _screen3dDungeonViewEnabled = !_screen3dDungeonViewEnabled;
|
||||
}
|
||||
|
||||
Std::vector<MapTile> getTiles(int fwd, int side);
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
61
engines/ultima/ultima4/views/imageview.cpp
Normal file
61
engines/ultima/ultima4/views/imageview.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/gfx/image.h"
|
||||
#include "ultima/ultima4/gfx/imagemgr.h"
|
||||
#include "ultima/ultima4/core/settings.h"
|
||||
#include "ultima/ultima4/views/imageview.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
ImageView::ImageView(int x, int y, int width, int height) : View(x, y, width, height) {
|
||||
}
|
||||
|
||||
ImageView::~ImageView() {
|
||||
}
|
||||
|
||||
void ImageView::draw(const Common::String &imageName, int x, int y) {
|
||||
ImageInfo *info = imageMgr->get(imageName);
|
||||
if (info) {
|
||||
info->_image->draw(SCALED(_bounds.left + x), SCALED(_bounds.top + y));
|
||||
return;
|
||||
}
|
||||
|
||||
SubImage *subimage = imageMgr->getSubImage(imageName);
|
||||
if (subimage) {
|
||||
info = imageMgr->get(subimage->_srcImageName);
|
||||
|
||||
if (info) {
|
||||
info->_image->drawSubRect(SCALED(_bounds.left + x), SCALED(_bounds.top + y),
|
||||
SCALED(subimage->left) / info->_prescale,
|
||||
SCALED(subimage->top) / info->_prescale,
|
||||
SCALED(subimage->width()) / info->_prescale,
|
||||
SCALED(subimage->height()) / info->_prescale);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
error("ERROR 1005: Unable to load the image \"%s\"", imageName.c_str());
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
47
engines/ultima/ultima4/views/imageview.h
Normal file
47
engines/ultima/ultima4/views/imageview.h
Normal 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_VIEWS_IMAGEVIEW_H
|
||||
#define ULTIMA4_VIEWS_IMAGEVIEW_H
|
||||
|
||||
#include "ultima/ultima4/views/view.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* A view for displaying bitmap images.
|
||||
*/
|
||||
class ImageView : public View {
|
||||
public:
|
||||
ImageView(int x = 0, int y = 0, int width = 320, int height = 200);
|
||||
virtual ~ImageView();
|
||||
|
||||
/**
|
||||
* Draw the image at the optionally specified offset.
|
||||
*/
|
||||
void draw(const Common::String &imageName, int x = 0, int y = 0);
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
289
engines/ultima/ultima4/views/menu.cpp
Normal file
289
engines/ultima/ultima4/views/menu.cpp
Normal file
@@ -0,0 +1,289 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/views/menu.h"
|
||||
#include "ultima/ultima4/events/event_handler.h"
|
||||
#include "ultima/ultima4/views/textview.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
Menu::Menu() :
|
||||
_closed(false),
|
||||
_title(""),
|
||||
_titleX(0),
|
||||
_titleY(0) {
|
||||
}
|
||||
|
||||
Menu::~Menu() {
|
||||
for (auto *i : _items)
|
||||
delete i;
|
||||
}
|
||||
|
||||
void Menu::removeAll() {
|
||||
_items.clear();
|
||||
}
|
||||
|
||||
void Menu::add(int id, Common::String text, short x, short y, int sc) {
|
||||
MenuItem *item = new MenuItem(text, x, y, sc);
|
||||
item->setId(id);
|
||||
_items.push_back(item);
|
||||
}
|
||||
|
||||
MenuItem *Menu::add(int id, MenuItem *item) {
|
||||
item->setId(id);
|
||||
_items.push_back(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
void Menu::addShortcutKey(int id, int shortcutKey) {
|
||||
for (auto *i : _items) {
|
||||
if (i->getId() == id) {
|
||||
i->addShortcutKey(shortcutKey);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::setClosesMenu(int id) {
|
||||
for (auto *i : _items) {
|
||||
if (i->getId() == id) {
|
||||
i->setClosesMenu(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Menu::MenuItemList::iterator Menu::getCurrent() {
|
||||
return _selected;
|
||||
}
|
||||
|
||||
void Menu::setCurrent(MenuItemList::iterator i) {
|
||||
_selected = i;
|
||||
highlight(*_selected);
|
||||
|
||||
MenuEvent event(this, MenuEvent::SELECT);
|
||||
setChanged();
|
||||
notifyObservers(event);
|
||||
}
|
||||
|
||||
void Menu::setCurrent(int id) {
|
||||
setCurrent(getById(id));
|
||||
}
|
||||
|
||||
void Menu::show(TextView *view) {
|
||||
if (_title.size() > 0)
|
||||
view->textAt(_titleX, _titleY, "%s", _title.c_str());
|
||||
|
||||
for (auto *mi : _items) {
|
||||
if (mi->isVisible()) {
|
||||
Common::String text(mi->getText());
|
||||
|
||||
if (mi->isSelected()) {
|
||||
text.setChar('\010', 0);
|
||||
}
|
||||
|
||||
if (mi->isHighlighted()) {
|
||||
view->textSelectedAt(mi->getX(), mi->getY(), view->colorizeString(text.c_str(), FG_YELLOW, mi->getScOffset(), 1).c_str());
|
||||
// hack for the custom U5 mix reagents menu
|
||||
// places cursor 1 column over, rather than 2.
|
||||
view->setCursorPos(mi->getX() - (view->getWidth() == 15 ? 1 : 2), mi->getY(), true);
|
||||
view->enableCursor();
|
||||
} else {
|
||||
view->textAt(mi->getX(), mi->getY(), "%s", view->colorizeString(text.c_str(), FG_YELLOW, mi->getScOffset(), 1).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Menu::isVisible() {
|
||||
bool visible = false;
|
||||
|
||||
for (const auto *mi : _items) {
|
||||
if (mi->isVisible())
|
||||
visible = true;
|
||||
}
|
||||
|
||||
return visible;
|
||||
}
|
||||
|
||||
void Menu::next() {
|
||||
MenuItemList::iterator i = _selected;
|
||||
if (isVisible()) {
|
||||
if (++i == _items.end())
|
||||
i = _items.begin();
|
||||
while (!(*i)->isVisible()) {
|
||||
if (++i == _items.end())
|
||||
i = _items.begin();
|
||||
}
|
||||
}
|
||||
|
||||
setCurrent(i);
|
||||
}
|
||||
|
||||
void Menu::prev() {
|
||||
MenuItemList::iterator i = _selected;
|
||||
if (isVisible()) {
|
||||
if (i == _items.begin())
|
||||
i = _items.end();
|
||||
i--;
|
||||
while (!(*i)->isVisible()) {
|
||||
if (i == _items.begin())
|
||||
i = _items.end();
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
setCurrent(i);
|
||||
}
|
||||
|
||||
void Menu::highlight(MenuItem *item) {
|
||||
// unhighlight all menu items first
|
||||
for (auto *mi : _items) {
|
||||
mi->setHighlighted(false);
|
||||
}
|
||||
if (item)
|
||||
item->setHighlighted(true);
|
||||
}
|
||||
|
||||
Menu::MenuItemList::iterator Menu::begin() {
|
||||
return _items.begin();
|
||||
}
|
||||
|
||||
Menu::MenuItemList::iterator Menu::end() {
|
||||
return _items.end();
|
||||
}
|
||||
|
||||
Menu::MenuItemList::iterator Menu::begin_visible() {
|
||||
if (!isVisible())
|
||||
return _items.end();
|
||||
|
||||
for (Menu::MenuItemList::iterator it = _items.begin(); it != _items.end(); it++) {
|
||||
if (!(*it)->isVisible())
|
||||
return it;
|
||||
}
|
||||
|
||||
return _items.end();
|
||||
}
|
||||
|
||||
void Menu::reset(bool highlightFirst) {
|
||||
_closed = false;
|
||||
|
||||
/* get the first visible menu item */
|
||||
_selected = begin_visible();
|
||||
|
||||
/* un-highlight and deselect each menu item */
|
||||
for (auto *mi : _items) {
|
||||
mi->setHighlighted(false);
|
||||
mi->setSelected(false);
|
||||
}
|
||||
|
||||
/* highlight the first visible menu item */
|
||||
if (highlightFirst)
|
||||
highlight(*_selected);
|
||||
|
||||
MenuEvent event(this, MenuEvent::RESET);
|
||||
setChanged();
|
||||
notifyObservers(event);
|
||||
}
|
||||
|
||||
Menu::MenuItemList::iterator Menu::getById(int id) {
|
||||
if (id == -1)
|
||||
return getCurrent();
|
||||
|
||||
for (Menu::MenuItemList::iterator it = _items.begin(); it != _items.end(); it++) {
|
||||
if ((*it)->getId() == id)
|
||||
return it;
|
||||
}
|
||||
return _items.end();
|
||||
}
|
||||
|
||||
MenuItem *Menu::getItemById(int id) {
|
||||
Menu::MenuItemList::iterator it = getById(id);
|
||||
if (it != _items.end())
|
||||
return *it;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Menu::activateItemAtPos(TextView *view, const Common::Point &pt) {
|
||||
for (Menu::MenuItemList::iterator it = begin(); it != end(); ++it) {
|
||||
Common::Rect r = view->getTextBounds((*it)->getX(), (*it)->getY(),
|
||||
(*it)->getText().size());
|
||||
|
||||
if (r.contains(pt)) {
|
||||
activateItem((*it)->getId(), MenuEvent::ACTIVATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::activateItem(int id, MenuEvent::Type action) {
|
||||
MenuItem *mi;
|
||||
|
||||
/* find the given menu item by id */
|
||||
if (id >= 0)
|
||||
mi = getItemById(id);
|
||||
/* or use the current item */
|
||||
else
|
||||
mi = *getCurrent();
|
||||
|
||||
if (!mi)
|
||||
error("Error: Unable to find menu item with id '%d'", id);
|
||||
|
||||
/* make sure the action given will activate the menu item */
|
||||
if (mi->getClosesMenu())
|
||||
setClosed(true);
|
||||
|
||||
MenuEvent event(this, (MenuEvent::Type)action, mi);
|
||||
mi->activate(event);
|
||||
setChanged();
|
||||
notifyObservers(event);
|
||||
}
|
||||
|
||||
bool Menu::activateItemByShortcut(int key, MenuEvent::Type action) {
|
||||
for (auto *i : _items) {
|
||||
if (i->hasShortcutKey(key)) {
|
||||
activateItem(i->getId(), action);
|
||||
// if the selection doesn't close the menu, highlight the selection
|
||||
if (!i->getClosesMenu())
|
||||
setCurrent(i->getId());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Menu::getClosed() const {
|
||||
return _closed;
|
||||
}
|
||||
|
||||
void Menu::setClosed(bool closed) {
|
||||
this->_closed = closed;
|
||||
}
|
||||
|
||||
void Menu::setTitle(const Common::String &text, int x, int y) {
|
||||
_title = text;
|
||||
_titleX = x;
|
||||
_titleY = y;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
201
engines/ultima/ultima4/views/menu.h
Normal file
201
engines/ultima/ultima4/views/menu.h
Normal file
@@ -0,0 +1,201 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_VIEWS_MENU_H
|
||||
#define ULTIMA4_VIEWS_MENU_H
|
||||
|
||||
#include "ultima/ultima4/events/event_handler.h"
|
||||
#include "ultima/ultima4/views/menuitem.h"
|
||||
#include "ultima/ultima4/core/observable.h"
|
||||
#include "ultima/ultima4/core/types.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class Menu;
|
||||
class TextView;
|
||||
|
||||
class MenuEvent {
|
||||
public:
|
||||
enum Type {
|
||||
ACTIVATE,
|
||||
INCREMENT,
|
||||
DECREMENT,
|
||||
SELECT,
|
||||
RESET
|
||||
};
|
||||
|
||||
MenuEvent(const Menu *menu, Type type, const MenuItem *item = nullptr) {
|
||||
this->_menu = menu;
|
||||
this->_type = type;
|
||||
this->_item = item;
|
||||
}
|
||||
|
||||
const Menu *getMenu() {
|
||||
return _menu;
|
||||
}
|
||||
Type getType() {
|
||||
return _type;
|
||||
}
|
||||
const MenuItem *getMenuItem() {
|
||||
return _item;
|
||||
}
|
||||
|
||||
private:
|
||||
const Menu *_menu;
|
||||
Type _type;
|
||||
const MenuItem *_item;
|
||||
};
|
||||
|
||||
/**
|
||||
* Menu class definition
|
||||
*/
|
||||
class Menu : public Observable<Menu *, MenuEvent &> {
|
||||
public:
|
||||
typedef Common::List<MenuItem *> MenuItemList;
|
||||
|
||||
public:
|
||||
Menu();
|
||||
~Menu();
|
||||
|
||||
void removeAll();
|
||||
|
||||
/**
|
||||
* Adds an item to the menu list and returns the menu
|
||||
*/
|
||||
void add(int id, Common::String text, short x, short y, int shortcutKey = -1);
|
||||
|
||||
MenuItem *add(int id, MenuItem *item);
|
||||
void addShortcutKey(int id, int shortcutKey);
|
||||
void setClosesMenu(int id);
|
||||
|
||||
/**
|
||||
* Returns the menu item that is currently selected/highlighted
|
||||
*/
|
||||
MenuItemList::iterator getCurrent();
|
||||
|
||||
/**
|
||||
* Sets the current menu item to the one indicated by the iterator
|
||||
*/
|
||||
void setCurrent(MenuItemList::iterator i);
|
||||
void setCurrent(int id);
|
||||
void show(TextView *view);
|
||||
|
||||
/**
|
||||
* Checks the menu to ensure that there is at least 1 visible
|
||||
* item in the list. Returns true if there is at least 1 visible
|
||||
* item, false if nothing is visible.
|
||||
*/
|
||||
bool isVisible();
|
||||
|
||||
/**
|
||||
* Sets the selected iterator to the next visible menu item and highlights it
|
||||
*/
|
||||
void next();
|
||||
|
||||
/**
|
||||
* Sets the selected iterator to the previous visible menu item and highlights it
|
||||
*/
|
||||
void prev();
|
||||
|
||||
/**
|
||||
* Highlights a single menu item, un-highlighting any others
|
||||
*/
|
||||
void highlight(MenuItem *item);
|
||||
|
||||
/**
|
||||
* Returns an iterator pointing to the first menu item
|
||||
*/
|
||||
MenuItemList::iterator begin();
|
||||
|
||||
/**
|
||||
* Returns an iterator pointing just past the last menu item
|
||||
*/
|
||||
MenuItemList::iterator end();
|
||||
|
||||
/**
|
||||
* Returns an iterator pointing to the first visible menu item
|
||||
*/
|
||||
MenuItemList::iterator begin_visible();
|
||||
|
||||
/**
|
||||
* 'Resets' the menu. This does the following:
|
||||
* - un-highlights all menu items
|
||||
* - highlights the first menu item
|
||||
* - selects the first visible menu item
|
||||
*/
|
||||
void reset(bool highlightFirst = true);
|
||||
|
||||
/**
|
||||
* Returns an iterator pointing to the item associated with the given 'id'
|
||||
*/
|
||||
MenuItemList::iterator getById(int id);
|
||||
|
||||
/**
|
||||
* Returns the menu item associated with the given 'id'
|
||||
*/
|
||||
MenuItem *getItemById(int id);
|
||||
|
||||
/**
|
||||
* Activates any menu item at a given position
|
||||
*/
|
||||
void activateItemAtPos(TextView *view, const Common::Point &pt);
|
||||
|
||||
/**
|
||||
* Activates the menu item given by 'id', using 'action' to
|
||||
* activate it. If the menu item cannot be activated using
|
||||
* 'action', then it is not activated. This also un-highlights
|
||||
* the menu item given by 'menu' and highlights the new menu
|
||||
* item that was found for 'id'.
|
||||
*/
|
||||
void activateItem(int id, MenuEvent::Type action);
|
||||
|
||||
/**
|
||||
* Activates a menu item by it's shortcut key. True is returned if a
|
||||
* menu item get activated, false otherwise.
|
||||
*/
|
||||
bool activateItemByShortcut(int key, MenuEvent::Type action);
|
||||
|
||||
/**
|
||||
* Returns true if the menu has been closed.
|
||||
*/
|
||||
bool getClosed() const;
|
||||
|
||||
/**
|
||||
* Update whether the menu has been closed.
|
||||
*/
|
||||
void setClosed(bool closed);
|
||||
|
||||
void setTitle(const Common::String &text, int x, int y);
|
||||
|
||||
private:
|
||||
MenuItemList _items;
|
||||
MenuItemList::iterator _selected;
|
||||
bool _closed;
|
||||
Common::String _title;
|
||||
int _titleX, _titleY;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
258
engines/ultima/ultima4/views/menuitem.cpp
Normal file
258
engines/ultima/ultima4/views/menuitem.cpp
Normal file
@@ -0,0 +1,258 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/views/menu.h"
|
||||
#include "ultima/ultima4/views/menuitem.h"
|
||||
#include "ultima/ultima4/core/settings.h"
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
MenuItem::MenuItem(const Common::String &t, short x, short y, int sc) :
|
||||
_id(-1), _x(x), _y(y), _text(t), _highlighted(false),
|
||||
_selected(false), _visible(true), _scOffset(sc), _closesMenu(false) {
|
||||
// if the sc/scOffset is outside the range of the text string, assert
|
||||
assertMsg(sc == -1 || (sc >= 0 && sc <= (int)_text.size()), "sc value of %d out of range!", sc);
|
||||
if (sc != -1) addShortcutKey(tolower(_text[sc]));
|
||||
}
|
||||
|
||||
int MenuItem::getId() const {
|
||||
return _id;
|
||||
}
|
||||
short MenuItem::getX() const {
|
||||
return _x;
|
||||
}
|
||||
short MenuItem::getY() const {
|
||||
return _y;
|
||||
}
|
||||
int MenuItem::getScOffset() const {
|
||||
return _scOffset;
|
||||
}
|
||||
Common::String MenuItem::getText() const {
|
||||
return _text;
|
||||
}
|
||||
bool MenuItem::isHighlighted() const {
|
||||
return _highlighted;
|
||||
}
|
||||
bool MenuItem::isSelected() const {
|
||||
return _selected;
|
||||
}
|
||||
bool MenuItem::isVisible() const {
|
||||
return _visible;
|
||||
}
|
||||
bool MenuItem::hasShortcutKey(int sc) const {
|
||||
Common::Array<int>::const_iterator begin = _shortcutKeys.begin();
|
||||
Common::Array<int>::const_iterator end = _shortcutKeys.end();
|
||||
return (Common::find(begin, end, sc) != end);
|
||||
}
|
||||
bool MenuItem::getClosesMenu() const {
|
||||
return _closesMenu;
|
||||
}
|
||||
|
||||
void MenuItem::setId(int i) {
|
||||
_id = i;
|
||||
}
|
||||
|
||||
void MenuItem::setX(int x) {
|
||||
_x = x;
|
||||
}
|
||||
|
||||
void MenuItem::setY(int y) {
|
||||
_y = y;
|
||||
}
|
||||
|
||||
void MenuItem::setText(const Common::String &t) {
|
||||
_text = t;
|
||||
}
|
||||
|
||||
void MenuItem::setHighlighted(bool h) {
|
||||
_highlighted = h;
|
||||
}
|
||||
|
||||
void MenuItem::setSelected(bool s) {
|
||||
_selected = s;
|
||||
}
|
||||
|
||||
void MenuItem::setVisible(bool v) {
|
||||
_visible = v;
|
||||
}
|
||||
|
||||
void MenuItem::addShortcutKey(int sc) {
|
||||
if (!hasShortcutKey(sc))
|
||||
_shortcutKeys.push_back(sc);
|
||||
}
|
||||
|
||||
void MenuItem::setClosesMenu(bool closesMenu) {
|
||||
this->_closesMenu = closesMenu;
|
||||
}
|
||||
|
||||
BoolMenuItem::BoolMenuItem(const Common::String &text, short xp, short yp, int shortcutKey, bool *val) :
|
||||
MenuItem(text, xp, yp, shortcutKey),
|
||||
_val(val),
|
||||
_on("On"),
|
||||
_off("Off") {
|
||||
}
|
||||
|
||||
BoolMenuItem *BoolMenuItem::setValueStrings(const Common::String &onString, const Common::String &offString) {
|
||||
_on = onString;
|
||||
_off = offString;
|
||||
return this;
|
||||
}
|
||||
|
||||
Common::String BoolMenuItem::getText() const {
|
||||
char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer), _text.c_str(), *_val ? _on.c_str() : _off.c_str());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void BoolMenuItem::activate(MenuEvent &event) {
|
||||
if (event.getType() == MenuEvent::DECREMENT ||
|
||||
event.getType() == MenuEvent::INCREMENT ||
|
||||
event.getType() == MenuEvent::ACTIVATE)
|
||||
*_val = !(*_val);
|
||||
}
|
||||
|
||||
StringMenuItem::StringMenuItem(const Common::String &text, short xp, short yp, int shortcutKey,
|
||||
Common::String *val, const Std::vector<Common::String> &validSettings) :
|
||||
MenuItem(text, xp, yp, shortcutKey),
|
||||
_val(val),
|
||||
_validSettings(validSettings) {
|
||||
}
|
||||
|
||||
Common::String StringMenuItem::getText() const {
|
||||
char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer), _text.c_str(), _val->c_str());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void StringMenuItem::activate(MenuEvent &event) {
|
||||
Std::vector<Common::String>::const_iterator current =
|
||||
find(_validSettings.begin(), _validSettings.end(), *_val);
|
||||
|
||||
if (current == _validSettings.end())
|
||||
error("Error: menu Common::String '%s' not a valid choice", _val->c_str());
|
||||
|
||||
if (event.getType() == MenuEvent::INCREMENT || event.getType() == MenuEvent::ACTIVATE) {
|
||||
/* move to the next valid choice, wrapping if necessary */
|
||||
current++;
|
||||
if (current == _validSettings.end())
|
||||
current = _validSettings.begin();
|
||||
*_val = *current;
|
||||
|
||||
} else if (event.getType() == MenuEvent::DECREMENT) {
|
||||
/* move back one, wrapping if necessary */
|
||||
if (current == _validSettings.begin())
|
||||
current = _validSettings.end();
|
||||
current--;
|
||||
*_val = *current;
|
||||
}
|
||||
}
|
||||
|
||||
IntMenuItem::IntMenuItem(const Common::String &text, short xp, short yp, int shortcutKey, int *val,
|
||||
int min, int max, int increment, menuOutputType output) :
|
||||
MenuItem(text, xp, yp, shortcutKey),
|
||||
_val(val),
|
||||
_min(min),
|
||||
_max(max),
|
||||
_increment(increment),
|
||||
_output(output) {
|
||||
}
|
||||
|
||||
Common::String IntMenuItem::getText() const {
|
||||
// do custom formatting for some menu entries,
|
||||
// and generate a Common::String of the results
|
||||
char outputBuffer[20];
|
||||
|
||||
switch (_output) {
|
||||
case MENU_OUTPUT_REAGENT:
|
||||
snprintf(outputBuffer, sizeof(outputBuffer), "%2d", static_cast<short>(*_val));
|
||||
break;
|
||||
case MENU_OUTPUT_GAMMA:
|
||||
snprintf(outputBuffer, sizeof(outputBuffer), "%.1f", static_cast<float>(*_val) / 100);
|
||||
break;
|
||||
case MENU_OUTPUT_SHRINE:
|
||||
/*
|
||||
* is this code really necessary? the increments/decrements can be handled by IntMenuItem(),
|
||||
* as well as the looping once the max is reached. more importantly, the minimum value is
|
||||
* inconstant, and based upon another setting that can be changed independent of this one.
|
||||
* This variable could be set to it's minimum value, but when the gameCyclesPerSecond setting
|
||||
* is changed, the value of this setting could become out of bounds.
|
||||
*
|
||||
* settings.shrineTime is only used in one function within shrine.cpp, and that code appears
|
||||
* to handle the min value, caping the minimum interval at 1.
|
||||
*
|
||||
// make sure that the setting we're trying for is even possible
|
||||
if (event.getType() == MenuEvent::INCREMENT || event.getType() == MenuEvent::ACTIVATE) {
|
||||
settingsChanged.shrineTime++;
|
||||
if (settingsChanged.shrineTime > MAX_SHRINE_TIME)
|
||||
settingsChanged.shrineTime = MEDITATION_MANTRAS_PER_CYCLE / settingsChanged.gameCyclesPerSecond;
|
||||
} else if (event.getType() == MenuEvent::DECREMENT) {
|
||||
settingsChanged.shrineTime--;
|
||||
if (settingsChanged.shrineTime < (MEDITATION_MANTRAS_PER_CYCLE / settingsChanged.gameCyclesPerSecond))
|
||||
settingsChanged.shrineTime = MAX_SHRINE_TIME;
|
||||
}
|
||||
*
|
||||
*/
|
||||
snprintf(outputBuffer, sizeof(outputBuffer), "%d sec", *_val);
|
||||
break;
|
||||
case MENU_OUTPUT_SPELL:
|
||||
snprintf(outputBuffer, sizeof(outputBuffer), "%3g sec", static_cast<double>(*_val) / 5);
|
||||
break;
|
||||
case MENU_OUTPUT_VOLUME:
|
||||
if (*_val == 0) {
|
||||
snprintf(outputBuffer, sizeof(outputBuffer), "Disabled");
|
||||
} else if (*_val == MAX_VOLUME) {
|
||||
snprintf(outputBuffer, sizeof(outputBuffer), "Full");
|
||||
} else {
|
||||
snprintf(outputBuffer, sizeof(outputBuffer), "%d%s%s", *_val * 10, "%", "%");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// the buffer must contain a field character %d or %s depending
|
||||
// on the menuOutputType selected. MENU_OUTPUT_INT always uses
|
||||
// %d, whereas all others use %s
|
||||
char buffer[64];
|
||||
if (_output != MENU_OUTPUT_INT)
|
||||
snprintf(buffer, sizeof(buffer), _text.c_str(), outputBuffer);
|
||||
else
|
||||
snprintf(buffer, sizeof(buffer), _text.c_str(), *_val);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void IntMenuItem::activate(MenuEvent &event) {
|
||||
if (event.getType() == MenuEvent::INCREMENT || event.getType() == MenuEvent::ACTIVATE) {
|
||||
*_val += _increment;
|
||||
if (*_val > _max)
|
||||
*_val = _min;
|
||||
|
||||
} else if (event.getType() == MenuEvent::DECREMENT) {
|
||||
*_val -= _increment;
|
||||
if (*_val < _min)
|
||||
*_val = _max;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
146
engines/ultima/ultima4/views/menuitem.h
Normal file
146
engines/ultima/ultima4/views/menuitem.h
Normal file
@@ -0,0 +1,146 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_VIEWS_MENUITEM_H
|
||||
#define ULTIMA4_VIEWS_MENUITEM_H
|
||||
|
||||
#include "common/str.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class MenuEvent;
|
||||
|
||||
/**
|
||||
* custom output types for with menu items that need
|
||||
* to perform special calculations before displaying
|
||||
* its associated value
|
||||
*/
|
||||
enum menuOutputType {
|
||||
MENU_OUTPUT_INT,
|
||||
MENU_OUTPUT_GAMMA,
|
||||
MENU_OUTPUT_SHRINE,
|
||||
MENU_OUTPUT_SPELL,
|
||||
MENU_OUTPUT_VOLUME,
|
||||
MENU_OUTPUT_REAGENT
|
||||
};
|
||||
|
||||
class MenuItem {
|
||||
public:
|
||||
/**
|
||||
* MenuItem class
|
||||
*/
|
||||
MenuItem(const Common::String &text, short x, short y, int shortcutKey = -1);
|
||||
virtual ~MenuItem() {}
|
||||
|
||||
virtual void activate(MenuEvent &event) {}
|
||||
|
||||
// Accessor Methods
|
||||
int getId() const;
|
||||
short getX() const;
|
||||
short getY() const;
|
||||
int getScOffset() const;
|
||||
|
||||
virtual Common::String getText() const;
|
||||
bool isHighlighted() const;
|
||||
bool isSelected() const;
|
||||
bool isVisible() const;
|
||||
bool hasShortcutKey(int key) const;
|
||||
bool getClosesMenu() const;
|
||||
|
||||
void setId(int id);
|
||||
void setX(int x);
|
||||
void setY(int y);
|
||||
void setText(const Common::String &text);
|
||||
void setHighlighted(bool h = true);
|
||||
void setSelected(bool s = true);
|
||||
void setVisible(bool v = true);
|
||||
void addShortcutKey(int shortcutKey);
|
||||
void setClosesMenu(bool closesMenu);
|
||||
|
||||
protected:
|
||||
int _id;
|
||||
short _x, _y;
|
||||
Common::String _text;
|
||||
bool _highlighted;
|
||||
bool _selected;
|
||||
bool _visible;
|
||||
int _scOffset;
|
||||
Common::Array<int> _shortcutKeys;
|
||||
bool _closesMenu;
|
||||
};
|
||||
|
||||
/**
|
||||
* A menu item that toggles a boolean value, and displays the current
|
||||
* setting as part of the text.
|
||||
*/
|
||||
class BoolMenuItem : public MenuItem {
|
||||
public:
|
||||
BoolMenuItem(const Common::String &text, short xp, short yp, int shortcutKey, bool *val);
|
||||
|
||||
BoolMenuItem *setValueStrings(const Common::String &onString, const Common::String &offString);
|
||||
|
||||
void activate(MenuEvent &event) override;
|
||||
Common::String getText() const override;
|
||||
|
||||
protected:
|
||||
bool *_val;
|
||||
Common::String _on, _off;
|
||||
};
|
||||
|
||||
/**
|
||||
* A menu item that cycles through a list of possible Common::String values, and
|
||||
* displays the current setting as part of the text.
|
||||
*/
|
||||
class StringMenuItem : public MenuItem {
|
||||
public:
|
||||
StringMenuItem(const Common::String &text, short xp, short yp, int shortcutKey, Common::String *val, const Std::vector<Common::String> &validSettings);
|
||||
|
||||
void activate(MenuEvent &event) override;
|
||||
Common::String getText() const override;
|
||||
|
||||
protected:
|
||||
Common::String *_val;
|
||||
Std::vector<Common::String> _validSettings;
|
||||
};
|
||||
|
||||
/**
|
||||
* A menu item that cycles through a list of possible integer values,
|
||||
* and displays the current setting as part of the text.
|
||||
*/
|
||||
class IntMenuItem : public MenuItem {
|
||||
public:
|
||||
IntMenuItem(const Common::String &text, short xp, short yp, int shortcutKey, int *val, int min, int max, int increment, menuOutputType output = MENU_OUTPUT_INT);
|
||||
|
||||
void activate(MenuEvent &event) override;
|
||||
Common::String getText() const override;
|
||||
|
||||
protected:
|
||||
int *_val;
|
||||
int _min, _max, _increment;
|
||||
menuOutputType _output;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
412
engines/ultima/ultima4/views/stats.cpp
Normal file
412
engines/ultima/ultima4/views/stats.cpp
Normal file
@@ -0,0 +1,412 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/ultima4.h"
|
||||
#include "ultima/ultima4/core/config.h"
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
#include "ultima/ultima4/views/stats.h"
|
||||
#include "ultima/ultima4/game/armor.h"
|
||||
#include "ultima/ultima4/game/context.h"
|
||||
#include "ultima/ultima4/views/menu.h"
|
||||
#include "ultima/ultima4/game/names.h"
|
||||
#include "ultima/ultima4/game/player.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/game/spell.h"
|
||||
#include "ultima/ultima4/map/tile.h"
|
||||
#include "ultima/ultima4/game/weapon.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* StatsArea class implementation
|
||||
*/
|
||||
StatsArea::StatsArea() :
|
||||
_title(STATS_AREA_X * CHAR_WIDTH, 0 * CHAR_HEIGHT, STATS_AREA_WIDTH, 1),
|
||||
_mainArea(STATS_AREA_X * CHAR_WIDTH, STATS_AREA_Y * CHAR_HEIGHT, STATS_AREA_WIDTH, STATS_AREA_HEIGHT),
|
||||
_summary(STATS_AREA_X * CHAR_WIDTH, (STATS_AREA_Y + STATS_AREA_HEIGHT + 1) * CHAR_HEIGHT, STATS_AREA_WIDTH, 1),
|
||||
_view(STATS_PARTY_OVERVIEW) {
|
||||
// Generate a formatted Common::String for each menu item,
|
||||
// and then add the item to the menu. The Y value
|
||||
// for each menu item will be filled in later.
|
||||
for (int count = 0; count < 8; count++) {
|
||||
char outputBuffer[16];
|
||||
snprintf(outputBuffer, sizeof(outputBuffer), "-%-11s%%s", getReagentName((Reagent)count));
|
||||
_reagentsMixMenu.add(count, new IntMenuItem(outputBuffer, 1, 0, -1, (int *)g_context->_party->getReagentPtr((Reagent)count), 0, 99, 1, MENU_OUTPUT_REAGENT));
|
||||
}
|
||||
|
||||
_reagentsMixMenu.addObserver(this);
|
||||
}
|
||||
|
||||
void StatsArea::setView(StatsView view) {
|
||||
this->_view = view;
|
||||
update();
|
||||
}
|
||||
|
||||
void StatsArea::prevItem() {
|
||||
_view = (StatsView)(_view - 1);
|
||||
if (_view < STATS_CHAR1)
|
||||
_view = STATS_MIXTURES;
|
||||
if (_view <= STATS_CHAR8 && (_view - STATS_CHAR1 + 1) > g_context->_party->size())
|
||||
_view = (StatsView)(STATS_CHAR1 - 1 + g_context->_party->size());
|
||||
update();
|
||||
}
|
||||
|
||||
void StatsArea::nextItem() {
|
||||
_view = (StatsView)(_view + 1);
|
||||
if (_view > STATS_MIXTURES)
|
||||
_view = STATS_CHAR1;
|
||||
if (_view <= STATS_CHAR8 && (_view - STATS_CHAR1 + 1) > g_context->_party->size())
|
||||
_view = STATS_WEAPONS;
|
||||
update();
|
||||
}
|
||||
|
||||
void StatsArea::update(bool avatarOnly) {
|
||||
clear();
|
||||
|
||||
/*
|
||||
* update the upper stats box
|
||||
*/
|
||||
switch (_view) {
|
||||
case STATS_PARTY_OVERVIEW:
|
||||
showPartyView(avatarOnly);
|
||||
break;
|
||||
case STATS_CHAR1:
|
||||
case STATS_CHAR2:
|
||||
case STATS_CHAR3:
|
||||
case STATS_CHAR4:
|
||||
case STATS_CHAR5:
|
||||
case STATS_CHAR6:
|
||||
case STATS_CHAR7:
|
||||
case STATS_CHAR8:
|
||||
showPlayerDetails();
|
||||
break;
|
||||
case STATS_WEAPONS:
|
||||
showWeapons();
|
||||
break;
|
||||
case STATS_ARMOR:
|
||||
showArmor();
|
||||
break;
|
||||
case STATS_EQUIPMENT:
|
||||
showEquipment();
|
||||
break;
|
||||
case STATS_ITEMS:
|
||||
showItems();
|
||||
break;
|
||||
case STATS_REAGENTS:
|
||||
showReagents();
|
||||
break;
|
||||
case STATS_MIXTURES:
|
||||
showMixtures();
|
||||
break;
|
||||
case MIX_REAGENTS:
|
||||
showReagents(true);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* update the lower stats box (food, gold, etc.)
|
||||
*/
|
||||
if (g_context->_transportContext == TRANSPORT_SHIP)
|
||||
_summary.textAt(0, 0, "F:%04d SHP:%02d", g_ultima->_saveGame->_food / 100, g_ultima->_saveGame->_shipHull);
|
||||
else
|
||||
_summary.textAt(0, 0, "F:%04d G:%04d", g_ultima->_saveGame->_food / 100, g_ultima->_saveGame->_gold);
|
||||
|
||||
update(g_context->_aura);
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
||||
void StatsArea::update(Aura *observable, NoArg *arg) {
|
||||
Observer<Aura *>::update(observable, arg);
|
||||
}
|
||||
|
||||
void StatsArea::update(Aura *aura) {
|
||||
byte mask = 0xff;
|
||||
for (int i = 0; i < VIRT_MAX; i++) {
|
||||
if (g_ultima->_saveGame->_karma[i] == 0)
|
||||
mask &= ~(1 << i);
|
||||
}
|
||||
|
||||
switch (aura->getType()) {
|
||||
case Aura::NONE:
|
||||
_summary.drawCharMasked(0, STATS_AREA_WIDTH / 2, 0, mask);
|
||||
break;
|
||||
case Aura::HORN:
|
||||
_summary.drawChar(CHARSET_REDDOT, STATS_AREA_WIDTH / 2, 0);
|
||||
break;
|
||||
case Aura::JINX:
|
||||
_summary.drawChar('J', STATS_AREA_WIDTH / 2, 0);
|
||||
break;
|
||||
case Aura::NEGATE:
|
||||
_summary.drawChar('N', STATS_AREA_WIDTH / 2, 0);
|
||||
break;
|
||||
case Aura::PROTECTION:
|
||||
_summary.drawChar('P', STATS_AREA_WIDTH / 2, 0);
|
||||
break;
|
||||
case Aura::QUICKNESS:
|
||||
_summary.drawChar('Q', STATS_AREA_WIDTH / 2, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
_summary.update();
|
||||
}
|
||||
|
||||
void StatsArea::update(Party *party, PartyEvent &event) {
|
||||
update(); // Do a full update
|
||||
}
|
||||
|
||||
void StatsArea::update(Menu *menu, MenuEvent &event) {
|
||||
update(); // Do a full update
|
||||
}
|
||||
|
||||
void StatsArea::highlightPlayer(int player) {
|
||||
assertMsg(player < g_context->_party->size(), "player number out of range: %d", player);
|
||||
_mainArea.highlight(0, player * CHAR_HEIGHT, STATS_AREA_WIDTH * CHAR_WIDTH, CHAR_HEIGHT);
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::updateActivePartyMember(player);
|
||||
#endif
|
||||
}
|
||||
|
||||
void StatsArea::clear() {
|
||||
for (int i = 0; i < STATS_AREA_WIDTH; i++)
|
||||
_title.drawChar(CHARSET_HORIZBAR, i, 0);
|
||||
|
||||
_mainArea.clear();
|
||||
_summary.clear();
|
||||
}
|
||||
|
||||
void StatsArea::redraw() {
|
||||
_title.update();
|
||||
_mainArea.update();
|
||||
_summary.update();
|
||||
}
|
||||
|
||||
void StatsArea::setTitle(const Common::String &s) {
|
||||
int titleStart = (STATS_AREA_WIDTH / 2) - ((s.size() + 2) / 2);
|
||||
_title.textAt(titleStart, 0, "%c%s%c", 16, s.c_str(), 17);
|
||||
}
|
||||
|
||||
void StatsArea::showPartyView(bool avatarOnly) {
|
||||
const char *format = "%d%c%-9.8s%3d%s";
|
||||
|
||||
PartyMember *p = nullptr;
|
||||
int activePlayer = g_context->_party->getActivePlayer();
|
||||
|
||||
assertMsg(g_context->_party->size() <= 8, "party members out of range: %d", g_context->_party->size());
|
||||
|
||||
if (!avatarOnly) {
|
||||
for (int i = 0; i < g_context->_party->size(); i++) {
|
||||
p = g_context->_party->member(i);
|
||||
_mainArea.textAt(0, i, format, i + 1, (i == activePlayer) ? CHARSET_BULLET : '-', p->getName().c_str(), p->getHp(), _mainArea.colorizeStatus(p->getStatus()).c_str());
|
||||
}
|
||||
} else {
|
||||
p = g_context->_party->member(0);
|
||||
_mainArea.textAt(0, 0, format, 1, (activePlayer == 0) ? CHARSET_BULLET : '-', p->getName().c_str(), p->getHp(), _mainArea.colorizeStatus(p->getStatus()).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void StatsArea::showPlayerDetails() {
|
||||
int player = _view - STATS_CHAR1;
|
||||
|
||||
assertMsg(player < 8, "character number out of range: %d", player);
|
||||
|
||||
PartyMember *p = g_context->_party->member(player);
|
||||
setTitle(p->getName());
|
||||
_mainArea.textAt(0, 0, "%c %c", p->getSex(), p->getStatus());
|
||||
Common::String classStr = getClassName(p->getClass());
|
||||
int classStart = (STATS_AREA_WIDTH / 2) - (classStr.size() / 2);
|
||||
_mainArea.textAt(classStart, 0, "%s", classStr.c_str());
|
||||
_mainArea.textAt(0, 2, " MP:%02d LV:%d", p->getMp(), p->getRealLevel());
|
||||
_mainArea.textAt(0, 3, "STR:%02d HP:%04d", p->getStr(), p->getHp());
|
||||
_mainArea.textAt(0, 4, "DEX:%02d HM:%04d", p->getDex(), p->getMaxHp());
|
||||
_mainArea.textAt(0, 5, "INT:%02d EX:%04d", p->getInt(), p->getExp());
|
||||
_mainArea.textAt(0, 6, "W:%s", p->getWeapon()->getName().c_str());
|
||||
_mainArea.textAt(0, 7, "A:%s", p->getArmor()->getName().c_str());
|
||||
}
|
||||
|
||||
void StatsArea::showWeapons() {
|
||||
setTitle("Weapons");
|
||||
|
||||
int line = 0;
|
||||
int col = 0;
|
||||
_mainArea.textAt(0, line++, "A-%s", g_weapons->get(WEAP_HANDS)->getName().c_str());
|
||||
for (int w = WEAP_HANDS + 1; w < WEAP_MAX; w++) {
|
||||
int n = g_ultima->_saveGame->_weapons[w];
|
||||
if (n >= 100)
|
||||
n = 99;
|
||||
if (n >= 1) {
|
||||
const char *format = (n >= 10) ? "%c%d-%s" : "%c-%d-%s";
|
||||
|
||||
_mainArea.textAt(col, line++, format, w - WEAP_HANDS + 'A', n, g_weapons->get((WeaponType) w)->getAbbrev().c_str());
|
||||
if (line >= (STATS_AREA_HEIGHT)) {
|
||||
line = 0;
|
||||
col += 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatsArea::showArmor() {
|
||||
setTitle("Armour");
|
||||
|
||||
int line = 0;
|
||||
_mainArea.textAt(0, line++, "A -No Armour");
|
||||
for (int a = ARMR_NONE + 1; a < ARMR_MAX; a++) {
|
||||
if (g_ultima->_saveGame->_armor[a] > 0) {
|
||||
const char *format = (g_ultima->_saveGame->_armor[a] >= 10) ? "%c%d-%s" : "%c-%d-%s";
|
||||
|
||||
_mainArea.textAt(0, line++, format, a - ARMR_NONE + 'A', g_ultima->_saveGame->_armor[a], g_armors->get((ArmorType) a)->getName().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatsArea::showEquipment() {
|
||||
setTitle("Equipment");
|
||||
|
||||
int line = 0;
|
||||
_mainArea.textAt(0, line++, "%2d Torches", g_ultima->_saveGame->_torches);
|
||||
_mainArea.textAt(0, line++, "%2d Gems", g_ultima->_saveGame->_gems);
|
||||
_mainArea.textAt(0, line++, "%2d Keys", g_ultima->_saveGame->_keys);
|
||||
if (g_ultima->_saveGame->_sextants > 0)
|
||||
_mainArea.textAt(0, line++, "%2d Sextants", g_ultima->_saveGame->_sextants);
|
||||
}
|
||||
|
||||
void StatsArea::showItems() {
|
||||
int i, j;
|
||||
char buffer[17];
|
||||
|
||||
setTitle("Items");
|
||||
|
||||
int line = 0;
|
||||
if (g_ultima->_saveGame->_stones != 0) {
|
||||
j = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (g_ultima->_saveGame->_stones & (1 << i))
|
||||
buffer[j++] = getStoneName((Virtue) i)[0];
|
||||
}
|
||||
buffer[j] = '\0';
|
||||
_mainArea.textAt(0, line++, "Stones:%s", buffer);
|
||||
}
|
||||
if (g_ultima->_saveGame->_runes != 0) {
|
||||
j = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (g_ultima->_saveGame->_runes & (1 << i))
|
||||
buffer[j++] = getVirtueName((Virtue) i)[0];
|
||||
}
|
||||
buffer[j] = '\0';
|
||||
_mainArea.textAt(0, line++, "Runes:%s", buffer);
|
||||
}
|
||||
if (g_ultima->_saveGame->_items & (ITEM_CANDLE | ITEM_BOOK | ITEM_BELL)) {
|
||||
buffer[0] = '\0';
|
||||
if (g_ultima->_saveGame->_items & ITEM_BELL) {
|
||||
Common::strcat_s(buffer, getItemName(ITEM_BELL));
|
||||
Common::strcat_s(buffer, " ");
|
||||
}
|
||||
if (g_ultima->_saveGame->_items & ITEM_BOOK) {
|
||||
Common::strcat_s(buffer, getItemName(ITEM_BOOK));
|
||||
Common::strcat_s(buffer, " ");
|
||||
}
|
||||
if (g_ultima->_saveGame->_items & ITEM_CANDLE) {
|
||||
Common::strcat_s(buffer, getItemName(ITEM_CANDLE));
|
||||
buffer[15] = '\0';
|
||||
}
|
||||
_mainArea.textAt(0, line++, "%s", buffer);
|
||||
}
|
||||
if (g_ultima->_saveGame->_items & (ITEM_KEY_C | ITEM_KEY_L | ITEM_KEY_T)) {
|
||||
j = 0;
|
||||
if (g_ultima->_saveGame->_items & ITEM_KEY_T)
|
||||
buffer[j++] = getItemName(ITEM_KEY_T)[0];
|
||||
if (g_ultima->_saveGame->_items & ITEM_KEY_L)
|
||||
buffer[j++] = getItemName(ITEM_KEY_L)[0];
|
||||
if (g_ultima->_saveGame->_items & ITEM_KEY_C)
|
||||
buffer[j++] = getItemName(ITEM_KEY_C)[0];
|
||||
buffer[j] = '\0';
|
||||
_mainArea.textAt(0, line++, "3 Part Key:%s", buffer);
|
||||
}
|
||||
if (g_ultima->_saveGame->_items & ITEM_HORN)
|
||||
_mainArea.textAt(0, line++, "%s", getItemName(ITEM_HORN));
|
||||
if (g_ultima->_saveGame->_items & ITEM_WHEEL)
|
||||
_mainArea.textAt(0, line++, "%s", getItemName(ITEM_WHEEL));
|
||||
if (g_ultima->_saveGame->_items & ITEM_SKULL)
|
||||
_mainArea.textAt(0, line++, "%s", getItemName(ITEM_SKULL));
|
||||
}
|
||||
|
||||
void StatsArea::showReagents(bool active) {
|
||||
setTitle("Reagents");
|
||||
|
||||
int line = 0,
|
||||
r = REAG_ASH;
|
||||
Common::String shortcut("A");
|
||||
|
||||
_reagentsMixMenu.show(&_mainArea);
|
||||
|
||||
for (const auto *item : _reagentsMixMenu) {
|
||||
if (item->isVisible()) {
|
||||
// Insert the reagent menu item shortcut character
|
||||
shortcut.setChar('A' + r, 0);
|
||||
if (active)
|
||||
_mainArea.textAt(0, line++, "%s", _mainArea.colorizeString(shortcut, FG_YELLOW, 0, 1).c_str());
|
||||
else
|
||||
_mainArea.textAt(0, line++, "%s", shortcut.c_str());
|
||||
}
|
||||
r++;
|
||||
}
|
||||
}
|
||||
|
||||
void StatsArea::showMixtures() {
|
||||
setTitle("Mixtures");
|
||||
|
||||
int line = 0;
|
||||
int col = 0;
|
||||
for (int s = 0; s < SPELL_MAX; s++) {
|
||||
int n = g_ultima->_saveGame->_mixtures[s];
|
||||
if (n >= 100)
|
||||
n = 99;
|
||||
if (n >= 1) {
|
||||
_mainArea.textAt(col, line++, "%c-%02d", s + 'A', n);
|
||||
if (line >= (STATS_AREA_HEIGHT)) {
|
||||
if (col >= 10)
|
||||
break;
|
||||
line = 0;
|
||||
col += 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatsArea::resetReagentsMenu() {
|
||||
int i = 0, row = 0;
|
||||
|
||||
for (auto *item : _reagentsMixMenu) {
|
||||
if (g_ultima->_saveGame->_reagents[i++] > 0) {
|
||||
item->setVisible(true);
|
||||
item->setY(row++);
|
||||
} else {
|
||||
item->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
_reagentsMixMenu.reset(false);
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
167
engines/ultima/ultima4/views/stats.h
Normal file
167
engines/ultima/ultima4/views/stats.h
Normal file
@@ -0,0 +1,167 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_VIEWS_STATS_H
|
||||
#define ULTIMA4_VIEWS_STATS_H
|
||||
|
||||
#include "ultima/ultima4/core/observable.h"
|
||||
#include "ultima/ultima4/core/observer.h"
|
||||
#include "ultima/ultima4/views/menu.h"
|
||||
#include "ultima/ultima4/views/textview.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
struct SaveGame;
|
||||
class Aura;
|
||||
class Ingredients;
|
||||
class Menu;
|
||||
class MenuEvent;
|
||||
class Party;
|
||||
class PartyEvent;
|
||||
|
||||
#define STATS_AREA_WIDTH 15
|
||||
#define STATS_AREA_HEIGHT 8
|
||||
#define STATS_AREA_X TEXT_AREA_X
|
||||
#define STATS_AREA_Y 1
|
||||
|
||||
enum StatsView {
|
||||
STATS_PARTY_OVERVIEW,
|
||||
STATS_CHAR1,
|
||||
STATS_CHAR2,
|
||||
STATS_CHAR3,
|
||||
STATS_CHAR4,
|
||||
STATS_CHAR5,
|
||||
STATS_CHAR6,
|
||||
STATS_CHAR7,
|
||||
STATS_CHAR8,
|
||||
STATS_WEAPONS,
|
||||
STATS_ARMOR,
|
||||
STATS_EQUIPMENT,
|
||||
STATS_ITEMS,
|
||||
STATS_REAGENTS,
|
||||
STATS_MIXTURES,
|
||||
MIX_REAGENTS
|
||||
};
|
||||
|
||||
class StatsArea : public Observer<Aura *>, public Observer<Party *, PartyEvent &>,
|
||||
public Observer<Menu *, MenuEvent &>, public Observable<StatsArea *, Common::String> {
|
||||
public:
|
||||
StatsArea();
|
||||
|
||||
void setView(StatsView view);
|
||||
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Sets the stats item to the previous in sequence.
|
||||
*/
|
||||
void prevItem();
|
||||
|
||||
/**
|
||||
* Sets the stats item to the next in sequence.
|
||||
*/
|
||||
void nextItem();
|
||||
|
||||
/**
|
||||
* Update the stats (ztats) box on the upper right of the screen.
|
||||
*/
|
||||
virtual void update(bool avatarOnly = false);
|
||||
void update(Aura *observable, NoArg *arg) override;
|
||||
void update(Aura *aura) override;
|
||||
void update(Party *party, PartyEvent &event) override;
|
||||
void update(Menu *menu, MenuEvent &event) override;
|
||||
|
||||
void highlightPlayer(int player);
|
||||
|
||||
/**
|
||||
* Redraws the entire stats area
|
||||
*/
|
||||
void redraw();
|
||||
|
||||
TextView *getMainArea() {
|
||||
return &_mainArea;
|
||||
}
|
||||
|
||||
void resetReagentsMenu();
|
||||
Menu *getReagentsMenu() {
|
||||
return &_reagentsMixMenu;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* The basic party view.
|
||||
*/
|
||||
void showPartyView(bool avatarOnly);
|
||||
|
||||
/**
|
||||
* The individual character view.
|
||||
*/
|
||||
void showPlayerDetails();
|
||||
|
||||
/**
|
||||
* Weapons in inventory.
|
||||
*/
|
||||
void showWeapons();
|
||||
|
||||
/**
|
||||
* Armor in inventory.
|
||||
*/
|
||||
void showArmor();
|
||||
|
||||
/**
|
||||
* Equipment: touches, gems, keys, and sextants.
|
||||
*/
|
||||
void showEquipment();
|
||||
|
||||
/**
|
||||
* Items: runes, stones, and other miscellaneous quest items.
|
||||
*/
|
||||
void showItems();
|
||||
|
||||
/**
|
||||
* Unmixed reagents in inventory.
|
||||
*/
|
||||
void showReagents(bool active = false);
|
||||
|
||||
/**
|
||||
* Mixed reagents in inventory.
|
||||
*/
|
||||
void showMixtures();
|
||||
|
||||
/**
|
||||
* Sets the title of the stats area.
|
||||
*/
|
||||
void setTitle(const Common::String &s);
|
||||
|
||||
TextView _title;
|
||||
TextView _mainArea;
|
||||
TextView _summary;
|
||||
|
||||
StatsView _view;
|
||||
|
||||
Menu _reagentsMixMenu;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
312
engines/ultima/ultima4/views/textview.cpp
Normal file
312
engines/ultima/ultima4/views/textview.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/events/event_handler.h"
|
||||
#include "ultima/ultima4/gfx/image.h"
|
||||
#include "ultima/ultima4/gfx/imagemgr.h"
|
||||
#include "ultima/ultima4/core/settings.h"
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
#include "ultima/ultima4/views/textview.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
Image *TextView::_charset = nullptr;
|
||||
|
||||
TextView::TextView(int x, int y, int columns, int rows) : View(x, y, columns * CHAR_WIDTH, rows * CHAR_HEIGHT) {
|
||||
this->_columns = columns;
|
||||
this->_rows = rows;
|
||||
this->_cursorEnabled = false;
|
||||
this->_cursorFollowsText = false;
|
||||
this->_cursorX = 0;
|
||||
this->_cursorY = 0;
|
||||
this->_cursorPhase = 0;
|
||||
if (_charset == nullptr)
|
||||
_charset = imageMgr->get(BKGD_CHARSET)->_image;
|
||||
eventHandler->getTimer()->add(&cursorTimer, /*SCR_CYCLE_PER_SECOND*/4, this);
|
||||
}
|
||||
|
||||
TextView::~TextView() {
|
||||
eventHandler->getTimer()->remove(&cursorTimer, this);
|
||||
}
|
||||
|
||||
void TextView::reinit() {
|
||||
View::reinit();
|
||||
_charset = imageMgr->get(BKGD_CHARSET)->_image;
|
||||
}
|
||||
|
||||
void TextView::drawChar(int chr, int x, int y) {
|
||||
assertMsg(x < _columns, "x value of %d out of range", x);
|
||||
assertMsg(y < _rows, "y value of %d out of range", y);
|
||||
|
||||
_charset->drawSubRect(SCALED(_bounds.left + (x * CHAR_WIDTH)),
|
||||
SCALED(_bounds.top + (y * CHAR_HEIGHT)),
|
||||
0, SCALED(chr * CHAR_HEIGHT),
|
||||
SCALED(CHAR_WIDTH),
|
||||
SCALED(CHAR_HEIGHT));
|
||||
}
|
||||
|
||||
void TextView::drawCharMasked(int chr, int x, int y, byte mask) {
|
||||
drawChar(chr, x, y);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (mask & (1 << i)) {
|
||||
_screen->fillRect(SCALED(_bounds.left + (x * CHAR_WIDTH)),
|
||||
SCALED(_bounds.top + (y * CHAR_HEIGHT) + i),
|
||||
SCALED(CHAR_WIDTH),
|
||||
SCALED(1),
|
||||
0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextView::textSelectedAt(int x, int y, const char *text) {
|
||||
if (!settings._enhancements || !settings._enhancementsOptions._textColorization) {
|
||||
this->textAt(x, y, "%s", text);
|
||||
return;
|
||||
}
|
||||
|
||||
this->setFontColorBG(BG_BRIGHT);
|
||||
for (int i = 0; i < this->getWidth() - 1; i++)
|
||||
this->textAt(x - 1 + i, y, " ");
|
||||
this->textAt(x, y, "%s", text);
|
||||
this->setFontColorBG(BG_NORMAL);
|
||||
}
|
||||
|
||||
Common::String TextView::colorizeStatus(char statustype) {
|
||||
Common::String output;
|
||||
|
||||
if (!settings._enhancements || !settings._enhancementsOptions._textColorization) {
|
||||
output = statustype;
|
||||
return output;
|
||||
}
|
||||
|
||||
switch (statustype) {
|
||||
case 'P':
|
||||
output = FG_GREEN;
|
||||
break;
|
||||
case 'S':
|
||||
output = FG_PURPLE;
|
||||
break;
|
||||
case 'D':
|
||||
output = FG_RED;
|
||||
break;
|
||||
default:
|
||||
output = statustype;
|
||||
return output;
|
||||
}
|
||||
output += statustype;
|
||||
output += FG_WHITE;
|
||||
return output;
|
||||
}
|
||||
|
||||
Common::String TextView::colorizeString(Common::String input, ColorFG color, uint colorstart, uint colorlength) {
|
||||
if (!settings._enhancements || !settings._enhancementsOptions._textColorization)
|
||||
return input;
|
||||
|
||||
Common::String output = "";
|
||||
size_t length = input.size();
|
||||
size_t i;
|
||||
bool colorization = false;
|
||||
|
||||
// loop through the entire Common::String and
|
||||
for (i = 0; i < length; i++) {
|
||||
if (i == colorstart) {
|
||||
output += color;
|
||||
colorization = true;
|
||||
}
|
||||
output += input[i];
|
||||
if (colorization) {
|
||||
colorlength--;
|
||||
if (colorlength == 0) {
|
||||
output += FG_WHITE;
|
||||
colorization = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we reached the end of the Common::String without
|
||||
// resetting the color to white, do it now
|
||||
if (colorization)
|
||||
output += FG_WHITE;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void TextView::setFontColor(ColorFG fg, ColorBG bg) {
|
||||
_charset->setFontColorFG(fg);
|
||||
_charset->setFontColorBG(bg);
|
||||
}
|
||||
|
||||
void TextView::setFontColorFG(ColorFG fg) {
|
||||
_charset->setFontColorFG(fg);
|
||||
}
|
||||
|
||||
void TextView::setFontColorBG(ColorBG bg) {
|
||||
_charset->setFontColorBG(bg);
|
||||
}
|
||||
|
||||
void TextView::textAt(int x, int y, const char *fmt, ...) {
|
||||
char buffer[1024];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
optionAt(x, y, '\0', "%s", buffer);
|
||||
}
|
||||
|
||||
void TextView::optionAt(int x, int y, char key, const char *fmt, ...) {
|
||||
char buffer[1024];
|
||||
uint i;
|
||||
uint offset = 0;
|
||||
|
||||
bool reenableCursor = false;
|
||||
if (_cursorFollowsText && _cursorEnabled) {
|
||||
disableCursor();
|
||||
reenableCursor = true;
|
||||
}
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
for (i = 0; i < strlen(buffer); i++) {
|
||||
switch (buffer[i]) {
|
||||
case FG_GREY:
|
||||
case FG_BLUE:
|
||||
case FG_PURPLE:
|
||||
case FG_GREEN:
|
||||
case FG_RED:
|
||||
case FG_YELLOW:
|
||||
case FG_WHITE:
|
||||
setFontColorFG((ColorFG)buffer[i]);
|
||||
offset++;
|
||||
break;
|
||||
default:
|
||||
drawChar(buffer[i], x + (i - offset), y);
|
||||
}
|
||||
}
|
||||
|
||||
if (_cursorFollowsText)
|
||||
setCursorPos(x + i, y, true);
|
||||
if (reenableCursor)
|
||||
enableCursor();
|
||||
|
||||
if (key) {
|
||||
Common::Rect r(
|
||||
SCALED(_bounds.left + (x * CHAR_WIDTH)),
|
||||
SCALED(_bounds.top + (y * CHAR_HEIGHT)),
|
||||
SCALED(_bounds.left + (x + strlen(buffer) - offset) * CHAR_WIDTH),
|
||||
SCALED(_bounds.top + (y + 1) * CHAR_HEIGHT)
|
||||
);
|
||||
|
||||
_options.push_back(Option(r, key));
|
||||
}
|
||||
}
|
||||
|
||||
void TextView::scroll() {
|
||||
_screen->drawSubRectOn(_screen,
|
||||
SCALED(_bounds.left),
|
||||
SCALED(_bounds.top),
|
||||
SCALED(_bounds.left),
|
||||
SCALED(_bounds.top) + SCALED(CHAR_HEIGHT),
|
||||
SCALED(_bounds.width()),
|
||||
SCALED(_bounds.height()) - SCALED(CHAR_HEIGHT));
|
||||
|
||||
_screen->fillRect(SCALED(_bounds.left),
|
||||
SCALED(_bounds.top + (CHAR_HEIGHT * (_rows - 1))),
|
||||
SCALED(_bounds.width()),
|
||||
SCALED(CHAR_HEIGHT),
|
||||
0, 0, 0);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void TextView::setCursorPos(int x, int y, bool clearOld) {
|
||||
while (x >= _columns) {
|
||||
x -= _columns;
|
||||
y++;
|
||||
}
|
||||
assertMsg(y < _rows, "y value of %d out of range", y);
|
||||
|
||||
if (clearOld && _cursorEnabled) {
|
||||
drawChar(' ', _cursorX, _cursorY);
|
||||
update(_cursorX * CHAR_WIDTH, _cursorY * CHAR_HEIGHT, CHAR_WIDTH, CHAR_HEIGHT);
|
||||
}
|
||||
|
||||
_cursorX = x;
|
||||
_cursorY = y;
|
||||
|
||||
drawCursor();
|
||||
}
|
||||
|
||||
void TextView::enableCursor() {
|
||||
_cursorEnabled = true;
|
||||
drawCursor();
|
||||
}
|
||||
|
||||
void TextView::disableCursor() {
|
||||
_cursorEnabled = false;
|
||||
drawChar(' ', _cursorX, _cursorY);
|
||||
update(_cursorX * CHAR_WIDTH, _cursorY * CHAR_HEIGHT, CHAR_WIDTH, CHAR_HEIGHT);
|
||||
}
|
||||
|
||||
void TextView::drawCursor() {
|
||||
assertMsg(_cursorPhase >= 0 && _cursorPhase < 4, "invalid cursor phase: %d", _cursorPhase);
|
||||
|
||||
if (!_cursorEnabled)
|
||||
return;
|
||||
|
||||
drawChar(31 - _cursorPhase, _cursorX, _cursorY);
|
||||
update(_cursorX * CHAR_WIDTH, _cursorY * CHAR_HEIGHT, CHAR_WIDTH, CHAR_HEIGHT);
|
||||
}
|
||||
|
||||
void TextView::cursorTimer(void *data) {
|
||||
TextView *thiz = static_cast<TextView *>(data);
|
||||
thiz->_cursorPhase = (thiz->_cursorPhase + 1) % 4;
|
||||
thiz->drawCursor();
|
||||
}
|
||||
|
||||
char TextView::getOptionAt(const Common::Point &mousePos) {
|
||||
for (uint idx = 0; idx < _options.size(); ++idx) {
|
||||
if (_options[idx].contains(mousePos))
|
||||
return _options[idx]._key;
|
||||
}
|
||||
|
||||
return '\0';
|
||||
}
|
||||
|
||||
void TextView::clearOptions() {
|
||||
_options.clear();
|
||||
}
|
||||
|
||||
Common::Rect TextView::getTextBounds(int x, int y, int textWidth) const {
|
||||
return Common::Rect(
|
||||
SCALED(_bounds.left + (x * CHAR_WIDTH)),
|
||||
SCALED(_bounds.top + (y * CHAR_HEIGHT)),
|
||||
SCALED(_bounds.left + (x + textWidth * CHAR_WIDTH)),
|
||||
SCALED(_bounds.top + (y + 1) * CHAR_HEIGHT)
|
||||
);
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
149
engines/ultima/ultima4/views/textview.h
Normal file
149
engines/ultima/ultima4/views/textview.h
Normal file
@@ -0,0 +1,149 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_VIEWS_TEXTVIEW_H
|
||||
#define ULTIMA4_VIEWS_TEXTVIEW_H
|
||||
|
||||
#include "ultima/ultima4/views/view.h"
|
||||
#include "ultima/ultima4/gfx/image.h"
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
#define PRINTF_LIKE(x,y)
|
||||
|
||||
#define CHAR_WIDTH 8
|
||||
#define CHAR_HEIGHT 8
|
||||
|
||||
/**
|
||||
* A view of a text area. Keeps track of the cursor position.
|
||||
*/
|
||||
class TextView : public View {
|
||||
struct Option : public Common::Rect {
|
||||
char _key;
|
||||
Option() : Common::Rect(), _key('\0') {}
|
||||
Option(const Common::Rect &r, char key) : Common::Rect(r), _key(key) {}
|
||||
};
|
||||
protected:
|
||||
int _columns, _rows; /**< size of the view in character cells */
|
||||
bool _cursorEnabled; /**< whether the cursor is enabled */
|
||||
bool _cursorFollowsText; /**< whether the cursor is moved past the last character written */
|
||||
int _cursorX, _cursorY; /**< current position of cursor */
|
||||
int _cursorPhase; /**< the rotation state of the cursor */
|
||||
static Image *_charset; /**< image containing font */
|
||||
Common::Array<Option> _options;
|
||||
public:
|
||||
TextView(int x, int y, int columns, int rows);
|
||||
virtual ~TextView();
|
||||
|
||||
void reinit();
|
||||
|
||||
int getCursorX() const {
|
||||
return _cursorX;
|
||||
}
|
||||
int getCursorY() const {
|
||||
return _cursorY;
|
||||
}
|
||||
bool getCursorEnabled() const {
|
||||
return _cursorEnabled;
|
||||
}
|
||||
int getWidth() const {
|
||||
return _columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a character from the charset onto the view.
|
||||
*/
|
||||
void drawChar(int chr, int x, int y);
|
||||
|
||||
/**
|
||||
* Draw a character from the charset onto the view, but mask it with
|
||||
* horizontal lines. This is used for the avatar symbol in the
|
||||
* statistics area, where a line is masked out for each virtue in
|
||||
* which the player is not an avatar.
|
||||
*/
|
||||
void drawCharMasked(int chr, int x, int y, byte mask);
|
||||
|
||||
/**
|
||||
* Draw text at the given position
|
||||
*/
|
||||
void textAt(int x, int y, const char *fmt, ...);
|
||||
|
||||
/**
|
||||
* Draw an option at
|
||||
*/
|
||||
void optionAt(int x, int y, char key, const char *fmt, ...);
|
||||
|
||||
void scroll();
|
||||
|
||||
void setCursorFollowsText(bool follows) {
|
||||
_cursorFollowsText = follows;
|
||||
}
|
||||
void setCursorPos(int x, int y, bool clearOld = true);
|
||||
void enableCursor();
|
||||
void disableCursor();
|
||||
void drawCursor();
|
||||
static void cursorTimer(void *data);
|
||||
|
||||
// functions to modify the charset font palette
|
||||
void setFontColor(ColorFG fg, ColorBG bg);
|
||||
void setFontColorFG(ColorFG fg);
|
||||
void setFontColorBG(ColorBG bg);
|
||||
|
||||
// functions to add color to strings
|
||||
/**
|
||||
* Highlight the selected row using a background color
|
||||
*/
|
||||
void textSelectedAt(int x, int y, const char *text);
|
||||
|
||||
/**
|
||||
* Depending on the status type, apply colorization to the character
|
||||
*/
|
||||
Common::String colorizeStatus(char statustype);
|
||||
|
||||
/**
|
||||
* Depending on the status type, apply colorization to the character
|
||||
*/
|
||||
Common::String colorizeString(Common::String input, ColorFG color, uint colorstart, uint colorlength = 0);
|
||||
|
||||
/**
|
||||
* Checks if a given position has an option
|
||||
*/
|
||||
char getOptionAt(const Common::Point &mousePos);
|
||||
|
||||
/**
|
||||
* Clear the options list
|
||||
*/
|
||||
void clearOptions();
|
||||
|
||||
/**
|
||||
* Returns the physical screen dimensions of text at a given
|
||||
* text location
|
||||
*/
|
||||
Common::Rect getTextBounds(int x, int y, int textWidth) const;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
211
engines/ultima/ultima4/views/tileview.cpp
Normal file
211
engines/ultima/ultima4/views/tileview.cpp
Normal file
@@ -0,0 +1,211 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/core/config.h"
|
||||
#include "ultima/ultima4/gfx/image.h"
|
||||
#include "ultima/ultima4/gfx/imagemgr.h"
|
||||
#include "ultima/ultima4/core/settings.h"
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
#include "ultima/ultima4/gfx/screen.h"
|
||||
#include "ultima/ultima4/map/tile.h"
|
||||
#include "ultima/ultima4/map/tileanim.h"
|
||||
#include "ultima/ultima4/map/tileset.h"
|
||||
#include "ultima/ultima4/views/tileview.h"
|
||||
#include "ultima/ultima4/ultima4.h"
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
TileView::TileView(int x, int y, int columns, int rows) :
|
||||
View(x, y, columns * TILE_WIDTH, rows * TILE_HEIGHT) {
|
||||
_columns = columns;
|
||||
_rows = rows;
|
||||
_tileWidth = TILE_WIDTH;
|
||||
_tileHeight = TILE_HEIGHT;
|
||||
_tileSet = g_tileSets->get("base");
|
||||
_animated = Image::create(SCALED(_tileWidth), SCALED(_tileHeight), g_system->getScreenFormat());
|
||||
_dest = nullptr;
|
||||
}
|
||||
|
||||
TileView::TileView(int x, int y, int columns, int rows, const Common::String &tileset) :
|
||||
View(x, y, columns * TILE_WIDTH, rows * TILE_HEIGHT) {
|
||||
_columns = columns;
|
||||
_rows = rows;
|
||||
_tileWidth = TILE_WIDTH;
|
||||
_tileHeight = TILE_HEIGHT;
|
||||
_tileSet = g_tileSets->get(tileset);
|
||||
_animated = Image::create(SCALED(_tileWidth), SCALED(_tileHeight), g_system->getScreenFormat());
|
||||
_dest = nullptr;
|
||||
}
|
||||
|
||||
TileView::~TileView() {
|
||||
delete _animated;
|
||||
}
|
||||
|
||||
void TileView::reinit() {
|
||||
View::reinit();
|
||||
_tileSet = g_tileSets->get("base");
|
||||
|
||||
// Scratchpad needs to be re-inited if we rescale...
|
||||
if (_animated) {
|
||||
delete _animated;
|
||||
_animated = nullptr;
|
||||
}
|
||||
_animated = Image::create(SCALED(_tileWidth), SCALED(_tileHeight), _dest ? _dest->format() : g_system->getScreenFormat());
|
||||
}
|
||||
|
||||
void TileView::loadTile(MapTile &mapTile) {
|
||||
// This attempts to preload tiles in advance
|
||||
Tile *tile = _tileSet->get(mapTile._id);
|
||||
if (tile) {
|
||||
tile->getImage();
|
||||
}
|
||||
// But may fail if the tiles don't exist directly in the expected imagesets
|
||||
}
|
||||
|
||||
void TileView::drawTile(MapTile &mapTile, bool focus, int x, int y) {
|
||||
Tile *tile = _tileSet->get(mapTile._id);
|
||||
Image *image = tile->getImage();
|
||||
|
||||
assertMsg(x < _columns, "x value of %d out of range", x);
|
||||
assertMsg(y < _rows, "y value of %d out of range", y);
|
||||
|
||||
// Blank scratch pad
|
||||
_animated->fillRect(0, 0, SCALED(_tileWidth), SCALED(_tileHeight), 0, 0, 0, 255);
|
||||
|
||||
// Draw blackness on the tile.
|
||||
_animated->drawSubRectOn(_dest, SCALED(x * _tileWidth + _bounds.left),
|
||||
SCALED(y * _tileHeight + _bounds.top), 0, 0,
|
||||
SCALED(_tileWidth), SCALED(_tileHeight));
|
||||
|
||||
// Draw the tile to the screen
|
||||
if (tile->getAnim()) {
|
||||
// First, create our animated version of the tile
|
||||
#ifdef IOS_ULTIMA4
|
||||
animated->clearImageContents();
|
||||
#endif
|
||||
tile->getAnim()->draw(_animated, tile, mapTile, DIR_NONE);
|
||||
|
||||
// Then draw it to the screen
|
||||
_animated->drawSubRectOn(_dest, SCALED(x * _tileWidth + _bounds.left),
|
||||
SCALED(y * _tileHeight + _bounds.top), 0, 0,
|
||||
SCALED(_tileWidth), SCALED(_tileHeight));
|
||||
} else {
|
||||
image->drawSubRectOn(_dest, SCALED(x * _tileWidth + _bounds.left),
|
||||
SCALED(y * _tileHeight + _bounds.top),
|
||||
0, SCALED(_tileHeight * mapTile._frame),
|
||||
SCALED(_tileWidth), SCALED(_tileHeight));
|
||||
}
|
||||
|
||||
// Draw the focus around the tile if it has the focus
|
||||
if (focus)
|
||||
drawFocus(x, y);
|
||||
}
|
||||
|
||||
void TileView::drawTile(Std::vector<MapTile> &tiles, bool focus, int x, int y) {
|
||||
assertMsg(x < _columns, "x value of %d out of range", x);
|
||||
assertMsg(y < _rows, "y value of %d out of range", y);
|
||||
|
||||
// Clear tile contents
|
||||
_animated->fillRect(0, 0, SCALED(_tileWidth), SCALED(_tileHeight), 0, 0, 0, 255);
|
||||
_animated->drawSubRectOn(_dest,
|
||||
SCALED(x * _tileWidth + _bounds.left), SCALED(y * _tileHeight + _bounds.top),
|
||||
0, 0,
|
||||
SCALED(_tileWidth), SCALED(_tileHeight)
|
||||
);
|
||||
|
||||
// Iterate through rendering each of the needed tiles
|
||||
for (int t = tiles.size() - 1; t >= 0; --t) {
|
||||
MapTile &frontTile = tiles[t];
|
||||
Tile *frontTileType = _tileSet->get(frontTile._id);
|
||||
|
||||
if (!frontTileType) {
|
||||
// TODO: This leads to an error. It happens after graphics mode changes.
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the image for the tile
|
||||
Image *image = frontTileType->getImage();
|
||||
|
||||
// Draw the tile to the screen
|
||||
if (frontTileType->getAnim()) {
|
||||
// First, create our animated version of the tile
|
||||
frontTileType->getAnim()->draw(_animated, frontTileType, frontTile, DIR_NONE);
|
||||
} else {
|
||||
if (!image)
|
||||
// FIXME: This is a problem, error message it.
|
||||
return;
|
||||
image->drawSubRectOn(_animated, 0, 0,
|
||||
0, SCALED(_tileHeight * frontTile._frame),
|
||||
SCALED(_tileWidth), SCALED(_tileHeight)
|
||||
);
|
||||
}
|
||||
|
||||
// Then draw it to the screen
|
||||
_animated->drawSubRectOn(_dest, SCALED(x * _tileWidth + _bounds.left),
|
||||
SCALED(y * _tileHeight + _bounds.top), 0, 0,
|
||||
SCALED(_tileWidth), SCALED(_tileHeight)
|
||||
);
|
||||
}
|
||||
|
||||
// Draw the focus around the tile if it has the focus
|
||||
if (focus)
|
||||
drawFocus(x, y);
|
||||
}
|
||||
|
||||
void TileView::drawFocus(int x, int y) {
|
||||
assertMsg(x < _columns, "x value of %d out of range", x);
|
||||
assertMsg(y < _rows, "y value of %d out of range", y);
|
||||
|
||||
// Draw the focus rectangle around the tile
|
||||
if ((g_screen->_currentCycle * 4 / SCR_CYCLE_PER_SECOND) % 2) {
|
||||
// left edge
|
||||
_screen->fillRect(SCALED(x * _tileWidth + _bounds.left),
|
||||
SCALED(y * _tileHeight + _bounds.top),
|
||||
SCALED(2), SCALED(_tileHeight), 0xff, 0xff, 0xff);
|
||||
|
||||
// top edge
|
||||
_screen->fillRect(SCALED(x * _tileWidth + _bounds.left),
|
||||
SCALED(y * _tileHeight + _bounds.top),
|
||||
SCALED(_tileWidth), SCALED(2),
|
||||
0xff, 0xff, 0xff);
|
||||
|
||||
// Right edge
|
||||
_screen->fillRect(SCALED((x + 1) * _tileWidth + _bounds.left - 2),
|
||||
SCALED(y * _tileHeight + _bounds.top),
|
||||
SCALED(2), SCALED(_tileHeight),
|
||||
0xff, 0xff, 0xff);
|
||||
|
||||
// Bottom edge
|
||||
_screen->fillRect(SCALED(x * _tileWidth + _bounds.left),
|
||||
SCALED((y + 1) * _tileHeight + _bounds.top - 2),
|
||||
SCALED(_tileWidth), SCALED(2),
|
||||
0xff, 0xff, 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
void TileView::setTileset(Tileset *tileset) {
|
||||
this->_tileSet = tileset;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
73
engines/ultima/ultima4/views/tileview.h
Normal file
73
engines/ultima/ultima4/views/tileview.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_VIEWS_TILEVIEW_H
|
||||
#define ULTIMA4_VIEWS_TILEVIEW_H
|
||||
|
||||
#include "ultima/ultima4/views/view.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class Tile;
|
||||
class Tileset;
|
||||
class MapTile;
|
||||
|
||||
/**
|
||||
* A view of a grid of tiles. Used to draw Maps.
|
||||
* @todo
|
||||
* <ul>
|
||||
* <li>use for gem view</li>
|
||||
* <li>intialize from a Layout?</li>
|
||||
* </ul>
|
||||
*/
|
||||
class TileView : public View {
|
||||
public:
|
||||
TileView(int x, int y, int columns, int rows);
|
||||
TileView(int x, int y, int columns, int rows, const Common::String &tileset);
|
||||
virtual ~TileView();
|
||||
|
||||
void reinit();
|
||||
void drawTile(MapTile &mapTile, bool focus, int x, int y);
|
||||
void drawTile(Std::vector<MapTile> &tiles, bool focus, int x, int y);
|
||||
|
||||
/**
|
||||
* Draw a focus rectangle around the tile
|
||||
*/
|
||||
void drawFocus(int x, int y);
|
||||
void loadTile(MapTile &mapTile);
|
||||
void setTileset(Tileset *tileset);
|
||||
|
||||
void setDest(Image *dest) {
|
||||
_dest = dest;
|
||||
}
|
||||
protected:
|
||||
int _columns, _rows;
|
||||
int _tileWidth, _tileHeight;
|
||||
Tileset *_tileSet;
|
||||
Image *_animated; /**< a scratchpad image for drawing animations */
|
||||
Image *_dest; // Dest surface, nullptr by default for screen
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
99
engines/ultima/ultima4/views/view.cpp
Normal file
99
engines/ultima/ultima4/views/view.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/gfx/image.h"
|
||||
#include "ultima/ultima4/gfx/imagemgr.h"
|
||||
#include "ultima/ultima4/core/settings.h"
|
||||
#include "ultima/ultima4/views/view.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
Image *View::_screen = nullptr;
|
||||
|
||||
View::View(int x, int y, int width, int height) :
|
||||
_bounds(Common::Rect(x, y, x + width, y + height)),
|
||||
_highlighted(false) {
|
||||
if (_screen == nullptr)
|
||||
_screen = imageMgr->get("screen")->_image;
|
||||
}
|
||||
|
||||
void View::reinit() {
|
||||
_screen = imageMgr->get("screen")->_image;
|
||||
}
|
||||
|
||||
void View::clear() {
|
||||
unhighlight();
|
||||
_screen->fillRect(
|
||||
SCALED(_bounds.left), SCALED(_bounds.top),
|
||||
SCALED(_bounds.width()), SCALED(_bounds.height()), 0, 0, 0);
|
||||
}
|
||||
|
||||
void View::update() {
|
||||
if (_highlighted)
|
||||
drawHighlighted();
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::updateView();
|
||||
#endif
|
||||
}
|
||||
|
||||
void View::update(int x, int y, int width, int height) {
|
||||
if (_highlighted)
|
||||
drawHighlighted();
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::updateRectInView(x, y, width, height);
|
||||
#endif
|
||||
}
|
||||
|
||||
void View::highlight(int x, int y, int width, int height) {
|
||||
_highlighted = true;
|
||||
_highlightBounds = Common::Rect(x, y, x + width, y + height);
|
||||
|
||||
update(x, y, width, height);
|
||||
}
|
||||
|
||||
void View::unhighlight() {
|
||||
_highlighted = false;
|
||||
update(_highlightBounds.left, _highlightBounds.top,
|
||||
_highlightBounds.width(), _highlightBounds.height());
|
||||
_highlightBounds = Common::Rect();
|
||||
}
|
||||
|
||||
void View::drawHighlighted() {
|
||||
Image *screen = imageMgr->get("screen")->_image;
|
||||
|
||||
Image *tmp = Image::create(SCALED(_highlightBounds.width()),
|
||||
SCALED(_highlightBounds.height()), screen->format());
|
||||
if (!tmp)
|
||||
return;
|
||||
|
||||
screen->drawSubRectOn(tmp, 0, 0,
|
||||
SCALED(_bounds.left + _highlightBounds.left),
|
||||
SCALED(_bounds.top + _highlightBounds.top),
|
||||
SCALED(_highlightBounds.width()),
|
||||
SCALED(_highlightBounds.height()));
|
||||
tmp->drawHighlighted();
|
||||
tmp->draw(SCALED(_bounds.left + _highlightBounds.left), SCALED(_bounds.top + _highlightBounds.top));
|
||||
delete tmp;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
81
engines/ultima/ultima4/views/view.h
Normal file
81
engines/ultima/ultima4/views/view.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_VIEWS_VIEW_H
|
||||
#define ULTIMA4_VIEWS_VIEW_H
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
#define SCALED(n) ((n) * settings._scale)
|
||||
|
||||
class Image;
|
||||
|
||||
/**
|
||||
* Generic base class for reflecting the state of a game object onto
|
||||
* the screen.
|
||||
*/
|
||||
class View {
|
||||
public:
|
||||
View(int x, int y, int width, int height);
|
||||
virtual ~View() {}
|
||||
|
||||
/**
|
||||
* Hook for reinitializing when graphics reloaded.
|
||||
*/
|
||||
virtual void reinit();
|
||||
|
||||
/**
|
||||
* Clear the view to black.
|
||||
*/
|
||||
virtual void clear();
|
||||
|
||||
/**
|
||||
* Update the view to the screen.
|
||||
*/
|
||||
virtual void update();
|
||||
|
||||
/**
|
||||
* Update a piece of the view to the screen.
|
||||
*/
|
||||
virtual void update(int x, int y, int width, int height);
|
||||
|
||||
/**
|
||||
* Highlight a piece of the screen by drawing it in inverted colors.
|
||||
*/
|
||||
virtual void highlight(int x, int y, int width, int height);
|
||||
virtual void unhighlight();
|
||||
|
||||
protected:
|
||||
Common::Rect _bounds;
|
||||
Common::Rect _highlightBounds;
|
||||
bool _highlighted;
|
||||
void drawHighlighted();
|
||||
#ifdef IOS_ULTIMA4
|
||||
friend void U4IOS::updateScreenView();
|
||||
#endif
|
||||
static Image *_screen;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user