diff --git a/src/actor.h b/src/actor.h index 7dc4bf1ee..51ce9752c 100644 --- a/src/actor.h +++ b/src/actor.h @@ -1401,21 +1401,8 @@ AActor *P_LinePickActor(AActor *t1, angle_t angle, fixed_t distance, int pitch, struct FTranslatedLineTarget { AActor *linetarget; - angle_t hitangle; - fixedvec3 targetPosFromSrc; - angle_t targetAngleFromSrc; - fixedvec3 sourcePosFromTarget; - angle_t sourceAngleFromTarget; + angle_t angleFromSource; 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); - } }; diff --git a/src/g_doom/a_doomweaps.cpp b/src/g_doom/a_doomweaps.cpp index dfff95985..b43b5a1d5 100644 --- a/src/g_doom/a_doomweaps.cpp +++ b/src/g_doom/a_doomweaps.cpp @@ -61,7 +61,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Punch) if (t.linetarget) { S_Sound (self, CHAN_WEAPON, "*fist", 1, ATTN_NORM); - self->angle = t.SourceAngleToTarget(); + self->angle = t.angleFromSource; } return 0; } @@ -232,7 +232,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Saw) // turn to face target if (!(flags & SF_NOTURN)) { - angle = t.SourceAngleToTarget(); + angle = t.angleFromSource; if (angle - self->angle > ANG180) { if (angle - self->angle < (angle_t)(-ANG90 / 20)) @@ -696,7 +696,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BFGSpray) 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); } } diff --git a/src/g_doom/a_scriptedmarine.cpp b/src/g_doom/a_scriptedmarine.cpp index fbea811f7..0f9e48e59 100644 --- a/src/g_doom/a_scriptedmarine.cpp +++ b/src/g_doom/a_scriptedmarine.cpp @@ -293,7 +293,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_M_Saw) S_Sound (self, CHAN_WEAPON, hitsound, 1, ATTN_NORM); // turn to face target - angle = t.SourceAngleToTarget(); + angle = t.angleFromSource; if (angle - self->angle > ANG180) { if (angle - self->angle < (angle_t)(-ANG90/20)) @@ -344,7 +344,7 @@ static void MarinePunch(AActor *self, int damagemul) if (t.linetarget) { S_Sound (self, CHAN_WEAPON, "*fist", 1, ATTN_NORM); - self->angle = t.SourceAngleToTarget(); + self->angle = t.angleFromSource; } } diff --git a/src/g_heretic/a_chicken.cpp b/src/g_heretic/a_chicken.cpp index 3a863fbba..fb30df98d 100644 --- a/src/g_heretic/a_chicken.cpp +++ b/src/g_heretic/a_chicken.cpp @@ -189,7 +189,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_BeakAttackPL1) P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, "BeakPuff", true, &t); if (t.linetarget) { - player->mo->angle = t.SourceAngleToTarget(); + player->mo->angle = t.angleFromSource; } P_PlayPeck (player->mo); 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); if (t.linetarget) { - player->mo->angle = t.SourceAngleToTarget(); + player->mo->angle = t.angleFromSource; } P_PlayPeck (player->mo); player->chickenPeck = 12; diff --git a/src/g_heretic/a_hereticweaps.cpp b/src/g_heretic/a_hereticweaps.cpp index 3dfed825d..d85faaeb5 100644 --- a/src/g_heretic/a_hereticweaps.cpp +++ b/src/g_heretic/a_hereticweaps.cpp @@ -94,7 +94,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StaffAttack) { //S_StartSound(player->mo, sfx_stfhit); // turn to face target - self->angle = t.SourceAngleToTarget(); + self->angle = t.angleFromSource; } return 0; } @@ -324,7 +324,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GauntletAttack) S_Sound (self, CHAN_AUTO, "weapons/gauntletshit", 1, ATTN_NORM); } // turn to face target - angle = t.SourceAngleToTarget(); + angle = t.angleFromSource; if (angle-self->angle > ANG180) { if ((int)(angle-self->angle) < -ANG90/20) @@ -675,7 +675,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_DeathBallImpact) if (t.linetarget && self->target != t.linetarget) { self->tracer = t.linetarget; - angle = t.SourceAngleToTarget(); + angle = t.angleFromSource; newAngle = true; break; } diff --git a/src/g_hexen/a_clericstaff.cpp b/src/g_hexen/a_clericstaff.cpp index 3746db3e0..fcad8f20c 100644 --- a/src/g_hexen/a_clericstaff.cpp +++ b/src/g_hexen/a_clericstaff.cpp @@ -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); 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) && (!(t.linetarget->flags2&(MF2_DORMANT | MF2_INVULNERABLE)))) { diff --git a/src/g_hexen/a_fighteraxe.cpp b/src/g_hexen/a_fighteraxe.cpp index 4ce3aa866..aa6a6ec5c 100644 --- a/src/g_hexen/a_fighteraxe.cpp +++ b/src/g_hexen/a_fighteraxe.cpp @@ -245,7 +245,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FAxeAttack) { 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); useMana++; diff --git a/src/g_hexen/a_fighterhammer.cpp b/src/g_hexen/a_fighterhammer.cpp index d85360adc..eadf7a05c 100644 --- a/src/g_hexen/a_fighterhammer.cpp +++ b/src/g_hexen/a_fighterhammer.cpp @@ -57,7 +57,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FHammerAttack) AdjustPlayerAngle(pmo, &t); 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 goto hammerdone; @@ -73,7 +73,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FHammerAttack) AdjustPlayerAngle(pmo, &t); 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 goto hammerdone; diff --git a/src/g_hexen/a_fighterplayer.cpp b/src/g_hexen/a_fighterplayer.cpp index 4ad823ac7..24d54dd4d 100644 --- a/src/g_hexen/a_fighterplayer.cpp +++ b/src/g_hexen/a_fighterplayer.cpp @@ -30,7 +30,7 @@ void AdjustPlayerAngle (AActor *pmo, FTranslatedLineTarget *t) angle_t angle; int difference; - angle = t->SourceAngleToTarget(); + angle = t->angleFromSource; difference = (int)angle - (int)pmo->angle; 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 || (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); return true; diff --git a/src/g_hexen/a_magecone.cpp b/src/g_hexen/a_magecone.cpp index f023dc110..43ed1dddc 100644 --- a/src/g_hexen/a_magecone.cpp +++ b/src/g_hexen/a_magecone.cpp @@ -82,7 +82,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireConePL1) slope = P_AimLineAttack (self, angle, MELEERANGE, &t, 0, ALF_CHECK3D); 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; break; } diff --git a/src/g_strife/a_strifeweapons.cpp b/src/g_strife/a_strifeweapons.cpp index 83dfdc9eb..0c7979aff 100644 --- a/src/g_strife/a_strifeweapons.cpp +++ b/src/g_strife/a_strifeweapons.cpp @@ -120,7 +120,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_JabDagger) S_Sound (self, CHAN_WEAPON, t.linetarget->flags & MF_NOBLOOD ? "misc/metalhit" : "misc/meathit", 1, ATTN_NORM); - self->angle = t.SourceAngleToTarget(); + self->angle = t.angleFromSource; self->flags |= MF_JUSTATTACKED; P_DaggerAlert (self, t.linetarget); } diff --git a/src/p_local.h b/src/p_local.h index 7f30fc795..19315dedb 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -300,6 +300,7 @@ enum FFCF_NOFLOOR = 32, 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_NODROPOFF = 256, // Caller does not need a dropoff (saves some time when checking portals) }; void P_FindFloorCeiling (AActor *actor, int flags=0); diff --git a/src/p_map.cpp b/src/p_map.cpp index 81f64bac1..3748fafe9 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -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 { + enum + { + aim_up = 1, + aim_down = 2 + }; + fixed_t aimpitch; fixed_t attackrange; fixed_t shootz; // Height if not aiming up or down + fixed_t limitz; // height limit for portals to avoid bad setups AActor* shootthing; AActor* friender; // actor to check friendliness again + AActor* aimtarget; // if we want to aim at precisely this target. fixed_t toppitch, bottompitch; - AActor * linetarget; - AActor * thing_friend, *thing_other; - angle_t pitch_friend, pitch_other; + AimTarget linetarget; + AimTarget thing_friend, thing_other; + int flags; sector_t * lastsector; secplane_t * lastfloorplane; secplane_t * lastceilingplane; + int aimdir; + fixedvec3 startpos; + fixedvec2 aimtrace; + fixed_t startfrac; + bool crossedffloors; + bool unlinked; - bool AimTraverse3DFloors(const divline_t &trace, intercept_t * in); - - 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()) + // Creates a clone of this structure with the basic info copied. + aim_t Clone() { - int frontflag; - F3DFloor* rover; - int highpitch, lowpitch; + aim_t cloned; - 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); + cloned.aimtrace = aimtrace; + cloned.aimpitch = aimpitch; + 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; - - for (unsigned k = 0; ke->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; - } - } - } - else if (lowpitch >= bottompitch) - { - // blocks lower edge of view - if (highpitchtop.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 - } + res.linetarget = th; + res.pitch = pitch; + res.angleFromSource = R_PointToAngle2(startpos.x, startpos.y, th->X(), th->Y()); + res.unlinked = unlinked; + res.frac = frac; } } - lastsector = nextsector; - 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())) + void SetResult(AimTarget &res, AimTarget &set) { - line_t* li; - AActor* th; - fixed_t pitch; - fixed_t thingtoppitch; - fixed_t thingbottompitch; - fixed_t dist; - int thingpitch; - - if (in->isaline) + if (res.frac > set.frac) { - li = in->d.line; + res = set; + } + } - if (!(li->flags & ML_TWOSIDED) || (li->flags & ML_BLOCKEVERYTHING)) - return; // stop + //============================================================================ + // + // Result + // + //============================================================================ - // 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)); + AimTarget *Result() + { + AimTarget *result = &linetarget; + if (result->linetarget == NULL) + { + 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 (highpitchtop.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); - pitch = -(int)R_PointToAngle2(0, shootz, dist, open.bottom); - if (pitch < bottompitch) - 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 + // Don't autoaim certain special actors + if (!cl_doautoaim && th->flags6 & MF6_NOTAUTOAIMED) { - if (!(th->flags&MF_SHOOTABLE)) - continue; // corpse or something + continue; + } - // 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)) + // we must do one last check whether the trace has crossed a 3D floor + if (lastsector == th->Sector && th->Sector->e->XFloor.ffloors.Size()) + { + if (lastceilingplane) { - 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 - if (!cl_doautoaim && th->flags6 & MF6_NOTAUTOAIMED) - { - continue; - } + // check angles to see if the thing can be aimed at - // we must do one last check whether the trace has crossed a 3D floor - if (lastsector == th->Sector && th->Sector->e->XFloor.ffloors.Size()) - { - if (lastceilingplane) + 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) { - 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) + // if 3D floors were in the way do an extra visibility check for safety + if (!P_CheckSight(shootthing, th, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY)) { - 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); - fixed_t pitch = -(int)R_PointToAngle2(0, shootz, dist, ff_bottom); - // lower slope intersects with this 3d-floor - if (pitch < bottompitch) + // 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) { - 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 - - 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)) + if ((flags & ALF_NOFRIENDS) && th->IsFriend(friender) && aimtarget == NULL) { - // the thing can't be seen so we can safely exclude its range from our aiming field - if (thingtoppitchtoppitch) toppitch = thingbottompitch; - } - else if (thingbottompitch>bottompitch) - { - if (thingtoppitch 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) + else if (sv_smartaim != 0 && !(flags & ALF_FORCENOSMART) && aimtarget == NULL) { - fixed_t d3 = FixedDiv(FixedMul(P_AproxDistance(it.Trace().dx, it.Trace().dy), in->frac), cosine); - if (d3 > attackrange) + // 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 + 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; } } - } - - 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 { - linetarget = th; - aimpitch = thingpitch; + 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; } } - 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, int flags, AActor *target, AActor *friender) { - fixed_t x2; - 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; + fixed_t shootz = t1->Z() + (t1->height >> 1) - t1->floorclip; if (t1->player != NULL) { - aim.shootz += FixedMul(t1->player->mo->AttackZOffset, t1->player->crouchfactor); + shootz += FixedMul(t1->player->mo->AttackZOffset, t1->player->crouchfactor); } else { - aim.shootz += 8 * FRACUNIT; + shootz += 8 * FRACUNIT; } // 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.bottompitch = t1->pitch + vrange; - 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.crossedffloors = t1->Sector->e->XFloor.ffloors.Size() != 0; aim.lastsector = t1->Sector; - aim.lastfloorplane = aim.lastceilingplane = NULL; + aim.startfrac = 0; + aim.unlinked = false; + aim.aimtarget = target; - // set initial 3d-floor info - for (unsigned i = 0; iSector->e->XFloor.ffloors.Size(); i++) - { - F3DFloor * rover = t1->Sector->e->XFloor.ffloors[i]; - fixed_t bottomz = rover->bottom.plane->ZatPoint(t1); + aim.AimTraverse(); - 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 (aim.linetarget) - { - 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)); + *pLineTarget = *result; } - 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) { victim->linetarget = trace.Actor; - victim->hitangle = angle; - victim->targetPosFromSrc = trace.Actor->Pos(); - victim->targetAngleFromSrc = trace.Actor->angle; - victim->sourcePosFromTarget = t1->Pos(); - victim->sourceAngleFromTarget = t1->angle; + victim->angleFromSource = R_PointToAngle2(t1->X(), t1->Y(), trace.Actor->X(), trace.Actor->Y()); victim->unlinked = false; } } @@ -4422,7 +4565,7 @@ void P_TraceBleed(int damage, FTranslatedLineTarget *t, AActor *puff) fixed_t randpitch = (pr_tracebleed() - 128) << 16; 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); } //========================================================================== diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index be5fa34de..a00bc3b87 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -210,7 +210,7 @@ void P_LineOpening (FLineOpening &open, AActor *actor, const line_t *linedef, open.floorpic = front->GetTexture(sector_t::floor); open.floorterrain = front->GetTerrain(sector_t::floor); if (bf != FIXED_MIN) open.lowfloor = bf; - else + else if (!(flags & FFCF_NODROPOFF)) { // 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. @@ -224,7 +224,7 @@ void P_LineOpening (FLineOpening &open, AActor *actor, const line_t *linedef, open.floorpic = back->GetTexture(sector_t::floor); open.floorterrain = back->GetTerrain(sector_t::floor); if (ff != FIXED_MIN) open.lowfloor = ff; - else + else if (!(flags & FFCF_NODROPOFF)) { // 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. @@ -264,7 +264,8 @@ void P_LineOpening (FLineOpening &open, AActor *actor, const line_t *linedef, open.abovemidtex = open.touchmidtex = false; } - open.range = open.top - open.bottom; + // avoid overflows in the opening. + open.range = (fixed_t)MIN((QWORD)open.top - open.bottom, FIXED_MAX); } diff --git a/src/p_maputl.h b/src/p_maputl.h index b4155995f..5b702a2d1 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -351,9 +351,9 @@ public: 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); bool PortalRelocate(intercept_t *in, int flags, fixedvec3 *optpos = NULL); diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index fda1a4477..8f5d563ef 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1797,7 +1797,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) if (!(flags & CPF_NOTURN)) { // turn to face target - self->angle = t.SourceAngleToTarget(); + self->angle = t.angleFromSource; } if (flags & CPF_PULLIN) self->flags |= MF_JUSTATTACKED;