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/shared/core/map.h"
24 #include "ultima/shared/early/game.h"
25 #include "ultima/shared/gfx/visual_item.h"
26
27 namespace Ultima {
28 namespace Shared {
29
clear()30 void MapTile::clear() {
31 _tileId = _tileNum = -1;
32 _widgetNum = -1;
33 _widget = nullptr;
34 _itemNum = -1;
35 _isDoor = _isSecretDoor = false;
36 _isLadderUp = _isLadderDown = false;
37 _isWall = _isHallway = _isBeams = false;
38 }
39
40 /*-------------------------------------------------------------------*/
41
clear()42 void Map::MapBase::clear() {
43 _mapId = 0;
44 _data.clear();
45 _widgets.clear();
46 }
47
load(MapId mapId)48 void Map::MapBase::load(MapId mapId) {
49 _mapId = mapId;
50 }
51
synchronize(Common::Serializer & s)52 void Map::MapBase::synchronize(Common::Serializer &s) {
53 _viewportPos.synchronize(s);
54 uint size;
55 int transportIndex = -1;
56 Common::String name;
57
58 if (s.isSaving()) {
59 // Save widgets
60 size = 0;
61 for (uint idx = 0; idx < _widgets.size(); ++idx) {
62 if (_widgets[idx]->getClassName())
63 ++size;
64 if (_playerWidget == _widgets[idx].get())
65 transportIndex = (int)idx;
66 }
67 assert(transportIndex >= 0);
68
69 s.syncAsUint16LE(size);
70 for (uint idx = 0; idx < _widgets.size(); ++idx) {
71 name = _widgets[idx]->getClassName();
72 if (!name.empty()) {
73 s.syncString(name);
74 _widgets[idx]->synchronize(s);
75 }
76 }
77 s.syncAsUint16LE(transportIndex);
78
79 } else {
80 // Load widgets
81 s.syncAsUint16LE(size);
82 _widgets.clear();
83 for (uint idx = 0; idx < size; ++idx) {
84 s.syncString(name);
85
86 MapWidget *w = _map->createWidget(this, name);
87 w->synchronize(s);
88 addWidget(w);
89 }
90
91 s.syncAsUint16LE(transportIndex);
92 _playerWidget = _widgets[transportIndex].get();
93 }
94 }
95
setDimensions(const Point & size)96 void Map::MapBase::setDimensions(const Point &size) {
97 _data.resize(size.y);
98 for (int y = 0; y < size.y; ++y)
99 _data[y]._data.resize(size.x);
100 _size = size;
101 }
102
getDirectionDelta() const103 Point Map::MapBase::getDirectionDelta() const {
104 switch (_playerWidget->_direction) {
105 case DIR_LEFT:
106 return Point(-1, 0);
107 case DIR_RIGHT:
108 return Point(1, 0);
109 case DIR_UP:
110 return Point(0, -1);
111 default:
112 return Point(0, 1);
113 }
114 }
115
getDeltaPosition(const Point & delta)116 Point Map::MapBase::getDeltaPosition(const Point &delta) {
117 return _playerWidget->_position + delta;
118 }
119
resetViewport()120 void Map::MapBase::resetViewport() {
121 // Reset the viewport, so it's position will get recalculated
122 _viewportPos.reset();
123 }
124
getViewportPosition(const Point & viewportSize)125 Point Map::MapBase::getViewportPosition(const Point &viewportSize) {
126 Point &topLeft = _viewportPos._topLeft;
127
128 if (!_viewportPos.isValid() || _viewportPos._size != viewportSize) {
129 // Calculate the new position
130 topLeft.x = _playerWidget->_position.x - (viewportSize.x - 1) / 2;
131 topLeft.y = _playerWidget->_position.y - (viewportSize.y - 1) / 2;
132
133 // Fixed maps, so constrain top left corner so the map fills the viewport.
134 // This will accomodate future renderings with more tiles, or greater tile size
135 topLeft.x = CLIP((int)topLeft.x, 0, (int)(width() - viewportSize.x));
136 topLeft.y = CLIP((int)topLeft.y, 0, (int)(height() - viewportSize.y));
137 }
138
139 return topLeft;
140 }
141
shiftViewport(const Point & delta)142 void Map::MapBase::shiftViewport(const Point &delta) {
143 Point &topLeft = _viewportPos._topLeft;
144 topLeft += delta;
145
146 // Shift the viewport, but constraining the map to fill up the screen
147 topLeft.x = CLIP(topLeft.x, (int16)0, (int16)(width() - _viewportPos._size.x));
148 topLeft.y = CLIP(topLeft.y, (int16)0, (int16)(height() - _viewportPos._size.y));
149 }
150
addWidget(MapWidget * widget)151 void Map::MapBase::addWidget(MapWidget *widget) {
152 _widgets.push_back(MapWidgetPtr(widget));
153 }
154
removeWidget(MapWidget * widget)155 void Map::MapBase::removeWidget(MapWidget *widget) {
156 for (uint idx = 0; idx < _widgets.size(); ++idx) {
157 if (_widgets[idx].get() == widget) {
158 _widgets.remove_at(idx);
159 break;
160 }
161 }
162 }
163
getTileAt(const Point & pt,MapTile * tile)164 void Map::MapBase::getTileAt(const Point &pt, MapTile *tile) {
165 tile->clear();
166
167 // Get the base tile
168 tile->_tileNum = tile->_tileId = _data[pt.y][pt.x];
169
170 // Check for any widget on that map tile
171 for (int idx = (int)_widgets.size() - 1; idx >= 0; --idx) {
172 MapWidget *widget = _widgets[idx].get();
173 if (widget->_position == pt) {
174 tile->_widgetNum = idx;
175 tile->_widget = widget;
176 break;
177 }
178 }
179 }
180
update()181 void Map::MapBase::update() {
182 // Call the update method of each widget, to allow for things like npc movement, etc.
183 for (uint idx = 0; idx < _widgets.size(); ++idx)
184 _widgets[idx].get()->update(true);
185
186 // Call the update method of each widget, to allow for things like npc movement, etc.
187 for (uint idx = 0; idx < _widgets.size(); ++idx)
188 _widgets[idx].get()->update(false);
189 }
190
getPosition() const191 Point Map::MapBase::getPosition() const {
192 return _playerWidget->_position;
193 }
194
setPosition(const Point & pt)195 void Map::MapBase::setPosition(const Point &pt) {
196 _playerWidget->_position = pt;
197 }
198
getDirection() const199 Direction Map::MapBase::getDirection() const {
200 return _playerWidget->_direction;
201 }
202
setDirection(Direction dir)203 void Map::MapBase::setDirection(Direction dir) {
204 _playerWidget->_direction = dir;
205 }
206
207 /*------------------------------------------------------------------------*/
208
synchronize(Common::Serializer & s)209 void MapWidget::synchronize(Common::Serializer &s) {
210 s.syncAsUint16LE(_position.x);
211 s.syncAsSint16LE(_position.y);
212 s.syncAsByte(_direction);
213 s.syncString(_name);
214 }
215
addInfoMsg(const Common::String & text,bool newLine)216 void MapWidget::addInfoMsg(const Common::String &text, bool newLine) {
217 CInfoMsg msg(text, newLine);
218 msg.execute(_game->getView());
219 }
220
canMoveTo(const Point & destPos)221 MapWidget::CanMove MapWidget::canMoveTo(const Point &destPos) {
222 if (destPos.x < 0 || destPos.y < 0 || destPos.x >= (int)_map->width() || destPos.y >= (int)_map->height()) {
223 // If the map is fixed, allow moving beyond it's edges so it can be left
224 if (!_map->isMapWrapped())
225 return YES;
226 }
227
228 // Get the details of the position
229 MapTile destTile;
230 _map->getTileAt(destPos, &destTile);
231
232 // If there's a widget blocking the tile, return false
233 if (destTile._widget && destTile._widget->isBlocking())
234 return NO;
235
236 return UNSET;
237 }
238
moveTo(const Point & destPos,Direction dir)239 void MapWidget::moveTo(const Point &destPos, Direction dir) {
240 // If no direction is specified, we'll need to figure it out relative to the old position
241 if (dir == DIR_NONE) {
242 Point delta = destPos - _position;
243 if (ABS(delta.x) > ABS(delta.y))
244 _direction = delta.x > 0 ? DIR_EAST : DIR_WEST;
245 else if (delta.y != 0)
246 _direction = delta.y > 0 ? DIR_SOUTH : DIR_NORTH;
247 } else {
248 _direction = dir;
249 }
250
251 // Set new location
252 _position = destPos;
253
254 // Handle wrap around if need be on maps that wrap
255 if (_map->isMapWrapped()) {
256 if (_position.x < 0)
257 _position.x += _map->width();
258 else if (_position.x >= (int)_map->width())
259 _position.x -= _map->width();
260 if (_position.y < 0)
261 _position.y += _map->height();
262 else if (_position.y >= (int)_map->height())
263 _position.y -= _map->height();
264 }
265 }
266
267 /*-------------------------------------------------------------------*/
268
clear()269 void Map::clear() {
270 if (_mapArea)
271 _mapArea->clear();
272 _mapArea = nullptr;
273 }
274
load(MapId mapId)275 void Map::load(MapId mapId) {
276 _mapArea = nullptr;
277 }
278
synchronize(Common::Serializer & s)279 void Map::synchronize(Common::Serializer &s) {
280 int mapId;
281
282 if (s.isSaving()) {
283 // Saving
284 mapId = _mapArea->getMapId();
285 s.syncAsUint16LE(mapId);
286 } else {
287 // Loading
288 s.syncAsUint16LE(mapId);
289 load(mapId);
290 }
291
292 _mapArea->synchronize(s);
293 }
294
295 } // End of namespace Shared
296 } // End of namespace Ultima
297