diff --git a/src/actor.h b/src/actor.h index 8695986b2b..2da2c44cd0 100644 --- a/src/actor.h +++ b/src/actor.h @@ -1409,21 +1409,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 dfff959859..b43b5a1d50 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 fbea811f72..0f9e48e591 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 3a863fbba2..fb30df98d3 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 3dfed825de..d85faaeb55 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 3746db3e04..fcad8f20cc 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 4ce3aa866b..aa6a6ec5cf 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 d85360adcc..eadf7a05cb 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 4ad823ac74..24d54dd4de 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 f023dc1100..43ed1dddc0 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 83dfdc9ebe..0c7979affa 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/namedef.h b/src/namedef.h index 567d43f5ca..cca8727061 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -690,4 +690,5 @@ xx(Max_10_Exp) xx(__decorate_internal_int__) xx(__decorate_internal_bool__) -xx(__decorate_internal_state__) \ No newline at end of file +xx(__decorate_internal_state__) +xx(__decorate_internal_float__) diff --git a/src/p_local.h b/src/p_local.h index 7f30fc795a..19315dedb4 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 81f64bac1e..0fe73229cb 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -2837,8 +2837,7 @@ void FSlide::SlideTraverse(fixed_t startx, fixed_t starty, fixed_t endx, fixed_t } // set openrange, opentop, openbottom - P_LineOpening(open, slidemo, li, it.Trace().x + FixedMul(it.Trace().dx, in->frac), - it.Trace().y + FixedMul(it.Trace().dy, in->frac)); + P_LineOpening(open, slidemo, li, it.InterceptPoint(in)); if (open.range < slidemo->height) goto isblocking; // doesn't fit @@ -3192,8 +3191,7 @@ bool FSlide::BounceTraverse(fixed_t startx, fixed_t starty, fixed_t endx, fixed_ } - P_LineOpening(open, slidemo, li, it.Trace().x + FixedMul(it.Trace().dx, in->frac), - it.Trace().y + FixedMul(it.Trace().dy, in->frac)); // set openrange, opentop, openbottom + P_LineOpening(open, slidemo, li, it.InterceptPoint(in)); // set openrange, opentop, openbottom if (open.range < slidemo->height) goto bounceblocking; // doesn't fit @@ -3440,346 +3438,581 @@ 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; - - 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)); - - if (open.bottom >= open.top) - return; // stop - - 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 + res = set; } + } - // shoot a thing - th = in->d.thing; - if (th == shootthing) - continue; // can't shoot self + //============================================================================ + // + // Result + // + //============================================================================ - 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) + AimTarget *Result() + { + AimTarget *result = &linetarget; + if (result->linetarget == NULL) { - if (!(flags & ALF_CHECKNONSHOOTABLE)) // For info CCMD, ignore stuff about GHOST and SHOOTABLE flags + if (thing_other.linetarget != NULL) { - 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; - } + result = &thing_other; + } + else if (thing_friend.linetarget != NULL) + { + result = &thing_friend; } } - dist = FixedMul(attackrange, in->frac); + return result; + } - // Don't autoaim certain special actors - if (!cl_doautoaim && th->flags6 & MF6_NOTAUTOAIMED) - { - continue; - } - // we must do one last check whether the trace has crossed a 3D floor - if (lastsector == th->Sector && th->Sector->e->XFloor.ffloors.Size()) + //============================================================================ + // + // 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()) { - if (lastceilingplane) + 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++) { - 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) + sector_t * s = i == 1 ? li->frontsector : li->backsector; + + for (unsigned k = 0; k < s->e->XFloor.ffloors.Size(); k++) { - 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; + 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 } } } - // check angles to see if the thing can be aimed at + lastsector = nextsector; + lastceilingplane = nexttopplane; + lastfloorplane = nextbottomplane; + return true; + } - thingtoppitch = -(int)R_PointToAngle2(0, shootz, dist, th->Z() + th->height); + //============================================================================ + // + // traverses a sector portal + // + //============================================================================ - if (thingtoppitch > bottompitch) - continue; // shot over the thing + 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(); - thingbottompitch = -(int)R_PointToAngle2(0, shootz, dist, th->Z()); - if (thingbottompitch < toppitch) - continue; // shot under the thing + 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.startfrac = frac + FixedDiv(FRACUNIT, attackrange); // this is to skip the transition line to the portal which would produce a bogus opening + newtrace.lastsector = P_PointInSector(newtrace.startpos.x + FixedMul(aimtrace.x, newtrace.startfrac) , newtrace.startpos.y + FixedMul(aimtrace.y, newtrace.startfrac)); + newtrace.limitz = portal->threshold; + Printf("-----Entering %s portal from sector %d to sector %d\n", position ? "ceiling" : "floor", lastsector->sectornum, newtrace.lastsector->sectornum); + 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"); + } - if (crossedffloors) + //============================================================================ + // + // traverses a line portal + // simply calling PortalRelocate does not work here because more needs to be set up + // + //============================================================================ + + void EnterLinePortal(line_t *li, fixed_t frac) + { + aim_t newtrace = Clone(); + + FLinePortal *port = li->getPortal(); + line_t *dest = port->mDestination; + + newtrace.toppitch = toppitch; + newtrace.bottompitch = bottompitch; + newtrace.aimdir = aimdir; + newtrace.unlinked = (port->mType != PORTT_LINKED); + newtrace.startpos = startpos; + newtrace.aimtrace = aimtrace; + P_TranslatePortalXY(li, dest, newtrace.startpos.x, newtrace.startpos.y); + P_TranslatePortalZ(li, dest, newtrace.startpos.z); + P_TranslatePortalVXVY(li, dest, newtrace.aimtrace.x, newtrace.aimtrace.y); + + newtrace.startfrac = frac + FixedDiv(FRACUNIT, attackrange); // this is to skip the transition line to the portal which would produce a bogus opening + + fixed_t x = newtrace.startpos.x + FixedMul(newtrace.aimtrace.x, newtrace.startfrac); + fixed_t y = newtrace.startpos.y + FixedMul(newtrace.aimtrace.y, newtrace.startfrac); + + newtrace.lastsector = P_PointInSector(x, y); + P_TranslatePortalZ(li, dest, limitz); + Printf("-----Entering line portal from sector %d to sector %d\n", lastsector->sectornum, newtrace.lastsector->sectornum); + newtrace.AimTraverse(); + SetResult(linetarget, newtrace.linetarget); + SetResult(thing_friend, newtrace.thing_friend); + SetResult(thing_other, newtrace.thing_other); + } + + + //============================================================================ + // + // 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 3D floors were in the way do an extra visibility check for safety - if (!P_CheckSight(shootthing, th, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY)) + if ((rover->flags & FF_SHOOTTHROUGH) || !(rover->flags & FF_EXISTS)) continue; + + fixed_t bottomz = rover->bottom.plane->ZatPoint(startpos); + + if (bottomz >= startpos.z + shootthing->height) { - // 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 (thingtoppitchbottom.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); - // this thing can be hit! - if (thingtoppitch < toppitch) - thingtoppitch = toppitch; + FPathTraverse it(startpos.x, startpos.y, aimtrace.x, aimtrace.y, PT_ADDLINES | PT_ADDTHINGS | PT_COMPATIBLE | PT_DELTA, startfrac); + intercept_t *in; - if (thingbottompitch > bottompitch) - thingbottompitch = bottompitch; - - thingpitch = thingtoppitch / 2 + thingbottompitch / 2; - - if (flags & ALF_CHECK3D) + 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())) { - // 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) + 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) { - fixed_t d3 = FixedDiv(FixedMul(P_AproxDistance(it.Trace().dx, it.Trace().dy), in->frac), cosine); - if (d3 > attackrange) + 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->isLinePortal() && frontflag == 0) { + EnterLinePortal(li, in->frac); 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) + + 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.InterceptPoint(in), 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) { - // friends don't aim at friends (except players), at least not first - thing_friend = th; - pitch_friend = thingpitch; + 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; + lastsector = entersec; + // 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; + } } } - else if (!(th->flags3 & MF3_ISMONSTER) && th->player == NULL) + dist = FixedMul(attackrange, in->frac); + + // Don't autoaim certain special actors + if (!cl_doautoaim && th->flags6 & MF6_NOTAUTOAIMED) { - if (sv_smartaim < 3) + continue; + } + + // 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) { - // don't autoaim at barrels and other shootable stuff unless no monsters have been found - thing_other = th; - pitch_other = thingpitch; + 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; + } + } + } + + // 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 (!unlinked && !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 + if (thingtoppitch < toppitch) + { + if (thingbottompitch > toppitch) toppitch = thingbottompitch; + } + else if (thingbottompitch>bottompitch) + { + if (thingtoppitch < bottompitch) bottompitch = thingtoppitch; + } + if (toppitch < bottompitch) continue; + else return; + } + } + + // 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); + if (d3 > attackrange) + { + return; + } + } + } + + if ((flags & ALF_NOFRIENDS) && th->IsFriend(friender) && aimtarget == NULL) + { + continue; + } + else if (sv_smartaim != 0 && !(flags & ALF_FORCENOSMART) && aimtarget == NULL) + { + // 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; } } 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 +4023,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 +4058,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 +4379,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 +4609,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); } //========================================================================== @@ -4795,8 +4982,7 @@ bool P_UseTraverse(AActor *usething, fixed_t startx, fixed_t starty, fixed_t end } else { - P_LineOpening(open, NULL, in->d.line, it.Trace().x + FixedMul(it.Trace().dx, in->frac), - it.Trace().y + FixedMul(it.Trace().dy, in->frac)); + P_LineOpening(open, NULL, in->d.line, it.InterceptPoint(in)); } if (open.range <= 0 || (in->d.line->special != 0 && (i_compatflags & COMPATF_USEBLOCKING))) @@ -4904,8 +5090,7 @@ bool P_NoWayTraverse(AActor *usething, fixed_t startx, fixed_t starty, fixed_t e if (ld->special) continue; if (ld->isLinePortal()) return false; if (ld->flags&(ML_BLOCKING | ML_BLOCKEVERYTHING | ML_BLOCK_PLAYERS)) return true; - P_LineOpening(open, NULL, ld, it.Trace().x + FixedMul(it.Trace().dx, in->frac), - it.Trace().y + FixedMul(it.Trace().dy, in->frac)); + P_LineOpening(open, NULL, ld, it.InterceptPoint(in)); if (open.range <= 0 || open.bottom > usething->Z() + usething->MaxStepHeight || open.top < usething->Top()) return true; @@ -4989,8 +5174,7 @@ bool P_UsePuzzleItem(AActor *PuzzleItemUser, int PuzzleItemType) { // Check line if (in->d.line->special != UsePuzzleItem) { - P_LineOpening(open, NULL, in->d.line, it.Trace().x + FixedMul(it.Trace().dx, in->frac), - it.Trace().y + FixedMul(it.Trace().dy, in->frac)); + P_LineOpening(open, NULL, in->d.line, it.InterceptPoint(in)); if (open.range <= 0) { return false; // can't use through a wall diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index be5fa34deb..2a8c0efda1 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); } @@ -1286,9 +1287,33 @@ void FPathTraverse::AddThingIntercepts (int bx, int by, FBlockThingsIterator &it fixed_t frac = P_InterceptVector (&trace, &line); if (frac < startfrac) { // behind source + if (startfrac > 0) + { + // check if the trace starts within this actor + switch (i) + { + case 0: + line.y -= 2 * thing->radius; + break; + + case 1: + line.x -= 2 * thing->radius; + break; + + case 2: + line.y += 2 * thing->radius; + break; + + case 3: + line.x += 2 * thing->radius; + break; + } + fixed_t frac2 = P_InterceptVector(&trace, &line); + if (frac2 >= startfrac) goto addit; + } continue; } - + addit: intercept_t newintercept; newintercept.frac = frac; newintercept.isaline = false; @@ -1421,16 +1446,6 @@ void FPathTraverse::init (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int fl int mapystep; int count; - - validcount++; - intercept_index = intercepts.Size(); - this->startfrac = startfrac; - - if ( ((x1-bmaporgx)&(MAPBLOCKSIZE-1)) == 0) - x1 += FRACUNIT; // don't side exactly on a line - - if ( ((y1-bmaporgy)&(MAPBLOCKSIZE-1)) == 0) - y1 += FRACUNIT; // don't side exactly on a line trace.x = x1; trace.y = y1; @@ -1444,9 +1459,30 @@ void FPathTraverse::init (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int fl trace.dx = x2 - x1; trace.dy = y2 - y1; } + if (startfrac > 0) + { + fixed_t startdx = FixedMul(trace.dx, startfrac); + fixed_t startdy = FixedMul(trace.dy, startfrac); - _x1 = (long long)x1 + FixedMul(trace.dx, startfrac) - bmaporgx; - _y1 = (long long)y1 + FixedMul(trace.dy, startfrac) - bmaporgy; + x1 += startdx; + y1 += startdy; + x2 = trace.dx - startdx; + y2 = trace.dy - startdy; + flags |= PT_DELTA; + } + + validcount++; + intercept_index = intercepts.Size(); + this->startfrac = startfrac; + + if ( ((x1-bmaporgx)&(MAPBLOCKSIZE-1)) == 0) + x1 += FRACUNIT; // don't side exactly on a line + + if ( ((y1-bmaporgy)&(MAPBLOCKSIZE-1)) == 0) + y1 += FRACUNIT; // don't side exactly on a line + + _x1 = (long long)x1 - bmaporgx; + _y1 = (long long)y1 - bmaporgy; x1 -= bmaporgx; y1 -= bmaporgy; xt1 = int(_x1 >> MAPBLOCKSHIFT); @@ -1615,7 +1651,7 @@ void FPathTraverse::init (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int fl // //=========================================================================== -bool FPathTraverse::PortalRelocate(intercept_t *in, int flags, fixedvec3 *optpos) +int FPathTraverse::PortalRelocate(intercept_t *in, int flags, fixedvec3 *optpos) { if (!in->isaline || !in->d.line->isLinePortal()) return false; if (P_PointOnLineSidePrecise(trace.x, trace.y, in->d.line) == 1) return false; @@ -1635,7 +1671,7 @@ bool FPathTraverse::PortalRelocate(intercept_t *in, int flags, fixedvec3 *optpos } intercepts.Resize(intercept_index); init(hitx, hity, endx, endy, flags, in->frac); - return true; + return in->d.line->getPortal()->mType == PORTT_LINKED? 1:-1; } //=========================================================================== diff --git a/src/p_maputl.h b/src/p_maputl.h index b4155995fa..7f045c24f6 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -26,7 +26,6 @@ struct intercept_t } d; }; - //========================================================================== // // P_PointOnLineSide @@ -107,6 +106,10 @@ struct FLineOpening }; void P_LineOpening (FLineOpening &open, AActor *thing, const line_t *linedef, fixed_t x, fixed_t y, fixed_t refx=FIXED_MIN, fixed_t refy=0, int flags=0); +inline void P_LineOpening(FLineOpening &open, AActor *thing, const line_t *linedef, fixedvec2 xy, fixed_t refx = FIXED_MIN, fixed_t refy = 0, int flags = 0) +{ + P_LineOpening(open, thing, linedef, xy.x, xy.y, refx, refy, flags); +} class FBoundingBox; struct polyblock_t; @@ -351,14 +354,24 @@ 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); + int PortalRelocate(intercept_t *in, int flags, fixedvec3 *optpos = NULL); virtual ~FPathTraverse(); const divline_t &Trace() const { return trace; } + + inline fixedvec2 InterceptPoint(const intercept_t *in) + { + return + { + trace.x + FixedMul(trace.dx, in->frac), + trace.y + FixedMul(trace.dy, in->frac) + }; + } + }; //============================================================================ diff --git a/src/portal.cpp b/src/portal.cpp index 11d676a360..01afb0bc48 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -709,14 +709,13 @@ fixedvec2 P_GetOffsetPosition(fixed_t x, fixed_t y, fixed_t dx, fixed_t dy) // Teleport portals are intentionally ignored since skipping this stuff is their entire reason for existence. if (port->mFlags & PORTF_INTERACTIVE) { - fixed_t hitdx = FixedMul(it.Trace().dx, in->frac); - fixed_t hitdy = FixedMul(it.Trace().dy, in->frac); + fixedvec2 hit = it.InterceptPoint(in); if (port->mType == PORTT_LINKED) { // optimized handling for linked portals where we only need to add an offset. - actx = it.Trace().x + hitdx + port->mXDisplacement; - acty = it.Trace().y + hitdy + port->mYDisplacement; + hit.x += port->mXDisplacement; + hit.y += port->mYDisplacement; dest.x += port->mXDisplacement; dest.y += port->mYDisplacement; } @@ -724,15 +723,12 @@ fixedvec2 P_GetOffsetPosition(fixed_t x, fixed_t y, fixed_t dx, fixed_t dy) { // interactive ones are more complex because the vector may be rotated. // Note: There is no z-translation here, there's just too much code in the engine that wouldn't be able to handle interactive portals with a height difference. - actx = it.Trace().x + hitdx; - acty = it.Trace().y + hitdy; - - P_TranslatePortalXY(line, out, actx, acty); + P_TranslatePortalXY(line, out, hit.x, hit.y); P_TranslatePortalXY(line, out, dest.x, dest.y); } // update the fields, end this trace and restart from the new position - dx = dest.x - actx; - dy = dest.y - acty; + dx = dest.x - hit.x; + dy = dest.y - hit.y; repeat = true; } @@ -1108,9 +1104,16 @@ bool P_CollectConnectedGroups(int startgroup, const fixedvec3 &position, fixed_t bool retval = false; out.inited = true; + + processMask.setSize(Displacements.size); + if (Displacements.size == 1) + { + processMask.setBit(startgroup); + return false; + } + if (linkedPortals.Size() != 0) { - processMask.setSize(linkedPortals.Size()); processMask.clear(); foundPortals.Clear(); diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index c1724c2c56..8f5d563efe 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -360,8 +360,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetGibHealth) //=========================================================================== // // __decorate_internal_state__ +// __decorate_internal_int__ +// __decorate_internal_bool__ +// __decorate_internal_float__ // -// Returns the state passed in. +// Placeholders for forcing DECORATE to cast numbers. If actually called, +// returns whatever was passed. // //=========================================================================== @@ -373,14 +377,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, __decorate_internal_state__) ACTION_RETURN_STATE(returnme); } -//=========================================================================== -// -// __decorate_internal_int__ -// -// Returns the int passed in. -// -//=========================================================================== - DEFINE_ACTION_FUNCTION_PARAMS(AActor, __decorate_internal_int__) { PARAM_PROLOGUE; @@ -389,14 +385,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, __decorate_internal_int__) ACTION_RETURN_INT(returnme); } -//=========================================================================== -// -// __decorate_internal_bool__ -// -// Returns the bool passed in. -// -//=========================================================================== - DEFINE_ACTION_FUNCTION_PARAMS(AActor, __decorate_internal_bool__) { PARAM_PROLOGUE; @@ -405,6 +393,19 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, __decorate_internal_bool__) ACTION_RETURN_BOOL(returnme); } +DEFINE_ACTION_FUNCTION_PARAMS(AActor, __decorate_internal_float__) +{ + PARAM_PROLOGUE; + PARAM_OBJECT(self, AActor); + PARAM_FLOAT(returnme); + if (numret > 0) + { + ret->SetFloat(returnme); + return 1; + } + return 0; +} + //========================================================================== // // A_RearrangePointers @@ -1796,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; diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 9ad8e207f9..8239f0e475 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -3420,7 +3420,8 @@ bool FxVMFunctionCall::CheckEmitCast(VMFunctionBuilder *build, bool returnit, Ex FName funcname = Function->SymbolName; if (funcname == NAME___decorate_internal_int__ || funcname == NAME___decorate_internal_bool__ || - funcname == NAME___decorate_internal_state__) + funcname == NAME___decorate_internal_state__ || + funcname == NAME___decorate_internal_float__) { FxExpression *arg = (*ArgList)[0]; if (returnit) diff --git a/src/thingdef/thingdef_states.cpp b/src/thingdef/thingdef_states.cpp index c87c9f316a..ae18051190 100644 --- a/src/thingdef/thingdef_states.cpp +++ b/src/thingdef/thingdef_states.cpp @@ -711,6 +711,8 @@ FName CheckCastKludges(FName in) return NAME___decorate_internal_bool__; case NAME_State: return NAME___decorate_internal_state__; + case NAME_Float: + return NAME___decorate_internal_float__; default: return in; } diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 3bc3772bd1..ad7e9e9302 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -356,4 +356,5 @@ ACTOR Actor native //: Thinker native state __decorate_internal_state__(state); native int __decorate_internal_int__(int); native bool __decorate_internal_bool__(bool); + native float __decorate_internal_float__(float); }