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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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 "keys.h"
29 #include "names2.h"
30 #include "panel.h"
31 #include "game.h"
32 #include "tags.h"
33 #include "sector.h"
34 #include "player.h"
35 #include "quake.h"
36 #include "weapon.h"
37 #include "jtags.h"
38 
39 #include "net.h"
40 
41 #include "break.h"
42 #include "track.h"
43 #include "sprite.h"
44 #include "mathutil.h"
45 #include "light.h"
46 #include "text.h"
47 
48 #define LAVASIZ 128
49 #define LAVALOGSIZ 7
50 #define LAVAMAXDROPS 32
51 #define DEFAULT_DOOR_SPEED 800
52 
53 short FindNextSectorByTag(short sectnum, int tag);
54 short LevelSecrets;
55 BOOL TestVatorMatchActive(short match);
56 BOOL TestSpikeMatchActive(short match);
57 BOOL TestRotatorMatchActive(short match);
58 BOOL TestSlidorMatchActive(short match);
59 int PlayerCheckDeath(PLAYERp, short);
60 short DoVatorOperate(PLAYERp, short);
61 short DoVatorMatch(PLAYERp pp, short match);
62 short DoRotatorOperate(PLAYERp, short);
63 short DoRotatorMatch(PLAYERp pp, short match, BOOL);
64 short DoSlidorOperate(PLAYERp, short);
65 short DoSlidorMatch(PLAYERp pp, short match, BOOL);
66 
67 VOID KillMatchingCrackSprites(short match);
68 int DoTrapReset(short match);
69 int DoTrapMatch(short match);
70 
71 PLAYERp GlobPlayerP;
72 #if 0
73 char lavabakpic[(LAVASIZ + 2) * (LAVASIZ + 2)], lavainc[LAVASIZ];
74 int lavanumdrops, lavanumframes;
75 int lavadropx[LAVAMAXDROPS], lavadropy[LAVAMAXDROPS];
76 int lavadropsiz[LAVAMAXDROPS], lavadropsizlookup[LAVAMAXDROPS];
77 int lavaradx[32][128], lavarady[32][128], lavaradcnt[32];
78 #endif
79 
80 SECT_USERp SectUser[MAXSECTORS];
81 USERp User[MAXSPRITES];
82 
83 ANIM Anim[MAXANIM];
84 short AnimCnt = 0;
85 
86 SINE_WAVE_FLOOR SineWaveFloor[MAX_SINE_WAVE][21];
87 SINE_WALL SineWall[MAX_SINE_WALL][MAX_SINE_WALL_POINTS];
88 SPRING_BOARD SpringBoard[20];
89 int x_min_bound, y_min_bound, x_max_bound, y_max_bound;
90 
SetSectorWallBits(short sectnum,int bit_mask,BOOL set_sectwall,BOOL set_nextwall)91 void SetSectorWallBits(short sectnum, int bit_mask, BOOL set_sectwall, BOOL set_nextwall)
92     {
93     short wall_num, start_wall;
94 
95     wall_num = start_wall = sector[sectnum].wallptr;
96 
97     do
98         {
99         if (set_sectwall)
100             SET(wall[wall_num].extra, bit_mask);
101 
102         if (set_nextwall && wall[wall_num].nextwall >= 0)
103             SET(wall[wall[wall_num].nextwall].extra, bit_mask);
104 
105         wall_num = wall[wall_num].point2;
106         }
107     while(wall_num != start_wall);
108 
109     }
110 
WallSetupDontMove(VOID)111 VOID WallSetupDontMove(VOID)
112     {
113     int i,j,nexti,nextj;
114     SPRITEp spu, spl;
115     WALLp wallp;
116 
117     TRAVERSE_SPRITE_STAT(headspritestat[STAT_WALL_DONT_MOVE_UPPER],i,nexti)
118         {
119         TRAVERSE_SPRITE_STAT(headspritestat[STAT_WALL_DONT_MOVE_LOWER],j,nextj)
120             {
121             spu = &sprite[i];
122             spl = &sprite[j];
123 
124             if (spu->lotag == spl->lotag)
125                 {
126                 for (wallp = wall; wallp < &wall[numwalls]; wallp++)
127                     {
128                     if (wallp->x < spl->x && wallp->x > spu->x && wallp->y < spl->y && wallp->y > spu->y)
129                         {
130                         SET(wallp->extra, WALLFX_DONT_MOVE);
131                         }
132                     }
133                 }
134             }
135         }
136     }
137 
138 VOID
WallSetup(VOID)139 WallSetup(VOID)
140     {
141     short i = 0;
142     short NextSineWall = 0;
143     WALLp wp;
144 
145     WallSetupDontMove();
146 
147     memset(SineWall, -1, sizeof(SineWall));
148 
149     x_min_bound = 999999;
150     y_min_bound = 999999;
151     x_max_bound = -999999;
152     y_max_bound = -999999;
153 
154 
155     for (wp = wall, i = 0; wp < &wall[numwalls]; i++, wp++)
156         {
157         if (wp->picnum == FAF_PLACE_MIRROR_PIC)
158             wp->picnum = FAF_MIRROR_PIC;
159 
160         if (wall[i].picnum == FAF_PLACE_MIRROR_PIC+1)
161             wall[i].picnum = FAF_MIRROR_PIC+1;
162 
163         // get map min and max coordinates
164         x_min_bound = min(wp->x, x_min_bound);
165         y_min_bound = min(wp->y, y_min_bound);
166         x_max_bound = max(wp->x, x_max_bound);
167         y_max_bound = max(wp->y, y_max_bound);
168 
169         // this overwrites the lotag so it needs to be called LAST - its down there
170         // SetupWallForBreak(wp);
171 
172         switch (wp->lotag)
173             {
174         case TAG_WALL_LOOP_DONT_SPIN:
175                 {
176                 short wall_num, start_wall;
177 
178                 // set first wall
179                 SET(wp->extra, WALLFX_LOOP_DONT_SPIN);
180                 SET(wall[wp->nextwall].extra, WALLFX_LOOP_DONT_SPIN);
181 
182                 // move the the next wall
183                 start_wall = wp->point2;
184 
185                 // Travel all the way around loop setting wall bits
186                 for (wall_num = start_wall;
187                     wall[wall_num].lotag != TAG_WALL_LOOP_DONT_SPIN;
188                     wall_num = wall[wall_num].point2)
189                     {
190                     SET(wall[wall_num].extra, WALLFX_LOOP_DONT_SPIN);
191                     SET(wall[wall[wall_num].nextwall].extra, WALLFX_LOOP_DONT_SPIN);
192                     }
193 
194                 break;
195                 }
196 
197         case TAG_WALL_LOOP_DONT_SCALE:
198                 {
199                 short wall_num, start_wall;
200 
201                 // set first wall
202                 SET(wp->extra, WALLFX_DONT_SCALE);
203                 SET(wall[wp->nextwall].extra, WALLFX_DONT_SCALE);
204 
205                 // move the the next wall
206                 start_wall = wp->point2;
207 
208                 // Travel all the way around loop setting wall bits
209                 for (wall_num = start_wall;
210                     wall[wall_num].lotag != TAG_WALL_LOOP_DONT_SCALE;
211                     wall_num = wall[wall_num].point2)
212                     {
213                     SET(wall[wall_num].extra, WALLFX_DONT_SCALE);
214                     if (wall[wall_num].nextwall >= 0)
215                         SET(wall[wall[wall_num].nextwall].extra, WALLFX_DONT_SCALE);
216                     }
217 
218                 wp->lotag = 0;
219 
220                 break;
221                 }
222 
223         case TAG_WALL_LOOP_OUTER_SECONDARY:
224                 {
225                 short wall_num, start_wall;
226 
227                 // make sure its a red wall
228                 ASSERT(wp->nextwall >= 0);
229 
230                 // set first wall
231                 SET(wp->extra, WALLFX_LOOP_OUTER|WALLFX_LOOP_OUTER_SECONDARY);
232                 SET(wall[wp->nextwall].extra, WALLFX_LOOP_OUTER|WALLFX_LOOP_OUTER_SECONDARY);
233 
234                 // move the the next wall
235                 start_wall = wp->point2;
236 
237                 // Travel all the way around loop setting wall bits
238                 for (wall_num = start_wall;
239                     wall[wall_num].lotag != TAG_WALL_LOOP_OUTER_SECONDARY;
240                     wall_num = wall[wall_num].point2)
241                     {
242                     SET(wall[wall_num].extra, WALLFX_LOOP_OUTER|WALLFX_LOOP_OUTER_SECONDARY);
243                     SET(wall[wall[wall_num].nextwall].extra, WALLFX_LOOP_OUTER|WALLFX_LOOP_OUTER_SECONDARY);
244                     }
245 
246                 break;
247                 }
248 
249         case TAG_WALL_LOOP_OUTER:
250                 {
251                 short wall_num, start_wall;
252 
253                 // make sure its a red wall
254                 ASSERT(wp->nextwall >= 0);
255 
256                 // set first wall
257                 SET(wp->extra, WALLFX_LOOP_OUTER);
258                 SET(wall[wp->nextwall].extra, WALLFX_LOOP_OUTER);
259 
260                 // move the the next wall
261                 start_wall = wp->point2;
262 
263                 // Travel all the way around loop setting wall bits
264                 for (wall_num = start_wall;
265                     wall[wall_num].lotag != TAG_WALL_LOOP_OUTER;
266                     wall_num = wall[wall_num].point2)
267                     {
268                     SET(wall[wall_num].extra, WALLFX_LOOP_OUTER);
269                     SET(wall[wall[wall_num].nextwall].extra, WALLFX_LOOP_OUTER);
270                     }
271 
272                 wp->lotag = 0;
273 
274                 break;
275                 }
276 
277         case TAG_WALL_DONT_MOVE:
278                 {
279                 // set first wall
280                 SET(wp->extra, WALLFX_DONT_MOVE);
281                 break;
282                 }
283 
284         case TAG_WALL_LOOP_SPIN_2X:
285                 {
286                 short wall_num, start_wall;
287 
288                 // set first wall
289                 SET(wp->extra, WALLFX_LOOP_SPIN_2X);
290                 SET(wall[wp->nextwall].extra, WALLFX_LOOP_SPIN_2X);
291 
292                 // move the the next wall
293                 start_wall = wp->point2;
294 
295                 // Travel all the way around loop setting wall bits
296                 for (wall_num = start_wall;
297                     wall[wall_num].lotag != TAG_WALL_LOOP_SPIN_2X;
298                     wall_num = wall[wall_num].point2)
299                     {
300                     SET(wall[wall_num].extra, WALLFX_LOOP_SPIN_2X);
301                     SET(wall[wall[wall_num].nextwall].extra, WALLFX_LOOP_SPIN_2X);
302                     }
303 
304                 break;
305                 }
306 
307         case TAG_WALL_LOOP_SPIN_4X:
308                 {
309                 short wall_num, start_wall;
310 
311                 // set first wall
312                 SET(wp->extra, WALLFX_LOOP_SPIN_4X);
313                 SET(wall[wp->nextwall].extra, WALLFX_LOOP_SPIN_4X);
314 
315                 // move the the next wall
316                 start_wall = wp->point2;
317 
318                 // Travel all the way around loop setting wall bits
319                 for (wall_num = start_wall;
320                     wall[wall_num].lotag != TAG_WALL_LOOP_SPIN_4X;
321                     wall_num = wall[wall_num].point2)
322                     {
323                     SET(wall[wall_num].extra, WALLFX_LOOP_SPIN_4X);
324                     SET(wall[wall[wall_num].nextwall].extra, WALLFX_LOOP_SPIN_4X);
325                     }
326 
327                 break;
328                 }
329 
330         case TAG_WALL_LOOP_REVERSE_SPIN:
331                 {
332                 short wall_num, start_wall;
333 
334                 // set first wall
335                 SET(wp->extra, WALLFX_LOOP_REVERSE_SPIN);
336                 SET(wall[wp->nextwall].extra, WALLFX_LOOP_REVERSE_SPIN);
337 
338                 // move the the next wall
339                 start_wall = wp->point2;
340 
341                 // Travel all the way around loop setting wall bits
342                 for (wall_num = start_wall;
343                     wall[wall_num].lotag != TAG_WALL_LOOP_REVERSE_SPIN;
344                     wall_num = wall[wall_num].point2)
345                     {
346                     SET(wall[wall_num].extra, WALLFX_LOOP_REVERSE_SPIN);
347                     SET(wall[wall[wall_num].nextwall].extra, WALLFX_LOOP_REVERSE_SPIN);
348                     }
349 
350                 break;
351                 }
352 
353         case TAG_WALL_SINE_Y_BEGIN:
354         case TAG_WALL_SINE_X_BEGIN:
355                 {
356                 short wall_num, cnt, last_wall, num_points, type, tag_end;
357                 SINE_WALLp sw;
358                 short range = 250, speed = 3, peak = 0;
359 
360                 tag_end = wp->lotag + 2;
361 
362                 type = wp->lotag - TAG_WALL_SINE_Y_BEGIN;
363 
364 
365                 // count up num_points
366                 for (wall_num = i, num_points = 0;
367                     num_points < MAX_SINE_WALL_POINTS && wall[wall_num].lotag != tag_end;
368                     wall_num = wall[wall_num].point2, num_points++)
369                     {
370                     if (num_points == 0)
371                         {
372                         if (wall[wall_num].hitag)
373                             range = wall[wall_num].hitag;
374                         }
375                     else if (num_points == 1)
376                         {
377                         if (wall[wall_num].hitag)
378                             speed = wall[wall_num].hitag;
379                         }
380                     else if (num_points == 2)
381                         {
382                         if (wall[wall_num].hitag)
383                             peak = wall[wall_num].hitag;
384                         }
385                     }
386 
387                 if (peak)
388                     num_points = peak;
389 
390                 for (wall_num = i, cnt = 0;
391                     cnt < MAX_SINE_WALL_POINTS && wall[wall_num].lotag != tag_end;
392                     wall_num = wall[wall_num].point2, cnt++)
393                     {
394                     // set the first on up
395                     sw = &SineWall[NextSineWall][cnt];
396 
397                     sw->type = type;
398                     sw->wall = wall_num;
399                     sw->speed_shift = speed;
400                     sw->range = range;
401 
402                     // don't allow bullet holes/stars
403                     SET(wall[wall_num].extra, WALLFX_DONT_STICK);
404 
405                     if (!sw->type)
406                         sw->orig_xy = wall[wall_num].y - (sw->range >> 2);
407                     else
408                         sw->orig_xy = wall[wall_num].x - (sw->range >> 2);
409 
410                     sw->sintable_ndx = cnt * (2048 / num_points);
411                     }
412 
413                 NextSineWall++;
414 
415                 ASSERT(NextSineWall < MAX_SINE_WALL);
416 
417                 }
418             }
419 
420         // this overwrites the lotag so it needs to be called LAST
421         SetupWallForBreak(wp);
422         }
423     }
424 
425 
426 VOID
SectorLiquidSet(short i)427 SectorLiquidSet(short i)
428     {
429     SECT_USERp sectu;
430 
431     // ///////////////////////////////////
432     //
433     // CHECK for pics that mean something
434     //
435     // ///////////////////////////////////
436 
437     if (sector[i].floorpicnum >= 300 && sector[i].floorpicnum <= 307)
438         {
439         sectu = GetSectUser(i);
440 
441         SET(sector[i].extra, SECTFX_LIQUID_WATER);
442         }
443     else if (sector[i].floorpicnum >= 320 && sector[i].floorpicnum <= 343)
444         {
445         sectu = GetSectUser(i);
446 
447         SET(sector[i].extra, SECTFX_LIQUID_WATER);
448         }
449     else if (sector[i].floorpicnum >= 780 && sector[i].floorpicnum <= 794)
450         {
451         sectu = GetSectUser(i);
452 
453         SET(sector[i].extra, SECTFX_LIQUID_WATER);
454         }
455     else if (sector[i].floorpicnum >= 890 && sector[i].floorpicnum <= 897)
456         {
457         sectu = GetSectUser(i);
458 
459         SET(sector[i].extra, SECTFX_LIQUID_WATER);
460         }
461     else if (sector[i].floorpicnum >= 175 && sector[i].floorpicnum <= 182)
462         {
463         sectu = GetSectUser(i);
464 
465         SET(sector[i].extra, SECTFX_LIQUID_LAVA);
466         if (!sectu->damage)
467             sectu->damage = 40;
468         }
469     }
470 
471 VOID
SectorSetup(VOID)472 SectorSetup(VOID)
473     {
474     short i = 0, k, tag;
475     short NextSineWave = 0, rotcnt = 0, swingcnt = 0;
476 
477     short startwall, endwall, j, ndx, door_sector;
478 
479     WallSetup();
480 
481     for (ndx = 0; ndx < MAX_SECTOR_OBJECTS; ndx++)
482         {
483         memset(&SectorObject[ndx], -1, sizeof(SectorObject[0]));
484         // 0 pointers
485         //memset(&SectorObject[ndx].sectp, NULL, sizeof(SectorObject[0].sectp));
486         SectorObject[ndx].PreMoveAnimator = NULL;
487         SectorObject[ndx].PostMoveAnimator = NULL;
488         SectorObject[ndx].Animator = NULL;
489         SectorObject[ndx].controller = NULL;
490         SectorObject[ndx].sp_child = NULL;
491         SectorObject[ndx].xmid = MAXLONG;
492         }
493 
494     memset(SineWaveFloor, -1, sizeof(SineWaveFloor));
495     memset(SpringBoard, -1, sizeof(SpringBoard));
496 
497     LevelSecrets = 0;
498 
499     //for (i = 0; i < MAX_SW_PLAYERS; i++)
500     //    memset((Player + i)->HasKey, 0, sizeof((Player + i)->HasKey));
501 
502     for (i = 0; i < numsectors; i++)
503         {
504         tag = LOW_TAG(i);
505 
506 // DOH! NOT AGAIN! :(
507 //ASSERT(wall[4568].lotag != 307);
508 
509         // ///////////////////////////////////
510         //
511         // CHECK for pics that mean something
512         //
513         // ///////////////////////////////////
514 
515         // ///////////////////////////////////
516         //
517         // CHECK for flags
518         //
519         // ///////////////////////////////////
520 
521         if (TEST(sector[i].extra, SECTFX_SINK))
522             {
523             SectorLiquidSet(i);
524             }
525 
526         if (TEST(sector[i].floorstat, FLOOR_STAT_PLAX))
527             {
528             // don't do a z adjust for FAF area
529             if (sector[i].floorpicnum != FAF_PLACE_MIRROR_PIC)
530                 {
531                 SET(sector[i].extra, SECTFX_Z_ADJUST);
532                 }
533             }
534 
535         if (TEST(sector[i].ceilingstat, CEILING_STAT_PLAX))
536             {
537             // don't do a z adjust for FAF area
538             if (sector[i].ceilingpicnum != FAF_PLACE_MIRROR_PIC)
539                 {
540                 SET(sector[i].extra, SECTFX_Z_ADJUST);
541                 }
542             }
543 
544         // ///////////////////////////////////
545         //
546         // CHECK for sector/sprite objects
547         //
548         // ///////////////////////////////////
549 
550         if (tag >= TAG_OBJECT_CENTER && tag < TAG_OBJECT_CENTER + 100)
551             {
552             SetupSectorObject(i, tag);
553             }
554 
555         // ///////////////////////////////////
556         //
557         // CHECK lo and hi tags
558         //
559         // ///////////////////////////////////
560 
561         switch (tag)
562             {
563         case TAG_SECRET_AREA_TRIGGER:
564             LevelSecrets++;
565             break;
566 
567         case TAG_DOOR_SLIDING:
568             SetSectorWallBits(i, WALLFX_DONT_STICK, TRUE, TRUE);
569             break;
570 
571         case TAG_SINE_WAVE_FLOOR:
572         case TAG_SINE_WAVE_CEILING:
573         case TAG_SINE_WAVE_BOTH:
574                 {
575                 SINE_WAVE_FLOOR *swf;
576                 short near_sect = i, base_sect = i;
577                 short swf_ndx = 0;
578                 short cnt = 0, sector_cnt;
579                 int range;
580                 int range_diff = 0;
581                 int wave_diff = 0;
582                 short peak_dist = 0;
583                 short speed_shift = 3;
584                 short num;
585 
586 #define SINE_FLOOR (1<<0)
587 #define SINE_CEILING (1<<1)
588 
589                 num = (tag - TAG_SINE_WAVE_FLOOR) / 20;
590 
591                 // set the first on up
592                 swf = &SineWaveFloor[NextSineWave][swf_ndx];
593 
594                 swf->flags = 0;
595 
596                 switch (num)
597                     {
598                 case 0:
599                     SET(swf->flags, SINE_FLOOR);
600                     #define SINE_SLOPED BIT(3)
601                     if (TEST(sector[base_sect].floorstat, FLOOR_STAT_SLOPE))
602                         {
603                         SET(swf->flags, SINE_SLOPED);
604                         }
605                     break;
606                 case 1:
607                     SET(swf->flags, SINE_CEILING);
608                     break;
609                 case 2:
610                     SET(swf->flags, SINE_FLOOR | SINE_CEILING);
611                     break;
612                     }
613 
614 
615                 swf->sector = near_sect;
616                 ASSERT(sector[swf->sector].hitag != 0);
617                 swf->range = range = Z(sector[swf->sector].hitag);
618                 swf->floor_origz = sector[swf->sector].floorz - (range >> 2);
619                 swf->ceiling_origz = sector[swf->sector].ceilingz - (range >> 2);
620 
621                 // look for the rest by distance
622                 for (swf_ndx = 1, sector_cnt = 1; TRUE; swf_ndx++)
623                     {
624                     // near_sect = FindNextSectorByTag(base_sect,
625                     // TAG_SINE_WAVE_FLOOR + swf_ndx);
626                     near_sect = FindNextSectorByTag(base_sect, tag + swf_ndx);
627 
628                     if (near_sect >= 0)
629                         {
630                         swf = &SineWaveFloor[NextSineWave][swf_ndx];
631 
632                         if (swf_ndx == 1 && sector[near_sect].hitag)
633                             range_diff = sector[near_sect].hitag;
634                         else if (swf_ndx == 2 && sector[near_sect].hitag)
635                             speed_shift = sector[near_sect].hitag;
636                         else if (swf_ndx == 3 && sector[near_sect].hitag)
637                             peak_dist = sector[near_sect].hitag;
638 
639                         swf->sector = near_sect;
640                         swf->floor_origz = sector[swf->sector].floorz - (range >> 2);
641                         swf->ceiling_origz = sector[swf->sector].ceilingz - (range >> 2);
642                         range -= range_diff;
643                         swf->range = range;
644 
645                         base_sect = swf->sector;
646                         sector_cnt++;
647                         }
648                     else
649                         break;
650                     }
651 
652 
653                 ASSERT(swf_ndx <= SIZ(SineWaveFloor[NextSineWave]));
654 
655                 // more than 6 waves and something in high tag - set up wave
656                 // dissapate
657                 if (sector_cnt > 8 && sector[base_sect].hitag)
658                     {
659                     wave_diff = sector[base_sect].hitag;
660                     }
661 
662                 // setup the sintable_ndx based on the actual number of
663                 // sectors (swf_ndx)
664                 for (swf = &SineWaveFloor[NextSineWave][0], cnt = 0; swf->sector >= 0 && swf < (SINE_WAVE_FLOORp)&SineWaveFloor[SIZ(SineWaveFloor)]; swf++, cnt++)
665                     {
666                     if (peak_dist)
667                         swf->sintable_ndx = cnt * (2048 / peak_dist);
668                     else
669                         swf->sintable_ndx = cnt * (2048 / swf_ndx);
670 
671                     swf->speed_shift = speed_shift;
672                     }
673 
674                 // set up the a real wave that dissapates at the end
675                 if (wave_diff)
676                     {
677                     for (cnt = sector_cnt - 1; cnt >= 0; cnt--)
678                         {
679                         // only do the last (actually the first) few for the
680                         // dissapate
681                         if (cnt > 8)
682                             continue;
683 
684                         swf = &SineWaveFloor[NextSineWave][cnt];
685 
686                         swf->range -= wave_diff;
687 
688                         wave_diff += wave_diff;
689 
690                         if (swf->range < Z(4))
691                             swf->range = Z(4);
692 
693                         // reset origz's based on new range
694                         swf->floor_origz = sector[swf->sector].floorz - (swf->range >> 2);
695                         swf->ceiling_origz = sector[swf->sector].ceilingz - (swf->range >> 2);
696                         }
697                     }
698 
699                 NextSineWave++;
700 
701                 ASSERT(NextSineWave < MAX_SINE_WAVE);
702 
703                 break;
704                 }
705             }
706         }
707     }
708 
709 VOID
SectorMidPoint(short sectnum,int * xmid,int * ymid,int * zmid)710 SectorMidPoint(short sectnum, int *xmid, int *ymid, int *zmid)
711     {
712     short startwall, endwall, j;
713     int xsum = 0, ysum = 0;
714     WALLp wp;
715 
716     startwall = sector[sectnum].wallptr;
717     endwall = startwall + sector[sectnum].wallnum - 1;
718 
719     for (wp = &wall[startwall], j = startwall; j <= endwall; wp++, j++)
720         {
721         xsum += wp->x;
722         ysum += wp->y;
723         }
724 
725     *xmid = xsum / (endwall - startwall + 1);
726     *ymid = ysum / (endwall - startwall + 1);
727 
728     *zmid = DIV2(sector[sectnum].floorz + sector[sectnum].ceilingz);
729     }
730 
731 
732 VOID
DoSpringBoard(PLAYERp pp,short UNUSED (sectnum))733 DoSpringBoard(PLAYERp pp, short UNUSED(sectnum))
734     {
735     int sb;
736     int i;
737     VOID DoPlayerBeginForceJump(PLAYERp);
738 
739     #if 0
740     i = AnimGetGoal(&sector[sectnum].floorz);
741 
742     // if in motion return
743     if (i >= 0)
744         return;
745 
746     AnimSet(&sector[sectnum].floorz, sector[sectnum].floorz - Z(32), 512);
747 
748     for (sb = 0; sb < SIZ(SpringBoard); sb++)
749         {
750         // if empty set up an entry to close the sb later
751         if (SpringBoard[sb].Sector == -1)
752             {
753             pp->jump_speed = -sector[pp->cursectnum].hitag;
754             DoPlayerBeginForceJump(pp);
755 
756             SpringBoard[sb].Sector = sectnum;
757             SpringBoard[sb].TimeOut = 1 * 120;
758 
759             sector[sectnum].lotag = 0;
760             }
761         }
762     #else
763     pp->jump_speed = -sector[pp->cursectnum].hitag;
764     DoPlayerBeginForceJump(pp);
765     #endif
766 
767     return;
768     }
769 
770 
771 VOID
DoSpringBoardDown(VOID)772 DoSpringBoardDown(VOID)
773     {
774     unsigned sb;
775     SPRING_BOARD *sbp;
776 
777     for (sb = 0; sb < SIZ(SpringBoard); sb++)
778         {
779         sbp = &SpringBoard[sb];
780 
781         // if empty set up an entry to close the sb later
782         if (sbp->Sector != -1)
783             {
784             if ((sbp->TimeOut -= synctics) <= 0)
785                 {
786                 int destz;
787 
788                 destz = sector[nextsectorneighborz(sbp->Sector, sector[sbp->Sector].floorz, SEARCH_FLOOR, SEARCH_DOWN)].floorz;
789 
790                 AnimSet(&sector[sbp->Sector].floorz, destz, 256);
791 
792                 sector[sbp->Sector].lotag = TAG_SPRING_BOARD;
793 
794                 sbp->Sector = -1;
795                 }
796             }
797         }
798 
799 
800     return;
801     }
802 
803 short
FindSectorByTag(int x,int y,int tag)804 FindSectorByTag(int x, int y, int tag)
805     {
806     short i = 0, near_sector = -1;
807     int diff, near_diff = 9999999;
808     short wallnum;
809 
810     for (i = 0; i < numsectors; i++)
811         {
812         if (LOW_TAG(i) == tag)
813             {
814             // get the delta of the door/elevator
815             wallnum = sector[i].wallptr;
816 
817             // diff = labs(wall[wallnum].x - x) + labs(wall[wallnum].y - y);
818             diff = Distance(wall[wallnum].x, wall[wallnum].y, x, y);
819 
820             // if the door/elevator is closer than the last save it off
821             if (diff < near_diff)
822                 {
823                 near_diff = diff;
824                 near_sector = i;
825                 }
826             }
827         }
828 
829     return (near_sector);
830 
831     }
832 
833 short
FindSectorByTag_Wall(short wallnum,int tag)834 FindSectorByTag_Wall(short wallnum, int tag)
835     {
836     return (FindSectorByTag(wall[wallnum].x, wall[wallnum].y, tag));
837     }
838 
839 short
FindSectorByTag_Sprite(short SpriteNum,int tag)840 FindSectorByTag_Sprite(short SpriteNum, int tag)
841     {
842     return (FindSectorByTag(sprite[SpriteNum].x, sprite[SpriteNum].y, tag));
843     }
844 
845 #if 1
846 short
FindSectorMidByTag(short sectnum,int tag)847 FindSectorMidByTag(short sectnum, int tag)
848     {
849     short i = 0, near_sector = -1;
850     int diff, near_diff = 9999999, x, y;
851     int trash, fx, fy;
852 
853     // Get the mid x,y of the sector
854     SectorMidPoint(sectnum, &x, &y, &trash);
855 
856     for (i = 0; i < numsectors; i++)
857         {
858         if (sector[i].lotag == tag)
859             {
860             // get the delta of the door/elevator
861             SectorMidPoint(i, &fx, &fy, &trash);
862 
863             // diff = labs(wall[wallnum].x - x) + labs(wall[wallnum].y - y);
864             diff = Distance(fx, fy, x, y);
865 
866             // if the door/elevator is closer than the last save it off
867             if (diff < near_diff)
868                 {
869                 near_diff = diff;
870                 near_sector = i;
871                 }
872             }
873         }
874 
875     return (near_sector);
876 
877     }
878 
879 #endif
880 
881 short
FindNextSectorByTag(short sectnum,int tag)882 FindNextSectorByTag(short sectnum, int tag)
883     {
884     short next_sectnum, startwall, endwall, j;
885 
886     startwall = sector[sectnum].wallptr;
887     endwall = startwall + sector[sectnum].wallnum - 1;
888 
889     for (j = startwall; j <= endwall; j++)
890         {
891         next_sectnum = wall[j].nextsector;
892 
893         if (next_sectnum >= 0)
894             {
895             if (sector[next_sectnum].lotag == tag)
896                 {
897                 return (next_sectnum);
898                 }
899             }
900         }
901 
902     return (-1);
903 
904     }
905 
906 
907 int
SectorDistance(short sect1,int sect2)908 SectorDistance(short sect1, int sect2)
909     {
910     short wallnum1, wallnum2;
911 
912     if (sect1 < 0 || sect2 < 0)
913         return (9999999);
914 
915     wallnum1 = sector[sect1].wallptr;
916     wallnum2 = sector[sect2].wallptr;
917 
918     // return the distance between the two sectors.
919     return (Distance(wall[wallnum1].x, wall[wallnum1].y, wall[wallnum2].x, wall[wallnum2].y));
920     }
921 
922 
923 int
SectorDistanceByMid(short sect1,int sect2)924 SectorDistanceByMid(short sect1, int sect2)
925     {
926     int sx1, sy1, sx2, sy2, trash;
927 
928     SectorMidPoint(sect1, &sx1, &sy1, &trash);
929     SectorMidPoint(sect2, &sx2, &sy2, &trash);
930 
931     // return the distance between the two sectors.
932     return (Distance(sx1, sy1, sx2, sy2));
933     }
934 
935 short
DoSpawnActorTrigger(short match)936 DoSpawnActorTrigger(short match)
937     {
938     int i, nexti, pnum;
939     short spawn_count = 0, hidden;
940     SPRITEp sp;
941 
942     TRAVERSE_SPRITE_STAT(headspritestat[STAT_SPAWN_TRIGGER], i, nexti)
943         {
944         sp = &sprite[i];
945 
946         if (sp->hitag == match)
947             {
948             if (ActorSpawn(sp))
949                 {
950                 DoSpawnTeleporterEffectPlace(sp);
951                 PlaySound(DIGI_PLAYER_TELEPORT, &sp->x, &sp->y, &sp->z, v3df_none);
952                 spawn_count++;
953                 }
954             }
955         }
956 
957     return(spawn_count);
958     }
959 
960 int
OperateSector(short sectnum,short player_is_operating)961 OperateSector(short sectnum, short player_is_operating)
962     {
963     PLAYERp pp = GlobPlayerP;
964 
965     // Don't let actors operate locked or secret doors
966     if (!player_is_operating)
967         {
968         SPRITEp fsp;
969         short match;
970         short i,nexti;
971 
972 
973         if (SectUser[sectnum] && SectUser[sectnum]->stag == SECT_LOCK_DOOR)
974             return(FALSE);
975 
976         TRAVERSE_SPRITE_SECT(headspritesect[sectnum], i, nexti)
977             {
978             fsp = &sprite[i];
979 
980             if (SectUser[fsp->sectnum] && SectUser[fsp->sectnum]->stag == SECT_LOCK_DOOR)
981                 return(FALSE);
982 
983             if (fsp->statnum == STAT_VATOR && SP_TAG1(fsp) == SECT_VATOR && TEST_BOOL7(fsp))
984                 return(FALSE);
985             if (fsp->statnum == STAT_ROTATOR && SP_TAG1(fsp) == SECT_ROTATOR && TEST_BOOL7(fsp))
986                 return(FALSE);
987             if (fsp->statnum == STAT_SLIDOR && SP_TAG1(fsp) == SECT_SLIDOR && TEST_BOOL7(fsp))
988                 return(FALSE);
989 
990             }
991         }
992 
993     //CON_Message("Operating sectnum %d",sectnum);
994     //CON_Message("stag = %d, lock = %d",SectUser[sectnum]->stag,SECT_LOCK_DOOR);
995 
996     switch (LOW_TAG(sectnum))
997         {
998 
999     case TAG_VATOR:
1000         DoVatorOperate(pp, sectnum);
1001         return (TRUE);
1002 
1003     case TAG_ROTATOR:
1004         DoRotatorOperate(pp, sectnum);
1005         return (TRUE);
1006 
1007     case TAG_SLIDOR:
1008         DoSlidorOperate(pp, sectnum);
1009         return (TRUE);
1010         }
1011 
1012     return (FALSE);
1013     }
1014 
1015 int
OperateWall(short wallnum,short UNUSED (player_is_operating))1016 OperateWall(short wallnum, short UNUSED(player_is_operating))
1017     {
1018     WALLp wallp = &wall[wallnum];
1019 
1020     switch (LOW_TAG_WALL(wallnum))
1021         {
1022         }
1023 
1024     return (FALSE);
1025     }
1026 
1027 short
AnimateSwitch(SPRITEp sp,short tgt_value)1028 AnimateSwitch(SPRITEp sp, short tgt_value)
1029     {
1030 #define SWITCH_LEVER        581
1031 #define SWITCH_FUSE         558
1032 #define SWITCH_FLIP         561
1033 #define SWITCH_RED_CHAIN    563
1034 #define SWITCH_GREEN_CHAIN  565
1035 #define SWITCH_TOUCH        567
1036 #define SWITCH_DRAGON       569
1037 
1038 #define SWITCH_LIGHT        551
1039 #define SWITCH_1            575
1040 #define SWITCH_3            579
1041 
1042 #define SWITCH_SHOOTABLE_1  577
1043 #define SWITCH_4            571
1044 #define SWITCH_5            573
1045 #define SWITCH_6            583
1046 #define EXIT_SWITCH         2470
1047 
1048 #define SWITCH_SKULL        553
1049 
1050     // if the value is not ON or OFF
1051     // then it is a straight toggle
1052 
1053     switch (sp->picnum)
1054         {
1055         // set to TRUE/ON
1056         case SWITCH_SKULL:
1057         case SWITCH_LEVER:
1058         case SWITCH_LIGHT:
1059         case SWITCH_SHOOTABLE_1:
1060         case SWITCH_1:
1061         case SWITCH_3:
1062         case SWITCH_FLIP:
1063         case SWITCH_RED_CHAIN:
1064         case SWITCH_GREEN_CHAIN:
1065         case SWITCH_TOUCH:
1066         case SWITCH_DRAGON:
1067         case SWITCH_4:
1068         case SWITCH_5:
1069         case SWITCH_6:
1070         case EXIT_SWITCH:
1071 
1072             // dont toggle - return the current state
1073             if (tgt_value == 999)
1074                 return(OFF);
1075 
1076             sp->picnum += 1;
1077 
1078             // if the tgt_value should be true
1079             // flip it again - recursive but only once
1080             if (tgt_value == OFF)
1081                 {
1082                 AnimateSwitch(sp, tgt_value);
1083                 return(OFF);
1084                 }
1085 
1086             return(ON);
1087 
1088         // set to true
1089         case SWITCH_SKULL + 1:
1090         case SWITCH_LEVER + 1:
1091         case SWITCH_LIGHT + 1:
1092         case SWITCH_1 + 1:
1093         case SWITCH_3 + 1:
1094         case SWITCH_FLIP + 1:
1095         case SWITCH_RED_CHAIN + 1:
1096         case SWITCH_GREEN_CHAIN + 1:
1097         case SWITCH_TOUCH + 1:
1098         case SWITCH_DRAGON + 1:
1099         case SWITCH_SHOOTABLE_1 + 1:
1100         case SWITCH_4+1:
1101         case SWITCH_5+1:
1102         case SWITCH_6+1:
1103         case EXIT_SWITCH+1:
1104 
1105             // dont toggle - return the current state
1106             if (tgt_value == 999)
1107                 return(ON);
1108 
1109             sp->picnum -= 1;
1110 
1111             if (tgt_value == ON)
1112                 {
1113                 AnimateSwitch(sp, tgt_value);
1114                 return(ON);
1115                 }
1116 
1117             return(OFF);
1118         }
1119     return(OFF);
1120     }
1121 
1122 
1123 VOID
SectorExp(short SpriteNum,short sectnum,short orig_ang,int zh)1124 SectorExp(short SpriteNum, short sectnum, short orig_ang, int zh)
1125         {
1126         SPRITEp sp = &sprite[SpriteNum];
1127         USERp u = User[SpriteNum];
1128         SECT_USERp sectu = SectUser[sectnum];
1129         SECTORp sectp = &sector[sectnum];
1130         short explosion;
1131         SPRITEp exp;
1132         USERp eu;
1133         int x,y,z;
1134 
1135         RESET(sp->cstat, CSTAT_SPRITE_WALL|CSTAT_SPRITE_FLOOR);
1136         SectorMidPoint(sectnum, &x, &y, &z);
1137         sp->ang = orig_ang;
1138         sp->x = x;
1139         sp->y = y;
1140         sp->z = z;
1141 
1142         // randomize the explosions
1143         sp->ang += RANDOM_P2(256) - 128;
1144         sp->x += RANDOM_P2(1024) - 512;
1145         sp->y += RANDOM_P2(1024) - 512;
1146         sp->z = zh;
1147 
1148         // setup vars needed by SectorExp
1149         changespritesect(SpriteNum, sectnum);
1150         //setspritez(SpriteNum, sp->x, sp->y, sp->z);
1151         getzsofslope(sp->sectnum, sp->x, sp->y, &u->hiz, &u->loz);
1152 
1153         // spawn explosion
1154         explosion = SpawnSectorExp(SpriteNum);
1155         ASSERT(explosion >= 0);
1156         exp = &sprite[explosion];
1157         eu = User[explosion];
1158 
1159         exp->xrepeat += (RANDOM_P2(32<<8)>>8) - 16;
1160         exp->yrepeat += (RANDOM_P2(32<<8)>>8) - 16;
1161         eu->xchange = MOVEx(92, exp->ang);
1162         eu->ychange = MOVEy(92, exp->ang);
1163         }
1164 
1165 
1166 VOID
DoExplodeSector(short match)1167 DoExplodeSector(short match)
1168     {
1169     short orig_ang;
1170     int zh;
1171     USERp u;
1172     short cf,nextcf;
1173     short ed,nexted;
1174 
1175     int ss, nextss;
1176     SECTORp dsectp, ssectp;
1177     SPRITEp src_sp, dest_sp;
1178 
1179     SPRITEp esp;
1180     SECTORp sectp;
1181 
1182     orig_ang = 0;//sp->ang;
1183 
1184     TRAVERSE_SPRITE_STAT(headspritestat[STAT_EXPLODING_CEIL_FLOOR], cf, nextcf)
1185         {
1186         esp = &sprite[cf];
1187 
1188         if (match != esp->lotag)
1189             continue;
1190 
1191         if (!User[cf])
1192             u = SpawnUser(cf, 0, NULL);
1193 
1194         sectp = &sector[esp->sectnum];
1195 
1196         sectp->ceilingz -= Z(SP_TAG4(esp));
1197 
1198         if (SP_TAG5(esp))
1199             {
1200             sectp->floorheinum = SP_TAG5(esp);
1201             SET(sectp->floorstat, FLOOR_STAT_SLOPE);
1202             }
1203 
1204         if (SP_TAG6(esp))
1205             {
1206             sectp->ceilingheinum = SP_TAG6(esp);
1207             SET(sectp->ceilingstat, CEILING_STAT_SLOPE);
1208             }
1209 
1210         for (zh = sectp->ceilingz; zh < sectp->floorz; zh += Z(60))
1211             {
1212             SectorExp(cf, esp->sectnum, orig_ang, zh + Z(RANDOM_P2(64)) - Z(32));
1213             }
1214 
1215         // don't need it any more
1216         KillSprite(cf);
1217         }
1218     }
1219 
1220 
DoSpawnSpot(short SpriteNum)1221 int DoSpawnSpot(short SpriteNum)
1222     {
1223     USERp u = User[SpriteNum];
1224     SPRITEp sp = u->SpriteP;
1225 
1226     if ((u->WaitTics -= synctics) < 0)
1227         {
1228         change_sprite_stat(SpriteNum, STAT_SPAWN_SPOT);
1229         SpawnShrap(SpriteNum, -1);
1230 
1231         if (u->LastDamage == 1)
1232             {
1233             KillSprite(SpriteNum);
1234             return(0);
1235             }
1236         }
1237 
1238     return(0);
1239     }
1240 
1241 // spawns shrap when killing an object
1242 VOID
DoSpawnSpotsForKill(short match)1243 DoSpawnSpotsForKill(short match)
1244     {
1245     short sn, next_sn;
1246     SPRITEp sp;
1247     USERp u;
1248 
1249     if (match < 0)
1250         return;
1251 
1252     TRAVERSE_SPRITE_STAT(headspritestat[STAT_SPAWN_SPOT], sn, next_sn)
1253         {
1254         sp = &sprite[sn];
1255 
1256         // change the stat num and set the delay correctly to call SpawnShrap
1257         if (sp->hitag == SPAWN_SPOT && sp->lotag == match)
1258             {
1259             u = User[sn];
1260             change_sprite_stat(sn, STAT_NO_STATE);
1261             u->ActorActionFunc = DoSpawnSpot;
1262             u->WaitTics = SP_TAG5(sp) * 15;
1263             setspritez(sn, sp->x, sp->y, sp->z);
1264             // setting for Killed
1265             u->LastDamage = 1;
1266             }
1267         }
1268     }
1269 
1270 // spawns shrap when damaging an object
1271 VOID
DoSpawnSpotsForDamage(short match)1272 DoSpawnSpotsForDamage(short match)
1273     {
1274     short sn, next_sn;
1275     SPRITEp sp;
1276     USERp u;
1277 
1278     if (match < 0)
1279         return;
1280 
1281     TRAVERSE_SPRITE_STAT(headspritestat[STAT_SPAWN_SPOT], sn, next_sn)
1282         {
1283         sp = &sprite[sn];
1284 
1285         // change the stat num and set the delay correctly to call SpawnShrap
1286 
1287         if (sp->hitag == SPAWN_SPOT && sp->lotag == match)
1288             {
1289             u = User[sn];
1290             change_sprite_stat(sn, STAT_NO_STATE);
1291             u->ActorActionFunc = DoSpawnSpot;
1292             u->WaitTics = SP_TAG7(sp) * 15;
1293             // setting for Damaged
1294             u->LastDamage = 0;
1295             }
1296         }
1297     }
1298 
1299 VOID
DoSoundSpotMatch(short match,short sound_num,short UNUSED (sound_type))1300 DoSoundSpotMatch(short match, short sound_num, short UNUSED(sound_type))
1301     {
1302     short sn, next_sn;
1303     SPRITEp sp;
1304     int flags;
1305     short snd2play;
1306 
1307     //sound_type is not used
1308 
1309     sound_num--;
1310 
1311     ASSERT(sound_num >= 0);
1312 
1313     TRAVERSE_SPRITE_STAT(headspritestat[STAT_SOUND_SPOT], sn, next_sn)
1314         {
1315         sp = &sprite[sn];
1316 
1317         if (SP_TAG2(sp) == match && !TEST_BOOL6(sp))
1318             {
1319             short snd[3];
1320 
1321             snd[0] = SP_TAG13(sp); // tag4 is copied to tag13
1322             snd[1] = SP_TAG5(sp);
1323             snd[2] = SP_TAG6(sp);
1324 
1325             snd2play = 0;
1326             flags = 0;
1327 
1328             if (TEST_BOOL2(sp))
1329                 flags = v3df_follow|v3df_nolookup|v3df_init;
1330                 //flags = v3df_follow|v3df_ambient|v3df_nolookup;
1331 
1332             // play once and only once
1333             if (TEST_BOOL1(sp))
1334                 SET_BOOL6(sp);
1335 
1336             // don't pan
1337             if (TEST_BOOL4(sp))
1338                 flags |= v3df_dontpan;
1339             // add doppler
1340             if (TEST_BOOL5(sp))
1341                 flags |= v3df_doppler;
1342             // random
1343             if (TEST_BOOL3(sp))
1344                 {
1345                 if (snd[0] && snd[1])
1346                     {
1347                     snd2play = snd[RANDOM_RANGE(2)];
1348                     }
1349                 else
1350                 if (snd[0] && snd[1] && snd[2])
1351                     {
1352                     snd2play = snd[RANDOM_RANGE(3)];
1353                     }
1354                 }
1355             else
1356             if (snd[sound_num])
1357                 {
1358                 snd2play = snd[sound_num];
1359                 }
1360 
1361             if (snd2play <= 0)
1362                 continue;
1363 
1364             if (TEST_BOOL7(sp))
1365                 {
1366                 PLAYERp pp = GlobPlayerP;
1367 
1368                 ////DSPRINTF(ds,"PlayerSound %d",pp-Player);
1369                 //MONO_PRINT(ds);
1370                 //ASSERT(pp >= Player && pp <= Player+CommPlayers);
1371 
1372                 if (pp)
1373                     {
1374                     if(pp == Player+myconnectindex)
1375                         PlayerSound(snd2play,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1376                     }
1377                 }
1378             else
1379                 {
1380                 PlaySound(snd2play, &sp->x, &sp->y, &sp->z, flags);
1381 
1382                 //if (TEST(flags, v3df_follow)) // Just set it anyway
1383                     Set3DSoundOwner(sn);
1384                 }
1385             }
1386         }
1387     }
1388 
1389 VOID
DoSoundSpotStopSound(short match)1390 DoSoundSpotStopSound(short match)
1391     {
1392     short sn, next_sn;
1393     SPRITEp sp;
1394 
1395     TRAVERSE_SPRITE_STAT(headspritestat[STAT_SOUND_SPOT], sn, next_sn)
1396         {
1397         sp = &sprite[sn];
1398 
1399         // found match and is a follow type
1400         if (SP_TAG2(sp) == match && TEST_BOOL2(sp))
1401             {
1402             DeleteNoSoundOwner(sn);
1403             }
1404         }
1405     }
1406 
1407 VOID
DoStopSoundSpotMatch(short match)1408 DoStopSoundSpotMatch(short match)
1409     {
1410     short sn, next_sn;
1411     SPRITEp sp;
1412 
1413     TRAVERSE_SPRITE_STAT(headspritestat[STAT_STOP_SOUND_SPOT], sn, next_sn)
1414         {
1415         sp = &sprite[sn];
1416 
1417         if (SP_TAG2(sp) == match)
1418             {
1419             DoSoundSpotStopSound(SP_TAG5(sp));
1420             }
1421         }
1422     }
1423 
1424 
TestKillSectorObject(SECTOR_OBJECTp sop)1425 BOOL TestKillSectorObject(SECTOR_OBJECTp sop)
1426     {
1427     if (TEST(sop->flags, SOBJ_KILLABLE))
1428         {
1429         KillMatchingCrackSprites(sop->match_event);
1430         // get new sectnums
1431         CollapseSectorObject(sop, sop->xmid, sop->ymid);
1432         DoSpawnSpotsForKill(sop->match_event);
1433         KillSectorObjectSprites(sop);
1434         return(TRUE);
1435         }
1436 
1437     return(FALSE);
1438     }
1439 
1440 short
DoSectorObjectKillMatch(short match)1441 DoSectorObjectKillMatch(short match)
1442     {
1443     SECTOR_OBJECTp sop;
1444 
1445     for (sop = SectorObject; sop < &SectorObject[MAX_SECTOR_OBJECTS]; sop++)
1446         {
1447         if (sop->xmid == MAXLONG)
1448             continue;
1449 
1450         if (sop->match_event == match)
1451             return(TestKillSectorObject(sop));
1452         }
1453 
1454     return(FALSE);
1455     }
1456 
1457 
1458 BOOL
SearchExplodeSectorMatch(short match)1459 SearchExplodeSectorMatch(short match)
1460     {
1461     short i,nexti;
1462 
1463     // THIS IS ONLY CALLED FROM DoMatchEverything
1464     TRAVERSE_SPRITE_STAT(headspritestat[STAT_SPRITE_HIT_MATCH], i, nexti)
1465         {
1466         SPRITEp sp = &sprite[i];
1467 
1468         if (sp->hitag == match)
1469             {
1470             KillMatchingCrackSprites(match);
1471             DoExplodeSector(match);
1472             return(TRUE);
1473             }
1474         }
1475 
1476     return(FALSE);
1477     }
1478 
1479 VOID
KillMatchingCrackSprites(short match)1480 KillMatchingCrackSprites(short match)
1481     {
1482     short i,nexti;
1483     SPRITEp sp;
1484 
1485     TRAVERSE_SPRITE_STAT(headspritestat[STAT_SPRITE_HIT_MATCH], i, nexti)
1486         {
1487         sp = &sprite[i];
1488 
1489         if (sp->hitag == match)
1490             {
1491             if (TEST(SP_TAG8(sp), BIT(2)))
1492                 continue;
1493 
1494             KillSprite(i);
1495             }
1496         }
1497     }
1498 
1499 VOID
WeaponExplodeSectorInRange(short weapon)1500 WeaponExplodeSectorInRange(short weapon)
1501     {
1502     short i, nexti;
1503     SPRITEp wp = &sprite[weapon];
1504     USERp wu = User[weapon];
1505     SPRITEp sp;
1506     int dist;
1507     int radius;
1508     short match;
1509 
1510     TRAVERSE_SPRITE_STAT(headspritestat[STAT_SPRITE_HIT_MATCH], i, nexti)
1511         {
1512         sp = &sprite[i];
1513 
1514         // test to see if explosion is close to crack sprite
1515         dist = FindDistance3D(wp->x - sp->x, wp->y - sp->y, (wp->z - sp->z)>>4);
1516 
1517         if (sp->clipdist == 0)
1518             continue;
1519 
1520         radius = (((int)sp->clipdist) << 2) * 8;
1521 
1522         if ((unsigned int)dist > (wu->Radius/2) + radius)
1523             continue;
1524 
1525         if (!FAFcansee(wp->x,wp->y,wp->z,wp->sectnum,sp->x,sp->y,sp->z,sp->sectnum))
1526             continue;
1527 
1528         match = sp->hitag;
1529         // this and every other crack sprite of this type is now dead
1530         // don't use them
1531         #if 0
1532         KillMatchingCrackSprites(match);
1533         DoExplodeSector(match);
1534         DoMatchEverything(NULL, match, -1);
1535         #else
1536         // pass in explosion type
1537         MissileHitMatch(weapon, WPN_ROCKET, i);
1538         #endif
1539 
1540         return;
1541         }
1542     }
1543 
1544 
1545 VOID
ShootableSwitch(short SpriteNum,short UNUSED (Weapon))1546 ShootableSwitch(short SpriteNum, short UNUSED(Weapon))
1547     {
1548     SPRITEp sp = &sprite[SpriteNum];
1549 
1550     switch (sp->picnum)
1551         {
1552     case SWITCH_SHOOTABLE_1:
1553         //RESET(sp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
1554         OperateSprite(SpriteNum, FALSE);
1555         sp->picnum = SWITCH_SHOOTABLE_1 + 1;
1556         break;
1557     case SWITCH_FUSE:
1558     case SWITCH_FUSE + 1:
1559         RESET(sp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
1560         OperateSprite(SpriteNum, FALSE);
1561         sp->picnum = SWITCH_FUSE + 2;
1562         break;
1563         }
1564     }
1565 
DoDeleteSpriteMatch(short match)1566 VOID DoDeleteSpriteMatch(short match)
1567     {
1568     static short StatList[] =
1569         {
1570         STAT_DEFAULT,
1571         STAT_VATOR,
1572         STAT_SPIKE,
1573         STAT_TRAP,
1574         STAT_ITEM,
1575         STAT_LIGHTING,
1576         STAT_STATIC_FIRE,
1577         STAT_AMBIENT,
1578         STAT_FAF
1579         };
1580 
1581     int del_x = 0,del_y = 0;
1582     short i,nexti;
1583     unsigned stat;
1584     short found;
1585 
1586     while(TRUE)
1587         {
1588         found = -1;
1589 
1590         // search for a DELETE_SPRITE with same match tag
1591         TRAVERSE_SPRITE_STAT(headspritestat[STAT_DELETE_SPRITE], i, nexti)
1592             {
1593             if (sprite[i].lotag == match)
1594                 {
1595                 found = i;
1596                 del_x = sprite[i].x;
1597                 del_y = sprite[i].y;
1598                 break;
1599                 }
1600             }
1601 
1602         if (found == -1)
1603             return;
1604 
1605         for (stat = 0; stat < SIZ(StatList); stat++)
1606             {
1607             TRAVERSE_SPRITE_STAT(headspritestat[StatList[stat]], i, nexti)
1608                 {
1609                 if (del_x == sprite[i].x && del_y == sprite[i].y)
1610                     {
1611                     // special case lighting delete of Fade On/off after fades
1612                     if (StatList[stat] == STAT_LIGHTING)
1613                         {
1614                         // set shade to darkest and then kill it
1615                         sprite[i].shade = SPRITE_TAG6(i);
1616                         sprite[i].pal = 0;
1617                         SectorLightShade(&sprite[i], sprite[i].shade);
1618                         DiffuseLighting(&sprite[i]);
1619                         }
1620 
1621                     ////DSPRINTF(ds,"Delete Sprite stat %d, x %d, y %d",sprite[i].statnum, sprite[i].x, sprite[i].y);
1622                     //MONO_PRINT(ds);
1623                     SpriteQueueDelete(i);
1624                     KillSprite(i);
1625                     }
1626                 }
1627             }
1628 
1629         // kill the DELETE_SPRITE
1630         KillSprite(found);
1631         }
1632     }
1633 
1634 VOID
DoChangorMatch(short match)1635 DoChangorMatch(short match)
1636     {
1637     short sn, next_sn;
1638     SPRITEp sp;
1639     SECTORp sectp;
1640 
1641     TRAVERSE_SPRITE_STAT(headspritestat[STAT_CHANGOR], sn, next_sn)
1642         {
1643         sp = &sprite[sn];
1644         sectp = &sector[sp->sectnum];
1645 
1646         if (SP_TAG2(sp) != match)
1647             continue;
1648 
1649         if (TEST_BOOL1(sp))
1650             {
1651             sectp->ceilingpicnum = SP_TAG4(sp);
1652             sectp->ceilingz += Z(SP_TAG5(sp));
1653             sectp->ceilingheinum += SP_TAG6(sp);
1654 
1655             if (sectp->ceilingheinum)
1656                 SET(sectp->ceilingstat, CEILING_STAT_SLOPE);
1657             else
1658                 RESET(sectp->ceilingstat, CEILING_STAT_SLOPE);
1659 
1660             sectp->ceilingshade += SP_TAG7(sp);
1661             sectp->ceilingpal += SP_TAG8(sp);
1662             }
1663         else
1664             {
1665             sectp->floorpicnum = SP_TAG4(sp);
1666             sectp->floorz += Z(SP_TAG5(sp));
1667             sectp->floorheinum += SP_TAG6(sp);
1668 
1669             if (sectp->floorheinum)
1670                 SET(sectp->floorstat, FLOOR_STAT_SLOPE);
1671             else
1672                 RESET(sectp->floorstat, FLOOR_STAT_SLOPE);
1673 
1674             sectp->floorshade += SP_TAG7(sp);
1675             sectp->floorpal += SP_TAG8(sp);
1676             }
1677 
1678         sectp->visibility += SP_TAG9(sp);
1679 
1680         // if not set then go ahead and kill it
1681         if (TEST_BOOL2(sp) == 0)
1682             {
1683             KillSprite(sn);
1684             }
1685         }
1686     }
1687 
DoMatchEverything(PLAYERp pp,short match,short state)1688 VOID DoMatchEverything(PLAYERp pp, short match, short state)
1689     {
1690     PLAYERp bak;
1691 
1692     bak = GlobPlayerP;
1693     GlobPlayerP = pp;
1694     // CAREFUL! pp == NULL is a valid case for this routine
1695     DoStopSoundSpotMatch(match);
1696     DoSoundSpotMatch(match, 1, SOUND_EVERYTHING_TYPE);
1697     GlobPlayerP = bak;
1698 
1699     DoLightingMatch(match, state);
1700 
1701     DoQuakeMatch(match);
1702 
1703     // make sure all vators are inactive before allowing
1704     // to repress switch
1705     if (!TestVatorMatchActive(match))
1706         DoVatorMatch(pp, match);
1707 
1708     if (!TestSpikeMatchActive(match))
1709         DoSpikeMatch(pp, match);
1710 
1711     if (!TestRotatorMatchActive(match))
1712         DoRotatorMatch(pp, match, FALSE);
1713 
1714     if (!TestSlidorMatchActive(match))
1715         DoSlidorMatch(pp, match, FALSE);
1716 
1717     DoSectorObjectKillMatch(match);
1718     DoSectorObjectSetScale(match);
1719 
1720     DoSOevent(match, state);
1721     DoSpawnActorTrigger(match);
1722 
1723     // this may or may not find an exploding sector
1724     SearchExplodeSectorMatch(match);
1725 
1726     CopySectorMatch(match);
1727     DoWallMoveMatch(match);
1728     DoSpawnSpotsForKill(match);
1729 
1730     DoTrapReset(match);
1731     DoTrapMatch(match);
1732 
1733     SpawnItemsMatch(match);
1734     DoChangorMatch(match);
1735     DoDeleteSpriteMatch(match);
1736     }
1737 
ComboSwitchTest(short combo_type,short match)1738 BOOL ComboSwitchTest(short combo_type, short match)
1739     {
1740     short i,nexti;
1741     SPRITEp sp;
1742     BOOL state;
1743 
1744     TRAVERSE_SPRITE_STAT(headspritestat[STAT_DEFAULT], i, nexti)
1745         {
1746         sp = &sprite[i];
1747 
1748         if (sp->lotag == combo_type && sp->hitag == match)
1749             {
1750             // dont toggle - get the current state
1751             state = AnimateSwitch(sp, 999);
1752 
1753             // if any one is not set correctly then switch is not set
1754             if (state != SP_TAG3(sp))
1755                 {
1756                 return(FALSE);
1757                 }
1758             }
1759         }
1760 
1761     return(TRUE);
1762     }
1763 
1764 // NOTE: switches are always wall sprites
1765 int
OperateSprite(short SpriteNum,short player_is_operating)1766 OperateSprite(short SpriteNum, short player_is_operating)
1767     {
1768     SPRITEp sp = &sprite[SpriteNum];
1769     USERp u = User[SpriteNum];
1770     PLAYERp pp = NULL;
1771     short state;
1772     short key_num=0;
1773     extern STATE s_Pachinko1Operate[];
1774     extern STATE s_Pachinko2Operate[];
1775     extern STATE s_Pachinko3Operate[];
1776     extern STATE s_Pachinko4Operate[];
1777 
1778     if (Prediction)
1779         return(FALSE);
1780 
1781     if (sp->picnum == ST1)
1782         return(FALSE);
1783 
1784     if (player_is_operating)
1785         {
1786         pp = GlobPlayerP;
1787 
1788         if (!FAFcansee(pp->posx, pp->posy, pp->posz, pp->cursectnum, sp->x, sp->y, sp->z - DIV2(SPRITEp_SIZE_Z(sp)), sp->sectnum))
1789             return(FALSE);
1790         }
1791 
1792     switch (sp->lotag)
1793         {
1794         case TOILETGIRL_R0:
1795         case WASHGIRL_R0:
1796         case CARGIRL_R0:
1797         case MECHANICGIRL_R0:
1798         case SAILORGIRL_R0:
1799         case PRUNEGIRL_R0:
1800             //if(RANDOM_RANGE(1000) < 500) return(TRUE);
1801             //if(u->FlagOwner == 0)
1802                 {
1803                 short choose_snd;
1804 
1805                 u->FlagOwner = 1;
1806                 u->WaitTics = SEC(4);
1807 
1808                 if(pp != Player+myconnectindex) return(TRUE);
1809 
1810                 choose_snd = STD_RANDOM_RANGE(1000);
1811                 if(sp->lotag == CARGIRL_R0)
1812                     {
1813                     if(choose_snd > 700)
1814                         PlayerSound(DIGI_JG44052,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1815                     else
1816                     if(choose_snd > 500)
1817                         PlayerSound(DIGI_JG45014,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1818                     else
1819                     if(choose_snd > 250)
1820                         PlayerSound(DIGI_JG44068,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1821                     else
1822                         PlayerSound(DIGI_JG45010,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1823                     } else
1824                 if(sp->lotag == MECHANICGIRL_R0)
1825                     {
1826                     if(choose_snd > 700)
1827                         PlayerSound(DIGI_JG44027,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1828                     else
1829                     if(choose_snd > 500)
1830                         PlayerSound(DIGI_JG44038,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1831                     else
1832                     if(choose_snd > 250)
1833                         PlayerSound(DIGI_JG44039,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1834                     else
1835                         PlayerSound(DIGI_JG44048,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1836                     } else
1837                 if(sp->lotag == SAILORGIRL_R0)
1838                     {
1839                     if(choose_snd > 700)
1840                         PlayerSound(DIGI_JG45018,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1841                     else
1842                     if(choose_snd > 500)
1843                         PlayerSound(DIGI_JG45030,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1844                     else
1845                     if(choose_snd > 250)
1846                         PlayerSound(DIGI_JG45033,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1847                     else
1848                         PlayerSound(DIGI_JG45043,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1849                     } else
1850                 if(sp->lotag == PRUNEGIRL_R0)
1851                     {
1852                     if(choose_snd > 700)
1853                         PlayerSound(DIGI_JG45053,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1854                     else
1855                     if(choose_snd > 500)
1856                         PlayerSound(DIGI_JG45067,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1857                     else
1858                     if(choose_snd > 250)
1859                         PlayerSound(DIGI_JG46005,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1860                     else
1861                         PlayerSound(DIGI_JG46010,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1862                     } else
1863                 if(sp->lotag == TOILETGIRL_R0)
1864                     {
1865                     if(choose_snd > 700)
1866                         PlayerSound(DIGI_WHATYOUEATBABY,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1867                     else
1868                     if(choose_snd > 500)
1869                         PlayerSound(DIGI_WHATDIEDUPTHERE,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1870                     else
1871                     if(choose_snd > 250)
1872                         PlayerSound(DIGI_YOUGOPOOPOO,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1873                     else
1874                         PlayerSound(DIGI_PULLMYFINGER,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1875                     } else
1876                     {
1877                     if(choose_snd > 700)
1878                         PlayerSound(DIGI_SOAPYOUGOOD,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1879                     else
1880                     if(choose_snd > 500)
1881                         PlayerSound(DIGI_WASHWANG,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1882                     else
1883                     if(choose_snd > 250)
1884                         PlayerSound(DIGI_DROPSOAP,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1885                     else
1886                         PlayerSound(DIGI_REALTITS,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_follow,pp);
1887                     }
1888                 }
1889         return(TRUE);
1890 
1891     case PACHINKO1:
1892 
1893         // Don't mess with it if it's already going
1894         if(u->WaitTics > 0) return(TRUE);
1895 
1896         PlaySound(DIGI_PFLIP,&sp->x,&sp->y,&sp->z,v3df_none);
1897         u->WaitTics = SEC(3) + SEC(RANDOM_RANGE(10));
1898         ChangeState(SpriteNum,s_Pachinko1Operate);
1899 
1900         return(TRUE);
1901 
1902     case PACHINKO2:
1903 
1904         // Don't mess with it if it's already going
1905         if(u->WaitTics > 0) return(TRUE);
1906 
1907         PlaySound(DIGI_PFLIP,&sp->x,&sp->y,&sp->z,v3df_none);
1908         u->WaitTics = SEC(3) + SEC(RANDOM_RANGE(10));
1909         ChangeState(SpriteNum,s_Pachinko2Operate);
1910 
1911         return(TRUE);
1912 
1913     case PACHINKO3:
1914 
1915         // Don't mess with it if it's already going
1916         if(u->WaitTics > 0) return(TRUE);
1917 
1918         PlaySound(DIGI_PFLIP,&sp->x,&sp->y,&sp->z,v3df_none);
1919         u->WaitTics = SEC(3) + SEC(RANDOM_RANGE(10));
1920         ChangeState(SpriteNum,s_Pachinko3Operate);
1921 
1922         return(TRUE);
1923 
1924     case PACHINKO4:
1925 
1926         // Don't mess with it if it's already going
1927         if(u->WaitTics > 0) return(TRUE);
1928 
1929         PlaySound(DIGI_PFLIP,&sp->x,&sp->y,&sp->z,v3df_none);
1930         u->WaitTics = SEC(3) + SEC(RANDOM_RANGE(10));
1931         ChangeState(SpriteNum,s_Pachinko4Operate);
1932 
1933         return(TRUE);
1934 
1935     case SWITCH_LOCKED:
1936             key_num = sp->hitag;
1937             if (pp->HasKey[key_num - 1])
1938                 {
1939                 int i;
1940                 for(i=0; i<numsectors; i++)
1941                     {
1942                     if (SectUser[i] && SectUser[i]->stag == SECT_LOCK_DOOR && SectUser[i]->number == key_num)
1943                         SectUser[i]->number = 0;  // unlock all doors of this type
1944                     }
1945                 UnlockKeyLock(key_num, SpriteNum);
1946                 }
1947 
1948         return(TRUE);
1949 
1950     case TAG_COMBO_SWITCH_EVERYTHING:
1951 
1952         // change the switch state
1953         AnimateSwitch(sp, -1);
1954         PlaySound(DIGI_REGULARSWITCH, &sp->x, &sp->y, &sp->z, v3df_none);
1955 
1956         if (ComboSwitchTest(TAG_COMBO_SWITCH_EVERYTHING, sp->hitag))
1957             {
1958             DoMatchEverything(pp, sp->hitag, ON);
1959             }
1960 
1961         return(TRUE);
1962 
1963     case TAG_COMBO_SWITCH_EVERYTHING_ONCE:
1964 
1965         // change the switch state
1966         AnimateSwitch(sp, -1);
1967         PlaySound(DIGI_REGULARSWITCH, &sp->x, &sp->y, &sp->z, v3df_none);
1968 
1969         if (ComboSwitchTest(TAG_COMBO_SWITCH_EVERYTHING, sp->hitag))
1970             {
1971             DoMatchEverything(pp, sp->hitag, ON);
1972             }
1973 
1974         sp->lotag = 0;
1975         sp->hitag = 0;
1976         return(TRUE);
1977 
1978     case TAG_SWITCH_EVERYTHING:
1979         state = AnimateSwitch(sp, -1);
1980         DoMatchEverything(pp, sp->hitag, state);
1981         return(TRUE);
1982 
1983     case TAG_SWITCH_EVERYTHING_ONCE:
1984         state = AnimateSwitch(sp, -1);
1985         DoMatchEverything(pp, sp->hitag, state);
1986         sp->lotag = 0;
1987         sp->hitag = 0;
1988         return(TRUE);
1989 
1990     case TAG_LIGHT_SWITCH:
1991 
1992         state = AnimateSwitch(sp, -1);
1993         DoLightingMatch(sp->hitag, state);
1994         return(TRUE);
1995 
1996     case TAG_SPRITE_SWITCH_VATOR:
1997         {
1998         // make sure all vators are inactive before allowing
1999         // to repress switch
2000         if (!TestVatorMatchActive(sp->hitag))
2001             DoVatorMatch(pp, sp->hitag);
2002 
2003         if (!TestSpikeMatchActive(sp->hitag))
2004             DoSpikeMatch(pp, sp->hitag);
2005 
2006         if (!TestRotatorMatchActive(sp->hitag))
2007             DoRotatorMatch(pp, sp->hitag, FALSE);
2008 
2009         if (!TestSlidorMatchActive(sp->hitag))
2010             DoSlidorMatch(pp, sp->hitag, FALSE);
2011 
2012         return(TRUE);
2013         }
2014 
2015     case TAG_LEVEL_EXIT_SWITCH:
2016         {
2017         extern short Level;
2018         extern BOOL QuitFlag, ExitLevel, FinishedLevel;
2019 
2020         AnimateSwitch(sp, -1);
2021 
2022         PlaySound(DIGI_BIGSWITCH, &sp->x, &sp->y, &sp->z, v3df_none);
2023 
2024         if (sp->hitag)
2025             Level = sp->hitag;
2026         else
2027             Level++;
2028         ExitLevel = TRUE;
2029         FinishedLevel = TRUE;
2030 
2031         return (TRUE);
2032         }
2033 
2034     case TAG_SPRITE_GRATING:
2035         {
2036         USERp u;
2037         ANIMATOR DoGrating;
2038 
2039         change_sprite_stat(SpriteNum, STAT_NO_STATE);
2040 
2041         u = SpawnUser(SpriteNum, 0, NULL);
2042 
2043         u->ActorActionFunc = DoGrating;
2044 
2045         sp->lotag = 0;
2046         sp->hitag /= 2;
2047 
2048         return (TRUE);
2049         }
2050 
2051     case TAG_SO_SCALE_SWITCH:
2052             AnimateSwitch(sp, -1);
2053             DoSectorObjectSetScale(sp->hitag);
2054             return(TRUE);
2055 
2056     case TAG_SO_SCALE_ONCE_SWITCH:
2057             AnimateSwitch(sp, -1);
2058             DoSectorObjectSetScale(sp->hitag);
2059             sp->lotag = 0;
2060             sp->hitag = 0;
2061             return(TRUE);
2062 
2063     case TAG_SO_EVENT_SWITCH:
2064             {
2065             state = AnimateSwitch(sp, -1);
2066 
2067             DoMatchEverything(NULL, sp->hitag, state);
2068 
2069             sp->hitag = 0;
2070             sp->lotag = 0;
2071 
2072             PlaySound(DIGI_REGULARSWITCH, &sp->x, &sp->y, &sp->z, v3df_none);
2073             break;
2074             }
2075 
2076     case TAG_ROTATE_SO_SWITCH:
2077             {
2078             short so_num;
2079             SECTOR_OBJECTp sop;
2080 
2081             so_num = sp->hitag;
2082 
2083             ASSERT(so_num <= 20);
2084             ASSERT(SectorObject[so_num].num_sectors != -1);
2085 
2086             AnimateSwitch(sp, -1);
2087 
2088             sop = &SectorObject[so_num];
2089 
2090             sop->ang_tgt = NORM_ANGLE(sop->ang_tgt + 512);
2091 
2092             PlaySound(DIGI_BIGSWITCH, &sp->x, &sp->y, &sp->z, v3df_none);
2093 
2094             return (TRUE);
2095 
2096             break;
2097             }
2098         }
2099 
2100     return (FALSE);
2101     }
2102 
DoTrapReset(short match)2103 int DoTrapReset(short match)
2104     {
2105     short i, nexti;
2106     SPRITEp sp;
2107     USERp u;
2108 
2109     TRAVERSE_SPRITE_STAT(headspritestat[STAT_TRAP], i, nexti)
2110         {
2111         sp = &sprite[i];
2112         u = User[i];
2113 
2114         if (sp->lotag != match)
2115             continue;
2116 
2117         // if correct type and matches
2118         if (sp->hitag == FIREBALL_TRAP)
2119             u->WaitTics = 0;
2120 
2121         // if correct type and matches
2122         if (sp->hitag == BOLT_TRAP)
2123             u->WaitTics = 0;
2124 
2125         // if correct type and matches
2126         if (sp->hitag == SPEAR_TRAP)
2127             u->WaitTics = 0;
2128         }
2129     return(0);
2130     }
2131 
DoTrapMatch(short match)2132 int DoTrapMatch(short match)
2133     {
2134     short i, nexti;
2135     SPRITEp sp;
2136     USERp u;
2137     int InitFireballTrap(short SpriteNum);
2138 
2139     // may need to be reset to fire immediately
2140 
2141     TRAVERSE_SPRITE_STAT(headspritestat[STAT_TRAP], i, nexti)
2142         {
2143         sp = &sprite[i];
2144         u = User[i];
2145 
2146         if (sp->lotag != match)
2147             continue;
2148 
2149         // if correct type and matches
2150         if (sp->hitag == FIREBALL_TRAP)
2151             {
2152             u->WaitTics -= synctics;
2153 
2154             if (u->WaitTics <= 0)
2155                 {
2156                 u->WaitTics = 1 * 120;
2157                 InitFireballTrap(i);
2158                 }
2159             }
2160 
2161         // if correct type and matches
2162         if (sp->hitag == BOLT_TRAP)
2163             {
2164             u->WaitTics -= synctics;
2165 
2166             if (u->WaitTics <= 0)
2167                 {
2168                 u->WaitTics = 1 * 120;
2169                 InitBoltTrap(i);
2170                 }
2171             }
2172 
2173         // if correct type and matches
2174         if (sp->hitag == SPEAR_TRAP)
2175             {
2176             u->WaitTics -= synctics;
2177 
2178             if (u->WaitTics <= 0)
2179                 {
2180                 u->WaitTics = 1 * 120;
2181                 InitSpearTrap(i);
2182                 }
2183             }
2184         }
2185     return(0);
2186     }
2187 
2188 
2189 VOID
OperateTripTrigger(PLAYERp pp)2190 OperateTripTrigger(PLAYERp pp)
2191     {
2192     SECTORp sectp = &sector[pp->cursectnum];
2193 
2194     if (Prediction)
2195         return;
2196 
2197     #if 0
2198     // new method
2199     if (TEST(sectp->extra, SECTFX_TRIGGER))
2200         {
2201         SPRITEp sp;
2202 
2203         TRAVERSE_SPRITE_SECT(headspritesect[*sectp],i,nexti)
2204             {
2205             sp = &sprite[i];
2206 
2207             if (sp->statnum == STAT_TRIGGER && SP_TAG7(sp) == 0)
2208                 {
2209                 switch (SP_TAG3(sp))
2210                     {
2211                     case 1: // Secret Area
2212 
2213                         break;
2214                     }
2215                 }
2216             }
2217         }
2218     #endif
2219 
2220     // old method
2221     switch (LOW_TAG(pp->cursectnum))
2222         {
2223     // same tag for sector as for switch
2224     case TAG_LEVEL_EXIT_SWITCH:
2225         {
2226         extern short Level;
2227         extern BOOL QuitFlag, ExitLevel, FinishedLevel;
2228 
2229         if (sectp->hitag)
2230             Level = sectp->hitag;
2231         else
2232             Level++;
2233         ExitLevel = TRUE;
2234         FinishedLevel = TRUE;
2235         break;
2236         }
2237 
2238     case TAG_SECRET_AREA_TRIGGER:
2239         if (pp == Player+myconnectindex)
2240             PlayerSound(DIGI_ANCIENTSECRET, &pp->posx, &pp->posy, &pp->posz,
2241                  v3df_dontpan|v3df_doppler|v3df_follow,pp);
2242 
2243         sprintf(ds,"You found a secret area!");
2244         PutStringInfo(pp, ds);
2245         // always give to the first player
2246         Player->SecretsFound++;
2247         sectp->lotag = 0;
2248         sectp->hitag = 0;
2249         break;
2250 
2251     case TAG_TRIGGER_EVERYTHING:
2252         DoMatchEverything(pp, sectp->hitag, -1);
2253         break;
2254 
2255     case TAG_TRIGGER_EVERYTHING_ONCE:
2256         DoMatchEverything(pp, sectp->hitag, -1);
2257         sectp->lotag = 0;
2258         sectp->hitag = 0;
2259         break;
2260 
2261     case TAG_SECTOR_TRIGGER_VATOR:
2262         if (!TestVatorMatchActive(sectp->hitag))
2263              DoVatorMatch(pp, sectp->hitag);
2264         if (!TestSpikeMatchActive(sectp->hitag))
2265              DoSpikeMatch(pp, sectp->hitag);
2266         if (!TestRotatorMatchActive(sectp->hitag))
2267              DoRotatorMatch(pp, sectp->hitag, FALSE);
2268         if (!TestSlidorMatchActive(sectp->hitag))
2269              DoSlidorMatch(pp, sectp->hitag, FALSE);
2270         break;
2271 
2272     case TAG_LIGHT_TRIGGER:
2273         DoLightingMatch(sectp->hitag, -1);
2274         break;
2275 
2276     case TAG_SO_SCALE_TRIGGER:
2277             DoSectorObjectSetScale(sectp->hitag);
2278             break;
2279 
2280     case TAG_SO_SCALE_ONCE_TRIGGER:
2281             DoSectorObjectSetScale(sectp->hitag);
2282             sectp->lotag = 0;
2283             sectp->hitag = 0;
2284             break;
2285 
2286     case TAG_TRIGGER_ACTORS:
2287             {
2288             int dist;
2289             short i, nexti;
2290             SPRITEp sp;
2291             USERp u;
2292 
2293             dist = sectp->hitag;
2294 
2295             TRAVERSE_SPRITE_STAT(headspritestat[STAT_ENEMY], i, nexti)
2296                 {
2297                 sp = &sprite[i];
2298                 u = User[i];
2299 
2300                 if (TEST(u->Flags, SPR_WAIT_FOR_TRIGGER))
2301                     {
2302                     if (Distance(sp->x, sp->y, pp->posx, pp->posy) < dist)
2303                         {
2304                         u->tgt_sp = pp->SpriteP;
2305                         RESET(u->Flags, SPR_WAIT_FOR_TRIGGER);
2306                         }
2307                     }
2308                 }
2309 
2310             break;
2311             }
2312 
2313     case TAG_TRIGGER_MISSILE_TRAP:
2314             {
2315             // reset traps so they fire immediately
2316             DoTrapReset(sector[pp->cursectnum].hitag);
2317             break;
2318             }
2319 
2320     case TAG_TRIGGER_EXPLODING_SECTOR:
2321         {
2322         DoMatchEverything(NULL, sector[pp->cursectnum].hitag, -1);
2323         break;
2324         }
2325 
2326     case TAG_SPAWN_ACTOR_TRIGGER:
2327             {
2328             DoMatchEverything(NULL, sector[pp->cursectnum].hitag, -1);
2329 
2330             sector[pp->cursectnum].hitag = 0;
2331             sector[pp->cursectnum].lotag = 0;
2332             break;
2333             }
2334 
2335     case TAG_SO_EVENT_TRIGGER:
2336             {
2337             DoMatchEverything(NULL, sector[pp->cursectnum].hitag, -1);
2338 
2339             sector[pp->cursectnum].hitag = 0;
2340             sector[pp->cursectnum].lotag = 0;
2341 
2342             PlaySound(DIGI_REGULARSWITCH, &pp->posx, &pp->posy, &pp->posz, v3df_none);
2343             break;
2344             }
2345         }
2346     }
2347 
2348 VOID
OperateContinuousTrigger(PLAYERp pp)2349 OperateContinuousTrigger(PLAYERp pp)
2350     {
2351     if (Prediction)
2352         return;
2353 
2354     switch (LOW_TAG(pp->cursectnum))
2355         {
2356     case TAG_TRIGGER_MISSILE_TRAP:
2357             {
2358             #if 1
2359             DoTrapMatch(sector[pp->cursectnum].hitag);
2360             #else
2361             short i, nexti;
2362             SPRITEp sp;
2363             USERp u;
2364             int InitFireballTrap(short SpriteNum);
2365 
2366             TRAVERSE_SPRITE_STAT(headspritestat[STAT_TRAP], i, nexti)
2367                 {
2368                 sp = &sprite[i];
2369                 u = User[i];
2370 
2371                 // if correct type and matches
2372                 if (sp->hitag == FIREBALL_TRAP && sp->lotag == sector[pp->cursectnum].hitag)
2373                     {
2374                     u->WaitTics -= synctics;
2375 
2376                     if (u->WaitTics <= 0)
2377                         {
2378                         u->WaitTics = 1 * 120;
2379                         InitFireballTrap(i);
2380                         }
2381                     }
2382 
2383                 // if correct type and matches
2384                 if (sp->hitag == BOLT_TRAP && sp->lotag == sector[pp->cursectnum].hitag)
2385                     {
2386                     u->WaitTics -= synctics;
2387 
2388                     if (u->WaitTics <= 0)
2389                         {
2390                         u->WaitTics = 1 * 120;
2391                         InitBoltTrap(i);
2392                         }
2393                     }
2394 
2395                 // if correct type and matches
2396                 if (sp->hitag == SPEAR_TRAP && sp->lotag == sector[pp->cursectnum].hitag)
2397                     {
2398                     u->WaitTics -= synctics;
2399 
2400                     if (u->WaitTics <= 0)
2401                         {
2402                         u->WaitTics = 1 * 120;
2403                         InitSpearTrap(i);
2404                         }
2405                     }
2406                 }
2407             #endif
2408 
2409             break;
2410             }
2411         }
2412     }
2413 
2414 
PlayerTakeSectorDamage(PLAYERp pp)2415 short PlayerTakeSectorDamage(PLAYERp pp)
2416     {
2417     SECT_USERp sectu = SectUser[pp->cursectnum];
2418     USERp u = User[pp->PlayerSprite];
2419 
2420     // the calling routine must make sure sectu exists
2421     if ((u->DamageTics -= synctics) < 0)
2422         {
2423         u->DamageTics = DAMAGE_TIME;
2424 
2425         PlayerUpdateHealth(pp, -sectu->damage);
2426         PlayerCheckDeath(pp, -1);
2427         }
2428     return(0);
2429     }
2430 
2431 // Needed in order to see if Player should grunt if he can't find a wall to operate on
2432 // If player is too far away, don't grunt
2433 #define PLAYER_SOUNDEVENT_TAG 900
NearThings(PLAYERp pp)2434 BOOL NearThings(PLAYERp pp)
2435 {
2436     short sectnum;
2437     short rndnum;
2438     int daz;
2439     short neartagsect, neartagwall, neartagsprite;
2440     int neartaghitdist;
2441 
2442 
2443     // Check player's current sector for triggered sound
2444     if(sector[pp->cursectnum].hitag == PLAYER_SOUNDEVENT_TAG)
2445     {
2446             if(pp == Player+myconnectindex)
2447                 PlayerSound(sector[pp->cursectnum].lotag,&pp->posx,&pp->posy,&pp->posz,v3df_follow|v3df_dontpan,pp);
2448             return(FALSE);
2449     }
2450 
2451     neartag(pp->posx, pp->posy, pp->posz, pp->cursectnum, pp->pang,
2452         &neartagsect, &neartagwall, &neartagsprite,
2453         &neartaghitdist, 1024L, NTAG_SEARCH_LO_HI);
2454 
2455 
2456     // hit a sprite? Check to see if it has sound info in it!
2457     // This can work with any sprite!
2458     if (neartagsprite >= 0)
2459     {
2460         SPRITEp sp = &sprite[neartagsprite];
2461 
2462         // Go through list of cases
2463         if(sp->hitag == PLAYER_SOUNDEVENT_TAG)
2464         {
2465             if(pp == Player+myconnectindex)
2466                 PlayerSound(sp->lotag,&pp->posx,&pp->posy,&pp->posz,v3df_follow|v3df_dontpan,pp);
2467         }
2468         return (FALSE); // Return false so he doesn't grunt
2469     }
2470 
2471     if(neartagwall >= 0)
2472     {
2473         // Check player's current sector for triggered sound
2474         if(wall[neartagwall].hitag == PLAYER_SOUNDEVENT_TAG)
2475         {
2476             if(pp == Player+myconnectindex)
2477                 PlayerSound(wall[neartagwall].lotag,&pp->posx,&pp->posy,&pp->posz,v3df_follow|v3df_dontpan,pp);
2478             return(FALSE);  // We are playing a sound so don't return true
2479         }
2480         return(TRUE);
2481     }
2482     // This only gets called if nothing else worked, check for nearness to a wall
2483     {
2484     short hitsect, hitwall, hitsprite, dang;
2485     int hitx, hity, hitz;
2486 
2487 
2488     hitsect = hitwall = hitsprite = 0;
2489     dang = pp->pang;
2490 
2491     FAFhitscan(pp->posx, pp->posy, pp->posz - Z(30), pp->cursectnum,    // Start position
2492         sintable[NORM_ANGLE(dang + 512)],  // X vector of 3D ang
2493         sintable[NORM_ANGLE(dang)],        // Y vector of 3D ang
2494         0,                                 // Z vector of 3D ang
2495         &hitsect, &hitwall, &hitsprite, &hitx, &hity, &hitz, CLIPMASK_MISSILE);
2496 
2497     if (hitsect < 0)
2498         return (FALSE);
2499 
2500     if (Distance(hitx, hity, pp->posx, pp->posy) > 1500)
2501         return (FALSE);
2502 
2503     // hit a sprite?
2504     if (hitsprite >= 0)
2505         return (FALSE);
2506 
2507     if (neartagsect >= 0)
2508         return(TRUE);
2509 
2510     if(hitwall >= 0)
2511     {
2512         WALLp wp;
2513 
2514         wp =  &wall[hitwall];
2515 
2516         // Near a plain old vanilla wall.  Can't do anything but grunt.
2517         if (!TEST(wp->extra, WALLFX_DONT_STICK) && pp == Player+myconnectindex)
2518             {
2519             if(STD_RANDOM_RANGE(1000) > 970)
2520                 PlayerSound(DIGI_HITTINGWALLS,&pp->posx,&pp->posy,&pp->posz,v3df_follow|v3df_dontpan,pp);
2521             else
2522                 PlayerSound(DIGI_SEARCHWALL,&pp->posx,&pp->posy,&pp->posz,v3df_follow|v3df_dontpan,pp);
2523             }
2524 
2525         return(TRUE);
2526     }
2527 
2528     return(FALSE);
2529     }
2530 
2531 }
2532 
2533 
2534 
2535 
2536 #if 0  // Move to sector.h file because .def files could not find declaration!
2537 typedef struct
2538 {
2539 int dist;
2540 short sectnum, wallnum, spritenum;
2541 }NEAR_TAG_INFO, *NEAR_TAG_INFOp;
2542 #endif
2543 short nti_cnt;
2544 
2545 void
NearTagList(NEAR_TAG_INFOp ntip,PLAYERp pp,int z,int dist,int type,int count)2546 NearTagList(NEAR_TAG_INFOp ntip, PLAYERp pp, int z, int dist, int type, int count)
2547     {
2548     short save_lotag, save_hitag;
2549     short neartagsector, neartagwall, neartagsprite;
2550     int neartaghitdist;
2551 
2552 
2553     neartag(pp->posx, pp->posy, z, pp->cursectnum, pp->pang,
2554         &neartagsector, &neartagwall, &neartagsprite,
2555         &neartaghitdist, dist, type);
2556 
2557     if (neartagsector >= 0)
2558         {
2559         // save off values
2560         save_lotag = sector[neartagsector].lotag;
2561         save_hitag = sector[neartagsector].hitag;
2562 
2563         ntip->dist = neartaghitdist;
2564         ntip->sectnum = neartagsector;
2565         ntip->wallnum = -1;
2566         ntip->spritenum = -1;
2567         nti_cnt++;
2568         ntip++;
2569 
2570         if (nti_cnt >= count)
2571             return;
2572 
2573         // remove them
2574         sector[neartagsector].lotag = 0;
2575         sector[neartagsector].hitag = 0;
2576 
2577         NearTagList(ntip, pp, z, dist, type, count);
2578 
2579         // reset off values
2580         sector[neartagsector].lotag = save_lotag;
2581         sector[neartagsector].hitag = save_hitag;
2582         }
2583     else
2584     if (neartagwall >= 0)
2585         {
2586         // save off values
2587         save_lotag = wall[neartagwall].lotag;
2588         save_hitag = wall[neartagwall].hitag;
2589 
2590         ntip->dist = neartaghitdist;
2591         ntip->sectnum = -1;
2592         ntip->wallnum = neartagwall;
2593         ntip->spritenum = -1;
2594         nti_cnt++;
2595         ntip++;
2596 
2597         if (nti_cnt >= count)
2598             return;
2599 
2600         // remove them
2601         wall[neartagwall].lotag = 0;
2602         wall[neartagwall].hitag = 0;
2603 
2604         NearTagList(ntip, pp, z, dist, type, count);
2605 
2606         // reset off values
2607         wall[neartagwall].lotag = save_lotag;
2608         wall[neartagwall].hitag = save_hitag;
2609         }
2610     else
2611     if (neartagsprite >= 0)
2612         {
2613         // save off values
2614         save_lotag = sprite[neartagsprite].lotag;
2615         save_hitag = sprite[neartagsprite].hitag;
2616 
2617         ntip->dist = neartaghitdist;
2618         ntip->sectnum = -1;
2619         ntip->wallnum = -1;
2620         ntip->spritenum = neartagsprite;
2621         nti_cnt++;
2622         ntip++;
2623 
2624         if (nti_cnt >= count)
2625             return;
2626 
2627         // remove them
2628         sprite[neartagsprite].lotag = 0;
2629         sprite[neartagsprite].hitag = 0;
2630 
2631         NearTagList(ntip, pp, z, dist, type, count);
2632 
2633         // reset off values
2634         sprite[neartagsprite].lotag = save_lotag;
2635         sprite[neartagsprite].hitag = save_hitag;
2636         }
2637     else
2638         {
2639         ntip->dist = -1;
2640         ntip->sectnum = -1;
2641         ntip->wallnum = -1;
2642         ntip->spritenum = -1;
2643         nti_cnt++;
2644         ntip++;
2645 
2646         return;
2647         }
2648     }
2649 
2650 void
BuildNearTagList(NEAR_TAG_INFOp ntip,int size,PLAYERp pp,int z,int dist,int type,int count)2651 BuildNearTagList(NEAR_TAG_INFOp ntip, int size, PLAYERp pp, int z, int dist, int type, int count)
2652     {
2653     memset(ntip, -1, size);
2654     nti_cnt = 0;
2655     NearTagList(ntip, pp, z, dist, type, count);
2656     }
2657 
2658 
DoPlayerGrabStar(PLAYERp pp)2659 int DoPlayerGrabStar(PLAYERp pp)
2660     {
2661     SPRITEp sp = NULL;
2662     int i;
2663     extern short StarQueue[MAX_STAR_QUEUE];
2664 
2665     // MUST check exact z's of each star or it will never work
2666     for (i = 0; i < MAX_STAR_QUEUE; i++)
2667         {
2668         if (StarQueue[i] >= 0)
2669             {
2670             sp = &sprite[StarQueue[i]];
2671 
2672             if (FindDistance3D(sp->x - pp->posx, sp->y - pp->posy, (sp->z - pp->posz + Z(12))>>4) < 500)
2673                 {
2674                 break;
2675                 }
2676             }
2677         }
2678 
2679     if (i < MAX_STAR_QUEUE)
2680         {
2681         // Pull a star out of wall and up your ammo
2682         PlayerUpdateAmmo(pp, WPN_STAR, 1);
2683         PlaySound(DIGI_ITEM, &sp->x, &sp->y, &sp->z, v3df_none);
2684         KillSprite(StarQueue[i]);
2685         StarQueue[i] = -1;
2686         if (TEST(pp->WpnFlags, BIT(WPN_STAR)))
2687             return(TRUE);
2688         SET(pp->WpnFlags, BIT(WPN_STAR));
2689         InitWeaponStar(pp);
2690         return(TRUE);
2691         }
2692 
2693     return(FALSE);
2694     }
2695 
2696 
2697 
2698 VOID
PlayerOperateEnv(PLAYERp pp)2699 PlayerOperateEnv(PLAYERp pp)
2700     {
2701     SECT_USERp sectu = SectUser[pp->cursectnum];
2702     SECTORp sectp = &sector[pp->cursectnum];
2703     BOOL found;
2704 
2705     if (Prediction)
2706         return;
2707 
2708     ////DSPRINTF(ds,"dist %d sectnum %d wallnum %d spritenum %d",nti[nt_ndx].dist, nti[nt_ndx].sectnum, nti[nt_ndx].wallnum, nti[nt_ndx].spritenum);
2709     //MONO_PRINT(ds);
2710 
2711     //
2712     // Switch & door activations
2713     //
2714 
2715     if (TEST_SYNC_KEY(pp, SK_OPERATE))
2716         {
2717         if (FLAG_KEY_PRESSED(pp, SK_OPERATE))
2718             {
2719             // if space bar pressed
2720             short nt_ndx;
2721             NEAR_TAG_INFO nti[16];
2722 
2723             if (DoPlayerGrabStar(pp))
2724                 {
2725                 FLAG_KEY_RELEASE(pp, SK_OPERATE);
2726                 }
2727             else
2728                 {
2729                 NearThings(pp); // Check for player sound specified in a level sprite
2730                 }
2731 
2732             BuildNearTagList(nti, sizeof(nti), pp, pp->posz, 2048L, NTAG_SEARCH_LO_HI, 8);
2733 
2734             found = FALSE;
2735 
2736             // try and find a sprite
2737             for (nt_ndx = 0; nti[nt_ndx].dist >= 0; nt_ndx++)
2738                 {
2739                 if (nti[nt_ndx].spritenum >= 0 && nti[nt_ndx].dist < 1024 + 768)
2740                     {
2741                     if (OperateSprite(nti[nt_ndx].spritenum, TRUE))
2742                         {
2743                         FLAG_KEY_RELEASE(pp, SK_OPERATE);
2744                         found = TRUE;
2745                         }
2746                     }
2747                 }
2748 
2749             // if not found look at different z positions
2750             if (!found)
2751                 {
2752                 int z[3];
2753                 unsigned i;
2754                 NEAR_TAG_INFO nti[16];
2755                 short nt_ndx;
2756 
2757 
2758                 z[0] = pp->SpriteP->z - SPRITEp_SIZE_Z(pp->SpriteP) - Z(10);
2759                 z[1] = pp->SpriteP->z;
2760                 z[2] = DIV2(z[0] + z[1]);
2761 
2762                 for (i = 0; i < SIZ(z); i++)
2763                     {
2764                     BuildNearTagList(nti, sizeof(nti), pp, z[i], 1024 + 768L, NTAG_SEARCH_LO_HI, 8);
2765 
2766                     for (nt_ndx = 0; nti[nt_ndx].dist >= 0; nt_ndx++)
2767                         {
2768                         if (nti[nt_ndx].spritenum >= 0 && nti[nt_ndx].dist < 1024 + 768)
2769                             {
2770                             if (OperateSprite(nti[nt_ndx].spritenum, TRUE))
2771                                 {
2772                                 FLAG_KEY_RELEASE(pp, SK_OPERATE);
2773                                 break;
2774                                 }
2775                             }
2776                         }
2777                     }
2778                 }
2779 
2780             {
2781             int neartaghitdist;
2782             short neartagsector, neartagsprite, neartagwall;
2783 
2784             neartaghitdist = nti[0].dist;
2785             neartagsector = nti[0].sectnum;
2786             neartagwall = nti[0].wallnum;
2787             neartagsprite = nti[0].spritenum;
2788 
2789             if (neartagsector >= 0 && neartaghitdist < 1024)
2790                 {
2791                 if (OperateSector(neartagsector, TRUE))
2792                     {
2793                     // Release the key
2794                     FLAG_KEY_RELEASE(pp, SK_OPERATE);
2795                     }
2796                 }
2797 
2798             if (neartagwall >= 0 && neartaghitdist < 1024)
2799                 {
2800                 if (OperateWall(neartagwall, TRUE))
2801                     {
2802                     FLAG_KEY_RELEASE(pp, SK_OPERATE);
2803                     }
2804                 }
2805             }
2806 
2807             //
2808             // Trigger operations
2809             //
2810 
2811             switch (LOW_TAG(pp->cursectnum))
2812                 {
2813             case TAG_VATOR:
2814                 DoVatorOperate(pp, pp->cursectnum);
2815                 DoSpikeOperate(pp, pp->cursectnum);
2816                 DoRotatorOperate(pp, pp->cursectnum);
2817                 DoSlidorOperate(pp, pp->cursectnum);
2818                 break;
2819             case TAG_SPRING_BOARD:
2820                 DoSpringBoard(pp, pp->cursectnum);
2821                 FLAG_KEY_RELEASE(pp, SK_OPERATE);
2822                 break;
2823             case TAG_DOOR_ROTATE:
2824                 if (OperateSector(pp->cursectnum, TRUE))
2825                     FLAG_KEY_RELEASE(pp, SK_OPERATE);
2826                 break;
2827                 }
2828             }
2829         }
2830     else
2831         {
2832         // Reset the key when syncbit key is not in use
2833         FLAG_KEY_RESET(pp, SK_OPERATE);
2834         }
2835 
2836     // ////////////////////////////
2837     //
2838     // Sector Damage
2839     //
2840     // ////////////////////////////
2841 
2842     if (sectu && sectu->damage)
2843         {
2844         if (TEST(sectu->flags, SECTFU_DAMAGE_ABOVE_SECTOR))
2845             {
2846             PlayerTakeSectorDamage(pp);
2847             }
2848         else
2849         if ((SPRITEp_BOS(pp->SpriteP) >= sectp->floorz) && !TEST(pp->Flags, PF_DIVING))
2850             {
2851             PlayerTakeSectorDamage(pp);
2852             }
2853         }
2854     else
2855         {
2856         USERp u = User[pp->PlayerSprite];
2857         u->DamageTics = 0;
2858         }
2859 
2860 
2861     // ////////////////////////////
2862     //
2863     // Trigger stuff
2864     //
2865     // ////////////////////////////
2866 
2867     OperateContinuousTrigger(pp);
2868 
2869     // just changed sectors
2870     if (pp->lastcursectnum != pp->cursectnum)
2871         {
2872         OperateTripTrigger(pp);
2873 
2874         if (TEST(sector[pp->cursectnum].extra, SECTFX_WARP_SECTOR))
2875             {
2876             if (!TEST(pp->Flags2, PF2_TELEPORTED))
2877                 {
2878                 DoPlayerWarpTeleporter(pp);
2879                 }
2880             }
2881 
2882         RESET(pp->Flags2, PF2_TELEPORTED);
2883         }
2884     }
2885 
2886 
2887 
2888 VOID
DoSineWaveFloor(VOID)2889 DoSineWaveFloor(VOID)
2890     {
2891     SINE_WAVE_FLOOR *swf;
2892     int newz;
2893     short wave;
2894     char flags;
2895 
2896     for (wave = 0; wave < MAX_SINE_WAVE; wave++)
2897         {
2898         for (swf = &SineWaveFloor[wave][0], flags = swf->flags; swf->sector >= 0 && swf < &SineWaveFloor[wave][SIZ(SineWaveFloor[wave])]; swf++)
2899             {
2900 
2901             swf->sintable_ndx = NORM_ANGLE(swf->sintable_ndx + (synctics << swf->speed_shift));
2902 
2903             if (TEST(flags, SINE_FLOOR))
2904                 {
2905                 newz = swf->floor_origz + ((swf->range * sintable[swf->sintable_ndx]) >> 14);
2906                 sector[swf->sector].floorz = newz;
2907                 }
2908 
2909             if (TEST(flags, SINE_CEILING))
2910                 {
2911                 newz = swf->ceiling_origz + ((swf->range * sintable[swf->sintable_ndx]) >> 14);
2912                 sector[swf->sector].ceilingz = newz;
2913                 }
2914 
2915             }
2916         }
2917 
2918     /*  SLOPED SIN-WAVE FLOORS:
2919 
2920     It's best to program sloped sin-wave floors in 2 steps:
2921        1.  First set the floorz of the floor as the sin code normally does it.
2922        2.  Adjust the slopes by calling alignflorslope once for each sector.
2923 
2924     Note:  For this to work, the first wall of each sin-wave sector must be
2925            aligned on the same side of each sector for the entire strip.
2926     */
2927 
2928     for (wave = 0; wave < MAX_SINE_WAVE; wave++)
2929         {
2930         for (swf = &SineWaveFloor[wave][0], flags = swf->flags; swf->sector >= 0 && swf < &SineWaveFloor[wave][SIZ(SineWaveFloor[wave])]; swf++)
2931             {
2932             if (!TEST(sector[swf->sector].floorstat, FLOOR_STAT_SLOPE))
2933                 continue;
2934 
2935             if (TEST(flags, SINE_SLOPED))
2936                 {
2937                 WALLp wal;
2938                 if (sector[swf->sector].wallnum == 4)
2939                     {
2940                     //Set wal to the wall on the opposite side of the sector
2941                     wal = &wall[sector[swf->sector].wallptr+2];
2942 
2943                     //Pass (Sector, x, y, z)
2944                     alignflorslope(swf->sector,wal->x,wal->y,
2945                     sector[wal->nextsector].floorz);
2946                     }
2947                 }
2948             }
2949         }
2950     }
2951 
2952 
2953 VOID
DoSineWaveWall(VOID)2954 DoSineWaveWall(VOID)
2955     {
2956     SINE_WALL *sw;
2957     int new;
2958     short sw_num;
2959 
2960     for (sw_num = 0; sw_num < MAX_SINE_WAVE; sw_num++)
2961         {
2962         for (sw = &SineWall[sw_num][0]; sw->wall >= 0 && sw < &SineWall[sw_num][MAX_SINE_WALL_POINTS]; sw++)
2963             {
2964             // move through the sintable
2965             sw->sintable_ndx = NORM_ANGLE(sw->sintable_ndx + (synctics << sw->speed_shift));
2966 
2967             if (!sw->type)
2968                 {
2969                 new = sw->orig_xy + ((sw->range * sintable[sw->sintable_ndx]) >> 14);
2970                 // wall[sw->wall].y = new;
2971                 dragpoint(sw->wall, wall[sw->wall].x, new);
2972                 }
2973             else
2974                 {
2975                 new = sw->orig_xy + ((sw->range * sintable[sw->sintable_ndx]) >> 14);
2976                 // wall[sw->wall].x = new;
2977                 dragpoint(sw->wall, new, wall[sw->wall].y);
2978                 }
2979             }
2980         }
2981     }
2982 
2983 VOID
DoAnim(int numtics)2984 DoAnim(int numtics)
2985     {
2986     int i, animval;
2987 
2988     for (i = AnimCnt - 1; i >= 0; i--)
2989         {
2990         animval = *Anim[i].ptr;
2991 
2992         // if LESS THAN goal
2993         if (animval < Anim[i].goal)
2994             {
2995             // move it
2996             animval += (numtics * PIXZ(Anim[i].vel));
2997 
2998             Anim[i].vel += Anim[i].vel_adj * numtics;
2999 
3000             // if the other way make it equal
3001             if (animval > Anim[i].goal)
3002                 animval = Anim[i].goal;
3003             }
3004 
3005         // if GREATER THAN goal
3006         if (animval > Anim[i].goal)
3007             {
3008             animval -= (numtics * PIXZ(Anim[i].vel));
3009 
3010             Anim[i].vel += Anim[i].vel_adj * numtics;
3011 
3012             if (animval < Anim[i].goal)
3013                 animval = Anim[i].goal;
3014             }
3015 
3016         *Anim[i].ptr = animval;
3017 
3018         // EQUAL this entry has finished
3019         if (animval == Anim[i].goal)
3020             {
3021             ANIM_CALLBACKp acp = Anim[i].callback;
3022 
3023             // do a callback when done if not NULL
3024             if (Anim[i].callback)
3025                 (*Anim[i].callback) (&Anim[i], Anim[i].callbackdata);
3026 
3027             // only delete it if the callback has not changed
3028             // Logic here is that if the callback changed then something
3029             // else must be happening with it - dont delete it
3030             if (Anim[i].callback == acp)
3031                 {
3032                 // decrement the count
3033                 AnimCnt--;
3034 
3035                 // move the last entry to the current one to free the last
3036                 // entry up
3037                 Anim[i] = Anim[AnimCnt];
3038                 }
3039             }
3040         }
3041     }
3042 
3043 VOID
AnimClear(VOID)3044 AnimClear(VOID)
3045     {
3046     int i, animval;
3047 
3048 #if 1
3049     AnimCnt = 0;
3050 #else
3051     for (i = AnimCnt - 1; i >= 0; i--)
3052         {
3053         if (Anim[i].extra)
3054 	    {
3055             FreeMem(Anim[i].extra);
3056 	    Anim[i].extra = 0;
3057         }
3058         }
3059 
3060     AnimCnt = 0;
3061 #endif
3062     }
3063 
3064 short
AnimGetGoal(int * animptr)3065 AnimGetGoal(int *animptr)
3066     {
3067     int i, j;
3068 
3069     j = -1;
3070     for (i = 0; i < AnimCnt; i++)
3071         {
3072         if (animptr == Anim[i].ptr)
3073             {
3074             j = i;
3075             break;
3076             }
3077         }
3078 
3079     return (j);
3080     }
3081 
3082 void
AnimDelete(int * animptr)3083 AnimDelete(int *animptr)
3084     {
3085     int i, j;
3086 
3087     j = -1;
3088     for (i = 0; i < AnimCnt; i++)
3089         {
3090         if (animptr == Anim[i].ptr)
3091             {
3092             j = i;
3093             break;
3094             }
3095         }
3096 
3097     if (j == -1)
3098         return;
3099 
3100     // decrement the count
3101     AnimCnt--;
3102 
3103     // move the last entry to the current one to free the last entry up
3104     Anim[j] = Anim[AnimCnt];
3105 
3106     //DSPRINTF(ds, "Deleted a Anim");
3107     MONO_PRINT(ds);
3108 
3109     }
3110 
3111 
3112 short
AnimSet(int * animptr,int thegoal,int thevel)3113 AnimSet(int *animptr, int thegoal, int thevel)
3114     {
3115     int i, j;
3116 
3117     ASSERT(AnimCnt < MAXANIM - 1);
3118 
3119     j = AnimCnt;
3120 
3121     // look for existing animation and reset it
3122     for (i = 0; i < AnimCnt; i++)
3123         {
3124         if (animptr == Anim[i].ptr)
3125             {
3126             j = i;
3127             break;
3128             }
3129         }
3130 
3131     Anim[j].ptr = animptr;
3132     Anim[j].goal = thegoal;
3133     Anim[j].vel = Z(thevel);
3134     Anim[j].vel_adj = 0;
3135     Anim[j].callback = NULL;
3136     Anim[j].callbackdata = NULL;
3137 
3138     if (j == AnimCnt)
3139         AnimCnt++;
3140 
3141     return (j);
3142     }
3143 
3144 short
AnimSetCallback(short anim_ndx,ANIM_CALLBACKp call,ANIM_DATAp data)3145 AnimSetCallback(short anim_ndx, ANIM_CALLBACKp call, ANIM_DATAp data)
3146     {
3147     ASSERT(anim_ndx < AnimCnt);
3148 
3149     if (anim_ndx == -1)
3150         return (anim_ndx);
3151 
3152     Anim[anim_ndx].callback = call;
3153     Anim[anim_ndx].callbackdata = data;
3154 
3155     return (anim_ndx);
3156     }
3157 
3158 short
AnimSetVelAdj(short anim_ndx,short vel_adj)3159 AnimSetVelAdj(short anim_ndx, short vel_adj)
3160     {
3161     ASSERT(anim_ndx < AnimCnt);
3162 
3163     if (anim_ndx == -1)
3164         return (anim_ndx);
3165 
3166     Anim[anim_ndx].vel_adj = vel_adj;
3167 
3168     return (anim_ndx);
3169     }
3170 
3171 
initlava(void)3172 void initlava(void)
3173     {
3174 #if 0
3175     int x, y, z, r;
3176     int i;
3177     extern char tempbuf[];
3178 
3179 //char lavabakpic[(LAVASIZ + 2) * (LAVASIZ + 2)], lavainc[LAVASIZ];
3180 //int lavanumdrops, lavanumframes;
3181 //int lavadropx[LAVAMAXDROPS], lavadropy[LAVAMAXDROPS];
3182 //int lavadropsiz[LAVAMAXDROPS], lavadropsizlookup[LAVAMAXDROPS];
3183 //int lavaradx[32][128], lavarady[32][128], lavaradcnt[32];
3184 
3185 
3186 UPDATE TO NEW CODE
3187 
3188     static int lavaradx[24][96];
3189     static int lavarady[24][96];
3190 
3191     for
3192     lavaradcnt[z] = 0;
3193 
3194     for (x = -16; x <= 16; x++)
3195         for (y = -16; y <= 16; y++)
3196             {
3197             // r should only be between 0 and 31
3198             // lavaradcnt[r] should be less 127
3199             r = ksqrt(x * x + y * y);
3200             lavaradx[r][lavaradcnt[r]] = x;
3201             lavarady[r][lavaradcnt[r]] = y;
3202             // this was causing an overwrite in ElevatorAuto structure
3203             lavaradcnt[r]++;
3204             }
3205 
3206 //      for(z=0;z<16;z++)
3207 //              lavadropsizlookup[z] = 8 / (ksqrt(z)+1);
3208 
3209     for (z = 0; z < 16; z++)
3210         {
3211         lavadropsizlookup[z] = 8 / (ksqrt(16 - z) + 1);
3212         }
3213 
3214     for (z = 0; z < LAVASIZ; z++)
3215         {
3216         lavainc[z] = klabs((((z ^ 17) >> 4) & 7) - 4) + 12;
3217         }
3218 
3219     lavanumdrops = 0;
3220     lavanumframes = 0;
3221 #endif
3222     }
3223 
movelava(char * UNUSED (dapic))3224 void movelava(char * UNUSED(dapic))
3225     {
3226 #if 0
3227 //    #define COLOR_OFFSET 192
3228 #define COLOR_OFFSET LT_BROWN
3229 
3230     char dat, *ptr;
3231     int x, y, z, zx, dalavadropsiz, dadropsizlookup;
3232     intptr_t offs, offs2;
3233     int dalavax, dalavay;
3234 
3235     z = 3;
3236     if (lavanumdrops + z >= LAVAMAXDROPS)
3237         z = LAVAMAXDROPS - lavanumdrops - 1;
3238     while (z >= 0)
3239         {
3240         lavadropx[lavanumdrops] = (rand() & (LAVASIZ - 1));
3241         lavadropy[lavanumdrops] = (rand() & (LAVASIZ - 1));
3242         lavadropsiz[lavanumdrops] = 1;
3243         lavanumdrops++;
3244         z--;
3245         }
3246 
3247     z = lavanumdrops - 1;
3248     while (z >= 0)
3249         {
3250         dadropsizlookup = lavadropsizlookup[lavadropsiz[z]] * (((z & 1) << 1) - 1);
3251         dalavadropsiz = lavadropsiz[z];
3252         dalavax = lavadropx[z];
3253         dalavay = lavadropy[z];
3254         for (zx = lavaradcnt[lavadropsiz[z]] - 1; zx >= 0; zx--)
3255             {
3256             offs = (((lavaradx[dalavadropsiz][zx] + dalavax) & (LAVASIZ - 1)) << LAVALOGSIZ);
3257             offs += ((lavarady[dalavadropsiz][zx] + dalavay) & (LAVASIZ - 1));
3258 
3259             dapic[offs] += dadropsizlookup;
3260 
3261             if (dapic[offs] < COLOR_OFFSET)
3262                 dapic[offs] = COLOR_OFFSET;
3263             }
3264 
3265         lavadropsiz[z]++;
3266         if (lavadropsiz[z] > 10)
3267             {
3268             lavanumdrops--;
3269             lavadropx[z] = lavadropx[lavanumdrops];
3270             lavadropy[z] = lavadropy[lavanumdrops];
3271             lavadropsiz[z] = lavadropsiz[lavanumdrops];
3272             }
3273         z--;
3274         }
3275 
3276     // Back up dapic with 1 pixel extra on each boundary
3277     // (to prevent anding for wrap-around)
3278     offs = ((intptr_t) dapic);
3279     offs2 = (LAVASIZ + 2) + 1 + ((intptr_t) lavabakpic);
3280     for (x = 0; x < LAVASIZ; x++)
3281         {
3282         copybuf(offs, offs2, LAVASIZ >> 2);
3283         offs += LAVASIZ;
3284         offs2 += LAVASIZ + 2;
3285         }
3286     for (y = 0; y < LAVASIZ; y++)
3287         {
3288         lavabakpic[y + 1] = dapic[y + ((LAVASIZ - 1) << LAVALOGSIZ)];
3289         lavabakpic[y + 1 + (LAVASIZ + 1) * (LAVASIZ + 2)] = dapic[y];
3290         }
3291     for (x = 0; x < LAVASIZ; x++)
3292         {
3293         lavabakpic[(x + 1) * (LAVASIZ + 2)] = dapic[(x << LAVALOGSIZ) + (LAVASIZ - 1)];
3294         lavabakpic[(x + 1) * (LAVASIZ + 2) + (LAVASIZ + 1)] = dapic[x << LAVALOGSIZ];
3295         }
3296     lavabakpic[0] = dapic[LAVASIZ * LAVASIZ - 1];
3297     lavabakpic[LAVASIZ + 1] = dapic[LAVASIZ * (LAVASIZ - 1)];
3298     lavabakpic[(LAVASIZ + 2) * (LAVASIZ + 1)] = dapic[LAVASIZ - 1];
3299     lavabakpic[(LAVASIZ + 2) * (LAVASIZ + 2) - 1] = dapic[0];
3300 
3301     for (z = (LAVASIZ + 2) * (LAVASIZ + 2) - 4; z >= 0; z -= 4)
3302         {
3303         lavabakpic[z + 0] &= 31;
3304         lavabakpic[z + 1] &= 31;
3305         lavabakpic[z + 2] &= 31;
3306         lavabakpic[z + 3] &= 31;
3307         }
3308 
3309     for (x = LAVASIZ - 1; x >= 0; x--)
3310         {
3311         FAKETIMERHANDLER();
3312         offs = (x + 1) * (LAVASIZ + 2) + 1;
3313         ptr = (char *) ((x << LAVALOGSIZ) + (intptr_t) dapic);
3314 
3315         zx = ((x + lavanumframes) & (LAVASIZ - 1));
3316 
3317         offs2 = LAVASIZ - 1;
3318         for (y = offs; y < offs + LAVASIZ; y++)
3319             {
3320             dat = lavainc[(offs2--) & zx];
3321 
3322             dat += lavabakpic[y - (LAVASIZ + 2) - 1];
3323             dat += lavabakpic[y - (LAVASIZ + 2)];
3324             dat += lavabakpic[y - (LAVASIZ + 2) + 1];
3325             dat += lavabakpic[y - 1];
3326             dat += lavabakpic[y + 1];
3327             dat += lavabakpic[y + (LAVASIZ + 2)];
3328             dat += lavabakpic[y + (LAVASIZ + 2) - 1];
3329 
3330             *ptr++ = (dat >> 3) + COLOR_OFFSET;
3331             }
3332         }
3333 
3334     lavanumframes++;
3335 #endif
3336     }
3337 
3338 
3339 VOID
DoPanning(VOID)3340 DoPanning(VOID)
3341     {
3342     int nx, ny;
3343     short i,nexti;
3344     SPRITEp sp;
3345     SECTORp sectp;
3346     WALLp wallp;
3347 
3348     TRAVERSE_SPRITE_STAT(headspritestat[STAT_FLOOR_PAN], i, nexti)
3349         {
3350         sp = &sprite[i];
3351         sectp = &sector[sp->sectnum];
3352 
3353         nx = (((int) sintable[NORM_ANGLE(sp->ang + 512)]) * sp->xvel) >> 20;
3354         ny = (((int) sintable[sp->ang]) * sp->xvel) >> 20;
3355 
3356         sectp->floorxpanning += nx;
3357         sectp->floorypanning += ny;
3358 
3359         sectp->floorxpanning &= 255;
3360         sectp->floorypanning &= 255;
3361         }
3362 
3363     TRAVERSE_SPRITE_STAT(headspritestat[STAT_CEILING_PAN], i, nexti)
3364         {
3365         sp = &sprite[i];
3366         sectp = &sector[sp->sectnum];
3367 
3368         nx = (((int) sintable[NORM_ANGLE(sp->ang + 512)]) * sp->xvel) >> 20;
3369         ny = (((int) sintable[sp->ang]) * sp->xvel) >> 20;
3370 
3371         sectp->ceilingxpanning += nx;
3372         sectp->ceilingypanning += ny;
3373 
3374         sectp->ceilingxpanning &= 255;
3375         sectp->ceilingypanning &= 255;
3376         }
3377 
3378     TRAVERSE_SPRITE_STAT(headspritestat[STAT_WALL_PAN], i, nexti)
3379         {
3380         sp = &sprite[i];
3381         wallp = &wall[sp->owner];
3382 
3383         nx = (((int) sintable[NORM_ANGLE(sp->ang + 512)]) * sp->xvel) >> 20;
3384         ny = (((int) sintable[sp->ang]) * sp->xvel) >> 20;
3385 
3386         wallp->xpanning += nx;
3387         wallp->ypanning += ny;
3388 
3389         wallp->xpanning &= 255;
3390         wallp->ypanning &= 255;
3391         }
3392     }
3393 
3394 
3395 VOID
DoSector(VOID)3396 DoSector(VOID)
3397     {
3398     short i;
3399     SECTOR_OBJECTp sop;
3400     BOOL riding;
3401     extern BOOL DebugActorFreeze;
3402     int sync_flag;
3403     short pnum;
3404     int min_dist,dist,a,b,c;
3405     PLAYERp pp;
3406 
3407     if (DebugActorFreeze)
3408         return;
3409 
3410 #if 0
3411     MoveSectorObjects(synctics);
3412 #endif
3413 
3414 #if 0
3415     if (MoveSkip2 == 0)
3416         MoveSectorObjects(synctics * 2);
3417 #endif
3418 
3419 #if 0
3420     TRAVERSE_CONNECT(pnum)
3421         {
3422         pp = Player + pnum;
3423         pp->sop_riding = NULL;
3424         }
3425 
3426     for (sop = SectorObject; sop < &SectorObject[MAX_SECTOR_OBJECTS]; sop++)
3427         {
3428         if (MoveSkip2 == 0)
3429             MoveSectorObjects(sop, synctics * 2);
3430         }
3431 #endif
3432 
3433 #if 1
3434     for (sop = SectorObject; sop < &SectorObject[MAX_SECTOR_OBJECTS]; sop++)
3435         {
3436 
3437         if (sop->xmid == MAXLONG || sop->xmid == MAXSO)
3438             continue;
3439 
3440 
3441         riding = FALSE;
3442         min_dist = 999999;
3443 
3444         TRAVERSE_CONNECT(pnum)
3445             {
3446             pp = &Player[pnum];
3447 
3448             if (pp->sop_riding == sop)
3449                 {
3450                 riding = TRUE;
3451                 pp->sop_riding = NULL;
3452                 break;
3453                 }
3454             else
3455                 {
3456                 DISTANCE(pp->posx, pp->posy, sop->xmid, sop->ymid, dist, a, b, c);
3457                 if (dist < min_dist)
3458                     min_dist = dist;
3459                 }
3460             }
3461 
3462         if (sop->Animator)
3463             {
3464             (*sop->Animator)(sop);
3465             continue;
3466             }
3467 
3468         // force sync SOs to be updated regularly
3469         if ((sync_flag = TEST(sop->flags, SOBJ_SYNC1|SOBJ_SYNC2)) != 0)
3470             {
3471             if (sync_flag == SOBJ_SYNC1)
3472                 MoveSectorObjects(sop, synctics);
3473             else
3474                 {
3475                 if (MoveSkip2 == 0)
3476                     MoveSectorObjects(sop, synctics*2);
3477                 }
3478 
3479             continue;
3480             }
3481 
3482         if (riding)
3483             {
3484             // if riding move smoothly
3485             // update every time
3486             MoveSectorObjects(sop, synctics);
3487             }
3488         else
3489             {
3490             if (min_dist < 15000)
3491                 {
3492                 // if close update every other time
3493                 if (MoveSkip2 == 0)
3494                     MoveSectorObjects(sop, synctics * 2);
3495                 }
3496             else
3497                 {
3498                 // if further update every 4th time
3499                 if (MoveSkip4 == 0)
3500                     MoveSectorObjects(sop, synctics * 4);
3501                 }
3502             }
3503         }
3504 #endif
3505 
3506     DoPanning();
3507     DoLighting();
3508     DoSineWaveFloor();
3509     DoSineWaveWall();
3510     DoSpringBoardDown();
3511     }
3512 
3513 #if 0
3514 int inside(int x, int y, short sectnum)
3515     {
3516     WALLp wal;
3517     int i, x1, y1, x2, y2;
3518     char cnt;
3519 
3520     if ((sectnum < 0) || (sectnum >= numsectors))
3521         return (-1);
3522     cnt = 0;
3523 
3524     wal = &wall[sector[sectnum].wallptr];
3525     for (i = sector[sectnum].wallnum; i > 0; i--)
3526         {
3527         y1 = wal->y - y;
3528         y2 = wall[wal->point2].y - y;
3529         if ((y1 ^ y2) < 0)
3530             {
3531             x1 = wal->x - x;
3532             x2 = wall[wal->point2].x - x;
3533 
3534             if ((x1 ^ x2) < 0)
3535                 cnt ^= (x1 * y2 < x2 * y1) ^ (y1 < y2);
3536             else if (x1 >= 0)
3537                 cnt ^= 1;
3538             }
3539         wal++;
3540         }
3541     return (cnt);
3542     }
3543 }
3544 #endif
3545 
3546 #include "saveable.h"
3547 
3548 static saveable_code saveable_sector_code[] = {
3549 	SAVE_CODE(WallSetupDontMove),
3550 	SAVE_CODE(WallSetup),
3551 	SAVE_CODE(SectorLiquidSet),
3552 	SAVE_CODE(SectorSetup),
3553 	SAVE_CODE(DoSpringBoard),
3554 	SAVE_CODE(DoSpringBoardDown),
3555 	SAVE_CODE(DoSpawnActorTrigger),
3556 	SAVE_CODE(OperateSector),
3557 	SAVE_CODE(OperateWall),
3558 	SAVE_CODE(AnimateSwitch),
3559 	SAVE_CODE(SectorExp),
3560 	SAVE_CODE(DoExplodeSector),
3561 	SAVE_CODE(DoSpawnSpot),
3562 	SAVE_CODE(DoSpawnSpotsForKill),
3563 	SAVE_CODE(DoSpawnSpotsForDamage),
3564 	SAVE_CODE(DoSoundSpotMatch),
3565 	SAVE_CODE(DoSoundSpotStopSound),
3566 	SAVE_CODE(DoStopSoundSpotMatch),
3567 	SAVE_CODE(DoSectorObjectKillMatch),
3568 	SAVE_CODE(DoDeleteSpriteMatch),
3569 	SAVE_CODE(DoChangorMatch),
3570 	SAVE_CODE(DoMatchEverything),
3571 	SAVE_CODE(DoTrapReset),
3572 	SAVE_CODE(DoTrapMatch),
3573 	SAVE_CODE(OperateTripTrigger),
3574 	SAVE_CODE(OperateContinuousTrigger),
3575 	SAVE_CODE(PlayerTakeSectorDamage),
3576 	SAVE_CODE(NearThings),
3577 	SAVE_CODE(NearTagList),
3578 	SAVE_CODE(BuildNearTagList),
3579 	SAVE_CODE(DoPlayerGrabStar),
3580 	SAVE_CODE(PlayerOperateEnv),
3581 	SAVE_CODE(DoSineWaveFloor),
3582 	SAVE_CODE(DoSineWaveWall),
3583 	SAVE_CODE(DoAnim),
3584 	SAVE_CODE(AnimClear),
3585 	SAVE_CODE(AnimGetGoal),
3586 	SAVE_CODE(AnimDelete),
3587 	SAVE_CODE(AnimSet),
3588 	SAVE_CODE(AnimSetCallback),
3589 	SAVE_CODE(AnimSetVelAdj),
3590 	SAVE_CODE(DoPanning),
3591 	SAVE_CODE(DoSector),
3592 };
3593 
3594 saveable_module saveable_sector = {
3595 	// code
3596 	saveable_sector_code,
3597 	SIZ(saveable_sector_code),
3598 
3599 	// data
3600 	NULL,
3601 	0
3602 };
3603 
3604 
3605