diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 06f8bbc96..9d5a19f93 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -3913,3 +3913,165 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_WolfAttack) } +//========================================================================== +// +// A_Warp +// +//========================================================================== + +enum WARPF +{ + WARPF_ABSOLUTEOFFSET = 0x1, + WARPF_ABSOLUTEANGLE = 0x2, + WARPF_USECALLERANGLE = 0x4, + + WARPF_NOCHECKPOSITION = 0x8, + + WARPF_INTERPOLATE = 0x10, + WARPF_WARPINTERPOLATION = 0x20, + WARPF_COPYINTERPOLATION = 0x40, + + WARPF_STOP = 0x80, + WARPF_TOFLOOR = 0x100, + WARPF_TESTONLY = 0x200 +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) +{ + ACTION_PARAM_START(7); + + ACTION_PARAM_INT(destination_selector, 0); + ACTION_PARAM_FIXED(xofs, 1); + ACTION_PARAM_FIXED(yofs, 2); + ACTION_PARAM_FIXED(zofs, 3); + ACTION_PARAM_ANGLE(angle, 4); + ACTION_PARAM_INT(flags, 5); + ACTION_PARAM_STATE(success_state, 6); + + fixed_t + + oldx, + oldy, + oldz; + + AActor *reference = COPY_AAPTR(self, destination_selector); + + if (!reference) + { + ACTION_SET_RESULT(false); + return; + } + + if (!(flags & WARPF_ABSOLUTEANGLE)) + { + angle += (flags & WARPF_USECALLERANGLE) ? self->angle : reference->angle; + } + + if (!(flags & WARPF_ABSOLUTEOFFSET)) + { + angle_t fineangle = angle>>ANGLETOFINESHIFT; + oldx = xofs; + + // (borrowed from A_SpawnItemEx, assumed workable) + // in relative mode negative y values mean 'left' and positive ones mean 'right' + // This is the inverse orientation of the absolute mode! + + xofs = FixedMul(oldx, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); + yofs = FixedMul(oldx, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); + } + + oldx = self->x; + oldy = self->y; + oldz = self->z; + + if (flags & WARPF_TOFLOOR) + { + // set correct xy + + self->SetOrigin( + reference->x + xofs, + reference->y + yofs, + reference->z); + + // now the caller's floorz should be appropriate for the assigned xy-position + // assigning position again with + + if (zofs) + { + // extra unlink, link and environment calculation + self->SetOrigin( + self->x, + self->y, + self->floorz + zofs); + } + else + { + // if there is no offset, there should be no ill effect from moving down to the + // already identified floor + + // A_Teleport does the same thing anyway + self->z = self->floorz; + } + } + else + { + self->SetOrigin( + reference->x + xofs, + reference->y + yofs, + reference->z + zofs); + } + + if ((flags & WARPF_NOCHECKPOSITION) || P_TestMobjLocation(self)) + { + if (flags & WARPF_TESTONLY) + { + self->SetOrigin(oldx, oldy, oldz); + } + else + { + self->angle = angle; + + if (flags & WARPF_STOP) + { + self->velx = 0; + self->vely = 0; + self->velz = 0; + } + + if (flags & WARPF_WARPINTERPOLATION) + { + self->PrevX += self->x - oldx; + self->PrevY += self->y - oldy; + self->PrevZ += self->z - oldz; + } + else if (flags & WARPF_COPYINTERPOLATION) + { + self->PrevX = self->x + reference->PrevX - reference->x; + self->PrevY = self->y + reference->PrevY - reference->y; + self->PrevZ = self->z + reference->PrevZ - reference->z; + } + else if (! (flags & WARPF_INTERPOLATE)) + { + self->PrevX = self->x; + self->PrevY = self->y; + self->PrevZ = self->z; + } + } + + if (success_state) + { + ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! + // in this case, you have the statejump to help you handle all the success anyway. + ACTION_JUMP(success_state); + return; + } + + ACTION_SET_RESULT(true); + } + else + { + self->SetOrigin(oldx, oldy, oldz); + ACTION_SET_RESULT(false); + } + +} diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 51c1377d0..29b265ac6 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -237,6 +237,7 @@ ACTOR Actor native //: Thinker action native A_PlayerSkinCheck(state label); action native A_BasicAttack(int meleedamage, sound meleesound, class missiletype, float missileheight); action native A_Teleport(state teleportstate = "", class targettype = "BossSpot", class fogtype = "TeleportFog", int flags = 0, float mindist = 128, float maxdist = 0); + action native A_Warp(int ptr_destination, float xofs = 0, float yofs = 0, float zofs = 0, float angle = 0, int flags = 0, state success_state = ""); action native A_ThrowGrenade(class itemtype, float zheight = 0, float xyvel = 0, float zvel = 0, bool useammo = true); action native A_Weave(int xspeed, int yspeed, float xdist, float ydist); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index de545ed74..fb391ee91 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -242,6 +242,18 @@ Const Int PTROP_UNSAFEMASTER = 2; Const Int PTROP_NOSAFEGUARDS = PTROP_UNSAFETARGET|PTROP_UNSAFEMASTER; +// Flags for A_Warp + +Const Int WARPF_ABSOLUTEOFFSET = 0x1; +Const Int WARPF_ABSOLUTEANGLE = 0x2; +Const Int WARPF_USECALLERANGLE = 0x4; +Const Int WARPF_NOCHECKPOSITION = 0x8; +Const Int WARPF_INTERPOLATE = 0x10; +Const Int WARPF_WARPINTERPOLATION = 0x20; +Const Int WARPF_COPYINTERPOLATION = 0x40; +Const Int WARPF_STOP = 0x80; +Const Int WARPF_TOFLOOR = 0x100; +Const Int WARPF_TESTONLY = 0x200; // This is only here to provide one global variable for testing. native int testglobalvar;