I've just managed to sort out a way of slowing down the player and stopping the jumping so now if you are carrying something and hold down space bar you can get more accurate placement but yes I think I need to maybe have a decal or something positioned below the centre of the object being carried. The only downside to that is it then requires a more complicated set up than simply adding the script to every item you want to manipulate as it is now.
Btw the script allows the height, distance from player and left to right offset to be set in variables, in fact almost everything is easily configurable.
Here is the bit of script that determines whether one pickuppable is sitting on another:
local RayCast = IntersectAll
-- given angles and origin position return centre position
local function getCentre( p, x, y, z, xa, ya, za )
-- first rotate offsets
local pdo = dimensions[ p.obj ].offsets
local xo, yo, zo = U.Rotate3D( pdo.x, pdo.y, pdo.z, xa, ya, za )
-- now calculate centre of object
return x + xo, y + yo, z + zo
end
local function checkIfSatOn( pb, bpx, bpy, bpz, bax, bay, baz,
pt, tpx, tpy, tpz, tax, tay, taz )
-- can't be stacked if its origin is lower than the base
-- objects origin no matter what the relative orientations are
if tpy < bpy then return false end
-- now calculate centre of object
local tcx, tcy, tcz = getCentre( pt, tpx, tpy, tpz, tax, tay, taz )
local lowestY = tcy - ( dimensions[ pt.obj ].h / 2 + 5 )
-- now cast rays from around centre point to try and find the base object
local bObj = RayCast( tcx, tcy, tcz, tcx, lowestY , tcz, pt.obj )
if bObj ~= pb.obj then
bObj = RayCast( tcx + 1, tcy, tcz + 1, tcx + 1, lowestY, tcz + 1, pt.obj )
end
if bObj ~= pb.obj then
bObj = RayCast( tcx + 1, tcy, tcz - 1, tcx + 1, lowestY, tcz - 1, pt.obj )
end
if bObj ~= pb.obj then
bObj = RayCast( tcx - 1, tcy, tcz + 1, tcx + 1, lowestY, tcz + 1, pt.obj )
end
if bObj ~= pb.obj then
bObj = RayCast( tcx - 1, tcy, tcz - 1, tcx + 1, lowestY, tcz - 1, pt.obj )
end
if bObj ~= pb.obj then return false end
local q1 = Q.Conjugate( Q.FromEuler( bax, bay, baz ) )
local xo, yo, zo = U.Rotate3D( tpx - bpx, tpy - bpy, tpz - bpz, Q.ToEuler( q1 ) )
return true, xo, yo, zo, Q.Mul( q1 , Q.FromEuler( tax, tay, taz ) )
end
Here is the bit of the script that repositions the 'stack' of items:
local function repositionStack( p, nquat, nx, ny, nz)
if p.stackList ~= nil then
-- quaternion of base item
local xa, ya, za = Q.ToEuler( nquat )
for k, v in pairs( p.stackList ) do
-- calculate relative rotation
local pquat = Q.Mul( nquat, v.q )
-- rotate centre offsets to match new angle
local xo, yo, zo = U.Rotate3D( v.x, v.y, v.z, xa, ya, za )
local px, py, pz = nx + xo, ny + yo, nz + zo
local nxa, nya, nza = Q.ToEuler( pquat )
CollisionOff( k )
PositionObject( v.obj, px, py, pz )
RotateObject( v.obj, deg(nxa), deg(nya), deg(nza) )
CollisionOn( k )
repositionStack( pEnt[ k ], pquat, px, py, pz )
end
end
end
Just to give you some idea of how complex the thing is, and yes it is recursive!
And here are the user configurable variables:
---------------------
-- editable values --
local maxWeight = 400 -- default maximum weight of pickuppable or stack of pickuppables
local maxSize = 800 -- default maximum size of pickuppable
local minSpeed = 0.15 -- speed of movement at max weight
local carryOfst = 0 -- x offset of carrying position \
local carryHght = 25 -- y offset of carrying position | relative to player
local carryDist = 33 -- z offset of carrying position /
local maxStack = 50 -- maximum stack height
local levelAng = 15 -- number of degrees off horizontal still considered
-- as 'level' for stacking purposes
---------------------
And finally the global functions that can be called from other scripts:
-- ================================= --
-- start of global functions section --
-- ================================= --
-- global functions to read/set max weight we can lift
function PU_GetMaxCarryWeight()
function PU_SetMaxCarryWeight( value )
-- global functions to read/set max size of object we can pick up
function PU_GetMaxCarrySize()
function PU_SetMaxCarrySize( value )
-- stops entity specified from being 'pickuppable'
function PU_RemoveEntity( e )
-- returns nil or entity id of item being carried
function PU_GetEntityCarried()
-- returns nil or entity id of last dropped item
function PU_LastDropped()
-- returns empty list or list of all carried items
function PU_GetCarriedList()
-- returns a list of carried items by name
function PU_CarriedNames()
-- creates a copy of the current state for save/load purposes
function PU_SnapshotGlobalList()
-- ======================= --
Can you think of anything I've missed out?
Been there, done that, got all the T-Shirts!