- made P_AimLineAttack work through sector portals.

Note: Test output not removed yet!
This commit is contained in:
Christoph Oelckers 2016-03-02 20:44:02 +01:00
parent c160121f45
commit f5fd0df077
16 changed files with 517 additions and 385 deletions

View File

@ -1401,21 +1401,8 @@ AActor *P_LinePickActor(AActor *t1, angle_t angle, fixed_t distance, int pitch,
struct FTranslatedLineTarget struct FTranslatedLineTarget
{ {
AActor *linetarget; AActor *linetarget;
angle_t hitangle; angle_t angleFromSource;
fixedvec3 targetPosFromSrc;
angle_t targetAngleFromSrc;
fixedvec3 sourcePosFromTarget;
angle_t sourceAngleFromTarget;
bool unlinked; // found by a trace that went through an unlinked portal. bool unlinked; // found by a trace that went through an unlinked portal.
angle_t SourceAngleToTarget() const
{
return R_PointToAngle2(sourcePosFromTarget.x, sourcePosFromTarget.y, linetarget->X(), linetarget->Y());
}
angle_t TargetAngleToSource() const
{
return R_PointToAngle2(linetarget->X(), linetarget->Y(), sourcePosFromTarget.x, sourcePosFromTarget.y);
}
}; };

View File

@ -61,7 +61,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Punch)
if (t.linetarget) if (t.linetarget)
{ {
S_Sound (self, CHAN_WEAPON, "*fist", 1, ATTN_NORM); S_Sound (self, CHAN_WEAPON, "*fist", 1, ATTN_NORM);
self->angle = t.SourceAngleToTarget(); self->angle = t.angleFromSource;
} }
return 0; return 0;
} }
@ -232,7 +232,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Saw)
// turn to face target // turn to face target
if (!(flags & SF_NOTURN)) if (!(flags & SF_NOTURN))
{ {
angle = t.SourceAngleToTarget(); angle = t.angleFromSource;
if (angle - self->angle > ANG180) if (angle - self->angle > ANG180)
{ {
if (angle - self->angle < (angle_t)(-ANG90 / 20)) if (angle - self->angle < (angle_t)(-ANG90 / 20))
@ -696,7 +696,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BFGSpray)
damage = defdamage; damage = defdamage;
} }
int newdam = P_DamageMobj(t.linetarget, self->target, self->target, damage, dmgType, dmgFlags|DMG_USEANGLE, t.SourceAngleToTarget()); int newdam = P_DamageMobj(t.linetarget, self->target, self->target, damage, dmgType, dmgFlags|DMG_USEANGLE, t.angleFromSource);
P_TraceBleed(newdam > 0 ? newdam : damage, &t, self); P_TraceBleed(newdam > 0 ? newdam : damage, &t, self);
} }
} }

View File

@ -293,7 +293,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_M_Saw)
S_Sound (self, CHAN_WEAPON, hitsound, 1, ATTN_NORM); S_Sound (self, CHAN_WEAPON, hitsound, 1, ATTN_NORM);
// turn to face target // turn to face target
angle = t.SourceAngleToTarget(); angle = t.angleFromSource;
if (angle - self->angle > ANG180) if (angle - self->angle > ANG180)
{ {
if (angle - self->angle < (angle_t)(-ANG90/20)) if (angle - self->angle < (angle_t)(-ANG90/20))
@ -344,7 +344,7 @@ static void MarinePunch(AActor *self, int damagemul)
if (t.linetarget) if (t.linetarget)
{ {
S_Sound (self, CHAN_WEAPON, "*fist", 1, ATTN_NORM); S_Sound (self, CHAN_WEAPON, "*fist", 1, ATTN_NORM);
self->angle = t.SourceAngleToTarget(); self->angle = t.angleFromSource;
} }
} }

View File

@ -189,7 +189,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_BeakAttackPL1)
P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, "BeakPuff", true, &t); P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, "BeakPuff", true, &t);
if (t.linetarget) if (t.linetarget)
{ {
player->mo->angle = t.SourceAngleToTarget(); player->mo->angle = t.angleFromSource;
} }
P_PlayPeck (player->mo); P_PlayPeck (player->mo);
player->chickenPeck = 12; player->chickenPeck = 12;
@ -224,7 +224,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_BeakAttackPL2)
P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, "BeakPuff", true, &t); P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, "BeakPuff", true, &t);
if (t.linetarget) if (t.linetarget)
{ {
player->mo->angle = t.SourceAngleToTarget(); player->mo->angle = t.angleFromSource;
} }
P_PlayPeck (player->mo); P_PlayPeck (player->mo);
player->chickenPeck = 12; player->chickenPeck = 12;

View File

