Develop #4
@@ -8,9 +8,9 @@ function arrows.load(game, stage, camera)
|
|||||||
arrows.left_x = padding
|
arrows.left_x = padding
|
||||||
arrows.left_y = (game.canvas.height / 2) - (arrows.img_left:getHeight() /2)
|
arrows.left_y = (game.canvas.height / 2) - (arrows.img_left:getHeight() /2)
|
||||||
|
|
||||||
arrows.img_rigth = love.graphics.newImage('assets/sprites/arrows/rigth.png')
|
arrows.img_right = love.graphics.newImage('assets/sprites/arrows/right.png')
|
||||||
arrows.rigth_x = (game.canvas.width - arrows.img_rigth:getWidth()) - padding
|
arrows.right_x = (game.canvas.width - arrows.img_right:getWidth()) - padding
|
||||||
arrows.rigth_y = (game.canvas.height / 2) - (arrows.img_left:getHeight() /2)
|
arrows.right_y = (game.canvas.height / 2) - (arrows.img_left:getHeight() /2)
|
||||||
arrows.game = game
|
arrows.game = game
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -22,13 +22,17 @@ function arrows.draw()
|
|||||||
if arrows.game.level > 1 then
|
if arrows.game.level > 1 then
|
||||||
love.graphics.draw(arrows.img_left, arrows.left_x, arrows.left_y)
|
love.graphics.draw(arrows.img_left, arrows.left_x, arrows.left_y)
|
||||||
end
|
end
|
||||||
if arrows.game.level ~= arrows.game.end_level then
|
if arrows.game.level < arrows.game.end_level then
|
||||||
love.graphics.draw(arrows.img_rigth, arrows.rigth_x, arrows.rigth_y)
|
love.graphics.draw(arrows.img_right, arrows.right_x, arrows.right_y)
|
||||||
end
|
end
|
||||||
|
if arrows.game.level == arrows.game.end_level then
|
||||||
|
arrows.game.var_end = true
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function love.mousepressed(mx, my, button)
|
function arrows.mousepressed(mx, my, button)
|
||||||
x1,y1,x2,y2,x3,y3,x4,y4 = camera.gcam:getVisibleCorners()
|
x1,y1,x2,y2,x3,y3,x4,y4 = camera.gcam:getVisibleCorners()
|
||||||
wl, wt, ww, wh = camera.gcam:getWorld()
|
wl, wt, ww, wh = camera.gcam:getWorld()
|
||||||
if button == 1 then
|
if button == 1 then
|
||||||
@@ -39,10 +43,27 @@ function love.mousepressed(mx, my, button)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if x2 >= (ww - arrows.img_rigth:getWidth()) then
|
if x2 >= (ww - arrows.img_right:getWidth()) then
|
||||||
if mx >= (ww - x1 - arrows.img_rigth:getWidth()) and mx < (ww - arrows.img_rigth:getWidth()) then
|
same_size = false
|
||||||
if my >= arrows.rigth_y and my < arrows.rigth_y + arrows.img_rigth:getHeight() then
|
if ww == arrows.game.canvas.width and x1 == 0 then
|
||||||
arrows.game:nextLevel()
|
-- Verify canvas == world
|
||||||
|
same_size = true
|
||||||
|
end
|
||||||
|
if same_size then
|
||||||
|
-- canvas == world
|
||||||
|
if mx >= arrows.right_x then
|
||||||
|
if my >= arrows.left_y and my < arrows.left_y + arrows.img_left:getHeight() then
|
||||||
|
arrows.game:nextLevel()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- large world - scroll cam
|
||||||
|
if mx >= (ww - x1 - arrows.img_right:getWidth())
|
||||||
|
and mx <= (ww - arrows.img_right:getWidth())
|
||||||
|
or same_size then
|
||||||
|
if my >= arrows.right_y and my < arrows.right_y + arrows.img_right:getHeight() then
|
||||||
|
arrows.game:nextLevel()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,42 +3,185 @@ local tools = require 'assets/scripts/tools'
|
|||||||
local HC = require 'assets/scripts/vendor/HC'
|
local HC = require 'assets/scripts/vendor/HC'
|
||||||
|
|
||||||
local bell = {}
|
local bell = {}
|
||||||
|
local collision_debug = false
|
||||||
|
local collisions = {}
|
||||||
|
local cam_x, cam_y = nil
|
||||||
|
|
||||||
function bell.load(game, cam)
|
function bell.load(game, cam)
|
||||||
local body = {
|
sound_error = love.audio.newSource('assets/audio/fx/bills/campana_fallo.wav', 'static')
|
||||||
|
body = {
|
||||||
img = love.graphics.newImage('assets/sprites/bells/bell_vibrate.png'),
|
img = love.graphics.newImage('assets/sprites/bells/bell_vibrate.png'),
|
||||||
num_frames = 10,
|
num_frames = 10,
|
||||||
speed = 0.05,
|
speed = 0.05,
|
||||||
|
active = false,
|
||||||
x = -game.canvas.width,
|
x = -game.canvas.width,
|
||||||
y = -game.canvas.height
|
y = -game.canvas.height
|
||||||
}
|
}
|
||||||
g = anim8.newGrid(body.img:getWidth() / body.num_frames, body.img:getHeight(), body.img:getWidth(), body.img:getHeight())
|
g = anim8.newGrid(body.img:getWidth() / body.num_frames, body.img:getHeight(), body.img:getWidth(), body.img:getHeight())
|
||||||
body.animation = anim8.newAnimation(g('1-' .. body.num_frames, 1), body.speed, 'pauseAtEnd')
|
body.animation = anim8.newAnimation(g('1-' .. body.num_frames, 1), body.speed)
|
||||||
body.animation:pause()
|
body.animation:pause()
|
||||||
|
|
||||||
local cam_x, cam_y = cam:getVisible()
|
cam_x, cam_y = cam.gcam:getVisible()
|
||||||
bell_1 = tools.clone_table(body)
|
bell_1 = tools.clone_table(body)
|
||||||
bell_1.x = cam_x + (game.window.width / 4) - (bell_1.img:getWidth() / body.num_frames / 2)
|
bell_1.x = cam_x + (game.window.width / 4) - (bell_1.img:getWidth() / body.num_frames / 2)
|
||||||
bell_1.y = (game.window.height / 2) - (bell_1.img:getHeight() / 2)
|
bell_1.y = (game.window.height / 2) - (bell_1.img:getHeight() / 2)
|
||||||
|
bell_1.sound_loop = love.audio.newSource('assets/audio/fx/bills/campana_loop1.wav', 'static')
|
||||||
|
bell_1.sound_good = love.audio.newSource('assets/audio/fx/bills/campana_exito1.wav', 'static')
|
||||||
bell_2 = tools.clone_table(body)
|
bell_2 = tools.clone_table(body)
|
||||||
bell_2.x = cam_x + (game.window.width / 4 * 2) - (bell_1.img:getWidth() / body.num_frames / 2)
|
bell_2.x = cam_x + (game.window.width / 4 * 2) - (bell_1.img:getWidth() / body.num_frames / 2)
|
||||||
bell_2.y = bell_1.y
|
bell_2.y = bell_1.y
|
||||||
|
bell_2.sound_loop = love.audio.newSource('assets/audio/fx/bills/campana_loop2.wav', 'static')
|
||||||
|
bell_2.sound_good = love.audio.newSource('assets/audio/fx/bills/campana_exito2.wav', 'static')
|
||||||
bell_3 = tools.clone_table(body)
|
bell_3 = tools.clone_table(body)
|
||||||
bell_3.x = cam_x + (game.window.width / 4 * 3) - (bell_1.img:getWidth() / body.num_frames / 2)
|
bell_3.x = cam_x + (game.window.width / 4 * 3) - (bell_1.img:getWidth() / body.num_frames / 2)
|
||||||
bell_3.y = bell_1.y
|
bell_3.y = bell_1.y
|
||||||
|
bell_3.sound_loop = love.audio.newSource('assets/audio/fx/bills/campana_loop3.wav', 'static')
|
||||||
|
bell_3.sound_good = love.audio.newSource('assets/audio/fx/bills/campana_exito3.wav', 'static')
|
||||||
|
bells = {bell_1, bell_2, bell_3}
|
||||||
|
|
||||||
|
-- Collisions
|
||||||
|
collisions.size = 15
|
||||||
|
collisions.positions = {}
|
||||||
|
collisions.num = 15
|
||||||
|
collisions.correction = 45
|
||||||
|
|
||||||
|
-- Make positions collisions
|
||||||
|
for i = 1, collisions.num, 1 do
|
||||||
|
local tem_x, tem_y = tools.cicle_positions(0, 0, bell_1.img:getHeight() / 2, 360 / collisions.num * i)
|
||||||
|
collisions.positions[i] = {x=tem_x, y=tem_y}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add collisions
|
||||||
|
for key, bell in pairs(bells) do
|
||||||
|
bell.collisions = {}
|
||||||
|
bell.enable = {}
|
||||||
|
for key, collision in pairs(collisions.positions) do
|
||||||
|
bell.collisions[key] = HC.circle(bell.x + collision.x, collision.y, collisions.size)
|
||||||
|
bell.enable[key] = false
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function bell.update(dt, game, cam)
|
function bell.update(dt, game, cam)
|
||||||
bell_1.animation:update(dt)
|
if game.bells_enable then
|
||||||
bell_2.animation:update(dt)
|
-- Update cam position
|
||||||
bell_3.animation:update(dt)
|
cam_x, cam_y = cam.gcam:getVisible()
|
||||||
|
-- Ani bells
|
||||||
|
bell_1.animation:update(dt)
|
||||||
|
bell_2.animation:update(dt)
|
||||||
|
bell_3.animation:update(dt)
|
||||||
|
-- Bells fix pos
|
||||||
|
bell_1.x = cam_x + (game.window.width / 4) - (bell_1.img:getWidth() / body.num_frames / 2)
|
||||||
|
bell_2.x = cam_x + (game.window.width / 4 * 2) - (bell_1.img:getWidth() / body.num_frames / 2)
|
||||||
|
bell_3.x = cam_x + (game.window.width / 4 * 3) - (bell_1.img:getWidth() / body.num_frames / 2)
|
||||||
|
-- Collisions fix pos
|
||||||
|
for key, bell in pairs(bells) do
|
||||||
|
for key, collision in pairs(collisions.positions) do
|
||||||
|
bell.collisions[key]:moveTo(collisions.positions[key].x + bell.x + (bell.img:getHeight() / 2) + collisions.correction, collisions.positions[key].y + bell.y + (bell.img:getHeight() / 2))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Check collisions
|
||||||
|
for key, bell in pairs(bells) do
|
||||||
|
for key, collision in pairs(bell.collisions) do
|
||||||
|
for shape, delta in pairs(HC.collisions(bell.collisions[key])) do
|
||||||
|
if bell.sound_loop:isPlaying() == false then
|
||||||
|
bell.sound_loop:play()
|
||||||
|
end
|
||||||
|
bell.enable[key] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for key, bell in pairs(bells) do
|
||||||
|
for key, collision in pairs(bell.collisions) do
|
||||||
|
for shape, delta in pairs(HC.collisions(bell.collisions[key])) do
|
||||||
|
bell.enable[key] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Logic
|
||||||
|
local num_enable = 0
|
||||||
|
for key, bell in pairs(bells) do
|
||||||
|
for key, item in pairs(bell.enable) do
|
||||||
|
-- Count enables
|
||||||
|
num_enable = num_enable + 1
|
||||||
|
end
|
||||||
|
if num_enable > 1 then
|
||||||
|
-- Search emptys
|
||||||
|
local count_singles = 0
|
||||||
|
for key, item in pairs(bell.enable) do
|
||||||
|
if key > 1 and bell.enable[key] and bell.enable[key - 1] == false then
|
||||||
|
count_singles = count_singles + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Bad. Restart
|
||||||
|
if count_singles >= 2 then
|
||||||
|
for key, bell in pairs(bells) do
|
||||||
|
bell.sound_loop:stop()
|
||||||
|
end
|
||||||
|
sound_error:play()
|
||||||
|
for key, item in pairs(bell.enable) do
|
||||||
|
bell.enable[key] = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Check good
|
||||||
|
local good = true
|
||||||
|
for key, item in pairs(bell.enable) do
|
||||||
|
if bell.enable[key] == false then
|
||||||
|
good = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Enable animation good
|
||||||
|
if good then
|
||||||
|
bell.active = true
|
||||||
|
bell.animation:resume()
|
||||||
|
bell.sound_good:play()
|
||||||
|
bell.animation:resume()
|
||||||
|
for key, item in pairs(bell.enable) do
|
||||||
|
bell.enable[key] = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
good = true
|
||||||
|
count_singles = 0
|
||||||
|
end
|
||||||
|
num_enable = 0
|
||||||
|
end
|
||||||
|
-- Game over
|
||||||
|
game_over = true
|
||||||
|
for key, bell in pairs(bells) do
|
||||||
|
if not bell.active then
|
||||||
|
game_over = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for key, bell in pairs(bells) do
|
||||||
|
if game_over then
|
||||||
|
bell.animation:pauseAtEnd()
|
||||||
|
game.bells_enable = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if game_over then
|
||||||
|
game.status = game.status + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function bell.draw()
|
function bell.draw(game)
|
||||||
if bells_enable then
|
if game.bells_enable then
|
||||||
bell_1.animation:draw(bell_1.img, bell_1.x, bell_1.y)
|
-- Bells
|
||||||
bell_2.animation:draw(bell_2.img, bell_2.x, bell_2.y)
|
for key, bell in pairs(bells) do
|
||||||
bell_3.animation:draw(bell_3.img, bell_3.x, bell_3.y)
|
bell.animation:draw(bell.img, bell.x, bell.y)
|
||||||
|
end
|
||||||
|
-- Collisions
|
||||||
|
if collision_debug then
|
||||||
|
for key, bell in pairs(bells) do
|
||||||
|
for key, collision in pairs(collisions.positions) do
|
||||||
|
if bell.enable[key] then
|
||||||
|
bell.collisions[key]:draw('fill')
|
||||||
|
else
|
||||||
|
bell.collisions[key]:draw('line')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
local controls = {}
|
local HC = require 'assets/scripts/vendor/HC'
|
||||||
|
|
||||||
|
local controls = {}
|
||||||
local limit = 10
|
local limit = 10
|
||||||
|
|
||||||
function controls.load(game)
|
function controls.load(game)
|
||||||
|
-- add a circle to the scene
|
||||||
|
controls.mouse = HC.circle(400,300, 1)
|
||||||
|
controls.mouse:moveTo(love.mouse.getPosition())
|
||||||
end
|
end
|
||||||
|
|
||||||
function controls.update(dt, game)
|
function controls.update(dt, game, camera)
|
||||||
control_up, control_right, control_left, control_quit = false, false, false, false
|
control_up, control_right, control_left, control_quit = false, false, false, false
|
||||||
-- Keyboard
|
-- Keyboard
|
||||||
if love.keyboard.isDown('escape') or love.keyboard.isDown('q') then
|
if love.keyboard.isDown('escape') or love.keyboard.isDown('q') then
|
||||||
@@ -21,7 +24,12 @@ function controls.update(dt, game)
|
|||||||
|
|
||||||
control_up = true
|
control_up = true
|
||||||
end
|
end
|
||||||
-- Mouse
|
-- Mouse
|
||||||
|
-- move circle to mouse position
|
||||||
|
local cam_x, cam_y = camera.gcam:getPosition()
|
||||||
|
local mouse_x, mouse_y = love.mouse.getPosition()
|
||||||
|
controls.mouse:moveTo(cam_x + mouse_x - 640 , mouse_y)
|
||||||
|
|
||||||
if control_left then
|
if control_left then
|
||||||
if game.canvas.x > game.window.width / 2 then
|
if game.canvas.x > game.window.width / 2 then
|
||||||
game.canvas.x = game.canvas.x - limit
|
game.canvas.x = game.canvas.x - limit
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
local game = {}
|
local game = {}
|
||||||
|
local gamera = require 'assets/scripts/vendor/gamera'
|
||||||
|
|
||||||
function game.load()
|
function game.load()
|
||||||
-- Configuration
|
-- Configuration
|
||||||
@@ -8,7 +9,8 @@ function game.load()
|
|||||||
game.window = { width = width , height = height }
|
game.window = { width = width , height = height }
|
||||||
game.canvas = { x = width / 2, y= 0, width = canvas_width, height = canvas_height }
|
game.canvas = { x = width / 2, y= 0, width = canvas_width, height = canvas_height }
|
||||||
game.level = 1
|
game.level = 1
|
||||||
game.end_level = 2
|
game.end_level = 7
|
||||||
|
game.start_screen = true
|
||||||
|
|
||||||
love.window.setMode(game.window.width, game.window.height)
|
love.window.setMode(game.window.width, game.window.height)
|
||||||
|
|
||||||
@@ -17,20 +19,60 @@ function game.load()
|
|||||||
local gravity = 2
|
local gravity = 2
|
||||||
love.physics.setMeter(world_meter) -- Height earth in meters
|
love.physics.setMeter(world_meter) -- Height earth in meters
|
||||||
game.world = love.physics.newWorld(0, gravity * world_meter, true) -- Make earth
|
game.world = love.physics.newWorld(0, gravity * world_meter, true) -- Make earth
|
||||||
|
game.status = 1
|
||||||
-- Collisions
|
|
||||||
--game.collisions = HC.new(150)
|
|
||||||
|
|
||||||
-- Bells
|
-- Bells
|
||||||
bells_enable = false
|
game.bells_enable = false
|
||||||
|
|
||||||
|
game.music = love.audio.newSource("assets/audio/music/theme_biblia.wav")
|
||||||
|
|
||||||
|
game.stress = false
|
||||||
|
game.var_end = false
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function game.update(dt)
|
||||||
|
if game.bells_enable then
|
||||||
|
game:stopMusic()
|
||||||
|
else
|
||||||
|
if game.var_end == false then
|
||||||
|
game:playMusic()
|
||||||
|
else
|
||||||
|
game:stopMusic()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function game:nextLevel()
|
function game:nextLevel()
|
||||||
game.level = game.level + 1
|
if game.level == game.end_level then
|
||||||
|
game.level = 1
|
||||||
|
else
|
||||||
|
game.level = game.level + 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function game:prevLevel()
|
function game:prevLevel()
|
||||||
game.level = game.level - 1
|
if game.level > 1 then
|
||||||
|
game.level = game.level - 1
|
||||||
|
else
|
||||||
|
game.level = game.end_level
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function game:setNewSizeWorld(canvas_width, canvas_height)
|
||||||
|
local width, height = 1280, 720
|
||||||
|
game.window = { width = width , height = height }
|
||||||
|
game.canvas = { x = width / 2, y= 0, width = canvas_width, height = canvas_height }
|
||||||
|
camera.gcam = gamera.new(0, 0, game.window.width, game.window.height)
|
||||||
|
camera.gcam:setWorld(0, 0, game.canvas.width, game.canvas.height)
|
||||||
|
end
|
||||||
|
|
||||||
|
function game:playMusic()
|
||||||
|
game.music:play()
|
||||||
|
end
|
||||||
|
|
||||||
|
function game:stopMusic()
|
||||||
|
game.music:stop()
|
||||||
end
|
end
|
||||||
|
|
||||||
return game
|
return game
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
require 'assets/scripts/vendor/MessageInABottle'
|
||||||
|
|
||||||
|
local messages = {}
|
||||||
|
messages.active = true
|
||||||
|
|
||||||
|
function messages.load()
|
||||||
|
ocean = Ocean:new()
|
||||||
|
end
|
||||||
|
|
||||||
|
function messages.update(dt, game)
|
||||||
|
ocean:update(dt)
|
||||||
|
if game.status == 1 and messages.active then
|
||||||
|
messages.new_message('welcome', 10)
|
||||||
|
messages.active = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function messages.draw()
|
||||||
|
ocean:draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
function messages.new_message(cap, time)
|
||||||
|
local texts = {}
|
||||||
|
texts.welcome = "I must make decision. I won’t leave this room until I’ve decided to kill my girlfriend or not. How has it come to this? I’ve got little time. Her life is teetering through my fingers."
|
||||||
|
texts.cap1preview="She’s gorgeous in this picture. My mother has given me more than I’ll ever be able to give her back. Not only did she give me the gift of life, but a reason to go on. Her lessons and strong character have made me become the man I am now. What I most want in this world is for her to be proud of me. Proud of my work and my partner. But a bitch had to screw it all up. She stepped on what I loved most. She attacked all my mother taught me and undermined my deepest beliefs. I don’t believe half-way, like my siblings, but truly. And this girl did nothing but ridicule me. Make fun of me in front of my friends; call me a nutter in private; tell me I was wrong; brand me as a brute - a moron. But she’s the moron! She’s just an immoral, sinful whore…I need to calm down, clear my mind. Listen to God’s Word. If I go out now, I’ll kill her! I must settle down and think straight. God, speak to me!"
|
||||||
|
texts.cap2stage=" Corinthians 12:10 That is why, for Christ's sake, I delight in weaknesses, in insults, in hardships, in persecutions, in difficulties. For when I am weak, then I am strong uAs strong as she made me. She laughed at me in front of my friends, but to help me overcome my shyness. She said I was a nutter to help me overcome my issues, to make me realise the obsessions I’d inherited from my mother. Brute when I wasn’t tolerant with others. And I just got upset…like a moron. A part of my soul still belongs to her. But I mustn’t forget where I first met her, where I bought her first kisses. My parents must never know the truth. My mother shall never know about the smokes, the drinks I had. I must destroy that sheet of paper. Nobody must see it."
|
||||||
|
texts.cap3stage="While the smoke went through my nostrils, I remembered the barbecue parties we threw every Saturday with my family. My cousins adored her and she got on really well with my uncle and aunt. She was completely integrated in my family, so we invited her to come to this barbecue party. And then, while we were saying grace, she started to eat. In the silence you could hear her chewing on a greasy sausage. In shock, they all looked at her, and then, at my grimace. I couldn’t believe what was happening. I took her outside to the log cabin. She won’t easily forget what I said to her. I’d never met such an insensitive person, such a…and as an apology she gave us a fucking compact disc as a gift. Does she really think it would make up for what she’d done? What a shitty gift!"
|
||||||
|
texts.cap4stage="I love this song. I remember now why she bought this one. It was our song. A hidden apology for me. Maybe I got too carried away. After all, she’s not religious, she’d never said grace. She hadn’t even seen how it’s done. She did what anybody would have done: sit down and start to eat. Poor one. I shouldn’t have accepted the album, her tears were more than enough. She meant no harm. But I did…and now I must kill her. I must do it before she starts telling lies about me and harms my family's reputation. I can't allow this to happen. I'm not gay. I'm not sick. I'm not using this key. I'll go out that door and I'll do her in. No more memories, I've made up my mind."
|
||||||
|
texts.endbad="Die, bitch."
|
||||||
|
texts.endgood="My bra. It's so uncomfortable. I've always hated wearing it. It makes my breasts hurt. On our first secret anniversary he bought me an outfit. He's got me. He's always helped me grow as a person. To break ties. Like the one I'm in now. Telling my mother I'm a woman who's into other...women. And that the Lord justs wants me to be happy. I've got to be strong. 2 Corinthians 12:10 That is why, for Christ's sake, I delight in weaknesses, in insults, in hardships, in persecutions, in difficulties. For when I am weak, then I am strong uAs strong as she made me. She laughed at me in front of my friends, but to help me overcome my shyness. She said I was a nutter to help me overcome my issues, to make me realise the obsessions I’d inherited from my mother. Brute when I wasn’t tolerant with others. And I just got upset…like a moron. A part of my soul still belongs to her. Thanks my love."
|
||||||
|
local bottle = TimeBottle:new(cap, texts['' .. cap], time)
|
||||||
|
bottle:setX(30)
|
||||||
|
bottle:setY(500)
|
||||||
|
bottle:setWidth(1220)
|
||||||
|
bottle:setHeight(140)
|
||||||
|
bottle:setVolume(0)
|
||||||
|
ocean:addBottle(bottle)
|
||||||
|
end
|
||||||
|
|
||||||
|
return messages
|
||||||
|
|
||||||
@@ -6,7 +6,9 @@ local stage1 = {}
|
|||||||
local image
|
local image
|
||||||
|
|
||||||
function stage1.load(game, camera)
|
function stage1.load(game, camera)
|
||||||
image = love.graphics.newImage('assets/sprites/background/bg.jpg')
|
image = love.graphics.newImage('assets/sprites/background/fondo_puerta.jpg')
|
||||||
|
stage1.world = { w = image:getWidth(), h= 720}
|
||||||
|
game:setNewSizeWorld(stage1.world.w, stage1.world.h)
|
||||||
arrows.load(game, stage1, camera)
|
arrows.load(game, stage1, camera)
|
||||||
game.canvas.x = game.window.width
|
game.canvas.x = game.window.width
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,18 +6,54 @@ local stage2 = {}
|
|||||||
local image
|
local image
|
||||||
|
|
||||||
function stage2.load(game, camera)
|
function stage2.load(game, camera)
|
||||||
image = love.graphics.newImage('assets/sprites/background/bg2.jpg')
|
image_table = love.graphics.newImage('assets/sprites/table/table.jpg')
|
||||||
|
image_table_hover = love.graphics.newImage('assets/sprites/table/table_hover.jpg')
|
||||||
|
image_table_none = love.graphics.newImage('assets/sprites/table/table_none.jpg')
|
||||||
|
stage2.world = { w = image_table:getWidth(), h= 720}
|
||||||
|
game:setNewSizeWorld(stage2.world.w, stage2.world.h)
|
||||||
arrows.load(game, stage2, camera)
|
arrows.load(game, stage2, camera)
|
||||||
game.canvas.x = game.canvas.width / 2
|
game.canvas.x = game.window.width / 2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local count_time = 0
|
||||||
function stage2.update(dt, game)
|
function stage2.update(dt, game)
|
||||||
arrows.update(dt)
|
arrows.update(dt)
|
||||||
|
if game.status == 3 then
|
||||||
|
if count_time > 22 then
|
||||||
|
game.status = 4
|
||||||
|
game.bells_enable = true
|
||||||
|
else
|
||||||
|
count_time = dt + count_time
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function stage2.draw()
|
function stage2.draw(game, camera)
|
||||||
love.graphics.draw(image)
|
local mos_x, mos_y = love.mouse.getPosition()
|
||||||
|
-- No object
|
||||||
|
love.graphics.draw(image_table_none)
|
||||||
|
if game.status == 2 then
|
||||||
|
-- Normal
|
||||||
|
love.graphics.draw(image_table)
|
||||||
|
-- Hover
|
||||||
|
cam_x, cam_y = camera.gcam:getVisible()
|
||||||
|
if mos_x + cam_x > 527 and mos_x + cam_x < 653 and mos_y - cam_y > 335 and mos_y - cam_y < 423 then
|
||||||
|
love.graphics.draw(image_table_hover)
|
||||||
|
end
|
||||||
|
end
|
||||||
arrows.draw()
|
arrows.draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function stage2.mousepressed(game, messages, camera)
|
||||||
|
local mos_x, mos_y = love.mouse.getPosition()
|
||||||
|
if game.status == 2 then
|
||||||
|
cam_x, cam_y = camera.gcam:getVisible()
|
||||||
|
if mos_x + cam_x > 527 and mos_x + cam_x < 653 and mos_y - cam_y > 335 and mos_y - cam_y < 423 then
|
||||||
|
game.status = 5
|
||||||
|
messages.new_message('cap1preview', 10)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return stage2
|
return stage2
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
local anim8 = require 'assets/scripts/vendor/anim8'
|
||||||
|
local tools = require 'assets/scripts/tools'
|
||||||
|
local arrows = require 'assets/scripts/arrows'
|
||||||
|
|
||||||
|
local stage3 = {}
|
||||||
|
local image
|
||||||
|
|
||||||
|
function stage3.load(game, camera)
|
||||||
|
image_libreria = love.graphics.newImage('assets/sprites/libreria/libreria.jpg')
|
||||||
|
image_libreria_hover = love.graphics.newImage('assets/sprites/libreria/libreria_hover.jpg')
|
||||||
|
image_libreria_none = love.graphics.newImage('assets/sprites/libreria/libreria_none.jpg')
|
||||||
|
stage3.world = { w = image_libreria:getWidth(), h= 720}
|
||||||
|
game:setNewSizeWorld(stage3.world.w, stage3.world.h)
|
||||||
|
arrows.load(game, stage3, camera)
|
||||||
|
game.canvas.x = game.window.width / 2
|
||||||
|
end
|
||||||
|
|
||||||
|
local count_time = 0
|
||||||
|
function stage3.update(dt, game)
|
||||||
|
arrows.update(dt)
|
||||||
|
if game.status == 5 then
|
||||||
|
if count_time > 15 then
|
||||||
|
game.bells_enable = true
|
||||||
|
game.status = 6
|
||||||
|
else
|
||||||
|
count_time = dt + count_time
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function stage3.draw(game)
|
||||||
|
local mos_x, mos_y = love.mouse.getPosition()
|
||||||
|
-- No object
|
||||||
|
if game.status > 5 then
|
||||||
|
love.graphics.draw(image_libreria_none)
|
||||||
|
else
|
||||||
|
love.graphics.draw(image_libreria)
|
||||||
|
end
|
||||||
|
if game.status == 5 then
|
||||||
|
-- Normal
|
||||||
|
love.graphics.draw(image_libreria)
|
||||||
|
-- Hover
|
||||||
|
cam_x, cam_y = camera.gcam:getVisible()
|
||||||
|
if mos_x + cam_x > 709 and mos_x + cam_x < 791 and mos_y - cam_y > 202 and mos_y - cam_y < 354 then
|
||||||
|
love.graphics.draw(image_libreria_hover)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
arrows.draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
function stage3.mousepressed(game, messages, camera)
|
||||||
|
local mos_x, mos_y = love.mouse.getPosition()
|
||||||
|
if game.status == 5 then
|
||||||
|
cam_x, cam_y = camera.gcam:getVisible()
|
||||||
|
if mos_x + cam_x > 709 and mos_x + cam_x < 791 and mos_y - cam_y > 202 and mos_y - cam_y < 354 then
|
||||||
|
game.status = 6
|
||||||
|
print 'game.status'
|
||||||
|
print(game.status)
|
||||||
|
messages.new_message('cap2stage', 15)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return stage3
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
local anim8 = require 'assets/scripts/vendor/anim8'
|
||||||
|
local tools = require 'assets/scripts/tools'
|
||||||
|
local arrows = require 'assets/scripts/arrows'
|
||||||
|
|
||||||
|
local stage4 = {}
|
||||||
|
local image
|
||||||
|
|
||||||
|
function stage4.load(game, camera)
|
||||||
|
image_libreria = love.graphics.newImage('assets/sprites/chimenea/chimenea.jpg')
|
||||||
|
image_libreria_hover = love.graphics.newImage('assets/sprites/chimenea/chimenea_hover.jpg')
|
||||||
|
image_libreria_none = love.graphics.newImage('assets/sprites/chimenea/chimenea_none.jpg')
|
||||||
|
stage4.world = { w = image_libreria:getWidth(), h= 720}
|
||||||
|
game:setNewSizeWorld(stage4.world.w, stage4.world.h)
|
||||||
|
arrows.load(game, stage4, camera)
|
||||||
|
game.canvas.x = game.window.width / 2
|
||||||
|
end
|
||||||
|
|
||||||
|
local count_time = 0
|
||||||
|
function stage4.update(dt, game)
|
||||||
|
arrows.update(dt)
|
||||||
|
if game.status == 6 then
|
||||||
|
if count_time > 15 then
|
||||||
|
game.bells_enable = true
|
||||||
|
game.status = 6
|
||||||
|
else
|
||||||
|
count_time = dt + count_time
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function stage4.draw(game)
|
||||||
|
local mos_x, mos_y = love.mouse.getPosition()
|
||||||
|
-- No object
|
||||||
|
if game.status > 6 then
|
||||||
|
love.graphics.draw(image_libreria_none)
|
||||||
|
else
|
||||||
|
love.graphics.draw(image_libreria)
|
||||||
|
end
|
||||||
|
if game.status == 6 then
|
||||||
|
-- Normal
|
||||||
|
love.graphics.draw(image_libreria)
|
||||||
|
-- Hover
|
||||||
|
cam_x, cam_y = camera.gcam:getVisible()
|
||||||
|
if mos_x + cam_x > 1050 and mos_x + cam_x < 2000 and mos_y - cam_y > 220 and mos_y - cam_y < 275 then
|
||||||
|
love.graphics.draw(image_libreria_hover)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
arrows.draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
function stage4.mousepressed(game, messages, camera)
|
||||||
|
local mos_x, mos_y = love.mouse.getPosition()
|
||||||
|
if game.status == 6 then
|
||||||
|
cam_x, cam_y = camera.gcam:getVisible()
|
||||||
|
|
||||||
|
if mos_x + cam_x > 1050 and mos_x + cam_x < 2000 and mos_y - cam_y > 202 and mos_y - cam_y < 354 then
|
||||||
|
game.status = 7
|
||||||
|
messages.new_message('cap3stage', 15)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return stage4
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
local anim8 = require 'assets/scripts/vendor/anim8'
|
||||||
|
local tools = require 'assets/scripts/tools'
|
||||||
|
local arrows = require 'assets/scripts/arrows'
|
||||||
|
|
||||||
|
local stage5 = {}
|
||||||
|
local image
|
||||||
|
|
||||||
|
function stage5.load(game, camera)
|
||||||
|
image_tocadiscos = love.graphics.newImage('assets/sprites/tocadiscos/tocadiscos.jpg')
|
||||||
|
image_tocadiscos_hover = love.graphics.newImage('assets/sprites/tocadiscos/tocadiscos_hover.jpg')
|
||||||
|
image_tocadiscos_none = love.graphics.newImage('assets/sprites/tocadiscos/tocadiscos_none.jpg')
|
||||||
|
stage5.world = { w = image_tocadiscos:getWidth(), h= 720}
|
||||||
|
game:setNewSizeWorld(stage5.world.w, stage5.world.h)
|
||||||
|
arrows.load(game, stage5, camera)
|
||||||
|
game.canvas.x = game.window.width / 2
|
||||||
|
end
|
||||||
|
|
||||||
|
local count_time = 0
|
||||||
|
function stage5.update(dt, game)
|
||||||
|
arrows.update(dt)
|
||||||
|
if game.status == 7 then
|
||||||
|
if count_time > 15 then
|
||||||
|
game.bells_enable = true
|
||||||
|
game.status = 7
|
||||||
|
else
|
||||||
|
count_time = dt + count_time
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function stage5.draw(game)
|
||||||
|
local mos_x, mos_y = love.mouse.getPosition()
|
||||||
|
-- No object
|
||||||
|
if game.status > 7 then
|
||||||
|
love.graphics.draw(image_tocadiscos_none)
|
||||||
|
else
|
||||||
|
love.graphics.draw(image_tocadiscos)
|
||||||
|
end
|
||||||
|
if game.status == 7 then
|
||||||
|
-- Normal
|
||||||
|
love.graphics.draw(image_tocadiscos)
|
||||||
|
-- Hover
|
||||||
|
cam_x, cam_y = camera.gcam:getVisible()
|
||||||
|
if mos_x + cam_x > 1005 and mos_x + cam_x < 1150 and mos_y - cam_y > 470 and mos_y - cam_y < 505 then
|
||||||
|
love.graphics.draw(image_tocadiscos_hover)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
arrows.draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
function stage5.mousepressed(game, messages, camera)
|
||||||
|
local mos_x, mos_y = love.mouse.getPosition()
|
||||||
|
if game.status == 7 then
|
||||||
|
cam_x, cam_y = camera.gcam:getVisible()
|
||||||
|
if mos_x + cam_x > 1005 and mos_x + cam_x < 1150 and mos_y - cam_y > 470 and mos_y - cam_y < 505 then
|
||||||
|
game.status = 8
|
||||||
|
messages.new_message('cap4stage', 15)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return stage5
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
local anim8 = require 'assets/scripts/vendor/anim8'
|
||||||
|
local tools = require 'assets/scripts/tools'
|
||||||
|
local arrows = require 'assets/scripts/arrows'
|
||||||
|
|
||||||
|
local stage6 = {}
|
||||||
|
local image
|
||||||
|
|
||||||
|
function stage6.load(game, camera)
|
||||||
|
image_sujetador = love.graphics.newImage('assets/sprites/sujetador/sujetador.jpg')
|
||||||
|
image_sujetador_hover = love.graphics.newImage('assets/sprites/sujetador/sujetador_hover.jpg')
|
||||||
|
image_sujetador_none = love.graphics.newImage('assets/sprites/sujetador/sujetador_none.jpg')
|
||||||
|
stage6.world = { w = image_sujetador:getWidth(), h= 720}
|
||||||
|
game:setNewSizeWorld(stage6.world.w, stage6.world.h)
|
||||||
|
arrows.load(game, stage6, camera)
|
||||||
|
game.canvas.x = game.window.width / 2
|
||||||
|
end
|
||||||
|
|
||||||
|
local count_time = 0
|
||||||
|
function stage6.update(dt, game)
|
||||||
|
arrows.update(dt)
|
||||||
|
if game.status == 8 then
|
||||||
|
if count_time > 15 then
|
||||||
|
game.bells_enable = true
|
||||||
|
game.status = 7
|
||||||
|
else
|
||||||
|
count_time = dt + count_time
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function stage6.draw(game)
|
||||||
|
local mos_x, mos_y = love.mouse.getPosition()
|
||||||
|
-- No object
|
||||||
|
if game.status > 8 then
|
||||||
|
love.graphics.draw(image_sujetador_none)
|
||||||
|
else
|
||||||
|
love.graphics.draw(image_sujetador)
|
||||||
|
end
|
||||||
|
if game.status == 8 then
|
||||||
|
-- Normal
|
||||||
|
love.graphics.draw(image_sujetador)
|
||||||
|
-- Hover
|
||||||
|
cam_x, cam_y = camera.gcam:getVisible()
|
||||||
|
if mos_x + cam_x > 462 and mos_x + cam_x < 648 and mos_y - cam_y > 165 and mos_y - cam_y < 560 then
|
||||||
|
love.graphics.draw(image_sujetador_hover)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
arrows.draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
function stage6.mousepressed(game, messages, camera)
|
||||||
|
local mos_x, mos_y = love.mouse.getPosition()
|
||||||
|
if game.status == 8 then
|
||||||
|
cam_x, cam_y = camera.gcam:getVisible()
|
||||||
|
if mos_x + cam_x > 462 and mos_x + cam_x < 648 and mos_y - cam_y > 165 and mos_y - cam_y < 560 then
|
||||||
|
game.status = 9
|
||||||
|
messages.new_message('cap5stage', 15)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return stage6
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
local anim8 = require 'assets/scripts/vendor/anim8'
|
||||||
|
local tools = require 'assets/scripts/tools'
|
||||||
|
local arrows = require 'assets/scripts/arrows'
|
||||||
|
|
||||||
|
local stage7 = {}
|
||||||
|
local image
|
||||||
|
|
||||||
|
function stage7.load(game, camera)
|
||||||
|
if game.status == 9 then
|
||||||
|
image = love.graphics.newImage('assets/sprites/background/final bueno_3.jpg')
|
||||||
|
stage7.music = love.audio.newSource("assets/audio/fx/door_win.wav")
|
||||||
|
stage7.music2 = love.audio.newSource("assets/audio/music/our_song_end.mp3")
|
||||||
|
else
|
||||||
|
image = love.graphics.newImage('assets/sprites/background/dead.jpg')
|
||||||
|
stage7.music = love.audio.newSource("assets/audio/fx/door_lose.wav")
|
||||||
|
end
|
||||||
|
game:stopMusic()
|
||||||
|
stage7.world = { w = image:getWidth(), h= 720}
|
||||||
|
game:setNewSizeWorld(stage7.world.w, stage7.world.h)
|
||||||
|
arrows.load(game, stage7, camera)
|
||||||
|
game.canvas.x = game.window.width / 2
|
||||||
|
stage7.music2:play()
|
||||||
|
stage7.music:play()
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function stage7.update(dt, game)
|
||||||
|
arrows.update(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
function stage7.draw()
|
||||||
|
love.graphics.draw(image)
|
||||||
|
arrows.draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
return stage7
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
local anim8 = require 'assets/scripts/vendor/anim8'
|
||||||
|
local tools = require 'assets/scripts/tools'
|
||||||
|
local arrows = require 'assets/scripts/arrows'
|
||||||
|
|
||||||
|
local stage8 = {}
|
||||||
|
local image
|
||||||
|
|
||||||
|
function stage8.load(game, camera)
|
||||||
|
image = love.graphics.newImage('assets/sprites/background/fondo_vacio.jpg')
|
||||||
|
stage8.world = { w = image:getWidth(), h= 720}
|
||||||
|
game:setNewSizeWorld(stage8.world.w, stage8.world.h)
|
||||||
|
arrows.load(game, stage8, camera)
|
||||||
|
game.canvas.x = game.window.width / 2
|
||||||
|
end
|
||||||
|
|
||||||
|
function stage8.update(dt, game)
|
||||||
|
arrows.update(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
function stage8.draw()
|
||||||
|
love.graphics.draw(image)
|
||||||
|
arrows.draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
return stage8
|
||||||
@@ -15,17 +15,24 @@ end
|
|||||||
|
|
||||||
function stages.update(dt, game, camera)
|
function stages.update(dt, game, camera)
|
||||||
if game.level ~= currentLevel then
|
if game.level ~= currentLevel then
|
||||||
|
back_pressed = false
|
||||||
|
if game.level < currentLevel then
|
||||||
|
back_pressed = true
|
||||||
|
end
|
||||||
currentLevel = game.level
|
currentLevel = game.level
|
||||||
dynamic_stage = require("assets/scripts/stage"..currentLevel)
|
dynamic_stage = require("assets/scripts/stage"..currentLevel)
|
||||||
dynamic_stage.load(game, camera)
|
dynamic_stage.load(game, camera, back_pressed)
|
||||||
|
if back_pressed then
|
||||||
|
game.canvas.x = dynamic_stage.world.w - (game.window.width / 2)
|
||||||
|
end
|
||||||
dynamic_stage.update(dt, game)
|
dynamic_stage.update(dt, game)
|
||||||
else
|
else
|
||||||
dynamic_stage.update(dt, game)
|
dynamic_stage.update(dt, game)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function stages.draw()
|
function stages.draw(game, camera)
|
||||||
dynamic_stage.draw()
|
dynamic_stage.draw(game, camera)
|
||||||
end
|
end
|
||||||
|
|
||||||
return stages
|
return stages
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
local start = {}
|
||||||
|
|
||||||
|
function start.load(game)
|
||||||
|
start.img = love.graphics.newImage('assets/sprites/menu/start.jpg')
|
||||||
|
start.music = love.audio.newSource("assets/audio/music/main_theme_menu.mp3")
|
||||||
|
start.music:play()
|
||||||
|
start.status = 1
|
||||||
|
game:stopMusic()
|
||||||
|
end
|
||||||
|
|
||||||
|
function start.update(dt, game)
|
||||||
|
if game.status == start.status then
|
||||||
|
game:stopMusic()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function start.draw(game)
|
||||||
|
if game.status == start.status then
|
||||||
|
love.graphics.draw(start.img, 0, 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function start.mousepressed(game)
|
||||||
|
local mos_x, mos_y = love.mouse.getPosition()
|
||||||
|
if game.status == start.status then
|
||||||
|
if mos_x > 411 and mos_x < 578 and mos_y > 400 and mos_y < 479 then
|
||||||
|
game.status = start.status + 1
|
||||||
|
start.music:stop()
|
||||||
|
game:playMusic()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return start
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
local anim8 = require 'assets/scripts/vendor/anim8'
|
||||||
|
local tools = require 'assets/scripts/tools'
|
||||||
|
local arrows = require 'assets/scripts/arrows'
|
||||||
|
|
||||||
|
local stress = {}
|
||||||
|
local image
|
||||||
|
|
||||||
|
function stress.load(game, camera)
|
||||||
|
image = love.graphics.newImage('assets/sprites/stress/stress.png')
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function stress.update(dt, game)
|
||||||
|
stress.stress = game.stress
|
||||||
|
arrows.update(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
function stress.draw()
|
||||||
|
if stress.stress == true then
|
||||||
|
cam_x, cam_y = camera.gcam:getVisible()
|
||||||
|
love.graphics.setColor(255, 255, 255, 90)
|
||||||
|
love.graphics.draw(image, cam_x, cam_y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return stress
|
||||||
@@ -71,4 +71,12 @@ function tools.print_r(t)
|
|||||||
print()
|
print()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Get positions from circle
|
||||||
|
function tools.cicle_positions(x, y, radius, angle)
|
||||||
|
local pos_x, pos_y = nil, nil
|
||||||
|
pos_x = x + radius * math.cos(math.rad(angle))
|
||||||
|
pos_y = y + radius * math.sin(math.rad(angle))
|
||||||
|
return pos_x, pos_y
|
||||||
|
end
|
||||||
|
|
||||||
return tools
|
return tools
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
local stack = {}
|
||||||
|
|
||||||
|
function love.graphics.pushState()
|
||||||
|
--get state
|
||||||
|
local state = {}
|
||||||
|
state.blendMode = love.graphics.getBlendMode()
|
||||||
|
state.color = {love.graphics.getColor()}
|
||||||
|
state.font = love.graphics.getFont() or 12
|
||||||
|
state.lineStyle = love.graphics.getLineStyle()
|
||||||
|
state.scissor = love.graphics.getScissor() or
|
||||||
|
{love.graphics.getScissor()} or
|
||||||
|
{0,0,love.graphics.getWidth(), love.graphics.getHeight()}
|
||||||
|
|
||||||
|
-- push state
|
||||||
|
stack[#stack+1] = state
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.graphics.popState()
|
||||||
|
-- restore state
|
||||||
|
local state = stack[#stack]
|
||||||
|
love.graphics.setBlendMode( state.blendMode )
|
||||||
|
love.graphics.setColor( unpack(state.color) )
|
||||||
|
love.graphics.setFont( state.font )
|
||||||
|
love.graphics.setScissor( unpack(state.scissor) )
|
||||||
|
|
||||||
|
-- pop state
|
||||||
|
stack[#stack] = nil
|
||||||
|
end
|
||||||
|
|
||||||
@@ -0,0 +1,463 @@
|
|||||||
|
--[[
|
||||||
|
MessageInABottle.lua - 2010-11-08
|
||||||
|
http://bitbucket.org/dannyfritz/message-in-a-bottle/
|
||||||
|
Danny "TechnoCat" Fritz dannyfritz@gmail.com
|
||||||
|
Commons Attribution-ShareAlike 3.0
|
||||||
|
--]]
|
||||||
|
|
||||||
|
--An OO library for Lua by Kikito,
|
||||||
|
--can be found at https://github.com/kikito/middleclass
|
||||||
|
require 'assets/scripts/vendor/MiddleClass'
|
||||||
|
require 'assets/scripts/vendor/GraphicsStateStack'
|
||||||
|
|
||||||
|
|
||||||
|
MessageBottle = class('MessageBottle')
|
||||||
|
TimeBottle = MessageBottle:subclass('TimeBottle')
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Default values
|
||||||
|
--]]
|
||||||
|
MessageBottle.DEFAULT_SOUND = 'assets/audio/fx/messages/blip.ogg'
|
||||||
|
MessageBottle.DEFAULT_VOLUME = 0.5
|
||||||
|
MessageBottle.DEFAULT_BUTTON = ' ' --Button to close/select
|
||||||
|
MessageBottle.DEFAULT_INDICATOR = love.graphics.newImage('assets/sprites/messages/input-indicator.png') --Image to
|
||||||
|
|
||||||
|
MessageBottle.DEFAULT_X = 20
|
||||||
|
MessageBottle.DEFAULT_Y = 180
|
||||||
|
MessageBottle.DEFAULT_WIDTH = 300
|
||||||
|
MessageBottle.DEFAULT_HEIGHT = 80
|
||||||
|
|
||||||
|
MessageBottle.DEFAULT_FONT = love.graphics.newFont(18)
|
||||||
|
MessageBottle.DEFAULT_ALIGN = 'left'
|
||||||
|
MessageBottle.DEFAULT_FGCOLOR = {r=255,g=255,b=255}
|
||||||
|
MessageBottle.DEFAULT_FGALPHA = 1
|
||||||
|
|
||||||
|
MessageBottle.DEFAULT_BGRADIUS = 10 --pixels to draw the rounded corners
|
||||||
|
MessageBottle.DEFAULT_BGRADIUSSEGMENTS = 32 --how precisely to draw the circle
|
||||||
|
MessageBottle.DEFAULT_BGCOLOR = {r=0,g=0,b=0}
|
||||||
|
MessageBottle.DEFAULT_BGALPHA = 0.3
|
||||||
|
|
||||||
|
MessageBottle.DEFAULT_EASETIME = 0.5 --seconds to ease
|
||||||
|
MessageBottle.DEFAULT_FADETIME = 0.3 --seconds to fade alpha
|
||||||
|
MessageBottle.DEFAULT_EASEOFFSET = 10
|
||||||
|
|
||||||
|
MessageBottle.DEFAULT_ID = "NO_ID"
|
||||||
|
MessageBottle.DEFAULT_TEXT = "Text is not set."
|
||||||
|
|
||||||
|
TimeBottle.TIMEOUT = 6 --seconds to keep the bottle alive
|
||||||
|
|
||||||
|
function MessageBottle:drawBackground()
|
||||||
|
--draw background
|
||||||
|
--self.bgAlpha
|
||||||
|
--self.alpha
|
||||||
|
love.graphics.pushState()
|
||||||
|
local r,g,b,a = self.bgColor.r, self.bgColor.g, self.bgColor.b
|
||||||
|
local a = self.bgAlpha * self.alpha
|
||||||
|
local r = self.bgRadius
|
||||||
|
local x,y = self.x, self.y
|
||||||
|
local w,h = self.width, self.height
|
||||||
|
local segments = self.bgRadiusSegments
|
||||||
|
love.graphics.setColor(r,g,b,a)
|
||||||
|
--center rectangle
|
||||||
|
love.graphics.rectangle("fill",
|
||||||
|
x, y,
|
||||||
|
w, h)
|
||||||
|
--top border
|
||||||
|
love.graphics.rectangle("fill",
|
||||||
|
x, y-r,
|
||||||
|
w, r)
|
||||||
|
-- right border
|
||||||
|
love.graphics.rectangle("fill",
|
||||||
|
x+w, y,
|
||||||
|
r, h)
|
||||||
|
--bottom border
|
||||||
|
love.graphics.rectangle("fill",
|
||||||
|
x, y+h,
|
||||||
|
w, r)
|
||||||
|
--left border
|
||||||
|
love.graphics.rectangle("fill",
|
||||||
|
x-r, y,
|
||||||
|
r, h)
|
||||||
|
--top left corner
|
||||||
|
love.graphics.setScissor(
|
||||||
|
x-r, y-r,
|
||||||
|
r, r)
|
||||||
|
love.graphics.circle("fill",
|
||||||
|
x, y,
|
||||||
|
r, segments)
|
||||||
|
--top right corner
|
||||||
|
love.graphics.setScissor(
|
||||||
|
x+w, y-r,
|
||||||
|
r, r)
|
||||||
|
love.graphics.circle("fill",
|
||||||
|
x+w, y,
|
||||||
|
r, segments)
|
||||||
|
--bottom right corner
|
||||||
|
love.graphics.setScissor(
|
||||||
|
x+w, y+h,
|
||||||
|
r, r)
|
||||||
|
love.graphics.circle("fill",
|
||||||
|
x+w, y+h,
|
||||||
|
r, segments)
|
||||||
|
--bottom left corner
|
||||||
|
love.graphics.setScissor(
|
||||||
|
x-r, y+h,
|
||||||
|
r, r)
|
||||||
|
love.graphics.circle("fill",
|
||||||
|
x, y+h,
|
||||||
|
r, segments)
|
||||||
|
love.graphics.popState()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Ocean
|
||||||
|
This is a class to store and invoke the focussed bottles
|
||||||
|
(A queue)
|
||||||
|
--]]
|
||||||
|
Ocean = class('Ocean')
|
||||||
|
|
||||||
|
function Ocean:initialize()
|
||||||
|
super.initialize(self)
|
||||||
|
self.bottles = {}
|
||||||
|
self.response = ""
|
||||||
|
self.responseTime = 0
|
||||||
|
self.responseID = ""
|
||||||
|
self.time = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function Ocean:update(dt)
|
||||||
|
if #self.bottles > 0 then
|
||||||
|
self.time = self.time + dt
|
||||||
|
local bottle = self.bottles[1]
|
||||||
|
if bottle.focusTime == 0 then
|
||||||
|
bottle:enterCallback()
|
||||||
|
end
|
||||||
|
local tmp = self.response
|
||||||
|
self.response = bottle:update(dt)
|
||||||
|
if self.response==nil then
|
||||||
|
self.response = tmp
|
||||||
|
else
|
||||||
|
self.responseTime = self.time
|
||||||
|
self.responseID = self.bottles[1].id
|
||||||
|
end
|
||||||
|
if not bottle.alive then
|
||||||
|
bottle:exitCallback(self.response)
|
||||||
|
table.remove(self.bottles,1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Ocean:draw()
|
||||||
|
if #self.bottles > 0 then
|
||||||
|
self.bottles[1]:draw()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Ocean:addBottle(bottle)
|
||||||
|
local bottles = self.bottles
|
||||||
|
table.insert(bottles, #bottles+1, bottle)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Message Bottle
|
||||||
|
This is a default message bottle class
|
||||||
|
Bottle background is drawn here.
|
||||||
|
--]]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function MessageBottle:initialize(id, text)
|
||||||
|
super.initialize(self)
|
||||||
|
|
||||||
|
self.id = id or self.class.DEFAULT_ID
|
||||||
|
self.text = text or self.class.DEFAULT_TEXT
|
||||||
|
self.alive = true --Set to flag to mark for deletion
|
||||||
|
|
||||||
|
--Callbacks
|
||||||
|
self.enterCB = function() return end
|
||||||
|
self.exitCB = function(response) return end
|
||||||
|
|
||||||
|
--Sounds
|
||||||
|
self.sound = self.class.DEFAULT_SOUND
|
||||||
|
self.volume = self.class.DEFAULT_VOLUME
|
||||||
|
|
||||||
|
--Buttons
|
||||||
|
self.button = self.class.DEFAULT_BUTTON
|
||||||
|
self.buttonDown = false --Flag to make the button action happen on release
|
||||||
|
self.indicator = indicator or self.class.DEFAULT_INDICATOR
|
||||||
|
|
||||||
|
--Dimensions
|
||||||
|
self.x = self.class.DEFAULT_X
|
||||||
|
self.y = self.class.DEFAULT_Y
|
||||||
|
self.width = self.class.DEFAULT_WIDTH
|
||||||
|
self.height = self.class.DEFAULT_HEIGHT
|
||||||
|
|
||||||
|
--Foreground settings
|
||||||
|
self.fgColor = self.class.DEFAULT_FGCOLOR
|
||||||
|
self.fgAlpha = self.class.DEFAULT_FGALPHA
|
||||||
|
self.font = self.class.DEFAULT_FONT
|
||||||
|
self.align = self.class.DEFAULT_ALIGN
|
||||||
|
|
||||||
|
--Background settings
|
||||||
|
self.bgColor = self.class.DEFAULT_BGCOLOR
|
||||||
|
self.bgAlpha = self.class.DEFAULT_BGALPHA
|
||||||
|
self.bgRadius = self.class.DEFAULT_BGRADIUS
|
||||||
|
self.bgRadiusSegments = self.class.DEFAULT_RADIUSSEGMENTS
|
||||||
|
|
||||||
|
--Ease settings
|
||||||
|
self.easeTarget = self.class.DEFAULT_Y
|
||||||
|
self.easeOffset = self.class.DEFAULT_EASEOFFSET
|
||||||
|
self.easeTime = self.class.DEFAULT_EASETIME
|
||||||
|
|
||||||
|
--Fade settings
|
||||||
|
self.focusTime = 0 --seconds the message has been in focus
|
||||||
|
self.alpha = 0
|
||||||
|
self.fadeTime = self.class.DEFAULT_FADETIME
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:baseUpdate(dt, focusTime)
|
||||||
|
self.y = self:ease(focusTime,self.easeTime, self.easeTarget,self.easeOffset)
|
||||||
|
self.alpha = self:fade(focusTime,self.fadeTime, 0,255)
|
||||||
|
self.focusTime = self.focusTime + dt
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:enterCallback()
|
||||||
|
--Sound
|
||||||
|
self.source = love.audio.newSource(self.sound, "static")
|
||||||
|
self.source:setVolume(self.volume)
|
||||||
|
self.source:play()
|
||||||
|
self.enterCB()
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:exitCallback(response)
|
||||||
|
self.exitCB(response)
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:ease(t,d, a,b)
|
||||||
|
if d <= t then
|
||||||
|
function self:ease(t,d, a,b) return self.y end
|
||||||
|
end
|
||||||
|
local v = -(t/d)*(t/d-2)*b+a
|
||||||
|
return math.floor(v)
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:fade(t,d, a,b)
|
||||||
|
if d <= t then
|
||||||
|
function self:fade(t,d, a,b) return 255 end
|
||||||
|
end
|
||||||
|
local v = t/d*b+a
|
||||||
|
if v>=255 then
|
||||||
|
v=255
|
||||||
|
function self:fade(t,d, a,b) return 255 end
|
||||||
|
end
|
||||||
|
return v
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setX(x)
|
||||||
|
self.x = x
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setY(y)
|
||||||
|
self.easeTarget = y
|
||||||
|
self.y = y
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setPosition(x, y)
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.easeTarget = y
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setWidth(width)
|
||||||
|
self.width = width
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setHeight(height)
|
||||||
|
self.height = height
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setButton(button)
|
||||||
|
self.button = button
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setEase(ease)
|
||||||
|
self.ease = ease
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setEaseTime(easeTime)
|
||||||
|
self.easeTime = easeTime
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setFade(fade)
|
||||||
|
self.fade = fade
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setFadeTime(fadeTime)
|
||||||
|
self.fadeTime = fadeTime
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setEnterCallback(enterCB)
|
||||||
|
self.enterCB = enterCB
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setExitCallback(exitCB)
|
||||||
|
self.exitCB = exitCB
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setSound(sound)
|
||||||
|
if sound==nil then
|
||||||
|
self.volume = 0
|
||||||
|
return
|
||||||
|
end
|
||||||
|
self.sound = sound
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setVolume(volume)
|
||||||
|
self.volume = volume
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setIndicator(indicator)
|
||||||
|
self.indicator = love.graphics.newImage(indicator)
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setFgColor(r, g, b, a)
|
||||||
|
self.fgColor = {r, g, b, a}
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setFont(font, size)
|
||||||
|
self.font = love.graphics.newFont(font,size)
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setAlign(align)
|
||||||
|
self.align = align
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setBgColor(r, g, b, a)
|
||||||
|
self.bgColor = {r, g, b, a}
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setBgRadius(bgRadius)
|
||||||
|
self.bgRadius = bgRadius
|
||||||
|
end
|
||||||
|
|
||||||
|
function MessageBottle:setBgRadiusSegments(bgRadiusSegments)
|
||||||
|
self.bgRadiusSegments = bgRadiusSegments
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Staying Bottle
|
||||||
|
This pops up a message bottle and
|
||||||
|
goes away after a button is pressed
|
||||||
|
--]]
|
||||||
|
|
||||||
|
StayBottle = MessageBottle:subclass('StayBottle')
|
||||||
|
|
||||||
|
function StayBottle:initialize(id, text)
|
||||||
|
super.initialize(self, id, text)
|
||||||
|
end
|
||||||
|
|
||||||
|
function StayBottle:update(dt)
|
||||||
|
self:baseUpdate(dt,self.focusTime)
|
||||||
|
if love.keyboard.isDown(self.button) and self.buttonDown==false then
|
||||||
|
self.buttonDown = true
|
||||||
|
elseif self.buttonDown==true and not love.keyboard.isDown(self.button) then
|
||||||
|
self.alive = false
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function StayBottle:draw()
|
||||||
|
love.graphics.pushState()
|
||||||
|
self:drawBackground()
|
||||||
|
local color = self.fgColor
|
||||||
|
love.graphics.setColor(color.r,color.g,color.b,self.alpha)
|
||||||
|
love.graphics.setFont(self.font)
|
||||||
|
local text = ""
|
||||||
|
text = text..self.text..'\n'
|
||||||
|
local button = self.button
|
||||||
|
if button == " " then
|
||||||
|
button = "space"
|
||||||
|
end
|
||||||
|
if math.floor(self.focusTime)%2 == 0 then
|
||||||
|
text = text..'press \''..button..'\' to continue'
|
||||||
|
else
|
||||||
|
text = text..'press \''..button..'\' to continue...'
|
||||||
|
end
|
||||||
|
love.graphics.printf(text, self.x,self.y, self.width, self.align)
|
||||||
|
local floaty = math.sin(self.focusTime*3)*3
|
||||||
|
local y = self.y + self.height + floaty - self.indicator:getHeight()/2
|
||||||
|
love.graphics.draw(self.indicator,self.x+self.width/2, y)
|
||||||
|
love.graphics.popState()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Timed Bottle
|
||||||
|
This pops up a message bottle and
|
||||||
|
goes away after a set time duration
|
||||||
|
--]]
|
||||||
|
|
||||||
|
function TimeBottle:initialize(id, text, timeout)
|
||||||
|
super.initialize(self, id, text)
|
||||||
|
self.timeout = timeout or self.class.TIMEOUT
|
||||||
|
end
|
||||||
|
|
||||||
|
function TimeBottle:update(dt)
|
||||||
|
self:baseUpdate(dt,self.focusTime)
|
||||||
|
self.timeout = self.timeout - dt
|
||||||
|
if love.keyboard.isDown(self.button) and self.buttonDown==false then
|
||||||
|
self.buttonDown = true
|
||||||
|
elseif self.buttonDown==true and not love.keyboard.isDown(self.button) then
|
||||||
|
self.alive = false
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
if self.timeout <= 0 then --count down a timer
|
||||||
|
self.alive = false
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function TimeBottle:draw()
|
||||||
|
love.graphics.pushState()
|
||||||
|
self:drawBackground()
|
||||||
|
local color = self.fgColor
|
||||||
|
love.graphics.setColor(color.r,color.g,color.b,self.alpha)
|
||||||
|
love.graphics.setFont(self.font)
|
||||||
|
love.graphics.printf(self.text, self.x,self.y, self.width, self.align)
|
||||||
|
love.graphics.popState()
|
||||||
|
end
|
||||||
|
|
||||||
|
function TimeBottle:setTimeout(timeout)
|
||||||
|
self.timeout = timeout
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Choice Bottle
|
||||||
|
This pops up a message with choices bottle
|
||||||
|
and goes away after a choice is made
|
||||||
|
--]]
|
||||||
|
|
||||||
|
ChoiceBottle = MessageBottle:subclass('ChoiceBottle')
|
||||||
|
|
||||||
|
function ChoiceBottle:initialize(id, text, ...)
|
||||||
|
super.initialize(self, id, text)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ChoiceBottle:update(dt)
|
||||||
|
--Draw text and choices along with cursor
|
||||||
|
--Check keyboard / mouse input for selection
|
||||||
|
end
|
||||||
|
|
||||||
|
function ChoiceBottle:draw()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
-----------------------------------------------------------------------------------
|
||||||
|
-- MiddleClass.lua
|
||||||
|
-- Enrique García ( enrique.garcia.cota [AT] gmail [DOT] com ) - 19 Oct 2009
|
||||||
|
-- Based on YaciCode, from Julien Patte and LuaObject, from Sébastien Rocca-Serra
|
||||||
|
-----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local _classes = setmetatable({}, {__mode = "k"}) -- weak table storing references to all declared _classes and their included modules
|
||||||
|
|
||||||
|
Object = { name = "Object" } -- The 'Object' class
|
||||||
|
|
||||||
|
_classes[Object] = { modules={} } -- adds Object to the list of _classes
|
||||||
|
|
||||||
|
-- creates a new instance
|
||||||
|
Object.new = function(theClass, ...)
|
||||||
|
assert(_classes[theClass]~=nil, "Use class:new instead of class.new")
|
||||||
|
|
||||||
|
local instance = setmetatable({ class = theClass }, theClass.__classDict)
|
||||||
|
instance:initialize(...)
|
||||||
|
return instance
|
||||||
|
end
|
||||||
|
|
||||||
|
local _metamethods = { '__add', '__sub', '__mul', '__div', '__mod', '__pow', '__unm',
|
||||||
|
'__lt', '__le', '__call', '__gc', '__tostring', '__concat' }
|
||||||
|
|
||||||
|
-- creates a subclass
|
||||||
|
Object.subclass = function(theClass, name)
|
||||||
|
assert(_classes[theClass]~=nil, "Use class:subclass instead of class.subclass")
|
||||||
|
assert( type(name)=="string", "You must provide a name(string) for your class")
|
||||||
|
|
||||||
|
local theSubclass = { name = name, superclass = theClass, __classDict = {} }
|
||||||
|
local classDict = theSubclass.__classDict
|
||||||
|
|
||||||
|
-- classDict is the instances' metatable. It "points to himself" so they start looking for methods there.
|
||||||
|
classDict.__index = classDict
|
||||||
|
|
||||||
|
-- metamethods & methods are "looked up" through implementation and __index
|
||||||
|
for _,m in ipairs(_metamethods) do
|
||||||
|
classDict[m]= function(...)
|
||||||
|
assert( type(theClass.__classDict[m])=='function', "Class " .. theSubclass.name .. " does not implement the " .. m .. " metamethod")
|
||||||
|
return theClass.__classDict[m](...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
setmetatable(classDict, {__index = theClass.__classDict})
|
||||||
|
|
||||||
|
-- control how the new methods are inserted on the subclass, and how they are looked up
|
||||||
|
setmetatable(theSubclass, {
|
||||||
|
__index = function(_,methodName)
|
||||||
|
-- search first on the subclass's classdict, then on the superclass
|
||||||
|
return classDict[methodName]~= nil and classDict[methodName] or theClass[methodName]
|
||||||
|
end,
|
||||||
|
__newindex = function(_, methodName, method) -- when adding new methods, include a "super" function
|
||||||
|
if type(method) == 'function' then
|
||||||
|
local fenv = getfenv(method)
|
||||||
|
local newenv = setmetatable( {super = theClass.__classDict}, {__index = fenv, __newindex = fenv} )
|
||||||
|
setfenv( method, newenv )
|
||||||
|
end
|
||||||
|
rawset(classDict, methodName, method)
|
||||||
|
end,
|
||||||
|
__tostring = function() return ("class ".. name) end,
|
||||||
|
__call = function(_, ...) return theSubclass:new(...) end
|
||||||
|
})
|
||||||
|
-- instance methods go after the setmetatable, so we can use "super"
|
||||||
|
theSubclass.initialize = function(instance,...) super.initialize(instance) end
|
||||||
|
|
||||||
|
_classes[theSubclass]={ modules={} } --registers the new class on the list of _classes
|
||||||
|
|
||||||
|
theClass:subclassed(theSubclass) -- hook method. By default it does nothing
|
||||||
|
|
||||||
|
return theSubclass
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Mixin extension function - simulates very basically ruby's include(module)
|
||||||
|
-- module is a lua table of functions. The functions will be copied to the class
|
||||||
|
-- if present in the module, the included() method will be called
|
||||||
|
Object.include = function(theClass, module, ... )
|
||||||
|
assert(_classes[theClass]~=nil, "Use class:includes instead of class.includes")
|
||||||
|
for methodName,method in pairs(module) do
|
||||||
|
if methodName ~="included" then theClass[methodName] = method end
|
||||||
|
end
|
||||||
|
if type(module.included)=="function" then module:included(theClass, ... ) end
|
||||||
|
_classes[theClass].modules[module] = true
|
||||||
|
return theClass
|
||||||
|
end
|
||||||
|
|
||||||
|
-- built-in methods
|
||||||
|
Object.__classDict = {
|
||||||
|
initialize = function(instance, ...) end, -- empty method
|
||||||
|
destroy = function(instance) end, -- empty method
|
||||||
|
__tostring = function(instance) return ("instance of ".. instance.class.name) end,
|
||||||
|
subclassed = function(theClass, other) end -- empty method
|
||||||
|
}
|
||||||
|
Object.__classDict.__index = Object.__classDict -- instances of Object need this
|
||||||
|
|
||||||
|
-- This allows doing tostring(obj) and Object() instead of Object:new()
|
||||||
|
setmetatable(Object, { __index = Object.__classDict, __newindex = Object.__classDict,
|
||||||
|
__tostring = function() return ("class Object") end,
|
||||||
|
__call = Object.new
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Returns true if aClass is a subclass of other, false otherwise
|
||||||
|
function subclassOf(other, aClass)
|
||||||
|
if _classes[aClass]==nil or _classes[other]==nil then return false end
|
||||||
|
if aClass.superclass==nil then return false end -- aClass is Object, or a non-class
|
||||||
|
return aClass.superclass == other or subclassOf(other, aClass.superclass)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns true if obj is an instance of aClass (or one of its subclasses) false otherwise
|
||||||
|
function instanceOf(aClass, obj)
|
||||||
|
if _classes[aClass]==nil or type(obj)~='table' or _classes[obj.class]==nil then return false end
|
||||||
|
if obj.class==aClass then return true end
|
||||||
|
return subclassOf(aClass, obj.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns true if the a module has already been included on a class (or a superclass of that class)
|
||||||
|
function includes(module, aClass)
|
||||||
|
if _classes[aClass]==nil or _classes[aClass].modules==nil then return false end
|
||||||
|
if _classes[aClass].modules[module] then return true end
|
||||||
|
return includes(module, aClass.superclass)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Creates a new class named 'name'. Uses Object if no baseClass is specified. Additional parameters for compatibility
|
||||||
|
function class(name, baseClass, ...)
|
||||||
|
baseClass = baseClass or Object
|
||||||
|
return baseClass:subclass(name, ...)
|
||||||
|
end
|
||||||
@@ -0,0 +1,724 @@
|
|||||||
|
--
|
||||||
|
-- lovebird
|
||||||
|
--
|
||||||
|
-- Copyright (c) 2016 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.
|
||||||
|
--
|
||||||
|
|
||||||
|
local socket = require "socket"
|
||||||
|
|
||||||
|
local lovebird = { _version = "0.4.1" }
|
||||||
|
|
||||||
|
lovebird.loadstring = loadstring or load
|
||||||
|
lovebird.inited = false
|
||||||
|
lovebird.host = "*"
|
||||||
|
lovebird.buffer = ""
|
||||||
|
lovebird.lines = {}
|
||||||
|
lovebird.connections = {}
|
||||||
|
lovebird.pages = {}
|
||||||
|
|
||||||
|
lovebird.wrapprint = true
|
||||||
|
lovebird.timestamp = true
|
||||||
|
lovebird.allowhtml = false
|
||||||
|
lovebird.echoinput = true
|
||||||
|
lovebird.port = 8000
|
||||||
|
lovebird.whitelist = { "127.0.0.1" }
|
||||||
|
lovebird.maxlines = 200
|
||||||
|
lovebird.updateinterval = .5
|
||||||
|
|
||||||
|
|
||||||
|
lovebird.pages["index"] = [[
|
||||||
|
<?lua
|
||||||
|
-- Handle console input
|
||||||
|
if req.parsedbody.input then
|
||||||
|
local str = req.parsedbody.input
|
||||||
|
if lovebird.echoinput then
|
||||||
|
lovebird.pushline({ type = 'input', str = str })
|
||||||
|
end
|
||||||
|
if str:find("^=") then
|
||||||
|
str = "print(" .. str:sub(2) .. ")"
|
||||||
|
end
|
||||||
|
xpcall(function() assert(lovebird.loadstring(str, "input"))() end,
|
||||||
|
lovebird.onerror)
|
||||||
|
end
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="x-ua-compatible" content="IE=Edge"/>
|
||||||
|
<title>lovebird</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: helvetica, verdana, sans;
|
||||||
|
background: #FFFFFF;
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
.timestamp {
|
||||||
|
color: #909090;
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
.repeatcount {
|
||||||
|
color: #F0F0F0;
|
||||||
|
background: #505050;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
padding-left: 4px;
|
||||||
|
padding-right: 4px;
|
||||||
|
padding-top: 0px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
border-radius: 7px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.errormarker {
|
||||||
|
color: #F0F0F0;
|
||||||
|
background: #8E0000;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 17px;
|
||||||
|
padding-top: 0px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.greybordered {
|
||||||
|
margin: 12px;
|
||||||
|
background: #F0F0F0;
|
||||||
|
border: 1px solid #E0E0E0;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
.inputline {
|
||||||
|
font-family: mono, courier;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #606060;
|
||||||
|
}
|
||||||
|
.inputline:before {
|
||||||
|
content: '\00B7\00B7\00B7';
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
.errorline {
|
||||||
|
color: #8E0000;
|
||||||
|
}
|
||||||
|
#header {
|
||||||
|
background: #101010;
|
||||||
|
height: 25px;
|
||||||
|
color: #F0F0F0;
|
||||||
|
padding: 9px
|
||||||
|
}
|
||||||
|
#title {
|
||||||
|
float: left;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
#title a {
|
||||||
|
color: #F0F0F0;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
#title a:hover {
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
#version {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
#status {
|
||||||
|
float: right;
|
||||||
|
font-size: 14px;
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
#main a {
|
||||||
|
color: #000000;
|
||||||
|
text-decoration: none;
|
||||||
|
background: #E0E0E0;
|
||||||
|
border: 1px solid #D0D0D0;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding-left: 2px;
|
||||||
|
padding-right: 2px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
#main a:hover {
|
||||||
|
background: #D0D0D0;
|
||||||
|
border: 1px solid #C0C0C0;
|
||||||
|
}
|
||||||
|
#console {
|
||||||
|
position: absolute;
|
||||||
|
top: 40px; bottom: 0px; left: 0px; right: 312px;
|
||||||
|
}
|
||||||
|
#input {
|
||||||
|
position: absolute;
|
||||||
|
margin: 10px;
|
||||||
|
bottom: 0px; left: 0px; right: 0px;
|
||||||
|
}
|
||||||
|
#inputbox {
|
||||||
|
width: 100%;
|
||||||
|
font-family: mono, courier;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
#output {
|
||||||
|
overflow-y: scroll;
|
||||||
|
position: absolute;
|
||||||
|
margin: 10px;
|
||||||
|
line-height: 17px;
|
||||||
|
top: 0px; bottom: 36px; left: 0px; right: 0px;
|
||||||
|
}
|
||||||
|
#env {
|
||||||
|
position: absolute;
|
||||||
|
top: 40px; bottom: 0px; right: 0px;
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
#envheader {
|
||||||
|
padding: 5px;
|
||||||
|
background: #E0E0E0;
|
||||||
|
}
|
||||||
|
#envvars {
|
||||||
|
position: absolute;
|
||||||
|
left: 0px; right: 0px; top: 25px; bottom: 0px;
|
||||||
|
margin: 10px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="header">
|
||||||
|
<div id="title">
|
||||||
|
<a href="https://github.com/rxi/lovebird">lovebird</a>
|
||||||
|
<span id="version"><?lua echo(lovebird._version) ?></span>
|
||||||
|
</div>
|
||||||
|
<div id="status"></div>
|
||||||
|
</div>
|
||||||
|
<div id="main">
|
||||||
|
<div id="console" class="greybordered">
|
||||||
|
<div id="output"> <?lua echo(lovebird.buffer) ?> </div>
|
||||||
|
<div id="input">
|
||||||
|
<form method="post"
|
||||||
|
onkeydown="return onInputKeyDown(event);"
|
||||||
|
onsubmit="onInputSubmit(); return false;">
|
||||||
|
<input id="inputbox" name="input" type="text"
|
||||||
|
autocomplete="off"></input>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="env" class="greybordered">
|
||||||
|
<div id="envheader"></div>
|
||||||
|
<div id="envvars"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
document.getElementById("inputbox").focus();
|
||||||
|
|
||||||
|
var changeFavicon = function(href) {
|
||||||
|
var old = document.getElementById("favicon");
|
||||||
|
if (old) document.head.removeChild(old);
|
||||||
|
var link = document.createElement("link");
|
||||||
|
link.id = "favicon";
|
||||||
|
link.rel = "shortcut icon";
|
||||||
|
link.href = href;
|
||||||
|
document.head.appendChild(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
var truncate = function(str, len) {
|
||||||
|
if (str.length <= len) return str;
|
||||||
|
return str.substring(0, len - 3) + "...";
|
||||||
|
}
|
||||||
|
|
||||||
|
var geturl = function(url, onComplete, onFail) {
|
||||||
|
var req = new XMLHttpRequest();
|
||||||
|
req.onreadystatechange = function() {
|
||||||
|
if (req.readyState != 4) return;
|
||||||
|
if (req.status == 200) {
|
||||||
|
if (onComplete) onComplete(req.responseText);
|
||||||
|
} else {
|
||||||
|
if (onFail) onFail(req.responseText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
url += (url.indexOf("?") > -1 ? "&_=" : "?_=") + Math.random();
|
||||||
|
req.open("GET", url, true);
|
||||||
|
req.send();
|
||||||
|
}
|
||||||
|
|
||||||
|
var divContentCache = {}
|
||||||
|
var updateDivContent = function(id, content) {
|
||||||
|
if (divContentCache[id] != content) {
|
||||||
|
document.getElementById(id).innerHTML = content;
|
||||||
|
divContentCache[id] = content
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var onInputSubmit = function() {
|
||||||
|
var b = document.getElementById("inputbox");
|
||||||
|
var req = new XMLHttpRequest();
|
||||||
|
req.open("POST", "/", true);
|
||||||
|
req.send("input=" + encodeURIComponent(b.value));
|
||||||
|
/* Do input history */
|
||||||
|
if (b.value && inputHistory[0] != b.value) {
|
||||||
|
inputHistory.unshift(b.value);
|
||||||
|
}
|
||||||
|
inputHistory.index = -1;
|
||||||
|
/* Reset */
|
||||||
|
b.value = "";
|
||||||
|
refreshOutput();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Input box history */
|
||||||
|
var inputHistory = [];
|
||||||
|
inputHistory.index = 0;
|
||||||
|
var onInputKeyDown = function(e) {
|
||||||
|
var key = e.which || e.keyCode;
|
||||||
|
if (key != 38 && key != 40) return true;
|
||||||
|
var b = document.getElementById("inputbox");
|
||||||
|
if (key == 38 && inputHistory.index < inputHistory.length - 1) {
|
||||||
|
/* Up key */
|
||||||
|
inputHistory.index++;
|
||||||
|
}
|
||||||
|
if (key == 40 && inputHistory.index >= 0) {
|
||||||
|
/* Down key */
|
||||||
|
inputHistory.index--;
|
||||||
|
}
|
||||||
|
b.value = inputHistory[inputHistory.index] || "";
|
||||||
|
b.selectionStart = b.value.length;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Output buffer and status */
|
||||||
|
var refreshOutput = function() {
|
||||||
|
geturl("/buffer", function(text) {
|
||||||
|
updateDivContent("status", "connected ●");
|
||||||
|
if (updateDivContent("output", text)) {
|
||||||
|
var div = document.getElementById("output");
|
||||||
|
div.scrollTop = div.scrollHeight;
|
||||||
|
}
|
||||||
|
/* Update favicon */
|
||||||
|
changeFavicon("data:image/png;base64," +
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAP1BMVEUAAAAAAAAAAAD////19fUO"+
|
||||||
|
"Dg7v7+/h4eGzs7MlJSUeHh7n5+fY2NjJycnGxsa3t7eioqKfn5+QkJCHh4d+fn7zU+b5AAAAAnRS"+
|
||||||
|
"TlPlAFWaypEAAABRSURBVBjTfc9HDoAwDERRQ+w0ern/WQkZaUBC4e/mrWzppH9VJjbjZg1Ii2rM"+
|
||||||
|
"DyR1JZ8J0dVWggIGggcEwgbYCRbuPRqgyjHNpzUP+39GPu9fgloC5L9DO0sAAAAASUVORK5CYII="
|
||||||
|
);
|
||||||
|
},
|
||||||
|
function(text) {
|
||||||
|
updateDivContent("status", "disconnected ○");
|
||||||
|
/* Update favicon */
|
||||||
|
changeFavicon("data:image/png;base64," +
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAYFBMVEUAAAAAAAAAAADZ2dm4uLgM"+
|
||||||
|
"DAz29vbz8/Pv7+/h4eHIyMiwsLBtbW0lJSUeHh4QEBDn5+fS0tLDw8O0tLSioqKfn5+QkJCHh4d+"+
|
||||||
|
"fn5ycnJmZmZgYGBXV1dLS0tFRUUGBgZ0He44AAAAAnRSTlPlAFWaypEAAABeSURBVBjTfY9HDoAw"+
|
||||||
|
"DAQD6Z3ey/9/iXMxkVDYw0g7F3tJReosUKHnwY4pCM+EtOEVXrb7wVRA0dMbaAcUwiVeDQq1Jp4a"+
|
||||||
|
"xUg5kE0ooqZu68Di2Tgbs/DiY/9jyGf+AyFKBAK7KD2TAAAAAElFTkSuQmCC"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setInterval(refreshOutput,
|
||||||
|
<?lua echo(lovebird.updateinterval) ?> * 1000);
|
||||||
|
|
||||||
|
/* Environment variable view */
|
||||||
|
var envPath = "";
|
||||||
|
var refreshEnv = function() {
|
||||||
|
geturl("/env.json?p=" + envPath, function(text) {
|
||||||
|
var json = eval("(" + text + ")");
|
||||||
|
|
||||||
|
/* Header */
|
||||||
|
var html = "<a href='#' onclick=\"setEnvPath('')\">env</a>";
|
||||||
|
var acc = "";
|
||||||
|
var p = json.path != "" ? json.path.split(".") : [];
|
||||||
|
for (var i = 0; i < p.length; i++) {
|
||||||
|
acc += "." + p[i];
|
||||||
|
html += " <a href='#' onclick=\"setEnvPath('" + acc + "')\">" +
|
||||||
|
truncate(p[i], 10) + "</a>";
|
||||||
|
}
|
||||||
|
updateDivContent("envheader", html);
|
||||||
|
|
||||||
|
/* Handle invalid table path */
|
||||||
|
if (!json.valid) {
|
||||||
|
updateDivContent("envvars", "Bad path");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Variables */
|
||||||
|
var html = "<table>";
|
||||||
|
for (var i = 0; json.vars[i]; i++) {
|
||||||
|
var x = json.vars[i];
|
||||||
|
var fullpath = (json.path + "." + x.key).replace(/^\./, "");
|
||||||
|
var k = truncate(x.key, 15);
|
||||||
|
if (x.type == "table") {
|
||||||
|
k = "<a href='#' onclick=\"setEnvPath('" + fullpath + "')\">" +
|
||||||
|
k + "</a>";
|
||||||
|
}
|
||||||
|
var v = "<a href='#' onclick=\"insertVar('" +
|
||||||
|
fullpath.replace(/\.(-?[0-9]+)/g, "[$1]") +
|
||||||
|
"');\">" + x.value + "</a>"
|
||||||
|
html += "<tr><td>" + k + "</td><td>" + v + "</td></tr>";
|
||||||
|
}
|
||||||
|
html += "</table>";
|
||||||
|
updateDivContent("envvars", html);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var setEnvPath = function(p) {
|
||||||
|
envPath = p;
|
||||||
|
refreshEnv();
|
||||||
|
}
|
||||||
|
var insertVar = function(p) {
|
||||||
|
var b = document.getElementById("inputbox");
|
||||||
|
b.value += p;
|
||||||
|
b.focus();
|
||||||
|
}
|
||||||
|
setInterval(refreshEnv, <?lua echo(lovebird.updateinterval) ?> * 1000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
]]
|
||||||
|
|
||||||
|
|
||||||
|
lovebird.pages["buffer"] = [[ <?lua echo(lovebird.buffer) ?> ]]
|
||||||
|
|
||||||
|
|
||||||
|
lovebird.pages["env.json"] = [[
|
||||||
|
<?lua
|
||||||
|
local t = _G
|
||||||
|
local p = req.parsedurl.query.p or ""
|
||||||
|
p = p:gsub("%.+", "."):match("^[%.]*(.*)[%.]*$")
|
||||||
|
if p ~= "" then
|
||||||
|
for x in p:gmatch("[^%.]+") do
|
||||||
|
t = t[x] or t[tonumber(x)]
|
||||||
|
-- Return early if path does not exist
|
||||||
|
if type(t) ~= "table" then
|
||||||
|
echo('{ "valid": false, "path": ' .. string.format("%q", p) .. ' }')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
?>
|
||||||
|
{
|
||||||
|
"valid": true,
|
||||||
|
"path": "<?lua echo(p) ?>",
|
||||||
|
"vars": [
|
||||||
|
<?lua
|
||||||
|
local keys = {}
|
||||||
|
for k in pairs(t) do
|
||||||
|
if type(k) == "number" or type(k) == "string" then
|
||||||
|
table.insert(keys, k)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.sort(keys, lovebird.compare)
|
||||||
|
for _, k in pairs(keys) do
|
||||||
|
local v = t[k]
|
||||||
|
?>
|
||||||
|
{
|
||||||
|
"key": "<?lua echo(k) ?>",
|
||||||
|
"value": <?lua echo(
|
||||||
|
string.format("%q",
|
||||||
|
lovebird.truncate(
|
||||||
|
lovebird.htmlescape(
|
||||||
|
tostring(v)), 26))) ?>,
|
||||||
|
"type": "<?lua echo(type(v)) ?>",
|
||||||
|
},
|
||||||
|
<?lua end ?>
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.init()
|
||||||
|
-- Init server
|
||||||
|
lovebird.server = assert(socket.bind(lovebird.host, lovebird.port))
|
||||||
|
lovebird.addr, lovebird.port = lovebird.server:getsockname()
|
||||||
|
lovebird.server:settimeout(0)
|
||||||
|
-- Wrap print
|
||||||
|
lovebird.origprint = print
|
||||||
|
if lovebird.wrapprint then
|
||||||
|
local oldprint = print
|
||||||
|
print = function(...)
|
||||||
|
oldprint(...)
|
||||||
|
lovebird.print(...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Compile page templates
|
||||||
|
for k, page in pairs(lovebird.pages) do
|
||||||
|
lovebird.pages[k] = lovebird.template(page, "lovebird, req",
|
||||||
|
"pages." .. k)
|
||||||
|
end
|
||||||
|
lovebird.inited = true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.template(str, params, chunkname)
|
||||||
|
params = params and ("," .. params) or ""
|
||||||
|
local f = function(x) return string.format(" echo(%q)", x) end
|
||||||
|
str = ("?>"..str.."<?lua"):gsub("%?>(.-)<%?lua", f)
|
||||||
|
str = "local echo " .. params .. " = ..." .. str
|
||||||
|
local fn = assert(lovebird.loadstring(str, chunkname))
|
||||||
|
return function(...)
|
||||||
|
local output = {}
|
||||||
|
local echo = function(str) table.insert(output, str) end
|
||||||
|
fn(echo, ...)
|
||||||
|
return table.concat(lovebird.map(output, tostring))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.map(t, fn)
|
||||||
|
local res = {}
|
||||||
|
for k, v in pairs(t) do res[k] = fn(v) end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.trace(...)
|
||||||
|
local str = "[lovebird] " .. table.concat(lovebird.map({...}, tostring), " ")
|
||||||
|
print(str)
|
||||||
|
if not lovebird.wrapprint then lovebird.print(str) end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.unescape(str)
|
||||||
|
local f = function(x) return string.char(tonumber("0x"..x)) end
|
||||||
|
return (str:gsub("%+", " "):gsub("%%(..)", f))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.parseurl(url)
|
||||||
|
local res = {}
|
||||||
|
res.path, res.search = url:match("/([^%?]*)%??(.*)")
|
||||||
|
res.query = {}
|
||||||
|
for k, v in res.search:gmatch("([^&^?]-)=([^&^#]*)") do
|
||||||
|
res.query[k] = lovebird.unescape(v)
|
||||||
|
end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.htmlescape(str)
|
||||||
|
return str:gsub("<", "<")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.truncate(str, len)
|
||||||
|
if #str <= len then
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
return str:sub(1, len - 3) .. "..."
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.compare(a, b)
|
||||||
|
local na, nb = tonumber(a), tonumber(b)
|
||||||
|
if na then
|
||||||
|
if nb then return na < nb end
|
||||||
|
return false
|
||||||
|
elseif nb then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return tostring(a) < tostring(b)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.checkwhitelist(addr)
|
||||||
|
if lovebird.whitelist == nil then return true end
|
||||||
|
for _, a in pairs(lovebird.whitelist) do
|
||||||
|
local ptn = "^" .. a:gsub("%.", "%%."):gsub("%*", "%%d*") .. "$"
|
||||||
|
if addr:match(ptn) then return true end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.clear()
|
||||||
|
lovebird.lines = {}
|
||||||
|
lovebird.buffer = ""
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.pushline(line)
|
||||||
|
line.time = os.time()
|
||||||
|
line.count = 1
|
||||||
|
table.insert(lovebird.lines, line)
|
||||||
|
if #lovebird.lines > lovebird.maxlines then
|
||||||
|
table.remove(lovebird.lines, 1)
|
||||||
|
end
|
||||||
|
lovebird.recalcbuffer()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.recalcbuffer()
|
||||||
|
local function doline(line)
|
||||||
|
local str = line.str
|
||||||
|
if not lovebird.allowhtml then
|
||||||
|
str = lovebird.htmlescape(line.str):gsub("\n", "<br>")
|
||||||
|
end
|
||||||
|
if line.type == "input" then
|
||||||
|
str = '<span class="inputline">' .. str .. '</span>'
|
||||||
|
else
|
||||||
|
if line.type == "error" then
|
||||||
|
str = '<span class="errormarker">!</span> ' .. str
|
||||||
|
str = '<span class="errorline">' .. str .. '</span>'
|
||||||
|
end
|
||||||
|
if line.count > 1 then
|
||||||
|
str = '<span class="repeatcount">' .. line.count .. '</span> ' .. str
|
||||||
|
end
|
||||||
|
if lovebird.timestamp then
|
||||||
|
str = os.date('<span class="timestamp">%H:%M:%S</span> ', line.time) ..
|
||||||
|
str
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
lovebird.buffer = table.concat(lovebird.map(lovebird.lines, doline), "<br>")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.print(...)
|
||||||
|
local t = {}
|
||||||
|
for i = 1, select("#", ...) do
|
||||||
|
table.insert(t, tostring(select(i, ...)))
|
||||||
|
end
|
||||||
|
local str = table.concat(t, " ")
|
||||||
|
local last = lovebird.lines[#lovebird.lines]
|
||||||
|
if last and str == last.str then
|
||||||
|
-- Update last line if this line is a duplicate of it
|
||||||
|
last.time = os.time()
|
||||||
|
last.count = last.count + 1
|
||||||
|
lovebird.recalcbuffer()
|
||||||
|
else
|
||||||
|
-- Create new line
|
||||||
|
lovebird.pushline({ type = "output", str = str })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.onerror(err)
|
||||||
|
lovebird.pushline({ type = "error", str = err })
|
||||||
|
if lovebird.wrapprint then
|
||||||
|
lovebird.origprint("[lovebird] ERROR: " .. err)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.onrequest(req, client)
|
||||||
|
local page = req.parsedurl.path
|
||||||
|
page = page ~= "" and page or "index"
|
||||||
|
-- Handle "page not found"
|
||||||
|
if not lovebird.pages[page] then
|
||||||
|
return "HTTP/1.1 404\r\nContent-Length: 8\r\n\r\nBad page"
|
||||||
|
end
|
||||||
|
-- Handle page
|
||||||
|
local str
|
||||||
|
xpcall(function()
|
||||||
|
local data = lovebird.pages[page](lovebird, req)
|
||||||
|
str = "HTTP/1.1 200 OK\r\n" ..
|
||||||
|
"Content-Length: " .. #data .. "\r\n" ..
|
||||||
|
"\r\n" .. data
|
||||||
|
end, lovebird.onerror)
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.receive(client, pattern)
|
||||||
|
while 1 do
|
||||||
|
local data, msg = client:receive(pattern)
|
||||||
|
if not data then
|
||||||
|
if msg == "timeout" then
|
||||||
|
-- Wait for more data
|
||||||
|
coroutine.yield(true)
|
||||||
|
else
|
||||||
|
-- Disconnected -- yielding nil means we're done
|
||||||
|
coroutine.yield(nil)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.send(client, data)
|
||||||
|
local idx = 1
|
||||||
|
while idx < #data do
|
||||||
|
local res, msg = client:send(data, idx)
|
||||||
|
if not res and msg == "closed" then
|
||||||
|
-- Handle disconnect
|
||||||
|
coroutine.yield(nil)
|
||||||
|
else
|
||||||
|
idx = idx + res
|
||||||
|
coroutine.yield(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.onconnect(client)
|
||||||
|
-- Create request table
|
||||||
|
local requestptn = "(%S*)%s*(%S*)%s*(%S*)"
|
||||||
|
local req = {}
|
||||||
|
req.socket = client
|
||||||
|
req.addr, req.port = client:getsockname()
|
||||||
|
req.request = lovebird.receive(client, "*l")
|
||||||
|
req.method, req.url, req.proto = req.request:match(requestptn)
|
||||||
|
req.headers = {}
|
||||||
|
while 1 do
|
||||||
|
local line, msg = lovebird.receive(client, "*l")
|
||||||
|
if not line or #line == 0 then break end
|
||||||
|
local k, v = line:match("(.-):%s*(.*)$")
|
||||||
|
req.headers[k] = v
|
||||||
|
end
|
||||||
|
if req.headers["Content-Length"] then
|
||||||
|
req.body = lovebird.receive(client, req.headers["Content-Length"])
|
||||||
|
end
|
||||||
|
-- Parse body
|
||||||
|
req.parsedbody = {}
|
||||||
|
if req.body then
|
||||||
|
for k, v in req.body:gmatch("([^&]-)=([^&^#]*)") do
|
||||||
|
req.parsedbody[k] = lovebird.unescape(v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Parse request line's url
|
||||||
|
req.parsedurl = lovebird.parseurl(req.url)
|
||||||
|
-- Handle request; get data to send and send
|
||||||
|
local data = lovebird.onrequest(req)
|
||||||
|
lovebird.send(client, data)
|
||||||
|
-- Clear up
|
||||||
|
client:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function lovebird.update()
|
||||||
|
if not lovebird.inited then lovebird.init() end
|
||||||
|
-- Handle new connections
|
||||||
|
while 1 do
|
||||||
|
-- Accept new connections
|
||||||
|
local client = lovebird.server:accept()
|
||||||
|
if not client then break end
|
||||||
|
client:settimeout(0)
|
||||||
|
local addr = client:getsockname()
|
||||||
|
if lovebird.checkwhitelist(addr) then
|
||||||
|
-- Connection okay -- create and add coroutine to set
|
||||||
|
local conn = coroutine.wrap(function()
|
||||||
|
xpcall(function() lovebird.onconnect(client) end, function() end)
|
||||||
|
end)
|
||||||
|
lovebird.connections[conn] = true
|
||||||
|
else
|
||||||
|
-- Reject connection not on whitelist
|
||||||
|
lovebird.trace("got non-whitelisted connection attempt: ", addr)
|
||||||
|
client:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Handle existing connections
|
||||||
|
for conn in pairs(lovebird.connections) do
|
||||||
|
-- Resume coroutine, remove if it has finished
|
||||||
|
local status = conn()
|
||||||
|
if status == nil then
|
||||||
|
lovebird.connections[conn] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return lovebird
|
||||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 200 KiB |
|
After Width: | Height: | Size: 189 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 606 KiB After Width: | Height: | Size: 656 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 175 KiB |
|
After Width: | Height: | Size: 177 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 304 B |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 219 KiB |
|
After Width: | Height: | Size: 221 KiB |
|
After Width: | Height: | Size: 214 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 160 KiB |
@@ -0,0 +1,36 @@
|
|||||||
|
-- Configuration
|
||||||
|
function love.conf(t)
|
||||||
|
t.identity = nil -- The name of the save directory (string)
|
||||||
|
t.version = "0.10.0" -- The LÖVE version this game was made for (string)
|
||||||
|
t.console = true -- Attach a console (boolean, Windows only)
|
||||||
|
|
||||||
|
t.title = "Shall I Kill Her" -- The title of the window the game is in (string)
|
||||||
|
t.window.icon = nil -- Filepath to an image to use as the window's icon (string)
|
||||||
|
t.window.width = 1280 -- The window width (number)
|
||||||
|
t.window.height = 720 -- The window height (number)
|
||||||
|
t.window.borderless = false -- Remove all border visuals from the window (boolean)
|
||||||
|
t.window.resizable = false -- Let the window be user-resizable (boolean)
|
||||||
|
t.window.minwidth = 1 -- Minimum window width if the window is resizable (number)
|
||||||
|
t.window.minheight = 1 -- Minimum window height if the window is resizable (number)
|
||||||
|
t.window.fullscreen = false -- Enable fullscreen (boolean)
|
||||||
|
t.window.fullscreentype = "desktop" -- Standard fullscreen or desktop fullscreen mode (string)
|
||||||
|
t.window.vsync = false -- Enable vertical sync (boolean)
|
||||||
|
t.window.fsaa = 0 -- The number of samples to use with multi-sampled antialiasing (number)
|
||||||
|
t.window.display = 1 -- Index of the monitor to show the window in (number)
|
||||||
|
t.window.highdpi = false -- Enable high-dpi mode for the window on a Retina display (boolean). Added in 0.9.1
|
||||||
|
t.window.srgb = false -- Enable sRGB gamma correction when drawing to the screen (boolean). Added in 0.9.1
|
||||||
|
|
||||||
|
t.modules.audio = true -- Enable the audio module (boolean)
|
||||||
|
t.modules.event = true -- Enable the event module (boolean)
|
||||||
|
t.modules.graphics = true -- Enable the graphics module (boolean)
|
||||||
|
t.modules.image = true -- Enable the image module (boolean)
|
||||||
|
t.modules.joystick = true -- Enable the joystick module (boolean)
|
||||||
|
t.modules.keyboard = true -- Enable the keyboard module (boolean)
|
||||||
|
t.modules.math = true -- Enable the math module (boolean)
|
||||||
|
t.modules.mouse = true -- Enable the mouse module (boolean)
|
||||||
|
t.modules.physics = true -- Enable the physics module (boolean)
|
||||||
|
t.modules.sound = true -- Enable the sound module (boolean)
|
||||||
|
t.modules.system = true -- Enable the system module (boolean)
|
||||||
|
t.modules.timer = true -- Enable the timer module (boolean)
|
||||||
|
t.modules.window = true -- Enable the window module (boolean)
|
||||||
|
end
|
||||||
@@ -0,0 +1,288 @@
|
|||||||
|
local anim8 = {
|
||||||
|
_VERSION = 'anim8 v2.1.0',
|
||||||
|
_DESCRIPTION = 'An animation library for LÖVE',
|
||||||
|
_URL = 'https://github.com/kikito/anim8',
|
||||||
|
_LICENSE = [[
|
||||||
|
MIT LICENSE
|
||||||
|
|
||||||
|
Copyright (c) 2011 Enrique García Cota
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
}
|
||||||
|
|
||||||
|
local Grid = {}
|
||||||
|
|
||||||
|
local _frames = {}
|
||||||
|
|
||||||
|
local function assertPositiveInteger(value, name)
|
||||||
|
if type(value) ~= 'number' then error(("%s should be a number, was %q"):format(name, tostring(value))) end
|
||||||
|
if value < 1 then error(("%s should be a positive number, was %d"):format(name, value)) end
|
||||||
|
if value ~= math.floor(value) then error(("%s should be an integer, was %d"):format(name, value)) end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function createFrame(self, x, y)
|
||||||
|
local fw, fh = self.frameWidth, self.frameHeight
|
||||||
|
return love.graphics.newQuad(
|
||||||
|
self.left + (x-1) * fw + x * self.border,
|
||||||
|
self.top + (y-1) * fh + y * self.border,
|
||||||
|
fw,
|
||||||
|
fh,
|
||||||
|
self.imageWidth,
|
||||||
|
self.imageHeight
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getGridKey(...)
|
||||||
|
return table.concat( {...} ,'-' )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getOrCreateFrame(self, x, y)
|
||||||
|
if x < 1 or x > self.width or y < 1 or y > self.height then
|
||||||
|
error(("There is no frame for x=%d, y=%d"):format(x, y))
|
||||||
|
end
|
||||||
|
local key = self._key
|
||||||
|
_frames[key] = _frames[key] or {}
|
||||||
|
_frames[key][x] = _frames[key][x] or {}
|
||||||
|
_frames[key][x][y] = _frames[key][x][y] or createFrame(self, x, y)
|
||||||
|
return _frames[key][x][y]
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parseInterval(str)
|
||||||
|
if type(str) == "number" then return str,str,1 end
|
||||||
|
str = str:gsub('%s', '') -- remove spaces
|
||||||
|
local min, max = str:match("^(%d+)-(%d+)$")
|
||||||
|
assert(min and max, ("Could not parse interval from %q"):format(str))
|
||||||
|
min, max = tonumber(min), tonumber(max)
|
||||||
|
local step = min <= max and 1 or -1
|
||||||
|
return min, max, step
|
||||||
|
end
|
||||||
|
|
||||||
|
function Grid:getFrames(...)
|
||||||
|
local result, args = {}, {...}
|
||||||
|
local minx, maxx, stepx, miny, maxy, stepy
|
||||||
|
|
||||||
|
for i=1, #args, 2 do
|
||||||
|
minx, maxx, stepx = parseInterval(args[i])
|
||||||
|
miny, maxy, stepy = parseInterval(args[i+1])
|
||||||
|
for y = miny, maxy, stepy do
|
||||||
|
for x = minx, maxx, stepx do
|
||||||
|
result[#result+1] = getOrCreateFrame(self,x,y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
local Gridmt = {
|
||||||
|
__index = Grid,
|
||||||
|
__call = Grid.getFrames
|
||||||
|
}
|
||||||
|
|
||||||
|
local function newGrid(frameWidth, frameHeight, imageWidth, imageHeight, left, top, border)
|
||||||
|
assertPositiveInteger(frameWidth, "frameWidth")
|
||||||
|
assertPositiveInteger(frameHeight, "frameHeight")
|
||||||
|
assertPositiveInteger(imageWidth, "imageWidth")
|
||||||
|
assertPositiveInteger(imageHeight, "imageHeight")
|
||||||
|
|
||||||
|
left = left or 0
|
||||||
|
top = top or 0
|
||||||
|
border = border or 0
|
||||||
|
|
||||||
|
local key = getGridKey(frameWidth, frameHeight, imageWidth, imageHeight, left, top, border)
|
||||||
|
|
||||||
|
local grid = setmetatable(
|
||||||
|
{ frameWidth = frameWidth,
|
||||||
|
frameHeight = frameHeight,
|
||||||
|
imageWidth = imageWidth,
|
||||||
|
imageHeight = imageHeight,
|
||||||
|
left = left,
|
||||||
|
top = top,
|
||||||
|
border = border,
|
||||||
|
width = math.floor(imageWidth/frameWidth),
|
||||||
|
height = math.floor(imageHeight/frameHeight),
|
||||||
|
_key = key
|
||||||
|
},
|
||||||
|
Gridmt
|
||||||
|
)
|
||||||
|
return grid
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
local Animation = {}
|
||||||
|
|
||||||
|
local function cloneArray(arr)
|
||||||
|
local result = {}
|
||||||
|
for i=1,#arr do result[i] = arr[i] end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parseDurations(durations, frameCount)
|
||||||
|
local result = {}
|
||||||
|
if type(durations) == 'number' then
|
||||||
|
for i=1,frameCount do result[i] = durations end
|
||||||
|
else
|
||||||
|
local min, max, step
|
||||||
|
for key,duration in pairs(durations) do
|
||||||
|
assert(type(duration) == 'number', "The value [" .. tostring(duration) .. "] should be a number")
|
||||||
|
min, max, step = parseInterval(key)
|
||||||
|
for i = min,max,step do result[i] = duration end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #result < frameCount then
|
||||||
|
error("The durations table has length of " .. tostring(#result) .. ", but it should be >= " .. tostring(frameCount))
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
local function parseIntervals(durations)
|
||||||
|
local result, time = {0},0
|
||||||
|
for i=1,#durations do
|
||||||
|
time = time + durations[i]
|
||||||
|
result[i+1] = time
|
||||||
|
end
|
||||||
|
return result, time
|
||||||
|
end
|
||||||
|
|
||||||
|
local Animationmt = { __index = Animation }
|
||||||
|
local nop = function() end
|
||||||
|
|
||||||
|
local function newAnimation(frames, durations, onLoop)
|
||||||
|
local td = type(durations);
|
||||||
|
if (td ~= 'number' or durations <= 0) and td ~= 'table' then
|
||||||
|
error("durations must be a positive number. Was " .. tostring(durations) )
|
||||||
|
end
|
||||||
|
onLoop = onLoop or nop
|
||||||
|
durations = parseDurations(durations, #frames)
|
||||||
|
local intervals, totalDuration = parseIntervals(durations)
|
||||||
|
return setmetatable({
|
||||||
|
frames = cloneArray(frames),
|
||||||
|
durations = durations,
|
||||||
|
intervals = intervals,
|
||||||
|
totalDuration = totalDuration,
|
||||||
|
onLoop = onLoop,
|
||||||
|
timer = 0,
|
||||||
|
position = 1,
|
||||||
|
status = "playing",
|
||||||
|
flippedH = false,
|
||||||
|
flippedV = false
|
||||||
|
},
|
||||||
|
Animationmt
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Animation:clone()
|
||||||
|
local newAnim = newAnimation(self.frames, self.durations, self.onLoop)
|
||||||
|
newAnim.flippedH, newAnim.flippedV = self.flippedH, self.flippedV
|
||||||
|
return newAnim
|
||||||
|
end
|
||||||
|
|
||||||
|
function Animation:flipH()
|
||||||
|
self.flippedH = not self.flippedH
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Animation:flipV()
|
||||||
|
self.flippedV = not self.flippedV
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
local function seekFrameIndex(intervals, timer)
|
||||||
|
local high, low, i = #intervals-1, 1, 1
|
||||||
|
|
||||||
|
while(low <= high) do
|
||||||
|
i = math.floor((low + high) / 2)
|
||||||
|
if timer > intervals[i+1] then low = i + 1
|
||||||
|
elseif timer <= intervals[i] then high = i - 1
|
||||||
|
else
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
|
||||||
|
function Animation:update(dt)
|
||||||
|
if self.status ~= "playing" then return end
|
||||||
|
|
||||||
|
self.timer = self.timer + dt
|
||||||
|
local loops = math.floor(self.timer / self.totalDuration)
|
||||||
|
if loops ~= 0 then
|
||||||
|
self.timer = self.timer - self.totalDuration * loops
|
||||||
|
local f = type(self.onLoop) == 'function' and self.onLoop or self[self.onLoop]
|
||||||
|
f(self, loops)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.position = seekFrameIndex(self.intervals, self.timer)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Animation:pause()
|
||||||
|
self.status = "paused"
|
||||||
|
end
|
||||||
|
|
||||||
|
function Animation:gotoFrame(position)
|
||||||
|
self.position = position
|
||||||
|
self.timer = self.intervals[self.position]
|
||||||
|
end
|
||||||
|
|
||||||
|
function Animation:pauseAtEnd()
|
||||||
|
self.position = #self.frames
|
||||||
|
self.timer = self.totalDuration
|
||||||
|
self:pause()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Animation:pauseAtStart()
|
||||||
|
self.position = 1
|
||||||
|
self.timer = 0
|
||||||
|
self:pause()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Animation:resume()
|
||||||
|
self.status = "playing"
|
||||||
|
end
|
||||||
|
|
||||||
|
function Animation:draw(image, x, y, r, sx, sy, ox, oy, ...)
|
||||||
|
local frame = self.frames[self.position]
|
||||||
|
if self.flippedH or self.flippedV then
|
||||||
|
r,sx,sy,ox,oy = r or 0, sx or 1, sy or 1, ox or 0, oy or 0
|
||||||
|
local _,_,w,h = frame:getViewport()
|
||||||
|
|
||||||
|
if self.flippedH then
|
||||||
|
sx = sx * -1
|
||||||
|
ox = w - ox
|
||||||
|
end
|
||||||
|
if self.flippedV then
|
||||||
|
sy = sy * -1
|
||||||
|
oy = h - oy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
love.graphics.draw(image, frame, x, y, r, sx, sy, ox, oy, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
anim8.newGrid = newGrid
|
||||||
|
anim8.newAnimation = newAnimation
|
||||||
|
|
||||||
|
return anim8
|
||||||
@@ -0,0 +1,781 @@
|
|||||||
|
local _PACKAGE = (...):match("^(.+)[%./][^%./]+") or ""
|
||||||
|
local normal_map = require(_PACKAGE..'/normal_map')
|
||||||
|
local util = require(_PACKAGE..'/util')
|
||||||
|
local anim8 = require(_PACKAGE..'/anim8')
|
||||||
|
local vector = require(_PACKAGE..'/vector')
|
||||||
|
|
||||||
|
local body = {}
|
||||||
|
body.__index = body
|
||||||
|
|
||||||
|
body.glowShader = love.graphics.newShader(_PACKAGE.."/shaders/glow.glsl")
|
||||||
|
body.materialShader = love.graphics.newShader(_PACKAGE.."/shaders/material.glsl")
|
||||||
|
|
||||||
|
local function new(id, type, ...)
|
||||||
|
local args = {...}
|
||||||
|
local obj = setmetatable({}, body)
|
||||||
|
obj.id = id
|
||||||
|
obj.type = type
|
||||||
|
obj.shine = true
|
||||||
|
obj.red = 1.0
|
||||||
|
obj.green = 1.0
|
||||||
|
obj.blue = 1.0
|
||||||
|
obj.alpha = 1.0
|
||||||
|
obj.glowRed = 255
|
||||||
|
obj.glowGreen = 255
|
||||||
|
obj.glowBlue = 255
|
||||||
|
obj.glowStrength = 0.0
|
||||||
|
obj.tileX = 0
|
||||||
|
obj.tileY = 0
|
||||||
|
obj.zheight = 1
|
||||||
|
|
||||||
|
obj.rotation = 0
|
||||||
|
obj.scalex = 1
|
||||||
|
obj.scaley = 1
|
||||||
|
|
||||||
|
obj.castsNoShadow = false
|
||||||
|
obj.visible = true
|
||||||
|
obj.is_on_screen = true
|
||||||
|
|
||||||
|
if obj.type == "circle" then
|
||||||
|
obj.x = args[1] or 0
|
||||||
|
obj.y = args[2] or 0
|
||||||
|
|
||||||
|
circle_canvas = love.graphics.newCanvas(args[3]*2, args[3]*2)
|
||||||
|
util.drawto(circle_canvas, 0, 0, 1, function()
|
||||||
|
love.graphics.circle('fill', args[3], args[3], args[3])
|
||||||
|
end)
|
||||||
|
obj.img = love.graphics.newImage(circle_canvas:newImageData())
|
||||||
|
obj.imgWidth = obj.img:getWidth()
|
||||||
|
obj.imgHeight = obj.img:getHeight()
|
||||||
|
obj.ix = obj.imgWidth * 0.5
|
||||||
|
obj.iy = obj.imgHeight * 0.5
|
||||||
|
obj:generateNormalMapFlat("top")
|
||||||
|
|
||||||
|
obj:setShadowType('circle', args[3], args[4], args[5])
|
||||||
|
elseif obj.type == "rectangle" then
|
||||||
|
local x = args[1] or 0
|
||||||
|
local y = args[2] or 0
|
||||||
|
local width = args[3] or 64
|
||||||
|
local height = args[4] or 64
|
||||||
|
local ox = args[5] or width * 0.5
|
||||||
|
local oy = args[6] or height * 0.5
|
||||||
|
|
||||||
|
obj:setPoints(
|
||||||
|
x - ox, y - oy,
|
||||||
|
x - ox + width, y - oy,
|
||||||
|
x - ox + width, y - oy + height,
|
||||||
|
x - ox, y - oy + height
|
||||||
|
)
|
||||||
|
elseif obj.type == "polygon" then
|
||||||
|
obj:setPoints(...)
|
||||||
|
elseif obj.type == "image" then
|
||||||
|
obj.img = args[1]
|
||||||
|
obj.x = args[2] or 0
|
||||||
|
obj.y = args[3] or 0
|
||||||
|
if obj.img then
|
||||||
|
obj.imgWidth = obj.img:getWidth()
|
||||||
|
obj.imgHeight = obj.img:getHeight()
|
||||||
|
obj.ix = obj.imgWidth * 0.5
|
||||||
|
obj.iy = obj.imgHeight * 0.5
|
||||||
|
end
|
||||||
|
obj:generateNormalMapFlat("top")
|
||||||
|
obj:setShadowType('rectangle', args[4] or obj.imgWidth, args[5] or obj.imgHeight, args[6], args[7])
|
||||||
|
obj.reflective = true
|
||||||
|
elseif obj.type == "animation" then
|
||||||
|
obj.img = args[1]
|
||||||
|
obj.x = args[2] or 0
|
||||||
|
obj.y = args[3] or 0
|
||||||
|
obj.animations = {}
|
||||||
|
obj.castsNoShadow = true
|
||||||
|
obj:generateNormalMapFlat("top")
|
||||||
|
obj.reflective = true
|
||||||
|
elseif obj.type == "refraction" then
|
||||||
|
obj.x = args[2] or 0
|
||||||
|
obj.y = args[3] or 0
|
||||||
|
obj:setNormalMap(args[1], args[4], args[5])
|
||||||
|
obj.width = args[4] or obj.normalWidth
|
||||||
|
obj.height = args[5] or obj.normalHeight
|
||||||
|
obj.ox = obj.width * 0.5
|
||||||
|
obj.oy = obj.height * 0.5
|
||||||
|
obj.refraction = true
|
||||||
|
elseif obj.type == "reflection" then
|
||||||
|
obj.x = args[2] or 0
|
||||||
|
obj.y = args[3] or 0
|
||||||
|
obj:setNormalMap(args[1], args[4], args[5])
|
||||||
|
obj.width = args[4] or obj.normalWidth
|
||||||
|
obj.height = args[5] or obj.normalHeight
|
||||||
|
obj.ox = obj.width * 0.5
|
||||||
|
obj.oy = obj.height * 0.5
|
||||||
|
obj.reflection = true
|
||||||
|
end
|
||||||
|
|
||||||
|
obj:commit_changes()
|
||||||
|
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
|
||||||
|
-- refresh
|
||||||
|
function body:refresh()
|
||||||
|
if self.shadowType == 'polygon' and self:has_changed() then
|
||||||
|
self.data = {unpack(self.unit_data)}
|
||||||
|
local center = vector(self.x, self.y)
|
||||||
|
for i = 1, #self.data, 2 do
|
||||||
|
local point = vector(self.data[i], self.data[i+1])
|
||||||
|
point = point:rotate(self.rotation)
|
||||||
|
point = point:scale(self.scalex, self.scaley)
|
||||||
|
self.data[i], self.data[i+1] = (point + center):unpack()
|
||||||
|
end
|
||||||
|
self:commit_changes()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:has_changed()
|
||||||
|
return self:position_changed() or
|
||||||
|
self:rotation_changed() or
|
||||||
|
self:scale_changed()
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:position_changed()
|
||||||
|
return self.old_x ~= self.x or
|
||||||
|
self.old_y ~= self.y
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:rotation_changed()
|
||||||
|
return self.old_rotation ~= self.rotation
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:scale_changed()
|
||||||
|
return self.old_scalex ~= self.scalex or
|
||||||
|
self.old_scaley ~= self.scaley
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:commit_changes()
|
||||||
|
self.old_x, self.old_y = self.x, self.y
|
||||||
|
self.old_rotation = self.rotation
|
||||||
|
self.old_scalex, self.old_scaley = self.scalex, self.scaley
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:newGrid(frameWidth, frameHeight, imageWidth, imageHeight, left, top, border)
|
||||||
|
return anim8.newGrid(
|
||||||
|
frameWidth, frameHeight,
|
||||||
|
imageWidth or self.img:getWidth(), imageHeight or self.img:getHeight(),
|
||||||
|
left, top, border
|
||||||
|
)
|
||||||
|
end
|
||||||
|
-- frameWidth, frameHeight, imageWidth, imageHeight, left, top, border
|
||||||
|
function body:addAnimation(name, frames, durations, onLoop)
|
||||||
|
self.animations[name] = anim8.newAnimation(frames, durations, onLoop)
|
||||||
|
|
||||||
|
if not self.current_animation_name then
|
||||||
|
self:setAnimation(name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:setAnimation(name)
|
||||||
|
self.current_animation_name = name
|
||||||
|
self.animation = self.animations[self.current_animation_name]
|
||||||
|
|
||||||
|
local frame = self.animation.frames[self.animation.position]
|
||||||
|
_,_,self.width, self.height = frame:getViewport()
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:gotoFrame(frame) self.animation:gotoFrame(frame) end
|
||||||
|
function body:pause() self.animation:pause() end
|
||||||
|
function body:resume() self.animation:resume() end
|
||||||
|
function body:flipH() self.animation:flipH() end
|
||||||
|
function body:flipV() self.animation:flipV() end
|
||||||
|
function body:pauseAtEnd() self.animation:pauseAtEnd() end
|
||||||
|
function body:pauseAtStart() self.animation:pauseAtStart() end
|
||||||
|
|
||||||
|
function body:update(dt)
|
||||||
|
self:refresh()
|
||||||
|
if self.type == "animation" and self.animation then
|
||||||
|
local frame = self.animation.frames[self.animation.position]
|
||||||
|
_,_,self.width, self.height = frame:getViewport()
|
||||||
|
self.imgWidth, self.imgHeight = self.width, self.height
|
||||||
|
self.normalWidth, self.normalHeight = self.width, self.height
|
||||||
|
self.ix, self.iy = self.imgWidth * 0.5,self.imgHeight * 0.5
|
||||||
|
self.nx, self.ny = self.ix, self.iy
|
||||||
|
self.animation:update(dt)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:rotate(angle)
|
||||||
|
self:setRotation(self.rotation + angle)
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:setRotation(angle)
|
||||||
|
self.rotation = angle
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:scale(sx, sy)
|
||||||
|
self.scalex = self.scalex + sx
|
||||||
|
self.scaley = self.scaley + (sy or sx)
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:setScale(sx, sy)
|
||||||
|
self.scalex = sx
|
||||||
|
self.scaley = sy or sx
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set position
|
||||||
|
function body:setPosition(x, y)
|
||||||
|
if x ~= self.x or y ~= self.y then
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- move position
|
||||||
|
function body:move(x, y)
|
||||||
|
if x then
|
||||||
|
self.x = self.x + x
|
||||||
|
end
|
||||||
|
if y then
|
||||||
|
self.y = self.y + y
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get x position
|
||||||
|
function body:getPosition()
|
||||||
|
return self.x, self.y
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get width
|
||||||
|
function body:getWidth()
|
||||||
|
return self.width
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get height
|
||||||
|
function body:getHeight()
|
||||||
|
return self.height
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get image width
|
||||||
|
function body:getImageWidth()
|
||||||
|
return self.imgWidth
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get image height
|
||||||
|
function body:getImageHeight()
|
||||||
|
return self.imgHeight
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set offset
|
||||||
|
function body:setOffset(ox, oy)
|
||||||
|
if ox ~= self.ox or oy ~= self.oy then
|
||||||
|
self.ox = ox
|
||||||
|
self.oy = oy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set offset
|
||||||
|
function body:setImageOffset(ix, iy)
|
||||||
|
if ix ~= self.ix or iy ~= self.iy then
|
||||||
|
self.ix = ix
|
||||||
|
self.iy = iy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set offset
|
||||||
|
function body:setNormalOffset(nx, ny)
|
||||||
|
if nx ~= self.nx or ny ~= self.ny then
|
||||||
|
self.nx = nx
|
||||||
|
self.ny = ny
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set glow color
|
||||||
|
function body:setGlowColor(red, green, blue)
|
||||||
|
self.glowRed = red
|
||||||
|
self.glowGreen = green
|
||||||
|
self.glowBlue = blue
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set glow alpha
|
||||||
|
function body:setGlowStrength(strength)
|
||||||
|
self.glowStrength = strength
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:setVisible(visible)
|
||||||
|
self.visible = visible
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get radius
|
||||||
|
function body:getRadius()
|
||||||
|
return self.radius * self.scalex
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set radius
|
||||||
|
function body:setRadius(radius)
|
||||||
|
if radius ~= self.radius then
|
||||||
|
self.radius = radius
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set polygon data
|
||||||
|
function body:setPoints(...)
|
||||||
|
self.unit_data = {...}
|
||||||
|
|
||||||
|
--calculate l,r,t,b
|
||||||
|
self.x, self.y, self.width, self.height = self.unit_data[1], self.unit_data[2], 0, 0
|
||||||
|
for i = 1, #self.unit_data, 2 do
|
||||||
|
local px, py = self.unit_data[i], self.unit_data[i+1]
|
||||||
|
if px < self.x then self.x = px end
|
||||||
|
if py < self.y then self.y = py end
|
||||||
|
if px > self.width then self.width = px end
|
||||||
|
if py > self.height then self.height = py end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- normalize width and height
|
||||||
|
self.width = self.width - self.x
|
||||||
|
self.height = self.height - self.y
|
||||||
|
for i = 1, #self.unit_data, 2 do
|
||||||
|
self.unit_data[i], self.unit_data[i+1] = self.unit_data[i] - self.x, self.unit_data[i+1] - self.y
|
||||||
|
end
|
||||||
|
self.x = self.x + (self.width * 0.5)
|
||||||
|
self.y = self.y + (self.height * 0.5)
|
||||||
|
|
||||||
|
local poly_canvas = love.graphics.newCanvas(self.width, self.height)
|
||||||
|
util.drawto(poly_canvas, 0, 0, 1, function()
|
||||||
|
love.graphics.polygon('fill', self.unit_data)
|
||||||
|
end)
|
||||||
|
|
||||||
|
--normalize points to be around the center x y
|
||||||
|
for i = 1, #self.unit_data, 2 do
|
||||||
|
self.unit_data[i], self.unit_data[i+1] = self.unit_data[i] - self.width * 0.5, self.unit_data[i+1] - self.height * 0.5
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self.img then
|
||||||
|
self.img = love.graphics.newImage(poly_canvas:newImageData())
|
||||||
|
self.imgWidth = self.img:getWidth()
|
||||||
|
self.imgHeight = self.img:getHeight()
|
||||||
|
self.ix = self.imgWidth * 0.5
|
||||||
|
self.iy = self.imgHeight * 0.5
|
||||||
|
self:generateNormalMapFlat("top")
|
||||||
|
end
|
||||||
|
--wrapping with polygon normals causes edges to show
|
||||||
|
--also we do not need wrapping for this default normal map
|
||||||
|
self.normal:setWrap("clamp", "clamp")
|
||||||
|
self.shadowType = "polygon"
|
||||||
|
self:refresh()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get polygon data
|
||||||
|
function body:getPoints()
|
||||||
|
return unpack(self.data)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set shadow on/off
|
||||||
|
function body:setShadow(b)
|
||||||
|
self.castsNoShadow = not b
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set shine on/off
|
||||||
|
function body:setShine(b)
|
||||||
|
self.shine = b
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set glass color
|
||||||
|
function body:setColor(red, green, blue)
|
||||||
|
self.red = red
|
||||||
|
self.green = green
|
||||||
|
self.blue = blue
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set glass alpha
|
||||||
|
function body:setAlpha(alpha)
|
||||||
|
self.alpha = alpha
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set reflection on/off
|
||||||
|
function body:setReflection(reflection)
|
||||||
|
self.reflection = reflection
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set refraction on/off
|
||||||
|
function body:setRefraction(refraction)
|
||||||
|
self.refraction = refraction
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set reflective on other objects on/off
|
||||||
|
function body:setReflective(reflective)
|
||||||
|
self.reflective = reflective
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set refractive on other objects on/off
|
||||||
|
function body:setRefractive(refractive)
|
||||||
|
self.refractive = refractive
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set image
|
||||||
|
function body:setImage(img)
|
||||||
|
if img then
|
||||||
|
self.img = img
|
||||||
|
self.imgWidth = self.img:getWidth()
|
||||||
|
self.imgHeight = self.img:getHeight()
|
||||||
|
self.ix = self.imgWidth * 0.5
|
||||||
|
self.iy = self.imgHeight * 0.5
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set normal
|
||||||
|
function body:setNormalMap(normal, width, height, nx, ny)
|
||||||
|
if normal then
|
||||||
|
self.normal = normal
|
||||||
|
self.normal:setWrap("repeat", "repeat")
|
||||||
|
self.normalWidth = width or self.normal:getWidth()
|
||||||
|
self.normalHeight = height or self.normal:getHeight()
|
||||||
|
self.nx = nx or self.normalWidth * 0.5
|
||||||
|
self.ny = ny or self.normalHeight * 0.5
|
||||||
|
self.normalVert = {
|
||||||
|
{0.0, 0.0, 0.0, 0.0},
|
||||||
|
{self.normalWidth, 0.0, self.normalWidth / self.normal:getWidth(), 0.0},
|
||||||
|
{self.normalWidth, self.normalHeight, self.normalWidth / self.normal:getWidth(), self.normalHeight / self.normal:getHeight()},
|
||||||
|
{0.0, self.normalHeight, 0.0, self.normalHeight / self.normal:getHeight()}
|
||||||
|
}
|
||||||
|
self.normalMesh = love.graphics.newMesh(self.normalVert, "fan")
|
||||||
|
self.normalMesh:setTexture(self.normal)
|
||||||
|
else
|
||||||
|
self.normalMesh = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set height map
|
||||||
|
function body:setHeightMap(heightMap, strength)
|
||||||
|
self:setNormalMap(normal_map.fromHeightMap(heightMap, strength))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- generate flat normal map
|
||||||
|
function body:generateNormalMapFlat(mode)
|
||||||
|
self:setNormalMap(normal_map.generateFlat(self.img, mode))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- generate faded normal map
|
||||||
|
function body:generateNormalMapGradient(horizontalGradient, verticalGradient)
|
||||||
|
self:setNormalMap(normal_map.generateGradient(self.img, horizontalGradient, verticalGradient))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- generate normal map
|
||||||
|
function body:generateNormalMap(strength)
|
||||||
|
self:setNormalMap(normal_map.fromHeightMap(self.img, strength))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set material
|
||||||
|
function body:setMaterial(material)
|
||||||
|
if material then
|
||||||
|
self.material = material
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set normal
|
||||||
|
function body:setGlowMap(glow)
|
||||||
|
self.glow = glow
|
||||||
|
self.glowStrength = 1.0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set tile offset
|
||||||
|
function body:setNormalTileOffset(tx, ty)
|
||||||
|
self.tileX = tx / self.normalWidth
|
||||||
|
self.tileY = ty / self.normalHeight
|
||||||
|
self.normalVert = {
|
||||||
|
{0.0, 0.0, self.tileX, self.tileY},
|
||||||
|
{self.normalWidth, 0.0, self.tileX + 1.0, self.tileY},
|
||||||
|
{self.normalWidth, self.normalHeight, self.tileX + 1.0, self.tileY + 1.0},
|
||||||
|
{0.0, self.normalHeight, self.tileX, self.tileY + 1.0}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get type
|
||||||
|
function body:getType()
|
||||||
|
return self.type
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get type
|
||||||
|
function body:setShadowType(type, ...)
|
||||||
|
self.shadowType = type
|
||||||
|
local args = {...}
|
||||||
|
if self.shadowType == "circle" then
|
||||||
|
self.radius = args[1] or 16
|
||||||
|
self.ox = args[2] or 0
|
||||||
|
self.oy = args[3] or 0
|
||||||
|
elseif self.shadowType == "rectangle" then
|
||||||
|
self.shadowType = "polygon"
|
||||||
|
local width = args[1] or 64
|
||||||
|
local height = args[2] or 64
|
||||||
|
self.ox = args[3] or width * 0.5
|
||||||
|
self.oy = args[4] or height * 0.5
|
||||||
|
|
||||||
|
self:setPoints(
|
||||||
|
self.x - self.ox, self.y - self.oy,
|
||||||
|
self.x - self.ox + width, self.y - self.oy,
|
||||||
|
self.x - self.ox + width, self.y - self.oy + height,
|
||||||
|
self.x - self.ox, self.y - self.oy + height
|
||||||
|
)
|
||||||
|
elseif self.shadowType == "polygon" then
|
||||||
|
self:setPoints(args)
|
||||||
|
elseif self.shadowType == "image" then
|
||||||
|
if self.img then
|
||||||
|
self.width = self.imgWidth
|
||||||
|
self.height = self.imgHeight
|
||||||
|
self.shadowVert = {
|
||||||
|
{0.0, 0.0, 0.0, 0.0},
|
||||||
|
{self.width, 0.0, 1.0, 0.0},
|
||||||
|
{self.width, self.height, 1.0, 1.0},
|
||||||
|
{0.0, self.height, 0.0, 1.0}
|
||||||
|
}
|
||||||
|
if not self.shadowMesh then
|
||||||
|
self.shadowMesh = love.graphics.newMesh(self.shadowVert)
|
||||||
|
self.shadowMesh:setTexture(self.img)
|
||||||
|
self.shadowMesh:setAttributeEnabled("VertexColor", true)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.width = 64
|
||||||
|
self.height = 64
|
||||||
|
end
|
||||||
|
self.shadowX = args[1] or 0
|
||||||
|
self.shadowY = args[2] or 0
|
||||||
|
self.fadeStrength = args[3] or 0.0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:isVisible()
|
||||||
|
return self.visible and self.is_on_screen
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:inLightRange(light)
|
||||||
|
local l, t, w = light.x - light.range, light.y - light.range, light.range*2
|
||||||
|
return self:inRange(l,t,w,w,1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:inRange(l, t, w, h, s)
|
||||||
|
local radius
|
||||||
|
if self.type == 'circle' then
|
||||||
|
radius = self.radius * self.scalex
|
||||||
|
else
|
||||||
|
local sw = (self.width * self.scalex)
|
||||||
|
local sh = (self.height * self.scaley)
|
||||||
|
radius = (sw > sh and sw or sh)
|
||||||
|
end
|
||||||
|
|
||||||
|
local bx, by, bw, bh = self.x - radius, self.y - radius, radius * 2, radius * 2
|
||||||
|
|
||||||
|
return self.visible and (bx+bw) > (l/s) and bx < (l+w)/s and (by+bh) > (t/s) and by < (t+h)/s
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:drawAnimation()
|
||||||
|
self.animation:draw(self.img, self.x, self.y, self.rotation, self.scalex, self.scaley, self.ix, self.iy)
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:drawNormal()
|
||||||
|
if not self.refraction and not self.reflection and self.normalMesh then
|
||||||
|
love.graphics.setColor(255, 255, 255)
|
||||||
|
if self.type == 'animation' then
|
||||||
|
self.animation:draw(self.normal, self.x, self.y, self.rotation, self.scalex, self.scaley, self.nx, self.ny)
|
||||||
|
else
|
||||||
|
love.graphics.draw(self.normalMesh, self.x, self.y, self.rotation, self.scalex, self.scaley, self.nx, self.ny)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:drawGlow()
|
||||||
|
love.graphics.setColor(self.glowRed * self.glowStrength, self.glowGreen * self.glowStrength, self.glowBlue * self.glowStrength)
|
||||||
|
|
||||||
|
if self.type == "circle" then
|
||||||
|
love.graphics.circle("fill", self.x, self.y, self.radius * self.scalex)
|
||||||
|
elseif self.type == "polygon" then
|
||||||
|
love.graphics.polygon("fill", unpack(self.data))
|
||||||
|
elseif (self.type == "image" or self.type == "animation") and self.img then
|
||||||
|
if self.glow then
|
||||||
|
love.graphics.setShader(self.glowShader)
|
||||||
|
self.glowShader:send("glowImage", self.glow)
|
||||||
|
self.glowShader:send("glowTime", love.timer.getTime() * 0.5)
|
||||||
|
love.graphics.setColor(255, 255, 255)
|
||||||
|
else
|
||||||
|
love.graphics.setColor(0, 0, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.type == "animation" then
|
||||||
|
self.animation:draw(self.img, self.x, self.y, self.rotation, self.scalex, self.scaley, self.ix, self.iy)
|
||||||
|
else
|
||||||
|
love.graphics.draw(self.img, self.x, self.y, self.rotation, self.scalex, self.scaley, self.ix, self.iy)
|
||||||
|
end
|
||||||
|
|
||||||
|
love.graphics.setShader()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:drawRefraction()
|
||||||
|
if self.refraction and self.normal then
|
||||||
|
love.graphics.setColor(255, 255, 255)
|
||||||
|
if self.tileX == 0.0 and self.tileY == 0.0 then
|
||||||
|
love.graphics.draw(self.normal, self.x, self.y, self.rotation, self.scalex, self.scaley, self.nx, self.ny)
|
||||||
|
else
|
||||||
|
self.normalMesh:setVertices(self.normalVert)
|
||||||
|
love.graphics.draw(self.normalMesh, self.x, self.y, self.rotation, self.scalex, self.scaley, self.nx, self.ny)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
love.graphics.setColor(0, 0, 0)
|
||||||
|
|
||||||
|
if not self.refractive then
|
||||||
|
if self.type == "circle" then
|
||||||
|
love.graphics.circle("fill", self.x, self.y, self.radius * self.scalex)
|
||||||
|
elseif self.type == "polygon" then
|
||||||
|
love.graphics.polygon("fill", unpack(self.data))
|
||||||
|
elseif self.type == "image" and self.img then
|
||||||
|
love.graphics.draw(self.img, self.x, self.y, self.rotation, self.scalex, self.scaley, self.ix, self.iy)
|
||||||
|
elseif self.type == 'animation' then
|
||||||
|
self.animation:draw(self.img, self.x, self.y, self.rotation, self.scalex, self.scaley, self.ix, self.iy)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:drawReflection()
|
||||||
|
if self.reflection and self.normal then
|
||||||
|
love.graphics.setColor(255, 0, 0)
|
||||||
|
self.normalMesh:setVertices(self.normalVert)
|
||||||
|
love.graphics.draw(self.normalMesh, self.x, self.y, self.rotation, self.scalex, self.scaley, self.nx, self.ny)
|
||||||
|
end
|
||||||
|
if self.reflective and self.img then
|
||||||
|
love.graphics.setColor(0, 255, 0)
|
||||||
|
if self.type == 'animation' then
|
||||||
|
self.animation:draw(self.img, self.x, self.y, self.rotation, self.scalex, self.scaley, self.ix, self.iy)
|
||||||
|
else
|
||||||
|
love.graphics.draw(self.img, self.x, self.y, self.rotation, self.scalex, self.scaley, self.ix, self.iy)
|
||||||
|
end
|
||||||
|
elseif not self.reflection and self.img then
|
||||||
|
love.graphics.setColor(0, 0, 0)
|
||||||
|
if self.type == 'animation' then
|
||||||
|
self.animation:draw(self.img, self.x, self.y, self.rotation, self.scalex, self.scaley, self.ix, self.iy)
|
||||||
|
else
|
||||||
|
love.graphics.draw(self.img, self.x, self.y, self.rotation, self.scalex, self.scaley, self.ix, self.iy)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:drawMaterial()
|
||||||
|
if self.material and self.normal then
|
||||||
|
love.graphics.setShader(self.materialShader)
|
||||||
|
love.graphics.setColor(255, 255, 255)
|
||||||
|
self.materialShader:send("material", self.material)
|
||||||
|
if self.type == 'animation' then
|
||||||
|
self.animation:draw(self.normal, self.x, self.y, self.rotation, self.scalex, self.scaley, self.nx, self.ny)
|
||||||
|
else
|
||||||
|
love.graphics.draw(self.normal, self.x, self.y, self.rotation, self.scalex, self.scaley, self.nx, self.ny)
|
||||||
|
end
|
||||||
|
love.graphics.setShader()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:drawStencil()
|
||||||
|
if not self.refraction and not self.reflection and not self.castsNoShadow then
|
||||||
|
love.graphics.draw(self.img, self.x, self.y, self.rotation, self.scalex, self.scaley, self.ix, self.iy)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:drawShadow(light)
|
||||||
|
if self.castsNoShadow or (self.zheight - light.z) > 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
love.graphics.setColor(self.red, self.green, self.blue, self.alpha)
|
||||||
|
if self.shadowType == "polygon" then
|
||||||
|
self:drawPolyShadow(light)
|
||||||
|
elseif self.shadowType == "circle" then
|
||||||
|
self:drawCircleShadow(light)
|
||||||
|
elseif self.shadowType == "image" and self.img then
|
||||||
|
self:drawImageShadow(light)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--using shadow point calculations from this article
|
||||||
|
--http://web.cs.wpi.edu/~matt/courses/cs563/talks/shadow/shadow.html
|
||||||
|
function body:drawPolyShadow(light)
|
||||||
|
local lightPosition = vector(light.x, light.y)
|
||||||
|
local lh = lightPosition * self.zheight
|
||||||
|
|
||||||
|
local height_diff = (self.zheight - light.z)
|
||||||
|
if height_diff == 0 then -- prevent inf
|
||||||
|
height_diff = -0.001
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, #self.data, 2 do
|
||||||
|
local vertex = vector(self.data[i], self.data[i + 1])
|
||||||
|
local nextVertex = vector(self.data[(i + 2) % #self.data], self.data[(i + 2) % #self.data + 1])
|
||||||
|
local startToEnd = nextVertex - vertex
|
||||||
|
if vector(startToEnd.y, -startToEnd.x) * (vertex - lightPosition) > 0 then
|
||||||
|
local point1 = (lh - (vertex * light.z))/height_diff
|
||||||
|
local point2 = (lh - (nextVertex * light.z))/height_diff
|
||||||
|
love.graphics.polygon("fill",
|
||||||
|
vertex.x, vertex.y, point1.x, point1.y,
|
||||||
|
point2.x, point2.y, nextVertex.x, nextVertex.y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--using shadow point calculations from this article
|
||||||
|
--http://web.cs.wpi.edu/~matt/courses/cs563/talks/shadow/shadow.html
|
||||||
|
function body:drawCircleShadow(light)
|
||||||
|
local selfPos = vector(self.x - self.ox, self.y - self.oy)
|
||||||
|
local lightPosition = vector(light.x, light.y)
|
||||||
|
local lh = lightPosition * self.zheight
|
||||||
|
local height_diff = (self.zheight - light.z)
|
||||||
|
local radius = self.radius * self.scalex
|
||||||
|
if height_diff == 0 then -- prevent inf
|
||||||
|
height_diff = -0.001
|
||||||
|
end
|
||||||
|
|
||||||
|
local angle = math.atan2(light.x - selfPos.x, selfPos.y - light.y) + math.pi / 2
|
||||||
|
local point1 = vector(selfPos.x + math.sin(angle) * radius,
|
||||||
|
selfPos.y - math.cos(angle) * radius)
|
||||||
|
local point2 = vector(selfPos.x - math.sin(angle) * radius,
|
||||||
|
selfPos.y + math.cos(angle) * radius)
|
||||||
|
local point3 = (lh - (point1 * light.z))/height_diff
|
||||||
|
local point4 = (lh - (point2 * light.z))/height_diff
|
||||||
|
|
||||||
|
local shadow_radius = point3:dist(point4)/2
|
||||||
|
local circleCenter = (point3 + point4)/2
|
||||||
|
|
||||||
|
if lightPosition:dist(selfPos) <= radius then
|
||||||
|
love.graphics.circle("fill", circleCenter.x, circleCenter.y, shadow_radius)
|
||||||
|
else
|
||||||
|
love.graphics.polygon("fill", point1.x, point1.y,
|
||||||
|
point2.x, point2.y,
|
||||||
|
point4.x, point4.y,
|
||||||
|
point3.x, point3.y)
|
||||||
|
if lightPosition:dist(circleCenter) < light.range then -- dont draw circle if way off screen
|
||||||
|
local angle1 = math.atan2(point3.y - circleCenter.y, point3.x - circleCenter.x)
|
||||||
|
local angle2 = math.atan2(point4.y - circleCenter.y, point4.x - circleCenter.x)
|
||||||
|
if angle1 < angle2 then
|
||||||
|
love.graphics.arc("fill", circleCenter.x, circleCenter.y, shadow_radius, angle1, angle2)
|
||||||
|
else
|
||||||
|
love.graphics.arc("fill", circleCenter.x, circleCenter.y, shadow_radius, angle1 - math.pi, angle2 - math.pi)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function body:drawImageShadow(light)
|
||||||
|
local height_diff = (light.z - self.zheight)
|
||||||
|
if height_diff <= 0.1 then -- prevent shadows from leaving thier person like peter pan.
|
||||||
|
height_diff = 0.1
|
||||||
|
end
|
||||||
|
|
||||||
|
local length = 1.0 / height_diff
|
||||||
|
local shadowRotation = math.atan2((self.x) - light.x, (self.y + self.oy) - light.y)
|
||||||
|
local shadowStartY = self.imgHeight + (math.cos(shadowRotation) + 1.0) * self.shadowY
|
||||||
|
local shadowX = math.sin(shadowRotation) * self.imgHeight * length
|
||||||
|
local shadowY = (length * math.cos(shadowRotation) + 1.0) * shadowStartY
|
||||||
|
|
||||||
|
self.shadowMesh:setVertices({
|
||||||
|
{shadowX, shadowY, 0, 0, self.red, self.green, self.blue, self.alpha},
|
||||||
|
{shadowX + self.imgWidth, shadowY, 1, 0, self.red, self.green, self.blue, self.alpha},
|
||||||
|
{self.imgWidth, shadowStartY, 1, 1, self.red, self.green, self.blue, self.alpha},
|
||||||
|
{0, shadowStartY, 0, 1, self.red, self.green, self.blue, self.alpha}
|
||||||
|
})
|
||||||
|
|
||||||
|
love.graphics.draw(self.shadowMesh, self.x, self.y, self.rotation, self.scalex, self.scaley, self.ox, self.oy)
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable({new = new}, {__call = function(_, ...) return new(...) end})
|
||||||
@@ -0,0 +1,344 @@
|
|||||||
|
--[[
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Marcus Ihde, Tim Anema
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
]]
|
||||||
|
local _PACKAGE = string.gsub(...,"%.","/") or ""
|
||||||
|
if string.len(_PACKAGE) > 0 then _PACKAGE = _PACKAGE .. "/" end
|
||||||
|
local Light = require(_PACKAGE..'light')
|
||||||
|
local Body = require(_PACKAGE..'body')
|
||||||
|
local util = require(_PACKAGE..'util')
|
||||||
|
local PostShader = require(_PACKAGE..'postshader')
|
||||||
|
|
||||||
|
local light_world = {}
|
||||||
|
light_world.__index = light_world
|
||||||
|
|
||||||
|
light_world.image_mask = love.graphics.newShader(_PACKAGE.."/shaders/image_mask.glsl")
|
||||||
|
light_world.shadowShader = love.graphics.newShader(_PACKAGE.."/shaders/shadow.glsl")
|
||||||
|
light_world.refractionShader = love.graphics.newShader(_PACKAGE.."shaders/refraction.glsl")
|
||||||
|
light_world.reflectionShader = love.graphics.newShader(_PACKAGE.."shaders/reflection.glsl")
|
||||||
|
|
||||||
|
local function new(options)
|
||||||
|
local obj = {}
|
||||||
|
obj.lights = {}
|
||||||
|
obj.bodies = {}
|
||||||
|
obj.post_shader = PostShader()
|
||||||
|
|
||||||
|
obj.l, obj.t, obj.s = 0, 0, 1
|
||||||
|
obj.ambient = {0, 0, 0}
|
||||||
|
obj.refractionStrength = 8.0
|
||||||
|
obj.reflectionStrength = 16.0
|
||||||
|
obj.reflectionVisibility = 1.0
|
||||||
|
obj.shadowBlur = 2.0
|
||||||
|
obj.glowBlur = 1.0
|
||||||
|
obj.glowTimer = 0.0
|
||||||
|
obj.glowDown = false
|
||||||
|
|
||||||
|
obj.disableGlow = false
|
||||||
|
obj.disableMaterial = false
|
||||||
|
obj.disableReflection = true
|
||||||
|
obj.disableRefraction = true
|
||||||
|
|
||||||
|
options = options or {}
|
||||||
|
for k, v in pairs(options) do obj[k] = v end
|
||||||
|
|
||||||
|
local world = setmetatable(obj, light_world)
|
||||||
|
world:refreshScreenSize()
|
||||||
|
|
||||||
|
return world
|
||||||
|
end
|
||||||
|
|
||||||
|
function light_world:refreshScreenSize(w, h)
|
||||||
|
w, h = w or love.graphics.getWidth(), h or love.graphics.getHeight()
|
||||||
|
|
||||||
|
self.w, self.h = w, h
|
||||||
|
self.render_buffer = love.graphics.newCanvas(w, h)
|
||||||
|
self.shadow_buffer = love.graphics.newCanvas(w, h)
|
||||||
|
self.normalMap = love.graphics.newCanvas(w, h)
|
||||||
|
self.shadowMap = love.graphics.newCanvas(w, h)
|
||||||
|
self.glowMap = love.graphics.newCanvas(w, h)
|
||||||
|
self.refractionMap = love.graphics.newCanvas(w, h)
|
||||||
|
self.reflectionMap = love.graphics.newCanvas(w, h)
|
||||||
|
|
||||||
|
self.post_shader:refreshScreenSize(w, h)
|
||||||
|
end
|
||||||
|
|
||||||
|
function light_world:update(dt)
|
||||||
|
for i = 1, #self.bodies do
|
||||||
|
self.bodies[i].is_on_screen = self.bodies[i]:inRange(-self.l,-self.t,self.w,self.h,self.s)
|
||||||
|
if self.bodies[i]:isVisible() then
|
||||||
|
self.bodies[i]:update(dt)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for i = 1, #self.lights do
|
||||||
|
self.lights[i].is_on_screen = self.lights[i]:inRange(self.l,self.t,self.w,self.h,self.s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function light_world:draw(cb)
|
||||||
|
util.drawto(self.render_buffer, self.l, self.t, self.s, function()
|
||||||
|
cb( self.l,self.t,self.w,self.h,self.s)
|
||||||
|
_ = self.disableMaterial or self:drawMaterial( self.l,self.t,self.w,self.h,self.s)
|
||||||
|
self:drawShadows( self.l,self.t,self.w,self.h,self.s)
|
||||||
|
_ = self.disableGlow or self:drawGlow( self.l,self.t,self.w,self.h,self.s)
|
||||||
|
_ = self.disableRefraction or self:drawRefraction( self.l,self.t,self.w,self.h,self.s)
|
||||||
|
_ = self.disableReflection or self:drawReflection( self.l,self.t,self.w,self.h,self.s)
|
||||||
|
end)
|
||||||
|
self.post_shader:drawWith(self.render_buffer, self.l, self.t, self.s)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- draw normal shading
|
||||||
|
function light_world:drawShadows(l,t,w,h,s)
|
||||||
|
love.graphics.setCanvas( self.normalMap )
|
||||||
|
love.graphics.clear()
|
||||||
|
love.graphics.setCanvas()
|
||||||
|
util.drawto(self.normalMap, l, t, s, function()
|
||||||
|
for i = 1, #self.bodies do
|
||||||
|
if self.bodies[i]:isVisible() then
|
||||||
|
self.bodies[i]:drawNormal()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
self.shadowShader:send('normalMap', self.normalMap)
|
||||||
|
self.shadowShader:send("invert_normal", self.normalInvert == true)
|
||||||
|
|
||||||
|
love.graphics.setCanvas( self.shadow_buffer )
|
||||||
|
love.graphics.clear()
|
||||||
|
love.graphics.setCanvas()
|
||||||
|
for i = 1, #self.lights do
|
||||||
|
local light = self.lights[i]
|
||||||
|
if light:isVisible() then
|
||||||
|
-- create shadow map for this light
|
||||||
|
love.graphics.setCanvas( self.shadowMap )
|
||||||
|
love.graphics.clear()
|
||||||
|
love.graphics.setCanvas()
|
||||||
|
util.drawto(self.shadowMap, l, t, s, function()
|
||||||
|
--I dont know if it uses both or just calls both
|
||||||
|
love.graphics.stencil(function()
|
||||||
|
local angle = light.direction - (light.angle / 2.0)
|
||||||
|
love.graphics.arc("fill", light.x, light.y, light.range, angle, angle + light.angle)
|
||||||
|
end)
|
||||||
|
love.graphics.setStencilTest("greater",0)
|
||||||
|
love.graphics.stencil(function()
|
||||||
|
love.graphics.setShader(self.image_mask)
|
||||||
|
for k = 1, #self.bodies do
|
||||||
|
if self.bodies[k]:inLightRange(light) and self.bodies[k]:isVisible() then
|
||||||
|
self.bodies[k]:drawStencil()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
love.graphics.setShader()
|
||||||
|
end)
|
||||||
|
love.graphics.setStencilTest("equal", 0)
|
||||||
|
for k = 1, #self.bodies do
|
||||||
|
if self.bodies[k]:inLightRange(light) and self.bodies[k]:isVisible() then
|
||||||
|
self.bodies[k]:drawShadow(light)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
-- draw scene for this light using normals and shadowmap
|
||||||
|
self.shadowShader:send('lightColor', {light.red / 255.0, light.green / 255.0, light.blue / 255.0})
|
||||||
|
self.shadowShader:send("lightPosition", {(light.x + l/s) * s, (light.y + t/s) * s, (light.z * 10) / 255.0})
|
||||||
|
self.shadowShader:send('lightRange',light.range * s)
|
||||||
|
self.shadowShader:send("lightSmooth", light.smooth)
|
||||||
|
self.shadowShader:send("lightGlow", {1.0 - light.glowSize, light.glowStrength})
|
||||||
|
util.drawCanvasToCanvas(self.shadowMap, self.shadow_buffer, {
|
||||||
|
blendmode = 'add',
|
||||||
|
shader = self.shadowShader,
|
||||||
|
stencil = function()
|
||||||
|
local angle = light.direction - (light.angle / 2.0)
|
||||||
|
love.graphics.arc("fill", (light.x + l/s) * s, (light.y + t/s) * s, light.range, angle, angle + light.angle)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- add in ambient color
|
||||||
|
util.drawto(self.shadow_buffer, l, t, s, function()
|
||||||
|
love.graphics.setBlendMode("add")
|
||||||
|
love.graphics.setColor({self.ambient[1], self.ambient[2], self.ambient[3]})
|
||||||
|
love.graphics.rectangle("fill", -l/s, -t/s, w/s,h/s)
|
||||||
|
end)
|
||||||
|
|
||||||
|
self.post_shader:drawBlur(self.shadow_buffer, {self.shadowBlur})
|
||||||
|
util.drawCanvasToCanvas(self.shadow_buffer, self.render_buffer, {blendmode = "multiply"})
|
||||||
|
love.graphics.setStencilTest()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- draw material
|
||||||
|
function light_world:drawMaterial(l,t,w,h,s)
|
||||||
|
for i = 1, #self.bodies do
|
||||||
|
if self.bodies[i]:isVisible() then
|
||||||
|
self.bodies[i]:drawMaterial()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- draw glow
|
||||||
|
function light_world:drawGlow(l,t,w,h,s)
|
||||||
|
if self.glowDown then
|
||||||
|
self.glowTimer = math.max(0.0, self.glowTimer - love.timer.getDelta())
|
||||||
|
else
|
||||||
|
self.glowTimer = math.min(self.glowTimer + love.timer.getDelta(), 1.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.glowTimer == 1.0 or self.glowTimer == 0.0 then
|
||||||
|
self.glowDown = not self.glowDown
|
||||||
|
end
|
||||||
|
|
||||||
|
local has_glow = false
|
||||||
|
-- create glow map
|
||||||
|
love.graphics.setCanvas( self.glowMap )
|
||||||
|
love.graphics.clear()
|
||||||
|
love.graphics.setCanvas()
|
||||||
|
util.drawto(self.glowMap, l, t, s, function()
|
||||||
|
for i = 1, #self.bodies do
|
||||||
|
if self.bodies[i]:isVisible() and self.bodies[i].glowStrength > 0.0 then
|
||||||
|
has_glow = true
|
||||||
|
self.bodies[i]:drawGlow()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
if has_glow then
|
||||||
|
self.post_shader:drawBlur(self.glowMap, {self.glowBlur})
|
||||||
|
util.drawCanvasToCanvas(self.glowMap, self.render_buffer, {blendmode = "add"})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- draw refraction
|
||||||
|
function light_world:drawRefraction(l,t,w,h,s)
|
||||||
|
-- create refraction map
|
||||||
|
love.graphics.setCanvas( self.refractionMap )
|
||||||
|
love.graphics.clear()
|
||||||
|
love.graphics.setCanvas()
|
||||||
|
util.drawto(self.refractionMap, l, t, s, function()
|
||||||
|
for i = 1, #self.bodies do
|
||||||
|
if self.bodies[i]:isVisible() then
|
||||||
|
self.bodies[i]:drawRefraction()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
self.refractionShader:send("backBuffer", self.render_buffer)
|
||||||
|
self.refractionShader:send("refractionStrength", self.refractionStrength)
|
||||||
|
util.drawCanvasToCanvas(self.refractionMap, self.render_buffer, {shader = self.refractionShader})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- draw reflection
|
||||||
|
function light_world:drawReflection(l,t,w,h,s)
|
||||||
|
-- create reflection map
|
||||||
|
love.graphics.setCanvas( self.reflectionMap )
|
||||||
|
love.graphics.clear()
|
||||||
|
love.graphics.setCanvas()
|
||||||
|
util.drawto(self.reflectionMap, l, t, s, function()
|
||||||
|
for i = 1, #self.bodies do
|
||||||
|
if self.bodies[i]:isVisible() then
|
||||||
|
self.bodies[i]:drawReflection()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
self.reflectionShader:send("backBuffer", self.render_buffer)
|
||||||
|
self.reflectionShader:send("reflectionStrength", self.reflectionStrength)
|
||||||
|
self.reflectionShader:send("reflectionVisibility", self.reflectionVisibility)
|
||||||
|
util.drawCanvasToCanvas(self.reflectionMap, self.render_buffer, {shader = self.reflectionShader})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- new light
|
||||||
|
function light_world:newLight(x, y, red, green, blue, range)
|
||||||
|
self.lights[#self.lights + 1] = Light(x, y, red, green, blue, range)
|
||||||
|
return self.lights[#self.lights]
|
||||||
|
end
|
||||||
|
|
||||||
|
function light_world:clear()
|
||||||
|
light_world:clearLights()
|
||||||
|
light_world:clearBodies()
|
||||||
|
end
|
||||||
|
|
||||||
|
function light_world:setTranslation(l, t, s)
|
||||||
|
self.l, self.t, self.s = l or self.l, t or self.t, s or self.s
|
||||||
|
end
|
||||||
|
|
||||||
|
function light_world:setScale(s) self.s = s end
|
||||||
|
function light_world:clearLights() self.lights = {} end
|
||||||
|
function light_world:clearBodies() self.bodies = {} end
|
||||||
|
function light_world:setAmbientColor(red, green, blue) self.ambient = {red, green, blue} end
|
||||||
|
function light_world:setShadowBlur(blur) self.shadowBlur = blur end
|
||||||
|
function light_world:setGlowStrength(strength) self.glowBlur = strength end
|
||||||
|
function light_world:setRefractionStrength(strength) self.refractionStrength = strength end
|
||||||
|
function light_world:setReflectionStrength(strength) self.reflectionStrength = strength end
|
||||||
|
function light_world:setReflectionVisibility(visibility) self.reflectionVisibility = visibility end
|
||||||
|
function light_world:getBodyCount() return #self.bodies end
|
||||||
|
function light_world:getBody(n) return self.bodies[n] end
|
||||||
|
function light_world:getLightCount() return #self.lights end
|
||||||
|
function light_world:getLight(n) return self.lights[n] end
|
||||||
|
function light_world:newRectangle(...) return self:newBody("rectangle", ...) end
|
||||||
|
function light_world:newAnimationGrid(...) return self:newBody("animation", ...) end
|
||||||
|
function light_world:newCircle(...) return self:newBody("circle", ...) end
|
||||||
|
function light_world:newPolygon(...) return self:newBody("polygon", ...) end
|
||||||
|
function light_world:newImage(...) return self:newBody("image", ...) end
|
||||||
|
|
||||||
|
function light_world:newRefraction(...)
|
||||||
|
self.disableRefraction = false
|
||||||
|
return self:newBody("refraction", ...)
|
||||||
|
end
|
||||||
|
function light_world:newReflection(normal, ...)
|
||||||
|
self.disableReflection = false
|
||||||
|
return self:newBody("reflection", ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- new body
|
||||||
|
function light_world:newBody(type, ...)
|
||||||
|
local id = #self.bodies + 1
|
||||||
|
self.bodies[id] = Body(id, type, ...)
|
||||||
|
return self.bodies[#self.bodies]
|
||||||
|
end
|
||||||
|
|
||||||
|
function light_world:is_body(target)
|
||||||
|
return target.type ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function light_world:is_light(target)
|
||||||
|
return target.angle ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function light_world:remove(to_kill)
|
||||||
|
if self:is_body(to_kill) then
|
||||||
|
for i = 1, #self.bodies do
|
||||||
|
if self.bodies[i] == to_kill then
|
||||||
|
table.remove(self.bodies, i)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif self:is_light(to_kill) then
|
||||||
|
for i = 1, #self.lights do
|
||||||
|
if self.lights[i] == to_kill then
|
||||||
|
table.remove(self.lights, i)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- failed to find it
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable({new = new}, {__call = function(_, ...) return new(...) end})
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
local _PACKAGE = (...):match("^(.+)[%./][^%./]+") or ""
|
||||||
|
local util = require(_PACKAGE..'/util')
|
||||||
|
|
||||||
|
local light = {}
|
||||||
|
light.__index = light
|
||||||
|
|
||||||
|
local function new(x, y, r, g, b, range)
|
||||||
|
local obj = {
|
||||||
|
direction = 0,
|
||||||
|
angle = math.pi * 2.0,
|
||||||
|
x = x or 0,
|
||||||
|
y = y or 0,
|
||||||
|
z = 1,
|
||||||
|
red = r or 255,
|
||||||
|
green = g or 255,
|
||||||
|
blue = b or 255,
|
||||||
|
range = range or 300,
|
||||||
|
smooth = 1.0,
|
||||||
|
glowSize = 0.1,
|
||||||
|
glowStrength = 0.0,
|
||||||
|
visible = true,
|
||||||
|
is_on_screen = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return setmetatable(obj, light)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set position
|
||||||
|
function light:setPosition(x, y, z)
|
||||||
|
if x ~= self.x or y ~= self.y or (z and z ~= self.z) then
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
if z then
|
||||||
|
self.z = z
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- move position
|
||||||
|
function light:move(x, y, z)
|
||||||
|
if x then
|
||||||
|
self.x = self.x + x
|
||||||
|
end
|
||||||
|
if y then
|
||||||
|
self.y = self.y + y
|
||||||
|
end
|
||||||
|
if z then
|
||||||
|
self.z = self.z + z
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get position
|
||||||
|
function light:getPosition()
|
||||||
|
return self.x, self.y, self.z
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set color
|
||||||
|
function light:setColor(red, green, blue)
|
||||||
|
self.red = red
|
||||||
|
self.green = green
|
||||||
|
self.blue = blue
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get range
|
||||||
|
function light:getRange()
|
||||||
|
return self.range
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set range
|
||||||
|
function light:setRange(range)
|
||||||
|
if range ~= self.range then
|
||||||
|
self.range = range
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set direction
|
||||||
|
function light:setDirection(direction)
|
||||||
|
if direction ~= self.direction then
|
||||||
|
if direction > math.pi * 2 then
|
||||||
|
self.direction = math.mod(direction, math.pi * 2)
|
||||||
|
elseif direction < 0.0 then
|
||||||
|
self.direction = math.pi * 2 - math.mod(math.abs(direction), math.pi * 2)
|
||||||
|
else
|
||||||
|
self.direction = direction
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set angle
|
||||||
|
function light:setAngle(angle)
|
||||||
|
if angle ~= self.angle then
|
||||||
|
if angle > math.pi then
|
||||||
|
self.angle = math.mod(angle, math.pi)
|
||||||
|
elseif angle < 0.0 then
|
||||||
|
self.angle = math.pi - math.mod(math.abs(angle), math.pi)
|
||||||
|
else
|
||||||
|
self.angle = angle
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set glow size
|
||||||
|
function light:setSmooth(smooth)
|
||||||
|
self.smooth = smooth
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set glow size
|
||||||
|
function light:setGlowSize(size)
|
||||||
|
self.glowSize = size
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set glow strength
|
||||||
|
function light:setGlowStrength(strength)
|
||||||
|
self.glowStrength = strength
|
||||||
|
end
|
||||||
|
|
||||||
|
function light:isVisible()
|
||||||
|
return self.visible and self.is_on_screen
|
||||||
|
end
|
||||||
|
|
||||||
|
function light:inRange(l,t,w,h,s)
|
||||||
|
local lx, ly, rs = (self.x + l/s) * s, (self.y + t/s) * s, self.range * s
|
||||||
|
return self.visible and (lx + rs) > 0 and (lx - rs) < w/s and (ly + rs) > 0 and (ly - rs) < h/s
|
||||||
|
end
|
||||||
|
|
||||||
|
function light:setVisible(visible)
|
||||||
|
self.visible = visible
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable({new = new}, {__call = function(_, ...) return new(...) end})
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
local normal_map = {}
|
||||||
|
|
||||||
|
function normal_map.fromHeightMap(heightMap, strength)
|
||||||
|
local imgData = heightMap:getData()
|
||||||
|
local imgData2 = love.image.newImageData(heightMap:getWidth(), heightMap:getHeight())
|
||||||
|
local red, green, blue, alpha
|
||||||
|
local x, y
|
||||||
|
local matrix = {}
|
||||||
|
matrix[1] = {}
|
||||||
|
matrix[2] = {}
|
||||||
|
matrix[3] = {}
|
||||||
|
strength = strength or 1.0
|
||||||
|
|
||||||
|
for i = 0, heightMap:getHeight() - 1 do
|
||||||
|
for k = 0, heightMap:getWidth() - 1 do
|
||||||
|
for l = 1, 3 do
|
||||||
|
for m = 1, 3 do
|
||||||
|
if k + (l - 1) < 1 then
|
||||||
|
x = heightMap:getWidth() - 1
|
||||||
|
elseif k + (l - 1) > heightMap:getWidth() - 1 then
|
||||||
|
x = 1
|
||||||
|
else
|
||||||
|
x = k + l - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if i + (m - 1) < 1 then
|
||||||
|
y = heightMap:getHeight() - 1
|
||||||
|
elseif i + (m - 1) > heightMap:getHeight() - 1 then
|
||||||
|
y = 1
|
||||||
|
else
|
||||||
|
y = i + m - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local red, green, blue, alpha = imgData:getPixel(x, y)
|
||||||
|
matrix[l][m] = red
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
red = (255 + ((matrix[1][2] - matrix[2][2]) + (matrix[2][2] - matrix[3][2])) * strength) / 2.0
|
||||||
|
green = (255 + ((matrix[2][2] - matrix[1][1]) + (matrix[2][3] - matrix[2][2])) * strength) / 2.0
|
||||||
|
blue = 192
|
||||||
|
|
||||||
|
imgData2:setPixel(k, i, red, green, blue)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return love.graphics.newImage(imgData2)
|
||||||
|
end
|
||||||
|
|
||||||
|
function normal_map.generateFlat(img, mode)
|
||||||
|
local imgData = img:getData()
|
||||||
|
local imgNormalData = love.image.newImageData(img:getWidth(), img:getHeight())
|
||||||
|
local color
|
||||||
|
|
||||||
|
if mode == "top" then
|
||||||
|
color = {127, 127, 255}
|
||||||
|
elseif mode == "front" then
|
||||||
|
color = {127, 0, 127}
|
||||||
|
elseif mode == "back" then
|
||||||
|
color = {127, 255, 127}
|
||||||
|
elseif mode == "left" then
|
||||||
|
color = {31, 0, 223}
|
||||||
|
elseif mode == "right" then
|
||||||
|
color = {223, 0, 127}
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 0, img:getHeight() - 1 do
|
||||||
|
for k = 0, img:getWidth() - 1 do
|
||||||
|
local r, g, b, a = imgData:getPixel(k, i)
|
||||||
|
imgNormalData:setPixel(k, i, color[1], color[2], color[3], a)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return love.graphics.newImage(imgNormalData)
|
||||||
|
end
|
||||||
|
|
||||||
|
function normal_map.generateGradient(img, horizontalGradient, verticalGradient)
|
||||||
|
horizontalGradient = horizontalGradient or "gradient"
|
||||||
|
verticalGradient = verticalGradient or horizontalGradient
|
||||||
|
|
||||||
|
local imgData = img:getData()
|
||||||
|
local imgWidth, imgHeight = img:getWidth(), img:getHeight()
|
||||||
|
local imgNormalData = love.image.newImageData(imgWidth, imgHeight)
|
||||||
|
local dx = 255.0 / imgWidth
|
||||||
|
local dy = 255.0 / imgHeight
|
||||||
|
local nx
|
||||||
|
local ny
|
||||||
|
local nz
|
||||||
|
|
||||||
|
for i = 0, imgWidth - 1 do
|
||||||
|
for k = 0, imgHeight - 1 do
|
||||||
|
local r, g, b, a = imgData:getPixel(i, k)
|
||||||
|
if a > 0 then
|
||||||
|
if horizontalGradient == "gradient" then
|
||||||
|
nx = i * dx
|
||||||
|
elseif horizontalGradient == "inverse" then
|
||||||
|
nx = 255 - i * dx
|
||||||
|
else
|
||||||
|
nx = 127
|
||||||
|
end
|
||||||
|
|
||||||
|
if verticalGradient == "gradient" then
|
||||||
|
ny = 127 - k * dy * 0.5
|
||||||
|
nz = 255 - k * dy * 0.5
|
||||||
|
elseif verticalGradient == "inverse" then
|
||||||
|
ny = 127 + k * dy * 0.5
|
||||||
|
nz = 127 - k * dy * 0.25
|
||||||
|
else
|
||||||
|
ny = 255
|
||||||
|
nz = 127
|
||||||
|
end
|
||||||
|
|
||||||
|
imgNormalData:setPixel(i, k, nx, ny, nz, 255)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return love.graphics.newImage(imgNormalData)
|
||||||
|
end
|
||||||
|
|
||||||
|
return normal_map
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
--[[
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Marcus Ihde
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
]]
|
||||||
|
local _PACKAGE = (...):match("^(.+)[%./][^%./]+") or ""
|
||||||
|
local util = require(_PACKAGE..'/util')
|
||||||
|
|
||||||
|
local post_shader = {}
|
||||||
|
post_shader.__index = post_shader
|
||||||
|
|
||||||
|
local files = love.filesystem.getDirectoryItems(_PACKAGE.."/shaders/postshaders")
|
||||||
|
local shaders = {}
|
||||||
|
|
||||||
|
for i,v in ipairs(files) do
|
||||||
|
local name = _PACKAGE.."/shaders/postshaders".."/"..v
|
||||||
|
if love.filesystem.isFile(name) then
|
||||||
|
local str = love.filesystem.read(name)
|
||||||
|
local effect = love.graphics.newShader(name)
|
||||||
|
local defs = {}
|
||||||
|
for vtype, extern in str:gmatch("extern (%w+) (%w+)") do
|
||||||
|
defs[extern] = true
|
||||||
|
end
|
||||||
|
local shaderName = name:match(".-([^\\|/]-[^%.]+)$"):gsub("%.glsl", "")
|
||||||
|
shaders[shaderName] = {effect, defs}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function new()
|
||||||
|
local obj = {effects = {}}
|
||||||
|
local class = setmetatable(obj, post_shader)
|
||||||
|
class:refreshScreenSize()
|
||||||
|
return class
|
||||||
|
end
|
||||||
|
|
||||||
|
function post_shader:refreshScreenSize(w, h)
|
||||||
|
w, h = w or love.graphics.getWidth(), h or love.graphics.getHeight()
|
||||||
|
self.back_buffer = love.graphics.newCanvas(w, h)
|
||||||
|
end
|
||||||
|
|
||||||
|
function post_shader:addEffect(shaderName, ...)
|
||||||
|
self.effects[shaderName] = {...}
|
||||||
|
end
|
||||||
|
|
||||||
|
function post_shader:removeEffect(shaderName)
|
||||||
|
self.effects[shaderName] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function post_shader:toggleEffect(shaderName, ...)
|
||||||
|
if self.effects[shaderName] ~= nil then
|
||||||
|
self:removeEffect(shaderName)
|
||||||
|
else
|
||||||
|
self:addEffect(shaderName, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function post_shader:drawWith(canvas)
|
||||||
|
for shader, args in pairs(self.effects) do
|
||||||
|
if shader == "bloom" then
|
||||||
|
self:drawBloom(canvas, args)
|
||||||
|
elseif shader == "blur" then
|
||||||
|
self:drawBlur(canvas, args)
|
||||||
|
elseif shader == "tilt_shift" then
|
||||||
|
self:drawTiltShift(canvas, args)
|
||||||
|
else
|
||||||
|
self:drawShader(shader, canvas, args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
util.drawCanvasToCanvas(canvas)
|
||||||
|
end
|
||||||
|
|
||||||
|
function post_shader:drawBloom(canvas, args)
|
||||||
|
shaders['blurv'][1]:send("steps", args[1] or 2.0)
|
||||||
|
shaders['blurh'][1]:send("steps", args[1] or 2.0)
|
||||||
|
util.drawCanvasToCanvas(canvas, self.back_buffer, {shader = shaders['blurv'][1]})
|
||||||
|
util.process(self.back_buffer, {shader = shaders['blurh'][1]})
|
||||||
|
util.process(self.back_buffer, {shader = shaders['contrast'][1]})
|
||||||
|
util.process(canvas, {shader = shaders['contrast'][1]})
|
||||||
|
util.drawCanvasToCanvas(self.back_buffer, canvas, {blendmode = "add", color = {255, 255, 255, (args[2] or 0.25) * 255}})
|
||||||
|
end
|
||||||
|
|
||||||
|
function post_shader:drawBlur(canvas, args)
|
||||||
|
shaders['blurv'][1]:send("steps", args[1] or 0.0)
|
||||||
|
shaders['blurh'][1]:send("steps", args[2] or args[1] or 0.0)
|
||||||
|
util.process(canvas, {shader = shaders['blurv'][1], blendmode = "alpha"})
|
||||||
|
util.process(canvas, {shader = shaders['blurh'][1], blendmode = "alpha"})
|
||||||
|
end
|
||||||
|
|
||||||
|
function post_shader:drawTiltShift(canvas, args)
|
||||||
|
shaders['blurv'][1]:send("steps", args[1] or 2.0)
|
||||||
|
shaders['blurh'][1]:send("steps", args[2] or 2.0)
|
||||||
|
util.drawCanvasToCanvas(canvas, self.back_buffer, {shader = shaders['blurv'][1]})
|
||||||
|
util.process(self.back_buffer, {shader = shaders['blurh'][1]})
|
||||||
|
shaders['tilt_shift'][1]:send("imgBuffer", canvas)
|
||||||
|
util.drawCanvasToCanvas(self.back_buffer, canvas, {shader = shaders['tilt_shift'][1]})
|
||||||
|
end
|
||||||
|
|
||||||
|
function post_shader:drawShader(shaderName, canvas, args)
|
||||||
|
local current_arg = 1
|
||||||
|
|
||||||
|
local effect = shaders[shaderName]
|
||||||
|
if effect == nil then
|
||||||
|
print("no shader called "..shaderName)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for def in pairs(effect[2]) do
|
||||||
|
if def == "time" then
|
||||||
|
effect[1]:send("time", love.timer.getTime())
|
||||||
|
elseif def == "palette" then
|
||||||
|
effect[1]:send("palette", unpack(process_palette({
|
||||||
|
args[current_arg],
|
||||||
|
args[current_arg + 1],
|
||||||
|
args[current_arg + 2],
|
||||||
|
args[current_arg + 3]
|
||||||
|
})))
|
||||||
|
current_arg = current_arg + 4
|
||||||
|
elseif def == "tint" then
|
||||||
|
effect[1]:send("tint", {process_tint(args[1], args[2], args[3])})
|
||||||
|
current_arg = current_arg + 3
|
||||||
|
elseif def == "imgBuffer" then
|
||||||
|
effect[1]:send("imgBuffer", canvas)
|
||||||
|
else
|
||||||
|
local value = args[current_arg]
|
||||||
|
if value ~= nil then
|
||||||
|
effect[1]:send(def, value)
|
||||||
|
end
|
||||||
|
current_arg = current_arg + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
util.drawCanvasToCanvas(canvas, self.back_buffer, {shader = effect[1]})
|
||||||
|
util.drawCanvasToCanvas(self.back_buffer, canvas)
|
||||||
|
end
|
||||||
|
|
||||||
|
function process_tint(r, g, b)
|
||||||
|
return (r and r/255.0 or 1.0), (g and g/255.0 or 1.0), (b and b/255.0 or 1.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function process_palette(palette)
|
||||||
|
for i = 1, #palette do
|
||||||
|
palette[i] = {process_tint(unpack(palette[i]))}
|
||||||
|
end
|
||||||
|
return palette
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable({new = new}, {__call = function(_, ...) return new(...) end})
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
extern Image glowImage;
|
||||||
|
extern float glowTime;
|
||||||
|
|
||||||
|
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) {
|
||||||
|
vec3 glowInfo = Texel(glowImage, texture_coords).rgb;
|
||||||
|
|
||||||
|
if(glowInfo.r != glowInfo.g) {
|
||||||
|
float glowStrength = glowTime + glowInfo.b;
|
||||||
|
if(mod(glowStrength, 2.0) < 1.0) {
|
||||||
|
glowInfo.b = mod(glowStrength, 1.0);
|
||||||
|
} else {
|
||||||
|
glowInfo.b = 1.0 - mod(glowStrength, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Texel(texture, texture_coords) * (glowInfo.g + glowInfo.b * (glowInfo.r - glowInfo.g));
|
||||||
|
}
|
||||||
|
|
||||||
|
return vec4(Texel(texture, texture_coords).rgb * glowInfo.r, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
//https://love2d.org/wiki/love.graphics.setStencil image mask
|
||||||
|
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords) {
|
||||||
|
if (Texel(texture, texture_coords).rgb == vec3(0.0))
|
||||||
|
discard;
|
||||||
|
return vec4(1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
extern Image material;
|
||||||
|
|
||||||
|
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) {
|
||||||
|
vec4 normal = Texel(texture, texture_coords);
|
||||||
|
if(normal.a == 1.0) {
|
||||||
|
return Texel(material, vec2(normal.x, normal.y));
|
||||||
|
} else {
|
||||||
|
return vec4(0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
extern float exposure = 0.7;
|
||||||
|
extern float brightness = 1.0;
|
||||||
|
extern vec3 lumacomponents = vec3(1.0, 1.0, 1.0);
|
||||||
|
const vec3 lumcoeff = vec3(0.212671, 0.715160, 0.072169);
|
||||||
|
|
||||||
|
vec4 effect(vec4 vcolor, Image texture, vec2 texcoord, vec2 pixel_coords) {
|
||||||
|
vec4 input0 = Texel(texture, texcoord);
|
||||||
|
input0 *= (exp2(input0)*vec4(exposure));
|
||||||
|
vec4 lumacomponents = vec4(lumcoeff * lumacomponents, 0.0 );
|
||||||
|
float luminance = dot(input0,lumacomponents);
|
||||||
|
vec4 luma = vec4(luminance);
|
||||||
|
return vec4(luma.rgb * brightness, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
extern float steps = 2.0;
|
||||||
|
|
||||||
|
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) {
|
||||||
|
vec2 pSize = vec2(1.0 / love_ScreenSize.x, 1.0 / love_ScreenSize.y);
|
||||||
|
vec4 col = Texel(texture, texture_coords);
|
||||||
|
for(int i = 1; i <= steps; i++) {
|
||||||
|
col = col + Texel(texture, vec2(texture_coords.x, texture_coords.y - pSize.y * i));
|
||||||
|
col = col + Texel(texture, vec2(texture_coords.x, texture_coords.y + pSize.y * i));
|
||||||
|
}
|
||||||
|
col = col / (steps * 2.0 + 1.0);
|
||||||
|
return vec4(col.r, col.g, col.b, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
extern float steps = 2.0;
|
||||||
|
|
||||||
|
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) {
|
||||||
|
vec2 pSize = vec2(1.0 / love_ScreenSize.x, 1.0 / love_ScreenSize.y);
|
||||||
|
vec4 col = Texel(texture, texture_coords);
|
||||||
|
for(int i = 1; i <= steps; i++) {
|
||||||
|
col = col + Texel(texture, vec2(texture_coords.x - pSize.x * i, texture_coords.y));
|
||||||
|
col = col + Texel(texture, vec2(texture_coords.x + pSize.x * i, texture_coords.y));
|
||||||
|
}
|
||||||
|
col = col / (steps * 2.0 + 1.0);
|
||||||
|
return vec4(col.r, col.g, col.b, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
extern vec2 redStrength = vec2(4.0, 3.0);
|
||||||
|
extern vec2 greenStrength = vec2(-2.0, -1.0);
|
||||||
|
extern vec2 blueStrength = vec2(1.0, -3.0);
|
||||||
|
|
||||||
|
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) {
|
||||||
|
vec2 pSize = vec2(1.0 / love_ScreenSize.x, 1.0 / love_ScreenSize.y);
|
||||||
|
float colRed = Texel(texture, vec2(texture_coords.x + pSize.x * redStrength.x, texture_coords.y - pSize.y * redStrength.y)).r;
|
||||||
|
float colGreen = Texel(texture, vec2(texture_coords.x + pSize.x * greenStrength.x, texture_coords.y - pSize.y * greenStrength.y)).g;
|
||||||
|
float colBlue = Texel(texture, vec2(texture_coords.x + pSize.x * blueStrength.x, texture_coords.y - pSize.y * blueStrength.y)).b;
|
||||||
|
|
||||||
|
return vec4(colRed, colGreen, colBlue, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) {
|
||||||
|
vec3 col = Texel(texture, texture_coords).rgb * 2.0;
|
||||||
|
col *= col;
|
||||||
|
return vec4(col, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#define distortion 0.2
|
||||||
|
vec2 radialDistortion(vec2 coord) {
|
||||||
|
vec2 cc = coord - 0.5;
|
||||||
|
float dist = dot(cc, cc) * distortion;
|
||||||
|
return coord + cc * (1.0 + dist) * dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 checkTexelBounds(Image texture, vec2 coords) {
|
||||||
|
vec2 ss = step(coords, vec2(1.0, 1.0)) * step(vec2(0.0, 0.0), coords);
|
||||||
|
return Texel(texture, coords) * ss.x * ss.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) {
|
||||||
|
vec2 coords = radialDistortion(texture_coords);
|
||||||
|
vec4 texcolor = checkTexelBounds(texture, coords);
|
||||||
|
texcolor.a = 1.0;
|
||||||
|
return texcolor;
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
Edge shader
|
||||||
|
Author: Themaister
|
||||||
|
License: Public domain.
|
||||||
|
|
||||||
|
modified by slime73 for use with love2d and mari0
|
||||||
|
*/
|
||||||
|
|
||||||
|
vec3 grayscale(vec3 color)
|
||||||
|
{
|
||||||
|
return vec3(dot(color, vec3(0.3, 0.59, 0.11)));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 effect(vec4 vcolor, Image texture, vec2 tex, vec2 pixel_coords)
|
||||||
|
{
|
||||||
|
vec4 texcolor = Texel(texture, tex);
|
||||||
|
|
||||||
|
float x = 0.5 / love_ScreenSize.x;
|
||||||
|
float y = 0.5 / love_ScreenSize.y;
|
||||||
|
vec2 dg1 = vec2( x, y);
|
||||||
|
vec2 dg2 = vec2(-x, y);
|
||||||
|
|
||||||
|
vec3 c00 = Texel(texture, tex - dg1).xyz;
|
||||||
|
vec3 c02 = Texel(texture, tex + dg2).xyz;
|
||||||
|
vec3 c11 = texcolor.xyz;
|
||||||
|
vec3 c20 = Texel(texture, tex - dg2).xyz;
|
||||||
|
vec3 c22 = Texel(texture, tex + dg1).xyz;
|
||||||
|
|
||||||
|
vec2 texsize = love_ScreenSize.xy;
|
||||||
|
|
||||||
|
vec3 first = mix(c00, c20, fract(tex.x * texsize.x + 0.5));
|
||||||
|
vec3 second = mix(c02, c22, fract(tex.x * texsize.x + 0.5));
|
||||||
|
|
||||||
|
vec3 res = mix(first, second, fract(tex.y * texsize.y + 0.5));
|
||||||
|
vec4 final = vec4(5.0 * grayscale(abs(res - c11)), 1.0);
|
||||||
|
return clamp(final, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
extern vec3 palette[4];
|
||||||
|
|
||||||
|
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords){
|
||||||
|
vec4 pixel = Texel(texture, texture_coords);
|
||||||
|
int index = int(min(0.9999, max(0.0001,(pixel.r + pixel.g + pixel.b) / 3.0)) * 4);
|
||||||
|
|
||||||
|
return vec4(palette[index], 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords) {
|
||||||
|
vec4 rgb = Texel(texture, texture_coords);
|
||||||
|
vec4 intens = smoothstep(0.2,0.8,rgb) + normalize(vec4(rgb.xyz, 1.0));
|
||||||
|
|
||||||
|
if (fract(pixel_coords.y * 0.5) > 0.5) intens = rgb * 0.8;
|
||||||
|
intens.a = 1.0;
|
||||||
|
return intens;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
extern float time = 0.0;
|
||||||
|
extern vec3 tint = vec3(1.0, 1.0, 1.0);
|
||||||
|
extern float fudge = 0.1;
|
||||||
|
|
||||||
|
float rand(vec2 position, float seed) {
|
||||||
|
return fract(sin(dot(position.xy,vec2(12.9898, 78.233))) * seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords){
|
||||||
|
vec4 pixel = Texel(texture, texture_coords);
|
||||||
|
float intensity = (pixel.r + pixel.g + pixel.b) / 3.0 + (rand(texture_coords, time) - 0.5) * fudge;
|
||||||
|
return vec4(intensity * tint.r, intensity * tint.g, intensity * tint.b, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
caligari's scanlines
|
||||||
|
|
||||||
|
Copyright (C) 2011 caligari
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
(caligari gave their consent to have this shader distributed under the GPL
|
||||||
|
in this message:
|
||||||
|
|
||||||
|
http://board.byuu.org/viewtopic.php?p=36219#p36219
|
||||||
|
|
||||||
|
"As I said to Hyllian by PM, I'm fine with the GPL (not really a bi
|
||||||
|
deal...)"
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// 0.5 = the spot stays inside the original pixel
|
||||||
|
// 1.0 = the spot bleeds up to the center of next pixel
|
||||||
|
#define PHOSPHOR_WIDTH 0.9
|
||||||
|
#define PHOSPHOR_HEIGHT 0.65
|
||||||
|
|
||||||
|
// Used to counteract the desaturation effect of weighting.
|
||||||
|
#define COLOR_BOOST 1.9
|
||||||
|
|
||||||
|
// Constants used with gamma correction.
|
||||||
|
#define InputGamma 2.4
|
||||||
|
#define OutputGamma 2.2
|
||||||
|
|
||||||
|
// Uncomment to only draw every third pixel, which highlights the shape
|
||||||
|
// of individual (remaining) spots.
|
||||||
|
// #define DEBUG
|
||||||
|
|
||||||
|
// Uncomment one of these to choose a gamma correction method.
|
||||||
|
// If none are uncommented, no gamma correction is done.
|
||||||
|
// #define REAL_GAMMA
|
||||||
|
#define FAKE_GAMMA
|
||||||
|
// #define FAKER_GAMMA
|
||||||
|
|
||||||
|
#ifdef REAL_GAMMA
|
||||||
|
#define GAMMA_IN(color) pow(color, vec4(InputGamma))
|
||||||
|
#define GAMMA_OUT(color) pow(color, vec4(1.0 / OutputGamma))
|
||||||
|
|
||||||
|
#elif defined FAKE_GAMMA
|
||||||
|
/*
|
||||||
|
* Approximations:
|
||||||
|
* for 1<g<2 : x^g ~ ax + bx^2
|
||||||
|
* where a=6/(g+1)-2 and b=1-a
|
||||||
|
* for 2<g<3 : x^g ~ ax^2 + bx^3
|
||||||
|
* where a=12/(g+1)-3 and b=1-a
|
||||||
|
* for 1<g<2 : x^(1/g) ~ (sqrt(a^2+4bx)-a)
|
||||||
|
* where a=6/(g+1)-2 and b=1-a
|
||||||
|
* for 2<g<3 : x^(1/g) ~ (a sqrt(x) + b sqrt(sqrt(x)))
|
||||||
|
* where a = 6 - 15g / 2(g+1) and b = 1-a
|
||||||
|
*/
|
||||||
|
vec4 A_IN = vec4( 12.0/(InputGamma+1.0)-3.0 );
|
||||||
|
vec4 B_IN = vec4(1.0) - A_IN;
|
||||||
|
vec4 A_OUT = vec4(6.0 - 15.0 * OutputGamma / 2.0 / (OutputGamma+1.0));
|
||||||
|
vec4 B_OUT = vec4(1.0) - A_OUT;
|
||||||
|
#define GAMMA_IN(color) ( (A_IN + B_IN * color) * color * color )
|
||||||
|
#define GAMMA_OUT(color) ( A_OUT * sqrt(color) + B_OUT * sqrt( sqrt(color) ) )
|
||||||
|
|
||||||
|
#elif defined FAKER_GAMMA
|
||||||
|
vec4 A_IN = vec4(6.0/( InputGamma/OutputGamma + 1.0 ) - 2.0);
|
||||||
|
vec4 B_IN = vec4(1.0) - A_IN;
|
||||||
|
#define GAMMA_IN(color) ( (A_IN + B_IN * color) * color )
|
||||||
|
#define GAMMA_OUT(color) color
|
||||||
|
|
||||||
|
#else // No gamma correction
|
||||||
|
#define GAMMA_IN(color) color
|
||||||
|
#define GAMMA_OUT(color) color
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
vec4 grid_color( vec2 coords )
|
||||||
|
{
|
||||||
|
vec2 snes = floor( coords * love_ScreenSize );
|
||||||
|
if ( (mod(snes.x, 3.0) == 0.0) && (mod(snes.y, 3.0) == 0.0) )
|
||||||
|
return texture2D(_tex0_, coords);
|
||||||
|
else
|
||||||
|
return vec4(0.0);
|
||||||
|
}
|
||||||
|
#define TEX2D(coords) GAMMA_IN( grid_color( coords ) )
|
||||||
|
|
||||||
|
#else // DEBUG
|
||||||
|
#define TEX2D(coords) GAMMA_IN( texture2D(_tex0_, coords) )
|
||||||
|
|
||||||
|
#endif // DEBUG
|
||||||
|
|
||||||
|
vec2 onex = vec2( 1.0/love_ScreenSize.x, 0.0 );
|
||||||
|
vec2 oney = vec2( 0.0, 1.0/love_ScreenSize.y );
|
||||||
|
|
||||||
|
vec4 effect(vec4 vcolor, Image texture, vec2 texCoord, vec2 pixel_coords)
|
||||||
|
{
|
||||||
|
vec2 coords = (texCoord * love_ScreenSize.xy);
|
||||||
|
vec2 pixel_start = floor(coords);
|
||||||
|
coords -= pixel_start;
|
||||||
|
vec2 pixel_center = pixel_start + vec2(0.5);
|
||||||
|
vec2 texture_coords = pixel_center / love_ScreenSize.xy;
|
||||||
|
|
||||||
|
vec4 color = vec4(0.0);
|
||||||
|
vec4 pixel;
|
||||||
|
vec3 centers = vec3(-0.25,-0.5,-0.75);
|
||||||
|
vec3 posx = vec3(coords.x);
|
||||||
|
vec3 hweight;
|
||||||
|
float vweight;
|
||||||
|
float dx,dy;
|
||||||
|
float w;
|
||||||
|
|
||||||
|
float i,j;
|
||||||
|
|
||||||
|
for (j = -1.0; j<=1.0; j++) {
|
||||||
|
// Vertical weight
|
||||||
|
dy = abs(coords.y - 0.5 - j );
|
||||||
|
vweight = smoothstep(1.0,0.0, dy / PHOSPHOR_HEIGHT);
|
||||||
|
|
||||||
|
if (vweight !=0.0 ) {
|
||||||
|
for ( i = -1.0; i<=1.0; i++ ) {
|
||||||
|
pixel = TEX2D(
|
||||||
|
texture_coords
|
||||||
|
+ i * onex
|
||||||
|
+ j * oney
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Evaluate the distance (in x) from
|
||||||
|
* the pixel (posx) to the RGB centers
|
||||||
|
* (~centers):
|
||||||
|
* x_red = 0.25
|
||||||
|
* x_green = 0.5
|
||||||
|
* x_blue = 0.75
|
||||||
|
* if the distance > PHOSPHOR_WIDTH,
|
||||||
|
* this pixel doesn't contribute
|
||||||
|
* otherwise, smoothstep gives the
|
||||||
|
* weight of the contribution
|
||||||
|
*/
|
||||||
|
hweight = smoothstep(
|
||||||
|
1.0, 0.0,
|
||||||
|
abs((posx + centers - vec3(i))
|
||||||
|
/ vec3(PHOSPHOR_WIDTH))
|
||||||
|
);
|
||||||
|
color.rgb +=
|
||||||
|
pixel.rgb *
|
||||||
|
hweight *
|
||||||
|
vec3(vweight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
color *= vec4(COLOR_BOOST);
|
||||||
|
color.a = 1.0;
|
||||||
|
|
||||||
|
return clamp(GAMMA_OUT(color), 0.0, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
Plain (and obviously inaccurate) phosphor.
|
||||||
|
Author: Themaister
|
||||||
|
License: Public Domain
|
||||||
|
*/
|
||||||
|
// modified by slime73 for use with love pixeleffects
|
||||||
|
|
||||||
|
vec3 to_focus(float pixel)
|
||||||
|
{
|
||||||
|
pixel = mod(pixel + 3.0, 3.0);
|
||||||
|
if (pixel >= 2.0) // Blue
|
||||||
|
return vec3(pixel - 2.0, 0.0, 3.0 - pixel);
|
||||||
|
else if (pixel >= 1.0) // Green
|
||||||
|
return vec3(0.0, 2.0 - pixel, pixel - 1.0);
|
||||||
|
else // Red
|
||||||
|
return vec3(1.0 - pixel, pixel, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 effect(vec4 vcolor, Image texture, vec2 texture_coords, vec2 pixel_coords)
|
||||||
|
{
|
||||||
|
float y = mod(texture_coords.y * love_ScreenSize.y, 1.0);
|
||||||
|
float intensity = exp(-0.2 * y);
|
||||||
|
|
||||||
|
vec2 one_x = vec2(1.0 / (3.0 * love_ScreenSize.x), 0.0);
|
||||||
|
|
||||||
|
vec3 color = Texel(texture, texture_coords - 0.0 * one_x).rgb;
|
||||||
|
vec3 color_prev = Texel(texture, texture_coords - 1.0 * one_x).rgb;
|
||||||
|
vec3 color_prev_prev = Texel(texture, texture_coords - 2.0 * one_x).rgb;
|
||||||
|
|
||||||
|
float pixel_x = 3.0 * texture_coords.x * love_ScreenSize.x;
|
||||||
|
|
||||||
|
vec3 focus = to_focus(pixel_x - 0.0);
|
||||||
|
vec3 focus_prev = to_focus(pixel_x - 1.0);
|
||||||
|
vec3 focus_prev_prev = to_focus(pixel_x - 2.0);
|
||||||
|
|
||||||
|
vec3 result =
|
||||||
|
0.8 * color * focus +
|
||||||
|
0.6 * color_prev * focus_prev +
|
||||||
|
0.3 * color_prev_prev * focus_prev_prev;
|
||||||
|
|
||||||
|
result = 2.3 * pow(result, vec3(1.4));
|
||||||
|
|
||||||
|
return vec4(intensity * result, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#define glarebasesize 0.896
|
||||||
|
#define power 0.50
|
||||||
|
extern float time;
|
||||||
|
|
||||||
|
const vec3 green = vec3(0.17, 0.62, 0.25);
|
||||||
|
|
||||||
|
float luminance(vec3 color)
|
||||||
|
{
|
||||||
|
return (0.212671 * color.r) + (0.715160 * color.g) + (0.072169 * color.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
float scanline(float ypos)
|
||||||
|
{
|
||||||
|
|
||||||
|
float c = mod(time * 3.0 + ypos * 5.0, 15.0);
|
||||||
|
return 1.0 - smoothstep(0.0, 1.0, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 effect(vec4 vcolor, Image texture, vec2 texcoord, vec2 pixel_coords)
|
||||||
|
{
|
||||||
|
vec4 texcolor = Texel(texture, texcoord);
|
||||||
|
|
||||||
|
vec4 sum = vec4(0.0);
|
||||||
|
vec4 bum = vec4(0.0);
|
||||||
|
|
||||||
|
vec2 glaresize = vec2(glarebasesize) / love_ScreenSize.xy;
|
||||||
|
|
||||||
|
float y_one = 1.0 / love_ScreenSize.y;
|
||||||
|
|
||||||
|
int j;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = -2; i < 2; i++)
|
||||||
|
{
|
||||||
|
for (j = -1; j < 1; j++)
|
||||||
|
{
|
||||||
|
sum += Texel(texture, texcoord + vec2(-i, j)*glaresize) * power;
|
||||||
|
bum += Texel(texture, texcoord + vec2(j, i)*glaresize) * power;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float a = (scanline(texcoord.y) + scanline(texcoord.y + y_one * 1.5) + scanline(texcoord.y - y_one * 1.5)) / 3.0;
|
||||||
|
|
||||||
|
vec4 finalcolor;
|
||||||
|
|
||||||
|
if (texcolor.r < 2.0)
|
||||||
|
{
|
||||||
|
finalcolor = sum*sum*sum*0.001+bum*bum*bum*0.0080 * (0.8 + 0.05 * a) + texcolor;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
finalcolor = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
float lum = pow(luminance(finalcolor.rgb), 1.4);
|
||||||
|
|
||||||
|
finalcolor.rgb = lum * green + (a * 0.03);
|
||||||
|
finalcolor.a = 1.0;
|
||||||
|
|
||||||
|
return finalcolor;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
const float pixel_w = 2.0;
|
||||||
|
const float pixel_h = 2.0;
|
||||||
|
|
||||||
|
vec4 effect(vec4 vcolor, Image texture, vec2 uv, vec2 pixel_coords)
|
||||||
|
{
|
||||||
|
float dx = pixel_w*(1.0/love_ScreenSize.x);
|
||||||
|
float dy = pixel_h*(1.0/love_ScreenSize.y);
|
||||||
|
vec2 coord = vec2(dx*floor(uv.x/dx), dy*floor(uv.y/dy));
|
||||||
|
return Texel(texture, coord);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#define nsamples 5
|
||||||
|
extern number blurstart = 1.0; // 0 to 1
|
||||||
|
extern number blurwidth = -0.02; // -1 to 1
|
||||||
|
|
||||||
|
|
||||||
|
vec4 effect(vec4 vcolor, Image texture, vec2 texture_coords, vec2 pixel_coords)
|
||||||
|
{
|
||||||
|
vec4 c = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < nsamples; i++)
|
||||||
|
{
|
||||||
|
number scale = blurstart + blurwidth * (i / float(nsamples-1));
|
||||||
|
c.rgb += Texel(texture, texture_coords * scale).rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
c.rgb /= nsamples;
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||