swwmgz_m/zscript/swwm_thinkers_player.zsc
2022-03-24 18:08:38 +01:00

696 lines
22 KiB
Text

// player-specific thinkers
// "Full History" contains all messages since session start, nothing is flushed
// this can be accessed from a section of the knowledge base
Class SWWMFullHistory : Thinker
{
String lastmap;
Array<MsgLine> msg;
static clearscope SWWMFullHistory Get()
{
let fh = SWWMFullHistory(ThinkerIterator.Create("SWWMFullHistory",STAT_STATIC).Next());
return fh;
}
static play void PushMessage( String str, int tic, int type )
{
let fh = SWWMFullHistory(ThinkerIterator.Create("SWWMFullHistory",STAT_STATIC).Next());
if ( !fh )
{
fh = new("SWWMFullHistory");
fh.ChangeStatNum(STAT_STATIC);
}
MsgLine m;
if ( level.mapname != fh.lastmap )
{
// push a map change label
m = new("MsgLine");
m.str = level.levelname;
m.type = -1;
fh.lastmap = level.mapname;
fh.msg.Push(m);
}
m = new("MsgLine");
m.str = str;
m.tic = tic;
m.type = type;
fh.msg.Push(m);
}
}
// Dedicated mission log (for custom maps)
Class SWWMMissionLog : Thinker
{
Array<String> entries;
bool clockset;
int year, month, day, hour, minute;
String tz;
static clearscope SWWMMissionLog Get()
{
let ml = SWWMMissionLog(ThinkerIterator.Create("SWWMMissionLog",STAT_STATIC).Next());
return ml;
}
static play void AddLog( String str )
{
let ml = SWWMMissionLog(ThinkerIterator.Create("SWWMMissionLog",STAT_STATIC).Next());
if ( !ml )
{
ml = new("SWWMMissionLog");
ml.ChangeStatNum(STAT_STATIC);
}
if ( ml.entries.Find(str) < ml.entries.Size() ) return;
ml.entries.Push(str);
Console.Printf(StringTable.Localize("$SWWM_NEWMISSION"));
}
static play void SetClock( int year, int month, int day, int hour, int minute, String tz = "JST" )
{
let ml = SWWMMissionLog(ThinkerIterator.Create("SWWMMissionLog",STAT_STATIC).Next());
if ( !ml )
{
ml = new("SWWMMissionLog");
ml.ChangeStatNum(STAT_STATIC);
}
ml.clockset = true;
ml.year = year;
ml.month = month;
ml.day = day;
ml.hour = hour;
ml.minute = minute;
ml.tz = tz;
}
}
// Stats
Class WeaponUsage
{
Class<Weapon> w;
int kills;
}
Class MonsterKill
{
Class<Actor> m;
int kills;
}
Class LevelStat
{
bool hub;
String levelname, mapname;
int kcount, ktotal;
int icount, itotal;
int scount, stotal;
int time, par, suck;
}
Class SWWMStats : Thinker
{
PlayerInfo myplayer;
int lastspawn, dashcount, boostcount, stompcount, airtime, kills,
deaths, damagedealt, hdamagedealt, damagetaken, hdamagetaken,
mkill, hiscore, hhiscore, topdealt, toptaken, skill, wponch,
busts, buttslams, secrets, items, parries, pparries, pats,
befriend, smooch;
double grounddist, airdist, swimdist, fuelusage, topspeed, teledist;
Array<WeaponUsage> wstats;
Array<MonsterKill> mstats;
Array<LevelStat> lstats;
Array<Class<Weapon> > alreadygot;
int favweapon;
// these two are used for mission updates
Array<int> clustervisit;
Array<bool> secretdone;
// hackaround for stuff getting lost
Array<Class<SWWMCollectible> > ownedcollectibles;
int plushuses;
// for pistol start info (to avoid it within hubs)
int lastcluster;
// for trash removal achievement
int nazicleanup;
// stored for hexen
int puzzlecnt, realpuzzlecnt;
// easter eggs
int silveregg;
bool oldcheat;
bool GotWeapon( Class<Weapon> which )
{
for ( int i=0; i<alreadygot.Size(); i++ )
{
if ( alreadygot[i] == which ) return true;
}
alreadygot.Push(which);
return false;
}
private Class<Weapon> WeaponFromInflictor( Actor inflictor, Name damagetype )
{
Class<Weapon> which = myplayer.ReadyWeapon?myplayer.ReadyWeapon.GetClass():null;
if ( inflictor is 'SWWMPuff' ) inflictor = inflictor.master; // special puffs transfer real inflictor through master pointer
if ( inflictor is 'Weapon' ) which = Weapon(inflictor).GetClass();
if ( which is 'DualExplodiumGun' ) which = 'ExplodiumGun'; // don't credit sister weapon
if ( inflictor && inflictor.FindInventory("ParriedBuff") ) which = 'DoomWeapon'; // gross hack
// properly credit some projectiles to their respective gun
else if ( inflictor is 'AirBullet' ) which = 'DeepImpact';
else if ( inflictor is 'PusherProjectile' ) which = 'PusherWeapon';
else if ( (inflictor is 'ExplodiumMagArm') || (inflictor is 'ExplodiumMagProj') || (inflictor is 'ExplodiumBulletImpact') ) which = 'ExplodiumGun';
else if ( (inflictor is 'DragonBreathArm') || ((inflictor is 'SaltImpact') && !inflictor.Args[0]) || ((inflictor is 'SaltBeam') && !inflictor.Args[1]) || (inflictor is 'CorrodeDebuff') || (inflictor is 'CorrosiveFlechette') || ((inflictor is 'TheBall') && !inflictor.special1) || (inflictor is 'GoldenImpact') || (inflictor is 'GoldenSubImpact') || (inflictor is 'GoldenSubSubImpact') ) which = 'Spreadgun';
else if ( ((inflictor is 'SaltImpact') && inflictor.Args[0]) || ((inflictor is 'SaltBeam') && inflictor.Args[1]) || ((inflictor is 'TheBall') && inflictor.special1) ) which = 'Wallbuster';
else if ( (inflictor is 'EvisceratorChunk') || (inflictor is 'EvisceratorProj') ) which = 'Eviscerator';
else if ( (inflictor is 'HellblazerMissile') || (inflictor is 'HellblazerRavagerArm') || (inflictor is 'HellblazerWarheadArm') ) which = 'Hellblazer';
else if ( (inflictor is 'BigBiospark') || (inflictor is 'BiosparkBall') || (inflictor is 'BiosparkBeamImpact') || (inflictor is 'BiosparkComboImpact') || (inflictor is 'BiosparkComboImpactSub') || (inflictor is 'BiosparkBeam') || (inflictor is 'BiosparkArc') || (inflictor is 'BiosparkCore') ) which = 'Sparkster';
else if ( (inflictor is 'SilverAirRip') || (inflictor is 'SilverAirRip2') || (inflictor is 'SilverImpact') || (inflictor is 'FatChodeImpact') || (inflictor is 'FatChodeExplosionArm') ) which = 'SilverBullet';
else if ( (inflictor is 'CandyBeam') || (inflictor is 'CandyPop') || (inflictor is 'CandyMagArm') || (inflictor is 'CandyGunProj') || (inflictor is 'CandyMagProj') || (inflictor is 'CandyBulletImpact') ) which = 'CandyGun';
else if ( (inflictor is 'YnykronBeam') || (inflictor is 'YnykronImpact') || (inflictor is 'YnykronSingularity') || (inflictor is 'YnykronCloud') || (inflictor is 'YnykronVoidBeam') || (inflictor is 'YnykronLightningArc') || (inflictor is 'YnykronLightningImpact') ) which = 'Ynykron';
else if ( (inflictor is 'Demolitionist') || (inflictor is 'DemolitionistShockwave') || (inflictor is 'DemolitionistRadiusShockwave') || (inflictor is 'SWWMGesture')
|| (inflictor is 'SWWMItemGesture') ) which = 'SWWMWeapon'; // hack to assume Demolitionist as weapon
else if ( inflictor is 'BigPunchSplash' )
{
// guess from damagetype
if ( (damagetype == 'Jump') || (damagetype == 'Dash') || (damagetype == 'Buttslam') || (damagetype == 'GroundPound') )
which = 'SWWMWeapon';
else if ( damagetype == 'Love' )
which = 'SWWMGesture';
// others are just weapon melee, so keep the readyweapon
}
else if ( inflictor is 'FroggyChair' )
which = 'SWWMItemGesture'; // more gross hacks
if ( damagetype == 'Falling' )
which = 'Weapon'; // the gross hacks continue
return which;
}
void AddDamageDealt( int dmg )
{
int upper = dmg/1000000000;
int lower = dmg%1000000000;
if ( hdamagedealt+upper > 999999999 ) hdamagedealt = 999999999;
else hdamagedealt += upper;
damagedealt += lower;
if ( damagedealt > 999999999 )
{
upper = damagedealt/1000000000;
lower = damagedealt%1000000000;
if ( hdamagedealt+upper > 999999999 ) hdamagedealt = 999999999;
else hdamagedealt += upper;
damagedealt = lower;
}
}
void AddDamageTaken( int dmg )
{
int upper = dmg/1000000000;
int lower = dmg%1000000000;
if ( hdamagetaken+upper > 999999999 ) hdamagetaken = 999999999;
else hdamagetaken += upper;
damagetaken += lower;
if ( damagetaken > 999999999 )
{
upper = damagetaken/1000000000;
lower = damagetaken%1000000000;
if ( hdamagetaken+upper > 999999999 ) hdamagetaken = 999999999;
else hdamagetaken += upper;
damagetaken = lower;
}
}
void AddLevelStats()
{
let ls = new("LevelStat");
ls.hub = !!(level.clusterflags&level.CLUSTER_HUB);
ls.levelname = level.levelname;
int iof = ls.levelname.IndexOf(" - by: ");
if ( iof != -1 ) ls.levelname.Truncate(iof);
ls.mapname = level.mapname;
ls.kcount = level.killed_monsters;
ls.ktotal = level.total_monsters;
ls.icount = level.found_items;
ls.itotal = level.total_items;
ls.scount = level.found_secrets;
ls.stotal = level.total_secrets;
ls.time = level.maptime;
ls.par = level.partime;
ls.suck = level.sucktime;
lstats.Push(ls);
}
void AddWeaponKill( Actor inflictor, Actor victim, Name damagetype )
{
if ( victim )
{
Class<Actor> mvictim = SWWMUtility.MergeMonster(victim.GetClass());
bool found = false;
for ( int i=0; i<mstats.Size(); i++ )
{
if ( mstats[i].m != mvictim ) continue;
found = true;
mstats[i].kills++;
break;
}
if ( !found )
{
let ms = new("MonsterKill");
ms.m = mvictim;
ms.kills = 1;
mstats.Push(ms);
}
}
Class<Weapon> which = WeaponFromInflictor(inflictor,damagetype);
if ( !which ) return;
for ( int i=0; i<wstats.Size(); i++ )
{
if ( wstats[i].w != which ) continue;
wstats[i].kills++;
if ( (favweapon == -1) || (wstats[favweapon].kills < wstats[i].kills) ) favweapon = i;
return;
}
let ws = new("WeaponUsage");
ws.w = which;
ws.kills = 1;
wstats.Push(ws);
if ( (favweapon == -1) || (wstats[favweapon].kills < ws.kills) )
favweapon = wstats.Size()-1;
}
static clearscope SWWMStats Find( PlayerInfo p )
{
let ti = ThinkerIterator.Create("SWWMStats",STAT_STATIC);
SWWMStats t;
while ( t = SWWMStats(ti.Next()) )
{
if ( t.myplayer != p ) continue;
return t;
}
return null;
}
}
// Scoring
Class SWWMCredits : Thinker
{
PlayerInfo myplayer;
int credits, hcredits;
static void Give( PlayerInfo p, int amount, int hamount = 0, bool cheat = false )
{
let c = Find(p);
if ( !c ) return;
if ( c.credits+amount < c.credits ) c.credits = int.max;
else c.credits += amount;
while ( c.credits > 999999999 )
{
c.credits -= 1000000000;
c.hcredits++;
}
if ( (c.hcredits+hamount < c.hcredits) || (c.hcredits+hamount > 999999999) ) c.hcredits = 999999999;
else c.hcredits += hamount;
let s = SWWMStats.Find(p);
if ( s && ((c.hcredits > s.hhiscore) || ((c.credits > s.hiscore) && (c.hcredits >= s.hhiscore))) )
{
s.hiscore = c.credits;
s.hhiscore = c.hcredits;
}
SWWMLoreLibrary.Add(p,"ScoreSystem");
}
static clearscope bool CanTake( PlayerInfo p, int amount, int hamount = 0 )
{
let c = Find(p);
if ( !c ) return false;
int req = amount, hreq = hamount;
while ( req > 999999999 )
{
req -= 1000000000;
hreq++;
}
// waaaaay too much
if ( (c.hcredits-hreq < 0) || (c.hcredits-hreq > c.hcredits) ) return false;
// too much!
if ( ((c.credits-amount < 0) || (c.credits-amount > c.credits)) && (c.hcredits-hreq <= 0) ) return false;
return true;
}
static bool Take( PlayerInfo p, int amount, int hamount = 0 )
{
let c = Find(p);
if ( !c ) return false;
int req = amount, hreq = hamount;
while ( req > 999999999 )
{
req -= 1000000000;
hreq++;
}
// waaaaay too much
if ( (c.hcredits-hreq < 0) || (c.hcredits-hreq > c.hcredits) ) return false;
// too much!
if ( ((c.credits-amount < 0) || (c.credits-amount > c.credits)) && (c.hcredits-hreq <= 0) ) return false;
c.hcredits -= hreq;
c.credits -= req;
while ( c.credits < 0 )
{
c.credits += 1000000000;
c.hcredits--;
}
return true;
}
static clearscope int, int Get( PlayerInfo p )
{
let c = Find(p);
if ( !c ) return 0;
return c.credits, c.hcredits;
}
static clearscope SWWMCredits Find( PlayerInfo p )
{
let ti = ThinkerIterator.Create("SWWMCredits",STAT_STATIC);
SWWMCredits t;
while ( t = SWWMCredits(ti.Next()) )
{
if ( t.myplayer != p ) continue;
return t;
}
return null;
}
}
// Trading history between players
Class SWWMTrade
{
int timestamp, type, amt;
String other;
Class<Inventory> what;
}
Class SWWMTradeHistory : Thinker
{
PlayerInfo myplayer;
Array<SWWMTrade> ent;
static void RegisterSend( PlayerInfo p, PlayerInfo other, Class<Inventory> what, int amt )
{
let th = Find(p);
if ( !th ) return;
SWWMTrade t = new("SWWMTrade");
t.timestamp = level.totaltime;
t.type = 0;
t.other = other.GetUserName();
t.what = what;
t.amt = amt;
th.ent.Push(t);
}
static void RegisterReceive( PlayerInfo p, PlayerInfo other, Class<Inventory> what, int amt )
{
let th = Find(p);
if ( !th ) return;
SWWMTrade t = new("SWWMTrade");
t.timestamp = level.totaltime;
t.type = 1;
t.other = other.GetUserName();
t.what = what;
t.amt = amt;
th.ent.Push(t);
}
static clearscope SWWMTradeHistory Find( PlayerInfo p )
{
let ti = ThinkerIterator.Create("SWWMTradeHistory",STAT_STATIC);
SWWMTradeHistory th;
while ( th = SWWMTradeHistory(ti.Next()) )
{
if ( th.myplayer != p ) continue;
return th;
}
return Null;
}
}
// Lore holder
enum ELoreTab
{
LORE_ITEM,
LORE_ENEMY,
LORE_PEOPLE,
LORE_LORE // lol
};
Class SWWMLore
{
String tag, text, assoc;
TextureID img;
int tab;
bool read;
}
Class SWWMLoreLibrary : Thinker
{
PlayerInfo myplayer;
Array<SWWMLore> ent;
int lastaddtic;
static bool PreVerify( String ref )
{
// restrictions
let mlog = SWWMMissionLog.Get();
if ( !(gameinfo.gametype&GAME_Raven) && (!mlog || ((mlog.year < 2171) && (mlog.month < 3))) )
{
if ( ref ~== "Parthoris" ) return true;
if ( ref ~== "Sidhe" ) return true;
if ( ref ~== "SerpentRiders" ) return true;
}
if ( !(gameinfo.gametype&GAME_Hexen) && (!mlog || ((mlog.year < 2171) && (mlog.month < 4))) )
{
if ( ref ~== "Cronos" ) return true;
if ( ref ~== "Kirin" ) return true; // not met
if ( ref ~== "Fabricator" ) return true; // not yet introduced
if ( ref ~== "Administrators" ) return true; // not met
}
// check if entry is for a collectible
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
let c = (Class<SWWMCollectible>)(AllActorClasses[i]);
if ( !c || (c == 'SWWMCollectible') ) continue;
let def = GetDefaultByType(c);
// skip if we match and it's not for this game
if ( (c.GetClassName() == ref) && !def.ValidGame() )
return true;
}
ref = ref.MakeUpper();
String tag = String.Format("SWWM_LORETAG_%s",ref);
String tab = String.Format("SWWM_LORETAB_%s",ref);
String text = String.Format("SWWM_LORETXT_%s",ref);
// check that it's valid
if ( StringTable.Localize(tag,false) == tag ) return true;
if ( StringTable.Localize(tab,false) == tab )
{
Console.Printf("Entry \"%s\" defines no tab.",ref);
return true;
}
if ( StringTable.Localize(text,false) == text )
{
Console.Printf("Entry \"%s\" defines no text.",ref);
return true;
}
return false;
}
bool DirectAdd( String ref )
{
if ( PreVerify(ref) ) return true;
return InternalAdd(ref);
}
private bool InternalAdd( String ref )
{
ref = ref.MakeUpper();
String tag = String.Format("SWWM_LORETAG_%s",ref);
String tab = String.Format("SWWM_LORETAB_%s",ref);
String text = String.Format("SWWM_LORETXT_%s",ref);
String assoc = String.Format("SWWM_LOREREL_%s",ref);
// redirects
let mlog = SWWMMissionLog.Get();
if ( (gameinfo.gametype&GAME_Hexen) || (mlog && (mlog.year >= 2171) && (mlog.month >= 4)) )
{
if ( text ~== "SWWM_LORETXT_AKARIPROJECT" )
text = "SWWM_LORETXT_AKARIPROJECT3"; // mentions kirin
else if ( text ~== "SWWM_LORETXT_ANARUKON" )
text = "SWWM_LORETXT_ANARUKON2"; // comments from miyamoto-xanai wedding
else if ( text ~== "SWWM_LORETXT_BRCALBUM" )
text = "SWWM_LORETXT_BRCALBUM2"; // comment about kirin's song
else if ( text ~== "SWWM_LORETXT_DEMOLITIONIST" )
text = "SWWM_LORETXT_DEMOLITIONIST3"; // married to kirin
else if ( text ~== "SWWM_LORETXT_DEMONINVASION" )
text = "SWWM_LORETXT_DEMONINVASION4"; // meeting with azkhan
else if ( text ~== "SWWM_LORETXT_GHOULHUNT" )
text = "SWWM_LORETXT_GHOULHUNT2"; // met anthon anderken during the wedding
else if ( text ~== "SWWM_LORETXT_GODS" )
text = "SWWM_LORETXT_GODS2"; // beyond gods
else if ( text ~== "SWWM_LORETXT_HELL" )
text = "SWWM_LORETXT_HELL4"; // met father nostros during the wedding
else if ( text ~== "SWWM_LORETXT_IBUKIMILK" )
text = "SWWM_LORETXT_IBUKIMILK3"; // tasted by kirin
else if ( text ~== "SWWM_LORETXT_MAIDBOT" )
text = "SWWM_LORETXT_MAIDBOT2"; // married to kirin
else if ( text ~== "SWWM_LORETXT_NANA" )
text = "SWWM_LORETXT_NANA3"; // stuff that happened at the wedding
else if ( text ~== "SWWM_LORETXT_NOVOSKHANA" )
text = "SWWM_LORETXT_NOVOSKHANA2"; // met up with the empress
else if ( text ~== "SWWM_LORETXT_RAGEKIT" )
text = "SWWM_LORETXT_RAGEKIT2"; // kirin's reactions to demo using this item
else if ( text ~== "SWWM_LORETXT_SANKAIDERIHA" )
text = "SWWM_LORETXT_SANKAIDERIHA2"; // comments about kirin
else if ( text ~== "SWWM_LORETXT_SAYA" )
text = "SWWM_LORETXT_SAYA3"; // married kirin
else if ( text ~== "SWWM_LORETXT_SAFETYTETHER" )
text = "SWWM_LORETXT_SAFETYTETHER2"; // we're in cronos now
else if ( text ~== "SWWM_LORETXT_SERPENTRIDERS" )
text = "SWWM_LORETXT_SERPENTRIDERS2"; // defeated d'sparil
else if ( text ~== "SWWM_LORETXT_XANIMEN" )
text = "SWWM_LORETXT_XANIMEN2"; // footnote about nuoma
else if ( text ~== "SWWM_LORETXT_YNYKRON" )
text = "SWWM_LORETXT_YNYKRON2"; // confirmed to harm (but not kill) gods
else if ( text ~== "SWWM_LORETXT_ZANAVETH2" )
text = "SWWM_LORETXT_ZANAVETH22"; // met at wedding
}
if ( (gameinfo.gametype&GAME_Raven) || (mlog && (mlog.year >= 2171) && (mlog.month >= 3)) )
{
if ( text ~== "SWWM_LORETXT_AKARIPROJECT" )
text = "SWWM_LORETXT_AKARIPROJECT2"; // fiction becomes reality
else if ( text ~== "SWWM_LORETXT_DECADEMECH" )
text = "SWWM_LORETXT_DECADEMECH2"; // extra info
else if ( text ~== "SWWM_LORETXT_DEMONINVASION" )
text = "SWWM_LORETXT_DEMONINVASION3"; // events of doom 64 and such
else if ( text ~== "SWWM_LORETXT_DOOMGUY" )
text = "SWWM_LORETXT_DOOMGUY3"; // he gone
else if ( text ~== "SWWM_LORETXT_HELL" )
text = "SWWM_LORETXT_HELL3"; // invasion was a thing of the past
else if ( text ~== "SWWM_LORETXT_UAC" )
text = "SWWM_LORETXT_UAC3"; // events of doom 64 and more
}
if ( (gameinfo.gametype&GAME_Raven) || SWWMUtility.IsEviternity() || (mlog && (mlog.year >= 2150) && (mlog.month >= 5)) )
{
if ( text ~== "SWWM_LORETXT_AKARILABS" )
text = "SWWM_LORETXT_AKARILABS2"; // demo won, akari project announced
else if ( text ~== "SWWM_LORETXT_BIGSHOT" )
text = "SWWM_LORETXT_BIGSHOT2"; // predictions about crimes_m
else if ( text ~== "SWWM_LORETXT_DEMOLITIONIST" )
text = "SWWM_LORETXT_DEMOLITIONIST2"; // demo rewarded with maidbot frame
else if ( text ~== "SWWM_LORETXT_DEMONINVASION" )
text = "SWWM_LORETXT_DEMONINVASION2"; // victory against invasion
else if ( text ~== "SWWM_LORETXT_DOOMGUY" )
text = "SWWM_LORETXT_DOOMGUY2"; // decommissioned
else if ( text ~== "SWWM_LORETXT_GENERICCUBE" )
text = "SWWM_LORETXT_GENERICCUBE2"; // info from mykka
else if ( text ~== "SWWM_LORETXT_HELL" )
text = "SWWM_LORETXT_HELL2"; // events of tnt/plutonia
else if ( text ~== "SWWM_LORETXT_IBUKIMILK" )
text = "SWWM_LORETXT_IBUKIMILK2"; // tasted by demo
else if ( text ~== "SWWM_LORETXT_NANA" )
text = "SWWM_LORETXT_NANA2"; // demo met nana
else if ( text ~== "SWWM_LORETXT_SAYA" )
text = "SWWM_LORETXT_SAYA2"; // dating demo
else if ( text ~== "SWWM_LORETXT_UAC" )
text = "SWWM_LORETXT_UAC2"; // uac "reformed"
else if ( text ~== "SWWM_LORETXT_VOICEBOX" )
text = "SWWM_LORETXT_VOICEBOX2"; // remarks on demo's voice as a maidbot
else if ( text ~== "SWWM_LORETXT_ZANAVETH3" )
text = "SWWM_LORETXT_ZANAVETH32"; // iagb happened
}
// check if existing
for ( int i=0; i<ent.Size(); i++ )
{
if ( ent[i].tag != "$"..tag ) continue;
return true;
}
SWWMLore e = new("SWWMLore");
e.tag = "$"..tag;
if ( StringTable.Localize(e.tag) == "" )
{
Console.Printf("Entry \"%s\" has an empty tag.",ref);
return true;
}
String ttab = StringTable.Localize(tab,false);
if ( ttab ~== "People" ) e.tab = LORE_PEOPLE;
else if ( ttab ~== "Lore" ) e.tab = LORE_LORE;
else if ( ttab ~== "Item" ) e.tab = LORE_ITEM;
else if ( ttab ~== "Enemy" ) e.tab = LORE_ENEMY;
else
{
Console.Printf("Entry \"%s\" has an incorrect tab setting of \"%s\".",ref,ttab);
return true;
}
e.text = "$"..text;
if ( StringTable.Localize(e.text) == "" )
{
Console.Printf("Entry \"%s\" has empty text.",ref);
return true;
}
e.assoc = "$"..assoc;
e.read = false;
// image for certain entries
e.img = TexMan.CheckForTexture("graphics/KBase/PFP_"..ref..".png",TexMan.Type_Any);
// "new lore" message
if ( (level.maptime > 0) && (gametic > lastaddtic) && (myplayer == players[consoleplayer]) && (!menuactive || (menuactive == Menu.OnNoPause)) && (myplayer.mo is 'Demolitionist') )
Console.Printf(StringTable.Localize("$SWWM_NEWLORE"));
lastaddtic = gametic;
ent.Push(e);
return true;
}
static void Add( PlayerInfo p, String ref )
{
if ( deathmatch || PreVerify(ref) ) return;
SWWMLoreLibrary ll = Find(p);
if ( !ll )
{
ll = new("SWWMLoreLibrary");
ll.ChangeStatNum(STAT_STATIC);
ll.myplayer = p;
}
ll.InternalAdd(ref);
}
void MarkRead( int idx )
{
if ( (idx < 0) || (idx >= ent.Size()) ) return;
if ( !ent[idx].read )
{
ent[idx].read = true;
// add associated entries
Array<String> rel;
rel.Clear();
String assocstr = StringTable.Localize(ent[idx].assoc);
assocstr.Split(rel,";",0);
for ( int i=0; i<rel.Size(); i++ )
{
if ( (rel[i] != "") && !DirectAdd(rel[i]) )
Console.Printf("Related entry \"%s\" not found, please update LANGUAGE.txt",rel[i]);
}
}
}
clearscope int FindEntry( String tag )
{
for ( int i=0; i<ent.Size(); i++ )
{
if ( ent[i].tag ~== tag )
return i;
}
return -1;
}
static clearscope SWWMLoreLibrary Find( PlayerInfo p )
{
let ti = ThinkerIterator.Create("SWWMLoreLibrary",STAT_STATIC);
SWWMLoreLibrary ll;
while ( ll = SWWMLoreLibrary(ti.Next()) )
{
if ( ll.myplayer != p ) continue;
return ll;
}
return Null;
}
}