Repository: miko/Love2d-samples Branch: master Commit: 50a8b4b43d51 Files: 47 Total size: 64.7 KB Directory structure: gitextract_xy8xzx00/ ├── CodeCapture/ │ ├── CodeCapture.lua │ ├── README.md │ └── main.lua ├── CollisionExample/ │ ├── README.md │ └── main.lua ├── Fireworks/ │ ├── Firework.lua │ ├── FireworkEngine.lua │ ├── Particle.lua │ ├── README.md │ ├── Vector.lua │ ├── class.lua │ └── main.lua ├── GameOfLife/ │ ├── Readme.md │ └── main.lua ├── LICENSE ├── MikoIntroScreen/ │ ├── Intro.lua │ ├── README.md │ ├── class.lua │ ├── conf.lua │ ├── main.lua │ └── zero-project - 01 - Celtic dream.ogg ├── Minefield/ │ ├── conf.lua │ ├── main.lua │ └── menu.lua ├── Obey/ │ ├── B.lua │ ├── Char.lua │ ├── E.lua │ ├── O.lua │ ├── README.md │ ├── Y.lua │ ├── class.lua │ └── main.lua ├── README.md ├── SidescrollerCollision/ │ ├── README.md │ └── main.lua ├── SpriteSheet/ │ ├── README.md │ ├── SpriteSheet.lua │ └── main.lua ├── TexturedPolygon/ │ ├── README.md │ └── main.lua └── VerbNounParser/ ├── Console.lua ├── Door.lua ├── Key.lua ├── Parser.lua ├── README.md ├── Window.lua └── main.lua ================================================ FILE CONTENTS ================================================ ================================================ FILE: CodeCapture/CodeCapture.lua ================================================ local M={} M.KONAMI={'up','up','down','down','left','right','left','right','b','a'} local STATES={} local CURRSTATE local function setNextState(char, state) if not state then state=STATES end if not state[char] then state[char]={} end return state[char] end local function getNextState(char, state) if not state then state=STATES end return state[char] end function M.setCode(c, fn) if type(c)=='string' then local c2={} for char in c:gmatch('.') do c2[#c2+1]=char end c=c2 end local st for k,v in ipairs(c) do st=setNextState(v, st) end st.done=fn end function M.keypressed(a) CURRSTATE=getNextState(a, CURRSTATE) if CURRSTATE then if CURRSTATE.done then CURRSTATE.done() CURRSTATE=nil else --tracking... end else -- start from beginning CURRSTATE=getNextState(a) end end return M ================================================ FILE: CodeCapture/README.md ================================================ CodeCapture =========== This sample is inspired by the [KonamiCode] thread. This is a simple library, which can be used to register a sequence of codes (key and/or mouse pressess), ofr which the given function should be called. The code is implemented as a simple finite state machine, so you can register many different codes at the same time, and if they share a common prefix, they also share the state. This sample has several magic codes: * "qwerty" * "second" * "secundo" * KONAMI (UP UP DOWN DOWN LEFT RIGHT LEFT RIGHT b a) * a MOUSE-LEFT b MOUSE-RIGHT * "quit" and "exit" Each sequence causes the change of text displayed, except the last ones, which quit the program. [KonamiCode]: http://love2d.org/forums/viewtopic.php?f=5&t=2632 ================================================ FILE: CodeCapture/main.lua ================================================ local CodeCapture=require 'CodeCapture' function love.load() CodeCapture.setCode("qwerty", function() MODE='ONE' end) CodeCapture.setCode('second', function() MODE='TWO' end) CodeCapture.setCode('secundo', function() MODE='DUO' end) CodeCapture.setCode(CodeCapture.KONAMI, function() MODE='KONAMI' end) CodeCapture.setCode({'a','mouse-l','b','mouse-r'}, function() MODE='WITH MOUSE' end) CodeCapture.setCode('quit', function() love.event.quit() end) CodeCapture.setCode('exit', function() love.event.quit() end) MODE='NONE' end function love.draw() love.graphics.print(MODE, 10, 10) end function love.keypressed(a,b) CodeCapture.keypressed(a) end function love.mousepressed(x,y,b) CodeCapture.keypressed('mouse-'..b) end ================================================ FILE: CollisionExample/README.md ================================================ Collision example =========== This sample is inspired by the [RestrictRectangleMovement] thread. The sample illustrates (in just one of many ways) how you can restrict movement of your cursor (character) to stay on screen and not go thru other obstacles. How the collision works. ======================== Every new frame function love.update() checks if any of the arrow keys is pressed, and if so, computes the new position of the character, keeping the old position ofr reference. Then the function checks if this new position is off-screen (by calling isOnScreen()), or if the character at this new position collides with any other object. If there is collision, then the move is not allowed, so the previously stored position is recalled, and the new position is discarded. Every object has its position x,y and its size - width w and height. So the collision is detected when any two rectangles collide. [RestrictRectangleMovement]: http://love2d.org/forums/viewtopic.php?f=3&t=5072 ================================================ FILE: CollisionExample/main.lua ================================================ local lg=love.graphics function love.load() W, H=lg.getWidth(), lg.getHeight() local w,h=40,40 Obstacles={} for i=1,20 do table.insert(Obstacles, {x=math.random(W-w), y=math.random(H-h), w=w, h=h}) end w,h=20,20 Cursor={x=math.random(W-w), y=math.random(H-h), w=w, h=h} while isColliding() do Cursor.x, Cursor.y=math.random(W-w), math.random(H-h) end collision=false end function love.draw() lg.setColor(255,0,0,255) for k,v in ipairs(Obstacles) do lg.rectangle('fill', v.x, v.y, v.w, v.h) end lg.setColor(0, 255,0,255) lg.rectangle('fill', Cursor.x, Cursor.y, Cursor.w, Cursor.h) lg.setColor(255, 255,255,255) if collision then lg.print('COLLISION!!!', W/2, 0) end end local isDown=love.keyboard.isDown function love.update(dt) local dx,dy=0,0 if isDown('right') or isDown('d') then dx=1 elseif isDown('left') or isDown('a') then dx=-1 elseif isDown('up') or isDown('w') then dy=-1 elseif isDown('down') or isDown('s') then dy=1 end local currX, currY=Cursor.x, Cursor.y Cursor.x, Cursor.y=Cursor.x+dx, Cursor.y+dy collision=false if not isOnScreen() or isColliding() then Cursor.x, Cursor.y=currX, currY collision=true end end function isOnScreen() if Cursor.x>0 and Cursor.x+Cursor.w0 and Cursor.y+Cursor.hCursor.w or oy>Cursor.h then return false else return true end end function isColliding() for k,v in ipairs(Obstacles) do if isCollidingWith(v) then return true end end return false end ================================================ FILE: Fireworks/Firework.lua ================================================ require 'class' local Vector=require 'Vector' local Particle=require 'Particle' STATE={ROCKET=1, EXPLODE=2, LIVE=3, DEAD=4} local M=class(function(self, x, y) self.Position=Vector(x, y) self.state=STATE.ROCKET self.Particles={} self.numParticles=math.random(150, 450) self.elapsed=0 self.R=math.random(50, 255) self.G=math.random(50, 255) self.B=math.random(50, 255) self.StartPosition=Vector(math.random(0, love.graphics.getWidth()), love.graphics.getHeight()) self.elapsed=0 self.livetime=math.random(3,20)/10 end) function M:update(dt) self.elapsed=self.elapsed+dt local state=self.state if state==STATE.LIVE or state==STATE.EXPLODE then if state==STATE.EXPLODE then for i=1,math.random(50,120) do self:addParticle(self.Position.x, self.Position.y) end if #self.Particles>= self.numParticles then self.state=STATE.LIVE end end for k=#self.Particles,1,-1 do local p=self.Particles[k] if p:isDead() then table.remove(self.Particles, k) else p:update(dt) end end if state==STATE.LIVE and #self.Particles==0 then self.state=STATE.DEAD end elseif state==STATE.DEAD then return elseif state==STATE.ROCKET then local pct=self.elapsed/self.livetime*100 if pct>=100 then self.elapsed=0 self.state=STATE.EXPLODE end else error('UNKNOWN STATE: '..state) end end function M:draw() if self.state==STATE.ROCKET then local pct=self.elapsed/self.livetime local d=(self.StartPosition-self.Position)*pct local d2=(self.StartPosition-self.Position)*(pct+0.03) local pos1=self.StartPosition-d local pos2=self.StartPosition-d2 love.graphics.setColor(self.R, self.G, self.B, 255) love.graphics.setLineWidth(3) love.graphics.line(pos1.x, pos1.y, pos2.x, pos2.y) love.graphics.circle('fill', self.StartPosition.x, self.StartPosition.y, 10, 10) else for k,v in ipairs(self.Particles) do v:draw() end end end function M:isDead() return self.state==STATE.DEAD end function M:addParticle(x, y) table.insert(self.Particles, Particle(x, y, self.R, self.G, self.B)) end return M ================================================ FILE: Fireworks/FireworkEngine.lua ================================================ require 'class' Firework=require 'Firework' local M=class(function(self) self.Fireworks={} end) function M:update(dt) for k=#self.Fireworks,1,-1 do local f=self.Fireworks[k] if f:isDead() then table.remove(self.Fireworks, k) else f:update(dt) end end end function M:draw() for k,v in ipairs(self.Fireworks) do v:draw() end love.graphics.setColor(255,255,255,255) love.graphics.print('Fireworks: '..#self.Fireworks, 10, 10) end function M:addFirework(x, y) table.insert(self.Fireworks, Firework(x, y)) end return M ================================================ FILE: Fireworks/Particle.lua ================================================ require 'class' local Vector=require 'Vector' local lg=love.graphics local M=class(function(self, x, y, R, G, B, v) self.dead=false self.Position=Vector(x, y) self.Velocity=Vector(math.random(10,50),0):rotate(math.random(0, 360)) self.elapsed=0 self.livetime=math.random(20,50)/10 self.R=R or math.random(50, 255) self.G=G or math.random(50, 255) self.B=B or math.random(50, 255) self.variable=v or 'R' self[self.variable]=math.random(50, 255) end) function M:update(dt) self.elapsed=self.elapsed+dt self.Position=self.Position+self.Velocity*dt self.Velocity=self.Velocity+Vector(0,50)*dt if self.elapsed>=self.livetime then self.dead=true end end function M:isDead() return self.dead end function M:draw() local alpha=math.ceil(255-255*(self.elapsed/self.livetime)) if alpha<0 then alpha=0 end lg.setColor(self.R, self.G, self.B, alpha) lg.circle('fill', self.Position.x, self.Position.y, 2, 7) end return M ================================================ FILE: Fireworks/README.md ================================================ Fireworks example ============== This is an example of fireworks, inspired by the [facepunch] thread, which is mentioned in [love2d] forum. Nothing complicated here, just keep clicking with lefto mouse button. [love2d]: http://love2d.org/forums/viewtopic.php?f=3&t=3909 [facepunch]: http://www.facepunch.com/threads/1136691 ================================================ FILE: Fireworks/Vector.lua ================================================ require 'class' local M=class(function(self, x, y) self.x=x or 0 self.y=y or 0 end) function M:isVector() return getmetatable(self)==M end function M:__add(v) return M(self.x+v.x, self.y+v.y) end function M:__sub(v) return M(self.x-v.x, self.y-v.y) end function M:__mul(m) if type(self)=='number' and M.isVector(m) then self, m=m, self end if type(m)=='number' then return M(self.x*m, self.y*m) else return self.x*m.x+self.y*m.y end end function M:len() return (self*self)^0.5 end function M:rotate(phi) phi=math.rad(phi) local c, s = math.cos(phi), math.sin(phi) self.x, self.y = c * self.x - s * self.y, s * self.x + c * self.y return self end function M:__tostring() return string.format('<%s,%s>', self.x, self.y) end return M ================================================ FILE: Fireworks/class.lua ================================================ --[[ -- $Id: class.lua 10 2008-07-01 22:18:32Z basique $ -- Simple class implementation -- Taken from http://lua-users.org/wiki/SimpleLuaClasses -- Original author unknown --]] function class(base,ctor) local c = {} -- a new class instance if not ctor and type(base) == 'function' then ctor = base base = nil elseif type(base) == 'table' then -- our new class is a shallow copy of the base class! for i,v in pairs(base) do c[i] = v end c._base = base end -- the class will be the metatable for all its objects, -- and they will look up their methods in it. c.__index = c -- expose a ctor which can be called by () local mt = {} mt.__call = function(class_tbl,...) local obj = {} setmetatable(obj,c) if ctor then ctor(obj,...) else -- make sure that any stuff from the base class is initialized! if base and base.init then base.init(obj,...) end end return obj end c.init = ctor c.is_a = function(self,klass) local m = getmetatable(self) while m do if m == klass then return true end m = m._base end return false end setmetatable(c,mt) return c end ================================================ FILE: Fireworks/main.lua ================================================ FireworkEngine=require 'FireworkEngine' function love.load() FE=FireworkEngine() love.graphics.setBlendMode('additive') end function love.update(dt) FE:update(dt) end function love.draw() FE:draw() end function love.mousepressed(x, y, b) if b=='l' then FE:addFirework(x, y) end end function love.keypressed(k) if k=='q' or k=='escape' then love.event.quit() end end ================================================ FILE: GameOfLife/Readme.md ================================================ Game of Life ============ Game of Life inspired by [this thread](http://love2d.org/forums/viewtopic.php?f=4&t=2858) Controls: 0, 1, 2 - set cell border width left,right - decrease/increase no of columns up, down - decrease/increase no of rows +/- - increase/decrease cell size n - next cell generation r,space - toggle running mode c - clear the table x - make some random cells alive q,escape - quit left mouse button - toggle cell dead/alive right mouse button - check the status of a cell Sample screenshot: ------------------ ![Screenshot](../../raw/master/GameOfLife/life.png) ================================================ FILE: GameOfLife/main.lua ================================================ -- Game of Life by miko -- Controls: -- 0, 1, 2 - set cell border width -- left,right - decrease/increase no of columns -- up, down - decrease/increase no of rows -- +/- - increase/decrease cell size -- n - next cell generation -- r,space - toggle running mode -- c - clear the table -- x - make some random cells alive -- q,escape - quit -- left mouse button - toggle cell dead/alive -- right mouse button - check the status of a cell local lg=love.graphics function love.load() rows, cols=30,30 cellsize=20 lineWidth=1+1 elapsed=0 tick=0.1 canvas=lg.newCanvas() lg.setNewFont(20) status=nil init() end function init() CELLS={} for c=1,cols do CELLS[c]={} for r=1,cols do CELLS[c][r]=false end end generation=1 end function love.draw() if not cached then cached=true lg.setCanvas(canvas) if lineWidth>0 then lg.setLineWidth(lineWidth) end for c=1,cols do for r=1,rows do local cell=CELLS[c][r] if cell then lg.setColor(255,0,0,255) else lg.setColor(0,0,0,255) end lg.rectangle('fill', (c-1)*cellsize, (r-1)*cellsize, cellsize, cellsize) if lineWidth>0 then lg.setColor(255,255,255,255) lg.rectangle('line', (c-1)*cellsize, (r-1)*cellsize, cellsize, cellsize) end end end lg.setCanvas() end lg.setColor(255,255,255,255) lg.draw(canvas, 0, 0, 0) local msg=string.format("Cols=%d Rows=%d CellSize=%d Gen=%d %s RUNNING FPS=%d ", cols, rows, cellsize, generation, running and '' or 'NOT', love.timer.getFPS()) if status then msg=msg.."\n"..status end lg.setColor(0,255,0,255) lg.print(msg, 0,0,0) lg.setColor(0,0,255,255) lg.print(msg, 1,1,0) lg.setColor(255,0,0,255) lg.print(msg, 2,2,0) end function mouse2cell(x, y) local cx, cy=math.floor(x/cellsize), math.floor(y/cellsize) if cx>=0 and cx=0 and cy=tick then elapsed=elapsed-tick nextGeneration() cached=nil end end function love.mousepressed(x, y, b) --local cx, cy=mouse2cell(x, y) local mr, mc=mouse2cell(x, y) status=nil if mr then if b=='l' then CELLS[mc][mr]=not CELLS[mc][mr] status=string.format('Cell c=%d,r=%d changed to %s', mc, mr, CELLS[mc][mr] and 'ALIVE' or 'DEAD') elseif b=='r' then status=string.format('Cell c=%d,y=%d %s neighbours: %d', mc, mr, CELLS[mc][mr] and 'ALIVE' or 'DEAD', countNeighbours(mc, mr)) end cached=nil end end function love.keypressed(a, b) if a=='q' or a=='escape' then love.event.push("q") end if a=='r' or a==' ' then running=not running end if a=='n' then nextGeneration() cached=nil end if a=='c' then init() cached=nil end if a=='1' or a=='2' or a=='0' then lineWidth=tonumber(a) cached=nil end if a=='down' then rows=rows+1 cached=nil init() end if a=='up' then rows=rows-1 cached=nil init() end if a=='right' then cols=cols+1 cached=nil init() end if a=='left' then cols=cols-1 cached=nil init() end if a=='+' or a=='=' then cellsize=math.min(cellsize+1, 30) cached=nil end if a=='-' or a=='_' then cellsize=math.max(cellsize-1, 1) cached=nil end if a=='x' then for i=1,cols*rows*0.1 do CELLS[math.random(1,cols)][math.random(1, rows)]=true end cached=nil end end function countNeighbours(c, r) local n=0 local cy=c local cx=r if cx>1 and cy>1 then if CELLS[cy-1][cx-1] then n=n+1 end end if cy>1 then if CELLS[cy-1][cx] then n=n+1 end end if cx1 then if CELLS[cy-1][cx+1] then n=n+1 end end if cx>1 then if CELLS[cy][cx-1] then n=n+1 end end if cx1 and cy', self.ctype, self.name or 'unnamed') end local Object=O -- local M=class(function(self) self.Objects={} self:setDuration(6) self:setDelay(2) end) function M:addText(txt) local obj=Object(txt) table.insert(self.Objects, obj) return obj end function M:addImage(filename) local img if type(filename)=='string' then img=lg.newImage(filename) else img=filename end local obj=Object(img) table.insert(self.Objects, obj) return obj end function M:addAudio(audio) if type(audio)=='string' then audio=love.audio.newSource(audio, 'static') end local obj=Object(audio, 'audio') table.insert(self.Objects, obj) return obj end function M:setDuration(d) self.duration=d or 6 return self end function M:setDelay(d) self.delay=d or 2 return self end function M:makeChaos() return self end -- len - in seconds, just before duration function M:setBlinks(len, obj, ...) if not obj then return self end self.blink=len self.blinkObjects=self.blinkObjects or {} table.insert(self.blinkObjects, obj) return self:setBlinks(len, ...) end function M:_setupBlinks() local count=0 for k,v in pairs(self.blinkObjects) do count=count+#v._DATA end self.blinkChars=count self.blinkdt=self.blink/count function self.nextCharIterator() local T=self.blinkObjects local objKey,obj=next(T) local idxKey, idx return function() idxKey, idx=next(obj._DATA, idxKey) if not idx then objKey,obj=next(T, objKey) if not obj then return end idxKey, idx=next(obj._DATA, idxKey) end return obj, idxKey, idx end end SAMPLES={} for obj, idxk, idxo in self:nextCharIterator() do local c=idxo.char:upper() if not SAMPLES[c] then local x=0 if c==' ' then x=1 elseif c=='!' then x=2 else x=c:byte()-45 -- 49 for "0", 65 for "A" end SAMPLES[c]=makeSample(self.blinkdt, 2^((x-2)/12)*400) end end end function M:start() local s=self save() math.randomseed(os.time()) love.update=function(dt) s:update(dt) end love.draw=function() s:draw() end love.keypressed=function(a, b) s:keypressed(a, b) end self.elapsed=0 lg.setColor(255, 255, 255, 255) for k,v in ipairs(self.Objects) do v:start() end lg.setCanvas() if self.blink then self:_setupBlinks() end return self end function M:stop() for k,v in ipairs(self.Objects) do v:stop() end restore() return self end function M:keypressed(a, b) if a==' ' then self.paused=not self.paused else self:stop() end end function M:update(dt) if self.paused then return end self.elapsed=self.elapsed+dt if self.elapsed>self.duration+self.delay then self:stop() elseif self.elapsed>self.duration then if self.pct~=100 then self.pct=100 end if self.blink and self.elapsed-self.duration<=self.blink then BLINKDT=(BLINKDT or 0)+dt if BLINKDT>self.blinkdt then BLINKDT=BLINKDT-self.blinkdt if self.objHilited then self.objHilited:setHilited() end --local obj=self.blinkObjects[math.random(1, #self.blinkObjects)] iterator=iterator or self:nextCharIterator() local obj, idxk, idxo=iterator() SRC=SRC or {} local src=love.audio.newSource(SAMPLES[idxo.char:upper()]) src:setVolume(2) src:play() SRC[#SRC+1]=src --obj:setHilited(math.random(#obj._DATA)) obj:setHilited(idxk) self.objHilited=obj end else if self.objHilited then self.objHilited:setHilited() end end return end self.pct=self.elapsed/self.duration*100 end function M:draw() local pct=self.pct if not pct then return end for k,v in ipairs(self.Objects) do v:draw(pct) end end return M ================================================ FILE: MikoIntroScreen/README.md ================================================ LOVEJam Intro Screen =========== This sample is inspired by the [LOVEJam] site. This is a splash screen intro, and also an engine, where you can easily make similar intros for yourself. The intro consists of some linef of texts and some images and audio files. The texts will be split by letters, and the images and letters will be put randomly, then will find their way home. Also, the letters get some random colors at the beginning. [LOVEJam]: http://love2d.org/jam ================================================ FILE: MikoIntroScreen/class.lua ================================================ --[[ -- $Id: class.lua 10 2008-07-01 22:18:32Z basique $ -- Simple class implementation -- Taken from http://lua-users.org/wiki/SimpleLuaClasses -- Original author unknown --]] function class(base,ctor) local c = {} -- a new class instance if not ctor and type(base) == 'function' then ctor = base base = nil elseif type(base) == 'table' then -- our new class is a shallow copy of the base class! for i,v in pairs(base) do c[i] = v end c._base = base end -- the class will be the metatable for all its objects, -- and they will look up their methods in it. c.__index = c -- expose a ctor which can be called by () local mt = {} mt.__call = function(class_tbl,...) local obj = {} setmetatable(obj,c) if ctor then ctor(obj,...) else -- make sure that any stuff from the base class is initialized! if base and base.init then base.init(obj,...) end end return obj end c.init = ctor c.is_a = function(self,klass) local m = getmetatable(self) while m do if m == klass then return true end m = m._base end return false end setmetatable(c,mt) return c end ================================================ FILE: MikoIntroScreen/conf.lua ================================================ function love.conf(t) t.title = "miko lovejam intro" t.author = "miko" t.identity = "mario" t.screen.width = 800 t.screen.height = 600 end ================================================ FILE: MikoIntroScreen/main.lua ================================================ local Intro=require 'Intro' function love.load() I=Intro() local t1=I:addText('LoveJam Test Jam entry!'):setPosition(20, 50):setColor(0, 255, 0):setFont(60):center() local t2=I:addText('Made with Love2d'):setPosition(400, 200):setColor(0, 0, 255):setFont(30) local t3=I:addText('(C) 2011 miko - average Love user'):setPosition(10, 500):setColor(255,0,0):setFont(40):center() I:addImage('logo.png'):setPosition(200, 180) I:addImage('face-grin.png'):setPosition(400, 350) I:addAudio('zero-project - 01 - Celtic dream.ogg') I:setDuration(5) I:setDelay(5) I:setBlinks(2, t1, t2) I:start() end function love.update(dt) love.event.quit() end --[[ function love.keypressed(a, b) love.event.quit() end --]] ================================================ FILE: Minefield/conf.lua ================================================ function love.conf(t) t.modules.joystick = false t.modules.audio = false t.modules.keyboard = true t.modules.event = true t.modules.image = false t.modules.graphics = true t.modules.timer = true t.modules.mouse = false t.modules.sound = false t.modules.physics = false t.console = false t.title = "Minefield" t.author = "MiKo" t.screen.fullscreen = false t.screen.vsync = true t.screen.fsaa = 0 --t.screen.height = 800 --t.screen.width = 1024 end ================================================ FILE: Minefield/main.lua ================================================ local lg=love.graphics ORGWIDTH, ORGHEIGHT=lg.getWidth(), lg.getHeight() local Menu=require 'menu' function love.load() ReplayDelay=0.2 lg.setBackgroundColor(255,255,255) mode='menu' fullscreen=false diagonal=true initScreen() initGame() end function initScreen() W, H=lg.getWidth(), lg.getHeight() --CellSize=30 CellSize=math.floor(math.min(W/25, H/25)) CX,CY=math.floor(W/CellSize), math.floor(H/CellSize) BorderWidthX=(W-(CX-2)*CellSize)/2 BorderWidthY=(H-(CY-2)*CellSize)/2 CX,CY=CX-2,CY-2 lg.setNewFont(CellSize) end function initGame() CenterX=math.floor(CX/2) PX, PY=CenterX, CY+1 -- player position Bombs, Visited={}, {} for x=1,CX do Bombs[x]={} Visited[x]={} end local bomblimit=math.floor(CX*CY/10) for i=1,bomblimit do local x,y=0,0 repeat x, y=math.random(1, CX), math.random(1, CY) until not Bombs[x][y] and not (x==CenterX and (y==1 or y==CY)) Bombs[x][y]=true end crashed=nil won=nil showingBombs=nil History={} elapsed=0 end function drawCell(X, Y, Color) if Color then lg.setColor(Color) end lg.rectangle('fill', BorderWidthX+CellSize*(X-1), BorderWidthY+CellSize*(Y-1), CellSize, CellSize) end function drawPlayer(X, Y) lg.setColor(255, 255, 0) lg.circle('fill', BorderWidthX+CellSize*(X-0.5), BorderWidthY+CellSize*(Y-0.5), CellSize/2, CellSize) end function drawBomb(X, Y) lg.circle('fill', BorderWidthX+CellSize*(X-0.5), BorderWidthY+CellSize*(Y-0.5), CellSize/4, CellSize) end function drawBorders() lg.setColor(0, 0, 255) lg.rectangle('fill', 0, 0, W, BorderWidthY) lg.rectangle('fill', 0, H-BorderWidthY, W, BorderWidthY) lg.rectangle('fill', 0, 0, BorderWidthX, H) lg.rectangle('fill', W-BorderWidthX, 0, BorderWidthX, H) lg.setColor(0, 255, 0) drawCell(math.floor(CX/2), CY+1) drawCell(math.floor(CX/2), 0) end function drawVisited() lg.setColor(200, 100, 100) for x=1,CX do for y=1,CY do if Visited[x][y] then drawCell(x, y) end end end end function countBombs() local n=0 if diagonal then for x=PX-1,PX+1 do for y=PY-1,PY+1 do if Bombs[x] and Bombs[x][y] then n=n+1 end end end else if Bombs[PX-1] and Bombs[PX-1][PY] then n=n+1 end if Bombs[PX+1] and Bombs[PX+1][PY] then n=n+1 end if Bombs[PX] and Bombs[PX][PY-1] then n=n+1 end if Bombs[PX] and Bombs[PX][PY+1] then n=n+1 end end return n end function drawMsgs() local n=countBombs() lg.setColor(255, 0, 0) lg.printf('Bombs: '..n, 0,0, W, 'right') lg.printf(string.format('Time: %d s', elapsed), 0,0, W, 'left') if replaying then lg.printf('Replaying...', 0, H/2, W, 'center') else if crashed then lg.printf('Bum!\n\nPress SPACE to play again, R for replay, M for menu', 0, H/2, W, 'center') end if won then lg.printf('Congratulations! You win in '..math.floor(elapsed)..' seconds!\n\nPress SPACE to play again, R for replay, M for menu', 0, H/2, W, 'center') end end end function drawBombs() lg.setColor(0, 0, 0) for x=1,CX do for y=1,CY do if Bombs[x][y] then drawBomb(x, y) end end end end function drawHelp() lg.setColor(0,0,0) lg.printf([[Your goal is to go from the bottom gate to the top gate of the minefield, without stepping on a mine. The counter at the top right corner shows the number of mines in all 8 neighbouring cells. Use cursor keys/ WASD for movement, q/ESCAPE for quit. In the menu you can change some options (screen resolution, etc). Press any key to return to menu, then select Play.]], W*0.2, H/5, W*0.6, 'center') end function love.draw() if mode=='help' then drawHelp() return elseif mode=='menu' then Menu:draw() return end drawBorders() drawVisited() drawPlayer(PX, PY) if showingBombs then drawBombs() end drawMsgs() end function replayGame() histIndex=1 replaying=true Visited={} for x=1,CX do Visited[x]={} end end function love.keypressed(a, b) if a=='q' or a=='escape' then love.event.quit() end if mode=='menu' then Menu:keypressed(a, b) return elseif mode=='help' then mode='menu' return end if a=='b' then showingBombs=not showingBombs end if replaying then return end if crashed or won then if a==' ' then initGame() end if a=='r' then replayGame() end if a=='m' then mode='menu' end else local moved if (a=='up' or a=='w') and (PY>1 or PX==CenterX) then PY=PY-1; moved=true end if (a=='down' or a=='s') and PY1 and PY<=CY then PX=PX-1; moved=true end if (a=='right' or a=='d') and PXReplayDelay then timer=timer-ReplayDelay local move=History[histIndex] histIndex=histIndex+1 if not move then timer=nil replaying=nil histIndex=nil else PX, PY=unpack(move) Visited[PX][PY]=true end end end function resize(W, H) lg.setMode(W, H, fullscreen , true) initScreen() end function love.quit() fullscreen=false resize(ORGWIDTH, ORGHEIGHT) end ================================================ FILE: Minefield/menu.lua ================================================ local lg=love.graphics local Options={ {name='play', title='Play the game!'}, {name='help', title='Help'}, {name='diagonal', title='Check diagonal positions?', options={'YES','NO'}, value='YES'}, --{name='resolution', title='Screen resolution', options={'1280x1024', '1024x800', '800x600', '640x480', '320x240', '256x192'}, value='1024x800'}, {name='resolution', title='Screen resolution', options={}, value=nil}, {name='fullscreen', title='Fullscreen', options={'YES', 'NO'}, value='NO'}, {name='quit', title='Quit'} } local modes=lg.getModes() for k,v in ipairs(modes) do Options[4].options[k]=v.width..'x'..v.height if v.width==ORGWIDTH and v.height==ORGHEIGHT then Options[4].value=Options[4].options[k] end end table.insert(Options[4].options, '256x192') if not Options[4].value then Options[4].value=Options[4].options[1] end local Menu={ currentOption=1 } function Menu:draw() for k,v in ipairs(Options) do if k==self.currentOption then lg.setColor(50,50,255) else lg.setColor(50,50,50) end if v.value then lg.printf(string.format('%s [%s]', v.title, v.value), 0, (2+k)*CellSize*2, W, 'center') else lg.printf(v.title, 0, (2+k)*CellSize*2, W, 'center') end end end function Menu:keypressed(a, b) if a=='up' or a=='w' then self.currentOption=self.currentOption-1 if self.currentOption==0 then self.currentOption=#Options end return elseif a=='down' or a=='s' then self.currentOption=self.currentOption+1 if self.currentOption>#Options then self.currentOption=1 end return elseif a=='enter' or a=='return' then self:onSelect(Options[self.currentOption].name) end local o=Options[self.currentOption] if o.options then local idx, previdx for k,v in ipairs(o.options) do if v==o.value then idx=k previdx=k break end end if a=='right' or a=='d' then idx=idx+1 if idx>#o.options then idx=1 end elseif a=='left' or a=='a' then idx=idx-1 if idx<1 then idx=#o.options end end if previdx~=idx then o.value=o.options[idx] self:onChange(o.name, o.value) end else if a=='right' or a=='left' or a=='d' or a=='a' then self:onSelect(o.name) end end end function Menu:onChange(k, v) if k=='resolution' then local w, h=v:match('(%d+)x(%d+)') resize(w, h) end if k=='fullscreen' then if v=='YES' then fullscreen=true else fullscreen=false end resize(W, H) end if k=='diagonal' then if v=='YES' then diagonal=true else diagonal=false end end end function Menu:onSelect(k) if k=='quit' then love.event.quit() elseif k=='play' then mode='play' initGame() elseif k=='help' then mode='help' end end return Menu ================================================ FILE: Obey/B.lua ================================================ local Char=require('Char') local lg=love.graphics local M=class(Char, function(self, W, H) Char.init(self, 'B', W, H) self.color={0, 255,0,255} local P={} local p=5 for i=-90, 90, p do P[#P+1]=W*math.cos(math.rad(i)) P[#P+1]=H*0.3*math.sin(math.rad(i)) end self.POINTS=P end) function M:_draw() lg.setColor(self.color) lg.translate(-self.W/2, -self.H/5) lg.polygon('fill', self.POINTS) lg.translate(0, 2*self.H/5) lg.polygon('fill', self.POINTS) lg.translate(self.W/2, -self.H/5) lg.setColor(0, 0, 0, 255) lg.circle('fill', 0.6*self.W/2, -0.6*self.H/3, self.H/15) lg.circle('fill', 0.6*self.W/2, 0.6*self.H/3, self.H/15) end return M ================================================ FILE: Obey/Char.lua ================================================ require 'class' local lg=love.graphics local M=class(function(self, name, W, H) self.W, self.H=W, H self.position={0,0} self.rotation=0 self._scale={1,1} self.name=name or 'unnamed' end) function M:draw() lg.push() lg.translate(self.position[1], self.position[2]) lg.rotate(math.rad(self.rotation)) lg.scale(self._scale[1], self._scale[2]) --predraw if self._draw then self:_draw() end --postdraw lg.pop() end function M:setPosition(x, y) self.position={x, y} return self end function M:setScale(sx, sy) self._scale={sx, sy or sx} return self end function M:setRotation(r) self.rotation=r return self end function M:rotate(dr) local newrotation=self.rotation+dr if self.animated then self._startRotation=self.rotation self._endRotation=newrotation else self.rotation=newrotation end return self end function M:move(dx, dy) local newposition={self.position[1]+dx, self.position[2]+dy} if self.animated then self._startPosition=self.position self._endPosition=newposition else self.position=newposition end return self end function M:scale(dsx, dsy) dsy=dsy or dsx local newscalex=self._scale[1]*dsx local newscaley=self._scale[2]*dsy if self.animated then self._startScale=self._scale self._endScale={newscalex, newscaley} else self._scale={newscalex, newscaley} end return self end function M:setAnimation(duration) if not duration then self.animated=nil else self.animated=true self.duration=duration self._startPosition=self.position self._endPosition=self.position self._startRotation=self.rotation self._endRotation=self.rotation self._startScale=self.scale self._endScale=self.scale end return self end function M:update(dt) if not self.animated then return end if not self._elapsed then self._elapsed=0 else self._elapsed=self._elapsed+dt end if self._elapsed>=self.duration then self.animated=nil self._scale=self._endScale self.position=self._endPosition self.rotation=self._endRotation self:onAnimationFinished() else local pct=self._elapsed/self.duration self.rotation=self._startRotation*(1-pct)+self._endRotation*pct self._scale={ self._startScale[1]*(1-pct)+self._endScale[1]*pct, self._startScale[2]*(1-pct)+self._endScale[2]*pct } self.position={ self._startPosition[1]*(1-pct)+self._endPosition[1]*pct, self._startPosition[2]*(1-pct)+self._endPosition[2]*pct } end end function M:onAnimationFinished() end return M ================================================ FILE: Obey/E.lua ================================================ local Char=require('Char') local lg=love.graphics local M=class(Char, function(self, W, H) Char.init(self, 'E', W, H) self.color={0, 0, 255,255} end) function M:_draw() lg.setColor(self.color) local H2=self.H/2 local W2=self.W/2 local w=math.min(self.W/10, self.H/10) lg.setLineWidth(2*w) lg.line(W2,-H2+w, -W2+w,-H2+w, -W2+w,0, W2,0, -W2+w,0, -W2+w,H2-w, W2,H2-w) end return M ================================================ FILE: Obey/O.lua ================================================ local Char=require('Char') local lg=love.graphics local M=class(Char, function(self, W, H) Char.init(self, 'O', W, H) self.color={255,255,0,255} self.dx=1.1*self.W/2*math.cos(math.rad(45)) self.dy=-1.1*self.H/2*math.sin(math.rad(45)) end) function M:_draw() lg.setColor(self.color) lg.circle('fill', 0, 0, self.W/2, self.W/2 ) lg.circle('fill', self.dx, self.dy, 0.1*self.W, self.W/2 ) lg.circle('fill', -self.dx, self.dy, 0.1*self.W, self.W/2 ) end return M ================================================ FILE: Obey/README.md ================================================ Obeying avatar =========== This sample is inspired by the [AngryLovers] thread. It creates an avatar animation. The key here is to [obey]. [AngryLovers]: http://love2d.org/forums/viewtopic.php?f=5&t=4573 [obey]: http://love2d.org/forums/viewtopic.php?f=3&t=9 ================================================ FILE: Obey/Y.lua ================================================ local Char=require('Char') local lg=love.graphics local M=class(Char, function(self, W, H) Char.init(self, 'Y', W, H) self.color={0,0,255, 255} end) function M:_draw() lg.setColor(self.color) local W2=self.W/2 local H2=self.H/2 local w=math.min(self.W/10, self.H/10) lg.setLineWidth(2*w) lg.line(-W2,-H2, 0,0, 0,H2, 0,0, W2,-H2) end return M ================================================ FILE: Obey/class.lua ================================================ --[[ -- $Id: class.lua 10 2008-07-01 22:18:32Z basique $ -- Simple class implementation -- Taken from http://lua-users.org/wiki/SimpleLuaClasses -- Original author unknown --]] function class(base,ctor) local c = {} -- a new class instance if not ctor and type(base) == 'function' then ctor = base base = nil elseif type(base) == 'table' then -- our new class is a shallow copy of the base class! for i,v in pairs(base) do c[i] = v end c._base = base end -- the class will be the metatable for all its objects, -- and they will look up their methods in it. c.__index = c -- expose a ctor which can be called by () local mt = {} mt.__call = function(class_tbl,...) local obj = {} setmetatable(obj,c) if ctor then ctor(obj,...) else -- make sure that any stuff from the base class is initialized! if base and base.init then base.init(obj,...) end end return obj end c.init = ctor c.is_a = function(self,klass) local m = getmetatable(self) while m do if m == klass then return true end m = m._base end return false end setmetatable(c,mt) return c end ================================================ FILE: Obey/main.lua ================================================ if type(love._version)~='string' then error('love 0.8+ required!') end local lg=love.graphics --W,H=512, 512 W,H=90,90 local O=require 'O'(W, H):setPosition(W/4, H/4):setScale(0.5) local B=require 'B'(W, H):setPosition(3*W/4, H/4):setScale(0.5) local E=require 'E'(W, H):setPosition(W/4, 3*H/4):setScale(0.5) local Y=require 'Y'(W, H):setPosition(3*W/4, 3*H/4):setScale(0.5) local time=3 if false then -- RECTANGLE O:setAnimation(time):move(W/4,0.9*-H/10):scale(2,0.6) B:setAnimation(time):rotate(-90):scale(0.4, 1.6):move(-W/4, -H/8) E:setAnimation(time):move(W/4,H/10):rotate(90):scale(1,2) Y:setAnimation(time):move(-W/4,-H/4):scale(2,0.8) else --CIRCLE O:setAnimation(time):move(W/4,0.8*-H/10):scale(0.8, 0.7) B:setAnimation(time):rotate(-90):scale(0.4,0.7):move(-W/4, -H/8) E:setAnimation(time):move(W/4,H/10):rotate(90):scale(1) Y:setAnimation(time):move(-W/4,-H/4):scale(0.8) end function Y:onAnimationFinished() local scr=lg.newScreenshot():encode('miko.png') end function love.load() lg.setMode(W, H) Chars={O, B, E, Y} end function love.draw() for k,v in ipairs(Chars) do v:draw() end end function love.update(dt) for k,v in ipairs(Chars) do v:update(dt) end end ================================================ FILE: README.md ================================================ Love2d samples ============== This is a repository for sample games and applications which I am playing with while learning [love2d][love2d] platform. Some of those are inspired by forum posts, the others are to test some features. [love2d]: http://love2d.org/ ================================================ FILE: SidescrollerCollision/README.md ================================================ Sidescroller Collision example =========== This sample is a follow-up to my earlier [CollisionExample]. This time this is about a sidescroller type of game, where you can only move left or right, and jump up. This collision sample is tile-based, i.e. the obstacles are represented as tile map. The sample illustrates (in just one of many ways) how you can restrict movement of your cursor (character) to stay on screen and not go thru other obstacles. So: move with LEFT/RIGHT, jump with SPACE. Toggle debug with d. How the collision works. ======================== There are two different cases: one for moving left/right, and another one for fumping up/falling down. You need to check it separately, because you want to move right/left while falling, etc. The map is tile-based, but the character movement is arbitrary, so you character could occupy more than one tile - you need to check for this when looking for collisions. So first find out which tiles would be occupied, then check if any of them would collide. There is another constraint of the movement - the character must stay on the map. [CollisionExample]: https://github.com/miko/Love2d-samples/tree/master/CollisionExample ================================================ FILE: SidescrollerCollision/main.lua ================================================ function love.load() CELLSIZE=32 PLAYERSIZE=16 Player={x=92, y=100, G=-100, S=100, jumping=false, falling=false, Cells={}} createMap() end function love.update(dt) playermove(dt) end function love.draw() love.graphics.setColor(255,255,255) for y=1,#map do for x=1,#map[y] do if map[y][x] == 1 then love.graphics.rectangle("fill",x*CELLSIZE,y*CELLSIZE,CELLSIZE,CELLSIZE) else if DEBUG then love.graphics.rectangle("line",x*CELLSIZE,y*CELLSIZE,CELLSIZE,CELLSIZE) end end end end love.graphics.setColor(255,0,0,128) love.graphics.rectangle("fill",Player.x,Player.y,PLAYERSIZE, PLAYERSIZE) if DEBUG then love.graphics.setColor(0,255,0) love.graphics.print(string.format("Player at (%06.2f , %06.2f) jumping=%s falling=", Player.x, Player.y, tostring(Player.jumping), tostring(Player.falling)), 50,0) love.graphics.print(string.format("Player occupies cells(%d): %s", #Player.Cells, table.concat(Player.Cells, ' | ')), 450,0) end end -- is user off map? function isOffMap(x, y) if x (1+#map[1])*CELLSIZE or y(1+#map)*CELLSIZE then return true else return false end end function createMap() map = { {0,0,0,0,0,0,0,0,0,0,}, {0,0,0,1,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,1,0,}, {0,0,0,0,0,0,0,1,0,0,}, {0,0,0,1,0,0,0,0,0,0,}, {0,0,0,0,0,0,1,0,0,0,}, {1,1,1,1,1,1,1,1,1,1,}, } end -- which tile is that? function posToTile(x, y) local tx=math.floor(x/CELLSIZE) local ty=math.floor(y/CELLSIZE) return tx, ty end -- Find out which cells are occupied by a player (check for each corner) function playerOnCells(x, y) local Cells={} local tx,ty=posToTile(x, y) local key=tx..','..ty Cells[key]=true Cells[#Cells+1]=key tx,ty=posToTile(x+PLAYERSIZE, y) key=tx..','..ty if not Cells[key] then Cells[key]=true Cells[#Cells+1]=key end tx,ty=posToTile(x+PLAYERSIZE, y+PLAYERSIZE) key=tx..','..ty if not Cells[key] then Cells[key]=true Cells[#Cells+1]=key end tx,ty=posToTile(x, y+PLAYERSIZE) key=tx..','..ty if not Cells[key] then Cells[key]=true Cells[#Cells+1]=key end return Cells end local isDown = love.keyboard.isDown function playermove(dt) -- Moving right or left? local newX, newY if isDown("left") then newX=Player.x-Player.S*dt end if isDown("right") then newX=Player.x+Player.S*dt end if newX then -- trying to move to a side local offmap=isOffMap(newX, Player.y) local colliding=isColliding(playerOnCells(newX, Player.y)) if not offmap and not colliding then Player.x=newX end end -- jumping up or falling down Player.G = Player.G + Player.S*dt if not Player.jumping and isDown(" ") and not Player.falling then Player.jumping = true Player.G = -100 end -- check only for upper or lower collision newY= Player.y + Player.G*dt -- always falling local coll=isColliding(playerOnCells(Player.x, newY)) if coll then if Player.G>=0 then -- falling down on the ground Player.jumping=false Player.falling=false end Player.G=0 else Player.falling=true -- falling down end if not isOffMap(Player.x, newY) and not coll then Player.y=newY end if DEBUG then Player.Cells=playerOnCells(Player.x, Player.y) -- end end -- list of tiles function isColliding(T) local collision=false for k,v in ipairs(T) do local x,y=v:match('(%d+),(%d+)') x,y=tonumber(x), tonumber(y) if not map[y] or not map[y][x] then collision=true -- off-map elseif map[tonumber(y)][tonumber(x)] == 1 then collision=true end end return collision end function love.keypressed(k) if k=='escape' then love.event.quit() end if k=='d' then DEBUG=not DEBUG end end ================================================ FILE: SpriteSheet/README.md ================================================ SpriteSheet =========== This sample is inspired by the [spritesheet] thread. This is a simple library, which can be used to create and play many animations from within one spritesheet. [spritesheet]: http://love2d.org/forums/viewtopic.php?f=4&t=3103 ================================================ FILE: SpriteSheet/SpriteSheet.lua ================================================ local lg=love.graphics local Animation={} Animation.__index=Animation function Animation.new(spritesheet) local obj={parent=spritesheet, name=name, frames={}, currentFrame=0, delay=0.1, playing=true, elapsed=0} return setmetatable(obj, Animation) end function Animation:draw(x, y) local quad=self.frames[self.currentFrame] if quad then lg.drawq(self.parent.img, quad, x, y) end end function Animation:update(dt) if #self.frames==0 or not self.playing then return end self.elapsed=self.elapsed+dt if self.elapsed>=self.delay then self.elapsed=self.elapsed-self.delay self.currentFrame=self.currentFrame+1 if self.currentFrame>#self.frames then self.currentFrame=1 end end end function Animation:addFrame(col, row) local parent=self.parent local w,h=parent.w, parent.h local quad=lg.newQuad((col-1)*w, (row-1)*h, w, h, parent.imgw, parent.imgh) self.frames[#self.frames+1]=quad return self end function Animation:play() self.playing=true end function Animation:stop() self.playing=false self.currentFrame=1 self.elapsed=0 end function Animation:pause() self.playing=false end function Animation:setDelay(s) self.delay=s return self end function Animation:getDelay() return self.delay end function Animation:isPaused() return self.playing==false end local SpriteSheet={} SpriteSheet.__index=SpriteSheet function SpriteSheet.new(img, w, h) if type(img)=='string' then img=lg.newImage(img) end local obj={img=img, w=w, h=h, Animations={}} obj.imgw=img:getWidth() obj.imgh=img:getHeight() return setmetatable(obj, SpriteSheet) end function SpriteSheet:createAnimation(...) local a=Animation.new(self) return a end return SpriteSheet ================================================ FILE: SpriteSheet/main.lua ================================================ local SpriteSheet=require 'SpriteSheet' function love.load() local img='spritesheet.png' framewidth=32 frameheight=32 S=SpriteSheet.new(img, framewidth, frameheight) selected=0 Animations={} for row=1,5 do local a=S:createAnimation() for col=1,4 do a:addFrame(col, row) end Animations[#Animations+1]=a end a=S:createAnimation() for row=1,5 do for col=1,4 do a:addFrame(col, row) end end Animations[#Animations+1]=a end function love.update(dt) for k,v in ipairs(Animations) do v:update(dt) end end function love.draw() for k,v in ipairs(Animations) do v:draw(k*framewidth, 0) if k==selected then love.graphics.setColor(255,0,0,127) love.graphics.rectangle('fill', k*framewidth,0, framewidth, frameheight) love.graphics.setColor(255,255, 255,255) end end local a=Animations[selected] if a then love.graphics.print(string.format("Animation: %d Delay: %f", selected, a:getDelay()), 100, 100) end love.graphics.printf("Press 1.."..#Animations.." to select an animation. \r\nWhen selected, press SPACE to toggle playing, and RIGHT/LEFT to increase/decrease the delay between frames.", 0, 150, 800) end function love.keypressed(k) if k=='q' or k=='escape' then love.event.quit() end local n=tonumber(k) local a=Animations[n] if a then selected=n else a=Animations[selected] if a then if k==" " then if a:isPaused() then a:play() else a:pause() end elseif k=="right" then a:setDelay(a:getDelay()-0.01) elseif k=="left" then a:setDelay(a:getDelay()+0.01) else selected=0 end else selected=0 end end end ================================================ FILE: TexturedPolygon/README.md ================================================ Textured Polygon =========== This sample is inspired by the [TexturedPolygon] thread. Given the texture image, it places it (multiple times) on your framebuffer (only on non-black pixels). So create your image (e.g. filled polygon) first, and then call texturize(framebuffer, texture) to get your image. [TexturedPolygon]: http://love2d.org/forums/viewtopic.php?f=4&t=3561 ================================================ FILE: TexturedPolygon/main.lua ================================================ -- Parameters: -- target - framebuffer with black/white pixels -- texture - file name or imageData -- Returns: -- Image function texturize(target, texture) if type(texture)=='string' then texture=love.image.newImageData(texture) end tx, ty=texture:getWidth(), texture:getHeight() local function placeTexture(x, y, r, g, b, a) if r+g+b==0 then -- black return 0,0,0,0 else -- non-black, needs a texture return texture:getPixel(x%tx, y%ty) end end local id=target:getImageData() id:mapPixel(placeTexture) return love.graphics.newImage(id) end -- make texturized polygon function makePolygon(V, texture) local t=love.image.newImageData(texture) local minX, maxX, minY, maxY for n=1,#V, 2 do if not minX or minX>V[n] then minX=V[n] end if not maxX or maxXV[n+1] then minY=V[n+1] end if not maxY or maxY0.5 then self.cursorTime=self.cursorTime-0.5 self.showCursor=not self.showCursor end end function M:draw() love.graphics.setScissor(self.x, self.y, self.w, self.h) love.graphics.setColor(255,255,255,128) love.graphics.rectangle('fill', self.x, self.y, self.w, self.h) love.graphics.setColor(55,55,55,228) --love.graphics.setLineWidth(3) love.graphics.rectangle('line', self.x, self.y, self.w, self.h) for k=1, #self.data do love.graphics.print(self.data[k], self.x+5, self.y+self.h-40-(#self.data-k)*15) end local l=self.prompt..self.line if self.showCursor then l=l..'_' end love.graphics.print(l, self.x+5, self.y+self.h-20) love.graphics.setScissor() end function M:keyPressed(k, u) if k=='delete' or k=='backspace' then if #self.line>0 then self.line=self.line:sub(1, -2) end elseif k=='return' then if type(self.callback)=='function' then self.callback(self.line, self) end self.line='' else if u>31 then if u<127 then self.line=self.line..string.char(u) else self.line=self.line..k end end end end function M:addLine(l) if type(l)=='table' then for k,v in ipairs(l) do self:addLine(v) end else table.insert(self.data, l) if #self.data>8 then table.remove(self.data, 1) end end end return M ================================================ FILE: VerbNounParser/Door.lua ================================================ local M={} local MT={} MT.__index=M function M.new(P) local o=setmetatable({}, MT) o:initialize(P) return o end function M:getName() return 'Door' end function M:initialize(P) self.color=P.color or 'black' self.open=false self.x=100+math.random(500) self.y=250 self.w=80 self.h=100 end function M:openAction(P) local found=false if #P>0 then -- find color for k,v in ipairs(P) do if v:lower()==self.color:lower() then found=true break end end else found=true end if found then if not self.open then self.open=true return true, 'Door '..self.color..' opened' else return true, 'Door '..self.color..' already open!' end else return nil, 'Not for me' end end function M:closeAction(P) local found=false if #P>0 then -- find color for k,v in ipairs(P) do if v:lower()==self.color:lower() then found=true break end end else found=true end if found then if self.open then self.open=false return true, 'Door '..self.color..' closed' else return true, 'Door '..self.color..' already closed!' end else return nil, 'Not for me' end end local Colors={red={255,0,0}, green={0,255,0}, black={0,0,0}} function M:draw() if Colors[self.color] then love.graphics.setColor(Colors[self.color]) end if self.open then love.graphics.rectangle('fill', self.x, self.y, self.w/4, self.h) else love.graphics.rectangle('fill', self.x, self.y, self.w, self.h) end end return M ================================================ FILE: VerbNounParser/Key.lua ================================================ local M={} local MT={} MT.__index=M function M.new(P) local o=setmetatable({}, MT) o:initialize(P) return o end function M:getName() return 'Key' end function M:initialize(P) self.taken=false self.x=100+math.random(500) self.y=250 end function M:takeAction(P) if not self.taken then self.taken=true return true, 'Key taken' else return true, 'Key already taken!' end end function M:dropAction(P) if self.taken then self.taken=false return true, 'Key dropped' else return true, 'Key already dropped!' end end function M:draw() love.graphics.setColor(0, 0, 0) local x, y if self.taken then x, y=20, 20 else x, y=self.x, self.y end love.graphics.rectangle('fill', x+10, y, 50, 20) love.graphics.circle('fill', x, y+10, 20) end return M ================================================ FILE: VerbNounParser/Parser.lua ================================================ local P={} local MT={} MT.__index=P function P.new(...) local o=setmetatable({}, MT) o:initialize(...) return o end function P:registerObject(o) table.insert(self.Objects, o) end function P:findObjects(name) local o={} for k,v in ipairs(self.Objects) do if name:lower()==v:getName():lower() then o[#o+1]=v end end return o end function P:initialize(...) self.Objects={} end function P:parse(buf) -- tokenize local tokens={} for token in buf:gmatch('%w+') do table.insert(tokens, token:lower()) end -- find object local objects for k,v in ipairs(tokens) do local o=self:findObjects(v) if #o>0 then objects=o table.remove(tokens, k) break end end if not objects then return nil, 'No objects found' end -- find action local object=objects[1] -- assuming all objects found have the same methods for k,v in ipairs(tokens) do local method=v..'Action' if type(object[method])=='function' then table.remove(tokens, k) local res={} for a,b in ipairs(objects) do local st,re=b[method](b, tokens) if st then res[#res+1]=re end end return true, res end end return nil, 'Unknown action for this object '..object:getName() end function P:getObjects() return self.Objects end return P ================================================ FILE: VerbNounParser/README.md ================================================ VerbNounParser =========== This sample is inspired by the [verb-noun parser] thread. The idea is to register a sef of objects (instances of classes with given name and a set of actions, or methods). So you can have classes like Door or Window, and objects, like reddoor, greendoor or window. Now when user writes "open door", the parser first tokenizes this into tokens, then looks for all objects with a name equals to any token ("open", "door"), and when found - for this token, looks for a function called token+"Action" (like "openAction"), and calls it with remaining tokens (minus object name and action name). Note that the Door class implements "color discriminator" - if you pass a color name, only door object with this color will respond. So try following commands: ``` open window open door close red door close green door Open door Close door red ad green please! Please, take the key. Drop my key now. exit ``` [verb-noun parser]: http://love2d.org/forums/viewtopic.php?f=4&t=10291 ================================================ FILE: VerbNounParser/Window.lua ================================================ local M={} local MT={} MT.__index=M function M.new(P) local o=setmetatable({}, MT) o:initialize(P) return o end function M:getName() return 'Window' end function M:initialize(P) self.open=false self.x=100+math.random(500) self.y=150 self.w=50 self.h=50 end function M:openAction(P) if not self.open then self.open=true return true, 'Window opened' else return true, 'Window already open!' end end function M:closeAction(P) if self.open then self.open=false return true, 'Window closed' else return true, 'Window already closed!' end end function M:draw() love.graphics.setColor(0, 100, 255) if self.open then love.graphics.rectangle('fill', self.x, self.y, self.w/4, self.h) else love.graphics.rectangle('fill', self.x, self.y, self.w, self.h) end end return M ================================================ FILE: VerbNounParser/main.lua ================================================ local Parser=require 'Parser' local Door=require 'Door' local Window=require 'Window' local Key=require 'Key' local Console=require 'Console' local Objects function love.load() love.graphics.setBackgroundColor(255, 255, 255) P=Parser.new() local blackdoor=Door.new({color='black'}) local greendoor=Door.new({color='green'}) local reddoor=Door.new({color='red'}) local window=Window.new() console=Console.new() local key=Key.new() P:registerObject(greendoor) P:registerObject(reddoor) P:registerObject(blackdoor) P:registerObject(window) P:registerObject(key) console:setCallback(function(buf) if buf=='exit' or buf=='quit' then love.event.quit() end local st, res=P:parse(buf) if st then console:addLine(res) else console:addLine('Error: '.. res) end end) end function love.draw() for k,v in ipairs(P:getObjects()) do v:draw() end console:draw() end function love.update(dt) console:update(dt) end function love.keypressed(k, u) console:keyPressed(k, u) end