1
0

add bolt projections

This commit is contained in:
Rokas Puzonas 2022-08-05 22:16:25 +00:00
parent afeb219786
commit 088156f880
7 changed files with 172 additions and 27 deletions

View File

@ -1,7 +1,8 @@
{
"$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json",
"Lua.diagnostics.disable": [
"param-type-mismatch"
"param-type-mismatch",
"cast-local-type"
],
"Lua.runtime.version": "LuaJIT",
"Lua.workspace.checkThirdParty": false,

View File

@ -404,6 +404,7 @@ end
local function getInfoAboutItemsTouchedBySegment(self, x1,y1, x2,y2, filter)
local cells, len = getCellsTouchedBySegment(self, x1,y1,x2,y2)
local cell, rect, l,t,w,h, ti1,ti2, tii0,tii1
local nx1,ny1
local visited, itemInfo, itemInfoLen = {},{},0
for i=1,len do
cell = cells[i]
@ -414,12 +415,19 @@ local function getInfoAboutItemsTouchedBySegment(self, x1,y1, x2,y2, filter)
rect = self.rects[item]
l,t,w,h = rect.x,rect.y,rect.w,rect.h
ti1,ti2 = rect_getSegmentIntersectionIndices(l,t,w,h, x1,y1, x2,y2, 0, 1)
ti1,ti2,nx1,ny1 = rect_getSegmentIntersectionIndices(l,t,w,h, x1,y1, x2,y2, 0, 1)
if ti1 and ((0 < ti1 and ti1 < 1) or (0 < ti2 and ti2 < 1)) then
-- the sorting is according to the t of an infinite line, not the segment
tii0,tii1 = rect_getSegmentIntersectionIndices(l,t,w,h, x1,y1, x2,y2, -math.huge, math.huge)
itemInfoLen = itemInfoLen + 1
itemInfo[itemInfoLen] = {item = item, ti1 = ti1, ti2 = ti2, weight = min(tii0,tii1)}
itemInfo[itemInfoLen] = {
item = item,
ti1 = ti1,
ti2 = ti2,
nx = nx1,
ny = ny1,
weight = min(tii0,tii1)
}
end
end
end

View File

@ -22,6 +22,7 @@ function MainState:enter(_, host_socket)
require("systems.map"),
require("systems.sprite"),
require("systems.player"),
require("systems.bolt"),
require("systems.screen-scaler")
}

90
src/systems/bolt.lua Normal file
View File

