by Xangi » Mon Oct 30, 2017 3:58 pm
Welp, turns out Nash's fix doesn't work, likely because the Blood actor isn't actually a puff and is therefore not subject to the flag he suggested, or some other reason.
Here is a weapon, use 'give TracerShotgun' and test it out.
https://mega.nz/#!u01zXZjD!6-A_XeC1TP_3 ... 0_kYg4oEl0
Steps to reproduce with near-100% chance:
1.) Use the freeze command
2.) summon a pinky demon
3.) center view and press directly against it
4.) Shoot until it dies
5.) Unfreeze time.
You should then see the trajectories of 20 (30 if you're extremely unlucky) tracers, many of which will be directed upwards at an angle far higher than the aim angle. This behavior is the result of an unsafe assumption being made in the tracer spawn code, which is that a vector from player(+offsets) -> puff is equivalent to, or at least close to, the hitscan line. Obviously it's not when you're extremely close to an enemy, as can be demonstrated here. No need to enable or disable autoaim, it works no matter what. In fact, you can predict the vectors by looking closely at the tracers and the blood actors, then mentally drawing a line that passes through both.
It would be possible to work around this if FBF_PUFFTARGET/FBF_PUFFMASTER/FBF_PUFFTRACER could return pointers to Blood actors (which would allow manual deletion of tracers after they pass their puff), or if it was reasonable to wrap firebullets in a loop which used explicit angles and separately spawned the tracers. Unfortunately one of those is impossible and the other would interfere with autoaim. If there's another workaround that doesn't kill performance, autoaim, or require tearing apart pre-existing weapons I'll be glad to use that.
EDIT: Okay so, firstly my assumption about the source of this issue is dead on, and I do think that maybe there should be a flag to use hitscan angle instead of puff direction if possible. Secondly there is a workaround which does basically just that (while preserving autoaim), and it's this:
Code: Select all
action void A_FireBulletsProperTracer(double anglex, double angley, int numbullets, int damage, int multiplier=3, string pufftype="PlayerPuff", double range=8192, string missile="", double spawnheight=0, double spawnofs_xy=0)
{
//Declare variables required
let p = players[consoleplayer].mo;
int zoffset = ((p.height/2)+p.AttackZOffset);
double aimPitch, ax, ay;
Vector3 aimVec;
//Determine pitch including autoaim
A_FireBullets(0, 0, 1, 0, "AutoAimPuff", FBF_NORANDOM|FBF_NORANDOMPUFFZ, 1);
AutoAimPuff other = AutoAimPuff(ThinkerIterator.Create("AutoAimPuff").Next()); //We know one will exist because we just spawned it
aimVec = (other.pos.x-p.pos.x, other.pos.y-p.pos.y, other.pos.z-(p.pos.z+zoffset)); //manually create vector to apply offset
aimPitch = -asin(aimVec.z/aimVec.Length()); //hooray trig
//fire bullets, in this case 10 with a 5x5 spread
for(int i = 0; i < 10; i++)
{
ax = frandom(-anglex, anglex);
ay = frandom(-angley, angley);
A_FireBullets(ax, aimPitch+ay, -1, damage * random(1, multiplier), pufftype, FBF_EXPLICITANGLE|FBF_NOPITCH|FBF_NORANDOM, range);
//spawn the tracers in the origin of the hitscan, and move them towards it
p.A_SpawnProjectile (missile, zoffset+spawnheight, spawnofs_xy, p.angle+ax, CMF_AIMDIRECTION|CMF_ABSOLUTEANGLE, aimPitch+ay);
}
}
In ZScript we can use an incredibly short range dummy puff to get the aim pitch while also accounting for autoaim, then use that along with some other hackery to create the desired effect. There's also a way better way to do this if you don't want autoaim, which is to just use the player's pitch. So far I haven't encountered any issues with this, but it would probably be faster running native.
Welp, turns out Nash's fix doesn't work, likely because the Blood actor isn't actually a puff and is therefore not subject to the flag he suggested, or some other reason.
[imgur]https://imgur.com/M2YA1Ks[/imgur]
Here is a weapon, use 'give TracerShotgun' and test it out.
https://mega.nz/#!u01zXZjD!6-A_XeC1TP_3kEpDZs9Sx96Cs9mphxk2O0_kYg4oEl0
Steps to reproduce with near-100% chance:
1.) Use the freeze command
2.) summon a pinky demon
3.) center view and press directly against it
4.) Shoot until it dies
5.) Unfreeze time.
You should then see the trajectories of 20 (30 if you're extremely unlucky) tracers, many of which will be directed upwards at an angle far higher than the aim angle. This behavior is the result of an unsafe assumption being made in the tracer spawn code, which is that a vector from player(+offsets) -> puff is equivalent to, or at least close to, the hitscan line. Obviously it's not when you're extremely close to an enemy, as can be demonstrated here. No need to enable or disable autoaim, it works no matter what. In fact, you can predict the vectors by looking closely at the tracers and the blood actors, then mentally drawing a line that passes through both.
It would be possible to work around this if FBF_PUFFTARGET/FBF_PUFFMASTER/FBF_PUFFTRACER could return pointers to Blood actors (which would allow manual deletion of tracers after they pass their puff), or if it was reasonable to wrap firebullets in a loop which used explicit angles and separately spawned the tracers. Unfortunately one of those is impossible and the other would interfere with autoaim. If there's another workaround that doesn't kill performance, autoaim, or require tearing apart pre-existing weapons I'll be glad to use that.
EDIT: Okay so, firstly my assumption about the source of this issue is dead on, and I do think that maybe there should be a flag to use hitscan angle instead of puff direction if possible. Secondly there is a workaround which does basically just that (while preserving autoaim), and it's this:
[code]
action void A_FireBulletsProperTracer(double anglex, double angley, int numbullets, int damage, int multiplier=3, string pufftype="PlayerPuff", double range=8192, string missile="", double spawnheight=0, double spawnofs_xy=0)
{
//Declare variables required
let p = players[consoleplayer].mo;
int zoffset = ((p.height/2)+p.AttackZOffset);
double aimPitch, ax, ay;
Vector3 aimVec;
//Determine pitch including autoaim
A_FireBullets(0, 0, 1, 0, "AutoAimPuff", FBF_NORANDOM|FBF_NORANDOMPUFFZ, 1);
AutoAimPuff other = AutoAimPuff(ThinkerIterator.Create("AutoAimPuff").Next()); //We know one will exist because we just spawned it
aimVec = (other.pos.x-p.pos.x, other.pos.y-p.pos.y, other.pos.z-(p.pos.z+zoffset)); //manually create vector to apply offset
aimPitch = -asin(aimVec.z/aimVec.Length()); //hooray trig
//fire bullets, in this case 10 with a 5x5 spread
for(int i = 0; i < 10; i++)
{
ax = frandom(-anglex, anglex);
ay = frandom(-angley, angley);
A_FireBullets(ax, aimPitch+ay, -1, damage * random(1, multiplier), pufftype, FBF_EXPLICITANGLE|FBF_NOPITCH|FBF_NORANDOM, range);
//spawn the tracers in the origin of the hitscan, and move them towards it
p.A_SpawnProjectile (missile, zoffset+spawnheight, spawnofs_xy, p.angle+ax, CMF_AIMDIRECTION|CMF_ABSOLUTEANGLE, aimPitch+ay);
}
}
[/code]
In ZScript we can use an incredibly short range dummy puff to get the aim pitch while also accounting for autoaim, then use that along with some other hackery to create the desired effect. There's also a way better way to do this if you don't want autoaim, which is to just use the player's pitch. So far I haven't encountered any issues with this, but it would probably be faster running native.