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, ¶ms))
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, ¶ms);
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