1 /** @file polyobjs.h Polyobject thinkers and management.
2 *
3 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2005-2013 Daniel Swanson <danij@dengine.net>
5 * @authors Copyright © 1999 Activision
6 *
7 * @par License
8 * GPL: http://www.gnu.org/licenses/gpl.html
9 *
10 * <small>This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version. This program is distributed in the hope that it
14 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16 * Public License for more details. You should have received a copy of the GNU
17 * General Public License along with this program; if not, write to the Free
18 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19 * 02110-1301 USA</small>
20 */
21
22 #include "common.h"
23 #include "polyobjs.h"
24
25 #include "acs/script.h"
26 #include "dmu_lib.h"
27 #include "g_common.h"
28 #include "p_actor.h"
29 #include "p_map.h"
30 #include "p_mapspec.h"
31 #include "p_mapsetup.h"
32 #include "p_start.h"
33
34 #define POBJ_PERPETUAL 0xffffffffu // -1
35
36 /// @return Tag of the found polyobj; otherwise @c 0.
findMirrorPolyobj(int tag)37 static int findMirrorPolyobj(int tag)
38 {
39 #if __JHEXEN__
40 for(int i = 0; i < numpolyobjs; ++i)
41 {
42 Polyobj *po = Polyobj_ById(i);
43 if(po->tag == tag)
44 {
45 return P_ToXLine(Polyobj_FirstLine(po))->arg2;
46 }
47 }
48 #else
49 DENG_UNUSED(tag);
50 #endif
51 return 0;
52 }
53
startSoundSequence(Polyobj * poEmitter)54 static void startSoundSequence(Polyobj *poEmitter)
55 {
56 #if __JHEXEN__
57 if (poEmitter)
58 {
59 SN_StartSequence((mobj_t *)poEmitter, SEQ_DOOR_STONE + poEmitter->seqType);
60 }
61 #else
62 DENG_UNUSED(poEmitter);
63 #endif
64 }
65
stopSoundSequence(Polyobj * poEmitter)66 static void stopSoundSequence(Polyobj *poEmitter)
67 {
68 #if __JHEXEN__
69 SN_StopSequence((mobj_t *)poEmitter);
70 #else
71 DENG_UNUSED(poEmitter);
72 #endif
73 }
74
PO_SetDestination(Polyobj * po,coord_t dist,uint fineAngle,float speed)75 static void PO_SetDestination(Polyobj *po, coord_t dist, uint fineAngle, float speed)
76 {
77 DENG_ASSERT(po != 0);
78 DENG_ASSERT(fineAngle < FINEANGLES);
79 po->dest[VX] = po->origin[VX] + dist * FIX2FLT(finecosine[fineAngle]);
80 po->dest[VY] = po->origin[VY] + dist * FIX2FLT(finesine[fineAngle]);
81 po->speed = speed;
82 }
83
PODoor_UpdateDestination(polydoor_t * pd)84 static void PODoor_UpdateDestination(polydoor_t *pd)
85 {
86 DENG2_ASSERT(pd != 0);
87 Polyobj *po = Polyobj_ByTag(pd->polyobj);
88
89 // Only sliding doors need the destination info. (Right? -jk)
90 if(pd->type == PODOOR_SLIDE)
91 {
92 PO_SetDestination(po, FIX2FLT(pd->dist), pd->direction, FIX2FLT(pd->intSpeed));
93 }
94 }
95
T_RotatePoly(void * polyThinker)96 void T_RotatePoly(void *polyThinker)
97 {
98 DENG_ASSERT(polyThinker != 0);
99
100 polyevent_t *pe = (polyevent_t *)polyThinker;
101 uint absSpeed;
102 Polyobj *po = Polyobj_ByTag(pe->polyobj);
103
104 if(Polyobj_Rotate(po, pe->intSpeed))
105 {
106 absSpeed = abs(pe->intSpeed);
107
108 if(pe->dist == POBJ_PERPETUAL)
109 {
110 // perpetual polyobj.
111 return;
112 }
113
114 pe->dist -= absSpeed;
115
116 if(int(pe->dist) <= 0)
117 {
118 if(po->specialData == pe)
119 po->specialData = 0;
120
121 stopSoundSequence(po);
122 P_NotifyPolyobjFinished(po->tag);
123 Thinker_Remove(&pe->thinker);
124 po->angleSpeed = 0;
125 }
126
127 if(pe->dist < absSpeed)
128 {
129 pe->intSpeed = pe->dist * (pe->intSpeed < 0 ? -1 : 1);
130 }
131 }
132 }
133
EV_RotatePoly(Line * line,byte * args,int direction,dd_bool overRide)134 dd_bool EV_RotatePoly(Line *line, byte *args, int direction, dd_bool overRide)
135 {
136 DENG_UNUSED(line);
137 DENG_ASSERT(args != 0);
138
139 int tag = args[0];
140 Polyobj *po = Polyobj_ByTag(tag);
141 if(po)
142 {
143 if(po->specialData && !overRide)
144 { // Poly is already moving, so keep going...
145 return false;
146 }
147 }
148 else
149 {
150 Con_Error("EV_RotatePoly: Invalid polyobj tag: %d\n", tag);
151 }
152
153 polyevent_t *pe = (polyevent_t *)Z_Calloc(sizeof(*pe), PU_MAP, 0);
154 pe->thinker.function = T_RotatePoly;
155 Thinker_Add(&pe->thinker);
156
157 pe->polyobj = tag;
158
159 if(args[2])
160 {
161 if(args[2] == 255)
162 {
163 // Perpetual rotation.
164 pe->dist = POBJ_PERPETUAL;
165 po->destAngle = POBJ_PERPETUAL;
166 }
167 else
168 {
169 pe->dist = args[2] * (ANGLE_90 / 64); // Angle
170 po->destAngle = po->angle + pe->dist * direction;
171 }
172 }
173 else
174 {
175 pe->dist = ANGLE_MAX - 1;
176 po->destAngle = po->angle + pe->dist;
177 }
178
179 pe->intSpeed = (args[1] * direction * (ANGLE_90 / 64)) >> 3;
180 po->specialData = pe;
181 po->angleSpeed = pe->intSpeed;
182 startSoundSequence(po);
183
184 int mirror; // tag
185 while((mirror = findMirrorPolyobj(tag)) != 0)
186 {
187 po = Polyobj_ByTag(mirror);
188 if(po && po->specialData && !overRide)
189 {
190 // Mirroring po is already in motion.
191 break;
192 }
193
194 pe = (polyevent_t *)Z_Calloc(sizeof(*pe), PU_MAP, 0);
195 pe->thinker.function = T_RotatePoly;
196 Thinker_Add(&pe->thinker);
197
198 po->specialData = pe;
199 pe->polyobj = mirror;
200 if(args[2])
201 {
202 if(args[2] == 255)
203 {
204 pe->dist = POBJ_PERPETUAL;
205 po->destAngle = POBJ_PERPETUAL;
206 }
207 else
208 {
209 pe->dist = args[2] * (ANGLE_90 / 64); // Angle
210 po->destAngle = po->angle + pe->dist * -direction;
211 }
212 }
213 else
214 {
215 pe->dist = ANGLE_MAX - 1;
216 po->destAngle = po->angle + pe->dist;
217 }
218 direction = -direction;
219 pe->intSpeed = (args[1] * direction * (ANGLE_90 / 64)) >> 3;
220 po->angleSpeed = pe->intSpeed;
221
222 po = Polyobj_ByTag(tag);
223 if(po)
224 {
225 po->specialData = pe;
226 }
227 else
228 {
229 Con_Error("EV_RotatePoly: Invalid polyobj num: %d\n", tag);
230 }
231
232 tag = mirror;
233 startSoundSequence(po);
234 }
235 return true;
236 }
237
T_MovePoly(void * polyThinker)238 void T_MovePoly(void *polyThinker)
239 {
240 DENG_ASSERT(polyThinker != 0);
241
242 polyevent_t *pe = (polyevent_t *)polyThinker;
243 Polyobj *po = Polyobj_ByTag(pe->polyobj);
244
245 if(Polyobj_MoveXY(po, pe->speed[MX], pe->speed[MY]))
246 {
247 uint const absSpeed = abs(pe->intSpeed);
248
249 pe->dist -= absSpeed;
250 if(int(pe->dist) <= 0)
251 {
252 if(po->specialData == pe)
253 po->specialData = NULL;
254
255 stopSoundSequence(po);
256 P_NotifyPolyobjFinished(po->tag);
257 Thinker_Remove(&pe->thinker);
258 po->speed = 0;
259 }
260
261 if(pe->dist < absSpeed)
262 {
263 pe->intSpeed = pe->dist * (pe->intSpeed < 0 ? -1 : 1);
264
265 pe->speed[MX] = FIX2FLT(FixedMul(pe->intSpeed, finecosine[pe->fangle]));
266 pe->speed[MY] = FIX2FLT(FixedMul(pe->intSpeed, finesine[pe->fangle]));
267 }
268 }
269 }
270
write(MapStateWriter * msw) const271 void polyevent_s::write(MapStateWriter *msw) const
272 {
273 Writer1 *writer = msw->writer();
274
275 Writer_WriteByte(writer, 1); // Write a version byte.
276
277 // Note we don't bother to save a byte to tell if the function
278 // is present as we ALWAYS add one when loading.
279
280 Writer_WriteInt32(writer, polyobj);
281 Writer_WriteInt32(writer, intSpeed);
282 Writer_WriteUInt32(writer, dist);
283 Writer_WriteInt32(writer, fangle);
284 Writer_WriteInt32(writer, FLT2FIX(speed[VX]));
285 Writer_WriteInt32(writer, FLT2FIX(speed[VY]));
286 }
287
read(MapStateReader * msr)288 int polyevent_s::read(MapStateReader *msr)
289 {
290 Reader1 *reader = msr->reader();
291 int mapVersion = msr->mapVersion();
292
293 if(mapVersion >= 4)
294 {
295 // Note: the thinker class byte has already been read.
296 /*int ver =*/ Reader_ReadByte(reader); // version byte.
297
298 // Start of used data members.
299 polyobj = Reader_ReadInt32(reader);
300 intSpeed = Reader_ReadInt32(reader);
301 dist = Reader_ReadUInt32(reader);
302 fangle = Reader_ReadInt32(reader);
303 speed[VX] = FIX2FLT(Reader_ReadInt32(reader));
304 speed[VY] = FIX2FLT(Reader_ReadInt32(reader));
305 }
306 else
307 {
308 // Its in the old pre V4 format which serialized polyevent_t
309 // Padding at the start (an old thinker_t struct)
310 byte junk[16]; // sizeof thinker_t
311 Reader_Read(reader, junk, 16);
312
313 // Start of used data members.
314 polyobj = Reader_ReadInt32(reader);
315 intSpeed = Reader_ReadInt32(reader);
316 dist = Reader_ReadUInt32(reader);
317 fangle = Reader_ReadInt32(reader);
318 speed[VX] = FIX2FLT(Reader_ReadInt32(reader));
319 speed[VY] = FIX2FLT(Reader_ReadInt32(reader));
320 }
321
322 thinker.function = T_RotatePoly;
323
324 return true; // Add this thinker.
325 }
326
SV_WriteMovePoly(polyevent_t const * th,MapStateWriter * msw)327 void SV_WriteMovePoly(polyevent_t const *th, MapStateWriter *msw)
328 {
329 Writer1 *writer = msw->writer();
330
331 Writer_WriteByte(writer, 1); // Write a version byte.
332
333 // Note we don't bother to save a byte to tell if the function
334 // is present as we ALWAYS add one when loading.
335
336 Writer_WriteInt32(writer, th->polyobj);
337 Writer_WriteInt32(writer, th->intSpeed);
338 Writer_WriteUInt32(writer, th->dist);
339 Writer_WriteInt32(writer, th->fangle);
340 Writer_WriteInt32(writer, FLT2FIX(th->speed[VX]));
341 Writer_WriteInt32(writer, FLT2FIX(th->speed[VY]));
342 }
343
SV_ReadMovePoly(polyevent_t * th,MapStateReader * msr)344 int SV_ReadMovePoly(polyevent_t *th, MapStateReader *msr)
345 {
346 Reader1 *reader = msr->reader();
347 int mapVersion = msr->mapVersion();
348
349 if(mapVersion >= 4)
350 {
351 // Note: the thinker class byte has already been read.
352 /*int ver =*/ Reader_ReadByte(reader); // version byte.
353
354 // Start of used data members.
355 th->polyobj = Reader_ReadInt32(reader);
356 th->intSpeed = Reader_ReadInt32(reader);
357 th->dist = Reader_ReadUInt32(reader);
358 th->fangle = Reader_ReadInt32(reader);
359 th->speed[VX] = FIX2FLT(Reader_ReadInt32(reader));
360 th->speed[VY] = FIX2FLT(Reader_ReadInt32(reader));
361 }
362 else
363 {
364 // Its in the old pre V4 format which serialized polyevent_t
365 // Padding at the start (an old thinker_t struct)
366 byte junk[16]; // sizeof thinker_t
367 Reader_Read(reader, junk, 16);
368
369 // Start of used data members.
370 th->polyobj = Reader_ReadInt32(reader);
371 th->intSpeed = Reader_ReadInt32(reader);
372 th->dist = Reader_ReadUInt32(reader);
373 th->fangle = Reader_ReadInt32(reader);
374 th->speed[VX] = FIX2FLT(Reader_ReadInt32(reader));
375 th->speed[VY] = FIX2FLT(Reader_ReadInt32(reader));
376 }
377
378 th->thinker.function = T_MovePoly;
379
380 return true; // Add this thinker.
381 }
382
EV_MovePoly(Line * line,byte * args,dd_bool timesEight,dd_bool override)383 dd_bool EV_MovePoly(Line *line, byte *args, dd_bool timesEight, dd_bool override)
384 {
385 DENG_UNUSED(line);
386 DENG_ASSERT(args != 0);
387
388 int tag = args[0];
389 Polyobj *po = Polyobj_ByTag(tag);
390 DENG_ASSERT(po != 0);
391
392 // Already moving?
393 if(po->specialData && !override)
394 return false;
395
396 polyevent_t *pe = (polyevent_t *)Z_Calloc(sizeof(*pe), PU_MAP, 0);
397 pe->thinker.function = T_MovePoly;
398 Thinker_Add(&pe->thinker);
399
400 pe->polyobj = tag;
401 if(timesEight)
402 {
403 pe->dist = args[3] * 8 * FRACUNIT;
404 }
405 else
406 {
407 pe->dist = args[3] * FRACUNIT; // Distance
408 }
409 pe->intSpeed = args[1] * (FRACUNIT / 8);
410 po->specialData = pe;
411
412 angle_t angle = args[2] * (ANGLE_90 / 64);
413
414 pe->fangle = angle >> ANGLETOFINESHIFT;
415 pe->speed[MX] = FIX2FLT(FixedMul(pe->intSpeed, finecosine[pe->fangle]));
416 pe->speed[MY] = FIX2FLT(FixedMul(pe->intSpeed, finesine[pe->fangle]));
417 startSoundSequence(po);
418
419 PO_SetDestination(po, FIX2FLT(pe->dist), pe->fangle, FIX2FLT(pe->intSpeed));
420
421 int mirror; // tag
422 while((mirror = findMirrorPolyobj(tag)) != 0)
423 {
424 po = Polyobj_ByTag(mirror);
425
426 // Is the mirror already in motion?
427 if(po && po->specialData && !override)
428 break;
429
430 pe = (polyevent_t *)Z_Calloc(sizeof(*pe), PU_MAP, 0);
431 pe->thinker.function = T_MovePoly;
432 Thinker_Add(&pe->thinker);
433
434 pe->polyobj = mirror;
435 po->specialData = pe;
436 if(timesEight)
437 {
438 pe->dist = args[3] * 8 * FRACUNIT;
439 }
440 else
441 {
442 pe->dist = args[3] * FRACUNIT; // Distance
443 }
444 pe->intSpeed = args[1] * (FRACUNIT / 8);
445 angle = angle + ANGLE_180; // reverse the angle
446 pe->fangle = angle >> ANGLETOFINESHIFT;
447 pe->speed[MX] = FIX2FLT(FixedMul(pe->intSpeed, finecosine[pe->fangle]));
448 pe->speed[MY] = FIX2FLT(FixedMul(pe->intSpeed, finesine[pe->fangle]));
449 tag = mirror;
450 startSoundSequence(po);
451
452 PO_SetDestination(po, FIX2FLT(pe->dist), pe->fangle, FIX2FLT(pe->intSpeed));
453 }
454 return true;
455 }
456
T_PolyDoor(void * polyDoorThinker)457 void T_PolyDoor(void *polyDoorThinker)
458 {
459 DENG_ASSERT(polyDoorThinker != 0);
460
461 polydoor_t *pd = (polydoor_t *)polyDoorThinker;
462 Polyobj *po = Polyobj_ByTag(pd->polyobj);
463
464 if(pd->tics)
465 {
466 if(!--pd->tics)
467 {
468 startSoundSequence(po);
469
470 // Movement is about to begin. Update the destination.
471 PODoor_UpdateDestination(pd);
472 }
473 return;
474 }
475
476 switch(pd->type)
477 {
478 case PODOOR_SLIDE:
479 if(Polyobj_MoveXY(po, pd->speed[MX], pd->speed[MY]))
480 {
481 int absSpeed = abs(pd->intSpeed);
482 pd->dist -= absSpeed;
483 if(pd->dist <= 0)
484 {
485 stopSoundSequence(po);
486 if(!pd->close)
487 {
488 pd->dist = pd->totalDist;
489 pd->close = true;
490 pd->tics = pd->waitTics;
491 pd->direction = (ANGLE_MAX >> ANGLETOFINESHIFT) - pd->direction;
492 pd->speed[MX] = -pd->speed[MX];
493 pd->speed[MY] = -pd->speed[MY];
494 }
495 else
496 {
497 if(po->specialData == pd)
498 po->specialData = NULL;
499
500 P_NotifyPolyobjFinished(po->tag);
501 Thinker_Remove(&pd->thinker);
502 }
503 }
504 }
505 else
506 {
507 if(po->crush || !pd->close)
508 {
509 // Continue moving if the po is a crusher, or is opening.
510 return;
511 }
512 else
513 {
514 // Open back up.
515 pd->dist = pd->totalDist - pd->dist;
516 pd->direction = (ANGLE_MAX >> ANGLETOFINESHIFT) - pd->direction;
517 pd->speed[MX] = -pd->speed[MX];
518 pd->speed[MY] = -pd->speed[MY];
519 // Update destination.
520 PODoor_UpdateDestination(pd);
521 pd->close = false;
522 startSoundSequence(po);
523 }
524 }
525 break;
526
527 case PODOOR_SWING:
528 if(Polyobj_Rotate(po, pd->intSpeed))
529 {
530 int absSpeed = abs(pd->intSpeed);
531 if(pd->dist == -1)
532 {
533 // Perpetual polyobj.
534 return;
535 }
536
537 pd->dist -= absSpeed;
538 if(pd->dist <= 0)
539 {
540 stopSoundSequence(po);
541 if(!pd->close)
542 {
543 pd->dist = pd->totalDist;
544 pd->close = true;
545 pd->tics = pd->waitTics;
546 pd->intSpeed = -pd->intSpeed;
547 }
548 else
549 {
550 if(po->specialData == pd)
551 po->specialData = NULL;
552
553 P_NotifyPolyobjFinished(po->tag);
554 Thinker_Remove(&pd->thinker);
555 }
556 }
557 }
558 else
559 {
560 if(po->crush || !pd->close)
561 {
562 // Continue moving if the po is a crusher, or is opening.
563 return;
564 }
565 else
566 {
567 // Open back up and rewait.
568 pd->dist = pd->totalDist - pd->dist;
569 pd->intSpeed = -pd->intSpeed;
570 pd->close = false;
571 startSoundSequence(po);
572 }
573 }
574 break;
575
576 default:
577 break;
578 }
579 }
580
write(MapStateWriter * msw) const581 void polydoor_s::write(MapStateWriter *msw) const
582 {
583 Writer1 *writer = msw->writer();
584
585 Writer_WriteByte(writer, 1); // Write a version byte.
586
587 Writer_WriteByte(writer, type);
588
589 // Note we don't bother to save a byte to tell if the function
590 // is present as we ALWAYS add one when loading.
591
592 Writer_WriteInt32(writer, polyobj);
593 Writer_WriteInt32(writer, intSpeed);
594 Writer_WriteInt32(writer, dist);
595 Writer_WriteInt32(writer, totalDist);
596 Writer_WriteInt32(writer, direction);
597 Writer_WriteInt32(writer, FLT2FIX(speed[VX]));
598 Writer_WriteInt32(writer, FLT2FIX(speed[VY]));
599 Writer_WriteInt32(writer, tics);
600 Writer_WriteInt32(writer, waitTics);
601 Writer_WriteByte(writer, close);
602 }
603
read(MapStateReader * msr)604 int polydoor_s::read(MapStateReader *msr)
605 {
606 Reader1 *reader = msr->reader();
607 int mapVersion = msr->mapVersion();
608
609 if(mapVersion >= 4)
610 {
611 // Note: the thinker class byte has already been read.
612 /*int ver =*/ Reader_ReadByte(reader); // version byte.
613
614 // Start of used data members.
615 type = podoortype_t(Reader_ReadByte(reader));
616 polyobj = Reader_ReadInt32(reader);
617 intSpeed = Reader_ReadInt32(reader);
618 dist = Reader_ReadInt32(reader);
619 totalDist = Reader_ReadInt32(reader);
620 direction = Reader_ReadInt32(reader);
621 speed[VX] = FIX2FLT(Reader_ReadInt32(reader));
622 speed[VY] = FIX2FLT(Reader_ReadInt32(reader));
623 tics = Reader_ReadInt32(reader);
624 waitTics = Reader_ReadInt32(reader);
625 close = Reader_ReadByte(reader);
626 }
627 else
628 {
629 // Its in the old pre V4 format which serialized polydoor_t
630 // Padding at the start (an old thinker_t struct)
631 byte junk[16]; // sizeof thinker_t
632 Reader_Read(reader, junk, 16);
633
634 // Start of used data members.
635 polyobj = Reader_ReadInt32(reader);
636 intSpeed = Reader_ReadInt32(reader);
637 dist = Reader_ReadInt32(reader);
638 totalDist = Reader_ReadInt32(reader);
639 direction = Reader_ReadInt32(reader);
640 speed[VX] = FIX2FLT(Reader_ReadInt32(reader));
641 speed[VY] = FIX2FLT(Reader_ReadInt32(reader));
642 tics = Reader_ReadInt32(reader);
643 waitTics = Reader_ReadInt32(reader);
644 type = podoortype_t(Reader_ReadByte(reader));
645 close = Reader_ReadByte(reader);
646 }
647
648 thinker.function = T_PolyDoor;
649
650 return true; // Add this thinker.
651 }
652
EV_OpenPolyDoor(Line * line,byte * args,podoortype_t type)653 dd_bool EV_OpenPolyDoor(Line *line, byte *args, podoortype_t type)
654 {
655 DENG_UNUSED(line);
656 DENG_ASSERT(args != 0);
657
658 int tag = args[0];
659 Polyobj *po = Polyobj_ByTag(tag);
660 if(po)
661 {
662 if(po->specialData)
663 { // Is already moving.
664 return false;
665 }
666 }
667 else
668 {
669 Con_Error("EV_OpenPolyDoor: Invalid polyobj num: %d\n", tag);
670 }
671
672 polydoor_t *pd = (polydoor_t *)Z_Calloc(sizeof(*pd), PU_MAP, 0);
673 pd->thinker.function = T_PolyDoor;
674 Thinker_Add(&pd->thinker);
675
676 angle_t angle = 0;
677
678 pd->type = type;
679 pd->polyobj = tag;
680 if(type == PODOOR_SLIDE)
681 {
682 pd->waitTics = args[4];
683 pd->intSpeed = args[1] * (FRACUNIT / 8);
684 pd->totalDist = args[3] * FRACUNIT; // Distance.
685 pd->dist = pd->totalDist;
686 angle = args[2] * (ANGLE_90 / 64);
687 pd->direction = angle >> ANGLETOFINESHIFT;
688 pd->speed[MX] = FIX2FLT(FixedMul(pd->intSpeed, finecosine[pd->direction]));
689 pd->speed[MY] = FIX2FLT(FixedMul(pd->intSpeed, finesine[pd->direction]));
690 startSoundSequence(po);
691 }
692 else if(type == PODOOR_SWING)
693 {
694 pd->waitTics = args[3];
695 pd->direction = 1;
696 pd->intSpeed = (args[1] * pd->direction * (ANGLE_90 / 64)) >> 3;
697 pd->totalDist = args[2] * (ANGLE_90 / 64);
698 pd->dist = pd->totalDist;
699 startSoundSequence(po);
700 }
701
702 po->specialData = pd;
703 PODoor_UpdateDestination(pd);
704
705 int mirror; // tag
706 while((mirror = findMirrorPolyobj(tag)) != 0)
707 {
708 po = Polyobj_ByTag(mirror);
709 if(po && po->specialData)
710 {
711 // Mirroring po is already in motion.
712 break;
713 }
714
715 pd = (polydoor_t *)Z_Calloc(sizeof(*pd), PU_MAP, 0);
716 pd->thinker.function = T_PolyDoor;
717 Thinker_Add(&pd->thinker);
718
719 pd->polyobj = mirror;
720 pd->type = type;
721 po->specialData = pd;
722 if(type == PODOOR_SLIDE)
723 {
724 pd->waitTics = args[4];
725 pd->intSpeed = args[1] * (FRACUNIT / 8);
726 pd->totalDist = args[3] * FRACUNIT; // Distance.
727 pd->dist = pd->totalDist;
728 angle = angle + ANGLE_180; // Reverse the angle.
729 pd->direction = angle >> ANGLETOFINESHIFT;
730 pd->speed[MX] = FIX2FLT(FixedMul(pd->intSpeed, finecosine[pd->direction]));
731 pd->speed[MY] = FIX2FLT(FixedMul(pd->intSpeed, finesine[pd->direction]));
732 startSoundSequence(po);
733 }
734 else if(type == PODOOR_SWING)
735 {
736 pd->waitTics = args[3];
737 pd->direction = -1;
738 pd->intSpeed = (args[1] * pd->direction * (ANGLE_90 / 64)) >> 3;
739 pd->totalDist = args[2] * (ANGLE_90 / 64);
740 pd->dist = pd->totalDist;
741 startSoundSequence(po);
742 }
743 tag = mirror;
744 PODoor_UpdateDestination(pd);
745 }
746
747 return true;
748 }
749
thrustMobj(struct mobj_s * mo,void * linep,void * pop)750 static void thrustMobj(struct mobj_s *mo, void *linep, void *pop)
751 {
752 Line *line = (Line *) linep;
753 Polyobj *po = (Polyobj *) pop;
754 coord_t thrust[2], force;
755 polyevent_t *pe;
756 uint thrustAn;
757
758 // Clients do no polyobj <-> mobj interaction.
759 if(IS_CLIENT) return;
760
761 // Cameras don't interact with polyobjs.
762 if(P_MobjIsCamera(mo)) return;
763
764 if(!(mo->flags & MF_SHOOTABLE) && !mo->player) return;
765
766 thrustAn = (P_GetAnglep(line, DMU_ANGLE) - ANGLE_90) >> ANGLETOFINESHIFT;
767
768 pe = (polyevent_t *) po->specialData;
769 if(pe)
770 {
771 if(pe->thinker.function == (thinkfunc_t) T_RotatePoly)
772 {
773 force = FIX2FLT(pe->intSpeed >> 8);
774 }
775 else
776 {
777 force = FIX2FLT(pe->intSpeed >> 3);
778 }
779
780 force = MINMAX_OF(1, force, 4);
781 }
782 else
783 {
784 force = 1;
785 }
786
787 thrust[VX] = force * FIX2FLT(finecosine[thrustAn]);
788 thrust[VY] = force * FIX2FLT(finesine[thrustAn]);
789 mo->mom[MX] += thrust[VX];
790 mo->mom[MY] += thrust[VY];
791
792 if(po->crush)
793 {
794 if(!P_CheckPositionXY(mo, mo->origin[VX] + thrust[VX], mo->origin[VY] + thrust[VY]))
795 {
796 P_DamageMobj(mo, NULL, NULL, 3, false);
797 }
798 }
799 }
800
PO_InitForMap()801 void PO_InitForMap()
802 {
803 #if !defined(__JHEXEN__)
804 return; // Disabled -- awaiting line argument translation.
805 #endif
806
807 App_Log(DE2_DEV_MAP_VERBOSE, "Initializing polyobjects for map...");
808
809 // thrustMobj will handle polyobj <-> mobj interaction.
810 Polyobj_SetCallback(thrustMobj);
811
812 for(int i = 0; i < numpolyobjs; ++i)
813 {
814 Polyobj *po = Polyobj_ById(i);
815
816 // Init game-specific properties.
817 po->specialData = NULL;
818
819 // Find the mapspot associated with this polyobj.
820 uint j = 0;
821 mapspot_t const *spot = 0;
822 while(j < numMapSpots && !spot)
823 {
824 if((mapSpots[j].doomEdNum == PO_SPAWN_DOOMEDNUM ||
825 mapSpots[j].doomEdNum == PO_SPAWNCRUSH_DOOMEDNUM) &&
826 mapSpots[j].angle == angle_t(po->tag))
827 {
828 // Polyobj mapspot.
829 spot = &mapSpots[j];
830 }
831 else
832 {
833 j++;
834 }
835 }
836
837 if(spot)
838 {
839 po->crush = (spot->doomEdNum == PO_SPAWNCRUSH_DOOMEDNUM? 1 : 0);
840 Polyobj_MoveXY(po, -po->origin[VX] + spot->origin[VX],
841 -po->origin[VY] + spot->origin[VY]);
842 }
843 else
844 {
845 App_Log(DE2_MAP_WARNING, "Missing spawn spot for PolyObj #%i", i);
846 }
847 }
848 }
849
PO_Busy(int tag)850 dd_bool PO_Busy(int tag)
851 {
852 Polyobj *po = Polyobj_ByTag(tag);
853 return (po && po->specialData);
854 }
855
write(MapStateWriter * msw) const856 void polyobj_s::write(MapStateWriter *msw) const
857 {
858 Writer1 *writer = msw->writer();
859
860 Writer_WriteByte(writer, 1); // write a version byte (unused).
861
862 Writer_WriteInt32(writer, tag);
863 Writer_WriteInt32(writer, angle);
864 Writer_WriteInt32(writer, FLT2FIX(origin[VX]));
865 Writer_WriteInt32(writer, FLT2FIX(origin[VY]));
866 }
867
read(MapStateReader * msr)868 int polyobj_s::read(MapStateReader *msr)
869 {
870 Reader1 *reader = msr->reader();
871
872 angle_t newAngle = angle_t(Reader_ReadInt32(reader));
873 Polyobj_Rotate(this, newAngle);
874 destAngle = newAngle;
875
876 coord_t newOrigin[2];
877 newOrigin[0] = FIX2FLT(Reader_ReadInt32(reader));
878 newOrigin[1] = FIX2FLT(Reader_ReadInt32(reader));
879 Polyobj_MoveXY(this, newOrigin[0] - origin[0], newOrigin[1] - origin[1]);
880
881 /// @todo What about speed? It isn't saved at all?
882
883 return true;
884 }
885