//============================================================================= // KFUnlockManager //============================================================================= // //============================================================================= // Killing Floor 2 // Copyright (C) 2015 Tripwire Interactive LLC //============================================================================= class KFUnlockManager extends Object abstract native dependson(KFCharacterInfo_Human); enum ESharedContentUnlock { SCU_None, SCU_Zweihander, SCU_ChainBat, SCU_ChiappaRhino, SCU_IonThruster, SCU_MosinNagant, SCU_G18RiotShield, SCU_CompoundBow, SCU_G18C, SCU_Blunderbuss, SCU_Minigun, SCU_MineReconstructor, SCU_FrostFang, SCU_GravityImploder, SCU_FAMAS, SCU_Thermite }; enum EPlatformRestriction { PR_All, PR_XboxOne, PR_PC, PR_PS4, PR_Console, }; struct native SharedContent { var name Name; var string IconPath; var int ID; }; /** contains ids (app or microtrans) to unlock this content */ var array SharedContentList; /** contains owned title information (will be empty on PC) */ var array CrossTitleContent; /** Cache a copy of the online subsystem (save to class default)*/ var OnlineSubsystem MyOnlineSubsystem; /** Unlock cosmetics in dev build */ var const bool bDebugUnlocks; cpptext { /** Checks if a Steam AppID is owned by the logged-in player */ UBOOL GetAppIDAvailable(INT ID); /** Check if a Steam inventory item is owned by the logged-in player */ UBOOL GetInventoryIDAvailable(INT ID); /** TRUE if this PRI is valid for shared content unlock */ UBOOL CanShareContent(APlayerReplicationInfo* PRI); /** return TRUE if the UnlockId bit is set for a given set of flags */ UBOOL CheckSharedUnlock(int UnlockFlags, int UnlockId); /** Steam Id of the Killing Floor 2 group */ static const FUniqueNetId KF2Group; } /**************************************************************************************** * Shared Weapon Unlocks *****************************************************************************************/ /** * Called once per PRI to set up shared weapon content * Network: Local Player */ static native function InitSharedUnlocksFor(KFPlayerReplicationInfo PRI, optional const out array InCrossTitleContent); /** * returns TRUE if any player on this server has this unlock * Network: All */ static native function bool IsSharedContentUnlocked(int UnlockId); /** *Returns TRUE if a player has event weapon skin */ static native function bool GetObjectiveItemGranted(int UnlockId); /** * returns a list of all available (aka connected ) players with a given unlock * Network: All */ static native function GetSharedContentPlayerList(int UnlockId, out array out_PRIArray); /**************************************************************************************** * Platform Restrictions *****************************************************************************************/ /** * returns TRUE if the content is restricted for the current platform * Network: All */ static native function bool IsPlatformRestricted( EPlatformRestriction PlatformRestrictionType ); /**************************************************************************************** * Character Unlocks *****************************************************************************************/ /** Returns whether a unique numeric ID is unlocked. Zero is always unlocked. */ static native private function bool GetIDAvailable(INT ID); static function bool GetWeaponSkinAvailable(INT ID) { return GetIDAvailable(ID); } static event bool GetEmoteAvailable( INT ID ) { return GetIDAvailable( ID ); } static final event bool GetHeadShotEffectAvailable(INT ID) { return GetIDAvailable(ID); } //@HSL_BEGIN - JRO - 5/12/2016 - Need a native accessor method. Can't make GetAvailable an event due to the KFUnlockableAsset being a non-native interface static event bool GetAvailableCharacterArchetype(KFCharacterInfo_Human archetype) { return GetAvailable(archetype); } //@HSL_END /** Returns whether a UObject asset is unlocked and available for use */ static function bool GetAvailable(KFUnlockableAsset Asset) { //@HSL_BEGIN - JRO - 5/12/2016 - Disable non-starter characters while installing if ( !class'GameEngine'.static.IsGameFullyInstalled() && String(Asset.Name) != "CHR_MrFoster_archetype") { return false; } //@HSL_END return GetIDAvailable(Asset.GetAssetId()); } /** Returns whether a character skin variant is unlocked and available for use */ static function bool GetAvailableSkin(const out SkinVariant Asset) { return GetIDAvailable(Asset.UnlockAssetID); } /** Returns whether a character outfit variant is unlocked and available for use */ static function bool GetAvailableOutfit(const out OutfitVariants Asset) { local int i; for( i = 0; i < Asset.SkinVariations.Length; ++i) { // unlock if it has at least one available skin if ( GetIDAvailable(Asset.SkinVariations[i].UnlockAssetId) ) { return TRUE; } } return false; } /** Returns whether a character accessory or head variant is unlocked and available for use */ static function bool GetAvailableAttachment(const out AttachmentVariants Asset) { local int i; for( i = 0; i < Asset.AttachmentItem.SkinVariations.Length; ++i) { // unlock if it has at least one available skin if ( GetIDAvailable(Asset.AttachmentItem.SkinVariations[i].UnlockAssetId) ) { return TRUE; } } return false; } /** Checks to see that all cosmetic items for this character are owned */ static private event bool CheckCustomizationOwnership(KFPlayerReplicationInfo PRI) { local KFCharacterInfo_Human CharArch; local OutfitVariants Outfit; local SkinVariant Skin; local AttachmentVariants Attachment; local int i; CharArch = PRI.CharacterArchetypes[PRI.RepCustomizationInfo.CharacterIndex]; if ( CharArch != None ) { // body Outfit = CharArch.BodyVariants[PRI.RepCustomizationInfo.BodyMeshIndex]; Skin = Outfit.SkinVariations[PRI.RepCustomizationInfo.BodySkinIndex]; /*if( !GetIDAvailable(Outfit.UnlockAssetID) ) { ClearCharacterCustomization(PRI); return FALSE; } else*/ if( !GetIDAvailable(Skin.UnlockAssetID) ) { ClearCharacterCustomization(PRI); return FALSE; } // head Outfit = CharArch.HeadVariants[PRI.RepCustomizationInfo.HeadMeshIndex]; Skin = Outfit.SkinVariations[PRI.RepCustomizationInfo.HeadSkinIndex]; /*if( !GetIDAvailable(Outfit.UnlockAssetID) ) { ClearCharacterCustomization(PRI); return FALSE; } else*/ if( !GetIDAvailable(Skin.UnlockAssetID) ) { ClearCharacterCustomization(PRI); return FALSE; } // accessory for( i=0; i < `MAX_COSMETIC_ATTACHMENTS; i++ ) { if (i == 2 && PRI.WorldInfo.GRI.IsA('KFGameReplicationInfo_WeeklySurvival') && (class'KFGameEngine'.static.GetWeeklyEventIndexMod() == 12)) { continue; } if (PRI.RepCustomizationInfo.AttachmentSkinIndices[i] == INDEX_NONE) { continue; } Attachment = CharArch.CosmeticVariants[PRI.RepCustomizationInfo.AttachmentMeshIndices[i]]; if (Attachment.AttachmentItem == None) { return FALSE; } Skin = Attachment.AttachmentItem.SkinVariations[PRI.RepCustomizationInfo.AttachmentSkinIndices[i]]; if( !GetIDAvailable(Skin.UnlockAssetID) ) { ClearCharacterCustomization(PRI); return FALSE; } } } return TRUE; } static native private function ClearCharacterCustomization(KFPlayerReplicationInfo PRI); /**************************************************************************************** * Debugging *****************************************************************************************/ static native function TestSteamAPI(PlayerReplicationInfo PRI, byte CallFlags); defaultproperties { SharedContentList(SCU_Zweihander)={( Name=KFWeap_Edged_Zweihander, IconPath="WEP_UI_Zweihander_TEX.UI_WeaponSelect_Zweihander", ID=219640)} SharedContentList(SCU_ChainBat)={( Name=KFWeap_Blunt_ChainBat, IconPath="Wep_UI_ChainBat_TEX.UI_WeaponSelect_RRChainbat", ID=300380)} SharedContentList(SCU_ChiappaRhino)={( Name=KFWeap_Pistol_ChiappaRhino, IconPath="Wep_UI_ChiappaRhino_TEX.UI_WeaponSelect_ChiappaRhinos", ID=7704)} SharedContentList(SCU_IonThruster)={( Name=KFWeap_Edged_IonThruster, IconPath="WEP_UI_Ion_Sword_TEX.UI_WeaponSelect_IonSword", ID=7715)} SharedContentList(SCU_MosinNagant)={( Name=KFWeap_Rifle_MosinNagant, IconPath="wep_ui_mosin_tex.UI_WeaponSelect_MosinNagant", ID=7856)} SharedContentList(SCU_G18RiotShield)={( Name=KFWeap_SMG_G18, IconPath="WEP_UI_RiotShield_TEX.UI_WeaponSelect_RiotShield", ID=7850)} SharedContentList(SCU_CompoundBow)={( Name=KFWeap_Bow_CompoundBow, IconPath="WEP_UI_CompoundBow_TEX.UI_WeaponSelect_Compound_Bow", ID=8169)} SharedContentList(SCU_G18C)={( Name=KFWeap_Pistol_G18C, IconPath="wep_ui_g18c_tex.UI_WeaponSelect_G18C", ID=8293)} SharedContentList(SCU_Blunderbuss)={( Name=KFWeap_Pistol_Blunderbuss, IconPath="WEP_UI_Blunderbuss_TEX.UI_WeaponSelect_BlunderBluss", ID=8299)} SharedContentList(SCU_Minigun)={( Name=KFWeap_Minigun, IconPath="WEP_UI_Minigun_TEX.UI_WeaponSelect_Minigun", ID=8478)} SharedContentList(SCU_MineReconstructor)={( Name=KFWeap_Mine_Reconstructor, IconPath="WEP_UI_Mine_Reconstructor_TEX.UI_WeaponSelect_HMTechMineReconstructor", ID=8472)} SharedContentList(SCU_FrostFang)={( Name=KFWeap_Rifle_FrostShotgunAxe, IconPath="WEP_UI_Frost_Shotgun_Axe_TEX.UI_WeaponSelect_FrostGun", ID=8609)} SharedContentList(SCU_GravityImploder)={( Name=KFWeap_GravityImploder, IconPath="WEP_UI_Gravity_Imploder_TEX.UI_WeaponSelect_Gravity_Imploder", ID=8778)} SharedContentList(SCU_FAMAS)={( Name=KFWeap_AssaultRifle_FAMAS, IconPath="WEP_UI_Famas_TEX.UI_WeaponSelect_Famas", ID=8934)} SharedContentList(SCU_Thermite)={( Name=KFWeap_RocketLauncher_ThermiteBore, IconPath="WEP_UI_Thermite_TEX.UI_WeaponSelect_Thermite", ID=8940)} }