diff --git a/msu/hooks/entity/tactical/actor.nut b/msu/hooks/entity/tactical/actor.nut index a8412adb9..598599309 100644 --- a/msu/hooks/entity/tactical/actor.nut +++ b/msu/hooks/entity/tactical/actor.nut @@ -1,4 +1,33 @@ ::MSU.MH.hook("scripts/entity/tactical/actor", function(q) { + q.m.MSU_IsPreviewing <- false; + q.m.MSU_PreviewSkill <- null; + q.m.MSU_PreviewMovement <- null; + + // Overwrite the vanilla function to not set the preview action points while the actor is previewing. + // Because of the way our affordability preview system is set up we call skill_container.update() multiple times + // and that function sets the AP of the character. But during the preview we don't want to reset the + // action points due to these update calls otherwise it sets the preview action points back + // to the current action points of the actor, leading to wrong info in the UI. - Midas + q.setActionPoints = @() function( _a ) + { + this.m.ActionPoints = ::Math.round(_a); + if (!this.isPreviewing()) + this.setPreviewActionPoints(_a); + } + + q.setFatigue = @() function( _f ) + { + this.m.Fatigue = ::Math.max(0, ::Math.round(_f)); + if (!this.isPreviewing()) + this.setPreviewFatigue(_f); + } + + q.setDirty = @(__original) function( _value ) + { + if (this.m.MSU_PreviewSkill == null && this.m.MSU_PreviewMovement == null) + __original(_value); + } + q.onMovementStart = @(__original) function ( _tile, _numTiles ) { __original(_tile, _numTiles); @@ -27,6 +56,28 @@ return ret; } + q.isPreviewing <- function() + { + return this.m.MSU_IsPreviewing; + } + + q.getPreviewSkill <- function() + { + return this.m.MSU_PreviewSkill; + } + + q.getPreviewMovement <- function() + { + return this.m.MSU_PreviewMovement; + } + + q.resetPreview <- function() + { + this.m.MSU_IsPreviewing = false; + this.m.MSU_PreviewSkill = null; + this.m.MSU_PreviewMovement = null; + } + // VANILLAFIX: http://battlebrothersgame.com/forums/topic/oncombatstarted-is-not-called-for-ai-characters/ // This fix is spread out over 4 files: tactical_entity_manager, actor, player, standard_bearer q.onCombatStart <- function() diff --git a/msu/hooks/skills/skill.nut b/msu/hooks/skills/skill.nut index 9f0cbe013..ab5cacf23 100644 --- a/msu/hooks/skills/skill.nut +++ b/msu/hooks/skills/skill.nut @@ -62,8 +62,7 @@ q.m.IsBaseValuesSaved <- false; q.m.ScheduledChanges <- []; - q.m.IsApplyingPreview <- false; - q.m.PreviewField <- {}; + q.m.PreviewField <- {}; // Deprecated - part of the first Affordability Preview system q.isType = @() function( _t, _any = true, _only = false ) { @@ -335,15 +334,18 @@ { } + // Deprecated - part of the first Affordability Preview system q.onAffordablePreview <- function( _skill, _movementTile ) { } + // Deprecated - part of the first Affordability Preview system q.modifyPreviewField <- function( _skill, _field, _newChange, _multiplicative ) { ::MSU.Skills.modifyPreview(this, _skill, _field, _newChange, _multiplicative); } + // Deprecated - part of the first Affordability Preview system q.modifyPreviewProperty <- function( _skill, _field, _newChange, _multiplicative ) { ::MSU.Skills.modifyPreview(this, null, _field, _newChange, _multiplicative); @@ -486,11 +488,13 @@ ::MSU.QueueBucket.VeryLate.push(function() { ::MSU.MH.hook("scripts/skills/skill", function(q) { + // Deprecated - part of the first Affordability Preview system + // i.e. the hooks in this foreach loop are for the deprecated feature foreach (func in ::MSU.Skills.PreviewApplicableFunctions) { q[func] = @(__original) function() { - if (!this.m.IsApplyingPreview) return __original(); + if (!this.getContainer().m.MSU_IsApplyingPreview) return __original(); local temp = {}; foreach (field, change in this.m.PreviewField) @@ -549,22 +553,33 @@ q.isAffordablePreview = @(__original) function() { - if (!this.getContainer().m.IsPreviewing) return __original(); - this.m.IsApplyingPreview = true; + if (!this.getContainer().getActor().m.MSU_IsPreviewing) return __original(); + this.getContainer().m.MSU_IsApplyingPreview = true; local ret = __original(); - this.m.IsApplyingPreview = false; + this.getContainer().m.MSU_IsApplyingPreview = false; return ret; } q.getCostString = @(__original) function() { - if (!this.getContainer().m.IsPreviewing) return __original(); + if (!this.getContainer().getActor().m.MSU_IsPreviewing) return __original(); local preview = ::Tactical.TurnSequenceBar.m.ActiveEntityCostsPreview; if (preview != null && preview.id == this.getContainer().getActor().getID()) { - this.m.IsApplyingPreview = true; + local previewFatigue = this.getContainer().getActor().getPreviewFatigue(); + local previewAP = this.getContainer().getActor().getPreviewActionPoints(); + + this.getContainer().update(); // During this update actor.isPreviewing() is true + this.getContainer().m.MSU_IsApplyingPreview = true; local ret = __original(); - this.m.IsApplyingPreview = false; + this.getContainer().m.MSU_IsApplyingPreview = false; + this.getContainer().getActor().m.MSU_IsPreviewing = false; + this.getContainer().update(); // Do a normal update i.e. where actor.isPreviewing() is false + this.getContainer().getActor().m.MSU_IsPreviewing = true; + + this.getContainer().getActor().setPreviewFatigue(previewFatigue); + this.getContainer().getActor().setPreviewActionPoints(previewAP); + local skillID = this.getContainer().getActor().getPreviewSkillID(); local str = " after " + (skillID == "" ? "moving" : "using " + this.getContainer().getSkillByID(skillID).getName()); ret = ::MSU.String.replace(ret, "Fatigue[/color]", "Fatigue[/color]" + str); @@ -575,3 +590,69 @@ } }); }); + +::MSU.QueueBucket.VeryLate.push(function() { + // Legacy support for the first Affordablity Preview system which has been deprecated + ::MSU.MH.rawHookTree("scripts/skills/skill", function(p) { + local obj = p; + while (!("onAffordablePreview" in obj)) + { + obj = obj[obj.SuperName]; + } + + if (obj.ClassName == "skill") + return; + + local parentName = p.SuperName; + + local onUpdate = "onUpdate" in p ? p.onUpdate : null; + p.onUpdate <- function( _properties ) + { + if (this.getContainer().getActor().isPreviewing() && ::MSU.Skills.QueuedPreviewChanges.len() != 0) + { + foreach (change in ::MSU.Skills.QueuedPreviewChanges[this]) + { + change.ValueBefore = change.TargetSkill != null ? change.TargetSkill.m[change.Field] : _properties[change.Field]; + } + + // To ensure that the executeScheduledChanges function for this skill is called + if (this.getContainer().m.ScheduledChangesSkills.find(this) == null) + this.getContainer().m.ScheduledChangesSkills.push(this); + } + + if (onUpdate != null) onUpdate(_properties); + else this[parentName].onUpdate(_properties); + } + + local executeScheduledChanges = "executeScheduledChanges" in p ? p.executeScheduledChanges : null; + p.executeScheduledChanges <- function() + { + if (executeScheduledChanges != null) executeScheduledChanges(); + else this[parentName].executeScheduledChanges(); + + if (this.getContainer().getActor().isPreviewing() && ::MSU.Skills.QueuedPreviewChanges.len() != 0) + { + local currentProperties = this.getContainer().getActor().getCurrentProperties(); + foreach (change in ::MSU.Skills.QueuedPreviewChanges[this]) + { + local target = change.TargetSkill != null ? change.TargetSkill.m : currentProperties; + + if (change.Multiplicative) change.CurrChange *= target[change.Field] / change.ValueBefore; + else change.CurrChange += target[change.Field] - change.ValueBefore; + + local previewTable = change.TargetSkill == null ? this.getContainer().m.PreviewProperty : change.TargetSkill.m.PreviewField; + + if (!(change.Field in previewTable)) + previewTable[change.Field] <- { Change = change.Multiplicative ? 1 : 0, Multiplicative = change.Multiplicative }; + + if (change.Multiplicative) + previewTable[change.Field].Change *= change.NewChange / (change.CurrChange == 0 ? 1 : change.CurrChange); + else if (typeof change.NewChange == "bool") + previewTable[change.Field].Change = change.NewChange; + else + previewTable[change.Field].Change += change.NewChange - change.CurrChange; + } + } + } + }); +}); diff --git a/msu/hooks/skills/skill_container.nut b/msu/hooks/skills/skill_container.nut index 138dba667..6f00fd36d 100644 --- a/msu/hooks/skills/skill_container.nut +++ b/msu/hooks/skills/skill_container.nut @@ -1,7 +1,7 @@ ::MSU.MH.hook("scripts/skills/skill_container", function(q) { q.m.ScheduledChangesSkills <- []; - q.m.IsPreviewing <- false; q.m.PreviewProperty <- {}; + q.m.MSU_IsApplyingPreview <- false; q.update = @(__original) function() { @@ -220,109 +220,17 @@ ]); } + // Deprecated - part of the first Affordability Preview system q.onAffordablePreview <- function( _skill, _movementTile ) { this.m.PreviewProperty.clear(); - foreach (skill in this.m.Skills) - { - skill.m.PreviewField.clear(); - } - - this.callSkillsFunction("onAffordablePreview", [ - _skill, - _movementTile, - ], false); - - if (::MSU.Skills.QueuedPreviewChanges.len() == 0) return; - - local propertiesClone = this.getActor().getBaseProperties().getClone(); - - local getChange = function( _function ) - { - local skills = _function == "executeScheduledChanges" ? this.m.ScheduledChangesSkills : this.m.Skills; - foreach (skill in skills) - { - if (!skill.isGarbage()) - { - foreach (caller, changes in ::MSU.Skills.QueuedPreviewChanges) - { - if (caller == skill) - { - foreach (change in changes) - { - local target = change.TargetSkill != null ? change.TargetSkill.m : propertiesClone; - change.ValueBefore = target[change.Field]; - } - } - } - - if (_function == "executeScheduledChanges") skill[_function](); - else skill[_function](propertiesClone); - - foreach (caller, changes in ::MSU.Skills.QueuedPreviewChanges) - { - if (caller == skill) - { - foreach (change in changes) - { - if (typeof change.NewChange == "bool") continue; - - local target = change.TargetSkill != null ? change.TargetSkill.m : propertiesClone; - if (target[change.Field] == change.ValueBefore) continue; - - if (change.Multiplicative) change.CurrChange *= target[change.Field] / change.ValueBefore; - else change.CurrChange += target[change.Field] - change.ValueBefore; - } - } - } - } - } - } foreach (skill in this.m.Skills) { - skill.softReset(); - } - - getChange("onUpdate"); - getChange("onAfterUpdate"); - getChange("executeScheduledChanges"); - - foreach (changes in ::MSU.Skills.QueuedPreviewChanges) - { - foreach (change in changes) - { - local target; - local previewTable; - if (change.TargetSkill != null) - { - target = change.TargetSkill.m; - previewTable = change.TargetSkill.m.PreviewField; - } - else - { - target = this.getActor().getCurrentProperties(); - previewTable = this.m.PreviewProperty; - } - - if (!(change.Field in previewTable)) previewTable[change.Field] <- { Change = change.Multiplicative ? 1 : 0, Multiplicative = change.Multiplicative }; - - if (change.Multiplicative) - { - previewTable[change.Field].Change *= change.NewChange / (change.CurrChange == 0 ? 1 : change.CurrChange); - } - else if (typeof change.NewChange == "bool") - { - previewTable[change.Field].Change = change.NewChange; - } - else - { - previewTable[change.Field].Change += change.NewChange - change.CurrChange; - } - } + skill.m.PreviewField.clear(); + if (!skill.isGarbage()) + skill.onAffordablePreview(_skill, _movementTile); } - - ::MSU.Skills.QueuedPreviewChanges.clear(); } //Vanilla Overwrites start @@ -383,13 +291,13 @@ q.onTurnEnd = @() function() { - this.m.IsPreviewing = false; + this.getActor().resetPreview(); this.callSkillsFunctionWhenAlive("onTurnEnd"); } q.onWaitTurn = @() function() { - this.m.IsPreviewing = false; + this.getActor().resetPreview(); this.callSkillsFunctionWhenAlive("onWaitTurn"); } @@ -486,7 +394,7 @@ q.onCombatFinished = @() function() { - this.m.IsPreviewing = false; + this.getActor().resetPreview(); this.callSkillsFunction("onCombatFinished"); } diff --git a/msu/hooks/states/tactical_state.nut b/msu/hooks/states/tactical_state.nut index 299ab8c2b..107f084dd 100644 --- a/msu/hooks/states/tactical_state.nut +++ b/msu/hooks/states/tactical_state.nut @@ -1,13 +1,13 @@ ::MSU.MH.hook("scripts/states/tactical_state", function(q) { q.executeEntityTravel = @(__original) function( _activeEntity, _mouseEvent ) { - _activeEntity.getSkills().m.IsPreviewing = false; + _activeEntity.resetPreview(); return __original(_activeEntity, _mouseEvent); } q.executeEntitySkill = @(__original) function( _activeEntity, _targetTile ) { - _activeEntity.getSkills().m.IsPreviewing = false; + _activeEntity.resetPreview(); return __original(_activeEntity, _targetTile); } diff --git a/msu/hooks/ui/screens/tactical/modules/turn_sequence_bar/turn_sequence_bar.nut b/msu/hooks/ui/screens/tactical/modules/turn_sequence_bar/turn_sequence_bar.nut index aa38e2558..1018e8ea2 100644 --- a/msu/hooks/ui/screens/tactical/modules/turn_sequence_bar/turn_sequence_bar.nut +++ b/msu/hooks/ui/screens/tactical/modules/turn_sequence_bar/turn_sequence_bar.nut @@ -32,37 +32,54 @@ q.setActiveEntityCostsPreview = @(__original) function( _costsPreview ) { - if (::MSU.Mod.ModSettings.getSetting("ExpandedSkillTooltips").getValue()) - { - local activeEntity = this.getActiveEntity(); - if (activeEntity != null) - { - local skillID = "SkillID" in _costsPreview ? _costsPreview.SkillID : ""; - local skill; - local movementTile; - if (skillID == "") - { - local movement = ::Tactical.getNavigator().getCostForPath(activeEntity, ::Tactical.getNavigator().getLastSettings(), activeEntity.getActionPoints(), activeEntity.getFatigueMax() - activeEntity.getFatigue()); - movementTile = movement.End; - } - else skill = activeEntity.getSkills().getSkillByID(skillID); + if (!::MSU.Mod.ModSettings.getSetting("ExpandedSkillTooltips").getValue()) + return __original(_costsPreview); - activeEntity.getSkills().m.IsPreviewing = true; - activeEntity.getSkills().onAffordablePreview(skill, movementTile); - } - } + local activeEntity = this.getActiveEntity(); + if (activeEntity == null) + return __original(_costsPreview); + // The original function also updates the UI, but we don't want to do that yet, + // we want to do that after our skill container affordability event has run. + // If we don't switcheroo to disable it here, then we'd need to manually call `updateCostsPreview` + // on the JSHandle at the end again, and this can lead to slightly glitchy UI animation due to double updating. this.m.MSU_JSHandle.__JSHandle = this.m.JSHandle; this.m.JSHandle = this.m.MSU_JSHandle; __original(_costsPreview); this.m.JSHandle = this.m.MSU_JSHandle.__JSHandle; + + local skillID = "SkillID" in _costsPreview ? _costsPreview.SkillID : ""; + local skill; + local movement; + if (skillID == "") + movement = ::Tactical.getNavigator().getCostForPath(activeEntity, ::Tactical.getNavigator().getLastSettings(), activeEntity.getActionPoints(), activeEntity.getFatigueMax() - activeEntity.getFatigue()); + else + skill = activeEntity.getSkills().getSkillByID(skillID); + + local previewFatigue = activeEntity.getPreviewFatigue(); + local previewAP = activeEntity.getPreviewActionPoints(); + + activeEntity.m.MSU_IsPreviewing = true; + activeEntity.getSkills().onAffordablePreview(skill, movement == null ? null : movement.End); // Deprecated - part of the first Affordability Preview system + activeEntity.m.MSU_PreviewSkill = skill; + activeEntity.m.MSU_PreviewMovement = movement; + activeEntity.getSkills().update(); // During this update actor.isPreviewing() is true + ::MSU.Skills.QueuedPreviewChanges.clear(); + this.m.JSHandle.asyncCall("updateCostsPreview", this.m.ActiveEntityCostsPreview); + + activeEntity.m.MSU_IsPreviewing = false; + activeEntity.getSkills().update(); // Do a normal update i.e. where actor.isPreviewing() is false + activeEntity.m.MSU_IsPreviewing = true; + + activeEntity.setPreviewFatigue(previewFatigue); + activeEntity.setPreviewActionPoints(previewAP); } q.resetActiveEntityCostsPreview = @(__original) function() { local activeEntity = this.getActiveEntity(); - if (activeEntity != null) activeEntity.getSkills().m.IsPreviewing = false; + if (activeEntity != null) activeEntity.resetPreview(); __original(); } }); diff --git a/msu/utils/skills.nut b/msu/utils/skills.nut index 21ab9422d..e5eabaaf4 100644 --- a/msu/utils/skills.nut +++ b/msu/utils/skills.nut @@ -1,9 +1,9 @@ ::MSU.Skills <- { - PreviewApplicableFunctions = [ + PreviewApplicableFunctions = [ // Deprecated - part of the first Affordability Preview system "getActionPointCost", "getFatigueCost" ], - QueuedPreviewChanges = {}, + QueuedPreviewChanges = {}, // Deprecated - part of the first Affordability Preview system SoftResetFields = [ "ActionPointCost", "FatigueCost", @@ -48,6 +48,7 @@ }); } + // Deprecated - part of the first Affordability Preview system function addPreviewApplicableFunction( _name ) { ::MSU.requireString(_name); @@ -66,6 +67,7 @@ } // Private + // Deprecated - part of the first Affordability Preview system function modifyPreview( _caller, _targetSkill, _field, _newChange, _multiplicative ) { if (!(_caller in this.QueuedPreviewChanges)) this.QueuedPreviewChanges[_caller] <- [];