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