Skip to content

Instantly share code, notes, and snippets.

@Kolosso7

Kolosso7/Corwin

Created Mar 26, 2017
Embed
What would you like to do?
A 3D game for the Codea app
--# Main
-- Dimensions of screen: 1024,768
Worlds = {}
function Worlds.addTri(pos1,pos2,pos3,addTex,col,world)
local col,world = color(255),Worlds.town
local m = world.mesh
do
verts = m.vertices
verts[#verts+1],verts[#verts+2],verts[#verts+3] = pos1,pos2,pos3
m.vertices = verts
end
do
local cols = {}
for i=1, m.size-3 do
cols[i] = m:color(i)
end
cols[#cols+1],cols[#cols+2],cols[#cols+3] = col,col,col
m.colors = cols
end
do
local tex = {}
for i=1, m.size-3 do
tex[i] = m:texCoord(i)
end
tex[#tex+1],tex[#tex+2],tex[#tex+3] = addTex[1],addTex[2],addTex[3]
m.texCoords = tex
end
end
function Worlds.addWall(world,x,z,w,h,r,addTex,col,y,solid)
local solid,y,col = solid or true, y or 0,col or color(255)
local m = world.mesh
do
local verts = m.vertices
local cos,sin = math.cos,math.sin
local tl,tr,bl,br = vec3(x+sin(math.rad(r))*w*-.5,y+h,z+cos(math.rad(r))*w*-.5),vec3(x+sin(math.rad(r))*w*.5,y+h,z+cos(math.rad(r))*w*.5),vec3(x+sin(math.rad(r))*w*-.5,y,z+cos(math.rad(r))*w*-.5),vec3(x+sin(math.rad(r))*w*.5,y,z+cos(math.rad(r))*w*.5)
verts[#verts+1],verts[#verts+2],verts[#verts+3] = tl,tr,br
verts[#verts+1],verts[#verts+2],verts[#verts+3] = br,bl,tl
m.vertices = verts
end
do
local cols = {}
for i=1, m.size-6 do
cols[i] = m:color(i)
end
cols[#cols+1],cols[#cols+2],cols[#cols+3] = col,col,col
cols[#cols+1],cols[#cols+2],cols[#cols+3] = col,col,col
m.colors = cols
end
do
local tex = {}
for i=1, m.size-6 do
tex[i] = m:texCoord(i)
end
local tl,tr,bl,br = addTex[1],addTex[2],addTex[3],addTex[4]
tex[#tex+1],tex[#tex+2],tex[#tex+3] = tl,tr,br
tex[#tex+1],tex[#tex+2],tex[#tex+3] = br,bl,tl
m.texCoords = tex
end
end
function Worlds.addFloor(x,z,w,d,y,addTex,col,world)
local col,addTex,r,y = col or color(255), addTex ~= false and {vec2(0,1),vec2(1,1),vec2(0,0),vec2(1,0)}, r or 0, y or 0
local m = world.mesh
do
local verts = m.vertices
local torad = math.pi/180
--local cos = math.cos
local tl,tr,bl,br = vec3(x+w*-.5,y,z+d*.5),vec3(x+w*.5,y,z+d*.5),vec3(x+w*-.5,y,z+d*-.5),vec3(x+w*.5,y,z+d*-.5)
verts[#verts+1],verts[#verts+2],verts[#verts+3] = tr,tl,bl
verts[#verts+1],verts[#verts+2],verts[#verts+3] = bl,br,tr
m.vertices = verts
end
do
local cols = {}
for i=1, m.size-6 do
cols[i] = m:color(i)
end
cols[#cols+1],cols[#cols+2],cols[#cols+3] = col,col,col
cols[#cols+1],cols[#cols+2],cols[#cols+3] = col,col,col
m.colors = cols
end
do
local tex = {}
for i=1, m.size-6 do
tex[i] = m:texCoord(i)
end
local tl,tr,bl,br = addTex[1],addTex[2],addTex[3],addTex[4]
tex[#tex+1],tex[#tex+2],tex[#tex+3] = tl,tr,br
tex[#tex+1],tex[#tex+2],tex[#tex+3] = br,bl,tl
m.texCoords = tex
end
end
function Worlds.addHouse(x,z,walTex,doorTex,rooTex,door,world,y)
local door,world,y = 1,Worlds.town,0
world.houses[#world.houses+1] = {x = x, z = z}
Worlds.addWall(world,x,z-200,400,300,270,walTex)
Worlds.addWall(world,x,z+200,400,300,90,walTex)
Worlds.addWall(world,x-200,z,400,300,0,walTex)
Worlds.addWall(world,x+200,z,400,300,180,walTex)
do
local dz = door*201
Worlds.addWall(world,x,z+dz,120,200,math.deg(math.atan(dz,0)),doorTex,color(200))
end
local m = world.mesh
do
local v = m.vertices
local addV = {
vec3(x,400,z-200),vec3(x,400,z+200),vec3(x-200,300,z+200),
vec3(x-200,300,z+200),vec3(x-200,300,z-200),vec3(x,400,z-200),
vec3(x,400,z+200),vec3(x,400,z-200),vec3(x+200,300,z-200),
vec3(x+200,300,z-200),vec3(x+200,300,z+200),vec3(x,400,z+200),
vec3(x-200,300,z+200),vec3(x,400,z+200),vec3(x+200,300,z+200),
vec3(x+200,300,z-200),vec3(x,400,z-200),vec3(x-200,300,z-200)
}
table.move(addV,1,#addV,#v+1,v)
local cols = {}
for i=1,m.size do
cols[i] = m:color(i)
end
local w = color(255)
for i=#cols+1,#cols + #addV do
cols[i] = w
end
local tc = {}
for i=1,m.size do
tc[i] = m:texCoord(i)
end
local tl,tr,bl,br = rooTex[1],rooTex[2],rooTex[3],rooTex[4]
local addTc = {tl,tr,br,br,bl,tl,tl,tr,br,br,bl,tl,bl,vec2(bl.x+(br.x-bl.x)/2,bl.y+(tl.y-bl.y)/3),br,bl,vec2(bl.x+(br.x-bl.x)/2,bl.y+(tl.y-bl.y)/3),br}
table.move(addTc,1,#addTc,#tc+1,tc)
m.vertices,m.colors,m.texCoords = v,cols,tc
end
end
Player = {x=0,y=0,z=0,vel = 0, hp = 20, rotation=vec2(0,0),inventory = {}}
do
local s = Player
function s.draw() -- Player.draw
do
s.onGround = false
local oldx,oldy,oldz = s.x,s.y,s.z -- Store x,y,z positions for collision sensing
s.vel = math.max(s.vel - 0.77,-55)
s.y = s.y + s.vel -- Simulate gravity
if s.y < 0 then -- if below ground level,
s.vel = 0 -- set velocity to 0
s.y = 0 -- and teleport to ground level
s.onGround = true -- Set onGround to true for the jump button
end
if World == Worlds.town then
local x,y,z = s.x,s.y,s.z
local moving = World.jx ~= 0 or World.jy ~= 0 -- jx and jy are the x,y positions of the joystick
if moving then
local new = vec2(World.jy/6,World.jx/6):rotate(Player.rotation.x) -- vec2 of next step of player
x,z = x+new.x,z+new.y -- add the step to current position (and assign it to the local variables)
s.x,s.z = x,z -- set the position of the player to the local variables x and z
end
for i,v in ipairs(World.houses) do
local oldY = (((oldx < v.x + 30 and oldx > v.x - 30) and 400) or ((oldx > v.x and 400 - math.abs(v.x - oldx-30)/2) or 400 - math.abs(oldx - v.x-30)/2)) -- formula for the y position whilst on the roof depending on the x position of the last frame.
local Y = (((x < v.x + 30 and x > v.x - 30) and 400) or ((x > v.x and 400 - math.abs(x - v.x-30)/2) or 400 - math.abs(v.x - x-30)/2)) -- formula for the y position whilst on the roof depending on the x position of the current frame.
if x < v.x + 230 and x > v.x - 230 and z > v.z - 230 and z < v.z + 230 then -- x,z within the walls
if oldy >= oldY and y < Y then -- old y is higher than the roof and current y is below the roof
s.vel = math.max(s.vel, 0)
s.y = Y -- Set y to where the roof y would be
s.onGround = true
break
elseif moving then
if oldy < oldY and oldx > v.x - 230 and oldx < v.x + 230 then -- if old y was below old roof y and also within the walls of -x and +x then
Player.z = v.z + (oldz > v.z and 230 or -230) -- set z to outside +z and -z walls
break
elseif oldy < 300 and oldz > v.z - 230 and oldz < v.z + 230 then
Player.x = v.x + (oldx > v.x and 230 or -230)
break
end
end
end
end
end
end
local posx,posy,posz = s.x,s.y+160,s.z
perspective()
local lookAtX = posx+math.cos(s.rotation.x)
local lookAtY = posy+math.tan(s.rotation.y)
local lookAtZ = posz+math.sin(s.rotation.x)
camera(posx,posy,posz,lookAtX,lookAtY,lookAtZ,0,1,0)
end
end
do
local intro = {mesh = mesh(),sky = color(0),noUI = true, dialogTimer = 5, text = 0, details = {t="Greetings, Player."}, dialog = {{t="Do you know who this is?",delay=3},{t="This is Corwin.",delay=3},{t="He has become the overlord of a town called Corwinia.",delay=4},{t="Those who inhabit this town are called Corwinians.",delay=4},{t="Corwin does not consider his people, people.",delay=3},{t="He treats them unfairly.",delay=3},{t="The Corwinians are not fond of Corwin.",delay=3},{t="However, they cannot tell anyone that because Corwin executes anyone who displeases him.",delay=6},{t="He has built a wall surrounding the town to keep people from leaving.",delay=4},{t="He also has guards scattered across the town to execute those who break the rules.",delay=6},{t="The town feels like it's from centuries in the past when it's actually the year 220.",delay=6},{t="Corwin is so cruel, he has even mutated his people to make them look similar to him.",delay=5},{t="You are a trained Corwinian guard and must end the madness.",delay=5},{t="Defeat the various guards and ultimately, Corwin.",delay=3},{t="Go, our hero!",delay=2}}, draw = function()
local s = Worlds.intro
s.dialogTimer = s.dialogTimer - DeltaTime
if s.dialogTimer <= 0 then
if s.text == #s.dialog then
World = Worlds.town
else
s.text = s.text + 1
s.details = s.dialog[s.text]
s.dialogTimer = s.dialogTimer + s.details.delay
end
end
Player.draw() -- Set up the camera and everything
if s.text ~= 0 and s.text < 14 then
Worlds.intro.mesh:draw()
translate(400,60,0)
else
translate(400,160,0)
end
fill(255)
fontSize(25)
textWrapWidth(300)
font("ArialRoundedMTBold")
rotate(270,0,1,0)
text(s.details.t)
end}
intro.mesh.texture = readImage("Platformer Art:Guy Standing")
Worlds.addWall(intro,400,0,168,224,0,{vec2(0,1),vec2(1,1),vec2(0,0),vec2(1,0)},color(255),120)
Worlds.intro = intro
end
do
local emptyMatrix = matrix()
local guardM = mesh() -- mesh to be drawn for all the guards
guardM.vertices = {vec3(-40,180,0),vec3(40,180,0),vec3(40,0,0),vec3(40,0,0),vec3(-40,0,0),vec3(-40,180,0)}
guardM.texture = readImage("Platformer Art:Guy Standing")
guardM.texCoords = {vec2(0,1),vec2(1,1),vec2(1,0),vec2(1,0),vec2(0,0),vec2(0,1)}
guardM:setColors(255,255,255)
local town = {sortTimer = 25, jx = 0, jy = 0, cButton = {}, mesh = mesh(),sky = color(255, 42.5, 85, 255),houses = {}, guardM = guardM, guards = {}}
-- Each guard table contains the keys: pos (vec2), hp (number), hitTime (number)
local gSort = function(a,b) -- sort function for sorting the guards
local pDist = vec2(Player.x,Player.z)
return a.pos:dist(pDist) > b.pos:dist(pDist)
end
local pl = Player -- upValue for town.draw
local guards = town.guards -- upValue for town.draw
local nHouses = #town.houses -- upValue for town.draw
town.draw = function() -- ************* town.draw ********************
town.sortTimer = town.sortTimer - 1 -- time before the code sorts the guards
if #guards > 1 and town.sortTimer == 0 then
town.sortTimer = 25
table.sort(guards,gSort) -- sort the guards from frathest to closest
end
pl.draw()
town.mesh:draw()
if pl.shooting then -- If shoot button was pressed
pl.shooting = false
for i = #guards, 1, -1 do -- check each guard individually from closest to farthest
local v = guards[i]
local dist = ((pl.x-v.pos.x)^2 + (pl.z-v.pos.y)^2)^.5 -- pythagorean theorem to calculate dist
local y = pl.y + 160 + math.tan(pl.rotation.y) * dist -- find the y of the point we're looking at
if y < 180 and y > 0 and vec2(math.cos(pl.rotation.x)*dist+pl.x,math.sin(pl.rotation.x)*dist+pl.z):dist(v.pos) < 30 then -- check if we're aiming at the enemy.
v.hp = v.hp - 4 -- lower HP.
v.hitTime = 0.5 -- set the timer for the red tint.
break -- We don't want the bullets to be able to pierce.
end
end
end
for gi,g in ipairs(guards) do -- iterate over each guard from farthest to closest
if g.hp < 1 then -- if the guard's health is below 1, remove it
table.remove(guards,gi)
else
local ch = vec2(pl.x - g.pos.x,pl.z - g.pos.y):normalize() -- direction to player
local old = vec2(g.pos.x,g.pos.y) -- old position
g.pos.x,g.pos.y = 10 * ch.x + g.pos.x, 10 * ch.y + g.pos.y -- move in direction to player
local x,z = g.pos.x,g.pos.y
for _,v in ipairs(town.houses) do -- iterate over each house to see if the guards colliding.
if x > v.x - 230 and x < v.x + 230 and z < v.z + 230 and z > v.z - 230 then -- if inside house
if old.x > v.x - 230 and old.x < v.x + 230 then -- We can check like as if the world is 2D because the guards will never be able to jump.
g.pos.y = v.z + (old.y > v.z and 230 or -230)
elseif old.y > v.z - 230 and old.y < v.z + 230 then
g.pos.x = v.x + (old.x > v.x and 230 or -230)
end
break -- The houses won't be close enough for a guard to be colliding with 2 simultaneously
end
end
for _,v in ipairs(guards) do -- iterate over the guards to see if this guards colliding w/ another
if g ~= v and v.pos:dist(g.pos) < 80 then -- if colliding then
local ch = vec2(v.pos.x - g.pos.x,v.pos.y - g.pos.y):normalize() * 80
v.pos.x,v.pos.y = g.pos.x + ch.x, g.pos.y + ch.y
end
end
translate(g.pos.x,0,g.pos.y) -- Now we translate to the guard's position.
rotate(math.deg(math.atan(pl.x-g.pos.x,pl.z-g.pos.y)),0,1,0) -- rotate towards the player.
if g.hitTime > 0 then -- if the guards timer for the red tint
g.hitTime = g.hitTime - DeltaTime
guardM:setColors(255,0,0)
else
guardM:setColors(255,255,255)
end
guardM:draw()
--[[ -- uncomment to display all the guards positions
local dist = ((pl.x-g.pos.x)^2 + (pl.z-g.pos.y)^2)^.5
local yt = pl.y + 160 + math.tan(pl.rotation.y) * dist
stroke(0)
line(-40,yt,40,yt)
translate(0,90,20)
fill(0)
text(gi, 0,0)
--]]
resetMatrix()
end
end
viewMatrix(emptyMatrix) -- go back to 2D drawing for the UI
ortho()
UI.draw() -- draw the buttons
translate(512,384) -- middle of the screen
stroke(255)
strokeWidth(5)
line(-30,0,30,0) -- draw the crosshair
line(0,-30,0,30)
translate(-392,-264) -- goto bottom left of screen
stroke(0)
fill(200)
ellipse(0,0,160) -- draw the joystick
translate(town.jx,town.jy)
ellipse(0,0,50)
end
--[ -- Uncomment to spawn guards
for i=1, 8 do -- Change the 8 to change the amount of guards are spawned
town.guards[i] = {hitTime = 0, pos = vec2(math.random(-3000,-1000),math.random(0,2000)),hp=15}
end
--]]
Worlds.town = town
town.mesh.texture = readImage("SpaceCute:Background")
local t = {vec2(0,1),vec2(1,1),vec2(0,0),vec2(1,0)}
Worlds.addFloor(0,0,15000,15000,0,{vec2(0,1),vec2(1,1),vec2(0,0),vec2(1,0)},color(170, 80, 40),town) -- ground
Worlds.addHouse(0,0,t,t,t)
Worlds.addHouse(500,0,t,t,t)
Worlds.addHouse(1000,0,t,t,t)
end
-----------------------------------------UI------------------------------------------
UI = {}
function UI.createImage(t)
local img = image(#t[1],#t)
for x=1, img.width do
for y=1, img.height do
img:set(x,y,t[x][y])
end
end
return img
end
function UI.draw()
if World.noUI then return end
noSmooth()
if World.cButton then
World.cButton.mesh:draw()
fill(230)
font("ArialMT")
for i,b in pairs(World.cButton.all) do
if b.text then
fontSize(0.3*b.size)
text(b.text,b.pos.x,b.pos.y)
end
end
end
end
local inRect = UI.inRect
local posX,posY = 120,120 -- joystick position
local pi = math.pi
function UI.touch() -- touch detection for the UI
for tIndex,t in pairs(touches) do
local usingUi = false
if not World.noUI and World.cButton then
local cb = World.cButton
for _,b in pairs(cb.all) do
if vec2(t.x,t.y):dist(b.pos) < b.size*.5 and t.state == BEGAN then
b.touch = t
cb.mesh:setRectColor(b.id,30,50,127.5)
end
if b.touch == t then
usingUi = true
if t.state == ENDED then
b.touch = nil
touches[tIndex] = nil
if b.callback then b.callback() end
cb.mesh:setRectColor(b.id,60,100,255)
break
end
end
end
end
-- touch detection for joystick
if World == Worlds.town then
if vec2(t.x,t.y):dist(vec2(posX,posY)) < 80 then
if t.state == BEGAN and World.jTouch == nil then
World.jTouch = t
end
if World.jTouch == t then
World.jx,World.jy = t.x-posX,t.y-posY
usingUi = true
end
elseif World.jTouch == t then
usingUi = true
local pos = vec2(t.x-posX,t.y-posY):normalize()
World.jx,World.jy = pos.x * 80, pos.y * 80
end
if t == World.jTouch then
if t.state == ENDED then
World.jTouch = nil
World.jx,World.jy = 0,0
touches[t.id] = nil
usingUi = true
end
end
end
if not usingUi then -- if this touch is not pressing any buttons or anything, then
local rot = Player.rotation
local deltX = t.deltaX
local change = rot.x + deltX*.012
rot.x = change % 360 == 0 and (deltX < 0 and -1 or 1) or change
rot.y = rot.y + t.deltaY*.012
touches[t.id] = nil
if rot.y > pi*.5 then
rot.y = pi*.5
elseif rot.y < pi*-.5 then
rot.y = pi*-.5
end
end
end
end
displayMode(FULLSCREEN)
supportedOrientations(LANDSCAPE_ANY)
function setup()
World = Worlds.intro -- set this to Worlds.town to skip the intro
touches = {} -- table to keep all the touches
do
local img = image(150,150) -- texture for the buttons
setContext(img)
stroke(200)
strokeWidth(8)
fill(255)
ellipse(75,75,150)
setContext()
UI.cButtonImg = img
--[[ FUNCTION DESCRIPTION: UI.addCircleButton ===================================
Below, the parameters between <> are mandatory and those between [] are optional.
3 parameters:
<world (table)>: The world you wish to own this button
<b (table)>: The details of the button
<key> (string): The key of the button
b can include details such as:
<pos (2D vector)>: The x and y position of the button
[text (string)]: The text to be displayed over the button
[callback (function)]: The function to be called when the button is pressed
[size (number)]: The size of the button (defaults to 100)
NOTE: world must contain a table, cButton. If not, an error WILL occur
--]]
function UI.addCircleButton(world,b,key)
local cb = world.cButton
if not cb.mesh then
local m = mesh()
cb.mesh = m
m.texture = img
end
b.size = b.size or 100
b.id = cb.mesh:addRect(b.pos.x,b.pos.y,b.size,b.size)
cb.mesh:setRectTex(b.id,0,0,1,1)
cb.mesh:setRectColor(b.id,60,100,255)
if cb.all then
cb.all[key] = b
else
cb.all = {[key] = b}
end
end
end
local town = Worlds.town
UI.addCircleButton(town, {size = 100, pos = vec2(70,708), text = "❚❚"}, "pause")
local pl = Player
UI.addCircleButton(town, {size = 100, pos = vec2(939,240), text = "⇪",callback = function()
if pl.onGround then
pl.vel = 26 -- height of jump
end
end}, "jump")
UI.addCircleButton(town, {size = 120, pos = vec2(929,698), text = "⇵"}, "changeWeapon")
local torad = math.pi/180
UI.addCircleButton(town, {size = 150, pos = vec2(914,110), text = "⚡️",callback = function()
pl.shooting = true
end}, "shoot")
end
function draw()
background(World.sky)
UI.touch()
World.draw() -- Draw the current world (there are two)
end
function touched(t)
local T = touches[t.id]
if T then
T.state,T.x,T.y,T.deltaX,T.deltaY = t.state,t.x,t.y,t.deltaX,t.deltaY -- Avoid creating a new table.
else
touches[t.id] = {state=t.state,x=t.x,y=t.y,id=t.id,deltaX=t.deltaX,deltaY=t.deltaY}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment