805 lines
26 KiB
Ucode
805 lines
26 KiB
Ucode
/* CODE BREAKING WARNING in v5.17
|
|
Removed AchInfo objects. Used AchDef.CurrentProgress instead
|
|
|
|
Changed variables and functions:
|
|
! AchDef
|
|
- SpawnReplicationInfo(), GetAchievementByIndex()
|
|
! GetAchievementByID()
|
|
+ ClientSetAchProgress()
|
|
+ GetIcon(), IsUnlocked()
|
|
*/
|
|
|
|
Class ScrnAchievements extends ScrnCustomProgressBytes
|
|
Abstract;
|
|
|
|
#exec OBJ LOAD FILE=KillingFloorHUD.utx
|
|
#exec OBJ LOAD FILE=KillingFloor2HUD.utx
|
|
|
|
var texture LockedIcon;
|
|
|
|
const DATA_USE_PREVIOUS = -1;
|
|
const DATA_DONT_USE = -2;
|
|
|
|
// Copy-Pasted from ScrnBalance
|
|
const ACH_ALLFLAGS = 0xFFFFFFFF;
|
|
const ACH_ENABLE = 0x0001;
|
|
const ACH_HARD = 0x0002;
|
|
const ACH_SUI = 0x0004;
|
|
const ACH_HOE = 0x0008;
|
|
const ACH_SCRNZEDS = 0x0010;
|
|
const ACH_WPCZEDS = 0x0020;
|
|
const ACH_HARDPAT = 0x0040;
|
|
const ACH_DOOM3 = 0x0080;
|
|
|
|
struct AchDef
|
|
{
|
|
var name ID;
|
|
var string DisplayName, Description;
|
|
var texture Icon;
|
|
var int MaxProgress;
|
|
|
|
var bool bForceShow; // show achievement event if it was earned before
|
|
|
|
// Filters achievements to mach ScrnBalanceSrv.AchievementFlags.
|
|
// Used for hiding achievements can't be earned on the current server
|
|
// FilterAll - AchievementFlags must have all bits set respecting filter mask
|
|
// FilterAny - AchievementFlags must have at least one bit set respecting filter mask
|
|
// Achievement will be shown in the list only if both FilterMaskAll and FilterMaskAny
|
|
// requirements are met or mask = 0
|
|
var int FilterMaskAll, FilterMaskAny;
|
|
|
|
// How many BITS required to store progress value of the achievement?
|
|
// Value range: 1 - 32. Default = 1
|
|
// Special Values:
|
|
// -1 - use value from previous achievement
|
|
var int DataSize;
|
|
|
|
var int CurrentProgress;
|
|
var bool bUnlockedJustNow;
|
|
var bool bDisplayFlag; // use to show/hime achievement from the list
|
|
|
|
var name Group;
|
|
};
|
|
var array<AchDef> AchDefs;
|
|
var protected int VisibleAchCount; // achievement count with bDisplayFlag = true. Valid only after SetVisibility() call.
|
|
|
|
struct AchStrInfo
|
|
{
|
|
var ScrnAchievements AchHandler;
|
|
var int AchIndex;
|
|
};
|
|
|
|
//system data to store AchDefs
|
|
struct SysInfoRec
|
|
{
|
|
var int ArrayIndex;
|
|
var byte BitOffset;
|
|
var byte RealDataSize; // AchDef store suggested size for storing data. This one stores actual
|
|
};
|
|
var private array<SysInfoRec> SysInfo;
|
|
|
|
var name DefaultAchGroup; // default value to set to AchDefs.Group
|
|
var private array< class<ScrnAchievements> > AchClassList; // add here custom achievement classes to load their data from SP custom progress
|
|
|
|
struct AchGroupInfo {
|
|
var name Group; // AchDef.Group
|
|
var localized string Caption; // what player sees in GUI
|
|
};
|
|
var array<AchGroupInfo> GroupInfo;
|
|
|
|
|
|
var protected bool bNeedToSetDefaultAchievementData; //if true SetDefaultAchievementData() will be called on InitData()
|
|
|
|
replication
|
|
{
|
|
reliable if( Role == ROLE_Authority )
|
|
ClientSetAchProgress;
|
|
}
|
|
|
|
|
|
simulated function InitData()
|
|
{
|
|
local int i, index;
|
|
local byte offset;
|
|
|
|
if ( Role == ROLE_Authority )
|
|
log(GetItemName(String(class.name)) $ " - loading achievement data for "$PlayerController(Owner).PlayerReplicationInfo.PlayerName, 'ScrnBalance');
|
|
else
|
|
log(GetItemName(String(class.name)) $ " - loading achievement my data", 'ScrnBalance');
|
|
StopWatch(false); // reset timer
|
|
|
|
if ( default.bNeedToSetDefaultAchievementData ) {
|
|
// since all instances of the same class have the same data, SetDefaultAchievementData() can be called only once
|
|
// and then set to default values
|
|
SetDefaultAchievementData();
|
|
for ( i = 0; i < AchDefs.Length; ++i ) {
|
|
if ( AchDefs[i].Group == '' )
|
|
AchDefs[i].Group = DefaultAchGroup;
|
|
default.AchDefs[i].Group = AchDefs[i].Group;
|
|
default.AchDefs[i].FilterMaskAny = AchDefs[i].FilterMaskAny;
|
|
default.AchDefs[i].FilterMaskAll = AchDefs[i].FilterMaskAll;
|
|
}
|
|
if ( Level.NetMode != NM_DedicatedServer ) {
|
|
for ( i = 0; i < AchDefs.Length; ++i ) {
|
|
default.AchDefs[i].DisplayName = AchDefs[i].DisplayName;
|
|
default.AchDefs[i].Description = AchDefs[i].Description;
|
|
default.AchDefs[i].Icon = AchDefs[i].Icon;
|
|
}
|
|
}
|
|
default.bNeedToSetDefaultAchievementData = false;
|
|
}
|
|
|
|
SysInfo.Length = AchDefs.Length;
|
|
for ( i = 0; i < AchDefs.Length; ++i ) {
|
|
|
|
|
|
if ( AchDefs[i].DataSize == DATA_DONT_USE ) {
|
|
SysInfo[i].ArrayIndex = DATA_DONT_USE;
|
|
SysInfo[i].BitOffset = 0;
|
|
SysInfo[i].RealDataSize = 32;
|
|
}
|
|
else if ( AchDefs[i].DataSize == DATA_USE_PREVIOUS && i > 0 ) {
|
|
SysInfo[i].ArrayIndex = SysInfo[i-1].ArrayIndex;
|
|
SysInfo[i].BitOffset = SysInfo[i-1].BitOffset;
|
|
SysInfo[i].RealDataSize = SysInfo[i-1].RealDataSize;
|
|
}
|
|
else {
|
|
SysInfo[i].ArrayIndex = index;
|
|
SysInfo[i].BitOffset = offset;
|
|
SysInfo[i].RealDataSize = clamp(AchDefs[i].DataSize, 1, 32);
|
|
|
|
//calculate position for the next record
|
|
offset += SysInfo[i].RealDataSize;
|
|
if ( offset >= 8) {
|
|
index += offset/8;
|
|
offset = offset%8;
|
|
}
|
|
// this prevents permamently locked AchDefs in cases when data size is set too short
|
|
// and isn't able to store MaxProgress
|
|
AchDefs[i].MaxProgress = clamp(AchDefs[i].MaxProgress, 1, 2**SysInfo[i].RealDataSize - 1);
|
|
}
|
|
|
|
//SpawnReplicationInfo(i);
|
|
}
|
|
if ( offset > 0)
|
|
index++;
|
|
Data.Length = index;
|
|
|
|
StopWatch(true); // log elapsed time
|
|
}
|
|
|
|
|
|
|
|
simulated function SetDefaultAchievementData()
|
|
{
|
|
local int i;
|
|
|
|
if ( Level.NetMode == NM_DedicatedServer )
|
|
return;
|
|
|
|
for ( i = 0; i < AchDefs.Length; ++i ) {
|
|
if (InStr(AchDefs[i].Description, "%c") != -1 )
|
|
ReplaceText(AchDefs[i].Description, "%c", string(AchDefs[i].MaxProgress));
|
|
}
|
|
}
|
|
|
|
simulated function int GetAchievementCount()
|
|
{
|
|
return min(AchDefs.length, SysInfo.length);
|
|
}
|
|
|
|
simulated function bool IsAchievementIndexValid(int AchIndex)
|
|
{
|
|
return AchIndex >= 0 && AchIndex < GetAchievementCount();
|
|
}
|
|
|
|
protected simulated function int ReadProgressFromData(int AchIndex)
|
|
{
|
|
/* Warning! Need to be cautious with data sizes in binary shifting
|
|
and negative bit in comparision (byte is unsigned type)
|
|
Binary shift operators used on byte var return int value anyway, e.g.:
|
|
byte b = 0xFF;
|
|
b = 0xFF << 4 >> 4; // b = 0xFF
|
|
b = 0xFF << 4;
|
|
b = 0xFF >> 4; // b = 0x0F
|
|
b = byte(0xFF << 4) >> 4; // b = 0x0F
|
|
*/
|
|
local int count, result, index, bits2load; //bits2load must be integer, cuz it can be negative
|
|
local byte offset, sh, bits_loaded;
|
|
|
|
|
|
count = GetAchievementCount();
|
|
if ( AchIndex < 0 || AchIndex >= count )
|
|
return 0;
|
|
|
|
|
|
index = SysInfo[AchIndex].ArrayIndex;
|
|
if ( index == DATA_DONT_USE )
|
|
return 0; // child functions should override ReadProgressFromData to implement getting data from other sources
|
|
|
|
offset = SysInfo[AchIndex].BitOffset;
|
|
result = Data[index] >>> offset; //load current byte, shifting by offset - amount of bytes that don't belong to the current record
|
|
bits_loaded = 8 - offset; // how much bits of information are loaded
|
|
bits2load = SysInfo[AchIndex].RealDataSize - bits_loaded;
|
|
|
|
//debug
|
|
// LogBytes(Data);
|
|
// log("ReadProgressFromData("$AchIndex$"):"
|
|
// @ "index="$index
|
|
// @ "offset="$offset
|
|
// @ "value_loaded="$result
|
|
// @ "bits_loaded="$bits_loaded
|
|
// @ "bits2load="$bits2load
|
|
// ,'ScrnBalance');
|
|
|
|
if (bits2load < 0) {
|
|
//loaded too much - need to clear higher bits
|
|
result = result & 0xFF>>(offset-bits2load);
|
|
}
|
|
else {
|
|
while ( bits2load > 0 ) {
|
|
sh = min(8, bits2load);
|
|
// I'm really sorry about those, who'll try to understand a meaning of the expression below :)
|
|
result = (Data[++index] & 0xFF>>(8-sh))<< bits_loaded | result;
|
|
bits_loaded += sh;
|
|
bits2load -= sh;
|
|
}
|
|
}
|
|
|
|
//log("result="$result,'ScrnBalance');
|
|
|
|
return result;
|
|
}
|
|
|
|
protected function WriteProgressToData(int AchIndex, int Value)
|
|
{
|
|
local int index;
|
|
local byte offset, bits2write_total, bits2write_cur, mask, val2write;
|
|
|
|
if ( !IsAchievementIndexValid(AchIndex) )
|
|
return;
|
|
|
|
|
|
index = SysInfo[AchIndex].ArrayIndex;
|
|
if ( index == DATA_DONT_USE )
|
|
return; // child functions should override WriteProgressToData to implement writting data to other sources
|
|
|
|
offset = SysInfo[AchIndex].BitOffset;
|
|
bits2write_total = SysInfo[AchIndex].RealDataSize;
|
|
|
|
//debug
|
|
// log("WriteProgressToData("$AchIndex$", "$Value$"):"
|
|
// @ "index="$index
|
|
// @ "offset="$offset
|
|
// @ "bits2write_total="$bits2write_total
|
|
// ,'ScrnBalance');
|
|
|
|
while ( bits2write_total > 0 ) {
|
|
bits2write_cur = min(bits2write_total, 8 - offset);
|
|
// mask has 1 in bits that belong to current value
|
|
mask = (0xFF << offset) & (0xFF >> (8- offset - bits2write_cur));
|
|
//clear old value
|
|
Data[index] = Data[index] & (mask ^ 0xFF);
|
|
//write new value
|
|
val2write = (value << offset) & mask;
|
|
Data[index] = Data[index] | val2write;
|
|
|
|
value = value >>> bits2write_cur;
|
|
bits2write_total -= bits2write_cur;
|
|
index++;
|
|
offset = 0;
|
|
}
|
|
//debug
|
|
//LogBytes(Data);
|
|
|
|
UpdateValueString();
|
|
}
|
|
|
|
|
|
|
|
// load achievement progress from Data
|
|
protected simulated function UpdateAchievements()
|
|
{
|
|
local int i, count, a;
|
|
|
|
count = GetAchievementCount();
|
|
|
|
for ( i = 0; i < count; ++i ) {
|
|
a = ReadProgressFromData(i);
|
|
if ( a != AchDefs[i].CurrentProgress ) {
|
|
AchDefs[i].CurrentProgress = a;
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated protected function ClientSetAchProgress(int AchIndex, int NewValue, bool bSilent)
|
|
{
|
|
local int OldProgress, MaxProgress;
|
|
local ScrnPlayerController PC;
|
|
|
|
OldProgress = AchDefs[AchIndex].CurrentProgress;
|
|
MaxProgress = AchDefs[AchIndex].MaxProgress;
|
|
|
|
if ( Role < ROLE_Authority ) {
|
|
// authority (solo or listen server) already have this values set in ProgressAchievement()
|
|
AchDefs[AchIndex].CurrentProgress = NewValue;
|
|
|
|
if ( OldProgress < MaxProgress && NewValue == MaxProgress )
|
|
AchDefs[AchIndex].bUnlockedJustNow = true;
|
|
}
|
|
|
|
PC = ScrnPlayerController(Level.GetLocalPlayerController());
|
|
if ( PC == none )
|
|
return;
|
|
|
|
// debug
|
|
// PC.ClientMessage(GetItemName(String(name)) $ "("$AchIndex$") = "$NewValue);
|
|
// log(GetItemName(String(name)) $ "("$AchIndex$") = "$NewValue, 'ScrnBalance');
|
|
|
|
if ( !bSilent ) {
|
|
// display achievement in the following circumtances:
|
|
// - it forced to be shown every time by achievement author
|
|
// - it has beed unlocked just now
|
|
// - it has been found out just now (first progression)
|
|
// - there was no achievement progress shown recently (higher progress requirement = rarer displayings)
|
|
// - every 20% of progression
|
|
if ( AchDefs[AchIndex].bForceShow || AchDefs[AchIndex].bUnlockedJustNow || OldProgress == 0 || NewValue == 1
|
|
|| ( PC.bAlwaysDisplayAchProgression && MaxProgress < 1000 )
|
|
|| PC.AchievementDisplayCooldown < -MaxProgress/5
|
|
|| NewValue % (max(MaxProgress/5, 1)) == 0) {
|
|
PC.DisplayAchievementStatus(self, AchIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
function bool ProgressAchievement(int AchIndex, int Inc)
|
|
{
|
|
local int count, IndexToWrite, ProgressToWrite;
|
|
local bool result;
|
|
local bool bSilent;
|
|
|
|
if ( !IsAchievementIndexValid(AchIndex) || Inc == 0 || Level.Game.GameDifficulty < 2 )
|
|
return false;
|
|
|
|
bReplicateDataToClients = false; // further data updates will be send via ClientSetAchProgress()
|
|
result = (Inc > 0 && AchDefs[AchIndex].CurrentProgress < AchDefs[AchIndex].MaxProgress)
|
|
|| (Inc < 0 && AchDefs[AchIndex].CurrentProgress > 0);
|
|
count = GetAchievementCount();
|
|
|
|
// in cases when more achevements are sharing the same data
|
|
while ( AchIndex > 0 && AchDefs[AchIndex].DataSize == DATA_USE_PREVIOUS )
|
|
AchIndex--;
|
|
|
|
IndexToWrite = AchIndex;
|
|
|
|
do {
|
|
if ( Inc < 0 ) {
|
|
AchDefs[AchIndex].CurrentProgress += Inc;
|
|
if ( AchDefs[AchIndex].CurrentProgress < 0 )
|
|
AchDefs[AchIndex].CurrentProgress = 0;
|
|
ClientSetAchProgress(AchIndex, AchDefs[AchIndex].CurrentProgress, true);
|
|
}
|
|
else if ( AchDefs[AchIndex].CurrentProgress < AchDefs[AchIndex].MaxProgress ) {
|
|
AchDefs[AchIndex].CurrentProgress += Inc;
|
|
result = true;
|
|
if ( AchDefs[AchIndex].CurrentProgress >= AchDefs[AchIndex].MaxProgress ) {
|
|
AchDefs[AchIndex].bUnlockedJustNow = true;
|
|
AchDefs[AchIndex].CurrentProgress = AchDefs[AchIndex].MaxProgress; // in case prevprogress + inc > max
|
|
class'ScrnBalance'.default.Mut.AchievementEarned(self, AchIndex);
|
|
AnnounceEarn(self, AchIndex, true);
|
|
}
|
|
ClientSetAchProgress(AchIndex, AchDefs[AchIndex].CurrentProgress, bSilent);
|
|
bSilent = true; //show progress only for a first achievement, if more of them are sharing same data
|
|
}
|
|
else if ( AchDefs[AchIndex].bForceShow ) {
|
|
// achievement is already unlocked, but it is achieved again and author want to show it every time
|
|
AchDefs[AchIndex].bUnlockedJustNow = false;
|
|
ClientSetAchProgress(AchIndex, AchDefs[AchIndex].MaxProgress, false);
|
|
class'ScrnBalance'.default.Mut.AchievementEarned(self, AchIndex);
|
|
AnnounceEarn(self, AchIndex, false);
|
|
}
|
|
ProgressToWrite = max(ProgressToWrite, AchDefs[AchIndex].CurrentProgress);
|
|
AchIndex++;
|
|
} until ( AchIndex >= count || AchDefs[AchIndex].DataSize != DATA_USE_PREVIOUS );
|
|
|
|
WriteProgressToData(IndexToWrite, ProgressToWrite);
|
|
|
|
return result;
|
|
}
|
|
|
|
static final function bool ProgressAchievementByID(ClientPerkRepLink L, name ID, int Inc)
|
|
{
|
|
local SRCustomProgress S;
|
|
local ScrnAchievements A;
|
|
local int i;
|
|
|
|
if ( L == none || L.Level.Game.GameDifficulty < 2 )
|
|
return false;
|
|
|
|
for( S = L.CustomLink; S != none; S = S.NextLink ) {
|
|
A = ScrnAchievements(S);
|
|
if( A != none ) {
|
|
for ( i = 0; i < A.AchDefs.length; ++i ) {
|
|
if ( A.AchDefs[i].ID == ID )
|
|
return A.ProgressAchievement(i, Inc);
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// override to handle achievement earning events
|
|
function AchievementEarned(ScrnAchievements A, int Index, bool bFirstTimeEarned) {}
|
|
|
|
static final private function AnnounceEarn(ScrnAchievements AchOwner, int AchIndex, bool bFirstTimeEarned)
|
|
{
|
|
local SRCustomProgress S;
|
|
local ScrnAchievements A;
|
|
|
|
for( S = AchOwner.RepLink.CustomLink; S != none; S = S.NextLink ) {
|
|
A = ScrnAchievements(S);
|
|
if( A != none )
|
|
A.AchievementEarned(AchOwner, AchIndex, bFirstTimeEarned);
|
|
}
|
|
}
|
|
|
|
static final function bool ResetAchievementByID(ClientPerkRepLink L, name ID)
|
|
{
|
|
local SRCustomProgress S;
|
|
local ScrnAchievements A;
|
|
local int i;
|
|
|
|
if ( L == none || L.Level.Game.GameDifficulty < 2 )
|
|
return false;
|
|
|
|
for( S = L.CustomLink; S != none; S = S.NextLink ) {
|
|
A = ScrnAchievements(S);
|
|
if( A != none ) {
|
|
for ( i = 0; i < A.AchDefs.length; ++i ) {
|
|
if ( A.AchDefs[i].ID == ID ) {
|
|
A.ProgressAchievement(i, -A.AchDefs[i].CurrentProgress);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
simulated function UpdateData()
|
|
{
|
|
super.UpdateData();
|
|
UpdateAchievements();
|
|
}
|
|
|
|
|
|
// add custom progress values for all AchDefs
|
|
static final function InitAchievements(ClientPerkRepLink L)
|
|
{
|
|
local int i;
|
|
|
|
if ( L == none )
|
|
return;
|
|
|
|
for ( i = 0; i < class'ScrnAchievements'.default.AchClassList.Length; ++i ) {
|
|
if ( class'ScrnAchievements'.default.AchClassList[i] != none )
|
|
L.AddCustomValue(class'ScrnAchievements'.default.AchClassList[i]);
|
|
}
|
|
}
|
|
|
|
// use this function to add custom achievements to the list
|
|
static final function RegisterAchievements( class<ScrnAchievements> NewAchClass )
|
|
{
|
|
local int i;
|
|
|
|
for ( i = 0; i < class'ScrnAchievements'.default.AchClassList.Length; ++i ) {
|
|
if ( class'ScrnAchievements'.default.AchClassList[i] == NewAchClass )
|
|
return;
|
|
}
|
|
class'ScrnAchievements'.default.AchClassList[i] = NewAchClass;
|
|
}
|
|
|
|
static final function UnRegisterAchievements( class<ScrnAchievements> AchClass )
|
|
{
|
|
local int i;
|
|
|
|
for ( i = 0; i < class'ScrnAchievements'.default.AchClassList.Length; ++i ) {
|
|
if ( class'ScrnAchievements'.default.AchClassList[i] == AchClass ) {
|
|
class'ScrnAchievements'.default.AchClassList.remove(i, 1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static final function ResetAchList()
|
|
{
|
|
class'ScrnAchievements'.default.AchClassList.Length = 2;
|
|
class'ScrnAchievements'.default.AchClassList[0] = Class'ScrnBalanceSrv.Ach';
|
|
class'ScrnAchievements'.default.AchClassList[1] = Class'ScrnBalanceSrv.AchMaps';
|
|
}
|
|
|
|
simulated function GetAchievementStats(out int Completed, out int Total, optional int AchievementFlags, optional name Group)
|
|
{
|
|
local int i, count;
|
|
|
|
Completed = 0;
|
|
Total = 0;
|
|
count = GetAchievementCount();
|
|
|
|
for ( i = 0; i < count; ++i ) {
|
|
if ( (Group == '' || Group == AchDefs[i].Group) && FilterMached(AchievementFlags, AchDefs[i].FilterMaskAll, AchDefs[i].FilterMaskAny) ) {
|
|
Total++;
|
|
if ( AchDefs[i].CurrentProgress >= AchDefs[i].MaxProgress )
|
|
Completed++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static final function GetGlobalAchievementStats(ClientPerkRepLink L, out int Completed, out int Total, optional int AchievementFlags, optional name Group)
|
|
{
|
|
local SRCustomProgress S;
|
|
local ScrnAchievements A;
|
|
local int c, t;
|
|
|
|
Completed = 0;
|
|
Total = 0;
|
|
|
|
if ( L == none )
|
|
return;
|
|
|
|
for( S = L.CustomLink; S != none; S = S.NextLink ) {
|
|
A = ScrnAchievements(S);
|
|
if( A != none ) {
|
|
A.GetAchievementStats(c, t, AchievementFlags, Group);
|
|
Completed += c;
|
|
Total += t;
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated function string GetDisplayString()
|
|
{
|
|
local int Completed, Total;
|
|
GetAchievementStats(Completed, Total);
|
|
return Completed $ " / " $ Total;
|
|
}
|
|
|
|
|
|
|
|
//returns true if Flags matched both FilterAll and FilterAny, or their values are 0
|
|
static function bool FilterMached(int Flags, int FilterMaskAll, int FilterMaskAny)
|
|
{
|
|
return Flags == 0 || (
|
|
(FilterMaskAll == 0 || (FilterMaskAll & Flags) == FilterMaskAll)
|
|
&& (FilterMaskAny == 0 || (FilterMaskAny & Flags) != 0) ); //this reminding me LISP ;)
|
|
}
|
|
|
|
simulated function int GetVisibleAchCount()
|
|
{
|
|
return VisibleAchCount;
|
|
}
|
|
|
|
// returns real achievement index (which can be used to access AchDefs) by visible index
|
|
// uses bDisplayFlag
|
|
// returns -1 if visible index is you of bounds
|
|
simulated function int GetVisibleAchIndex(int VisibleIndex)
|
|
{
|
|
local int i;
|
|
if ( VisibleAchCount < 0 )
|
|
return -1;
|
|
|
|
for ( i=0; i < AchDefs.Length; ++i ) {
|
|
if ( AchDefs[i].bDisplayFlag ) {
|
|
if (VisibleIndex == 0)
|
|
return i;
|
|
else
|
|
VisibleIndex--;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// sets bDisplayFlag of AchDefs
|
|
simulated function SetVisibility(optional int AchievementFlags, optional name Group, optional bool bOnlyLocked)
|
|
{
|
|
local int i;
|
|
|
|
VisibleAchCount = 0;
|
|
for ( i = 0; i < AchDefs.length; ++i ) {
|
|
|
|
AchDefs[i].bDisplayFlag = (!bOnlyLocked || !IsUnlocked(i) )
|
|
&& (Group == '' || Group == AchDefs[i].Group)
|
|
&& FilterMached(AchievementFlags, AchDefs[i].FilterMaskAll, AchDefs[i].FilterMaskAny);
|
|
|
|
if ( AchDefs[i].bDisplayFlag )
|
|
VisibleAchCount++;
|
|
// else
|
|
// log("Achievement "$AchDefs[i].ID $ " didn't pass filter " $ IntToHex(AchievementFlags), 'ScrnBalance');
|
|
}
|
|
}
|
|
|
|
static final function AchStrInfo GetAchievementByID(ClientPerkRepLink L, name ID, optional bool bOnlyLocked )
|
|
{
|
|
local SRCustomProgress S;
|
|
local ScrnAchievements A;
|
|
local int i;
|
|
local AchStrInfo result;
|
|
|
|
if ( L == none )
|
|
return result;
|
|
|
|
for( S = L.CustomLink; S != none; S = S.NextLink ) {
|
|
A = ScrnAchievements(S);
|
|
if( A != none ) {
|
|
for ( i = 0; i < A.AchDefs.length; ++i ) {
|
|
if ( A.AchDefs[i].ID == ID ) {
|
|
if ( !bOnlyLocked || !A.IsUnlocked(i) ) {
|
|
result.AchHandler = A;
|
|
result.AchIndex = i;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// returns true if achievement with a given ID is unlocked
|
|
// returns false if achievement is locked or doesn't exist
|
|
static final function bool IsAchievementUnlocked(ClientPerkRepLink L, name ID)
|
|
{
|
|
local AchStrInfo A;
|
|
|
|
A = GetAchievementByID(L, ID, false);
|
|
if ( A.AchHandler == none )
|
|
return false;
|
|
|
|
return A.AchHandler.IsUnlocked(A.AchIndex);
|
|
}
|
|
|
|
// sets list of all achievement handlers with bDisplayFlag set
|
|
// returns visible achievement count
|
|
static final function int GetAllAchievements(ClientPerkRepLink L, out array<ScrnAchievements> AchHandlers,
|
|
optional int AchievementFlags, optional name Group, optional bool bOnlyLocked )
|
|
{
|
|
local SRCustomProgress S;
|
|
local ScrnAchievements A;
|
|
local int count;
|
|
|
|
AchHandlers.Length = 0;
|
|
|
|
if ( L == none )
|
|
return 0;
|
|
|
|
for( S = L.CustomLink; S != none; S = S.NextLink ) {
|
|
A = ScrnAchievements(S);
|
|
if( A != none ) {
|
|
AchHandlers[AchHandlers.Length] = A;
|
|
A.SetVisibility(AchievementFlags, Group, bOnlyLocked);
|
|
count += A.VisibleAchCount;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static final function ResetAchievements(ClientPerkRepLink L, name Group)
|
|
{
|
|
local SRCustomProgress S;
|
|
local ScrnAchievements A;
|
|
local int i;
|
|
|
|
if ( L == none )
|
|
return;
|
|
|
|
for( S = L.CustomLink; S != none; S = S.NextLink ) {
|
|
A = ScrnAchievements(S);
|
|
if( A != none ) {
|
|
for ( i = 0; i < A.AchDefs.length; ++i ) {
|
|
if ( Group == A.AchDefs[i].Group ) {
|
|
A.AchDefs[i].CurrentProgress = 0;
|
|
A.WriteProgressToData(i, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated function texture GetIcon(int AchIndex)
|
|
{
|
|
if ( AchDefs[AchIndex].CurrentProgress >= AchDefs[AchIndex].MaxProgress )
|
|
return AchDefs[AchIndex].Icon;
|
|
|
|
return LockedIcon;
|
|
}
|
|
|
|
simulated function bool IsUnlocked(int AchIndex)
|
|
{
|
|
return AchDefs[AchIndex].CurrentProgress >= AchDefs[AchIndex].MaxProgress;
|
|
}
|
|
|
|
|
|
static final function RetrieveGroups(ClientPerkRepLink L, out array<name> GroupNames, out array<string> GroupCaptions)
|
|
{
|
|
local SRCustomProgress S;
|
|
local ScrnAchievements A;
|
|
local int i, j;
|
|
local bool bFound;
|
|
|
|
if ( L == none )
|
|
return;
|
|
|
|
if ( GroupNames.length != GroupCaptions.length )
|
|
GroupCaptions.length = GroupNames.length;
|
|
|
|
for( S = L.CustomLink; S != none; S = S.NextLink ) {
|
|
A = ScrnAchievements(S);
|
|
if( A != none ) {
|
|
for ( i = 0; i < A.GroupInfo.length; ++i ) {
|
|
bFound = false;
|
|
for ( j = 0; j < GroupNames.length; ++j ) {
|
|
if ( A.GroupInfo[i].Group == GroupNames[j] ) {
|
|
bFound = true;
|
|
break;
|
|
}
|
|
}
|
|
if ( !bFound ) {
|
|
j = GroupNames.length;
|
|
GroupNames[j] = A.GroupInfo[i].Group;
|
|
GroupCaptions[j] = A.GroupInfo[i].Caption;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
simulated function string LocalGroupCaption(ClientPerkRepLink L, name GroupName)
|
|
{
|
|
local int i;
|
|
|
|
for ( i = 0; i < GroupInfo.length; ++i ) {
|
|
if ( GroupInfo[i].Group == GroupName )
|
|
return GroupInfo[i].Caption;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
static final function string GroupCaption(ClientPerkRepLink L, name GroupName)
|
|
{
|
|
local SRCustomProgress S;
|
|
local ScrnAchievements A;
|
|
local string result;
|
|
|
|
if ( L != none ) {
|
|
for( S = L.CustomLink; S != none; S = S.NextLink ) {
|
|
A = ScrnAchievements(S);
|
|
if( A != none ) {
|
|
result = A.LocalGroupCaption(L, GroupName);
|
|
if ( result != "" )
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
return string(GroupName);
|
|
}
|
|
|
|
simulated function ClientPerkRepLink GetRepLink()
|
|
{
|
|
if ( RepLink == none && Level.NetMode == NM_Client )
|
|
RepLink = Class'ScrnClientPerkRepLink'.Static.FindMe(Level.GetLocalPlayerController());
|
|
return RepLink;
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
LockedIcon=Texture'KillingFloorHUD.Achievements.KF_Achievement_Lock'
|
|
AchClassList(0)=Class'ScrnBalanceSrv.Ach'
|
|
AchClassList(1)=Class'ScrnBalanceSrv.AchMaps'
|
|
GroupInfo(0)=(Caption="ALL")
|
|
bNeedToSetDefaultAchievementData=True
|
|
bFixedDataSize=True
|
|
bReplicateDataToClients=True
|
|
}
|