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