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 "lastexpress/game/beetle.h"
24 
25 #include "lastexpress/game/inventory.h"
26 #include "lastexpress/game/logic.h"
27 #include "lastexpress/game/scenes.h"
28 #include "lastexpress/game/state.h"
29 
30 #include "lastexpress/lastexpress.h"
31 #include "lastexpress/resource.h"
32 
33 namespace LastExpress {
34 
Beetle(LastExpressEngine * engine)35 Beetle::Beetle(LastExpressEngine *engine) : _engine(engine), _data(NULL) {}
36 
~Beetle()37 Beetle::~Beetle() {
38 	SAFE_DELETE(_data);
39 
40 	// Free passed pointers
41 	_engine = NULL;
42 }
43 
load()44 void Beetle::load() {
45 	// Only load in chapter 2 & 3
46 	if (getProgress().chapter != kChapter2 &&  getProgress().chapter != kChapter3)
47 		return;
48 
49 	// Already loaded
50 	if (_data)
51 		return;
52 
53 	// Do not load if beetle is in the wrong location
54 	if (getInventory()->get(kItemBeetle)->location != kObjectLocation3)
55 		return;
56 
57 	///////////////////////
58 	// Load Beetle data
59 	_data = new BeetleData();
60 
61 	// Load sequences
62 	_data->sequences.push_back(loadSequence("BW000.seq"));        // 0
63 	_data->sequences.push_back(loadSequence("BT000045.seq"));
64 	_data->sequences.push_back(loadSequence("BT045000.seq"));
65 	_data->sequences.push_back(loadSequence("BW045.seq"));
66 	_data->sequences.push_back(loadSequence("BT045090.seq"));
67 	_data->sequences.push_back(loadSequence("BT090045.seq"));     // 5
68 	_data->sequences.push_back(loadSequence("BW090.seq"));
69 	_data->sequences.push_back(loadSequence("BT090135.seq"));
70 	_data->sequences.push_back(loadSequence("BT135090.seq"));
71 	_data->sequences.push_back(loadSequence("BW135.seq"));
72 	_data->sequences.push_back(loadSequence("BT135180.seq"));     // 10
73 	_data->sequences.push_back(loadSequence("BT180135.seq"));
74 	_data->sequences.push_back(loadSequence("BW180.seq"));
75 	_data->sequences.push_back(loadSequence("BT180225.seq"));
76 	_data->sequences.push_back(loadSequence("BT225180.seq"));
77 	_data->sequences.push_back(loadSequence("BW225.seq"));        // 15
78 	_data->sequences.push_back(loadSequence("BT225270.seq"));
79 	_data->sequences.push_back(loadSequence("BT270225.seq"));
80 	_data->sequences.push_back(loadSequence("BW270.seq"));
81 	_data->sequences.push_back(loadSequence("BT270315.seq"));
82 	_data->sequences.push_back(loadSequence("BT315270.seq"));     // 20
83 	_data->sequences.push_back(loadSequence("BW315.seq"));
84 	_data->sequences.push_back(loadSequence("BT315000.seq"));
85 	_data->sequences.push_back(loadSequence("BT000315.seq"));
86 	_data->sequences.push_back(loadSequence("BA135.seq"));
87 	_data->sequences.push_back(loadSequence("BL045.seq"));        // 25
88 	_data->sequences.push_back(loadSequence("BL000.seq"));
89 	_data->sequences.push_back(loadSequence("BL315.seq"));
90 	_data->sequences.push_back(loadSequence("BL180.seq"));
91 
92 	// Init fields
93 	_data->field_74 = 0;
94 
95 	// Check that all sequences are loaded properly
96 	_data->isLoaded = true;
97 	for (uint i = 0; i < _data->sequences.size(); i++) {
98 		if (!_data->sequences[i]->isLoaded()) {
99 			_data->isLoaded = false;
100 			break;
101 		}
102 	}
103 
104 	_data->field_D9 = 10;
105 	_data->coordOffset = 5;
106 	_data->coordY = 178;
107 	_data->currentSequence = 0;
108 	_data->offset = 0;
109 	_data->frame = NULL;
110 	_data->field_D5 = 0;
111 	_data->indexes[0] = 29;
112 	_data->field_DD = 0;
113 }
114 
unload()115 void Beetle::unload() {
116 	// Remove sequences from display list
117 	if (_data)
118 		getScenes()->removeFromQueue(_data->frame);
119 
120 	// Delete all loaded sequences
121 	SAFE_DELETE(_data);
122 }
123 
isLoaded() const124 bool Beetle::isLoaded() const {
125 	if (!_data)
126 		return false;
127 
128 	return _data->isLoaded;
129 }
130 
catchBeetle()131 bool Beetle::catchBeetle() {
132 	if (!_data)
133 		error("[Beetle::catchBeetle] Sequences have not been loaded");
134 
135 	if (getInventory()->getSelectedItem() == kItemMatchBox
136 	 && getInventory()->hasItem(kItemMatch)
137 	 && ABS((int16)(getCoords().x - _data->coordX)) < 10
138 	 && ABS((int16)(getCoords().y - _data->coordY)) < 10) {
139 		return true;
140 	}
141 
142 	_data->field_D5 = 0;
143 	move();
144 
145 	return false;
146 }
147 
isCatchable() const148 bool Beetle::isCatchable() const {
149 	if (!_data)
150 		error("[Beetle::isCatchable] Sequences have not been loaded");
151 
152 	return (_data->indexes[_data->offset] >= 30);
153 }
154 
update()155 void Beetle::update() {
156 	if (!_data)
157 		error("[Beetle::update] Sequences have not been loaded");
158 
159 	if (!_data->isLoaded)
160 		return;
161 
162 	move();
163 
164 	if (_data->field_D5)
165 		_data->field_D5--;
166 
167 	if (_data->currentSequence && _data->indexes[_data->offset] != 29) {
168 		drawUpdate();
169 		return;
170 	}
171 
172 	if (getInventory()->get(kItemBeetle)->location == kObjectLocation3) {
173 		if ((!_data->field_DD && rnd(10) < 1)
174 		  || (_data->field_DD && rnd(30) < 1)
175 		  || rnd(100) < 1) {
176 
177 			_data->field_DD++;
178 			if (_data->field_DD > 3)
179 				_data->field_DD = 0;
180 
181 			updateData(24);
182 
183 			_data->coordX = (int16)(rnd(250) + 190);
184 			_data->coordOffset = (int16)(rnd(5) + 5);
185 
186 			if (_data->field_D9 > 1)
187 				_data->field_D9--;
188 
189 			drawUpdate();
190 		}
191 	}
192 }
193 
drawUpdate()194 void Beetle::drawUpdate() {
195 	if (!_data)
196 		error("[Beetle::drawUpdate] Sequences have not been loaded");
197 
198 	if (_data->frame != NULL) {
199 		getScenes()->setCoordinates(_data->frame);
200 		getScenes()->removeFromQueue(_data->frame);
201 	}
202 
203 	// Update current frame
204 	switch (_data->indexes[_data->offset]) {
205 	default:
206 		_data->currentFrame += 10;
207 		break;
208 
209 	case 3:
210 	case 6:
211 	case 9:
212 	case 12:
213 	case 15:
214 	case 18:
215 	case 21:
216 	case 24:
217 	case 25:
218 	case 26:
219 	case 27:
220 	case 28:
221 		_data->currentFrame++;
222 		break;
223 	}
224 
225 	// Update current sequence
226 	if (_data->currentSequence->count() <= _data->currentFrame) {
227 		switch (_data->indexes[_data->offset]) {
228 		default:
229 			_data->offset++;
230 			_data->currentSequence = _data->sequences[_data->indexes[_data->offset]];
231 			break;
232 
233 		case 3:
234 		case 6:
235 		case 9:
236 		case 12:
237 		case 15:
238 		case 18:
239 		case 21:
240 			break;
241 		}
242 
243 		_data->currentFrame = 0;
244 		if (_data->indexes[_data->offset] == 29) {
245 			SAFE_DELETE(_data->frame);
246 			_data->currentSequence = NULL; // pointer to existing sequence
247 			return;
248 		}
249 	}
250 
251 	// Update coordinates
252 	switch (_data->indexes[_data->offset]) {
253 	default:
254 		break;
255 
256 	case 0:
257 		_data->coordY -= _data->coordOffset;
258 		break;
259 
260 	case 3:
261 		_data->coordX += _data->coordOffset;
262 		_data->coordY -= _data->coordOffset;
263 		break;
264 
265 	case 6:
266 		_data->coordX += _data->coordOffset;
267 		break;
268 
269 	case 9:
270 		_data->coordX += _data->coordOffset;
271 		_data->coordY += _data->coordOffset;
272 		break;
273 
274 	case 12:
275 		_data->coordY += _data->coordOffset;
276 		break;
277 
278 	case 15:
279 		_data->coordX -= _data->coordOffset;
280 		_data->coordY += _data->coordOffset;
281 		break;
282 
283 	case 18:
284 		_data->coordX -= _data->coordOffset;
285 		break;
286 
287 	case 21:
288 		_data->coordX -= _data->coordOffset;
289 		_data->coordY -= _data->coordOffset;
290 		break;
291 	}
292 
293 	// Update beetle data
294 	int rnd = rnd(100);
295 	if (_data->coordX < 165 || _data->coordX > 465) {
296 		uint index = 0;
297 
298 		if (rnd >= 30) {
299 			if (rnd >= 70)
300 				index = (_data->coordX < 165) ? 9 : 15;
301 			else
302 				index = (_data->coordX < 165) ? 6 : 18;
303 		} else {
304 			index = (_data->coordX < 165) ? 3 : 21;
305 		}
306 
307 		updateData(index);
308 	}
309 
310 	if (_data->coordY < 178) {
311 		switch (_data->indexes[_data->offset]) {
312 		default:
313 			updateData(26);
314 			break;
315 
316 		case 3:
317 			updateData(25);
318 			break;
319 
320 		case 21:
321 			updateData(27);
322 			break;
323 		}
324 	}
325 
326 	if (_data->coordY > 354) {
327 		switch (_data->indexes[_data->offset]) {
328 		default:
329 			break;
330 
331 		case 9:
332 		case 12:
333 		case 15:
334 			updateData(28);
335 			break;
336 		}
337 	}
338 
339 	// Invert direction
340 	invertDirection();
341 
342 	SequenceFrame *frame = new SequenceFrame(_data->currentSequence, (uint16)_data->currentFrame);
343 	updateFrame(frame);
344 
345 	invertDirection();
346 
347 	getScenes()->addToQueue(frame);
348 
349 	SAFE_DELETE(_data->frame);
350 	_data->frame = frame;
351 }
352 
invertDirection()353 void Beetle::invertDirection() {
354 	if (!_data)
355 		error("[Beetle::invertDirection] Sequences have not been loaded");
356 
357 	switch (_data->indexes[_data->offset]) {
358 	default:
359 		break;
360 
361 	case 24:
362 	case 25:
363 	case 26:
364 	case 27:
365 	case 28:
366 		_data->coordY = -_data->coordY;
367 		break;
368 	}
369 }
370 
move()371 void Beetle::move() {
372 	if (!_data)
373 		error("[Beetle::move] Sequences have not been loaded");
374 
375 	if (_data->indexes[_data->offset] >= 24 && _data->indexes[_data->offset] <= 29)
376 		return;
377 
378 	if (_data->field_D5)
379 		return;
380 
381 	if (ABS((int)(getCoords().x - _data->coordX)) > 35)
382 		return;
383 
384 	if (ABS((int)(getCoords().y - _data->coordY)) > 35)
385 		return;
386 
387 	int32 deltaX = getCoords().x - _data->coordX;
388 	int32 deltaY = -getCoords().y - _data->coordY;
389 	uint32 index = 0;
390 
391 	// FIXME: check code path
392 	if (deltaX >= 0) {
393 		if (deltaY > 0) {
394 			if (100 * deltaY - 241 * deltaX <= 0) {
395 				if (100 * deltaY  - 41 * deltaX <= 0)
396 					index = 18;
397 				else
398 					index = 15;
399 			} else {
400 				index = 12;
401 			}
402 
403 			goto update_data;
404 		}
405 	}
406 
407 	if (deltaX < 0) {
408 
409 		if (deltaY > 0) {
410 			if (100 * deltaY + 241 * deltaX <= 0) {
411 				if (100 * deltaY + 41 * deltaX <= 0)
412 					index = 6;
413 				else
414 					index = 9;
415 			} else {
416 				index = 12;
417 			}
418 
419 			goto update_data;
420 		}
421 
422 		if (deltaY <= 0) {
423 			if (100 * deltaY - 41 * deltaX <= 0) {
424 				if (100 * deltaY - 241 * deltaX <= 0)
425 					index = 0;
426 				else
427 					index = 3;
428 			} else {
429 				index = 6;
430 			}
431 
432 			goto update_data;
433 		}
434 	}
435 
436 update_data:
437 	updateData(index);
438 
439 	if (_data->coordOffset >= 15) {
440 		_data->field_D5 = 0;
441 		return;
442 	}
443 
444 	_data->coordOffset = _data->coordOffset + (int16)(4 * rnd(100)/100 + _data->field_D9);
445 	_data->field_D5 = 0;
446 }
447 
448 // Update the beetle sequence to show the correct frames in the correct place
updateFrame(SequenceFrame * frame) const449 void Beetle::updateFrame(SequenceFrame *frame) const {
450 	if (!_data)
451 		error("[Beetle::updateFrame] Sequences have not been loaded");
452 
453 	if (!frame)
454 		return;
455 
456 	// Update coordinates
457 	if (_data->coordX > 0)
458 		frame->getInfo()->xPos1 = (uint16)_data->coordX;
459 
460 	if (_data->coordY > 0)
461 		frame->getInfo()->yPos1 = (uint16)_data->coordY;
462 }
463 
updateData(uint32 index)464 void Beetle::updateData(uint32 index) {
465 	if (!_data)
466 		error("[Beetle::updateData] Sequences have not been loaded");
467 
468 	if (!_data->isLoaded)
469 		return;
470 
471 	if (index == 25 || index == 26 || index == 27 || index == 28) {
472 		_data->indexes[0] = index;
473 		_data->indexes[1] = 29;
474 		_data->offset = 0;
475 
476 		_data->currentSequence = _data->sequences[index];
477 		_data->currentFrame = 0;
478 		_data->index = index;
479 	} else {
480 		if (!_data->sequences[index])
481 			return;
482 
483 		if (_data->index == index)
484 			return;
485 
486 		_data->offset = 0;
487 
488 		// Special case for sequence 24
489 		if (index == 24) {
490 			_data->indexes[0] = index;
491 			_data->coordY = 178;
492 			_data->index = _data->indexes[1];
493 			_data->indexes[1] = (_data->coordX >= 265) ? 15 : 9;
494 			_data->currentFrame = 0;
495 			_data->currentSequence = _data->sequences[index];
496 		} else {
497 			if (index <= _data->index) {
498 				for (uint32 i = _data->index - 1; i > index; ++_data->offset) {
499 					_data->indexes[_data->offset] = i;
500 					i -= 3;
501 				}
502 			} else {
503 				for (uint32 i = _data->index + 1; i < index; ++_data->offset) {
504 					_data->indexes[_data->offset] = i;
505 					i += 3;
506 				}
507 			}
508 
509 			_data->index = index;
510 			_data->indexes[_data->offset] = index;
511 			_data->currentFrame = 0;
512 			_data->offset = 0;
513 			_data->currentSequence = _data->sequences[_data->indexes[0]];
514 		}
515 	}
516 }
517 
518 } // End of namespace LastExpress
519