@ -94,7 +94,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StaffAttack)
{ {
//S_StartSound(player->mo, sfx_stfhit); //S_StartSound(player->mo, sfx_stfhit);
// turn to face target // turn to face target
self->angle = t.SourceAngleToTarget(); self->angle = t.angleFromSource;
} }
return 0; return 0;
} }
@ -324,7 +324,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GauntletAttack)
S_Sound (self, CHAN_AUTO, "weapons/gauntletshit", 1, ATTN_NORM); S_Sound (self, CHAN_AUTO, "weapons/gauntletshit", 1, ATTN_NORM);
} }
// turn to face target // turn to face target
angle = t.SourceAngleToTarget(); angle = t.angleFromSource;
if (angle-self->angle > ANG180) if (angle-self->angle > ANG180)
{ {
if ((int)(angle-self->angle) < -ANG90/20) if ((int)(angle-self->angle) < -ANG90/20)
@ -675,7 +675,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_DeathBallImpact)
if (t.linetarget && self->target != t.linetarget) if (t.linetarget && self->target != t.linetarget)
{ {
self->tracer = t.linetarget; self->tracer = t.linetarget;
angle = t.SourceAngleToTarget(); angle = t.angleFromSource;
newAngle = true; newAngle = true;
break; break;
} }

View File

@ -79,7 +79,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CStaffCheck)
P_LineAttack(pmo, angle, fixed_t(1.5*MELEERANGE), slope, damage, NAME_Melee, puff, false, &t); P_LineAttack(pmo, angle, fixed_t(1.5*MELEERANGE), slope, damage, NAME_Melee, puff, false, &t);
if (t.linetarget != NULL) if (t.linetarget != NULL)
{ {
pmo->angle = t.SourceAngleToTarget(); pmo->angle = t.angleFromSource;
if (((t.linetarget->player && (!t.linetarget->IsTeammate(pmo) || level.teamdamage != 0)) || t.linetarget->flags3&MF3_ISMONSTER) if (((t.linetarget->player && (!t.linetarget->IsTeammate(pmo) || level.teamdamage != 0)) || t.linetarget->flags3&MF3_ISMONSTER)
&& (!(t.linetarget->flags2&(MF2_DORMANT | MF2_INVULNERABLE)))) && (!(t.linetarget->flags2&(MF2_DORMANT | MF2_INVULNERABLE))))
{ {

View File

@ -245,7 +245,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FAxeAttack)
{ {
if (t.linetarget->flags3&MF3_ISMONSTER || t.linetarget->player) if (t.linetarget->flags3&MF3_ISMONSTER || t.linetarget->player)
{ {
P_ThrustMobj(t.linetarget, t.hitangle, power); P_ThrustMobj(t.linetarget, t.angleFromSource, power);
} }
AdjustPlayerAngle(pmo, &t); AdjustPlayerAngle(pmo, &t);
useMana++; useMana++;

View File

@ -57,7 +57,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FHammerAttack)
AdjustPlayerAngle(pmo, &t); AdjustPlayerAngle(pmo, &t);
if (t.linetarget->flags3 & MF3_ISMONSTER || t.linetarget->player) if (t.linetarget->flags3 & MF3_ISMONSTER || t.linetarget->player)
{ {
P_ThrustMobj(t.linetarget, t.hitangle, power); P_ThrustMobj(t.linetarget, t.angleFromSource, power);
} }
pmo->weaponspecial = false; // Don't throw a hammer pmo->weaponspecial = false; // Don't throw a hammer
goto hammerdone; goto hammerdone;
@ -73,7 +73,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FHammerAttack)
AdjustPlayerAngle(pmo, &t); AdjustPlayerAngle(pmo, &t);
if (t.linetarget->flags3 & MF3_ISMONSTER || t.linetarget->player) if (t.linetarget->flags3 & MF3_ISMONSTER || t.linetarget->player)
{ {
P_ThrustMobj(t.linetarget, t.hitangle, power); P_ThrustMobj(t.linetarget, t.angleFromSource, power);
} }
pmo->weaponspecial = false; // Don't throw a hammer pmo->weaponspecial = false; // Don't throw a hammer
goto hammerdone; goto hammerdone;

View File

@ -30,7 +30,7 @@ void AdjustPlayerAngle (AActor *pmo, FTranslatedLineTarget *t)
angle_t angle; angle_t angle;
int difference; int difference;
angle = t->SourceAngleToTarget(); angle = t->angleFromSource;
difference = (int)angle - (int)pmo->angle; difference = (int)angle - (int)pmo->angle;
if (abs(difference) > MAX_ANGLE_ADJUST) if (abs(difference) > MAX_ANGLE_ADJUST)
{ {
@ -82,7 +82,7 @@ static bool TryPunch(APlayerPawn *pmo, angle_t angle, int damage, fixed_t power)
if (t.linetarget->player != NULL || if (t.linetarget->player != NULL ||
(t.linetarget->Mass != INT_MAX && (t.linetarget->flags3 & MF3_ISMONSTER))) (t.linetarget->Mass != INT_MAX && (t.linetarget->flags3 & MF3_ISMONSTER)))
{ {
P_ThrustMobj (t.linetarget, t.hitangle, power); P_ThrustMobj (t.linetarget, t.angleFromSource, power);
} }
AdjustPlayerAngle (pmo, &t); AdjustPlayerAngle (pmo, &t);
return true; return true;

View File

@ -82,7 +82,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireConePL1)
slope = P_AimLineAttack (self, angle, MELEERANGE, &t, 0, ALF_CHECK3D); slope = P_AimLineAttack (self, angle, MELEERANGE, &t, 0, ALF_CHECK3D);
if (t.linetarget) if (t.linetarget)
{ {
P_DamageMobj (t.linetarget, self, self, damage, NAME_Ice, DMG_USEANGLE, t.SourceAngleToTarget()); P_DamageMobj (t.linetarget, self, self, damage, NAME_Ice, DMG_USEANGLE, t.angleFromSource);
conedone = true; conedone = true;
break; break;
} }

View File

@ -120,7 +120,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_JabDagger)
S_Sound (self, CHAN_WEAPON, S_Sound (self, CHAN_WEAPON,
t.linetarget->flags & MF_NOBLOOD ? "misc/metalhit" : "misc/meathit", t.linetarget->flags & MF_NOBLOOD ? "misc/metalhit" : "misc/meathit",
1, ATTN_NORM); 1, ATTN_NORM);
self->angle = t.SourceAngleToTarget(); self->angle = t.angleFromSource;
self->flags |= MF_JUSTATTACKED; self->flags |= MF_JUSTATTACKED;
P_DaggerAlert (self, t.linetarget); P_DaggerAlert (self, t.linetarget);
} }

View File

@ -300,6 +300,7 @@ enum
FFCF_NOFLOOR = 32, FFCF_NOFLOOR = 32,
FFCF_NOCEILING = 64, FFCF_NOCEILING = 64,
FFCF_RESTRICTEDPORTAL = 128, // current values in the iterator's return are through a restricted portal type (i.e. some features are blocked.) FFCF_RESTRICTEDPORTAL = 128, // current values in the iterator's return are through a restricted portal type (i.e. some features are blocked.)
FFCF_NODROPOFF = 256, // Caller does not need a dropoff (saves some time when checking portals)
}; };
void P_FindFloorCeiling (AActor *actor, int flags=0); void P_FindFloorCeiling (AActor *actor, int flags=0);

