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] = §or[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(§or[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 == §or[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(§or[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