1 /************************************************************************
2 * *
3 * FreeSynd - a remake of the classic Bullfrog game "Syndicate". *
4 * *
5 * Copyright (C) 2005 Stuart Binge <skbinge@gmail.com> *
6 * Copyright (C) 2005 Joost Peters <joostp@users.sourceforge.net> *
7 * Copyright (C) 2006 Trent Waddington <qg@biodome.org> *
8 * Copyright (C) 2006 Tarjei Knapstad <tarjei.knapstad@gmail.com> *
9 * Copyright (C) 2010 Bohdan Stelmakh <chamel@users.sourceforge.net> *
10 * Copyright (C) 2016 Benoit Blancard <benblan@users.sourceforge.net>*
11 * *
12 * This program is free software; you can redistribute it and / or *
13 * modify it under the terms of the GNU General Public License as *
14 * published by the Free Software Foundation; either version 2 of the *
15 * License, or (at your option) any later version. *
16 * *
17 * This program is distributed in the hope that it will be useful, *
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
20 * General Public License for more details. *
21 * *
22 * You can view the GNU General Public License, online, at the GNU *
23 * project's web site; see <http://www.gnu.org/licenses/gpl.html>. *
24 * The full text of the license is also included in the file COPYING. *
25 * *
26 ************************************************************************/
27
28 #include "common.h"
29 #include "utils/log.h"
30 #include "app.h"
31 #include "vehicle.h"
32 #include "core/gamesession.h"
33 #include "mission.h"
34
35 uint16 SFXObject::sfxIdCnt = 0;
36 const int Static::kStaticOrientation1 = 0;
37 const int Static::kStaticOrientation2 = 2;
38
MapObject(uint16 anId,int m,ObjectNature aNature)39 MapObject::MapObject(uint16 anId, int m, ObjectNature aNature):
40 size_x_(1), size_y_(1), size_z_(2),
41 map_(m), frame_(0), elapsed_carry_(0),
42 frames_per_sec_(8),
43 dir_(0),
44 time_show_anim_(-1), time_showing_anim_(-1),
45 is_frame_drawn_(false),
46 state_(0xFFFFFFFF)
47 {
48 nature_ = aNature;
49 id_ = anId;
50 }
51
natureName()52 const char* MapObject::natureName() {
53 switch (nature_) {
54 case kNaturePed:
55 return "Ped";
56 case kNatureWeapon:
57 return "Weapon";
58 case kNatureStatic:
59 return "Static";
60 case kNatureVehicle:
61 return "Vehicle";
62 default:
63 return "Undefined";
64 }
65 }
66
setOffX(int n)67 void MapObject::setOffX(int n)
68 {
69 pos_.ox = n;
70 while (pos_.ox < 0) {
71 pos_.ox += 256;
72 pos_.tx--;
73 }
74 while (pos_.ox > 255) {
75 pos_.ox -= 256;
76 pos_.tx++;
77 }
78 }
79
setOffY(int n)80 void MapObject::setOffY(int n)
81 {
82 pos_.oy = n;
83 while (pos_.oy < 0) {
84 pos_.oy += 256;
85 pos_.ty--;
86 }
87 while (pos_.oy > 255) {
88 pos_.oy -= 256;
89 pos_.ty++;
90 }
91 }
92
setOffZ(int n)93 void MapObject::setOffZ(int n)
94 {
95 pos_.oz = n;
96 while (pos_.oz < 0) {
97 pos_.oz += 128;
98 pos_.tz--;
99 }
100 while (pos_.oz > 127) {
101 pos_.oz -= 128;
102 pos_.tz++;
103 }
104 }
105
addOffs(int & x,int & y)106 void MapObject::addOffs(int &x, int &y)
107 {
108 x += ((pos_.ox - pos_.oy) * (TILE_WIDTH / 2)) / 256;
109 y += ((pos_.ox + pos_.oy) * (TILE_HEIGHT / 3)) / 256;
110 y -= (pos_.oz * (TILE_HEIGHT / 3)) / 128;
111 }
112
animate(int elapsed)113 bool MapObject::animate(int elapsed)
114 {
115 int frame_tics_ = 1000 / frames_per_sec_;
116 int total_elapsed = elapsed + elapsed_carry_;
117 elapsed_carry_ = total_elapsed % frame_tics_;
118 int framewas = frame_;
119 bool changed = true;
120 frame_ += (total_elapsed / frame_tics_);
121 if (framewas == frame_)
122 changed = false;
123 frame_ %= frames_per_sec_ << 3;
124 return changed;
125 }
126
setDirection(int dir)127 void MapObject::setDirection(int dir) {
128 assert(dir >= 0);
129 dir_ = dir;
130 }
131
132 /* NOTE posx = targetx - objx
133 * posy = targety - objy
134 * if dir == NULL, object callers dir_ will be set
135 */
setDirection(int posx,int posy,int * dir)136 void MapObject::setDirection(int posx, int posy, int * dir) {
137
138 int direction = -1;
139
140 if (posx == 0) {
141 if (posy < 0) {
142 direction = 128;
143 } else if (posy > 0) {
144 direction = 0;
145 }
146 } else if (posy == 0) {
147 if (posx > 0) {
148 direction = 64;
149 } else if (posx < 0) {
150 direction = 192;
151 }
152 } else if (posx < 0) {
153 if (posy > 0) {
154 posx = -posx;
155 direction = (int)((128.0
156 * atan((double)posy / (double)posx)) / PI + 192.0);
157 } else { // posy < 0
158 int swapx = -posx;
159 posx = -posy;
160 posy = swapx;
161 direction = (int)((128.0
162 * atan((double)posy / (double)posx)) / PI + 128.0);
163 }
164 } else if (posx > 0 && posy < 0) {
165 posy = -posy;
166 direction = (int)((128.0
167 * atan((double)posy / (double)posx)) / PI + 64.0);
168 } else { // posx > 0 && posy > 0
169 int swapx = posx;
170 posx = posy;
171 posy = swapx;
172 direction = (int)((128.0
173 * atan((double)posy / (double)posx)) / PI);
174 }
175 if (direction != -1) {
176 if (dir == NULL)
177 dir_ = direction;
178 else
179 *dir = direction;
180 }
181 }
182
setDirectionTowardObject(const MapObject & object)183 void MapObject::setDirectionTowardObject(const MapObject &object) {
184 WorldPoint objectPos(object.position());
185
186 this->setDirectionTowardPosition(objectPos);
187 }
188
setDirectionTowardPosition(const WorldPoint & pos)189 void MapObject::setDirectionTowardPosition(const WorldPoint &pos) {
190 WorldPoint thisPedPos(position());
191
192 this->setDirection(pos.x - thisPedPos.x, pos.y - thisPedPos.y);
193 }
194
195 /*!
196 * @returns direction for selected number of surfaces
197 */
getDirection(int snum)198 int MapObject::getDirection(int snum) {
199 assert(snum > 0);
200
201 int direction = 0;
202 int sinc = 256 / snum;
203 int sdec = sinc / 2;
204 do {
205 int s = direction * sinc;
206 if (direction == 0) {
207 if ((256 - sdec) <= dir_ || (s + sdec) > dir_)
208 break;
209 } else if ((s - sdec) <= dir_ && (s + sdec) > dir_)
210 break;
211 direction++;
212 } while (direction < snum);
213 assert(direction < snum);
214
215 return direction;
216 }
217
218 /*
219 * NOTE: inc_xyz should point to array of three elements of type
220 * double for x,y,z
221 */
isBlocker(WorldPoint * pStartPt,WorldPoint * pEndPt,double * inc_xyz)222 bool MapObject::isBlocker(WorldPoint * pStartPt, WorldPoint * pEndPt,
223 double * inc_xyz)
224 {
225 // TODO: better set values of size for object, use to values
226 // for single coordinate, rel_x_start(-vlaue) and rel_x_end(+value).
227 // Vehicle and other objects with different directions will be handleDamage
228 // correctly
229
230 /* NOTE: algorithm, checks whether object is located within range
231 * defined by "start" and "end", then we calculate distances from x, y, z
232 * to their respective pStartPt, choose shortest, then longest between
233 * them and recalculate position of entering of shot and exit point
234 */
235
236 // range_x check
237 int range_x_h = pos_.tx * 256 + pos_.ox;
238 int range_x_l = range_x_h - size_x_;
239 range_x_h += size_x_;
240 range_x_h--;
241 int low_num = pStartPt->x;
242 int high_num = pEndPt->x;
243 bool flipped_x = false;
244 if (pStartPt->x > pEndPt->x) {
245 high_num = pStartPt->x;
246 low_num = pEndPt->x;
247 flipped_x = true;
248 }
249 if (range_x_l > high_num || range_x_h < low_num)
250 return false;
251
252 // range_y check
253 int range_y_h = pos_.ty * 256 + pos_.oy;
254 int range_y_l = range_y_h - size_y_;
255 range_y_h += size_y_;
256 range_y_h--;
257 bool flipped_y = false;
258 if (pStartPt->y > pEndPt->y) {
259 high_num = pStartPt->y;
260 low_num = pEndPt->y;
261 flipped_y = true;
262 } else {
263 low_num = pStartPt->y;
264 high_num = pEndPt->y;
265 }
266 if (range_y_l > high_num || range_y_h < low_num)
267 return false;
268
269 // range_z check
270 int range_z_l = pos_.tz * 128 + pos_.oz;
271 int range_z_h = range_z_l + size_z_;
272 range_z_h--;
273 bool flipped_z = false;
274 if (pStartPt->z > pEndPt->z) {
275 high_num = pStartPt->z;
276 low_num = pEndPt->z;
277 flipped_z = true;
278 } else {
279 low_num = pStartPt->z;
280 high_num = pEndPt->z;
281 }
282 if (range_z_l > high_num || range_z_h < low_num)
283 return false;
284
285 double d_l[3];
286 double d_h[3];
287 if (inc_xyz[0] != 0) {
288 d_l[0] = ((double)(range_x_l - pStartPt->x)) / inc_xyz[0];
289 d_h[0] = ((double)(range_x_h - pStartPt->x)) / inc_xyz[0];
290 } else {
291 d_l[0] = 0.0;
292 d_h[0] = 0.0;
293 }
294 if (inc_xyz[1] != 0) {
295 d_l[1] = ((double)(range_y_l - pStartPt->y)) / inc_xyz[1];
296 d_h[1] = ((double)(range_y_h - pStartPt->y)) / inc_xyz[1];
297 } else {
298 d_l[1] = 0.0;
299 d_h[1] = 0.0;
300 }
301 if (inc_xyz[0] != 0) {
302 d_l[2] = ((double)(range_z_l - pStartPt->z)) / inc_xyz[2];
303 d_h[2] = ((double)(range_z_h - pStartPt->z)) / inc_xyz[2];
304 } else {
305 d_l[2] = 0.0;
306 d_h[2] = 0.0;
307 }
308
309 // shortest distances to starting point
310 double d_s[3];
311 if (d_l[0] > d_h[0])
312 d_s[0] = d_h[0];
313 else
314 d_s[0] = d_l[0];
315
316 if (d_l[1] > d_h[1])
317 d_s[1] = d_h[1];
318 else
319 d_s[1] = d_l[1];
320
321 if (d_l[2] > d_h[2])
322 d_s[2] = d_h[2];
323 else
324 d_s[2] = d_l[2];
325
326 // TODO: another look at this function later
327 uint8 indx = 0;
328 // longest non-zero distance to start
329 if (d_s[0] != 0.0) {
330 if (d_s[1] != 0) {
331 if (d_s[0] > d_s[1]) {
332 if (d_s[2] != 0.0 && d_s[0] < d_s[2])
333 indx = 2;
334 } else {
335 indx = 1;
336 if (d_s[2] != 0.0 && d_s[1] < d_s[2])
337 indx = 2;
338 }
339 }
340 } else if (d_s[1] != 0) {
341 indx = 1;
342 if (d_s[2] != 0.0) {
343 if (d_s[1] < d_s[2])
344 indx = 2;
345 }
346 } else
347 indx = 2;
348
349 int range_g_l = (int)(d_l[indx] * inc_xyz[0] + pStartPt->x);
350 int range_g_h = (int)(d_h[indx] * inc_xyz[0] + pStartPt->x);
351 if (range_g_h < range_g_l) {
352 low_num = range_g_h;
353 high_num = range_g_l;
354 } else {
355 low_num = range_g_l;
356 high_num = range_g_h;
357 }
358 if (low_num > range_x_h || high_num < range_x_l)
359 return false;
360 if (range_x_l < low_num)
361 range_x_l = low_num;
362 if (range_x_h > high_num)
363 range_x_h = high_num;
364
365 range_g_l = (int)(d_l[indx] * inc_xyz[1] + pStartPt->y);
366 range_g_h = (int)(d_h[indx] * inc_xyz[1] + pStartPt->y);
367 if (range_g_h < range_g_l) {
368 low_num = range_g_h;
369 high_num = range_g_l;
370 } else {
371 low_num = range_g_l;
372 high_num = range_g_h;
373 }
374 if (low_num > range_y_h || high_num < range_y_l)
375 return false;
376 if (range_y_l < low_num)
377 range_y_l = low_num;
378 if (range_y_h > high_num)
379 range_y_h = high_num;
380
381 range_g_l = (int)(d_l[indx] * inc_xyz[2] + pStartPt->z);
382 range_g_h = (int)(d_h[indx] * inc_xyz[2] + pStartPt->z);
383 if (range_g_h < range_g_l) {
384 low_num = range_g_h;
385 high_num = range_g_l;
386 } else {
387 low_num = range_g_l;
388 high_num = range_g_h;
389 }
390 if (low_num > range_z_h || high_num < range_z_l)
391 return false;
392 if (range_z_l < low_num)
393 range_z_l = low_num;
394 if (range_z_h > high_num)
395 range_z_h = high_num;
396
397 // restoring coordinates to their respective low/high values
398 if (flipped_x) {
399 pStartPt->x = range_x_h;
400 pEndPt->x = range_x_l;
401 } else {
402 pStartPt->x = range_x_l;
403 pEndPt->x = range_x_h;
404 }
405
406 if (flipped_y) {
407 pStartPt->y = range_y_h;
408 pEndPt->y = range_y_l;
409 } else {
410 pStartPt->y = range_y_l;
411 pEndPt->y = range_y_h;
412 }
413
414 if (flipped_z) {
415 pStartPt->z = range_z_h;
416 pEndPt->z = range_z_l;
417 } else {
418 pStartPt->z = range_z_l;
419 pEndPt->z = range_z_h;
420 }
421
422 return true;
423 }
424
offzOnStairs(uint8 twd)425 void MapObject::offzOnStairs(uint8 twd) {
426 switch (twd) {
427 case 0x01:
428 pos_.oz = 127 - (pos_.oy >> 1);
429 break;
430 case 0x02:
431 pos_.oz = pos_.oy >> 1;
432 break;
433 case 0x03:
434 pos_.oz = pos_.ox >> 1;
435 break;
436 case 0x04:
437 pos_.oz = 127 - (pos_.ox >> 1);
438 break;
439 default:
440 pos_.oz = 0;
441 break;
442 }
443 }
444
445 /*!
446 * Constructor of the class.
447 * \param m Map id
448 * \param type Type of SfxObject (see SFXObject::SfxTypeEnum)
449 * \param t_show
450 * \param managed True means object is destroyed by another object than Mission
451 */
SFXObject(int m,SfxTypeEnum type,int t_show,bool managed)452 SFXObject::SFXObject(int m, SfxTypeEnum type, int t_show, bool managed) : MapObject(sfxIdCnt++, m, kNatureUndefined) {
453 type_ = type;
454 managed_ = managed;
455 draw_all_frames_ = true;
456 loopAnimation_ = false;
457 setTimeShowAnim(0);
458 reset();
459 switch(type) {
460 case SFXObject::sfxt_Unknown:
461 FSERR(Log::k_FLG_UI, "SFXObject", "SFXObject", ("Sfx object of type Unknown created"));
462 sfx_life_over_ = true;
463 break;
464 case SFXObject::sfxt_BulletHit:
465 anim_ = 382;
466 break;
467 case SFXObject::sfxt_FlamerFire:
468 anim_ = 383;
469 setFramesPerSec(12);
470 break;
471 case SFXObject::sfxt_Smoke:
472 anim_ = 244;
473 break;
474 case SFXObject::sfxt_Fire_LongSmoke:
475 // point of impact for laser
476 anim_ = 389;
477 break;
478 case SFXObject::sfxt_ExplosionFire:
479 anim_ = 390;
480 setFramesPerSec(6);
481 break;
482 case SFXObject::sfxt_ExplosionBall:
483 anim_ = 391;
484 setFramesPerSec(6);
485 break;
486 case SFXObject::sfxt_LargeFire:
487 anim_ = 243;
488 setTimeShowAnim(3000 + t_show);
489 break;
490 case SFXObject::sfxt_SelArrow:
491 anim_ = 601;
492 time_show_anim_ = -1;
493 setFramesPerSec(6);
494 break;
495 case SFXObject::sfxt_AgentFirst:
496 anim_ = 1951;
497 time_show_anim_ = -1;
498 setFramesPerSec(4);
499 break;
500 case SFXObject::sfxt_AgentSecond:
501 anim_ = 1952;
502 time_show_anim_ = -1;
503 setFramesPerSec(4);
504 break;
505 case SFXObject::sfxt_AgentThird:
506 anim_ = 1953;
507 time_show_anim_ = -1;
508 setFramesPerSec(4);
509 break;
510 case SFXObject::sfxt_AgentFourth:
511 anim_ = 1954;
512 time_show_anim_ = -1;
513 setFramesPerSec(4);
514 break;
515 }
516 }
517
draw(int x,int y)518 void SFXObject::draw(int x, int y) {
519 addOffs(x, y);
520 g_App.gameSprites().drawFrame(anim_, frame_, x, y);
521 }
522
animate(int elapsed)523 bool SFXObject::animate(int elapsed) {
524
525 if (is_frame_drawn_) {
526 bool changed = draw_all_frames_ ? MapObject::animate(elapsed) : false;
527 if (type_ == SFXObject::sfxt_ExplosionBall) {
528 int z = pos_.tz * 128 + pos_.oz;
529 // 250 per sec
530 z += ((elapsed + elapsed_left_) >> 2);
531 elapsed_left_ = elapsed &3;
532 if (z > (g_Session.getMission()->mmax_z_ - 1) * 128)
533 z = (g_Session.getMission()->mmax_z_ - 1) * 128;
534 pos_.tz = z / 128;
535 pos_.oz = z % 128;
536 }
537 if (frame_ > g_App.gameSprites().lastFrame(anim_)
538 && !leftTimeShowAnim(elapsed))
539 {
540 if (loopAnimation_) {
541 reset();
542 } else {
543 sfx_life_over_ = true;
544 }
545 }
546 return changed;
547 }
548 is_frame_drawn_ = true;
549 return false;
550 }
551
correctZ()552 void SFXObject::correctZ() {
553 if (type_ == SFXObject::sfxt_ExplosionBall) {
554 int z = pos_.tz * 128 + pos_.oz;
555 z += 512;
556 if (z > (g_Session.getMission()->mmax_z_ - 1) * 128)
557 z = (g_Session.getMission()->mmax_z_ - 1) * 128;
558 pos_.tz = z / 128;
559 pos_.oz = z % 128;
560 }
561 }
562
reset()563 void SFXObject::reset() {
564 sfx_life_over_ = false;
565 frame_ = 0;
566 elapsed_left_ = 0;
567 }
568
ShootableMapObject(uint16 anId,int m,ObjectNature aNature)569 ShootableMapObject::ShootableMapObject(uint16 anId, int m, ObjectNature aNature):
570 MapObject(anId, m, aNature)
571 {}
572
ShootableMovableMapObject(uint16 anId,int m,ObjectNature aNature)573 ShootableMovableMapObject::ShootableMovableMapObject(uint16 anId, int m, ObjectNature aNature):
574 ShootableMapObject(anId, m, aNature) {
575 speed_ = 0;
576 base_speed_ = 0;
577 dist_to_pos_ = 0;
578 }
579
580 /*!
581 * This method adds the given offsets to the object's offX and offY
582 * and moves it to a new tile if necessary.
583 * \param nOffX amount to add to offX
584 * \param nOffY amount to add to offY
585 */
updatePlacement(int nOffX,int nOffY)586 bool ShootableMovableMapObject::updatePlacement(int nOffX, int nOffY)
587 {
588
589 pos_.ox = nOffX;
590 pos_.oy = nOffY;
591 bool changed = false;
592
593 while (pos_.ox < 0) {
594 pos_.ox += 256;
595 pos_.tx--;
596 changed = true;
597 }
598 while (pos_.ox > 255) {
599 pos_.ox -= 256;
600 pos_.tx++;
601 changed = true;
602 }
603 while (pos_.oy < 0) {
604 pos_.oy += 256;
605 pos_.ty--;
606 changed = true;
607 }
608 while (pos_.oy > 255) {
609 pos_.oy -= 256;
610 pos_.ty++;
611 changed = true;
612 }
613
614 return changed;
615 }
616
loadInstance(uint8 * data,uint16 id,int m)617 Static *Static::loadInstance(uint8 * data, uint16 id, int m)
618 {
619 LevelData::Statics * gamdata =
620 (LevelData::Statics *) data;
621 Static *s = NULL;
622
623 // TODO: verify whether object description is correct
624 // subtype for doors, use instead orientation?
625
626 // NOTE: objects states are usually mixed with type,
627 // no separation between object type and its state
628 uint16 curanim = READ_LE_UINT16(gamdata->index_current_anim);
629 uint16 baseanim = READ_LE_UINT16(gamdata->index_base_anim);
630 uint16 curframe = READ_LE_UINT16(gamdata->index_current_frame);
631 switch(gamdata->sub_type) {
632 case 0x01:
633 // phone booth
634 s = new EtcObj(id, m, curanim, curanim, curanim);
635 s->setSizeX(128);
636 s->setSizeY(128);
637 s->setSizeZ(128);
638 break;
639 case 0x05:// 1040-1043, 1044 - damaged
640 // crossroad things
641 s = new Semaphore(id, m, 1040, 1044);
642 s->setSizeX(48);
643 s->setSizeY(48);
644 s->setSizeZ(48);
645 s->state_ = sttsem_Stt0;
646 s->setHealth(1);
647 s->setStartHealth(1);
648 break;
649 case 0x06:
650 // crossroad things
651 s = new Semaphore(id, m, 1040, 1044);
652 s->setSizeX(48);
653 s->setSizeY(48);
654 s->setSizeZ(48);
655 s->state_ = sttsem_Stt1;
656 s->setHealth(1);
657 s->setStartHealth(1);
658 break;
659 case 0x07:
660 // crossroad things
661 s = new Semaphore(id, m, 1040, 1044);
662 s->setSizeX(48);
663 s->setSizeY(48);
664 s->setSizeZ(48);
665 s->state_ = sttsem_Stt2;
666 s->setHealth(1);
667 s->setStartHealth(1);
668 break;
669 case 0x08:
670 // crossroad things
671 s = new Semaphore(id, m, 1040, 1044);
672 s->setSizeX(48);
673 s->setSizeY(48);
674 s->setSizeZ(48);
675 s->state_ = sttsem_Stt3;
676 s->setHealth(1);
677 s->setStartHealth(1);
678 break;
679 case 0x0B:
680 // 0x0270 animation, is this object present in original game?
681 //s = new EtcObj(m, curanim, curanim, curanim);
682 //printf("0x0B anim %X\n", curanim);
683 break;
684 case 0x0A:
685 s = new NeonSign(id, m, curanim);
686 s->setFrame(g_App.gameSprites().getFrameFromFrameIndx(curframe));
687 s->setExcludedFromBlockers(true);
688 s->setSizeX(32);
689 s->setSizeY(1);
690 s->setSizeZ(48);
691 break;
692 case 0x0C: // closed door
693 if (gamdata->orientation == 0x00 || gamdata->orientation == 0x80
694 || gamdata->orientation == 0x7E || gamdata->orientation == 0xFE) {
695 s = new Door(id, m, baseanim, baseanim + 2, baseanim + 4, baseanim + 6);
696 s->setOrientation(kStaticOrientation1);
697 s->setSizeX(256);
698 s->setSizeY(1);
699 s->setSizeZ(196);
700 } else {
701 baseanim++;
702 s = new Door(id, m, baseanim, baseanim + 2, baseanim + 4, baseanim + 6);
703 s->setOrientation(kStaticOrientation2);
704 s->setSizeX(1);
705 s->setSizeY(256);
706 s->setSizeZ(196);
707 }
708 break;
709 case 0x0D: // closed door
710 if (gamdata->orientation == 0x00 || gamdata->orientation == 0x80
711 || gamdata->orientation == 0x7E || gamdata->orientation == 0xFE) {
712 s = new Door(id, m, baseanim, baseanim + 2, baseanim + 4, baseanim + 6);
713 s->setOrientation(kStaticOrientation1);
714 s->setSizeX(256);
715 s->setSizeY(1);
716 s->setSizeZ(196);
717 } else {
718 baseanim++;
719 s = new Door(id, m, baseanim, baseanim + 2, baseanim + 4, baseanim + 6);
720 s->setOrientation(kStaticOrientation2);
721 s->setSizeX(1);
722 s->setSizeY(256);
723 s->setSizeZ(196);
724 }
725 break;
726 case 0x0E: // opening doors, not open
727 if (gamdata->orientation == 0x00 || gamdata->orientation == 0x80
728 || gamdata->orientation == 0x7E || gamdata->orientation == 0xFE) {
729 s = new Door(id, m, baseanim, baseanim + 2, baseanim + 4, baseanim + 6);
730 s->setOrientation(kStaticOrientation1);
731 s->setSizeX(256);
732 s->setSizeY(1);
733 s->setSizeZ(196);
734 } else {
735 baseanim++;
736 s = new Door(id, m, baseanim, baseanim + 2, baseanim + 4, baseanim + 6);
737 s->setOrientation(kStaticOrientation2);
738 s->setSizeX(1);
739 s->setSizeY(256);
740 s->setSizeZ(196);
741 }
742 s->state_ = sttdoor_Opening;
743 break;
744 case 0x0F: // opening doors, not open
745 if (gamdata->orientation == 0x00 || gamdata->orientation == 0x80
746 || gamdata->orientation == 0x7E || gamdata->orientation == 0xFE) {
747 s = new Door(id, m, baseanim, baseanim + 2, baseanim + 4, baseanim + 6);
748 s->setOrientation(kStaticOrientation1);
749 s->setSizeX(256);
750 s->setSizeY(1);
751 s->setSizeZ(196);
752 } else {
753 baseanim++;
754 s = new Door(id, m, baseanim, baseanim + 2, baseanim + 4, baseanim + 6);
755 s->setOrientation(kStaticOrientation2);
756 s->setSizeX(1);
757 s->setSizeY(256);
758 s->setSizeZ(196);
759 }
760 s->state_ = sttdoor_Opening;
761 break;
762 case 0x11:
763 // ???? what is this
764 //s = new EtcObj(m, bas, curanim, curanim);
765 //printf("0x11 anim %X\n", curanim);
766 break;
767 case 0x12:
768 // open window
769 s = new WindowObj(id, m, curanim - 2, curanim, curanim + 2, curanim + 4);
770 if (gamdata->orientation == 0x00 || gamdata->orientation == 0x80) {
771 s->setOrientation(kStaticOrientation1);
772 s->setSizeX(96);
773 s->setSizeY(4);
774 s->setSizeZ(96);
775 } else {
776 s->setOrientation(kStaticOrientation2);
777 s->setSizeX(4);
778 s->setSizeY(96);
779 s->setSizeZ(96);
780 }
781 s->setHealth(1);
782 s->setStartHealth(1);
783 s->state_ = Static::sttwnd_Open;
784 break;
785 case 0x13:
786 // closed window
787 s = new WindowObj(id, m, curanim, curanim + 2, curanim + 4, curanim + 6);
788 if (gamdata->orientation == 0x00 || gamdata->orientation == 0x80) {
789 s->setOrientation(kStaticOrientation1);
790 s->setSizeX(96);
791 s->setSizeY(4);
792 s->setSizeZ(96);
793 } else {
794 s->setOrientation(kStaticOrientation2);
795 s->setSizeX(4);
796 s->setSizeY(96);
797 s->setSizeZ(96);
798 }
799 s->setHealth(1);
800 s->setStartHealth(1);
801 s->state_ = Static::sttwnd_Closed;
802 break;
803 case 0x15:
804 // damaged window
805 s = new WindowObj(id, m, curanim - 6, curanim - 4, curanim - 2, curanim);
806 s->setExcludedFromBlockers(true);
807 s->setHealth(0);
808 s->setStartHealth(1);
809 s->state_ = Static::sttwnd_Damaged;
810 break;
811 case 0x16:
812 // TODO: set state if damaged trees exist
813 s = new Tree(id, m, curanim, curanim + 1, curanim + 2);
814 s->setSizeX(64);
815 s->setSizeY(64);
816 s->setSizeZ(256);
817 s->setHealth(1);
818 s->setStartHealth(1);
819 break;
820 case 0x19:
821 // trash bin
822 s = new EtcObj(id, m, curanim, curanim, curanim);
823 s->setSizeX(64);
824 s->setSizeY(64);
825 s->setSizeZ(96);
826 break;
827 case 0x1A:
828 // mail box
829 s = new EtcObj(id, m, curanim, curanim, curanim);
830 s->setSizeX(64);
831 s->setSizeY(64);
832 s->setSizeZ(96);
833 break;
834 case 0x1C:
835 // ???? what is this?
836 //s = new EtcObj(m, curanim, curanim, curanim);
837 //printf("0x1C anim %X\n", curanim);
838 break;
839 case 0x1F:
840 // advertisement on wall
841 s = new EtcObj(id, m, curanim, curanim, curanim, smt_Advertisement);
842 s->setExcludedFromBlockers(true);
843 break;
844
845 case 0x20:
846 // window without light
847 s = new AnimWindow(id, m, curanim);
848 s->setStateMasks(sttawnd_LightOff);
849 s->setTimeShowAnim(30000 + (rand() % 30000));
850 break;
851 case 0x21:
852 // window light turns on
853 s = new AnimWindow(id, m, curanim - 2);
854 s->setTimeShowAnim(1000 + (rand() % 1000));
855 s->setStateMasks(sttawnd_LightSwitching);
856
857 // NOTE : 0x22 should have existed but it doesn't appear anywhere
858
859 case 0x23:
860 // window with person's shadow non animated,
861 // even though on 1 map person appears I will ignore it
862 s = new AnimWindow(id, m, 1959 + ((gamdata->orientation & 0x40) >> 5));
863 s->setStateMasks(sttawnd_ShowPed);
864 s->setTimeShowAnim(15000 + (rand() % 5000));
865 break;
866 case 0x24:
867 // window with person's shadow, hides, actually animation
868 // is of ped standing, but I will ignore it
869 s = new AnimWindow(id, m, 1959 + 8 + ((gamdata->orientation & 0x40) >> 5));
870 s->setStateMasks(sttawnd_PedDisappears);
871 break;
872 case 0x25:
873 s = new AnimWindow(id, m, curanim);
874
875 // NOTE : orientation, I assume, plays role of hidding object,
876 // orientation 0x40, 0x80 are drawn (gamdata->desc always 7)
877 // window without light
878 s->setTimeShowAnim(30000 + (rand() % 30000));
879 if (gamdata->orientation == 0x40 || gamdata->orientation == 0x80)
880 s->setStateMasks(sttawnd_LightOff);
881 else
882 s->setStateMasks(sttawnd_LightOn);
883
884 break;
885
886 case 0x26:
887 // 0x00,0x80 south - north = 0
888 // 0x40,0xC0 weast - east = 2
889 s = new LargeDoor(id, m, curanim, curanim + 1, curanim + 2);
890 if (gamdata->orientation == 0x00 || gamdata->orientation == 0x80) {
891 s->setOrientation(kStaticOrientation1);
892 s->setSizeX(384);
893 s->setSizeY(64);
894 s->setSizeZ(192);
895 } else {
896 s->setOrientation(kStaticOrientation2);
897 s->setSizeX(64);
898 s->setSizeY(384);
899 s->setSizeZ(192);
900 }
901 break;
902 #ifdef _DEBUG
903 default:
904 printf("uknown static object type %02X , %02X, %X\n",
905 gamdata->sub_type, gamdata->orientation,
906 READ_LE_UINT16(gamdata->index_current_frame));
907 printf("x = %i, xoff = %i, ", gamdata->mapposx[1],
908 gamdata->mapposx[0]);
909 printf("y = %i, yoff = %i, ", gamdata->mapposy[1],
910 gamdata->mapposy[0]);
911 printf("z = %i, zoff = %i\n", gamdata->mapposz[1],
912 gamdata->mapposz[0]);
913 break;
914 #endif
915 }
916
917 if (s) {
918 int z = READ_LE_UINT16(gamdata->mapposz) >> 7;
919 int oz = gamdata->mapposz[0] & 0x7F;
920 // trick to draw
921 if (s->type() == Static::smt_Advertisement)
922 z += 1;
923
924 s->setPosition(gamdata->mapposx[1], gamdata->mapposy[1],
925 z, gamdata->mapposx[0], gamdata->mapposy[0], oz);
926
927 //s->setMainType(gamdata->sub_type);
928 #if 0
929 if (s->tileX() == 66 && s->tileY() == 49)
930 oz = 2;
931 #endif
932 s->setDirection(gamdata->orientation);
933 }
934
935 return s;
936 }
937
Door(uint16 anId,int m,int anim,int closingAnim,int openAnim,int openingAnim)938 Door::Door(uint16 anId, int m, int anim, int closingAnim, int openAnim, int openingAnim) :
939 Static(anId, m, Static::smt_Door), anim_(anim), closing_anim_(closingAnim),
940 open_anim_(openAnim), opening_anim_(openingAnim) {
941 state_ = Static::sttdoor_Closed;
942 }
943
draw(int x,int y)944 void Door::draw(int x, int y)
945 {
946 addOffs(x, y);
947 g_App.gameSprites().drawFrame(anim_ + (state_ << 1), frame_, x, y);
948 }
949
animate(int elapsed,Mission * obj)950 bool Door::animate(int elapsed, Mission *obj)
951 {
952 PedInstance *p = NULL;
953 int x = tileX();
954 int y = tileY();
955 int z = tileZ();
956 MapObject::ObjectNature nature;
957 int si;
958 char inc_rel = 0, rel_inc = 0;
959 char *i = 0, *j = 0;
960 bool found = false;
961
962 bool changed = MapObject::animate(elapsed);
963 switch(state_) {
964 case Static::sttdoor_Open:
965 if (orientation_ == kStaticOrientation1) {
966 i = &rel_inc;
967 j = &inc_rel;
968 } else if (orientation_ == kStaticOrientation2) {
969 i = &inc_rel;
970 j = &rel_inc;
971 }
972 assert(i != 0 && j != 0);
973 for(*i = 0; *i < 2; *i += 1) {
974 nature = MapObject::kNaturePed; si = 0;
975 do {
976 p = (PedInstance *)(obj->findObjectWithNatureAtPos(x + inc_rel,
977 y + rel_inc, z, &nature, &si, true));
978 if (!p && state_ == Static::sttdoor_Open && (!found)) {
979 state_ = Static::sttdoor_Closing;
980 setExcludedFromBlockers(false);
981 frame_ = 0;
982 } else if (p && p->isAlive()){
983 state_ = Static::sttdoor_Open;
984 setExcludedFromBlockers(true);
985 found = true;
986 p->hold_on_.wayFree = 0;
987 }
988 } while (p);
989 }
990 break;
991 case Static::sttdoor_Closed:
992 if (orientation_ == kStaticOrientation1) {
993 i = &rel_inc;
994 j = &inc_rel;
995 } else if (orientation_ == kStaticOrientation2) {
996 i = &inc_rel;
997 j = &rel_inc;
998 }
999 assert(i != 0 && j != 0);
1000 *i = 1;
1001 nature = MapObject::kNaturePed; si = 0;
1002 do {
1003 p = (PedInstance *)(obj->findObjectWithNatureAtPos(x + inc_rel,
1004 y + rel_inc, z, &nature, &si, true));
1005 if (p && p->isAlive()) {
1006 if (!found) {
1007 state_ = Static::sttdoor_Opening;
1008 setExcludedFromBlockers(false);
1009 found = true;
1010 frame_ = 0;
1011 }
1012 p->hold_on_.wayFree = 1;
1013 p->hold_on_.tilex = x;
1014 p->hold_on_.tiley = y;
1015 p->hold_on_.tilez = z;
1016 p->hold_on_.xadj = 0;
1017 p->hold_on_.yadj = 0;
1018 p->hold_on_.pathBlocker = this;
1019 }
1020 } while (p);
1021 *i = 0;
1022 nature = MapObject::kNaturePed; si = 0;
1023 do {
1024 p = (PedInstance *)(obj->findObjectWithNatureAtPos(x + inc_rel,
1025 y + rel_inc, z, &nature, &si, true));
1026 if (p && p->isAlive()) {
1027 if (!found) {
1028 state_ = Static::sttdoor_Opening;
1029 setExcludedFromBlockers(false);
1030 found = true;
1031 frame_ = 0;
1032 }
1033 p->hold_on_.wayFree = 1;
1034 p->hold_on_.tilex = x;
1035 p->hold_on_.tiley = y;
1036 p->hold_on_.tilez = z;
1037 p->hold_on_.xadj = 0;
1038 p->hold_on_.yadj = 0;
1039 p->hold_on_.pathBlocker = this;
1040 }
1041 } while (p);
1042 break;
1043 case Static::sttdoor_Closing:
1044 if (frame_ >= g_App.gameSprites().lastFrame(closing_anim_)) {
1045 state_ = Static::sttdoor_Closed;
1046 setExcludedFromBlockers(false);
1047 frame_ = 0;
1048 }
1049 break;
1050 case Static::sttdoor_Opening:
1051 if (frame_ >= g_App.gameSprites().lastFrame(opening_anim_)) {
1052 state_ = Static::sttdoor_Open;
1053 setExcludedFromBlockers(true);
1054 frame_ = 0;
1055 }
1056 break;
1057 }
1058 return changed;
1059 }
1060
isPathBlocker()1061 bool Door::isPathBlocker()
1062 {
1063 return state_ != Static::sttdoor_Open;
1064 }
1065
1066
LargeDoor(uint16 anId,int m,int anim,int closingAnim,int openingAnim)1067 LargeDoor::LargeDoor(uint16 anId, int m, int anim, int closingAnim, int openingAnim):
1068 Static(anId, m, Static::smt_LargeDoor), anim_(anim),
1069 closing_anim_(closingAnim), opening_anim_(openingAnim) {
1070 state_ = Static::sttdoor_Closed;
1071 }
1072
draw(int x,int y)1073 void LargeDoor::draw(int x, int y)
1074 {
1075 addOffs(x, y);
1076 switch(state_) {
1077 case Static::sttdoor_Open:
1078 break;
1079 case Static::sttdoor_Closing:
1080 g_App.gameSprites().drawFrame(closing_anim_, frame_, x, y);
1081 break;
1082 case Static::sttdoor_Closed:
1083 g_App.gameSprites().drawFrame(anim_, frame_, x, y);
1084 break;
1085 case Static::sttdoor_Opening:
1086 g_App.gameSprites().drawFrame(opening_anim_, frame_, x, y);
1087 break;
1088 }
1089 }
1090
animate(int elapsed,Mission * obj)1091 bool LargeDoor::animate(int elapsed, Mission *obj)
1092 {
1093 // TODO: there must be somewhere locked door
1094 VehicleInstance *v = NULL;
1095 PedInstance *p = NULL;
1096 int x = tileX();
1097 int y = tileY();
1098 int z = tileZ();
1099 MapObject::ObjectNature nature;
1100 int si;
1101 char inc_rel = 0, rel_inc = 0;
1102 char *i = 0, *j = 0;
1103 bool found = false;
1104 std::vector<PedInstance *> found_peds;
1105 found_peds.reserve(256);
1106 std::vector<PedInstance *> found_peds_mid;
1107 found_peds_mid.reserve(256);
1108 char sign;
1109 int set_wayFree = 0;
1110
1111 bool changed = MapObject::animate(elapsed);
1112 uint32 cur_state = state_;
1113 switch(state_) {
1114 case Static::sttdoor_Open:
1115 if (orientation_ == kStaticOrientation1) {
1116 i = &rel_inc;
1117 j = &inc_rel;
1118 } else if (orientation_ == kStaticOrientation2) {
1119 i = &inc_rel;
1120 j = &rel_inc;
1121 }
1122 assert(i != 0 && j != 0);
1123 *j = -1;
1124 for(*i = -2; *i < 3; (*i)++) {
1125 nature = MapObject::kNatureVehicle; si = 0;
1126 v = (VehicleInstance *)(obj->findObjectWithNatureAtPos(x + inc_rel,
1127 y + rel_inc,z, &nature, &si, true));
1128 if (!v && !found) {
1129 state_ = Static::sttdoor_Closing;
1130 setExcludedFromBlockers(false);
1131 } else if (v){
1132 state_ = Static::sttdoor_Open;
1133 setExcludedFromBlockers(true);
1134 found = true;
1135 v->hold_on_.wayFree = 0;
1136 }
1137 }
1138 *j = 1;
1139 for(*i = -2; *i < 3; (*i)++) {
1140 nature = MapObject::kNatureVehicle; si = 0;
1141 v = (VehicleInstance *)(obj->findObjectWithNatureAtPos(x + inc_rel,
1142 y + rel_inc,z,&nature,&si,true));
1143 if (!v && !found) {
1144 state_ = Static::sttdoor_Closing;
1145 setExcludedFromBlockers(false);
1146 } else if (v){
1147 state_ = Static::sttdoor_Open;
1148 setExcludedFromBlockers(true);
1149 found = true;
1150 v->hold_on_.wayFree = 0;
1151 }
1152 }
1153 *j = -1;
1154 for (*i = -1; *i <= 1; (*i)++ ) {
1155 nature = MapObject::kNaturePed; si = 0;
1156 do {
1157 p = (PedInstance *)(obj->findObjectWithNatureAtPos(x + rel_inc,
1158 y + inc_rel, z, &nature, &si, true));
1159 if (p) {
1160 found_peds.push_back(p);
1161 if (!found && p->hasAccessCard()) {
1162 state_ = Static::sttdoor_Open;
1163 setExcludedFromBlockers(true);
1164 found = true;
1165 }
1166 }
1167 } while (p);
1168 }
1169 *j = 1;
1170 for (*i = -1; *i <= 1; (*i)++ ) {
1171 nature = MapObject::kNaturePed; si = 0;
1172 do {
1173 p = (PedInstance *)(obj->findObjectWithNatureAtPos(x + rel_inc,
1174 y + inc_rel, z, &nature, &si, true));
1175 if (p) {
1176 found_peds.push_back(p);
1177 if (!found && p->hasAccessCard()) {
1178 state_ = Static::sttdoor_Open;
1179 setExcludedFromBlockers(true);
1180 found = true;
1181 }
1182 }
1183 } while (p);
1184 }
1185 *j = 0;
1186 for (*i = -1; *i <= 1; (*i)++ ) {
1187 nature = MapObject::kNaturePed; si = 0;
1188 do {
1189 p = (PedInstance *)(obj->findObjectWithNatureAtPos(x + rel_inc,
1190 y + inc_rel, z, &nature, &si, true));
1191 if (p) {
1192 found_peds_mid.push_back(p);
1193 if (!found && p->hasAccessCard()) {
1194 state_ = Static::sttdoor_Open;
1195 setExcludedFromBlockers(true);
1196 found = true;
1197 }
1198 }
1199 } while (p);
1200 }
1201 if (state_ == Static::sttdoor_Open) {
1202 for (std::vector<PedInstance *>::iterator it = found_peds.begin();
1203 it != found_peds.end(); ++it )
1204 {
1205 (*it)->hold_on_.wayFree = 0;
1206 }
1207 for (std::vector<PedInstance *>::iterator it = found_peds_mid.begin();
1208 it != found_peds_mid.end(); ++it )
1209 {
1210 (*it)->hold_on_.wayFree = 0;
1211 }
1212 } else {
1213 for (std::vector<PedInstance *>::iterator it = found_peds.begin();
1214 it != found_peds.end(); ++it )
1215 {
1216 p = *it;
1217 p->hold_on_.wayFree = 2;
1218 p->hold_on_.tilex = x;
1219 p->hold_on_.tiley = y;
1220 if (orientation_ == kStaticOrientation1) {
1221 p->hold_on_.xadj = 1;
1222 p->hold_on_.yadj = 0;
1223 } else if (orientation_ == kStaticOrientation2) {
1224 p->hold_on_.xadj = 0;
1225 p->hold_on_.yadj = 1;
1226 }
1227 p->hold_on_.tilez = z;
1228 p->hold_on_.pathBlocker = this;
1229 }
1230 for (std::vector<PedInstance *>::iterator it = found_peds_mid.begin();
1231 it != found_peds_mid.end(); ++it )
1232 {
1233 p = *it;
1234 ShootableMapObject::DamageInflictType d;
1235 d.dtype = MapObject::dmg_Collision;
1236 d.d_owner = NULL;
1237 d.dvalue = 1024;
1238 d.ddir = -1;
1239 p->handleHit(d);
1240 }
1241 }
1242 break;
1243 case Static::sttdoor_Closed:
1244 if (orientation_ == kStaticOrientation1) {
1245 i = &rel_inc;
1246 j = &inc_rel;
1247 sign = 1;
1248 } else if (orientation_ == kStaticOrientation2) {
1249 i = &inc_rel;
1250 j = &rel_inc;
1251 sign = -1;
1252 }
1253 assert(i != 0 && j != 0);
1254 *j = -1 * sign;
1255 *i = -2;
1256 nature = MapObject::kNatureVehicle; si = 0;
1257 v = (VehicleInstance *)(obj->findObjectWithNatureAtPos(x + inc_rel,
1258 y + rel_inc,z, &nature, &si,true));
1259 if (v) {
1260 if (!found) {
1261 state_ = Static::sttdoor_Opening;
1262 setExcludedFromBlockers(false);
1263 found = true;
1264 }
1265 v->hold_on_.wayFree = 1;
1266 v->hold_on_.pathBlocker = this;
1267 }
1268 *j = 1 * sign;
1269 *i = 2;
1270 nature = MapObject::kNatureVehicle; si = 0;
1271 v = (VehicleInstance *)(obj->findObjectWithNatureAtPos(x + inc_rel,
1272 y + rel_inc,z, &nature, &si,true));
1273 if (v) {
1274 if (!found) {
1275 state_ = Static::sttdoor_Opening;
1276 setExcludedFromBlockers(false);
1277 found = true;
1278 }
1279 v->hold_on_.wayFree = 1;
1280 v->hold_on_.pathBlocker = this;
1281 }
1282 *j = -1;
1283 for (*i = -1; *i <= 1; (*i)++ ) {
1284 nature = MapObject::kNaturePed; si = 0;
1285 do {
1286 p = (PedInstance *)(obj->findObjectWithNatureAtPos(x + rel_inc,
1287 y + inc_rel, z, &nature, &si, true));
1288 if (p) {
1289 found_peds.push_back(p);
1290 if (!found && p->hasAccessCard()) {
1291 state_ = Static::sttdoor_Opening;
1292 setExcludedFromBlockers(false);
1293 found = true;
1294 }
1295 }
1296 } while (p);
1297 }
1298 *j = 1;
1299 for (*i = -1; *i <= 1; (*i)++ ) {
1300 nature = MapObject::kNaturePed; si = 0;
1301 do {
1302 p = (PedInstance *)(obj->findObjectWithNatureAtPos(x + rel_inc,
1303 y + inc_rel, z, &nature, &si, true));
1304 if (p) {
1305 found_peds.push_back(p);
1306 if (!found && p->hasAccessCard()) {
1307 state_ = Static::sttdoor_Opening;
1308 setExcludedFromBlockers(false);
1309 found = true;
1310 }
1311 }
1312 } while (p);
1313 }
1314 set_wayFree = state_ == Static::sttdoor_Opening ? 1 : 2;
1315 for (std::vector<PedInstance *>::iterator it = found_peds.begin();
1316 it != found_peds.end(); ++it )
1317 {
1318 p = *it;
1319 p->hold_on_.wayFree = set_wayFree;
1320 p->hold_on_.tilex = x;
1321 p->hold_on_.tiley = y;
1322 if (orientation_ == kStaticOrientation1) {
1323 p->hold_on_.xadj = 1;
1324 p->hold_on_.yadj = 0;
1325 } else if (orientation_ == kStaticOrientation2) {
1326 p->hold_on_.xadj = 0;
1327 p->hold_on_.yadj = 1;
1328 }
1329 p->hold_on_.tilez = z;
1330 p->hold_on_.pathBlocker = this;
1331 }
1332 break;
1333 case Static::sttdoor_Closing:
1334 if (frame_ >= g_App.gameSprites().lastFrame(closing_anim_)) {
1335 state_ = Static::sttdoor_Closed;
1336 setExcludedFromBlockers(false);
1337 }
1338 case Static::sttdoor_Opening:
1339 if (state_ == Static::sttdoor_Opening
1340 && frame_ >= g_App.gameSprites().lastFrame(opening_anim_))
1341 {
1342 state_ = Static::sttdoor_Open;
1343 setExcludedFromBlockers(true);
1344 }
1345 if (orientation_ == kStaticOrientation1) {
1346 i = &rel_inc;
1347 j = &inc_rel;
1348 sign = 1;
1349 } else if (orientation_ == kStaticOrientation2) {
1350 i = &inc_rel;
1351 j = &rel_inc;
1352 sign = -1;
1353 }
1354 assert(i != 0 && j != 0);
1355 *j = -1 * sign;
1356 *i = -2;
1357 set_wayFree = state_ == Static::sttdoor_Opening ? 1 : 2;
1358 nature = MapObject::kNatureVehicle; si = 0;
1359 v = (VehicleInstance *)(obj->findObjectWithNatureAtPos(x + inc_rel,
1360 y + rel_inc,z, &nature, &si,true));
1361 if (v) {
1362 v->hold_on_.wayFree = 1;
1363 v->hold_on_.pathBlocker = this;
1364 }
1365 *j = 1 * sign;
1366 *i = 2;
1367 nature = MapObject::kNatureVehicle; si = 0;
1368 v = (VehicleInstance *)(obj->findObjectWithNatureAtPos(x + inc_rel,
1369 y + rel_inc,z, &nature, &si,true));
1370 if (v) {
1371 v->hold_on_.wayFree = 1;
1372 v->hold_on_.pathBlocker = this;
1373 }
1374 *j = -1;
1375 for (*i = -1; *i <= 1; (*i)++ ) {
1376 nature = MapObject::kNaturePed; si = 0;
1377 do {
1378 p = (PedInstance *)(obj->findObjectWithNatureAtPos(x + rel_inc,
1379 y + inc_rel, z, &nature, &si, true));
1380 if (p) {
1381 found_peds.push_back(p);
1382 }
1383 } while (p);
1384 }
1385 *j = 1;
1386 for (*i = -1; *i <= 1; (*i)++ ) {
1387 nature = MapObject::kNaturePed; si = 0;
1388 do {
1389 p = (PedInstance *)(obj->findObjectWithNatureAtPos(x + rel_inc,
1390 y + inc_rel, z, &nature, &si, true));
1391 if (p) {
1392 found_peds.push_back(p);
1393 }
1394 } while (p);
1395 }
1396 for (std::vector<PedInstance *>::iterator it = found_peds.begin();
1397 it != found_peds.end(); ++it )
1398 {
1399 p = *it;
1400 p->hold_on_.wayFree = set_wayFree;
1401 p->hold_on_.tilex = x;
1402 p->hold_on_.tiley = y;
1403 if (orientation_ == kStaticOrientation1) {
1404 p->hold_on_.xadj = 1;
1405 p->hold_on_.yadj = 0;
1406 } else if (orientation_ == kStaticOrientation2) {
1407 p->hold_on_.xadj = 0;
1408 p->hold_on_.yadj = 1;
1409 }
1410 p->hold_on_.tilez = z;
1411 p->hold_on_.pathBlocker = this;
1412 }
1413 break;
1414 }
1415 if (cur_state != state_)
1416 frame_ = 0;
1417 return changed;
1418 }
1419
isPathBlocker()1420 bool LargeDoor::isPathBlocker()
1421 {
1422 return state_ != Static::sttdoor_Open;
1423 }
1424
1425
Tree(uint16 anId,int m,int anim,int burningAnim,int damagedAnim)1426 Tree::Tree(uint16 anId, int m, int anim, int burningAnim, int damagedAnim) :
1427 Static(anId, m, Static::smt_Tree), anim_(anim), burning_anim_(burningAnim),
1428 damaged_anim_(damagedAnim) {
1429 state_ = Static::stttree_Healthy;
1430 }
1431
draw(int x,int y)1432 void Tree::draw(int x, int y)
1433 {
1434 addOffs(x, y);
1435 switch (state_) {
1436 case Static::stttree_Healthy:
1437 g_App.gameSprites().drawFrame(anim_, frame_, x, y);
1438 break;
1439 case Static::stttree_Burning:
1440 g_App.gameSprites().drawFrame(burning_anim_, frame_, x, y);
1441 break;
1442 case Static::stttree_Damaged:
1443 g_App.gameSprites().drawFrame(damaged_anim_, frame_, x, y);
1444 break;
1445 }
1446 }
1447
animate(int elapsed,Mission * obj)1448 bool Tree::animate(int elapsed, Mission *obj) {
1449
1450 if (state_ == Static::stttree_Burning) {
1451 if (!(leftTimeShowAnim(elapsed))) {
1452 state_ = Static::stttree_Damaged;
1453 frame_ = 0;
1454 setFramesPerSec(2);
1455 return true;
1456 }
1457 }
1458
1459 return MapObject::animate(elapsed);
1460 }
1461
1462 /*!
1463 * Implementation for the Tree. Tree burns only when hit by laser of fire.
1464 * \param d Damage information
1465 */
handleHit(DamageInflictType & d)1466 void Tree::handleHit(DamageInflictType &d) {
1467 if (isAlive() &&
1468 (d.dtype == dmg_Laser || d.dtype == dmg_Burn || d.dtype == dmg_Explosion)) {
1469 decreaseHealth(d.dvalue);
1470 if (isDead()) {
1471 state_ = Static::stttree_Burning;
1472 setTimeShowAnim(10000);
1473 setExcludedFromBlockers(true);
1474 }
1475 }
1476 }
1477
WindowObj(uint16 anId,int m,int anim,int openAnim,int breakingAnim,int damagedAnim)1478 WindowObj::WindowObj(uint16 anId, int m, int anim, int openAnim, int breakingAnim,
1479 int damagedAnim) :
1480 Static(anId, m, Static::smt_Window), anim_(anim), open_anim_(openAnim),
1481 breaking_anim_(breakingAnim), damaged_anim_(damagedAnim) {}
1482
animate(int elapsed,Mission * obj)1483 bool WindowObj::animate(int elapsed, Mission *obj) {
1484 bool updated = MapObject::animate(elapsed);
1485
1486 if (state_ == Static::sttwnd_Breaking
1487 && frame_ >= g_App.gameSprites().lastFrame(breaking_anim_)
1488 ) {
1489 state_ = sttwnd_Damaged;
1490 updated = true;
1491 }
1492 return updated;
1493 }
1494
draw(int x,int y)1495 void WindowObj::draw(int x, int y)
1496 {
1497 addOffs(x, y);
1498 g_App.gameSprites().drawFrame(anim_ + (state_ << 1), frame_, x, y);
1499 }
1500
1501 /*!
1502 * Implementation for the Tree. Tree burns only when hit by laser of fire.
1503 * \param d Damage information
1504 */
handleHit(DamageInflictType & d)1505 void WindowObj::handleHit(DamageInflictType &d) {
1506 if (isAlive() &&
1507 (d.dtype == dmg_Bullet || d.dtype == dmg_Explosion)) {
1508 decreaseHealth(d.dvalue);
1509 if (isDead()) {
1510 state_ = Static::sttwnd_Breaking;
1511 setExcludedFromBlockers(true);
1512 frame_ = 0;
1513 setFramesPerSec(6);
1514 }
1515 }
1516 }
1517
EtcObj(uint16 anId,int m,int anim,int burningAnim,int damagedAnim,StaticType aType)1518 EtcObj::EtcObj(uint16 anId, int m, int anim, int burningAnim, int damagedAnim, StaticType aType) :
1519 Static(anId, m, aType), anim_(anim), burning_anim_(burningAnim),
1520 damaged_anim_(damagedAnim) {}
1521
draw(int x,int y)1522 void EtcObj::draw(int x, int y)
1523 {
1524 addOffs(x, y);
1525 g_App.gameSprites().drawFrame(anim_, frame_, x, y);
1526 }
1527
NeonSign(uint16 anId,int m,int anim)1528 NeonSign::NeonSign(uint16 anId, int m, int anim) : Static(anId, m, Static::smt_NeonSign) {
1529 anim_ = anim;
1530 }
1531
draw(int x,int y)1532 void NeonSign::draw(int x, int y)
1533 {
1534 addOffs(x, y);
1535 g_App.gameSprites().drawFrame(anim_, frame_, x, y);
1536 }
1537
Semaphore(uint16 anId,int m,int anim,int damagedAnim)1538 Semaphore::Semaphore(uint16 anId, int m, int anim, int damagedAnim) :
1539 Static(anId, m, Static::smt_Semaphore), anim_(anim),
1540 damaged_anim_(damagedAnim), elapsed_left_smaller_(0),
1541 elapsed_left_bigger_(0), up_down_(1)
1542 {
1543 setFramesPerSec(2);
1544 }
1545
animate(int elapsed,Mission * obj)1546 bool Semaphore::animate(int elapsed, Mission *obj) {
1547 if (state_ == Static::sttsem_Damaged) {
1548 if (elapsed_left_bigger_ == 0)
1549 return false;
1550 int chng = (elapsed + elapsed_left_smaller_) >> 1;
1551 elapsed_left_smaller_ = elapsed & 2;
1552 elapsed_left_bigger_ -= chng;
1553 if (elapsed_left_bigger_ < 0) {
1554 chng += elapsed_left_bigger_;
1555 elapsed_left_bigger_ = 0;
1556 }
1557 int z = pos_.tz * 128 + pos_.oz - chng;
1558 pos_.tz = z / 128;
1559 pos_.oz = z % 128;
1560 return true;
1561 }
1562
1563 int chng = (elapsed + elapsed_left_smaller_) >> 2;
1564 elapsed_left_smaller_ = elapsed & 4;
1565 if (chng) {
1566 int oz = pos_.oz + chng * up_down_;
1567 if (oz > 127) {
1568 oz = 127 - (oz & 0x7F);
1569 up_down_ -= 2;
1570 } else if (oz < 64) {
1571 oz = 64 + (64 - oz);
1572 up_down_ += 2;
1573 }
1574 pos_.oz = oz;
1575 }
1576
1577 chng = (elapsed + elapsed_left_bigger_) >> 6;
1578 elapsed_left_bigger_ = elapsed & 63;
1579 if (chng) {
1580 // Direction is used as storage for animation change, not my idea
1581 dir_ += chng;
1582 dir_ &= 0xFF;
1583 state_ = dir_ >> 6;
1584 state_++;
1585 if (state_ > Static::sttsem_Stt3)
1586 state_ = Static::sttsem_Stt0;
1587 }
1588
1589 return MapObject::animate(elapsed);
1590 }
1591
1592 /*!
1593 * Implementation for the Semaphore.
1594 * \param d Damage information
1595 */
handleHit(DamageInflictType & d)1596 void Semaphore::handleHit(DamageInflictType &d) {
1597 if (isAlive() &&
1598 (d.dtype == dmg_Laser || d.dtype == dmg_Explosion)) {
1599 decreaseHealth(d.dvalue);
1600 if (isDead()) {
1601 state_ = Static::sttsem_Damaged;
1602 // To make this thing reach the ground need to get solid surface 0x0F
1603 Mission * m = g_Session.getMission();
1604 int z = pos_.tz;
1605 int indx = pos_.tx + pos_.ty * m->mmax_x_ + pos_.tz * m->mmax_m_xy;
1606 elapsed_left_bigger_ = 0;
1607 while (z != 0) {
1608 z--;
1609 indx -= m->mmax_m_xy;
1610 int twd = m->mtsurfaces_[indx].twd;
1611 if (twd == 0x0F) {
1612 elapsed_left_bigger_ = (pos_.tz - z) * 128 + pos_.oz;
1613 break;
1614 }
1615 }
1616 setExcludedFromBlockers(true);
1617 }
1618 }
1619 }
1620
draw(int x,int y)1621 void Semaphore::draw(int x, int y)
1622 {
1623 addOffs(x, y);
1624 g_App.gameSprites().drawFrame(anim_ + state_, frame_, x, y);
1625 }
1626
AnimWindow(uint16 anId,int m,int anim)1627 AnimWindow::AnimWindow(uint16 anId, int m, int anim) : Static(anId, m, smt_AnimatedWindow) {
1628 setExcludedFromBlockers(true);
1629 setFramesPerSec(4);
1630 anim_ = anim;
1631 }
1632
draw(int x,int y)1633 void AnimWindow::draw(int x, int y)
1634 {
1635 // When light is on, don't draw window
1636 // because lighted window is part of the map
1637 if (state_ == Static::sttawnd_LightOn)
1638 return;
1639 addOffs(x, y);
1640 g_App.gameSprites().drawFrame(anim_ + (state_ << 1), frame_, x, y);
1641 }
1642
animate(int elapsed,Mission * obj)1643 bool AnimWindow::animate(int elapsed, Mission *obj)
1644 {
1645 switch (state_) {
1646 case Static::sttawnd_LightOff:
1647 if (!leftTimeShowAnim(elapsed)) {
1648 // decide to start switching lights on
1649 // or continue being in dark
1650 if (rand() % 100 > 60) {
1651 setTimeShowAnim(30000 + (rand() % 30000));
1652 } else {
1653 state_ = Static::sttawnd_LightSwitching;
1654 frame_ = 0;
1655 setTimeShowAnim(1000 + (rand() % 1000));
1656 }
1657 }
1658 break;
1659 case Static::sttawnd_LightSwitching:
1660 if (!leftTimeShowAnim(elapsed)) {
1661 state_ = Static::sttawnd_LightOn;
1662 setTimeShowAnim(30000 + (rand() % 30000));
1663 }
1664 break;
1665 case Static::sttawnd_PedAppears:
1666 if (frame_ >= g_App.gameSprites().lastFrame(anim_
1667 + (Static::sttawnd_PedAppears << 1)))
1668 {
1669 state_ = Static::sttawnd_ShowPed;
1670 setTimeShowAnim(15000 + (rand() % 15000));
1671 }
1672 break;
1673 case Static::sttawnd_ShowPed:
1674 if (!leftTimeShowAnim(elapsed)) {
1675 // continue showing ped or hide it
1676 if (rand() % 100 > 50) {
1677 setTimeShowAnim(15000 + (rand() % 5000));
1678 } else {
1679 frame_ = 0;
1680 state_ = Static::sttawnd_PedDisappears;
1681 }
1682 }
1683 break;
1684 case Static::sttawnd_PedDisappears:
1685 if (frame_ >= g_App.gameSprites().lastFrame(anim_
1686 + (Static::sttawnd_PedDisappears << 1)))
1687 {
1688 state_ = Static::sttawnd_LightOn;
1689 setTimeShowAnim(30000 + (rand() % 30000));
1690 }
1691 break;
1692 case Static::sttawnd_LightOn:
1693 if (!leftTimeShowAnim(elapsed)) {
1694 // we will continue showing lightson or switch
1695 // lights off or show ped
1696 int rnd_v = rand() % 100;
1697 if (rnd_v > 80) {
1698 setTimeShowAnim(30000 + (rand() % 30000));
1699 } else if (rnd_v > 60) {
1700 frame_ = 0;
1701 state_ = Static::sttawnd_PedAppears;
1702 } else if (rnd_v > 10) {
1703 state_ = Static::sttawnd_LightOff;
1704 setTimeShowAnim(30000 + (rand() % 30000));
1705 } else {
1706 frame_ = 0;
1707 state_ = Static::sttawnd_LightSwitching;
1708 setTimeShowAnim(1000 + (rand() % 1000));
1709 }
1710 }
1711 break;
1712 }
1713
1714 return MapObject::animate(elapsed);
1715 return false;
1716 }
1717
1718