2015-03-27 20:25:59 +00:00

273 lines
6.9 KiB
Lua

--[[
Copyright (c) 2014 Team Sparkle
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
]]
-- World object class. Ideally, everything should inherit from this.
function wobj_new(settings)
local this = {
p = settings.p,
f = settings.f or vec(0,0,1),
vl = settings.vl or vec(0,0,0),
vg = settings.vg or vec(0,0,0),
al = settings.al or vec(0,0,0),
ag = settings.ag or vec(0,0,0),
phys = settings.phys or phys_map_none(),
vl2d = settings.vl2d or false,
grav = settings.grav or nil,
aclimb = settings.aclimb or false,
damp = settings.damp or 0.0,
ac_jerk = vec(),
} this.this = this
-- Getter for the position.
-- DO NOT ALTER THE TABLE DIRECTLY.
function this.pos_get()
return this.p
end
-- Calculate jump speed required to jump d blocks high.
function this.get_jump_speed(d)
local grav = this.grav or 1.0
local acc = MODE_GRAVITY*4.0*grav
-- d = a*t*t/2
-- d, a known; find t
---- 2*d/a = t*t
---- t = sqrt(2*d/a)
local t = math.sqrt(2*d/acc)
-- v = a*t
-- a, t known; find v
local v = acc * t
return v
end
-- Gets called every tick to deal with world collisions and whatnot.
function this.on_blockage(blockage, delta)
if blockage.x ~= 0 then
this.vg.x = 0.0
end
if blockage.z ~= 0 then
this.vg.z = 0.0
end
if blockage.y ~= 0 then
this.vg.y = 0.0
end
end
-- Call this if you want to advance this by some amount.
function this.phys_advance(sec_delta)
-- Build a matrix for moving stuff around.
local fs = vec(0,1,0)
local fz = norm3(cam.f)
if this.vl2d then
local suby = math.sqrt(1.0 - fz.y*fz.y)
if suby <= -EPSILON or suby >= EPSILON then
fz = mul_cv(1.0/suby, fz)
end
fz.y = 0.0
end
local fx = norm3(cross(fz, fs))
local fy = norm3(cross(fx, fz))
local m = mat3(fx, fy, fz)
-- Apply gravity.
-- d = v*t + (a*t*t)/2
-- Tested to be consistent over a wide variety of sec_delta values.
if this.grav then
local acc = MODE_GRAVITY*4.0*this.grav
this.vg.y = this.vg.y + sec_delta*acc
end
-- Apply acceleration.
this.vg = add_vv(this.vg, mul_cv(sec_delta, add_vv(
mul_mv(m, this.al),
this.ag
)))
-- Dampen.
local oldvgy = this.vg.y
this.vg = mul_cv(math.exp(-sec_delta*this.damp), this.vg)
this.vg.y = oldvgy
-- Advance.
local np = add_vv(this.p, mul_cv(sec_delta,
add_vv(
mul_mv(m, this.vl),
this.vg
)
))
local delta = sub_vv(np, this.p)
local mdelta = this.phys.move_by(this, vec(), delta)
local blockage = ftzv(sub_vv(delta, mdelta), 100)
this.ac_jerk = mul_cv(math.exp(-sec_delta * 10.0), this.ac_jerk)
if this.aclimb then
local ac_mdelta = this.phys.move_by(this, vec(0,-1.01,0), delta)
local ac_blockage = ftzv(sub_vv(delta, ac_mdelta), 100)
ac_blockage.y = blockage.y
if len3(ac_blockage) - len3(blockage) < -0.01 then
mdelta = ac_mdelta
blockage = ac_blockage
this.p.y = this.p.y - 1.01
this.ac_jerk.y = this.ac_jerk.y + 1.01
end
end
this.p = add_vv(this.p, mdelta)
-- Apply blockages
this.on_blockage(blockage, delta)
end
-- Called every time this feels it needs to be updated.
function this.tick(sec_current, sec_delta)
this.phys_advance(sec_delta)
end
return this
end
-- Camera class.
function cam_new(settings)
local this = wobj_new(settings)
this.phys_stand = this.phys
this.phys_crouch = settings.phys_crouch or this.phys
this.phys_noclip = phys_map_none {}
this.aclimb_stand = this.aclimb
this.zoom = 1.0
this.is_zoomed = false
this.vp = vec()
this.vn = vec()
this.ay = math.pi*2.0
this.ax = 0.0
this.vax = 0
this.vay = 0
this.vaxp = 0
this.vayp = 0
this.vaxn = 0
this.vayn = 0
this.jump_height = settings.jump_height or 3.2
this.jump_key = false
this.crouch_key = false
this.grounded = false
local s_on_blockage = this.on_blockage
function this.on_blockage(blockage, delta, ...)
if blockage.y > 0 then
this.grounded = true
end
return s_on_blockage(blockage, delta, ...)
end
local s_tick = this.tick
function this.tick(sec_current, sec_delta)
if this.vl2d then
-- Undo noclip.
if this.phys == this.phys_noclip then this.phys = this.phys_stand end
-- Apply crouch if need be.
if this.crouch_key then
if this.phys == this.phys_stand then
if this.grounded then
this.p.y = this.p.y + 1
this.ac_jerk.y = this.ac_jerk.y - 1
end
this.phys = this.phys_crouch
this.aclimb = false
end
else
if this.phys == this.phys_crouch then
if not this.phys_stand.collides_with_map(this, vec(0, -1.01, 0)) then
if this.grounded then
this.p.y = this.p.y - 1
this.ac_jerk.y = this.ac_jerk.y + 1
end
this.phys = this.phys_stand
this.aclimb = this.aclimb_stand
end
end
end
-- Apply jump if need be.
if this.jump_key then
if this.grounded then
this.vg.y = -this.get_jump_speed(this.jump_height)
this.grounded = false
end
this.jump_key = false
end
-- Check if we can still jump.
-- TODO.
-- Calculate the forward vector.
this.ax = this.ax + sec_delta * (this.vaxp - this.vaxn)
local max_ax = math.pi*0.49
if this.ax > max_ax then this.ax = max_ax end
if this.ax < -max_ax then this.ax = -max_ax end
this.ay = this.ay + sec_delta * (this.vayp - this.vayn)
this.f = vec(
math.cos(this.ax)*math.sin(this.ay),
math.sin(this.ax),
math.cos(this.ax)*math.cos(this.ay),
0)
-- Set local motion
this.al = sub_vv(this.vp, this.vn)
else
-- Assuming noclip.
-- Set physics.
this.phys = this.phys_noclip
-- Calculate the forward vector.
this.ax = this.ax + sec_delta * (this.vaxp - this.vaxn)
local max_ax = math.pi*0.49
if this.ax > max_ax then this.ax = max_ax end
if this.ax < -max_ax then this.ax = -max_ax end
this.ay = this.ay + sec_delta * (this.vayp - this.vayn)
this.f = vec(
math.cos(this.ax)*math.sin(this.ay),
math.sin(this.ax),
math.cos(this.ax)*math.cos(this.ay),
0)
-- Set local motion
this.vl = sub_vv(this.vp, this.vn)
end
-- Move along.
return s_tick(sec_current, sec_delta)
end
return this
end