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 
24 
25 #include "common/file.h"
26 #include "common/textconsole.h"
27 
28 #include "agos/agos.h"
29 #include "agos/intern.h"
30 
31 namespace AGOS {
32 
getBackExit(int n)33 uint16 AGOSEngine::getBackExit(int n) {
34 	switch (n) {
35 	case 0:
36 		return 2;
37 	case 1:
38 		return 3;
39 	case 2:
40 		return 0;
41 	case 3:
42 		return 1;
43 	case 4:
44 		return 5;
45 	case 5:
46 		return 4;
47 	}
48 
49 	return 0;
50 }
51 
getDoorState(Item * item,uint16 d)52 uint16 AGOSEngine::getDoorState(Item *item, uint16 d) {
53 	uint16 mask = 3;
54 	uint16 n;
55 
56 	SubRoom *subRoom = (SubRoom *)findChildOfType(item, kRoomType);
57 	if (subRoom == NULL)
58 		return 0;
59 
60 	d <<= 1;
61 	mask <<= d;
62 	n = subRoom->roomExitStates & mask;
63 	n >>= d;
64 
65 	return n;
66 }
67 
getExitOf(Item * item,uint16 d)68 uint16 AGOSEngine::getExitOf(Item *item, uint16 d) {
69 	SubRoom *subRoom;
70 	uint16 x;
71 	uint16 y = 0;
72 
73 	subRoom = (SubRoom *)findChildOfType(item, kRoomType);
74 	if (subRoom == NULL)
75 		return 0;
76 	x = d;
77 	while (x > y) {
78 		if (getDoorState(item, y) == 0)
79 			d--;
80 		y++;
81 	}
82 	return subRoom->roomExit[d];
83 }
84 
changeDoorState(SubRoom * r,uint16 d,uint16 n)85 void AGOSEngine::changeDoorState(SubRoom *r, uint16 d, uint16 n) {
86 	uint16 mask=3;
87 	d <<= 1;
88 	mask <<= d;
89 	n <<= d;
90 	r->roomExitStates &= ~mask;
91 	r->roomExitStates |= n;
92 }
93 
setDoorState(Item * i,uint16 d,uint16 n)94 void AGOSEngine::setDoorState(Item *i, uint16 d, uint16 n) {
95 	Item *j;
96 	SubRoom *r, *r1;
97 	uint16 d1;
98 	uint16 y = 0;
99 
100 	r = (SubRoom *)findChildOfType(i, kRoomType);
101 	if (r == NULL)
102 		return;
103 	d1 = d;
104 	while (d > y) {
105 		if (getDoorState(i, y) == 0)
106 			d1--;
107 		y++;
108 	}
109 	changeDoorState(r, d, n);
110 
111 	j = derefItem(r->roomExit[d1]);
112 	if (j == NULL)
113 		return;
114 	r1 = (SubRoom *)findChildOfType(j, kRoomType);
115 	if (r1 == NULL)
116 		return;
117 	d = getBackExit(d);
118 	d1 = d;
119 	y = 0;
120 	while (d > y) {
121 		if (getDoorState(j, y) == 0)
122 			d1--;
123 		y++;
124 	}
125 	/* Check are a complete exit pair */
126 	if (derefItem(r1->roomExit[d1]) != i)
127 		return;
128 	/* Change state of exit coming back */
129 	changeDoorState(r1, d, n);
130 }
131 
132 // Elvira 1 specific
getDoorOf(Item * i,uint16 d)133 Item *AGOSEngine::getDoorOf(Item *i, uint16 d) {
134 	SubGenExit *g;
135 	Item *x;
136 
137 	g = (SubGenExit *)findChildOfType(i, kGenExitType);
138 	if (g == NULL)
139 		return 0;
140 
141 	x = derefItem(g->dest[d]);
142 	if (x == NULL)
143 		return 0;
144 	if (isRoom(x))
145 		return 0;
146 	return x;
147 }
148 
getExitOf_e1(Item * item,uint16 d)149 Item *AGOSEngine::getExitOf_e1(Item *item, uint16 d) {
150 	SubGenExit *g;
151 	Item *x;
152 
153 	g = (SubGenExit *)findChildOfType(item, kGenExitType);
154 	if (g == NULL)
155 		return 0;
156 
157 	x = derefItem(g->dest[d]);
158 	if (x == NULL)
159 		return 0;
160 	if (isRoom(x))
161 		return x;
162 	if (x->state != 0)
163 		return 0;
164 	return derefItem(x->parent);
165 }
166 
moveDirn(Item * i,uint x)167 void AGOSEngine_Waxworks::moveDirn(Item *i, uint x) {
168 	Item *d;
169 	uint16 n;
170 
171 	if (i->parent == 0)
172 		return;
173 
174 	n = getExitOf(derefItem(i->parent), x);
175 	if (derefItem(n) == NULL) {
176 		loadRoomItems(n);
177 		n = getExitOf(derefItem(i->parent), x);
178 	}
179 
180 	d = derefItem(n);
181 	if (d) {
182 		n = getDoorState(derefItem(i->parent), x);
183 		if (n == 1) {
184 			if (!canPlace(i, d))
185 				setItemParent(i, d);
186 		}
187 	}
188 }
189 
moveDirn(Item * i,uint x)190 void AGOSEngine_Elvira2::moveDirn(Item *i, uint x) {
191 	SubSuperRoom *sr;
192 	Item *d, *p;
193 	uint16 a, n;
194 
195 	if (i->parent == 0)
196 		return;
197 
198 	p = derefItem(i->parent);
199 	if (findChildOfType(p, kSuperRoomType)) {
200 		n = getExitState(p, _superRoomNumber,x);
201 		if (n == 1) {
202 			sr = (SubSuperRoom *)findChildOfType(p, kSuperRoomType);
203 			switch (x) {
204 			case 0: a = -(sr->roomX); break;
205 			case 1: a = 1; break;
206 			case 2: a = sr->roomX; break;
207 			case 3: a = 0xFFFF; break;
208 			case 4: a = -(sr->roomX * sr->roomY); break;
209 			case 5: a = (sr->roomX * sr->roomY); break;
210 			default: return;
211 			}
212 			_superRoomNumber += a;
213 		}
214 		return;
215 	}
216 
217 	n = getExitOf(derefItem(i->parent), x);
218 
219 	d = derefItem(n);
220 	if (d) {
221 		n = getDoorState(derefItem(i->parent), x);
222 		if (n == 1) {
223 			if (!canPlace(i, d))
224 				setItemParent(i, d);
225 		}
226 	}
227 }
228 
moveDirn(Item * i,uint x)229 void AGOSEngine::moveDirn(Item *i, uint x) {
230 	Item *d, *p;
231 
232 	p = derefItem(i->parent);
233 	if (p == 0)
234 		return;
235 
236 
237 	d = getExitOf_e1(p, x);
238 	if (d) {
239 		if (canPlace(i, d))
240 			return;
241 
242 		setItemParent(i, d);
243 		return;
244 	}
245 
246 	d = getDoorOf(p, x);
247 	if (d) {
248 		const byte *name = getStringPtrByID(d->itemName, true);
249 		if (d->state == 1)
250 			showMessageFormat("%s is closed.\n", name);
251 		else
252 			showMessageFormat("%s is locked.\n", name);
253 		return;
254 	}
255 
256 	showMessageFormat("You can't go that way.\n");
257 }
258 
259 // Elvira 2 specific
changeExitStates(SubSuperRoom * sr,int n,int d,uint16 s)260 int AGOSEngine_Elvira2::changeExitStates(SubSuperRoom *sr, int n, int d, uint16 s) {
261 	int b, bd;
262 	uint16 mask;
263 
264 	switch (d) {
265 	case 0:
266 		b = -(sr->roomX);
267 		bd = 2;
268 		if (((n % (sr->roomX * sr->roomY)) / sr->roomX) == 0)
269 			return 0;
270 		else
271 			break;
272 	case 1:
273 		b = 1;
274 		bd = 3;
275 		if (((n % (sr->roomX * sr->roomY)) % sr->roomX) == 0)
276 			return 0;
277 		else
278 			break;
279 	case 2:
280 		b = sr->roomX;
281 		bd = 0;
282 		if (((n % (sr->roomX * sr->roomY)) / sr->roomX) == (sr->roomY - 1))
283 			return 0;
284 		else
285 			break;
286 	case 3:
287 		b = -1;
288 		bd = 1;
289 		if (((n % (sr->roomX * sr->roomY)) % sr->roomX) == 1)
290 			return 0;
291 		else
292 			break;
293 	case 4:
294 		b = -(sr->roomX * sr->roomY);
295 		bd = 5;
296 		if (n < (sr->roomX * sr->roomY))
297 			return 0;
298 		else
299 			break;
300 	case 5:
301 		b = sr->roomX * sr->roomY;
302 		bd = 4;
303 		if (n > (sr->roomX * sr->roomY * (sr->roomZ - 1)))
304 			return 0;
305 		else
306 			break;
307 	default:
308 		return 0;
309 	}
310 
311 	n--;
312 	d <<= 1;
313 	mask = (3 << d);
314 	sr->roomExitStates[n] &= ~mask;
315 	sr->roomExitStates[n] |= (s << d);
316 
317 	bd <<= 1;
318 	mask = (3 << bd);
319 	sr->roomExitStates[n + b] &= ~mask;
320 	sr->roomExitStates[n + b] |= (s << bd);
321 	return 1;
322 }
323 
getExitState(Item * i,uint16 x,uint16 d)324 uint16 AGOSEngine_Elvira2::getExitState(Item *i, uint16 x, uint16 d) {
325 	SubSuperRoom *sr;
326 	uint16 mask = 3;
327 	uint16 n;
328 
329 	sr = (SubSuperRoom *)findChildOfType(i, kSuperRoomType);
330 	if (sr == NULL)
331 		return 0;
332 
333 	d <<= 1;
334 	mask <<= d;
335 	n = sr->roomExitStates[x - 1] & mask;
336 	n >>= d;
337 	return n;
338 }
339 
setExitState(Item * i,uint16 n,uint16 d,uint16 s)340 void AGOSEngine_Elvira2::setExitState(Item *i, uint16 n, uint16 d, uint16 s) {
341 	SubSuperRoom *sr = (SubSuperRoom *)findChildOfType(i, kSuperRoomType);
342 	if (sr)
343 		changeExitStates(sr, n, d, s);
344 }
345 
setSRExit(Item * i,int n,int d,uint16 s)346 void AGOSEngine_Elvira2::setSRExit(Item *i, int n, int d, uint16 s) {
347 	uint16 mask = 3;
348 
349 	SubSuperRoom *sr = (SubSuperRoom *)findChildOfType(i, kSuperRoomType);
350 	if (sr) {
351 		n--;
352 		d <<= 1;
353 		mask <<= d;
354 		s <<= d;
355 		sr->roomExitStates[n] &= ~mask;
356 		sr->roomExitStates[n] |= s;
357 	}
358 }
359 
360 // Waxworks specific
loadRoomItems(uint16 room)361 bool AGOSEngine::loadRoomItems(uint16 room) {
362 	byte *p;
363 	uint i, minNum, maxNum;
364 	char filename[30];
365 	Common::File in;
366 	Item *item, *itemTmp;
367 
368 	if (_roomsList == NULL)
369 		return 0;
370 
371 	_currentRoom = room;
372 	room -= 2;
373 
374 	if (_roomsListPtr) {
375 		p = _roomsListPtr;
376 		for (;;) {
377 			minNum = READ_BE_UINT16(p); p += 2;
378 			if (minNum == 0)
379 				break;
380 
381 			maxNum = READ_BE_UINT16(p); p += 2;
382 
383 			 for (uint16 z = minNum; z <= maxNum; z++) {
384 				uint16 itemNum = z + 2;
385 				item = derefItem(itemNum);
386 				_itemArrayPtr[itemNum] = 0;
387 
388 				uint16 num = (itemNum - _itemArrayInited);
389 				_roomStates[num].state = item->state;
390 				_roomStates[num].classFlags = item->classFlags;
391 				SubRoom *subRoom = (SubRoom *)findChildOfType(item, kRoomType);
392 				_roomStates[num].roomExitStates = subRoom->roomExitStates;
393 			}
394 		}
395 	}
396 
397 	p = _roomsList;
398 	while (*p) {
399 		for (i = 0; *p; p++, i++)
400 			filename[i] = *p;
401 		filename[i] = 0;
402 		p++;
403 
404 		_roomsListPtr = p;
405 
406 		for (;;) {
407 			minNum = READ_BE_UINT16(p); p += 2;
408 			if (minNum == 0)
409 				break;
410 
411 			maxNum = READ_BE_UINT16(p); p += 2;
412 
413 			if (room >= minNum && room <= maxNum) {
414 				in.open(filename);
415 				if (in.isOpen() == false) {
416 					error("loadRoomItems: Can't load rooms file '%s'", filename);
417 				}
418 
419 				while ((i = in.readUint16BE()) != 0) {
420 					uint16 itemNum = i + 2;
421 
422 					_itemArrayPtr[itemNum] = (Item *)allocateItem(sizeof(Item));
423 					readItemFromGamePc(&in, _itemArrayPtr[itemNum]);
424 
425 					item = derefItem(itemNum);
426 					item->parent = 0;
427 					item->child = 0;
428 
429 					 for (uint16 z = _itemArrayInited; z; z--) {
430 						itemTmp = derefItem(z);
431 
432 						if (!itemTmp)
433 							continue;
434 						if (itemTmp->parent != itemNum)
435 							continue;
436 						if (item->child == 0) {
437 							item->child = z;
438 							continue;
439 						}
440 						uint16 child = item->child;
441 						while (itemTmp->next != 0) {
442 							if (itemTmp->next == child) {
443 								item->child = z;
444 								break;
445 							}
446 
447 							itemTmp = derefItem(itemTmp->next);
448 						}
449 					}
450 
451 					uint16 num = (itemNum - _itemArrayInited);
452 					item->state = _roomStates[num].state;
453 					item->classFlags = _roomStates[num].classFlags;
454 					SubRoom *subRoom = (SubRoom *)findChildOfType(item, kRoomType);
455 					subRoom->roomExitStates = _roomStates[num].roomExitStates;
456 
457 				}
458 				in.close();
459 
460 				return 1;
461 			}
462 		}
463 	}
464 
465 	debug(1,"loadRoomItems: didn't find %d", room);
466 	return 0;
467 }
468 
469 } // End of namespace AGOS
470