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(§or[sectnum].floorz);
741
742 // if in motion return
743 if (i >= 0)
744 return;
745
746 AnimSet(§or[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(§or[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 = §or[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 = §or[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 = §or[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 = §or[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 = §or[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 = §or[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 = §or[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