View File

@ -3440,346 +3440,535 @@ bool P_BounceActor(AActor *mo, AActor *BlockingMobj, bool ontop)
// //
//============================================================================ //============================================================================
struct AimTarget : public FTranslatedLineTarget
{
angle_t pitch;
fixed_t frac;
void Clear()
{
memset(this, 0, sizeof(*this));
frac = FIXED_MAX;
}
};
struct aim_t struct aim_t
{ {
enum
{
aim_up = 1,
aim_down = 2
};
fixed_t aimpitch; fixed_t aimpitch;
fixed_t attackrange; fixed_t attackrange;
fixed_t shootz; // Height if not aiming up or down fixed_t shootz; // Height if not aiming up or down
fixed_t limitz; // height limit for portals to avoid bad setups
AActor* shootthing; AActor* shootthing;
AActor* friender; // actor to check friendliness again AActor* friender; // actor to check friendliness again
AActor* aimtarget; // if we want to aim at precisely this target.
fixed_t toppitch, bottompitch; fixed_t toppitch, bottompitch;
AActor * linetarget; AimTarget linetarget;
AActor * thing_friend, *thing_other; AimTarget thing_friend, thing_other;
angle_t pitch_friend, pitch_other;
int flags; int flags;
sector_t * lastsector; sector_t * lastsector;
secplane_t * lastfloorplane; secplane_t * lastfloorplane;
secplane_t * lastceilingplane; secplane_t * lastceilingplane;
int aimdir;
fixedvec3 startpos;
fixedvec2 aimtrace;
fixed_t startfrac;
bool crossedffloors; bool crossedffloors;
bool unlinked;
bool AimTraverse3DFloors(const divline_t &trace, intercept_t * in); // Creates a clone of this structure with the basic info copied.
aim_t Clone()
void AimTraverse(fixed_t startx, fixed_t starty, fixed_t endx, fixed_t endy, AActor *target = NULL);
};
//============================================================================
//
// AimTraverse3DFloors
//
//============================================================================
bool aim_t::AimTraverse3DFloors(const divline_t &trace, intercept_t * in)
{
sector_t * nextsector;
secplane_t * nexttopplane, *nextbottomplane;
line_t * li = in->d.line;
nextsector = NULL;
nexttopplane = nextbottomplane = NULL;
if (li->backsector == NULL) return true; // shouldn't really happen but crashed once for me...
if (li->frontsector->e->XFloor.ffloors.Size() || li->backsector->e->XFloor.ffloors.Size())
{ {
int frontflag; aim_t cloned;
F3DFloor* rover;
int highpitch, lowpitch;
fixed_t trX = trace.x + FixedMul(trace.dx, in->frac); cloned.aimtrace = aimtrace;
fixed_t trY = trace.y + FixedMul(trace.dy, in->frac); cloned.aimpitch = aimpitch;
fixed_t dist = FixedMul(attackrange, in->frac); cloned.aimtarget = aimtarget;
cloned.attackrange = attackrange;
cloned.shootthing = shootthing;
cloned.friender = friender;
cloned.shootz = shootz;
cloned.unlinked = unlinked;
cloned.flags = flags;
return cloned;
}
frontflag = P_PointOnLineSide(shootthing->X(), shootthing->Y(), li); // Crosing a line portal does not require a recursive call. We can just alter the current set of data
void CrossLinePortal(line_t *line)
{
}
// 3D floor check. This is not 100% accurate but normally sufficient when //============================================================================
// combined with a final sight check //
for (int i = 1; i <= 2; i++) // SetResult
//
//============================================================================
void SetResult(AimTarget &res, fixed_t frac, AActor *th, fixed_t pitch)
{
if (res.frac > frac)
{ {
sector_t * s = i == 1 ? li->frontsector : li->backsector; res.linetarget = th;
res.pitch = pitch;
for (unsigned k = 0; k<s->e->XFloor.ffloors.Size(); k++) res.angleFromSource = R_PointToAngle2(startpos.x, startpos.y, th->X(), th->Y());
{ res.unlinked = unlinked;
crossedffloors = true; res.frac = frac;
rover = s->e->XFloor.ffloors[k];
if ((rover->flags & FF_SHOOTTHROUGH) || !(rover->flags & FF_EXISTS)) continue;
fixed_t ff_bottom = rover->bottom.plane->ZatPoint(trX, trY);
fixed_t ff_top = rover->top.plane->ZatPoint(trX, trY);
highpitch = -(int)R_PointToAngle2(0, shootz, dist, ff_top);
lowpitch = -(int)R_PointToAngle2(0, shootz, dist, ff_bottom);
if (highpitch <= toppitch)
{
// blocks completely
if (lowpitch >= bottompitch) return false;
// blocks upper edge of view
if (lowpitch>toppitch)
{
toppitch = lowpitch;
if (frontflag != i - 1)
{
nexttopplane = rover->bottom.plane;
}
}
}
else if (lowpitch >= bottompitch)
{
// blocks lower edge of view
if (highpitch<bottompitch)
{
bottompitch = highpitch;
if (frontflag != i - 1)
{
nextbottomplane = rover->top.plane;
}
}
}
// trace is leaving a sector with a 3d-floor
if (frontflag == i - 1)
{
if (s == lastsector)
{
// upper slope intersects with this 3d-floor
if (rover->bottom.plane == lastceilingplane && lowpitch > toppitch)
{
toppitch = lowpitch;
}
// lower slope intersects with this 3d-floor
if (rover->top.plane == lastfloorplane && highpitch < bottompitch)
{
bottompitch = highpitch;
}
}
}
if (toppitch >= bottompitch) return false; // stop
}
} }
} }
lastsector = nextsector; void SetResult(AimTarget &res, AimTarget &set)
lastceilingplane = nexttopplane;
lastfloorplane = nextbottomplane;
return true;
}
//============================================================================
//
// PTR_AimTraverse
// Sets linetaget and aimpitch when a target is aimed at.
//
//============================================================================
void aim_t::AimTraverse(fixed_t startx, fixed_t starty, fixed_t endx, fixed_t endy, AActor *target)
{
FPathTraverse it(startx, starty, endx, endy, PT_ADDLINES | PT_ADDTHINGS | PT_COMPATIBLE);
intercept_t *in;
while ((in = it.Next()))
{ {
line_t* li; if (res.frac > set.frac)
AActor* th;
fixed_t pitch;
fixed_t thingtoppitch;
fixed_t thingbottompitch;
fixed_t dist;
int thingpitch;
if (in->isaline)
{ {
li = in->d.line; res = set;
}
}
if (!(li->flags & ML_TWOSIDED) || (li->flags & ML_BLOCKEVERYTHING)) //============================================================================
return; // stop //
// Result
//
//============================================================================
// Crosses a two sided line. AimTarget *Result()
// A two sided line will restrict the possible target ranges. {
FLineOpening open; AimTarget *result = &linetarget;
P_LineOpening(open, NULL, li, it.Trace().x + FixedMul(it.Trace().dx, in->frac), if (result->linetarget == NULL)
it.Trace().y + FixedMul(it.Trace().dy, in->frac)); {
if (thing_other.linetarget != NULL)
{
result = &thing_other;
}
else if (thing_friend.linetarget != NULL)
{
result = &thing_friend;
}
}
return result;
}
if (open.bottom >= open.top)
return; // stop
//============================================================================
//
// AimTraverse3DFloors
//
//============================================================================
bool AimTraverse3DFloors(const divline_t &trace, intercept_t * in, int frontflag, int *planestocheck)
{
sector_t * nextsector;
secplane_t * nexttopplane, *nextbottomplane;
line_t * li = in->d.line;
nextsector = NULL;
nexttopplane = nextbottomplane = NULL;
*planestocheck = aimdir;
if (li->backsector == NULL) return true; // shouldn't really happen but crashed once for me...
if (li->frontsector->e->XFloor.ffloors.Size() || li->backsector->e->XFloor.ffloors.Size())
{
F3DFloor* rover;
int highpitch, lowpitch;
fixed_t trX = trace.x + FixedMul(trace.dx, in->frac);
fixed_t trY = trace.y + FixedMul(trace.dy, in->frac);
fixed_t dist = FixedMul(attackrange, in->frac);
// 3D floor check. This is not 100% accurate but normally sufficient when
// combined with a final sight check
for (int i = 1; i <= 2; i++)
{
sector_t * s = i == 1 ? li->frontsector : li->backsector;
for (unsigned k = 0; k < s->e->XFloor.ffloors.Size(); k++)
{
crossedffloors = true;
rover = s->e->XFloor.ffloors[k];
if ((rover->flags & FF_SHOOTTHROUGH) || !(rover->flags & FF_EXISTS)) continue;
fixed_t ff_bottom = rover->bottom.plane->ZatPoint(trX, trY);
fixed_t ff_top = rover->top.plane->ZatPoint(trX, trY);
highpitch = -(int)R_PointToAngle2(0, shootz, dist, ff_top);
lowpitch = -(int)R_PointToAngle2(0, shootz, dist, ff_bottom);
if (highpitch <= toppitch)
{
// blocks completely
if (lowpitch >= bottompitch) return false;
// blocks upper edge of view
if (lowpitch > toppitch)
{
toppitch = lowpitch;
if (frontflag != i - 1)
{
nexttopplane = rover->bottom.plane;
*planestocheck &= ~aim_up;
}
}
}
else if (lowpitch >= bottompitch)
{
// blocks lower edge of view
if (highpitch<bottompitch)
{
bottompitch = highpitch;
if (frontflag != i - 1)
{
nextbottomplane = rover->top.plane;
*planestocheck &= ~aim_down;
}
}
}
// trace is leaving a sector with a 3d-floor
if (frontflag == i - 1)
{
if (s == lastsector)
{
// upper slope intersects with this 3d-floor
if (rover->bottom.plane == lastceilingplane && lowpitch > toppitch)
{
toppitch = lowpitch;
}
// lower slope intersects with this 3d-floor
if (rover->top.plane == lastfloorplane && highpitch < bottompitch)
{
bottompitch = highpitch;
}
}
}
if (toppitch >= bottompitch) return false; // stop
}
}
}
lastsector = nextsector;
lastceilingplane = nexttopplane;
lastfloorplane = nextbottomplane;
return true;
}
//============================================================================
//
// traverses a sector portal
//
//============================================================================
void EnterSectorPortal(int position, fixed_t frac, sector_t *entersec, fixed_t newtoppitch, fixed_t newbottompitch)
{
AActor *portal = entersec->SkyBoxes[position];
if (portal == NULL)
if (position == sector_t::ceiling && portal->threshold < limitz) return;
else if (position == sector_t::floor && portal->threshold > limitz) return;
aim_t newtrace = Clone();
Printf("-----Entering %s portal\n", position ? "ceiling" : "floor");
newtrace.toppitch = newtoppitch;
newtrace.bottompitch = newbottompitch;
newtrace.aimdir = position == sector_t::ceiling? aim_t::aim_up : aim_t::aim_down;
newtrace.startpos = { startpos.x + portal->scaleX, startpos.y + portal->scaleY, startpos.z };
newtrace.lastsector = P_PointInSector(startpos.x, startpos.y);
newtrace.startfrac = frac + FixedDiv(FRACUNIT, attackrange); // this is to skip the transition line to the portal which will produce a bogus opening
newtrace.limitz = portal->threshold;
newtrace.AimTraverse();
SetResult(linetarget, newtrace.linetarget);
SetResult(thing_friend, newtrace.thing_friend);
SetResult(thing_other, newtrace.thing_other);
Printf("-----Exiting %s portal\n", position ? "ceiling" : "floor");
}
//============================================================================
//
// PTR_AimTraverse
// Sets linetaget and aimpitch when a target is aimed at.
//
//============================================================================
void AimTraverse()
{
// for smart aiming
linetarget.Clear();
thing_friend.Clear();
thing_other.Clear();
crossedffloors = lastsector->e->XFloor.ffloors.Size() != 0;
lastfloorplane = lastceilingplane = NULL;
// check the initial sector for 3D-floors and portals
bool ceilingportalstate = (aimdir & aim_t::aim_up) && toppitch < 0 && !lastsector->PortalBlocksMovement(sector_t::ceiling);
bool floorportalstate = (aimdir & aim_t::aim_down) && bottompitch > 0 && !lastsector->PortalBlocksMovement(sector_t::floor);
for (auto rover : lastsector->e->XFloor.ffloors)
{
if ((rover->flags & FF_SHOOTTHROUGH) || !(rover->flags & FF_EXISTS)) continue;
fixed_t bottomz = rover->bottom.plane->ZatPoint(startpos);
if (bottomz >= startpos.z + shootthing->height)
{
lastceilingplane = rover->bottom.plane;
// no ceiling portal if below a 3D floor
ceilingportalstate = false;
}
bottomz = rover->top.plane->ZatPoint(startpos);
if (bottomz <= startpos.z)
{
lastfloorplane = rover->top.plane;
// no floor portal if above a 3D floor
floorportalstate = false;
}
}
if (ceilingportalstate) EnterSectorPortal(sector_t::ceiling, 0, lastsector, toppitch, MIN(0, bottompitch));
if (floorportalstate) EnterSectorPortal(sector_t::floor, 0, lastsector, MAX(0, toppitch), bottompitch);
FPathTraverse it(startpos.x, startpos.y, aimtrace.x, aimtrace.y, PT_ADDLINES | PT_ADDTHINGS | PT_COMPATIBLE | PT_DELTA, startfrac);
intercept_t *in;
Printf("Start AimTraverse, start = %f,%f,%f, vect = %f,%f,%f\n",
startpos.x / 65536., startpos.y / 65536., startpos.y / 65536.,
aimtrace.x / 65536., aimtrace.y / 65536.);
while ((in = it.Next()))
{
line_t* li;
AActor* th;
fixed_t pitch;
fixed_t thingtoppitch;
fixed_t thingbottompitch;
fixed_t dist;
int thingpitch;
if (linetarget.linetarget != NULL && in->frac > linetarget.frac) return; // we already found something better in another portal section.
if (in->isaline)
{
li = in->d.line;
int frontflag = P_PointOnLineSidePrecise(startpos.x, startpos.y, li);
Printf("Found line %d: toppitch = %f, bottompitch = %f\n", int(li - lines), ANGLE2DBL(toppitch), ANGLE2DBL(bottompitch));
if (!(li->flags & ML_TWOSIDED) || (li->flags & ML_BLOCKEVERYTHING))
return; // stop
// Crosses a two sided line.
// A two sided line will restrict the possible target ranges.
FLineOpening open;
P_LineOpening(open, NULL, li, it.Trace().x + FixedMul(it.Trace().dx, in->frac),
it.Trace().y + FixedMul(it.Trace().dy, in->frac), FIXED_MIN, 0, FFCF_NODROPOFF);
// The following code assumes that portals on the front of the line have already been processed.
if (open.range <= 0 || open.bottom >= open.top)
return;
dist = FixedMul(attackrange, in->frac);
if (open.bottom != FIXED_MIN)
{
pitch = -(int)R_PointToAngle2(0, shootz, dist, open.bottom);
if (pitch < bottompitch) bottompitch = pitch;
}
if (open.top != FIXED_MAX)
{
pitch = -(int)R_PointToAngle2(0, shootz, dist, open.top);
if (pitch > toppitch) toppitch = pitch;
}
if (toppitch >= bottompitch)
return;
int planestocheck;
if (!AimTraverse3DFloors(it.Trace(), in, frontflag, &planestocheck))
return;
Printf("After line %d: toppitch = %f, bottompitch = %f, planestocheck = %d\n", int(li - lines), ANGLE2DBL(toppitch), ANGLE2DBL(bottompitch), planestocheck);
sector_t *entersec = frontflag ? li->frontsector : li->backsector;
sector_t *exitsec = frontflag ? li->backsector : li->frontsector;
// check portal in backsector when aiming up/downward is possible, the line doesn't have portals on both sides and there's actually a portal in the backsector
if ((planestocheck & aim_up) && toppitch < 0 && open.top != FIXED_MAX && !entersec->PortalBlocksMovement(sector_t::ceiling))
{
EnterSectorPortal(sector_t::ceiling, in->frac, entersec, toppitch, MIN(0, bottompitch));
}
if ((planestocheck & aim_down) && bottompitch > 0 && open.bottom != FIXED_MIN && !entersec->PortalBlocksMovement(sector_t::floor))
{
EnterSectorPortal(sector_t::floor, in->frac, entersec, MAX(0, toppitch), bottompitch);
}
continue; // shot continues
}
// shoot a thing
th = in->d.thing;
if (th == shootthing)
continue; // can't shoot self
if (aimtarget != NULL && th != aimtarget)
continue; // only care about target, and you're not it
// If we want to start a conversation anything that has one should be
// found, regardless of other settings.
if (!(flags & ALF_CHECKCONVERSATION) || th->Conversation == NULL)
{
if (!(flags & ALF_CHECKNONSHOOTABLE)) // For info CCMD, ignore stuff about GHOST and SHOOTABLE flags
{
if (!(th->flags&MF_SHOOTABLE))
continue; // corpse or something
// check for physical attacks on a ghost
if ((th->flags3 & MF3_GHOST) &&
shootthing->player && // [RH] Be sure shootthing is a player
shootthing->player->ReadyWeapon &&
(shootthing->player->ReadyWeapon->flags2 & MF2_THRUGHOST))
{
continue;
}
}
}
dist = FixedMul(attackrange, in->frac); dist = FixedMul(attackrange, in->frac);
pitch = -(int)R_PointToAngle2(0, shootz, dist, open.bottom); // Don't autoaim certain special actors
if (pitch < bottompitch) if (!cl_doautoaim && th->flags6 & MF6_NOTAUTOAIMED)
bottompitch = pitch;
pitch = -(int)R_PointToAngle2(0, shootz, dist, open.top);
if (pitch > toppitch)
toppitch = pitch;
if (toppitch >= bottompitch)
return; // stop
if (!AimTraverse3DFloors(it.Trace(), in)) return;
continue; // shot continues
}
// shoot a thing
th = in->d.thing;
if (th == shootthing)
continue; // can't shoot self
if (target != NULL && th != target)
continue; // only care about target, and you're not it
// If we want to start a conversation anything that has one should be
// found, regardless of other settings.
if (!(flags & ALF_CHECKCONVERSATION) || th->Conversation == NULL)
{
if (!(flags & ALF_CHECKNONSHOOTABLE)) // For info CCMD, ignore stuff about GHOST and SHOOTABLE flags
{ {
if (!(th->flags&MF_SHOOTABLE)) continue;
continue; // corpse or something }
// check for physical attacks on a ghost // we must do one last check whether the trace has crossed a 3D floor
if ((th->flags3 & MF3_GHOST) && if (lastsector == th->Sector && th->Sector->e->XFloor.ffloors.Size())
shootthing->player && // [RH] Be sure shootthing is a player {
shootthing->player->ReadyWeapon && if (lastceilingplane)
(shootthing->player->ReadyWeapon->flags2 & MF2_THRUGHOST))
{ {
continue; fixed_t ff_top = lastceilingplane->ZatPoint(th);
fixed_t pitch = -(int)R_PointToAngle2(0, shootz, dist, ff_top);
// upper slope intersects with this 3d-floor
if (pitch > toppitch)
{
toppitch = pitch;
}
}
if (lastfloorplane)
{
fixed_t ff_bottom = lastfloorplane->ZatPoint(th);
fixed_t pitch = -(int)R_PointToAngle2(0, shootz, dist, ff_bottom);
// lower slope intersects with this 3d-floor
if (pitch < bottompitch)
{
bottompitch = pitch;
}
} }
} }
}
dist = FixedMul(attackrange, in->frac);
// Don't autoaim certain special actors // check angles to see if the thing can be aimed at
if (!cl_doautoaim && th->flags6 & MF6_NOTAUTOAIMED)
{
continue;
}
// we must do one last check whether the trace has crossed a 3D floor thingtoppitch = -(int)R_PointToAngle2(0, shootz, dist, th->Z() + th->height);
if (lastsector == th->Sector && th->Sector->e->XFloor.ffloors.Size())
{ if (thingtoppitch > bottompitch)
if (lastceilingplane) continue; // shot over the thing
thingbottompitch = -(int)R_PointToAngle2(0, shootz, dist, th->Z());
if (thingbottompitch < toppitch)
continue; // shot under the thing
if (crossedffloors)
{ {
fixed_t ff_top = lastceilingplane->ZatPoint(th); // if 3D floors were in the way do an extra visibility check for safety
fixed_t pitch = -(int)R_PointToAngle2(0, shootz, dist, ff_top); if (!P_CheckSight(shootthing, th, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY))
// upper slope intersects with this 3d-floor
if (pitch > toppitch)
{ {
toppitch = pitch; // the thing can't be seen so we can safely exclude its range from our aiming field
if (thingtoppitch < toppitch)
{
if (thingbottompitch > toppitch) toppitch = thingbottompitch;
}
else if (thingbottompitch>bottompitch)
{
if (thingtoppitch < bottompitch) bottompitch = thingtoppitch;
}
if (toppitch < bottompitch) continue;
else return;
} }
} }
if (lastfloorplane)
// this thing can be hit!
if (thingtoppitch < toppitch)
thingtoppitch = toppitch;
if (thingbottompitch > bottompitch)
thingbottompitch = bottompitch;
thingpitch = thingtoppitch / 2 + thingbottompitch / 2;
if (flags & ALF_CHECK3D)
{ {
fixed_t ff_bottom = lastfloorplane->ZatPoint(th); // We need to do a 3D distance check here because this is nearly always used in
fixed_t pitch = -(int)R_PointToAngle2(0, shootz, dist, ff_bottom); // combination with P_LineAttack. P_LineAttack uses 3D distance but FPathTraverse
// lower slope intersects with this 3d-floor // only 2D. This causes some problems with Hexen's weapons that use different
if (pitch < bottompitch) // attack modes based on distance to target
fixed_t cosine = finecosine[thingpitch >> ANGLETOFINESHIFT];
if (cosine != 0)
{ {
bottompitch = pitch; fixed_t d3 = FixedDiv(FixedMul(P_AproxDistance(it.Trace().dx, it.Trace().dy), in->frac), cosine);
if (d3 > attackrange)
{
return;
}
} }
} }
}
// check angles to see if the thing can be aimed at if ((flags & ALF_NOFRIENDS) && th->IsFriend(friender) && aimtarget == NULL)
thingtoppitch = -(int)R_PointToAngle2(0, shootz, dist, th->Z() + th->height);
if (thingtoppitch > bottompitch)
continue; // shot over the thing
thingbottompitch = -(int)R_PointToAngle2(0, shootz, dist, th->Z());
if (thingbottompitch < toppitch)
continue; // shot under the thing
if (crossedffloors)
{
// if 3D floors were in the way do an extra visibility check for safety
if (!P_CheckSight(shootthing, th, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY))
{ {
// the thing can't be seen so we can safely exclude its range from our aiming field continue;
if (thingtoppitch<toppitch)
{
if (thingbottompitch>toppitch) toppitch = thingbottompitch;
}
else if (thingbottompitch>bottompitch)
{
if (thingtoppitch<bottompitch) bottompitch = thingtoppitch;
}
if (toppitch < bottompitch) continue;
else return;
} }
} else if (sv_smartaim != 0 && !(flags & ALF_FORCENOSMART) && aimtarget == NULL)
// this thing can be hit!
if (thingtoppitch < toppitch)
thingtoppitch = toppitch;
if (thingbottompitch > bottompitch)
thingbottompitch = bottompitch;
thingpitch = thingtoppitch / 2 + thingbottompitch / 2;
if (flags & ALF_CHECK3D)
{
// We need to do a 3D distance check here because this is nearly always used in
// combination with P_LineAttack. P_LineAttack uses 3D distance but FPathTraverse
// only 2D. This causes some problems with Hexen's weapons that use different
// attack modes based on distance to target
fixed_t cosine = finecosine[thingpitch >> ANGLETOFINESHIFT];
if (cosine != 0)
{ {
fixed_t d3 = FixedDiv(FixedMul(P_AproxDistance(it.Trace().dx, it.Trace().dy), in->frac), cosine); // try to be a little smarter about what to aim at!
if (d3 > attackrange) // In particular avoid autoaiming at friends and barrels.
if (th->IsFriend(friender))
{ {
if (sv_smartaim < 2)
{
// friends don't aim at friends (except players), at least not first
Printf("Hit friend %s at %f,%f,%f\n", th->GetClass()->TypeName.GetChars(), th->X() / 65536., th->Y() / 65536., th->Z() / 65536.);
SetResult(thing_friend, in->frac, th, thingpitch);
}
}
else if (!(th->flags3 & MF3_ISMONSTER) && th->player == NULL)
{
if (sv_smartaim < 3)
{
// don't autoaim at barrels and other shootable stuff unless no monsters have been found
Printf("Hit other %s at %f,%f,%f\n", th->GetClass()->TypeName.GetChars(), th->X() / 65536., th->Y() / 65536., th->Z() / 65536.);
SetResult(thing_other, in->frac, th, thingpitch);
}
}
else
{
Printf("Hit target %s at %f,%f,%f\n", th->GetClass()->TypeName.GetChars(), th->X() / 65536., th->Y() / 65536., th->Z() / 65536.);
SetResult(linetarget, in->frac, th, thingpitch);
return; return;
} }
} }
}
if ((flags & ALF_NOFRIENDS) && th->IsFriend(friender))
{
continue;
}
else if (sv_smartaim != 0 && !(flags & ALF_FORCENOSMART))
{
// try to be a little smarter about what to aim at!
// In particular avoid autoaiming at friends and barrels.
if (th->IsFriend(friender))
{
if (sv_smartaim < 2)
{
// friends don't aim at friends (except players), at least not first
thing_friend = th;
pitch_friend = thingpitch;
}
}
else if (!(th->flags3 & MF3_ISMONSTER) && th->player == NULL)
{
if (sv_smartaim < 3)
{
// don't autoaim at barrels and other shootable stuff unless no monsters have been found
thing_other = th;
pitch_other = thingpitch;
}
}
else else
{ {
linetarget = th; Printf("Hit target %s at %f,%f,%f\n", th->GetClass()->TypeName.GetChars(), th->X() / 65536., th->Y() / 65536., th->Z() / 65536.);
aimpitch = thingpitch; SetResult(linetarget, in->frac, th, thingpitch);
return; return;
} }
} }
else
{
linetarget = th;
aimpitch = thingpitch;
return;
}
} }
} };
//============================================================================ //============================================================================
// //
@ -3790,25 +3979,14 @@ void aim_t::AimTraverse(fixed_t startx, fixed_t starty, fixed_t endx, fixed_t en
fixed_t P_AimLineAttack(AActor *t1, angle_t angle, fixed_t distance, FTranslatedLineTarget *pLineTarget, fixed_t vrange, fixed_t P_AimLineAttack(AActor *t1, angle_t angle, fixed_t distance, FTranslatedLineTarget *pLineTarget, fixed_t vrange,
int flags, AActor *target, AActor *friender) int flags, AActor *target, AActor *friender)
{ {
fixed_t x2; fixed_t shootz = t1->Z() + (t1->height >> 1) - t1->floorclip;
fixed_t y2;
aim_t aim;
angle >>= ANGLETOFINESHIFT;
aim.flags = flags;
aim.shootthing = t1;
aim.friender = (friender == NULL) ? t1 : friender;
x2 = t1->X() + (distance >> FRACBITS)*finecosine[angle];
y2 = t1->Y() + (distance >> FRACBITS)*finesine[angle];
aim.shootz = t1->Z() + (t1->height >> 1) - t1->floorclip;
if (t1->player != NULL) if (t1->player != NULL)
{ {
aim.shootz += FixedMul(t1->player->mo->AttackZOffset, t1->player->crouchfactor); shootz += FixedMul(t1->player->mo->AttackZOffset, t1->player->crouchfactor);
} }
else else
{ {
aim.shootz += 8 * FRACUNIT; shootz += 8 * FRACUNIT;
} }
// can't shoot outside view angles // can't shoot outside view angles
@ -3836,65 +4014,34 @@ fixed_t P_AimLineAttack(AActor *t1, angle_t angle, fixed_t distance, FTranslated
} }
} }
} }
aim_t aim;
aim.flags = flags;
aim.shootthing = t1;
aim.friender = (friender == NULL) ? t1 : friender;
aim.aimdir = aim_t::aim_up | aim_t::aim_down;
aim.startpos = t1->Pos();
aim.aimtrace = Vec2Angle(distance, angle);
aim.limitz = aim.shootz = shootz;
aim.toppitch = t1->pitch - vrange; aim.toppitch = t1->pitch - vrange;
aim.bottompitch = t1->pitch + vrange; aim.bottompitch = t1->pitch + vrange;
aim.attackrange = distance; aim.attackrange = distance;
aim.linetarget = NULL;
// for smart aiming
aim.thing_friend = aim.thing_other = NULL;
// Information for tracking crossed 3D floors
aim.aimpitch = t1->pitch; aim.aimpitch = t1->pitch;
aim.crossedffloors = t1->Sector->e->XFloor.ffloors.Size() != 0;
aim.lastsector = t1->Sector; aim.lastsector = t1->Sector;
aim.lastfloorplane = aim.lastceilingplane = NULL; aim.startfrac = 0;
aim.unlinked = false;
aim.aimtarget = target;
// set initial 3d-floor info aim.AimTraverse();
for (unsigned i = 0; i<t1->Sector->e->XFloor.ffloors.Size(); i++)
{
F3DFloor * rover = t1->Sector->e->XFloor.ffloors[i];
fixed_t bottomz = rover->bottom.plane->ZatPoint(t1);
if (bottomz >= t1->Top()) aim.lastceilingplane = rover->bottom.plane; AimTarget *result = aim.Result();
bottomz = rover->top.plane->ZatPoint(t1);
if (bottomz <= t1->Z()) aim.lastfloorplane = rover->top.plane;
}
aim.AimTraverse(t1->X(), t1->Y(), x2, y2, target);
if (!aim.linetarget)
{
if (aim.thing_other)
{
aim.linetarget = aim.thing_other;
aim.aimpitch = aim.pitch_other;
}
else if (aim.thing_friend)
{
aim.linetarget = aim.thing_friend;
aim.aimpitch = aim.pitch_friend;
}
}
if (pLineTarget) if (pLineTarget)
{ {
if (aim.linetarget) *pLineTarget = *result;
{
pLineTarget->linetarget = aim.linetarget;
pLineTarget->hitangle = angle;
pLineTarget->targetPosFromSrc = aim.linetarget->Pos();
pLineTarget->targetAngleFromSrc = aim.linetarget->angle;
pLineTarget->sourcePosFromTarget = t1->Pos();
pLineTarget->sourceAngleFromTarget = t1->angle;
pLineTarget->unlinked = false;
}
else
memset(pLineTarget, 0, sizeof(*pLineTarget));
} }
return aim.linetarget ? aim.aimpitch : t1->pitch; return result->linetarget ? result->pitch : t1->pitch;
} }
@ -4188,11 +4335,7 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance,
if (victim != NULL) if (victim != NULL)
{ {
victim->linetarget = trace.Actor; victim->linetarget = trace.Actor;
victim->hitangle = angle; victim->angleFromSource = R_PointToAngle2(t1->X(), t1->Y(), trace.Actor->X(), trace.Actor->Y());
victim->targetPosFromSrc = trace.Actor->Pos();
victim->targetAngleFromSrc = trace.Actor->angle;
victim->sourcePosFromTarget = t1->Pos();
victim->sourceAngleFromTarget = t1->angle;
victim->unlinked = false; victim->unlinked = false;
} }
} }
@ -4422,7 +4565,7 @@ void P_TraceBleed(int damage, FTranslatedLineTarget *t, AActor *puff)
fixed_t randpitch = (pr_tracebleed() - 128) << 16; fixed_t randpitch = (pr_tracebleed() - 128) << 16;
P_TraceBleed(damage, t->linetarget->X(), t->linetarget->Y(), t->linetarget->Z() + t->linetarget->height / 2, P_TraceBleed(damage, t->linetarget->X(), t->linetarget->Y(), t->linetarget->Z() + t->linetarget->height / 2,
t->linetarget, t->SourceAngleToTarget(), 0); t->linetarget, t->angleFromSource, 0);
} }
//========================================================================== //==========================================================================

