-- LUA Script - precede every function and global member with lowercase name of script + '_main' -- Default script - does nothing. g_pickuppable_dist = 30 g_pickuppable_list = {} g_pickuppable_name = {} g_player_carrying = false g_key_pressed = false g_box_parameters = {["box1"] = {height = 16, width = 20, length = 20}, ["box2"] = {height = 16, width = 20, length = 30}, ["box3"] = {height = 21, width = 25, length = 50}, ["Pot"] = {height = 10, width = 10, length = 10}, ["Teapot"] = {height = 10, width = 10, length = 10}}; function pickuppable_init_name(e, name) g_pickuppable_name[e] = name end function pickuppable_PlayerLookingAt (e, dist, fov) local Ent = g_Entity[e] if Ent == nil then return false end dist = dist or 3000 fov = fov or 1 local name = g_pickuppable_name[e] local HH = g_box_parameters[name].height / 2 local XA = math.rad(GetEntityAngleX (e)) local ZA = math.rad(GetEntityAngleZ (e)) local Cx = (Ent.x - math.sin(XA) * HH) local Cz = (Ent.z + math.sin(ZA) * HH) local Cy = (Ent.y + math.cos(XA) * HH) local Dx = Cx - g_PlayerPosX local Dz = Cz - g_PlayerPosZ local Dy = Cy - (g_PlayerPosY + 30) local sqr_dist = (Dx*Dx + Dy*Dy + Dz*Dz) if sqr_dist > dist*dist then return false end local function limitAngle (Angle) if Angle <= 0 then Angle = 360 + Angle elseif Angle > 360 then Angle = Angle - 360 end return Angle end local angleY = limitAngle(math.atan2(Dx, Dz) * (180.0 / math.pi)) local pAng = g_PlayerAngY while pAng < 0 or pAng > 360 do if pAng <= 0 then pAng = 360 + pAng elseif pAng > 360 then pAng = pAng - 360 end end local L = limitAngle(angleY - fov / 2) local R = limitAngle(angleY + fov / 2) if L < R and (pAng > L and pAng < R) or L > R and (pAng > L or pAng < R) then local PlayerVertAngle = g_PlayerAngX -- Put this in because the angle appears to be behaving strangely -- Up goes from 360 -> 280, Down goes 0 -> 85! if PlayerVertAngle < 280 then PlayerVertAngle = -PlayerVertAngle else PlayerVertAngle = 2 * (360 - PlayerVertAngle) end local EntAng = math.deg(math.asin(Dy/math.sqrt(sqr_dist))) return math.abs(EntAng - PlayerVertAngle) < fov / 2 else return false end end function pickuppable_main(e) if g_pickuppable_list[e] == nil then g_pickuppable_list[e] = {picked_up = false, stackedon = nil, stackAng = 0, Sx = 0, Sz = 0, SFP = true, SA = 0, SD = 0, x = 0, y = 0, z = 0, xr = nil, yr = nil, zr = nil}; g_key_pressed = false return end local PlistEnt = g_pickuppable_list[e] local Ent = g_Entity[e] if PlistEnt.picked_up then local Px, Py, Pz = g_PlayerPosX, g_PlayerPosY, g_PlayerPosZ local Ay = math.rad(g_PlayerAngY) local PlayerVertAngle = g_PlayerAngX local dist = g_pickuppable_dist local offsetx, offsetz = dist * math.sin(Ay), dist * math.cos(Ay) -- Put this in because the angle appears to be behaving strangely -- Up goes from 360 -> 280, Down goes 0 -> 85! if PlayerVertAngle < 280 then PlayerVertAngle = -PlayerVertAngle else PlayerVertAngle = 2 * (360 - PlayerVertAngle) end offsetx = offsetx + dist * math.sin(Ay) offsetz = offsetz + dist * math.cos(Ay) PlistEnt.x = Px + offsetx PlistEnt.y = Py + 15 + PlayerVertAngle PlistEnt.z = Pz + offsetz -- make sure we don't sink the entity into the terrain local TH = GetTerrainHeight(PlistEnt.x, PlistEnt.z) if PlistEnt.y < TH then PlistEnt.y = TH + 1 end if PlistEnt.xr == nil then PlistEnt.xr = 0 -- GetEntityAngleX (e) PlistEnt.yr = GetEntityAngleY (e) - math.deg(Ay) PlistEnt.zr = 0 -- GetEntityAngleZ (e) end CollisionOff(e) SetPosition(e, PlistEnt.x, PlistEnt.y, PlistEnt.z) SetRotation(e, PlistEnt.xr, math.deg(math.rad(PlistEnt.yr) + Ay), PlistEnt.zr) handle_stacking(e, Px, Pz, PlistEnt.y, Ay) CollisionOn(e) local stackEnt = nil local is_overlapping, Xo, Zo = false, 0, 0 for k, _ in pairs(g_pickuppable_list) do if k ~= e then is_overlapping, Xo, Zo = pickuppable_is_overlapping(e, k) if is_overlapping then stackEnt = k break elseif PlistEnt.stackedon == k then PlistEnt.stackedon = nil end end end if stackEnt then Prompt("Press x to stack") else Prompt("Press x to drop") end if not g_key_pressed then if g_InKey == "x" then g_key_pressed = true PlistEnt.picked_up = false PlistEnt.stackedon = stackEnt if PlistEnt.stackedon ~= nil then local SEnt = g_pickuppable_list[stackEnt] PlistEnt.Sx = Xo PlistEnt.Sz = Zo PlistEnt.SFP = true end PlistEnt.stackAng = 0 g_player_carrying = false PlistEnt.xr = nil end end if g_InKey == "," then PlistEnt.yr = PlistEnt.yr - 1 if PlistEnt.yr < 0 then PlistEnt.yr = PlistEnt.yr + 360 end setFPandAng(e, -1) elseif g_InKey == "." then PlistEnt.yr = PlistEnt.yr + 1 if PlistEnt.yr > 360 then PlistEnt.yr = PlistEnt.yr - 360 end setFPandAng(e, 1) elseif g_InKey == "6" then PlistEnt.xr = PlistEnt.xr - 1 if PlistEnt.xr < 0 then PlistEnt.xr = PlistEnt.xr + 360 end elseif g_InKey == "7" then PlistEnt.xr = PlistEnt.xr + 1 if PlistEnt.xr > 360 then PlistEnt.xr = PlistEnt.xr - 360 end elseif g_InKey == "8" then PlistEnt.zr = PlistEnt.zr - 1 if PlistEnt.zr < 0 then PlistEnt.zr = PlistEnt.zr + 360 end elseif g_InKey == "9" then PlistEnt.zr = PlistEnt.zr + 1 if PlistEnt.zr > 360 then PlistEnt.zr = PlistEnt.zr - 360 end end elseif not g_player_carrying then if pickuppable_PlayerLookingAt (e, 150, 20) then Prompt("Press x to pick up " .. g_pickuppable_name[e]) if g_InKey == "x" and not g_key_pressed then g_key_pressed = true PlistEnt.picked_up = true PlistEnt.x = Ent.x PlistEnt.z = Ent.z PlistEnt.stackedon = nil g_player_carrying = true end end end if g_InKey ~= "x" then g_key_pressed = false end PromptLocal(e, "Ent=" .. e .. " YA=" .. math.modf(GetEntityAngleY (e)) .. " SO=" .. (PlistEnt.stackedon or 0)) end function setFPandAng(e, AInc) for k, _ in pairs(g_pickuppable_list) do local PEnt = g_pickuppable_list[k] if PEnt.stackedon == e then PEnt.yr = PEnt.yr + AInc if PEnt.yr < 0 then PEnt.yr = PEnt.yr + 360 elseif PEnt.yr > 360 then PEnt.yr = PEnt.yr - 360 end PEnt.SFP = true setFPandAng(k, AInc) end end end function handle_stacking(e, Px, Pz, Y, Ay) local SEnt = g_pickuppable_list[e] for k, _ in pairs(g_pickuppable_list) do local PEnt = g_pickuppable_list[k] if PEnt.stackedon == e then if PEnt.SFP then PEnt.x = SEnt.x + PEnt.Sx PEnt.z = SEnt.z + PEnt.Sz local Dx, Dz = PEnt.x - Px, PEnt.z - Pz local Ang = math.atan2(Dx, Dz) PEnt.SA, PEnt.SD = Ang - Ay, math.sqrt(Dx*Dx+Dz*Dz) PEnt.SFP = false end local offsetx, offsetz = PEnt.SD * math.sin(Ay + PEnt.SA), PEnt.SD * math.cos(Ay + PEnt.SA) local name = g_pickuppable_name[e] PEnt.x = Px + offsetx PEnt.z = Pz + offsetz PEnt.y = Y + g_box_parameters[name].height CollisionOff(k) SetPosition(k, PEnt.x, PEnt.y, PEnt.z) SetRotation(k, PEnt.xr, math.deg(math.rad(PEnt.yr) + Ay), PEnt.zr) handle_stacking(k, Px, Pz, PEnt.y, Ay) CollisionOn(k) end end end function pickuppable_is_overlapping(e1, e2) -- Check the entity is level first if math.modf(GetEntityAngleX (e2)) ~= 0 or math.modf(GetEntityAngleZ (e2)) ~= 0 then return false end; local name = g_pickuppable_name[e2] local H = g_box_parameters[name].height local HW = g_box_parameters[name].width / 2 local HL = g_box_parameters[name].length / 2 local Ent1 = g_Entity[e1] local Ent2 = g_Entity[e2] local DX, DY, DZ = Ent1.x - Ent2.x, Ent1.y - Ent2.y, Ent1.z - Ent2.z local ang1 = math.rad(Ent2.angley) local ang2 = math.atan(DX, DZ) local L = math.sqrt(DX*DX + DZ*DZ) local NDX = math.sin(ang2 - ang1) * L local NDZ = math.cos(ang2 - ang1) * L return (DY > H and DY < H * 1.5 and NDZ > -HL and NDZ < HL and NDX > -HW and NDX < HW), DX, DZ end