diff --git a/lib/tick.lua b/lib/tick.lua new file mode 100644 index 0000000..0bfda03 --- /dev/null +++ b/lib/tick.lua @@ -0,0 +1,168 @@ +-- +-- tick +-- +-- Copyright (c) 2015 rxi +-- +-- This library is free software; you can redistribute it and/or modify it +-- under the terms of the MIT license. See LICENSE for details. +-- Original Source: https://github.com/rxi/tick/blob/master/tick.lua +-- + + +local tick = { _version = "0.1.1" } +tick.__index = tick + + +local iscallable = function(x) + if type(x) == "function" then return true end + local mt = getmetatable(x) + return mt and mt.__call ~= nil +end + +local noop = function() +end + + +local event = {} +event.__index = event + +function event.new(parent, fn, delay, recur, err) + err = err or 0 + -- Create and return event + return setmetatable({ + parent = parent, + delay = delay, + timer = delay + err, + fn = fn, + recur = recur, + }, event) +end + + +function event:after(fn, delay) + -- Error check + if self.recur then + error("cannot chain a recurring event") + end + -- Chain event + local oldfn = self.fn + local e = event.new(self.parent, fn, delay, false) + self.fn = function() + oldfn() + e.timer = e.timer + self.parent.err + self.parent:add(e) + end + return e +end + + +function event:stop() + tick.remove(self.parent, self) +end + + + +function tick.group() + return setmetatable({ err = 0 }, tick) +end + + +function tick:add(e) + self[e] = true + table.insert(self, e) + return e +end + + +function tick:remove(e) + if type(e) == "number" then + -- Remove and return event + local idx = e + e = self[idx] + self[e] = nil + self[idx] = self[#self] + table.remove(self) + return e + end + self[e] = false + for i, v in ipairs(self) do + if v == e then + return self:remove(i) + end + end +end + + +function tick:update(dt) + for i = #self, 1, -1 do + local e = self[i] + e.timer = e.timer - dt + while e.timer <= 0 do + if e.recur then + e.timer = e.timer + e.delay + else + self:remove(i) + end + self.err = e.timer + e.fn() + if not e.recur then + break + end + end + end + self.err = 0 +end + + +function tick:event(fn, delay, recur) + delay = tonumber(delay) + -- Error check + if not iscallable(fn) then + error("expected `fn` to be callable") + end + if type(delay) ~= "number" then + error("expected `delay` to be a number") + end + if delay < 0 then + error("expected `delay` of zero or greater") + end + -- If, factoring in the timing error, the event should happen *now* the + -- function is immediately called and the error is temporarily carried + -- through. This assures nested events with delays shorter than the update() + -- delta-time do not accumulate error; several nested events with very small + -- delays may end up being called on the same frame. A dummy event is created + -- and returned so :after() still functions correctly. + local d = delay + self.err + if d < 0 then + local err = self.err + self.err = d + fn() + self.err = err + return self:add(event.new(self, noop, delay, recur, self.err)) + end + -- Create, add and return a normal event + return self:add(event.new(self, fn, delay, recur, self.err)) +end + + +function tick:delay(fn, delay) + return self:event(fn, delay, false) +end + + +function tick:recur(fn, delay) + return self:event(fn, delay, true) +end + + +local group = tick.group() + +local bound = { + update = function(...) return tick.update(group, ...) end, + delay = function(...) return tick.delay (group, ...) end, + recur = function(...) return tick.recur (group, ...) end, + remove = function(...) return tick.remove(group, ...) end, +} +setmetatable(bound, tick) + +return bound diff --git a/main.lua b/main.lua index c7d36e5..0bcd1b0 100644 --- a/main.lua +++ b/main.lua @@ -1,49 +1,35 @@ function love.load() - -- Load all required classes - Object = require "lib/classic" - require "src/entity" - require "src/canvas" - require "src/example_player" - require "src/example_enemy" - require "src/map" - - -- Initiate game field - - Game_field = Canvas() - -- initiate player and an enemy - player = Player( - Game_field.width/2 - ,Game_field.height - ,50,50,10,500,500,Game_field) + -- Load the class functions + Object = require "lib/classic" - enemy = Enemy( - 0,0 - ,50,50,70,Game_field) + -- Load the Game context + require("src/game_context") + Context = Game_Context() - -- to measure the duration of any key being pressed + -- Initiate the keys constants (speed of players depends on how long a key is pressed) key_counter =0 key_pressed =false end function love.update(frame_rate) - Game_field:update() - + if key_pressed then key_counter = key_counter +1; end - player:update(frame_rate,key_counter*frame_rate) - enemy:update(frame_rate) + -- Updat the game context passing the time a key was pressed + Context:update(frame_rate,key_counter) + end function love.draw() - Game_field:draw() - player:draw() - enemy:draw() + + Context:draw() + end -- Callback functions for key pressed diff --git a/src/canvas.lua b/src/canvas.lua index c991722..263749d 100644 --- a/src/canvas.lua +++ b/src/canvas.lua @@ -3,9 +3,10 @@ Canvas = Object:extend() -function Canvas:new() - self.width,self.height = love.graphics.getDimensions() - self.map = Map(EXAMPLE_ARENA_1, self.width, self.height) +function Canvas:new(width,height,map) + self.width = width + self.height = height + self.map = map end diff --git a/src/example_enemy.lua b/src/example_enemy.lua index 7eab50b..e296aba 100644 --- a/src/example_enemy.lua +++ b/src/example_enemy.lua @@ -3,14 +3,12 @@ Enemy = Entity:extend() -function Enemy:new(x,y,w,h,speed,canvas) +function Enemy:new(x,y,w,h,speed) Enemy.super.new(self, x,y,w,h ) -- Metadata self.name = "Alien" self.image = love.graphics.newImage("graphics/player_example.png") - self.max_x = canvas.width - self.max_y = canvas.height -- The scale between the actual width and height of the player image and -- The desired dimensions of the player entity @@ -34,3 +32,15 @@ function Enemy:update(dt) local ux, uy = 0, 1 -- Unit vector to move downward self.move(self,ux,uy,distance) end + +function Enemy:within_boundaries(max_x,max_y) + if self.x < 0 + or (self.x+self.w) > max_x + or self.y < 0 + or (self.y+self.h) > max_y + then + return false + else + return true + end +end \ No newline at end of file diff --git a/src/example_player.lua b/src/example_player.lua index 0817a31..c6894a3 100644 --- a/src/example_player.lua +++ b/src/example_player.lua @@ -6,15 +6,13 @@ Player = Entity:extend() --constructor -function Player:new(x,y,w,h,speed,acceleration, max_speed,canvas) +function Player:new(x,y,w,h,speed,acceleration, max_speed) Player.super.new(self, x,y,w,h) -- MetaData self.name = "Player" self.image = love.graphics.newImage("graphics/enemy_example.png") - self.max_x = canvas.width - self.max_y = canvas.height - + -- The scale between the actual width and height of the player image and -- The desired dimensions of the player entity self.scale_w = self.w/self.image:getWidth() @@ -61,9 +59,19 @@ function Player:update(dt,duration_since_button_clicked) Player.move(self,ux,uy,distance) - -- Ensure the player stays within the game field boundary - if self.x < 0 then self.x = 0 end - if (self.x+self.w) > self.max_x then self.x = self.max_x-self.w end - if self.y < 0 then self.y = 0 end - if (self.y+self.h) > self.max_y then self.y = self.max_y-self.h end +end +function Player:within_boundaries(max_x,max_y) + if self.x < 0 then + self.x = 0 + end + if (self.x+self.w) > max_x then + self.x = max_x-self.w + end + if self.y < 0 then + self.y = 0 + end + + if (self.y+self.h) > max_y then + self.y = max_y-self.h + end end diff --git a/src/game_context.lua b/src/game_context.lua new file mode 100644 index 0000000..64ba603 --- /dev/null +++ b/src/game_context.lua @@ -0,0 +1,88 @@ + +-- This is an entry point class which encapsulate all the game components +-- And makes sure the entities behavior does not violate the game rules +-- Any Interaction between the game entities should be implemented in this class + +Game_Context = Object:extend() + +-- initiates the game componentes +function Game_Context:new() + -- get the width and heights of the canvas + max_width,max_height = love.graphics.getDimensions() + + -- initiate game map + require "src/map" + self.map =Map(EXAMPLE_ARENA_1, max_width, max_height) + + -- Initiate game field + require "src/canvas" + self.Game_field = Canvas(max_width,max_height,self.map) + + + -- initiate player and + require "src/entity" + require "src/example_player" + self.player = self.create_player(self,self.Game_field.width/2,self.Game_field.height,10,500); + + -- initiate enemies + require "src/example_enemy" + math.randomseed(os.time()) -- increase the randomness of the enemy position + self.enemy_number =4 -- This constant should come from level config file + self.enemies={} -- an array containing the game enemies + self.enemy_counter =self.enemy_number -- counts how many enemies still alive + + for e =1,self.enemy_number do + -- randomly choose a position for the enemy witihin 10%-90% of the width of the canvas + local x_pos = math.random(self.Game_field.width*0.1,self.Game_field.width*0.9) + self.enemies[e]= self.create_enemy(self,x_pos,0,70) + end + + +end + +function Game_Context:update(frame_rate , key_counter) + + -- Update game components + self.Game_field:update() + + self.player:update(frame_rate,key_counter*frame_rate) + + for i,e in ipairs(self.enemies)do + e:update(frame_rate) + end + + -- Ensure the player stays within the game field boundary + self.player:within_boundaries(self.Game_field.width,self.Game_field.height) + + -- if an enemy gets out of the game field box kill it and reduce the enemy counter + for i,e in ipairs(self.enemies) do + if e:within_boundaries(self.Game_field.width,self.Game_field.height) ~= true then + table.remove(self.enemies,i) + self.enemy_counter = self.enemy_counter-1 + end + end +end + + +function Game_Context:draw() + + self.Game_field:draw() + self.player:draw() + for i,e in ipairs(self.enemies)do + e:draw(frame_rate) + end +end + + +function Game_Context:create_player(init_x,init_y,init_speed,acceleration) + + player = Player(init_x,init_y + ,50,50,init_speed,acceleration,500) + return player +end +function Game_Context:create_enemy(pos_x,pos_y,speed) + enemy = Enemy( + pos_x,pos_y + ,50,50,speed) + return enemy +end