View File

@ -210,7 +210,7 @@ void P_LineOpening (FLineOpening &open, AActor *actor, const line_t *linedef,
open.floorpic = front->GetTexture(sector_t::floor); open.floorpic = front->GetTexture(sector_t::floor);
open.floorterrain = front->GetTerrain(sector_t::floor); open.floorterrain = front->GetTerrain(sector_t::floor);
if (bf != FIXED_MIN) open.lowfloor = bf; if (bf != FIXED_MIN) open.lowfloor = bf;
else else if (!(flags & FFCF_NODROPOFF))
{ {
// We must check through the portal for the actual dropoff. // We must check through the portal for the actual dropoff.
// If there's no lines in the lower sections we'd never get a usable value otherwise. // If there's no lines in the lower sections we'd never get a usable value otherwise.
@ -224,7 +224,7 @@ void P_LineOpening (FLineOpening &open, AActor *actor, const line_t *linedef,
open.floorpic = back->GetTexture(sector_t::floor); open.floorpic = back->GetTexture(sector_t::floor);
open.floorterrain = back->GetTerrain(sector_t::floor); open.floorterrain = back->GetTerrain(sector_t::floor);
if (ff != FIXED_MIN) open.lowfloor = ff; if (ff != FIXED_MIN) open.lowfloor = ff;
else else if (!(flags & FFCF_NODROPOFF))
{ {
// We must check through the portal for the actual dropoff. // We must check through the portal for the actual dropoff.
// If there's no lines in the lower sections we'd never get a usable value otherwise. // If there's no lines in the lower sections we'd never get a usable value otherwise.
@ -264,7 +264,8 @@ void P_LineOpening (FLineOpening &open, AActor *actor, const line_t *linedef,
open.abovemidtex = open.touchmidtex = false; open.abovemidtex = open.touchmidtex = false;
} }
open.range = open.top - open.bottom; // avoid overflows in the opening.
open.range = (fixed_t)MIN<QWORD>((QWORD)open.top - open.bottom, FIXED_MAX);
} }

View File

@ -351,9 +351,9 @@ public:
intercept_t *Next(); intercept_t *Next();
FPathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags) FPathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, fixed_t startfrac = 0)
{ {
init(x1, y1, x2, y2, flags); init(x1, y1, x2, y2, flags, startfrac);
} }
void init(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, fixed_t startfrac = 0); void init(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, fixed_t startfrac = 0);
bool PortalRelocate(intercept_t *in, int flags, fixedvec3 *optpos = NULL); bool PortalRelocate(intercept_t *in, int flags, fixedvec3 *optpos = NULL);

View File

@ -1797,7 +1797,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch)
if (!(flags & CPF_NOTURN)) if (!(flags & CPF_NOTURN))
{ {
// turn to face target // turn to face target
self->angle = t.SourceAngleToTarget(); self->angle = t.angleFromSource;
} }
if (flags & CPF_PULLIN) self->flags |= MF_JUSTATTACKED; if (flags & CPF_PULLIN) self->flags |= MF_JUSTATTACKED;