Skuggasveinn wrote: ↑Sat May 25, 2019 6:49 pmWasn't there someone that had found a way to make the party move up an elevation in a smooth way ?
That was me, but importantly, my approach
does not work with items or monsters on the slope:
Code: Select all
-- Slopes occupy their own tile, the two tiles in front of them, and the tiles
-- directly above those tiles. For example, if you have a slope at 10,10 with
-- a facing of 0 and elevation of 0, it occupies the tiles 10,10; 10,9; and
-- 10,8 at elevations 0 and 1. None of these six tiles should be walls.
--
-- Do not overlap slopes (remembering that they occupy six tiles). You can use
-- slopes above and below each other as long as the difference between their
-- elevations is at least 2.
-- Do not allow slopes to be entered from the side at their bottom elevation,
-- unless it is from another, aligned slope, as this will cause an ugly camera
-- snap. Letting players enter from the slope's top elevation is fine.
-- Slopes can be moved.
local slopeComponent = {
class = "Timer",
name = "slope",
timerInterval = 0,
currentLevelOnly = true,
onInit = function(self)
local g = self.go
local dx,dy = getForward(g.facing)
spawn("slope_platform_icb",g.level,g.x,g.y,0,g.elevation,g.id.."plat1")
spawn("slope_platform_icb",g.level,g.x+dx,g.y+dy,0,g.elevation,g.id.."plat2")
spawn("slope_platform",g.level,g.x+dx*2,g.y+dy*2,0,g.elevation+1,g.id.."plat3")
end,
onActivate = function(self)
local g = self.go
local p = party
local dx,dy = getForward(g.facing)
if (p.x == g.x or p.x == g.x+dx) and (p.y == g.y or p.y == g.y+dy) and (p.elevation >= g.elevation) then
local ppos = p:getWorldPosition()
local gpos = g:getWorldPosition()
local y
if g.facing == 0 then
y=(ppos[3]-gpos[3])
elseif g.facing == 1 then
y=(ppos[1]-gpos[1])
elseif g.facing == 2 then
y=(gpos[3]-ppos[3])
else -- g.facing == 3
y=(gpos[1]-ppos[1])
end
y = ((y+1.5)/6+g.elevation)*g.map:getModuleHeight()
local plat1 = findEntity(g.id.."plat1")
-- no need to check for elevation, the worldPositionY set will take care of that
if plat1.x ~= g.x or plat1.y ~= g.y or plat1.facing ~= g.facing or plat1.level ~= g.level then
plat1:setPosition(g.x,g.y,g.facing,g.elevation,g.level)
end
plat1:setWorldPositionY(y)
local plat2 = findEntity(g.id.."plat2")
-- no need to check for elevation, the worldPositionY set will take care of that
if plat2.x ~= g.x+dx or plat2.y ~= g.y+dy or plat2.facing ~= g.facing or plat2.level ~= g.level then
plat2:setPosition(g.x+dx,g.y+dy,g.facing,g.elevation,g.level)
end
plat2:setWorldPositionY(y)
-- stops the party from falling through the platform after moving
-- upwards, and from briefly entering the falling state after moving downwards
local mdir = party.script.getMovementDirection()
if not mdir or mdir%2 == g.facing%2 then
if party.elevation <= g.elevation+1 and not party.party:isFalling() then
party:setWorldPositionY(y)
end
else -- hack that lets party walk between multiple adjacent aligned slopes...
local dx2,dy2 = getForward(mdir)
for e in g.map:entitiesAt(p.x+dx2,p.y+dy2) do
if e.name == "slope_platform_icb" then
e:setWorldPositionY(y)
end
end
end
elseif p.x == g.x+dx*2 and p.y == g.y+dy*2 and p.elevation == g.elevation then
-- finish moving the party to the upper elevation
p:setWorldPositionY(g:getWorldPosition()[2]+g.map:getModuleHeight())
elseif p.x == g.x-dx and p.y == g.y-dy and p.elevation == g.elevation then
-- prepare platform for party entering the slope so that their position
-- doesn't snap upwards for one frame. this is not needed for entering
-- the slope from the top because the platform is below them (at worst)
-- in that case.
local plat1 = findEntity(g.id.."plat1")
-- no need to check for elevation, the worldPositionY set will take care of that
if plat1.x ~= g.x or plat1.y ~= g.y or plat1.facing ~= g.facing or plat1.level ~= g.level then
plat1:setPosition(g.x,g.y,g.facing,g.elevation,g.level)
end
plat1:setWorldPositionY(party:getWorldPosition()[2])
local plat2 = findEntity(g.id.."plat2")
-- no need to check for elevation, the worldPositionY set will take care of that
if plat2.x ~= g.x+dx or plat2.y ~= g.y+dy or plat2.facing ~= g.facing or plat2.level ~= g.level then
plat2:setPosition(g.x+dx,g.y+dy,g.facing,g.elevation,g.level)
end
end
end,
}
defineObject{
name = "slope_platform",
placement = "floor",
components = {
{
class = "Platform"
}
}
}
-- Because of how ItemConstrainBoxes are checked, there needs to be one in both
-- squares of the slope, otherwise you can put items on the slope from the top
-- or bottom.
defineObject{
name = "slope_platform_icb",
placement = "floor",
components = {
{
class = "Platform"
},
{
class = "ItemConstrainBox",
offset = vec(0,24,0),
size = vec(3,50,3),
}
}
}
defineObject{
name = "g1_deep_dungeon_slope",
placement = "floor",
components = {
{
class = "Model",
model = "mod_assets/avenie/models/dungeon_slope.fbx",
staticShadow = true,
material = "deep_dungeon_stairs",
},
slopeComponent,
slopeConstrainBox,
},
editorIcon = 44,
}
The ItemConstrainBoxComponents prevent the player from dropping items on the slope, but not from throwing items onto the slope, and of course the platforms will not be positioned right for the items. In my mod, the Grimrock inventory system is gone entirely and the player can't throw or drop items at all, but that's not the case in yours, so you'll need to do something to deal with thrown items.
The easiest way to deal with this is a cop-out: add a big ProjectileColliderComponent and teleport any items that fall onto the slope to the bottom of the slope. The harder but actually reasonable way is to have the timer check for items and set their rotation and world Y position appropriately.
There's also a camera snap when moving from side to side on slopes after entering them from above (or maybe it was after entering from below, can't remember), which I haven't gotten around to fixing yet, but it should be easy to fix.
getMovementDirection() is just a function that returns the direction the party is moving in, or nil if they aren't moving.