G1R:MP Forum Alpha
Log in Join
Gothic 1 Remake Multiplayer

The Colony Forum

News, support, development discussions, server communities, and stories from beyond the Barrier.

Colony discussion

no fall damage script.

Started by asvra in Scripts

  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
09-07-2026, 12:34 PM (This post was last modified: 09-07-2026, 12:36 PM by asvra.)
#1
NO FALL DAMAGE - G1R:MP (server API 0.1.2 / Protocol 31+)


    Works in two layers:
      1. The engine stat "resistance_falling" is set to STAT_VALUE when a player
        spawns, after every revive and refreshed every REFRESH_MS (the engine may
        overwrite the stat when it loads the character).
      2. Fallback: if damage of a DAMAGE_TYPES kind with no attacker still arrives
        (that is how the engine reports a fall), health is restored to the value
        it had before the hit.

<?xml version="1.0" encoding="utf-8"?>
<scripts>
    <script src="server.lua" type="server" />
</scripts>

Server Script
Quote:-- === CONFIG ==============================================================
local ENABLED_BY_DEFAULT = true          -- every player from the moment they join
local STAT_VALUE        = 100000        -- resistance_falling; 0 = normal fall damage
local REFRESH_MS        = 15000        -- stat refresh interval (ms)
local DAMAGE_TYPES      = {environment = true, fall = true, falling = true}  -- damage kinds reported as a fall
local RESTORE_HEALTH    = true          -- fallback: undo fall damage by restoring health
local LOG                = false        -- outputDebugString on every restore
local ADMIN_PERMISSION  = "admin.world" -- /nofall on|off: permission checked through the gothic_rp bridge
-- =========================================================================

local enabled = {}      -- playerId -> true/false (nil = default)
local previous = {}      -- playerId -> stat value before enabling (restored on disable/stop)

local function isEnabled(playerId)
    if enabled[playerId] == nil then return ENABLED_BY_DEFAULT end
    return enabled[playerId] == true
end

local function applyStat(playerId)
    if not isPlayerConnected(playerId) then return false end
    if isEnabled(playerId) then
        if previous[playerId] == nil then
            previous[playerId] = tonumber(getPlayerStat(playerId, "resistance_falling")) or 0
        end
        return setPlayerStat(playerId, "resistance_falling", STAT_VALUE) ~= false
    elseif previous[playerId] ~= nil then
        setPlayerStat(playerId, "resistance_falling", previous[playerId])
        previous[playerId] = nil
    end
    return true
end

-- 1) the stat: on spawn, on revive, periodic refresh
addEventHandler("onPlayerSpawn", root, function(playerId)
    setTimer(function() applyStat(playerId) end, 500, 1)  -- the engine may still be loading stats right after spawn
end)
addEventHandler("onPlayerRevived", root, function(playerId)
    setTimer(function() applyStat(playerId) end, 500, 1)
end)
setTimer(function()
    for _, playerId in ipairs(getPlayers()) do applyStat(playerId) end
end, REFRESH_MS, 0)

-- 2) fallback: restore health after fall damage (no attacker)
addEventHandler("onPlayerDamage", root, function(playerId, damage, healthAfter, attackerId, damageType)
    if not RESTORE_HEALTH or not isEnabled(playerId) then return end
    local kind = tostring(damageType or ""):lower()
    if not DAMAGE_TYPES[kind] then return end
    local attacker = tonumber(attackerId)
    if attacker and attacker >= 0 then return end  -- hit by someone or something - not a fall
    damage = tonumber(damage) or 0
    healthAfter = tonumber(healthAfter) or 0
    if damage <= 0 or healthAfter <= 0 then return end  -- death cannot be undone here
    local restored = math.min(healthAfter + damage, tonumber(getPlayerMaxHealth(playerId)) or (healthAfter + damage))
    setPlayerHealth(playerId, restored)
    if LOG then
        outputDebugString(("[nofall] player=%s restored %d fall damage (%s)"):format(tostring(playerId), damage, kind), 3)
    end
end)

-- API for other resources
addEvent("nofall:set", false)
addEventHandler("nofall:set", root, function(playerId, state)
    playerId = tonumber(playerId)
    if not playerId then return end
    enabled[playerId] = state ~= false
    applyStat(playerId)
end)

addEventHandler("onPlayerQuit", root, function(playerId)
    enabled[playerId] = nil
    previous[playerId] = nil
end)

-- Admin command: permission via gothic_rp's synchronous event bridge (permission_bridge.lua);
-- refused when gothic_rp is not running.
local bridgeGranted = false
addEvent("g1r:permissionSeen", false)
addEvent("g1r:permissionGrant", false)
addEventHandler("g1r:permissionGrant", root, function() bridgeGranted = true end)
local function isWorldAdmin(playerId)
    bridgeGranted = false
    triggerEvent("g1r:permissionQuery", root, playerId, ADMIN_PERMISSION)
    return bridgeGranted
end
addCommandHandler("nofall", function(playerId, commandName, mode)
    if not isWorldAdmin(playerId) then
        outputChatBox("No permission.", playerId, 230, 90, 90, false)
        return
    end
    mode = tostring(mode or ""):lower()
    if mode == "on" then enabled[playerId] = true elseif mode == "off" then enabled[playerId] = false end
    applyStat(playerId)
    outputChatBox(("No fall damage: %s (/nofall on|off)."):format(isEnabled(playerId) and "ENABLED" or "disabled"), playerId, 216, 185, 120, false)
end)

addEventHandler("onResourceStart", resourceRoot, function()
    for _, playerId in ipairs(getPlayers()) do applyStat(playerId) end
    outputDebugString(("[nofall] start: default %s, resistance_falling=%d, restore health=%s"):format(
        ENABLED_BY_DEFAULT and "enabled" or "disabled", STAT_VALUE, tostring(RESTORE_HEALTH)), 3)
end)
addEventHandler("onResourceStop", resourceRoot, function()
    for _, playerId in ipairs(getPlayers()) do
        if previous[playerId] ~= nil then setPlayerStat(playerId, "resistance_falling", previous[playerId]) end
    end
end)
Forum Jump: