mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
87 lines
3.0 KiB
Lua
87 lines
3.0 KiB
Lua
-- Gallery #5 (Mechanic designer): a new battle mechanic with no engine
|
|
-- fork. A rain field effect scales WATER and FIRE damage, lives behind an
|
|
-- opt-in ruleset, and keeps its own counter in mod.save.
|
|
--
|
|
-- The parity lesson is the ruleset gate: install this mod, leave the
|
|
-- ruleset on gen1_faithful, and every battle is byte-for-byte vanilla
|
|
-- because the hook returns next(...) untouched.
|
|
local RULESET = "example_weather_battles"
|
|
local RAIN_TURNS = 5
|
|
|
|
local BOOST = { WATER = 1.5 }
|
|
local DAMPEN = { FIRE = 0.5 }
|
|
|
|
return function(mod)
|
|
-- ------- the field effect, declared as a status record
|
|
|
|
mod.content.statuses:register("EXAMPLE_RAIN", {
|
|
id = "EXAMPLE_RAIN",
|
|
label = "RAIN",
|
|
hudLabel = "RAIN",
|
|
-- a field effect is never inflicted on a battler; the record is the
|
|
-- declaration the HUD and other mods read, the hook is the behavior
|
|
canInflict = function() return false end,
|
|
})
|
|
|
|
-- ------- the ruleset that turns it on
|
|
-- Read the vanilla record out of the merged registry rather than
|
|
-- requiring the module: same table, no engine_internals permission.
|
|
|
|
local base = mod.content.rulesets:get("gen1_faithful")
|
|
if not base then
|
|
mod.log:error("gen1_faithful missing from the rulesets registry; "
|
|
.. "another mod removed it, so %s cannot be derived", RULESET)
|
|
return
|
|
end
|
|
local weather = {}
|
|
for key, value in pairs(base) do weather[key] = value end
|
|
weather.name = "WEATHER"
|
|
-- the marker the damage hook gates on; unknown fields ride through the
|
|
-- schema untouched, which is what makes rulesets extensible
|
|
weather.exampleWeather = true
|
|
mod.content.rulesets:register(RULESET, weather)
|
|
|
|
-- ------- rain lifecycle, in this mod's own save namespace
|
|
|
|
local function raining()
|
|
return (mod.save:get("turnsLeft", 0)) > 0
|
|
end
|
|
|
|
mod.events:on("battle.started", function(ev)
|
|
local ruleset = ev.battle and ev.battle.ruleset
|
|
if ruleset and ruleset.exampleWeather then
|
|
mod.save:set("turnsLeft", RAIN_TURNS)
|
|
else
|
|
mod.save:set("turnsLeft", 0)
|
|
end
|
|
end)
|
|
|
|
mod.events:on("battle.turn_started", function()
|
|
local left = mod.save:get("turnsLeft", 0)
|
|
if left > 0 then mod.save:set("turnsLeft", left - 1) end
|
|
end)
|
|
|
|
mod.events:on("battle.ended", function()
|
|
mod.save:set("turnsLeft", 0)
|
|
end)
|
|
|
|
-- ------- the one behavior change
|
|
|
|
mod.hooks:wrap("battle.damage", function(next, ctx)
|
|
-- the two gates, cheapest first: the player has to have picked the
|
|
-- ruleset, and it has to still be raining
|
|
if not (ctx.ruleset and ctx.ruleset.exampleWeather and raining()) then
|
|
return next(ctx)
|
|
end
|
|
local moveType = ctx.move and ctx.move.type
|
|
local scale = BOOST[moveType] or DAMPEN[moveType]
|
|
if not scale then return next(ctx) end
|
|
|
|
-- Damage.compute returns (damage, info); pass the second value through
|
|
-- untouched or the crit and type-effectiveness flags vanish
|
|
local damage, info = next(ctx)
|
|
if type(damage) ~= "number" then return damage, info end
|
|
return math.max(1, math.floor(damage * scale)), info
|
|
end)
|
|
end
|