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