-
Notifications
You must be signed in to change notification settings - Fork 0
Examples
Each example below is a separate main.lua. Use its own mod folder and a manifest with hostOnly: true. Reload from Pause > Lua Mods after edits.
local catches = tonumber(htf.get_data("catches", "0")) or 0
htf.on("fish_hooked", function(fish_name)
catches = catches + 1
htf.set_data("catches", tostring(catches))
htf.log("Catch " .. catches .. ": " .. fish_name)
end)
htf.button("Show catches", function()
htf.chat("Crew catches: " .. catches)
end)Counts rod attachments observed by the host. The stored count survives reloading scripts and restarting the game. It is shared across worlds on this installation, not per player.
htf.on("boss_killed", function(boss_name)
if htf.money(100) then
htf.chat("Defeated " .. boss_name .. ". Crew reward: $100.")
end
end)Mini-bosses also trigger this event. Environmental deaths may count. The callback does not receive killer information.
htf.command("crew", function(args)
local message = table.concat(args, " ")
if message == "" then
htf.log("Usage: /crew message")
return
end
htf.chat(string.sub(message, 1, 120))
end)The host types /crew Return to the boat. Command arguments are split at spaces; quotation marks do not group words. Keep the manifest display name short so the message and its prefix fit the 200-character limit.
htf.on("player_joined", function(player_name, steam_id)
htf.log("Joined: " .. player_name .. " (" .. steam_id .. ")")
htf.after(2, function()
if htf.is_host() then
htf.chat("Welcome, " .. string.sub(player_name, 1, 40))
end
end)
end)This is a broadcast, not a private message. The roster event includes the host and players already present on the first poll. It does not guarantee the named player is still connected two seconds later.
htf.every(300, function()
htf.chat("Crew notice: check your equipment before leaving.")
end)Timers use unscaled time and include time spent paused. Host-only timers wait until hosting. Repeating timers resume with one callback, without replaying all missed intervals. Reloading clears and recreates the timer.
htf.on("fish_sold", function(item, seller)
local name = seller and seller.name or "Unknown"
htf.log(name .. " sold " .. item.name .. " for $" .. item.worth)
end)The seller is the last holder, not guaranteed to be the person who originally caught the fish. A shared-money grant does not trigger this event. The sale is already being processed; returning false cannot stop it.
htf.button("Heal crew", function()
for _, player in ipairs(htf.players.list()) do
if player.steam_id ~= "0" then
htf.players.heal(player.steam_id, 100)
end
end
end)This button restores up to 100 health through the host's native game API. It does not revive dead players. Client installations cannot use it to heal themselves on somebody else's server.
htf.on("player_died", function(player)
if not player or player.steam_id == "0" then return end
local key = "deaths_" .. player.steam_id
local deaths = (tonumber(htf.get_data(key, "0")) or 0) + 1
htf.set_data(key, tostring(deaths))
end)Counts only deaths observed while this script is loaded. Config data persists across this installation's worlds. Use a new per-session Lua table instead when you do not want persistent totals.
htf.on("island_loaded", function(index)
local spawn = htf.world.spawn_position()
if spawn then
htf.log("Island " .. index .. " spawn: " .. spawn.x .. ", " .. spawn.y .. ", " .. spawn.z)
end
end)Island indices are zero-based. The event observes host load completion, not every remote client's loading state. A newly queued island change may mean spawn_position() is already nil by delivery time.
The package's optional examples/crew-tools combines sale logging, saved death counts, heal/feed buttons, /luacrew, and /luareturn steam_id. The return command verifies a connected Steam ID before sending the current spawn position. Install the folder under mods with its included manifest. It requires framework 0.2.0 and does not run unless you install the example.
Copy examples/world-tools under BepInEx/plugins/HowToLua/mods/, reload, and host a spare test world. The script registers commands and buttons without changing gameplay on load.
-
/luacatalogwrites definition IDs, names and spawn eligibility to the BepInEx log. -
/luaspawn definition_idspawns one eligible item at the host's position offset by +3 world-X and +1 world-Y. It does not aim at the crosshair or check terrain clearance. Use an ID from the catalog, not a network instance ID. - Show boat status broadcasts the current motor index and radar state.
- Save world requests a native save and reports acceptance/refusal in the log. Acceptance does not prove successful disk persistence.
- Boat motor changes and NPC quest progression are logged while the example is active.
Spawning respects the framework's shared 8-per-second/128-live-item limits and excludes protected prefabs. Keep only one installed folder with this example's manifest ID.
htf.command("baitreward", function(args)
local id = tonumber(args[1])
if not id or id ~= math.floor(id) or id < 1 or id > 255 then
htf.log("Usage: /baitreward bait_id")
return
end
for _, player in ipairs(htf.players.list()) do
if player.steam_id ~= "0" then
htf.inventory.give_bait(player.steam_id, id, 1)
end
end
end)This is a free native grant, not a shop purchase. Unknown/unavailable bait IDs return false. Inspect htf.catalog.baits() for IDs; bait zero is the default and cannot be granted as stock. See Gameplay API before adding costs or progression conditions.