1 /*
2 ** p_things.cpp
3 ** ACS-accessible thing utilities
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2007 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 ** notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 ** notice, this list of conditions and the following disclaimer in the
17 ** documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 ** derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34
35 #include "doomtype.h"
36 #include "p_local.h"
37 #include "info.h"
38 #include "s_sound.h"
39 #include "tables.h"
40 #include "doomstat.h"
41 #include "m_random.h"
42 #include "c_console.h"
43 #include "c_dispatch.h"
44 #include "a_sharedglobal.h"
45 #include "gi.h"
46 #include "templates.h"
47 #include "g_level.h"
48 #include "v_text.h"
49 #include "i_system.h"
50
51 // Set of spawnable things for the Thing_Spawn and Thing_Projectile specials.
52 FClassMap SpawnableThings;
53
54 static FRandom pr_leadtarget ("LeadTarget");
55
P_Thing_Spawn(int tid,AActor * source,int type,angle_t angle,bool fog,int newtid)56 bool P_Thing_Spawn (int tid, AActor *source, int type, angle_t angle, bool fog, int newtid)
57 {
58 int rtn = 0;
59 const PClass *kind;
60 AActor *spot, *mobj;
61 FActorIterator iterator (tid);
62
63 kind = P_GetSpawnableType(type);
64
65 if (kind == NULL)
66 return false;
67
68 // Handle decorate replacements.
69 kind = kind->GetReplacement();
70
71 if ((GetDefaultByType (kind)->flags3 & MF3_ISMONSTER) &&
72 ((dmflags & DF_NO_MONSTERS) || (level.flags2 & LEVEL2_NOMONSTERS)))
73 return false;
74
75 if (tid == 0)
76 {
77 spot = source;
78 }
79 else
80 {
81 spot = iterator.Next();
82 }
83 while (spot != NULL)
84 {
85 mobj = Spawn (kind, spot->Pos(), ALLOW_REPLACE);
86
87 if (mobj != NULL)
88 {
89 ActorFlags2 oldFlags2 = mobj->flags2;
90 mobj->flags2 |= MF2_PASSMOBJ;
91 if (P_TestMobjLocation (mobj))
92 {
93 rtn++;
94 mobj->angle = (angle != ANGLE_MAX ? angle : spot->angle);
95 if (fog)
96 {
97 P_SpawnTeleportFog(mobj, spot->X(), spot->Y(), spot->Z() + TELEFOGHEIGHT, false, true);
98 }
99 if (mobj->flags & MF_SPECIAL)
100 mobj->flags |= MF_DROPPED; // Don't respawn
101 mobj->tid = newtid;
102 mobj->AddToHash ();
103 mobj->flags2 = oldFlags2;
104 }
105 else
106 {
107 // If this is a monster, subtract it from the total monster
108 // count, because it already added to it during spawning.
109 mobj->ClearCounters();
110 mobj->Destroy ();
111 }
112 }
113 spot = iterator.Next();
114 }
115
116 return rtn != 0;
117 }
118
119 // [BC] Added
120 // [RH] Fixed
121
P_MoveThing(AActor * source,fixed_t x,fixed_t y,fixed_t z,bool fog)122 bool P_MoveThing(AActor *source, fixed_t x, fixed_t y, fixed_t z, bool fog)
123 {
124 fixed_t oldx, oldy, oldz;
125
126 oldx = source->X();
127 oldy = source->Y();
128 oldz = source->Z();
129
130 source->SetOrigin (x, y, z, false);
131 if (P_TestMobjLocation (source))
132 {
133 if (fog)
134 {
135 P_SpawnTeleportFog(source, x, y, z, false, true);
136 P_SpawnTeleportFog(source, oldx, oldy, oldz, true, true);
137 }
138 source->PrevX = x;
139 source->PrevY = y;
140 source->PrevZ = z;
141 if (source == players[consoleplayer].camera)
142 {
143 R_ResetViewInterpolation();
144 }
145 return true;
146 }
147 else
148 {
149 source->SetOrigin (oldx, oldy, oldz, false);
150 return false;
151 }
152 }
153
P_Thing_Move(int tid,AActor * source,int mapspot,bool fog)154 bool P_Thing_Move (int tid, AActor *source, int mapspot, bool fog)
155 {
156 AActor *target;
157
158 if (tid != 0)
159 {
160 FActorIterator iterator1(tid);
161 source = iterator1.Next();
162 }
163 FActorIterator iterator2 (mapspot);
164 target = iterator2.Next ();
165
166 if (source != NULL && target != NULL)
167 {
168 return P_MoveThing(source, target->X(), target->Y(), target->Z(), fog);
169 }
170 return false;
171 }
172
P_Thing_Projectile(int tid,AActor * source,int type,const char * type_name,angle_t angle,fixed_t speed,fixed_t vspeed,int dest,AActor * forcedest,int gravity,int newtid,bool leadTarget)173 bool P_Thing_Projectile (int tid, AActor *source, int type, const char *type_name, angle_t angle,
174 fixed_t speed, fixed_t vspeed, int dest, AActor *forcedest, int gravity, int newtid,
175 bool leadTarget)
176 {
177 int rtn = 0;
178 const PClass *kind;
179 AActor *spot, *mobj, *targ = forcedest;
180 FActorIterator iterator (tid);
181 double fspeed = speed;
182 int defflags3;
183
184 if (type_name == NULL)
185 {
186 kind = P_GetSpawnableType(type);
187 }
188 else
189 {
190 kind = PClass::FindClass(type_name);
191 }
192 if (kind == NULL || kind->ActorInfo == NULL)
193 {
194 return false;
195 }
196
197 // Handle decorate replacements.
198 kind = kind->GetReplacement();
199
200 defflags3 = GetDefaultByType (kind)->flags3;
201 if ((defflags3 & MF3_ISMONSTER) &&
202 ((dmflags & DF_NO_MONSTERS) || (level.flags2 & LEVEL2_NOMONSTERS)))
203 return false;
204
205 if (tid == 0)
206 {
207 spot = source;
208 }
209 else
210 {
211 spot = iterator.Next();
212 }
213 while (spot != NULL)
214 {
215 FActorIterator tit (dest);
216
217 if (dest == 0 || (targ = tit.Next()))
218 {
219 do
220 {
221 fixed_t z = spot->Z();
222 if (defflags3 & MF3_FLOORHUGGER)
223 {
224 z = ONFLOORZ;
225 }
226 else if (defflags3 & MF3_CEILINGHUGGER)
227 {
228 z = ONCEILINGZ;
229 }
230 else if (z != ONFLOORZ)
231 {
232 z -= spot->floorclip;
233 }
234 mobj = Spawn (kind, spot->X(), spot->Y(), z, ALLOW_REPLACE);
235
236 if (mobj)
237 {
238 mobj->tid = newtid;
239 mobj->AddToHash ();
240 P_PlaySpawnSound(mobj, spot);
241 if (gravity)
242 {
243 mobj->flags &= ~MF_NOGRAVITY;
244 if (!(mobj->flags3 & MF3_ISMONSTER) && gravity == 1)
245 {
246 mobj->gravity = FRACUNIT/8;
247 }
248 }
249 else
250 {
251 mobj->flags |= MF_NOGRAVITY;
252 }
253 mobj->target = spot;
254
255 if (targ != NULL)
256 {
257 fixedvec3 vect = mobj->Vec3To(targ);
258 vect.z += targ->height / 2;
259 TVector3<double> aim(vect.x, vect.y, vect.z);
260
261 if (leadTarget && speed > 0 && (targ->velx | targ->vely | targ->velz))
262 {
263 // Aiming at the target's position some time in the future
264 // is basically just an application of the law of sines:
265 // a/sin(A) = b/sin(B)
266 // Thanks to all those on the notgod phorum for helping me
267 // with the math. I don't think I would have thought of using
268 // trig alone had I been left to solve it by myself.
269
270 TVector3<double> tvel(targ->velx, targ->vely, targ->velz);
271 if (!(targ->flags & MF_NOGRAVITY) && targ->waterlevel < 3)
272 { // If the target is subject to gravity and not underwater,
273 // assume that it isn't moving vertically. Thanks to gravity,
274 // even if we did consider the vertical component of the target's
275 // velocity, we would still miss more often than not.
276 tvel.Z = 0.0;
277 if ((targ->velx | targ->vely) == 0)
278 {
279 goto nolead;
280 }
281 }
282 double dist = aim.Length();
283 double targspeed = tvel.Length();
284 double ydotx = -aim | tvel;
285 double a = acos (clamp (ydotx / targspeed / dist, -1.0, 1.0));
286 double multiplier = double(pr_leadtarget.Random2())*0.1/255+1.1;
287 double sinb = -clamp (targspeed*multiplier * sin(a) / fspeed, -1.0, 1.0);
288
289 // Use the cross product of two of the triangle's sides to get a
290 // rotation vector.
291 TVector3<double> rv(tvel ^ aim);
292 // The vector must be normalized.
293 rv.MakeUnit();
294 // Now combine the rotation vector with angle b to get a rotation matrix.
295 TMatrix3x3<double> rm(rv, cos(asin(sinb)), sinb);
296 // And multiply the original aim vector with the matrix to get a
297 // new aim vector that leads the target.
298 TVector3<double> aimvec = rm * aim;
299 // And make the projectile follow that vector at the desired speed.
300 double aimscale = fspeed / dist;
301 mobj->velx = fixed_t (aimvec[0] * aimscale);
302 mobj->vely = fixed_t (aimvec[1] * aimscale);
303 mobj->velz = fixed_t (aimvec[2] * aimscale);
304 mobj->angle = R_PointToAngle2 (0, 0, mobj->velx, mobj->vely);
305 }
306 else
307 {
308 nolead:
309 mobj->angle = mobj->AngleTo(targ);
310 aim.Resize (fspeed);
311 mobj->velx = fixed_t(aim[0]);
312 mobj->vely = fixed_t(aim[1]);
313 mobj->velz = fixed_t(aim[2]);
314 }
315 if (mobj->flags2 & MF2_SEEKERMISSILE)
316 {
317 mobj->tracer = targ;
318 }
319 }
320 else
321 {
322 mobj->angle = angle;
323 mobj->velx = FixedMul (speed, finecosine[angle>>ANGLETOFINESHIFT]);
324 mobj->vely = FixedMul (speed, finesine[angle>>ANGLETOFINESHIFT]);
325 mobj->velz = vspeed;
326 }
327 // Set the missile's speed to reflect the speed it was spawned at.
328 if (mobj->flags & MF_MISSILE)
329 {
330 mobj->Speed = fixed_t (sqrt (double(mobj->velx)*mobj->velx + double(mobj->vely)*mobj->vely + double(mobj->velz)*mobj->velz));
331 }
332 // Hugger missiles don't have any vertical velocity
333 if (mobj->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER))
334 {
335 mobj->velz = 0;
336 }
337 if (mobj->flags & MF_SPECIAL)
338 {
339 mobj->flags |= MF_DROPPED;
340 }
341 if (mobj->flags & MF_MISSILE)
342 {
343 if (P_CheckMissileSpawn (mobj, spot->radius))
344 {
345 rtn = true;
346 }
347 }
348 else if (!P_TestMobjLocation (mobj))
349 {
350 // If this is a monster, subtract it from the total monster
351 // count, because it already added to it during spawning.
352 mobj->ClearCounters();
353 mobj->Destroy ();
354 }
355 else
356 {
357 // It spawned fine.
358 rtn = 1;
359 }
360 }
361 } while (dest != 0 && (targ = tit.Next()));
362 }
363 spot = iterator.Next();
364 }
365
366 return rtn != 0;
367 }
368
P_Thing_Damage(int tid,AActor * whofor0,int amount,FName type)369 int P_Thing_Damage (int tid, AActor *whofor0, int amount, FName type)
370 {
371 FActorIterator iterator (tid);
372 int count = 0;
373 AActor *actor;
374
375 actor = (tid == 0 ? whofor0 : iterator.Next());
376 while (actor)
377 {
378 AActor *next = tid == 0 ? NULL : iterator.Next ();
379 if (actor->flags & MF_SHOOTABLE)
380 {
381 if (amount > 0)
382 {
383 P_DamageMobj (actor, NULL, whofor0, amount, type);
384 }
385 else if (actor->health < actor->SpawnHealth())
386 {
387 actor->health -= amount;
388 if (actor->health > actor->SpawnHealth())
389 {
390 actor->health = actor->SpawnHealth();
391 }
392 if (actor->player != NULL)
393 {
394 actor->player->health = actor->health;
395 }
396 }
397 count++;
398 }
399 actor = next;
400 }
401 return count;
402 }
403
P_RemoveThing(AActor * actor)404 void P_RemoveThing(AActor * actor)
405 {
406 // Don't remove live players.
407 if (actor->player == NULL || actor != actor->player->mo)
408 {
409 // Don't also remove owned inventory items
410 if (actor->IsKindOf(RUNTIME_CLASS(AInventory)) && static_cast<AInventory*>(actor)->Owner != NULL) return;
411
412 // be friendly to the level statistics. ;)
413 actor->ClearCounters();
414 actor->Destroy ();
415 }
416
417 }
418
P_Thing_Raise(AActor * thing,AActor * raiser)419 bool P_Thing_Raise(AActor *thing, AActor *raiser)
420 {
421 FState * RaiseState = thing->GetRaiseState();
422 if (RaiseState == NULL)
423 {
424 return true; // monster doesn't have a raise state
425 }
426
427 AActor *info = thing->GetDefault ();
428
429 thing->velx = thing->vely = 0;
430
431 // [RH] Check against real height and radius
432 fixed_t oldheight = thing->height;
433 fixed_t oldradius = thing->radius;
434 ActorFlags oldflags = thing->flags;
435
436 thing->flags |= MF_SOLID;
437 thing->height = info->height; // [RH] Use real height
438 thing->radius = info->radius; // [RH] Use real radius
439 if (!P_CheckPosition (thing, thing->Pos()))
440 {
441 thing->flags = oldflags;
442 thing->radius = oldradius;
443 thing->height = oldheight;
444 return false;
445 }
446
447
448 S_Sound (thing, CHAN_BODY, "vile/raise", 1, ATTN_IDLE);
449
450 thing->Revive();
451
452 if (raiser != NULL)
453 {
454 // Let's copy the friendliness of the one who raised it.
455 thing->CopyFriendliness(raiser, false);
456 }
457
458 thing->SetState (RaiseState);
459 return true;
460 }
461
P_Thing_CanRaise(AActor * thing)462 bool P_Thing_CanRaise(AActor *thing)
463 {
464 FState * RaiseState = thing->GetRaiseState();
465 if (RaiseState == NULL)
466 {
467 return false;
468 }
469
470 AActor *info = thing->GetDefault();
471
472 // Check against real height and radius
473 ActorFlags oldflags = thing->flags;
474 fixed_t oldheight = thing->height;
475 fixed_t oldradius = thing->radius;
476
477 thing->flags |= MF_SOLID;
478 thing->height = info->height;
479 thing->radius = info->radius;
480
481 bool check = P_CheckPosition (thing, thing->Pos());
482
483 // Restore checked properties
484 thing->flags = oldflags;
485 thing->radius = oldradius;
486 thing->height = oldheight;
487
488 if (!check)
489 {
490 return false;
491 }
492
493 return true;
494 }
495
P_Thing_SetVelocity(AActor * actor,fixed_t vx,fixed_t vy,fixed_t vz,bool add,bool setbob)496 void P_Thing_SetVelocity(AActor *actor, fixed_t vx, fixed_t vy, fixed_t vz, bool add, bool setbob)
497 {
498 if (actor != NULL)
499 {
500 if (!add)
501 {
502 actor->velx = actor->vely = actor->velz = 0;
503 if (actor->player != NULL) actor->player->velx = actor->player->vely = 0;
504 }
505 actor->velx += vx;
506 actor->vely += vy;
507 actor->velz += vz;
508 if (setbob && actor->player != NULL)
509 {
510 actor->player->velx += vx;
511 actor->player->vely += vy;
512 }
513 }
514 }
515
P_GetSpawnableType(int spawnnum)516 const PClass *P_GetSpawnableType(int spawnnum)
517 {
518 if (spawnnum < 0)
519 { // A named arg from a UDMF map
520 FName spawnname = FName(ENamedName(-spawnnum));
521 if (spawnname.IsValidName())
522 {
523 return PClass::FindClass(spawnname);
524 }
525 }
526 else
527 { // A numbered arg from a Hexen or UDMF map
528 const PClass **type = SpawnableThings.CheckKey(spawnnum);
529 if (type != NULL)
530 {
531 return *type;
532 }
533 }
534 return NULL;
535 }
536
537 struct MapinfoSpawnItem
538 {
539 FName classname; // DECORATE is read after MAPINFO so we do not have the actual classes available here yet.
540 // These are for error reporting. We must store the file information because it's no longer available when these items get resolved.
541 FString filename;
542 int linenum;
543 };
544
545 typedef TMap<int, MapinfoSpawnItem> SpawnMap;
546 static SpawnMap SpawnablesFromMapinfo;
547 static SpawnMap ConversationIDsFromMapinfo;
548
SpawnableSort(const void * a,const void * b)549 static int STACK_ARGS SpawnableSort(const void *a, const void *b)
550 {
551 return (*((FClassMap::Pair **)a))->Key - (*((FClassMap::Pair **)b))->Key;
552 }
553
DumpClassMap(FClassMap & themap)554 static void DumpClassMap(FClassMap &themap)
555 {
556 FClassMap::Iterator it(themap);
557 FClassMap::Pair *pair, **allpairs;
558 int i = 0;
559
560 // Sort into numerical order, since their arrangement in the map can
561 // be in an unspecified order.
562 allpairs = new FClassMap::Pair *[themap.CountUsed()];
563 while (it.NextPair(pair))
564 {
565 allpairs[i++] = pair;
566 }
567 qsort(allpairs, i, sizeof(*allpairs), SpawnableSort);
568 for (int j = 0; j < i; ++j)
569 {
570 pair = allpairs[j];
571 Printf ("%d %s\n", pair->Key, pair->Value->TypeName.GetChars());
572 }
573 delete[] allpairs;
574 }
575
CCMD(dumpspawnables)576 CCMD(dumpspawnables)
577 {
578 DumpClassMap(SpawnableThings);
579 }
580
CCMD(dumpconversationids)581 CCMD (dumpconversationids)
582 {
583 DumpClassMap(StrifeTypes);
584 }
585
586
ParseSpawnMap(FScanner & sc,SpawnMap & themap,const char * descript)587 static void ParseSpawnMap(FScanner &sc, SpawnMap & themap, const char *descript)
588 {
589 TMap<int, bool> defined;
590 int error = 0;
591
592 MapinfoSpawnItem editem;
593
594 editem.filename = sc.ScriptName;
595
596 while (true)
597 {
598 if (sc.CheckString("}")) return;
599 else if (sc.CheckNumber())
600 {
601 int ednum = sc.Number;
602 sc.MustGetStringName("=");
603 sc.MustGetString();
604
605 bool *def = defined.CheckKey(ednum);
606 if (def != NULL)
607 {
608 sc.ScriptMessage("%s %d defined more than once", descript, ednum);
609 error++;
610 }
611 else if (ednum < 0)
612 {
613 sc.ScriptMessage("%s must be positive, got %d", descript, ednum);
614 error++;
615 }
616 defined[ednum] = true;
617 editem.classname = sc.String;
618
619 themap.Insert(ednum, editem);
620 }
621 else
622 {
623 sc.ScriptError("Number expected");
624 }
625 }
626 if (error > 0)
627 {
628 sc.ScriptError("%d errors encountered in %s definition", error, descript);
629 }
630 }
631
ParseSpawnNums()632 void FMapInfoParser::ParseSpawnNums()
633 {
634 ParseOpenBrace();
635 ParseSpawnMap(sc, SpawnablesFromMapinfo, "Spawn number");
636 }
637
ParseConversationIDs()638 void FMapInfoParser::ParseConversationIDs()
639 {
640 ParseOpenBrace();
641 ParseSpawnMap(sc, ConversationIDsFromMapinfo, "Conversation ID");
642 }
643
644
InitClassMap(FClassMap & themap,SpawnMap & thedata)645 void InitClassMap(FClassMap &themap, SpawnMap &thedata)
646 {
647 themap.Clear();
648 SpawnMap::Iterator it(thedata);
649 SpawnMap::Pair *pair;
650 int error = 0;
651
652 while (it.NextPair(pair))
653 {
654 const PClass *cls = NULL;
655 if (pair->Value.classname != NAME_None)
656 {
657 cls = PClass::FindClass(pair->Value.classname);
658 if (cls == NULL)
659 {
660 Printf(TEXTCOLOR_RED "Script error, \"%s\" line %d:\nUnknown actor class %s\n",
661 pair->Value.filename.GetChars(), pair->Value.linenum, pair->Value.classname.GetChars());
662 error++;
663 }
664 themap.Insert(pair->Key, cls);
665 }
666 else
667 {
668 themap.Remove(pair->Key);
669 }
670 }
671 if (error > 0)
672 {
673 I_Error("%d unknown actor classes found", error);
674 }
675 thedata.Clear(); // we do not need this any longer
676 }
677
InitSpawnablesFromMapinfo()678 void InitSpawnablesFromMapinfo()
679 {
680 InitClassMap(SpawnableThings, SpawnablesFromMapinfo);
681 InitClassMap(StrifeTypes, ConversationIDsFromMapinfo);
682 }
683
684
P_Thing_Warp(AActor * caller,AActor * reference,fixed_t xofs,fixed_t yofs,fixed_t zofs,angle_t angle,int flags,fixed_t heightoffset,fixed_t radiusoffset,angle_t pitch)685 int P_Thing_Warp(AActor *caller, AActor *reference, fixed_t xofs, fixed_t yofs, fixed_t zofs, angle_t angle, int flags, fixed_t heightoffset, fixed_t radiusoffset, angle_t pitch)
686 {
687 if (flags & WARPF_MOVEPTR)
688 {
689 AActor *temp = reference;
690 reference = caller;
691 caller = temp;
692 }
693
694 fixedvec3 old = caller->Pos();
695 zofs += FixedMul(reference->height, heightoffset);
696
697
698 if (!(flags & WARPF_ABSOLUTEANGLE))
699 {
700 angle += (flags & WARPF_USECALLERANGLE) ? caller->angle : reference->angle;
701 }
702
703 const fixed_t rad = FixedMul(radiusoffset, reference->radius);
704 const angle_t fineangle = angle >> ANGLETOFINESHIFT;
705
706 if (!(flags & WARPF_ABSOLUTEPOSITION))
707 {
708 if (!(flags & WARPF_ABSOLUTEOFFSET))
709 {
710 fixed_t xofs1 = xofs;
711
712 // (borrowed from A_SpawnItemEx, assumed workable)
713 // in relative mode negative y values mean 'left' and positive ones mean 'right'
714 // This is the inverse orientation of the absolute mode!
715
716 xofs = FixedMul(xofs1, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]);
717 yofs = FixedMul(xofs1, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]);
718 }
719
720 if (flags & WARPF_TOFLOOR)
721 {
722 // set correct xy
723 // now the caller's floorz should be appropriate for the assigned xy-position
724 // assigning position again with.
725 // extra unlink, link and environment calculation
726 caller->SetOrigin(reference->Vec3Offset(
727 xofs + FixedMul(rad, finecosine[fineangle]),
728 yofs + FixedMul(rad, finesine[fineangle]),
729 0), true);
730 caller->SetZ(caller->floorz + zofs);
731 }
732 else
733 {
734 caller->SetOrigin(reference->Vec3Offset(
735 xofs + FixedMul(rad, finecosine[fineangle]),
736 yofs + FixedMul(rad, finesine[fineangle]),
737 zofs), true);
738 }
739 }
740 else // [MC] The idea behind "absolute" is meant to be "absolute". Override everything, just like A_SpawnItemEx's.
741 {
742 if (flags & WARPF_TOFLOOR)
743 {
744 caller->SetOrigin(xofs + FixedMul(rad, finecosine[fineangle]), yofs + FixedMul(rad, finesine[fineangle]), zofs);
745 caller->SetZ(caller->floorz + zofs);
746 }
747 else
748 {
749 caller->SetOrigin(xofs + FixedMul(rad, finecosine[fineangle]), yofs + FixedMul(rad, finesine[fineangle]), zofs);
750 }
751 }
752
753 if ((flags & WARPF_NOCHECKPOSITION) || P_TestMobjLocation(caller))
754 {
755 if (flags & WARPF_TESTONLY)
756 {
757 caller->SetOrigin(old, true);
758 }
759 else
760 {
761 caller->angle = angle;
762
763 if (flags & WARPF_COPYPITCH)
764 caller->SetPitch(reference->pitch, false);
765
766 if (pitch)
767 caller->SetPitch(caller->pitch + pitch, false);
768
769 if (flags & WARPF_COPYVELOCITY)
770 {
771 caller->velx = reference->velx;
772 caller->vely = reference->vely;
773 caller->velz = reference->velz;
774 }
775 if (flags & WARPF_STOP)
776 {
777 caller->velx = 0;
778 caller->vely = 0;
779 caller->velz = 0;
780 }
781
782 if (flags & WARPF_WARPINTERPOLATION)
783 {
784 caller->PrevX += caller->X() - old.x;
785 caller->PrevY += caller->Y() - old.y;
786 caller->PrevZ += caller->Z() - old.z;
787 }
788 else if (flags & WARPF_COPYINTERPOLATION)
789 {
790 caller->PrevX = caller->X() + reference->PrevX - reference->X();
791 caller->PrevY = caller->Y() + reference->PrevY - reference->Y();
792 caller->PrevZ = caller->Z() + reference->PrevZ - reference->Z();
793 }
794 else if (!(flags & WARPF_INTERPOLATE))
795 {
796 caller->PrevX = caller->X();
797 caller->PrevY = caller->Y();
798 caller->PrevZ = caller->Z();
799 }
800 if ((flags & WARPF_BOB) && (reference->flags2 & MF2_FLOATBOB))
801 {
802 caller->AddZ(reference->GetBobOffset());
803 }
804 }
805 return true;
806 }
807 caller->SetOrigin(old, true);
808 return false;
809 }