Facebook
From HolyMobDEixie, 1 Year ago, written in Plain Text.
Embed
Download Paste or View Raw
Hits: 144
  1. /***********************************************************************/
  2. /** Player skills are divided into trees/groups (sword, alchemy, signs).
  3. /** Each skill tree has its ring levels (reflecting the rings in GUI, where
  4. /** the innermost ring is the highest one). In order to learn a skill of
  5. /** Nth level you must know at least one skill on the (N-1)th level (you
  6. /** can always learn ring 1 skills). The lowest ring level is 1.
  7. /** Some skills have a ring level of -1 -> this means that this is a skill
  8. /** that is learned automatically without spending skill points (e.g. some
  9. /** mastery skill).
  10. /**
  11. /** Skills are grouped in skill definitions. A definition holds information
  12. /** about which skills are available, to which trees and rings they belong
  13. /** etc. Basically if you would like to make several character classes then
  14. /** each of them would use a different skill definition (e.g. mage, warrior).
  15. /** Each definition can make it's own skill placement so for example skill A
  16. /** might be a sword tree ring 2 skill in warriors definition and a combat
  17. /** tree ring 5 skill in rogue definition.
  18. /** This manager loads whole definition and caches it - that's all the skill
  19. /** data you need for this player object.
  20. /**
  21. /** This can be used in multiplayer, addons or for replacers.
  22. /***********************************************************************/
  23. /** Copyright © 2012-2014
  24. /** Author : Tomek Kozera
  25. /**                      Bartosz Bigaj
  26. /***********************************************************************/
  27.  
  28. class W3PlayerAbilityManager extends W3AbilityManager
  29. {
  30.         private   saved var skills : array<SSkill>;                                                                     //all skills in all skill trees
  31.        
  32.         private   saved var resistStatsItems : array<array<SResistanceValue>>;          //holds cached resist stats from items
  33.         private   saved var toxicityOffset : float;                                                                     //mutagens, locked percent of max toxicity
  34.         private                 var pathPointsSpent : array<int>;                                                       //amount of skillpoints spent in each skill path
  35.         private   saved var skillSlots : array<SSkillSlot>;                                                     //skill slots for skills chosen by player      
  36.         protected saved var skillAbilities : array<name>;                                                       //cached list of non-blocked non-GlobalPassive skill abilities
  37.         private                 var totalSkillSlotsCount : int;                                                         //amount of skill slots
  38.         private                 var tempSkills : array<ESkill>;                                                         //list of temporarily added skills
  39.         private   saved var mutagenSlots : array<SMutagenSlot>;                                         //list of mutagen slots
  40.         private                 var temporaryTutorialSkills : array<STutorialTemporarySkill>;   //temp skills added for duration of mutagens tutorial in character panel
  41.         private   saved var ep1SkillsInitialized : bool;
  42.         private   saved var ep2SkillsInitialized : bool;
  43.        
  44.         private const var LINK_BONUS_BLUE, LINK_BONUS_GREEN, LINK_BONUS_RED : name;     //ability added on link color match
  45.         private   saved var isMutationSystemEnabled : bool;                                                     //if system was unlocked through quest
  46.         private   saved var equippedMutation : EPlayerMutationType;                                     //currently active mutation
  47.         private   saved var mutations : array< SMutation >;                                                     //all mutations
  48.         private   saved var mutationUnlockedSlotsIndexes : array< int >;                        //array holding indexes of skillSlots that can be unlocked via Master Mutation
  49.         private   saved var mutationSkillSlotsInitialized : bool;                                       //if mutation skill slots were initialized
  50.         private const var MUTATION_SKILL_GROUP_ID : int;                                                        //group id for skill slots unlocked by Mutation System
  51.        
  52.                 default LINK_BONUS_BLUE = 'SkillLinkBonus_Blue';
  53.                 default LINK_BONUS_GREEN = 'SkillLinkBonus_Green';
  54.                 default LINK_BONUS_RED = 'SkillLinkBonus_Red';
  55.                
  56.                 default ep1SkillsInitialized = false;
  57.                 default ep2SkillsInitialized = false;
  58.        
  59.         public final function Init(ownr : CActor, cStats : CCharacterStats, isFromLoad : bool, diff : EDifficultyMode) : bool
  60.         {
  61.                 var skillDefs : array<name>;
  62.                 var i : int;
  63.                
  64.                 isInitialized = false; 
  65.                
  66.                 if(!ownr)
  67.                 {
  68.                         LogAssert(false, "W3PlayerAbilityManager.Init: owner is NULL!!!!");
  69.                         return false;
  70.                 }
  71.                 else if(!( (CPlayer)ownr ))
  72.                 {
  73.                         LogAssert(false, "W3PlayerAbilityManager.Init: trying to create for non-player object!! Aborting!!");
  74.                         return false;
  75.                 }
  76.                
  77.                 //array init
  78.                 resistStatsItems.Resize(EnumGetMax('EEquipmentSlots')+1);
  79.                 pathPointsSpent.Resize(EnumGetMax('ESkillPath')+1);
  80.                
  81.                 //add default player character ability
  82.                 ownr.AddAbility(theGame.params.GLOBAL_PLAYER_ABILITY);
  83.                
  84.                 if(!super.Init(ownr,cStats, isFromLoad, diff))
  85.                         return false;
  86.                        
  87.                 LogChannel('CHR', "Init W3PlayerAbilityManager "+isFromLoad);          
  88.                
  89.                 // init skills
  90.                 if(!isFromLoad)
  91.                 {
  92.                         InitSkillSlots();
  93.        
  94.                         //set skill definitions
  95.                         skillDefs = charStats.GetAbilitiesWithTag('SkillDefinitionName');              
  96.                         LogAssert(skillDefs.Size()>0, "W3PlayerAbilityManager.Init: actor <<" + owner + ">> has no skills!!");
  97.                        
  98.                         for(i=0; i<skillDefs.Size(); i+=1)
  99.                                 CacheSkills(skillDefs[i], skills);
  100.                                
  101.                         LoadMutagenSlotsDataFromXML();
  102.                        
  103.                         //add initial skills
  104.                         InitSkills();
  105.                        
  106.                         PrecacheModifierSkills();
  107.                 }
  108.                 else
  109.                 {
  110.                         tempSkills.Clear();
  111.                         temporaryTutorialSkills.Clear();
  112.                        
  113.                         if ( !ep1SkillsInitialized && theGame.GetDLCManager().IsEP1Available() )
  114.                         {                              
  115.                                 ep1SkillsInitialized = FixMissingSkills();
  116.                         }
  117.                         if ( !ep2SkillsInitialized && theGame.GetDLCManager().IsEP2Available() )
  118.                         {
  119.                                 ep2SkillsInitialized = FixMissingSkills();
  120.                         }                      
  121.                 }
  122.                
  123.                 isInitialized = true;
  124.                
  125.                 return true;   
  126.         }
  127.        
  128.         private function FixMissingSkills() : bool
  129.         {
  130.                 var i : int;
  131.                 var newSkills : array<SSkill>;
  132.                 var skillDefs : array<name>;
  133.                 var fixedSomething : bool;
  134.                
  135.                 skillDefs = charStats.GetAbilitiesWithTag('SkillDefinitionName');              
  136.                 LogAssert(skillDefs.Size()>0, "W3PlayerAbilityManager.Init: actor <<" + owner + ">> has no skills!!");
  137.                 fixedSomething = false;
  138.                
  139.                 for( i = 0; i < skillDefs.Size(); i+=1 )
  140.                         CacheSkills(skillDefs[i], newSkills);  
  141.  
  142.                 for(i=0; i<newSkills.Size(); i+=1)
  143.                 {
  144.                         //completely new skill
  145.                         if(i >= skills.Size())
  146.                         {
  147.                                 skills.PushBack( newSkills[i] );
  148.                                 fixedSomething = true;
  149.                                 continue;
  150.                         }
  151.        
  152.                         //missing skill in the middle of array
  153.                         if(skills[i].skillType == S_SUndefined && newSkills[i].skillType != S_SUndefined)
  154.                         {
  155.                                 skills[i] = newSkills[i];
  156.                                 fixedSomething = true;
  157.                         }
  158.                 }
  159.                
  160.                 return fixedSomething;
  161.         }
  162.        
  163.         public function OnOwnerRevived()
  164.         {
  165.                 var i : int;
  166.                
  167.                 super.OnOwnerRevived();
  168.                
  169.                 if(owner == GetWitcherPlayer())
  170.                         GetWitcherPlayer().RemoveTemporarySkills();
  171.         }
  172.        
  173.         private final function PrecacheModifierSkills()
  174.         {
  175.                 var i, j : int;
  176.                 var dm : CDefinitionsManagerAccessor;
  177.                 var skill : SSkill;
  178.                 var skillIT : int;
  179.                
  180.                 dm = theGame.GetDefinitionsManager();
  181.                 if( !dm )
  182.                 {
  183.                         return;
  184.                 }
  185.                
  186.                 for( skillIT = 0; skillIT < skills.Size(); skillIT += 1 )
  187.                 {
  188.                         //skill = skills[ skillIT ];
  189.                        
  190.                         for( i = 0; i < skills.Size(); i += 1 )
  191.                         {
  192.                                 if( i != skillIT )
  193.                                 {
  194.                                         for( j = 0; j < skills[ skillIT ].modifierTags.Size(); j += 1)
  195.                                         {
  196.                                                 //if skill has modifier tag
  197.                                                 if( dm.AbilityHasTag( skills[ i ].abilityName, skills[ skillIT ].modifierTags[ j ] ) )
  198.                                                 {
  199.                                                         skills[ skillIT ].precachedModifierSkills.PushBack( i );
  200.                                                 }
  201.                                         }
  202.                                 }
  203.                         }
  204.                 }
  205.         }
  206.        
  207.         // Called after Init() when other managers are initialized (since effect manager is and must be initialized after ability manager)
  208.         public final function PostInit()
  209.         {              
  210.                 var i, playerLevel : int;
  211.        
  212.                 if(CanUseSkill(S_Sword_5))
  213.                         AddPassiveSkillBuff(S_Sword_5);
  214.                        
  215.                 //fill skill slot availability
  216.                 if( (W3PlayerWitcher)owner )
  217.                 {
  218.                         playerLevel = ((W3PlayerWitcher)owner).GetLevel();
  219.                         for(i=0; i<skillSlots.Size(); i+=1)
  220.                         {
  221.                                 skillSlots[i].unlocked = ( playerLevel >= skillSlots[i].unlockedOnLevel);
  222.                         }
  223.                 }
  224.         }
  225.        
  226.         public final function GetPlayerSkills() : array<SSkill> //#B
  227.                 //cache indexes of skill slots unlocked via mutation system
  228.                         isMutationSystemEnabled = true;
  229.         {
  230.                 return skills;
  231.         }
  232.        
  233.         public final function AddTempNonAlchemySkills() : array<SSimpleSkill>
  234.         {
  235.                 var i, cnt, j : int;
  236.                 var ret : array<SSimpleSkill>;
  237.                 var temp : SSimpleSkill;
  238.        
  239.                 tempSkills.Clear();
  240.        
  241.                 for(i=0; i<skills.Size(); i+=1)
  242.                 {
  243.                         if(skills[i].skillPath == ESP_Signs && skills[i].level < skills[i].maxLevel)
  244.                         {
  245.                                 temp.skillType = skills[i].skillType;
  246.                                 temp.level = skills[i].level;
  247.                                 ret.PushBack(temp);
  248.                                
  249.                                 tempSkills.PushBack(skills[i].skillType);
  250.                                
  251.                                 cnt = skills[i].maxLevel - skills[i].level;
  252.                                 for(j=0; j<cnt; j+=1)
  253.                                         AddSkill(skills[i].skillType, true);
  254.                         }
  255.                 }
  256.                
  257.                 return ret;
  258.         }
  259.  
  260.         public final function GetPlayerSkill(type : ESkill) : SSkill //#B
  261.         {
  262.                 return skills[type];
  263.         }
  264.        
  265.         // Adds a passive skill Buff from given skill
  266.         private final function AddPassiveSkillBuff(skill : ESkill)
  267.         {
  268.                 if(skill == S_Sword_5 && GetStat(BCS_Focus) >= 1)
  269.                         owner.AddEffectDefault(EET_BattleTrance, owner, "BattleTranceSkill");
  270.         }
  271.  
  272.         private final function ReloadAcquiredSkills(out acquiredSkills : array<SRestoredSkill>)
  273.         {
  274.                 var i, j : int;
  275.                
  276.                 for(j=acquiredSkills.Size()-1; j>=0; j-=1)             
  277.                 {
  278.                         for(i=0; i<skills.Size(); i+=1)
  279.                         {
  280.                                 if(skills[i].skillType == acquiredSkills[j].skillType)
  281.                                 {
  282.                                         skills[i].level = acquiredSkills[j].level;
  283.                                         skills[i].isNew = acquiredSkills[j].isNew;
  284.                                         skills[i].remainingBlockedTime = acquiredSkills[j].remainingBlockedTime;
  285.                                        
  286.                                         if(!skills[i].isCoreSkill)
  287.                                                 pathPointsSpent[skills[i].skillPath] = pathPointsSpent[skills[i].skillPath] + 1;
  288.                                        
  289.                                         acquiredSkills.Erase(j);
  290.                                        
  291.                                         break;
  292.                                 }
  293.                         }
  294.                 }
  295.         }
  296.        
  297.         ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  298.         //////////////////////////////////    ---===  @EVENTS  ===---    ////////////////////////////////////////////////////////////////////////
  299.         ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  300.                
  301.         // Called when Focus Stat current value has changed
  302.         protected final function OnFocusChanged()
  303.         {
  304.                 var points : float;
  305.                 var buff : W3Effect_Toxicity;
  306.                
  307.                 points = GetStat(BCS_Focus);
  308.                
  309.                 if(points < 1 && owner.HasBuff(EET_BattleTrance))
  310.                 {
  311.                         owner.RemoveBuff(EET_BattleTrance);
  312.                 }
  313.                 else if(points >= 1 && !owner.HasBuff(EET_BattleTrance))
  314.                 {
  315.                         if(CanUseSkill(S_Sword_5))
  316.                                 owner.AddEffectDefault(EET_BattleTrance, owner, "BattleTranceSkill");
  317.                 }
  318.                
  319.                 if ( points >= owner.GetStatMax(BCS_Focus) && owner.HasAbility('Runeword 8 _Stats', true) && !owner.HasBuff(EET_Runeword8) )
  320.                 {
  321.                         owner.AddEffectDefault(EET_Runeword8, owner, "max focus");
  322.                 }
  323.         }
  324.        
  325.         // Called when Vitality Stat current value has changed
  326.         protected final function OnVitalityChanged()
  327.         {
  328.                 var vitPerc : float;
  329.                
  330.                 vitPerc = GetStatPercents(BCS_Vitality);               
  331.                
  332.                 if(vitPerc < theGame.params.LOW_HEALTH_EFFECT_SHOW && !owner.HasBuff(EET_LowHealth))
  333.                         owner.AddEffectDefault(EET_LowHealth, owner, 'vitality_change');
  334.                 else if(vitPerc >= theGame.params.LOW_HEALTH_EFFECT_SHOW && owner.HasBuff(EET_LowHealth))
  335.                         owner.RemoveBuff(EET_LowHealth);
  336.                        
  337.                 if(vitPerc < 1.f)
  338.                         ResetOverhealBonus();
  339.        
  340.                 theTelemetry.SetCommonStatFlt(CS_VITALITY, GetStat(BCS_Vitality));
  341.         }
  342.         // Called when Air Stat current value has changed
  343.         protected final function OnAirChanged()
  344.         {
  345.                 if(GetStat(BCS_Air) > 0)
  346.                 {
  347.                         if ( owner.HasBuff(EET_Drowning) )
  348.                                 owner.RemoveBuff(EET_Drowning);
  349.                                
  350.                         if( owner.HasBuff(EET_Choking) )
  351.                                 owner.RemoveBuff(EET_Choking);
  352.                 }
  353.         }
  354.        
  355.         // Called when Toxicity Stat current value has changed
  356.         protected final function OnToxicityChanged()
  357.         {
  358.                 var tox : float;
  359.        
  360.                 if( !((W3PlayerWitcher)owner) )
  361.                         return;
  362.                        
  363.                 tox = GetStat(BCS_Toxicity);
  364.        
  365.                 //apply toxicity debuff
  366.                 if(tox == 0 && owner.HasBuff(EET_Toxicity))
  367.                         owner.RemoveBuff(EET_Toxicity);
  368.                 else if(tox > 0 && !owner.HasBuff(EET_Toxicity))
  369.                         owner.AddEffectDefault(EET_Toxicity,owner,'toxicity_change');
  370.                        
  371.                 theTelemetry.SetCommonStatFlt(CS_TOXICITY, GetStat(BCS_Toxicity));
  372.         }
  373.        
  374.         ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  375.         //////////////////////////////////    ---===  @MUTAGENS  ===---    /////////////////////////////////////////////////////////////////////
  376.         ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  377.        
  378.         public final function GetPlayerSkillMutagens() : array<SMutagenSlot>
  379.         {
  380.                 return mutagenSlots;
  381.         }
  382.        
  383.         public final function GetSkillGroupIdOfMutagenSlot(eqSlot : EEquipmentSlots) : int
  384.         {
  385.                 var i : int;
  386.                
  387.                 i = GetMutagenSlotIndex(eqSlot);
  388.                 if(i<0)
  389.                         return -1;
  390.                        
  391.                 return mutagenSlots[i].skillGroupID;
  392.         }
  393.        
  394.         //returns true if given mutagen slot is unlocked and can be used
  395.         public final function IsSkillMutagenSlotUnlocked(eqSlot : EEquipmentSlots) : bool
  396.         {
  397.                 var i : int;
  398.                
  399.                 i = GetMutagenSlotIndex(eqSlot);
  400.                 if(i<0)
  401.                         return false;
  402.                 if( equippedMutation != EPMT_None )
  403.                 {
  404.                         return false;
  405.                 }
  406.                 */
  407.                
  408.                 return ((W3PlayerWitcher)owner).GetLevel() >= mutagenSlots[i].unlockedAtLevel;
  409.         }
  410.        
  411.         private final function GetMutagenSlotForGroupId(groupID : int) : EEquipmentSlots
  412.         {
  413.                 var i : int;
  414.                
  415.                 for(i=0; i<mutagenSlots.Size(); i+=1)
  416.                 {
  417.                         if(mutagenSlots[i].skillGroupID == groupID)
  418.                         {
  419.                                 return mutagenSlots[i].equipmentSlot;
  420.                         }
  421.                 }
  422.                
  423.                 return EES_InvalidSlot;
  424.         }
  425.        
  426.         public final function GetSkillGroupsCount() : int
  427.         {
  428.                 return mutagenSlots.Size();
  429.         }
  430.        
  431.         public final function GetSkillGroupIDFromIndex(idx : int) : int
  432.         {
  433.                 if(idx >= 0 && idx <mutagenSlots.Size())
  434.                         return mutagenSlots[idx].skillGroupID;
  435.                        
  436.                 return -1;
  437.         }
  438.        
  439.         //returns index of mutagen slot paired with given equipment slot
  440.         private final function GetMutagenSlotIndex(eqSlot : EEquipmentSlots) : int
  441.         {
  442.                 var i : int;
  443.                
  444.                 for(i=0; i<mutagenSlots.Size(); i+=1)
  445.                         if(mutagenSlots[i].equipmentSlot == eqSlot)
  446.                                 return i;
  447.                                
  448.                 return -1;
  449.         }
  450.        
  451.         //returns index of mutagen slot paired with given item
  452.         private final function GetMutagenSlotIndexFromItemId(item : SItemUniqueId) : int
  453.         {
  454.                 var i : int;
  455.                
  456.                 for(i=0; i<mutagenSlots.Size(); i+=1)
  457.                         if(mutagenSlots[i].item == item)
  458.                                 return i;
  459.                                
  460.                 return -1;
  461.         }      
  462.        
  463.         public final function OnSkillMutagenEquipped(item : SItemUniqueId, slot : EEquipmentSlots, prevColor : ESkillColor)
  464.         {
  465.                 var i : int;
  466.                 var newColor : ESkillColor;
  467.                 var tutState : W3TutorialManagerUIHandlerStateCharDevMutagens;
  468.                
  469.                 i = GetMutagenSlotIndex(slot);
  470.                 if(i<0)
  471.                         return;
  472.                
  473.                 mutagenSlots[i].item = item;
  474.                
  475.                 //update link
  476.                 newColor = GetSkillGroupColor(mutagenSlots[i].skillGroupID);
  477.                 LinkUpdate(newColor, prevColor );
  478.                
  479.                 //"synergy" skill bonus
  480.                 if(CanUseSkill(S_Alchemy_s19))
  481.                 {
  482.                         MutagenSynergyBonusEnable(item, true, GetSkillLevel(S_Alchemy_s19));
  483.                 }
  484.                
  485.                 //tutorial
  486.                 if(ShouldProcessTutorial('TutorialCharDevMutagens'))
  487.                 {
  488.                         tutState = (W3TutorialManagerUIHandlerStateCharDevMutagens)theGame.GetTutorialSystem().uiHandler.GetCurrentState();
  489.                         if(tutState)
  490.                         {
  491.                                 tutState.EquippedMutagen();
  492.                         }
  493.                 }
  494.                
  495.                 theTelemetry.LogWithValueStr(TE_HERO_MUTAGEN_USED, owner.GetInventory().GetItemName( item ) );
  496.                
  497.                 //trial of grasses achievement
  498.                 theGame.GetGamerProfile().CheckTrialOfGrasses();
  499.         }
  500.        
  501.         public final function OnSkillMutagenUnequipped(item : SItemUniqueId, slot : EEquipmentSlots, prevColor : ESkillColor)
  502.         {
  503.                 var i : int;
  504.                 var newColor : ESkillColor;
  505.                
  506.                 i = GetMutagenSlotIndex(slot);
  507.                 if(i<0)
  508.                         return;
  509.                
  510.                 //"synergy" skill bonus
  511.                 if(CanUseSkill(S_Alchemy_s19))
  512.                 {
  513.                         MutagenSynergyBonusEnable(item, false, GetSkillLevel(S_Alchemy_s19));
  514.                 }
  515.                
  516.                 mutagenSlots[i].item = GetInvalidUniqueId();
  517.                
  518.                 newColor = GetSkillGroupColor(mutagenSlots[i].skillGroupID);
  519.                 LinkUpdate(newColor, prevColor);
  520.         }
  521.        
  522.         //called after mutagens were swapped (without equip/unequip handling)
  523.         public final function OnSwappedMutagensPost(a : SItemUniqueId, b : SItemUniqueId)
  524.         {
  525.                 var oldSlotIndexA, oldSlotIndexB : int;
  526.                 var oldColorA, oldColorB, newColorA, newColorB : ESkillColor;
  527.        
  528.                 oldSlotIndexA = GetMutagenSlotIndexFromItemId(a);
  529.                 oldSlotIndexB = GetMutagenSlotIndexFromItemId(b);
  530.                
  531.                 oldColorA = GetSkillGroupColor(mutagenSlots[oldSlotIndexA].skillGroupID);
  532.                 oldColorB = GetSkillGroupColor(mutagenSlots[oldSlotIndexB].skillGroupID);
  533.                
  534.                 mutagenSlots[oldSlotIndexA].item = b;
  535.                 mutagenSlots[oldSlotIndexB].item = a;
  536.                
  537.                 newColorA = GetSkillGroupColor(mutagenSlots[oldSlotIndexA].skillGroupID);
  538.                 newColorB = GetSkillGroupColor(mutagenSlots[oldSlotIndexB].skillGroupID);
  539.                
  540.                 LinkUpdate(newColorA, oldColorA);
  541.                 LinkUpdate(newColorB, oldColorB);
  542.         }
  543.        
  544.         //Called when "synergy" skill is equipped or unequipped. Goes through all mutagens and enables/disables the bonus
  545.         private final function MutagensSyngergyBonusProcess(enable : bool, skillLevel : int)
  546.         {
  547.                 var i : int;
  548.                 var inv : CInventoryComponent;
  549.                
  550.                 inv = owner.GetInventory();
  551.                 for(i=0; i<mutagenSlots.Size(); i+=1)
  552.                 {
  553.                         //has mutagen in this slot
  554.                         if(inv.IsIdValid(mutagenSlots[i].item))
  555.                         {
  556.                                 MutagenSynergyBonusEnable(mutagenSlots[i].item, enable, skillLevel);
  557.                         }
  558.                 }
  559.         }
  560.        
  561.         //turns on/off "syngergy" skill bonus for given mutagen
  562.         private final function MutagenSynergyBonusEnable(mutagenItemId : SItemUniqueId, enable : bool, bonusSkillLevel : int)
  563.         {
  564.                 var i, count : int;
  565.                 var color : ESkillColor;
  566.                 //calculate current bonus
  567.                 count = 1;
  568.                
  569.                 for (i=0; i < mutagenSlots.Size(); i+=1)
  570.                 {
  571.                         if (mutagenSlots[i].item == mutagenItemId)
  572.                         {
  573.                                 //skillGroupID
  574.                                 color = owner.GetInventory().GetSkillMutagenColor( mutagenItemId );
  575.                                 count += GetGroupBonusCount(color, mutagenSlots[i].skillGroupID);
  576.                                 break;
  577.                         }
  578.                 }
  579.        
  580.                 if(enable)
  581.                 {
  582.                         owner.AddAbilityMultiple(GetMutagenBonusAbilityName(mutagenItemId), count * bonusSkillLevel);
  583.                 }
  584.                 else
  585.                 {
  586.                         owner.RemoveAbilityMultiple(GetMutagenBonusAbilityName(mutagenItemId), count * bonusSkillLevel);
  587.                 }
  588.         }
  589.        
  590.         //returns name of ability holding "syngery" skill bonus for this mutagen
  591.         public final function GetMutagenBonusAbilityName(mutagenItemId : SItemUniqueId) : name
  592.         {
  593.                 var i : int;
  594.                 var abs : array<name>;
  595.                 owner.GetInventory().GetItemContainedAbilities(mutagenItemId, abs);
  596.                
  597.                 for(i=0; i<abs.Size(); i+=1)
  598.                 {
  599.                         if(theGame.GetDefinitionsManager().AbilityHasTag(abs[i], 'alchemy_s19'))
  600.                                 return abs[i];
  601.                 }
  602.                 return '';
  603.         }
  604.        
  605.         ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  606.         //////////////////////////////////    ---===  @LINKS BETWEEN SKILLSLOTS ===---    //////////////////////////////////////////////////////
  607.         ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  608.         /*
  609.         private final function GetLinkColor(skillSlotIndex : int, dir : EDirectionZ) : ESkillColor
  610.         {
  611.                 var ind : int;
  612.                 var color : ESkillColor;
  613.        
  614.                 ind = GetSkillIndex(skillSlots[skillSlotIndex].socketedSkill);
  615.                 if(ind < 0)
  616.                         return SC_Undefined;
  617.                        
  618.                 ind = GetSkillIndex(skillSlots[skillSlotIndex].socketedSkill);
  619.                 if(ind < 0)
  620.                         return SC_Undefined;
  621.                         color = skills[ind].linkRight;
  622.                 else if(dir == DZ_Up || dir == DZ_Down)
  623.                         color = skills[ind].linkVertical;
  624.                        
  625.                 if(dir == DZ_Left)
  626.                         color = skills[ind].linkLeft;
  627.                 else if(dir == DZ_Right)
  628.                         color = skills[ind].linkRight;
  629.                 else if(dir == DZ_Up || dir == DZ_Down)
  630.                         color = skills[ind].linkVertical;
  631.                        
  632.                 if(color == SC_Socketable && HasSkillMutagen(skills[ind].skillType))
  633.                         color = theGame.GetDefinitionsManager().GetMutagenIngredientColor(skills[ind].equippedMutagenName);
  634.        
  635.         //given skill slot index and direction returns color of the opposite slot's link
  636.         private final function GetLinkOppositeColor(skillSlotIndex : int, dir : EDirectionZ) : ESkillColor
  637.         {
  638.                 var neighbourSlotID, neighbourSkillIndex : int;
  639.        
  640.                 switch(dir)
  641.                 {
  642.                         case DZ_Down :
  643.                                 neighbourSlotID = skillSlots[skillSlotIndex].neighbourDown;
  644.                                 break;
  645.                         case DZ_Up :
  646.                                 neighbourSlotID = skillSlots[skillSlotIndex].neighbourUp;
  647.                                 break;
  648.                         case DZ_Left :
  649.                                 neighbourSlotID = skillSlots[skillSlotIndex].neighbourLeft;
  650.                                 break;
  651.                         case DZ_Right :
  652.                                 neighbourSlotID = skillSlots[skillSlotIndex].neighbourRight;
  653.                                 break;
  654.                 }
  655.                
  656.                 if(neighbourSlotID < 0)
  657.                         return SC_Undefined;
  658.                        
  659.                 neighbourSkillIndex = GetSkillIndexFromSlotID(neighbourSlotID);
  660.                 if(neighbourSkillIndex < 0)
  661.                         return SC_Undefined;
  662.                
  663.                 switch(dir)
  664.                 {
  665.                         case DZ_Up :
  666.                         case DZ_Down :          return skills[neighbourSkillIndex].linkVertical;
  667.                         case DZ_Left :          return skills[neighbourSkillIndex].linkLeft;
  668.                         case DZ_Right :         return skills[neighbourSkillIndex].linkRight;
  669.                 }
  670.         }*/
  671.                
  672.                 return color;
  673.         }
  674.        
  675.         //given skill slot index and direction returns color of the opposite slot's link
  676.         private final function GetLinkOppositeColor(skillSlotIndex : int, dir : EDirectionZ) : ESkillColor
  677.         {
  678.                 var neighbourSlotID, neighbourSkillIndex : int;
  679.        
  680.                 switch(dir)
  681.                 {
  682.                         case DZ_Down :
  683.                                 neighbourSlotID = skillSlots[skillSlotIndex].neighbourDown;
  684.                                 break;
  685.                         case DZ_Up :
  686.                                 neighbourSlotID = skillSlots[skillSlotIndex].neighbourUp;
  687.                                 break;
  688.                         case DZ_Left :
  689.                                 neighbourSlotID = skillSlots[skillSlotIndex].neighbourLeft;
  690.                                 break;
  691.                         case DZ_Right :
  692.                                 neighbourSlotID = skillSlots[skillSlotIndex].neighbourRight;
  693.                                 break;
  694.                 }
  695.                
  696.                 if(neighbourSlotID < 0)
  697.                         return SC_Undefined;
  698.                        
  699.                 neighbourSkillIndex = GetSkillIndexFromSlotID(neighbourSlotID);
  700.                 if(neighbourSkillIndex < 0)
  701.                         return SC_Undefined;
  702.                
  703.                 switch(dir)
  704.                 {
  705.                         case DZ_Up :
  706.                         case DZ_Down :          return skills[neighbourSkillIndex].linkVertical;
  707.                         case DZ_Left :          return skills[neighbourSkillIndex].linkLeft;
  708.                         case DZ_Right :         return skills[neighbourSkillIndex].linkRight;
  709.                 }
  710.         }*/
  711.        
  712.         public final function GetSkillGroupIdFromSkillSlotId(skillSlotId : int) : int
  713.         {
  714.                 var i : int;
  715.                
  716.                 for(i=0; i<skillSlots.Size(); i+=1)
  717.                 {
  718.                         if(skillSlots[i].id == skillSlotId)
  719.                         {
  720.                                 return skillSlots[i].groupID;
  721.                         }
  722.                 }
  723.                
  724.                 return -1;
  725.         }
  726.        
  727.         public function GetMutagenSlotIDFromGroupID(groupID : int) : int
  728.         {
  729.                 return GetMutagenSlotForGroupId(groupID);
  730.         }
  731.                
  732.         public final function GetGroupBonus(groupID : int) : name
  733.         {
  734.                 var groupColor : ESkillColor;
  735.                 var item : SItemUniqueId;
  736.                
  737.                 groupColor = GetSkillGroupColor(groupID);
  738.                
  739.                 /*if(groupColor != SC_None)
  740.                 {
  741.                         //if mutagen overrides color then there is no basic bonus
  742.                         if(GetWitcherPlayer().GetItemEquippedOnSlot(GetMutagenSlotForGroupId(groupID), item))
  743.                                 return '';
  744.                 }*/
  745.                
  746.                 switch (groupColor)
  747.                 {
  748.                         case SC_None: return '';
  749.                         case SC_Blue: return LINK_BONUS_BLUE;
  750.                         case SC_Green: return LINK_BONUS_GREEN;
  751.                         case SC_Red: return LINK_BONUS_RED;
  752.                 }
  753.         }
  754.        
  755.         public final function GetGroupBonusCount(commonColor : ESkillColor, groupID : int) : int
  756.         {
  757.                 var groupColorCount : int;
  758.                 var item : SItemUniqueId;
  759.                
  760.                 groupColorCount = GetSkillGroupColorCount(commonColor, groupID);
  761.                
  762.                 /*if(groupColor != SC_None)
  763.                 {
  764.                         //if mutagen overrides color then there is no basic bonus
  765.                         if(GetWitcherPlayer().GetItemEquippedOnSlot(GetMutagenSlotForGroupId(groupID), item))
  766.                                 return '';
  767.                 }*/
  768.                         return groupColorCount;
  769.         }      
  770.        
  771.         //returns color of the whole group
  772.         public final function GetSkillGroupColor(groupID : int) : ESkillColor
  773.         {
  774.                 var i : int;
  775.                 var commonColor : ESkillColor;
  776.                 var mutagenSlot : EEquipmentSlots;
  777.                 var skillColors : array<ESkillColor>;
  778.                 var item : SItemUniqueId;
  779.                
  780.                 //get skills' colors
  781.                 for(i=0; i<skillSlots.Size(); i+=1)
  782.                 {
  783.                         if(skillSlots[i].unlocked && skillSlots[i].groupID == groupID)
  784.                         {
  785.                                 skillColors.PushBack(GetSkillColor(skillSlots[i].socketedSkill));
  786.                         }
  787.                 }
  788.                
  789.                 //check for common color
  790.                 commonColor = SC_None;
  791.                 for(i=0; i<skillColors.Size(); i+=1)
  792.                 {
  793.                         if(skillColors[i] != SC_None && skillColors[i] != SC_Yellow)    //color not set (bug?) or perk
  794.                         {
  795.                                 if(commonColor == SC_None)
  796.                                 {
  797.                                         commonColor = skillColors[i];
  798.                                 }
  799.                                 else if(skillColors[i] != commonColor)
  800.                                 {
  801.                                         //bonus broken
  802.                                         commonColor = SC_None;
  803.                                         break;
  804.                                 }
  805.                         }
  806.                 }
  807.                
  808.                 //no bonus
  809.                 if(commonColor == SC_None)
  810.                         return SC_None;
  811.                        
  812.                 //if bonus, check for mutagen override
  813.                 mutagenSlot = GetMutagenSlotForGroupId(groupID);
  814.                 if(IsSkillMutagenSlotUnlocked(mutagenSlot))
  815.                 {
  816.                         if(GetWitcherPlayer().GetItemEquippedOnSlot(mutagenSlot, item))
  817.                                 return owner.GetInventory().GetSkillMutagenColor( item );
  818.                 }
  819.                
  820.                 return commonColor;
  821.         }
  822.        
  823.         //returns color of the whole group - how many common color
  824.         public final function GetSkillGroupColorCount(commonColor : ESkillColor, groupID : int) : ESkillColor
  825.         {
  826.                 var count, i : int;
  827.                 var mutagenSlot : EEquipmentSlots;
  828.                 var skillColors : array<ESkillColor>;
  829.                 var item : SItemUniqueId;
  830.                
  831.                 //get skills' colors
  832.                 for(i=0; i<skillSlots.Size(); i+=1)
  833.                 {
  834.                         if(skillSlots[i].unlocked && skillSlots[i].groupID == groupID && CanUseSkill(skillSlots[i].socketedSkill))
  835.                         {
  836.                                 skillColors.PushBack(GetSkillColor(skillSlots[i].socketedSkill));
  837.                         }
  838.                 }
  839.                
  840.                 //check for common color
  841.                 count = 0;
  842.                 for(i=0; i<skillColors.Size(); i+=1)
  843.                 {
  844.                         if(skillColors[i] == commonColor )      //color not set (bug?) or perk
  845.                         {
  846.                                 count = count + 1;
  847.                         }
  848.                 }
  849.                
  850.                 return count;
  851.         }      
  852.                
  853.         //checks which bonus to update on given link and calls update
  854.         private final function LinkUpdate(newColor : ESkillColor, prevColor : ESkillColor)
  855.         {
  856.                 //no change
  857.                 if(newColor == prevColor)
  858.                         return;
  859.                
  860.                 //remove previous link and add current
  861.                 UpdateLinkBonus(prevColor, false);
  862.                 UpdateLinkBonus(newColor, true);
  863.         }
  864.        
  865.         //updates link bonus
  866.         private final function UpdateLinkBonus(a : ESkillColor, added : bool)
  867.         {      
  868.                 return;
  869.                 if(added)
  870.                 {
  871.                         if(a == SC_Blue)
  872.                                 charStats.AddAbility(LINK_BONUS_BLUE, true);
  873.                         else if(a == SC_Green)
  874.                                 charStats.AddAbility(LINK_BONUS_GREEN, true);
  875.                         else if(a == SC_Red)
  876.                                 charStats.AddAbility(LINK_BONUS_RED, true);
  877.                 }
  878.                 else
  879.                 {
  880.                         if(a == SC_Blue)
  881.                                 charStats.RemoveAbility(LINK_BONUS_BLUE);
  882.                         else if(a == SC_Green)
  883.                                 charStats.RemoveAbility(LINK_BONUS_GREEN);
  884.                         else if(a == SC_Red)
  885.                                 charStats.RemoveAbility(LINK_BONUS_RED);
  886.                 }
  887.         }
  888.        
  889.         public final function GetSkillColor(skill : ESkill) : ESkillColor
  890.         {
  891.                 switch(skills[skill].skillPath)
  892.                 {
  893.                         case ESP_Sword :                return SC_Red;
  894.                         case ESP_Signs :                return SC_Blue;
  895.                         case ESP_Alchemy :              return SC_Green;
  896.                         case ESP_Perks :        return SC_Yellow;
  897.                         default :                               return SC_None;
  898.                 }
  899.         }
  900.        
  901.         /*
  902.         public final function GetSkillLinkColorVertical(skill : ESkill, out color : ESkillColor, out isJoker : bool)
  903.         {
  904.                 var ind : int;
  905.                
  906.                 ind = GetSkillIndex(skill);
  907.                 if(ind < 0)
  908.                 {
  909.                         isJoker = false;
  910.                         color = SC_Undefined;
  911.                 }
  912.                 else
  913.                 {
  914.                         //TODO
  915.                         color = skills[ind].linkVertical;
  916.                         isJoker = false;
  917.                 }
  918.         }
  919.        
  920.         public final function GetSkillLinkColorLeft(skill : ESkill, out color : ESkillColor, out isJoker : bool)
  921.         {
  922.                 var ind : int;
  923.                
  924.                 ind = GetSkillIndex(skill);
  925.                 if(ind < 0)
  926.                 {
  927.                         isJoker = false;
  928.                         color = SC_Undefined;
  929.                 }
  930.                 else
  931.                 {
  932.                         //TODO
  933.                         color = skills[ind].linkLeft;
  934.                         isJoker = false;
  935.                 }
  936.         }
  937.        
  938.         public final function GetSkillLinkColorRight(skill : ESkill, out color : ESkillColor, out isJoker : bool)
  939.         {
  940.                 var ind : int;
  941.                
  942.                 ind = GetSkillIndex(skill);
  943.                 if(ind < 0)
  944.                 {
  945.                         isJoker = false;
  946.                         color = SC_Undefined;
  947.                 }
  948.                 else
  949.                 {
  950.                         //TODO
  951.                         color = skills[ind].linkRight;
  952.                         isJoker = false;
  953.                 }
  954.         }*/
  955.        
  956.         ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  957.         //////////////////////////////////    ---===  @SKILLS  ===---    ///////////////////////////////////////////////////////////////////////
  958.         ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  959.        
  960.         public final function GetSkillLevel(skill : ESkill) : int
  961.         {
  962.                 return skills[skill].level;
  963.         }
  964.        
  965.         public final function GetBoughtSkillLevel(skill : ESkill) : int
  966.         {
  967.                 return skills[skill].level;
  968.         }
  969.        
  970.         public final function GetSkillMaxLevel(skill : ESkill) : int
  971.         {
  972.                 return skills[skill].maxLevel;
  973.         }
  974.        
  975.         public final function GetSkillStaminaUseCost(skill : ESkill, optional isPerSec : bool) : float
  976.         {
  977.                 var reductionCounter : int;
  978.                 var ability, attributeName : name;
  979.                 var ret, costReduction : SAbilityAttributeValue;
  980.        
  981.                 ability = '';
  982.                
  983.                 //search skills
  984.                 if(CanUseSkill(skill))
  985.                         ability = GetSkillAbilityName(skill);
  986.                
  987.                 if(isPerSec)
  988.                         attributeName = theGame.params.STAMINA_COST_PER_SEC_DEFAULT;
  989.                 else
  990.                         attributeName = theGame.params.STAMINA_COST_DEFAULT;
  991.                
  992.                 ret = GetSkillAttributeValue(ability, attributeName, true, true);
  993.                
  994.                 //cost reduction
  995.                 reductionCounter = GetSkillLevel(skill) - 1;
  996.                 if(reductionCounter > 0)
  997.                 {
  998.                         costReduction = GetSkillAttributeValue(ability, 'stamina_cost_reduction_after_1', false, false) * reductionCounter;
  999.                         ret -= costReduction;
  1000.                 }
  1001.                
  1002.                 return CalculateAttributeValue(ret);
  1003.         }
  1004.        
  1005.         public final function GetSkillAttributeValue(abilityName: name, attributeName : name, addBaseCharAttribute : bool, addSkillModsAttribute : bool) : SAbilityAttributeValue
  1006.         {
  1007.                 // OPTIMIZE
  1008.                 var min, max, ret : SAbilityAttributeValue;
  1009.                 var i, j : int;
  1010.                 var dm : CDefinitionsManagerAccessor;
  1011.                 var skill : SSkill;
  1012.                 var skillEnum : ESkill;
  1013.                 var skillLevel : int;
  1014.        
  1015.                 //value from skill ability
  1016.                 ret = super.GetSkillAttributeValue(abilityName, attributeName, addBaseCharAttribute, addSkillModsAttribute);
  1017.                                
  1018.                 //bonus from other skills that modify this value
  1019.                 if(addSkillModsAttribute)
  1020.                 {
  1021.                         //find skill/perk/bookperk structure for given ability
  1022.                        
  1023.                         skillEnum = SkillNameToEnum( abilityName );
  1024.                         if( skillEnum != S_SUndefined )
  1025.                         {
  1026.                                 skill = skills[skillEnum];
  1027.                         }
  1028.                         else
  1029.                         {
  1030.                                 LogAssert(false, "W3PlayerAbilityManager.GetSkillAttributeValue: cannot find skill for ability <<" + abilityName + ">>! Aborting");
  1031.                                 return min;
  1032.                         }
  1033.                        
  1034.                         dm = theGame.GetDefinitionsManager();
  1035.                        
  1036.                         for( j = 0; j < skill.precachedModifierSkills.Size(); j += 1 )
  1037.                         {
  1038.                                 i = skill.precachedModifierSkills[ j ];
  1039.                        
  1040.                                 if( CanUseSkill( skills[i].skillType ) )
  1041.                                 {
  1042.                                         dm.GetAbilityAttributeValue(skills[i].abilityName, attributeName, min, max);
  1043.  
  1044.                                         skillLevel = GetSkillLevel(i);
  1045.                                         ret += GetAttributeRandomizedValue( min * skillLevel, max * skillLevel );
  1046.                                 }
  1047.                         }
  1048.                 }
  1049.                
  1050.                 //value from character stats
  1051.                 if(addBaseCharAttribute)
  1052.                 {
  1053.                         ret += GetAttributeValueInternal(attributeName);
  1054.                 }
  1055.                
  1056.                 return ret;
  1057.         }
  1058.        
  1059.         protected final function GetStaminaActionCostInternal(action : EStaminaActionType, isPerSec : bool, out cost : SAbilityAttributeValue, out delay : SAbilityAttributeValue, optional abilityName : name)
  1060.         {
  1061.                 var attributeName : name;
  1062.                 var skill : ESkill;
  1063.        
  1064.                 super.GetStaminaActionCostInternal(action, isPerSec, cost, delay, abilityName);
  1065.                
  1066.                 if(isPerSec)
  1067.                 {
  1068.                         attributeName = theGame.params.STAMINA_COST_PER_SEC_DEFAULT;
  1069.                 }
  1070.                 else
  1071.                 {
  1072.                         attributeName = theGame.params.STAMINA_COST_DEFAULT;
  1073.                 }
  1074.                
  1075.                 if(action == ESAT_LightAttack && CanUseSkill(S_Sword_1) )
  1076.                         cost += GetSkillAttributeValue(SkillEnumToName(S_Sword_1), attributeName, false, true);
  1077.                 else if(action == ESAT_HeavyAttack && CanUseSkill(S_Sword_2) )
  1078.                         cost += GetSkillAttributeValue(SkillEnumToName(S_Sword_2), attributeName, false, true);
  1079.                 else if ((action == ESAT_Sprint || action == ESAT_Jump) && thePlayer.HasBuff(EET_Mutagen24) && !thePlayer.IsInCombat())
  1080.                 {
  1081.                         cost.valueAdditive = 0;
  1082.                         cost.valueBase = 0;
  1083.                         cost.valueMultiplicative = 0;
  1084.                 }
  1085.                
  1086.                 //level 3 blizzard potion removes stamina cost if you also have battle trance and maxed focus
  1087.                 if(thePlayer.HasBuff(EET_Blizzard) && owner == GetWitcherPlayer() && GetWitcherPlayer().GetPotionBuffLevel(EET_Blizzard) == 3 && thePlayer.HasBuff(EET_BattleTrance) && GetStatPercents(BCS_Focus) == 1)
  1088.                 {
  1089.                         cost.valueAdditive = 0;
  1090.                         cost.valueBase = 0;
  1091.                         cost.valueMultiplicative = 0;
  1092.                 }
  1093.         }
  1094.                
  1095.         /*
  1096.                 TK - apparently not wanted currently - commenting out entire final function    
  1097.                
  1098.         //returns action's stamina cost and delay
  1099.         public final function GetStaminaActionCost(action : EStaminaActionType, out cost : float, out delay : float, optional fixedCost : float, optional fixedDelay : float, optional abilityName : name, optional dt : float, optional costMult : float)
  1100.         {
  1101.                 super.GetStaminaActionCost(action, cost, delay, fixedCost, fixedDelay, abilityName, dt, costMult);
  1102.                
  1103.                 if(dt == 0)
  1104.                 {
  1105.                         //round up to full stamina segments if not a continuous mode
  1106.                         //cost = CeilF(cost / theGame.params.STAMINA_SEGMENT_SIZE) * theGame.params.STAMINA_SEGMENT_SIZE;
  1107.                         //i commented it because it was taking 10 stamina instead of 0.2.../ PF - which is as intended...
  1108.                 }
  1109.         }
  1110.         */
  1111.        
  1112.         /*
  1113.                 Returns list of skill related abilities that the character has:
  1114.                 - only known skills
  1115.                 - only unblocked abilities
  1116.                 - all abilities having one of the tags passed (can be empty)
  1117.         */
  1118.         protected final function GetNonBlockedSkillAbilitiesList( optional tags : array<name> ) : array<name>
  1119.         {
  1120.                 var i, j : int;
  1121.                 var ret : array<name>;
  1122.                 var dm : CDefinitionsManagerAccessor;
  1123.                 var abilityName : name;
  1124.                
  1125.                 if(tags.Size() == 0)
  1126.                         return ret;
  1127.        
  1128.                 dm = theGame.GetDefinitionsManager();
  1129.                 for(i=0; i<skillAbilities.Size(); i+=1)         //skill abilities holds only abilities of equipped skills
  1130.                 {
  1131.                         abilityName = skillAbilities[i];
  1132.                        
  1133.                         for(j=0; j<tags.Size(); j+=1)
  1134.                         {
  1135.                                 if(dm.AbilityHasTag(abilityName, tags[j]))
  1136.                                 {
  1137.                                         ret.PushBack(abilityName);
  1138.                                 }
  1139.                         }
  1140.                 }
  1141.                
  1142.                 return ret;
  1143.         }
  1144.        
  1145.         public final function IsSkillBlocked(skill : ESkill) : bool
  1146.         {
  1147.                 return skills[skill].remainingBlockedTime != 0;
  1148.         }
  1149.        
  1150.         //returns true if lock changed state
  1151.         public final function BlockSkill(skill : ESkill, block : bool, optional cooldown : float) : bool
  1152.         {
  1153.                 var i : int;
  1154.                 var min : float;
  1155.        
  1156.                 if(block)
  1157.                 {
  1158.                         if(skills[skill].remainingBlockedTime == -1 || (cooldown > 0 && cooldown <= skills[skill].remainingBlockedTime) )
  1159.                                 return false;   //already locked for good or locked for longer
  1160.                        
  1161.                         //lock                 
  1162.                         if(cooldown > 0)
  1163.                                 skills[skill].remainingBlockedTime = cooldown;
  1164.                         else
  1165.                                 skills[skill].remainingBlockedTime = -1;
  1166.                                
  1167.                         //find next timer call time
  1168.                         min = 1000000;
  1169.                         for(i=0; i<skills.Size(); i+=1)
  1170.                         {
  1171.                                 if(skills[i].remainingBlockedTime > 0)
  1172.                                 {
  1173.                                         min = MinF(min, skills[i].remainingBlockedTime);
  1174.                                 }
  1175.                         }
  1176.                        
  1177.                         //schedule next update
  1178.                         if(min != 1000000)
  1179.                                 GetWitcherPlayer().AddTimer('CheckBlockedSkills', min, , , , true);
  1180.                        
  1181.                         //also block skill's ability
  1182.                         if(theGame.GetDefinitionsManager().IsAbilityDefined(skills[skill].abilityName) && charStats.HasAbility(skills[skill].abilityName))
  1183.                                 BlockAbility(GetSkillAbilityName(skill), block, cooldown);
  1184.                        
  1185.                         // J_Slash: Active Learner
  1186.                         if(HasLearnedSkill(skill))                      //IsSkillEquipped(skill))
  1187.                                 OnSkillUnequip(skill);
  1188.                        
  1189.                         return true;
  1190.                 }
  1191.                 else
  1192.                 {
  1193.                         if(skills[skill].remainingBlockedTime == 0)
  1194.                                 return false;           //already unlocked
  1195.                
  1196.                         skills[skill].remainingBlockedTime = 0;
  1197.                        
  1198.                         if(theGame.GetDefinitionsManager().IsAbilityDefined(skills[skill].abilityName) && charStats.HasAbility(skills[skill].abilityName))
  1199.                                 BlockAbility(GetSkillAbilityName(skill), false);
  1200.                        
  1201.                         // J_Slash: Active Learner
  1202.                         if(HasLearnedSkill(skill))                      //(IsSkillEquipped(skill))
  1203.                                 OnSkillEquip(skill);
  1204.                                
  1205.                         return true;
  1206.                 }
  1207.         }
  1208.        
  1209.         // Runs through all skills and checks their cooldowns. Unblocks those that have their cooldown finished.
  1210.         // Returns time till next call or -1 if no calls needed
  1211.         public final function CheckBlockedSkills(dt : float) : float
  1212.         {
  1213.                 var i : int;
  1214.                 var cooldown, min : float;
  1215.                
  1216.                 min = 1000000;
  1217.                 for(i=0; i<skills.Size(); i+=1)
  1218.                 {
  1219.                         if(skills[i].remainingBlockedTime > 0)
  1220.                         {
  1221.                                 skills[i].remainingBlockedTime = MaxF(0, skills[i].remainingBlockedTime - dt);
  1222.                                
  1223.                                 if(skills[i].remainingBlockedTime == 0)
  1224.                                 {
  1225.                                         BlockSkill(skills[i].skillType, false);
  1226.                                 }
  1227.                                 else
  1228.                                 {
  1229.                                         min = MinF(min, skills[i].remainingBlockedTime);
  1230.                                 }
  1231.                         }
  1232.                 }
  1233.                
  1234.                 if(min == 1000000)
  1235.                         min = -1;
  1236.                        
  1237.                 return min;
  1238.         }
  1239.        
  1240.         //@Override
  1241.         public final function BlockAbility(abilityName : name, block : bool, optional cooldown : float) : bool
  1242.         {
  1243.                 var i : int;
  1244.        
  1245.                 if( super.BlockAbility(abilityName, block, cooldown))
  1246.                 {
  1247.                         //if ability was blocked then remove it from cached arrays
  1248.                         if(block)
  1249.                         {
  1250.                                 skillAbilities.Remove(abilityName);
  1251.                         }
  1252.                         else
  1253.                         {
  1254.                                 //if added then if it's a skill ability then put it to proper cached array
  1255.                                 for(i=0; i<skills.Size(); i+=1)
  1256.                                 {      
  1257.                                         if(skills[i].abilityName == abilityName)
  1258.                                         {
  1259.                                                 if(!theGame.GetDefinitionsManager().AbilityHasTag(skills[i].abilityName, theGame.params.SKILL_GLOBAL_PASSIVE_TAG))
  1260.                                                         skillAbilities.PushBack(abilityName);
  1261.                                                        
  1262.                                                 break;
  1263.                                         }
  1264.                                 }
  1265.                         }
  1266.                        
  1267.                         return true;                   
  1268.                 }
  1269.                
  1270.                 return false;
  1271.         }
  1272.                
  1273.         //adds all initial skills to the player
  1274.         protected final function InitSkills()
  1275.         {
  1276.                 var atts : array<name>;
  1277.                 var i, size : int;
  1278.                 var skillEnum : ESkill;
  1279.                
  1280.                 charStats.GetAllContainedAbilities(atts);
  1281.                 size = atts.Size();
  1282.                 for( i = 0; i < size; i += 1 )
  1283.                 {
  1284.                         skillEnum = SkillNameToEnum( atts[i] );
  1285.                         if( skillEnum != S_SUndefined )
  1286.                         {
  1287.                                 if( !IsAbilityBlocked( atts[i] ) )
  1288.                                 {
  1289.                                         AddSkillInternal( skillEnum, false, false, true );
  1290.                                 }
  1291.                                 continue;
  1292.                         }
  1293.                 }
  1294.         }
  1295.        
  1296.         protected final function IsCoreSkill(skill : ESkill) : bool
  1297.         {
  1298.                 return skills[skill].isCoreSkill;
  1299.         }
  1300.        
  1301.         // Loads a single skill definition for this player from the XML and caches it (basically loads all skills data)
  1302.         protected final function CacheSkills(skillDefinitionName : name, out cache : array<SSkill>)
  1303.         {
  1304.                 var dm : CDefinitionsManagerAccessor;
  1305.                 var main, sks : SCustomNode;
  1306.                 var i, size, size2, j : int;
  1307.                 var skillType : ESkill;
  1308.                 var bFound : bool;
  1309.                 var tmpName : name;
  1310.                 var skillDefs : array<name>;
  1311.                
  1312.                 dm = theGame.GetDefinitionsManager();
  1313.                 sks = dm.GetCustomDefinition('skills');
  1314.                
  1315.                 //find definition
  1316.                 bFound = false;
  1317.                 size = sks.subNodes.Size();            
  1318.                 cache.Clear();
  1319.                 cache.Resize( S_Perk_MAX );
  1320.                 for( i = 0; i < size; i += 1 )
  1321.                 {
  1322.                         if(dm.GetCustomNodeAttributeValueName(sks.subNodes[i], 'def_name', tmpName))
  1323.                         {
  1324.                                 if(tmpName == skillDefinitionName)
  1325.                                 {
  1326.                                         bFound = true;
  1327.                                         main = sks.subNodes[i];
  1328.                                        
  1329.                                         //do the caching                                       
  1330.                                         size2 = main.subNodes.Size();
  1331.                                         for( j = 0; j < size2; j += 1 )
  1332.                                         {
  1333.                                                 dm.GetCustomNodeAttributeValueName(main.subNodes[j], 'skill_name', tmpName);
  1334.                                                 skillType = SkillNameToEnum(tmpName);
  1335.                                                
  1336.                                                 if( skillType != S_SUndefined )
  1337.                                                 {
  1338.                                                         if( cache[skillType].skillType == skillType )
  1339.                                                         {
  1340.                                                                 LogChannel('Skills', "W3AbilityManager.CacheSkills: actor's <<" + this + ">> skill <<" + skillType + ">> is already defined!!! Skipping!!!");
  1341.                                                                 continue;
  1342.                                                         }
  1343.                                                        
  1344.                                                         CacheSkill( skillType, tmpName, main.subNodes[j], cache[skillType] );
  1345.                                                 }
  1346.                                                 else
  1347.                                                 {
  1348.                                                         LogAssert(false, "W3PlayerAbilityManager.CacheSkills: skill <<" + tmpName + ">> is not defined in PST enum, ignoring skill!");
  1349.                                                 }
  1350.                                         }
  1351.                                 }
  1352.                         }
  1353.                 }
  1354.                
  1355.                 if( !bFound )
  1356.                 {
  1357.                         LogAssert(false, "W3AbilityManager.CacheSkills: cannot find skill definition named <<" + skillDefinitionName + ">> aborting!");
  1358.                 }
  1359.         }
  1360.        
  1361.         private final function CacheSkill( skillType : int, abilityName : name, definitionNode : SCustomNode, out skill : SSkill )
  1362.         {
  1363.                 var dm : CDefinitionsManagerAccessor = theGame.GetDefinitionsManager();
  1364.                 var modifiers, reqSkills : SCustomNode;
  1365.                 var pathType : ESkillPath;
  1366.                 var subpathType : ESkillSubPath;
  1367.                 var tmpName : name;
  1368.                 var tmpInt, k, size : int;
  1369.                 var tmpString : string;
  1370.                 var tmpBool : bool;
  1371.                
  1372.                 skill.wasEquippedOnUIEnter = false;
  1373.                 skill.level = 0;
  1374.                
  1375.                 //skill type
  1376.                 skill.skillType = skillType;
  1377.                 skill.abilityName = abilityName;
  1378.                
  1379.                 //path type
  1380.                 if(dm.GetCustomNodeAttributeValueName(definitionNode, 'pathType_name', tmpName))
  1381.                 {
  1382.                         pathType = SkillPathNameToType(tmpName);
  1383.                         if(pathType != ESP_NotSet)
  1384.                                 skill.skillPath = pathType;
  1385.                         else if(skill.skillType != S_Perk_08)   //perk 08 is a hidden skill now
  1386.                                 LogAssert(false, "W3PlayerAbilityManager.CacheSkill: skill <<" + skill.skillType + ">> has wrong path type set <<" + tmpName + ">>");
  1387.                 }
  1388.                
  1389.                 //subpath type
  1390.                 if(dm.GetCustomNodeAttributeValueName(definitionNode, 'subpathType_name', tmpName))
  1391.                 {
  1392.                         subpathType = SkillSubPathNameToType(tmpName);
  1393.                         if(subpathType != ESSP_NotSet)
  1394.                                 skill.skillSubPath = subpathType;
  1395.                         else if(skill.skillType != S_Perk_08)   //perk 08 is a hidden skill now
  1396.                                 LogAssert(false, "W3PlayerAbilityManager.CacheSkill: skill <<" + skill.skillType + ">> has wrong subpath type set <<" + tmpName + ">>");
  1397.                 }
  1398.                
  1399.                 //required skills list
  1400.                 reqSkills = dm.GetCustomDefinitionSubNode(definitionNode,'required_skills');
  1401.                 if(reqSkills.values.Size() > 0)
  1402.                 {
  1403.                         size = reqSkills.values.Size();
  1404.                         for(k=0; k<size; k+=1)
  1405.                         {
  1406.                                 if(IsNameValid(reqSkills.values[k]))
  1407.                                 {
  1408.                                         skill.requiredSkills.PushBack(SkillNameToEnum(reqSkills.values[k]));
  1409.                                 }
  1410.                         }
  1411.                 }
  1412.                
  1413.                 //required skills 'mode'
  1414.                 if(dm.GetCustomNodeAttributeValueBool(reqSkills, 'isAlternative', tmpBool))
  1415.                         skill.requiredSkillsIsAlternative = tmpBool;
  1416.                
  1417.                 //skill priority used for autoleveling
  1418.                 if(dm.GetCustomNodeAttributeValueInt(definitionNode, 'priority', tmpInt))
  1419.                         skill.priority = tmpInt;
  1420.                
  1421.                 //required points spent in same path
  1422.                 if(dm.GetCustomNodeAttributeValueInt(definitionNode, 'requiredPointsSpent', tmpInt))
  1423.                         skill.requiredPointsSpent = tmpInt;
  1424.                
  1425.                 //localisation
  1426.                 if(dm.GetCustomNodeAttributeValueString(definitionNode, 'localisationName', tmpString))
  1427.                         skill.localisationNameKey = tmpString;
  1428.                 if(dm.GetCustomNodeAttributeValueString(definitionNode, 'localisationDescription', tmpString))
  1429.                         skill.localisationDescriptionKey = tmpString;
  1430.                 if(dm.GetCustomNodeAttributeValueString(definitionNode, 'localisationDescriptionLevel2', tmpString))
  1431.                         skill.localisationDescriptionLevel2Key = tmpString;
  1432.                 if(dm.GetCustomNodeAttributeValueString(definitionNode, 'localisationDescriptionLevel3', tmpString))
  1433.                         skill.localisationDescriptionLevel3Key = tmpString;
  1434.                        
  1435.                 //cost
  1436.                 if(dm.GetCustomNodeAttributeValueInt(definitionNode, 'cost', tmpInt))
  1437.                         skill.cost = tmpInt;
  1438.                        
  1439.                 //maxLevel
  1440.                 if(dm.GetCustomNodeAttributeValueInt(definitionNode, 'maxLevel', tmpInt))
  1441.                         skill.maxLevel = tmpInt;
  1442.                 else
  1443.                         skill.maxLevel = 1;
  1444.                        
  1445.                 //is core skill
  1446.                 if(dm.GetCustomNodeAttributeValueBool(definitionNode, 'isCoreSkill', tmpBool))
  1447.                         skill.isCoreSkill = tmpBool;
  1448.                        
  1449.                 //GUI ID
  1450.                 if(dm.GetCustomNodeAttributeValueInt(definitionNode, 'guiPositionID', tmpInt))
  1451.                         skill.positionID = tmpInt;
  1452.        
  1453.                 //modifier tags
  1454.                 modifiers = dm.GetCustomDefinitionSubNode(definitionNode,'modifier_tags');
  1455.                 if(modifiers.values.Size() > 0)
  1456.                 {
  1457.                         size = modifiers.values.Size();
  1458.                         for(k=0; k<size; k+=1)
  1459.                         {
  1460.                                 if(IsNameValid(modifiers.values[k]))
  1461.                                 {
  1462.                                         skill.modifierTags.PushBack(modifiers.values[k]);
  1463.                                 }
  1464.                         }
  1465.                 }
  1466.                
  1467.                 //icon
  1468.                 if(dm.GetCustomNodeAttributeValueString(definitionNode, 'iconPath', tmpString))
  1469.                         skill.iconPath = tmpString;
  1470.                        
  1471.                 //link colors
  1472.                 /*
  1473.                 if(!skill.isCoreSkill)
  1474.                 {
  1475.                         if(dm.GetCustomNodeAttributeValueString(main.subNodes[i], 'linkVertical', tmpString))
  1476.                                 skill.linkVertical = LinkStringToType(tmpString);
  1477.                        
  1478.                         if(dm.GetCustomNodeAttributeValueString(main.subNodes[i], 'linkLeft', tmpString))
  1479.                                 skill.linkLeft = LinkStringToType(tmpString);
  1480.                                
  1481.                         if(dm.GetCustomNodeAttributeValueString(main.subNodes[i], 'linkRight', tmpString))
  1482.                                 skill.linkRight = LinkStringToType(tmpString);
  1483.                 }*/
  1484.         }
  1485.        
  1486.         private final function LoadMutagenSlotsDataFromXML()
  1487.         {              
  1488.                 var mut : SCustomNode;
  1489.                 var i : int;
  1490.                 var mutagen : SMutagenSlot;
  1491.                 var dm : CDefinitionsManagerAccessor;
  1492.        
  1493.                 //mutagen slots
  1494.                 dm = theGame.GetDefinitionsManager();
  1495.                 mut = dm.GetCustomDefinition('mutagen_slots');         
  1496.                
  1497.                 for(i=0; i<mut.subNodes.Size(); i+=1)
  1498.                 {
  1499.                         dm.GetCustomNodeAttributeValueInt(mut.subNodes[i], 'skillGroup', mutagen.skillGroupID);
  1500.                         dm.GetCustomNodeAttributeValueInt(mut.subNodes[i], 'unlockedAtLevel', mutagen.unlockedAtLevel);
  1501.                        
  1502.                         mutagen.item = GetInvalidUniqueId();
  1503.                         mutagen.equipmentSlot = EES_SkillMutagen1 + i;
  1504.                        
  1505.                         if(mutagen.equipmentSlot > EES_SkillMutagen4)
  1506.                         {
  1507.                                 LogAssert(false, "W3PlayerAbilityManager.LoadMutagenSlotsDataFromXML: too many slots defined in XML!!! Aborting");
  1508.                                 return;
  1509.                         }
  1510.                
  1511.                         mutagenSlots.PushBack(mutagen);
  1512.                 }
  1513.         }
  1514.        
  1515.         //Acquires skill.
  1516.         //The temporary flag informs that the skill was not developed through character development but as a temporary bonus and will be lost soon
  1517.         public final function AddSkill(skill : ESkill, isTemporary : bool)
  1518.         {
  1519.                 var i : int;
  1520.                 var learnedAll, ret : bool;
  1521.                 var tree : ESkillPath;
  1522.                 var uiStateCharDev : W3TutorialManagerUIHandlerStateCharacterDevelopment;
  1523.                 var uiStateSpecialAttacks : W3TutorialManagerUIHandlerStateSpecialAttacks;
  1524.        
  1525.                 ret = AddSkillInternal(skill, true, isTemporary);
  1526.                
  1527.                 if(!ret)
  1528.                         return;
  1529.                        
  1530.                 //dendrology achievement - fully develop one skill tree
  1531.                 tree = GetSkillPathType(skill);
  1532.                 learnedAll = true;
  1533.                 for(i=0; i<skills.Size(); i+=1)
  1534.                 {
  1535.                         if(skills[i].skillPath == tree && skills[i].level == 0)
  1536.                         {
  1537.                                 learnedAll = false;
  1538.                                 break;
  1539.                         }
  1540.                 }
  1541.                
  1542.                 if(learnedAll)
  1543.                         theGame.GetGamerProfile().AddAchievement(EA_Dendrology);
  1544.                 //tutorial
  1545.                 //tutorial
  1546.                 if(ShouldProcessTutorial('TutorialCharDevBuySkill'))
  1547.                 {
  1548.                         uiStateCharDev = (W3TutorialManagerUIHandlerStateCharacterDevelopment)theGame.GetTutorialSystem().uiHandler.GetCurrentState();
  1549.                         if(uiStateCharDev)
  1550.                         {
  1551.                                 uiStateCharDev.OnBoughtSkill(skill);
  1552.                         }
  1553.                 }
  1554.                 if(ShouldProcessTutorial('TutorialSpecialAttacks') || ShouldProcessTutorial('TutorialAlternateSigns'))
  1555.                 {
  1556.                         uiStateSpecialAttacks = (W3TutorialManagerUIHandlerStateSpecialAttacks)theGame.GetTutorialSystem().uiHandler.GetCurrentState();
  1557.                         if(uiStateSpecialAttacks)
  1558.                                 uiStateSpecialAttacks.OnBoughtSkill(skill);
  1559.                 }
  1560.         }
  1561.        
  1562.         protected final function AddSkillInternal(skill : ESkill, spendPoints : bool, isTemporary : bool, optional skipTutorialMessages : bool) : bool
  1563.         {
  1564.                 if(skill == S_SUndefined )
  1565.                 {
  1566.                         LogAssert(false,"W3AbilityManager.AddSkill: trying to add undefined skill, aborting!");
  1567.                         return false;
  1568.                 }      
  1569.                 if(HasLearnedSkill(skill) && skills[skill].level >= skills[skill].maxLevel)
  1570.                 {
  1571.                         LogAssert(false,"W3AbilityManager.AddSkill: trying to add skill already known <<" + SkillEnumToName(skill) + ">>, aborting!");
  1572.                         return false;
  1573.                 }
  1574.                
  1575.                 // J_Slash: Active Learner
  1576.                 if(!isTemporary && skills[skill].level > 0 && skills[skill].remainingBlockedTime <= 0 && !IsAbilityBlocked(skills[skill].abilityName))
  1577.                         OnSkillUnequip(skill);
  1578.                 //add skill
  1579.                 skills[skill].level += 1;
  1580.                 //add path point spent if not core skill
  1581.                 //add path point spent if not core skill
  1582.                 if(!skills[skill].isCoreSkill)
  1583.                         pathPointsSpent[skills[skill].skillPath] = pathPointsSpent[skills[skill].skillPath] + 1;
  1584.                
  1585.                 if(!isTemporary)
  1586.                 {
  1587.                         LogSkills("Skill <<" + skills[skill].abilityName + ">> learned");
  1588.                        
  1589.                         if(spendPoints)
  1590.                                 ((W3PlayerWitcher)owner).levelManager.SpendPoints(ESkillPoint, skills[skill].cost);
  1591.  
  1592.                         // J_Slash: Active Learner
  1593.                         if(!IsAbilityBlocked(skills[skill].abilityName) && skills[skill].remainingBlockedTime <= 0)
  1594.                                 OnSkillEquip(skill);
  1595.                        
  1596.                         //if ( this.IsSkillEquipped(skill) )
  1597.                                 //OnSkillEquippedLevelChange(skill, GetSkillLevel(skill) - 1, GetSkillLevel(skill));
  1598.                         theTelemetry.LogWithValueStr(TE_HERO_SKILL_UP, SkillEnumToName(skill));
  1599.                 }
  1600.                
  1601.                 return true;
  1602.         }      
  1603.                
  1604.         //removes temporary skill granted through another skill's bonus
  1605.         //FIXME - update for skill slots - used by non-tutorial testing fakes and skill_swors_s19
  1606.         public final function RemoveTemporarySkill(skill : SSimpleSkill) : bool
  1607.         {
  1608.                 var ind : int;
  1609.                
  1610.                 LogAssert( skill.skillType >= S_SUndefined, "W3AbilityManager.RemoveTemporarySkill: trying to remove undefined skill" );
  1611.                
  1612.                 if(!skills[skill.skillType].isCoreSkill)
  1613.                         pathPointsSpent[skills[skill.skillType].skillPath] = pathPointsSpent[skills[skill.skillType].skillPath] - (skills[skill.skillType].level - skill.level);
  1614.                        
  1615.                 skills[skill.skillType].level = skill.level;
  1616.                
  1617.                 if(skills[skill.skillType].level < 1)
  1618.                 {
  1619.                         ind = GetSkillSlotID(skill.skillType);
  1620.                         if(ind >= 0)
  1621.                                 UnequipSkill(ind);
  1622.                 }
  1623.                
  1624.                 tempSkills.Remove(skill.skillType);
  1625.                 return true;
  1626.         }
  1627.                
  1628.         public final function HasLearnedSkill(skill : ESkill) : bool
  1629.         {
  1630.                 return skills[skill].level > 0;
  1631.         }
  1632.        
  1633.         private final function GetSkillFromAbilityName(abilityName : name) : ESkill
  1634.         {
  1635.                 var i : int;
  1636.                
  1637.                 for(i=0; i<skills.Size(); i+=1)
  1638.                         if(skills[i].abilityName == abilityName)
  1639.                                 return skills[i].skillType;
  1640.                                
  1641.                 return S_SUndefined;
  1642.         }
  1643.        
  1644.         public final function CanLearnSkill(skill : ESkill) : bool
  1645.         {
  1646.                 var j : int;
  1647.                 var hasSomeRequiredSkill : bool;
  1648.                
  1649.                 //if skill type is valid at all
  1650.                 if(skill == S_SUndefined)
  1651.                         return false;
  1652.                
  1653.                 //if skill is already known
  1654.                 if(skills[skill].level >= skills[skill].maxLevel)
  1655.                         return false;
  1656.                        
  1657.                 //if requirements are not met
  1658.                 // #J removed this logic since it does not apply to current design
  1659.                 /*if(skills[skill].requiredSkills.Size() > 0)
  1660.                 {
  1661.                         if(skills[skill].requiredSkillsIsAlternative)
  1662.                                 hasSomeRequiredSkill = false;
  1663.                         else
  1664.                                 hasSomeRequiredSkill = true;
  1665.                
  1666.                         for(j=0; j<skills[skill].requiredSkills.Size(); j+=1)
  1667.                         {
  1668.                                 if(skills[skill].requiredSkillsIsAlternative)
  1669.                                 {
  1670.                                         if(HasLearnedSkill(skills[skill].requiredSkills[j]))
  1671.                                         {
  1672.                                                 hasSomeRequiredSkill = true;
  1673.                                                 break;
  1674.                                         }
  1675.                                 }
  1676.                                 else if(!HasLearnedSkill(skills[skill].requiredSkills[j]))
  1677.                                 {
  1678.                                         return false;   //conjunction check and some skill is missing
  1679.                                 }
  1680.                         }
  1681.                        
  1682.                         if(!hasSomeRequiredSkill)
  1683.                                 return false;           //alternative check and no skill is known
  1684.                 }*/
  1685.                
  1686.                 //path spent points requirement
  1687.                 if(skills[skill].requiredPointsSpent > 0 && pathPointsSpent[skills[skill].skillPath] < skills[skill].requiredPointsSpent)
  1688.                         return false;
  1689.                        
  1690.                 //cost
  1691.                 if(((W3PlayerWitcher)owner).levelManager.GetPointsFree(ESkillPoint) < skills[skill].cost)
  1692.                         return false;
  1693.                        
  1694.                 //all conditions ok
  1695.                 return true;
  1696.         }
  1697.        
  1698.         public final function HasSpentEnoughPoints(skill : ESkill) : bool // #J
  1699.         {
  1700.                 if (skills[skill].requiredPointsSpent > 0 && pathPointsSpent[skills[skill].skillPath] < skills[skill].requiredPointsSpent)
  1701.                 {
  1702.                         return false;
  1703.                 }
  1704.        
  1705.                 return true;
  1706.         }
  1707.        
  1708.         public final function GetPathPointsSpent(skillPath : ESkillPath) : int
  1709.         {
  1710.                 return pathPointsSpent[skillPath];
  1711.         }
  1712.        
  1713.         public final function PathPointsSpentInSkillPathOfSkill(skill : ESkill) : int // #J
  1714.         {
  1715.                 return pathPointsSpent[skills[skill].skillPath];
  1716.         }
  1717.        
  1718.         // Returns ability name that this skill grants
  1719.         public final function GetSkillAbilityName(skill : ESkill) : name
  1720.         {
  1721.                 return skills[skill].abilityName;
  1722.         }
  1723.  
  1724.         public final function GetSkillLocalisationKeyName(skill : ESkill) : string //#B
  1725.         {
  1726.                 return skills[skill].localisationNameKey;
  1727.         }
  1728.  
  1729.         public final function GetSkillLocalisationKeyDescription(skill : ESkill, optional level : int) : string //#B
  1730.         {
  1731.                 switch (level)
  1732.                 {
  1733.                         case 2:
  1734.                                 return skills[skill].localisationDescriptionLevel2Key;
  1735.                         case 3:
  1736.                                 return skills[skill].localisationDescriptionLevel3Key;
  1737.                         case 4:
  1738.                                 return skills[skill].localisationDescriptionLevel3Key;
  1739.                         case 5:
  1740.                                 return skills[skill].localisationDescriptionLevel3Key;
  1741.                         default:
  1742.                                 return skills[skill].localisationDescriptionKey;
  1743.                 }
  1744.         }
  1745.  
  1746.         public final function GetSkillIconPath(skill : ESkill) : string //#B
  1747.         {
  1748.                 return skills[skill].iconPath;
  1749.         }
  1750.        
  1751.         public final function GetSkillSubPathType(skill : ESkill) : ESkillSubPath
  1752.         {
  1753.                 return skills[skill].skillSubPath;
  1754.         }
  1755.        
  1756.         public final function GetSkillPathType(skill : ESkill) : ESkillPath
  1757.         {
  1758.                 return skills[skill].skillPath;
  1759.         }
  1760.        
  1761.         ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1762.         //////////////////////////////////    ---===  @RESISTS  ===---    //////////////////////////////////////////////////////////////////////
  1763.         ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////       
  1764.        
  1765.         protected function GetItemResistStatIndex( slot : EEquipmentSlots, stat : ECharacterDefenseStats ) : int
  1766.         {
  1767.                 var i, size : int;
  1768.                 size = resistStatsItems[slot].Size();
  1769.                 for ( i = 0; i < size; i+=1 )
  1770.                 {
  1771.                         if ( resistStatsItems[slot][i].type == stat )
  1772.                         {
  1773.                                 return i;
  1774.                         }
  1775.                 }                              
  1776.                 return -1;
  1777.         }
  1778.        
  1779.         //@Override
  1780.         //updates resist stat value - Overrides parent, we need to take armor durability into considreation
  1781.         protected final function RecalcResistStat(stat : ECharacterDefenseStats)
  1782.         {              
  1783.                 var witcher : W3PlayerWitcher;
  1784.                 var item : SItemUniqueId;
  1785.                 var slot, idxItems : int;
  1786.                 var itemResists : array<ECharacterDefenseStats>;
  1787.                 var resistStat : SResistanceValue;
  1788.  
  1789.                 //take all resists
  1790.                 super.RecalcResistStat(stat);
  1791.                
  1792.                 //check if character can have item slots => durability
  1793.                 witcher = (W3PlayerWitcher)owner;
  1794.                 if(!witcher)
  1795.                         return;
  1796.  
  1797.                 GetResistStat( stat, resistStat );
  1798.                
  1799.                 for(slot=0; slot < resistStatsItems.Size(); slot+=1)
  1800.                 {
  1801.                         //get item if it has durability
  1802.                         if( witcher.GetItemEquippedOnSlot(slot, item) && witcher.inv.HasItemDurability(item))
  1803.                         {
  1804.                                 itemResists = witcher.inv.GetItemResistanceTypes(item);
  1805.                                 //check if item boosts resist stat
  1806.                                 if(itemResists.Contains(stat))
  1807.                                 {                      
  1808.                                         //remove resists from items            
  1809.                                         resistStat.points.valueBase -= CalculateAttributeValue(witcher.inv.GetItemAttributeValue(item, ResistStatEnumToName(stat, true)));
  1810.                                         resistStat.percents.valueBase -= CalculateAttributeValue(witcher.inv.GetItemAttributeValue(item, ResistStatEnumToName(stat, false)));
  1811.  
  1812.                                         //calculate item durability modified resistances
  1813.                                         SetItemResistStat(slot, stat);
  1814.  
  1815.                                         //then add resists from items with durability modification
  1816.                                         idxItems = GetItemResistStatIndex( slot, stat );
  1817.                                         if(idxItems >= 0)
  1818.                                         {
  1819.                                                 resistStat.percents.valueBase += CalculateAttributeValue(resistStatsItems[slot][idxItems].percents);
  1820.                                                 resistStat.points.valueBase   += CalculateAttributeValue(resistStatsItems[slot][idxItems].points);
  1821.                                         }
  1822.                                 }
  1823.                         }
  1824.                 }
  1825.                
  1826.                 SetResistStat( stat, resistStat );
  1827.         }
  1828.        
  1829.         // Updates cached durability-modified item resist
  1830.         private final function SetItemResistStat(slot : EEquipmentSlots, stat : ECharacterDefenseStats)
  1831.         {
  1832.                 var item : SItemUniqueId;
  1833.                 var tempResist : SResistanceValue;
  1834.                 var witcher : W3PlayerWitcher;
  1835.                 var i : int;
  1836.                
  1837.                 witcher = (W3PlayerWitcher)owner;
  1838.                 if(!witcher)
  1839.                         return;
  1840.                        
  1841.                 //get cached stat index
  1842.                 i = GetItemResistStatIndex( slot, stat );
  1843.                
  1844.                 //get equipped item
  1845.                 if( witcher.GetItemEquippedOnSlot(slot, item) && witcher.inv.HasItemDurability(item) )
  1846.                 {
  1847.                         //set item resist with durability
  1848.                         if(i >= 0)
  1849.                         {
  1850.                                 //if this resist is already cached then update the value
  1851.                                 witcher.inv.GetItemResistStatWithDurabilityModifiers(item, stat, resistStatsItems[slot][i].points, resistStatsItems[slot][i].percents);
  1852.                         }
  1853.                         else
  1854.                         {
  1855.                                 //if this resist is not cached then add it to cached array
  1856.                                 witcher.inv.GetItemResistStatWithDurabilityModifiers(item, stat, tempResist.points, tempResist.percents);
  1857.                                 tempResist.type = stat;
  1858.                                 resistStatsItems[slot].PushBack(tempResist);
  1859.                         }                      
  1860.                 }
  1861.                 else if(i >= 0)
  1862.                 {
  1863.                         //no item in that slot but something cached - delete the cached item resist
  1864.                         resistStatsItems[slot].Erase(i);
  1865.                 }
  1866.         }
  1867.                
  1868.         // called when item durability has changed to update the cached durability-modified resists from that item
  1869.         public final function RecalcItemResistDurability(slot : EEquipmentSlots, itemId : SItemUniqueId)
  1870.         {
  1871.                 var i : int;
  1872.                 var witcher : W3PlayerWitcher;
  1873.                 var itemResists : array<ECharacterDefenseStats>;
  1874.        
  1875.                 witcher = (W3PlayerWitcher)owner;
  1876.                 if(!witcher)
  1877.                         return;
  1878.                        
  1879.                 itemResists = witcher.inv.GetItemResistanceTypes(itemId);
  1880.                 for(i=0; i<itemResists.Size(); i+=1)
  1881.                 {
  1882.                         if(itemResists[i] != CDS_None)
  1883.                         {
  1884.                                 RecalcResistStatFromItem(itemResists[i], slot);
  1885.                         }
  1886.                 }
  1887.         }
  1888.        
  1889.         // updates resistances of given type from given item. When we call this we know that the item HAS NOT CHANGED
  1890.         private final function RecalcResistStatFromItem(stat : ECharacterDefenseStats, slot : EEquipmentSlots)
  1891.         {
  1892.                 var deltaResist, prevCachedResist : SResistanceValue;
  1893.                 var idx : int;
  1894.                 var resistStat : SResistanceValue;
  1895.                
  1896.                 idx = GetItemResistStatIndex( slot, stat );
  1897.                 prevCachedResist = resistStatsItems[slot][idx];
  1898.                                                
  1899.                 //calculate new item durability modified resistances
  1900.                 SetItemResistStat(slot, stat);
  1901.                
  1902.                 //get diff
  1903.                 deltaResist.points = resistStatsItems[slot][idx].points - prevCachedResist.points;
  1904.                 deltaResist.percents = resistStatsItems[slot][idx].percents - prevCachedResist.percents;
  1905.                
  1906.                 //update global resist
  1907.                 if ( GetResistStat( stat, resistStat ) )
  1908.                 {
  1909.                         resistStat.percents += deltaResist.percents;
  1910.                         resistStat.points += deltaResist.points;
  1911.                         SetResistStat( stat, resistStat );
  1912.                 }
  1913.         }
  1914.                
  1915.         ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1916.         //////////////////////////////////    ---===  @STATS  ===---    ////////////////////////////////////////////////////////////////////////
  1917.         ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1918.        
  1919.         public final function DrainStamina(action : EStaminaActionType, optional fixedCost : float, optional fixedDelay : float, optional abilityName : name, optional dt : float, optional costMult : float) : float
  1920.         {      
  1921.                 var cost : float;
  1922.                 var mutagen : W3Mutagen21_Effect;
  1923.                 var min, max : SAbilityAttributeValue;
  1924.                
  1925.                 if(FactsDoesExist("debug_fact_stamina_boy"))
  1926.                         return 0;
  1927.                        
  1928.                 cost = super.DrainStamina(action, fixedCost, fixedDelay, abilityName, dt, costMult);
  1929.                
  1930.                 if(cost > 0 && dt > 0)
  1931.                 {
  1932.                         //if it's continuous cost then set up a timer that will do the flooring once the continuous cost stops
  1933.                         owner.AddTimer('AbilityManager_FloorStaminaSegment', 0.1, , , , true);
  1934.                 }
  1935.                
  1936.                 // Mutagen 21 - action costing stamina heal geralt, Whirl and Rend handled separately due to their hacks
  1937.                 if (cost > 0 && dt <= 0 && owner == thePlayer && thePlayer.HasBuff(EET_Mutagen21) && abilityName != 'sword_s1' && abilityName != 'sword_s2')
  1938.                 {      
  1939.                         mutagen = (W3Mutagen21_Effect)thePlayer.GetBuff(EET_Mutagen21);
  1940.                         mutagen.Heal();
  1941.                 }
  1942.                
  1943.                 //Force abort sign cast if stamina reached 0. Otherwise if we have regen, stamina might regenerate before it is checked in
  1944.                 //next tick and as a result making even per tick test will always see your stamina >0
  1945.                 if(owner == GetWitcherPlayer() && GetStat(BCS_Stamina, true) <= 0.f)
  1946.                 {
  1947.                         GetWitcherPlayer().GetSignEntity(GetWitcherPlayer().GetCurrentlyCastSign()).OnSignAborted(true);
  1948.                 }
  1949.                
  1950.                 return cost;
  1951.         }
  1952.        
  1953.         public function GainStat( stat : EBaseCharacterStats, amount : float )
  1954.         {
  1955.                 //while under runeword 8 effect, don't add focus
  1956.                 if(stat == BCS_Focus && owner.HasBuff(EET_Runeword8))
  1957.                         return;
  1958.                        
  1959.                 super.GainStat(stat, amount);
  1960.         }
  1961.        
  1962.         //floors current stamina to full segment
  1963.         public final function FloorStaminaSegment()
  1964.         {
  1965.                 //someone forgot to disable stamina segments when they disabled stamina segments... I want to strangle them...
  1966.                 /*
  1967.                 var wastedStamina : float;
  1968.        
  1969.                 wastedStamina = ModF(GetStat(BCS_Stamina, true), theGame.params.STAMINA_SEGMENT_SIZE);
  1970.                 InternalReduceStat(BCS_Stamina, wastedStamina);
  1971.                 */
  1972.         }
  1973.        
  1974.         //needs to make a locked stamina check
  1975.         public final function GetStat(stat : EBaseCharacterStats, optional skipLock : bool) : float    
  1976.         {
  1977.                 var value, lock : float;
  1978.                 var i : int;
  1979.        
  1980.                 value = super.GetStat(stat, skipLock);
  1981.                
  1982.                 if(stat == BCS_Toxicity && !skipLock && toxicityOffset > 0)
  1983.                 {
  1984.                         value += toxicityOffset;
  1985.                 }
  1986.                
  1987.                 return value;
  1988.         }
  1989.        
  1990.         public final function AddToxicityOffset(val : float)
  1991.         {
  1992.                 if(val > 0)
  1993.                         toxicityOffset += val;
  1994.         }
  1995.        
  1996.         public final function SetToxicityOffset( val : float)
  1997.         {
  1998.                 if(val >= 0)
  1999.                         toxicityOffset = val;
  2000.         }
  2001.                
  2002.         public final function RemoveToxicityOffset(val : float)
  2003.         {
  2004.                 if(val > 0)
  2005.                         toxicityOffset -= val;
  2006.                
  2007.                 if (toxicityOffset < 0)
  2008.                         toxicityOffset = 0;
  2009.         }
  2010.        
  2011.         // #Y TODO: Implement calculation
  2012.         public final function GetOffenseStat():int
  2013.         {
  2014.                 var steelDmg, silverDmg : float;
  2015.                 var steelCritChance, steelCritDmg : float;
  2016.                 var silverCritChance, silverCritDmg : float;
  2017.                 var attackPower : SAbilityAttributeValue;
  2018.                 var item : SItemUniqueId;
  2019.                 var value : SAbilityAttributeValue;
  2020.                
  2021.                 // steel and silve ap bonus
  2022.                 if (CanUseSkill(S_Sword_s04))
  2023.                         attackPower += GetSkillAttributeValue(SkillEnumToName(S_Sword_s04), PowerStatEnumToName(CPS_AttackPower), false, true) * GetSkillLevel(S_Sword_s04);
  2024.                 if (CanUseSkill(S_Sword_s21))
  2025.                         attackPower += GetSkillAttributeValue(SkillEnumToName(S_Sword_s21), PowerStatEnumToName(CPS_AttackPower), false, true) * GetSkillLevel(S_Sword_s21);
  2026.                 attackPower = attackPower * 0.5;
  2027.                
  2028.                 // steel and silve crit and crit dmg bonus
  2029.                 if (CanUseSkill(S_Sword_s08))
  2030.                 {
  2031.                         steelCritChance += CalculateAttributeValue(GetSkillAttributeValue(SkillEnumToName(S_Sword_s08), theGame.params.CRITICAL_HIT_CHANCE, false, true)) * GetSkillLevel(S_Sword_s08);
  2032.                         steelCritDmg += CalculateAttributeValue(GetSkillAttributeValue(SkillEnumToName(S_Sword_s08), theGame.params.CRITICAL_HIT_DAMAGE_BONUS, false, true)) * GetSkillLevel(S_Sword_s08);
  2033.                 }
  2034.                 if (CanUseSkill(S_Sword_s17))
  2035.                 {
  2036.                         steelCritChance += CalculateAttributeValue(GetSkillAttributeValue(SkillEnumToName(S_Sword_s17), theGame.params.CRITICAL_HIT_CHANCE, false, true)) * GetSkillLevel(S_Sword_s17);
  2037.                         steelCritDmg += CalculateAttributeValue(GetSkillAttributeValue(SkillEnumToName(S_Sword_s17), theGame.params.CRITICAL_HIT_DAMAGE_BONUS, false, true)) * GetSkillLevel(S_Sword_s17);
  2038.                 }
  2039.                 steelCritChance /= 2;
  2040.                 steelCritDmg /= 2;
  2041.                 silverCritChance = steelCritChance;
  2042.                 silverCritDmg = steelCritDmg;
  2043.                
  2044.                 if (GetWitcherPlayer().GetItemEquippedOnSlot(EES_SteelSword, item))
  2045.                 {
  2046.                         value = thePlayer.GetInventory().GetItemAttributeValue(item, theGame.params.DAMAGE_NAME_SLASHING);
  2047.                         steelDmg += value.valueBase;
  2048.                         steelCritChance += CalculateAttributeValue(thePlayer.GetInventory().GetItemAttributeValue(item, theGame.params.CRITICAL_HIT_CHANCE));
  2049.                         steelCritDmg += CalculateAttributeValue(thePlayer.GetInventory().GetItemAttributeValue(item, theGame.params.CRITICAL_HIT_DAMAGE_BONUS));
  2050.                 }
  2051.                 else
  2052.                 {
  2053.                         steelDmg += 0;
  2054.                         steelCritChance += 0;
  2055.                         steelCritDmg +=0;
  2056.                 }
  2057.                
  2058.                 if (GetWitcherPlayer().GetItemEquippedOnSlot(EES_SilverSword, item))
  2059.                 {
  2060.                         value = thePlayer.GetInventory().GetItemAttributeValue(item, theGame.params.DAMAGE_NAME_SILVER);
  2061.                         silverDmg += value.valueBase;
  2062.                         silverCritChance += CalculateAttributeValue(thePlayer.GetInventory().GetItemAttributeValue(item, theGame.params.CRITICAL_HIT_CHANCE));
  2063.                         silverCritDmg += CalculateAttributeValue(thePlayer.GetInventory().GetItemAttributeValue(item, theGame.params.CRITICAL_HIT_DAMAGE_BONUS));
  2064.                 }
  2065.                 else
  2066.                 {
  2067.                         silverDmg += 0;
  2068.                         silverCritChance += 0;
  2069.                         silverCritDmg +=0;
  2070.                 }
  2071.                
  2072.                 steelCritChance += CalculateAttributeValue(GetWitcherPlayer().GetAttributeValue(theGame.params.CRITICAL_HIT_CHANCE));
  2073.                 silverCritChance += CalculateAttributeValue(GetWitcherPlayer().GetAttributeValue(theGame.params.CRITICAL_HIT_CHANCE));
  2074.                 steelCritDmg += CalculateAttributeValue(GetWitcherPlayer().GetAttributeValue(theGame.params.CRITICAL_HIT_DAMAGE_BONUS));
  2075.                 silverCritDmg += CalculateAttributeValue(GetWitcherPlayer().GetAttributeValue(theGame.params.CRITICAL_HIT_DAMAGE_BONUS));
  2076.                 attackPower += GetWitcherPlayer().GetPowerStatValue(CPS_AttackPower);
  2077.                
  2078.                 steelCritChance *= 100;
  2079.                 silverCritChance *= 100;
  2080.                 steelDmg = steelDmg * (100 - steelCritChance) + steelDmg * (1 + steelCritDmg) * steelCritChance;
  2081.                 steelDmg *= attackPower.valueMultiplicative;
  2082.                 steelDmg /= 100;
  2083.                 silverDmg = silverDmg * (100 - silverCritChance) + silverDmg * (1 + silverCritDmg) * silverCritChance;
  2084.                 silverDmg *= attackPower.valueMultiplicative;
  2085.                 silverDmg /= 100;
  2086.                 return RoundMath((steelDmg + silverDmg)/2);
  2087.         }
  2088.        
  2089.         // #Y TODO: Implement calculation
  2090.         public final function GetDefenseStat():int
  2091.         {
  2092.                 var valArmor : SAbilityAttributeValue;
  2093.                 var valResists : float;
  2094.                 var fVal1, fVal2 : float;
  2095.                
  2096.                 valArmor = thePlayer.GetTotalArmor();
  2097.                 thePlayer.GetResistValue(CDS_SlashingRes, fVal1, fVal2);
  2098.                 valResists += fVal2;
  2099.                 thePlayer.GetResistValue(CDS_PiercingRes, fVal1, fVal2);
  2100.                 valResists += fVal2;
  2101.                 thePlayer.GetResistValue(CDS_BludgeoningRes, fVal1, fVal2);
  2102.                 valResists += fVal2;
  2103.                 thePlayer.GetResistValue(CDS_RendingRes, fVal1, fVal2);
  2104.                 valResists += fVal2;
  2105.                 thePlayer.GetResistValue(CDS_ElementalRes, fVal1, fVal2);
  2106.                 valResists += fVal2;
  2107.                
  2108.                 valResists = valResists / 5;
  2109.                
  2110.                 fVal1 = 100 - valArmor.valueBase;
  2111.                 fVal1 *= valResists;
  2112.                 fVal1 += valArmor.valueBase;
  2113.                
  2114.                 return RoundMath(fVal1);
  2115.         }
  2116.        
  2117.         // #Y TODO: Implement calculation
  2118.         public final function GetSignsStat():float
  2119.         {
  2120.                 var sp : SAbilityAttributeValue;
  2121.                
  2122.                 sp += thePlayer.GetSkillAttributeValue(S_Magic_1, PowerStatEnumToName(CPS_SpellPower), true, true);
  2123.                 sp += thePlayer.GetSkillAttributeValue(S_Magic_2, PowerStatEnumToName(CPS_SpellPower), true, true);
  2124.                 sp += thePlayer.GetSkillAttributeValue(S_Magic_3, PowerStatEnumToName(CPS_SpellPower), true, true);
  2125.                 sp += thePlayer.GetSkillAttributeValue(S_Magic_4, PowerStatEnumToName(CPS_SpellPower), true, true);
  2126.                 sp += thePlayer.GetSkillAttributeValue(S_Magic_5, PowerStatEnumToName(CPS_SpellPower), true, true);
  2127.                 sp.valueMultiplicative /= 5;
  2128.                
  2129.                 return sp.valueMultiplicative;
  2130.         }
  2131.                
  2132.         //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2133.         //////////////////////////////////////////////  @SLOTS  //////////////////////////////////////////////////////////////////////////
  2134.         //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2135.        
  2136.         event OnLevelGained(currentLevel : int)
  2137.         {
  2138.                 var i : int;
  2139.        
  2140.                 for(i=0; i<skillSlots.Size(); i+=1)
  2141.                 {
  2142.                         if(currentLevel >= skillSlots[i].unlockedOnLevel)
  2143.                                 skillSlots[i].unlocked = true;
  2144.                 }
  2145.         }
  2146.        
  2147.         //loads skill slots data from XML
  2148.         private final function InitSkillSlots()
  2149.         {
  2150.                 var slot : SSkillSlot;
  2151.                 var dm : CDefinitionsManagerAccessor;
  2152.                 var main : SCustomNode;
  2153.                 var i, tmpInt : int;
  2154.        
  2155.                 dm = theGame.GetDefinitionsManager();
  2156.                 main = dm.GetCustomDefinition('skill_slots');
  2157.                
  2158.                 for(i=0; i<main.subNodes.Size(); i+=1)
  2159.                 {
  2160.                         if(!dm.GetCustomNodeAttributeValueInt(main.subNodes[i], 'id', slot.id))                
  2161.                         {
  2162.                                 LogAssert(false, "W3PlayerAbilityManager.InitSkillSlots: slot definition is not valid!");
  2163.                                 continue;
  2164.                         }
  2165.                                                
  2166.                         if(!dm.GetCustomNodeAttributeValueInt(main.subNodes[i], 'unlockedOnLevel', slot.unlockedOnLevel))
  2167.                                 slot.unlockedOnLevel = 0;
  2168.                        
  2169.                         if(!dm.GetCustomNodeAttributeValueInt(main.subNodes[i], 'group', slot.groupID))
  2170.                                 slot.groupID = -1;
  2171.                        
  2172.                         if(dm.GetCustomNodeAttributeValueInt(main.subNodes[i], 'neighbourUp', tmpInt))
  2173.                                 slot.neighbourUp = tmpInt;
  2174.                         else
  2175.                                 slot.neighbourUp = -1;
  2176.                                
  2177.                         if(dm.GetCustomNodeAttributeValueInt(main.subNodes[i], 'neighbourDown', tmpInt))
  2178.                                 slot.neighbourDown = tmpInt;
  2179.                         else
  2180.                                 slot.neighbourDown = -1;
  2181.                                
  2182.                         if(dm.GetCustomNodeAttributeValueInt(main.subNodes[i], 'neighbourLeft', tmpInt))
  2183.                                 slot.neighbourLeft = tmpInt;
  2184.                         else
  2185.                                 slot.neighbourLeft = -1;
  2186.                                
  2187.                         if(dm.GetCustomNodeAttributeValueInt(main.subNodes[i], 'neighbourRight', tmpInt))
  2188.                                 slot.neighbourRight = tmpInt;
  2189.                         else
  2190.                                 slot.neighbourRight = -1;
  2191.                                
  2192.                         //slot.unlocked =  cannot set it now since LevelManager does not exist yet. Instead it's set in PostLoad                       
  2193.                         totalSkillSlotsCount = Max(totalSkillSlotsCount, slot.id);
  2194.                         LogChannel('CHR', "Init W3PlayerAbilityManager, totalSkillSlotsCount "+totalSkillSlotsCount);
  2195.                         skillSlots.PushBack(slot);
  2196.                        
  2197.                         slot.id = -1;
  2198.                         slot.unlockedOnLevel = 0;
  2199.                         slot.neighbourUp = -1;
  2200.                         slot.neighbourDown = -1;
  2201.                         slot.neighbourLeft = -1;
  2202.                         slot.neighbourRight = -1;
  2203.                         slot.groupID = -1;
  2204.                 }
  2205.         }
  2206.         //returns skill slot ID for given equipped skill
  2207.         //returns skill slot ID for given equipped skill
  2208.         public final function GetSkillSlotID(skill : ESkill) : int
  2209.         {
  2210.                 var i : int;
  2211.                
  2212.                 if(skill == S_SUndefined)
  2213.                         return -1;
  2214.                
  2215.                 for(i=0; i<skillSlots.Size(); i+=1)
  2216.                 {
  2217.                         if(skillSlots[i].socketedSkill == skill)
  2218.                         {
  2219.                                 if(skillSlots[i].unlocked)
  2220.                                         return skillSlots[i].id;
  2221.                                 else
  2222.                                         return -1;
  2223.                         }
  2224.                 }
  2225.                
  2226.                 return -1;
  2227.         }
  2228.        
  2229.         public final function GetSkillSlotIDFromIndex(skillSlotIndex : int) : int
  2230.         {
  2231.                 if(skillSlotIndex >= 0 && skillSlotIndex < skillSlots.Size())
  2232.                         return skillSlots[skillSlotIndex].id;
  2233.                        
  2234.                 return -1;
  2235.         }
  2236.        
  2237.         /*
  2238.                 Returns index of skillSlot for given slot ID.
  2239.                 Returns -1 if not found.
  2240.                 If checkIfUnlocked flag is set will return -1 if given slot in locked.
  2241.         */
  2242.         public final function GetSkillSlotIndex(slotID : int, checkIfUnlocked : bool) : int
  2243.         {
  2244.                 var i : int;
  2245.                
  2246.                 for(i=0; i<skillSlots.Size(); i+=1)
  2247.                 {
  2248.                         if(skillSlots[i].id == slotID)
  2249.                         {
  2250.                                 if(!checkIfUnlocked)
  2251.                                         return i;
  2252.                                
  2253.                                 if(skillSlots[i].unlocked)
  2254.                                         return i;
  2255.                                 else
  2256.                                         return -1;
  2257.                         }
  2258.                 }
  2259.                
  2260.                 return -1;
  2261.         }
  2262.                
  2263.         public final function GetSkillSlotIndexFromSkill(skill : ESkill) : int
  2264.         {
  2265.                 var i : int;
  2266.        
  2267.                 for(i=0; i<skillSlots.Size(); i+=1)
  2268.                         if(skillSlots[i].socketedSkill == skill)
  2269.                                 return i;
  2270.                                
  2271.                 return -1;
  2272.         }
  2273.        
  2274.         //returns true if succeeded
  2275.         public final function EquipSkill(skill : ESkill, slotID : int) : bool
  2276.         {
  2277.                 var idx : int;
  2278.                 var prevColor : ESkillColor;
  2279.                 var uiState : W3TutorialManagerUIHandlerStateCharacterDevelopment;              // J_Slash: Active Learner
  2280.                
  2281.                 if(!HasLearnedSkill(skill) || IsCoreSkill(skill))
  2282.                         return false;
  2283.                        
  2284.                 idx = GetSkillSlotIndex(slotID, true);         
  2285.                
  2286.                 if(idx < 0)
  2287.                         return false;
  2288.                
  2289.                 prevColor = GetSkillGroupColor(skillSlots[idx].groupID);
  2290.                
  2291.                 UnequipSkill(slotID);
  2292.        
  2293.                 skillSlots[idx].socketedSkill = skill;
  2294.                
  2295.                 LinkUpdate(GetSkillGroupColor(skillSlots[idx].groupID), prevColor);
  2296.                
  2297.                 // J_Slash: Active Learner
  2298.                 if ( CanUseSkill(S_Alchemy_s19) )
  2299.                 {
  2300.                         MutagensSyngergyBonusProcess(false, GetSkillLevel(S_Alchemy_s19));
  2301.                         MutagensSyngergyBonusProcess(true, GetSkillLevel(S_Alchemy_s19));
  2302.                 }
  2303.  
  2304.                 if(ShouldProcessTutorial('TutorialCharDevEquipSkill'))
  2305.                 {
  2306.                         uiState = (W3TutorialManagerUIHandlerStateCharacterDevelopment)theGame.GetTutorialSystem().uiHandler.GetCurrentState();
  2307.                         if(uiState)
  2308.                                 uiState.EquippedSkill();
  2309.                 }
  2310.                
  2311.                 //OnSkillEquip(skill);
  2312.                
  2313.                 return true;
  2314.         }
  2315.         //returns true if succeeded
  2316.         public final function UnequipSkill(slotID : int) : bool
  2317.         {
  2318.                 var idx : int;
  2319.                 var prevColor : ESkillColor;
  2320.                 var skill : ESkill;
  2321.        
  2322.                 idx = GetSkillSlotIndex(slotID, true);
  2323.                 if(idx < 0)
  2324.                         return false;
  2325.                
  2326.                 // Update synegry bonus
  2327.                 if ( CanUseSkill(S_Alchemy_s19) )
  2328.                         MutagensSyngergyBonusProcess(false, GetSkillLevel(S_Alchemy_s19));
  2329.                        
  2330.                 //update links
  2331.                 prevColor = GetSkillGroupColor(skillSlots[idx].groupID);
  2332.                 skill = skillSlots[idx].socketedSkill;
  2333.                 skillSlots[idx].socketedSkill = S_SUndefined;
  2334.                 LinkUpdate(GetSkillGroupColor(skillSlots[idx].groupID), prevColor);
  2335.                
  2336.                 // J_Slash: Active Learner
  2337.                 if ( CanUseSkill(S_Alchemy_s19) )
  2338.                 {
  2339.                         MutagensSyngergyBonusProcess(false, GetSkillLevel(S_Alchemy_s19));
  2340.                         MutagensSyngergyBonusProcess(true, GetSkillLevel(S_Alchemy_s19));
  2341.                 }
  2342.                
  2343.                 //OnSkillUnequip(skill);
  2344.                
  2345.                 return true;
  2346.         }
  2347.        
  2348.         //called when char panel closes and a skill equip was done
  2349.         private final function OnSkillEquip(skill : ESkill)
  2350.         {
  2351.                 var skillName : name;
  2352.                 var names, abs : array<name>;
  2353.                 var buff : W3Effect_Toxicity;
  2354.                 var witcher : W3PlayerWitcher;
  2355.                 var i, skillLevel : int;
  2356.                 var isPassive, isNight : bool;
  2357.                 var m_alchemyManager : W3AlchemyManager;
  2358.                 var recipe : SAlchemyRecipe;
  2359.                 //var uiState : W3TutorialManagerUIHandlerStateCharacterDevelopment;            // J_Slash: Active Learner
  2360.                 var battleTrance : W3Effect_BattleTrance;
  2361.                 var mutagens : array<CBaseGameplayEffect>;
  2362.                 var trophy : SItemUniqueId;
  2363.                 var horseManager : W3HorseManager;
  2364.                 var weapon, armor : W3RepairObjectEnhancement;
  2365.                 var foodBuff : W3Effect_WellFed;
  2366.                 var commonMenu : CR4CommonMenu;
  2367.                 var guiMan : CR4GuiManager;
  2368.                 //always active
  2369.                 //always active
  2370.                 if(IsCoreSkill(skill))
  2371.                         return;
  2372.                
  2373.                 witcher = GetWitcherPlayer();
  2374.        
  2375.                 //add passive Buff that this skill grants
  2376.                 AddPassiveSkillBuff(skill);
  2377.                
  2378.                 //cache skill ability
  2379.                 isPassive = theGame.GetDefinitionsManager().AbilityHasTag(skills[skill].abilityName, theGame.params.SKILL_GLOBAL_PASSIVE_TAG);
  2380.                
  2381.                 for( i = 0; i < GetSkillLevel(skill); i += 1 )
  2382.                 {
  2383.                         if(isPassive)
  2384.                                 owner.AddAbility(skills[skill].abilityName, true);
  2385.                         else
  2386.                                 skillAbilities.PushBack(skills[skill].abilityName);
  2387.                 }
  2388.                
  2389.                 //M.J. - adrenaline hack for sword skills
  2390.                 if(GetSkillPathType(skill) == ESP_Sword)
  2391.                 {
  2392.                         owner.AddAbilityMultiple('sword_adrenalinegain', GetSkillLevel(skill) );
  2393.                 }
  2394.                
  2395.                 //some stamina hack for magic skills
  2396.                 if(GetSkillPathType(skill) == ESP_Signs)
  2397.                 {
  2398.                         owner.AddAbilityMultiple('magic_staminaregen', GetSkillLevel(skill) );
  2399.                 }
  2400.                
  2401.                 //M.J. - potion duration hack for alchemy skills
  2402.                 if(GetSkillPathType(skill) == ESP_Alchemy)
  2403.                 {
  2404.                         owner.AddAbilityMultiple('alchemy_potionduration', GetSkillLevel(skill) );
  2405.                 }
  2406.                
  2407.                 // Update Synergy bonus
  2408.                 if ( CanUseSkill(S_Alchemy_s19) )
  2409.                 {
  2410.                         MutagensSyngergyBonusProcess(false, GetSkillLevel(S_Alchemy_s19));
  2411.                         MutagensSyngergyBonusProcess(true, GetSkillLevel(S_Alchemy_s19));
  2412.                 }
  2413.                 else if(skill == S_Alchemy_s20)
  2414.                 {
  2415.                         if ( GetWitcherPlayer().GetStatPercents(BCS_Toxicity) >= GetWitcherPlayer().GetToxicityDamageThreshold() )
  2416.                                 owner.AddEffectDefault(EET_IgnorePain, owner, 'IgnorePain');
  2417.                 }
  2418.                 //custom instant skill checks
  2419.                 if(skill == S_Alchemy_s18)
  2420.                 {
  2421.                         m_alchemyManager = new W3AlchemyManager in this;
  2422.                         m_alchemyManager.Init();
  2423.                         names = witcher.GetAlchemyRecipes();
  2424.                         skillName = SkillEnumToName(S_Alchemy_s18);
  2425.                         for(i=0; i<names.Size(); i+=1)
  2426.                         {
  2427.                                 m_alchemyManager.GetRecipe(names[i], recipe);
  2428.                                 if ((recipe.cookedItemType != EACIT_Bolt) && (recipe.cookedItemType != EACIT_Undefined) && (recipe.level <= GetSkillLevel(S_Alchemy_s18)))
  2429.                                         charStats.AddAbility(skillName, true);
  2430.                         }
  2431.                 }
  2432.                 else if(skill == S_Alchemy_s15 && owner.HasBuff(EET_Toxicity))
  2433.                 {
  2434.                         buff = (W3Effect_Toxicity)owner.GetBuff(EET_Toxicity);
  2435.                         buff.RecalcEffectValue();
  2436.                 }
  2437.                 else if(skill == S_Alchemy_s13)
  2438.                 {
  2439.                         mutagens = GetWitcherPlayer().GetDrunkMutagens();
  2440.                         if(mutagens.Size() > 0)
  2441.                                 charStats.AddAbilityMultiple(GetSkillAbilityName(skill), GetSkillLevel(skill));
  2442.                 }              
  2443.                 else if(skill == S_Magic_s11)           //yrden damaging
  2444.                 {
  2445.                         ((W3YrdenEntity) (witcher.GetSignEntity(ST_Yrden))).SkillEquipped(skill);
  2446.                 }
  2447.                 else if(skill == S_Magic_s07)           //battle trance spell power bonus
  2448.                 {
  2449.                         if(owner.HasBuff(EET_BattleTrance))
  2450.                                 owner.AddAbility( GetSkillAbilityName(S_Magic_s07) );
  2451.                 }
  2452.                 else if(skill == S_Perk_08)
  2453.                 {
  2454.                         //change level 3 items abilities from 2 to 3
  2455.                         thePlayer.ChangeAlchemyItemsAbilities(true);
  2456.                 }
  2457.                 else if(skill == S_Alchemy_s19)
  2458.                 {
  2459.                 //      MutagensSyngergyBonusProcess(true, GetSkillLevel(skill));
  2460.                 }
  2461.                 else if(skill == S_Perk_01)
  2462.                 {
  2463.                         isNight = theGame.envMgr.IsNight();
  2464.                         SetPerk01Abilities(!isNight, isNight);
  2465.                 }
  2466.                 else if(skill == S_Perk_05)
  2467.                 {
  2468.                         SetPerkArmorBonus(S_Perk_05, true);
  2469.                 }
  2470.                 else if(skill == S_Perk_06)
  2471.                 {
  2472.                         SetPerkArmorBonus(S_Perk_06, true);
  2473.                 }
  2474.                 else if(skill == S_Perk_07)
  2475.                 {
  2476.                         SetPerkArmorBonus(S_Perk_07, true);
  2477.                 }
  2478.                 else if(skill == S_Perk_11)
  2479.                 {
  2480.                         battleTrance = (W3Effect_BattleTrance)owner.GetBuff(EET_BattleTrance);
  2481.                         if(battleTrance)
  2482.                                 battleTrance.OnPerk11Equipped();
  2483.                 }
  2484.                 else if(skill == S_Perk_19 && witcher.HasBuff(EET_BattleTrance))
  2485.                 {
  2486.                         skillLevel = FloorF(witcher.GetStat(BCS_Focus));
  2487.                         witcher.RemoveAbilityMultiple(thePlayer.GetSkillAbilityName(S_Sword_5), skillLevel);
  2488.                         witcher.AddAbilityMultiple(thePlayer.GetSkillAbilityName(S_Perk_19), skillLevel);
  2489.                 }              
  2490.                 else if(skill == S_Perk_22)
  2491.                 {
  2492.                         GetWitcherPlayer().UpdateEncumbrance();
  2493.                         guiMan = theGame.GetGuiManager();
  2494.                         if(guiMan)
  2495.                         {
  2496.                                 commonMenu = theGame.GetGuiManager().GetCommonMenu();
  2497.                                 if(commonMenu)
  2498.                                 {
  2499.                                         commonMenu.UpdateItemsCounter();
  2500.                                 }
  2501.                         }
  2502.                 }
  2503.                
  2504.                 if(GetSkillPathType(skill) == ESP_Alchemy)
  2505.                         witcher.RecalcPotionsDurations();
  2506.                
  2507.                 //tutorial
  2508.                 // J_Slash: Active Learner
  2509.                 /*if(ShouldProcessTutorial('TutorialCharDevEquipSkill'))
  2510.                 {
  2511.                         uiState = (W3TutorialManagerUIHandlerStateCharacterDevelopment)theGame.GetTutorialSystem().uiHandler.GetCurrentState();
  2512.                         if(uiState)
  2513.                                 uiState.EquippedSkill();
  2514.                 }*/
  2515.                
  2516.                 //trial of grasses achievement
  2517.                 theGame.GetGamerProfile().CheckTrialOfGrasses();
  2518.         }
  2519.        
  2520.         private final function OnSkillUnequip(skill : ESkill)
  2521.         {
  2522.                 var i, skillLevel : int;
  2523.                 var isPassive : bool;
  2524.                 var petard : W3Petard;
  2525.                 var ents : array<CGameplayEntity>;
  2526.                 var mutagens : array<CBaseGameplayEffect>;
  2527.                 var tox : W3Effect_Toxicity;
  2528.                 var names, abs : array<name>;
  2529.                 var skillName : name;
  2530.                 var battleTrance : W3Effect_BattleTrance;
  2531.                 var trophy : SItemUniqueId;
  2532.                 var horseManager : W3HorseManager;
  2533.                 var witcher : W3PlayerWitcher;
  2534.                 var weapon, armor : W3RepairObjectEnhancement;
  2535.                 var foodBuff : W3Effect_WellFed;
  2536.                 var commonMenu : CR4CommonMenu;
  2537.                 var guiMan : CR4GuiManager;
  2538.                 //always active
  2539.                 //always active
  2540.                 if(IsCoreSkill(skill))
  2541.                         return;
  2542.                        
  2543.                 //cache skill ability
  2544.                 isPassive = theGame.GetDefinitionsManager().AbilityHasTag(skills[skill].abilityName, theGame.params.SKILL_GLOBAL_PASSIVE_TAG);
  2545.                
  2546.                 skillLevel = skills[skill].level;
  2547.                        
  2548.                 for( i = 0; i < skillLevel; i += 1 )
  2549.                 {
  2550.                         if(isPassive)
  2551.                                 owner.RemoveAbility(skills[skill].abilityName);
  2552.                         else
  2553.                                 skillAbilities.Remove(skills[skill].abilityName);
  2554.                 }
  2555.                
  2556.                 //M.J. - adrenaline hack for sword skills
  2557.                 if(GetSkillPathType(skill) == ESP_Sword)
  2558.                 {
  2559.                         owner.RemoveAbilityMultiple('sword_adrenalinegain', skillLevel );
  2560.                 }
  2561.                
  2562.                 //some hack for magic skills
  2563.                 if(GetSkillPathType(skill) == ESP_Signs)
  2564.                 {
  2565.                         owner.RemoveAbilityMultiple('magic_staminaregen', GetSkillLevel(skill) );
  2566.                 }
  2567.                
  2568.                 //M.J. - potion duration hack for alchemy skills
  2569.                 if(GetSkillPathType(skill) == ESP_Alchemy)
  2570.                 {
  2571.                         owner.RemoveAbilityMultiple('alchemy_potionduration', GetSkillLevel(skill) );
  2572.                 }
  2573.                
  2574.                 //custom skill stuff           
  2575.                 if(skill == S_Magic_s11)                //yrden damaging
  2576.                 {
  2577.                         ((W3YrdenEntity) (GetWitcherPlayer().GetSignEntity(ST_Yrden))).SkillUnequipped(skill);
  2578.                 }
  2579.                 else if(skill == S_Magic_s07)   //battle trance spell power bonus
  2580.                 {
  2581.                         owner.RemoveAbility( GetSkillAbilityName(S_Magic_s07) );
  2582.                 }
  2583.                 else if(skill == S_Alchemy_s04) //bonus random potion effect when drinking potion
  2584.                 {
  2585.                         owner.RemoveEffect(GetWitcherPlayer().GetSkillBonusPotionEffect());
  2586.                 }
  2587.                 /*
  2588.                 else if(skill == PROXIMITY_BOMBS)       //proximity -> explode existing proximities, disable proxy on flying ones
  2589.                 {
  2590.                         FindGameplayEntitiesInSphere(ents, owner.GetWorldPosition(), theGame.params.MAX_THROW_RANGE + 0.5, 1000);
  2591.                         for(i=ents.Size()-1; i>=0; i-=1)
  2592.                         {
  2593.                                 petard = (W3Petard)ents[i];
  2594.                                 if(petard)
  2595.                                 {
  2596.                                         if(petard.IsStuck())
  2597.                                                 petard.ProcessEffect();
  2598.                                         else if(petard.IsProximity())
  2599.                                                 petard.DisableProximity();
  2600.                                 }
  2601.                         }
  2602.                 }*/
  2603.                 {
  2604.                         FindGameplayEntitiesInSphere(ents, owner.GetWorldPosition(), theGame.params.MAX_THROW_RANGE + 0.5, 1000);
  2605.                         for(i=ents.Size()-1; i>=0; i-=1)
  2606.                         {
  2607.                                 petard = (W3Petard)ents[i];
  2608.                                 if(petard)
  2609.                                 {
  2610.                                         if(petard.IsStuck())
  2611.                                                 petard.ProcessEffect();
  2612.                                         else if(petard.IsProximity())
  2613.                                                 petard.DisableProximity();
  2614.                                 }
  2615.                         }
  2616.                 }*/
  2617.                 else if(skill == S_Alchemy_s13)
  2618.                 {
  2619.                         mutagens = GetWitcherPlayer().GetDrunkMutagens();
  2620.                        
  2621.                         if(mutagens.Size() > 0)
  2622.                                 charStats.RemoveAbilityMultiple(GetSkillAbilityName(S_Alchemy_s13), GetSkillLevel(skill));
  2623.                 }
  2624.                 else if(skill == S_Alchemy_s20)
  2625.                 {
  2626.                         owner.RemoveBuff(EET_IgnorePain);
  2627.                 }
  2628.                 else if(skill == S_Alchemy_s15 && owner.HasBuff(EET_Toxicity))
  2629.                 {
  2630.                         tox = (W3Effect_Toxicity)owner.GetBuff(EET_Toxicity);
  2631.                         tox.RecalcEffectValue();
  2632.                 }
  2633.                 else if(skill == S_Alchemy_s18)                 //toxicity pool upgrade per known recipe
  2634.                 {
  2635.                         names = GetWitcherPlayer().GetAlchemyRecipes();
  2636.                         skillName = SkillEnumToName(S_Alchemy_s18);
  2637.                         for(i=0; i<names.Size(); i+=1)
  2638.                                 charStats.RemoveAbility(skillName);
  2639.                 }
  2640.                 else if(skill == S_Sword_s13)                   //slowmo for aiming
  2641.                 {
  2642.                         theGame.RemoveTimeScale( theGame.GetTimescaleSource(ETS_ThrowingAim) );
  2643.                 }
  2644.                 else if(skill == S_Alchemy_s08)
  2645.                 {
  2646.                         skillLevel = GetSkillLevel(S_Alchemy_s08);
  2647.                         for (i=0; i < skillLevel; i+=1)
  2648.                                 thePlayer.SkillReduceBombAmmoBonus();
  2649.                 }
  2650.                 else if(skill == S_Perk_08)
  2651.                 {
  2652.                         //change level 3 items abilities from 3 to 2
  2653.                         thePlayer.ChangeAlchemyItemsAbilities(false);
  2654.                 }
  2655.                 else if(skill == S_Alchemy_s19)
  2656.                 {
  2657.                         MutagensSyngergyBonusProcess(false, GetSkillLevel(skill));
  2658.                 }
  2659.                 else if(skill == S_Perk_01)
  2660.                 {
  2661.                         SetPerk01Abilities(false, false);
  2662.                 }
  2663.                 else if(skill == S_Perk_05)
  2664.                 {
  2665.                         SetPerkArmorBonus(S_Perk_05, false);
  2666.                 }
  2667.                 else if(skill == S_Perk_06)
  2668.                 {
  2669.                         SetPerkArmorBonus(S_Perk_06, false);
  2670.                 }
  2671.                 else if(skill == S_Perk_07)
  2672.                 {
  2673.                         SetPerkArmorBonus(S_Perk_07, false);
  2674.                 }
  2675.                 else if(skill == S_Perk_11)
  2676.                 {
  2677.                         battleTrance = (W3Effect_BattleTrance)owner.GetBuff(EET_BattleTrance);
  2678.                         if(battleTrance)
  2679.                                 battleTrance.OnPerk11Unequipped();
  2680.                 }              
  2681.                 else if(skill == S_Perk_19 && owner.HasBuff(EET_BattleTrance))
  2682.                 {
  2683.                         skillLevel = FloorF(owner.GetStat(BCS_Focus));
  2684.                         owner.RemoveAbilityMultiple(thePlayer.GetSkillAbilityName(S_Perk_19), skillLevel);
  2685.                         owner.AddAbilityMultiple(thePlayer.GetSkillAbilityName(S_Sword_5), skillLevel);
  2686.                 }
  2687.                 else if(skill == S_Perk_22)
  2688.                 {
  2689.                         GetWitcherPlayer().UpdateEncumbrance();
  2690.                         guiMan = theGame.GetGuiManager();
  2691.                         if(guiMan)
  2692.                         {
  2693.                                 commonMenu = theGame.GetGuiManager().GetCommonMenu();
  2694.                                 if(commonMenu)
  2695.                                 {
  2696.                                         commonMenu.UpdateItemsCounter();
  2697.                                 }
  2698.                         }
  2699.                 }
  2700.                
  2701.                 if(GetSkillPathType(skill) == ESP_Alchemy)
  2702.                         GetWitcherPlayer().RecalcPotionsDurations();
  2703.                
  2704.                 // Update synegry bonus
  2705.                 if ( CanUseSkill(S_Alchemy_s19) )
  2706.                 {
  2707.                         MutagensSyngergyBonusProcess(false, GetSkillLevel(S_Alchemy_s19));
  2708.                         MutagensSyngergyBonusProcess(true, GetSkillLevel(S_Alchemy_s19));
  2709.                 }
  2710.         }
  2711.        
  2712.         //goes through all armor pieces and updates perk bonus
  2713.         private final function SetPerkArmorBonus(skill : ESkill, enable : bool)
  2714.         {
  2715.                 var item : SItemUniqueId;
  2716.                 var armors : array<SItemUniqueId>;
  2717.                 var light, medium, heavy, i, cnt : int;
  2718.                 var armorType : EArmorType;
  2719.                 var witcher : W3PlayerWitcher;
  2720.                
  2721.                 if(skill != S_Perk_05 && skill != S_Perk_06 && skill != S_Perk_07)
  2722.                         return;
  2723.        
  2724.                 witcher = GetWitcherPlayer();
  2725.                 armors.Resize(4);
  2726.                
  2727.                 if(witcher.inv.GetItemEquippedOnSlot(EES_Armor, item))
  2728.                         armors[0] = item;
  2729.                        
  2730.                 if(witcher.inv.GetItemEquippedOnSlot(EES_Boots, item))
  2731.                         armors[1] = item;
  2732.                        
  2733.                 if(witcher.inv.GetItemEquippedOnSlot(EES_Pants, item))
  2734.                         armors[2] = item;
  2735.                        
  2736.                 if(witcher.inv.GetItemEquippedOnSlot(EES_Gloves, item))
  2737.                         armors[3] = item;
  2738.                
  2739.                 light = 0;
  2740.                 medium = 0;
  2741.                 heavy = 0;
  2742.                 for(i=0; i<armors.Size(); i+=1)
  2743.                 {
  2744.                         armorType = witcher.inv.GetArmorType(armors[i]);
  2745.                         if(armorType == EAT_Light)
  2746.                                 light += 1;
  2747.                         else if(armorType == EAT_Medium)
  2748.                                 medium += 1;
  2749.                         else if(armorType == EAT_Heavy)
  2750.                                 heavy += 1;
  2751.                 }
  2752.                
  2753.                 if(skill == S_Perk_05)
  2754.                         cnt = light;
  2755.                 else if(skill == S_Perk_06)
  2756.                         cnt = medium;
  2757.                 else
  2758.                         cnt = heavy;
  2759.                        
  2760.                 if(cnt > 0)
  2761.                         UpdatePerkArmorBonus(skill, enable, cnt);
  2762.         }
  2763.        
  2764.         // adds/removes perk armor bonus
  2765.         public final function UpdatePerkArmorBonus(skill : ESkill, enable : bool, optional count : int)
  2766.         {
  2767.                 var abilityName : name;
  2768.                
  2769.                 abilityName = GetSkillAbilityName(skill);
  2770.                
  2771.                 if(count == 0)
  2772.                         count = 1;
  2773.                
  2774.                 if(enable)
  2775.                         charStats.AddAbilityMultiple(abilityName, count);
  2776.                 else
  2777.                         charStats.RemoveAbilityMultiple(abilityName, count);
  2778.         }      
  2779.        
  2780.         //sets day/night perk01's abilities
  2781.         public final function SetPerk01Abilities(enableDay : bool, enableNight : bool)
  2782.         {
  2783.                 var abilityName : name;
  2784.                 var i : int;
  2785.                 var dm : CDefinitionsManagerAccessor;
  2786.                 var abs : array<name>;
  2787.                 var enable : bool;
  2788.                
  2789.                 abilityName = GetSkillAbilityName(S_Perk_01);
  2790.                 dm = theGame.GetDefinitionsManager();
  2791.                 dm.GetContainedAbilities(abilityName, abs);
  2792.                
  2793.                 for(i=0; i<abs.Size(); i+=1)
  2794.                 {
  2795.                         if(dm.AbilityHasTag(abs[i], 'Day'))
  2796.                                 enable = enableDay;
  2797.                         else
  2798.                                 enable = enableNight;
  2799.                                
  2800.                         if(enable)
  2801.                                 charStats.AddAbility(abs[i], false);
  2802.                         else
  2803.                                 charStats.RemoveAbility(abs[i]);
  2804.                 }
  2805.         }
  2806.        
  2807.         //called when equipped skill changes level
  2808.         private final function OnSkillEquippedLevelChange(skill : ESkill, prevLevel : int, currLevel : int)
  2809.         {
  2810.                 var cnt, i : int;
  2811.                 var names : array<name>;
  2812.                 var skillAbilityName : name;
  2813.                 var mutagens : array<CBaseGameplayEffect>;
  2814.                 var recipe : SAlchemyRecipe;
  2815.                 var m_alchemyManager : W3AlchemyManager;
  2816.                 var ignorePain : W3Effect_IgnorePain;
  2817.                
  2818.                 //never changes levels
  2819.                 if(IsCoreSkill(skill))
  2820.                         return;
  2821.                
  2822.                 if(skill == S_Alchemy_s08)
  2823.                 {
  2824.                         if(currLevel < prevLevel)
  2825.                                 thePlayer.SkillReduceBombAmmoBonus();
  2826.                 }
  2827.                 else if(skill == S_Alchemy_s18)
  2828.                 {
  2829.                         m_alchemyManager = new W3AlchemyManager in this;
  2830.                         m_alchemyManager.Init();
  2831.                         names = GetWitcherPlayer().GetAlchemyRecipes();
  2832.                         skillAbilityName = SkillEnumToName(S_Alchemy_s18);
  2833.                         cnt = 0;
  2834.                        
  2835.                         //count how much we should have
  2836.                         for(i=0; i<names.Size(); i+=1)
  2837.                         {
  2838.                                 m_alchemyManager.GetRecipe(names[i], recipe);
  2839.                                 if ((recipe.cookedItemType != EACIT_Bolt) && (recipe.cookedItemType != EACIT_Undefined) && (recipe.level <= GetSkillLevel(S_Alchemy_s18)))
  2840.                                         cnt += 1;
  2841.                         }
  2842.                        
  2843.                         //add/remove abilities
  2844.                         cnt -= owner.GetAbilityCount(skillAbilityName);
  2845.                         if(cnt > 0)
  2846.                                 charStats.AddAbilityMultiple(skillAbilityName, cnt);
  2847.                         else if(cnt < 0)
  2848.                                 charStats.RemoveAbilityMultiple(skillAbilityName, -cnt);
  2849.                 }
  2850.                 else if(skill == S_Alchemy_s13)
  2851.                 {
  2852.                         mutagens = GetWitcherPlayer().GetDrunkMutagens();
  2853.                         skillAbilityName = GetSkillAbilityName(S_Alchemy_s13);                 
  2854.                        
  2855.                         if(mutagens.Size() > 0)
  2856.                                 charStats.AddAbilityMultiple(skillAbilityName, GetSkillLevel(skill));
  2857.                         else
  2858.                                 charStats.RemoveAbilityMultiple(skillAbilityName, GetSkillLevel(skill));                                               
  2859.                 }
  2860.                 else if(skill == S_Alchemy_s19)
  2861.                 {
  2862.                         //remove old, add new
  2863.                         if ( CanUseSkill(S_Alchemy_s19) )
  2864.                         {
  2865.                                 MutagensSyngergyBonusProcess(false, prevLevel);
  2866.                                 MutagensSyngergyBonusProcess(true, currLevel);
  2867.                         }
  2868.                 }
  2869.                 else if(skill == S_Alchemy_s20)
  2870.                 {
  2871.                         if(owner.HasBuff(EET_IgnorePain))
  2872.                         {
  2873.                                 ignorePain = (W3Effect_IgnorePain)owner.GetBuff(EET_IgnorePain);
  2874.                                 ignorePain.OnSkillLevelChanged(currLevel - prevLevel);
  2875.                         }
  2876.                 }
  2877.                 else if(skill == S_Perk_08)
  2878.                 {
  2879.                         if(currLevel == 3)
  2880.                                 thePlayer.ChangeAlchemyItemsAbilities(true);
  2881.                         else if(currLevel == 2 && prevLevel == 3)
  2882.                                 thePlayer.ChangeAlchemyItemsAbilities(false);
  2883.                 }
  2884.                
  2885.                 //some hack for sword skills
  2886.                 if(GetSkillPathType(skill) == ESP_Sword)
  2887.                 {
  2888.                         if ( (currLevel - prevLevel) > 0)
  2889.                                 owner.AddAbilityMultiple('sword_adrenalinegain', currLevel - prevLevel );
  2890.                         else if ( (currLevel - prevLevel) < 0)
  2891.                                 owner.RemoveAbilityMultiple('sword_adrenalinegain', currLevel - prevLevel );
  2892.                 }
  2893.                
  2894.                 //some hack for magic skills
  2895.                 if(GetSkillPathType(skill) == ESP_Signs)
  2896.                 {
  2897.                         if ( (currLevel - prevLevel) > 0)
  2898.                                 owner.AddAbilityMultiple('magic_staminaregen', currLevel - prevLevel );
  2899.                         else if ( (currLevel - prevLevel) < 0)
  2900.                                 owner.RemoveAbilityMultiple('magic_staminaregen', currLevel - prevLevel );
  2901.                 }
  2902.                
  2903.                 //some hack for alchemy skills
  2904.                 if(GetSkillPathType(skill) == ESP_Alchemy)
  2905.                 {
  2906.                         if ( (currLevel - prevLevel) > 0)
  2907.                                 owner.AddAbilityMultiple('alchemy_potionduration', currLevel - prevLevel );
  2908.                         else if ( (currLevel - prevLevel) < 0)
  2909.                                 owner.RemoveAbilityMultiple('alchemy_potionduration', currLevel - prevLevel );
  2910.                 }
  2911.                
  2912.                 if(GetSkillPathType(skill) == ESP_Alchemy)
  2913.                         GetWitcherPlayer().RecalcPotionsDurations();
  2914.         }
  2915.        
  2916.         public final function CanUseSkill(skill : ESkill) : bool
  2917.         {
  2918.                 var ind : int;
  2919.                
  2920.                 // J_Slash: Active Learner
  2921.                 //if(!IsSkillEquipped(skill))
  2922.                         //return false;
  2923.                        
  2924.                 if(skills[skill].level < 1)
  2925.                         return false;
  2926.                        
  2927.                 if(skills[skill].remainingBlockedTime != 0)
  2928.                         return false;
  2929.                        
  2930.                 if(theGame.GetDefinitionsManager().IsAbilityDefined(skills[skill].abilityName) && charStats.HasAbility(skills[skill].abilityName))
  2931.                         return !IsAbilityBlocked(skills[skill].abilityName);
  2932.                
  2933.                 return true;
  2934.         }
  2935.                
  2936.         public final function IsSkillEquipped(skill : ESkill) : bool
  2937.         {
  2938.                 var i, idx : int;
  2939.                                
  2940.                 //core skills always equipped
  2941.                 if(IsCoreSkill(skill))
  2942.                         return true;
  2943.                
  2944.                 //skill slots
  2945.                 for(i=0; i<skillSlots.Size(); i+=1)
  2946.                         if(skillSlots[i].socketedSkill == skill)
  2947.                                 return true;
  2948.                
  2949.                 //temp skills always equipped
  2950.                 if(tempSkills.Contains(skill))
  2951.                         return true;
  2952.                
  2953.                 return false;
  2954.         }
  2955.        
  2956.         //sets skill on given skill slot. Returns false if skillslot is locked
  2957.         public final function GetSkillOnSlot(slotID : int, out skill : ESkill) : bool
  2958.         {
  2959.                 var idx : int;
  2960.                        
  2961.                 if(slotID > 0 && slotID <= totalSkillSlotsCount)
  2962.                 {
  2963.                         idx = GetSkillSlotIndex(slotID, true);
  2964.                         if(idx >= 0)
  2965.                         {
  2966.                                 skill = skillSlots[idx].socketedSkill;
  2967.                                 return true;
  2968.                         }
  2969.                 }
  2970.                
  2971.                 skill = S_SUndefined;
  2972.                 return false;
  2973.         }
  2974.        
  2975.         public final function GetSkillSlots() : array<SSkillSlot>
  2976.         {
  2977.                 return skillSlots;
  2978.         }
  2979.        
  2980.         public final function GetSkillSlotsCount() : int
  2981.         {
  2982.                 return totalSkillSlotsCount;
  2983.         }
  2984.        
  2985.         public final function IsSkillSlotUnlocked(slotIndex : int) : bool
  2986.         {
  2987.                 if(slotIndex >= 0 && slotIndex < skillSlots.Size())
  2988.                         return skillSlots[slotIndex].unlocked;
  2989.                        
  2990.                 return false;
  2991.         }
  2992.        
  2993.         //resets character dev
  2994.         public final function ResetCharacterDev()
  2995.         {
  2996.                 var i : int;
  2997.                 var skillType : ESkill;
  2998.                
  2999.                 for(i=0; i<skills.Size(); i+=1)
  3000.                 {                      
  3001.                         skillType = skills[i].skillType;
  3002.                        
  3003.                         if(IsCoreSkill(skillType))
  3004.                                 continue;
  3005.                        
  3006.                         if(IsSkillEquipped(skillType))
  3007.                                 UnequipSkill(GetSkillSlotID(skillType));
  3008.                                
  3009.                         // J_Slash: Active Learner
  3010.                         if(HasLearnedSkill(skillType))
  3011.                                 OnSkillUnequip(skillType);
  3012.                        
  3013.                         skills[i].level = 0;
  3014.                 }
  3015.                
  3016.                 for(i=0; i<pathPointsSpent.Size(); i+=1)
  3017.                 {
  3018.                         pathPointsSpent[i] = 0;
  3019.                 }
  3020.                
  3021.                 owner.RemoveAbilityAll('sword_adrenalinegain');
  3022.                 owner.RemoveAbilityAll('magic_staminaregen');
  3023.                 owner.RemoveAbilityAll('alchemy_potionduration');
  3024.         }
  3025.        
  3026.         //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3027.         //////////////////////////////////////////////  @TUTORIAL  ///////////////////////////////////////////////////////////////////////
  3028.         //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3029.        
  3030.         /*
  3031.                 Stores (as returned value) skills equipped in three skill slots connected with first mutagen slot.
  3032.                 Then it unequips those skills,
  3033.         */
  3034.         public final function TutorialMutagensUnequipPlayerSkills() : array<STutorialSavedSkill>
  3035.         {
  3036.                 var savedSkills : array<STutorialSavedSkill>;           //array of skills that were initially in the slots, needed to restore them after tutorial is done              
  3037.                 var i : int;
  3038.                 var slots : array<int>;                                                         //slot IDs of slots that are in the group connected to equipped mutagen slot
  3039.                 var equippedSkill : ESkill;
  3040.                 var savedSkill : STutorialSavedSkill;
  3041.                
  3042.                 //get three skill slots' indexes of group in which we have the mutagen
  3043.                 slots = TutorialGetConnectedSkillsSlotsIDs();
  3044.                
  3045.                 //save equipped skills and clear slots
  3046.                 for(i=0; i<slots.Size(); i+=1)
  3047.                 {                      
  3048.                         if(GetSkillOnSlot(slots[i], equippedSkill) && equippedSkill != S_SUndefined)
  3049.                         {
  3050.                                 //save skill
  3051.                                 savedSkill.skillType = equippedSkill;
  3052.                                 savedSkill.skillSlotID = slots[i];
  3053.                                 savedSkills.PushBack(savedSkill);
  3054.                                
  3055.                                 //clear slot
  3056.                                 UnequipSkill(slots[i]);
  3057.                         }
  3058.                 }
  3059.                
  3060.                 //update UI
  3061.                 TutorialUpdateUI();
  3062.                
  3063.                 return savedSkills;
  3064.         }
  3065.        
  3066.         /*
  3067.                 'learns' temporary skill if not known and equips it in first skill slot.
  3068.                 Such temporary skill has the same color as the color of equipped mutagen.
  3069.         */
  3070.         public final function TutorialMutagensEquipOneGoodSkill()
  3071.         {              
  3072.                 var slots : array<int>;
  3073.                                
  3074.                 //get three skill slots' indexes of group in which we have the mutagen
  3075.                 slots = TutorialGetConnectedSkillsSlotsIDs();
  3076.                
  3077.                 //select temp skill
  3078.                 TutorialSelectAndAddTempSkill();
  3079.                                
  3080.                 //equip temp skill to first slot
  3081.                 EquipSkill(temporaryTutorialSkills[0].skillType, ArrayFindMinInt(slots));
  3082.                
  3083.                 //update UI
  3084.                 TutorialUpdateUI();
  3085.         }
  3086.        
  3087.         //Adds one improper temp skill to second slot
  3088.         public final function TutorialMutagensEquipOneGoodOneBadSkill()
  3089.         {
  3090.                 var slots : array<int>;
  3091.                
  3092.                 //add temp skill
  3093.                 TutorialSelectAndAddTempSkill(true);
  3094.                
  3095.                 //equip to second slot
  3096.                 slots = TutorialGetConnectedSkillsSlotsIDs();
  3097.                 ArraySortInts(slots);
  3098.                 EquipSkill(temporaryTutorialSkills[1].skillType, slots[1] );
  3099.                
  3100.                 //refresh UI
  3101.                 TutorialUpdateUI();            
  3102.         }
  3103.        
  3104.         //Removes improper skill from second slot and adds two proper ones to slot 2 & 3
  3105.         public final function TutorialMutagensEquipThreeGoodSkills()
  3106.         {
  3107.                 var slots : array<int>;        
  3108.                
  3109.                 //we no longer need the temp wrong color skill - remove it
  3110.                 TutorialGetRidOfTempSkill(1);
  3111.                                
  3112.                 //add two proper color temp skills
  3113.                 TutorialSelectAndAddTempSkill(false, 1);
  3114.                 TutorialSelectAndAddTempSkill(false, 2);
  3115.                
  3116.                 //equip to second & third slots
  3117.                 slots = TutorialGetConnectedSkillsSlotsIDs();
  3118.                 ArraySortInts(slots);
  3119.                 EquipSkill(temporaryTutorialSkills[1].skillType, slots[1]);
  3120.                 EquipSkill(temporaryTutorialSkills[2].skillType, slots[2]);
  3121.                
  3122.                 //refresh UI
  3123.                 TutorialUpdateUI();    
  3124.         }
  3125.        
  3126.         //removes all temp skills of tutorial and restores previous skills
  3127.         public final function TutorialMutagensCleanupTempSkills(savedEquippedSkills : array<STutorialSavedSkill>)
  3128.         {
  3129.                 //remove 3 temp skills
  3130.                 TutorialGetRidOfTempSkill(2);
  3131.                 TutorialGetRidOfTempSkill(1);
  3132.                 TutorialGetRidOfTempSkill(0);
  3133.                
  3134.                 //restore skills you had previously equipped
  3135.                 EquipSkill(savedEquippedSkills[0].skillType, savedEquippedSkills[0].skillSlotID);
  3136.                 EquipSkill(savedEquippedSkills[1].skillType, savedEquippedSkills[1].skillSlotID);
  3137.                 EquipSkill(savedEquippedSkills[2].skillType, savedEquippedSkills[2].skillSlotID);
  3138.                
  3139.                 TutorialUpdateUI();
  3140.         }
  3141.        
  3142.         private final function TutorialGetRidOfTempSkill(tutTempArrIdx : int)
  3143.         {
  3144.                 var tempSkill : ESkill;
  3145.                 var i, ind : int;
  3146.                
  3147.                 tempSkill = temporaryTutorialSkills[tutTempArrIdx].skillType;
  3148.                 if(temporaryTutorialSkills[tutTempArrIdx].wasLearned)
  3149.                 {
  3150.                         if(!skills[tempSkill].isCoreSkill)
  3151.                                 pathPointsSpent[skills[tempSkill].skillPath] = pathPointsSpent[skills[tempSkill].skillPath] - 1;
  3152.                        
  3153.                         skills[tempSkill].level = 0;
  3154.                 }
  3155.                
  3156.                 ind = GetSkillSlotID(tempSkill);
  3157.                 if(ind >= 0)
  3158.                         UnequipSkill(ind);
  3159.                        
  3160.                 temporaryTutorialSkills.EraseFast(tutTempArrIdx);
  3161.                 tempSkills.Remove(tempSkill);
  3162.         }
  3163.        
  3164.         //Selects and 'learns' temp skill matching for mutagen on EES_SkillMutange1 slot.
  3165.         //If 'of wrong' color is set, temp skill will have different color than the mutagen.
  3166.         //If 'index' is set then it picks next in line skill. Eg. we have 3 skills prepared for chosing so index =1 will select the second in line.
  3167.         private final function TutorialSelectAndAddTempSkill(optional ofWrongColor : bool, optional index : int)
  3168.         {
  3169.                 var witcher : W3PlayerWitcher;
  3170.                 var mutagenColor : ESkillColor;                         //color of equipped mutagen
  3171.                 var tempSkill : ESkill;
  3172.                 var tutSkill : STutorialTemporarySkill;
  3173.                 var mutagenItemId : SItemUniqueId;
  3174.                
  3175.                 //get mutagen color
  3176.                 witcher = GetWitcherPlayer();
  3177.                 witcher.GetItemEquippedOnSlot(EES_SkillMutagen1, mutagenItemId);
  3178.                 mutagenColor = witcher.inv.GetSkillMutagenColor(mutagenItemId);
  3179.                
  3180.                 if(!ofWrongColor)
  3181.                 {
  3182.                         if(mutagenColor == SC_Blue)
  3183.                         {
  3184.                                 if(index == 0)                  tempSkill = S_Magic_s01;
  3185.                                 else if(index == 1)             tempSkill = S_Magic_s02;
  3186.                                 else if(index == 2)             tempSkill = S_Magic_s03;
  3187.                         }
  3188.                         else if(mutagenColor == SC_Red)
  3189.                         {
  3190.                                 if(index == 0)                  tempSkill = S_Sword_s01;
  3191.                                 else if(index == 1)             tempSkill = S_Sword_s02;
  3192.                                 else if(index == 2)             tempSkill = S_Sword_s03;
  3193.                         }
  3194.                         else if(mutagenColor == SC_Green)
  3195.                         {
  3196.                                 if(index == 0)                  tempSkill = S_Alchemy_s01;
  3197.                                 else if(index == 1)             tempSkill = S_Alchemy_s02;
  3198.                                 else if(index == 2)             tempSkill = S_Alchemy_s03;
  3199.                         }
  3200.                 }
  3201.                 else
  3202.                 {
  3203.                         if(mutagenColor == SC_Green)
  3204.                                 tempSkill = S_Magic_s01;
  3205.                         else
  3206.                                 tempSkill = S_Alchemy_s01;
  3207.                 }
  3208.                                        
  3209.                 //add temp skill if not known
  3210.                 if(GetSkillLevel(tempSkill) <= 0)
  3211.                 {
  3212.                         tempSkills.PushBack(tempSkill);
  3213.                         AddSkill(tempSkill, true);
  3214.                         tutSkill.wasLearned = true;
  3215.                 }
  3216.                 else
  3217.                 {
  3218.                         tutSkill.wasLearned = false;
  3219.                 }
  3220.                
  3221.                 tutSkill.skillType = tempSkill;
  3222.                 temporaryTutorialSkills.PushBack(tutSkill);
  3223.         }
  3224.        
  3225.         //returns array of Slot IDs of those three slots that are connected to EES_SkillMutagen1 mutagen slot
  3226.         private final function TutorialGetConnectedSkillsSlotsIDs() : array<int>
  3227.         {
  3228.                 var i, connectedSkillsGroupID, processedSlots : int;
  3229.                 var slots : array<int>;
  3230.                
  3231.                 connectedSkillsGroupID = GetSkillGroupIdOfMutagenSlot(EES_SkillMutagen1);
  3232.                
  3233.                 for(i=0; i<skillSlots.Size(); i+=1)
  3234.                 {
  3235.                         if(skillSlots[i].groupID == connectedSkillsGroupID)
  3236.                         {
  3237.                                 slots.PushBack(skillSlots[i].id);
  3238.                                 processedSlots += 1;
  3239.                                
  3240.                                 if(processedSlots == 3)
  3241.                                         break;
  3242.                         }
  3243.                 }
  3244.                
  3245.                 return slots;
  3246.         }
  3247.        
  3248.         private final function TutorialUpdateUI()
  3249.         {
  3250.                 ( (CR4CharacterMenu) ((CR4MenuBase)theGame.GetGuiManager().GetRootMenu()).GetLastChild() ).UpdateData(false);
  3251.         }
  3252.        
  3253.         //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3254.         //////////////////////////////////////////////  @HAXXX  //////////////////////////////////////////////////////////////////////////
  3255.         //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3256.         //returns true if slot was unlocked in the process
  3257.                 //unequip mutagens
  3258.                 //witcher.UnequipItemFromSlot( EES_SkillMutagen1 );
  3259.                 //witcher.UnequipItemFromSlot( EES_SkillMutagen2 );
  3260.                 //witcher.UnequipItemFromSlot( EES_SkillMutagen3 );
  3261.                 //witcher.UnequipItemFromSlot( EES_SkillMutagen4 );
  3262.                 //update current progress
  3263.                 //not witcher
  3264.                 //research
  3265.                 witcher.inv.RemoveItem( item );
  3266.                 mutations[ idx ].progress.redUsed += redPoints;
  3267.                 mutations[ idx ].progress.greenUsed += greenPoints;
  3268.                 mutations[ idx ].progress.blueUsed += bluePoints;
  3269.                 mutations[ idx ].progress.overallProgress = -1; //reset cache
  3270.                                 skillSlots[ idx ].unlocked = false;
  3271.         //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3272.         //////////////////////////////////////////////  @HAXXX  //////////////////////////////////////////////////////////////////////////
  3273.         //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  3274.         final function Debug_HAX_UnlockSkillSlot(slotIndex : int) : bool
  3275.         {
  3276.                 if(!IsSkillSlotUnlocked(slotIndex))
  3277.                 {
  3278.                         skillSlots[slotIndex].unlocked = true;
  3279.                         LogSkills("W3PlayerAbilityManager.Debug_HAX_UnlockSkillSlot: unlocking skill slot " + slotIndex + " for debug purposes");
  3280.                         return true;
  3281.                 }
  3282.                
  3283.                 return false;
  3284.         }
  3285.        
  3286.         final function DBG_SkillSlots()
  3287.         {
  3288.                 var i : int;
  3289.                
  3290.                 for(i=0; i<skillSlots.Size(); i+=1)
  3291.                 {
  3292.                         LogChannel('DEBUG_SKILLS', i + ") ID=" + skillSlots[i].id + " | skill=" + skillSlots[i].socketedSkill + " | groupID=" + skillSlots[i].groupID + " | unlockedAt=" + skillSlots[i].unlockedOnLevel);
  3293.                 }
  3294.                
  3295.                 LogChannel('DEBUG_SKILLS',"");
  3296.         }
  3297. }
  3298.  
  3299. exec function dbgskillslots()
  3300. {
  3301.         thePlayer.DBG_SkillSlots();
  3302. }
  3303.