swwmgz_m/zscript/utility/swwm_utility_info.zsc
Marisa the Magician 2aa0ea4680 More work towards Legacy of Rust support (with caveats).
As of this commit, do not consider the experience when playing that new
expansion to be complete. I've only partially written some of the mission texts
and rudimentarily enhanced some boss fights.

Currently there is one major limitation in that the intermission texts cannot
be replaced, as they're hardcoded inside the UMAPINFO. I don't know if I can
work around that.
2025-08-20 16:47:24 +02:00

530 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 'ID24Banshee':
basetag = "ID24BANSHEE";
break;
case 'ID24Ghoul':
basetag = "ID24GHOUL";
break;
case 'ID24Mindweaver':
basetag = "ID24MINDWEAVER";
break;
case 'ID24PlasmaGuy':
basetag = "ID24SHOCKTROOPER";
break;
case 'ID24Vassago':
basetag = "ID24VASSAGO";
break;
case 'ID24Tyrant':
case 'ID24TyrantBoss1':
case 'ID24TyrantBoss2':
basetag = "ID24TYRANT";
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;
}
// special boss tyrants in LoR final map
if ( (a == 'ID24TyrantBoss1') || (a == 'ID24TyrantBoss2') ) return 'ID24Tyrant';
// 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;
}
}