Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local ret = {}
- local prim = require("drawingPrimitives")
- local tools = require("debugFunctions")
- local function checkTypes(check, ...)
- --input is ({tocheck, tocheck, tocheck...}, type, type, type...)
- local types = {...}
- for i=1, math.max(#check, #types) do
- --if a type was inputted and a check is not that type
- if types[i] and type(check[i]) ~= types[i] then
- if type(check[i]) == "table" and check[i].isClass then
- if not check[i]:isClass(types[i]) then
- return false
- end
- else
- return false
- end
- end
- end
- return true
- end
- local Object = {funcs = setmetatable({}, {ClassName = "Object"}), ClassName = "Object"}
- getmetatable(Object.funcs).__parent = Object
- function Object:inherit(classname)
- local cls = {ClassName = classname}
- setmetatable(cls, {__index = self})
- cls.funcs = {}
- setmetatable(cls.funcs, {
- __index = self.funcs, --if an instance doesn't have a func, look in parent's funcs
- __parent = cls,
- ClassName = classname --so that thing.ClassName works
- })
- return cls
- end
- function Object:new(...)
- local obj = {}
- setmetatable(obj, {__index = self.funcs})
- obj.data = {}
- if obj.init then obj:init(...) end
- return obj
- end
- function Object:getParentClass()
- local t = getmetatable(self)
- if not t then return nil end
- return getmetatable(self).__index
- end
- function Object:isClass(name)
- if self.ClassName == name then
- return true
- end
- local p = self:getParentClass()
- if p then
- return p:isClass(name)
- else--went up the tree the whole way
- return false
- end
- end
- function Object.funcs:getClass()
- return getmetatable(self.funcs).__parent
- end
- function Object.funcs:isClass(classname, lookinsubclass)
- --getmetatable(self).__index is funcs
- local c = getmetatable(getmetatable(self).__index).__parent
- --if not type(c.ClassName) == "string" then
- -- return false --also handles ClassName == nil
- --else
- if c.ClassName == classname then
- return true
- elseif lookinsubclass and c.isClass then
- return c:isClass(classname)
- end
- return false
- end
- function Object.funcs:find(classname, lookinsubclass)--TODO: look up lua iterator
- local ret = {}
- for k,v in pairs(self) do
- if v.isClass ~= nil and v:isClass(classname, lookinsubclass) then
- table.insert(ret, v)
- end
- end
- return ret
- end
- function Object.funcs:drawOut(k) --draw the outside
- if self.data.drawOut then
- self.data.drawOut(self, k)
- end
- end
- function Object.funcs:drawIn(k) --draw the inisde
- if self.data.drawIn then
- self.data.drawIn(self, k)
- end
- for _, k in pairs(self:getVisibleChildren()) do
- --draw the outside of the children
- local child = self[k]
- local cType = type(child)
- if cType == "table" and getmetatable(child) then
- self[k]:drawOut(self[k], k)
- else
- if not self.data.pimitiveMeta then
- self.data.primitiveMeta = {}
- end
- if not self.data.primitiveMeta[k] then
- self.data.primitiveMeta[k] = {}
- self.data.primitiveMeta[k].X = math.random(0, 100)/100
- self.data.primitiveMeta[k].Y = math.random(0, 100)/100
- end
- prim.drawOut(child, self.data.primitiveMeta[k])
- end
- end
- end
- function Object.funcs:drawIcon(x, y, s, k) --draw the icon
- if self.data.drawIcon then
- self.data.drawIcon(self, x, y, s, k)
- end
- end
- function Object.funcs:keyIsVisible(k)
- if type(k) == "string" then
- if k == "data" then
- return false
- end
- end
- return true
- end
- function Object.funcs:getVisibleChildren()
- local ret = {}
- for k, v in pairs(self) do
- if self:keyIsVisible(k) then
- table.insert(ret,k)
- end
- end
- return ret
- end
- function Object.funcs:countVisibleChildren()
- local c = 0
- for k, v in pairs(self) do
- if self:keyIsVisible(k) then
- c = c + 1
- end
- end
- return c
- end
- function Object.funcs:update(...)
- if self.data.updateChilds then
- for i=1,#self.data.updateChilds do
- self.data.updateChilds[i]:update(...)
- end
- end
- if self.data.onUpdate then
- self.data.onUpdate(self, ...)
- end
- end
- function Object.funcs:afterUpdate(...)
- if self.data.updateChilds then
- for i=1,#self.data.updateChilds do
- if self.data.updateChilds[i].afterUpdate then
- self.data.updateChilds[i]:afterUpdate(...)
- end
- end
- end
- if self.data.afterUpdate then
- self.data.afterUpdate(self, ...)
- end
- end
- function Object.funcs:addUpdateChild(child)
- if not self.data.updateChilds then
- self.data.updateChilds = {}
- end
- table.insert(self.data.updateChilds, child)
- end
- function Object.funcs:placeIn(B)
- if self.Parent then
- for k,v in pairs(self.Parent) do
- if v == self then
- --might have issues doing this during pairs()
- table.remove(self.Parent, V)
- break
- end
- end
- --[[ --if the above has issues use this
- local toDel = nil
- for k,v in pairs(self.Parent) do
- if v == self then
- if type(k) == "number" then
- toDel = k
- else
- self.Parent[k] = nil
- end
- break
- end
- end
- if toDel then table.remove(self.Parent, toDel) end
- ]]
- end
- if B then
- table.insert(B, self)
- self.Parent = B
- else
- self.Parent = nil
- end
- end
- Object.funcs.putIn = Object.funcs.placeIn
- ret.Object = Object
- local Button = Object:inherit("Button")
- function Button.funcs:init(btn, pressfunc, updatefunc)
- self.data.lastValue = false
- self.data.toWatch = btn
- self.data.onPress = pressfunc
- self.data.onUpdate = updatefunc
- end
- function Button.funcs:update(...)
- if self.data.updateChilds then
- for i=1,#self.data.updateChilds do
- self.data.updateChilds[i]:update(...)
- end
- end
- local value = love.keyboard.isDown(self.data.toWatch)
- if value ~= self.data.lastValue and self.data.onPress then
- self.data.onPress(self, value)
- elseif self.data.onUpdate then
- self.data.onUpdate(self, value)
- end
- self.data.lastValue = value
- end
- ret.Button = Button
- local Movable = Object:inherit("Movable")
- function Movable.funcs:init(...)
- --onCollide, onContinuedCollision, onCollisionEnd
- local args = { ... }
- self.data.Velocity = {0, 0}
- self.data.StaticFriction = 0.2
- self.data.KineticFriction = 0.9
- self.data.Hardness = 1.0
- self.data.Push = {0, 0}--used internally during collisions
- self.data.Pushes = 0
- self.data.Tier = 1
- self.data.Targets = {default = true}
- self.data.TargetTags = {all = true, default = true}
- self.data.Colliding = {}--things currently colliding with
- if args[1] and args[2] then
- self.data.Position = {args[1], args[2]}
- else
- self.data.Position = {0, 0}
- end
- end
- function Movable.funcs:update(...)
- --move based on velocity
- self.data.Position[1] = self.data.Position[1] + self.data.Velocity[1]
- self.data.Position[2] = self.data.Position[2] + self.data.Velocity[2]
- --gather directions I am being pushed in by collisions
- local t = self.data.Tier
- local cols = self:find("Collider", true)
- local area = self.Parent and self.Parent:find("Movable", true)
- if self.Parent and area then
- for i=1,#cols do
- for j=1,#area do--for each Movable in where my parent is, that is not me, and also is a target
- if area[j] ~= self and self:isTarget(area[j]) then
- if not area[j].data.Tier > t then
- local collided = false
- local cols2 = area[j]:find("Collider", true)
- for k=1,#cols2 do --for each of its colliders
- if cols[i]:isColliding(cols2[k]) then
- --push = push + collider:getPush(otherCollider)
- local p = self.data.Pushes
- local newPush = cols[i]:getPush(cols2[k])
- local w = self.data.Push[3]
- self.data.Push[1] = ((self.data.Push[1]*p) + (newPush[1]*w))/(p+w)
- self.data.Push[2] = ((self.data.Push[2]*p) + (newPush[2]*w))/(p+w)
- self.data.Pushes = p + w
- collided = true
- end
- end
- --deal with callbacks
- if collided and self.data.onCollide then
- if self.data.Colliding[area[j]] then
- if self.data.onContinuedCollision then self.data.onContinuedCollision(area[j]) end
- else
- if self.data.onCollide then self.data.onCollide(area[j]) end
- self.data.Colliding[area[j]] = true
- end
- elseif self.data.Colliding[area[j]] then --if I WAS colliding with it...
- if self.data.onCollisionEnd then self.data.onCollisionEnd(area[j]) end
- self.data.Colliding[area[j]] = nil
- end
- end
- end
- end
- end
- end
- --https://www.gamedev.net/forums/topic/55270-dragfriction/
- --https://www.dummies.com/education/science/physics/static-friction-along-ramps/
- --http://www.asawicki.info/Mirror/Car%20Physics%20for%20Games/Car%20Physics%20for%20Games.html
- if math.abs(self.data.Velocity[1]) > self.data.StaticFriction then
- self.data.Velocity[1] = self.data.Velocity[1] * 0.9
- else
- self.data.Velocity[1] = 0
- end
- if math.abs(self.data.Velocity[2]) > self.data.StaticFriction then
- self.data.Velocity[2] = self.data.Velocity[2] * 0.9
- else
- self.data.Velocity[2] = 0
- end
- Object.funcs.update(self, ...)
- end
- function Movable.funcs:afterUpdate(...)
- Object.funcs.afterUpdate(self, ...)
- --move based on push
- self.data.Position[1] = self.data.Position[1] + self.data.Push[1]
- self.data.Position[2] = self.data.Position[2] + self.data.Push[2]
- --reset push
- self.data.Push = {0, 0}
- self.data.Pushes = 0
- end
- function Movable.funcs:isTarget(mov)
- for k,v in pairs(self.data.Targets) do
- if mov.data.TargetTags[k] then
- return true
- end
- end
- return false
- end
- function Movable.funcs:isTouching(mov)
- if type(mov) == "table" and mov.isClass and mov.isClass("Movable", true) then
- if mov ~= self and self:isTarget(mov) then
- if not mov.data.Tier > self.data.Tier then
- local cols = self:find("Collider", true)
- local cols2 = mov:find("Collider", true)
- local collided = false
- for i=1,#cols do--for each of my colliders
- for j=1,#cols2 do--for each of its colliders
- if cols[i]:isColliding(cols2[j]) then
- return true
- end
- end
- end
- end
- end
- end
- return false
- end
- function Movable.funcs:addTag(t)
- self.data.TargetTags["default"] = nil
- if type(t) == "table" then
- for k,v in pairs(t) do
- if type(v) == "string" then
- self.data.TargetTags[v] = true
- elseif type(k) ~= "number" then
- self.data.TargetTags[k] = true
- end
- end
- else
- self.data.TargetTags[t] = true
- end
- end
- function Movable.funcs:removeTag(t)
- if type(t) == "table" then
- for k,v in pairs(t) do
- if type(v) == "string" then
- self.data.TargetTags[v] = nil
- elseif type(k) ~= "number" then
- self.data.TargetTags[k] = nil
- end
- end
- else
- self.data.TargetTags[t] = nil
- end
- --if the only entry left is "all" then re-add "default"
- local next = next
- local i1, v1 = next(t)
- if i1 == "all" and next(t, i1) == nil then
- self.data.TargetTags["default"] = true
- end
- end
- function Movable.funcs:addTarget(t)
- self.data.Targets["default"] = nil
- if type(t) == "table" then
- for k,v in pairs(t) do
- if type(v) == "string" then
- self.data.Targets[v] = true
- elseif type(k) ~= "number" then
- self.data.Targets[k] = true
- end
- end
- else
- self.data.Targets[t] = true
- end
- end
- function Movable.funcs:removeTarget(t)
- if type(t) == "table" then
- for k,v in pairs(t) do
- if type(v) == "string" then
- self.data.Targets[v] = nil
- elseif type(k) ~= "number" then
- self.data.Targets[k] = nil
- end
- end
- else
- self.data.Targets[t] = nil
- end
- end
- ret.Movable = Movable
- local Collider = Object:inherit("Collider")
- function Collider.funcs:init(...)
- local args = { ... }
- if args[1] and args[2] then
- self.data.Position = {args[1], args[2]}
- self.data.Hardness = args[3]
- else
- self.data.Position = {0, 0}
- self.data.Hardness = args[1]
- end
- end
- ret.Collider = Collider
- local CircleCol = Collider:inherit("CircleCol")
- function CircleCol.funcs:init(...)
- --radius, posx, posy
- --radius
- local args = { ... }
- if checkTypes(inp, "number", "number", "number") then
- self.data.Position = {args[2], args[3]}
- self.data.Radius = args[1]
- else
- self.data.Position = {0, 0}
- self.data.Radius = args[1]
- end
- self.data.dx = math.random(0, 100)/100
- self.data.dy = math.random(0, 100)/100
- end
- function CircleCol.funcs:isColliding(otherCol)
- if otherCol:isClass("CircleCol") then
- --another circle! time to check collision
- local x1 = self.data.Position[1]
- local y1 = self.data.Position[2]
- local x2 = otherCol.Position[1]
- local y2 = otherCol.Position[2]
- local a = x1 - x2
- local b = y1 - y2
- local dist = sqrt((a*a) + (b*b))
- local r1 = self.data.Radius
- local r2 = otherCol.data.Radius
- return dist < r1 + r2
- else --if it a different type of collider, ignore it
- return false
- end
- end
- function CircleCol.funcs:getPush(otherCol)
- if otherCol:isClass("CircleCol") and self.Parent and otherCol.Parent then
- local x1 = self.data.Position[1]
- local y1 = self.data.Position[2]
- local x2 = otherCol.Position[1]
- local y2 = otherCol.Position[2]
- local a = x1 - x2
- local b = y1 - y2
- local dist = sqrt((a*a) + (b*b))
- local r1 = self.data.Radius
- local r2 = otherCol.data.Radius
- local psh = ((r1+r2)-dist)/2
- if otherCol.Parent.data.Tier > self.Parent.data.Tier then
- psh = psh * 2
- end
- local px = (a / dist)*psh*otherCol.Parent.data.Hardness
- local py = (b / dist)*psh*otherCol.Parent.data.Hardness
- return {px, py, otherCol.Parent.data.Hardness^2}--note:weight value is a bit arbitrary
- else --if it a different type of collider, ignore it
- return {0,0,0}
- end
- end
- ret.CircleCol = CircleCol
- local RectCol = Collider:inherit("RectCol")
- function RectCol.funcs:init(...)
- --sx, sy, posx, posy
- --sx, sy
- local args = { ... }
- if checkTypes(inp, "number", "number", "number", "number") then
- self.data.Position = {args[3], args[4]}
- self.data.SizeX = args[1]
- self.data.SizeY = args[2]
- else
- self.data.Position = {0, 0}
- self.data.SizeX = args[1]
- self.data.SizeY = args[2]
- end
- self.data.dx = math.random(0, 100)/100
- self.data.dy = math.random(0, 100)/100
- end
- function RectCol.funcs:isColliding(otherCol)
- if otherCol:isClass("RectCol") then
- --another rectangle! time to check collision
- local x1 = self.data.Position[1]
- local y1 = self.data.Position[2]
- local x2 = otherCol.Position[1]
- local y2 = otherCol.Position[2]
- local sx1 = self.data.SizeX
- local sx2 = otherCol.data.SizeX
- local sy1 = self.data.SizeY
- local sy2 = otherCol.data.SizeY
- return ( abs(x1-x2) <= (sx1+sx2)/2 ) and ( abs(y1-y2) <= (sy1+sy2)/2 )
- else --if it a different type of collider, ignore it
- return false
- end
- end
- function RectCol.funcs:getPush(otherCol)
- if otherCol:isClass("RectCol") and self.Parent then
- local x1 = self.data.Position[1]
- local y1 = self.data.Position[2]
- local x2 = otherCol.Position[1]
- local y2 = otherCol.Position[2]
- local sx1 = self.data.SizeX
- local sx2 = otherCol.data.SizeX
- local sy1 = self.data.SizeY
- local sy2 = otherCol.data.SizeY
- local px, py = 0, 0
- if abs(x1-x2) / (sx1+sx2) > abs(y1-y2) / (sy1+sy2) then
- --pushing on x
- px = (((sx1+sx2)/2) - abs(x1-x2))/2
- else
- --pushing on y
- py = (((sy1+sy2)/2) - abs(y1-y2))/2
- end
- if otherCol.Parent.data.Tier > self.Parent.data.Tier then
- px = px * 2
- py = py * 2
- end
- return {px, py, self.Parent.data.Hardness^2}--note:weight value is a bit arbitrary
- else --if it a different type of collider, ignore it
- return {0,0,0}
- end
- end
- ret.RectCol = RectCol
- --[[classes to add:
- movable ✓
- update() ✓
- move based on velocity ✓
- find all of my colliders ✓
- only take the ones that are set to not be zones(?) ✓
- check which ones are colliding ✓
- collider:update() ✓
- for each Movable in where my parent is, that is not my parent ✓
- for each of its colliders ✓
- collider:isColliding(otherCollider) ✓
- if so, ✓
- push = push + collider:getPush(otherCollider) ✓
- afterUpdate() ✓
- pos = pos + push ✓
- collider ✓
- percent isSoft ✓
- circle collider ✓
- isColliding(collider) ✓
- getPush(collider) ✓
- rectangle collider ✓
- isColliding(collider) ✓
- getPush(collider) ✓
- ]]
- --[[other things to change/consider:
- ]]
- local TextDisplay = Movable:inherit("TextDisplay")
- function TextDisplay.funcs:init(text, ...)
- Movable.funcs.init(self, ...)
- self.data.text = text
- self.data.fade = 0
- self.data.fadeSpeed = 1/0.2
- self.data.visible = false
- self.data.drawIcon = function(self, x, y, s, k)
- love.graphics.setColor(1,1,1)
- love.graphics.print(self.data.text, x + s/10, y + s/2)
- end
- end
- function TextDisplay.funcs:drawOut(k)
- if self.data.drawOut then
- self.data.drawOut(self, k)
- else
- local p = self.data.Position
- love.graphics.setColor(1,1,1,self.data.fade)
- love.graphics.print(self.data.text, p[1], p[2])
- --love.graphics.setColor(1,1,1)
- --love.graphics.print(self.data.fade, 30, 30)
- --love.graphics.print(self.data.visible and "true" or "false", 30, 40)
- --tools.showPos(p[1], p[2])
- end
- end
- function TextDisplay.funcs:update(...)
- Object.funcs.update(self, ...)
- local args = { ... }
- local d = self.data
- if d.visible then
- if d.fade < 1 then
- d.fade = d.fade + (d.fadeSpeed*args[1])
- if d.fade > 1 then
- d.fade = 1
- end
- end
- else
- if d.fade > 0 then
- d.fade = d.fade - (d.fadeSpeed*args[1])
- if d.fade < 0 then
- d.fade = 0
- end
- end
- end
- end
- ret.TextDisplay = TextDisplay
- return ret
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement