1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "ultima/ultima4/core/config.h"
24 #include "ultima/ultima4/gfx/image.h"
25 #include "ultima/ultima4/gfx/imagemgr.h"
26 #include "ultima/ultima4/core/settings.h"
27 #include "ultima/ultima4/core/utils.h"
28 #include "ultima/ultima4/gfx/screen.h"
29 #include "ultima/ultima4/map/tile.h"
30 #include "ultima/ultima4/map/tileanim.h"
31 #include "ultima/ultima4/map/tileset.h"
32 #include "ultima/ultima4/views/tileview.h"
33 #include "ultima/ultima4/ultima4.h"
34 
35 namespace Ultima {
36 namespace Ultima4 {
37 
TileView(int x,int y,int columns,int rows)38 TileView::TileView(int x, int y, int columns, int rows) :
39 		View(x, y, columns * TILE_WIDTH, rows * TILE_HEIGHT) {
40 	_columns = columns;
41 	_rows = rows;
42 	_tileWidth = TILE_WIDTH;
43 	_tileHeight = TILE_HEIGHT;
44 	_tileSet = g_tileSets->get("base");
45 	_animated = Image::create(SCALED(_tileWidth), SCALED(_tileHeight), false, Image::HARDWARE);
46 	_dest = nullptr;
47 }
48 
TileView(int x,int y,int columns,int rows,const Common::String & tileset)49 TileView::TileView(int x, int y, int columns, int rows, const Common::String &tileset) :
50 		View(x, y, columns * TILE_WIDTH, rows * TILE_HEIGHT) {
51 	_columns = columns;
52 	_rows = rows;
53 	_tileWidth = TILE_WIDTH;
54 	_tileHeight = TILE_HEIGHT;
55 	_tileSet = g_tileSets->get(tileset);
56 	_animated = Image::create(SCALED(_tileWidth), SCALED(_tileHeight), false, Image::HARDWARE);
57 	_dest = nullptr;
58 }
59 
~TileView()60 TileView::~TileView() {
61 	delete _animated;
62 }
63 
reinit()64 void TileView::reinit() {
65 	View::reinit();
66 	_tileSet = g_tileSets->get("base");
67 
68 	// Scratchpad needs to be re-inited if we rescale...
69 	if (_animated) {
70 		delete _animated;
71 		_animated = nullptr;
72 	}
73 	_animated = Image::create(SCALED(_tileWidth), SCALED(_tileHeight), false, Image::HARDWARE);
74 }
75 
loadTile(MapTile & mapTile)76 void TileView::loadTile(MapTile &mapTile) {
77 	// This attempts to preload tiles in advance
78 	Tile *tile = _tileSet->get(mapTile._id);
79 	if (tile) {
80 		tile->getImage();
81 	}
82 	// But may fail if the tiles don't exist directly in the expected imagesets
83 }
84 
drawTile(MapTile & mapTile,bool focus,int x,int y)85 void TileView::drawTile(MapTile &mapTile, bool focus, int x, int y) {
86 	Tile *tile = _tileSet->get(mapTile._id);
87 	Image *image = tile->getImage();
88 
89 	assertMsg(x < _columns, "x value of %d out of range", x);
90 	assertMsg(y < _rows, "y value of %d out of range", y);
91 
92 	// Blank scratch pad
93 	_animated->fillRect(0, 0, SCALED(_tileWidth), SCALED(_tileHeight), 0, 0, 0, 255);
94 
95 	// Draw blackness on the tile.
96 	_animated->drawSubRectOn(_dest, SCALED(x * _tileWidth + _bounds.left),
97 	    SCALED(y * _tileHeight + _bounds.top), 0, 0,
98 	    SCALED(_tileWidth), SCALED(_tileHeight));
99 
100 	// Draw the tile to the screen
101 	if (tile->getAnim()) {
102 		// First, create our animated version of the tile
103 #ifdef IOS_ULTIMA4
104 		animated->clearImageContents();
105 #endif
106 		tile->getAnim()->draw(_animated, tile, mapTile, DIR_NONE);
107 
108 		// Then draw it to the screen
109 		_animated->drawSubRectOn(_dest, SCALED(x * _tileWidth + _bounds.left),
110 			SCALED(y * _tileHeight + _bounds.top), 0, 0,
111 			SCALED(_tileWidth), SCALED(_tileHeight));
112 	} else {
113 		image->drawSubRectOn(_dest, SCALED(x * _tileWidth + _bounds.left),
114 			SCALED(y * _tileHeight + _bounds.top),
115 			0, SCALED(_tileHeight * mapTile._frame),
116 			SCALED(_tileWidth), SCALED(_tileHeight));
117 	}
118 
119 	// Draw the focus around the tile if it has the focus
120 	if (focus)
121 		drawFocus(x, y);
122 }
123 
drawTile(Std::vector<MapTile> & tiles,bool focus,int x,int y)124 void TileView::drawTile(Std::vector<MapTile> &tiles, bool focus, int x, int y) {
125 	assertMsg(x < _columns, "x value of %d out of range", x);
126 	assertMsg(y < _rows, "y value of %d out of range", y);
127 
128 	// Clear tile contents
129 	_animated->fillRect(0, 0, SCALED(_tileWidth), SCALED(_tileHeight), 0, 0, 0, 255);
130 	_animated->drawSubRectOn(_dest,
131 		SCALED(x * _tileWidth + _bounds.left), SCALED(y * _tileHeight + _bounds.top),
132 		0, 0,
133 		SCALED(_tileWidth), SCALED(_tileHeight)
134 	);
135 
136 	// Iterate through rendering each of the needed tiles
137  	for (Std::vector<MapTile>::reverse_iterator t = tiles.rbegin(); t != tiles.rend(); ++t) {
138 		MapTile &frontTile = *t;
139 		Tile *frontTileType = _tileSet->get(frontTile._id);
140 
141 		if (!frontTileType) {
142 			// TODO: This leads to an error. It happens after graphics mode changes.
143 			return;
144 		}
145 
146 		// Get the image for the tile
147 		Image *image = frontTileType->getImage();
148 
149 		// Draw the tile to the screen
150 		if (frontTileType->getAnim()) {
151 			// First, create our animated version of the tile
152 			frontTileType->getAnim()->draw(_animated, frontTileType, frontTile, DIR_NONE);
153 		} else {
154 			if (!image)
155 				// FIXME: This is a problem, error message it.
156 				return;
157 			image->drawSubRectOn(_animated, 0, 0,
158 				0, SCALED(_tileHeight * frontTile._frame),
159 				SCALED(_tileWidth),  SCALED(_tileHeight)
160 			);
161 		}
162 
163 		// Then draw it to the screen
164 		_animated->drawSubRectOn(_dest, SCALED(x * _tileWidth + _bounds.left),
165 			SCALED(y * _tileHeight + _bounds.top), 0, 0,
166 				SCALED(_tileWidth), SCALED(_tileHeight)
167 		);
168 	}
169 
170 	// Draw the focus around the tile if it has the focus
171 	if (focus)
172 		drawFocus(x, y);
173 }
174 
drawFocus(int x,int y)175 void TileView::drawFocus(int x, int y) {
176 	assertMsg(x < _columns, "x value of %d out of range", x);
177 	assertMsg(y < _rows, "y value of %d out of range", y);
178 
179 	// Draw the focus rectangle around the tile
180 	if ((g_screen->_currentCycle * 4 / SCR_CYCLE_PER_SECOND) % 2) {
181 		// left edge
182 		_screen->fillRect(SCALED(x * _tileWidth + _bounds.left),
183 			SCALED(y * _tileHeight + _bounds.top),
184 			SCALED(2), SCALED(_tileHeight), 0xff, 0xff, 0xff);
185 
186 		// top edge
187 		_screen->fillRect(SCALED(x * _tileWidth + _bounds.left),
188 			SCALED(y * _tileHeight + _bounds.top),
189 			SCALED(_tileWidth), SCALED(2),
190 			0xff, 0xff, 0xff);
191 
192 		// Right edge
193 		_screen->fillRect(SCALED((x + 1) * _tileWidth + _bounds.left - 2),
194 		    SCALED(y * _tileHeight + _bounds.top),
195 			SCALED(2), SCALED(_tileHeight),
196 		    0xff, 0xff, 0xff);
197 
198 		// Bottom edge
199 		_screen->fillRect(SCALED(x * _tileWidth + _bounds.left),
200 			SCALED((y + 1) * _tileHeight + _bounds.top - 2),
201 			SCALED(_tileWidth), SCALED(2),
202 			0xff, 0xff, 0xff);
203 	}
204 }
205 
setTileset(Tileset * tileset)206 void TileView::setTileset(Tileset *tileset) {
207 	this->_tileSet = tileset;
208 }
209 
210 } // End of namespace Ultima4
211 } // End of namespace Ultima
212