TODO: bloodied footsteps, can still re-use code from Soundless Mound there.
508 lines
14 KiB
Text
508 lines
14 KiB
Text
// identification and tagging
|
|
extend Class SWWMUtility
|
|
{
|
|
// because GetTag() returns the localized string, we need to do things the hard way
|
|
static play String GetFunTag( SWWMHandler hnd, Actor a, String defstr = "" )
|
|
{
|
|
// look up fun tag services if available
|
|
foreach ( sv:hnd.funtagsv )
|
|
{
|
|
if ( !sv ) continue;
|
|
String res = sv.GetString("GetFunTag",objectArg:a);
|
|
if ( res == "" ) continue;
|
|
return res;
|
|
}
|
|
int ntags = 1;
|
|
String basetag = "";
|
|
switch ( a.GetClassName() )
|
|
{
|
|
// Doom
|
|
case 'ZombieMan':
|
|
case 'StealthZombieMan':
|
|
basetag = "ZOMBIE";
|
|
break;
|
|
case 'ShotgunGuy':
|
|
case 'StealthShotgunGuy':
|
|
basetag = "SHOTGUN";
|
|
break;
|
|
case 'ChaingunGuy':
|
|
case 'StealthChaingunGuy':
|
|
basetag = "HEAVY";
|
|
break;
|
|
case 'DoomImp':
|
|
case 'StealthDoomImp':
|
|
basetag = "IMP";
|
|
break;
|
|
case 'Demon':
|
|
case 'StealthDemon':
|
|
basetag = "DEMON";
|
|
break;
|
|
case 'Spectre':
|
|
basetag = "SPECTRE";
|
|
break;
|
|
case 'LostSoul':
|
|
case 'LostSoulEvit2':
|
|
case 'LostSoulCount':
|
|
basetag = "LOST";
|
|
break;
|
|
case 'Cacodemon':
|
|
case 'StealthCacodemon':
|
|
basetag = "CACO";
|
|
break;
|
|
case 'HellKnight':
|
|
case 'StealthHellKnight':
|
|
basetag = "HELL";
|
|
break;
|
|
case 'BaronOfHell':
|
|
case 'StealthBaron':
|
|
basetag = "BARON";
|
|
break;
|
|
case 'Arachnotron':
|
|
case 'StealthArachnotron':
|
|
basetag = "ARACH";
|
|
break;
|
|
case 'PainElemental':
|
|
basetag = "PAIN";
|
|
break;
|
|
case 'Revenant':
|
|
case 'StealthRevenant':
|
|
basetag = "REVEN";
|
|
break;
|
|
case 'Fatso':
|
|
case 'StealthFatso':
|
|
basetag = "MANCU";
|
|
break;
|
|
case 'Archvile':
|
|
case 'StealthArchvile':
|
|
basetag = "ARCH";
|
|
break;
|
|
case 'SpiderMastermind':
|
|
basetag = "SPIDER";
|
|
break;
|
|
case 'Cyberdemon':
|
|
case 'CyberdemonEvit2':
|
|
case 'CyberdemonMAP24':
|
|
basetag = "CYBER";
|
|
break;
|
|
case 'SWWMBossBrain':
|
|
basetag = "BOSSBRAIN";
|
|
break;
|
|
case 'WolfensteinSS':
|
|
if ( IsUltDoom2() )
|
|
{
|
|
basetag = "ELITEZOMBIE";
|
|
break;
|
|
}
|
|
// ensure it's not being replaced
|
|
if ( CheckDehackery('WolfensteinSS') ) break;
|
|
case 'SWWMSS':
|
|
basetag = "WOLFSS";
|
|
break;
|
|
case 'SWWMHangingKeen':
|
|
basetag = "KEEN";
|
|
break;
|
|
case 'MBFHelperDog':
|
|
case 'SWWMDog':
|
|
basetag = "DOG";
|
|
break;
|
|
case 'SWWMGuard':
|
|
basetag = "WOLFGUARD";
|
|
break;
|
|
case 'SWWMHans':
|
|
basetag = "WOLFHANS";
|
|
break;
|
|
// Heretic
|
|
case 'Chicken':
|
|
basetag = "CHICKEN";
|
|
break;
|
|
case 'Beast':
|
|
basetag = "BEAST";
|
|
break;
|
|
case 'Clink':
|
|
basetag = "CLINK";
|
|
break;
|
|
case 'Sorcerer1':
|
|
case 'Sorcerer2':
|
|
basetag = "DSPARIL";
|
|
break;
|
|
case 'HereticImp':
|
|
case 'HereticImpLeader':
|
|
basetag = "HERETICIMP";
|
|
break;
|
|
case 'Ironlich':
|
|
basetag = "IRONLICH";
|
|
break;
|
|
case 'Knight':
|
|
case 'KnightGhost':
|
|
basetag = "BONEKNIGHT";
|
|
break;
|
|
case 'Minotaur':
|
|
case 'MinotaurFriend':
|
|
basetag = "MINOTAUR";
|
|
break;
|
|
case 'Mummy':
|
|
case 'MummyGhost':
|
|
basetag = "MUMMY";
|
|
break;
|
|
case 'MummyLeader':
|
|
case 'MummyLeaderGhost':
|
|
basetag = "MUMMYLEADER";
|
|
break;
|
|
case 'Snake':
|
|
basetag = "SNAKE";
|
|
break;
|
|
case 'Wizard':
|
|
basetag = "WIZARD";
|
|
break;
|
|
// Hexen
|
|
case 'FireDemon':
|
|
basetag = "FIREDEMON";
|
|
break;
|
|
case 'Demon1':
|
|
case 'Demon1Mash':
|
|
case 'Demon2':
|
|
case 'Demon2Mash':
|
|
basetag = "DEMON1";
|
|
break;
|
|
case 'Ettin':
|
|
case 'EttinMash':
|
|
basetag = "ETTIN";
|
|
break;
|
|
case 'Centaur':
|
|
case 'CentaurMash':
|
|
basetag = "CENTAUR";
|
|
break;
|
|
case 'CentaurLeader':
|
|
basetag = "SLAUGHTAUR";
|
|
break;
|
|
case 'Bishop':
|
|
basetag = "BISHOP";
|
|
break;
|
|
case 'IceGuy':
|
|
basetag = "ICEGUY";
|
|
break;
|
|
case 'Serpent':
|
|
case 'SerpentLeader':
|
|
basetag = "SERPENT";
|
|
break;
|
|
case 'Wraith':
|
|
case 'WraithBuried':
|
|
basetag = "WRAITH";
|
|
break;
|
|
case 'Dragon':
|
|
basetag = "DRAGON";
|
|
break;
|
|
case 'Korax':
|
|
basetag = "KORAX";
|
|
break;
|
|
case 'FighterBoss':
|
|
basetag = "FBOSS";
|
|
break;
|
|
case 'MageBoss':
|
|
basetag = "MBOSS";
|
|
break;
|
|
case 'ClericBoss':
|
|
basetag = "CBOSS";
|
|
break;
|
|
case 'Heresiarch':
|
|
basetag = "HERESIARCH";
|
|
break;
|
|
case 'Pig':
|
|
basetag = "PIG";
|
|
break;
|
|
// eviternity
|
|
case 'ArchangelusA':
|
|
case 'ArchangelusB':
|
|
basetag = "ANGEL";
|
|
break;
|
|
case 'AstralCaco':
|
|
basetag = "ASTRAL";
|
|
break;
|
|
case 'Annihilator':
|
|
basetag = "ANNIHIL";
|
|
break;
|
|
case 'FormerCaptain':
|
|
basetag = "FCAPTAIN";
|
|
break;
|
|
case 'NightmareDemon':
|
|
basetag = "NDEMON";
|
|
break;
|
|
// eviternity 2
|
|
case 'FormerCorporal':
|
|
basetag = "FCORPORAL";
|
|
break;
|
|
case 'AstralArachnotron':
|
|
basetag = "ASTRALARACH";
|
|
break;
|
|
case 'AstralCacodemon':
|
|
basetag = "ASTRAL";
|
|
break;
|
|
case 'Veilimp':
|
|
basetag = "VEILIMP";
|
|
break;
|
|
case 'GoldenAstralCaco':
|
|
case 'GoldenAstralCacoBoss':
|
|
basetag = "ASTRALGOLD";
|
|
break;
|
|
case 'DukeOfHell':
|
|
basetag = "DUKE";
|
|
break;
|
|
case 'AstralBabyCaco':
|
|
basetag = "ASTRALBABY";
|
|
break;
|
|
case 'NightmareCacodemon':
|
|
basetag = "NAC";
|
|
break;
|
|
case 'AstralMancubus':
|
|
basetag = "ASTRALFATSO";
|
|
break;
|
|
case 'NecromenaceA':
|
|
case 'NecromenaceB':
|
|
case 'NecromenaceC':
|
|
case 'NecromenaceD':
|
|
basetag = "NECROMENACE";
|
|
break;
|
|
case 'The_Origin_Phase_1':
|
|
case 'The_Origin_Phase_2':
|
|
case 'The_Origin_Phase_3':
|
|
case 'The_Absolute_Origin_Phase_1':
|
|
case 'The_Absolute_Origin_Phase_2':
|
|
case 'The_Absolute_Origin_Phase_3':
|
|
basetag = "ORIGIN";
|
|
break;
|
|
case 'SpectralAstralCacodemon':
|
|
basetag = "SAC";
|
|
break;
|
|
case 'GrandDukeofHell':
|
|
basetag = "GDUKE";
|
|
break;
|
|
}
|
|
if ( basetag == "" ) return a.GetTag(defstr);
|
|
String funtag = "FN_"..basetag.."_FUN";
|
|
String lfuntag = StringTable.Localize(funtag,false);
|
|
if ( lfuntag != funtag ) return lfuntag;
|
|
String nfuntag = "FN_"..basetag.."_FUNN";
|
|
String lnfuntag = StringTable.Localize(nfuntag,false);
|
|
if ( lnfuntag == nfuntag ) return a.GetTag(defstr);
|
|
ntags = lnfuntag.ToInt();
|
|
return StringTable.Localize(String.Format("$FN_%s_FUN%d",basetag,Random[FunTags](1,ntags)));
|
|
}
|
|
|
|
// used to "substitute" a monster class for another so killcount stats are merged
|
|
// e.g.: "stealth" monsters and their non-stealth counterparts,
|
|
// or "HereticImp" and "HereticImpLeader", which have the same exact tag,
|
|
// and would result in an odd "duplication" of monster names
|
|
static play Class<Actor> MergeMonster( SWWMHandler hnd, Class<Actor> a )
|
|
{
|
|
// see if any services can resolve this first
|
|
foreach ( sv:hnd.mergemonstersv )
|
|
{
|
|
if ( !sv ) continue;
|
|
String res = sv.GetString("MergeMonster",stringArg:a.GetClassName());
|
|
if ( res == "" ) continue;
|
|
Class<Actor> rescls = res;
|
|
return rescls;
|
|
}
|
|
// stealth monsters, the worst thing ever invented
|
|
if ( a == 'StealthArachnotron' ) return 'Arachnotron';
|
|
if ( a == 'StealthArchvile' ) return 'Archvile';
|
|
if ( a == 'StealthBaron' ) return 'BaronOfHell';
|
|
if ( a == 'StealthCacodemon' ) return 'Cacodemon';
|
|
if ( a == 'StealthChaingunGuy' ) return 'ChaingunGuy';
|
|
if ( a == 'StealthDemon' ) return 'Demon';
|
|
if ( a == 'StealthHellKnight' ) return 'HellKnight';
|
|
if ( a == 'StealthDoomImp' ) return 'DoomImp';
|
|
if ( a == 'StealthFatso' ) return 'Fatso';
|
|
if ( a == 'StealthRevenant' ) return 'Revenant';
|
|
if ( a == 'StealthShotgunGuy' ) return 'ShotgunGuy';
|
|
if ( a == 'StealthZombieMan' ) return 'ZombieMan';
|
|
// eviternity 2 hackery
|
|
if ( a.GetClassName() == 'LostSoulEvit2' ) return 'LostSoul';
|
|
if ( a.GetClassName() == 'LostSoulCount' ) return 'LostSoul';
|
|
if ( a.GetClassName() == 'CyberdemonEvit2' ) return 'Cyberdemon';
|
|
if ( a.GetClassName() == 'CyberdemonMAP24' ) return 'Cyberdemon';
|
|
// heretic monsters
|
|
if ( a == 'Sorcerer2' ) return 'Sorcerer1';
|
|
if ( a == 'HereticImpLeader' ) return 'HereticImp';
|
|
if ( a == 'KnightGhost' ) return 'Knight';
|
|
if ( a == 'MummyGhost' ) return 'Mummy';
|
|
if ( a == 'MummyLeaderGhost' ) return 'MummyLeader';
|
|
// hexen monsters
|
|
if ( a == 'CentaurMash' ) return 'Centaur';
|
|
if ( a == 'Demon1Mash' ) return 'Demon1';
|
|
if ( a == 'Demon2' ) return 'Demon1';
|
|
if ( a == 'Demon2Mash' ) return 'Demon1';
|
|
if ( a == 'EttinMash' ) return 'Ettin';
|
|
if ( a == 'SerpentLeader' ) return 'Serpent';
|
|
if ( a == 'WraithBuried' ) return 'Wraith';
|
|
return a;
|
|
}
|
|
|
|
// because gendered languages
|
|
static bool SellFemaleItem( Inventory i, String loc = "SWWM_SELLEXTRA_FEM" )
|
|
{
|
|
// no gendered string alt
|
|
if ( StringTable.Localize("$"..loc) == loc )
|
|
return false;
|
|
if ( i is 'DeepImpact' ) return true;
|
|
if ( i is 'ExplodiumGun' ) return true;
|
|
if ( i is 'Wallbuster' ) return true;
|
|
if ( i is 'HeavyMahSheenGun' ) return true;
|
|
if ( i is 'Quadravol' ) return true;
|
|
if ( i is 'Sparkster' ) return true;
|
|
if ( i is 'CandyGun' ) return true;
|
|
//if ( i is 'RayKhom' ) return true;
|
|
//if ( i is 'RafanKos' ) return true;
|
|
if ( i is 'HealthNuggetItem' ) return true;
|
|
if ( i is 'ArmorNuggetItem' ) return true;
|
|
if ( i is 'WarArmor' ) return true;
|
|
if ( i is 'FuckingInvinciball' ) return true;
|
|
if ( i is 'SWWMLamp' ) return true;
|
|
if ( i is 'AngerySigil' ) return true;
|
|
return false;
|
|
}
|
|
|
|
// returns the plural tag (if available)
|
|
static string GetAmmoTag( Inventory i )
|
|
{
|
|
if ( i is 'MagAmmo' ) return StringTable.Localize("$T_"..MagAmmo(i).PickupTag.."S");
|
|
if ( i is 'SWWMAmmo' ) return StringTable.Localize("$T_"..SWWMAmmo(i).PickupTag.."S");
|
|
return i.GetTag();
|
|
}
|
|
// because of zscript weirdness with GetDefaultByType
|
|
static string GetAmmoTagClass( Class<Inventory> i )
|
|
{
|
|
if ( i is 'MagAmmo' ) return StringTable.Localize("$T_"..GetDefaultByType((Class<MagAmmo>)(i)).PickupTag.."S");
|
|
if ( i is 'SWWMAmmo' ) return StringTable.Localize("$T_"..GetDefaultByType((Class<SWWMAmmo>)(i)).PickupTag.."S");
|
|
return GetDefaultByType(i).GetTag();
|
|
}
|
|
|
|
// IsZeroDamage() can lead to some false negatives, we have to account for that
|
|
static play bool ValidProjectile( Actor a )
|
|
{
|
|
if ( !a.bMISSILE ) return false;
|
|
if ( a is 'AirBullet' ) return true;
|
|
if ( a is 'ExplodiumMagProj' ) return true;
|
|
if ( a is 'TheBall' ) return true;
|
|
if ( a is 'EvisceratorChunk' ) return true;
|
|
if ( a is 'EvisceratorProj' ) return true;
|
|
if ( a is 'HellblazerMissile' ) return true;
|
|
if ( a is 'QuadProj' ) return true;
|
|
if ( a is 'BigBiospark' ) return true;
|
|
if ( a is 'BiosparkBall' ) return true;
|
|
if ( a is 'BiosparkCore' ) return true;
|
|
if ( a is 'CandyGunProj' ) return true;
|
|
if ( a is 'CandyMagProj' ) return true;
|
|
if ( a is 'MisterGrenade' ) return true;
|
|
if ( a is 'LoveHeart' ) return true;
|
|
if ( !a.IsZeroDamage() ) return true;
|
|
return false;
|
|
}
|
|
|
|
// Is this a beam projectile? (speed == length)
|
|
static bool IsBeamProj( Actor a )
|
|
{
|
|
if ( a is 'SaltBeam' ) return true;
|
|
if ( a is 'BiosparkBeam' ) return true;
|
|
if ( a is 'BiosparkArc' ) return true;
|
|
if ( a is 'CandyBeam' ) return true;
|
|
if ( a is 'YnykronBeam' ) return true;
|
|
if ( a is 'YnykronLightningArc' ) return true;
|
|
if ( a is 'YnykronAltBeam' ) return true;
|
|
if ( a is 'MykradvoTendril' ) return true;
|
|
if ( a is 'MisterRailBeam' ) return true;
|
|
return false;
|
|
}
|
|
// is this a YBeam type? (real pitch is pitch-90)
|
|
static bool IsYBeam( Actor a )
|
|
{
|
|
if ( a is 'MisterRailBeam' ) return true;
|
|
return false;
|
|
}
|
|
|
|
static bool IdentifyingDog( Actor a )
|
|
{
|
|
if ( a is 'MBFHelperDog' ) return true;
|
|
if ( a is 'SWWMDog' ) return true;
|
|
if ( a.GetClassName() == 'GermanDog' ) return true; // brote dote
|
|
if ( a.GetClassName() == '64HellHound' ) return true; // brote dote 64
|
|
if ( a.GetClassName() == 'AbyssDemon2' ) return true; // CH
|
|
if ( a.GetClassName() == 'WHOLETTHEDOGSOUT' ) return true; // CH
|
|
// more dogs will be added as found
|
|
// because all dogs must be pet
|
|
return false;
|
|
}
|
|
|
|
static bool IdentifyingCaco( Actor a )
|
|
{
|
|
if ( a is 'DeadCacodemon' ) return false;
|
|
if ( a is 'Cacodemon' ) return true;
|
|
if ( a.Species == 'RLCacodemon' ) return true; // DRLA
|
|
if ( a.Species == 'Caco' ) return true; // CH and others
|
|
if ( a.Species == 'Cacodemon' ) return true; // Beautiful Doom
|
|
if ( a.GetClassName() == 'AstralCaco' ) return true; // Eviternity
|
|
if ( a.GetParentClass().GetClassName() == 'LEG_BaseCaco' ) return true; // LEGION
|
|
return false;
|
|
}
|
|
|
|
// Друг
|
|
static bool IdentifyingDrug( Actor a )
|
|
{
|
|
if ( a is 'Beast' ) return true;
|
|
return false;
|
|
}
|
|
|
|
static bool IdentifyingDoubleBoi( Actor a )
|
|
{
|
|
if ( a is 'Ettin' ) return true;
|
|
return false;
|
|
}
|
|
|
|
static bool IsVipItem( Actor target )
|
|
{
|
|
if ( (target is 'Chancebox') && (target.CurState==target.SpawnState) )
|
|
return true;
|
|
if ( target is 'SWWMCollectible' )
|
|
return true;
|
|
if ( (target is 'Ynykron')/* || (target is 'RafanKos')*/ )
|
|
return true;
|
|
if ( (target is 'GoldShell') || (target is 'YnykronAmmo')/* || (target is 'UltimatePod') || (target is 'UltimateAmmo')*/ )
|
|
return true;
|
|
if ( (target is 'Mykradvo') || (target is 'AngerySigil') || (target is 'DivineSprite') )
|
|
return true;
|
|
if ( target is 'PuzzleItem' )
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// used by the store
|
|
static bool IsVipItemClass( Class<Actor> target )
|
|
{
|
|
if ( (target is 'Ynykron')/* || (target is 'RafanKos')*/ )
|
|
return true;
|
|
if ( (target is 'GoldShell') || (target is 'YnykronAmmo')/* || (target is 'UltimatePod') || (target is 'UltimateAmmo')*/ )
|
|
return true;
|
|
if ( (target is 'Mykradvo') || (target is 'AngerySigil') || (target is 'DivineSprite') )
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static bool IsScoreItem( Actor target )
|
|
{
|
|
if ( target is 'Key' )
|
|
return true;
|
|
if ( target is 'HammerspaceEmbiggener' )
|
|
return true;
|
|
return target.bCOUNTITEM;
|
|
}
|
|
|
|
// return the highest parent class in hierarchy before a specific "highest class"
|
|
// useful to check stuff such as monster subtypes and the like
|
|
static Class<Object> GetParentClassBefore( Class<Object> baseclass, Class<Object> highestclass )
|
|
{
|
|
Class<Object> step = baseclass;
|
|
while ( (step.GetParentClass() != highestclass) && step.GetParentClass() )
|
|
step = step.GetParentClass();
|
|
return step;
|
|
}
|
|
}
|