Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --# Emitter
- Emitter = class()
- function Emitter:init(args)
- self.particleMesh = mesh()
- self.particleMesh.texture = args.tex
- self.minLife = args.minLife or 1
- self.maxLife = args.maxLife or 1
- self.spread = args.spread or 360
- self.angle = args.angle or 0
- self.minSpeed = args.minSpeed or 10
- self.maxSpeed = args.maxSpeed or 50
- self.minSize = args.minSize or 10
- self.maxSize = args.maxSize or 15
- self.growth = args.growth or 1
- self.startColor = args.startColor or color(255, 255, 255, 255)
- self.endColor = args.endColor or color(0, 0, 0, 0)
- self.streak = args.streak or false
- self.streakMult = args.streakMult or 2
- self.accel = args.accel or vec2(0,0)
- self.rAccel = args.rAccel or vec2(0,0)
- self.particles = {}
- self.pCount = 0
- for i = 1,100 do
- table.insert(self.particles, Particle())
- end
- end
- function Emitter:emit(pos,count)
- for i = 1,#self.particles do
- local p = self.particles[i]
- if p.dead then
- self.pCount = math.max(i, self.pCount)
- p.dead = false
- p.pos = pos
- p.life = math.random(self.minLife, self.maxLife)
- p.size = math.random(self.minSize, self.maxSize)
- p.maxLife = p.life
- p.vel = vec2(0, math.random(self.minSpeed, self.maxSpeed))
- p.vel = p.vel:rotate(math.rad(self.angle + math.random(-self.spread/2, self.spread/2)))
- count = count - 1
- if count == 0 then return end
- end
- end
- end
- function Emitter:draw()
- -- update
- self.particleMesh:clear()
- for i = 1,self.pCount do
- local p = self.particles[i]
- if not p.dead then
- p.prevPos = p.pos
- p.pos = p.pos + p.vel * DeltaTime
- p.vel = p.vel + (self.accel + vec2(math.random(-self.rAccel.x, self.rAccel.x), math.random(-self.rAccel.y, self.rAccel.y))) * DeltaTime
- p.life = math.max(0, p.life - DeltaTime)
- p.size = p.size + DeltaTime * self.growth
- if p.life == 0 then p.dead = true end
- local interp = p.life / p.maxLife
- p.col.r = interp * self.startColor.r + (1-interp) * self.endColor.r
- p.col.g = interp * self.startColor.g + (1-interp) * self.endColor.g
- p.col.b = interp * self.startColor.b + (1-interp) * self.endColor.b
- p.col.a = interp * self.startColor.a + (1-interp) * self.endColor.a
- local ind = self.particleMesh:addRect(p.pos.x, p.pos.y, p.size, p.size)
- self.particleMesh:setRectColor(ind, p.col)
- if self.streak then
- local dir = (p.pos - p.prevPos)
- local len = dir:len()
- local pos = (p.pos + p.prevPos) * 0.5
- local ang = math.atan2(dir.y, dir.x)
- self.particleMesh:setRect(ind, pos.x, pos.y, p.size * self.streakMult, p.size, ang)
- end
- end
- end
- self.particleMesh:draw()
- end
- Particle = class()
- function Particle:init()
- self.pos = vec2(0,0)
- self.prevPos = vec2(0,0)
- self.vel = vec2(0,0)
- self.life = 0
- self.maxLife = 0
- self.dead = true
- self.col = color(0, 0, 0, 255)
- self.size = 0
- end
- --# SlammerPod
- SlammerPod = class()
- function SlammerPod:init(x,y)
- self.name = "pod"
- self.health = 1
- self.chassis = createBox(x, y, 60, 80)
- self.chassis.gravityScale = 1
- self.chassis.density = 3
- self.chassis.fixedRotation = true
- self.chassis.info = self
- self.rock = createCircle(self.chassis.x,self.chassis.y - 150, 35)
- self.rock.density = 0.75
- self.rock.info = self
- owners[self.rock] = self
- self.rock.angularDamping = 0.1
- self.rope = physics.joint(ROPE, self.chassis, self.rock, self.chassis.position, self.rock.position, 150)
- debugDraw:addJoint(self.rope)
- self.links = {}
- local lc = 10
- local len = (self.chassis.y - self.rock.y) / lc
- local p = self.chassis.position - vec2(0, len/2)
- local prev = self.chassis
- for i = 1,lc do
- local link = createBox(p.x, p.y, 5, len)
- link.mask = {10}
- local j = physics.joint(REVOLUTE, prev, link, p + vec2(0, len/2))
- prev = link
- debugDraw:addJoint(j)
- table.insert(self.links, link)
- p.y = p.y - len
- end
- local j = physics.joint(REVOLUTE, prev, self.rock, self.rock.position)
- debugDraw:addJoint(j)
- self.stick = VirtualStick({
- moved = function(v) self:stickMoved(v) end,
- released = function(v) self:stickReleased(v) end,
- radius = 80,
- deadZoneRadius = 10,
- })
- self.stick:activate()
- self.dir = vec2(0,0)
- self.anim = 0.0
- --sprite("Cargo Bot:Smoke Particle")
- self.sparks = Emitter({tex = "Tyrian Remastered:Bullet Fire A",
- minSize = 5,
- maxSize = 5,
- minSpeed = 100,
- maxSpeed = 150,
- accel = vec2(0,-1000),
- rAccel = vec2(1500,1500),
- streak = true,
- streakMult = 3})
- self.smoke = Emitter({tex = "Cargo Bot:Smoke Particle",
- minSize = 5,
- maxSize = 25,
- minSpeed = 0,
- maxSpeed = 0,
- accel = vec2(0,1000),
- rAccel = vec2(1500,1500),
- })
- end
- function SlammerPod:stickMoved(v)
- self.dir = v
- end
- function SlammerPod:stickReleased(v)
- self.dir = vec2(0,0)
- end
- function SlammerPod:draw()
- self.anim = self.anim + DeltaTime
- local gain = 250 * self.chassis.mass
- local damp = 0.75
- local vel = self.chassis.linearVelocity
- self.chassis:applyForce(gain * self.dir - damp * vel)
- self.chassis.linearDamping = vel:len() / 50
- pushMatrix()
- translate(self.chassis.x, self.chassis.y)
- rotate(self.chassis.angle)
- sprite("Tyrian Remastered:Part H", 0, 0, 60)
- pushMatrix()
- rotate(self.anim * 100)
- sprite("Tyrian Remastered:Orb 2", 0,0,30)
- popMatrix()
- popMatrix()
- pushMatrix()
- translate(self.rock.x, self.rock.y)
- rotate(self.rock.angle)
- sprite("Tyrian Remastered:Mine Spiked Huge", 0, 0, self.rock.radius * 2.5)
- popMatrix()
- for k,v in ipairs(self.links) do
- pushMatrix()
- translate(v.x,v.y)
- rotate(v.angle)
- sprite("Tyrian Remastered:Ring", 0,0,15,25)
- popMatrix()
- end
- self.sparks:draw()
- --self.()stick:draw()
- end
- function SlammerPod:hit(other, contact, first)
- if contact.state == BEGAN then
- local imp = math.max(0, (contact.normalImpulse-25) / 25)
- if imp > 0.0 then
- self.sparks:emit(contact.position, 10)
- sound(SOUND_HIT, 47051, imp)
- if other and other.damage then other:damage(imp) end
- end
- end
- end
- --# Worm
- Worm = class()
- function Worm:init(x,y,t)
- self.name = "worm"
- -- you can accept and set parameters here
- self.body = createCircle(x,y,35)
- self.body.info = self
- self.body.gravityScale = 0
- self.target = t
- self.dir = vec2(0,0)
- self.health = 1
- self.power = 1
- self.stun = 0
- self.base = physics.body(CIRCLE, 35)
- self.base.fixedRotation = true
- self.base.gravityScale = 0
- self.base.position = self.body.position
- self.base.mask = {15}
- self.servo = physics.joint(REVOLUTE, self.base, self.body, self.base.position)
- self.servo.enableMotor = true
- self.servo.maxMotorTorque = 1000
- self.motorSpeed = 0
- end
- function Worm:draw()
- self.dir = (self.target.chassis.position - self.body.position)
- self.dir = self.dir:normalize()
- -- Codea does not automatically call this method
- local gain = 29.400 * self.body.mass * self.power
- local damp = 0.75 * self.power
- local vel = self.body.linearVelocity
- self.body:applyForce(gain * self.dir - damp * vel)
- self.body.linearDamping = vel:len() / 100 * self.power
- self.body.gravityScale = 1-self.power
- local tAng = math.atan2(self.dir.y, self.dir.x) + math.pi/2
- self.servo.motorSpeed = (tAng - math.rad(self.servo.jointAngle)) * 100
- --if self.power == 0
- pushMatrix()
- translate(self.body.x, self.body.y)
- rotate(self.body.angle)
- sprite("Tyrian Remastered:Organic Claw", 0,-14,60)
- sprite("Tyrian Remastered:Part N")
- popMatrix()
- fill(120, 30, 30, 255)
- rect(self.body.x,self.body.y+50,50,10)
- fill(255, 0, 0, 255)
- rect(self.body.x,self.body.y+50,50*self.health,10)
- end
- function Worm:damage(dmg)
- self.health = math.max(0, self.health - dmg)
- if self.health == 0 then
- self.power = 0
- self.servo.enableMotor = false
- sound(SOUND_RANDOM, 41402, 1)
- end
- end
- WormPart = class()
- function WormPart:init(x,y,r,worm,parent)
- self.body = physics.body(CIRCLE, r)
- self.body.x = x
- self.body.y = y
- self.worm = worm
- self.health = 1
- end
- --# Main
- supportedOrientations(LANDSCAPE_ANY)
- --displayMode(FULLSCREEN)
- physics.resume()
- -- Use this function to perform your initial setup
- function setup()
- debugDraw = PhysicsDebugDraw()
- -- test classes
- -- to add your own, make sure to define setup() and cleanup()
- -- in addition to draw() and touched()
- tests = {Test1()}
- iparameter("test_number", 1, #tests)
- iparameter("use_accelerometer", 0, 1)
- currentTestIndex = 1
- currentTest = nil
- setTest(currentTestIndex)
- defaultGravity = physics.gravity()
- end
- function setTest(t)
- if currentTest then
- if currentTest.cleanup then
- currentTest:cleanup()
- end
- cleanup()
- end
- currentTestIndex = t
- currentTest = tests[t]
- currentTest:setup()
- end
- function nextTest()
- local t = currentTestIndex + 1
- if t > #tests then
- t = 1
- end
- setTest(t)
- end
- function createCircle(x,y,r)
- local circle = physics.body(CIRCLE, r)
- -- enable smooth motion
- circle.interpolate = true
- circle.x = x
- circle.y = y
- circle.restitution = 0.25
- debugDraw:addBody(circle)
- return circle
- end
- function createBox(x,y,w,h)
- -- polygons are defined by a series of points in counter-clockwise order
- local box = physics.body(POLYGON, vec2(-w/2,h/2), vec2(-w/2,-h/2), vec2(w/2,-h/2), vec2(w/2,h/2))
- box.interpolate = true
- box.x = x
- box.y = y
- box.restitutions = 0.25
- debugDraw:addBody(box)
- return box
- end
- function createGround()
- local ground = physics.body(POLYGON, vec2(0,20), vec2(0,0), vec2(WIDTH,0), vec2(WIDTH,20))
- ground.type = STATIC
- debugDraw:addBody(ground)
- return ground
- end
- function cleanup()
- debugDraw:clear()
- end
- -- This function gets called once every frame
- function draw()
- -- This sets the background color to black
- background(0, 0, 0)
- if test_number ~= currentTestIndex then
- setTest(test_number)
- end
- currentTest:draw()
- --debugDraw:draw()
- local str = string.format("Test %d - %s", currentTestIndex, currentTest.title)
- text(str, WIDTH/2, HEIGHT - 20)
- if use_accelerometer == 1 then
- physics.gravity(Gravity)
- else
- --physics.gravity(defaultGravity)
- end
- end
- function touched(touch)
- if debugDraw:touched(touch) == false then
- currentTest:touched(touch)
- end
- end
- function collide(contact)
- --debugDraw:collide(contact)
- if currentTest.collide then
- currentTest:collide(contact)
- end
- end
- --# Test1
- Test1 = class()
- function Test1:init()
- self.title = "SlammerFall"
- end
- function Test1:setup()
- POD = 0
- WORM = 1
- owners = {}
- self.levelWidth = 1024*4
- self.levelHeight = 768*2
- self.stars = mesh()
- self.stars.texture = "SpaceCute:Star"
- self.stars2 = mesh()
- self.stars2.texture = "SpaceCute:Star"
- for i = 1,200 do
- local size = math.random(5,15)
- self.stars:addRect(math.random(0,self.levelWidth), math.random(0,self.levelHeight), size,size)
- self.stars2:addRect(math.random(0,self.levelWidth*2), math.random(0,self.levelHeight*2), size,size)
- end
- self.pod = SlammerPod(self.levelWidth/2, HEIGHT/2)
- self.worm1 = Worm(self.levelWidth, HEIGHT, self.pod)
- self.worm1.name = "worm1"
- self.worm2 = Worm(self.levelWidth/2, HEIGHT, self.pod)
- self.worm2.name = "worm2"
- self.worm3 = Worm(9, HEIGHT, self.pod)
- self.worm3.name = "worm3"
- self.camPos = self.pod.chassis.position
- self.landMesh = mesh()
- local points = {vec2(0,0)}
- for x = 0,self.levelWidth,50 do
- table.insert(points, vec2(x,50 + math.random(0, 25)))
- end
- table.insert(points, vec2(self.levelWidth, 0))
- self.landMesh.vertices = triangulate(points)
- self.landMesh:setColors(128, 128, 128)
- self.land = physics.body(CHAIN, true, unpack(points))
- self.land.type = STATIC
- self.land.restitution = 0.3
- self.leftBarrier = physics.body(EDGE, vec2(0,0), vec2(0,self.levelHeight))
- self.rightBarrier = physics.body(EDGE, vec2(self.levelWidth,0), vec2(self.levelWidth,self.levelHeight))
- self.topBarrier = physics.body(EDGE, vec2(0,self.levelHeight), vec2(self.levelWidth,self.levelHeight))
- end
- function Test1:draw()
- -- Codea does not automatically call this method
- pushMatrix()
- local interp = 0.95
- self.camPos = self.camPos * interp + self.pod.chassis.position * (1-interp)
- self.camPos.x = math.max(self.camPos.x, WIDTH/2)
- self.camPos.x = math.min(self.camPos.x, self.levelWidth - WIDTH/2)
- self.camPos.y = math.max(self.camPos.y, HEIGHT/2)
- self.camPos.y = math.min(self.camPos.y, self.levelHeight - HEIGHT/2)
- pushMatrix()
- translate(-self.camPos.x/2 + WIDTH/2, -self.camPos.y/2 + HEIGHT/2)
- self.stars2:draw()
- popMatrix()
- translate(-self.camPos.x + WIDTH/2, -self.camPos.y + HEIGHT/2)
- self.stars:draw()
- self.landMesh:draw()
- self.pod:draw()
- self.worm1:draw()
- self.worm2:draw()
- self.worm3:draw()
- popMatrix()
- end
- function Test1:collide(contact)
- if contact.state ~= BEGAN then return end
- local infoA = contact.bodyA.info
- local infoB = contact.bodyB.info
- if infoA and infoA.hit then
- infoA:hit(infoB, contact, true)
- end
- if infoB and infoB.hit then
- infoB:hit(infoA, contact, false)
- end
- end
- --# PhysicsDebugDraw
- PhysicsDebugDraw = class()
- function PhysicsDebugDraw:init()
- self.bodies = {}
- self.joints = {}
- self.touchMap = {}
- self.contacts = {}
- end
- function PhysicsDebugDraw:addBody(body)
- table.insert(self.bodies,body)
- end
- function PhysicsDebugDraw:addJoint(joint)
- table.insert(self.joints,joint)
- end
- function PhysicsDebugDraw:clear()
- -- deactivate all bodies
- for i,body in ipairs(self.bodies) do
- body:destroy()
- end
- for i,joint in ipairs(self.joints) do
- joint:destroy()
- end
- self.bodies = {}
- self.joints = {}
- self.contacts = {}
- self.touchMap = {}
- end
- function PhysicsDebugDraw:draw()
- pushStyle()
- smooth()
- strokeWidth(5)
- stroke(128,0,128)
- local gain = 2.0
- local damp = 0.5
- for k,v in pairs(self.touchMap) do
- local worldAnchor = v.body:getWorldPoint(v.anchor)
- local touchPoint = v.tp
- local diff = touchPoint - worldAnchor
- local vel = v.body:getLinearVelocityFromWorldPoint(worldAnchor)
- v.body:applyForce( (1/1) * diff * gain - vel * damp, worldAnchor)
- line(touchPoint.x, touchPoint.y, worldAnchor.x, worldAnchor.y)
- end
- stroke(0,255,0,255)
- strokeWidth(5)
- for k,joint in pairs(self.joints) do
- local a = joint.anchorA
- local b = joint.anchorB
- line(a.x,a.y,b.x,b.y)
- end
- stroke(255,255,255,255)
- noFill()
- for i,body in ipairs(self.bodies) do
- pushMatrix()
- translate(body.x, body.y)
- rotate(body.angle)
- if body.type == STATIC then
- stroke(255,255,255,255)
- elseif body.type == DYNAMIC then
- stroke(150,255,150,255)
- elseif body.type == KINEMATIC then
- stroke(150,150,255,255)
- end
- if body.shapeType == POLYGON then
- strokeWidth(5.0)
- local points = body.points
- for j = 1,#points do
- a = points[j]
- b = points[(j % #points)+1]
- line(a.x, a.y, b.x, b.y)
- end
- elseif body.shapeType == CHAIN or body.shapeType == EDGE then
- strokeWidth(5.0)
- local points = body.points
- for j = 1,#points-1 do
- a = points[j]
- b = points[j+1]
- line(a.x, a.y, b.x, b.y)
- end
- elseif body.shapeType == CIRCLE then
- strokeWidth(5.0)
- line(0,0,body.radius-3,0)
- strokeWidth(2.5)
- ellipse(0,0,body.radius*2)
- end
- popMatrix()
- end
- stroke(255, 0, 0, 255)
- fill(255, 0, 0, 255)
- for k,v in pairs(self.contacts) do
- for m,n in ipairs(v.points) do
- ellipse(n.x, n.y, 10, 10)
- end
- end
- popStyle()
- end
- function PhysicsDebugDraw:touched(touch)
- local touchPoint = vec2(touch.x, touch.y)
- if touch.state == BEGAN then
- for i,body in ipairs(self.bodies) do
- if body.type == DYNAMIC and body:testPoint(touchPoint) then
- self.touchMap[touch.id] = {tp = touchPoint, body = body, anchor = body:getLocalPoint(touchPoint)}
- return true
- end
- end
- elseif touch.state == MOVING and self.touchMap[touch.id] then
- self.touchMap[touch.id].tp = touchPoint
- return true
- elseif touch.state == ENDED and self.touchMap[touch.id] then
- self.touchMap[touch.id] = nil
- return true;
- end
- return false
- end
- function PhysicsDebugDraw:collide(contact)
- if contact.state == BEGAN then
- self.contacts[contact.id] = contact
- elseif contact.state == MOVING then
- self.contacts[contact.id] = contact
- elseif contact.state == ENDED then
- self.contacts[contact.id] = nil
- end
- end
- --# Controller
- -- Base class for controllers
- --
- -- Controllers translate touch events into callbacks to functions
- -- that do something in the app. (Model/View/Controller style).
- --
- -- Controllers can draw a representation of their current state on
- -- the screen, but you can choose not to.
- --
- -- A controller can be installed as the global handler for touch
- -- events by calling its activate() method
- Controller = class()
- function Controller:activate()
- touched = function(t)
- self:touched(t)
- end
- end
- function Controller:draw()
- -- nothing
- end
- -- Utility functions
- function touchPos(t)
- return vec2(t.x, t.y)
- end
- function clamp(x, min, max)
- return math.max(min, math.min(max, x))
- end
- function clampAbs(x, maxAbs)
- return clamp(x, -maxAbs, maxAbs)
- end
- function clampLen(vec, maxLen)
- return vec:normalize() * math.min(vec:len(), maxLen)
- end
- -- projects v onto the direction represented by the given unit vector
- function project(v, unit)
- return v:dot(unit)
- end
- function sign(x)
- if x == 0 then
- return 0
- elseif x < 0 then
- return -1
- elseif x > 0 then
- return 1
- else
- return x -- x is NaN
- end
- end
- function doNothing()
- end
- --# VirtualStick
- -- A virtual analogue joystick with a dead-zone at the center,
- -- which activates wherever the user touches their finger
- --
- -- Arguments:
- -- radius - radius of the stick (default = 100)
- -- deadZoneRadius - radius of the stick's dead zone (default = 25)
- -- moved(v) - Called when the stick is moved
- -- v : vec2 - in the range vec2(-1,-1) and vec2(1,1)
- -- pressed() - Called when the user starts using the stick (optional)
- -- released() - Called when the user releases the stick (optional)
- VirtualStick = class(Controller)
- function VirtualStick:init(args)
- self.radius = args.radius or 100
- self.deadZoneRadius = args.deadZoneRadius or 25
- self.releasedCallback = args.released or doNothing
- self.steerCallback = args.moved or doNothing
- self.pressedCallback = args.pressed or doNothing
- end
- function VirtualStick:touched(t)
- local pos = touchPos(t)
- if t.state == BEGAN and self.touchId == nil then
- self.touchId = t.id
- self.touchStart = pos
- self.stickOffset = vec2(0, 0)
- self.pressedCallback()
- elseif t.id == self.touchId then
- if t.state == MOVING then
- self.stickOffset = clampLen(pos - self.touchStart, self.radius)
- self.steerCallback(self:vector())
- elseif t.state == ENDED then
- self:reset()
- self.releasedCallback()
- end
- end
- end
- function VirtualStick:vector()
- local stickRange = self.radius - self.deadZoneRadius
- local stickAmount = math.max(self.stickOffset:len() - self.deadZoneRadius, 0)
- local stickDirection = self.stickOffset:normalize()
- return stickDirection * (stickAmount/stickRange)
- end
- function VirtualStick:reset()
- self.touchId = nil
- self.touchStart = nil
- self.stickOffset = nil
- end
- function VirtualStick:draw()
- if self.touchId ~= nil then
- pushStyle()
- ellipseMode(RADIUS)
- strokeWidth(1)
- stroke(255, 255, 255, 255)
- noFill()
- pushMatrix()
- translate(self.touchStart.x, self.touchStart.y)
- ellipse(0, 0, self.radius, self.radius)
- ellipse(0, 0, self.deadZoneRadius, self.deadZoneRadius)
- translate(self.stickOffset.x, self.stickOffset.y)
- ellipse(0, 0, 25, 25)
- popMatrix()
- popStyle()
- end
- end
Add Comment
Please, Sign In to add comment