1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 1997, 2005 - 3D Realms Entertainment
4 
5 This file is part of Shadow Warrior version 1.2
6 
7 Shadow Warrior is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 
16 See the GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 
22 Original Source: 1997 - Frank Maddin and Jim Norwood
23 Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
24 */
25 //-------------------------------------------------------------------------
26 #include "build.h"
27 
28 #include "names2.h"
29 #include "panel.h"
30 //#include "keys.h"
31 #include "tags.h"
32 #include "sector.h"
33 #include "ai.h"
34 #include "player.h"
35 #include "game.h"
36 #include "interpso.h"
37 #include "network.h"
38 #include "sprite.h"
39 #include "track.h"
40 #include "weapon.h"
41 
42 
43 void DoTrack(SECTOR_OBJECTp sop, short locktics, int *nx, int *ny);
44 void DoAutoTurretObject(SECTOR_OBJECTp sop);
45 void DoTornadoObject(SECTOR_OBJECTp sop);
46 
47 #define ACTOR_STD_JUMP (-384)
48 int GlobSpeedSO;
49 
50 #undef BOUND_4PIX
51 //#define BOUND_4PIX(x) ( TRUNC4((x) + MOD4(x)) )
52 #define BOUND_4PIX(x) (x)
53 
54 // determine if moving down the track will get you closer to the player
55 short
TrackTowardPlayer(SPRITEp sp,TRACKp t,TRACK_POINTp start_point)56 TrackTowardPlayer(SPRITEp sp, TRACKp t, TRACK_POINTp start_point)
57 {
58     TRACK_POINTp end_point;
59     int end_dist, start_dist;
60 
61     // determine which end of the Track we are starting from
62     if (start_point == t->TrackPoint)
63     {
64         end_point = t->TrackPoint + t->NumPoints - 1;
65     }
66     else
67     {
68         end_point = t->TrackPoint;
69     }
70 
71     end_dist = Distance(end_point->x, end_point->y, sp->x, sp->y);
72     start_dist = Distance(start_point->x, start_point->y, sp->x, sp->y);
73 
74     if (end_dist < start_dist)
75     {
76         return TRUE;
77     }
78 
79     return FALSE;
80 
81 }
82 
83 short
TrackStartCloserThanEnd(short SpriteNum,TRACKp t,TRACK_POINTp start_point)84 TrackStartCloserThanEnd(short SpriteNum, TRACKp t, TRACK_POINTp start_point)
85 {
86     SPRITEp sp = User[SpriteNum]->SpriteP;
87 
88     TRACK_POINTp end_point;
89     int end_dist, start_dist;
90 
91     // determine which end of the Track we are starting from
92     if (start_point == t->TrackPoint)
93     {
94         end_point = t->TrackPoint + t->NumPoints - 1;
95     }
96     else
97     {
98         end_point = t->TrackPoint;
99     }
100 
101     end_dist = Distance(end_point->x, end_point->y, sp->x, sp->y);
102     start_dist = Distance(start_point->x, start_point->y, sp->x, sp->y);
103 
104     if (start_dist < end_dist)
105     {
106         return TRUE;
107     }
108 
109     return FALSE;
110 
111 }
112 
113 /*
114 
115 !AIC - Looks at endpoints to figure direction of the track and the closest
116 point to the sprite.
117 
118 */
119 
120 short
ActorFindTrack(short SpriteNum,int8_t player_dir,int track_type,short * track_point_num,short * track_dir)121 ActorFindTrack(short SpriteNum, int8_t player_dir, int track_type, short *track_point_num, short *track_dir)
122 {
123     USERp u = User[SpriteNum];
124     SPRITEp sp = User[SpriteNum]->SpriteP;
125 
126     int dist, near_dist = 999999, zdiff;
127     short track_sect=0;
128 
129     short i;
130     short end_point[2] = {0,0};
131 
132     TRACKp t, near_track = NULL;
133     TRACK_POINTp tp, near_tp = NULL;
134 
135 #define TOWARD_PLAYER 1
136 #define AWAY_FROM_PLAYER -1
137 
138     // look at all tracks finding the closest endpoint
139     for (t = &Track[0]; t < &Track[MAX_TRACKS]; t++)
140     {
141         tp = t->TrackPoint;
142 
143         // Skip if high tag is not ONE of the track type we are looking for
144         if (!TEST(t->ttflags, track_type))
145             continue;
146 
147         // Skip if already someone on this track
148         if (TEST(t->flags, TF_TRACK_OCCUPIED))
149         {
150             //DSPRINTF(ds,"occupied!");
151             MONO_PRINT(ds);
152             continue;
153         }
154 
155         switch (track_type)
156         {
157         case BIT(TT_DUCK_N_SHOOT):
158         {
159             if (!u->ActorActionSet->Duck)
160                 return -1;
161 
162             end_point[1] = 0;
163             break;
164         }
165 
166         // for ladders only look at first track point
167         case BIT(TT_LADDER):
168         {
169             if (!u->ActorActionSet->Climb)
170                 return -1;
171 
172             end_point[1] = 0;
173             break;
174         }
175 
176         case BIT(TT_JUMP_UP):
177         case BIT(TT_JUMP_DOWN):
178         {
179             if (!u->ActorActionSet->Jump)
180                 return -1;
181 
182             end_point[1] = 0;
183             break;
184         }
185 
186         case BIT(TT_TRAVERSE):
187         {
188             if (!u->ActorActionSet->Crawl || !u->ActorActionSet->Jump)
189                 return -1;
190 
191             break;
192         }
193 
194         // look at end point also
195         default:
196             end_point[1] = t->NumPoints - 1;
197             break;
198         }
199 
200         zdiff = Z(16);
201 
202         // Look at both track end points to see wich is closer
203         for (i = 0; i < 2; i++)
204         {
205             tp = t->TrackPoint + end_point[i];
206 
207             dist = Distance(tp->x, tp->y, sp->x, sp->y);
208 
209             if (dist < 15000 && dist < near_dist)
210             {
211                 // make sure track start is on approximate z level - skip if
212                 // not
213                 if (labs(sp->z - tp->z) > zdiff)
214                 {
215                     continue;
216                 }
217 
218                 // determine if the track leads in the direction we want it
219                 // to
220                 if (player_dir == TOWARD_PLAYER)
221                 {
222                     if (!TrackTowardPlayer(u->tgt_sp, t, tp))
223                     {
224                         continue;
225                     }
226                 }
227                 else if (player_dir == AWAY_FROM_PLAYER)
228                 {
229                     if (TrackTowardPlayer(u->tgt_sp, t, tp))
230                     {
231                         continue;
232                     }
233                 }
234 
235                 // make sure the start distance is closer than the end
236                 // distance
237                 if (!TrackStartCloserThanEnd(SpriteNum, t, tp))
238                 {
239                     continue;
240                 }
241 
242                 near_dist = dist;
243                 near_track = t;
244                 near_tp = tp;
245 
246                 *track_point_num = end_point[i];
247                 *track_dir = i ? -1 : 1;
248             }
249         }
250 
251     }
252 
253     if (near_dist < 15000)
254     {
255         // get the sector number of the point
256         COVERupdatesector(near_tp->x, near_tp->y, &track_sect);
257 
258         // if can see the point, return the track number
259         if (FAFcansee(sp->x, sp->y, sp->z - Z(16), sp->sectnum, near_tp->x, near_tp->y, sector[track_sect].floorz - Z(32), track_sect))
260         {
261             //DSPRINTF(ds,"Found track point in sector %d\n",track_sect);
262             MONO_PRINT(ds);
263             return near_track - &Track[0];
264         }
265 
266         return -1;
267     }
268     else
269     {
270         return -1;
271     }
272 }
273 
274 
275 void
NextTrackPoint(SECTOR_OBJECTp sop)276 NextTrackPoint(SECTOR_OBJECTp sop)
277 {
278     sop->point += sop->dir;
279 
280     if (sop->point > Track[sop->track].NumPoints - 1)
281         sop->point = 0;
282 
283     if (sop->point < 0)
284         sop->point = Track[sop->track].NumPoints - 1;
285 }
286 
287 
288 void
NextActorTrackPoint(short SpriteNum)289 NextActorTrackPoint(short SpriteNum)
290 {
291     USERp u = User[SpriteNum];
292 
293     u->point += u->track_dir;
294 
295     if (u->point > Track[u->track].NumPoints - 1)
296         u->point = 0;
297 
298     if (u->point < 0)
299         u->point = Track[u->track].NumPoints - 1;
300 }
301 
302 void
TrackAddPoint(TRACKp t,TRACK_POINTp tp,short SpriteNum)303 TrackAddPoint(TRACKp t, TRACK_POINTp tp, short SpriteNum)
304 {
305     SPRITEp sp = &sprite[SpriteNum];
306     TRACK_POINTp tpoint = (tp + t->NumPoints);
307 
308     //    //DSPRINTF(ds,"3 ndx = %d, numpoints = %d", t - Track, t->NumPoints);
309     //    MONO_PRINT(ds);
310 
311     tpoint->x = sp->x;
312     tpoint->y = sp->y;
313     tpoint->z = sp->z;
314     tpoint->ang = sp->ang;
315     tpoint->tag_low = sp->lotag;
316     tpoint->tag_high = sp->hitag;
317 
318     t->NumPoints++;
319 
320     KillSprite(SpriteNum);
321 }
322 
323 int
TrackClonePoint(short SpriteNum)324 TrackClonePoint(short SpriteNum)
325 {
326     SPRITEp sp = &sprite[SpriteNum], np;
327     short New;
328 
329     New = COVERinsertsprite(sp->sectnum, sp->statnum);
330 
331     ASSERT(New != -1);
332 
333     np = &sprite[New];
334 
335     np->cstat = np->extra = 0;
336     np->x = sp->x;
337     np->y = sp->y;
338     np->z = sp->z;
339     np->ang = sp->ang;
340     np->lotag = sp->lotag;
341     np->hitag = sp->hitag;
342 
343     return New;
344 }
345 
QuickJumpSetup(short stat,short lotag,short type)346 void QuickJumpSetup(short stat, short lotag, short type)
347 {
348     short SpriteNum = 0, NextSprite, ndx;
349     TRACK_POINTp tp;
350     TRACKp t;
351     SPRITEp nsp;
352     short start_sprite, end_sprite;
353 
354     // make short quick jump tracks
355     TRAVERSE_SPRITE_STAT(headspritestat[stat], SpriteNum, NextSprite)
356     {
357 
358         // find an open track
359         for (ndx = 0; ndx < MAX_TRACKS; ndx++)
360         {
361             if (Track[ndx].NumPoints == 0)
362                 break;
363         }
364 
365         ASSERT(ndx < MAX_TRACKS);
366 
367         ////DSPRINTF(ds,"1 ndx = %d, numpoints = %d\n", ndx, Track[ndx].NumPoints);
368         //MONO_PRINT(ds);
369 
370         FreeMem(Track[ndx].TrackPoint);
371         Track[ndx].TrackPoint = (TRACK_POINTp)CallocMem((4 * sizeof(TRACK_POINT)), 1);
372 
373         tp = Track[ndx].TrackPoint;
374         t = &Track[ndx];
375 
376         // set track type
377         SET(t->ttflags, BIT(type));
378         t->flags = 0;
379 
380         // clone point
381         end_sprite = TrackClonePoint(SpriteNum);
382         start_sprite = TrackClonePoint(SpriteNum);
383 
384         // add start point
385         nsp = &sprite[start_sprite];
386         nsp->lotag = TRACK_START;
387         nsp->hitag = 0;
388         TrackAddPoint(t, tp, start_sprite);
389 
390         ////DSPRINTF(ds,"2 ndx = %d, numpoints = %d\n", ndx, Track[ndx].NumPoints);
391         //MONO_PRINT(ds);
392 
393         // add jump point
394         nsp = &sprite[SpriteNum];
395         nsp->x += 64 * (int) sintable[NORM_ANGLE(nsp->ang + 512)] >> 14;
396         nsp->y += 64 * (int) sintable[nsp->ang] >> 14;
397         nsp->lotag = lotag;
398         TrackAddPoint(t, tp, SpriteNum);
399 
400         // add end point
401         nsp = &sprite[end_sprite];
402         nsp->x += 2048 * (int) sintable[NORM_ANGLE(nsp->ang + 512)] >> 14;
403         nsp->y += 2048 * (int) sintable[nsp->ang] >> 14;
404         nsp->lotag = TRACK_END;
405         nsp->hitag = 0;
406         TrackAddPoint(t, tp, end_sprite);
407     }
408 }
409 
410 
QuickScanSetup(short stat,short lotag,short type)411 void QuickScanSetup(short stat, short lotag, short type)
412 {
413     short SpriteNum = 0, NextSprite, ndx;
414     TRACK_POINTp tp;
415     TRACKp t;
416     SPRITEp nsp;
417     short start_sprite, end_sprite;
418 
419     // make short quick jump tracks
420     TRAVERSE_SPRITE_STAT(headspritestat[stat], SpriteNum, NextSprite)
421     {
422 
423         // find an open track
424         for (ndx = 0; ndx < MAX_TRACKS; ndx++)
425         {
426             if (Track[ndx].NumPoints == 0)
427                 break;
428         }
429 
430         ASSERT(ndx < MAX_TRACKS);
431 
432         // save space for 3 points
433         FreeMem(Track[ndx].TrackPoint);
434         Track[ndx].TrackPoint = (TRACK_POINTp)CallocMem((4 * sizeof(TRACK_POINT)), 1);
435 
436         ASSERT(Track[ndx].TrackPoint != NULL);
437 
438         tp = Track[ndx].TrackPoint;
439         t = &Track[ndx];
440 
441         // set track type
442         SET(t->ttflags, BIT(type));
443         t->flags = 0;
444 
445         // clone point
446         end_sprite = TrackClonePoint(SpriteNum);
447         start_sprite = TrackClonePoint(SpriteNum);
448 
449         // add start point
450         nsp = &sprite[start_sprite];
451         nsp->lotag = TRACK_START;
452         nsp->hitag = 0;
453         nsp->x += 64 * (int) sintable[NORM_ANGLE(nsp->ang + 1024 + 512)] >> 14;
454         nsp->y += 64 * (int) sintable[NORM_ANGLE(nsp->ang + 1024)] >> 14;
455         TrackAddPoint(t, tp, start_sprite);
456 
457         // add jump point
458         nsp = &sprite[SpriteNum];
459         nsp->lotag = lotag;
460         TrackAddPoint(t, tp, SpriteNum);
461 
462         // add end point
463         nsp = &sprite[end_sprite];
464         nsp->x += 64 * (int) sintable[NORM_ANGLE(nsp->ang + 512)] >> 14;
465         nsp->y += 64 * (int) sintable[nsp->ang] >> 14;
466         nsp->lotag = TRACK_END;
467         nsp->hitag = 0;
468         TrackAddPoint(t, tp, end_sprite);
469     }
470 }
471 
QuickExitSetup(short stat,short type)472 void QuickExitSetup(short stat, short type)
473 {
474     short SpriteNum = 0, NextSprite, ndx;
475     TRACK_POINTp tp;
476     TRACKp t;
477     SPRITEp nsp;
478     short start_sprite, end_sprite;
479 
480     TRAVERSE_SPRITE_STAT(headspritestat[stat], SpriteNum, NextSprite)
481     {
482 
483         // find an open track
484         for (ndx = 0; ndx < MAX_TRACKS; ndx++)
485         {
486             if (Track[ndx].NumPoints == 0)
487                 break;
488         }
489 
490         ASSERT(ndx < MAX_TRACKS);
491 
492         // save space for 3 points
493         FreeMem(Track[ndx].TrackPoint);
494         Track[ndx].TrackPoint = (TRACK_POINTp)CallocMem((4 * sizeof(TRACK_POINT)), 1);
495 
496         ASSERT(Track[ndx].TrackPoint != NULL);
497 
498         tp = Track[ndx].TrackPoint;
499         t = &Track[ndx];
500 
501         // set track type
502         SET(t->ttflags, BIT(type));
503         t->flags = 0;
504 
505         // clone point
506         end_sprite = TrackClonePoint(SpriteNum);
507         start_sprite = TrackClonePoint(SpriteNum);
508 
509         // add start point
510         nsp = &sprite[start_sprite];
511         nsp->lotag = TRACK_START;
512         nsp->hitag = 0;
513         TrackAddPoint(t, tp, start_sprite);
514 
515         KillSprite(SpriteNum);
516 
517         // add end point
518         nsp = &sprite[end_sprite];
519         nsp->x += 1024 * (int) sintable[NORM_ANGLE(nsp->ang + 512)] >> 14;
520         nsp->y += 1024 * (int) sintable[nsp->ang] >> 14;
521         nsp->lotag = TRACK_END;
522         nsp->hitag = 0;
523         TrackAddPoint(t, tp, end_sprite);
524     }
525 }
526 
QuickLadderSetup(short stat,short lotag,short type)527 void QuickLadderSetup(short stat, short lotag, short type)
528 {
529     short SpriteNum = 0, NextSprite, ndx;
530     TRACK_POINTp tp;
531     TRACKp t;
532     SPRITEp nsp;
533     short start_sprite, end_sprite;
534 
535     TRAVERSE_SPRITE_STAT(headspritestat[stat], SpriteNum, NextSprite)
536     {
537 
538         // find an open track
539         for (ndx = 0; ndx < MAX_TRACKS; ndx++)
540         {
541             if (Track[ndx].NumPoints == 0)
542                 break;
543         }
544 
545         ASSERT(ndx < MAX_TRACKS);
546 
547         // save space for 3 points
548         FreeMem(Track[ndx].TrackPoint);
549         Track[ndx].TrackPoint = (TRACK_POINTp)CallocMem((4 * sizeof(TRACK_POINT)), 1);
550 
551         ASSERT(Track[ndx].TrackPoint != NULL);
552 
553         tp = Track[ndx].TrackPoint;
554         t = &Track[ndx];
555 
556         // set track type
557         SET(t->ttflags, BIT(type));
558         t->flags = 0;
559 
560         // clone point
561         end_sprite = TrackClonePoint(SpriteNum);
562         start_sprite = TrackClonePoint(SpriteNum);
563 
564         // add start point
565         nsp = &sprite[start_sprite];
566         nsp->lotag = TRACK_START;
567         nsp->hitag = 0;
568         nsp->x += MOVEx(256,nsp->ang + 1024);
569         nsp->y += MOVEy(256,nsp->ang + 1024);
570         TrackAddPoint(t, tp, start_sprite);
571 
572         // add climb point
573         nsp = &sprite[SpriteNum];
574         nsp->lotag = lotag;
575         TrackAddPoint(t, tp, SpriteNum);
576 
577         // add end point
578         nsp = &sprite[end_sprite];
579         nsp->x += MOVEx(512,nsp->ang);
580         nsp->y += MOVEy(512,nsp->ang);
581         nsp->lotag = TRACK_END;
582         nsp->hitag = 0;
583         TrackAddPoint(t, tp, end_sprite);
584     }
585 }
586 
587 
588 void
TrackSetup(void)589 TrackSetup(void)
590 {
591     short SpriteNum = 0, NextSprite, ndx;
592     TRACK_POINTp tp;
593     TRACKp t;
594     TRACK_POINTp New;
595     int size;
596 
597     // put points on track
598     for (ndx = 0; ndx < MAX_TRACKS; ndx++)
599     {
600         if (headspritestat[STAT_TRACK + ndx] == -1)
601         {
602             // for some reason I need at least one record allocated
603             // can't remember why at this point
604             Track[ndx].TrackPoint = (TRACK_POINTp)CallocMem(sizeof(TRACK_POINT) * 1, 1);
605             continue;
606         }
607 
608         ASSERT(Track[ndx].TrackPoint == NULL);
609 
610         // make the track array rather large.  I'll resize it to correct size
611         // later.
612         Track[ndx].TrackPoint = (TRACK_POINTp)CallocMem(sizeof(TRACK_POINT) * 500, 1);
613 
614         ASSERT(Track[ndx].TrackPoint != NULL);
615 
616         tp = Track[ndx].TrackPoint;
617         t = &Track[ndx];
618 
619         // find the first point and save it
620         TRAVERSE_SPRITE_STAT(headspritestat[STAT_TRACK + ndx], SpriteNum, NextSprite)
621         {
622             if (LOW_TAG_SPRITE(SpriteNum) == TRACK_START)
623             {
624                 ASSERT(t->NumPoints == 0);
625 
626                 TrackAddPoint(t, tp, SpriteNum);
627                 break;
628             }
629         }
630 
631         // didn't find the start point of the track
632         if (t->NumPoints == 0)
633         {
634             int i, nexti;
635             auto const sp = (uspritetype const *)&sprite[headspritestat[STAT_TRACK+ndx]];
636             buildprintf("WARNING: Did not find first point of Track Number %d, x %d, y %d\n", ndx, sp->x, sp->y);
637             for (i=headspritestat[STAT_TRACK+ndx]; i>=0; i=nexti)
638             {
639                 // neuter the track's sprite list
640                 nexti = nextspritestat[i];
641                 deletesprite(i);
642             }
643             continue;
644         }
645 
646         // set up flags for track types
647         if (tp->tag_low == TRACK_START && tp->tag_high)
648             SET(t->ttflags, BIT(tp->tag_high));
649 
650         // while there are still sprites on this status list
651         while (headspritestat[STAT_TRACK + ndx] != -1)
652         {
653             short next_sprite = -1;
654             int dist, low_dist = 999999;
655 
656             // find the closest point to the last point
657             TRAVERSE_SPRITE_STAT(headspritestat[STAT_TRACK + ndx], SpriteNum, NextSprite)
658             {
659                 dist = Distance((tp + t->NumPoints - 1)->x, (tp + t->NumPoints - 1)->y, sprite[SpriteNum].x, sprite[SpriteNum].y);
660 
661                 if (dist < low_dist)
662                 {
663                     next_sprite = SpriteNum;
664                     low_dist = dist;
665                 }
666 
667             }
668 
669             // save the closest one off and kill it
670             if (next_sprite != -1)
671             {
672                 ASSERT(low_dist < 20000);
673                 TrackAddPoint(t, tp, next_sprite);
674             }
675 
676         }
677 
678         size = (Track[ndx].NumPoints + 1) * sizeof(TRACK_POINT);
679         New = (TRACK_POINTp)CallocMem(size, 1);
680         memcpy(New, Track[ndx].TrackPoint, size);
681         FreeMem(Track[ndx].TrackPoint);
682         Track[ndx].TrackPoint = New;
683 
684         ASSERT(Track[ndx].TrackPoint != NULL);
685     }
686 
687     QuickJumpSetup(STAT_QUICK_JUMP, TRACK_ACTOR_QUICK_JUMP, TT_JUMP_UP);
688     QuickJumpSetup(STAT_QUICK_JUMP_DOWN, TRACK_ACTOR_QUICK_JUMP_DOWN, TT_JUMP_DOWN);
689     QuickJumpSetup(STAT_QUICK_SUPER_JUMP, TRACK_ACTOR_QUICK_SUPER_JUMP, TT_SUPER_JUMP_UP);
690     QuickScanSetup(STAT_QUICK_SCAN, TRACK_ACTOR_QUICK_SCAN, TT_SCAN);
691     QuickLadderSetup(STAT_QUICK_LADDER, TRACK_ACTOR_CLIMB_LADDER, TT_LADDER);
692     QuickExitSetup(STAT_QUICK_EXIT, TT_EXIT);
693     QuickJumpSetup(STAT_QUICK_OPERATE, TRACK_ACTOR_QUICK_OPERATE, TT_OPERATE);
694     QuickJumpSetup(STAT_QUICK_DUCK, TRACK_ACTOR_QUICK_DUCK, TT_DUCK_N_SHOOT);
695     QuickJumpSetup(STAT_QUICK_DEFEND, TRACK_ACTOR_QUICK_DEFEND, TT_HIDE_N_SHOOT);
696 
697 }
698 
699 SPRITEp
FindBoundSprite(short tag)700 FindBoundSprite(short tag)
701 {
702     short sn, next_sn;
703 
704     TRAVERSE_SPRITE_STAT(headspritestat[STAT_ST1], sn, next_sn)
705     {
706         if (sprite[sn].hitag == tag)
707         {
708             return &sprite[sn];
709         }
710     }
711 
712     return NULL;
713 }
714 
715 
716 void
SectorObjectSetupBounds(SECTOR_OBJECTp sop)717 SectorObjectSetupBounds(SECTOR_OBJECTp sop)
718 {
719     int xlow, ylow, xhigh, yhigh;
720     short sp_num, next_sp_num, startwall, endwall;
721     int i, k, j;
722     SPRITEp BoundSprite;
723     SWBOOL FoundOutsideLoop = FALSE;
724     SWBOOL SectorInBounds;
725     SECTORp *sectp;
726     USERp u = User[sop->sp_child - sprite];
727 
728     static unsigned char StatList[] =
729     {
730         STAT_DEFAULT, STAT_MISC, STAT_ITEM, STAT_TRAP,
731         STAT_SPAWN_SPOT, STAT_SOUND_SPOT, STAT_WALL_MOVE,
732         STAT_WALLBLOOD_QUEUE,
733         STAT_SPRITE_HIT_MATCH,
734         STAT_AMBIENT,
735         STAT_DELETE_SPRITE,
736         STAT_SPAWN_TRIGGER, // spawing monster trigger - for Randy's bullet train.
737         //STAT_FLOOR_PAN, STAT_CEILING_PAN
738     };
739 
740     // search for 2 sprite bounding tags
741 
742     BoundSprite = FindBoundSprite(500 + ((sop - SectorObject) * 5));
743 
744     //DSPRINTF(ds,"tagnum %d, so num %d",500 + ((sop - SectorObject) * 5), sop - SectorObject);
745     MONO_PRINT(ds);
746 
747     ASSERT(BoundSprite != NULL);
748     xlow = BoundSprite->x;
749     ylow = BoundSprite->y;
750 
751     KillSprite(BoundSprite - sprite);
752 
753     BoundSprite = FindBoundSprite(501 + ((sop - SectorObject) * 5));
754     ASSERT(BoundSprite != NULL);
755     xhigh = BoundSprite->x;
756     yhigh = BoundSprite->y;
757 
758     KillSprite(BoundSprite - sprite);
759 
760     // set radius for explosion checking - based on bounding box
761     u->Radius = DIV4((xhigh - xlow) + (yhigh - ylow));
762     u->Radius -= DIV4(u->Radius); // trying to get it a good size
763 
764     // search for center sprite if it exists
765 
766     BoundSprite = FindBoundSprite(SECT_SO_CENTER);
767     if (BoundSprite)
768     {
769         sop->xmid = BoundSprite->x;
770         sop->ymid = BoundSprite->y;
771         sop->zmid = BoundSprite->z;
772         KillSprite(BoundSprite - sprite);
773     }
774 
775 #if 0
776     // look for players on sector object
777     PLAYERp pp;
778     short pnum;
779     TRAVERSE_CONNECT(pnum)
780     {
781         pp = &Player[pnum];
782 
783         if (pp->posx > xlow && pp->posx < xhigh && pp->posy > ylow && pp->posy < yhigh)
784         {
785             pp->RevolveQ16Ang = pp->q16ang;
786             pp->RevolveX = pp->posx;
787             pp->RevolveY = pp->posy;
788             pp->RevolveDeltaAng = 0;
789             SET(pp->Flags, PF_PLAYER_RIDING);
790 
791             pp->sop_riding = sop;
792         }
793     }
794 #endif
795 
796 
797     // look through all sectors for whole sectors that are IN bounds
798     for (k = 0; k < numsectors; k++)
799     {
800         startwall = sector[k].wallptr;
801         endwall = startwall + sector[k].wallnum - 1;
802 
803         SectorInBounds = TRUE;
804 
805         for (j = startwall; j <= endwall; j++)
806         {
807             // all walls have to be in bounds to be in sector object
808             if (!(wall[j].x > xlow && wall[j].x < xhigh && wall[j].y > ylow && wall[j].y < yhigh))
809             {
810                 SectorInBounds = FALSE;
811                 break;
812             }
813         }
814 
815         if (SectorInBounds)
816         {
817             sop->sector[sop->num_sectors] = k;
818             sop->sectp[sop->num_sectors] = &sector[k];
819 
820             // all sectors in sector object have this flag set - for colision
821             // detection and recognition
822             SET(sector[k].extra, SECTFX_SECTOR_OBJECT);
823 
824             sop->zorig_floor[sop->num_sectors] = sector[k].floorz;
825             sop->zorig_ceiling[sop->num_sectors] = sector[k].ceilingz;
826 
827             if (TEST(sector[k].extra, SECTFX_SINK))
828                 sop->zorig_floor[sop->num_sectors] += Z(SectUser[k]->depth);
829 
830             // lowest and highest floorz's
831             if (sector[k].floorz > sop->floor_loz)
832                 sop->floor_loz = sector[k].floorz;
833 
834             if (sector[k].floorz < sop->floor_hiz)
835                 sop->floor_hiz = sector[k].floorz;
836 
837             sop->num_sectors++;
838         }
839 
840         ASSERT((uint16_t)sop->num_sectors < SIZ(SectorObject[0].sector));
841     }
842 
843     //
844     // Make sure every sector object has an outer loop tagged - important
845     //
846 
847     FoundOutsideLoop = FALSE;
848 
849     for (sectp = sop->sectp, j = 0; *sectp; sectp++, j++)
850     {
851         startwall = (*sectp)->wallptr;
852         endwall = startwall + (*sectp)->wallnum - 1;
853 
854         // move all walls in sectors
855         for (k = startwall; k <= endwall; k++)
856         {
857             // for morph point - tornado style
858             if (wall[k].lotag == TAG_WALL_ALIGN_SLOPE_TO_POINT)
859                 sop->morph_wall_point = k;
860 
861             if (wall[k].extra && TEST(wall[k].extra, WALLFX_LOOP_OUTER))
862                 FoundOutsideLoop = TRUE;
863 
864             // each wall has this set - for collision detection
865             SET(wall[k].extra, WALLFX_SECTOR_OBJECT|WALLFX_DONT_STICK);
866             uint16_t const nextwall = wall[k].nextwall;
867             if (nextwall < MAXWALLS)
868                 SET(wall[nextwall].extra, WALLFX_SECTOR_OBJECT|WALLFX_DONT_STICK);
869         }
870     }
871 
872     if (!FoundOutsideLoop)
873     {
874         TerminateGame();
875         printf("Forgot to tag outer loop for Sector Object #%d", (int)(sop - SectorObject));
876         exit(1);
877     }
878 
879     so_addinterpolation(sop);
880 
881     for (i = 0; i < (int)SIZ(StatList); i++)
882     {
883         TRAVERSE_SPRITE_STAT(headspritestat[StatList[i]], sp_num, next_sp_num)
884         {
885             SPRITEp sp = &sprite[sp_num];
886             USERp u;
887 
888             ASSERT(sp_num != -1);
889 
890             if (sp->x > xlow && sp->x < xhigh && sp->y > ylow && sp->y < yhigh)
891             {
892                 // some delete sprites ride others don't
893                 if (sp->statnum == STAT_DELETE_SPRITE)
894                 {
895                     if (!TEST_BOOL2(sp))
896                         continue;
897                 }
898 
899                 if (User[sp_num] == NULL)
900                     u = SpawnUser(sp_num, 0, NULL);
901                 else
902                     u = User[sp_num];
903 
904                 u->RotNum = 0;
905 
906                 u->ox = sp->x;
907                 u->oy = sp->y;
908                 u->oz = sp->z;
909 
910                 switch (sp->statnum)
911                 {
912                 case STAT_WALL_MOVE:
913                     ////DSPRINTF(ds,"Damage Wall attached ");
914                     //MONO_PRINT(ds);
915                     break;
916                 case STAT_DEFAULT:
917                     switch (sp->hitag)
918                     {
919                     case SO_CLIP_BOX:
920                     {
921                         short ang2;
922                         sop->clipdist = 0;
923                         sop->clipbox_dist[sop->clipbox_num] = sp->lotag;
924                         sop->clipbox_xoff[sop->clipbox_num] = sop->xmid - sp->x;
925                         sop->clipbox_yoff[sop->clipbox_num] = sop->ymid - sp->y;
926 
927                         sop->clipbox_vdist[sop->clipbox_num] = ksqrt(SQ(sop->xmid - sp->x) + SQ(sop->ymid - sp->y));
928 
929                         ang2 = getangle(sp->x - sop->xmid, sp->y - sop->ymid);
930                         sop->clipbox_ang[sop->clipbox_num] = GetDeltaAngle(sop->ang, ang2);
931 
932                         sop->clipbox_num++;
933                         KillSprite(sp_num);
934 
935 
936                         goto cont;
937                     }
938                     case SO_SHOOT_POINT:
939                         sp->owner = -1;
940                         change_sprite_stat(sp_num, STAT_SO_SHOOT_POINT);
941                         RESET(sp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
942                         break;
943                     default:
944                         break;
945                     }
946                     break;
947                 }
948 
949 
950                 u->sx = sop->xmid - sp->x;
951                 u->sy = sop->ymid - sp->y;
952                 u->sz = sector[sop->mid_sector].floorz - sp->z;
953 
954                 SET(u->Flags, SPR_SO_ATTACHED);
955 
956                 u->sang = sp->ang;
957                 u->spal = sp->pal;
958 
959                 // search SO's sectors to make sure that it is not on a
960                 // sector
961 
962                 // place all sprites on list
963                 uint16_t sn;
964                 for (sn = 0; sn < (int)SIZ(sop->sp_num); sn++)
965                 {
966                     if (sop->sp_num[sn] == -1)
967                         break;
968                 }
969 
970                 ASSERT(sn < SIZ(sop->sp_num) - 1);
971 
972                 sop->sp_num[sn] = sp_num;
973                 so_setspriteinterpolation(sop, sp);
974 
975 
976                 if (!TEST(sop->flags, SOBJ_SPRITE_OBJ))
977                 {
978                     // determine if sprite is on a SO sector - set flag if
979                     // true
980                     for (j = 0; j < sop->num_sectors; j++)
981                     {
982                         if (sop->sector[j] == sp->sectnum)
983                         {
984                             SET(u->Flags, SPR_ON_SO_SECTOR);
985                             u->sz = sector[sp->sectnum].floorz - sp->z;
986                             break;
987                         }
988                     }
989                 }
990             }
991 
992 cont:
993             continue;
994         }
995     }
996 
997     // for SPRITE OBJECT sprites, set the u->sz value to the difference
998     // between the zmid and the sp->z
999     if (TEST(sop->flags, SOBJ_SPRITE_OBJ))
1000     {
1001         SPRITEp sp;
1002         USERp u;
1003         int zmid = -9999999;
1004 
1005         // choose the lowest sprite for the zmid
1006         for (i = 0; sop->sp_num[i] != -1; i++)
1007         {
1008             sp = &sprite[sop->sp_num[i]];
1009             u = User[sop->sp_num[i]];
1010 
1011             if (sp->z > zmid)
1012                 zmid = sp->z;
1013         }
1014 
1015         ASSERT(zmid != -9999999);
1016 
1017         sop->zmid = zmid;
1018 
1019         for (i = 0; sop->sp_num[i] != -1; i++)
1020         {
1021             sp = &sprite[sop->sp_num[i]];
1022             u = User[sop->sp_num[i]];
1023 
1024             u->sz = sop->zmid - sp->z;
1025         }
1026 
1027     }
1028 }
1029 
1030 
1031 void
SetupSectorObject(short sectnum,short tag)1032 SetupSectorObject(short sectnum, short tag)
1033 {
1034     SPRITEp sp;
1035     SECTOR_OBJECTp sop;
1036     short object_num, SpriteNum, NextSprite;
1037     short j;
1038     short New;
1039     USERp u;
1040 
1041     tag -= (TAG_OBJECT_CENTER - 1);
1042     // sector[sectnum].lotag = tag;
1043 
1044     object_num = tag / 5;
1045     sop = &SectorObject[object_num];
1046 
1047     // initialize stuff first time through
1048     if (sop->num_sectors == -1)
1049     {
1050         void DoTornadoObject(SECTOR_OBJECTp sop);
1051         void MorphTornado(SECTOR_OBJECTp sop);
1052         void MorphFloor(SECTOR_OBJECTp sop);
1053         void ScaleSectorObject(SECTOR_OBJECTp sop);
1054         void DoAutoTurretObject(SECTOR_OBJECTp sop);
1055 
1056         memset(sop->sectp, 0, sizeof(sop->sectp));
1057         sop->crush_z = 0;
1058         sop->drive_angspeed = 0;
1059         sop->drive_angslide = 0;
1060         sop->drive_slide = 0;
1061         sop->drive_speed = 0;
1062         sop->num_sectors = 0;
1063         sop->update = 15000;
1064         sop->flags = 0;
1065         sop->clipbox_num = 0;
1066         sop->bob_amt = 0;
1067         sop->vel_rate = 6;
1068         sop->z_rate = 256;
1069         sop->zdelta = sop->z_tgt = 0;
1070         sop->wait_tics = 0;
1071         sop->spin_speed = 0;
1072         sop->spin_ang = 0;
1073         sop->ang_orig = 0;
1074         sop->clipdist = 1024;
1075         sop->target_dist = 0;
1076         sop->turn_speed = 4;
1077         sop->floor_loz = -9999999;
1078         sop->floor_hiz = 9999999;
1079         sop->player_xoff = sop->player_yoff = 0;
1080         sop->ang_tgt = sop->ang = sop->ang_moving = 0;
1081         sop->op_main_sector = -1;
1082         sop->ram_damage = 0;
1083         sop->max_damage = -9999;
1084 
1085         sop->scale_type = SO_SCALE_NONE;
1086         sop->scale_dist = 0;
1087         sop->scale_speed = 20;
1088         sop->scale_dist_min = -1024;
1089         sop->scale_dist_max = 1024;
1090         sop->scale_rand_freq = 64>>3;
1091 
1092         sop->scale_x_mult = 256;
1093         sop->scale_y_mult = 256;
1094 
1095         sop->morph_ang = RANDOM_P2(2048);
1096         sop->morph_z_speed = 20;
1097         sop->morph_speed = 32;
1098         sop->morph_dist_max = 1024;
1099         sop->morph_rand_freq = 64;
1100         sop->morph_dist = 0;
1101         sop->morph_xoff = 0;
1102         sop->morph_yoff = 0;
1103 
1104         sop->PreMoveAnimator = NULL;
1105         sop->PostMoveAnimator = NULL;
1106         sop->Animator = NULL;
1107     }
1108 
1109     switch (tag % 5)
1110     {
1111     case TAG_OBJECT_CENTER - 500:
1112 
1113         sop->mid_sector = sectnum;
1114         SectorMidPoint(sectnum, &sop->xmid, &sop->ymid, &sop->zmid);
1115         //sop->zmid = sector[sectnum].floorz;
1116         //sop->zmid = DIV2(sector[sectnum].floorz + sector[sectnum].ceilingz);
1117 
1118         sop->dir = 1;
1119         sop->track = HIGH_TAG(sectnum);
1120 
1121         // spawn a sprite to make it easier to integrate with sprite routines
1122         New = SpawnSprite(STAT_SO_SP_CHILD, 0, NULL, sectnum,
1123                           sop->xmid, sop->ymid, sop->zmid, 0, 0);
1124         sop->sp_child = &sprite[New];
1125         u = User[New];
1126         u->sop_parent = sop;
1127         SET(u->Flags2, SPR2_SPRITE_FAKE_BLOCK); // for damage test
1128 
1129         // check for any ST1 sprites laying on the center sector
1130         TRAVERSE_SPRITE_SECT(headspritesect[sectnum], SpriteNum, NextSprite)
1131         {
1132             sp = &sprite[SpriteNum];
1133 
1134             if (sp->statnum == STAT_ST1)
1135             {
1136                 switch (sp->hitag)
1137                 {
1138                 case SO_SCALE_XY_MULT:
1139                     if (SP_TAG5(sp))
1140                         sop->scale_x_mult = SP_TAG5(sp);
1141                     if (SP_TAG6(sp))
1142                         sop->scale_y_mult = SP_TAG6(sp);
1143                     KillSprite(SpriteNum);
1144                     break;
1145 
1146                 case SO_SCALE_POINT_INFO:
1147 
1148                     memset(sop->scale_point_dist,0,sizeof(sop->scale_point_dist));
1149                     sop->scale_point_base_speed = SP_TAG2(sp);
1150                     for (j = 0; j < (int)SIZ(sop->scale_point_speed); j++)
1151                     {
1152                         sop->scale_point_speed[j] = SP_TAG2(sp);
1153                     }
1154 
1155                     if (SP_TAG4(sp))
1156                         sop->scale_point_rand_freq = (uint8_t)SP_TAG4(sp);
1157                     else
1158                         sop->scale_point_rand_freq = 64;
1159 
1160                     sop->scale_point_dist_min = -SP_TAG5(sp);
1161                     sop->scale_point_dist_max = SP_TAG6(sp);
1162                     KillSprite(SpriteNum);
1163                     break;
1164 
1165                 case SO_SCALE_INFO:
1166                     SET(sop->flags, SOBJ_DYNAMIC);
1167                     sop->scale_speed = SP_TAG2(sp);
1168                     sop->scale_dist_min = -SP_TAG5(sp);
1169                     sop->scale_dist_max = SP_TAG6(sp);
1170 
1171                     sop->scale_type = SP_TAG4(sp);
1172                     sop->scale_active_type = SP_TAG7(sp);
1173 
1174                     if (SP_TAG8(sp))
1175                         sop->scale_rand_freq = (uint8_t)SP_TAG8(sp);
1176                     else
1177                         sop->scale_rand_freq = 64>>3;
1178 
1179                     if (SP_TAG3(sp) == 0)
1180                         sop->scale_dist = sop->scale_dist_min;
1181                     else if (SP_TAG3(sp) == 1)
1182                         sop->scale_dist = sop->scale_dist_max;
1183 
1184                     KillSprite(SpriteNum);
1185                     break;
1186 
1187                 case SPAWN_SPOT:
1188                     if (sp->clipdist == 3)
1189                     {
1190                         USERp u;
1191                         change_sprite_stat(SpriteNum, STAT_NO_STATE);
1192                         u = SpawnUser(SpriteNum, 0, NULL);
1193                         u->ActorActionFunc = NULL;
1194                     }
1195                     break;
1196 
1197                 case SO_AUTO_TURRET:
1198                     sop->Animator = DoAutoTurretObject;
1199                     KillSprite(SpriteNum);
1200                     break;
1201 
1202                 case SO_TORNADO:
1203                     if (SW_SHAREWARE) break;
1204                     sop->vel = 120;
1205                     SET(sop->flags, SOBJ_DYNAMIC);
1206                     sop->scale_type = SO_SCALE_CYCLE;
1207                     // spin stuff
1208                     sop->spin_speed = 16;
1209                     sop->last_ang = sop->ang;
1210                     // animators
1211                     sop->Animator = DoTornadoObject;
1212                     sop->PreMoveAnimator = ScaleSectorObject;
1213                     sop->PostMoveAnimator = MorphTornado;
1214                     // clip
1215                     sop->clipdist = 2500;
1216                     // morph point
1217                     sop->morph_speed = 16;
1218                     sop->morph_z_speed = 6;
1219                     sop->morph_dist_max = 1024;
1220                     sop->morph_rand_freq = 8;
1221                     sop->scale_dist_min = -768;
1222                     KillSprite(SpriteNum);
1223                     break;
1224                 case SO_FLOOR_MORPH:
1225                     if (SW_SHAREWARE) break;
1226                     SET(sop->flags, SOBJ_DYNAMIC);
1227                     sop->scale_type = SO_SCALE_NONE;
1228                     sop->morph_speed = 120;
1229                     sop->morph_z_speed = 7;
1230                     sop->PostMoveAnimator = MorphFloor;
1231                     sop->morph_dist_max = 4000;
1232                     sop->morph_rand_freq = 8;
1233                     KillSprite(SpriteNum);
1234                     break;
1235 
1236                 case SO_AMOEBA:
1237                     SET(sop->flags, SOBJ_DYNAMIC);
1238                     //sop->scale_type = SO_SCALE_CYCLE;
1239                     sop->scale_type = SO_SCALE_RANDOM_POINT;
1240                     sop->PreMoveAnimator = ScaleSectorObject;
1241 
1242                     memset(sop->scale_point_dist,0,sizeof(sop->scale_point_dist));;
1243                     sop->scale_point_base_speed = SCALE_POINT_SPEED;
1244                     for (j = 0; j < (int)SIZ(sop->scale_point_speed); j++)
1245                         sop->scale_point_speed[j] = SCALE_POINT_SPEED;
1246 
1247                     sop->scale_point_dist_min = -256;
1248                     sop->scale_point_dist_max = 256;
1249                     sop->scale_point_rand_freq = 32;
1250                     KillSprite(SpriteNum);
1251                     break;
1252                 case SO_MAX_DAMAGE:
1253                     u->MaxHealth = SP_TAG2(sp);
1254                     if (SP_TAG5(sp) != 0)
1255                         sop->max_damage = SP_TAG5(sp);
1256                     else
1257                         sop->max_damage = u->MaxHealth;
1258 
1259                     switch (sp->clipdist)
1260                     {
1261                     case 0:
1262                         break;
1263                     case 1:
1264                         SET(sop->flags, SOBJ_DIE_HARD);
1265                         break;
1266                     }
1267                     KillSprite(SpriteNum);
1268                     break;
1269 
1270                 case SO_DRIVABLE_ATTRIB:
1271 
1272                     sop->drive_angspeed = SP_TAG2(sp);
1273                     sop->drive_angspeed <<= 5;
1274                     sop->drive_angslide = SP_TAG3(sp);
1275                     if (sop->drive_angslide <= 0 || sop->drive_angslide == 32)
1276                         sop->drive_angslide = 1;
1277 
1278                     sop->drive_speed = SP_TAG6(sp);
1279                     sop->drive_speed <<= 5;
1280                     sop->drive_slide = SP_TAG7(sp);
1281                     if (sop->drive_slide <= 0)
1282                         sop->drive_slide = 1;
1283 
1284                     if (TEST_BOOL1(sp))
1285                         SET(sop->flags, SOBJ_NO_QUAKE);
1286 
1287                     if (TEST_BOOL3(sp))
1288                         SET(sop->flags, SOBJ_REMOTE_ONLY);
1289 
1290                     if (TEST_BOOL4(sp))
1291                     {
1292                         sop->crush_z = sp->z;
1293                         SET(sop->flags, SOBJ_RECT_CLIP);
1294                     }
1295 
1296                     //KillSprite(SpriteNum);
1297                     break;
1298 
1299                 case SO_RAM_DAMAGE:
1300                     sop->ram_damage = sp->lotag;
1301                     KillSprite(SpriteNum);
1302                     break;
1303                 case SECT_SO_CLIP_DIST:
1304                     sop->clipdist = sp->lotag;
1305                     KillSprite(SpriteNum);
1306                     break;
1307                 case SECT_SO_SPRITE_OBJ:
1308                     SET(sop->flags, SOBJ_SPRITE_OBJ);
1309                     KillSprite(SpriteNum);
1310                     break;
1311                 case SECT_SO_DONT_ROTATE:
1312                     SET(sop->flags, SOBJ_DONT_ROTATE);
1313                     KillSprite(SpriteNum);
1314                     break;
1315                 case SO_LIMIT_TURN:
1316                     sop->limit_ang_center = sp->ang;
1317                     sop->limit_ang_delta = sp->lotag;
1318                     KillSprite(SpriteNum);
1319                     break;
1320                 case SO_MATCH_EVENT:
1321                     sop->match_event = sp->lotag;
1322                     sop->match_event_sprite = SpriteNum;
1323                     break;
1324                 case SO_SET_SPEED:
1325                     sop->vel = sp->lotag * 256;
1326                     sop->vel_tgt = sop->vel;
1327                     KillSprite(SpriteNum);
1328                     break;
1329                 case SO_SPIN:
1330                     if (sop->spin_speed)
1331                         break;
1332                     sop->spin_speed = sp->lotag;
1333                     sop->last_ang = sop->ang;
1334                     KillSprite(SpriteNum);
1335                     break;
1336                 case SO_ANGLE:
1337                     sop->ang = sop->ang_moving = sp->ang;
1338                     sop->last_ang = sop->ang_orig = sop->ang;
1339                     sop->spin_ang = 0;
1340                     KillSprite(SpriteNum);
1341                     break;
1342                 case SO_SPIN_REVERSE:
1343 
1344                     sop->spin_speed = sp->lotag;
1345                     sop->last_ang = sop->ang;
1346 
1347                     if (sop->spin_speed >= 0)
1348                         sop->spin_speed = -sop->spin_speed;
1349 
1350                     KillSprite(SpriteNum);
1351                     break;
1352                 case SO_BOB_START:
1353                     sop->bob_amt = Z(sp->lotag);
1354                     sop->bob_sine_ndx = 0;
1355                     sop->bob_speed = 4;
1356                     KillSprite(SpriteNum);
1357                     break;
1358                 case SO_TURN_SPEED:
1359                     sop->turn_speed = sp->lotag;
1360                     KillSprite(SpriteNum);
1361                     break;
1362                 case SO_SYNC1:
1363                     SET(sop->flags, SOBJ_SYNC1);
1364                     KillSprite(SpriteNum);
1365                     break;
1366                 case SO_SYNC2:
1367                     SET(sop->flags, SOBJ_SYNC2);
1368                     KillSprite(SpriteNum);
1369                     break;
1370                 case SO_KILLABLE:
1371                     SET(sop->flags, SOBJ_KILLABLE);
1372                     KillSprite(SpriteNum);
1373                     break;
1374                 }
1375             }
1376         }
1377 
1378         if (sop->vel == -1)
1379             sop->vel = sop->vel_tgt = 8 * 256;
1380 
1381         SectorObjectSetupBounds(sop);
1382 
1383         if (sop->track >= SO_OPERATE_TRACK_START)
1384         {
1385             switch (sop->track)
1386             {
1387             case SO_TURRET_MGUN:
1388             case SO_TURRET:
1389             case SO_TANK:
1390                 sop->vel = 0;
1391                 SET(sop->flags, SOBJ_OPERATIONAL);
1392                 break;
1393             case SO_SPEED_BOAT:
1394                 sop->vel = 0;
1395                 sop->bob_amt = Z(2);
1396                 sop->bob_speed = 4;
1397                 SET(sop->flags, SOBJ_OPERATIONAL);
1398                 break;
1399             default:
1400                 SET(sop->flags, SOBJ_OPERATIONAL);
1401                 break;
1402             }
1403         }
1404 
1405         sector[sectnum].lotag = 0;
1406         sector[sectnum].hitag = 0;
1407 
1408         if (sop->max_damage <= 0)
1409             VehicleSetSmoke(sop, SpawnVehicleSmoke);
1410 
1411         // find radius
1412         //u->Radius = sop->
1413 
1414         break;
1415     }
1416 
1417 }
1418 
1419 void
PostSetupSectorObject(void)1420 PostSetupSectorObject(void)
1421 {
1422     SECTOR_OBJECTp sop;
1423 
1424     for (sop = SectorObject; sop < &SectorObject[MAX_SECTOR_OBJECTS]; sop++)
1425     {
1426         if (SO_EMPTY(sop))
1427             continue;
1428         FindMainSector(sop);
1429     }
1430 }
1431 
1432 
1433 SECTOR_OBJECTp
PlayerOnObject(short sectnum_match)1434 PlayerOnObject(short sectnum_match)
1435 {
1436     short i, j;
1437     SECTOR_OBJECTp sop;
1438 
1439     // place each sector object on the track
1440     //for (i = 0; !SO_EMPTY(&SectorObject[i]) && (i < MAX_SECTOR_OBJECTS); i++)
1441     for (i = 0; (i < MAX_SECTOR_OBJECTS); i++)
1442     {
1443         sop = &SectorObject[i];
1444 
1445         if (sop->track < SO_OPERATE_TRACK_START)
1446             continue;
1447 
1448         for (j = 0; j < sop->num_sectors; j++)
1449         {
1450             if (sop->sector[j] == sectnum_match && TEST(sector[sectnum_match].extra, SECTFX_OPERATIONAL))
1451             {
1452                 return sop;
1453             }
1454         }
1455     }
1456 
1457     return NULL;
1458 }
1459 
1460 
1461 void
PlaceSectorObjectsOnTracks(void)1462 PlaceSectorObjectsOnTracks(void)
1463 {
1464     short i, j, k, startwall, endwall;
1465     SWBOOL found;
1466 
1467     // place each sector object on the track
1468     for (i = 0; i < MAX_SECTOR_OBJECTS; i++)
1469     {
1470         int low_dist = 999999, dist;
1471         SECTOR_OBJECTp sop = &SectorObject[i];
1472         TRACK_POINTp tpoint = NULL;
1473 
1474         if (SO_EMPTY(sop))
1475             continue;
1476 
1477 
1478         // save off the original x and y locations of the walls AND sprites
1479         sop->num_walls = 0;
1480         for (j = 0; sop->sector[j] != -1; j++)
1481         {
1482             startwall = sector[sop->sector[j]].wallptr;
1483             endwall = startwall + sector[sop->sector[j]].wallnum - 1;
1484 
1485             // move all walls in sectors
1486             for (k = startwall; k <= endwall; k++)
1487             {
1488                 sop->xorig[sop->num_walls] = sop->xmid - wall[k].x;
1489                 sop->yorig[sop->num_walls] = sop->ymid - wall[k].y;
1490                 sop->num_walls++;
1491             }
1492         }
1493 
1494         ASSERT((uint16_t)sop->num_walls < SIZ(sop->xorig));
1495 
1496         if (sop->track <= -1)
1497             continue;
1498 
1499         if (sop->track >= SO_OPERATE_TRACK_START)
1500             continue;
1501 
1502         found = FALSE;
1503         // find the closest point on the track and put SOBJ on it
1504         for (j = 0; j < Track[sop->track].NumPoints; j++)
1505         {
1506             tpoint = Track[sop->track].TrackPoint;
1507 
1508             dist = Distance((tpoint + j)->x, (tpoint + j)->y, sop->xmid, sop->ymid);
1509 
1510             if (dist < low_dist)
1511             {
1512                 low_dist = dist;
1513                 sop->point = j;
1514                 found = TRUE;
1515                 ////DSPRINTF(ds,"point = %d, dist = %d, x1=%d, y1=%d",j,low_dist,(tpoint +j)->x,(tpoint+j)->y);
1516                 //MONO_PRINT(ds);
1517             }
1518         }
1519 
1520         if (!found)
1521         {
1522             //DSPRINTF(ds,"track not found");
1523             MONO_PRINT(ds);
1524             sop->track = -1;
1525             continue;
1526         }
1527 
1528         NextTrackPoint(sop);
1529 
1530         sop->ang = getangle((tpoint + sop->point)->x - sop->xmid, (tpoint + sop->point)->y - sop->ymid);
1531 
1532         sop->ang_moving = sop->ang_tgt = sop->ang;
1533     }
1534 
1535 }
1536 
1537 
1538 void
PlaceActorsOnTracks(void)1539 PlaceActorsOnTracks(void)
1540 {
1541     short i, nexti, j, tag;
1542     SPRITEp sp;
1543     USERp u;
1544     TRACK_POINTp tpoint = NULL;
1545 
1546     // place each actor on the track
1547     TRAVERSE_SPRITE_STAT(headspritestat[STAT_ENEMY], i, nexti)
1548     {
1549         int low_dist = 999999, dist;
1550 
1551         sp = User[i]->SpriteP;
1552         u = User[i];
1553 
1554         tag = LOW_TAG_SPRITE(i);
1555 
1556         if (tag < TAG_ACTOR_TRACK_BEGIN || tag > TAG_ACTOR_TRACK_END)
1557             continue;
1558 
1559         // setup sprite track defaults
1560         u->track = tag - TAG_ACTOR_TRACK_BEGIN;
1561 
1562         // if facing left go backward
1563         if (BETWEEN(sp->ang, 513, 1535))
1564         {
1565             u->track_dir = -1;
1566         }
1567         else
1568         {
1569             u->track_dir = 1;
1570         }
1571 
1572         u->track_vel = sp->xvel * 256;
1573         u->vel_tgt = u->track_vel;
1574         u->vel_rate = 6;
1575 
1576         // find the closest point on the track and put SOBJ on it
1577         for (j = 0; j < Track[u->track].NumPoints; j++)
1578         {
1579             tpoint = Track[u->track].TrackPoint;
1580 
1581             dist = Distance((tpoint + j)->x, (tpoint + j)->y, sp->x, sp->y);
1582 
1583             if (dist < low_dist)
1584             {
1585                 low_dist = dist;
1586                 u->point = j;
1587             }
1588         }
1589 
1590         NextActorTrackPoint(i);
1591 
1592         if (Track[u->track].NumPoints == 0)
1593         {
1594             buildprintf("WARNING: Sprite %d (%d, %d) placed on track %d with no points!\n", i, TrackerCast(sp->x), TrackerCast(sp->y), u->track);
1595             continue;
1596         }
1597 
1598         // check angle in the "forward" direction
1599         sp->ang = getangle((tpoint + u->point)->x - sp->x, (tpoint + u->point)->y - sp->y);
1600     }
1601 }
1602 
1603 
1604 void
MovePlayer(PLAYERp pp,SECTOR_OBJECTp sop,int nx,int ny)1605 MovePlayer(PLAYERp pp, SECTOR_OBJECTp sop, int nx, int ny)
1606 {
1607     void DoPlayerZrange(PLAYERp pp);
1608 
1609     // make sure your standing on the so
1610     if (TEST(pp->Flags, PF_JUMPING | PF_FALLING | PF_FLYING))
1611         return;
1612 
1613     pp->sop_riding = sop;
1614 
1615     // if player has NOT moved and player is NOT riding
1616     // set up the player for riding
1617     if (!TEST(pp->Flags, PF_PLAYER_MOVED) && !TEST(pp->Flags, PF_PLAYER_RIDING))
1618     {
1619         SET(pp->Flags, PF_PLAYER_RIDING);
1620 
1621         pp->RevolveQ16Ang = pp->q16ang;
1622         pp->RevolveX = pp->posx;
1623         pp->RevolveY = pp->posy;
1624 
1625         // set the delta angle to 0 when moving
1626         pp->RevolveDeltaAng = 0;
1627     }
1628 
1629     pp->posx += BOUND_4PIX(nx);
1630     pp->posy += BOUND_4PIX(ny);
1631 
1632     if (TEST(sop->flags, SOBJ_DONT_ROTATE))
1633     {
1634         UpdatePlayerSprite(pp);
1635         return;
1636     }
1637 
1638     if (TEST(pp->Flags, PF_PLAYER_MOVED))
1639     {
1640         // Player is moving
1641 
1642         // save the current information so when Player stops
1643         // moving then you
1644         // know where he was last
1645         pp->RevolveQ16Ang = pp->q16ang;
1646         pp->RevolveX = pp->posx;
1647         pp->RevolveY = pp->posy;
1648 
1649         // set the delta angle to 0 when moving
1650         pp->RevolveDeltaAng = 0;
1651     }
1652     else
1653     {
1654         // Player is NOT moving
1655 
1656         // Move saved x&y variables
1657         pp->RevolveX += BOUND_4PIX(nx);
1658         pp->RevolveY += BOUND_4PIX(ny);
1659 
1660         // Last known angle is now adjusted by the delta angle
1661         pp->RevolveQ16Ang = NORM_Q16ANGLE(pp->q16ang - fix16_from_int(pp->RevolveDeltaAng));
1662     }
1663 
1664     // increment Players delta angle
1665     pp->RevolveDeltaAng = NORM_ANGLE(pp->RevolveDeltaAng + GlobSpeedSO);
1666 
1667     rotatepoint(*(vec2_t *)&sop->xmid, *(vec2_t *)&pp->RevolveX, pp->RevolveDeltaAng, (vec2_t *)&pp->posx);
1668 
1669     // THIS WAS CAUSING PROLEMS!!!!
1670     // Sectors are still being manipulated so you can end up in a void (-1) sector
1671     //COVERupdatesector(pp->posx, pp->posy, &pp->cursectnum);
1672 
1673     // New angle is formed by taking last known angle and
1674     // adjusting by the delta angle
1675     pp->camq16ang += NORM_Q16ANGLE(pp->RevolveQ16Ang + fix16_from_int(pp->RevolveDeltaAng)) - pp->q16ang;
1676     pp->camq16ang = NORM_Q16ANGLE(pp->camq16ang);
1677     pp->q16ang = NORM_Q16ANGLE(pp->RevolveQ16Ang + fix16_from_int(pp->RevolveDeltaAng));
1678 
1679     UpdatePlayerSprite(pp);
1680 }
1681 
1682 void
MovePoints(SECTOR_OBJECTp sop,short delta_ang,int nx,int ny)1683 MovePoints(SECTOR_OBJECTp sop, short delta_ang, int nx, int ny)
1684 {
1685     int j, k;
1686     vec2_t rxy;
1687     short startwall, endwall, pnum;
1688     PLAYERp pp;
1689     SECTORp *sectp;
1690     SPRITEp sp;
1691     WALLp wp;
1692     USERp u;
1693     short i, rot_ang;
1694     SWBOOL PlayerMove = TRUE;
1695 
1696     if (sop->xmid >= MAXSO)
1697         PlayerMove = FALSE;
1698 
1699     // move along little midpoint
1700     sop->xmid += BOUND_4PIX(nx);
1701     sop->ymid += BOUND_4PIX(ny);
1702 
1703     if (sop->xmid >= MAXSO)
1704         PlayerMove = FALSE;
1705 
1706     // move child sprite along also
1707     sop->sp_child->x = sop->xmid;
1708     sop->sp_child->y = sop->ymid;
1709 
1710     //COVERupdatesector(sop->xmid, sop->ymid, &sop->sectnum);
1711 
1712     // setting floorz if need be
1713     //if (!TEST(sop->flags, SOBJ_SPRITE_OBJ))
1714     if (TEST(sop->flags, SOBJ_ZMID_FLOOR))
1715         sop->zmid = sector[sop->mid_sector].floorz;
1716 
1717     for (sectp = sop->sectp, j = 0; *sectp; sectp++, j++)
1718     {
1719         if (TEST(sop->flags, SOBJ_SPRITE_OBJ | SOBJ_DONT_ROTATE))
1720             goto PlayerPart;
1721 
1722         startwall = (*sectp)->wallptr;
1723         endwall = startwall + (*sectp)->wallnum - 1;
1724 
1725         // move all walls in sectors
1726         for (wp = &wall[startwall], k = startwall; k <= endwall; wp++, k++)
1727         {
1728             if (TEST(wp->extra, WALLFX_LOOP_DONT_SPIN | WALLFX_DONT_MOVE))
1729                 continue;
1730 
1731             if (wp->extra && TEST(wp->extra, WALLFX_LOOP_OUTER))
1732             {
1733                 dragpoint(k, wp->x += BOUND_4PIX(nx), wp->y += BOUND_4PIX(ny), 0);
1734             }
1735             else
1736             {
1737                 wp->x += BOUND_4PIX(nx);
1738                 wp->y += BOUND_4PIX(ny);
1739             }
1740 
1741             rot_ang = delta_ang;
1742 
1743             if (TEST(wp->extra, WALLFX_LOOP_REVERSE_SPIN))
1744                 rot_ang = -delta_ang;
1745 
1746             if (TEST(wp->extra, WALLFX_LOOP_SPIN_2X))
1747                 rot_ang = NORM_ANGLE(rot_ang * 2);
1748 
1749             if (TEST(wp->extra, WALLFX_LOOP_SPIN_4X))
1750                 rot_ang = NORM_ANGLE(rot_ang * 4);
1751 
1752             rotatepoint(*(vec2_t *)&sop->xmid, *(vec2_t *)&wp->x, rot_ang, &rxy);
1753 
1754             if (wp->extra && TEST(wp->extra, WALLFX_LOOP_OUTER))
1755             {
1756                 dragpoint(k, rxy.x, rxy.y, 0);
1757             }
1758             else
1759             {
1760                 wp->x = rxy.x;
1761                 wp->y = rxy.y;
1762             }
1763         }
1764 
1765 PlayerPart:
1766 
1767         TRAVERSE_CONNECT(pnum)
1768         {
1769             pp = Player + pnum;
1770 
1771             // if controlling a sector object
1772             if (pp->sop)
1773                 continue;
1774 
1775             if (!pp->lo_sectp)
1776                 continue;
1777 
1778             if (TEST(sector[pp->lo_sectp - sector].extra, SECTFX_NO_RIDE))
1779             {
1780 #if 0
1781                 short nr, nextnr;
1782                 SWBOOL skip = TRUE;
1783                 TRAVERSE_SPRITE_STAT(headspritestat[STAT_NO_RIDE], nr, nextnr)
1784                 {
1785                     if (sprite[nr].lotag == sop - SectorObject)
1786                         skip = TRUE;
1787                     else
1788                         skip = FALSE;
1789                 }
1790 
1791                 if (skip)
1792 #endif
1793                 continue;
1794             }
1795 
1796             // move the player
1797             if (pp->lo_sectp - sector == sop->sector[j])
1798             {
1799                 if (PlayerMove)
1800                     MovePlayer(pp, sop, nx, ny);
1801             }
1802         }
1803     }
1804 
1805     for (i = 0; sop->sp_num[i] != -1; i++)
1806     {
1807         sp = &sprite[sop->sp_num[i]];
1808         u = User[sop->sp_num[i]];
1809 
1810         // if its a player sprite || NOT attached
1811         if (!u || u->PlayerP || !TEST(u->Flags, SPR_SO_ATTACHED))
1812             continue;
1813 
1814         // move the player
1815         TRAVERSE_CONNECT(pnum)
1816         {
1817             pp = Player + pnum;
1818 
1819             if (pp->lo_sp && pp->lo_sp == sp)
1820             {
1821                 if (PlayerMove)
1822                     MovePlayer(pp, sop, nx, ny);
1823             }
1824         }
1825 
1826         sp->x = sop->xmid - u->sx;
1827         sp->y = sop->ymid - u->sy;
1828 
1829         // sprites z update
1830         if (TEST(sop->flags, SOBJ_SPRITE_OBJ))
1831         {
1832             // Sprite Objects follow zmid
1833             sp->z = sop->zmid - u->sz;
1834         }
1835         else
1836         {
1837             // Sector Objects can either have sprites ON or OFF of the sector
1838             if (TEST(u->Flags, SPR_ON_SO_SECTOR))
1839             {
1840                 // move with sector its on
1841                 sp->z = sector[sp->sectnum].floorz - u->sz;
1842             }
1843             else
1844             {
1845                 // move with the mid sector
1846                 sp->z = sector[sop->mid_sector].floorz - u->sz;
1847             }
1848         }
1849 
1850         int16_t oldang = sp->ang;
1851         sp->ang = u->sang;
1852 
1853         if (TEST(u->Flags, SPR_ON_SO_SECTOR))
1854         {
1855             if (TEST(sop->flags, SOBJ_DONT_ROTATE))
1856                 continue;
1857 
1858             // IS part of a sector - sprite can do things based on the
1859             // current sector it is in
1860             if (TEST(wall[sector[sp->sectnum].wallptr].extra, WALLFX_LOOP_DONT_SPIN))
1861                 continue;
1862 
1863             if (TEST(wall[sector[sp->sectnum].wallptr].extra, WALLFX_LOOP_REVERSE_SPIN))
1864             {
1865                 rotatepoint(*(vec2_t *)&sop->xmid, *(vec2_t *)&sp->x, -delta_ang, (vec2_t *)&sp->x);
1866                 sp->ang = NORM_ANGLE(sp->ang - delta_ang);
1867             }
1868             else
1869             {
1870                 rotatepoint(*(vec2_t *)&sop->xmid, *(vec2_t *)&sp->x, delta_ang, (vec2_t *)&sp->x);
1871                 sp->ang = NORM_ANGLE(sp->ang + delta_ang);
1872             }
1873 
1874         }
1875         else
1876         {
1877             if (!TEST(sop->flags, SOBJ_DONT_ROTATE))
1878             {
1879                 // NOT part of a sector - independant of any sector
1880                 rotatepoint(*(vec2_t *)&sop->xmid, *(vec2_t *)&sp->x, delta_ang, (vec2_t *)&sp->x);
1881                 sp->ang = NORM_ANGLE(sp->ang + delta_ang);
1882             }
1883 
1884             // Does not necessarily move with the sector so must accout for
1885             // moving across sectors
1886             if (sop->xmid < MAXSO) // special case for operating SO's
1887                 setspritez(sop->sp_num[i], (vec3_t *)sp);
1888         }
1889 
1890         u->oangdiff += GetDeltaAngle(sp->ang, oldang);
1891 
1892         if (TEST(sp->extra, SPRX_BLADE))
1893         {
1894             DoBladeDamage(sop->sp_num[i]);
1895         }
1896     }
1897 
1898     TRAVERSE_CONNECT(pnum)
1899     {
1900         pp = Player + pnum;
1901 
1902         // if player was on a sector object
1903         if (pp->sop_riding)
1904         {
1905             // update here AFTER sectors/player has been manipulated
1906             // prevents you from falling into map HOLEs created by moving
1907             // Sectors and sprites around.
1908             //if (sop->xmid < MAXSO)
1909             COVERupdatesector(pp->posx, pp->posy, &pp->cursectnum);
1910 
1911             // in case you are in a whirlpool
1912             // move perfectly with the ride in the z direction
1913             if TEST(pp->Flags, PF_CRAWLING)
1914             {
1915                 // move up some for really fast moving plats
1916                 //pp->posz -= PLAYER_HEIGHT + Z(12);
1917                 DoPlayerZrange(pp);
1918                 pp->posz = pp->loz - PLAYER_CRAWL_HEIGHT;
1919                 pp->SpriteP->z = pp->loz;
1920             }
1921             else
1922             {
1923                 // move up some for really fast moving plats
1924                 //pp->posz -= Z(24);
1925                 DoPlayerZrange(pp);
1926 
1927                 if (!TEST(pp->Flags, PF_JUMPING | PF_FALLING | PF_FLYING))
1928                 {
1929                     pp->posz = pp->loz - PLAYER_HEIGHT;
1930                     pp->SpriteP->z = pp->loz;
1931                 }
1932             }
1933         }
1934         else
1935         {
1936             // if player was not on any sector object set Riding flag to false
1937             RESET(pp->Flags, PF_PLAYER_RIDING);
1938         }
1939     }
1940 }
1941 
1942 void
RefreshPoints(SECTOR_OBJECTp sop,int nx,int ny,SWBOOL dynamic)1943 RefreshPoints(SECTOR_OBJECTp sop, int nx, int ny, SWBOOL dynamic)
1944 {
1945     short wallcount = 0, j, k, startwall, endwall, delta_ang_from_orig;
1946     SECTORp *sectp;
1947     WALLp wp;
1948     short ang;
1949     int dx,dy,x,y;
1950 
1951     // do scaling
1952     if (dynamic && sop->PreMoveAnimator)
1953         (*sop->PreMoveAnimator)(sop);
1954 
1955     for (sectp = sop->sectp, j = 0; *sectp; sectp++, j++)
1956     {
1957         if (!TEST(sop->flags, SOBJ_SPRITE_OBJ))
1958         {
1959             startwall = (*sectp)->wallptr;
1960             endwall = startwall + (*sectp)->wallnum - 1;
1961 
1962             // move all walls in sectors back to the original position
1963             for (wp = &wall[startwall], k = startwall; k <= endwall; wp++, k++)
1964             {
1965                 if (!(wp->extra && TEST(wp->extra, WALLFX_DONT_MOVE)))
1966                 {
1967                     dx = x = sop->xmid - sop->xorig[wallcount];
1968                     dy = y = sop->ymid - sop->yorig[wallcount];
1969 
1970                     if (dynamic && sop->scale_type)
1971                     {
1972                         if (!TEST(wp->extra, WALLFX_DONT_SCALE))
1973                         {
1974                             ang = NORM_ANGLE(getangle(x - sop->xmid, y - sop->ymid));
1975 
1976                             if (sop->scale_type == SO_SCALE_RANDOM_POINT)
1977                             {
1978                                 // was causing memory overwrites
1979                                 //ScaleRandomPoint(sop, k, ang, x, y, &dx, &dy);
1980                                 ScaleRandomPoint(sop, wallcount, ang, x, y, &dx, &dy);
1981                             }
1982                             else
1983                             {
1984                                 int xmul = (sop->scale_dist * sop->scale_x_mult)>>8;
1985                                 int ymul = (sop->scale_dist * sop->scale_y_mult)>>8;
1986 
1987                                 dx = x + ((xmul * sintable[NORM_ANGLE(ang+512)]) >> 14);
1988                                 dy = y + ((ymul * sintable[ang]) >> 14);
1989                             }
1990                         }
1991                     }
1992 
1993                     if (wp->extra && TEST(wp->extra, WALLFX_LOOP_OUTER))
1994                     {
1995                         dragpoint(k, dx, dy, 0);
1996                     }
1997                     else
1998                     {
1999                         wp->x = dx;
2000                         wp->y = dy;
2001                     }
2002                 }
2003 
2004                 wallcount++;
2005             }
2006         }
2007     }
2008 
2009     if (sop->spin_speed)
2010     {
2011         // same as below - ignore the objects angle
2012         // last_ang is the last true angle before SO started spinning
2013         delta_ang_from_orig = NORM_ANGLE(sop->last_ang + sop->spin_ang - sop->ang_orig);
2014     }
2015     else
2016     {
2017         // angle traveling + the new spin angle all offset from the original
2018         // angle
2019         delta_ang_from_orig = NORM_ANGLE(sop->ang + sop->spin_ang - sop->ang_orig);
2020     }
2021 
2022     // Note that this delta angle is from the original angle
2023     // nx,ny are 0 so the points are not moved, just rotated
2024     MovePoints(sop, delta_ang_from_orig, nx, ny);
2025 
2026     // do morphing - angle independent
2027     if (dynamic && sop->PostMoveAnimator)
2028         (*sop->PostMoveAnimator)(sop);
2029 }
2030 
KillSectorObjectSprites(SECTOR_OBJECTp sop)2031 void KillSectorObjectSprites(SECTOR_OBJECTp sop)
2032 {
2033     SPRITEp sp;
2034     USERp u;
2035     int i;
2036 
2037     for (i = 0; sop->sp_num[i] != -1; i++)
2038     {
2039         sp = &sprite[sop->sp_num[i]];
2040         u = User[sop->sp_num[i]];
2041 
2042         // not a part of the so anymore
2043         RESET(u->Flags, SPR_SO_ATTACHED);
2044 
2045         if (sp->picnum == ST1 && sp->hitag == SPAWN_SPOT)
2046             continue;
2047 
2048         so_stopspriteinterpolation(sop, sp);
2049         KillSprite(sop->sp_num[i]);
2050     }
2051 
2052     // clear the list
2053     sop->sp_num[0] = -1;
2054 }
2055 
UpdateSectorObjectSprites(SECTOR_OBJECTp sop)2056 void UpdateSectorObjectSprites(SECTOR_OBJECTp sop)
2057 {
2058     SPRITEp sp;
2059     int i;
2060 
2061     for (i = 0; sop->sp_num[i] != -1; i++)
2062     {
2063         sp = &sprite[sop->sp_num[i]];
2064 
2065         setspritez(sop->sp_num[i], (vec3_t *)sp);
2066     }
2067 }
2068 
2069 SECTOR_OBJECTp
DetectSectorObject(SECTORp sectph)2070 DetectSectorObject(SECTORp sectph)
2071 {
2072     short j;
2073     SECTORp *sectp;
2074     SECTOR_OBJECTp sop;
2075 
2076 
2077     // collapse the SO to a single point
2078     // move all points to nx,ny
2079     for (sop = SectorObject; sop < &SectorObject[MAX_SECTOR_OBJECTS]; sop++)
2080     {
2081         if (SO_EMPTY(sop))
2082             continue;
2083 
2084         for (sectp = sop->sectp, j = 0; *sectp; sectp++, j++)
2085         {
2086             if (sectph == *sectp)
2087                 return sop;
2088         }
2089     }
2090 
2091     return NULL;
2092 }
2093 
2094 SECTOR_OBJECTp
DetectSectorObjectByWall(WALLp wph)2095 DetectSectorObjectByWall(WALLp wph)
2096 {
2097     short j, k, startwall, endwall;
2098     SECTORp *sectp;
2099     WALLp wp;
2100     SECTOR_OBJECTp sop;
2101 
2102 //    if (wph->nextsector >= 0)
2103 //        return(DetectSectorObject(&sector[wph->nextsector]));
2104 
2105     // collapse the SO to a single point
2106     // move all points to nx,ny
2107     for (sop = SectorObject; sop < &SectorObject[MAX_SECTOR_OBJECTS]; sop++)
2108     {
2109         if (SO_EMPTY(sop))
2110             continue;
2111 
2112         for (sectp = sop->sectp, j = 0; *sectp; sectp++, j++)
2113         {
2114             startwall = (*sectp)->wallptr;
2115             endwall = startwall + (*sectp)->wallnum - 1;
2116 
2117             for (wp = &wall[startwall], k = startwall; k <= endwall; wp++, k++)
2118             {
2119                 // if outer wall check the NEXTWALL also
2120                 if (TEST(wp->extra, WALLFX_LOOP_OUTER))
2121                 {
2122                     uint16_t const nextwall = wp->nextwall;
2123                     if (nextwall < MAXWALLS && wph == &wall[nextwall])
2124                         return sop;
2125                 }
2126 
2127                 if (wph == wp)
2128                     return sop;
2129             }
2130         }
2131     }
2132 
2133     return NULL;
2134 }
2135 
2136 
2137 void
CollapseSectorObject(SECTOR_OBJECTp sop,int nx,int ny)2138 CollapseSectorObject(SECTOR_OBJECTp sop, int nx, int ny)
2139 {
2140     short j, k, startwall, endwall;
2141     SECTORp *sectp;
2142     WALLp wp;
2143 
2144     // collapse the SO to a single point
2145     // move all points to nx,ny
2146     for (sectp = sop->sectp, j = 0; *sectp; sectp++, j++)
2147     {
2148         if (!TEST(sop->flags, SOBJ_SPRITE_OBJ))
2149         {
2150             startwall = (*sectp)->wallptr;
2151             endwall = startwall + (*sectp)->wallnum - 1;
2152 
2153             // move all walls in sectors back to the original position
2154             for (wp = &wall[startwall], k = startwall; k <= endwall; wp++, k++)
2155             {
2156                 if (TEST(wp->extra, WALLFX_DONT_MOVE))
2157                     continue;
2158 
2159                 if (wp->extra && TEST(wp->extra, WALLFX_LOOP_OUTER))
2160                 {
2161                     dragpoint(k, nx, ny, 0);
2162                 }
2163                 else
2164                 {
2165                     wp->x = nx;
2166                     wp->y = ny;
2167                 }
2168             }
2169         }
2170     }
2171 }
2172 
2173 
2174 void
MoveZ(SECTOR_OBJECTp sop)2175 MoveZ(SECTOR_OBJECTp sop)
2176 {
2177     short i;
2178     SECTORp *sectp;
2179 
2180     if (sop->bob_amt)
2181     {
2182         sop->bob_sine_ndx = (totalsynctics << sop->bob_speed) & 2047;
2183         sop->bob_diff = ((sop->bob_amt * (int) sintable[sop->bob_sine_ndx]) >> 14);
2184 
2185         // for all sectors
2186         for (i = 0, sectp = &sop->sectp[0]; *sectp; sectp++, i++)
2187         {
2188             if (SectUser[sop->sector[i]] && TEST(SectUser[sop->sector[i]]->flags, SECTFU_SO_DONT_BOB))
2189                 continue;
2190 
2191             (*sectp)->floorz = sop->zorig_floor[i] + sop->bob_diff;
2192         }
2193     }
2194 
2195     if (TEST(sop->flags, SOBJ_MOVE_VERTICAL))
2196     {
2197         i = AnimGetGoal(&sop->zmid);
2198         if (i < 0)
2199             RESET(sop->flags, SOBJ_MOVE_VERTICAL);
2200     }
2201 
2202     if (TEST(sop->flags, SOBJ_ZDIFF_MODE))
2203     {
2204         return;
2205     }
2206 
2207     // move all floors
2208     if (TEST(sop->flags, SOBJ_ZDOWN))
2209     {
2210         for (i = 0, sectp = &sop->sectp[0]; *sectp; sectp++, i++)
2211         {
2212             AnimSet(&(*sectp)->floorz, sop->zorig_floor[i] + sop->z_tgt, sop->z_rate);
2213         }
2214 
2215         RESET(sop->flags, SOBJ_ZDOWN);
2216     }
2217     else if (TEST(sop->flags, SOBJ_ZUP))
2218     {
2219         for (i = 0, sectp = &sop->sectp[0]; *sectp; sectp++, i++)
2220         {
2221             AnimSet(&(*sectp)->floorz, sop->zorig_floor[i] + sop->z_tgt, sop->z_rate);
2222         }
2223 
2224         RESET(sop->flags, SOBJ_ZUP);
2225     }
2226 }
2227 
CallbackSOsink(ANIMp ap,void * data)2228 void CallbackSOsink(ANIMp ap, void *data)
2229 {
2230     SECTOR_OBJECTp sop;
2231     SPRITEp sp;
2232     USERp u;
2233     SECT_USERp su;
2234     short startwall, endwall, j;
2235     short dest_sector = -1;
2236     short src_sector = -1;
2237     short i, nexti, ndx;
2238     char found = FALSE;
2239     int tgt_depth;
2240 
2241     sop = (SECTOR_OBJECTp)data;
2242 
2243     for (i = 0; sop->sector[i] != -1; i++)
2244     {
2245         if (SectUser[sop->sector[i]] && TEST(SectUser[sop->sector[i]]->flags, SECTFU_SO_SINK_DEST))
2246         {
2247             src_sector = sop->sector[i];
2248             break;
2249         }
2250     }
2251 
2252     ASSERT(src_sector != -1);
2253 
2254     for (i = 0; sop->sector[i] != -1; i++)
2255     {
2256         if (ap->ptr == &sector[sop->sector[i]].floorz)
2257         {
2258             dest_sector = sop->sector[i];
2259             break;
2260         }
2261     }
2262 
2263     ASSERT(dest_sector != -1);
2264 
2265 
2266     sector[dest_sector].floorpicnum = sector[src_sector].floorpicnum;
2267     sector[dest_sector].floorshade = sector[src_sector].floorshade;
2268 //    sector[dest_sector].floorz = sector[src_sector].floorz;
2269 
2270     RESET(sector[dest_sector].floorstat, FLOOR_STAT_RELATIVE);
2271 
2272     su = GetSectUser(dest_sector);
2273 
2274     ASSERT(su != NULL);
2275 
2276     ASSERT(GetSectUser(src_sector));
2277     tgt_depth = (GetSectUser(src_sector))->depth;
2278 
2279 #if 0
2280     for (w = &Water[0]; w < &Water[MAX_WATER]; w++)
2281     {
2282         if (w->sector == dest_sector)
2283         {
2284             ndx = AnimSet(&w->depth, Z(tgt_depth), ap->vel>>8);
2285             AnimSetVelAdj(ndx, ap->vel_adj);
2286 
2287             // This is interesting
2288             // Added a depth_fract to the struct so I could do a
2289             // 16.16 Fixed point representation to change the depth
2290             // in a more precise way
2291             ndx = AnimSet((int *)&su->depth_fract, tgt_depth<<16, (ap->vel<<8)>>8);
2292             AnimSetVelAdj(ndx, ap->vel_adj);
2293 
2294             found = TRUE;
2295             break;
2296         }
2297     }
2298 #else
2299     {
2300         short sectnum;
2301         for (sectnum = 0; sectnum < numsectors; sectnum++)
2302         {
2303             if (sectnum == dest_sector)
2304             {
2305                 // This is interesting
2306                 // Added a depth_fract to the struct so I could do a
2307                 // 16.16 Fixed point representation to change the depth
2308                 // in a more precise way
2309                 ndx = AnimSet((int *)&su->depth_fract, tgt_depth<<16, (ap->vel<<8)>>8);
2310                 AnimSetVelAdj(ndx, ap->vel_adj);
2311                 found = TRUE;
2312                 break;
2313             }
2314         }
2315     }
2316 #endif
2317 
2318     ASSERT(found);
2319 
2320     TRAVERSE_SPRITE_SECT(headspritesect[dest_sector], i, nexti)
2321     {
2322         sp = &sprite[i];
2323         u = User[i];
2324 
2325         if (!u || u->PlayerP || !TEST(u->Flags, SPR_SO_ATTACHED))
2326             continue;
2327 
2328         // move sprite WAY down in water
2329         ndx = AnimSet(&u->sz, -u->sz - SPRITEp_SIZE_Z(sp) - Z(100), ap->vel>>8);
2330         AnimSetVelAdj(ndx, ap->vel_adj);
2331     }
2332 
2333 
2334     // Take out any blocking walls
2335     startwall = sector[dest_sector].wallptr;
2336     endwall = startwall + sector[dest_sector].wallnum - 1;
2337     for (j = startwall; j <= endwall; j++)
2338     {
2339         RESET(wall[j].cstat, CSTAT_WALL_BLOCK);
2340     }
2341 
2342     return;
2343 }
2344 
2345 
2346 void
MoveSectorObjects(SECTOR_OBJECTp sop,short locktics)2347 MoveSectorObjects(SECTOR_OBJECTp sop, short locktics)
2348 {
2349     int nx, ny;
2350     short speed;
2351     short delta_ang;
2352 
2353     so_setinterpolationtics(sop, locktics);
2354 
2355     if (sop->track >= SO_OPERATE_TRACK_START)
2356     {
2357         if (TEST(sop->flags, SOBJ_UPDATE_ONCE))
2358         {
2359             RESET(sop->flags, SOBJ_UPDATE_ONCE);
2360             RefreshPoints(sop, 0, 0, FALSE);
2361         }
2362         return;
2363     }
2364 
2365     nx = 0;
2366     ny = 0;
2367 
2368     // if pausing the return
2369     if (sop->wait_tics)
2370     {
2371         sop->wait_tics -= locktics;
2372         if (sop->wait_tics <= 0)
2373             sop->wait_tics = 0;
2374 
2375         return;
2376     }
2377 
2378     delta_ang = 0;
2379 
2380     if (sop->track > -1)
2381         DoTrack(sop, locktics, &nx, &ny);
2382 
2383     // get delta to target angle
2384     delta_ang = GetDeltaAngle(sop->ang_tgt, sop->ang);
2385 
2386     sop->ang = NORM_ANGLE(sop->ang + (delta_ang >> sop->turn_speed));
2387     delta_ang = delta_ang >> sop->turn_speed;
2388 
2389     // move z values
2390     MoveZ(sop);
2391 
2392     // calculate the spin speed
2393     speed = sop->spin_speed * locktics;
2394     // spin_ang is incremented by the spin_speed
2395     sop->spin_ang = NORM_ANGLE(sop->spin_ang + speed);
2396 
2397     if (sop->spin_speed)
2398     {
2399         // ignore delta angle if spinning
2400         GlobSpeedSO = speed;
2401     }
2402     else
2403     {
2404         // The actual delta from the last frame
2405         GlobSpeedSO = speed;
2406         GlobSpeedSO += delta_ang;
2407     }
2408 
2409     if (TEST(sop->flags, SOBJ_DYNAMIC))
2410     {
2411         // trick tricks
2412         RefreshPoints(sop, nx, ny, TRUE);
2413     }
2414     else
2415     {
2416         // Update the points so there will be no warping
2417         if (TEST(sop->flags, SOBJ_UPDATE|SOBJ_UPDATE_ONCE) ||
2418             sop->vel ||
2419             (sop->ang != sop->ang_tgt) ||
2420             GlobSpeedSO)
2421         {
2422             RESET(sop->flags, SOBJ_UPDATE_ONCE);
2423             RefreshPoints(sop, nx, ny, FALSE);
2424         }
2425     }
2426 }
2427 
DoTrack(SECTOR_OBJECTp sop,short locktics,int * nx,int * ny)2428 void DoTrack(SECTOR_OBJECTp sop, short locktics, int *nx, int *ny)
2429 {
2430     TRACK_POINTp tpoint;
2431     int dx, dy, dz;
2432     int dist;
2433 
2434     tpoint = Track[sop->track].TrackPoint + sop->point;
2435 
2436     // calculate an angle to the target
2437 
2438     if (sop->vel)
2439         sop->ang_moving = sop->ang_tgt = getangle(tpoint->x - sop->xmid, tpoint->y - sop->ymid);
2440 
2441     // NOTE: Jittery ride - try new value out here
2442     // NOTE: Put a loop around this (locktics) to make it more acuruate
2443 #define TRACK_POINT_SIZE 200
2444     //dist = Distance(sop->xmid, sop->ymid, tpoint->x, tpoint->y);
2445     //if (dist < TRACK_POINT_SIZE)
2446     if (sop->target_dist < 100)
2447     {
2448         switch (tpoint->tag_low)
2449         {
2450         case TRACK_MATCH_EVERYTHING:
2451             DoMatchEverything(NULL, tpoint->tag_high, -1);
2452             break;
2453 
2454         case TRACK_MATCH_EVERYTHING_ONCE:
2455             DoMatchEverything(NULL, tpoint->tag_high, -1);
2456             tpoint->tag_low = 0;
2457             tpoint->tag_high = 0;
2458             break;
2459 
2460         case TRACK_SPIN:
2461             if (sop->spin_speed)
2462                 break;
2463 
2464             sop->spin_speed = tpoint->tag_high;
2465             sop->last_ang = sop->ang;
2466             break;
2467 
2468         case TRACK_SPIN_REVERSE:
2469         {
2470             if (!sop->spin_speed)
2471                 break;
2472 
2473             if (sop->spin_speed >= 0)
2474             {
2475                 sop->spin_speed = -sop->spin_speed;
2476             }
2477         }
2478         break;
2479 
2480         case TRACK_SPIN_STOP:
2481             if (!sop->spin_speed)
2482                 break;
2483 
2484             sop->spin_speed = 0;
2485             break;
2486 
2487         case TRACK_BOB_START:
2488             SET(sop->flags, SOBJ_ZMID_FLOOR);
2489             sop->bob_amt = Z(tpoint->tag_high);
2490             sop->bob_sine_ndx = 0;
2491             sop->bob_speed = 4;
2492             break;
2493 
2494         case TRACK_BOB_STOP:
2495             sop->bob_speed = 0;
2496             sop->bob_sine_ndx = 0;
2497             sop->bob_amt = 0;
2498             break;
2499 
2500         case TRACK_BOB_SPEED:
2501             sop->bob_speed = tpoint->tag_high;
2502             break;
2503 
2504         case TRACK_REVERSE:
2505             sop->dir *= -1;
2506             break;
2507         case TRACK_STOP:
2508             sop->vel = 0;
2509             sop->wait_tics = tpoint->tag_high * 128;
2510             break;
2511         case TRACK_SET_SPEED:
2512             sop->vel = tpoint->tag_high * 256;
2513             sop->vel_tgt = sop->vel;
2514             break;
2515 
2516         //
2517         // Controls the velocity
2518         //
2519 
2520         case TRACK_VEL_RATE:
2521             sop->vel_rate = tpoint->tag_high;
2522             break;
2523         case TRACK_SPEED_UP:
2524             RESET(sop->flags, SOBJ_SLOW_DOWN | SOBJ_SPEED_UP);
2525             if (sop->dir < 0)
2526             {
2527                 // set target to new slower target
2528                 sop->vel_tgt = sop->vel_tgt - (tpoint->tag_high * 256);
2529                 SET(sop->flags, SOBJ_SLOW_DOWN);
2530             }
2531             else
2532             {
2533                 sop->vel_tgt = sop->vel_tgt + (tpoint->tag_high * 256);
2534                 SET(sop->flags, SOBJ_SPEED_UP);
2535             }
2536 
2537             break;
2538 
2539         case TRACK_SLOW_DOWN:
2540             RESET(sop->flags, SOBJ_SLOW_DOWN | SOBJ_SPEED_UP);
2541             if (sop->dir > 0)
2542             {
2543                 sop->vel_tgt = sop->vel_tgt - (tpoint->tag_high * 256);
2544                 SET(sop->flags, SOBJ_SLOW_DOWN);
2545             }
2546             else
2547             {
2548                 sop->vel_tgt = sop->vel_tgt + (tpoint->tag_high * 256);
2549                 SET(sop->flags, SOBJ_SPEED_UP);
2550             }
2551             break;
2552 
2553         //
2554         // Controls z
2555         //
2556 
2557         case TRACK_SO_SINK:
2558         {
2559             SECTORp *sectp;
2560             short dest_sector = -1;
2561             short i,ndx;
2562 
2563             for (i = 0; sop->sector[i] != -1; i++)
2564             {
2565                 if (SectUser[sop->sector[i]] && TEST(SectUser[sop->sector[i]]->flags, SECTFU_SO_SINK_DEST))
2566                 {
2567                     dest_sector = sop->sector[i];
2568                     break;
2569                 }
2570             }
2571 
2572             ASSERT(dest_sector != -1);
2573 
2574             sop->bob_speed = 0;
2575             sop->bob_sine_ndx = 0;
2576             sop->bob_amt = 0;
2577 
2578             //DSPRINTF(ds,"dest sector %d",dest_sector);
2579             MONO_PRINT(ds);
2580 
2581             for (i = 0, sectp = &sop->sectp[0]; *sectp; sectp++, i++)
2582             {
2583                 if (SectUser[sop->sector[i]] && TEST(SectUser[sop->sector[i]]->flags, SECTFU_SO_DONT_SINK))
2584                     continue;
2585 
2586                 ndx = AnimSet(&(*sectp)->floorz, sector[dest_sector].floorz, tpoint->tag_high);
2587                 AnimSetCallback(ndx, CallbackSOsink, sop);
2588                 AnimSetVelAdj(ndx, 6);
2589             }
2590 
2591             break;
2592         }
2593 
2594         case TRACK_SO_FORM_WHIRLPOOL:
2595         {
2596             // for lowering the whirlpool in level 1
2597             SECTORp *sectp;
2598             short i;
2599             SECT_USERp sectu;
2600 
2601             for (i = 0, sectp = &sop->sectp[0]; *sectp; sectp++, i++)
2602             {
2603                 sectu = SectUser[*sectp - sector];
2604 
2605                 if (sectu && sectu->stag == SECT_SO_FORM_WHIRLPOOL)
2606                 {
2607                     AnimSet(&(*sectp)->floorz, (*sectp)->floorz + Z(sectu->height), 128);
2608                     (*sectp)->floorshade += sectu->height/6;
2609 
2610                     RESET((*sectp)->extra, SECTFX_NO_RIDE);
2611                 }
2612             }
2613 
2614             break;
2615         }
2616 
2617         case TRACK_MOVE_VERTICAL:
2618         {
2619             int zr;
2620             SET(sop->flags, SOBJ_MOVE_VERTICAL);
2621 
2622             if (tpoint->tag_high > 0)
2623                 zr = tpoint->tag_high;
2624             else
2625                 zr = 256;
2626 
2627             // look at the next point
2628             NextTrackPoint(sop);
2629             tpoint = Track[sop->track].TrackPoint + sop->point;
2630 
2631             // set anim
2632             AnimSet(&sop->zmid, tpoint->z, zr);
2633 
2634             // move back to current point by reversing direction
2635             sop->dir *= -1;
2636             NextTrackPoint(sop);
2637             tpoint = Track[sop->track].TrackPoint + sop->point;
2638             sop->dir *= -1;
2639 
2640             break;
2641         }
2642 
2643         case TRACK_WAIT_FOR_EVENT:
2644         {
2645             if (tpoint->tag_high == -1)
2646                 break;
2647 
2648             SET(sop->flags, SOBJ_WAIT_FOR_EVENT);
2649             sop->save_vel = sop->vel;
2650             sop->save_spin_speed = sop->spin_speed;
2651 
2652             sop->vel = sop->spin_speed = 0;
2653             // only set event if non-zero
2654             if (tpoint->tag_high)
2655                 sop->match_event = tpoint->tag_high;
2656             tpoint->tag_high = -1;
2657             break;
2658         }
2659 
2660         case TRACK_ZDIFF_MODE:
2661             SET(sop->flags, SOBJ_ZDIFF_MODE);
2662             sop->zdelta = Z(tpoint->tag_high);
2663             break;
2664         case TRACK_ZRATE:
2665             sop->z_rate = Z(tpoint->tag_high);
2666             break;
2667         case TRACK_ZUP:
2668             RESET(sop->flags, SOBJ_ZDOWN | SOBJ_ZUP);
2669             if (sop->dir < 0)
2670             {
2671                 sop->z_tgt = sop->z_tgt + Z(tpoint->tag_high);
2672                 SET(sop->flags, SOBJ_ZDOWN);
2673             }
2674             else
2675             {
2676                 sop->z_tgt = sop->z_tgt - Z(tpoint->tag_high);
2677                 SET(sop->flags, SOBJ_ZUP);
2678             }
2679             break;
2680         case TRACK_ZDOWN:
2681             RESET(sop->flags, SOBJ_ZDOWN | SOBJ_ZUP);
2682             if (sop->dir > 0)
2683             {
2684                 sop->z_tgt = sop->z_tgt + Z(tpoint->tag_high);
2685                 SET(sop->flags, SOBJ_ZDOWN);
2686             }
2687             else
2688             {
2689                 sop->z_tgt = sop->z_tgt - Z(tpoint->tag_high);
2690                 SET(sop->flags, SOBJ_ZUP);
2691             }
2692             break;
2693         }
2694 
2695         // get the next point
2696         NextTrackPoint(sop);
2697         tpoint = Track[sop->track].TrackPoint + sop->point;
2698 
2699         // calculate distance to target poing
2700         sop->target_dist = Distance(sop->xmid, sop->ymid, tpoint->x, tpoint->y);
2701 
2702         // calculate a new angle to the target
2703         sop->ang_moving = sop->ang_tgt = getangle(tpoint->x - sop->xmid, tpoint->y - sop->ymid);
2704 
2705         if (TEST(sop->flags, SOBJ_ZDIFF_MODE))
2706         {
2707             int dist;
2708             short i;
2709 
2710             // set dx,dy,dz up for finding the z magnitude
2711             dx = tpoint->x;
2712             dy = tpoint->y;
2713             dz = tpoint->z - sop->zdelta;
2714 
2715             // find the distance to the target (player)
2716             dist = DIST(dx, dy, sop->xmid, sop->ymid);
2717 
2718             // (velocity * difference between the target and the object)
2719             // / distance
2720             sop->z_rate = (sop->vel * (sop->zmid - dz)) / dist;
2721 
2722             // take absolute value and convert to pixels (divide by 256)
2723             sop->z_rate = PIXZ(labs(sop->z_rate));
2724 
2725             if (TEST(sop->flags, SOBJ_SPRITE_OBJ))
2726             {
2727                 // only modify zmid for sprite_objects
2728                 AnimSet(&sop->zmid, dz, sop->z_rate);
2729             }
2730             else
2731             {
2732                 // churn through sectors setting their new z values
2733                 for (i = 0; sop->sector[i] != -1; i++)
2734                 {
2735                     AnimSet(&sector[sop->sector[i]].floorz, dz - (sector[sop->mid_sector].floorz - sector[sop->sector[i]].floorz), sop->z_rate);
2736                 }
2737             }
2738         }
2739     }
2740     else
2741     {
2742 
2743         // make velocity approach the target velocity
2744         if (TEST(sop->flags, SOBJ_SPEED_UP))
2745         {
2746             if ((sop->vel += (locktics << sop->vel_rate)) >= sop->vel_tgt)
2747             {
2748                 sop->vel = sop->vel_tgt;
2749                 RESET(sop->flags, SOBJ_SPEED_UP);
2750             }
2751         }
2752         else if (TEST(sop->flags, SOBJ_SLOW_DOWN))
2753         {
2754             if ((sop->vel -= (locktics << sop->vel_rate)) <= sop->vel_tgt)
2755             {
2756                 sop->vel = sop->vel_tgt;
2757                 RESET(sop->flags, SOBJ_SLOW_DOWN);
2758             }
2759         }
2760     }
2761 
2762     // calculate a new x and y
2763     if (sop->vel && !TEST(sop->flags,SOBJ_MOVE_VERTICAL))
2764     {
2765         *nx = (DIV256(sop->vel)) * locktics * (int) sintable[NORM_ANGLE(sop->ang_moving + 512)] >> 14;
2766         *ny = (DIV256(sop->vel)) * locktics * (int) sintable[sop->ang_moving] >> 14;
2767 
2768         dist = Distance(sop->xmid, sop->ymid, sop->xmid + *nx, sop->ymid + *ny);
2769         sop->target_dist -= dist;
2770     }
2771 }
2772 
2773 
2774 void
OperateSectorObjectForTics(SECTOR_OBJECTp sop,short newang,int newx,int newy,short locktics)2775 OperateSectorObjectForTics(SECTOR_OBJECTp sop, short newang, int newx, int newy, short locktics)
2776 {
2777     int i;
2778     SECTORp *sectp;
2779 
2780     if (Prediction)
2781         return;
2782 
2783     if (sop->track < SO_OPERATE_TRACK_START)
2784         return;
2785 
2786     so_setinterpolationtics(sop, locktics);
2787 
2788     if (sop->bob_amt)
2789     {
2790         sop->bob_sine_ndx = (totalsynctics << sop->bob_speed) & 2047;
2791         sop->bob_diff = ((sop->bob_amt * (int) sintable[sop->bob_sine_ndx]) >> 14);
2792 
2793         // for all sectors
2794         for (i = 0, sectp = &sop->sectp[0]; *sectp; sectp++, i++)
2795         {
2796             if (SectUser[sop->sector[i]] && TEST(SectUser[sop->sector[i]]->flags, SECTFU_SO_DONT_BOB))
2797                 continue;
2798 
2799             (*sectp)->floorz = sop->zorig_floor[i] + sop->bob_diff;
2800         }
2801     }
2802 
2803     GlobSpeedSO = 0;
2804 
2805     //sop->ang_tgt = newang;
2806     sop->ang_moving = newang;
2807 
2808     sop->spin_ang = 0;
2809     sop->ang = newang;
2810 
2811     RefreshPoints(sop, newx - sop->xmid, newy - sop->ymid, FALSE);
2812 }
2813 
2814 void
OperateSectorObject(SECTOR_OBJECTp sop,short newang,int newx,int newy)2815 OperateSectorObject(SECTOR_OBJECTp sop, short newang, int newx, int newy)
2816 {
2817     OperateSectorObjectForTics(sop, newang, newx, newy, synctics);
2818 }
2819 
2820 void
PlaceSectorObject(SECTOR_OBJECTp sop,int newx,int newy)2821 PlaceSectorObject(SECTOR_OBJECTp sop, int newx, int newy)
2822 {
2823     so_setinterpolationtics(sop, synctics);
2824     RefreshPoints(sop, newx - sop->xmid, newy - sop->ymid, FALSE);
2825 }
2826 
VehicleSetSmoke(SECTOR_OBJECTp sop,ANIMATORp animator)2827 void VehicleSetSmoke(SECTOR_OBJECTp sop, ANIMATORp animator)
2828 {
2829     short SpriteNum, NextSprite;
2830     SECTORp *sectp;
2831     SPRITEp sp;
2832     USERp u;
2833 
2834     for (sectp = sop->sectp; *sectp; sectp++)
2835     {
2836         TRAVERSE_SPRITE_SECT(headspritesect[*sectp - sector], SpriteNum, NextSprite)
2837         {
2838             sp = &sprite[SpriteNum];
2839             u = User[SpriteNum];
2840 
2841             switch (sp->hitag)
2842             {
2843 
2844             case SPAWN_SPOT:
2845                 if (sp->clipdist == 3)
2846                 {
2847                     if (animator)
2848                     {
2849                         if (sp->statnum == STAT_NO_STATE)
2850                             break;
2851 
2852                         change_sprite_stat(SpriteNum, STAT_NO_STATE);
2853                         DoSoundSpotMatch(sp->lotag, 1, 0);
2854                         DoSpawnSpotsForDamage(sp->lotag);
2855                     }
2856                     else
2857                     {
2858                         change_sprite_stat(SpriteNum, STAT_SPAWN_SPOT);
2859                         DoSoundSpotStopSound(sp->lotag);
2860                     }
2861 
2862                     u->ActorActionFunc = animator;
2863                 }
2864                 break;
2865             }
2866         }
2867     }
2868 }
2869 
2870 
2871 void
KillSectorObject(SECTOR_OBJECTp sop)2872 KillSectorObject(SECTOR_OBJECTp sop)
2873 {
2874     int newx = MAXSO;
2875     int newy = MAXSO;
2876     short newang = 0;
2877 
2878     if (sop->track < SO_OPERATE_TRACK_START)
2879         return;
2880 
2881     sop->ang_tgt = sop->ang_moving = newang;
2882 
2883     sop->spin_ang = 0;
2884     sop->ang = sop->ang_tgt;
2885 
2886     RefreshPoints(sop, newx - sop->xmid, newy - sop->ymid, FALSE);
2887 }
2888 
2889 
TornadoSpin(SECTOR_OBJECTp sop)2890 void TornadoSpin(SECTOR_OBJECTp sop)
2891 {
2892     short delta_ang, speed;
2893     short locktics = synctics;
2894 
2895     // get delta to target angle
2896     delta_ang = GetDeltaAngle(sop->ang_tgt, sop->ang);
2897 
2898     sop->ang = NORM_ANGLE(sop->ang + (delta_ang >> sop->turn_speed));
2899     delta_ang = delta_ang >> sop->turn_speed;
2900 
2901     // move z values
2902     MoveZ(sop);
2903 
2904     // calculate the spin speed
2905     speed = sop->spin_speed * locktics;
2906     // spin_ang is incremented by the spin_speed
2907     sop->spin_ang = NORM_ANGLE(sop->spin_ang + speed);
2908 
2909     if (sop->spin_speed)
2910     {
2911         // ignore delta angle if spinning
2912         GlobSpeedSO = speed;
2913     }
2914     else
2915     {
2916         // The actual delta from the last frame
2917         GlobSpeedSO = speed;
2918         GlobSpeedSO += delta_ang;
2919     }
2920 }
2921 
2922 void
DoTornadoObject(SECTOR_OBJECTp sop)2923 DoTornadoObject(SECTOR_OBJECTp sop)
2924 {
2925     int xvect,yvect;
2926     short cursect;
2927     // this made them move together more or less - cool!
2928     //static short ang = 1024;
2929     int floor_dist;
2930     vec3_t pos;
2931     int ret;
2932     short *ang = &sop->ang_moving;
2933 
2934     xvect = (sop->vel * sintable[NORM_ANGLE(*ang + 512)]);
2935     yvect = (sop->vel * sintable[NORM_ANGLE(*ang)]);
2936 
2937     cursect = sop->op_main_sector; // for sop->vel
2938     floor_dist = DIV4(labs(sector[cursect].ceilingz - sector[cursect].floorz));
2939     pos.x = sop->xmid;
2940     pos.y = sop->ymid;
2941     pos.z = floor_dist;
2942 
2943     PlaceSectorObject(sop, MAXSO, MAXSO);
2944     ret = clipmove(&pos, &cursect, xvect, yvect, (int)sop->clipdist, Z(0), floor_dist, CLIPMASK_ACTOR);
2945 
2946     if (ret)
2947     {
2948         *ang = NORM_ANGLE(*ang + 1024 + RANDOM_P2(512) - 256);
2949     }
2950 
2951     TornadoSpin(sop);
2952     RefreshPoints(sop, pos.x - sop->xmid, pos.y - sop->ymid, TRUE);
2953 }
2954 
2955 void
DoAutoTurretObject(SECTOR_OBJECTp sop)2956 DoAutoTurretObject(SECTOR_OBJECTp sop)
2957 {
2958     short SpriteNum = sop->sp_child - sprite;
2959     SPRITEp shootp;
2960     USERp u = User[SpriteNum];
2961     short delta_ang;
2962     int diff;
2963     short i;
2964 
2965     if (sop->max_damage != -9999 && sop->max_damage <= 0)
2966         return;
2967 
2968 
2969     u->WaitTics -= synctics;
2970 
2971     // check for new player if doesn't have a target or time limit expired
2972     if (!u->tgt_sp || u->WaitTics < 0)
2973     {
2974         // 4 seconds
2975         u->WaitTics = 4*120;
2976         DoActorPickClosePlayer(SpriteNum);
2977     }
2978 
2979     if (MoveSkip2 == 0)
2980     {
2981         for (i = 0; sop->sp_num[i] != -1; i++)
2982         {
2983             if (sprite[sop->sp_num[i]].statnum == STAT_SO_SHOOT_POINT)
2984             {
2985                 shootp = &sprite[sop->sp_num[i]];
2986 
2987                 if (!FAFcansee(shootp->x, shootp->y, shootp->z-Z(4), shootp->sectnum,
2988                                u->tgt_sp->x, u->tgt_sp->y, SPRITEp_UPPER(u->tgt_sp), u->tgt_sp->sectnum))
2989                 {
2990                     return;
2991                 }
2992             }
2993         }
2994 
2995 
2996         // FirePausing
2997         if (u->Counter > 0)
2998         {
2999             u->Counter -= synctics*2;
3000             if (u->Counter <= 0)
3001                 u->Counter = 0;
3002         }
3003 
3004         if (u->Counter == 0)
3005         {
3006             shootp = NULL;
3007             for (i = 0; sop->sp_num[i] != -1; i++)
3008             {
3009                 if (sprite[sop->sp_num[i]].statnum == STAT_SO_SHOOT_POINT)
3010                 {
3011                     shootp = &sprite[sop->sp_num[i]];
3012 
3013                     if (SP_TAG5(shootp))
3014                         u->Counter = SP_TAG5(shootp);
3015                     else
3016                         u->Counter = 12;
3017                     InitTurretMgun(sop);
3018                 }
3019             }
3020         }
3021 
3022         //sop->ang_tgt = getangle(sop->xmid - u->tgt_sp->x, sop->ymid - u->tgt_sp->y);
3023         sop->ang_tgt = getangle(u->tgt_sp->x - sop->xmid,  u->tgt_sp->y - sop->ymid);
3024 
3025         // get delta to target angle
3026         delta_ang = GetDeltaAngle(sop->ang_tgt, sop->ang);
3027 
3028         //sop->ang += delta_ang >> 4;
3029         sop->ang = NORM_ANGLE(sop->ang + (delta_ang >> 3));
3030         //sop->ang += delta_ang >> 2;
3031 
3032         if (sop->limit_ang_center >= 0)
3033         {
3034             diff = GetDeltaAngle(sop->ang, sop->limit_ang_center);
3035 
3036             if (labs(diff) >= sop->limit_ang_delta)
3037             {
3038                 if (diff < 0)
3039                     sop->ang = sop->limit_ang_center - sop->limit_ang_delta;
3040                 else
3041                     sop->ang = sop->limit_ang_center + sop->limit_ang_delta;
3042 
3043             }
3044         }
3045 
3046         OperateSectorObjectForTics(sop, sop->ang, sop->xmid, sop->ymid, 2*synctics);
3047     }
3048 }
3049 
3050 
3051 void
DoActorHitTrackEndPoint(USERp u)3052 DoActorHitTrackEndPoint(USERp u)
3053 {
3054     SPRITEp sp = u->SpriteP;
3055 
3056     RESET(Track[u->track].flags, TF_TRACK_OCCUPIED);
3057 
3058     // jump the current track & determine if you should go to another
3059     if (TEST(u->Flags, SPR_RUN_AWAY))
3060     {
3061         short FindTrackAwayFromPlayer(USERp);
3062 
3063         //DSPRINTF(ds, "End Of Track - Looking for another!\n");
3064         MONO_PRINT(ds);
3065 
3066         // look for another track leading away from the player
3067         u->track = FindTrackAwayFromPlayer(u);
3068 
3069         if (u->track >= 0)
3070         {
3071             sp->ang = NORM_ANGLE(getangle((Track[u->track].TrackPoint + u->point)->x - sp->x, (Track[u->track].TrackPoint + u->point)->y - sp->y));
3072             //DSPRINTF(ds, "Track Away From Player!\n");
3073             MONO_PRINT(ds);
3074         }
3075         else
3076         {
3077             RESET(u->Flags, SPR_RUN_AWAY);
3078             DoActorSetSpeed(sp - sprite, NORM_SPEED);
3079             u->track = -1;
3080         }
3081     }
3082     else if (TEST(u->Flags, SPR_FIND_PLAYER))
3083     {
3084         short FindTrackToPlayer(USERp);
3085 
3086         //DSPRINTF(ds, "End Of Track - Looking for another!\n");
3087         MONO_PRINT(ds);
3088 
3089         // look for another track leading away from the player
3090         u->track = FindTrackToPlayer(u);
3091 
3092         if (u->track >= 0)
3093         {
3094             sp->ang = NORM_ANGLE(getangle((Track[u->track].TrackPoint + u->point)->x - sp->x, (Track[u->track].TrackPoint + u->point)->y - sp->y));
3095         }
3096         else
3097         {
3098             RESET(u->Flags, SPR_FIND_PLAYER);
3099             DoActorSetSpeed(sp - sprite, NORM_SPEED);
3100             u->track = -1;
3101         }
3102     }
3103     else
3104     {
3105         //DSPRINTF(ds, "End Of Track - DONT Look for another!\n");
3106         MONO_PRINT(ds);
3107 
3108         u->track = -1;
3109     }
3110 }
3111 
3112 
3113 void
ActorLeaveTrack(short SpriteNum)3114 ActorLeaveTrack(short SpriteNum)
3115 {
3116     USERp u = User[SpriteNum];
3117 
3118     if (u->track == -1)
3119         return;
3120 
3121     RESET(u->Flags, SPR_FIND_PLAYER|SPR_RUN_AWAY|SPR_CLIMBING);
3122     RESET(Track[u->track].flags, TF_TRACK_OCCUPIED);
3123     u->track = -1;
3124 }
3125 
3126 /*
3127 ScanToWall
3128 (lsp->x, lsp->y, SPRITEp_TOS(sp) - DIV2(SPRITEp_SIZE_Z(sp)), lsp->sectnum,
3129     sintable[NORM_ANGLE(lsp->ang + 1024 + 512)],
3130     sintable[lsp->ang + 1024],
3131     0,
3132     &hitinfo);
3133 */
3134 
3135 
3136 SWBOOL
ActorTrackDecide(TRACK_POINTp tpoint,short SpriteNum)3137 ActorTrackDecide(TRACK_POINTp tpoint, short SpriteNum)
3138 {
3139     SPRITEp sp;
3140     USERp u = User[SpriteNum];
3141 
3142     sp = u->SpriteP;
3143 
3144     //DSPRINTF(ds,"tpoint->tag_low = %d, u->ID = %d\n",tpoint->tag_low,u->ID);
3145     MONO_PRINT(ds);
3146 
3147     switch (tpoint->tag_low)
3148     {
3149     case TRACK_START:
3150 
3151         // if track has a type and actor is going the right direction jump
3152         // the track
3153         if (Track[u->track].ttflags)
3154         {
3155             if (u->track_dir == -1)
3156             {
3157                 DoActorHitTrackEndPoint(u);
3158                 return FALSE;
3159             }
3160         }
3161 
3162         break;
3163 
3164     case TRACK_END:
3165         // if track has a type and actor is going to right direction jump the
3166         // track
3167         if (Track[u->track].ttflags)
3168         {
3169             if (u->track_dir == 1)
3170             {
3171                 DoActorHitTrackEndPoint(u);
3172                 return FALSE;
3173             }
3174         }
3175 
3176         break;
3177 
3178 
3179     case TRACK_ACTOR_WAIT_FOR_PLAYER:
3180     {
3181         SET(u->Flags, SPR_WAIT_FOR_PLAYER);
3182         u->Dist = tpoint->tag_high;
3183         break;
3184     }
3185 
3186     case TRACK_ACTOR_WAIT_FOR_TRIGGER:
3187     {
3188         SET(u->Flags, SPR_WAIT_FOR_TRIGGER);
3189         u->Dist = tpoint->tag_high;
3190         break;
3191     }
3192 
3193     //
3194     // Controls the velocity
3195     //
3196 
3197     case TRACK_ACTOR_VEL_RATE:
3198         u->vel_rate = tpoint->tag_high;
3199         break;
3200     case TRACK_ACTOR_SPEED_UP:
3201         RESET(u->Flags, SPR_SLOW_DOWN | SPR_SPEED_UP);
3202         if (u->track_dir < 0)
3203         {
3204             // set target to new slower target
3205             u->vel_tgt = u->vel_tgt - (tpoint->tag_high * 256);
3206             SET(u->Flags, SPR_SLOW_DOWN);
3207         }
3208         else
3209         {
3210             u->vel_tgt = u->vel_tgt + (tpoint->tag_high * 256);
3211             SET(u->Flags, SPR_SPEED_UP);
3212         }
3213 
3214         break;
3215 
3216     case TRACK_ACTOR_SLOW_DOWN:
3217         RESET(u->Flags, SPR_SLOW_DOWN | SPR_SPEED_UP);
3218         if (u->track_dir > 0)
3219         {
3220             u->vel_tgt = u->vel_tgt - (tpoint->tag_high * 256);
3221             SET(u->Flags, SPR_SLOW_DOWN);
3222         }
3223         else
3224         {
3225             u->vel_tgt = u->vel_tgt + (tpoint->tag_high * 256);
3226             SET(u->Flags, SPR_SPEED_UP);
3227         }
3228         break;
3229 
3230     // Reverse it
3231     case TRACK_ACTOR_REVERSE:
3232         u->track_dir *= -1;
3233         break;
3234 
3235     case TRACK_ACTOR_STAND:
3236         NewStateGroup(SpriteNum, u->ActorActionSet->Stand);
3237         break;
3238 
3239     case TRACK_ACTOR_JUMP:
3240         if (u->ActorActionSet->Jump)
3241         {
3242             sp->ang = tpoint->ang;
3243 
3244             if (!tpoint->tag_high)
3245                 u->jump_speed = ACTOR_STD_JUMP;
3246             else
3247                 u->jump_speed = -tpoint->tag_high;
3248 
3249             DoActorBeginJump(SpriteNum);
3250             u->ActorActionFunc = DoActorMoveJump;
3251         }
3252 
3253         break;
3254 
3255     case TRACK_ACTOR_QUICK_JUMP:
3256     case TRACK_ACTOR_QUICK_SUPER_JUMP:
3257         if (u->ActorActionSet->Jump)
3258         {
3259             int DoActorMoveJump(short SpriteNum);
3260             int PickJumpSpeed(short SpriteNum, int pix_height);
3261             int zdiff;
3262             hitdata_t hitinfo;
3263 
3264             sp->ang = tpoint->ang;
3265 
3266 
3267             ActorLeaveTrack(SpriteNum);
3268 
3269             if (tpoint->tag_high)
3270             {
3271                 u->jump_speed = -tpoint->tag_high;
3272             }
3273             else
3274             {
3275                 RESET(sp->cstat, CSTAT_SPRITE_BLOCK);
3276 
3277                 FAFhitscan(sp->x, sp->y, sp->z - Z(24), sp->sectnum,      // Start position
3278                            sintable[NORM_ANGLE(sp->ang + 512)],      // X vector of 3D ang
3279                            sintable[sp->ang],    // Y vector of 3D ang
3280                            0,                            // Z vector of 3D ang
3281                            &hitinfo, CLIPMASK_MISSILE);
3282 
3283                 SET(sp->cstat, CSTAT_SPRITE_BLOCK);
3284 
3285                 ASSERT(hitinfo.sect >= 0);
3286 
3287                 if (hitinfo.sprite >= 0)
3288                     return FALSE;
3289 
3290                 if (hitinfo.wall < 0)
3291                     return FALSE;
3292 
3293                 zdiff = labs(sp->z - sector[wall[hitinfo.wall].nextsector].floorz) >> 8;
3294 
3295                 u->jump_speed = PickJumpSpeed(SpriteNum, zdiff);
3296             }
3297 
3298             DoActorBeginJump(SpriteNum);
3299             u->ActorActionFunc = DoActorMoveJump;
3300 
3301             return FALSE;
3302         }
3303 
3304         break;
3305 
3306     case TRACK_ACTOR_QUICK_JUMP_DOWN:
3307 
3308         if (u->ActorActionSet->Jump)
3309         {
3310             int DoActorMoveJump(short SpriteNum);
3311 
3312             sp->ang = tpoint->ang;
3313 
3314             ActorLeaveTrack(SpriteNum);
3315 
3316             if (tpoint->tag_high)
3317             {
3318                 u->jump_speed = -tpoint->tag_high;
3319             }
3320             else
3321             {
3322                 u->jump_speed = -350;
3323             }
3324 
3325             DoActorBeginJump(SpriteNum);
3326             u->ActorActionFunc = DoActorMoveJump;
3327             return FALSE;
3328         }
3329 
3330         break;
3331 
3332     case TRACK_ACTOR_QUICK_SCAN:
3333 
3334         if (u->ActorActionSet->Jump)
3335         {
3336             ActorLeaveTrack(SpriteNum);
3337             return FALSE;
3338         }
3339 
3340         break;
3341 
3342     case TRACK_ACTOR_QUICK_DUCK:
3343 
3344         if (u->Rot != u->ActorActionSet->Duck)
3345         {
3346             int DoActorDuck(short SpriteNum);
3347 
3348             sp->ang = tpoint->ang;
3349 
3350             ActorLeaveTrack(SpriteNum);
3351 
3352             if (!tpoint->tag_high)
3353                 u->WaitTics = 4 * 120;
3354             else
3355                 u->WaitTics = tpoint->tag_high * 128;
3356 
3357             InitActorDuck(SpriteNum);
3358             u->ActorActionFunc = DoActorDuck;
3359             return FALSE;
3360         }
3361 
3362         break;
3363 
3364     case TRACK_ACTOR_OPERATE:
3365     case TRACK_ACTOR_QUICK_OPERATE:
3366     {
3367         short nearsector, nearwall, nearsprite;
3368         int nearhitdist;
3369         int z[2];
3370         int i;
3371 
3372         if (u->Rot == u->ActorActionSet->Sit || u->Rot == u->ActorActionSet->Stand)
3373             return FALSE;
3374 
3375         sp->ang = tpoint->ang;
3376 
3377 //          //DSPRINTF(ds,"sp->x = %ld, sp->y = %ld, sp->sector = %d, tp->x = %ld, tp->y = %ld, tp->ang = %d\n",sp->x,sp->y,sp->sectnum,tpoint->x,tpoint->y,tpoint->ang);
3378 //          MONO_PRINT(ds);
3379 
3380         z[0] = sp->z - SPRITEp_SIZE_Z(sp) + Z(5);
3381         z[1] = sp->z - DIV2(SPRITEp_SIZE_Z(sp));
3382 
3383         for (i = 0; i < (int)SIZ(z); i++)
3384         {
3385             neartag(sp->x, sp->y, z[i], sp->sectnum, sp->ang,
3386                     &nearsector, &nearwall, &nearsprite,
3387                     &nearhitdist, 1024L, NTAG_SEARCH_LO_HI, NULL);
3388 
3389 //              //DSPRINTF(ds,"nearsector = %d, nearwall = %d, nearsprite = %d hitdist == %ld\n",nearsector,nearwall,nearsprite,nearhitdist);
3390 //              MONO_PRINT(ds);
3391 
3392             if (nearsprite >= 0 && nearhitdist < 1024)
3393             {
3394                 if (OperateSprite(nearsprite, FALSE))
3395                 {
3396                     if (!tpoint->tag_high)
3397                         u->WaitTics = 2 * 120;
3398                     else
3399                         u->WaitTics = tpoint->tag_high * 128;
3400 
3401                     NewStateGroup(SpriteNum, u->ActorActionSet->Stand);
3402                 }
3403             }
3404         }
3405 
3406         if (nearsector >= 0 && nearhitdist < 1024)
3407         {
3408             if (OperateSector(nearsector, FALSE))
3409             {
3410                 if (!tpoint->tag_high)
3411                     u->WaitTics = 2 * 120;
3412                 else
3413                     u->WaitTics = tpoint->tag_high * 128;
3414 
3415                 NewStateGroup(SpriteNum, u->ActorActionSet->Sit);
3416             }
3417         }
3418 
3419         if (nearwall >= 0 && nearhitdist < 1024)
3420         {
3421             if (OperateWall(nearwall, FALSE))
3422             {
3423                 if (!tpoint->tag_high)
3424                     u->WaitTics = 2 * 120;
3425                 else
3426                     u->WaitTics = tpoint->tag_high * 128;
3427 
3428                 NewStateGroup(SpriteNum, u->ActorActionSet->Stand);
3429             }
3430         }
3431 
3432         break;
3433     }
3434 
3435     case TRACK_ACTOR_JUMP_IF_FORWARD:
3436         if (u->ActorActionSet->Jump && u->track_dir == 1)
3437         {
3438             if (!tpoint->tag_high)
3439                 u->jump_speed = ACTOR_STD_JUMP;
3440             else
3441                 u->jump_speed = -tpoint->tag_high;
3442 
3443             DoActorBeginJump(SpriteNum);
3444         }
3445 
3446         break;
3447 
3448     case TRACK_ACTOR_JUMP_IF_REVERSE:
3449         if (u->ActorActionSet->Jump && u->track_dir == -1)
3450         {
3451             if (!tpoint->tag_high)
3452                 u->jump_speed = ACTOR_STD_JUMP;
3453             else
3454                 u->jump_speed = -tpoint->tag_high;
3455 
3456             DoActorBeginJump(SpriteNum);
3457         }
3458 
3459         break;
3460 
3461     case TRACK_ACTOR_CRAWL:
3462         if (u->Rot != u->ActorActionSet->Crawl)
3463             NewStateGroup(SpriteNum, u->ActorActionSet->Crawl);
3464         else
3465             NewStateGroup(SpriteNum, u->ActorActionSet->Rise);
3466         break;
3467 
3468     case TRACK_ACTOR_SWIM:
3469         if (u->Rot != u->ActorActionSet->Swim)
3470             NewStateGroup(SpriteNum, u->ActorActionSet->Swim);
3471         else
3472             NewStateGroup(SpriteNum, u->ActorActionSet->Rise);
3473         break;
3474 
3475     case TRACK_ACTOR_FLY:
3476         NewStateGroup(SpriteNum, u->ActorActionSet->Fly);
3477         break;
3478 
3479     case TRACK_ACTOR_SIT:
3480 
3481         if (u->ActorActionSet->Sit)
3482         {
3483             if (!tpoint->tag_high)
3484                 u->WaitTics = 3 * 120;
3485             else
3486                 u->WaitTics = tpoint->tag_high * 128;
3487 
3488             NewStateGroup(SpriteNum, u->ActorActionSet->Sit);
3489         }
3490 
3491         break;
3492 
3493     case TRACK_ACTOR_DEATH1:
3494         if (u->ActorActionSet->Death2)
3495         {
3496             u->WaitTics = 4 * 120;
3497             NewStateGroup(SpriteNum, u->ActorActionSet->Death1);
3498         }
3499         break;
3500 
3501     case TRACK_ACTOR_DEATH2:
3502 
3503         if (u->ActorActionSet->Death2)
3504         {
3505             u->WaitTics = 4 * 120;
3506             NewStateGroup(SpriteNum, u->ActorActionSet->Death2);
3507         }
3508 
3509         break;
3510 
3511     case TRACK_ACTOR_DEATH_JUMP:
3512 
3513         if (u->ActorActionSet->DeathJump)
3514         {
3515             SET(u->Flags, SPR_DEAD);
3516             sp->xvel <<= 1;
3517             u->jump_speed = -495;
3518             DoActorBeginJump(SpriteNum);
3519             NewStateGroup(SpriteNum, u->ActorActionSet->DeathJump);
3520         }
3521 
3522         break;
3523 
3524     case TRACK_ACTOR_CLOSE_ATTACK1:
3525 
3526         if (u->ActorActionSet->CloseAttack[0])
3527         {
3528             if (!tpoint->tag_high)
3529                 u->WaitTics = 2 * 120;
3530             else
3531                 u->WaitTics = tpoint->tag_high * 128;
3532 
3533             NewStateGroup(SpriteNum, u->ActorActionSet->CloseAttack[0]);
3534         }
3535 
3536         break;
3537 
3538     case TRACK_ACTOR_CLOSE_ATTACK2:
3539 
3540         if (u->ActorActionSet->CloseAttack[1])
3541         {
3542             if (!tpoint->tag_high)
3543                 u->WaitTics = 4 * 120;
3544             else
3545                 u->WaitTics = tpoint->tag_high * 128;
3546 
3547             NewStateGroup(SpriteNum, u->ActorActionSet->CloseAttack[1]);
3548         }
3549 
3550         break;
3551 
3552     case TRACK_ACTOR_ATTACK1:
3553     case TRACK_ACTOR_ATTACK2:
3554     case TRACK_ACTOR_ATTACK3:
3555     case TRACK_ACTOR_ATTACK4:
3556     case TRACK_ACTOR_ATTACK5:
3557     case TRACK_ACTOR_ATTACK6:
3558     {
3559         STATEp **ap = &u->ActorActionSet->Attack[0] + (tpoint->tag_low - TRACK_ACTOR_ATTACK1);
3560 
3561 
3562         if (*ap)
3563         {
3564             if (!tpoint->tag_high)
3565                 u->WaitTics = 4 * 120;
3566             else
3567                 u->WaitTics = tpoint->tag_high * 128;
3568 
3569             NewStateGroup(SpriteNum, *ap);
3570         }
3571 
3572         break;
3573     }
3574 
3575     case TRACK_ACTOR_ZDIFF_MODE:
3576         if (TEST(u->Flags, SPR_ZDIFF_MODE))
3577         {
3578             RESET(u->Flags, SPR_ZDIFF_MODE);
3579             sp->z = sector[sp->sectnum].floorz;
3580             sp->zvel = 0;
3581         }
3582         else
3583         {
3584             SET(u->Flags, SPR_ZDIFF_MODE);
3585         }
3586         break;
3587 
3588     case TRACK_ACTOR_CLIMB_LADDER:
3589 
3590         if (u->ActorActionSet->Jump)
3591         {
3592             short hit_sect, hit_wall, hit_sprite;
3593             int bos_z,nx,ny;
3594             int dist;
3595             SPRITEp lsp;
3596             SPRITEp FindNearSprite(SPRITEp, short);
3597 
3598             //
3599             // Get angle and x,y pos from CLIMB_MARKER
3600             //
3601             lsp = FindNearSprite(sp, STAT_CLIMB_MARKER);
3602 
3603             if (!lsp)
3604             {
3605                 ActorLeaveTrack(SpriteNum);
3606                 return FALSE;
3607             }
3608 
3609             // determine where the player is supposed to be in relation to the ladder
3610             // move out in front of the ladder
3611             nx = MOVEx(100, lsp->ang);
3612             ny = MOVEy(100, lsp->ang);
3613 
3614             sp->x = lsp->x + nx;
3615             sp->y = lsp->y + ny;
3616 
3617             sp->ang = NORM_ANGLE(lsp->ang + 1024);
3618 
3619             //
3620             // Get the z height to climb
3621             //
3622 
3623             neartag(sp->x, sp->y, SPRITEp_TOS(sp) - DIV2(SPRITEp_SIZE_Z(sp)), sp->sectnum,
3624                     sp->ang,
3625                     &hit_sect, &hit_wall, &hit_sprite,
3626                     &dist, 600L, NTAG_SEARCH_LO_HI, NULL);
3627 
3628             if (hit_wall < 0)
3629             {
3630                 ActorLeaveTrack(SpriteNum);
3631                 return FALSE;
3632             }
3633 
3634 #if DEBUG
3635             if (wall[hit_wall].nextsector < 0)
3636             {
3637                 TerminateGame();
3638                 printf("Take out white wall ladder x = %d, y = %d",wall[hit_wall].x, wall[hit_wall].y);
3639                 exit(0);
3640             }
3641 #endif
3642 
3643             // destination z for climbing
3644             u->sz = sector[wall[hit_wall].nextsector].floorz;
3645 
3646             DoActorZrange(SpriteNum);
3647 
3648             //
3649             // Adjust for YCENTERING
3650             //
3651 
3652             SET(sp->cstat, CSTAT_SPRITE_YCENTER);
3653             bos_z = SPRITEp_BOS(sp);
3654             if (bos_z > u->loz)
3655             {
3656                 u->sy = (bos_z - sp->z);
3657                 sp->z -= u->sy;
3658             }
3659 
3660             //
3661             // Misc climb setup
3662             //
3663 
3664             SET(u->Flags, SPR_CLIMBING);
3665             NewStateGroup(SpriteNum, u->ActorActionSet->Climb);
3666 
3667             sp->zvel = -Z(1);
3668         }
3669 
3670         break;
3671 
3672     case TRACK_ACTOR_SET_JUMP:
3673         u->jump_speed = -tpoint->tag_high;
3674         break;
3675     }
3676 
3677     return TRUE;
3678 }
3679 
3680 /*
3681 
3682 !AIC - This is where actors follow tracks.  Its massy, hard to read, and more
3683 complex than it needs to be.  It was taken from sector object track movement
3684 code.  The routine above ActorTrackDecide() is where a track tag is recognized
3685 and acted upon.  There are quite a few of these that are not useful to us at
3686 present time.
3687 
3688 */
3689 
3690 int
ActorFollowTrack(short SpriteNum,short locktics)3691 ActorFollowTrack(short SpriteNum, short locktics)
3692 {
3693     USERp u = User[SpriteNum];
3694     SPRITEp sp = User[SpriteNum]->SpriteP;
3695     PLAYERp pp;
3696 
3697     int move_actor(short SpriteNum, int xchange, int ychange, int zchange);
3698 
3699     TRACK_POINTp tpoint;
3700     short pnum;
3701     int nx = 0, ny = 0, nz = 0, dx, dy, dz;
3702     int dist;
3703 
3704     // if not on a track then better not go here
3705     if (u->track == -1)
3706         return TRUE;
3707 
3708     // if lying in wait for player
3709     if (TEST(u->Flags, SPR_WAIT_FOR_PLAYER | SPR_WAIT_FOR_TRIGGER))
3710     {
3711         if (TEST(u->Flags, SPR_WAIT_FOR_PLAYER))
3712         {
3713             TRAVERSE_CONNECT(pnum)
3714             {
3715                 pp = &Player[pnum];
3716 
3717                 if (Distance(sp->x, sp->y, pp->posx, pp->posy) < u->Dist)
3718                 {
3719                     u->tgt_sp = pp->SpriteP;
3720                     RESET(u->Flags, SPR_WAIT_FOR_PLAYER);
3721                     return TRUE;
3722                 }
3723             }
3724         }
3725 
3726         u->Tics = 0;
3727         return TRUE;
3728     }
3729 
3730     // if pausing the return
3731     if (u->WaitTics)
3732     {
3733         u->WaitTics -= locktics;
3734         if (u->WaitTics <= 0)
3735         {
3736             RESET(u->Flags, SPR_DONT_UPDATE_ANG);
3737             NewStateGroup(SpriteNum, u->ActorActionSet->Run);
3738             u->WaitTics = 0;
3739         }
3740 
3741         return TRUE;
3742     }
3743 
3744     tpoint = Track[u->track].TrackPoint + u->point;
3745 
3746     if (!(TEST(u->Flags, SPR_CLIMBING | SPR_DONT_UPDATE_ANG)))
3747     {
3748         sp->ang = getangle(tpoint->x - sp->x, tpoint->y - sp->y);
3749     }
3750 
3751     if ((dist = Distance(sp->x, sp->y, tpoint->x, tpoint->y)) < 200) // 64
3752     {
3753         if (!ActorTrackDecide(tpoint, SpriteNum))
3754             return TRUE;
3755 
3756         // get the next point
3757         NextActorTrackPoint(SpriteNum);
3758         tpoint = Track[u->track].TrackPoint + u->point;
3759 
3760         if (!(TEST(u->Flags, SPR_CLIMBING | SPR_DONT_UPDATE_ANG)))
3761         {
3762             // calculate a new angle to the target
3763             sp->ang = getangle(tpoint->x - sp->x, tpoint->y - sp->y);
3764         }
3765 
3766         if (TEST(u->Flags, SPR_ZDIFF_MODE))
3767         {
3768             int dist;
3769 
3770             // set dx,dy,dz up for finding the z magnitude
3771             dx = tpoint->x;
3772             dy = tpoint->y;
3773             dz = tpoint->z;
3774 
3775             // find the distance to the target (player)
3776             dist = DIST(dx, dy, sp->x, sp->y);
3777 
3778             // (velocity * difference between the target and the object) /
3779             // distance
3780             sp->zvel = -((sp->xvel * (sp->z - dz)) / dist);
3781         }
3782     }
3783     else
3784     {
3785         // make velocity approach the target velocity
3786         if (TEST(u->Flags, SPR_SPEED_UP))
3787         {
3788             if ((u->track_vel += (locktics << u->vel_rate)) >= u->vel_tgt)
3789             {
3790                 u->track_vel = u->vel_tgt;
3791                 RESET(u->Flags, SPR_SPEED_UP);
3792             }
3793 
3794             // update the real velocity
3795             sp->xvel = DIV256(u->track_vel);
3796         }
3797         else if (TEST(u->Flags, SPR_SLOW_DOWN))
3798         {
3799             if ((u->track_vel -= (locktics << u->vel_rate)) <= u->vel_tgt)
3800             {
3801                 u->track_vel = u->vel_tgt;
3802                 RESET(u->Flags, SOBJ_SLOW_DOWN);
3803             }
3804 
3805             sp->xvel = DIV256(u->track_vel);
3806         }
3807 
3808         nx = 0;
3809         ny = 0;
3810 
3811         if (TEST(u->Flags, SPR_CLIMBING))
3812         {
3813             if (SPRITEp_TOS(sp) + DIV4(SPRITEp_SIZE_Z(sp)) < u->sz)
3814             {
3815                 ANIMATOR DoActorMoveJump;
3816                 ANIMATOR NinjaJumpActionFunc;
3817                 RESET(u->Flags, SPR_CLIMBING);
3818 
3819                 sp->zvel = 0;
3820 
3821                 sp->ang = getangle(tpoint->x - sp->x, tpoint->y - sp->y);
3822 
3823                 ActorLeaveTrack(SpriteNum);
3824                 RESET(sp->cstat, CSTAT_SPRITE_YCENTER);
3825                 sp->z += u->sy;
3826 
3827                 DoActorSetSpeed(SpriteNum, SLOW_SPEED);
3828                 u->ActorActionFunc = NinjaJumpActionFunc;
3829                 u->jump_speed = -650;
3830                 DoActorBeginJump(SpriteNum);
3831 
3832                 return TRUE;
3833             }
3834         }
3835         else
3836         {
3837             // calculate a new x and y
3838             nx = sp->xvel * (int) sintable[NORM_ANGLE(sp->ang + 512)] >> 14;
3839             ny = sp->xvel * (int) sintable[sp->ang] >> 14;
3840         }
3841 
3842         nz = 0;
3843 
3844         if (sp->zvel)
3845             nz = sp->zvel * locktics;
3846     }
3847 
3848     u->ret = move_sprite(SpriteNum, nx, ny, nz, u->ceiling_dist, u->floor_dist, 0, locktics);
3849 
3850 
3851     if (u->ret)
3852     {
3853         if (!TEST(u->Flags, SPR_JUMPING|SPR_FALLING))
3854             ActorLeaveTrack(SpriteNum);
3855     }
3856 
3857 
3858     return TRUE;
3859 }
3860 
3861 
3862 #include "saveable.h"
3863 
3864 static saveable_code saveable_track_code[] =
3865 {
3866     SAVE_CODE(DoTrack),
3867     SAVE_CODE(DoTornadoObject),
3868     SAVE_CODE(DoAutoTurretObject),
3869     SAVE_CODE(DoActorHitTrackEndPoint),
3870     SAVE_CODE(CallbackSOsink),
3871 };
3872 
3873 saveable_module saveable_track =
3874 {
3875     // code
3876     saveable_track_code,
3877     SIZ(saveable_track_code),
3878 
3879     // data
3880     NULL,0
3881 };
3882 
3883