swwmgz_m/zscript/swwm_thinkers.zsc

438 lines
9.8 KiB
Text

// various stat tracking thinkers and others
// Korax instakill handler
Class UglyBoyGetsFuckedUp : Thinker
{
bool wedone;
override void Tick()
{
if ( wedone ) return;
if ( level.killed_monsters < level.total_monsters )
{
// stop portal door
int sidx = level.CreateSectorTagIterator(145).Next();
if ( sidx == -1 ) return;
Sector door = level.Sectors[sidx];
let ti = ThinkerIterator.Create("SectorEffect");
SectorEffect se;
while ( se = SectorEffect(ti.Next()) )
{
if ( se.GetSector() != door ) continue;
se.Destroy();
door.StopSoundSequence(CHAN_VOICE);
}
return;
}
wedone = true;
level.ExecuteSpecial(Door_Open,null,null,false,145,8);
Destroy();
}
}
// Track last damage source to blame fall damage on
Class SWWMWhoPushedMe : Thinker
{
Actor tracked, instigator;
static void SetInstigator( Actor b, Actor whomst )
{
if ( !b || !whomst ) return;
let ti = ThinkerIterator.Create("SWWMWhoPushedMe",STAT_INFO);
SWWMWhoPushedMe ffd;
while ( ffd = SWWMWhoPushedMe(ti.Next()) )
{
if ( ffd.tracked != b ) continue;
ffd.instigator = whomst;
return;
}
ffd = new("SWWMWhoPushedMe");
ffd.ChangeStatNum(STAT_INFO);
ffd.tracked = b;
ffd.instigator = whomst;
}
static Actor RecallInstigator( Actor b )
{
if ( !b ) return null;
let ti = ThinkerIterator.Create("SWWMWhoPushedMe",STAT_INFO);
SWWMWhoPushedMe ffd;
while ( ffd = SWWMWhoPushedMe(ti.Next()) )
{
if ( ffd.tracked != b ) continue;
Actor whomst = ffd.instigator;
ffd.Destroy();
return whomst;
}
return null;
}
}
Class SWWMDamageAccumulator : Thinker
{
Actor victim, inflictor, source;
Array<Int> amounts;
int total;
Name type;
bool dontgib;
int flags;
override void Tick()
{
Super.Tick();
// so many damn safeguards in this
if ( !victim || (victim.Health <= 0) )
{
Destroy();
return;
}
int gibhealth = victim.GetGibHealth();
// お前はもう死んでいる
if ( (victim.health-total <= gibhealth) && !dontgib )
{
// safeguard for inflictors that have somehow ceased to exist, which apparently STILL CAN HAPPEN
if ( inflictor ) inflictor.bEXTREMEDEATH = true;
else type = 'Extreme';
}
// make sure accumulation isn't reentrant
if ( inflictor && (inflictor is 'EvisceratorChunk') ) inflictor.bAMBUSH = true;
// 何?
for ( int i=0; i<amounts.Size(); i++ )
{
if ( !victim ) break;
victim.DamageMobj(inflictor,source,amounts[i],type,DMG_THRUSTLESS|flags);
}
// clean up
if ( inflictor )
{
if ( inflictor is 'EvisceratorChunk' ) inflictor.bAMBUSH = false;
inflictor.bEXTREMEDEATH = false;
}
Destroy();
}
static void Accumulate( Actor victim, int amount, Actor inflictor, Actor source, Name type, bool dontgib = false, int flags = 0 )
{
if ( !victim ) return;
let ti = ThinkerIterator.Create("SWWMDamageAccumulator",STAT_USER);
SWWMDamageAccumulator a, match = null;
while ( a = SWWMDamageAccumulator(ti.Next()) )
{
if ( a.victim != victim ) continue;
match = a;
break;
}
if ( !match )
{
match = new("SWWMDamageAccumulator");
match.ChangeStatNum(STAT_USER);
match.victim = victim;
match.amounts.Clear();
}
match.amounts.Push(amount);
match.total += amount;
match.inflictor = inflictor;
match.source = source;
match.type = type;
match.dontgib = dontgib;
match.flags = flags;
}
static clearscope int GetAmount( Actor victim )
{
let ti = ThinkerIterator.Create("SWWMDamageAccumulator",STAT_USER);
SWWMDamageAccumulator a, match = null;
while ( a = SWWMDamageAccumulator(ti.Next()) )
{
if ( a.victim != victim ) continue;
return a.total;
}
return 0;
}
}
// prevents floors/ceilings from ever moving again, as they're "broken crushers"
// optional "instant" parameter is used by wall busting, no crusher malfunction animation will play
Class SWWMCrusherBroken : Thinker
{
Sector fsec, csec;
double diffh;
int fphase, cphase;
int ftics, ctics;
static void Create( Sector f, Sector c, double diffh, bool instant = false )
{
if ( !f && !c ) return;
let ti = ThinkerIterator.Create("SWWMCrusherBroken",STAT_USER);
SWWMCrusherBroken cb;
while ( cb = SWWMCrusherBroken(ti.Next()) )
{
if ( (cb.fsec == f) && (cb.csec == c) )
{
if ( instant )
{
// force it to be instant
if ( f ) cb.fphase = 3;
if ( c ) cb.cphase = 3;
}
return; // we already have this
}
if ( cb.fsec && (cb.fsec == f) )
{
cb.Destroy(); // we override this one
continue;
}
if ( cb.csec && (cb.csec == c) )
{
cb.Destroy(); // we override this one
continue;
}
}
cb = new("SWWMCrusherBroken");
cb.fsec = f;
cb.csec = c;
cb.ChangeStatNum(STAT_USER);
cb.diffh = diffh;
if ( f && f.floordata ) f.floordata.Destroy();
if ( c && c.ceilingdata ) c.ceilingdata.Destroy();
if ( instant )
{
if ( f ) cb.fphase = 3;
if ( c ) cb.cphase = 3;
}
}
override void Tick()
{
if ( fsec )
{
if ( cphase <= 0 )
{
if ( level.CreateFloor(fsec,Floor.floorLowerByValue,null,16.,diffh*.4) )
{
ftics = int(diffh*.4/16.)+40;
fphase = 1;
}
}
else if ( fphase == 1 )
{
ftics--;
if ( (ftics <= 0) && level.CreateFloor(fsec,Floor.floorLowerByValue,null,1.,diffh*.6) )
{
ftics = int(diffh*.6)+8;
fphase = 2;
}
}
else if ( fphase == 2 )
{
ftics--;
if ( ftics <= 0 ) fphase = 3;
}
else if ( (fphase >= 3) && fsec.floordata )
{
fsec.floordata.Destroy();
fsec.StopSoundSequence(CHAN_WEAPON);
}
}
if ( csec )
{
if ( cphase <= 0 )
{
if ( level.CreateCeiling(csec,Ceiling.ceilRaiseByValue,null,16.,16.,diffh*.4) )
{
ctics = int(diffh*.4/16.)+40;
cphase = 1;
}
}
else if ( cphase == 1 )
{
ctics--;
if ( (ctics <= 0) && level.CreateCeiling(csec,Ceiling.ceilRaiseByValue,null,1.,1.,diffh*.6) )
{
ctics = int(diffh*.6)+10;
cphase = 2;
}
}
else if ( cphase == 2 )
{
ctics--;
if ( ctics <= 0 ) cphase = 3;
}
else if ( (cphase >= 3) && csec.ceilingdata )
{
csec.ceilingdata.Destroy();
csec.StopSoundSequence(CHAN_VOICE);
}
}
}
}
// cache data for manual lockdefs parsing nonsense
Class LIEntry
{
int locknumber;
bool hascolor;
Color mapcolor;
}
Class SWWMCachedLockInfo : Thinker
{
Array<LIEntry> ent;
static clearscope bool IsValidLock( int l )
{
let ti = ThinkerIterator.Create("SWWMCachedLockInfo",STAT_STATIC);
SWWMCachedLockInfo cli = SWWMCachedLockInfo(ti.Next());
if ( !cli ) return false;
for ( int i=0; i<cli.ent.Size(); i++ )
{
if ( cli.ent[i].locknumber == l )
return true;
}
return false;
}
static clearscope Color GetLockColor( int l )
{
let ti = ThinkerIterator.Create("SWWMCachedLockInfo",STAT_STATIC);
SWWMCachedLockInfo cli = SWWMCachedLockInfo(ti.Next());
if ( !cli ) return 0;
for ( int i=0; i<cli.ent.Size(); i++ )
{
if ( (cli.ent[i].locknumber == l) && cli.ent[i].hascolor )
return cli.ent[i].mapcolor;
}
return 0;
}
static SWWMCachedLockInfo GetInstance()
{
let ti = ThinkerIterator.Create("SWWMCachedLockInfo",STAT_STATIC);
SWWMCachedLockInfo cli = SWWMCachedLockInfo(ti.Next());
if ( cli ) return cli;
cli = new("SWWMCachedLockInfo");
cli.ChangeStatNum(STAT_STATIC);
return cli;
}
}
Class SWWMCorpseCleaner : Thinker
{
transient ThinkerIterator ti;
Array<Actor> toclean;
int nstep, i;
private bool CmpDist( Actor activator, Vector3 a, Vector3 b )
{
double dista = level.Vec3Diff(activator.pos,a).length();
double distb = level.Vec3Diff(activator.pos,b).length();
return (dista > distb);
}
private int partition_cleanup( Actor activator, Array<Actor> a, int l, int h )
{
Actor pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpDist(activator,pv.pos,a[j].pos) )
{
i++;
Actor tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
Actor tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_cleanup( Actor activator, Array<Actor> a, int l, int h )
{
if ( l >= h ) return;
int p = partition_cleanup(activator,a,l,h);
qsort_cleanup(activator,a,l,p-1);
qsort_cleanup(activator,a,p+1,h);
}
void Init( Actor activator )
{
toclean.Clear();
let ti = ThinkerIterator.Create("Actor");
Actor a;
while ( a = Actor(ti.Next()) )
{
if ( !a.bKILLED || (a.tics != -1) ) continue;
toclean.Push(a);
}
if ( toclean.Size() <= 0 )
{
Destroy();
return;
}
nstep = max(1,toclean.Size()/100);
i = 0;
// sort by distance to activator (if any)
if ( !activator ) return;
qsort_cleanup(activator,toclean,0,toclean.Size()-1);
}
override void Tick()
{
for ( int j=0; j<nstep; j++ )
{
if ( i >= toclean.Size() )
{
Console.Printf("%d corpses cleaned.",toclean.Size());
Destroy();
return;
}
Actor a = toclean[i++];
if ( !a || !a.bKILLED || (a.tics != -1) ) continue;
let f = a.Spawn("SWWMItemFog",a.pos);
f.A_StartSound("bestsound",CHAN_ITEM);
a.Destroy();
}
}
}
Class SWWMFlyTracker : Thinker
{
Actor tracked, instigator;
Vector3 startpos, curpos;
int gracepd;
static void Track( Actor b, Actor whomst )
{
if ( !b || !whomst ) return;
let ti = ThinkerIterator.Create("SWWMFlyTracker",STAT_USER);
SWWMFlyTracker ffd;
while ( ffd = SWWMFlyTracker(ti.Next()) )
{
if ( ffd.tracked != b ) continue;
ffd.instigator = whomst;
return;
}
ffd = new("SWWMFlyTracker");
ffd.ChangeStatNum(STAT_USER);
ffd.tracked = b;
ffd.instigator = whomst;
ffd.curpos = ffd.startpos = b.pos;
}
override void Tick()
{
if ( !tracked || tracked.bFLOAT || tracked.bNOGRAVITY || (tracked.waterlevel > 1) || (tracked.pos.z <= tracked.floorz) || !tracked.TestMobjZ(false) )
{
gracepd++;
if ( gracepd < 10 ) return;
if ( instigator ) SWWMUtility.AchievementProgress('swwm_progress_flight',int(level.Vec3Diff(startpos,curpos).length()),instigator.player);
Destroy();
return;
}
gracepd = 0;
curpos = tracked.pos;
}
}