11_111/main.lua

local obj = require "obj"
local text = require "text"

obj.load_types()
line = love.graphics.line
function box(x, y, w, h) line(x, y, x + w, y, x + w, y + h, x, y + h, x, y) end
set_color = love.graphics.setColor

local line_width = 0.4
local cam = {
	x = 0, y = 0,
	scale = 1.1,
	panning = false,
}
local drag_start
local dragging
local selecting
local selection

for i = 1, 100 do
	obj.new("test", {math.random() * 1000 - 500, math.random() * 1000 - 500}, {
		avel = math.random() * 0.5 - 0.25,
		vel = {math.random() * 1 - 0.5, math.random() * 1 - 0.5}
	})
end

obj.new("test", {0, 0})
obj.new("x", {0, 60}, {avel = 100})

local function window_scale()
	local w, h = love.graphics.getDimensions()
	return 256 / math.min(w, h)
end

local function view_scale()
	return window_scale() / cam.scale
end

local function view_dimensions()
	local w, h = love.graphics.getDimensions()
	local scale = view_scale()
	return w * scale, h * scale
end

local function window_transform()
	local scale = window_scale()
	local trans = love.math.newTransform(0, 0, 0, 1/scale, 1/scale)
	return trans
end

local function view_transform()
	local w, h = view_dimensions()
	local trans = window_transform()
	trans:scale(cam.scale, cam.scale)
	trans:translate(-cam.x + w/2, -cam.y + h/2)
	return trans
end

local function draw_world()
	local w, h = view_dimensions()
	local cx1, cy1 = cam.x - w/2, cam.y - h/2
	local cx2, cy2 = cam.x + w/2, cam.y + h/2
	love.graphics.push()
	love.graphics.applyTransform(view_transform())
	-- the line thickness will scale according to the zoom amount. counteract this.
	love.graphics.setLineWidth(line_width / cam.scale)
	-- draw a grid
	set_color(0.1, 0.1, 0.1)
	for x = cx1 - cx1%64, cx2, 64 do
		for y = cy1 - cy1%64, cy2, 64 do
			line(x, y, x + 64, y)
			line(x, y, x, y + 64)
		end
	end
	-- draw all possibly visible objects
	for o in obj.in_box(
			cx1 - obj.max_size, cy1 - obj.max_size,
			cx2 + obj.max_size, cy2 + obj.max_size) do
		set_color(1, 1, 1)
		o:draw()
	end
	set_color(1, 1, 0.5)
	if selection then
		for o in pairs(selection) do
			local x, y = unpack(o.data.pos)
			box(x - o.hitbox, y - o.hitbox, o.hitbox*2, o.hitbox*2)
		end
	end
	if selecting then
		local x1, y1 = unpack(selecting.p1)
		local x2, y2 = unpack(selecting.p2)
		local w, h = x2 - x1, y2 - y1
		box(x1, y1, w, h)
	end
	love.graphics.pop()
end

local function draw_hud()
	love.graphics.push()
	love.graphics.applyTransform(window_transform())
	love.graphics.setLineWidth(line_width)
	-- things
	set_color(1, 1, 1)
	local total_energy = obj.total_energy()
	text.draw(("e: %f"):format(total_energy), 10, 10, {scale = 0.4})
	love.graphics.pop()
end

function love.draw()
	love.graphics.clear(0, 0, 0)
	draw_world()
	draw_hud()
end

function love.update()
	if not (selection or selecting) then
		for o in obj.all() do
			o:tick()
		end
	end
end

function love.mousepressed(x, y, button)
	local x, y = view_transform():inverseTransformPoint(x, y)
	if button == 1 then
		drag_start = {x, y}
	elseif button == 2 then
		cam.panning = true
	end
end

function love.mousereleased(x, y, button)
	local x, y = view_transform():inverseTransformPoint(x, y)
	if button == 1 then
		if not dragging then
			local clicked = obj.at(x, y)()
			if not love.keyboard.isDown "lshift" then
				if clicked and not (selection and selection[clicked]) then
					selection = {[clicked] = true}
				else
					selection = nil
				end
			elseif clicked then
				selection = selection or {}
				selection[clicked] = true
			end
		end
		dragging = false
		drag_start = nil
		selecting = nil
	elseif button == 2 then
		cam.panning = false
	end
end

function love.mousemoved(x, y, dx, dy)
	local x, y = view_transform():inverseTransformPoint(x, y)
	local scale = view_scale()
	dx, dy = dx * scale, dy * scale
	if drag_start then
		if not dragging then
			local clicked = obj.at(x, y)()
			selecting = not clicked and not love.keyboard.isDown "lshift"
		end
		if selecting then
			local sx, sy = unpack(drag_start)
			selecting = {
				p1 = {math.min(x, sx), math.min(y, sy)},
				p2 = {math.max(x, sx), math.max(y, sy)},
			}
			selection = {}
			for o in obj.in_box(
					selecting.p1[1], selecting.p1[2],
					selecting.p2[1], selecting.p2[2]) do
				selection[o] = true
			end
			if not next(selection) then selection = nil end
		elseif selection then
			for o in pairs(selection) do
				o:set_pos(o.data.pos[1] + dx, o.data.pos[2] + dy)
			end
		end
		dragging = true
	elseif cam.panning then
		cam.x = cam.x - dx
		cam.y = cam.y - dy
	end
end

function love.wheelmoved(_, y)
	cam.scale = math.min(math.max(cam.scale + (y * 0.1), 0.25), 4)
end