add bolt projections
This commit is contained in:
parent
afeb219786
commit
088156f880
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
90
src/systems/bolt.lua
Normal 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
|
||||
@ -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:")
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user