Jump to content

Players can stuck on void


KXWl1
  • DLC VERSION - IMPORTANT!: Vanilla - NO DLC, Reign of Giants, Shipwrecked, Hamlet Pending

I fixed this bug by adding a component (to player_common) which is similar to keeponland in DLC0002/DLC0003. The followings are useful code that prevent players from stucking into impassable tile:

local Drownable = Class(function(self, inst)
    self.inst = inst
	
	inst:AddTag("drownable")
	
    self.inst:StartUpdatingComponent(self)
end)

local function IsHole(x,y,z)
    return GetGroundTypeAtPosition(Vector3(x,y,z)) == GROUND.IMPASSABLE
end

local function IsWater(x,y,z)
    if IsDLCEnabled(CAPY_DLC) or IsDLCEnabled(PORKLAND_DLC)	then
        return GetWorld().Map:IsWater(GetWorld().Map:GetTileAtPoint(x, y, z))
	else
	    return GetGroundTypeAtPosition(Vector3(x,y,z)) == GROUND.INVALID
	end
end

local function IsPointNearWater(x,y,z)
    local radius = 3

    for i = -radius, radius, 1 do
	    if IsDLCEnabled(CAPY_DLC) or IsDLCEnabled(PORKLAND_DLC)	then
            if IsWater(x - radius, y, z + i) or IsWater(x + radius, y, z + i) then
                return true
            end
		end
        if IsHole(x - radius, y, z + i) or IsHole(x + radius, y, z + i) then
            return true
        end		
    end
	
    for i = -(radius - 1), radius - 1, 1 do
	    if IsDLCEnabled(CAPY_DLC) or IsDLCEnabled(PORKLAND_DLC)	then
            if IsWater(x + i, y, z - radius) or IsWater(x + i, y, z + radius) then
                return true
            end
		end
        if IsHole(x + i, y, z - radius) or IsHole(x + i, y, z + radius) then
            return true
        end
    end
	
    return false
end

local function FindLandBetweenPoints(p0x, p0y, p1x, p1y)
	local map = GetWorld().Map

	local dx = math.abs(p1x - p0x)
	local dy = math.abs(p1y - p0y)

    local ix = p0x < p1x and TILE_SCALE or -TILE_SCALE
    local iy = p0y < p1y and TILE_SCALE or -TILE_SCALE

    local e = 0;
    for i = 0, dx+dy - 1 do
        local tile_at_point = map:GetTileAtPoint(p0x, 0, p0y)
        if not IsPointNearWater(p0x, 0, p0y) then
			return map:GetTileCenterPoint(p0x, 0, p0y)
		end

        local e1 = e + dy
        local e2 = e - dx
        if math.abs(e1) < math.abs(e2) then
            p0x = p0x + ix
            e = e1
		else
            p0y = p0y + iy
            e = e2
        end
	end

	return nil
end

local function VecUtil_LengthSq(p1_x, p1_z)
    return p1_x * p1_x + p1_z * p1_z
end

local function FindRandomPointOnShoreFromOcean(x, y, z)
	local nodes = {}

    for i, node in ipairs(GetWorld().topology.nodes) do
		if node.type ~= NODE_TYPE.Blank and node.type ~= NODE_TYPE.Blocker then
			table.insert(nodes, {n = node, distsq = VecUtil_LengthSq(x - node.x, z - node.y)})
		end
	end
	table.sort(nodes, function(a, b) return a.distsq < b.distsq end)

	local num_rooms_to_pick = 4

	local closest = {}
	for i = 1, num_rooms_to_pick do
		table.insert(closest, nodes[i])
	end
	shuffleArray(closest)

	local dest_x, dest_y, dest_z
	for _, c in ipairs(closest) do
		dest_x, dest_y, dest_z = FindLandBetweenPoints(x, z, c.n.x, c.n.y)
		if dest_x ~= nil and TheSim:WorldPointInPoly(dest_x, dest_z, c.n.poly) then
			return dest_x, dest_y, dest_z
		end
	end

	for i = num_rooms_to_pick + 1, #nodes do
		local c = nodes[i]
		if c ~= nil then
			dest_x, dest_y, dest_z = FindLandBetweenPoints(x, z, c.n.x, c.n.y)
			if dest_x ~= nil and TheSim:WorldPointInPoly(dest_x, dest_z, c.n.poly) then
				return dest_x, dest_y, dest_z
			end
		end
	end

	return nil
end

function Drownable:OnUpdate(dt)
    local world = GetWorld()
    local function testfn(offset)
        local test_point = self.inst:GetPosition() + offset
        local tx, ty = world.Map:GetTileCoordsAtPoint(test_point.x, test_point.y, test_point.z)
        local actual_tile = world.Map:GetTile(tx, ty)
        return actual_tile ~= GROUND.IMPASSABLE
    end
    if not (self.inst.sg ~= nil and self.inst.sg:HasStateTag("busy")) and (self.inst.components.health == nil or not self.inst.components.health:IsInvincible()) then  --god mode void enterable
        local pt = self.inst:GetPosition()
        local radius = 1.75 --buffer zone because the walls aren't perfecly along the visual line 
        local result_offset = FindValidPositionByFan(0, radius, 12, testfn)

        local OnOcean = result_offset == nil 

        if OnOcean then 
            pt = self.inst:GetPosition()
            radius = 5 
            result_offset = FindValidPositionByFan(0, radius, 12, testfn)

            if result_offset then 
                local moveto = pt + result_offset
                self.inst.Transform:SetPosition(moveto.x, moveto.y, moveto.z)
            else
                if not CHEATS_ENABLED then
				    local px, py, pz = self.inst.Transform:GetWorldPosition()
		            local sx, sy, sz = FindRandomPointOnShoreFromOcean(px, py, pz)
			        if sx ~= nil then
                        self.inst.Transform:SetPosition(sx, sy, sz)
			        end
                end
            end 
        end 
    end 
end

return Drownable

 


Steps to Reproduce

1. c_speed(10)

2. try to go to that impassable area

3. you will get randomly go into it

4. now you can walk on the impassable and breaks the game balance.

5. This can also happen when tent are built too near impassable tile.




User Feedback


Clipping onto the void isn't really a problem. In Hamlet, Klei made it so walking near the land's edge again (from the void) auto-teleports you back in bounds. So people aren't getting "stuck" out of bounds. They can walk back in at any time. Why add extra preventative fixes for something that can already be fixed easily by players?

If someone wants to clip out of bounds, let them. I've had times where the game required me to clip out of bounds. (A chapter in Adventure Mode once spawned the Wooden Thing on an island, and it didn't generate a wormhole. Thankfully, the world had Moleworms, so I clipped out of bounds, and clipped back in.) Even recently it's been useful for harvesting the Grassy Turf in Adventure Mode Epilogue, if your RNG is horrible, and the world only gives you one viable Telestaff location. (Which happened to me.)

  • Thanks 1

Share this comment


Link to comment
Share on other sites



Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
  • Create New...