-
Notifications
You must be signed in to change notification settings - Fork 18
Capabilities
I want to:
- Prevent casting Teleport and Create Wall in a specific map.
- Prevent casting Teleport if it's raining.
First I'd make some capability, ISkillPrevention
.
local ISkillPrevention = class.interface("ISkillPrevention", {
can_use_skill = "function",
set_can_use_skill = "function",
},
{ ICapability })
return ISkillPrevention
Then I'd make some default implementation for this capability.
local SkillPrevention = class.class("SkillPrevention", { ISkillPrevention })
function SkillPrevention:init()
self.skills_enabled = {}
end
function SkillPrevention:can_use_skill(skill_id)
if not data["base.skill"][skill_id] then
return true
end
local enabled = self.skills_enabled[skill_id]
if enabled == nil
return true
end
return enabled
end
function SkillPrevention:set_can_use_skill(skill_id, enabled)
if not data["base.skill"][skill_id] then
return
end
if enabled == nil then
enabled = true
end
self.skills_enabled[skill_id] = not not enabled
end
return SkillPrevention
In the mod that defines this class, I'd have to register the default capability implementation for ISkillPrevention
so the runtime can instantiate it when chara:get_or_create_capability(ISkillPrevention)
gets called.
--- init.lua
local Capability = require("api.Capability")
local ISkillPrevention = require("mod.skill_prevention.api.ISkillPrevention")
local SkillPrevention = require("mod.skill_prevention.api.SkillPrevention")
Capability.set_default_implementation(ISkillPrevention, SkillPrevention)
Then, inside the map's archetype, I'd add a callback for base.on_map_set_archetype
that sets up the capability.
function my_map.on_set_archetype(map)
local skill_prevention = map:get_or_create_capability(ISkillPrevention)
skill_prevention:set_can_use_skill("elona.teleport", false)
skill_prevention:set_can_use_skill("elona.wall_creation", false)
end
Or maybe we could use some kind of extension system to specify the skills inside the map_archetype
entry itself...
data:add {
_type = "base.map_archetype",
_id = "my_map",
_ext = {
skill_prevention = {
disabled_skills = {
"elona.teleport",
"elona.wall_creation"
}
}
}
}
...and then bind to base.on_set_map_archetype
globally for the setup.
local function set_skill_preventions(map)
local _ext = map:archetype()._ext.skill_prevention
if _ext and _ext.disabled_skills then
local skill_prevention = map:get_or_create_capability(ISkillPrevention)
for _, skill_id in ipairs(_ext.disabled_skills)
skill_prevention:set_can_use_skill(skill_id, false)
end
end
end
Event.register("base.on_set_map_archetype", "Set skill preventions", set_skill_preventions)
Finally, to implement the logic, I'd bind to elona_sys.before_cast_magic
and check if the skill cannot be cast.
local function proc_magic_prevention(caster, params, result)
local map = caster:current_map()
local skill_prevention = map:get_capability(ISkillPrevention)
if skill_prevention and if not skill_prevention:can_use_skill(params.magic_id) then
Gui.mes("skill_prevention:cast.prevented")
return { blocked = true, turn_result = "turn_end" }
end
return result
end
Event.register("elona_sys.before_cast_magic", "Proc magic prevention", proc_magic_prevention)
The part where Create Wall is prevented based on the weather doesn't use the capability, only an event hook.
local function proc_create_wall_prevention(caster, params, result)
if Weather.is_raining() and params.magic_id == "elona.wall_creation" then
Gui.mes("skill_prevention:cast.prevented_create_wall")
return { blocked = true, turn_result = "turn_end" }
end
end
Event.register("elona_sys.before_cast_magic", "Prevent Create Wall if raining", proc_create_wall_prevention)
Ultimately, because it's important to notify the player of the reason the teleport was prevented if it's raining, an event callback is necessary to show the specific message. Because of this, I think it makes more sense to just return { blocked = true }
in the elona_sys.before_cast_magic
event along with displaying the message, instead of using the capability.