-- ========== GENERATED BY ClassDef Editor (Ctrl-Alt-F3) DO NOT EDIT MANUALLY! ========== DefineClass.ApplyGuiltyOrRighteous = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "effectType", name = "effectType", help = "Whether the effect is possive or negative (proud or guilty)", editor = "combo", default = "positive", items = function (self) return { "positive", "negative" } end, }, }, EditorView = Untranslated("Apply guilty or righteous effect on unit"), Documentation = "Apply guilty or righteous effect on unit", } function ApplyGuiltyOrRighteous:__exec(obj, context) ApplyGuiltyOrRighteousEffect(self.effectType) end function ApplyGuiltyOrRighteous:GetUIText(effect) if self.effectType == "positive" then return T(235589045798, "Some mercs may approve of this.") else return T(596690068964, "Some mercs may regret this.") end end function ApplyGuiltyOrRighteous:GetEditorView() local helperText = self.effectType == "positive" and "positive(righteous)" or "negative(guilty)" return Untranslated("Apply " .. helperText .. " effect on unit" ) end DefineClass.BanterSetUnitInteraction = { __parents = { "Effect", "UnitTarget", "BanterFunctionObjectBase", }, __generated_by_class = "EffectDef", properties = { { id = "Banters", name = "Banters", help = "List of banters to play.", editor = "preset_id_list", default = {}, preset_class = "BanterDef", item_default = "", }, { id = "Enabled", name = "Enabled", help = "Whether the banter is enabled.", editor = "bool", default = true, }, }, EditorView = Untranslated("Set banters to play when interacting with unit."), Documentation = "Set banters to play when interacting with unit.", EditorNestedObjCategory = "Interactions", } function BanterSetUnitInteraction:GetError() if not self.Banters then return "No banters" end for i,banter_id in ipairs(self.Banters) do if not Banters[banter_id] then return "Invalid banter ID " .. banter_id end end end function BanterSetUnitInteraction:__exec(obj, context) context = type(context) == "table" and context or {} local triggered = self:MatchMapUnits(obj, context) if not triggered or not context.target_units then return end for i,unit in ipairs(context.target_units) do unit.banters = self.Enabled and table.copy(self.Banters) end end DefineClass.BobbyRayConsumeStock = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "ItemConsumeProbability", help = "Probability of each item to get picked for consumption", editor = "number", default = 13, default = const.BobbyRay.FakePurchase_PickProbability, scale = "%", slider = true, min = 0, max = 100, modifiable = true, }, { id = "MinimumStockConsumption", help = "Minimum percentage of available stock consumed", editor = "number", default = 25, default = const.BobbyRay.FakePurchase_StockConsumedMin, scale = "%", slider = true, min = 0, max = 100, modifiable = true, }, { id = "MaximumStockConsumption", help = "Maximum percentage of available stock consumed (if only one unit is available, it is always consumed)", editor = "number", default = 50, default = const.BobbyRay.FakePurchase_StockConsumedMax, scale = "%", slider = true, min = 0, max = 100, modifiable = true, }, }, EditorNestedObjCategory = "Bobby Ray", EditorView = Untranslated("Consumes some of Bobby Ray's stock"), Documentation = "Consumes some of Bobby Ray's stock to simulate other buyers.\n\nEach item is evaluated independently.\nA roll with ItemConsumeProbability change determines whether an item will be purchased or not.\nIf chosen, a percentage of its stock is consumed (at least one unit is always consumed), between Min and Max StockConsumption values.", } function BobbyRayConsumeStock:__exec(obj, context) BobbyRayStoreConsumeRandomStock(self.ItemConsumeProbability, self.MinimumStockConsumption, self.MaximumStockConsumption) end DefineClass.BobbyRayRestockShop = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "RestockModifier_Standard", help = "Modifier to the number of standard items chosen for restock per event (applied to the Restock_StandardPercentage variables defined in the const editor).\n\n100 causes no change.", editor = "number", default = 100, scale = "%", slider = true, min = 10, max = 500, modifiable = true, }, { id = "RestockModifier_Used", help = "Modifier to the number of used items chosen for restock per event (applied to the Restock_UsedPercentage variables defined in the const editor).\n\n100 causes no change.", editor = "number", default = 100, scale = "%", slider = true, min = 10, max = 500, modifiable = true, }, }, EditorNestedObjCategory = "Bobby Ray", EditorView = Untranslated("Restocks Bobby Ray's Shop"), Documentation = "Restocks Bobby Ray's Shop\n\nFrom the whole pool of items that can appear in the shop (with respect to tier), a percentage between Restock_PercentageMin and Restock_PercentageMax (see the const editor, with separate variables controlling standard and used items) is restocked.\n\nThe above values are further multiplied by this effect's RestockModifier. Values below 100 decrease the number of items picked for restocking, values above 100 increase it.\n\nItems already present in the shop get their stock recalculated (never decreased, but restocking may have no effect), otherwise a new entry is created.", } function BobbyRayRestockShop:__exec(obj, context) BobbyRayStoreRestock(self.RestockModifier_Standard, self.RestockModifier_Used) end DefineClass.BobbyRaySetState = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "State", editor = "combo", default = "Closed", items = function (self) return {"Closed", 1, 2, 3, 4, 5, 6, 7, 8} end, }, }, EditorNestedObjCategory = "Bobby Ray", EditorView = Untranslated("Sets shop tier availability"), Documentation = "Possible values are:\nClosed (default, 0)\nTier-i unlocked (i>=1)", } function BobbyRaySetState:__exec(obj, context) SetQuestVar(QuestGetState("BobbyRayQuest"), "UnlockedTier", self.State == "Closed" and 0 or self.State) end DefineClass.ChangeTiredness = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "delta", name = "Delta", editor = "number", default = 0, }, }, RequiredObjClasses = { "Unit", "UnitData", }, EditorView = Untranslated("Changes a unit's Energy"), Documentation = "Changes a unit's Energy", EditorNestedObjCategory = "Units", } function ChangeTiredness:__exec(obj, context) if IsKindOfClasses(obj, "Unit", "UnitData") and not obj:IsDead() then obj:ChangeTired(self.delta) end end DefineClass.CityGrantLoyalty = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "City", help = "Change loyalty of the specified city", editor = "choice", default = false, items = function (self) return table.map(GetCurrentCampaignPreset().Cities, "Id") end, }, { id = "Amount", help = "Amount of loyalty to change with.", editor = "number", default = 0, }, { id = "SpecialConversationMessage", name = "Special Conversation Message", help = "The message to display in the log when the effect is executed from a conversation phrase.", editor = "text", default = false, translate = true, }, }, EditorView = Untranslated("Add to city loyalty."), Documentation = "Grants a given value of loyalty to a given city. The loyalty can be negative.", } function CityGrantLoyalty:GetError() if not self.City then return "Missing City" end end function CityGrantLoyalty:__exec(obj, context) local msgPrefix = false if IsKindOf(obj, "QuestsDef") and QuestIsBoolVar(obj,"Completed",true) then msgPrefix = T{858740141061, "Mission completed", DisplayName = obj.DisplayName} else msgPrefix = self.SpecialConversationMessage or "" end CityModifyLoyalty(self.City, self.Amount, msgPrefix) end function CityGrantLoyalty:GetPhraseTopRolloverText(negative, template, game) local city = gv_Cities and gv_Cities[self.City] local city_name =city and city.DisplayName or Untranslated(self.City) if self.Amount>0 then return T{571842717111, "Gained Loyalty with ",Amount = self.Amount, City = city_name} elseif self.Amount<0 then return T{749649970601, "Lost Loyalty with ",Amount = -self.Amount, City = city_name} end end DefineClass.CompleteGuardpostObjective = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "GuardpostObjective", name = "GuardpostObjective", editor = "preset_id", default = false, preset_class = "GuardpostObjective", }, }, EditorView = Untranslated("Complete guardpost objective "), Documentation = "Complete a guardpost objective, weakening the guardpost.", EditorNestedObjCategory = "Sectors", } function CompleteGuardpostObjective:__exec(obj, context) SetGuardpostObjectiveCompleted(self.GuardpostObjective) end DefineClass.ConversateWithUnit = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Group", name = "Group", help = "The group to affect. If left as default (empty) the interactable the effect is attached to will be affected.", editor = "combo", default = "", items = function (self) return GridMarkerGroupsCombo() end, }, }, EditorView = Untranslated("Start a conversation with the unit of group"), Documentation = "Start a conversation with the unit of group", EditorNestedObjCategory = "Interactions", } function ConversateWithUnit:GetError() if not self.Group then return "No group" end end function ConversateWithUnit:__exec(obj, context) local interactable = false local allInGroup = Groups[self.Group] for i, obj in ipairs(allInGroup) do if IsKindOf(obj, "Interactable") then interactable = obj break end end if not interactable then return end local unitsOnMap = GetAllPlayerUnitsOnMap() local closestUnit = false local closestDistance = false for i, u in ipairs(unitsOnMap) do if not CanInteractWith_SyncHelper(u, interactable) then goto continue end local dist = IsValid(u) and u:GetDist(interactable) if not closestDistance or dist < closestDistance then closestDistance = dist closestUnit = u end ::continue:: end if closestUnit then local conversation = FindEnabledConversation(interactable) if conversation then OpenConversationDialog(closestUnit, conversation, false, "interaction", interactable) end end end DefineClass.CustomCodeEffect = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "custom_code", name = "Custom Code", help = "Runs this code.", editor = "text", default = false, }, }, EditorView = Untranslated("Execute custom code"), Documentation = "Executes custom code", } function CustomCodeEffect:__exec(obj, context) if not self.custom_code then return end local custom_code_func, err = load(self.custom_code) if custom_code_func then -- Procall to ensure there isnt a yield (it cant be saved) procall(custom_code_func) else assert(custom_code_func) print(err) end end function CustomCodeEffect:GetError() if self.custom_code then local func, err = load(self.custom_code) if not func then return err end end end DefineClass.DisableInteractionMarkerEffect = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Group", name = "Group", help = "The group to affect. If left as default (empty) the interactable the effect is attached to will be affected.", editor = "combo", default = "", items = function (self) return GridMarkerGroupsCombo() end, }, { id = "Negate", name = "Negate", help = "Enable interaction markers instead.", editor = "bool", default = false, }, }, Documentation = "Disable interaction markers of a specific group.", EditorNestedObjCategory = "Interactable", } function DisableInteractionMarkerEffect:__exec(obj, context) local groupName = self.Group if groupName == "" then if context and context.interactable and IsKindOf(context.interactable, "Interactable") then context.interactable.enabled = self.Negate elseif IsKindOf(obj, "Interactable") then obj.enabled = self.Negate else assert(not "Non interactable object with DisableInteractionMarker effect and no group.") end return end MapForEach("map", "Interactable", function(m) if table.find(m.Groups, groupName) then m.enabled = self.Negate end end) end function DisableInteractionMarkerEffect:GetEditorView() if self.Group == "" then if self.Negate then return T(299761711576, "Enable attached interaction marker.") else return T(311484226676, "Disable attached interaction marker.") end end if self.Negate then return T{697268621451, "Enable interaction markers of group ", self} else return T{571476004863, "Disable interaction markers of group ", self} end end DefineClass.EndSectorWarningState = { __parents = { "Effect", }, __generated_by_class = "EffectDef", EditorView = Untranslated("End the current sector's Warning State"), Documentation = "End the Warning State for the current sector.", EditorNestedObjCategory = "Sector effects", } function EndSectorWarningState:__exec(obj, context) if gv_SatelliteView then return end EndWarningState() end DefineClass.ExecForEachUnitInSector = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Sector", help = "Sector id", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, { id = "Effects", name = "Effects", help = "Effects to execute.", editor = "nested_list", default = false, base_class = "Effect", }, }, Documentation = 'Executes effects for all units in all squads in the given sector. Target unit effects must be with TargetUnit = "current unit"', EditorView = Untranslated("For each unit from sector '' execute all nested effects."), EditorNestedObjCategory = "Units", } function ExecForEachUnitInSector:__exec(obj, context) local effects = self.Effects if not effects or #effects == 0 then return true end if not context then context = {} end context.is_sector_unit = true context.target_units = {} local squads = GetSquadsInSector(self.Sector) for i, squad in ipairs(squads) do for j, unit_id in ipairs(squad.units) do local unit = gv_UnitData[unit_id] context.target_units[1] = unit for _, effect in ipairs(effects) do effect:__exec(unit, context) end end end context.is_sector_unit = false end DefineClass.Explosion = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "LocationGroup", name = "Location Group", help = "Object group defining the location of the effect.", editor = "combo", default = false, items = function (self) return table.keys2(Groups) end, }, { id = "ExplosionType", help = "What kind of grenade/mine is the explosion caused by.", editor = "combo", default = "Landmine", items = function (self) return GrenadesComboItems({"Landmine"}) end, }, { id = "Damage", help = "Damage to be done.", editor = "number", default = 30, min = 0, max = 200, }, { id = "AreaOfEffect", name = "Area of Effect", help = "the blast range (radius) in number of tiles.", editor = "number", default = 3, min = 0, max = 20, }, { id = "Noise", name = "Noise", help = "Range (in tiles) in which the explosion alerts unaware enemies.", editor = "number", default = 20, template = true, min = 0, max = 100, modifiable = true, }, { id = "AppliedEffect", name = "Applied Effect", help = "What effect to be applied on the victim when the damage is dealt.", editor = "preset_id", default = false, preset_class = "CharacterEffectCompositeDef", preset_group = "Default", }, { id = "aoeType", name = "AOE Type", help = "additional effect that happens after the explosion (optional)", editor = "choice", default = "none", template = true, items = function (self) return {"none", "fire", "smoke", "teargas", "toxicgas"} end, }, }, EditorView = T(892386429862, --[[EffectDef Effects Explosion value]] "Create an explosion"), Documentation = "Create an explosion", } function Explosion:GetError() if not self.LocationGroup then return "Set the Location Group" end end function Explosion:__exec(obj, context) local objs = Groups and self.LocationGroup and Groups[self.LocationGroup] or empty_table if #objs <= 0 then return end local pos = AveragePoint(objs) local weapon = PlaceObject((self.ExplosionType == "Landmine") and "Landmine" or "Grenade") weapon.AreaOfEffect = self.AreaOfEffect weapon.BaseDamage = self.Damage weapon.Noise = self.Noise weapon.AppliedEffect = self.AppliedEffect weapon.aoeType = self.aoeType local proj = PlaceObject("FXGrenade") proj:SetPos(pos) proj.fx_actor_class = self.ExplosionType CreateGameTimeThread(function() ExplosionDamage(nil, weapon, pos, proj) DoneObject(proj) DoneObject(weapon) end) end DefineClass.FailGuardpostObjective = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "GuardpostObjective", name = "GuardpostObjective", editor = "preset_id", default = false, preset_class = "GuardpostObjective", }, }, EditorView = Untranslated("Fail guardpost objective "), Documentation = "Fail a guardpost objective", EditorNestedObjCategory = "Sectors", } function FailGuardpostObjective:__exec(obj, context) SetGuardpostObjectiveFailed(self.GuardpostObjective) end DefineClass.ForceResetAmbientLife = { __parents = { "Effect", }, __generated_by_class = "EffectDef", EditorView = Untranslated("Forces Reset of Ambient Life Behavior(Alt-Shift-A cheat)"), Documentation = "Does the same thing as the Alt + Shift + A which resets the Ambient life", } function ForceResetAmbientLife:__exec(obj, context) Msg("AmbientLifeDespawn") Msg("WallVisibilityChanged") g_AmbientLifeSpawn = true Msg("AmbientLifeSpawn") end function ForceResetAmbientLife:__waitexec(obj, context) self:__exec(obj, context) WaitMsg("AmbientLifeSpawned") end function ForceResetAmbientLife:__skip(obj, context) self:__exec(obj, context) end DefineClass.GoBerserk = { __parents = { "Effect", }, __generated_by_class = "EffectDef", RequiredObjClasses = { "Unit", }, EditorView = Untranslated("Go Berserk"), Documentation = "Go Berserk", EditorNestedObjCategory = "Units", } function GoBerserk:__exec(obj, context) if IsKindOf(obj, "Unit") and not obj:IsDead() then end end DefineClass.GrantExperienceEffect = { __parents = { "UnitTarget", "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Amount", name = "Amount", help = "Choose one of the predefined amounts", editor = "combo", default = "XPQuestReward_Small", items = function (self) return GetQuestRewardConstItems() end, }, { id = "logImportant", name = "Log as Important", help = "Whther to show the XP gain as important message in the combat log.", editor = "bool", default = false, }, }, EditorView = T(785742586188, --[[EffectDef Effects GrantExperienceEffect value]] "Grant to unit "), Documentation = "Grant experience to units. This is to be used when a map is loaded, for satellite view use GrantExperienceSector", EditorNestedObjCategory = "Units", } function GrantExperienceEffect:__exec(obj, context) if not context then context = {} end local units = self:MatchMapUnits(obj, context) if units and context.target_units then local amount = self.Amount if type(amount) == "string" then if const[amount] then amount = const[amount] else amount = tonumber(amount) end end RewardTeamExperience({ RewardExperience = amount }, { units = context.target_units }, self.logImportant) end end function GrantExperienceEffect:UnitCheck(unit, obj, context) return true end DefineClass.GrantExperienceSector = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Sector", name = "Sector", help = "Sector", editor = "combo", default = "current", items = function (self) return table.iappend({{text="current",value="current"}}, GetCampaignSectorsCombo()) end, }, { id = "Amount", name = "Amount", help = "Choose one of the predefined amounts", editor = "combo", default = "XPQuestReward_Small", items = function (self) return GetQuestRewardConstItems() end, }, { id = "logImportant", name = "Log as Important", help = "Whther to show the XP gain as important message in the combat log.", editor = "bool", default = false, }, }, EditorView = T(990833526031, --[[EffectDef Effects GrantExperienceSector value]] "Grant to all player units on sector "), Documentation = "Grant experience to all player mercs on the specified sector.", EditorNestedObjCategory = "Units", } function GrantExperienceSector:__exec(obj, context) local sector_id local getUnits = false if self.Sector == "current" then sector_id = gv_CurrentSectorId getUnits = not gv_SatelliteView else sector_id = self.Sector end local units = GetPlayerSectorUnits(sector_id, getUnits) if units then local amount = self.Amount if type(amount) == "string" then if const[amount] then amount = const[amount] else amount = tonumber(amount) end end RewardTeamExperience({ RewardExperience = amount }, { units = units, sector = sector_id }, self.logImportant) end end function GrantExperienceSector:GetError() local amount = self.Amount if type(amount) == "string" and not const[amount] and not tonumber(amount) then return "Invalid amount " .. tostring(amount) .. " - should be a number or a const" end end function GrantExperienceSector:UnitCheck(unit, obj, context) return true end function GrantExperienceSector:GetPhraseTopRolloverText(negative, template, game) local sector_id local getUnits = false if self.Sector == "current" then sector_id = gv_CurrentSectorId getUnits = not gv_SatelliteView else sector_id = self.Sector end local units = game and GetPlayerSectorUnits(sector_id, getUnits) or T(111137020067, "[Mercs]") local names = game and ConcatListWithAnd(table.map(units, function(o) return o.Nick; end)) or units local amount = self.Amount if type(amount) == "string" then if const[amount] then amount = const[amount] else amount = tonumber(amount) end end if amount>0 then return T{894599074999, "Gained XP: ()",Amount = amount, unit = names} elseif amount<0 then return T{955913519115, "Lost XP: ()",Amount = amount, unit = names} end end DefineClass.GroupAddStatusEffect = { __parents = { "Effect", "UnitTarget", }, __generated_by_class = "EffectDef", properties = { { id = "Status", name = "Status", editor = "preset_id", default = false, preset_class = "CharacterEffectCompositeDef", }, }, EditorView = Untranslated("Add effect to units in group "), Documentation = "Add status effect to units in group", EditorNestedObjCategory = "Units", } function GroupAddStatusEffect:__exec(obj, context) context = type(context) == "table" and context or {} self:MatchMapUnits(obj, context) for _, unit in ipairs(context.target_units) do if IsKindOf(unit, "StatusEffectObject") then unit:AddStatusEffect(self.Status) end end if not next(context.target_units) then print("GroupAddStatusEffect couldn't find group", self.TargetUnit) return end end DefineClass.GroupAlert = { __parents = { "Effect", "UnitTarget", }, __generated_by_class = "EffectDef", EditorView = Untranslated("Alert units from "), Documentation = "Alert units from given group", EditorNestedObjCategory = "Units", } function GroupAlert:__exec(obj, context) context = type(context) == "table" and context or {} self:MatchMapUnits(obj, context) local units = {} for _, unit in ipairs(context.target_units) do if IsKindOf(unit, "Unit") then units[#units + 1] = unit end end if not next(units) then print("GroupAlert couldn't find group", self.TargetUnit) return end CreateGameTimeThread(function() gv_CombatStartFromConversation = true TriggerUnitAlert("script", units, "aware") end, units) end DefineClass.GroupAssignToArea = { __parents = { "Effect", "UnitTarget", }, __generated_by_class = "EffectDef", properties = { { id = "Mode", name = "Assign Mode", editor = "combo", default = "overwrite", items = function (self) return { "overwrite", "add", "clear" } end, }, { id = "IndividualAreas", editor = "string_list", default = {}, no_edit = function(self) return self.Mode == "clear" end, no_validate = true, item_default = "", items = function (self) return GridMarkerFightAreaCombo("") end, max_items = 64, }, }, EditorView = Untranslated("Assign units from to tactical area(s)"), Documentation = "Assigns units from given group to tactical area(s)", EditorNestedObjCategory = "Units", } function GroupAssignToArea:__exec(obj, context) if not g_TacticalMap then return end context = type(context) == "table" and context or {} self:MatchMapUnits(obj, context) local reset = self.Mode == "overwrite" local area if self.Mode ~= "clear" and (#(self.IndividualAreas or empty_table) > 0) then area = self.IndividualAreas end for _, unit in ipairs(context.target_units) do if IsKindOf(unit, "Unit") then g_TacticalMap:AssignUnit(unit, area, reset) end end end function GroupAssignToArea:GetEditorView() if self.Mode == "clear" or #(self.IndividualAreas or empty_table) == 0 then return Untranslated("Clear assignment for units from ") else local areas = "" for _, area in ipairs(self.IndividualAreas) do if #areas == 0 then areas = area else areas = areas .. ", " .. area end end if self.Mode == "add" then return Untranslated(string.format("Add areas to assignement for units from : [%s]", areas)) end return Untranslated(string.format("Assign units from to areas [%s]", areas)) end return self.EditorView end DefineClass.GroupChangeName = { __parents = { "Effect", "UnitTarget", }, __generated_by_class = "EffectDef", properties = { { id = "ChangeName", name = "Change Name", help = "Name to be changed to", editor = "text", default = false, translate = true, }, }, EditorView = Untranslated("Change the name for units from to "), Documentation = "Change the name of units from given group", EditorNestedObjCategory = "Units", } function GroupChangeName:__exec(obj, context) context = type(context) == "table" and context or {} self:MatchMapUnits(obj, context) for _, unit in ipairs(context.target_units) do if IsKindOf(unit, "Unit") then unit.Name = self.ChangeName ObjModified(unit) end end if not next(context.target_units) then print(self.id, " couldn't find any units of: ", self.TargetUnit) return end end function GroupChangeName:GetError() if not self.ChangeName then return "Set Change Name" end end DefineClass.GroupChangeTiredness = { __parents = { "Effect", "UnitTarget", }, __generated_by_class = "EffectDef", properties = { { id = "Mode", editor = "choice", default = "Absolute", items = function (self) return { "Absolute", "Delta" } end, }, { id = "Level", editor = "choice", default = 0, no_edit = function(self) return self.Mode ~= "Absolute" end, items = function (self) return UnitTirednessComboItems end, }, { id = "Delta", editor = "number", default = 1, no_edit = function(self) return self.Mode == "Absolute" end, }, }, EditorView = Untranslated("Change Energy for units from "), Documentation = "Change Energy for units from given group", EditorNestedObjCategory = "Units", } function GroupChangeTiredness:__exec(obj, context) context = type(context) == "table" and context or {} self:MatchMapUnits(obj, context) for _, unit in ipairs(context.target_units) do if IsKindOf(unit, "Unit") then if self.Mode == "Absolute" then unit:SetTired(self.Level) else unit:ChangeTired(self.Delta) end end end if not next(context.target_units) then print("GroupChangeTiredness couldn't find any units of: ", self.TargetUnit) return end end function GroupChangeTiredness:GetError() if self.Mode == "Delta" and self.Delta == 0 then return "No effect" end end DefineClass.GroupRemoveStatusEffect = { __parents = { "Effect", "UnitTarget", }, __generated_by_class = "EffectDef", properties = { { id = "Status", name = "Status", editor = "preset_id", default = false, preset_class = "CharacterEffectCompositeDef", }, }, EditorView = Untranslated("Remove effect from units in group "), Documentation = "Remove status effect from units in group", EditorNestedObjCategory = "Units", } function GroupRemoveStatusEffect:__exec(obj, context) context = type(context) == "table" and context or {} self:MatchMapUnits(obj, context) for _, o in ipairs(context.target_units) do if IsKindOf(o, "StatusEffectObject") then o:RemoveStatusEffect(self.Status) end end if not next(context.target_units) then print("GroupRemoveStatusEffect couldn't find any units of: ", self.TargetUnit) return end end DefineClass.GroupSetAITargetModifier = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Group", name = "Group", help = "Unit group that will receive the modifier", editor = "combo", default = "false", items = function (self) return GetUnitGroups() end, }, { id = "Target", help = "Target unit group subject to the modifier", editor = "combo", default = "false", items = function (self) return GetUnitGroups() end, }, { id = "Modifier", help = "Modifier value, 100% means no modification (reset to default)", editor = "number", default = 100, scale = "%", }, }, EditorView = Untranslated("AI target modifier for against %"), Documentation = "Set AI Targeting modifier of a group of units against another group", EditorNestedObjCategory = "Units", } function GroupSetAITargetModifier:__exec(obj, context) if self.Modifier == 100 then if gv_AITargetModifiers[self.Group] then gv_AITargetModifiers[self.Group][self.Target] = nil if next(gv_AITargetModifiers[self.Group]) == nil then gv_AITargetModifiers[self.Group] = nil end end else gv_AITargetModifiers[self.Group] = gv_AITargetModifiers[self.Group] or {} gv_AITargetModifiers[self.Group][self.Target] = self.Modifier end end DefineClass.GroupSetArchetype = { __parents = { "Effect", "UnitTarget", }, __generated_by_class = "EffectDef", properties = { { id = "Archetype", editor = "choice", default = "", items = function (self) return table.keys2(Archetypes, true, "") end, }, }, EditorView = Untranslated("Change archetype for units from "), Documentation = "Change archetype for units from given group", EditorNestedObjCategory = "Units", } function GroupSetArchetype:__exec(obj, context) context = type(context) == "table" and context or {} self:MatchMapUnits(obj, context) local units = {} local archetype = self.Archetype if archetype == "" then archetype = nil end for _, unit in ipairs(context.target_units) do if IsKindOf(unit, "Unit") then unit.script_archetype = archetype end end end DefineClass.GroupSetBehaviorAdvanceTo = { __parents = { "Effect", "AnimParams", "UnitTarget", }, __generated_by_class = "EffectDef", properties = { { id = "MarkerId", help = "Marker description: can specify an id.", editor = "text", default = false, }, { id = "MarkerGroup", help = "Marker description: marker group.", editor = "combo", default = "", items = function (self) return GetBehaviorGroups end, }, { id = "MarkerType", help = "Marker description: markers type.", editor = "combo", default = "Position", items = function (self) return GetGridMarkerTypesCombo end, }, { id = "PropagateAnimParams", help = "AdvanceTo behavior passes the control to Roam behavior on finish and if this checked all the AnimParams here will be propagated to Roam", editor = "bool", default = false, }, }, EditorView = Untranslated(" Advance(s) to the marker"), Documentation = "Set units behavior to Advance towar a map marker.", EditorNestedObjCategory = "Units", } function GroupSetBehaviorAdvanceTo:__exec(obj, context) local markers = MapGetMarkers(self.MarkerType, self.MarkerGroup, function(o) return o:IsMarkerEnabled() and ((self.MarkerId or "" == "") or o.ID == self.MarkerID) end) if #(markers or empty_table) == 0 then printf("SetBehaviorAdvanceTo didn't find any markers (type %s, group %s, id %s)", tostring(self.MarkerType), tostring(self.MarkerGroup), tostring(self.MarkerId)) return end if g_Combat then return end context = type(context) == "table" and context or {} self:MatchMapUnits(obj, context) local params = self:GetAnimParams() params.PropagateAnimParams = self.PropagateAnimParams for _, o in ipairs(context.target_units) do if IsKindOf(o, "Unit") and not o:IsDead() then local marker = table.interaction_rand(markers, "GridMarker", o) o:SetCommandParams("AdvanceTo", params) o:SetCommand("AdvanceTo", marker:GetHandle()) end end end DefineClass.GroupSetBehaviorExit = { __parents = { "Effect", "AnimParams", "UnitTarget", }, __generated_by_class = "EffectDef", properties = { { id = "MarkerGroup", help = "Exit marker group.", editor = "combo", default = "", no_edit = function(self) return self.closest end, items = function (self) return GetBehaviorGroups end, }, { id = "closest", name = "Closest Available", editor = "bool", default = false, }, { id = "delay", name = "Delay", editor = "number", default = 0, scale = "sec", min = 0, }, }, EditorView = Untranslated(" exit the map via marker"), Documentation = "Units exit the map using a specified marker", EditorNestedObjCategory = "Units", } function GroupSetBehaviorExit:__exec(obj, context) context = type(context) == "table" and context or {} self:MatchMapUnits(obj, context) local units = context.target_units or empty_table for i = #units, 1, -1 do if not IsKindOf(units[i], "Unit") then table.remove(units, i) end end if #units == 0 then return end local marker_group = not self.closest and self.MarkerGroup or "" local markers = MapGetMarkers("Entrance", marker_group) if not markers or #markers == 0 then return end local marker if marker_group == "" then -- pick nearest local ucenter = AveragePoint2D(units) marker = ChooseClosestObject(markers, ucenter) else -- pick random marker = table.interaction_rand(markers, "GridMarker") end local start_time = self.delay + GameTime() if g_Combat then for _, unit in ipairs(units) do if not unit:IsDead() then unit:SetBehavior("ExitMap", { marker, start_time }) end end else local params = self:GetAnimParams() for _, unit in ipairs(units) do if unit.command == "ExplosionFly" then unit:SetCommandParams("ExitMap", params) unit:SetBehavior("ExitMap", { marker, start_time }) elseif not unit:IsDead() then unit:SetCommandParams("ExitMap", params) unit:SetCommand("ExitMap", marker, start_time) end end end end function GroupSetBehaviorExit:GetEditorView() return Untranslated("Set behavior of to ExitMap at ") end DefineClass.GroupSetBehaviorIdle = { __parents = { "Effect", "AnimParams", "UnitTarget", }, __generated_by_class = "EffectDef", EditorView = Untranslated("Set behavior of to Idle"), Documentation = "Set units behavior to Idle", EditorNestedObjCategory = "Units", } function GroupSetBehaviorIdle:__exec(obj, context) context = type(context) == "table" and context or {} self:MatchMapUnits(obj, context) local params = self:GetAnimParams() for _, o in ipairs(context.target_units) do if IsKindOf(o, "Unit") and not o:IsDead() then o:SetBehavior() o:SetCommandParams("Idle", params) o:SetCommand("Idle") end end end DefineClass.GroupSetBehaviorPatrol = { __parents = { "Effect", "AnimParams", "UnitTarget", }, __generated_by_class = "EffectDef", properties = { { id = "MarkerGroup", help = "Patrol markers group", editor = "combo", default = "", items = function (self) return GetBehaviorGroups end, }, { id = "Repeat", name = "Repeat Route", help = "Repeat route?", editor = "bool", default = false, }, { id = "Orient", name = "End Orient", help = "Use marker orientation on the end", editor = "bool", default = true, }, }, EditorView = Untranslated(" patrol(s) between markers"), Documentation = "Set unit behavior to Patrol between markers", EditorNestedObjCategory = "Units", } function GroupSetBehaviorPatrol:__exec(obj, context) context = type(context) == "table" and context or {} self:MatchMapUnits(obj, context) local params = self:GetAnimParams() for _, o in ipairs(context.target_units) do if IsKindOf(o, "Unit") and not o:IsDead() then o:SetCommandParams("Patrol", params) local behaviorParams = {self.MarkerGroup, 1, self.Repeat, self.Orient} if g_Combat then o:SetBehavior("Patrol", behaviorParams) elseif o.being_interacted_with then o:QueueCommand("Patrol", unpack_params(behaviorParams)) else o:SetCommand("Patrol", unpack_params(behaviorParams)) end end end end DefineClass.GroupSetBehaviorRoam = { __parents = { "Effect", "AnimParams", "UnitTarget", }, __generated_by_class = "EffectDef", properties = { { id = "MarkerId", help = "Marker description: can specify an id.", editor = "text", default = false, }, { id = "MarkerGroup", help = "Marker description: marker group.", editor = "combo", default = "", items = function (self) return GetBehaviorGroups end, }, { id = "MarkerType", help = "Marker description: markers type.", editor = "combo", default = "Position", items = function (self) return GetGridMarkerTypesCombo end, }, { id = "Orient", name = "End Orient", help = "Use marker orientation on the end", editor = "bool", default = true, }, }, EditorView = Untranslated(" roam(s) around marker"), Documentation = "Set units behavior to Roam around a map's marker.", EditorNestedObjCategory = "Units", } function GroupSetBehaviorRoam:__exec(obj, context) context = type(context) == "table" and context or {} self:MatchMapUnits(obj, context) local markers = MapGetMarkers(self.MarkerType, self.MarkerGroup, function(o) return o:IsMarkerEnabled() and ((self.MarkerId or "" == "") or o.ID == self.MarkerID) end) if #(markers or empty_table) == 0 then printf("SetBehaviorRoam didn't find any markers (type %s, group %s, id %s)", tostring(self.MarkerType), tostring(self.MarkerGroup), tostring(self.MarkerId)) return end local params = self:GetAnimParams() for _, o in ipairs(context.target_units) do if IsKindOf(o, "Unit") and not o:IsDead() then local marker = table.interaction_rand(markers, "GridMarker", o) o:SetCommandParams("Roam", params) if g_Combat then o:SetBehavior("Roam", { marker, self.Orient }) elseif o.being_interacted_with then o:QueueCommand("Roam", marker, self.Orient) else o:SetCommand("Roam", marker, self.Orient) end end end end DefineClass.GroupSetImmortal = { __parents = { "Effect", "UnitTarget", }, __generated_by_class = "EffectDef", properties = { { id = "setImmortal", name = "Set immortal to", editor = "bool", default = false, }, }, EditorView = Untranslated("Set the immortal property of units from to "), Documentation = "Set the immortal property of units from a given group.", EditorNestedObjCategory = "Units", } function GroupSetImmortal:__exec(obj, context) context = type(context) == "table" and context or {} self:MatchMapUnits(obj, context) for _, unit in ipairs(context.target_units) do if IsKindOf(unit, "Unit") then unit.immortal = self.setImmortal ObjModified(unit) end end if not next(context.target_units) then print(self.id, " couldn't find any units of: ", self.TargetUnit) return end end DefineClass.GroupSetInfected = { __parents = { "Effect", "UnitTarget", }, __generated_by_class = "EffectDef", properties = { { id = "setInfected", name = "Set infected to", editor = "bool", default = false, }, }, EditorView = Untranslated("Set the infected property of units from to "), Documentation = "Set the infected property of units from a given group. (Some animations change based on this property)", EditorNestedObjCategory = "Units", } function GroupSetInfected:__exec(obj, context) context = type(context) == "table" and context or {} self:MatchMapUnits(obj, context) for _, unit in ipairs(context.target_units) do if IsKindOf(unit, "Unit") then unit.infected = self.setInfected ObjModified(unit) end end if not next(context.target_units) then print(self.id, " couldn't find any units of: ", self.TargetUnit) return end end DefineClass.GroupSetRoutine = { __parents = { "Effect", "AnimParams", "UnitTarget", }, __generated_by_class = "EffectDef", properties = { { id = "Routine", help = "Set units of group to particular routine, or the one from their spawner.", editor = "combo", default = "spawner", items = function (self) local r = table.copy(UnitRoutines) r[#r+1] = "spawner" return r end, }, { id = "RoutineArea", help = "Area to play the routine in, or use the one from the spawner.", editor = "combo", default = "spawner", items = function (self) local g = table.copy(GridMarkerGroupsCombo()) g[1+#g] = "spawner" return g end, }, { id = "PropagateAnimParams", help = "AdvanceTo behavior passes the control to Roam behavior on finish and if this checked all the AnimParams here will be propagated to Roam", editor = "bool", default = false, }, }, EditorView = Untranslated("Set routine of to "), Documentation = "Set routine of units", EditorNestedObjCategory = "Units", } function GroupSetRoutine:__exec(obj, context) context = type(context) == "table" and context or {} self:MatchMapUnits(obj, context) local params = self:GetAnimParams() params.PropagateAnimParams = self.PropagateAnimParams for _, o in ipairs(context.target_units) do if IsKindOf(o, "Unit") then o.routine = self.Routine == "spawner" and o.routine_spawner.Routine or self.Routine o.routine_area = self.RoutineArea == "spawner" and "self" or self.RoutineArea o.behavior = false o:SetCommandParams("Idle", params) o:SetCommand("Idle") end end end DefineClass.GroupSetSide = { __parents = { "Effect", "UnitTarget", }, __generated_by_class = "EffectDef", properties = { { id = "Side", help = "The new side of group.", editor = "choice", default = false, items = function (self) return Sides end, }, { id = "CreateSquad", help = "If set to false the units will not create a new squad. Keep in mind they will be ejected from their old squad so they will despawn on the next presence check.", editor = "bool", default = true, }, }, EditorView = Untranslated("Change the side of to "), Documentation = "Change groups side", EditorNestedObjCategory = "Units", } function GroupSetSide:__exec(obj, context) context = type(context) == "table" and context or {} self:MatchMapUnits(obj, context) if not next(context.target_units) then print("GroupSetSide couldn't find group", self.TargetUnit) return end if self.CreateSquad then local squads = {} for _, o in ipairs(context.target_units) do if IsKindOf(o, "Unit") and o.Squad then table.insert_unique(squads, o.Squad) local memberArrayName = "members" .. o.Squad local memberArray = squads[memberArrayName] if not memberArray then memberArray = {} squads[memberArrayName] = memberArray end memberArray[#memberArray + 1] = o.session_id end end for i, s in ipairs(squads) do local squadObj = gv_Squads[s] local createNew = false if squadObj.Side ~= self.Side then -- If squad is not of this side already -- Check all units in the squad. local units = squadObj.units for _, u in ipairs(units) do -- If any of the units in the squad are not part of the group, -- they must remain on their old side in the old squad. local unit = g_Units[u] if not table.find(context.target_units, unit) then createNew = true break end end end if createNew then local unitsToChangeSide = squads["members" .. s] CreateNewSatelliteSquad({ Side = self.Side, CurrentSector = squadObj.CurrentSector, Name = SquadName:GetNewSquadName(self.Side, unitsToChangeSide) }, unitsToChangeSide) else SetSatelliteSquadSide(s, self.Side) end end else for _, o in ipairs(context.target_units) do if IsKindOf(o, "Unit") then local ud = gv_UnitData[o.session_id] RemoveUnitFromSquad(ud, "script") -- Dirty hack to prevent instant despawn when setting side to neutral. if self.Side == "neutral" then o.IsMerc = function() return false end end end end end for _, o in ipairs(context.target_units) do if IsKindOf(o, "Unit") then o:SetSide(self.Side) end end Msg("GroupChangeSide", self.TargetUnit, self.Side, context.target_units) CheckGameOver() end function GroupSetSide:__waitexec(obj, context) self:__exec(obj, context) end function GroupSetSide:GetError() if not self.Side then return "Set the new side!" end end DefineClass.GroupTeleport = { __parents = { "Effect", "AnimParams", "UnitTarget", }, __generated_by_class = "EffectDef", properties = { { id = "MarkerGroup", help = "Marker group where the units should be teleported to.", editor = "combo", default = "", items = function (self) return GetBehaviorGroups end, }, }, EditorView = Untranslated(" will be teleported to marker"), Documentation = "Units group teleports to a specified marker", EditorNestedObjCategory = "Units", } function GroupTeleport:__exec(obj, context) context = type(context) == "table" and context or {} self:MatchMapUnits(obj, context) local units = context.target_units or empty_table for i = #units, 1, -1 do if not IsKindOf(units[i], "Unit") then table.remove(units, i) end end if #(units or empty_table) == 0 then return string.format("No units from group '%s' to teleport", self.TargetUnit) end local markers = MapGetMarkers(nil, self.MarkerGroup) if not markers or #markers == 0 then return string.format("No markets in '%s' to teleport group '%s' to", self.MarkerGroup, self.TargetUnit) end local unit_pos, units_left = {} for _, marker in ipairs(markers) do units_left = {} local dest = GetUnitsDestinations(units, marker) for idx, unit in ipairs(units) do if dest[idx] then table.insert(unit_pos, unit) unit_pos[unit] = point(point_unpack(dest[idx])) else table.insert(units_left, unit) end end if #units_left == 0 then break end units = units_left end if #units_left > 0 then for _, unit in ipairs(units_left) do StoreErrorSource(unit, string.format("No position found for group '%s' teleport to markers '%s'!", self.TargetUnit, self.MarkerGroup)) end end for _, unit in ipairs(unit_pos) do NetSyncEvent("StartCombatAction", netUniqueId, "Teleport", unit, g_Combat and 0 or false, unit_pos[unit]) end return string.format("%d unit(s) from '%s' teleported to '%s'", #units, self.TargetUnit, self.MarkerGroup) end function GroupTeleport:GetEditorView() return Untranslated("Teleport to ") end DefineClass.Heal = { __parents = { "Effect", }, __generated_by_class = "EffectDef", RequiredObjClasses = { "Unit", "UnitData", }, EditorView = Untranslated("Restore unit's health"), Documentation = "Restores unit's health", EditorNestedObjCategory = "Units", } function Heal:__exec(obj, context) if IsKindOf(obj, "UnitData") then HealUnitData(obj) else obj:ReviveOnHealth() end end DefineClass.HealWounds = { __parents = { "Effect", }, __generated_by_class = "EffectDef", RequiredObjClasses = { "Unit", }, EditorView = Untranslated("Heal unit's wounds"), Documentation = "Heals unit's wounds", EditorNestedObjCategory = "Units", } function HealWounds:__exec(obj, context) obj:RemoveStatusEffect("Wounded", "all") end DefineClass.HerbalMedicineEffect = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "berserkChance", name = "Berserk Chance", editor = "number", default = 15, }, { id = "apChance", name = "AP Chance", editor = "number", default = 15, }, }, RequiredObjClasses = { "Unit", "UnitData", }, EditorView = Untranslated("Grant AP or Go Berserk"), Documentation = "Grant AP or Go Berserk", EditorNestedObjCategory = "Units", } function HerbalMedicineEffect:__exec(obj, context) if IsKindOf(obj, "Unit") and not obj:IsDead() then local berserkRoll = InteractionRand(100, "HerbalMedicine") local apRoll = InteractionRand(100, "HerbalMedicine") if berserkRoll < self.berserkChance then end if apRoll < self.apChance then end end end DefineClass.HideQuestBadge = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Show", help = "Show the badge instead. (if it was hidden with this effect)", editor = "bool", default = false, }, { id = "Quest", name = "Quest", help = "Which quest to associate this badge with. If the effect is placed on a quest it will attempt to find it itself.", editor = "preset_id", default = false, preset_class = "QuestsDef", }, { id = "BadgeIdx", name = "Badge Id", editor = "number", default = 1, sort_order = 9, min = 1, }, { id = "Preview", editor = "text", default = false, dont_save = true, read_only = true, sort_order = 10, }, { id = "LogLine", name = "Log Line", help = "Change the state of that line.", editor = "choice", default = false, items = function (self) return GetQuestNoteLinesCombo(self.Quest) end, }, }, Documentation = "Hide a quest badge", EditorNestedObjCategory = "Units", } function HideQuestBadge:__exec(obj, context) local questState = (self.Quest and gv_Quests[self.Quest]) or (IsKindOf(obj, "QuestsDef") and obj) or false assert(questState) if not questState then return end local magicParam = badgeHideIdentifierNote local preset, index = self:GetLinePreset() assert(preset) -- Missing quest probably if not preset then return end magicParam = magicParam .. tostring(index) .. "@" .. tostring(self.BadgeIdx) if self.Show then questState[magicParam] = nil else questState[magicParam] = true end UpdateQuestBadges(questState) if g_SatelliteUI then local badge = preset.Badges[self.BadgeIdx] if badge and badge.Sector then g_SatelliteUI:UpdateSectorVisuals(badge.Sector) end end end function HideQuestBadge:GetError() if not self.Quest or self.Quest=="" then return "Specify the quest!" end if not Quests[self.Quest] then return "Missing quest!" end if not self.LogLine then return "Specify the line!" end local preset = self:GetLinePreset() if not preset then return "Invalid line!" end if not preset.Badges or #preset.Badges == 0 then return "This line doesn't place badges." end if self.BadgeIdx > #preset.Badges then return "Badge id out of range" end end function HideQuestBadge:GetLinePreset() local line = self.LogLine local quest = Quests[self.Quest] if not quest or not line then return end local notePreset = table.find_value(quest.NoteDefs, "Idx", line) if not notePreset then return end return notePreset, line end function HideQuestBadge:GetEditorView() local actionWord = self.Show and "Show " or "Hide " actionWord = actionWord .. self.BadgeIdx .. " " --Hide quest badge placed by if self.LogLine then local linePreset = self:GetLinePreset() if not linePreset then return Untranslated("Hide Badge: Missing quest!") end local text = linePreset and linePreset.Text or "log line not found" if linePreset.Badges then local badge = linePreset.Badges[self.BadgeIdx] self.Preview = "Badge on " .. (badge.BadgeUnit or "") .. " in " .. (badge.Sector or "") return Untranslated(actionWord .. "badge placed by : ").. Untranslated(text) else self.Preview = "Invalid badge" return Untranslated("Invalid badge") end else self.Preview = "" return Untranslated(actionWord .. "badge placed by : invalid log line specified") end end DefineClass.InteractingMercReduceItemCondition = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "ItemId", editor = "combo", default = false, items = function (self) return ClassDescendantsCombo("InventoryItem") end, }, { id = "Equipped", editor = "bool", default = false, }, { id = "ReduceAmount", editor = "number", default = 1, min = 1, }, }, Documentation = "Reduce the condition of an interaction merc's item", EditorView = Untranslated("Reduce the condition of interaction merc's by "), } function InteractingMercReduceItemCondition:__exec(obj, context) local unit = context and context.target_units unit = unit and unit[1] if not unit then return end local item = false if self.Equipped then item = unit:GetItemInSlot("Handheld A", self.ItemId) or unit:GetItemInSlot("Handheld B", self.ItemId) else item = unit:GetItemInSlot("Inventory", self.ItemId) end if not item then return end unit:ItemModifyCondition(item, -self.ReduceAmount) CombatLog("short", T{597332256786, " condition decreased by .", DisplayName = item.DisplayName, dmg = self.ReduceAmount}) end function InteractingMercReduceItemCondition:GetError() if not self.ItemId then return "Set Item!" end end DefineClass.KillTimer = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Name", name = "Name", help = "Name of the timer.", editor = "text", default = false, }, { id = "StopTCE", name = "Stop TCE", help = "Wether to execute the effects after the timer or not.", editor = "bool", default = false, }, }, EditorView = Untranslated("Kill timer "), Documentation = "Kills the visual UI timer displayed in the upper center of the screen", EditorNestedObjCategory = "UI & Log", } function KillTimer:__exec(quest, context, TCE) local data = TimerGetData(self.Name) if data then data.time = 0 data.StopTCE = self.StopTCE Msg("TimerFinished", data.id) end end function KillTimer:GetError() if not self.Name then return "KillTimer needs a name!" end if GetParentTableOfKindNoCheck(self, "TestHarness") then return end end DefineClass.LightsSetState = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "MarkerId", help = "Marker description: can specify an id.", editor = "text", default = false, }, { id = "MarkerGroup", help = "Marker description: marker group.", editor = "combo", default = "", items = function (self) return LightsMarkerGroups end, }, { id = "MarkerType", help = "Marker description: markers type.", editor = "combo", default = "Position", items = function (self) return GetGridMarkerTypesCombo end, }, { id = "TurnOn", editor = "bool", default = true, }, { id = "EssentialLights", editor = "bool", default = true, }, { id = "OptionalLights", editor = "bool", default = true, }, { id = "AttachedLights", editor = "bool", default = true, }, }, Documentation = "Turns ON/OFF Lights inside marker's area", EditorNestedObjCategory = "Interactions", } function LightsSetState:GetEditorView() return string.format("Turns Lights %s in %s marker(s) area(s)", self.TurnOn and "ON" or "OFF", self.MarkerGroup) end function LightsSetState:__exec(obj, context) local markers = MapGetLightsMarkers(self.MarkerType, self.MarkerGroup, function(o) return o:IsMarkerEnabled() and ((self.MarkerId or "" == "") or o.ID == self.MarkerID) end) if #(markers or empty_table) == 0 then printf("%s didn't find any markers (type %s, group %s, id %s)", self.class, tostring(self.MarkerType), tostring(self.MarkerGroup), tostring(self.MarkerId)) return end local lights = GetLights(function(light) local detail = light:GetDetailClass() if not self.EssentialLights and detail == "Essential" then return false end if not self.OptionalLights and detail == "Optional" then return false end if not self.AttachedLights and light:GetParent() then return false end return true end) for _, marker in ipairs(markers) do marker.lights_off = not self.TurnOn for _, light in ipairs(lights) do if marker:IsInsideArea2D(light) then if marker.lights_off then marker:TurnLightOff(light) else marker:TurnLightOn(light) end end end end Msg("LightsStateUpdated") end DefineClass.LockpickableSetState = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Group", name = "Group", help = "The group in which the lockpick resides.", editor = "combo", default = false, items = function (self) return GridMarkerGroupsCombo() end, }, { id = "State", help = "The state to set the locking in.", editor = "choice", default = false, items = function (self) return { "unlocked", "locked", "change-difficulty" } end, }, { id = "NewDifficulty", name = "New Difficulty", help = 'The difficulty to change to if the state is "change-difficulty".', editor = "choice", default = "None", items = function (self) return const.DifficultyPresetsNew end, }, }, EditorView = Untranslated("Change the state of lockpickables in to "), Documentation = "Change lockpickable (containers, doors) state.", } function LockpickableSetState:__exec(obj, context) for _, o in ipairs(Groups[self.Group]) do if o:IsKindOf("Lockpickable") then if self.State == "change-difficulty" then o.lockpickDifficulty = self.NewDifficulty else o:SetLockpickState(self.State == "unlocked" and "closed" or self.State) end end end end function LockpickableSetState:GetError() if not self.State then return "Set the new state!" end if not self.Group then return "Set the group" end end DefineClass.LogMessageAdd = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "message", name = "Message", help = "The message to display.", editor = "text", default = false, translate = true, }, }, EditorView = Untranslated("Log:"), Documentation = "Add a short message in game log", EditorNestedObjCategory = "UI & Log", } function LogMessageAdd:__exec(obj, context) CombatLog("important", self.message) end function LogMessageAdd:GetError() if not self.message or self.message == "" then return "Add a message" end end DefineClass.ModifySatelliteAggro = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Halt", help = "Stop passive aggro increase", editor = "bool", default = false, }, { id = "AggroAmount", editor = "number", default = 0, }, { id = "HaltDays", editor = "number", default = false, }, { id = "AmountIsPercent", editor = "bool", default = true, }, }, Documentation = "Modify Satellite aggro", } function ModifySatelliteAggro:GetEditorView() if self.Halt then if self.HaltDays then return Untranslated("Halt satellite aggro for " .. self.HaltDays .. " days.") end return Untranslated("Halt satellite aggro") elseif self.AggroAmount == 0 and not self.Halt then return Untranslated("Unhalt satellite aggro") elseif self.AmountIsPercent then return Untranslated("Add " .. self.AggroAmount / 100 .. "% satellite aggro") else return Untranslated("Add " .. self.AggroAmount .. " satellite aggro") end end function ModifySatelliteAggro:__exec(obj, context) gv_SatelliteAttacksHalted = self.Halt -- When halting reset. if gv_SatelliteAttacksHalted then gv_SatelliteAggro = 0 gv_SatelliteAttacksHaltedFor = self.HaltDays return end ModifySatelliteAggression(self.AggroAmount, self.AmountIsPercent) end function ModifySatelliteAggro:GetError() if not self.AggroAmount then return "No amount specified" end end DefineClass.ModifyTrapSpawnersEffect = { __parents = { "Effect", "TrapSpawnProperties", }, __generated_by_class = "EffectDef", properties = { { id = "Group", name = "Spawner Group", help = "The spawner group to affect.", editor = "combo", default = "", items = function (self) return GridMarkerGroupsCombo() end, }, { category = "Spawner", id = "SpawnActive", name = "Override Spawner Status", help = "Override whether the spawner is active. If disabled any spawned traps will be despawned.", editor = "combo", default = "don't change", items = function (self) return {"don't change", "enable", "disable"} end, }, }, EditorView = Untranslated("Change trap spawners of group "), Documentation = "Change the properties of trap spawners in a specific group.", EditorNestedObjCategory = "Traps", } function ModifyTrapSpawnersEffect:__exec(obj, context) local props = self:GetPropertyList() for i, s in ipairs(Groups[self.Group]) do if s:IsKindOf("TrapSpawnMarker") then s:ApplyPropertyList(props) end end if self.SpawnActive ~= "dont change" then for i, s in ipairs(Groups[self.Group]) do if s:IsKindOf("TrapSpawnMarker") then s:SetActive(self.SpawnActive == "enable") end end end end DefineClass.MusicSetPlaylist = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Playlist", name = "Playlist", editor = "combo", default = false, items = function (self) return PresetsCombo("RadioStationPreset", "Default") end, }, }, EditorView = Untranslated("Changes the current playlist"), Documentation = "Changes the current playlist", } function MusicSetPlaylist:__exec(obj, context) StartRadioStation(self.Playlist) end DefineClass.MusicSetSectorPlaylist = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "SectorID", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, { id = "MusicExploration", name = "Exploration Music", editor = "combo", default = false, items = function (self) return PresetsCombo("RadioStationPreset", "Default") end, }, { id = "MusicConflict", name = "Conflict Music", editor = "combo", default = false, items = function (self) return PresetsCombo("RadioStationPreset", "Default") end, }, { id = "MusicCombat", name = "Combat Music", editor = "combo", default = false, items = function (self) return PresetsCombo("RadioStationPreset", "Default") end, }, }, EditorView = Untranslated("Changes the playlists of a sector"), Documentation = "Changes the playlists of a sector", } function MusicSetSectorPlaylist:__exec(obj, context) if GetSectorMusicOverride("MusicExploration") ~= self.MusicExploration then SetSectorMusicOverride(self.SectorID, "MusicExploration", self.MusicExploration) end if GetSectorMusicOverride("MusicConflict") ~= self.MusicConflict then SetSectorMusicOverride(self.SectorID, "MusicConflict", self.MusicConflict) end if GetSectorMusicOverride("MusicCombat") ~= self.MusicCombat then SetSectorMusicOverride(self.SectorID, "MusicCombat", self.MusicCombat) end ResetSectorStation() end DefineClass.MusicSetTrack = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Playlist", name = "Playlist", editor = "combo", default = false, items = function (self) return PresetsCombo("RadioStationPreset", "Default") end, }, { id = "Track", name = "Track", editor = "combo", default = false, items = function (self) return RadioPlaylistCombo(self.Playlist) end, }, }, EditorView = Untranslated("Plays a track and then continues with the current playlist(radio station)"), Documentation = "Plays a track and then continues with the current playlist(radio station)", } function MusicSetTrack:__exec(obj, context) local _, playlist = RadioPlaylistCombo(self.Playlist) local track = table.find_value(playlist, "path", self.Track) MusicPlayTrack(track) end DefineClass.NeutralNPCDontMove = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "TargetUnit", name = "NPC Name", help = "The name of the NPC to associate.", editor = "combo", default = false, items = function (self) return GetUnitGroups() end, }, }, EditorView = Untranslated("Prevent neutral unit from moving in combat."), Documentation = "Prevents neutral unit from moving in combat.", EditorNestedObjCategory = "", EditorNestedObjCategory = "Units", } function NeutralNPCDontMove:__exec(obj, context) local units = Groups[self.TargetUnit] or empty_table for _, unit in ipairs(units) do unit.neutral_ai_dont_move = true end end function NeutralNPCDontMove:GetError() if not self.TargetUnit then return "Specify Target Unit" end end DefineClass.NpcUnitGiveItem = { __parents = { "Effect", "LootTableFunctionObjectBase", }, __generated_by_class = "EffectDef", properties = { { id = "ItemId", name = "Item", editor = "preset_id", default = false, preset_class = "InventoryItemCompositeDef", }, { id = "LootTableId", name = "LootTable", editor = "preset_id", default = false, preset_class = "LootDef", }, { id = "DontDrop", editor = "bool", default = false, }, { id = "TargetUnit", name = "NPC Name", help = "The name of the NPC to associate.", editor = "combo", default = false, items = function (self) return GetUnitGroups() end, }, }, EditorView = Untranslated("Give / to "), Documentation = "Give an item to an npc, and have them equip items", EditorNestedObjCategory = "Units", } function NpcUnitGiveItem:__exec(obj, context) local units = empty_table local group = Groups[self.TargetUnit] if group then local unitClass = {} for i, obj in ipairs(group) do if IsKindOf(obj, "Unit") then unitClass[#unitClass + 1] = obj end end units = unitClass else units = context.target_units or units end if not units or not units[1] then return end local items = {} if self.ItemId and self.ItemId~="" then table.insert(items, PlaceInventoryItem(self.ItemId)) end if self.LootTableId then local loot_tbl = LootDefs[self.LootTableId] if loot_tbl then loot_tbl:GenerateLoot(self, {}, InteractionRand(nil, "NpcGive"), items) end end local unit = units[1] for i, item in ipairs(items) do item.drop_chance = self.DontDrop and 0 or 100 if unit:CanAddItem("Handheld A", item) then unit:AddItem("Handheld A", item) elseif unit:CanAddItem("Head", item) then unit:AddItem("Head", item) elseif unit:CanAddItem("Torso", item) then unit:AddItem("Torso", item) elseif unit:CanAddItem("Legs", item) then unit:AddItem("Legs", item) else unit:AddItem("Inventory", item) end end unit:UpdateOutfit() end function NpcUnitGiveItem:GetError() if not self.ItemId and not self.LootTableId then return "No items set" end if not self.TargetUnit then return "No target unit" end end DefineClass.NpcUnitTakeItem = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "ItemId", name = "Item", editor = "preset_id", default = false, preset_class = "InventoryItemCompositeDef", }, { id = "TargetUnit", name = "NPC Name", help = "The name of the NPC to associate.", editor = "combo", default = false, items = function (self) return GetUnitGroups() end, }, }, EditorView = Untranslated("Take from "), Documentation = "Take an item from an NPC", EditorNestedObjCategory = "Units", } function NpcUnitTakeItem:__exec(obj, context) local units = empty_table local group = Groups[self.TargetUnit] if group then local unitClass = {} for i, obj in ipairs(group) do if IsKindOf(obj, "Unit") then unitClass[#unitClass + 1] = obj end end units = unitClass else units = context.target_units or units end if not units or not units[1] then return end local unit = units[1] local itemsToRemove = {} local itemsToRemoveSlots = {} unit:ForEachItem(false, function(item, slot) if item.class == self.ItemId then itemsToRemove[#itemsToRemove + 1] = item itemsToRemoveSlots[#itemsToRemoveSlots + 1] = slot end end) for i, item in ipairs(itemsToRemove) do unit:RemoveItem(itemsToRemoveSlots[i], item) end unit:UpdateOutfit() end function NpcUnitTakeItem:GetError() if not self.ItemId then return "No items set" end if not self.TargetUnit then return "No target unit" end end DefineClass.PhraseSetEnabled = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Conversation", help = "The conversation the phrase is in.", editor = "preset_id", default = false, preset_class = "Conversation", }, { id = "PhraseId", help = "The Id of the phrase to enable/disable.", editor = "choice", default = false, items = function (self) return GetPhraseIdsCombo(self.Conversation) end, }, { id = "Enabled", help = "Enable or disable the phrase.", editor = "bool", default = true, }, }, Documentation = "Enables or disables a particular conversation phrase.", EditorNestedObjCategory = "Interactions", } function PhraseSetEnabled:GetEditorView() local enable = self.Enabled and Untranslated("Enable phrase ") or Untranslated("Disable phrase ") return Untranslated(": ") .. enable .. Untranslated("") end function PhraseSetEnabled:__exec(obj, context) SetPhraseEnabledState(self.Conversation .. "." .. self.PhraseId, self.Enabled) end function PhraseSetEnabled:OnAfterEditorNew(parent, ged, is_paste) local preset = GetParentTableOfKind(self, "Conversation") if preset:IsKindOf("Conversation") then self.Conversation = preset.id end end DefineClass.PhraseSetSeen = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Conversation", help = "The conversation the phrase is in.", editor = "preset_id", default = false, preset_class = "Conversation", }, { id = "PhraseId", help = "The Id of the phrase to enable/disable.", editor = "choice", default = false, items = function (self) return GetPhraseIdsCombo(self.Conversation) end, }, { id = "Seen", help = "Sets the phrase as not seen (highlighted) by default; check the box to set the phrase as seen (dimmed) instead.", editor = "bool", default = false, }, }, Documentation = "Changes a conversation's phrase \"seen\" (dimmed) status.", EditorNestedObjCategory = "Interactions", } function PhraseSetSeen:GetEditorView() local seen = self.Seen and Untranslated("seen") or Untranslated("not seen") return Untranslated(": Set phrase as ") .. seen end function PhraseSetSeen:__exec(obj, context) SetPhraseSeen(self.Conversation .. "." .. self.PhraseId, self.Seen) end function PhraseSetSeen:OnAfterEditorNew(parent, ged, is_paste) local preset = GetParentTableOfKind(self, "Conversation") if preset:IsKindOf("Conversation") then self.Conversation = preset.id end end DefineClass.PlayBanterEffect = { __parents = { "Effect", "BanterFunctionObjectBase", }, __generated_by_class = "EffectDef", properties = { { id = "Banters", name = "Banters", help = "List of banters to play.", editor = "preset_id_list", default = {}, preset_class = "BanterDef", item_default = "", }, { id = "searchInMarker", name = "Include Marker Units", help = "The only valid actors for a banter are the units passed by the previous conditions. By setting this on units inside a GridMarker will also be included as valid actors.", editor = "bool", default = true, }, { id = "banterSequentialWaitFor", name = "Wait For", help = "The banter event to wait for (if executing sequentially).", editor = "choice", default = "BanterDone", items = function (self) return {"", "BanterStart", "BanterDone", "BanterLineStart", "BanterLineDone"} end, }, { id = "searchInMap", name = "Include Whole Map", help = "The only valid actors for a banter are the units passed by the previous conditions. By setting this on units from the whole map will be considered. Your banter might spawn on a unit off screen.", editor = "bool", default = false, }, { id = "FallbackToMerc", name = "Radio", help = "If enabled the banter will appear over the first merc on map if the banter actor is not present on the map.", editor = "bool", default = false, }, { id = "WaitSetpieceEnd", help = "if enabled the banter will wait for the setpiece to end", editor = "bool", default = false, }, { id = "anyFallback", name = "Use any unit as fallback for Radio.", help = "Use this if the Radio banter is not played.", editor = "bool", default = false, }, { id = "AnyActorOverrideGroup", name = "Any Actor Override Group", help = 'Banter lines with actor "any" will be played by the first /object/ from the group.', editor = "text", default = false, }, }, Documentation = "An effect to play a banter.", EditorNestedObjCategory = "Interactions", } function PlayBanterEffect:GetError() if not self.Banters then return "No banters" end for i,banter_id in ipairs(self.Banters) do if not Banters[banter_id] then return "Invalid banter ID " .. banter_id end end end function PlayBanterEffect:GetEditorView() return Untranslated("Play banter(s): ").. Untranslated(table.concat(self.Banters, ", ")) end function PlayBanterEffect:__exec(obj, context) local is_marker = IsKindOf(obj, "GridMarker") local context_is_table = context and type(context)=="table" local targetUnitsTable = context_is_table and rawget(context, "target_units") targetUnitsTable = targetUnitsTable and table.icopy(targetUnitsTable) local units = targetUnitsTable or (is_marker and { obj } or {}) if self.searchInMarker and is_marker then MapForEach("map", "Unit", function(u) if obj:IsInsideArea(u:GetPos()) then units[#units + 1] = u end end) elseif self.searchInMap then MapForEach("map", "Unit", function(u) units[#units + 1] = u end) MapForEach("map", "CheeringDummy", function(u) units[#units + 1] = u end) end if context_is_table and rawget(context, "interactable") then units = table.copy(units) table.insert(units, 1, context.interactable) end local fallback = false if self.FallbackToMerc then for _, unit in ipairs(g_Units) do if unit:IsPlayerAlly() and not unit:IsDead() then fallback = unit break end end -- search for any actor also if not fallback and self.anyFallback then for _, unit in ipairs(g_Units) do if not unit:IsDead() then fallback = unit break end end end end local banters, banterActors = FilterAvailableBanters(self.Banters, context, units, fallback) if not banters then CombatLog("debug","No banters can be played with the selected actors") return end local idx = InteractionRand(#banters, "PlayBanterEffect") + 1 local banterToPlay = banters[idx] local actorsToPlayWith = banterActors[idx] local anyActorOverride = self.AnyActorOverrideGroup -- Support for banter from conversation if context_is_table and context and context.found_merc then anyActorOverride = context.found_merc end local banterObj = PlayBanter(banterToPlay, actorsToPlayWith, fallback, anyActorOverride, self.WaitSetpieceEnd) if banterObj then local playerPos = IsValid(obj) and obj:GetPos() if playerPos then banterObj:SetPos(playerPos) end local resumeData = {} resumeData.preset = banterObj.preset.id local banterUnits = {} for i, u in ipairs(banterObj.associated_units) do banterUnits[#banterUnits + 1] = u.handle end resumeData.units = banterUnits resumeData.fallbackUnit = banterObj.fallback_actor and banterObj.fallback_actor.handle resumeData.any_actor_override = banterObj.any_actor_override and banterObj.any_actor_override.handle resumeData.playerHandle = banterObj.handle if playerPos then resumeData.player_pos = playerPos end g_PlayingBanterEffects[self] = resumeData end return banterObj end function PlayBanterEffect:__skip(obj, context) for i, banterId in ipairs(self.Banters) do SkipBanterFromUI(banterId) end end function PlayBanterEffect:__waitexec(obj, context) local banterObj = self:__exec(obj, context) local event = self.banterSequentialWaitFor if banterObj and event ~= "" then local notTimedOut, preset_id while preset_id ~= banterObj.preset.id do notTimedOut, preset_id = WaitMsg(event) end end end function PlayBanterEffect:GetError() if #self.Banters == 0 then return "Add at least one banter" end if self.searchInMarker and self.searchInMap then return "Don't select both search in marker and search in map." end end function PlayBanterEffect:GetResumeData() local resumeData = g_PlayingBanterEffects[self] if resumeData and resumeData.playerHandle then local player = HandleToObject[resumeData.playerHandle] if IsValid(player) then resumeData.current_line = (player.current_line and player.current_line + 1) or 1 return "PlayBanterEffect", resumeData end end end DefineClass.PlayNotNowVR = { __parents = { "Effect", }, __generated_by_class = "EffectDef", EditorView = Untranslated('Unit plays "NotNow" VR'), Documentation = 'Unit plays "NotNow" VR.', EditorNestedObjCategory = "Units", } function PlayNotNowVR:__exec(obj, context) PlayVoiceResponse(obj, "NotNow") end DefineClass.PlaySetpiece = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "setpiece", name = "Setpiece", help = "The id of the set piece preset to play.", editor = "preset_id", default = false, preset_class = "SetpiecePrg", }, }, EditorView = Untranslated("Play setpiece"), Documentation = "Play a Set-piece.", EditorNestedObjCategory = "", EditorNestedObjCategory = "Interactions", } function PlaySetpiece:__exec(obj, context) local triggerUnits = false if type(context) == "table" and rawget(context, "target_units") then triggerUnits = rawget(context, "target_units") elseif Groups[obj] then triggerUnits = Groups[obj] elseif gv_CurrentSectorId then triggerUnits = table.map(GetPlayerMercsInSector(gv_CurrentSectorId), function(o) return g_Units[o] end) end local foundMerc = false if type(context) == "table" and rawget(context, "found_merc") then foundMerc = rawget(context, "found_merc") foundMerc = g_Units[foundMerc] end local setpiece = Setpieces[self.setpiece] if setpiece.TakePlayerControl then local dlg = OpenDialog("XSetpieceDlg", false, { setpiece = self.setpiece, setpiece_seed = InteractionRand(nil, "Setpiece"), triggerUnits = triggerUnits, extra_params = foundMerc and { {foundMerc} } }) if GameState.entering_sector then dlg:FadeOut(0) -- immediate black screen to hide any stray frames if we are started upon entering a sector (with a loading screen) end else CreateGameTimeThread(function() StartSetpiece(self.setpiece, false, InteractionRand(nil, "Setpiece"), triggerUnits, foundMerc) end) end end function PlaySetpiece:__waitexec(obj, context) self:__exec(obj, context) local setpiece = Setpieces[self.setpiece] if setpiece.TakePlayerControl then WaitMsg("SetpieceEnded") else WaitMsg("SetpieceEndExecution") end end function PlaySetpiece:GetError() if not self.setpiece then return "No setpiece set." end end DefineClass.PlayUnitVoiceResponse = { __parents = { "UnitTarget", "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "VoiceResponse", name = "Voice Response", help = "Voice response type to choose a phrase from; the phrases of the current unit will be used.", editor = "combo", default = false, items = function (self) return GetVoiceResponseCombo(self, self.TargetUnit) end, }, }, EditorView = Untranslated(" plays ''"), Documentation = "Target unit plays voice response", EditorNestedObjCategory = "Units", } function PlayUnitVoiceResponse:__exec(obj, context) if not self.VoiceResponse then return end if not context then context = {} end local units = self:MatchMapUnits(obj, context) if units and context.target_units then local unit = context.target_units[ AsyncRand(# context.target_units) +1] if not unit or not self.VoiceResponse then return end PlayVoiceResponse(unit, self.VoiceResponse, true) end end function PlayUnitVoiceResponse:GetError() if not self.TargetUnit then return "Choose target unit" end if not self.VoiceResponse then return "Choose voice response id" end end function PlayUnitVoiceResponse:UnitCheck(unit, obj, context) return true end DefineClass.PlayerGrantMoney = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Amount", name = "Amount", help = "Amount of money to grant.", editor = "number", default = 0, min = 0, }, }, EditorView = Untranslated("Grant "), Documentation = "Give money to player.", } function PlayerGrantMoney:__exec(obj, context) AddMoney(self.Amount, "deposit") end function PlayerGrantMoney:GetPhraseTopRolloverText(negative, template, game) local amount = self.Amount if amount>0 then return T{666348317447, " acquired",Amount = amount} elseif amount<0 then return T{194741866993, "Paid ",Amount = amount} end end function PlayerGrantMoney:GetPhraseFX() return "ConversationMoneyGained" end DefineClass.PlayerPayMoney = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Amount", name = "Amount", help = "Amount of money to pay.", editor = "number", default = 0, min = 0, }, }, EditorView = Untranslated("Pay "), Documentation = "Take money from player.", } function PlayerPayMoney:__exec(obj, context) AddMoney(-self.Amount, "expense") end function PlayerPayMoney:GetPhraseTopRolloverText(negative, template, game) local amount = self.Amount return T{194741866993, "Paid ",Amount = amount} end function PlayerPayMoney:GetUIText(context) return T{982094780061, "Give ", Amount = self.Amount} end DefineClass.QuestEffectBase = { __parents = { "Effect", "QuestFunctionObjectBase", }, __generated_by_class = "ClassDef", EditorNestedObjCategory = "Quests", } function QuestEffectBase:OnAfterEditorNew(obj, socket, paste, old_id) if not paste then local quest_def = GetParentTableOfKindNoCheck(obj, "QuestsDef") if quest_def then self.QuestId = quest_def.id end end end DefineClass.QuestKillTCE = { __parents = { "QuestEffectBase", }, __generated_by_class = "EffectDef", properties = { { id = "QuestId", name = "Quest id", help = "Quest to change.", editor = "preset_id", default = false, preset_class = "QuestsDef", }, { id = "TCE", name = "TCE", help = "Quest TCE to kill", editor = "choice", default = false, items = function (self) return GetQuestsVarsCombo(self.QuestId, "TCEState") end, }, }, EditorView = Untranslated("Kills '' TCE from '' quest"), Documentation = "Kills specific TCE from a given quest", } function QuestKillTCE:__exec(obj, context) if not RunningSequentialEffects then return false end if not self.QuestId or self.QuestId == "" or not self.TCE or self.TCE == "" then return false end for i = #RunningSequentialEffects, 1, -1 do local run_state = RunningSequentialEffects[i] if run_state[5] == self.QuestId and run_state[6] == self.TCE then DeleteThread(run_state[1]) table.remove(RunningSequentialEffects, i) return end end end function QuestKillTCE:GetError() if not self.QuestId or self.QuestId=="" then return "Specify the quest!" end end DefineClass.QuestSetVariableBool = { __parents = { "QuestEffectBase", }, __generated_by_class = "EffectDef", properties = { { category = "General", id = "QuestId", name = "Quest id", help = "Quest to change.", editor = "preset_id", default = false, preset_class = "QuestsDef", preset_filter = function (preset, obj, prop_meta) return QuestHasVariable(preset, "QuestVarBool") end, }, { category = "General", id = "Prop", name = "Quest Variable", help = "Quest variable to change.", editor = "choice", default = false, items = function (self) return GetQuestsVarsCombo(self.QuestId,"Bool") end, }, { category = "General", id = "Toggle", name = "Toggle Current Value", help = "Toggles the current flag value (changes it from 'true' to 'false' and vice versa).", editor = "bool", default = false, }, { category = "General", id = "Set", name = "Change to", help = "Value to set.", editor = "bool", default = true, no_edit = function(self) return self.Toggle end, }, }, Documentation = "A way to change the quest's flag. If not 'Toggle' then changes the flag to 'Set' value, else toggle the flag's value.", } function QuestSetVariableBool:__exec(obj, context) local quest = QuestGetState(self.QuestId or "") if not quest then return end if self.Toggle then SetQuestVar(quest, self.Prop, not rawget(quest, self.Prop)) else SetQuestVar(quest, self.Prop, self.Set) end end function QuestSetVariableBool:GetError() if not self.QuestId or self.QuestId=="" then return "Specify the quest!" end if not self.Prop then return "Specify the param to change!" end end function QuestSetVariableBool:GetEditorView() if self.Toggle then return Untranslated("Quest : toggle ") else return Untranslated("Quest : = " .. tostring(self.Set)) end end DefineClass.QuestSetVariableNum = { __parents = { "QuestEffectBase", }, __generated_by_class = "EffectDef", properties = { { category = "General", id = "QuestId", name = "Quest id", help = "Quest to change.", editor = "preset_id", default = false, preset_class = "QuestsDef", preset_filter = function (preset, obj, prop_meta) return QuestHasVariable(preset, "QuestVarNum") end, }, { category = "General", id = "Prop", name = "Quest Variable", help = "Quest variable to change.", editor = "choice", default = false, items = function (self) return GetQuestsVarsCombo(self.QuestId,"Num") end, }, { category = "General", id = "Amount", name = "Amount", help = "Value to set.", editor = "number", default = 0, }, { category = "General", id = "RandomRangeMax", name = "Random Amount Max", help = 'If set, the amount will be a random number between "Amount" and this number. Both values are inclusive.', editor = "number", default = false, }, { category = "General", id = "Percent", name = "Percent", help = "Percent change to current value, if operation is modify.", editor = "number", default = 100, }, { category = "General", id = "Operation", name = "Operation", help = "Modify the current value with amount/percent or set a new one.", editor = "combo", default = "modify", items = function (self) return {"set", "modify"} end, }, }, Documentation = "Change a quest variable's value. Modifies the current value wiyh amount/percent or sets a new amount.", } function QuestSetVariableNum:__exec(obj, context) local quest = QuestGetState(self.QuestId or "") if not quest then return end local prev_val = rawget(quest,self.Prop) local new_val local mod_amount = self.Amount if self.RandomRangeMax then mod_amount = mod_amount + InteractionRand(self.RandomRangeMax - mod_amount + 1, "QuestVariableNum") end if self.Operation=="set" then new_val = mod_amount elseif self.Operation=="modify" then new_val = (prev_val or 0) + mod_amount if self.Percent~=0 then new_val = MulDivRound(new_val, self.Percent, 100) end end SetQuestVar(quest, self.Prop, new_val) end function QuestSetVariableNum:GetError() if not self.QuestId or self.QuestId=="" then return "Specify the quest!" end if not self.Prop then return "Specify the param to change!" end if self.RandomRangeMax and self.RandomRangeMax < self.Amount then return "Max cannot be smaller than min (Amount)." end if self.Operation~="set" then local quest = QuestGetState(self.QuestId or "") local prev_val = rawget(quest,self.Prop) if not prev_val then return "This prop has not set any value and can not be changed" end end end function QuestSetVariableNum:GetEditorView() local printValue = "" if self.RandomRangeMax then printValue = printValue .. "-" end if self.Operation=="set" then return Untranslated("Quest : = " .. printValue) elseif self.Operation=="modify" then return Untranslated("Quest : = % from ( + " .. printValue .. ")") end end DefineClass.QuestSetVariableSpecialValue = { __parents = { "QuestEffectBase", }, __generated_by_class = "EffectDef", properties = { { category = "General", id = "QuestId", name = "Quest id", help = "Quest to change.", editor = "preset_id", default = false, preset_class = "QuestsDef", preset_filter = function (preset, obj, prop_meta) return QuestHasVariable(preset, "QuestVarNum") end, }, { category = "General", id = "Prop", name = "Quest Variable", help = "Quest variable to change.", editor = "choice", default = false, no_validate = true, items = function (self) return GetQuestsVarsCombo(self.QuestId,"Num") end, }, { category = "General", id = "Special", name = "Special value type", help = "Value to set.", editor = "combo", default = "current campaign time", items = function (self) return {"current campaign time"} end, }, }, Documentation = "Set a quest number variable to a special value, chosen from the Special property.", } function QuestSetVariableSpecialValue:__exec(obj, context) local quest = QuestGetState(self.QuestId or "") if not quest then return end local new_val if self.Special == "current campaign time" then new_val = Game.CampaignTime end SetQuestVar(quest, self.Prop, new_val) end function QuestSetVariableSpecialValue:GetError() if not self.QuestId or self.QuestId=="" then return "Specify the quest!" end end function QuestSetVariableSpecialValue:GetEditorView() if self.Special == "current campaign time" then return Untranslated("Quest : = current campaign time") end end DefineClass.QuestSetVariableText = { __parents = { "QuestEffectBase", }, __generated_by_class = "EffectDef", properties = { { category = "General", id = "QuestId", name = "Quest id", help = "Quest to change.", editor = "preset_id", default = false, preset_class = "QuestsDef", preset_filter = function (preset, obj, prop_meta) return QuestHasVariable(preset, "QuestVarText") end, }, { category = "General", id = "Prop", name = "Quest Variable", help = "Quest variable to change", editor = "choice", default = false, items = function (self) return GetQuestsVarsCombo(self.QuestId, "Text") end, }, { category = "General", id = "Text", name = "Text", help = "Value to set.", editor = "text", default = '""', }, }, EditorView = Untranslated("Quest : = ''"), Documentation = "Change a quest's text variable.", } function QuestSetVariableText:__exec(obj, context) local quest = QuestGetState(self.QuestId or "") if not quest then return end SetQuestVar(quest, self.Prop, self.Text) end function QuestSetVariableText:GetError() if not self.QuestId or self.QuestId=="" then return "Specify the quest!" end if not self.Prop then return "Specify the param to change!" end end DefineClass.QuestSetVariableTimer = { __parents = { "QuestEffectBase", }, __generated_by_class = "EffectDef", properties = { { category = "General", id = "QuestId", name = "Quest id", help = "Quest to change.", editor = "preset_id", default = false, preset_class = "QuestsDef", preset_filter = function (preset, obj, prop_meta) return QuestHasVariable(preset, "QuestVarNum") end, }, { category = "General", id = "Prop", name = "Quest Variable", help = "Quest variable to change.", editor = "choice", default = false, items = function (self) return GetQuestsVarsCombo(self.QuestId,"Num") end, }, { category = "General", id = "TimeAmount", editor = "number", default = 0, }, { category = "General", id = "TimeAmountRangeMax", help = "If set the time amount will be randomed between TimeAmount and this value.", editor = "number", default = 0, }, { category = "General", id = "Timescale", editor = "choice", default = "h", items = function (self) return GetTimeScalesCombo() end, }, }, Documentation = "Set a timer to a quest variable.", } function QuestSetVariableTimer:__exec(obj, context) local quest = QuestGetState(self.QuestId or "") if not quest then return end local prev_val = rawget(quest, self.Prop) local timeAmount = self.TimeAmount if self.TimeAmountRangeMax ~= 0 then timeAmount = timeAmount + InteractionRand(self.TimeAmountRangeMax - timeAmount + 1, "QuestVariableTimer") end timeAmount = timeAmount * (const.Scale[self.Timescale] or const.Scale.h) local triggerTime = Game.CampaignTime + timeAmount SetQuestVar(quest, self.Prop, triggerTime) end function QuestSetVariableTimer:GetError() if not self.QuestId or self.QuestId=="" then return "Specify the quest!" end if not self.Prop then return "Specify the param to change!" end if (self.TimeAmountRangeMax or 0) ~= 0 and self.TimeAmountRangeMax < self.TimeAmount then return "Max cannot be smaller than min (TimeAmount)." end end function QuestSetVariableTimer:GetEditorView() local printValue = "" if (self.TimeAmountRangeMax or 0) ~= 0 then printValue = printValue .. "-" end return Untranslated("Set quest timer in : for after " .. printValue .. " " .. self.Timescale) end DefineClass.RadioStartConversation = { __parents = { "Effect", "ConversationFunctionObjectBase", }, __generated_by_class = "EffectDef", properties = { { id = "Conversation", name = "Conversation", help = "Conversation to start.", editor = "preset_id", default = false, preset_class = "Conversation", }, { id = "Icon", editor = "combo", default = "UI/Hud/radio", items = function (self) return GetRadioConversationIconsCombo end, }, }, EditorView = Untranslated("Start radio conversation ."), Documentation = "Starts a specific radio conversation - no groups or conditions are checked.", EditorNestedObjCategory = "Interactions", } function RadioStartConversation:__exec(obj, context) StartConversationEffect(self.Conversation, {radio = true, icon = self.Icon}) end function RadioStartConversation:__waitexec(obj, context) StartConversationEffect(self.Conversation, {radio = true, icon = self.Icon}, "wait") end function RadioStartConversation:GetError() if not self.Conversation then return "Please specify conversation" end end function RadioStartConversation:GetResumeData(thread, stack, stack_index) return "RadioStartConversation", self.Conversation, self.Icon end DefineClass.RandomEffect = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Effects", name = "Effects", editor = "nested_list", default = false, base_class = "Effect", }, }, ReturnClass = "", EditorView = Untranslated("Play a random effect from a list."), Documentation = "Play a random effect from a list.", EditorNestedObjCategory = "", } function RandomEffect:__exec(obj, context) local num = #self.Effects if num > 0 then local roll = InteractionRand(num, "RandomEffect") + 1 self.Effects[roll]:__exec() end end function RandomEffect:GetError() if not self.Effects or #self.Effects < 1 then return "Please specify some effects" end end DefineClass.RandomEffectWithCondition = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Effects", name = "Effects", editor = "nested_list", default = false, base_class = "ConditionalEffect", }, }, ReturnClass = "", EditorView = Untranslated("Play a random effect from a list whose conditions evaluate to true."), Documentation = "Play a random effect from a list whose conditions evaluate to true.", EditorNestedObjCategory = "", } function RandomEffectWithCondition:__exec(obj, context) local valid = {} for i, eff in ipairs(self.Effects) do if EvalConditionList(eff.Conditions, obj, context) then valid[#valid + 1] = eff end end local num = #valid if num > 0 then local roll = InteractionRand(num, "RandomEffect") + 1 local effRolled = valid[roll] local effects = effRolled.Effects for _, effect in ipairs(effects) do effect:__exec(obj, context) end end end function RandomEffectWithCondition:GetError() if not self.Effects or #self.Effects < 1 then return "Please specify some effects" end end DefineClass.RechargeCDs = { __parents = { "Effect", }, __generated_by_class = "EffectDef", RequiredObjClasses = { "Unit", "UnitData", }, EditorView = Untranslated("Recharge a unit's CDs"), Documentation = "Recharge a unit's CDs", EditorNestedObjCategory = "Units", } function RechargeCDs:__exec(obj, context) if IsKindOfClasses(obj, "Unit", "UnitData") and not obj:IsDead() then obj:RechargeSignatures() end end DefineClass.RegenerateGuardpostObjective = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "GuardpostObjective", name = "GuardpostObjective", editor = "preset_id", default = false, preset_class = "GuardpostObjective", }, }, EditorView = Untranslated("Regenerate guardpost objective "), Documentation = "Regenerate a completed guardpost objective.", EditorNestedObjCategory = "Sectors", } function RegenerateGuardpostObjective:__exec(obj, context) SetGuardpostObjectiveRegenerated(self.GuardpostObjective) end DefineClass.ReplaceMercEffect = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "ExistingMerc", name = "Existing Merc", help = "Existing merc to replace.", editor = "combo", default = false, items = function (self) return MercPresetCombo() end, }, { id = "NewMercDef", name = "New Merc Definition", help = "New merc definition to use for replacement.", editor = "combo", default = false, items = function (self) return MercPresetCombo() end, }, }, EditorView = Untranslated("Replace with keeping his progress."), Documentation = "Replace a merc with another definition while keeping his progress. Example use: switch Larry to Larry_Clean.", EditorNestedObjCategory = "Units", } function ReplaceMercEffect:__exec(obj, context) ReplaceMerc(self.ExistingMerc, self.NewMercDef, "keepInventory") end function ReplaceMercEffect:GetError() if not self.ExistingMerc then return "Choose an existing merc to replace" end if not self.NewMercDef then return "Choose a new merc definition to replace the existing one with" end end function ReplaceMercEffect:GetUIText(context,template, game) local merc = gv_UnitData and gv_UnitData[self.Merc] local name if not merc then name = game and "" or Untranslated("[MercName]") else name = merc.Nick or merc.Name end return T{246469241790, "Recruit merc ()", name= name} end DefineClass.ResetAmbientLife = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Ephemeral", name = "Ephemeral Only", help = "All ambient life units or just ephemeral ones.", editor = "bool", default = true, }, { id = "KickPerpetualUnits", name = "Kick Perpetual Units", help = "If checked perpetual units will be reset too, otherwise they will stay in their markers.", editor = "bool", default = false, }, { id = "ForceImmediateKick", name = "Force Immediate Kick", help = "Forces immedite kick so the marker and the unit can be used right away - usually clearing the marker happens after playing exit animation. Anyway if UnitsStealForPerpetualMarkers effect is scheduled right after this one it can not work as expected since it will be called before the destructors and the units/marker will be still busy", editor = "bool", default = false, }, }, EditorView = Untranslated("Reset Ambient Life Behavior"), Documentation = "Forces all units in ambient life behavior to check the condition of the spot they are using and makes them leave if that condition is not met", } function ResetAmbientLife:__exec(obj, context) for _, unit in ipairs(g_Units) do if (not self.Ephemeral or unit.ephemeral) and unit:IsVisiting() then unit:ResetAmbientLife(self.KickPerpetualUnits, self.ForceImmediateKick) end end end DefineClass.RestoreHealth = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "amount", name = "Amount", editor = "number", default = 9999, }, }, RequiredObjClasses = { "Unit", "UnitData", }, EditorView = Untranslated("Restore unit's health"), Documentation = "Restores unit's health", EditorNestedObjCategory = "Units", } function RestoreHealth:__exec(obj, context) if IsKindOfClasses(obj, "Unit", "UnitData") and not obj:IsDead() then obj.HitPoints = Min(obj.MaxHitPoints, obj.HitPoints + self.amount) end end DefineClass.SatelliteShortcutSetSpeed = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "shortcut_id", editor = "combo", default = false, items = function (self) return PresetsCombo("SatelliteShortcutPreset") end, }, { id = "speed_const", help = "The constant denoting the shortcut's speed", editor = "combo", default = "RiverTravelTime", items = function (self) return ConstCategoryToCombo(const.SatelliteShortcut) end, }, }, Documentation = "Set the speed of a satellite shortcut", EditorNestedObjCategory = "Sector effects", } function SatelliteShortcutSetSpeed:__exec(obj, context) SatelliteShortcutChangeSpeed(self.shortcut_id, self.speed_const) end function SatelliteShortcutSetSpeed:GetError() if not self.shortcut_id then return "Specify shortcut!" end end function SatelliteShortcutSetSpeed:GetEditorView() if self.enable then return Untranslated("Enable satellite shortcut ", self) else return Untranslated("Disable satellite shortcut ", self) end end DefineClass.SatelliteShortcutUnlockEffect = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "shortcut_id", editor = "combo", default = false, items = function (self) return PresetsCombo("SatelliteShortcutPreset") end, }, { id = "enable", editor = "bool", default = true, }, }, Documentation = "Enable a satellite shortcut", EditorNestedObjCategory = "Sector effects", } function SatelliteShortcutUnlockEffect:__exec(obj, context) SatelliteShortcutSetEnabled(self.shortcut_id, self.enable) end function SatelliteShortcutUnlockEffect:GetError() if not self.shortcut_id then return "Specify shortcut!" end end function SatelliteShortcutUnlockEffect:GetEditorView() if self.enable then return Untranslated("Enable satellite shortcut ", self) else return Untranslated("Disable satellite shortcut ", self) end end DefineClass.ScatterAmbientLife = { __parents = { "Effect", }, __generated_by_class = "EffectDef", EditorView = Untranslated("Scatter Ambient Life"), Documentation = "orders all of the ambient life units on the map (ones spawned from AmbientZones) to stop what they are doing on the spot and trigger the conflict logic.", } function ScatterAmbientLife:__exec(obj, context) ChangeGameState({ConflictScripted=true}) end DefineClass.SectorAddOperationProgress = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "sector_id", name = "Sector Id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, { id = "operation", name = "Operation", help = "Operation to add progress to.", editor = "combo", default = false, items = function (self) return table.keys(SectorOperations) end, }, { id = "perc", name = "Add Perc", help = "Percent of the target progress that will be added.", editor = "number", default = 0, min = 0, max = 100, }, }, Documentation = "Adds progress to a given operation in a given sector", EditorView = Untranslated("Adds progress to in sector "), EditorNestedObjCategory = "Sector effects", } function SectorAddOperationProgress:__exec(obj, context) SectorOperations[self.operation]:BoostProgress(self.perc, gv_Sectors[self.sector_id]) end function SectorAddOperationProgress:GetError() if not self.sector_id then return "Specify sector!" end end DefineClass.SectorDisableAutoResolve = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "value", editor = "bool", default = true, }, { id = "sector_id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, }, Documentation = "Enable/Disable auto-resolve on the sector", } function SectorDisableAutoResolve:__exec(obj, context) local sector = gv_Sectors[self.sector_id] if sector then sector.autoresolve_disabled = not self.value end end function SectorDisableAutoResolve:GetEditorView() if self.value then return Untranslated("Enable auto-resolve for sector ") else return Untranslated("Disable auto-resolve for sector ") end end function SectorDisableAutoResolve:GetError() if not self.sector_id then return "Specify sector!" end end DefineClass.SectorEnableAutoDeploy = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "deploy", help = "Enable/disable auto-deploy on enter.", editor = "bool", default = true, }, { id = "sector_id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, }, Documentation = "Enable/Disable auto-deploy mode on sector enter", } function SectorEnableAutoDeploy:__exec(obj, context) local sector = gv_Sectors[self.sector_id] if sector then sector.enabled_auto_deploy = self.deploy end end function SectorEnableAutoDeploy:GetEditorView() if self.deploy then return Untranslated("Enable auto-deploy for sector ") else return Untranslated("Disable auto-deploy for sector ") end end function SectorEnableAutoDeploy:GetError() if not self.sector_id then return "Specify sector!" end end DefineClass.SectorEnableCustomOperation = { __parents = { "Effect", "LootTableFunctionObjectBase", }, __generated_by_class = "EffectDef", properties = { { id = "sector_id", name = "Sector", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, { id = "operation", name = "Operation", help = "Custom operation.", editor = "combo", default = false, items = function (self) return GetCustomOperations() end, }, { id = "DisplayLog", name = "Display in Log", help = "Whether to display enabling the Operation in the combat log", editor = "bool", default = false, }, { category = "Execution", id = "EffectsOnSuccess", name = "On Success", help = "Effects that are executed after the operation is completed.", editor = "nested_list", default = false, base_class = "Effect", }, { category = "Execution", id = "LootTableId", name = "Loot Table Id", help = "Loot table to generate items that will be granted after the operation is completed.", editor = "preset_id", default = false, preset_class = "LootDef", }, { category = "Execution", id = "GrantItemApply", name = "GrantItemApply", help = 'Loot table roll - "first"(merc) - only once, "all"(mercs) for each merc', editor = "combo", default = "first", items = function (self) return {"first", "all"} end, }, }, Documentation = "Enables custom operation in the sector", EditorView = Untranslated("Enables custom Operation in sector "), EditorNestedObjCategory = "Sector effects", } function SectorEnableCustomOperation:__exec(obj, context) local sector = gv_Sectors[self.sector_id] if not sector then return end sector.custom_operations = sector.custom_operations or {} sector.custom_operations[self.operation] = { status = "enabled", progress = 0, EffectsOnSuccess = self.EffectsOnSuccess, LootTableId = self.LootTableId, GrantItemApply = self.GrantItemApply } ObjModified(sector) ObjModified(gv_Squads) if self.DisplayLog then CombatLog("important",T{537039992460, "New Sector Operation available (): ", ActivityName = SectorOperations[self.operation] and SectorOperations[self.operation].display_name or "", sector = self.sector_id}) end end function SectorEnableCustomOperation:GetError() if not self.sector_id then return "Specify sector!" elseif not self.operation then return "Specify custom operation!" end end function SectorEnableCustomOperation:GetPhraseTopRolloverText(negative, template, game) return T{588881082990, "Operation is available: ", ActivityName = SectorOperations[self.operation] and SectorOperations[self.operation].display_name or ""} end DefineClass.SectorEnableWarningState = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Enable", name = "Enable", help = "Enable", editor = "bool", default = true, }, { id = "sector_id", name = "Sector Id", help = "Sector id.", editor = "combo", default = "current", items = function (self) return table.iappend({{text="current",value="current"}}, GetCampaignSectorsCombo()) end, }, }, Documentation = "Enable/Disable the Warning State mechanic for sector. (does NOT trigger it)", EditorView = Untranslated("Change sector's WarningStateEnabled to . (does NOT trigger it)"), EditorNestedObjCategory = "Sector effects", } function SectorEnableWarningState:__exec(obj, context) local sector_id = self.sector_id == "current" and gv_CurrentSectorId or self.sector_id local sector = gv_Sectors[sector_id] sector.warningStateEnabled = self.Enable end DefineClass.SectorEnterConflict = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "conflict_mode", help = "Force or resolve conflict in that sector.", editor = "bool", default = true, }, { id = "sector_id", help = "Sector id.", editor = "combo", default = false, items = function (self) return table.iappend({{text="current",value="current"}}, GetCampaignSectorsCombo()) end, }, { id = "disable_travel", help = "Disables all travel zones on the map.", editor = "bool", default = false, }, { id = "lock_conflict", help = "Killing all enemies on the map cannot resolve a locked conflict, only scripts can.", editor = "bool", default = false, }, { id = "no_exploration_resolve", help = "dont resolve conflict in exploration", editor = "bool", default = false, }, { id = "descr_id", help = 'This text is displayed when the "Conflict" text is rolled over and in the description of the conflict window. Define texts in the conflict descriptions editor.', editor = "combo", default = false, items = function (self) return PresetGroupCombo("ConflictDescription", "Default") end, }, { id = "spawn_mode", editor = "combo", default = false, items = function (self) return {"attack", "defend", "explore"} end, }, }, Documentation = "Forces/Resolve conflict mode for the speciafied sector", EditorNestedObjCategory = "Sector effects", } function SectorEnterConflict:__exec(obj, context) local sector = self.sector_id == "current" and gv_Sectors[gv_CurrentSectorId] or gv_Sectors[self.sector_id] if not sector then return end if self.conflict_mode then ForceEnterConflictEffect( sector, self.spawn_mode, self.disable_travel, self.lock_conflict, self.descr_id, self.no_exploration_resolve and "force-exploration-only" or "force" ) else sector.ForceConflict = false if gv_Sectors[gv_CurrentSectorId] == sector then if sector.conflict then sector.conflict.locked = false end CheckMapConflictResolved("no voice") else ResolveConflict(sector, "no voice") end end end function SectorEnterConflict:GetEditorView() if self.conflict_mode then return Untranslated("Force conflict mode for sector ") else return Untranslated("Remove forced conflict mode for sector ") end end function SectorEnterConflict:GetError() if not self.sector_id then return "Specify sector!" end end DefineClass.SectorGrantIntel = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "sector_id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, }, Documentation = "Grants intel for the sector", EditorView = Untranslated("Grant intel for sector "), EditorNestedObjCategory = "Sector effects", } function SectorGrantIntel:__exec(obj, context) DiscoverIntelForSector(self.sector_id) end function SectorGrantIntel:GetError() if not self.sector_id then return "Specify sector!" end end function SectorGrantIntel:GetPhraseTopRolloverText(negative, template, game) local campaign = CampaignPresets[Game and Game.Campaign or DefaultCampaign] local sector = gv_Sectors and gv_Sectors[self.sector_id] or table.find_value(campaign.Sectors, "Id", self.sector_id) if sector and sector.intel_discovered then return T{903574434021, "Intel is already available for ", sector = sector} end return T{223883822777, "Gained Intel for ", sector = sector} end DefineClass.SectorModifyEnemySquads = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "sector_id", name = "Sector", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, { id = "percent", name = "Percent", help = "Percent for enemy count modification.", editor = "number", default = 0, }, { id = "count", name = "Count", help = "Specific count for enemy count modification.", editor = "number", default = 0, }, { id = "UnitTemplate", name = "UnitTemplate", help = "Unit template that will be affected or all if false.", editor = "combo", default = false, items = function (self) return GetEnemySquadsUnitTemplates("all") end, }, }, Documentation = "The spawned enemy squads in that sector get their units or specified type of units decreased or increased by that specified percent. To remove all units from unittype, set pecent to -100", EditorNestedObjCategory = "Sector effects", } function SectorModifyEnemySquads:__exec(obj, context) local value, valueType = self.percent, "percent" local count = self.count if count and count ~= 0 then value = count valueType = "count" end ModifySectorEnemySquads(self.sector_id, value, valueType, self.UnitTemplate) end function SectorModifyEnemySquads:GetEditorView() local value, valueType = self.percent, "percent" local count = self.count if count and count ~= 0 then value = count valueType = "count" end local valString = valueType == "percent" and (tostring(value) .. "%") or tostring(value) if value > 0 then if self.UnitTemplate then return Untranslated("Increases units in enemy squads force in by " .. valString) else return Untranslated("Increases all enemies in by ".. valString) end else if self.UnitTemplate then return Untranslated("Decreases units in enemy squads force in by " .. valString) else return Untranslated("Decreases all enemies in by " .. valString) end end end function SectorModifyEnemySquads:GetError() if not self.sector_id then return "Specify sector!" end end DefineClass.SectorModifyMineProperties = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "DepletionTime", name = "Depletion Income Modifier Percent", help = "% modifier for depletion time", editor = "number", default = false, min = 1, max = 500, }, { id = "DailyIncome", name = "Daily Income Modifier Percent", help = "% modifier for profit per day at 100% loyalty. For instance 200 would double, and 50 would halve", editor = "number", default = false, min = 0, max = 1000, }, { id = "sector_id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, }, Documentation = "Modify diamond mine properties for a given sector", EditorNestedObjCategory = "Sector effects", } function SectorModifyMineProperties:__exec(obj, context) local sector = gv_Sectors[self.sector_id] if self.DepletionTime then if not sector.depletion_mods then sector.depletion_mods = {} end sector.depletion_mods[#sector.depletion_mods + 1] = self.DepletionTime end if self.DailyIncome then if not sector.income_mods then sector.income_mods = {} end sector.income_mods[#sector.income_mods + 1] = self.DailyIncome end end function SectorModifyMineProperties:GetEditorView() return Untranslated("Modify diamond mine related properties on ") end function SectorModifyMineProperties:GetError() if not self.sector_id then return "Specify sector!" end end function SectorModifyMineProperties:GetPhraseTopRolloverText(negative, template, game) if self.DailyIncome and next(gv_Sectors) then local name = gv_Sectors[self.sector_id].display_name if self.DailyIncome > 100 then return T{830978589245, " diamond production increased by <(value -100)>%", value = self.DailyIncome, Name = name} end if self.DailyIncome<100 then return T{340934605682, " diamond production decreased by <(100-value)>%", value = self.DailyIncome, Name = name} end end end DefineClass.SectorRemoveCustomOperation = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "sector_id", name = "Sector", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, { id = "operation", name = "Operation", help = "Remove custom operation.", editor = "combo", default = false, items = function (self) return GetCustomOperations() end, }, }, Documentation = "Removes custom operation in the sector, sets operation mercs to Idle", EditorView = Untranslated("Removes custom Operation in sector "), EditorNestedObjCategory = "Sector effects", } function SectorRemoveCustomOperation:__exec(obj, context) local sector = gv_Sectors[self.sector_id] if not sector then return end local operation = self.operation if sector.custom_operations and sector.custom_operations[operation] then sector.custom_operations[operation] = nil end local mercs = GetPlayerSectorUnits(self.sector_id) SectorOperation_CancelByGame(mercs, operation, true) ObjModified(sector) ObjModified(gv_Squads) end function SectorRemoveCustomOperation:GetError() if not self.sector_id then return "Specify sector!" elseif not self.operation then return "Specify custom operation!" end end function SectorRemoveCustomOperation:GetPhraseTopRolloverText(negative, template, game) return T{565334741205, " Operation is unavailable", ActivityName = SectorOperations[self.operation] and SectorOperations[self.operation].display_name or ""} end DefineClass.SectorReplaceEnemySquadList = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "sector_id", name = "Sector Id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, { id = "EnemySquadsList", name = "Enemy Squads List", help = "A random squad from the list will be chosen on guardpost spawn time.", editor = "preset_id_list", default = {}, preset_class = "EnemySquads", preset_filter = function (preset, obj, prop_meta) if preset.group ~= "Test Encounters" then return obj end end, item_default = "", }, { id = "StrongEnemySquadsList", name = "Strong Enemy Squads List", editor = "preset_id_list", default = {}, preset_class = "EnemySquads", preset_filter = function (preset, obj, prop_meta) if preset.group ~= "Test Encounters" then return obj end end, item_default = "", }, { id = "ExtraDefenderSquads", name = "Extra Defender Squads", editor = "preset_id_list", default = {}, preset_class = "EnemySquads", preset_filter = function (preset, obj, prop_meta) if preset.group ~= "Test Encounters" then return obj end end, item_default = "", }, }, EditorView = Untranslated("Overrides EnemySquadList in sector "), Documentation = "Overrides EnemySquadList in a given sector", EditorNestedObjCategory = "Sector effects", } function SectorReplaceEnemySquadList:__exec(obj, context) local sector = gv_Sectors[self.sector_id] if not sector then return end if sector.EnemySquadsList and #sector.EnemySquadsList > 0 then sector.EnemySquadsList = table.copy(self.EnemySquadsList) end if sector.StrongEnemySquadsList and #sector.StrongEnemySquadsList > 0 then sector.StrongEnemySquadsList = table.copy(self.StrongEnemySquadsList) end if sector.ExtraDefenderSquads and #sector.ExtraDefenderSquads > 0 then sector.ExtraDefenderSquads = table.copy(self.ExtraDefenderSquads) end end function SectorReplaceEnemySquadList:GetError() if not self.sector_id then return "Specify sector!" end end DefineClass.SectorReplaceTargetSectors = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "sector_id", name = "Sector Id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, { id = "TargetSectors", name = "Target Sectors", help = "Target sectors for spawned enemy squads.", editor = "string_list", default = {}, item_default = "", items = function (self) return GetCampaignSectorsCombo("") end, }, }, EditorView = Untranslated("Overrides TargetSectors in a given sector"), Documentation = "Overrides TargetSectors in a given sector", EditorNestedObjCategory = "Sector effects", } function SectorReplaceTargetSectors:__exec(obj, context) local sector = gv_Sectors[self.sector_id] if sector then sector.TargetSectors = table.copy(self.TargetSectors, "deep") end end function SectorReplaceTargetSectors:GetError() if not self.sector_id then return "Specify sector!" end end DefineClass.SectorSetAwarenessSequence = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "sector_id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, { id = "awareness_sequence", name = "Awareness Sequence", editor = "choice", default = "Standard", items = function (self) return { "Standard", "Skip Setpiece", "Skip All" } end, }, }, Documentation = "Enable/Disable auto-deploy mode on sector enter", } function SectorSetAwarenessSequence:__exec(obj, context) local sector = gv_Sectors[self.sector_id] if sector then sector.awareness_sequence = self.awareness_sequence end end function SectorSetAwarenessSequence:GetEditorView() return Untranslated(string.format("Set Awareness Sequence for for sector to %s", self.awareness_sequence)) end function SectorSetAwarenessSequence:GetError() if not self.sector_id then return "Specify sector!" end end DefineClass.SectorSetCustomConflictDesc = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "sector_id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, { id = "descr_id", help = 'This text is displayed when the "Conflict" text is rolled over and in the description of the conflict window. Define texts in the conflict descriptions editor.', editor = "combo", default = false, items = function (self) return PresetGroupCombo("ConflictDescription", "Default") end, }, }, Documentation = "Set a custom conflict description for the given sector. Cleared when the conflict is resolved.", EditorNestedObjCategory = "Sector effects", } function SectorSetCustomConflictDesc:__exec(obj, context) local sector = gv_Sectors[self.sector_id] sector.CustomConflictDescr = self.descr_id if sector.conflict and not sector.conflict.descr_id then sector.conflict.descr_id = self.descr_id end end function SectorSetCustomConflictDesc:GetEditorView() return Untranslated("Set conflict description for ") end function SectorSetCustomConflictDesc:GetError() if not self.sector_id then return "Specify sector!" end if not self.descr_id then return "Specify description preset!" end end DefineClass.SectorSetForceConflict = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "sector_id", name = "Sector Id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, { id = "force", name = "Force Conflict", help = "Force conflict value.", editor = "bool", default = false, }, }, Documentation = "Enable/Disable 'force conflict' for the sector - entering sectors with force conflict set to true results in conflict mode with or without enemy presence in the sector", EditorNestedObjCategory = "Sector effects", } function SectorSetForceConflict:__exec(obj, context) if not gv_Sectors[self.sector_id] then return end gv_Sectors[self.sector_id].ForceConflict = self.force end function SectorSetForceConflict:GetEditorView() if self.force then return Untranslated("Enable sector force conflict") else return Untranslated("Disable sector force conflict") end end function SectorSetForceConflict:GetError() if not self.sector_id then return "Specify sector!" end end DefineClass.SectorSetHospital = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "enable", help = "Add/remove hospital in sector.", editor = "bool", default = true, }, { id = "sector_id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, }, Documentation = "Enable/disable hospital in specified sector", EditorNestedObjCategory = "Sector effects", } function SectorSetHospital:__exec(obj, context) local sector = gv_Sectors[self.sector_id] sector.HospitalLocked = not self.enable sector.Hospital = true -- Legacy Msg("BuildingLockChanged", self.sector_id) end function SectorSetHospital:GetPhraseTopRolloverText(negative, template, game) return T{588881082990, "Operation is available: ", ActivityName = SectorOperations["HospitalTreatment"] and SectorOperations["HospitalTreatment"].display_name or ""} end function SectorSetHospital:GetEditorView() if self.enable then return Untranslated("Enable hospital in sector ") else return Untranslated("Disable hospital in sector ") end end function SectorSetHospital:GetError() if not self.sector_id then return "Specify sector!" end end DefineClass.SectorSetMap = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "sector_id", name = "Sector Id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, { id = "MapFile", editor = "combo", default = "", items = function (self) return ListMaps() end, }, { id = "image", editor = "ui_image", default = false, }, { id = "loading_screen", editor = "ui_image", default = false, }, }, Documentation = "Change the map of a sector", EditorNestedObjCategory = "Sector effects", } function SectorSetMap:__exec(obj, context) local sectorPreset = gv_Sectors[self.sector_id] if sectorPreset then sectorPreset.Map = self.MapFile if self.image and self.image ~= "" then sectorPreset.image = self.image end if self.loading_screen and self.loading_screen ~= "" then sectorPreset.override_loading_screen = self.loading_screen end end end function SectorSetMap:GetEditorView() return Untranslated("Change the sector map of to ", self) end function SectorSetMap:GetError() if not self.sector_id then return "Specify sector!" elseif self.enable_sticky and self.disable_sticky then return "You cannot both enable and disable sticky side, choose one" end end DefineClass.SectorSetMilitia = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "enable", help = "Set militia in sector.", editor = "bool", default = true, }, { id = "sector_id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, }, Documentation = "Enable/disable militia in specified sector", EditorNestedObjCategory = "Sector effects", } function SectorSetMilitia:__exec(obj, context) gv_Sectors[self.sector_id].Militia = self.enable end function SectorSetMilitia:GetEditorView() if self.enable then return Untranslated("Enable militia in sector ") else return Untranslated("Disable militia in sector ") end end function SectorSetMilitia:GetError() if not self.sector_id then return "Specify sector!" end end DefineClass.SectorSetMineProperties = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Depletion", editor = "combo", default = "no-change", items = function (self) return { "no-change", "enabled", "disabled" } end, }, { id = "HasMine", editor = "combo", default = "no-change", items = function (self) return { "no-change", "enabled", "disabled" } end, }, { id = "DepletionTime", help = "In how many days the mine will deplete", editor = "number", default = false, min = 1, max = 500, }, { id = "DailyIncome", help = "Profit per day at 100% loyalty", editor = "number", default = false, min = 0, }, { id = "sector_id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, }, Documentation = "Set diamond mine properties for a given sector.", EditorNestedObjCategory = "Sector effects", } function SectorSetMineProperties:__exec(obj, context) local sector = gv_Sectors[self.sector_id] if self.Depletion ~= "no-change" then sector.Depletion = self.Depletion == "enabled" end if self.HasMine ~= "no-change" then sector.Mine = self.HasMine == "enabled" end if self.DepletionTime then sector.DepletionTime = self.DepletionTime end if self.DailyIncome then sector.DailyIncome = self.DailyIncome end end function SectorSetMineProperties:GetEditorView() return Untranslated("Set diamond mine related properties on ") end function SectorSetMineProperties:GetError() if not self.sector_id then return "Specify sector!" end end DefineClass.SectorSetPort = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "enable", help = "Add/remove port in sector.", editor = "bool", default = true, }, { id = "sector_id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, }, Documentation = "Enable/disable port in specified sector", EditorNestedObjCategory = "Sector effects", } function SectorSetPort:__exec(obj, context) local sector = gv_Sectors[self.sector_id] sector.PortLocked = not self.enable sector.Port = true -- Legacy if sector.Side == "player1" or sector.Side == "player2" then gv_PlayerSectorCounts["Port"] = gv_PlayerSectorCounts["Port"] + 1 end Msg("BuildingLockChanged", self.sector_id) end function SectorSetPort:GetEditorView() if self.enable then return Untranslated("Enable port in sector ") else return Untranslated("Disable port in sector ") end end function SectorSetPort:GetError() if not self.sector_id then return "Specify sector!" end end DefineClass.SectorSetRAndROperation = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "enable", help = "Set RAndR operation in sector.", editor = "bool", default = true, }, { id = "sector_id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, }, Documentation = "Enable/disable RAndR operation in specified sector", EditorNestedObjCategory = "Sector effects", } function SectorSetRAndROperation:__exec(obj, context) gv_Sectors[self.sector_id].RAndRAllowed = self.enable end function SectorSetRAndROperation:GetEditorView() if self.enable then return Untranslated("Enable RAndR in sector ") else return Untranslated("Disable RAndR in sector ") end end function SectorSetRAndROperation:GetError() if not self.sector_id then return "Specify sector!" end end DefineClass.SectorSetRepairShopOperation = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "enable", editor = "bool", default = true, }, { id = "sector_id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, }, Documentation = "Enable/Disable Repair Shop operations in the specified sector.", EditorNestedObjCategory = "Sector effects", } function SectorSetRepairShopOperation:__exec(obj, context) gv_Sectors[self.sector_id].RepairShop = self.enable end function SectorSetRepairShopOperation:GetEditorView() if self.enable then return Untranslated("Enable Repair Shop operations in sector ") else return Untranslated("Disable Repair Shop operations in sector ") end end function SectorSetRepairShopOperation:GetError() if not self.sector_id then return "Specify sector!" end end DefineClass.SectorSetSide = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "sector_id", name = "Sector Id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, { id = "side", name = "Side", help = "Choose side for sector.", editor = "combo", default = "player1", items = function (self) return table.iappend(table.map(SideDefs, "Id"), {"dont-change"}) end, }, { id = "enable_sticky", name = "Enable Sticky Side", help = "This will forcefully set sticky side to true.", editor = "bool", default = false, }, { id = "disable_sticky", name = "Disable Sticky Side", help = "This will forcefully set sticky side to false.", editor = "bool", default = false, }, }, Documentation = "Change sector side and sticky side option (optional)", EditorNestedObjCategory = "Sector effects", } function SectorSetSide:__exec(obj, context) local sector = gv_Sectors[self.sector_id] if not sector then return end if self.enable_sticky then sector.StickySide = true elseif self.disable_sticky then sector.StickySide = false end if self.side == "dont-change" then if g_StartingCombat then return end UpdateSectorControl(self.sector_id) else SatelliteSectorSetSide(self.sector_id, self.side, "force") end end function SectorSetSide:GetEditorView() if self.enable_sticky then return Untranslated("Set side and enable sticky side in sector ") elseif self.disable_sticky then return Untranslated("Set side and disable sticky side in sector ") else return Untranslated("Set side in sector ") end end function SectorSetSide:GetError() if not self.sector_id then return "Specify sector!" elseif self.enable_sticky and self.disable_sticky then return "You cannot both enable and disable sticky side, choose one" end end DefineClass.SectorSpawnSquad = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "sector_id", name = "Sector", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, { id = "squad_def_id", name = "Squad", help = "Pre-defined enemy squad.", editor = "combo", default = false, items = function (self) return EnemySquadsComboItems("exclude test squads") end, }, { id = "side", name = "Side", editor = "combo", default = "enemy1", items = function (self) return {"enemy1", "enemy2", "ally"} end, }, }, Documentation = "Spawns a predefined squad on the sector", EditorView = Untranslated("Spawn a squad in sector "), EditorNestedObjCategory = "Sector effects", } function SectorSpawnSquad:__exec(obj, context) if not gv_Sectors[self.sector_id] then return end GenerateEnemySquad(self.squad_def_id, self.sector_id, "Effect", nil, self.side) end function SectorSpawnSquad:GetError() if not self.sector_id then return "Specify sector!" elseif not self.squad_def_id then return "Specify squad!" end end DefineClass.SectorSquadDespawn = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "sector_id", name = "Sector", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, { id = "Militia", name = "Remove militia", editor = "bool", default = true, }, { id = "Enemies", name = "Remove enemies", editor = "bool", default = true, }, }, Documentation = "Removes all enemy and militia squads in that sector.", EditorView = Untranslated("Removes enemy squads and militia in "), EditorNestedObjCategory = "Sector effects", } function SectorSquadDespawn:__exec(obj, context) local squads = GetSectorSquads(self.sector_id) for i = #squads, 1, -1 do local squad = squads[i] if self.Enemies and IsEnemySquad(squad.UniqueId) or self.Militia and squad.militia then RemoveSquad(squad) end end if not gv_SatelliteView and self.sector_id == gv_CurrentSectorId then LocalCheckUnitsMapPresence() end end function SectorSquadDespawn:GetError() if not self.sector_id then return "Specify sector!" end end DefineClass.SectorTrainMilitia = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "sector_id", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, { id = "Amount", editor = "number", default = false, default = const.Satellite.MilitiaUnitsPerTraining, min = 1, max = 8, }, }, Documentation = "Grants intel for the sector", EditorView = Untranslated("Train militia on "), EditorNestedObjCategory = "Sector effects", } function SectorTrainMilitia:__exec(obj, context) local sector = gv_Sectors[self.sector_id] local _, trained =SpawnMilitia(self.Amount, sector) if trained == 0 then return end local logText = T{769055535456, " militia dispatched to ", amount = trained, sector = sector } CombatLog("important", logText) end function SectorTrainMilitia:GetError() if not self.sector_id then return "Specify sector!" end end function SectorTrainMilitia:GetPhraseTopRolloverText(negative, template, game) local campaign = CampaignPresets[Game and Game.Campaign or DefaultCampaign] local sector = gv_Sectors and gv_Sectors[self.sector_id] or table.find_value(campaign.Sectors, "Id", self.sector_id) return T{445122419956, "Sent Militia to ", sector = sector} end DefineClass.SectorsGrantIntel = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "sector_id", help = "Sector id.", editor = "string_list", default = {}, item_default = "", items = function (self) return GetCampaignSectorsCombo() end, }, }, Documentation = "Grants intel for an array of sectors", EditorView = Untranslated("Grant intel for sectors "), EditorNestedObjCategory = "Sector effects", } function SectorsGrantIntel:__exec(obj, context) DiscoverIntelForSectors(self.sector_id) end function SectorsGrantIntel:GetError() if not self.sector_id then return "Specify sector!" end end function SectorsGrantIntel:GetPhraseTopRolloverText(negative, template, game) local campaign = CampaignPresets[Game and Game.Campaign or DefaultCampaign] local knew = {} local new = {} for i, s in ipairs(self.sector_id) do local sector = gv_Sectors and gv_Sectors[s] or table.find_value(campaign.Sectors, "Id", s) if sector then if sector.intel_discovered then knew[#knew + 1] = T{427357563595, "", SectorName = sector and sector.display_name or "",} else new[#new + 1] = T{165345603261, " (sector )", SectorName = sector and sector.display_name or "", sId = s} end end end local textCombined = false if #new > 0 then textCombined = T{309239191160, "Gained Intel for .", sectors = table.concat(new, ", ")} end if #knew > 0 then local knewText = T{124400089767, "Intel for is already available.", sectors = table.concat(knew, ", ")} if textCombined then textCombined = textCombined .. T(226690869750, "") .. knewText else textCombined = knewText end end return textCombined end DefineClass.SetBadgeEffect = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Remove", help = "Remove the badge instead.", editor = "bool", default = false, }, { id = "BadgeUnit", name = "Badge on Unit", help = "The unit/group to place the badge on, if it exists on the current map.", editor = "combo", default = false, items = function (self) return GridMarkerGroupsCombo() end, }, { id = "BadgePreset", name = "Badge Preset", help = "The badge preset to spawn.", editor = "preset_id", default = false, preset_class = "BadgePresetDef", }, { id = "Quest", name = "Quest", help = "Which quest to associate this badge with. If the effect is placed on a quest it will attempt to find it itself.", editor = "preset_id", default = false, preset_class = "QuestsDef", }, }, EditorView = T(946118376279, --[[EffectDef Effects SetBadgeEffect value]] "Place a badge of preset on unit "), Documentation = "Place a badge on a unit.", EditorNestedObjCategory = "Units", } function SetBadgeEffect:__exec(obj, context) local quest = (self.Quest and gv_Quests[self.Quest]) or (IsKindOf(obj, "QuestsDef") and obj) or false assert(quest) if not quest then return end if not self.BadgeUnit then return end local prefix = "" if self.Remove then prefix = badgeRemoveIdentifier end quest[badgeParamIdentifier .. self.BadgeUnit] = prefix .. (self.BadgePreset or "") UpdateQuestBadges(quest) end DefineClass.SetBehaviorVisitAL = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "ActorGroup", name = "Actor Group", help = "Unit group that will exit the map.", editor = "combo", default = "", items = function (self) return GetUnitGroups() end, }, { id = "MarkerGroup", name = "Marker Group", help = "Exit marker group.", editor = "combo", default = "", no_edit = function(self) return self.closest end, items = function (self) return GetALMarkersGroups end, }, { id = "Kick", name = "Kick", editor = "bool", default = true, }, }, EditorView = Untranslated(" visits marker"), Documentation = "Units vists specified Ambient Life marker", EditorNestedObjCategory = "Units", } function SetBehaviorVisitAL:__exec(obj, context) local group_actors = ValidateUnitGroupForEffectExec(self.ActorGroup, self, obj) local actors = table.ifilter(group_actors, function(_, actor) return IsKindOf(actor, "Unit") and not actor.perpetual_marker end) if #actors == 0 then return end local actor = actors[1] if not IsValid(actor) then return end local visitables = table.ifilter(g_Visitables, function(_, visitable) local marker_groups = visitable[1].Groups local from_group = not not table.find(marker_groups, self.MarkerGroup) return from_group end) if #visitables == 0 then return end local marker_visitable = visitables[1] if marker_visitable.reserved == actor.handle then return -- same unit already visiting end local marker = marker_visitable[1] if marker_visitable.reserved then if not self.Kick then return -- can't kick currently visiting unit end local unit = HandleToObject[marker_visitable.reserved] if IsValid(unit) then unit:FreeVisitable(marker_visitable) unit:SetCommand("Idle") end end actor:ReserveVisitable(marker_visitable) if marker:Random(100) < marker.ChanceSpawn then marker.perpetual_unit = actor actor.perpetual_marker = marker end if GameState and (GameState.entering_sector or GameState.setpiece_playing) and marker.Teleport then actor.teleport_allowed_once = true end actor:SetCommand("Visit", marker_visitable) end function SetBehaviorVisitAL:GetEditorView() return Untranslated("Sets to visit marker") end DefineClass.SetDeploymentModeEffect = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "DeploymentMode", name = "Deployment Mode", help = "Sector id.", editor = "combo", default = "defend", items = function (self) return {"defend", "attack"} end, }, { id = "DeploymentDir", name = "Deployment Direction", help = "Deployment Direction during attack", editor = "combo", default = "North", no_edit = function(self) return self.DeploymentMode == "defend" end, items = function (self) return {"North", "East", "South", "West"} end, }, }, Documentation = "Set Deployment Mode(and direction if attacking)r", EditorView = Untranslated("Set Deployment Mode"), } function SetDeploymentModeEffect:__exec(obj, context) SetDeploymentMode(self.DeploymentMode) if self.DeploymentMode == "attack" then gv_DeploymentDir = self.DeploymentDir end end DefineClass.SetSectorAutoResolveDefenderBonus = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "autoResolveDefenderBonus", name = "Auto Resolve Defender Bonus", editor = "number", default = 0, }, { id = "sector_id", name = "Sector Id", help = "Sector id.", editor = "combo", default = "current", items = function (self) return table.iappend({{text="current",value="current"}}, GetCampaignSectorsCombo()) end, }, }, Documentation = "Set a sector's Auto Resolve Defender Bonus.", EditorView = Untranslated("Set sector's Auto Resolve Defender Bonus to "), EditorNestedObjCategory = "Sector effects", } function SetSectorAutoResolveDefenderBonus:__exec(obj, context) local sector_id = self.sector_id == "current" and gv_CurrentSectorId or self.sector_id local sector = gv_Sectors[sector_id] sector.AutoResolveDefenderBonus = self.autoResolveDefenderBonus end DefineClass.SetSectorDiscovered = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "sector_id", name = "Sector Id", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, }, Documentation = "Sets a sector to discovered", EditorNestedObjCategory = "Sector effects", } function SetSectorDiscovered:__exec(obj, context) local sectorPreset = gv_Sectors[self.sector_id] if sectorPreset then sectorPreset.discovered = true if g_SatelliteUI then g_SatelliteUI:UpdateSectorVisuals(self.sector_id) end end end function SetSectorDiscovered:GetEditorView() return Untranslated("Set to discovered", self) end function SetSectorDiscovered:GetError() if not self.sector_id then return "Specify sector!" end end DefineClass.SetTimer = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Name", name = "Name", help = "Name of the timer.", editor = "text", default = false, }, { id = "Label", name = "Label", help = "The label to display on the timer.", editor = "text", default = false, translate = true, }, { id = "Time", name = "Time", help = "Time (in secs) to wait.", editor = "number", default = 60000, scale = "sec", }, }, EditorView = Untranslated("Set timer "), Documentation = "The effect set a visual UI timer displayed in the upper center of the screen", EditorNestedObjCategory = "UI & Log", } function SetTimer:__exec(quest, context, TCE) if not (GameState.Conflict or GameState.ConflictScripted or GameState.Combat) then -- NOTE: Combat can be started without going through Conflict first, e.g. Stealth Kill StoreErrorSource("once", "SetTimer running in a sector without Conflict/Combat!") end TimerCreate(self.Name, self.Label, self.Time) end function SetTimer:__waitexec(quest, context, TCE) self:__exec(quest, context, TCE) return TimerWait(self.Name) end function SetTimer:__skip(quest, context, TCE) return self:__exec(quest, context, TCE) end function SetTimer:GetError() if not self.Name then return "SetTimer needs a name!" end if not self.Time then return "SetTimer needs time specified!" end if GetParentTableOfKindNoCheck(self, "TestHarness") then return end local container = GetParentTableOfKind(self, "TriggeredConditionalEvent") if not container then return "SetTimer can only be used in TCEs" end if not container.SequentialEffects then return "SetTimer can be used only in TCEs with Sequential Effects execution!" end end function SetTimer:GetResumeData(thread, stack, stack_index) return "TimerWait", self.Name end DefineClass.ShowGuardpostObjective = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "GuardpostObjective", name = "GuardpostObjective", editor = "preset_id", default = false, preset_class = "GuardpostObjective", }, }, EditorView = Untranslated("Make guardpost objective visible."), Documentation = "Make a guardpost objective visible.", EditorNestedObjCategory = "Sectors", } function ShowGuardpostObjective:__exec(obj, context) SetGuardpostObjectiveSeen(self.GuardpostObjective) end DefineClass.ShowPopup = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "PopupId", help = "Popup notification preset id.", editor = "preset_id", default = false, preset_class = "PopupNotification", }, }, EditorView = Untranslated("Show popup notification "), Documentation = "Displays popup notification", EditorNestedObjCategory = "UI & Log", } function ShowPopup:GetError() if not self.PopupId then return "No PopupId" end end function ShowPopup:__exec(obj, context) local id = self.PopupId local preset = PopupNotifications[id] if not preset then StoreErrorSource(Presets.PopupNotification[1][1], "Trying to show missing pop-up: " .. id) return end if preset.OnceOnly and gv_DisabledPopups[id] then return end ShowPopupNotification(id) end function ShowPopup:__waitexec(obj, context) self:__exec(obj, context) Msg("ClosePopup" .. self.PopupId) end function ShowPopup:__skip(quest, context, TCE) return self:__exec(quest, context, TCE) end function ShowPopup:GetResumeData(thread, stack, stack_index) return "ShowPopup", self.PopupId end DefineClass.SleepEffect = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Sleep", name = "Sleep", help = "Sleep time in ms.", editor = "number", default = 1000, }, }, Documentation = "An effect to delay other effects' execution.", } function SleepEffect:GetEditorView() return T(206265192932, "Delay: ms") end function SleepEffect:__exec(obj, context) return end function SleepEffect:__waitexec(obj, context) Sleep(self.Sleep) end function SleepEffect:__skip(quest, context, TCE) end function SleepEffect:GetResumeData(thread, stack, stack_index) local remainig = GetThreadStatus(thread) - GameTime() assert(type(remainig) == "number") assert(remainig >= 0) return "Sleep", remainig end DefineClass.StartDeploymentInCurrentSector = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "WaitClicked", name = "Wait Clicked", help = "Waits for the button to be clicked before continuing to trigger next effects.", editor = "bool", default = false, }, { id = "EntranceZone", name = "EntranceZone", help = "Specific entrance zone", editor = "choice", default = "custom", items = function (self) return {"attacker", "defender", "custom"} end, }, }, Documentation = "Enter deployment mode if it is valid for the currently loaded sector", EditorView = Untranslated("Enter deployment mode"), } function StartDeploymentInCurrentSector:__exec(obj, context) if self.EntranceZone and self.EntranceZone ~= "custom" then SetDeploymentMode(self.EntranceZone) else gv_Deployment = "custom" end StartDeployment() end function StartDeploymentInCurrentSector:__waitexec(obj, context) self:__exec(obj, context) if self.WaitClicked then WaitMsg("DeploymentModeDone") end end function StartDeploymentInCurrentSector:__skip(quest, context, TCE) return self:__exec(quest, context, TCE) end function StartDeploymentInCurrentSector:GetResumeData(thread, stack, stack_index) return "StartDeploymentInCurrentSector", self.EntranceZone end DefineClass.TriggerGuardPostAttack = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "guardpost_sector_id", name = "Guardpost Sector", help = "Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, { id = "effect_target_sector_ids", name = "Target Sectors", help = "Attack one of these sectors.", editor = "string_list", default = {}, item_default = "last captured", items = function (self) return GetCampaignSectorsCombo("last captured") end, }, { id = "time", name = "Time", help = "Time to pass before spawning the squad.", editor = "number", default = 43200, scale = "h", }, { id = "custom_quest_id", name = "Custom Quest Id", help = "This could would be used in quests (ex. to check if the attack squad was defeated - with SquadDefeated condition).", editor = "text", default = false, }, { id = "reach_quest_id", name = "Reach Quest Id", help = "Quest to set variables to true when squad reaches it destination sector.", editor = "preset_id", default = false, preset_class = "QuestsDef", }, { id = "reach_quest_var", name = "Var to check", help = "Variable in reach quest to set to true when squad reaches it destination sector", editor = "set", default = false, max_items_in_set = 1, items = function (self) return table.keys2(QuestGetVariables(self.reach_quest_id), "sorted") end, }, }, Documentation = "Spawns enemy squad from guardpost after time.", EditorNestedObjCategory = "Sector effects", } function TriggerGuardPostAttack:GetEditorView() local targets = Untranslated(table.concat(self.effect_target_sector_ids, ", ")) return T{926384305749, "Spawns enemy squad from guardpost on to () after ", self, targets = targets} end function TriggerGuardPostAttack:__exec(obj, context) local gp = g_Guardposts[self.guardpost_sector_id] if not gp then StoreErrorSource(self, "TriggerGuardPostAttack - guardpost_sector_id should be sector with guardpost.") end if table.find(self.effect_target_sector_ids, "last captured") and not gv_LastSectorTakenByPlayer then return end if gp then gp:ForceSetNextSpawnTimeAndSector(self.time, self.effect_target_sector_ids, self.custom_quest_id, self.reach_quest_id, self.reach_quest_var) end end function TriggerGuardPostAttack:GetError() if not self.guardpost_sector_id then return "Specify Guardpost Sector" elseif not self.effect_target_sector_ids[1] or self.effect_target_sector_ids[1] == "" then return "Specify Target Sector!" end local sector = gv_Sectors and gv_Sectors[self.guardpost_sector_id] if sector and not sector.Guardpost then return "Sector should be a guardpost sector!" end end DefineClass.TriggerSectorWarningState = { __parents = { "Effect", }, __generated_by_class = "EffectDef", RequiredObjClasses = { "Unit", }, EditorView = Untranslated("Trigger the current sector's Warning State"), Documentation = "Triggers the Warning State for the current sector.", EditorNestedObjCategory = "Sector effects", } function TriggerSectorWarningState:__exec(obj, context) if gv_SatelliteView then return end local sector = gv_Sectors[gv_CurrentSectorId] local alliedUnits = GetPlayerSectorUnits(gv_CurrentSectorId, "getUnits") local enemyUnits = GetAllEnemyUnits(alliedUnits[1]) local triggeringUnit if IsKindOf(obj, "Unit") and table.find(alliedUnits, obj) then triggeringUnit = obj else triggeringUnit = alliedUnits[1] end EnterWarningState(enemyUnits, alliedUnits, triggeringUnit) end DefineClass.TriggerSquadAttack = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "source_sector_id", name = "Source Sector", help = "Source Sector id.", editor = "combo", default = false, items = function (self) return GetCampaignSectorsCombo() end, }, { id = "effect_target_sector_ids", name = "Target Sectors", help = "Attack one of these sectors.", editor = "string_list", default = {}, item_default = "last captured", items = function (self) return GetCampaignSectorsCombo("last captured") end, }, { id = "reach_quest_id", name = "Reach Quest Id", help = "Quest to set variables to true when squad reaches it destination sector.", editor = "preset_id", default = false, preset_class = "QuestsDef", }, { id = "reach_quest_var", name = "Var to check", help = "Variable in reach quest to set to true when squad reaches it destination sector", editor = "set", default = false, max_items_in_set = 1, items = function (self) return table.keys2(QuestGetVariables(self.reach_quest_id), "sorted") end, }, { id = "custom_quest_id", name = "Custom Quest Id", help = "This could would be used in quests (ex. to check if the attack squad was defeated - with SquadDefeated condition).", editor = "text", default = false, }, { id = "EnemySquadsList", name = "Enemy Squads List", help = "A random squad from the list will be chosen on guardpost spawn time", editor = "preset_id_list", default = {}, no_edit = function(self) return not self.Guardpost end, preset_class = "EnemySquads", preset_filter = function (preset, obj, prop_meta) if preset.group ~= "Test Encounters" then return obj end end, item_default = "", }, { id = "Squad", name = "Squad", help = "Squad that will be spawned in the sector on campaign start", editor = "combo", default = false, items = function (self) return EnemySquadsComboItems("exclude test squads") end, }, }, Documentation = "Spawns specific enemy squad after time.", EditorNestedObjCategory = "Sector effects", } function TriggerSquadAttack:GetEditorView() local targets = Untranslated(table.concat(self.effect_target_sector_ids, ", ")) return T{768343821287, "Spawns specific enemy squad to ()", self, targets = targets} end function TriggerSquadAttack:__exec(obj, context) if table.find(self.effect_target_sector_ids, "last captured") and not gv_LastSectorTakenByPlayer then return end local sector = gv_Sectors[self.source_sector_id] if not sector then return end local sector_ids = self.effect_target_sector_ids if gv_LastSectorTakenByPlayer then table.replace(sector_ids, "last captured", gv_LastSectorTakenByPlayer) end local target_sector_id = table.interaction_rand(sector_ids, "TriggerSquadAttack") --local next_squad = table.interaction_rand(sector.EnemySquadsList or empty_table, "TriggerSquadAttack") --local next_squad_units = GenerateRandEnemySquadUnits(next_squad) local squad_id = GenerateEnemySquad(self.Squad, self.source_sector_id, "TriggerSquadAttack") if squad_id then gv_CustomQuestIdToSquadId[self.custom_quest_id or self.Squad] = squad_id local squad = gv_Squads[squad_id] squad.on_reach_quest = self.reach_quest_id squad.on_reach_var = self.reach_quest_var if target_sector_id ~= self.source_sector_id then SendSatelliteSquadOnRoute(squad, target_sector_id) local timeToReach = GetTotalRouteTravelTime(squad.CurrentSector, squad.route, squad) AddTimelineEvent("squad-attack-" .. squad.UniqueId, Game.CampaignTime + timeToReach, "squad-attack", squad.UniqueId) end return squad_id end StoreErrorSource(sector, string.format("Sector '%s' does not have enemy squad '%s'", self.source_sector_id, self.Squad)) end function TriggerSquadAttack:GetError() if not self.Squad then return "Specify Squad" elseif not self.effect_target_sector_ids[1] or self.effect_target_sector_ids[1] == "" then return "Specify Target Sector!" end end DefineClass.UnitAddGrit = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "amount", name = "Amount", editor = "number", default = 30, }, }, RequiredObjClasses = { "Unit", "UnitData", }, EditorView = Untranslated("Add grit to the unit."), Documentation = "Add grit to the unit.", EditorNestedObjCategory = "Units", } function UnitAddGrit:__exec(obj, context) local unit = obj if IsKindOf(unit, "UnitData") then unit = g_Units[unit.session_id] end if IsKindOf(unit, "Unit") and not unit:IsDead() then unit:ApplyTempHitPoints(self.amount) end end DefineClass.UnitAddStatusEffect = { __parents = { "Effect", "UnitTarget", }, __generated_by_class = "EffectDef", properties = { { id = "Status", name = "Status", editor = "preset_id", default = false, preset_class = "CharacterEffectCompositeDef", }, }, RequiredObjClasses = { "Unit", }, EditorView = Untranslated("Add effect to the unit"), Documentation = "Add status effect to a unit", } function UnitAddStatusEffect:__exec(obj, context) if IsKindOf(obj, "Unit") and not obj:IsDead() then obj:AddStatusEffect(self.Status) end if not context then context = {} end local units = self:MatchMapUnits(obj, context) if units and context.target_units then for _, unit in ipairs(context.target_units) do if self.Status == "Wounded" then obj:AddWounds(1) else obj:AddStatusEffect(self.Status) end end end end function UnitAddStatusEffect:UnitCheck(unit, obj, context) return IsKindOf(unit, "Unit") and not unit:IsDead() end function UnitAddStatusEffect:GetEditorView() return T(237536962390, "Add to the unit") end DefineClass.UnitApplyAppearance = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "TargetUnit", name = "Target Unit", help = "Target unit for match.", editor = "combo", default = false, items = function (self) return GetUnitGroups() end, }, { id = "Appearance", name = "Appearance", help = "Appearance to set.", editor = "preset_id", default = false, preset_class = "AppearancePreset", }, }, EditorView = Untranslated(" changes appearance to "), Documentation = "Changes the appearance of an unit", EditorNestedObjCategory = "Units", } function UnitApplyAppearance:__exec(obj, context) local units = Groups[self.TargetUnit] or empty_table if units[1] then units[1]:ApplyAppearance(self.Appearance) end end function UnitApplyAppearance:GetError() if not self.TargetUnit then return "Choose target unit to die" end end DefineClass.UnitDie = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "TargetGroup", name = "Target Group", help = "Target group for match.", editor = "combo", default = false, items = function (self) return GetUnitGroups() end, }, { id = "skipAnim", name = "Skip Animation", editor = "bool", default = false, }, { id = "killImmortal", name = "Kill Immortal", help = "Makes units mortal and then kills them.", editor = "bool", default = false, }, }, EditorView = Untranslated("Units from group die"), Documentation = "Kill units from specified group", EditorNestedObjCategory = "Units", } function UnitDie:__exec(obj, context) local group = Groups[self.TargetGroup] or empty_table for i, v in ipairs(group) do if IsKindOf(v, "Unit") and not v:IsDead() then v.villain = false if self.killImmortal then v.immortal = false end v:SetCommand("Die", self.skipAnim) end end end function UnitDie:GetError() if not self.TargetGroup then return "Choose target unit to die" end end DefineClass.UnitEnvEffectTick = { __parents = { "UnitTarget", "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "EffectType", editor = "choice", default = "Burning", items = function (self) return {"Burning", "ToxicGas", "TearGas", "Smoke", "Darkness"} end, }, { id = "CombatMoment", editor = "choice", default = "n/a", items = function (self) return {"start turn", "end turn", "n/a"} end, }, }, EditorView = Untranslated("Trigger tick for "), Documentation = "Trigger environmental effect tick for target unit", EditorNestedObjCategory = "Units", } function UnitEnvEffectTick:__exec(obj, context) if not context then context = {} end local units = self:MatchMapUnits(obj, context) if units and context.target_units then local moment = (self.CombatMoment ~= "n/a") and self.CombatMoment or nil local func = EnvEffectBurningTick if self.EffectType == "ToxicGas" then func = EnvEffectToxicGasTick elseif self.EffectType == "TearGas" then func = EnvEffectTearGasTick elseif self.EffectType == "Smoke" then func = EnvEffectSmokeTick elseif self.EffectType == "Darkness" then func = EnvEffectDarknessTick end for _, unit in ipairs(context.target_units) do if IsValid(unit) and IsKindOf(unit, "Unit") and not unit:IsDead() then func(unit, nil, moment) end end end end function UnitEnvEffectTick:GetError() if not self.TargetUnit then return "Choose target unit" end end function UnitEnvEffectTick:UnitCheck(unit, obj, context) return true end DefineClass.UnitGrantAP = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "ap", name = "AP", help = "Action Points to Give", editor = "number", default = 8, }, }, RequiredObjClasses = { "Unit", }, EditorView = Untranslated("Grant AP to the unit"), Documentation = "Modify unit's AP", EditorNestedObjCategory = "Units", } function UnitGrantAP:__exec(obj, context) if IsKindOf(obj, "Unit") and not obj:IsDead() then if self.ap > 0 then obj:GainAP(self.ap*const.Scale.AP) else obj:ConsumeAP(self.ap*const.Scale.AP) end end end DefineClass.UnitGrantItem = { __parents = { "Effect", "LootTableFunctionObjectBase", }, __generated_by_class = "EffectDef", properties = { { id = "ItemId", name = "Item", help = "The id of the item that will be granted.", editor = "preset_id", default = false, preset_class = "InventoryItemCompositeDef", }, { id = "Amount", name = "Amount", help = "How many items are granted.", editor = "number", default = 1, min = 1, }, { id = "LootTableId", name = "Loot Table Id", help = "Loot table to generate items that will be granted.", editor = "preset_id", default = false, preset_class = "LootDef", }, { id = "GrantedItemsTextUI", editor = "text", default = false, dont_save = true, no_edit = true, translate = true, }, { id = "GrantedItemsEditorTextUI", editor = "text", default = false, dont_save = true, no_edit = true, translate = true, }, { id = "GeneratedItems", editor = "prop_table", default = false, dont_save = true, no_edit = true, }, { id = "actor", name = "Log Actor", help = "How to log the received items.", editor = "choice", default = "important", items = function (self) return {"short", "important"} end, }, }, Documentation = "Give item to a merc or to any from its squad.", EditorNestedObjCategory = "Units", } function UnitGrantItem:GetEditorView() if self.ItemId and self.LootTableId then return Untranslated("() and \'\' loot table items are given to the merc") end if self.ItemId then return Untranslated("() given to the merc") end if self.LootTableId then return Untranslated("Loot table items \'\' given to the merc") end return "" end function UnitGrantItem:__exec(obj, context) -- can work with both unit and a unit session_id local unit_id = type(obj) == "string" and obj or obj.session_id --please no SelectedObj here, this is sync local squad = gv_UnitData[unit_id] and gv_UnitData[unit_id].Squad if not squad then -- try to give to the first of player squads local squads = GetSectorSquadsFromSide(gv_CurrentSectorId, "player1") squad = #squads>0 and squads[1].UniqueId end --to any player squad assert(squad, "There is not unit or player squad on the sector") if not squad then if g_PlayerSquads and #g_PlayerSquads>0 then squad = g_PlayerSquads[1].UniqueId end end assert(squad, "There is not any player squad.The item is not given.") if not squad then return end local sector = gv_Squads[squad].CurrentSector local all_mercs = gv_Squads[squad].units if unit_id then all_mercs = table.copy(all_mercs) table.remove_entry(all_mercs, unit_id) table.insert(all_mercs, 1, unit_id) end unit_id = unit_id or all_mercs[1] -- loot table local items = UnitGrantItem.GenerateItems(self, "game") or {} CombatLogActorOverride = self.actor AddItemsToSquadBag(squad, items) for idx, merc in ipairs(all_mercs) do if #items<=0 then break end local unit = g_Units[merc] or gv_UnitData[merc] unit:AddItemsToInventory(items) end if #items > 0 then local unit = g_Units[unit_id] if unit then unit:DropItemsInContainer(items, function(unit, item, amount) CombatLog("important", T{740183432105, " Inventory full. dropped by ", amount = amount>1 and Untranslated(amount.." x ") or "", item = amount>1 and item.DisplayNamePlural or item.DisplayName, name = unit:GetDisplayName()}) end) else local stash = GetSectorInventory(sector) if stash then AddItemsToInventory(stash, items, true) end end end -- items if self.ItemId then local amount = self.Amount local item if amount > 0 then amount = AddItemToSquadBag(squad, self.ItemId, amount) end for idx, merc in ipairs(all_mercs) do if amount <= 0 then break end local unit = g_Units[merc] or gv_UnitData[merc] amount = unit:AddToInventory(self.ItemId, amount) end if amount > 0 then local unit = g_Units[unit_id] if unit then unit:DropItemContainer(self.ItemId, amount, function(unit, item, amount) CombatLog("important", T{740183432105, " Inventory full. dropped by ", amount = amount>1 and Untranslated(amount.." x ") or "", item = amount>1 and item.DisplayNamePlural or item.DisplayName, name = unit:GetDisplayName()}) end) else local stash = GetSectorInventory(sector) if stash then local itm = PlaceInventoryItem(self.ItemId) AddItemsToInventory(stash, {itm},true) end end end end self.GeneratedItems = false CombatLogActorOverride = false end function UnitGrantItem:GetError() if (not self.ItemId or self.ItemId == "") and not self.LootTableId then return "Set Item or loot table!" end end function UnitGrantItem:GetPhraseTopRolloverText(negative, template, game) local item = self.ItemId and InventoryItemDefs[self.ItemId] if item then local item_name = item.DisplayName local item_name_pl = item.DisplayNamePlural if self.ItemId == "Money" then return T{283114521154, " obtained", Amount = self.Amount} else return T{215465168779, " x obtained", Amount = self.Amount, item = self.Amount<=1 and item_name or item_name_pl} end end self:GenerateItems(game) if game then if self.GrantedItemsTextUI and self.GrantedItemsTextUI ~= "" then return T{871249882862, " obtained", items_list = self.GrantedItemsTextUI} end else if self.GrantedItemsEditorTextUI and self.GrantedItemsEditorTextUI ~= "" then return T{871249882862, " obtained", items_list = self.GrantedItemsEditorTextUI} end end end function UnitGrantItem:GenerateItems(game) if game and self.GeneratedItems then return self.GeneratedItems end -- items are already generated don't try again. if not game and self.GrantedItemsEditorTextUI then return end local items = {} local loot_tbl = LootDefs[self.LootTableId] if loot_tbl then loot_tbl:GenerateLoot(self, {}, InteractionRand(nil, "Loot"), items) end if #items > 0 then if game then self.GeneratedItems = items self.GrantedItemsTextUI = GetItemsNamesText(items) else self.GrantedItemsEditorTextUI = GetItemsNamesText(items) end end return items end function UnitGrantItem:GetPhraseFX() return "ConversationItemGained" end DefineClass.UnitJoinAsMerc = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "TargetUnit", name = "Target Unit", help = "Target unit for match.", editor = "combo", default = false, items = function (self) return GetUnitGroups() end, }, { id = "Merc", help = "The Merc to join the team.", editor = "combo", default = false, items = function (self) return MercPresetCombo() end, }, }, RequiredObjClasses = { "Unit", }, EditorView = Untranslated("Make join team as "), Documentation = "Unit joins as a specified merc.", EditorNestedObjCategory = "Units", } function UnitJoinAsMerc:__exec(obj, context) local units = Groups[self.TargetUnit] or empty_table units = table.ifilter(units, function(_, u) return IsKindOf(u, "Unit") end) -- can work with both unit and a unit session_id local unit_id = type(obj) == "string" and obj or obj.session_id --please no SelectedObj here, this is sync local squad = gv_UnitData[unit_id] and gv_UnitData[unit_id].Squad squad = squad and gv_Squads[squad] if units[1] and squad and self.Merc then units[1]:JoinSquadAs(self.Merc, squad) end end function UnitJoinAsMerc:GetError() if not self.TargetUnit then return "Choose target unit to join" end if not self.Merc then return "Choose merc as which the unit joins" end end function UnitJoinAsMerc:GetUIText(context,template, game) local merc = gv_UnitData and gv_UnitData[self.Merc] local name if not merc then name = game and "" or Untranslated("[MercName]") else name = merc.Nick or merc.Name end return T{246469241790, "Recruit merc ()", name= name} end DefineClass.UnitMakeNonVillain = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "UnitId", editor = "choice", default = false, items = function (self) return GetPersistentSessionIds() end, }, { id = "HealHP", help = "whether to also heal the unit to full", editor = "bool", default = false, }, }, EditorView = Untranslated("Make non villain."), Documentation = "Make all instances of the specified persistant villain unit non-villain (killable)", EditorNestedObjCategory = "Units", } function UnitMakeNonVillain:__exec(obj, context) local units = g_PersistentUnitData[self.UnitId] for i, u in ipairs(units) do MakeUnitNonVillain(u) -- Heal unit if enabled and exists, dont use -- standard heal logic to make it invisible. local mapUnit = g_Units[u.session_id] if mapUnit and self.HealHP then mapUnit.HitPoints = mapUnit.Health end end end DefineClass.UnitSetConflictIgnore = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "ConflictIgnore", name = "Conflict Ignore", help = "Whether to be afraid in conflicts or not.", editor = "bool", default = true, }, { id = "TargetUnit", name = "Target Unit", help = "Target unit for match.", editor = "combo", default = false, items = function (self) return GetUnitGroups() end, }, }, EditorView = Untranslated("Set ConflictIgnore to truefalse."), Documentation = "Sets units to be affraid(or not) during confict.", EditorNestedObjCategory = "Units", } function UnitSetConflictIgnore:__exec(obj, context) local objects = Groups[self.TargetUnit] or empty_table for _, obj in ipairs(objects) do if IsKindOf(obj, "Unit") then obj.conflict_ignore = self.ConflictIgnore end end end function UnitSetConflictIgnore:GetError() if not self.TargetUnit then return "Choose TargetUnit!" end end DefineClass.UnitSetHireStatus = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Status", name = "Hire Status", help = "The hiring status to set.", editor = "choice", default = "Available", items = function (self) return PresetGroupCombo("MercHireStatus", "Default") end, }, { id = "TargetUnit", name = "Target Unit", help = "Target unit for match.", editor = "combo", default = false, items = function (self) return GetTargetUnitCombo() end, }, }, EditorView = Untranslated("Set to status "), Documentation = "Sets the hiring status of the merc to a specified value.", EditorNestedObjCategory = "Units", } function UnitSetHireStatus:__exec(obj, context) if not self.Status then return false end local unit = UnitDataDefs[self.TargetUnit] local unitData = gv_UnitData[unit.id] if not unitData then return end unitData.HireStatus = self.Status end function UnitSetHireStatus:GetError() if not self.Status then return "Choose unit hiring status to set!" end end DefineClass.UnitSetOnline = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Online", name = "Online", help = "Whether to set it online or offline.", editor = "bool", default = true, }, { id = "TargetUnit", name = "Target Unit", help = "Target unit for match.", editor = "combo", default = false, items = function (self) return GetTargetUnitCombo() end, }, }, EditorView = Untranslated("Set messenger availability to "), Documentation = "Sets a merc's online/offline status in the PDA messenger. When offline mercs cannot be hired.", EditorNestedObjCategory = "Units", } function UnitSetOnline:__exec(obj, context) local unit = UnitDataDefs[self.TargetUnit] local unitData = gv_UnitData[unit.id] if not unitData then return end unitData:SetMessengerOnline(self.Online) end function UnitSetOnline:GetError() if not self.TargetUnit then return "Choose targetunit!" end end DefineClass.UnitSetStatusEffectImmunity = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Status", name = "Status", editor = "preset_id", default = false, preset_class = "CharacterEffectCompositeDef", }, { id = "Immune", editor = "bool", default = false, }, { id = "Reason", help = "Text label used for keeping track of the different reasons a unit can have for being immune to the specific status effect. All reasons must be cleared in order for the unit to be able to receive the effect again.", editor = "text", default = "script", }, }, RequiredObjClasses = { "Unit", }, ReturnClass = "", EditorView = Untranslated(""), Documentation = "Add or remove immunity to specified status effect to a target unit", EditorNestedObjCategory = "", } function UnitSetStatusEffectImmunity:GetEditorView() if self.Immune then return Untranslated("Make unit immune to ") end return Untranslated("Clear immunity to from unit") end function UnitSetStatusEffectImmunity:__exec(obj, context) if self.Immune then obj:AddStatusEffectImmunity(self.Status, self.Reason) else obj:RemoveStatusEffectImmunity(self.Status, self.Reason) end end DefineClass.UnitStartConversation = { __parents = { "Effect", "ConversationFunctionObjectBase", }, __generated_by_class = "EffectDef", properties = { { id = "Conversation", name = "Conversation", help = "Conversation to start.", editor = "preset_id", default = false, preset_class = "Conversation", }, }, EditorView = Untranslated("Start conversation ."), Documentation = "Starts a specific conversation - no groups or conditions are checked.", EditorNestedObjCategory = "", EditorNestedObjCategory = "Interactions", } function UnitStartConversation:__exec(obj, context) StartConversationEffect(self.Conversation, context) end function UnitStartConversation:__waitexec(obj, context) StartConversationEffect(self.Conversation, context, "wait") end function UnitStartConversation:GetError() if not self.Conversation then return "Please specify conversation" end end function UnitStartConversation:new(...) -- backward compatibility local ret = Effect.new(self, ...) if rawget(ret, "Group") then ret.Conversation = ret.Group ret.Group = nil end return ret end function UnitStartConversation:GetResumeData(thread, stack, stack_index) return "UnitStartConversation", self.Conversation end DefineClass.UnitStatBoost = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Stat", name = "Stat", editor = "combo", default = false, items = function (self) return GetUnitStatsCombo() end, }, { id = "Amount", name = "Amount", help = "How much to boost the specified Stat.", editor = "number", default = false, }, { id = "source", name = "Source", help = "What kind of source is the thing that provides the stat boost.", editor = "combo", default = "Book", items = function (self) return {"Book", "Other"} end, }, }, RequiredObjClasses = { "Unit", }, EditorView = Untranslated("Boost unit's stat "), Documentation = "Boosts unit's stat", EditorNestedObjCategory = "Units", } function UnitStatBoost:__exec(obj, context) if not obj.is_clone then local modId if self.source == "Book" then modId = string.format("StatBoostBook-%s-%s-%d", self.Stat, obj.session_id, GetPreciseTicks()) GainStat(obj, self.Stat, self.Amount, modId, "Studying") else modId = string.format("StatBoost-%s-%s-%d", self.Stat, obj.session_id, GetPreciseTicks()) GainStat(obj, self.Stat, self.Amount, modId) end end end function UnitStatBoost:GetError() if not self.Stat then return "Choose stat" elseif not self.Amount then return "Choose amount" end end DefineClass.UnitTakeDamage = { __parents = { "Effect", "UnitTarget", }, __generated_by_class = "EffectDef", properties = { { id = "Damage", editor = "number", default = 5, min = 0, }, { id = "FloatingText", help = "Will only be displayed if the target unit is visible for the player; parameter will be provided for the text.", editor = "text", default = T(477545213542, --[[EffectDef Effects UnitTakeDamage default]] ""), translate = true, }, { id = "LogMessage", help = " and parameters will be provided for the text.", editor = "text", default = T(879191059352, --[[EffectDef Effects UnitTakeDamage default]] " takes damage."), translate = true, }, }, RequiredObjClasses = { "Unit", }, EditorView = Untranslated("Deal damage to the unit"), Documentation = "Deal damage to the unit", } function UnitTakeDamage:__exec(obj, context) if not context then context = {} end local units = self:MatchMapUnits(obj, context) if units and context.target_units then local floating_text = T{self.FloatingText, Damage = self.Damage} local pov_team = GetPoVTeam() for _, unit in ipairs(context.target_units) do if not unit:IsDead() then local has_visibility = HasVisibilityTo(pov_team, obj) local log_msg = T{self.LogMessage, { Name = unit:GetLogName(), Damage = self.Damage}} unit:TakeDirectDamage(self.Damage, has_visibility and floating_text or false, "short", log_msg) end end end end function UnitTakeDamage:UnitCheck(unit, obj, context) return true end DefineClass.UnitTakeItem = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "ItemId", name = "Item", help = "The id of the item that will be taken.", editor = "preset_id", default = false, preset_class = "InventoryItemCompositeDef", }, { id = "Amount", name = "Amount", help = "How many items to take.", editor = "number", default = 1, min = 1, }, { id = "AnySquad", name = "Any player squad", help = "if true: takes item from any player's squad. if false: takes item from current squad", editor = "bool", default = false, }, { id = "AddToLogVar", name = "Add to log", help = "Add message to log.", editor = "bool", default = true, }, }, EditorView = Untranslated("Take () from merc(s) in squad."), Documentation = "Removes the first given item from the inventory of a merc in the squad.", EditorNestedObjCategory = "Units", } function UnitTakeItem:AddToLog(unit, item, amount) if self.AddToLogVar then local unit_name if type(unit)=="number" then unit_name = g_Classes.SquadBag.DisplayName else unit_name = unit:GetDisplayName() end CombatLog("short", T{795253111432, " x taken from ", amount = amount>=1 and amount or "", item = amount>1 and item.DisplayNamePlural or item.DisplayName, name = unit_name}) end end function UnitTakeItem:__exec(obj, context) -- can work with both unit and a unit session_id local unit_id = false if type(obj) == "string" then unit_id = obj elseif IsKindOf(obj, "Unit") then unit_id = obj.session_id end local all_mercs = false local squad = unit_id and gv_UnitData[unit_id] and gv_UnitData[unit_id].Squad if squad then all_mercs = table.copy(gv_Squads[squad].units) if self.AnySquad then local side = gv_Squads[squad].Side if side=="player1" then table.iappend(all_mercs,GetAllPlayerUnitsOnMapSessionId()) end for sq_id, sqd in pairs(gv_Squads) do if sq_id~=squad and (sqd.Side ==side) then table.iappend(all_mercs, sqd.units ) end end all_mercs = table.get_unique(all_mercs) end table.remove_entry(all_mercs, unit_id) table.insert(all_mercs, 1, unit_id) -- prioritize unit_id else all_mercs = GetAllPlayerUnitsOnMapSessionId() end TakeItemFromMercs(all_mercs, self.ItemId, self.Amount, function(unit, item, unit_amount, effect) effect:AddToLog(unit, item, unit_amount) end, self) InventoryUIRespawn() end function UnitTakeItem:GetError() if not self.ItemId then return "Set Item!" end end function UnitTakeItem:GetUIText(context, template, game) local item_name = InventoryItemDefs[self.ItemId].DisplayName local item_name_pl = InventoryItemDefs[self.ItemId].DisplayNamePlural local unit_id = ConversationGetPlayerMerc() local has_item = HasItemInSquad(unit_id, self.ItemId, self.Amount) if self.ItemId=="Money" then return has_item and T{304179156341, "Give ", Amount = self.Amount} or T{231000532062, " required", Amount = self.Amount} end if template then return T{template, Amount = self.Amount, item = self.Amount<=1 and item_name or item_name_pl} else return has_item and T{590581690149, "Give ",Amount =self.Amount, item = self.Amount<=1 and item_name or item_name_pl} or T{635806839384, " required",Amount =self.Amount, item = self.Amount<=1 and item_name or item_name_pl} end end function UnitTakeItem:GetPhraseTopRolloverText(negative, template, game) local item_name = InventoryItemDefs[self.ItemId].DisplayName local item_name_pl = InventoryItemDefs[self.ItemId].DisplayNamePlural if self.ItemId=="Money" then return T{792302629761, "Delivered ",Amount = self.Amount} else return T{652398655206, "Delivered x ",Amount = self.Amount, item = self.Amount<=1 and item_name or item_name_pl} end end DefineClass.UnitsAddToAmbientLife = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Group", name = "Group", help = "Units Group to add to Ambient Life.", editor = "combo", default = false, items = function (self) return GetUnitGroups() end, }, }, EditorView = Untranslated("Add objects of group to Ambient Zone"), Documentation = "Add objects of group to Ambient Zone marker's responsibilities.", } function UnitsAddToAmbientLife:GetError() if not self.Group then return "Choose Units Group!" end end function UnitsAddToAmbientLife:__exec(obj, context) local group_units = Groups[self.Group] or empty_table if #group_units == 0 then return end local zone MapForEach("map", "AmbientZoneMarker", function(marker) zone = marker return false end) if zone then zone:RegisterUnits(table.ifilter(group_units, function(i, u) return IsKindOf(u, "Unit") end)) end end DefineClass.UnitsDespawnAmbientLife = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Ephemeral", name = "Ephemeral Only", help = "All ambient life units or just ephemeral ones.", editor = "bool", default = true, }, }, EditorView = Untranslated("Despawn all Ambient Life Units"), Documentation = "Despawn all ambient units - abrupt, don't wait for exit animations.", } function UnitsDespawnAmbientLife:__exec(obj, context) MapForEach("map", "AmbientZoneMarker", function(zone) for _, units in ipairs(zone.units) do for idx = #units, 1, -1 do local unit = units[idx] if not self.Ephemeral or unit.ephemeral then table.remove(units, idx) DoneObject(unit) end end end end) end DefineClass.UnitsKickAmbientLife = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "AL_Group", name = "AL Group", help = "Target unit for match.", editor = "combo", default = false, items = function (self) return GetALMarkerGroups() end, }, }, EditorView = Untranslated("Kicks Ambient Life group from Their Markers"), Documentation = "Forces all units in ambient life behavior visiting specific group cancel their visit behavior", EditorNestedObjCategory = "Units", } function UnitsKickAmbientLife:__exec(obj, context) for _, unit in ipairs(g_Units) do if IsVisitingUnit(unit) then if unit.last_visit:IsInGroup(self.AL_Group) then unit:SetBehavior() unit:SetCommand("Idle") unit.perpetual_marker = false end end end end function UnitsKickAmbientLife:GetError() if not self.AL_Group then return "Choose group of AL markers to kick units from" end end function UnitsKickAmbientLife:GetUIText(context,template, game) local merc = gv_UnitData and gv_UnitData[self.Merc] local name if not merc then name = game and "" or Untranslated("[MercName]") else name = merc.Nick or merc.Name end return T{246469241790, "Recruit merc ()", name= name} end DefineClass.UnitsKickFromPerpetualMarkers = { __parents = { "Effect", }, __generated_by_class = "EffectDef", properties = { { id = "Ephemeral", name = "Ephemeral Only", help = "All ambient life units or just ephemeral ones.", editor = "bool", default = true, }, }, EditorView = Untranslated("Kick Units from Perpetual Markers"), Documentation = "Kicks all Ambient Life units which are in perpetual markers", } function UnitsKickFromPerpetualMarkers:__exec(obj, context) for _, unit in ipairs(g_Units) do if (not self.Ephemeral or unit.ephemeral) and unit.perpetual_marker then unit:SetBehavior() unit:SetCommand("Idle") unit.perpetual_marker = false end end end DefineClass.UnitsStealForPerpetualMarkers = { __parents = { "Effect", }, __generated_by_class = "EffectDef", EditorView = Untranslated("Steal Units for Perpetual Markers"), Documentation = "Steal units for perpetual markers as if the Ambient Life is respawn", } function UnitsStealForPerpetualMarkers:__exec(obj, context) AmbientLifePerpetualMarkersSteal() end DefineClass.UpdateInteractablesHighlight = { __parents = { "Effect", }, __generated_by_class = "EffectDef", Documentation = "Disable interaction markers of a specific group.", EditorNestedObjCategory = "Interactable", } function UpdateInteractablesHighlight:__exec(obj, context) if CurrentMap == "" or IsChangingMap() then return end if not g_Units then return end local ui_units = {} for _, unit in ipairs(g_Units) do if IsValid(unit) and not unit:IsDead() and unit.team and unit.team.control == "UI" then table.insert(ui_units, unit) end end if #ui_units == 0 then return end MapForEach("map", "Interactable", function(interactable) if not IsValid(interactable) then return end for _, unit in ipairs(ui_units) do if not UICanInteractWith(unit, interactable) then interactable:HighlightIntensely(false, "unit-nearby") end end if not (IsKindOf(interactable, "ContainerMarker") and interactable:IsMarkerEnabled()) then interactable:HighlightIntensely(false, "cursor") interactable:HighlightIntensely(false, "unit-nearby") end end) end function UpdateInteractablesHighlight:GetEditorView() return T(680362306074, "Forces update of all highlighted interactables") end DefineClass.WaitNpcIdle = { __parents = { "Effect", "UnitTarget", }, __generated_by_class = "EffectDef", properties = { { id = "TargetUnit", name = "NPC Name", help = "The name of the NPC to associate.", editor = "combo", default = false, items = function (self) return GetUnitGroups() end, }, }, EditorView = Untranslated("Wait for the NPC to become idle"), Documentation = "Wait for the NPC to become idle (requires sequential execution)", EditorNestedObjCategory = "Units", } function WaitNpcIdle:__exec(obj, context) assert(false) -- This effect requires sequential execution end function WaitNpcIdle:__waitexec(obj, context) local ctx = {} local units = self:MatchMapUnits(obj, ctx) local firstUnit = units and ctx.target_units firstUnit = firstUnit and firstUnit[1] if firstUnit then WaitIdle(firstUnit) end end function WaitNpcIdle:GetResumeData() return "WaitNpcIdle", self.TargetUnit end function WaitNpcIdle:GetError() if not self.TargetUnit then return "No target unit" end end