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 "common/serializer.h"
24 #include "xeen/map.h"
25 #include "xeen/interface.h"
26 #include "xeen/resources.h"
27 #include "xeen/saves.h"
28 #include "xeen/screen.h"
29 #include "xeen/xeen.h"
30 #include "xeen/dialogs/please_wait.h"
31
32 namespace Xeen {
33
34 const int MAP_GRID_PRIOR_INDEX[] = { 0, 0, 0, 0, 1, 2, 3, 4, 0 };
35
36 const int MAP_GRID_PRIOR_DIRECTION[] = { 0, 1, 2, 3, 1, 2, 3, 0, 0 };
37
38 const int MAP_GRID_PRIOR_INDEX2[] = { 0, 0, 0, 0, 2, 3, 4, 1, 0 };
39
40 const int MAP_GRID_PRIOR_DIRECTION2[] = { 0, 1, 2, 3, 0, 1, 2, 3, 0 };
41
MonsterStruct()42 MonsterStruct::MonsterStruct() {
43 _experience = 0;
44 _hp = 0;
45 _armorClass = 0;
46 _speed = 0;
47 _numberOfAttacks = 0;
48 _hatesClass = CLASS_KNIGHT;
49 _strikes = 0;
50 _dmgPerStrike = 0;
51 _attackType = DT_PHYSICAL;
52 _specialAttack = SA_NONE;
53 _hitChance = 0;
54 _rangeAttack = 0;
55 _monsterType = MONSTER_MONSTERS;
56 _fireResistence = 0;
57 _electricityResistence = 0;
58 _coldResistence = 0;
59 _poisonResistence = 0;
60 _energyResistence = 0;
61 _magicResistence = 0;
62 _phsyicalResistence = 0;
63 _field29 = 0;
64 _gold = 0;
65 _gems = 0;
66 _itemDrop = 0;
67 _flying = 0;
68 _imageNumber = 0;
69 _loopAnimation = 0;
70 _animationEffect = 0;
71 _fx = 0;
72 }
73
MonsterStruct(Common::String name,int experience,int hp,int armorClass,int speed,int numberOfAttacks,CharacterClass hatesClass,int strikes,int dmgPerStrike,DamageType attackType,SpecialAttack specialAttack,int hitChance,int rangeAttack,MonsterType monsterType,int fireResistence,int electricityResistence,int coldResistence,int poisonResistence,int energyResistence,int magicResistence,int phsyicalResistence,int field29,int gold,int gems,int itemDrop,bool flying,int imageNumber,int loopAnimation,int animationEffect,int fx,Common::String attackVoc)74 MonsterStruct::MonsterStruct(Common::String name, int experience, int hp, int armorClass,
75 int speed, int numberOfAttacks, CharacterClass hatesClass, int strikes,
76 int dmgPerStrike, DamageType attackType, SpecialAttack specialAttack,
77 int hitChance, int rangeAttack, MonsterType monsterType,
78 int fireResistence, int electricityResistence, int coldResistence,
79 int poisonResistence, int energyResistence, int magicResistence,
80 int phsyicalResistence, int field29, int gold, int gems, int itemDrop,
81 bool flying, int imageNumber, int loopAnimation, int animationEffect,
82 int fx, Common::String attackVoc):
83 _name(name), _experience(experience), _hp(hp), _armorClass(armorClass),
84 _speed(speed), _numberOfAttacks(numberOfAttacks), _hatesClass(hatesClass),
85 _strikes(strikes), _dmgPerStrike(dmgPerStrike), _attackType(attackType),
86 _specialAttack(specialAttack), _hitChance(hitChance), _rangeAttack(rangeAttack),
87 _monsterType(monsterType), _fireResistence(fireResistence),
88 _electricityResistence(electricityResistence), _coldResistence(coldResistence),
89 _poisonResistence(poisonResistence), _energyResistence(energyResistence),
90 _magicResistence(magicResistence), _phsyicalResistence(phsyicalResistence),
91 _field29(field29), _gold(gold), _gems(gems), _itemDrop(itemDrop),
92 _flying(flying), _imageNumber(imageNumber), _loopAnimation(loopAnimation),
93 _animationEffect(animationEffect), _fx(fx), _attackVoc(attackVoc) {
94 }
95
synchronize(Common::SeekableReadStream & s)96 void MonsterStruct::synchronize(Common::SeekableReadStream &s) {
97 char name[16];
98 Common::fill(name, name + 16, '\0');
99 s.read(name, 16);
100 name[15] = '\0';
101 _name = Common::String(name);
102
103 _experience = s.readUint32LE();
104 _hp = s.readUint16LE();
105 _armorClass = s.readByte();
106 _speed = s.readByte();
107 _numberOfAttacks = s.readByte();
108 _hatesClass = (CharacterClass)s.readByte();
109 _strikes = s.readUint16LE();
110 _dmgPerStrike = s.readByte();
111 _attackType = (DamageType)s.readByte();
112 _specialAttack = (SpecialAttack)s.readByte();
113 _hitChance = s.readByte();
114 _rangeAttack = s.readByte();
115 _monsterType = (MonsterType)s.readByte();
116 _fireResistence = s.readByte();
117 _electricityResistence = s.readByte();
118 _coldResistence = s.readByte();
119 _poisonResistence = s.readByte();
120 _energyResistence = s.readByte();
121 _magicResistence = s.readByte();
122 _phsyicalResistence = s.readByte();
123 _field29 = s.readByte();
124 _gold = s.readUint16LE();
125 _gems = s.readByte();
126 _itemDrop = s.readByte();
127 _flying = s.readByte() != 0;
128 _imageNumber = s.readByte();
129 _loopAnimation = s.readByte();
130 _animationEffect = s.readByte();
131 _fx = s.readByte();
132
133 char attackVoc[10];
134 Common::fill(attackVoc, attackVoc + 10, '\0');
135 s.read(attackVoc, 9);
136 attackVoc[9] = '\0';
137 _attackVoc = Common::String(attackVoc);
138 }
139
MonsterData()140 MonsterData::MonsterData() {
141 }
142
load(const Common::String & name)143 void MonsterData::load(const Common::String &name) {
144 File f(name);
145 synchronize(f);
146 }
147
synchronize(Common::SeekableReadStream & s)148 void MonsterData::synchronize(Common::SeekableReadStream &s) {
149 clear();
150
151 MonsterStruct spr;
152 while (!s.eos()) {
153 spr.synchronize(s);
154 push_back(spr);
155 }
156 }
157
158 /*------------------------------------------------------------------------*/
159
SurroundingMazes()160 SurroundingMazes::SurroundingMazes() {
161 clear();
162 }
163
clear()164 void SurroundingMazes::clear() {
165 _north = 0;
166 _east = 0;
167 _south = 0;
168 _west = 0;
169 }
170
synchronize(XeenSerializer & s)171 void SurroundingMazes::synchronize(XeenSerializer &s) {
172 s.syncAsUint16LE(_north);
173 s.syncAsUint16LE(_east);
174 s.syncAsUint16LE(_south);
175 s.syncAsUint16LE(_west);
176 }
177
operator [](int idx)178 int &SurroundingMazes::operator[](int idx) {
179 switch (idx) {
180 case DIR_NORTH:
181 return _north;
182 case DIR_EAST:
183 return _east;
184 case DIR_SOUTH:
185 return _south;
186 default:
187 return _west;
188 }
189 }
190
191 /*------------------------------------------------------------------------*/
192
MazeDifficulties()193 MazeDifficulties::MazeDifficulties() {
194 _unlockDoor = 0;
195 _unlockBox = 0;
196 _bashDoor = 0;
197 _bashGrate = 0;
198 _bashWall = 0;
199 _wallNoPass = -1;
200 _surfaceNoPass = -1;
201 _chance2Run = -1;
202 }
203
synchronize(XeenSerializer & s)204 void MazeDifficulties::synchronize(XeenSerializer &s) {
205 s.syncAsByte(_wallNoPass);
206 s.syncAsByte(_surfaceNoPass);
207 s.syncAsByte(_unlockDoor);
208 s.syncAsByte(_unlockBox);
209 s.syncAsByte(_bashDoor);
210 s.syncAsSint8(_bashGrate);
211 s.syncAsSint8(_bashWall);
212 s.syncAsSint8(_chance2Run);
213 }
214
215 /*------------------------------------------------------------------------*/
216
MazeData()217 MazeData::MazeData() {
218 clear();
219 }
220
clear()221 void MazeData::clear() {
222 for (int y = 0; y < MAP_HEIGHT; ++y) {
223 for (int x = 0; x < MAP_WIDTH; ++x)
224 _wallData[y][x]._data = 0;
225 Common::fill(&_seenTiles[y][0], &_seenTiles[y][MAP_WIDTH], false);
226 Common::fill(&_steppedOnTiles[y][0], &_steppedOnTiles[y][MAP_WIDTH], false);
227 _wallTypes[y] = 0;
228 _surfaceTypes[y] = 0;
229 }
230 _mazeNumber = 0;
231 _surroundingMazes.clear();
232 _mazeFlags = _mazeFlags2 = 0;
233 _floorType = 0;
234 _trapDamage = 0;
235 _wallKind = 0;
236 _tavernTips = 0;
237 _mazeId = 0;
238 }
239
synchronize(XeenSerializer & s)240 void MazeData::synchronize(XeenSerializer &s) {
241 byte b;
242
243 for (int y = 0; y < MAP_HEIGHT; ++y) {
244 for (int x = 0; x < MAP_WIDTH; ++x)
245 s.syncAsUint16LE(_wallData[y][x]._data);
246 }
247 for (int y = 0; y < MAP_HEIGHT; ++y) {
248 for (int x = 0; x < MAP_WIDTH; ++x) {
249 if (s.isLoading()) {
250 s.syncAsByte(b);
251 _cells[y][x]._surfaceId = b & 7;
252 _cells[y][x]._flags = b & 0xF8;
253 } else {
254 b = (_cells[y][x]._surfaceId & 7) | (_cells[y][x]._flags & 0xf8);
255 s.syncAsByte(b);
256 }
257 }
258 }
259
260 s.syncAsUint16LE(_mazeNumber);
261 _surroundingMazes.synchronize(s);
262 s.syncAsUint16LE(_mazeFlags);
263 s.syncAsUint16LE(_mazeFlags2);
264
265 for (int i = 0; i < 16; ++i)
266 s.syncAsByte(_wallTypes[i]);
267 for (int i = 0; i < 16; ++i)
268 s.syncAsByte(_surfaceTypes[i]);
269
270 s.syncAsByte(_floorType);
271 s.syncAsByte(_runPosition.x);
272 _difficulties.synchronize(s);
273 s.syncAsByte(_runPosition.y);
274 s.syncAsByte(_trapDamage);
275 s.syncAsByte(_wallKind);
276 s.syncAsByte(_tavernTips);
277
278 for (int y = 0; y < MAP_HEIGHT; ++y)
279 File::syncBitFlags(s, &_seenTiles[y][0], &_seenTiles[y][MAP_WIDTH]);
280 for (int y = 0; y < MAP_HEIGHT; ++y)
281 File::syncBitFlags(s, &_steppedOnTiles[y][0], &_steppedOnTiles[y][MAP_WIDTH]);
282 }
283
setAllTilesStepped()284 void MazeData::setAllTilesStepped() {
285 for (int y = 0; y < MAP_HEIGHT; ++y)
286 Common::fill(&_steppedOnTiles[y][0], &_steppedOnTiles[y][MAP_WIDTH], true);
287 }
288
clearCellSurfaces()289 void MazeData::clearCellSurfaces() {
290 for (int y = 0; y < MAP_HEIGHT; ++y) {
291 for (int x = 0; x < MAP_WIDTH; ++x)
292 _cells[y][x]._surfaceId = 0;
293 }
294 }
295
296 /*------------------------------------------------------------------------*/
297
MobStruct()298 MobStruct::MobStruct() {
299 _id = 0;
300 _direction = DIR_NORTH;
301 }
302
synchronize(XeenSerializer & s)303 bool MobStruct::synchronize(XeenSerializer &s) {
304 s.syncAsSint8(_pos.x);
305 s.syncAsSint8(_pos.y);
306 s.syncAsByte(_id);
307 s.syncAsByte(_direction);
308
309 return _id != 0xff || _pos.x != -1 || _pos.y != -1;
310 }
311
endOfList()312 void MobStruct::endOfList() {
313 _pos.x = _pos.y = -1;
314 _id = 0xff;
315 _direction = (Direction)-1;
316 }
317
318 /*------------------------------------------------------------------------*/
319
MazeObject()320 MazeObject::MazeObject() {
321 _id = 0;
322 _frame = 0;
323 _spriteId = 0;
324 _direction = DIR_NORTH;
325 _flipped = false;
326 _sprites = nullptr;
327 }
328
329 /*------------------------------------------------------------------------*/
330
MazeMonster()331 MazeMonster::MazeMonster() {
332 _frame = 0;
333 _id = 0;
334 _spriteId = 0;
335 _isAttacking = false;
336 _damageType = DT_PHYSICAL;
337 _field9 = 0;
338 _postAttackDelay = 0;
339 _hp = 0;
340 _effect1 = _effect2 = 0;
341 _effect3 = 0;
342 _sprites = nullptr;
343 _attackSprites = nullptr;
344 _monsterData = nullptr;
345 }
346
getTextColor() const347 int MazeMonster::getTextColor() const {
348 if (_hp == _monsterData->_hp)
349 return 15;
350 else if (_hp >= (_monsterData->_hp / 2))
351 return 9;
352 else
353 return 32;
354 }
355
356 /*------------------------------------------------------------------------*/
357
MazeWallItem()358 MazeWallItem::MazeWallItem() {
359 _id = 0;
360 _frame = 0;
361 _spriteId = 0;
362 _direction = DIR_NORTH;
363 _sprites = nullptr;
364 }
365
366 /*------------------------------------------------------------------------*/
367
MonsterObjectData(XeenEngine * vm)368 MonsterObjectData::MonsterObjectData(XeenEngine *vm): _vm(vm) {
369 }
370
synchronize(XeenSerializer & s,MonsterData & monsterData)371 void MonsterObjectData::synchronize(XeenSerializer &s, MonsterData &monsterData) {
372 Common::Array<MobStruct> mobStructs;
373 MobStruct mobStruct;
374 byte b;
375
376 if (s.isLoading()) {
377 _objectSprites.clear();
378 _monsterSprites.clear();
379 _monsterAttackSprites.clear();
380 _wallItemSprites.clear();
381 _objects.clear();
382 _monsters.clear();
383 _wallItems.clear();
384 }
385
386 byte objSprites[16];
387 int maxSprite = 0;
388 for (int i = 0; i < 16; ++i) {
389 objSprites[i] = (i >= (int)_objectSprites.size()) ? 0xff : _objectSprites[i]._spriteId;
390 s.syncAsByte(objSprites[i]);
391 if (s.isLoading() && objSprites[i] != 0xff)
392 maxSprite = i;
393 }
394
395 if (s.isLoading()) {
396 for (int i = 0; i <= maxSprite; ++i) {
397 if (objSprites[i] == 0xff)
398 _objectSprites.push_back(SpriteResourceEntry());
399 else
400 _objectSprites.push_back(SpriteResourceEntry(objSprites[i]));
401 }
402 }
403
404 for (uint i = 0; i < 16; ++i) {
405 b = (i >= _monsterSprites.size()) ? 0xff : _monsterSprites[i]._spriteId;
406 s.syncAsByte(b);
407 if (s.isLoading() && b != 0xff)
408 _monsterSprites.push_back(SpriteResourceEntry(b));
409 }
410 for (uint i = 0; i < 16; ++i) {
411 b = (i >= _wallItemSprites.size()) ? 0xff : _wallItemSprites[i]._spriteId;
412 s.syncAsByte(b);
413 if (s.isLoading() && b != 0xff)
414 _wallItemSprites.push_back(SpriteResourceEntry(b));
415 }
416
417 if (s.isSaving()) {
418 // Save objects
419 if (_objects.empty()) {
420 mobStruct.endOfList();
421 mobStruct.synchronize(s);
422 } else {
423 for (uint i = 0; i < _objects.size(); ++i) {
424 mobStruct._pos = _objects[i]._position;
425 mobStruct._id = _objects[i]._id;
426 mobStruct._direction = _objects[i]._direction;
427 mobStruct.synchronize(s);
428 }
429 }
430 mobStruct.endOfList();
431 mobStruct.synchronize(s);
432
433 // Save monsters
434 if (_monsters.empty()) {
435 mobStruct.endOfList();
436 mobStruct.synchronize(s);
437 } else {
438 for (uint i = 0; i < _monsters.size(); ++i) {
439 mobStruct._pos = _monsters[i]._position;
440 mobStruct._id = _monsters[i]._id;
441 mobStruct._direction = DIR_NORTH;
442 mobStruct.synchronize(s);
443 }
444 }
445 mobStruct.endOfList();
446 mobStruct.synchronize(s);
447
448 // Save wall items
449 for (uint i = 0; i < _wallItems.size(); ++i) {
450 mobStruct._pos = _wallItems[i]._position;
451 mobStruct._id = _wallItems[i]._id;
452 mobStruct._direction = _wallItems[i]._direction;
453 mobStruct.synchronize(s);
454 }
455 mobStruct.endOfList();
456 mobStruct.synchronize(s);
457
458 } else {
459 // Load monster/obbject data and merge together with sprite Ids
460 // Load objects
461 mobStruct.synchronize(s);
462 if (mobStruct._id == -1 && mobStruct._pos.x == -1)
463 // Empty array has a blank entry
464 mobStruct.synchronize(s);
465
466 do {
467 MazeObject obj;
468 obj._position = mobStruct._pos;
469 obj._id = mobStruct._id;
470 obj._direction = mobStruct._direction;
471 obj._frame = 100;
472
473 if (obj._id < (int)_objectSprites.size()) {
474 obj._spriteId = _objectSprites[obj._id]._spriteId;
475 obj._sprites = &_objectSprites[obj._id]._sprites;
476 }
477
478 _objects.push_back(obj);
479 mobStruct.synchronize(s);
480 } while (mobStruct._id != 255 || mobStruct._pos.x != -1);
481
482 // Load monsters
483 mobStruct.synchronize(s);
484 if (mobStruct._id == -1 && mobStruct._pos.x == -1)
485 // Empty array has a blank entry
486 mobStruct.synchronize(s);
487
488 while (mobStruct._id != 255 || mobStruct._pos.x != -1) {
489 MazeMonster mon;
490 mon._position = mobStruct._pos;
491 mon._id = mobStruct._id;
492 mon._frame = _vm->getRandomNumber(7);
493
494 if (mon._id < (int)_monsterSprites.size()) {
495 mon._spriteId = _monsterSprites[mon._id]._spriteId;
496 mon._sprites = &_monsterSprites[mon._id]._sprites;
497 mon._attackSprites = &_monsterSprites[mon._id]._attackSprites;
498 mon._monsterData = &monsterData[mon._spriteId];
499
500 MonsterStruct &md = *mon._monsterData;
501 mon._hp = md._hp;
502 mon._effect1 = mon._effect2 = md._animationEffect;
503 if (md._animationEffect)
504 mon._effect3 = _vm->getRandomNumber(7);
505
506 _monsters.push_back(mon);
507 } else {
508 assert(!mon._id);
509 }
510
511 mobStruct.synchronize(s);
512 }
513
514 // Load wall items. Unlike the previous two arrays, this has no dummy entry for an empty array
515 mobStruct.synchronize(s);
516 while (mobStruct._id != 255 || mobStruct._pos.x != -1) {
517 if (mobStruct._id < (int)_wallItemSprites.size()) {
518 MazeWallItem wi;
519 wi._position = mobStruct._pos;
520 wi._id = mobStruct._id;
521 wi._direction = mobStruct._direction;
522 wi._spriteId = _wallItemSprites[wi._id]._spriteId;
523 wi._sprites = &_wallItemSprites[wi._id]._sprites;
524
525 _wallItems.push_back(wi);
526 }
527
528 mobStruct.synchronize(s);
529 }
530 }
531 }
532
clearMonsterSprites()533 void MonsterObjectData::clearMonsterSprites() {
534 _monsterSprites.clear();
535 _monsterAttackSprites.clear();
536 }
537
addMonsterSprites(MazeMonster & monster)538 void MonsterObjectData::addMonsterSprites(MazeMonster &monster) {
539 Map &map = *g_vm->_map;
540 monster._monsterData = &map._monsterData[monster._spriteId];
541 int imgNumber = monster._monsterData->_imageNumber;
542 uint idx;
543
544 // Find the sprites for the monster, loading them in if necessary
545 for (idx = 0; idx < _monsterSprites.size(); ++idx) {
546 if (_monsterSprites[idx]._spriteId == monster._spriteId) {
547 monster._sprites = &_monsterSprites[idx]._sprites;
548 break;
549 }
550 }
551 if (idx == _monsterSprites.size()) {
552 _monsterSprites.push_back(SpriteResourceEntry(monster._spriteId));
553 _monsterSprites.back()._sprites.load(Common::String::format("%03u.mon", imgNumber));
554 monster._sprites = &_monsterSprites.back()._sprites;
555 }
556
557 // Find the attack sprites for the monster, loading them in if necessary
558 for (idx = 0; idx < _monsterAttackSprites.size(); ++idx) {
559 if (_monsterAttackSprites[idx]._spriteId == monster._spriteId) {
560 monster._attackSprites = &_monsterAttackSprites[idx]._sprites;
561 break;
562 }
563 }
564 if (idx == _monsterAttackSprites.size()) {
565 _monsterAttackSprites.push_back(SpriteResourceEntry(monster._spriteId));
566 _monsterAttackSprites.back()._sprites.load(Common::String::format("%03u.att", imgNumber));
567 monster._attackSprites = &_monsterAttackSprites.back()._sprites;
568 }
569 }
570
571 /*------------------------------------------------------------------------*/
572
HeadData()573 HeadData::HeadData() {
574 for (int y = 0; y < MAP_HEIGHT; ++y) {
575 for (int x = 0; x < MAP_WIDTH; ++x) {
576 _data[y][x]._left = _data[y][x]._right = 0;
577 }
578 }
579 }
580
synchronize(Common::SeekableReadStream & s)581 void HeadData::synchronize(Common::SeekableReadStream &s) {
582 for (int y = 0; y < MAP_HEIGHT; ++y) {
583 for (int x = 0; x < MAP_WIDTH; ++x) {
584 _data[y][x]._left = s.readByte();
585 _data[y][x]._right = s.readByte();
586 }
587 }
588 }
589
590 /*------------------------------------------------------------------------*/
591
synchronize(Common::SeekableReadStream & s)592 void AnimationEntry::synchronize(Common::SeekableReadStream &s) {
593 for (int i = 0; i < 4; ++i)
594 _frame1._frames[i] = s.readByte();
595 for (int i = 0; i < 4; ++i)
596 _flipped._flags[i] = s.readByte() != 0;
597 for (int i = 0; i < 4; ++i)
598 _frame2._frames[i] = s.readByte();
599 }
600
synchronize(Common::SeekableReadStream & s)601 void AnimationInfo::synchronize(Common::SeekableReadStream &s) {
602 AnimationEntry entry;
603
604 clear();
605 while (s.pos() < s.size()) {
606 entry.synchronize(s);
607 push_back(entry);
608 }
609 }
610
load(const Common::String & name)611 void AnimationInfo::load(const Common::String &name) {
612 File f(name);
613 synchronize(f);
614 f.close();
615 }
616
617 /*------------------------------------------------------------------------*/
618
Map(XeenEngine * vm)619 Map::Map(XeenEngine *vm) : _vm(vm), _mobData(vm) {
620 _loadCcNum = 0;
621 _sideTownPortal = 0;
622 _sideObjects = 0;
623 _sideMonsters = 0;
624 _sidePictures = 0;
625 _isOutdoors = false;
626 _mazeDataIndex = 0;
627 _currentSteppedOn = false;
628 _currentSurfaceId = 0;
629 _currentWall = 0;
630 _currentTile = 0;
631 _currentGrateUnlocked = false;
632 _currentCantRest = false;
633 _currentIsDrain = false;
634 _currentIsEvent = false;
635 _currentSky = 0;
636 _currentMonsterFlags = 0;
637 }
638
load(int mapId)639 void Map::load(int mapId) {
640 EventsManager &events = *g_vm->_events;
641 FileManager &files = *g_vm->_files;
642 Interface &intf = *g_vm->_interface;
643 Party &party = *g_vm->_party;
644 Patcher &patcher = *g_vm->_patcher;
645 Sound &sound = *g_vm->_sound;
646 IndoorDrawList &indoorList = intf._indoorList;
647 OutdoorDrawList &outdoorList = intf._outdoorList;
648
649 PleaseWait waitMsg(intf._falling);
650 waitMsg.show();
651
652 intf._objNumber = -1;
653 party._stepped = true;
654 party._mazeId = mapId;
655 saveMaze();
656 events.clearEvents();
657
658 _sideObjects = 1;
659 _sideMonsters = 1;
660 _sidePictures = 1;
661 if (mapId >= 113 && mapId <= 127) {
662 _sideTownPortal = 0;
663 } else {
664 _sideTownPortal = _loadCcNum;
665 }
666
667 if (_vm->getGameID() == GType_Swords || _vm->getGameID() == GType_DarkSide) {
668 _animationInfo.load("dark.dat");
669 _monsterData.load((_vm->getGameID() == GType_Swords) ? "monsters.swd" : "dark.mon");
670 _wallPicSprites.load("darkpic.dat");
671 } else if (_vm->getGameID() == GType_Clouds) {
672 _animationInfo.load("animinfo.cld");
673 _monsterData.load("monsters.cld");
674 _wallPicSprites.load("wallpics.cld");
675 } else if (_vm->getGameID() == GType_WorldOfXeen) {
676 files.setGameCc(1);
677
678 if (!_loadCcNum) {
679 _animationInfo.load("clouds.dat");
680 _monsterData.load("xeen.mon");
681 _wallPicSprites.load("xeenpic.dat");
682 _sidePictures = 0;
683 _sideMonsters = 0;
684 _sideObjects = 0;
685 } else {
686 switch (mapId) {
687 case 113:
688 case 114:
689 case 115:
690 case 116:
691 case 128:
692 _animationInfo.load("clouds.dat");
693 _monsterData.load("dark.mon");
694 _wallPicSprites.load("darkpic.dat");
695 _sideObjects = 0;
696 break;
697 case 117:
698 case 118:
699 case 119:
700 case 120:
701 case 124:
702 _animationInfo.load("clouds.dat");
703 _monsterData.load("xeen.mon");
704 _wallPicSprites.load("darkpic.dat");
705 _sideObjects = 0;
706 _sideMonsters = 0;
707 break;
708 case 125:
709 case 126:
710 case 127:
711 _animationInfo.load("clouds.dat");
712 _monsterData.load("dark.mon");
713 _wallPicSprites.load("xeenpic.dat");
714 _sideObjects = 0;
715 _sidePictures = 0;
716 break;
717 default:
718 _animationInfo.load("dark.dat");
719 _monsterData.load("dark.mon");
720 _wallPicSprites.load("darkpic.dat");
721 break;
722 }
723 }
724
725 files.setGameCc(_loadCcNum);
726 }
727
728 // Load any events for the new map
729 loadEvents(mapId, _loadCcNum);
730
731 // Iterate through loading the given maze as well as the two successive
732 // mazes in each of the four cardinal directions
733 int ccNum = files._ccNum;
734 MazeData *mazeDataP = &_mazeData[0];
735 bool mapDataLoaded = false;
736
737 for (int idx = 0; idx < 9; ++idx, ++mazeDataP) {
738 mazeDataP->_mazeId = mapId;
739
740 if (mapId == 0) {
741 mazeDataP->clear();
742 } else {
743 // Load in the maze's data file
744 Common::String datName = Common::String::format("maze%c%03d.dat",
745 (mapId >= 100) ? 'x' : '0', mapId);
746 File datFile(datName);
747 XeenSerializer datSer(&datFile, nullptr);
748 mazeDataP->synchronize(datSer);
749 datFile.close();
750
751 if (ccNum && mapId == 50)
752 mazeDataP->setAllTilesStepped();
753 if (!ccNum && party._gameFlags[0][25] &&
754 (mapId == 42 || mapId == 43 || mapId == 4)) {
755 mazeDataP->clearCellSurfaces();
756 }
757
758 _isOutdoors = (mazeDataP->_mazeFlags2 & FLAG_IS_OUTDOORS) != 0;
759
760 Common::String mobName = Common::String::format("maze%c%03d.mob", (mapId >= 100) ? 'x' : '0', mapId);
761
762 if (!mapDataLoaded) {
763 // Called once for the main map being loaded
764 mapDataLoaded = true;
765 _mazeName = getMazeName(mapId, ccNum);
766
767 // Load the monster/object data
768 File mobFile(mobName);
769 XeenSerializer sMob(&mobFile, nullptr);
770 _mobData.synchronize(sMob, _monsterData);
771 mobFile.close();
772
773 Common::String headName = Common::String::format("aaze%c%03d.hed",
774 (mapId >= 100) ? 'x' : '0', mapId);
775 File headFile(headName);
776 _headData.synchronize(headFile);
777 headFile.close();
778
779 if (!ccNum && mapId == 15) {
780 if ((_mobData._monsters[0]._position.x > 31 || _mobData._monsters[0]._position.y > 31) &&
781 (_mobData._monsters[1]._position.x > 31 || _mobData._monsters[1]._position.y > 31) &&
782 (_mobData._monsters[2]._position.x > 31 || _mobData._monsters[2]._position.y > 31)) {
783 party._gameFlags[0][56] = true;
784 }
785 }
786 } else if (File::exists(mobName)) {
787 // For surrounding maps, set up flags for whether objects are present
788
789 // WORKAROUND: In WOX Map 120, one of the maps for Deep Mine Alpha,
790 // has invalid monster data. So to work around it, we just ignore it
791 if (!(mapId == 120 && g_vm->getGameID() == GType_WorldOfXeen)) {
792 // Load the monster/object data
793 File mobFile(mobName);
794 XeenSerializer sMob(&mobFile, nullptr);
795 MonsterObjectData mobData(_vm);
796 mobData.synchronize(sMob, _monsterData);
797 mobFile.close();
798
799 mazeDataP->_objectsPresent.resize(mobData._objects.size());
800 for (uint objIndex = 0; objIndex < mobData._objects.size(); ++objIndex) {
801 const Common::Point &pt = mobData._objects[objIndex]._position;
802 mazeDataP->_objectsPresent[objIndex] = ABS(pt.x) != 128 && ABS(pt.y) != 128;
803 }
804 }
805 }
806 }
807
808 // Move to next surrounding maze
809 MazeData *baseMaze = &_mazeData[MAP_GRID_PRIOR_INDEX[idx]];
810 mapId = baseMaze->_surroundingMazes[MAP_GRID_PRIOR_DIRECTION[idx]];
811 if (!mapId) {
812 baseMaze = &_mazeData[MAP_GRID_PRIOR_INDEX2[idx]];
813 mapId = baseMaze->_surroundingMazes[MAP_GRID_PRIOR_DIRECTION2[idx]];
814 }
815 }
816
817 // Reload the monster data for the main maze that we're loading
818 mapId = party._mazeId;
819 Common::String filename = Common::String::format("maze%c%03d.mob",
820 (mapId >= 100) ? 'x' : '0', mapId);
821 File mobFile(filename);
822 XeenSerializer sMob(&mobFile, nullptr);
823 _mobData.synchronize(sMob, _monsterData);
824 mobFile.close();
825
826 // Load sprites for the objects
827 for (uint i = 0; i < _mobData._objectSprites.size(); ++i) {
828 files.setGameCc(_sideObjects);
829
830 if (party._cloudsCompleted && _mobData._objectSprites[i]._spriteId == 85 &&
831 mapId == 27 && ccNum) {
832 _mobData._objects[29]._spriteId = 0;
833 _mobData._objects[29]._id = 8;
834 _mobData._objectSprites[i]._sprites.clear();
835 } else if (mapId == 12 && party._gameFlags[0][43] &&
836 _mobData._objectSprites[i]._spriteId == 118 && !ccNum) {
837 filename = "085.obj";
838 _mobData._objectSprites[0]._spriteId = 85;
839 } else {
840 filename = Common::String::format("%03d.%cbj",
841 _mobData._objectSprites[i]._spriteId,
842 _mobData._objectSprites[i]._spriteId >= 100 ? '0' : 'o');
843 }
844
845 // Read in the object sprites
846 if (!_mobData._objectSprites[i].isEmpty())
847 _mobData._objectSprites[i]._sprites.load(filename);
848 }
849
850 // Load sprites for the monsters
851 for (uint i = 0; i < _mobData._monsterSprites.size(); ++i) {
852 MonsterObjectData::SpriteResourceEntry &spr = _mobData._monsterSprites[i];
853 uint imgNumber = _monsterData[spr._spriteId]._imageNumber;
854
855 files.setGameCc((spr._spriteId == 91 && _vm->getGameID() == GType_WorldOfXeen) ?
856 0 : _sideMonsters);
857 filename = Common::String::format("%03u.mon", imgNumber);
858 _mobData._monsterSprites[i]._sprites.load(filename);
859
860 filename = Common::String::format("%03u.att", imgNumber);
861 _mobData._monsterSprites[i]._attackSprites.load(filename);
862 }
863
864 // Load wall picture sprite resources
865 for (uint i = 0; i < _mobData._wallItemSprites.size(); ++i) {
866 filename = Common::String::format("%03d.pic", _mobData._wallItemSprites[i]._spriteId);
867 _mobData._wallItemSprites[i]._sprites.load(filename, _sidePictures);
868 }
869
870 files.setGameCc(ccNum);
871
872 // Handle loading miscellaneous sprites for the map
873 if (_isOutdoors) {
874 // Start playing relevant music
875 sound._musicSide = ccNum;
876 Common::String musName;
877
878 if (_vm->_files->_ccNum) {
879 int randIndex = _vm->getRandomNumber(6);
880 musName = Res.MUSIC_FILES2[_mazeData->_wallKind][randIndex];
881 } else {
882 musName = "outdoors.m";
883 }
884 if (musName != sound._currentMusic)
885 sound.playSong(musName, 207);
886
887 // Load sprite sets needed for scene rendering
888 _groundSprites.load("water.out");
889 _tileSprites.load("outdoor.til");
890 outdoorList._sky1._sprites = &_skySprites[0];
891 outdoorList._sky2._sprites = &_skySprites[0];
892 outdoorList._groundSprite._sprites = &_groundSprites;
893
894 for (int i = 0; i < TOTAL_SURFACES; ++i) {
895 _wallSprites._surfaces[i].clear();
896
897 if (_mazeData[0]._wallTypes[i] != 0) {
898 _wallSprites._surfaces[i].load(Common::String::format("%s.wal",
899 Res.OUTDOORS_WALL_TYPES[_mazeData[0]._wallTypes[i]]));
900 }
901
902 _surfaceSprites[i].clear();
903 if (i != 0 && _mazeData[0]._surfaceTypes[i] != 0)
904 _surfaceSprites[i].load(Res.SURFACE_NAMES[_mazeData[0]._surfaceTypes[i]]);
905 }
906 } else {
907 if (files._ccNum && (mapId == 125 || mapId == 126 || mapId == 127))
908 files.setGameCc(0);
909 sound._musicSide = files._ccNum;
910
911 // Start playing relevant music
912 const int MUS_INDEXES[] = { 1, 2, 3, 4, 3, 5 };
913 Common::String musName;
914
915 if (files._ccNum) {
916 int randIndex = _vm->getRandomNumber(6);
917 musName = Res.MUSIC_FILES2[MUS_INDEXES[_mazeData->_wallKind]][randIndex];
918 } else {
919 musName = Res.MUSIC_FILES1[MUS_INDEXES[_mazeData->_wallKind]];
920 }
921 if (musName != sound._currentMusic)
922 sound.playSong(musName, 207);
923
924 // Load sprite sets needed for scene rendering
925 _skySprites[1].load(Common::String::format("%s.sky",
926 Res.TERRAIN_TYPES[_mazeData[0]._wallKind]));
927 _groundSprites.load(Common::String::format("%s.gnd",
928 Res.TERRAIN_TYPES[_mazeData[0]._wallKind]));
929 _tileSprites.load(Common::String::format("%s.til",
930 Res.TERRAIN_TYPES[_mazeData[0]._wallKind]));
931
932 for (int i = 0; i < TOTAL_SURFACES; ++i) {
933 _surfaceSprites[i].clear();
934
935 if (_mazeData[0]._surfaceTypes[i] != 0 || i == 4)
936 _surfaceSprites[i].load(Res.SURFACE_NAMES[_mazeData[0]._surfaceTypes[i]]);
937 }
938
939 for (int i = 0; i < TOTAL_SURFACES; ++i)
940 _wallSprites._surfaces[i].clear();
941
942 _wallSprites._fwl1.load(Common::String::format("f%s1.fwl",
943 Res.TERRAIN_TYPES[_mazeData[0]._wallKind]), _sidePictures);
944 _wallSprites._fwl2.load(Common::String::format("f%s2.fwl",
945 Res.TERRAIN_TYPES[_mazeData[0]._wallKind]), _sidePictures);
946 _wallSprites._fwl3.load(Common::String::format("f%s3.fwl",
947 Res.TERRAIN_TYPES[_mazeData[0]._wallKind]), _sidePictures);
948 _wallSprites._fwl4.load(Common::String::format("f%s4.fwl",
949 Res.TERRAIN_TYPES[_mazeData[0]._wallKind]), _sidePictures);
950 _wallSprites._swl.load(Common::String::format("s%s.swl",
951 Res.TERRAIN_TYPES[_mazeData[0]._wallKind]), _sidePictures);
952
953 // Set entries in the indoor draw list to the correct sprites
954 // for drawing various parts of the background
955 indoorList._swl_0F1R._sprites = &_wallSprites._swl;
956 indoorList._swl_0F1L._sprites = &_wallSprites._swl;
957 indoorList._swl_1F1R._sprites = &_wallSprites._swl;
958 indoorList._swl_1F1L._sprites = &_wallSprites._swl;
959 indoorList._swl_2F2R._sprites = &_wallSprites._swl;
960 indoorList._swl_2F1R._sprites = &_wallSprites._swl;
961 indoorList._swl_2F1L._sprites = &_wallSprites._swl;
962 indoorList._swl_2F2L._sprites = &_wallSprites._swl;
963
964 indoorList._swl_3F1R._sprites = &_wallSprites._swl;
965 indoorList._swl_3F2R._sprites = &_wallSprites._swl;
966 indoorList._swl_3F3R._sprites = &_wallSprites._swl;
967 indoorList._swl_3F4R._sprites = &_wallSprites._swl;
968 indoorList._swl_3F1L._sprites = &_wallSprites._swl;
969 indoorList._swl_3F2L._sprites = &_wallSprites._swl;
970 indoorList._swl_3F3L._sprites = &_wallSprites._swl;
971 indoorList._swl_3F4L._sprites = &_wallSprites._swl;
972
973 indoorList._swl_4F4R._sprites = &_wallSprites._swl;
974 indoorList._swl_4F3R._sprites = &_wallSprites._swl;
975 indoorList._swl_4F2R._sprites = &_wallSprites._swl;
976 indoorList._swl_4F1R._sprites = &_wallSprites._swl;
977 indoorList._swl_4F1L._sprites = &_wallSprites._swl;
978 indoorList._swl_4F2L._sprites = &_wallSprites._swl;
979 indoorList._swl_4F3L._sprites = &_wallSprites._swl;
980 indoorList._swl_4F4L._sprites = &_wallSprites._swl;
981
982 indoorList._fwl_4F4R._sprites = &_wallSprites._fwl4;
983 indoorList._fwl_4F3R._sprites = &_wallSprites._fwl4;
984 indoorList._fwl_4F2R._sprites = &_wallSprites._fwl4;
985 indoorList._fwl_4F1R._sprites = &_wallSprites._fwl4;
986 indoorList._fwl_4F._sprites = &_wallSprites._fwl4;
987 indoorList._fwl_4F1L._sprites = &_wallSprites._fwl4;
988 indoorList._fwl_4F2L._sprites = &_wallSprites._fwl4;
989 indoorList._fwl_4F3L._sprites = &_wallSprites._fwl4;
990 indoorList._fwl_4F4L._sprites = &_wallSprites._fwl4;
991
992 indoorList._fwl_2F1R._sprites = &_wallSprites._fwl3;
993 indoorList._fwl_2F._sprites = &_wallSprites._fwl3;
994 indoorList._fwl_2F1L._sprites = &_wallSprites._fwl3;
995 indoorList._fwl_3F2R._sprites = &_wallSprites._fwl3;
996 indoorList._fwl_3F1R._sprites = &_wallSprites._fwl3;
997 indoorList._fwl_3F._sprites = &_wallSprites._fwl3;
998 indoorList._fwl_3F1L._sprites = &_wallSprites._fwl3;
999 indoorList._fwl_3F2L._sprites = &_wallSprites._fwl3;
1000
1001 indoorList._fwl_1F._sprites = &_wallSprites._fwl1;
1002 indoorList._fwl_1F1R._sprites = &_wallSprites._fwl1;
1003 indoorList._fwl_1F1L._sprites = &_wallSprites._fwl1;
1004 indoorList._horizon._sprites = &_wallSprites._fwl1;
1005
1006 indoorList._ground._sprites = &_groundSprites;
1007
1008 // Don't show horizon for certain maps
1009 if (_vm->_files->_ccNum) {
1010 if ((mapId >= 89 && mapId <= 112) || mapId == 128 || mapId == 129)
1011 indoorList._horizon._sprites = nullptr;
1012 } else {
1013 if (mapId >= 25 && mapId <= 27)
1014 indoorList._horizon._sprites = nullptr;
1015 }
1016 }
1017
1018 patcher.patch();
1019 loadSky();
1020
1021 files.setGameCc(ccNum);
1022 }
1023
findMap(int mapId)1024 void Map::findMap(int mapId) {
1025 if (mapId == -1)
1026 mapId = _vm->_party->_mazeId;
1027
1028 _mazeDataIndex = 0;
1029 while (_mazeDataIndex < 9 && _mazeData[_mazeDataIndex]._mazeId != mapId)
1030 ++_mazeDataIndex;
1031 if (_mazeDataIndex == 9)
1032 error("Could not find map %d", mapId);
1033 }
1034
mazeLookup(const Common::Point & pt,int layerShift,int wallMask)1035 int Map::mazeLookup(const Common::Point &pt, int layerShift, int wallMask) {
1036 Common::Point pos = pt;
1037 int mapId = _vm->_party->_mazeId;
1038
1039 if (pt.x < -16 || pt.y < -16 || pt.x >= 32 || pt.y >= 32) {
1040 _currentWall = INVALID_CELL;
1041 return INVALID_CELL;
1042 }
1043
1044 // Find the correct maze data out of the set to use
1045 findMap();
1046
1047 // Handle map changing to the north or south as necessary
1048 if (pos.y & 16) {
1049 if (pos.y >= 0) {
1050 pos.y -= 16;
1051 mapId = _mazeData[_mazeDataIndex]._surroundingMazes._north;
1052 } else {
1053 pos.y += 16;
1054 mapId = _mazeData[_mazeDataIndex]._surroundingMazes._south;
1055 }
1056
1057 if (mapId) {
1058 // Move to the correct map to north/south
1059 findMap(mapId);
1060 } else {
1061 // No map, so reached outside indoor area or outer space outdoors
1062 _currentSteppedOn = true;
1063 return _isOutdoors ? SURFTYPE_SPACE : INVALID_CELL;
1064 }
1065 }
1066
1067 // Handle map changing to the east or west as necessary
1068 if (pos.x & 16) {
1069 if (pos.x >= 0) {
1070 pos.x -= 16;
1071 mapId = _mazeData[_mazeDataIndex]._surroundingMazes._east;
1072 } else {
1073 pos.x += 16;
1074 mapId = _mazeData[_mazeDataIndex]._surroundingMazes._west;
1075 }
1076
1077 if (mapId)
1078 // Move to the correct map to east/west
1079 findMap(mapId);
1080 }
1081
1082 if (mapId) {
1083 if (_isOutdoors) {
1084 _currentSurfaceId = _mazeData[_mazeDataIndex]._wallData[pos.y][pos.x]._outdoors._surfaceId;
1085 } else {
1086 _currentSurfaceId = _mazeData[_mazeDataIndex]._cells[pos.y][pos.x]._surfaceId;
1087 }
1088
1089 if (mazeData()._surfaceTypes[_currentSurfaceId] == SURFTYPE_SPACE ||
1090 mazeData()._surfaceTypes[_currentSurfaceId] == SURFTYPE_SKY) {
1091 _currentSteppedOn = true;
1092 } else {
1093 _currentSteppedOn = _mazeData[_mazeDataIndex]._steppedOnTiles[pos.y][pos.x];
1094 }
1095
1096 return (_mazeData[_mazeDataIndex]._wallData[pos.y][pos.x]._data >> layerShift) & wallMask;
1097
1098 } else {
1099 _currentSteppedOn = _isOutdoors;
1100 return _isOutdoors ? SURFTYPE_SPACE : INVALID_CELL;
1101 }
1102 }
1103
loadEvents(int mapId,int ccNum)1104 void Map::loadEvents(int mapId, int ccNum) {
1105 // Load events
1106 Common::String filename = Common::String::format("maze%c%03d.evt",
1107 (mapId >= 100) ? 'x' : '0', mapId);
1108 File fEvents(filename, ccNum);
1109 XeenSerializer sEvents(&fEvents, nullptr);
1110 _events.synchronize(sEvents);
1111 fEvents.close();
1112
1113 // Load text data
1114 filename = Common::String::format("aaze%c%03d.txt",
1115 (mapId >= 100) ? 'x' : '0', mapId);
1116 File fText(filename, ccNum);
1117 _events._text.clear();
1118 while (fText.pos() < fText.size())
1119 _events._text.push_back(fText.readString());
1120 fText.close();
1121 }
1122
saveEvents()1123 void Map::saveEvents() {
1124 // Save eents
1125 int mapId = _mazeData[0]._mazeId;
1126 Common::String filename = Common::String::format("maze%c%03d.evt",
1127 (mapId >= 100) ? 'x' : '0', mapId);
1128 OutFile fEvents(filename);
1129 XeenSerializer sEvents(nullptr, &fEvents);
1130 _events.synchronize(sEvents);
1131 fEvents.finalize();
1132 }
1133
saveMap()1134 void Map::saveMap() {
1135 FileManager &files = *g_vm->_files;
1136 Party &party = *g_vm->_party;
1137 int mapId = _mazeData[0]._mazeId;
1138 if (!files._ccNum && mapId == 85)
1139 return;
1140
1141 // Save the primary maze
1142 Common::String datName = Common::String::format("maze%c%03d.dat", (mapId >= 100) ? 'x' : '0', mapId);
1143 OutFile datFile(datName);
1144 XeenSerializer datSer(nullptr, &datFile);
1145 _mazeData[0].synchronize(datSer);
1146 datFile.finalize();
1147
1148 if (!files._ccNum && mapId == 15) {
1149 for (uint idx = 0; idx < MIN(_mobData._monsters.size(), (uint)3); ++idx) {
1150 MazeMonster &mon = _mobData._monsters[idx];
1151 if (mon._position.x > 31 || mon._position.y > 31) {
1152 party._gameFlags[0][56] = true;
1153 break;
1154 }
1155 }
1156 }
1157
1158 if (!_isOutdoors) {
1159 // Iterate through the surrounding mazes
1160 for (int mazeIndex = 1; mazeIndex < 9; ++mazeIndex) {
1161 mapId = _mazeData[mazeIndex]._mazeId;
1162 if (mapId == 0)
1163 continue;
1164
1165 datName = Common::String::format("maze%c%03d.dat", (mapId >= 100) ? 'x' : '0', mapId);
1166 OutFile datFile2(datName);
1167 XeenSerializer datSer2(nullptr, &datFile2);
1168 _mazeData[mazeIndex].synchronize(datSer2);
1169 datFile2.finalize();
1170 }
1171 }
1172 }
1173
saveMonsters()1174 void Map::saveMonsters() {
1175 int mapId = _mazeData[0]._mazeId;
1176 Common::String filename = Common::String::format("maze%c%03d.mob",
1177 (mapId >= 100) ? 'x' : '0', mapId);
1178 OutFile fMob(filename);
1179 XeenSerializer sMob(nullptr, &fMob);
1180 _mobData.synchronize(sMob, _monsterData);
1181 fMob.finalize();
1182 }
1183
saveMaze()1184 void Map::saveMaze() {
1185 int mazeNum = _mazeData[0]._mazeNumber;
1186 if (!mazeNum || (mazeNum == 85 && !_vm->_files->_ccNum))
1187 return;
1188
1189 saveEvents();
1190 saveMap();
1191 saveMonsters();
1192 }
1193
clearMaze()1194 void Map::clearMaze() {
1195 _mazeData[0]._mazeNumber = 0;
1196 }
1197
cellFlagLookup(const Common::Point & pt)1198 void Map::cellFlagLookup(const Common::Point &pt) {
1199 Common::Point pos = pt;
1200 findMap();
1201
1202 int mapId = _vm->_party->_mazeId;
1203 findMap(mapId);
1204
1205 // Handle map changing to the north or south as necessary
1206 if (pos.y & 16) {
1207 if (pos.y >= 0) {
1208 pos.y -= 16;
1209 mapId = _mazeData[_mazeDataIndex]._surroundingMazes._north;
1210 } else {
1211 pos.y += 16;
1212 mapId = _mazeData[_mazeDataIndex]._surroundingMazes._south;
1213 }
1214
1215 findMap(mapId);
1216 }
1217
1218 // Handle map changing to the east or west as necessary
1219 if (pos.x & 16) {
1220 if (pos.x >= 0) {
1221 pos.x -= 16;
1222 mapId = _mazeData[_mazeDataIndex]._surroundingMazes._east;
1223 } else {
1224 pos.x += 16;
1225 mapId = _mazeData[_mazeDataIndex]._surroundingMazes._west;
1226 }
1227
1228 findMap(mapId);
1229 }
1230
1231 // Get the cell flags
1232 const MazeCell &cell = _mazeData[_mazeDataIndex]._cells[pos.y][pos.x];
1233 _currentGrateUnlocked = cell._flags & OUTFLAG_GRATE;
1234 _currentCantRest = cell._flags & RESTRICTION_REST;
1235 _currentIsDrain = cell._flags & OUTFLAG_DRAIN;
1236 _currentIsEvent = cell._flags & FLAG_AUTOEXECUTE_EVENT;
1237 _currentSky = (cell._flags & OUTFLAG_OBJECT_EXISTS) ? 1 : 0;
1238 _currentMonsterFlags = cell._flags & 7;
1239 }
1240
setCellSurfaceFlags(const Common::Point & pt,int bits)1241 void Map::setCellSurfaceFlags(const Common::Point &pt, int bits) {
1242 mazeLookup(pt, 0);
1243
1244 Common::Point mapPos(pt.x & 15, pt.y & 15);
1245 MazeCell &cell = _mazeData[_mazeDataIndex]._cells[mapPos.y][mapPos.x];
1246 cell._flags |= bits & 0xF8;
1247 }
1248
setWall(const Common::Point & pt,Direction dir,int v)1249 void Map::setWall(const Common::Point &pt, Direction dir, int v) {
1250 const int XOR_MASKS[4] = { 0xFFF, 0xF0FF, 0xFF0F, 0xFFF0 };
1251 mazeLookup(pt, 0, 0);
1252
1253 Common::Point mapPos(pt.x & 15, pt.y & 15);
1254 MazeWallLayers &wallLayer = _mazeData[_mazeDataIndex]._wallData[mapPos.y][mapPos.x];
1255 wallLayer._data &= XOR_MASKS[dir];
1256 wallLayer._data |= v << Res.WALL_SHIFTS[dir][2];
1257 }
1258
getCell(int idx)1259 int Map::getCell(int idx) {
1260 Party &party = *g_vm->_party;
1261 int mapId = party._mazeId;
1262 Direction dir = _vm->_party->_mazeDirection;
1263 Common::Point pt(
1264 _vm->_party->_mazePosition.x + Res.SCREEN_POSITIONING_X[_vm->_party->_mazeDirection][idx],
1265 _vm->_party->_mazePosition.y + Res.SCREEN_POSITIONING_Y[_vm->_party->_mazeDirection][idx]
1266 );
1267
1268 if (pt.x > 31 || pt.y > 31) {
1269 if (_vm->_files->_ccNum) {
1270 if ((mapId >= 53 && mapId <= 88 && mapId != 73) || (mapId >= 74 && mapId <= 120) ||
1271 mapId == 125 || mapId == 126 || mapId == 128 || mapId == 129) {
1272 _currentSurfaceId = SURFTYPE_DESERT;
1273 } else {
1274 _currentSurfaceId = 0;
1275 }
1276 } else {
1277 _currentSurfaceId = (mapId >= 25 && mapId <= 27) ? 7 : 0;
1278 }
1279 _currentWall = INVALID_CELL;
1280 return INVALID_CELL;
1281 }
1282
1283 findMap(mapId);
1284
1285 if (pt.y & 16) {
1286 if (pt.y >= 0) {
1287 pt.y -= 16;
1288 mapId = _mazeData[_mazeDataIndex]._surroundingMazes._north;
1289 } else {
1290 pt.y += 16;
1291 mapId = _mazeData[_mazeDataIndex]._surroundingMazes._south;
1292 }
1293
1294 if (!mapId) {
1295 mapId = party._mazeId;
1296
1297 if (_isOutdoors) {
1298 _currentSurfaceId = SURFTYPE_SPACE;
1299 _currentWall = 0;
1300 return 0;
1301 } else {
1302 if (_vm->_files->_ccNum) {
1303 if ((mapId >= 53 && mapId <= 88 && mapId != 73) || (mapId >= 74 && mapId <= 120) ||
1304 mapId == 125 || mapId == 126 || mapId == 128 || mapId == 129) {
1305 _currentSurfaceId = 6;
1306 } else {
1307 _currentSurfaceId = 0;
1308 }
1309 } else {
1310 _currentSurfaceId = (mapId >= 25 && mapId <= 27) ? SURFTYPE_ROAD : SURFTYPE_DEFAULT;
1311 }
1312
1313 _currentWall = INVALID_CELL;
1314 return INVALID_CELL;
1315 }
1316 }
1317
1318 findMap(mapId);
1319 }
1320
1321 if (pt.x & 16) {
1322 if (pt.x >= 0) {
1323 pt.x -= 16;
1324 mapId = _mazeData[_mazeDataIndex]._surroundingMazes._east;
1325 } else {
1326 pt.x += 16;
1327 mapId = _mazeData[_mazeDataIndex]._surroundingMazes._west;
1328 }
1329
1330 if (!mapId) {
1331 mapId = party._mazeId;
1332
1333 if (_isOutdoors) {
1334 _currentSurfaceId = SURFTYPE_SPACE;
1335 _currentWall = 0;
1336 return 0;
1337 } else {
1338 if (_vm->_files->_ccNum) {
1339 if ((mapId >= 53 && mapId <= 88 && mapId != 73) || (mapId >= 74 && mapId <= 120) ||
1340 mapId == 125 || mapId == 126 || mapId == 128 || mapId == 129) {
1341 _currentSurfaceId = 6;
1342 } else {
1343 _currentSurfaceId = 0;
1344 }
1345 } else {
1346 _currentSurfaceId = (mapId >= 25 && mapId <= 27) ? SURFTYPE_ROAD : SURFTYPE_DEFAULT;
1347 }
1348
1349 _currentWall = INVALID_CELL;
1350 return INVALID_CELL;
1351 }
1352 }
1353
1354 findMap(mapId);
1355 }
1356
1357 assert(pt.x >= 0 && pt.x < 16 && pt.y >= 0 && pt.y < 16);
1358 int wallData = _mazeData[_mazeDataIndex]._wallData[pt.y][pt.x]._data;
1359 if (_isOutdoors) {
1360 if (mapId) {
1361 _currentTile = (wallData >> 8) & 0xFF;
1362 _currentWall = (wallData >> 4) & 0xF;
1363 _currentSurfaceId = wallData & 0xF;
1364 } else {
1365 _currentSurfaceId = SURFTYPE_DEFAULT;
1366 _currentWall = 0;
1367 _currentTile = 0;
1368 }
1369 } else {
1370 if (!mapId)
1371 return 0;
1372
1373 _currentSurfaceId = _mazeData[_mazeDataIndex]._cells[pt.y][pt.x]._surfaceId;
1374 _currentWall = wallData;
1375 return (_currentWall >> Res.WALL_SHIFTS[dir][idx]) & 0xF;
1376 }
1377
1378 return _currentWall;
1379 }
1380
loadSky()1381 void Map::loadSky() {
1382 Party &party = *_vm->_party;
1383
1384 party._isNight = party._minutes < (5 * 60) || party._minutes >= (21 * 60);
1385 _skySprites[0].load(((party._mazeId >= 89 && party._mazeId <= 112) ||
1386 party._mazeId == 128 || party._mazeId == 129) || !party._isNight
1387 ? "sky.sky" : "night.sky");
1388 }
1389
getNewMaze()1390 void Map::getNewMaze() {
1391 Party &party = *_vm->_party;
1392 Common::Point pt = party._mazePosition;
1393 int mapId = party._mazeId;
1394
1395 // Get the correct map to use from the cached list
1396 findMap(mapId);
1397
1398 // Adjust Y and X to be in the 0-15 range, and on the correct surrounding
1399 // map if either value is < 0 or >= 16
1400 if (pt.y & 16) {
1401 if (pt.y >= 0) {
1402 pt.y -= 16;
1403 mapId = _mazeData[_mazeDataIndex]._surroundingMazes._north;
1404 } else {
1405 pt.y += 16;
1406 mapId = _mazeData[_mazeDataIndex]._surroundingMazes._south;
1407 }
1408
1409 if (mapId)
1410 findMap(mapId);
1411 }
1412
1413 if (pt.x & 16) {
1414 if (pt.x >= 0) {
1415 pt.x -= 16;
1416 mapId = _mazeData[_mazeDataIndex]._surroundingMazes._east;
1417 } else {
1418 pt.x += 16;
1419 mapId = _mazeData[_mazeDataIndex]._surroundingMazes._west;
1420 }
1421
1422 if (mapId)
1423 findMap(mapId);
1424 }
1425
1426 // Save the adjusted (0,0)-(15,15) position and load the given map.
1427 // This will make it the new center, with it's own surrounding mazees loaded
1428 party._mazePosition = pt;
1429 if (mapId)
1430 load(mapId);
1431 }
1432
getMazeName(int mapId,int ccNum)1433 Common::String Map::getMazeName(int mapId, int ccNum) {
1434 if (ccNum == -1)
1435 ccNum = g_vm->_files->_ccNum;
1436
1437 if (g_vm->getGameID() == GType_Clouds) {
1438 return Res._cloudsMapNames[mapId];
1439 } else {
1440 Common::String txtName = Common::String::format("%s%c%03d.txt",
1441 ccNum ? "dark" : "xeen", mapId >= 100 ? 'x' : '0', mapId);
1442 File fText(txtName, 1);
1443 char mazeName[33];
1444 fText.read(mazeName, 33);
1445 mazeName[32] = '\0';
1446
1447 Common::String name = Common::String(mazeName);
1448 fText.close();
1449 return name;
1450 }
1451 }
1452
1453 } // End of namespace Xeen
1454