- scriptified and consolidated the hitscan attacks.

This commit is contained in:
Christoph Oelckers 2022-12-23 17:58:02 +01:00
parent 1f5cd93564
commit 3af08ecbac
7 changed files with 356 additions and 540 deletions

View file

@ -106,7 +106,6 @@ void playerLookUp(int snum, ESyncBits actions);
void playerLookDown(int snum, ESyncBits actions);
void playerAimUp(int snum, ESyncBits actions);
void playerAimDown(int snum, ESyncBits actions);
void tracers(const DVector3& start, const DVector3& dest, int n);
DDukeActor* aim(DDukeActor* s, int aang, bool force = true);
DDukeActor* aim_(DDukeActor* actor, DDukeActor* weapon);
void checkweapons(player_struct* const p);

View file

@ -129,40 +129,6 @@ void forceplayerangle(player_struct* p)
//
//---------------------------------------------------------------------------
void tracers(const DVector3& start, const DVector3& dest, int n)
{
sectortype* sect = nullptr;
auto direction = dest - start;
if (direction.XY().Sum() < 192.75)
return;
auto pos = start;
auto add = direction / (n + 1);
for (int i = n; i > 0; i--)
{
pos += add;
updatesector(pos, &sect);
if (sect)
{
if (sect->lotag == 2)
{
DVector2 scale(0.0625 + (krand() & 3) * REPEAT_SCALE, 0.0625 + (krand() & 3) * REPEAT_SCALE);
CreateActor(sect, pos, PClass::FindActor(NAME_DukeWaterBubble), -32, scale, randomAngle(), 0., 0., ps[0].GetActor(), 5);
}
else
CreateActor(sect, pos, PClass::FindActor(NAME_DukeSmallSmoke), -32, DVector2(0.21875, 0.21875), nullAngle, 0., 0., ps[0].GetActor(), 5);
}
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
double hits(DDukeActor* actor)
{
double zoff;

View file

@ -81,234 +81,6 @@ void incur_damage_d(player_struct* p)
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void shootweapon(DDukeActor *actor, int p, DVector3 pos, DAngle ang, int atwith)
{
auto sectp = actor->sector();
double vel = 1024, zvel = 0;
HitInfo hit{};
if (actor->spr.extra >= 0) actor->spr.shade = -96;
if (p >= 0)
{
SetGameVarID(g_iAimAngleVarID, AUTO_AIM_ANGLE, actor, p);
OnEvent(EVENT_GETAUTOAIMANGLE, p, ps[p].GetActor(), -1);
int varval = GetGameVarID(g_iAimAngleVarID, actor, p).value();
DDukeActor* aimed = nullptr;
if (varval > 0)
{
aimed = aim(actor, varval);
}
if (aimed)
{
auto tex = TexMan.GetGameTexture(aimed->spr.spritetexture());
double dal = ((aimed->spr.scale.X * tex->GetDisplayHeight()) * 0.5) + 5;
switch (aimed->spr.picnum)
{
case DTILE_GREENSLIME:
case DTILE_ROTATEGUN:
dal -= 8;
break;
}
double dist = (ps[p].GetActor()->spr.pos.XY() - aimed->spr.pos.XY()).Length();
zvel = ((aimed->spr.pos.Z - pos.Z - dal) * 16) / dist;
ang = (aimed->spr.pos - pos).Angle();
}
if (isWW2GI())
{
int angRange = 32;
double zRange = 1;
SetGameVarID(g_iAngRangeVarID, 32, actor, p);
SetGameVarID(g_iZRangeVarID, 256, actor, p);
OnEvent(EVENT_GETSHOTRANGE, p, ps[p].GetActor(), -1);
angRange = GetGameVarID(g_iAngRangeVarID, actor, p).value();
zRange = GetGameVarID(g_iZRangeVarID, actor, p).value() / 256.;
ang += DAngle::fromBuild((angRange / 2) - (krand() & (angRange - 1)));
if (aimed == nullptr)
{
// no target
setFreeAimVelocity(vel, zvel, ps[p].Angles.getPitchWithView(), 16.);
}
zvel += (zRange / 2) - krandf(zRange);
}
else if (aimed == nullptr || atwith != DTILE_SHOTSPARK1)
{
ang += DAngle22_5 / 8 - randomAngle(22.5 / 4);
if (aimed == nullptr) setFreeAimVelocity(vel, zvel, ps[p].Angles.getPitchWithView(), 16.);
zvel += 0.5 - krandf(1);
}
pos.Z -= 2;
}
else
{
double x;
int j = findplayer(actor, &x);
pos.Z -= 4;
double dist = (ps[j].GetActor()->spr.pos.XY() - actor->spr.pos.XY()).Length();
zvel = ((ps[j].GetActor()->getOffsetZ() - pos.Z) * 16) / dist;
zvel += 0.5 - krandf(1);
if (actor->spr.picnum != DTILE_BOSS1)
{
ang += DAngle22_5 / 8 - randomAngle(22.5 / 4);
}
else
{
ang = (ps[j].GetActor()->spr.pos.XY() - pos.XY()).Angle() + DAngle22_5 / 2 - randomAngle(22.5);
}
}
actor->spr.cstat &= ~CSTAT_SPRITE_BLOCK_ALL;
hitscan(pos, sectp, DVector3(ang.ToVector() * vel, zvel * 64), hit, CLIPMASK1);
actor->spr.cstat |= CSTAT_SPRITE_BLOCK_ALL;
if (hit.hitSector == nullptr) return;
if ((krand() & 15) == 0 && hit.hitSector->lotag == 2)
tracers(hit.hitpos, pos, 8 - (ud.multimode >> 1));
DDukeActor* spark;
if (p >= 0)
{
spark = CreateActor(hit.hitSector, hit.hitpos, DTILE_SHOTSPARK1, -15, DVector2(0.15625, 0.15625), ang, 0., 0., actor, 4);
if (!spark) return;
spark->spr.extra = ScriptCode[gs.actorinfo[atwith].scriptaddress];
spark->spr.extra += (krand() % 6);
if (hit.hitWall == nullptr && hit.actor() == nullptr)
{
if (zvel < 0)
{
if (hit.hitSector->ceilingstat & CSTAT_SECTOR_SKY)
{
spark->spr.scale = DVector2(0, 0);
return;
}
else
checkhitceiling(hit.hitSector);
}
spawn(spark, PClass::FindActor(NAME_DukeSmallSmoke));
}
if (hit.actor())
{
checkhitsprite(hit.actor(), spark);
if (hit.actor()->isPlayer() && (ud.coop != 1 || ud.ffire == 1))
{
spark->spr.scale = DVector2(0, 0);
auto jib = spawn(spark, DTILE_JIBS6);
if (jib)
{
jib->spr.pos.Z += 4;
jib->vel.X = 1;
jib->spr.scale = DVector2(0.375, 0.375);
jib->spr.Angles.Yaw += DAngle22_5 / 2 - randomAngle(22.5);
}
}
else spawn(spark, PClass::FindActor(NAME_DukeSmallSmoke));
if (p >= 0 && isshootableswitch(hit.actor()->spr.spritetexture()))
{
checkhitswitch(p, nullptr, hit.actor());
return;
}
}
else if (hit.hitWall)
{
spawn(spark, PClass::FindActor(NAME_DukeSmallSmoke));
if (isadoorwall(hit.hitWall->walltexture) == 1)
goto SKIPBULLETHOLE;
if (isablockdoor(hit.hitWall->walltexture) == 1)
goto SKIPBULLETHOLE;
if (p >= 0 && isshootableswitch(hit.hitWall->walltexture))
{
checkhitswitch(p, hit.hitWall, nullptr);
return;
}
if (hit.hitWall->hitag != 0 || (hit.hitWall->twoSided() && hit.hitWall->nextWall()->hitag != 0))
goto SKIPBULLETHOLE;
if (hit.hitSector && hit.hitSector->lotag == 0)
if (!(tileflags(hit.hitWall->overtexture) & TFLAG_FORCEFIELD))
if ((hit.hitWall->twoSided() && hit.hitWall->nextSector()->lotag == 0) ||
(!hit.hitWall->twoSided() && hit.hitSector->lotag == 0))
if ((hit.hitWall->cstat & CSTAT_WALL_MASKED) == 0)
{
if (hit.hitWall->twoSided())
{
DukeSectIterator it(hit.hitWall->nextSector());
while (auto l = it.Next())
{
if (l->spr.statnum == STAT_EFFECTOR && l->spr.lotag == SE_13_EXPLOSIVE)
goto SKIPBULLETHOLE;
}
}
DukeStatIterator it(STAT_MISC);
while (auto l = it.Next())
{
if (l->spr.picnum == DTILE_BULLETHOLE)
if ((l->spr.pos - spark->spr.pos).Length() < 0.75 + krandf(0.5))
goto SKIPBULLETHOLE;
}
auto hole = spawn(spark, DTILE_BULLETHOLE);
if (hole)
{
hole->vel.X = -1 / 16.;
hole->spr.Angles.Yaw = hit.hitWall->delta().Angle() - DAngle90;
ssp(hole, CLIPMASK0);
hole->spr.cstat2 |= CSTAT2_SPRITE_DECAL;
}
}
SKIPBULLETHOLE:
if (hit.hitWall->cstat & CSTAT_WALL_BOTTOM_SWAP)
if (hit.hitWall->twoSided())
if (hit.hitpos.Z >= hit.hitWall->nextSector()->floorz)
hit.hitWall = hit.hitWall->nextWall();
checkhitwall(spark, hit.hitWall, hit.hitpos);
}
}
else
{
spark = CreateActor(hit.hitSector, hit.hitpos, DTILE_SHOTSPARK1, -15, DVector2(0.375, 0.375), ang, 0., 0., actor, 4);
if (spark)
{
spark->spr.extra = ScriptCode[gs.actorinfo[atwith].scriptaddress];
if (hit.actor())
{
checkhitsprite(hit.actor(), spark);
if (!hit.actor()->isPlayer())
spawn(spark, PClass::FindActor(NAME_DukeSmallSmoke));
else spark->spr.scale = DVector2(0, 0);
}
else if (hit.hitWall)
checkhitwall(spark, hit.hitWall, hit.hitpos);
}
}
if ((krand() & 255) < 4)
{
S_PlaySound3D(PISTOL_RICOCHET, spark, hit.hitpos);
}
}
//---------------------------------------------------------------------------
//
//
@ -617,12 +389,6 @@ void shoot_d(DDukeActor* actor, int atwith, PClass *cls)
switch (atwith)
{
case DTILE_SHOTSPARK1:
case DTILE_SHOTGUN:
case DTILE_CHAINGUN:
shootweapon(actor, p, spos, sang, atwith);
return;
case DTILE_FIRELASER:
case DTILE_SPIT:
case DTILE_COOLEXPLOSION1:

View file

@ -83,235 +83,6 @@ void incur_damage_r(player_struct* p)
//
//---------------------------------------------------------------------------
static void shootweapon(DDukeActor* actor, int p, DVector3 pos, DAngle ang, int atwith)
{
auto sectp = actor->sector();
double vel = 1024., zvel = 0;
HitInfo hit{};
if (actor->spr.extra >= 0) actor->spr.shade = -96;
if (p >= 0)
{
auto aimed = aim(actor, AUTO_AIM_ANGLE);
if (aimed)
{
auto tex = TexMan.GetGameTexture(aimed->spr.spritetexture());
double dal = ((aimed->spr.scale.X * tex->GetDisplayHeight()) * 0.5) + 5;
double dist = (ps[p].GetActor()->spr.pos.XY() - aimed->spr.pos.XY()).Length();
zvel = ((aimed->spr.pos.Z - pos.Z - dal) * 16) / dist;
ang = (aimed->spr.pos - pos).Angle();
}
if (atwith == RTILE_SHOTSPARK1)
{
if (aimed == nullptr)
{
ang += DAngle22_5 / 8 - randomAngle(22.5 / 4);
setFreeAimVelocity(vel, zvel, ps[p].Angles.getPitchWithView(), 16.);
zvel += 0.5 - krandf(1);
}
}
else
{
if (atwith == RTILE_SHOTGUN)
ang += DAngle22_5 / 2 - randomAngle(22.5);
else
ang += DAngle22_5 / 8 - randomAngle(22.5 / 4);
if (aimed == nullptr) setFreeAimVelocity(vel, zvel, ps[p].Angles.getPitchWithView(), 16.);
zvel += 0.5 - krandf(1);
}
pos.Z -= 2;
}
else
{
double x;
int j = findplayer(actor, &x);
pos.Z -= 4;
double dist = (ps[j].GetActor()->spr.pos.XY() - actor->spr.pos.XY()).Length();
zvel = ((ps[j].GetActor()->getOffsetZ() - pos.Z) * 16) / dist;
zvel += 0.5 - krandf(1);
ang += DAngle22_5 / 4 - randomAngle(22.5 / 2);
}
actor->spr.cstat &= ~CSTAT_SPRITE_BLOCK_ALL;
hitscan(pos, sectp, DVector3(ang.ToVector() * vel, zvel * 64), hit, CLIPMASK1);
if (isRRRA() && hit.hitSector != nullptr && (((hit.hitSector->lotag == ST_160_FLOOR_TELEPORT && zvel > 0) || (hit.hitSector->lotag == ST_161_CEILING_TELEPORT && zvel < 0))
&& hit.actor() == nullptr && hit.hitWall == nullptr))
{
DukeStatIterator its(STAT_EFFECTOR);
while (auto effector = its.Next())
{
auto Owner = effector->GetOwner();
if (effector->sector() == hit.hitSector && iseffector(effector) && Owner && effector->spr.lotag == SE_7_TELEPORT)
{
DVector3 npos;
npos.XY() = hit.hitpos.XY() + (Owner->spr.pos.XY() - effector->spr.pos.XY());
if (hit.hitSector->lotag == ST_161_CEILING_TELEPORT)
{
npos.Z = Owner->sector()->floorz;
}
else
{
npos.Z = Owner->sector()->ceilingz;
}
hitscan(npos, Owner->sector(), DVector3(ang.ToVector() * 1024, zvel * 0.25), hit, CLIPMASK1);
break;
}
}
}
actor->spr.cstat |= CSTAT_SPRITE_BLOCK_ALL;
if (hit.hitSector == nullptr) return;
if (atwith == RTILE_SHOTGUN)
if (hit.hitSector->lotag == 1)
if (krand() & 1)
return;
if ((krand() & 15) == 0 && hit.hitSector->lotag == 2)
tracers(hit.hitpos, pos, 8 - (ud.multimode >> 1));
DDukeActor* spark;
if (p >= 0)
{
spark = CreateActor(hit.hitSector, hit.hitpos, RTILE_SHOTSPARK1, -15, DVector2(0.15625, 0.15625), ang, 0., 0., actor, 4);
if (!spark) return;
spark->spr.extra = ScriptCode[gs.actorinfo[atwith].scriptaddress];
spark->spr.extra += (krand() % 6);
if (hit.hitWall == nullptr && hit.actor() == nullptr)
{
if (zvel < 0)
{
if (hit.hitSector->ceilingstat & CSTAT_SECTOR_SKY)
{
spark->spr.scale = DVector2(0, 0);
return;
}
else
checkhitceiling(hit.hitSector);
}
if (hit.hitSector->lotag != 1)
spawn(spark, PClass::FindActor(NAME_DukeSmallSmoke));
}
if (hit.actor())
{
if (hit.actor()->spr.picnum == RTILE_TORNADO)
return;
checkhitsprite(hit.actor(), spark);
if (hit.actor()->isPlayer() && (ud.coop != 1 || ud.ffire == 1))
{
auto jib = spawn(spark, RTILE_JIBS6);
spark->spr.scale = DVector2(0, 0);
if (jib)
{
jib->spr.pos.Z += 4;
jib->vel.X = 1;
jib->spr.scale = DVector2(0.375, 0.375);
jib->spr.Angles.Yaw += DAngle22_5 / 2 - randomAngle(22.5);
}
}
else spawn(spark, PClass::FindActor(NAME_DukeSmallSmoke));
if (p >= 0 && isshootableswitch(hit.actor()->spr.spritetexture()))
{
checkhitswitch(p, nullptr, hit.actor());
return;
}
}
else if (hit.hitWall != nullptr)
{
spawn(spark, PClass::FindActor(NAME_DukeSmallSmoke));
if (isadoorwall(hit.hitWall->walltexture) == 1)
goto SKIPBULLETHOLE;
if (isablockdoor(hit.hitWall->walltexture) == 1)
goto SKIPBULLETHOLE;
if (p >= 0 && isshootableswitch(hit.hitWall->walltexture))
{
checkhitswitch(p, hit.hitWall, nullptr);
return;
}
if (hit.hitWall->hitag != 0 || (hit.hitWall->twoSided() && hit.hitWall->nextWall()->hitag != 0))
goto SKIPBULLETHOLE;
if (hit.hitSector != nullptr && hit.hitSector->lotag == 0)
if (!(tileflags(hit.hitWall->overtexture) & TFLAG_FORCEFIELD))
if ((hit.hitWall->twoSided() && hit.hitWall->nextSector()->lotag == 0) ||
(!hit.hitWall->twoSided() && hit.hitSector->lotag == 0))
if ((hit.hitWall->cstat & CSTAT_WALL_MASKED) == 0)
{
if (hit.hitWall->twoSided())
{
DukeSectIterator it(hit.hitWall->nextSector());
while (auto l = it.Next())
{
if (l->spr.statnum == STAT_EFFECTOR && l->spr.lotag == SE_13_EXPLOSIVE)
goto SKIPBULLETHOLE;
}
}
DukeStatIterator it(STAT_MISC);
while (auto l = it.Next())
{
if (l->spr.picnum == RTILE_BULLETHOLE)
if ((l->spr.pos - spark->spr.pos).Length() < 0.75 + krandf(0.5))
goto SKIPBULLETHOLE;
}
auto hole = spawn(spark, RTILE_BULLETHOLE);
if (hole)
{
hole->vel.X = -1 / 16;
hole->spr.Angles.Yaw = hit.hitWall->delta().Angle() - DAngle90;
ssp(hole, CLIPMASK0);
hole->spr.cstat2 |= CSTAT2_SPRITE_DECAL;
}
}
SKIPBULLETHOLE:
if (hit.hitWall->cstat & CSTAT_WALL_BOTTOM_SWAP)
if (hit.hitWall->twoSided())
if (hit.hitpos.Z >= hit.hitWall->nextSector()->floorz)
hit.hitWall = hit.hitWall->nextWall();
checkhitwall(spark, hit.hitWall, hit.hitpos);
}
}
else
{
spark = CreateActor(hit.hitSector, hit.hitpos, RTILE_SHOTSPARK1, -15, DVector2(0.375, 0.375), ang, 0., 0., actor, 4);
if (!spark) return;
spark->spr.extra = ScriptCode[gs.actorinfo[atwith].scriptaddress];
if (hit.actor())
{
checkhitsprite(hit.actor(), spark);
if (!hit.actor()->isPlayer())
spawn(spark, PClass::FindActor(NAME_DukeSmallSmoke));
else spark->spr.scale = DVector2(0, 0);
}
else if (hit.hitWall != nullptr)
checkhitwall(spark, hit.hitWall, hit.hitpos);
}
if ((krand() & 255) < 10)
{
S_PlaySound3D(PISTOL_RICOCHET, spark, hit.hitpos);
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void shootstuff(DDukeActor* actor, int p, DVector3 pos, DAngle ang, int atwith)
{
auto sect = actor->sector();
@ -655,12 +426,6 @@ void shoot_r(DDukeActor* actor, int atwith, PClass* cls)
switch (atwith)
{
case RTILE_SHOTSPARK1:
case RTILE_SHOTGUN:
case RTILE_CHAINGUN:
shootweapon(actor, p, spos, sang, atwith);
return;
case RTILE_OWHIP:
case RTILE_UWHIP:
shootwhip(actor, p, spos, sang, atwith);

View file

@ -159,6 +159,7 @@ version "4.10"
#include "zscript/games/duke/actors/dukeweapons/melee.zs"
#include "zscript/games/duke/actors/dukeweapons/hitscan.zs"
#include "zscript/games/duke/actors/dukeweapons/shrinker.zs"
#include "zscript/games/duke/actors/dukeweapons/grower.zs"
#include "zscript/games/duke/actors/dukeweapons/tripbomb.zs"

View file

@ -11,47 +11,11 @@ class DukeRadiusExplosion : DukeActor
}
}
class DukeShotgunShot : DukeActor
{
default
{
pic "SHOTGUN";
}
}
class RedneckShotgunShot : DukeShotgunShot
{
}
class DukeChaingunShot : DukeActor
{
default
{
pic "CHAINGUN";
}
}
class RedneckChaingunShot : DukeChaingunShot
{
}
class DukeSectorEffector : DukeActor
{
//This never gets ticked, the handler goes directly to the native implementations.
}
class DukeShotSpark : DukeActor
{
default
{
pic "SHOTSPARK1";
+FORCERUNCON;
+LIGHTDAMAGE;
statnum STAT_MISC;
}
}
class RedneckMotoHit : DukeActor
{
default

View file

@ -0,0 +1,355 @@
extend class DukeActor
{
static void tracers(Vector3 start, Vector3 dest, int n)
{
sectortype sect = nullptr;
let direction = dest - start;
if (direction.XY.Sum() < 192.75)
return;
let pos = start;
let add = direction / (n + 1);
for (int i = n; i > 0; i--)
{
pos += add;
sect = Raze.updatesector(pos.XY, sect);
if (sect)
{
if (sect.lotag == ST_2_UNDERWATER)
{
Vector2 scale = (0.0625 + random(0, 3) * REPEAT_SCALE, 0.0625 + random(0, 3) * REPEAT_SCALE);
dlevel.SpawnActor(sect, pos, "DukeWaterBubble", -32, scale, frandom(0, 360), 0., 0., Duke.GetLocalPlayer().actor, STAT_MISC);
}
else
dlevel.SpawnActor(sect, pos, "DukeSmallSmoke", -32, (0.21875, 0.21875), 0, 0., 0., Duke.GetLocalPlayer().actor, STAT_MISC);
}
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
bool HitscanAttack(DukeActor actor, DukePlayer p, Vector3 pos, double ang, double hspread, double vspread, double enemyspread, bool forcespread, bool waterhalfhitchance = false, class<DukeActor> sparktype = "DukeShotSpark") const
{
let sectp = actor.sector;
double vel = 1024, zvel = 0;
HitInfo hit;
if (actor.extra >= 0) actor.shade = -96;
if (p != null)
{
let aimed = actor.aim(self);
if (aimed)
{
double dal = ((aimed.scale.X * aimed.spriteHeight()) * 0.5) + aimed.sparkoffset;
double dist = (p.actor.pos.XY - aimed.pos.XY).Length();
zvel = ((aimed.pos.Z - pos.Z - dal) * 16) / dist;
ang = (aimed.pos - pos).Angle();
}
if (aimed == nullptr || forcespread)
{
ang += hspread / 2 - frandom(0, hspread);
if (aimed == nullptr) [vel, zvel] = Raze.setFreeAimVelocity(vel, zvel, p.getPitchWithView(), 16);
zvel += vspread / 8 - frandom(0, vspread / 4);
}
pos.Z -= 2;
}
else
{
let j = actor.findplayer();
pos.Z -= 4;
double dist = (j.actor.pos.XY - actor.pos.XY).Length();
zvel = ((j.actor.pos.Z + j.actor.viewzoffset - pos.Z) * 16) / dist;
zvel += frandom(-0.5, 0.5);
if (!actor.bALTHITSCANDIRECTION)
{
ang += enemyspread / 2 - frandom(0, enemyspread);
}
else
{
// one of those lousy hacks in Duke.
ang = (j.actor.pos.XY - pos.XY).Angle() + enemyspread / 4 - frandom(0, enemyspread / 2);
}
}
actor.cstat &= ~CSTAT_SPRITE_BLOCK_ALL;
Raze.hitscan(pos, sectp, (ang.ToVector() * vel, zvel * 64), hit, CLIPMASK1);
if (Raze.isRRRA() && hit.hitSector != nullptr && ((hit.hitSector.lotag == ST_160_FLOOR_TELEPORT && zvel > 0) || (hit.hitSector.lotag == ST_161_CEILING_TELEPORT && zvel < 0))
&& hit.hitActor == nullptr && hit.hitWall == nullptr)
{
DukeStatIterator its;
for (let effector = its.First(STAT_EFFECTOR); effector; effector = its.Next())
{
if (effector.sector == hit.hitSector && effector.GetClassName() == 'DukeSectorEffector' && effector.ownerActor && effector.lotag == SE_7_TELEPORT)
{
let owner = effector.ownerActor;
Vector3 npos;
npos.XY = hit.hitpos.XY + (owner.pos.XY - effector.pos.XY);
if (hit.hitSector.lotag == ST_161_CEILING_TELEPORT)
{
npos.Z = owner.sector.floorz;
}
else
{
npos.Z = owner.sector.ceilingz;
}
Raze.hitscan(npos, owner.sector, (ang.ToVector() * 1024, zvel * 0.25), hit, CLIPMASK1);
break;
}
}
}
actor.cstat |= CSTAT_SPRITE_BLOCK_ALL;
if (hit.hitSector == nullptr) return true;
// RR shotgun only
if (waterhalfhitchance && hit.hitSector.lotag == ST_1_ABOVE_WATER && random(0, 1))
return true;
if (random(0, 15) == 0 && hit.hitSector.lotag == ST_2_UNDERWATER)
tracers(hit.hitpos, pos, 8 - (ud.multimode >> 1));
DukeActor spark = null;
if (p != null)
{
spark = dlevel.SpawnActor(hit.hitSector, hit.hitpos, sparktype, -15, (0.15625, 0.15625), ang, 0., 0., actor, STAT_PROJECTILE);
if (!spark) return true;
spark.extra = self.strength + random(0, 5);
if (hit.hitWall == nullptr && hit.hitActor == nullptr)
{
if (zvel < 0)
{
if (hit.hitSector.ceilingstat & CSTAT_SECTOR_SKY)
{
spark.scale = (0, 0);
return true;
}
else
dlevel.checkhitceiling(hit.hitSector, spark);
}
if (zvel < 0 || hit.hitSector.lotag != ST_1_ABOVE_WATER)
spark.spawn("DukeSmallSmoke");
}
let hitActor = DukeActor(hit.hitActor);
if (hitActor)
{
if (hitActor.bNOHITSCANHIT)
return true;
hitActor.OnHit(spark);
if (hitActor.isPlayer() && (ud.coop != 1 || ud.ffire == 1))
{
let jib = spark.spawn("DukeJibs6");
spark.scale = (0, 0);
if (jib)
{
jib.pos.Z += 4;
jib.vel.X = 1;
jib.scale = (0.375, 0.375);
jib.Angle += frandom(-11.25, 11.25);
}
}
else spark.spawn("DukeSmallSmoke");
if (p != null && Duke.isshootableswitch(hitActor.spritetexture()))
{
p.checkhitswitch(nullptr, hitActor);
return true;
}
}
else if (hit.hitWall)
{
spark.spawn("DukeSmallSmoke");
if (!(Raze.tileflags(hit.hitWall.walltexture) & (Duke.TFLAG_DOORWALL | Duke.TFLAG_BLOCKDOOR)))
{
if (p != null && Duke.isshootableswitch(hit.hitWall.walltexture))
{
p.checkhitswitch(hit.hitWall, nullptr);
return true;
}
if (!(hit.hitWall.hitag != 0 || (hit.hitWall.twoSided() && hit.hitWall.nextWallp().hitag != 0)))
{
if (hit.hitSector && hit.hitSector.lotag == 0 && !(Raze.tileflags(hit.hitWall.overtexture) & Duke.TFLAG_FORCEFIELD))
{
if ((hit.hitWall.twoSided() && hit.hitWall.nextSectorp().lotag == 0) || (!hit.hitWall.twoSided() && hit.hitSector.lotag == 0))
{
if ((hit.hitWall.cstat & CSTAT_WALL_MASKED) == 0)
{
bool ok = true;
if (hit.hitWall.twoSided())
{
DukeSectIterator it;
for (let l = it.First(hit.hitWall.nextSectorp()); l; l = it.Next())
{
if (l.statnum == STAT_EFFECTOR && l.lotag == SE_13_EXPLOSIVE)
{
ok = false;
break;
}
}
}
if (ok)
{
DukeStatIterator it;
for (let l = it.First(STAT_MISC); l; l = it.Next())
{
if (l is 'DukeBulletHole' && (l.pos - spark.pos).Length() < frandom(0.75, 1.25))
{
ok = false;
break;
}
}
if (ok)
{
let hole = spark.spawn("DukeBulletHole");
if (hole)
{
hole.vel.X = -1 / 16.;
hole.Angle = hit.hitWall.delta().Angle() - 90;
hole.DoMove(CLIPMASK0);
hole.cstat2 |= CSTAT2_SPRITE_DECAL;
}
}
}
}
}
}
}
}
if (hit.hitWall.cstat & CSTAT_WALL_BOTTOM_SWAP)
if (hit.hitWall.twoSided())
if (hit.hitpos.Z >= hit.hitWall.nextSectorp().floorz)
hit.hitWall = hit.hitWall.nextWallp();
dlevel.checkhitwall(hit.hitWall, spark, hit.hitpos);
}
}
else
{
spark = dlevel.SpawnActor(hit.hitSector, hit.hitpos, sparktype, -15, (0.375, 0.375), ang, 0., 0., actor, STAT_PROJECTILE);
if (spark)
{
spark.extra = self.strength;
let hitActor = DukeActor(hit.hitActor);
if (hitActor)
{
hitActor.OnHit(spark);
if (!hitActor.isPlayer())
spark.spawn("DukeSmallSmoke");
else spark.scale = (0, 0);
}
else if (hit.hitWall)
dlevel.checkhitwall(hit.hitWall, spark, hit.hitpos);
}
}
if (spark && random(0, 255) < 4)
{
spark.PlayActorSound("PISTOL_RICOCHET");
}
return true;
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class DukeShotSpark : DukeActor
{
default
{
pic "SHOTSPARK1";
+FORCERUNCON;
+LIGHTDAMAGE;
statnum STAT_MISC;
}
override bool ShootThis(DukeActor actor, DukePlayer p, Vector3 pos, double ang)
{
return HitscanAttack(actor, p, pos, ang, 5.625, 4, 5.625, false);
}
}
class DukeShotgunShot : DukeActor
{
default
{
pic "SHOTGUN";
}
override bool ShootThis(DukeActor actor, DukePlayer p, Vector3 pos, double ang)
{
return HitscanAttack(actor, p, pos, ang, 5.625, 4, 5.625, true);
}
}
class DukeChaingunShot : DukeActor
{
default
{
pic "CHAINGUN";
}
override bool ShootThis(DukeActor actor, DukePlayer p, Vector3 pos, double ang)
{
return HitscanAttack(actor, p, pos, ang, 5.625, 4, 5.625, true);
}
}
// RR' damage properties are a bit different.
class RedneckShotSpark : DukeShotSpark
{
override bool ShootThis(DukeActor actor, DukePlayer p, Vector3 pos, double ang)
{
return HitscanAttack(actor, p, pos, ang, 5.625, 4, 11.25, false);
}
}
class RedneckShotgunShot : DukeShotgunShot
{
override bool ShootThis(DukeActor actor, DukePlayer p, Vector3 pos, double ang)
{
return HitscanAttack(actor, p, pos, ang, 22.5, 4, 11.25, true, true);
}
}
class RedneckChaingunShot : DukeChaingunShot
{
override bool ShootThis(DukeActor actor, DukePlayer p, Vector3 pos, double ang)
{
return HitscanAttack(actor, p, pos, ang, 5.625, 4, 11.25, true);
}
}
// WW2GI uses different spread settings for its pistol
class WW2GIShotSpark : DukeShotSpark
{
override bool ShootThis(DukeActor actor, DukePlayer p, Vector3 pos, double ang)
{
return HitscanAttack(actor, p, pos, ang, 0.3515625, 0.25, 5.625, true);
}
}