From c160121f4535d9d5aab394e01c585e942a3463c0 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Tue, 1 Mar 2016 11:36:15 -0600 Subject: [PATCH 1/6] Add float casts to DECORATE for the sake of completeness --- src/namedef.h | 3 ++- src/thingdef/thingdef_codeptr.cpp | 35 ++++++++++++++-------------- src/thingdef/thingdef_expression.cpp | 3 ++- src/thingdef/thingdef_states.cpp | 2 ++ wadsrc/static/actors/actor.txt | 1 + 5 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/namedef.h b/src/namedef.h index 567d43f5c..cca872706 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/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index c1724c2c5..fda1a4477 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 diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 9ad8e207f..8239f0e47 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 c87c9f316..ae1805119 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 3bc3772bd..ad7e9e930 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); } From f5fd0df0775d7a339fa205198645009b408e29d4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 2 Mar 2016 20:44:02 +0100 Subject: [PATCH 2/6] - made P_AimLineAttack work through sector portals. Note: Test output not removed yet! --- src/actor.h | 15 +- src/g_doom/a_doomweaps.cpp | 6 +- src/g_doom/a_scriptedmarine.cpp | 4 +- src/g_heretic/a_chicken.cpp | 4 +- src/g_heretic/a_hereticweaps.cpp | 6 +- src/g_hexen/a_clericstaff.cpp | 2 +- src/g_hexen/a_fighteraxe.cpp | 2 +- src/g_hexen/a_fighterhammer.cpp | 4 +- src/g_hexen/a_fighterplayer.cpp | 4 +- src/g_hexen/a_magecone.cpp | 2 +- src/g_strife/a_strifeweapons.cpp | 2 +- src/p_local.h | 1 + src/p_map.cpp | 837 +++++++++++++++++------------- src/p_maputl.cpp | 7 +- src/p_maputl.h | 4 +- src/thingdef/thingdef_codeptr.cpp | 2 +- 16 files changed, 517 insertions(+), 385 deletions(-) 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; From 63d838c0a7e39974dfa95b3e5822b300b9b8952e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 2 Mar 2016 22:26:47 +0100 Subject: [PATCH 3/6] - fixed: P_CollectConnectedGroups could crash when being called before P_FinalizePortals. - fixed: Entering a sector portal did not properly set up the starting sector of the cloned trace. --- src/p_map.cpp | 5 +++-- src/portal.cpp | 9 ++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index 3748fafe9..8e59d246e 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -3674,15 +3674,15 @@ struct aim_t 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.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); @@ -3798,6 +3798,7 @@ struct aim_t 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)) { diff --git a/src/portal.cpp b/src/portal.cpp index 11d676a36..892c10ab7 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -1108,9 +1108,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(); From 4b23a1c0c79ec59723537313aaa2c4d8d0eeecc1 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 3 Mar 2016 00:40:48 +0100 Subject: [PATCH 4/6] - added handling for line portals to P_AimLineAttack. --- src/p_map.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++-- src/p_maputl.cpp | 4 ++-- src/p_maputl.h | 2 +- 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index 8e59d246e..8a5321cf3 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -3679,7 +3679,7 @@ struct aim_t 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 will produce a bogus opening + 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); @@ -3690,6 +3690,45 @@ struct aim_t Printf("-----Exiting %s portal\n", position ? "ceiling" : "floor"); } + //============================================================================ + // + // 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 @@ -3759,6 +3798,13 @@ struct aim_t 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 (!(li->flags & ML_TWOSIDED) || (li->flags & ML_BLOCKEVERYTHING)) return; // stop @@ -3886,7 +3932,7 @@ struct aim_t 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 (!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) diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index a00bc3b87..11284a7bf 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -1616,7 +1616,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; @@ -1636,7 +1636,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 5b702a2d1..d8044303e 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -356,7 +356,7 @@ public: 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; } }; From 6b1485a89fc0f17948dd35cc87944e9cf21795ed Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 3 Mar 2016 00:58:25 +0100 Subject: [PATCH 5/6] - refactored those unwieldy intercept position calculations into a subfunction. --- src/p_map.cpp | 18 ++++++------------ src/p_maputl.h | 15 ++++++++++++++- src/portal.cpp | 16 ++++++---------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index 8a5321cf3..0fe73229c 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 @@ -3811,8 +3809,7 @@ struct aim_t // 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); + 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. @@ -4985,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))) @@ -5094,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; @@ -5179,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.h b/src/p_maputl.h index d8044303e..7f045c24f 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; @@ -359,6 +362,16 @@ public: 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 892c10ab7..01afb0bc4 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; } From 90cad80422b422038dea28260ffec589d82f6b07 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 3 Mar 2016 02:48:05 +0100 Subject: [PATCH 6/6] - fixed some issues with FPathTraverse: * when starting directly on a blockline the trace was offset by one map unit. Do this only for the internal block trace calculations but not for the variable that's being exposed to the outside because in rare situations that can create incorrect values. * using startfrac could lead to an actor whose inside was right at that positon to be missed. * when using startfrac the adjusted trace start was not used for all calculations, which could cause the trace to fail. With these issues fixed, P_AimLineAttack can now successfully navigate line portals. --- src/p_maputl.cpp | 61 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index 11284a7bf..2a8c0efda 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -1287,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; @@ -1422,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; @@ -1445,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);