1 /** @file p_telept.c
2  *
3  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  * @authors Copyright © 2006-2013 Daniel Swanson <danij@dengine.net>
5  * @authors Copyright © 2003-2005 Samuel Villarreal <svkaiser@gmail.com>
6  * @authors Copyright © 1993-1996 by id Software, Inc.
7  *
8  * @par License
9  * GPL: http://www.gnu.org/licenses/gpl.html
10  *
11  * <small>This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by the
13  * Free Software Foundation; either version 2 of the License, or (at your
14  * option) any later version. This program is distributed in the hope that it
15  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
17  * Public License for more details. You should have received a copy of the GNU
18  * General Public License along with this program; if not, write to the Free
19  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA</small>
21  */
22 
23 #include <stdio.h>
24 #include <string.h>
25 
26 #include "jdoom64.h"
27 
28 #include "dmu_lib.h"
29 #include "p_mapsetup.h"
30 #include "p_map.h"
31 #include "mobj.h"
32 #include "p_actor.h"
33 #include "p_mapspec.h"
34 #include "p_terraintype.h"
35 #include "p_start.h"
36 
P_SpawnTeleFog(coord_t x,coord_t y,angle_t angle)37 mobj_t *P_SpawnTeleFog(coord_t x, coord_t y, angle_t angle)
38 {
39     return P_SpawnMobjXYZ(MT_TFOG, x, y, TELEFOGHEIGHT, angle, MSF_Z_FLOOR);
40 }
41 
42 typedef struct {
43     Sector *sec;
44     mobjtype_t type;
45     mobj_t *foundMobj;
46 } findmobjparams_t;
47 
findMobj(thinker_t * th,void * context)48 static int findMobj(thinker_t *th, void *context)
49 {
50     findmobjparams_t *params = (findmobjparams_t *) context;
51     mobj_t *mo = (mobj_t *) th;
52 
53     // Must be of the correct type?
54     if(params->type >= 0 && params->type != mo->type)
55         return false; // Continue iteration.
56 
57     // Must be in the specified sector?
58     if(params->sec && params->sec != Mobj_Sector(mo))
59         return false; // Continue iteration.
60 
61     // Found it!
62     params->foundMobj = mo;
63     return true; // Stop iteration.
64 }
65 
getTeleportDestination(short tag)66 static mobj_t* getTeleportDestination(short tag)
67 {
68     iterlist_t* list;
69 
70     list = P_GetSectorIterListForTag(tag, false);
71     if(list)
72     {
73         Sector* sec = NULL;
74         findmobjparams_t params;
75 
76         params.type = MT_TELEPORTMAN;
77         params.foundMobj = NULL;
78 
79         IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
80         IterList_RewindIterator(list);
81         while((sec = IterList_MoveIterator(list)) != NULL)
82         {
83             params.sec = sec;
84 
85             if(Thinker_Iterate(P_MobjThinker, findMobj, &params))
86             {   // Found one.
87                 return params.foundMobj;
88             }
89         }
90     }
91 
92     return NULL;
93 }
94 
EV_Teleport(Line * line,int side,mobj_t * mo,dd_bool spawnFog)95 int EV_Teleport(Line* line, int side, mobj_t* mo, dd_bool spawnFog)
96 {
97     mobj_t* dest;
98 
99     // Clients cannot teleport on their own.
100     if(IS_CLIENT) return 0;
101 
102     if(mo->flags2 & MF2_NOTELEPORT) return 0;
103 
104     // Don't teleport if hit back of line, so you can get out of teleporter.
105     if(side == 1) return 0;
106 
107     if((dest = getTeleportDestination(P_ToXLine(line)->tag)) != NULL)
108     {
109         // A suitable destination has been found.
110         coord_t oldPos[3], aboveFloor;
111         angle_t oldAngle;
112         mobj_t* fog;
113         uint an;
114 
115         memcpy(oldPos, mo->origin, sizeof(mo->origin));
116         oldAngle = mo->angle;
117         aboveFloor = mo->origin[VZ] - mo->floorZ;
118 
119         if(!P_TeleportMove(mo, dest->origin[VX], dest->origin[VY], false))
120             return 0;
121 
122         mo->origin[VZ] = mo->floorZ;
123 
124         if(spawnFog)
125         {
126             // Spawn teleport fog at source and destination.
127             if((fog = P_SpawnMobj(MT_TFOG, oldPos, oldAngle + ANG180, 0)))
128                 S_StartSound(SFX_TELEPT, fog);
129 
130             an = dest->angle >> ANGLETOFINESHIFT;
131             if((fog = P_SpawnMobjXYZ(MT_TFOG, dest->origin[VX] + 20 * FIX2FLT(finecosine[an]),
132                                               dest->origin[VY] + 20 * FIX2FLT(finesine[an]),
133                                               mo->origin[VZ], dest->angle + ANG180, 0)))
134             {
135                 // Emit sound, where?
136                 S_StartSound(SFX_TELEPT, fog);
137             }
138         }
139 
140         mo->angle = dest->angle;
141         if(mo->flags2 & MF2_FLOORCLIP)
142         {
143             mo->floorClip = 0;
144 
145             if(FEQUAL(mo->origin[VZ], P_GetDoublep(Mobj_Sector(mo), DMU_FLOOR_HEIGHT)))
146             {
147                 terraintype_t const *tt = P_MobjFloorTerrain(mo);
148                 if(tt->flags & TTF_FLOORCLIP)
149                 {
150                     mo->floorClip = 10;
151                 }
152             }
153         }
154 
155         mo->mom[MX] = mo->mom[MY] = mo->mom[MZ] = 0;
156 
157         // $voodoodolls Must be the real player.
158         if(mo->player && mo->player->plr->mo == mo)
159         {
160             mo->reactionTime = 18; // Don't move for a bit.
161             if(mo->player->powers[PT_FLIGHT] && aboveFloor > 0)
162             {
163                 mo->origin[VZ] = mo->floorZ + aboveFloor;
164                 if(mo->origin[VZ] + mo->height > mo->ceilingZ)
165                 {
166                     mo->origin[VZ] = mo->ceilingZ - mo->height;
167                 }
168             }
169             else
170             {
171                 //mo->dPlayer->clLookDir = 0; /* $unifiedangles */
172                 mo->dPlayer->lookDir = 0;
173             }
174             mo->player->viewHeight = (coord_t) cfg.common.plrViewHeight;
175             mo->player->viewHeightDelta = 0;
176             mo->player->viewZ = mo->origin[VZ] + mo->player->viewHeight;
177             mo->player->viewOffset[VX] = mo->player->viewOffset[VY] = mo->player->viewOffset[VZ] = 0;
178             mo->player->bob = 0;
179 
180             //mo->dPlayer->clAngle = mo->angle; /* $unifiedangles */
181             mo->dPlayer->flags |= DDPF_FIXANGLES | DDPF_FIXORIGIN | DDPF_FIXMOM;
182         }
183 
184         return 1;
185     }
186 
187     return 0;
188 }
189 
190 /**
191  * d64tc
192  * If the given doomed number is a type which fade spawns, this will return
193  * the corresponding mobjtype. Else -1.
194  *
195  * DJS - Added in order to cleanup EV_FadeSpawn() somewhat.
196  * \todo This is still far from ideal. An MF*_* flag would be better.
197  */
isFadeSpawner(int doomEdNum)198 static mobjtype_t isFadeSpawner(int doomEdNum)
199 {
200     typedef struct fadespawner_s {
201         int doomEdNum;
202         mobjtype_t type;
203     } fadespawner_t;
204 
205     static const fadespawner_t  spawners[] =
206     {
207         {7575, MT_SHOTGUN},
208         {7576, MT_CHAINGUN},
209         {7577, MT_SUPERSHOTGUN},
210         {7578, MT_MISC27},
211         {7579, MT_MISC28},
212         {7580, MT_MISC25},
213         {7581, MT_MISC11},
214         {7582, MT_MISC10},
215         {7583, MT_MISC0},
216         {7584, MT_MISC1},
217         {7585, MT_LASERGUN},
218         {7586, MT_LPOWERUP1},
219         {7587, MT_LPOWERUP2},
220         {7588, MT_LPOWERUP3},
221         {7589, MT_MEGA},
222         {7590, MT_MISC12},
223         {7591, MT_INS},
224         {7592, MT_INV},
225         {7593, MT_MISC13},
226         {7594, MT_MISC2},
227         {7595, MT_MISC3},
228         {7596, MT_MISC15},
229         {7597, MT_MISC16},
230         {7598, MT_MISC14},
231         {7599, MT_MISC22},
232         {7600, MT_MISC23},
233         {7601, MT_CLIP},
234         {7602, MT_MISC17},
235         {7603, MT_MISC18},
236         {7604, MT_MISC19},
237         {7605, MT_MISC20},
238         {7606, MT_MISC21},
239         {7607, MT_MISC24},
240         {7608, MT_POSSESSED},
241         {7609, MT_SHOTGUY},
242         {7610, MT_TROOP},
243         {7611, MT_NTROOP},
244         {7612, MT_SERGEANT},
245         {7613, MT_SHADOWS},
246         {7615, MT_HEAD},
247         {7617, MT_SKULL},
248         {7618, MT_PAIN},
249         {7619, MT_FATSO},
250         {7620, MT_BABY},
251         {7621, MT_CYBORG},
252         {7622, MT_BITCH},
253         {7623, MT_KNIGHT},
254         {7624, MT_BRUISER},
255         {7625, MT_MISC5},
256         {7626, MT_MISC8},
257         {7627, MT_MISC4},
258         {7628, MT_MISC9},
259         {7629, MT_MISC6},
260         {7630, MT_MISC7},
261         {-1, -1} // Terminator.
262     };
263     int                 i;
264 
265     for(i = 0; spawners[i].doomEdNum; ++i)
266         if(spawners[i].doomEdNum == doomEdNum)
267             return spawners[i].type;
268 
269     return -1;
270 }
271 
272 typedef struct {
273     Sector *sec;
274     coord_t spawnHeight;
275 } fadespawnparams_t;
276 
fadeSpawn(thinker_t * th,void * context)277 static int fadeSpawn(thinker_t *th, void *context)
278 {
279     fadespawnparams_t *params = (fadespawnparams_t *) context;
280     mobj_t *origin = (mobj_t *) th;
281     mobjtype_t spawntype;
282 
283     if(params->sec && params->sec != Mobj_Sector(origin))
284         return false; // Continue iteration.
285 
286     // Only fade spawn origins of a certain type.
287     spawntype = isFadeSpawner(origin->info->doomEdNum);
288     if(spawntype != -1)
289     {
290         coord_t pos[3];
291         angle_t an;
292         mobj_t *mo;
293 
294         an = origin->angle >> ANGLETOFINESHIFT;
295 
296         memcpy(pos, origin->origin, sizeof(pos));
297         pos[VX] += 20 * FIX2FLT(finecosine[an]);
298         pos[VY] += 20 * FIX2FLT(finesine[an]);
299         pos[VZ] = params->spawnHeight;
300 
301         if((mo = P_SpawnMobj(spawntype, pos, origin->angle, 0)))
302         {
303             mo->translucency = 255;
304             mo->spawnFadeTics = 0;
305             mo->intFlags |= MIF_FADE;
306 
307             // Emit sound, where?
308             S_StartSound(SFX_ITMBK, mo);
309 
310             if(MOBJINFO[spawntype].flags & MF_COUNTKILL)
311                 totalKills++;
312         }
313     }
314 
315     return false; // Continue iteration.
316 }
317 
318 /**
319  * d64tc
320  * kaiser - This sets a thing spawn depending on thing type placed in
321  *       tagged sector.
322  * \todo DJS - This is not a good design. There must be a better way
323  *       to do this using a new thing flag (MF_NOTSPAWNONSTART?).
324  */
EV_FadeSpawn(Line * li,mobj_t * mo)325 int EV_FadeSpawn(Line* li, mobj_t* mo)
326 {
327     iterlist_t*         list;
328 
329     list = P_GetSectorIterListForTag(P_ToXLine(li)->tag, false);
330     if(list)
331     {
332         Sector*             sec;
333         fadespawnparams_t   params;
334 
335         params.spawnHeight = mo->origin[VZ];
336 
337         IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
338         IterList_RewindIterator(list);
339         while((sec = IterList_MoveIterator(list)) != NULL)
340         {
341             params.sec = sec;
342             Thinker_Iterate(P_MobjThinker, fadeSpawn, &params);
343         }
344     }
345 
346     return 0;
347 }
348 
349 // Bitwise ops (for changeMobjFlags)
350 typedef enum {
351     BW_CLEAR,
352     BW_SET,
353     BW_XOR
354 } bitwiseop_t;
355 
356 typedef struct {
357     Sector *sec;
358     dd_bool notPlayers;
359     int flags;
360     bitwiseop_t op;
361 } pit_changemobjflagsparams_t;
362 
PIT_ChangeMobjFlags(thinker_t * th,void * context)363 int PIT_ChangeMobjFlags(thinker_t *th, void *context)
364 {
365     pit_changemobjflagsparams_t *parm = (pit_changemobjflagsparams_t *) context;
366     mobj_t *mo = (mobj_t *) th;
367 
368     if(parm->sec && parm->sec != Mobj_Sector(mo))
369         return false;
370 
371     if(parm->notPlayers && mo->player)
372         return false;
373 
374     switch(parm->op)
375     {
376     case BW_CLEAR:
377         mo->flags &= ~parm->flags;
378         break;
379 
380     case BW_SET:
381         mo->flags |= parm->flags;
382         break;
383 
384     case BW_XOR:
385         mo->flags ^= parm->flags;
386         break;
387 
388     default:
389         DENG_ASSERT(false);
390         break;
391     }
392 
393     return false; // Continue iteration.
394 }
395 
396 /**
397  * d64tc
398  * kaiser - removes things in tagged sector!
399  * DJS - actually, no it doesn't at least not directly.
400  *
401  * @todo fixme: It appears the MF_TELEPORT flag has been hijacked.
402  */
EV_FadeAway(Line * line,mobj_t * thing)403 int EV_FadeAway(Line *line, mobj_t *thing)
404 {
405     iterlist_t *list;
406 
407     DENG_UNUSED(thing);
408 
409     if(!line) return 0;
410 
411     if((list = P_GetSectorIterListForTag(P_ToXLine(line)->tag, false)) != 0)
412     {
413         Sector *sec = 0;
414         pit_changemobjflagsparams_t parm;
415 
416         parm.flags      = MF_TELEPORT;
417         parm.op         = BW_SET;
418         parm.notPlayers = true;
419 
420         IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
421         IterList_RewindIterator(list);
422         while((sec = IterList_MoveIterator(list)) != NULL)
423         {
424             parm.sec = sec;
425             Thinker_Iterate(P_MobjThinker, PIT_ChangeMobjFlags, &parm);
426         }
427     }
428 
429     return 0;
430 }
431