mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-17 19:24:01 +02:00
feat(mods): expose Fly and Softboiled field actions
This commit is contained in:
+2
-16
@@ -628,22 +628,8 @@ function PartyMenu:update(dt)
|
||||
end
|
||||
if self.softboiledFrom then
|
||||
local user = party[self.softboiledFrom]
|
||||
local heal = math.floor(user.stats.hp / 5)
|
||||
if mon == user or mon.hp <= 0 or mon.hp >= mon.stats.hp
|
||||
or user.hp <= heal then
|
||||
self.softboiledFrom = nil
|
||||
local TextBox = require("src.render.TextBox")
|
||||
self.game.stack:push(TextBox.new(self.game, Strings("It won't have\nany effect.")))
|
||||
else
|
||||
user.hp = user.hp - heal
|
||||
mon.hp = math.min(mon.stats.hp, mon.hp + heal)
|
||||
self.softboiledFrom = nil
|
||||
require("src.core.Sound").play(self.game.data, "Heal_HP")
|
||||
local def = self.game.data.pokemon[mon.species]
|
||||
local TextBox = require("src.render.TextBox")
|
||||
self.game.stack:push(TextBox.new(self.game,
|
||||
Strings("%s's HP\nwas restored!", mon.nickname or def.name)))
|
||||
end
|
||||
self.softboiledFrom = nil
|
||||
self.game.overworld:useSoftboiledFieldMove(user, mon)
|
||||
elseif self.swapFrom then
|
||||
if self.swapFrom ~= self.index then
|
||||
party[self.swapFrom], party[self.index] = party[self.index], party[self.swapFrom]
|
||||
|
||||
@@ -827,6 +827,23 @@ function OverworldState:useStrengthFieldMove(mon, onClose)
|
||||
return true
|
||||
end
|
||||
|
||||
function OverworldState:useSoftboiledFieldMove(user, target)
|
||||
local heal = user and user.stats and math.floor(user.stats.hp / 5) or 0
|
||||
if not user or not user.stats or not target or not target.stats
|
||||
or target == user or target.hp <= 0
|
||||
or target.hp >= target.stats.hp or user.hp <= heal then
|
||||
Game.stack:push(TextBox.new(Game, Strings("It won't have\nany effect.")))
|
||||
return false
|
||||
end
|
||||
user.hp = user.hp - heal
|
||||
target.hp = math.min(target.stats.hp, target.hp + heal)
|
||||
require("src.core.Sound").play(Game.data, "Heal_HP")
|
||||
local def = Game.data.pokemon[target.species]
|
||||
Game.stack:push(TextBox.new(Game,
|
||||
Strings("%s's HP\nwas restored!", target.nickname or def.name)))
|
||||
return true
|
||||
end
|
||||
|
||||
-- The battle transition's dungeon wipe uses the explicit map lists in
|
||||
-- data/maps/dungeon_maps.asm (field.dungeonTransitionMaps): singles plus
|
||||
-- inclusive map-id ranges -- faithful to the original's omissions
|
||||
|
||||
+91
-2
@@ -37,6 +37,57 @@ local function validPartySlot(party, slot)
|
||||
and party[slot] ~= nil
|
||||
end
|
||||
|
||||
local function outside(game, ow)
|
||||
return Map.isOutside(ow.map.def,
|
||||
FieldDefaults.field(game.data, "outsideTilesets"))
|
||||
end
|
||||
|
||||
local function knows(mon, moveId)
|
||||
for _, move in ipairs(mon.moves or {}) do
|
||||
if move.id == moveId then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function monInfo(game, mon, slot)
|
||||
local def = game.data.pokemon[mon.species] or {}
|
||||
return { slot = slot, species = mon.species,
|
||||
name = mon.nickname or def.name or mon.species, level = mon.level,
|
||||
hp = mon.hp, maxHp = mon.stats and mon.stats.hp or mon.hp }
|
||||
end
|
||||
|
||||
local function softboiledSources(game)
|
||||
local party, sources = game.save.party or {}, {}
|
||||
for sourceSlot, source in ipairs(party) do
|
||||
local heal = source.stats and math.floor(source.stats.hp / 5) or 0
|
||||
if knows(source, "SOFTBOILED") and source.hp > heal then
|
||||
local info = monInfo(game, source, sourceSlot)
|
||||
info.targets = {}
|
||||
for targetSlot, target in ipairs(party) do
|
||||
if target ~= source and target.hp > 0 and target.stats
|
||||
and target.hp < target.stats.hp then
|
||||
info.targets[#info.targets + 1] = monInfo(game, target, targetSlot)
|
||||
end
|
||||
end
|
||||
if #info.targets > 0 then sources[#sources + 1] = info end
|
||||
end
|
||||
end
|
||||
return sources
|
||||
end
|
||||
|
||||
local function flyDestinationAvailable(game, mapId)
|
||||
local field, save = game.data.field or {}, game.save
|
||||
for _, id in ipairs(field.flyOrder or {}) do
|
||||
if id == mapId then
|
||||
local def = game.data.maps and game.data.maps[id]
|
||||
return not not (save.visited and save.visited[id]
|
||||
and field.flyWarps and field.flyWarps[id]
|
||||
and def and Map.isFlyTown(def))
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function WorldAPI.new(game, modId)
|
||||
return setmetatable({ game = game, modId = modId }, WorldAPI)
|
||||
end
|
||||
@@ -141,10 +192,14 @@ function WorldAPI:availableFieldActions()
|
||||
and ow:partyKnows("DIG") then
|
||||
out[#out + 1] = { id = "dig", label = "DIG" }
|
||||
end
|
||||
if ow:partyKnows("TELEPORT") and Map.isOutside(ow.map.def,
|
||||
FieldDefaults.field(game.data, "outsideTilesets")) then
|
||||
if ow:partyKnows("TELEPORT") and outside(game, ow) then
|
||||
out[#out + 1] = { id = "teleport", label = "TELEPORT" }
|
||||
end
|
||||
local sources = softboiledSources(game)
|
||||
if #sources > 0 then
|
||||
out[#out + 1] = { id = "softboiled", label = "SOFTBOILED",
|
||||
sources = sources }
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
@@ -187,10 +242,44 @@ function WorldAPI:useFieldAction(id, opts)
|
||||
elseif id == "dig" or id == "teleport" then
|
||||
ow:beginTeleportOut()
|
||||
return true
|
||||
elseif id == "softboiled" then
|
||||
local sourceSlot = opts and tonumber(opts.sourceSlot)
|
||||
local targetSlot = opts and tonumber(opts.targetSlot)
|
||||
local allowed
|
||||
for _, source in ipairs(found.sources or {}) do
|
||||
if source.slot == sourceSlot then
|
||||
for _, target in ipairs(source.targets or {}) do
|
||||
if target.slot == targetSlot then allowed = true break end
|
||||
end
|
||||
end
|
||||
end
|
||||
if not allowed then return nil, "softboiled target unavailable" end
|
||||
if ow:useSoftboiledFieldMove(game.save.party[sourceSlot],
|
||||
game.save.party[targetSlot]) then return true end
|
||||
end
|
||||
return nil, "field action unavailable"
|
||||
end
|
||||
|
||||
-- FLY needs a destination choice, so it is exposed separately from the
|
||||
-- immediate actions above. The request is still checked against the same
|
||||
-- visited-town list as the native Town Map picker before the world may warp.
|
||||
function WorldAPI:canFly()
|
||||
local game, ow = self.game, self:overworld()
|
||||
return not not (ow and ow.map and outside(game, ow) and ow:partyKnows("FLY"))
|
||||
end
|
||||
|
||||
function WorldAPI:flyTo(mapId)
|
||||
local game, ow = self.game, self:overworld()
|
||||
if not ow then return nil, NO_OVERWORLD end
|
||||
if not self:canFly() then return nil, "fly unavailable" end
|
||||
if not acceptsMenuInput(game, ow) then return nil, "world is busy" end
|
||||
if not flyDestinationAvailable(game, mapId) then
|
||||
return nil, "destination unavailable"
|
||||
end
|
||||
ow:flyTo(mapId)
|
||||
return true
|
||||
end
|
||||
|
||||
-- A compact, read-only view of the active map for minimaps and companion UIs.
|
||||
-- `rows` describes collision terrain; optional `tileRows` reduces each real
|
||||
-- 8x8 map tile to its average Game Boy shade ("0" lightest, "3" darkest).
|
||||
|
||||
Reference in New Issue
Block a user