@ -0,0 +1,90 @@
local Bolt = {}
local Vec = require("lib.brinevector")
-- BUG: trajectory projection doesn't take into account the bolt collider
local BOLT_COLLIDER = {-3, -3, 3, 3}
function Bolt:update(dt)
for _, bolt in ipairs(self.pool.groups.bolt.entities) do
if bolt.vel.length < 20 and not bolt.pickupable then
bolt.pickupable = true
bolt.sprite.variant = "idle"
bolt.vel.x = 0
bolt.vel.y = 0
end
end
end
function Bolt:projectTrajectory(pos, step, count)
local physics = self.pool:getSystem(require("systems.physics"))
local step_size = step.length
local distance = step_size * (count-1)
local key_points = physics:project(pos, step.normalized, distance)
local trajectory = {}
table.insert(trajectory, pos)
local current_segment = 1
local current_distance = -step_size
local segment_direction, segment_size
local last_point = key_points[1] - step
do
local diff = key_points[2] - key_points[1]
segment_size = diff.length
segment_direction = diff.normalized
end
-- For every point that we want to place along our projected line
for _=1, count do
-- First check if the point about to be placed would be outside current segment
while current_distance+step_size > segment_size do
-- Check if there are any unused segments left
if current_segment < (#key_points-1) then
-- Switch next segment
current_distance = current_distance - segment_size
current_segment = current_segment + 1
local diff = key_points[current_segment+1] - key_points[current_segment]
segment_size = diff.length
segment_direction = diff.normalized
-- Adjust where the next point is going to be placed, so it's correct
local to_key_point = (key_points[current_segment]-last_point).length
last_point = key_points[current_segment] - segment_direction * to_key_point
-- If there are not segments left, just treat the last segment as infinite
else
segment_size = math.huge
end
end
-- Place point along segment
local point = last_point + segment_direction * step_size
table.insert(trajectory, point)
current_distance = current_distance + step_size
last_point = point
end
return trajectory
end
function Bolt:boltShot(player, bolt)
bolt.pickupable = nil
bolt.collider = BOLT_COLLIDER
bolt.bolt = true
bolt.sprite.variant = "active"
bolt.hidden = nil
self.pool:queue(bolt)
end
function Bolt:storeBolt(player, bolt)
bolt.pickupable = nil
bolt.collider = nil
bolt.bolt = nil
bolt.sprite.variant = "idle"
bolt.hidden = true
self.pool:queue(bolt)
end
return Bolt

View File

@ -116,7 +116,7 @@ function Multiplayer:handlePeerMessage(peer, cmd, ...)
local player = self:getPlayerEntityById(id)
if player and player.peer_index == peer:index() then
self.pool:emit("tryShootinBolt", player)
self.pool:emit("tryShootingBolt", player)
end
else
print("DEBUG INFORMATION:")

View File

@ -1,5 +1,6 @@
local Physics = {}
local bump = require("lib.bump")
local pprint = require("lib.pprint")
local Vec = require("lib.brinevector")
-- TODO: Tweak bump world `cellSize` at runtime, when the map switches
@ -7,7 +8,7 @@ local Vec = require("lib.brinevector")
function Physics:init()
self.bump = bump.newWorld()
self.boundCollisionFilter = function(...)
return self:_collisionFilter(...)
return self:collisionFilter(...)
end
end
@ -35,7 +36,7 @@ function Physics:removeFromGroup(group, e)
end
end
function Physics:_collisionFilter(entity, other)
function Physics:collisionFilter(entity, other)
if entity.hidden then return end
if other.hidden then return end
@ -104,4 +105,49 @@ function Physics:update(dt)
end
end
function Physics:castRay(from, to)
local items = self.bump:querySegmentWithCoords(from.x, from.y, to.x, to.y, function(item)
return not item.vel
end)
for _, item in ipairs(items) do
if item.nx ~= 0 or item.ny ~= 0 then
local pos = Vec(item.x1, item.y1)
local normal = Vec(item.nx, item.ny)
return pos, normal
end
end
end
function Physics:project(from, direction, distance)
local key_points = {}
table.insert(key_points, from)
local distance_traveled = 0
local position = from
-- While the ray hasen't traveled the required amount
while math.abs(distance_traveled - distance) > 0.01 do
-- Check if it collides with anything
local destination = position + direction * (distance-distance_traveled)
local intersect, normal = self:castRay(position, destination)
local new_position = intersect or destination
-- Update how much the ray has moved in total
distance_traveled = distance_traveled + (position - new_position).length
position = new_position
-- If it collided change its travel direction
if normal then
-- sx and sy and just number for flipped the direction when something
-- bounces.
-- When `normal.x` is zero, sx = 1, otherwise sx = -1. Same with sy
direction.x = direction.x * (1 - 2*math.abs(normal.x))
direction.y = direction.y * (1 - 2*math.abs(normal.y))
end
-- Save this point where it stopped/collided
table.insert(key_points, new_position)
end
return key_points
end
return Physics

View File

@ -4,7 +4,7 @@ local Vec = require("lib.brinevector")
local controls = data.controls
local BOLT_COLLIDER = {-3, -3, 3, 3}
local AIMED_BOLT_DISTANCE = 15
local function getDirection(up_key, down_key, left_key, right_key)
local dx = (love.keyboard.isDown(right_key) and 1 or 0)
@ -97,12 +97,7 @@ function Player:update(dt)
-- If the player is nearby a non-moving bolt, pick it up
for _, bolt in ipairs(self.pool.groups.bolt.entities) do
if (e.pos-bolt.pos).length < 20 and bolt.vel.length < 3 then
bolt.collider = nil
bolt.bolt = nil
bolt.sprite.variant = "idle"
bolt.hidden = true
self.pool:queue(bolt)
table.insert(e.bolts, bolt)
self.pool:emit("storeBolt", e, bolt)
end
end
@ -122,7 +117,7 @@ function Player:update(dt)
local bolt = e.bolts[1]
if e.aim_dir.x ~= 0 or e.aim_dir.y ~= 0 then
bolt.hidden = nil
bolt.pos = e.pos + e.aim_dir * 15
bolt.pos = e.pos + e.aim_dir * AIMED_BOLT_DISTANCE
else
bolt.hidden = true
end
@ -140,7 +135,7 @@ function Player:tryShootingBolt(player)
#player.bolts > 0
then
player.bolt_cooldown_timer = player.bolt_cooldown
self:shootBolt(player)
self.pool:emit("shootBolt", player)
return true
end
end
@ -149,21 +144,12 @@ function Player:shootBolt(player)
local bolt = table.remove(player.bolts, 1)
if not bolt then return end
bolt.pos = player.pos + player.aim_dir * 30
bolt.pos = player.pos + player.aim_dir * AIMED_BOLT_DISTANCE
bolt.vel = player.aim_dir * player.bolt_speed
bolt.bolt = true
bolt.sprite.variant = "active"
bolt.collider = BOLT_COLLIDER
bolt.hidden = nil
self.pool:queue(bolt)
self.pool:emit("boltShot", player, bolt)
end
function Player:storeBolt(player, bolt)
bolt.collider = nil
bolt.sprite.variant = "idle"
bolt.hidden = true
bolt.bolt = nil
self.pool:queue(bolt)
table.insert(player.bolts, bolt)
end
@ -197,10 +183,23 @@ function Player:spawnPlayer()
friction = 0.98,
max_speed = 400,
}
self:storeBolt(player, bolt)
self.pool:emit("storeBolt", player, bolt)
end
return player
end
function Player:draw()
for _, e in ipairs(self.pool.groups.controllable_player.entities) do
if e.aim_dir.x ~= 0 or e.aim_dir.y ~= 0 then
local boltSystem = self.pool:getSystem(require("systems.bolt"))
local pos = e.pos + e.aim_dir * AIMED_BOLT_DISTANCE*0
local vel = (e.aim_dir * e.bolt_speed).normalized * AIMED_BOLT_DISTANCE*1
for _, position in ipairs(boltSystem:projectTrajectory(pos, vel, 5)) do
love.graphics.circle("line", position.x, position.y, 5)
end
end
end
end
return Player