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 #include "common/endian.h"
25 #include "common/memstream.h"
26 #include "common/util.h"
27 
28 #include "cine/cine.h"
29 #include "cine/object.h"
30 #include "cine/part.h"
31 #include "cine/various.h"
32 
33 namespace Cine {
34 
35 /** Resets all elements in the object table. */
resetObjectTable()36 void resetObjectTable() {
37 	for (Common::Array<ObjectStruct>::iterator it = g_cine->_objectTable.begin(); it != g_cine->_objectTable.end(); ++it) {
38 		it->clear();
39 	}
40 }
41 
loadObject(char * pObjectName)42 void loadObject(char *pObjectName) {
43 	debug(5, "loadObject(\"%s\")", pObjectName);
44 	uint16 numEntry;
45 	uint16 entrySize;
46 	uint16 i;
47 	byte *ptr, *dataPtr;
48 
49 	checkDataDisk(-1);
50 
51 	ptr = dataPtr = readBundleFile(findFileInBundle(pObjectName));
52 
53 	setMouseCursor(MOUSE_CURSOR_DISK);
54 
55 	numEntry = READ_BE_UINT16(ptr); ptr += 2;
56 
57 	entrySize = READ_BE_UINT16(ptr); ptr += 2;
58 
59 	assert(numEntry <= NUM_MAX_OBJECT);
60 
61 	for (i = 0; i < numEntry; i++) {
62 		if (g_cine->_objectTable[i].costume != -2 && g_cine->_objectTable[i].costume != -3) { // flag is keep?
63 			Common::MemoryReadStream readS(ptr, entrySize);
64 
65 			g_cine->_objectTable[i].x = readS.readSint16BE();
66 			g_cine->_objectTable[i].y = readS.readSint16BE();
67 			g_cine->_objectTable[i].mask = readS.readUint16BE();
68 			g_cine->_objectTable[i].frame = readS.readSint16BE();
69 			g_cine->_objectTable[i].costume = readS.readSint16BE();
70 			readS.read(g_cine->_objectTable[i].name, 20);
71 			g_cine->_objectTable[i].part = readS.readUint16BE();
72 		}
73 		ptr += entrySize;
74 	}
75 
76 	if (!strcmp(pObjectName, "INTRO.OBJ")) {
77 		for (i = 0; i < 10; i++) {
78 			g_cine->_objectTable[i].costume = 0;
79 		}
80 	}
81 
82 	free(dataPtr);
83 }
84 
85 /**
86  * Remove overlay sprite from the list
87  * @param objIdx Remove overlay associated with this object
88  * @param param Remove overlay of this type
89  */
removeOverlay(uint16 objIdx,uint16 param)90 int removeOverlay(uint16 objIdx, uint16 param) {
91 	Common::List<overlay>::iterator it;
92 
93 	for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) {
94 		if (it->objIdx == objIdx && it->type == param) {
95 			g_cine->_overlayList.erase(it);
96 			return 1;
97 		}
98 	}
99 
100 	return 0;
101 }
102 
103 /**
104  * Add new overlay sprite to the list
105  * @param objIdx Associate the overlay with this object
106  * @param type Type of new overlay
107  * @todo Why are x, y, width and color left uninitialized?
108  */
addOverlay(uint16 objIdx,uint16 type)109 void addOverlay(uint16 objIdx, uint16 type) {
110 	Common::List<overlay>::iterator it;
111 	overlay tmp;
112 
113 	for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) {
114 		// This is done for both Future Wars and Operation Stealth
115 		if (g_cine->_objectTable[it->objIdx].mask >= g_cine->_objectTable[objIdx].mask) {
116 			break;
117 		}
118 
119 		// There are additional checks in Operation Stealth's implementation
120 		if (g_cine->getGameType() == Cine::GType_OS && (it->type == 2 || it->type == 3)) {
121 			break;
122 		}
123 	}
124 
125 	// In Operation Stealth's implementation we might bail out early
126 	if (g_cine->getGameType() == Cine::GType_OS && it != g_cine->_overlayList.end() && it->objIdx == objIdx && it->type == type) {
127 		return;
128 	}
129 
130 	tmp.objIdx = objIdx;
131 	tmp.type = type;
132 	tmp.x = 0;
133 	tmp.y = 0;
134 	tmp.width = 0;
135 	tmp.color = 0;
136 
137 	g_cine->_overlayList.insert(it, tmp);
138 }
139 
140 /**
141  * Add new background mask overlay
142  * @param objIdx Associate the overlay with this object
143  * @param param source background index
144  */
addGfxElement(int16 objIdx,int16 param,int16 type)145 void addGfxElement(int16 objIdx, int16 param, int16 type) {
146 	Common::List<overlay>::iterator it;
147 	overlay tmp;
148 
149 	for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) {
150 		if (g_cine->_objectTable[it->objIdx].mask >= g_cine->_objectTable[objIdx].mask || it->type == 2 || it->type == 3) {
151 			break;
152 		}
153 	}
154 
155 	if (it != g_cine->_overlayList.end() && it->objIdx == objIdx && it->type == type && it->x == param) {
156 		return;
157 	}
158 
159 	tmp.objIdx = objIdx;
160 	tmp.type = type;
161 	tmp.x = param;
162 	tmp.y = 0;
163 	tmp.width = 0;
164 	tmp.color = 0;
165 
166 	g_cine->_overlayList.insert(it, tmp);
167 }
168 
169 /**
170  * Remove background mask overlay
171  * @param objIdx Remove overlay associated with this object
172  * @param param Remove overlay using this background
173  * @todo Check that it works
174  */
removeGfxElement(int16 objIdx,int16 param,int16 type)175 void removeGfxElement(int16 objIdx, int16 param, int16 type) {
176 	Common::List<overlay>::iterator it;
177 
178 	for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) {
179 		if (it->objIdx == objIdx && it->type == type && it->x == param) {
180 			g_cine->_overlayList.erase(it);
181 			return;
182 		}
183 	}
184 }
185 
setupObject(byte objIdx,uint16 param1,uint16 param2,uint16 param3,uint16 param4)186 void setupObject(byte objIdx, uint16 param1, uint16 param2, uint16 param3, uint16 param4) {
187 	g_cine->_objectTable[objIdx].x = param1;
188 	g_cine->_objectTable[objIdx].y = param2;
189 	g_cine->_objectTable[objIdx].mask = param3;
190 	g_cine->_objectTable[objIdx].frame = param4;
191 
192 	if (g_cine->getGameType() == Cine::GType_OS) {
193 		resetGfxEntityEntry(objIdx);
194 	} else { // Future Wars
195 		if (removeOverlay(objIdx, 0)) {
196 			addOverlay(objIdx, 0);
197 		}
198 	}
199 }
200 
subObjectParam(byte objIdx,byte paramIdx,int16 newValue)201 void subObjectParam(byte objIdx, byte paramIdx, int16 newValue) {
202 	addObjectParam(objIdx, paramIdx, -newValue);
203 }
204 
addObjectParam(byte objIdx,byte paramIdx,int16 newValue)205 void addObjectParam(byte objIdx, byte paramIdx, int16 newValue) {
206 	int16 currentValue = getObjectParam(objIdx, paramIdx);
207 	modifyObjectParam(objIdx, paramIdx, currentValue + newValue);
208 }
209 
modifyObjectParam(byte objIdx,byte paramIdx,int16 newValue)210 void modifyObjectParam(byte objIdx, byte paramIdx, int16 newValue) {
211 	// Operation Stealth checks object index range, Future Wars doesn't.
212 	if (g_cine->getGameType() == Cine::GType_OS && objIdx >= NUM_MAX_OBJECT)
213 		return;
214 
215 	switch (paramIdx) {
216 	case 1:
217 		g_cine->_objectTable[objIdx].x = newValue;
218 		break;
219 	case 2:
220 		g_cine->_objectTable[objIdx].y = newValue;
221 		break;
222 	case 3:
223 		g_cine->_objectTable[objIdx].mask = newValue;
224 
225 		if (g_cine->getGameType() == Cine::GType_OS) { // Operation Stealth specific
226 			resetGfxEntityEntry(objIdx);
227 		} else { // Future Wars specific
228 			if (removeOverlay(objIdx, 0)) {
229 				addOverlay(objIdx, 0);
230 			}
231 		}
232 		break;
233 	case 4:
234 		g_cine->_objectTable[objIdx].frame = newValue;
235 		break;
236 	case 5:
237 		// TODO: Test if this really breaks the newspaper machine on the airport in Operation Stealth.
238 		if (g_cine->getGameType() == Cine::GType_FW && newValue == -1) {
239 			g_cine->_objectTable[objIdx].costume = g_cine->_globalVars[0];
240 		} else {
241 			g_cine->_objectTable[objIdx].costume = newValue;
242 		}
243 		break;
244 	case 6:
245 		g_cine->_objectTable[objIdx].part = newValue;
246 		break;
247 	}
248 }
249 
250 /**
251  * Check if at least one of the range B's endpoints is inside range A,
252  * not counting the starting and ending points of range A.
253  * Used at least by Operation Stealth's opcode 0x8D i.e. 141.
254  */
compareRanges(uint16 aStart,uint16 aEnd,uint16 bStart,uint16 bEnd)255 bool compareRanges(uint16 aStart, uint16 aEnd, uint16 bStart, uint16 bEnd) {
256 	return (bStart > aStart && bStart < aEnd) || (bEnd > aStart && bEnd < aEnd);
257 }
258 
compareObjectParamRanges(uint16 objIdx1,uint16 xAdd1,uint16 yAdd1,uint16 maskAdd1,uint16 objIdx2,uint16 xAdd2,uint16 yAdd2,uint16 maskAdd2)259 uint16 compareObjectParamRanges(uint16 objIdx1, uint16 xAdd1, uint16 yAdd1, uint16 maskAdd1, uint16 objIdx2, uint16 xAdd2, uint16 yAdd2, uint16 maskAdd2) {
260 	assert(objIdx1 < NUM_MAX_OBJECT && objIdx2 < NUM_MAX_OBJECT);
261 	const ObjectStruct &obj1 = g_cine->_objectTable[objIdx1];
262 	const ObjectStruct &obj2 = g_cine->_objectTable[objIdx2];
263 
264 	if (compareRanges(obj1.x,    obj1.x    + xAdd1,    obj2.x,    obj2.x    + xAdd2) &&
265 		compareRanges(obj1.y,    obj1.y    + yAdd1,    obj2.y,    obj2.y    + yAdd2) &&
266 		compareRanges(obj1.mask, obj1.mask + maskAdd1, obj2.mask, obj2.mask + maskAdd2)) {
267 		return kCmpEQ;
268 	} else {
269 		return 0;
270 	}
271 }
272 
compareObjectParam(byte objIdx,byte type,int16 value)273 uint16 compareObjectParam(byte objIdx, byte type, int16 value) {
274 	uint16 compareResult = 0;
275 	int16 objectParam = getObjectParam(objIdx, type);
276 
277 	if (objectParam > value) {
278 		compareResult |= kCmpGT;
279 	} else if (objectParam < value) {
280 		compareResult |= kCmpLT;
281 	} else {
282 		compareResult |= kCmpEQ;
283 	}
284 
285 	return compareResult;
286 }
287 
288 /**
289  * @bug In Operation Stealth, if you try to go downstairs to the sea in the
290  * location between bank and hotel, getObjectParam is called with paramIdx 16
291  * and crashes
292  */
getObjectParam(uint16 objIdx,uint16 paramIdx)293 int16 getObjectParam(uint16 objIdx, uint16 paramIdx) {
294 	assert(objIdx <= NUM_MAX_OBJECT);
295 
296 	paramIdx--;
297 
298 	assert(paramIdx <= 5);
299 
300 	switch (paramIdx) {
301 	case 0:
302 		return g_cine->_objectTable[objIdx].x;
303 	case 1:
304 		return g_cine->_objectTable[objIdx].y;
305 	case 2:
306 		return g_cine->_objectTable[objIdx].mask;
307 	case 3:
308 		return g_cine->_objectTable[objIdx].frame;
309 	case 4:
310 		return g_cine->_objectTable[objIdx].costume;
311 	case 5:
312 		return g_cine->_objectTable[objIdx].part;
313 	}
314 
315 	return 0;
316 }
317 
318 } // End of namespace Cine
319