- completely redid how A_PainShootSkull checks the legality of the LS spawn.

It turned out that the Boom method does not work well with portals and fixing it while keeping it doesn't look feasible - the entire approach was bad from the start.
Instead, let's use the same approach as P_XYMovement: Spawn the Lost Soul at the center of the PE, and then use multiple P_TryMoves to get it to its intended location.
This will check all blocking lines, just like Boom did, but it will also properly handle z-positioning and portal transitions.
This commit is contained in:
Christoph Oelckers 2016-11-23 18:54:16 +01:00
parent 7325e3f0f8
commit f8c6adb7eb

View file

@ -62,77 +62,78 @@ void A_PainShootSkull (VMFrameStack *stack, AActor *self, DAngle Angle, PClassAc
} }
// okay, there's room for another one // okay, there's room for another one
prestep = 4 + (self->radius + GetDefaultByType(spawntype)->radius) * 1.5; double otherradius = GetDefaultByType(spawntype)->radius;
prestep = 4 + (self->radius + otherradius) * 1.5;
// NOTE: The following code contains some advance work for line-to-line portals which is currenty inactive. DVector2 move = Angle.ToVector(prestep);
DVector3 spawnpos = self->PosPlusZ(8.0);
DVector3 destpos = spawnpos + move;
DVector2 dist = Angle.ToVector(prestep); other = Spawn(spawntype, spawnpos, ALLOW_REPLACE);
DVector3 pos = self->Vec3Offset(dist.X, dist.Y, 8., true);
DVector3 src = self->Pos();
for (int i = 0; i < 2; i++) // Now check if the spawn is legal. Unlike Boom's hopeless attempt at fixing it, let's do it the same way
// P_XYMovement solves the line skipping: Spawn the Lost Soul near the PE's center and then use multiple
// smaller steps to get it to its intended position. This will also result in proper clipping, but
// it will avoid all the problems of the Boom method, which checked too many lines and despite some
// adjustments never worked with portals.
if (other != nullptr)
{ {
// Check whether the Lost Soul is being fired through a 1-sided // phares double maxmove = other->radius - 1;
// wall or an impassible line, or a "monsters can't cross" line.// |
// If it is, then we don't allow the spawn. // V
FBoundingBox box(MIN(src.X, pos.X), MIN(src.Y, pos.Y), MAX(src.X, pos.X), MAX(src.Y, pos.Y)); if (maxmove <= 0) maxmove = MAXMOVE;
FBlockLinesIterator it(box);
line_t *ld;
bool inportal = false;
while ((ld = it.Next())) const double xspeed = fabs(move.X);
const double yspeed = fabs(move.Y);
int steps = 1;
if (xspeed > yspeed)
{ {
if (ld->isLinePortal() && i == 0) if (xspeed > maxmove)
{ {
if (P_PointOnLineSidePrecise(src, ld) == 0 && steps = int(1 + xspeed / maxmove);
P_PointOnLineSidePrecise(pos, ld) == 1)
{
// crossed a portal line from front to back, we need to repeat the check on the other side as well.
inportal = true;
} }
} }
else if (!(ld->flags & ML_TWOSIDED) || else
(ld->flags & (ML_BLOCKING | ML_BLOCKMONSTERS | ML_BLOCKEVERYTHING)))
{ {
if (box.inRange(ld)) if (yspeed > maxmove)
{ {
if (P_PointOnLineSidePrecise(src, ld) != P_PointOnLineSidePrecise(pos, ld)) steps = int(1 + yspeed / maxmove);
return; // line blocks trajectory // ^
} }
} }
}
if (!inportal) break;
// recalculate position and redo the check on the other side of the portal DVector2 stepmove = move / steps;
pos = self->Vec3Offset(dist.X, dist.Y, 8.); self->flags &= ~MF_SOLID; // make it solid again
src.X = pos.X - dist.X; other->flags2 |= MF2_NOTELEPORT; // we do not want the LS to teleport
src.Y = pos.Y - dist.Y; for (int i = 0; i < steps; i++)
{
} DVector2 ptry = other->Pos().XY() + stepmove;
DAngle oldangle = other->Angles.Yaw;
other = Spawn (spawntype, pos, ALLOW_REPLACE); if (!P_TryMove(other, ptry, 0, nullptr))
// Check to see if the new Lost Soul's z value is above the
// ceiling of its new sector, or below the floor. If so, kill it.
if (other->Top() > other->Sector->HighestCeilingAt(other) ||
other->Z() < other->Sector->LowestFloorAt(other))
{
// kill it immediately
P_DamageMobj (other, self, self, TELEFRAG_DAMAGE, NAME_None);// ^
return; // |
} // phares
// Check for movements.
if (!P_CheckPosition (other, other->Pos()))
{ {
// kill it immediately // kill it immediately
other->ClearCounters();
P_DamageMobj(other, self, self, TELEFRAG_DAMAGE, NAME_None); P_DamageMobj(other, self, self, TELEFRAG_DAMAGE, NAME_None);
return; return;
} }
if (other->Pos().XY() != ptry)
{
// If the new position does not match the desired position, the player
// must have gone through a portal.
// For that we need to adjust the movement vector for the following steps.
DAngle anglediff = deltaangle(oldangle, other->Angles.Yaw);
if (anglediff != 0)
{
stepmove = stepmove.Rotated(anglediff);
}
}
}
self->flags |= MF_SOLID; // don't let the LS be stuck in the PE while checking the move
// [RH] Lost souls hate the same things as their pain elementals // [RH] Lost souls hate the same things as their pain elementals
other->CopyFriendliness (self, !(flags & PAF_NOTARGET)); other->CopyFriendliness (self, !(flags & PAF_NOTARGET));
@ -142,6 +143,7 @@ void A_PainShootSkull (VMFrameStack *stack, AActor *self, DAngle Angle, PClassAc
CallAction(stack, A_SkullAttack, other); CallAction(stack, A_SkullAttack, other);
} }
} }
}
DEFINE_ACTION_FUNCTION(AActor, A_PainShootSkull) DEFINE_ACTION_FUNCTION(AActor, A_PainShootSkull)