@ -20,11 +20,12 @@ 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 path = ... .. ' . '
local path = ... .. " . "
local wf = { }
wf.Math = require ( path .. ' mlib.mlib ' )
wf.Math = require ( path .. " mlib.mlib " )
World = { }
World.__index = World
@ -32,14 +33,28 @@ World.__index = World
function wf . newWorld ( xg , yg , sleep )
local world = wf.World . new ( wf , xg , yg , sleep )
world.box2d_world : setCallbacks ( world.collisionOnEnter , world.collisionOnExit , world.collisionPre , world.collisionPost )
world.box2d_world : setCallbacks (
world.collisionOnEnter ,
world.collisionOnExit ,
world.collisionPre ,
world.collisionPost
)
world : collisionClear ( )
world : addCollisionClass ( ' Default ' )
world : addCollisionClass ( " Default " )
-- Points all box2d_world functions to this wf.World object
-- This means that the user can call world:setGravity for instance without having to say world.box2d_world:setGravity
for k , v in pairs ( world.box2d_world . __index ) do
if k ~= ' __gc ' and k ~= ' __eq ' and k ~= ' __index ' and k ~= ' __tostring ' and k ~= ' update ' and k ~= ' destroy ' and k ~= ' type ' and k ~= ' typeOf ' then
if
k ~= " __gc "
and k ~= " __eq "
and k ~= " __index "
and k ~= " __tostring "
and k ~= " update "
and k ~= " destroy "
and k ~= " type "
and k ~= " typeOf "
then
world [ k ] = function ( self , ... )
return v ( self.box2d_world , ... )
end
@ -84,18 +99,20 @@ function World:draw(alpha)
for _ , body in ipairs ( bodies ) do
local fixtures = body : getFixtures ( )
for _ , fixture in ipairs ( fixtures ) do
if fixture : getShape ( ) : type ( ) == ' PolygonShape ' then
love.graphics . polygon ( ' line ' , body : getWorldPoints ( fixture : getShape ( ) : getPoints ( ) ) )
elseif fixture : getShape ( ) : type ( ) == ' EdgeShape ' or fixture : getShape ( ) : type ( ) == ' ChainShape ' then
if fixture : getShape ( ) : type ( ) == " PolygonShape " then
love.graphics . polygon ( " line " , body : getWorldPoints ( fixture : getShape ( ) : getPoints ( ) ) )
elseif fixture : getShape ( ) : type ( ) == " EdgeShape " or fixture : getShape ( ) : type ( ) == " ChainShape " then
local points = { body : getWorldPoints ( fixture : getShape ( ) : getPoints ( ) ) }
for i = 1 , # points , 2 do
if i < # points - 2 then love.graphics . line ( points [ i ] , points [ i + 1 ] , points [ i + 2 ] , points [ i + 3 ] ) end
if i < # points - 2 then
love.graphics . line ( points [ i ] , points [ i + 1 ] , points [ i + 2 ] , points [ i + 3 ] )
end
elseif fixture : getShape ( ) : type ( ) == ' CircleShape ' then
end
elseif fixture : getShape ( ) : type ( ) == " CircleShape " then
local body_x , body_y = body : getPosition ( )
local shape_x , shape_y = fixture : getShape ( ) : getPoint ( )
local r = fixture : getShape ( ) : getRadius ( )
love.graphics . circle ( ' line ' , body_x + shape_x , body_y + shape_y , r , 360 )
love.graphics . circle ( " line " , body_x + shape_x , body_y + shape_y , r , 360 )
end
end
end
@ -106,8 +123,12 @@ function World:draw(alpha)
local joints = self.box2d_world : getJoints ( )
for _ , joint in ipairs ( joints ) do
local x1 , y1 , x2 , y2 = joint : getAnchors ( )
if x1 and y1 then love.graphics . circle ( ' line ' , x1 , y1 , 4 ) end
if x2 and y2 then love.graphics . circle ( ' line ' , x2 , y2 , 4 ) end
if x1 and y1 then
love.graphics . circle ( " line " , x1 , y1 , 4 )
end
if x2 and y2 then
love.graphics . circle ( " line " , x2 , y2 , 4 )
end
end
love.graphics . setColor ( 255 , 255 , 255 , alpha )
@ -115,15 +136,17 @@ function World:draw(alpha)
love.graphics . setColor ( 64 , 64 , 222 , alpha )
for _ , query_draw in ipairs ( self.query_debug_draw ) do
query_draw.frames = query_draw.frames - 1
if query_draw.type == ' circle ' then
love.graphics . circle ( ' line ' , query_draw.x , query_draw.y , query_draw.r )
elseif query_draw.type == ' rectangle ' then
love.graphics . rectangle ( ' line ' , query_draw.x , query_draw.y , query_draw.w , query_draw.h )
elseif query_draw.type == ' line ' then
if query_draw.type == " circle " then
love.graphics . circle ( " line " , query_draw.x , query_draw.y , query_draw.r )
elseif query_draw.type == " rectangle " then
love.graphics . rectangle ( " line " , query_draw.x , query_draw.y , query_draw.w , query_draw.h )
elseif query_draw.type == " line " then
love.graphics . line ( query_draw.x1 , query_draw.y1 , query_draw.x2 , query_draw.y2 )
elseif query_draw.type == ' polygon ' then
elseif query_draw.type == " polygon " then
local triangles = love.math . triangulate ( query_draw.vertices )
for _ , triangle in ipairs ( triangles ) do love.graphics . polygon ( ' line ' , triangle ) end
for _ , triangle in ipairs ( triangles ) do
love.graphics . polygon ( " line " , triangle )
end
end
end
for i = # self.query_debug_draw , 1 , - 1 do
@ -143,7 +166,9 @@ function World:setExplicitCollisionEvents(value)
end
function World : addCollisionClass ( collision_class_name , collision_class )
if self.collision_classes [ collision_class_name ] then error ( ' Collision class ' .. collision_class_name .. ' already exists. ' ) end
if self.collision_classes [ collision_class_name ] then
error ( " Collision class " .. collision_class_name .. " already exists. " )
end
if self.explicit_collision_events then
self.collision_classes [ collision_class_name ] = collision_class or { }
@ -177,10 +202,18 @@ function World:collisionClassesSet()
local collision_table = self : getCollisionCallbacksTable ( )
for collision_class_name , collision_list in pairs ( collision_table ) do
for _ , collision_info in ipairs ( collision_list ) do
if collision_info.type == ' enter ' then self : addCollisionEnter ( collision_class_name , collision_info.other ) end
if collision_info.type == ' exit ' then self : addCollisionExit ( collision_class_name , collision_info.other ) end
if collision_info.type == ' pre ' then self : addCollisionPre ( collision_class_name , collision_info.other ) end
if collision_info.type == ' post ' then self : addCollisionPost ( collision_class_name , collision_info.other ) end
if collision_info.type == " enter " then
self : addCollisionEnter ( collision_class_name , collision_info.other )
end
if collision_info.type == " exit " then
self : addCollisionExit ( collision_class_name , collision_info.other )
end
if collision_info.type == " pre " then
self : addCollisionPre ( collision_class_name , collision_info.other )
end
if collision_info.type == " post " then
self : addCollisionPost ( collision_class_name , collision_info.other )
end
end
end
@ -214,25 +247,33 @@ end
function World : addCollisionEnter ( type1 , type2 )
if not self : isCollisionBetweenSensors ( type1 , type2 ) then
table.insert ( self.collisions . on_enter.non_sensor , { type1 = type1 , type2 = type2 } )
else table.insert ( self.collisions . on_enter.sensor , { type1 = type1 , type2 = type2 } ) end
else
table.insert ( self.collisions . on_enter.sensor , { type1 = type1 , type2 = type2 } )
end
end
function World : addCollisionExit ( type1 , type2 )
if not self : isCollisionBetweenSensors ( type1 , type2 ) then
table.insert ( self.collisions . on_exit.non_sensor , { type1 = type1 , type2 = type2 } )
else table.insert ( self.collisions . on_exit.sensor , { type1 = type1 , type2 = type2 } ) end
else
table.insert ( self.collisions . on_exit.sensor , { type1 = type1 , type2 = type2 } )
end
end
function World : addCollisionPre ( type1 , type2 )
if not self : isCollisionBetweenSensors ( type1 , type2 ) then
table.insert ( self.collisions . pre.non_sensor , { type1 = type1 , type2 = type2 } )
else table.insert ( self.collisions . pre.sensor , { type1 = type1 , type2 = type2 } ) end
else
table.insert ( self.collisions . pre.sensor , { type1 = type1 , type2 = type2 } )
end
end
function World : addCollisionPost ( type1 , type2 )
if not self : isCollisionBetweenSensors ( type1 , type2 ) then
table.insert ( self.collisions . post.non_sensor , { type1 = type1 , type2 = type2 } )
else table.insert ( self.collisions . post.sensor , { type1 = type1 , type2 = type2 } ) end
else
table.insert ( self.collisions . post.sensor , { type1 = type1 , type2 = type2 } )
end
end
function World : doesType1IgnoreType2 ( type1 , type2 )
@ -246,31 +287,46 @@ function World:doesType1IgnoreType2(type1, type2)
end
local ignored_types = { }
for _ , collision_class_type in ipairs ( collision_ignores [ type1 ] ) do
if collision_class_type == ' All ' then
if collision_class_type == " All " then
for _ , collision_class_name in ipairs ( all ) do
table.insert ( ignored_types , collision_class_name )
end
else table.insert ( ignored_types , collision_class_type ) end
else
table.insert ( ignored_types , collision_class_type )
end
end
for key , _ in pairs ( collision_ignores [ type1 ] ) do
if key == ' except ' then
if key == " except " then
for _ , except_type in ipairs ( collision_ignores [ type1 ] . except ) do
for i = # ignored_types , 1 , - 1 do
if ignored_types [ i ] == except_type then table.remove ( ignored_types , i ) end
if ignored_types [ i ] == except_type then
table.remove ( ignored_types , i )
end
end
end
end
end
for _ , ignored_type in ipairs ( ignored_types ) do
if ignored_type == type2 then return true end
if ignored_type == type2 then
return true
end
end
end
function World : isCollisionBetweenSensors ( type1 , type2 )
if not self.is_sensor_memo [ type1 ] then self.is_sensor_memo [ type1 ] = { } end
if not self.is_sensor_memo [ type1 ] [ type2 ] then self.is_sensor_memo [ type1 ] [ type2 ] = ( self : doesType1IgnoreType2 ( type1 , type2 ) or self : doesType1IgnoreType2 ( type2 , type1 ) ) end
if self.is_sensor_memo [ type1 ] [ type2 ] then return true
else return false end
if not self.is_sensor_memo [ type1 ] then
self.is_sensor_memo [ type1 ] = { }
end
if not self.is_sensor_memo [ type1 ] [ type2 ] then
self.is_sensor_memo [ type1 ] [ type2 ] = (
self : doesType1IgnoreType2 ( type1 , type2 ) or self : doesType1IgnoreType2 ( type2 , type1 )
)
end
if self.is_sensor_memo [ type1 ] [ type2 ] then
return true
else
return false
end
end
-- https://love2d.org/forums/viewtopic.php?f=4&t=75441
@ -289,18 +345,18 @@ function World:generateCategoriesMasks()
end
for object_type , ignore_list in pairs ( collision_ignores ) do
for key , ignored_type in pairs ( ignore_list ) do
if ignored_type == ' All ' then
if ignored_type == " All " then
for _ , all_object_type in ipairs ( all ) do
table.insert ( incoming [ all_object_type ] , object_type )
table.insert ( expanded [ object_type ] , all_object_type )
end
elseif type ( ignored_type ) == ' string ' then
if ignored_type ~= ' All ' then
elseif type ( ignored_type ) == " string " then
if ignored_type ~= " All " then
table.insert ( incoming [ ignored_type ] , object_type )
table.insert ( expanded [ object_type ] , ignored_type )
end
end
if key == ' except ' then
if key == " except " then
for _ , except_ignored_type in ipairs ( ignored_type ) do
for i , v in ipairs ( incoming [ except_ignored_type ] ) do
if v == object_type then
@ -322,7 +378,9 @@ function World:generateCategoriesMasks()
end
local edge_groups = { }
for k , v in pairs ( incoming ) do
table.sort ( v , function ( a , b ) return string.lower ( a ) < string.lower ( b ) end )
table.sort ( v , function ( a , b )
return string.lower ( a ) < string.lower ( b )
end )
end
local i = 0
for k , v in pairs ( incoming ) do
@ -330,7 +388,10 @@ function World:generateCategoriesMasks()
for _ , c in ipairs ( v ) do
str = str .. c
end
if not edge_groups [ str ] then i = i + 1 ; edge_groups [ str ] = { n = i } end
if not edge_groups [ str ] then
i = i + 1
edge_groups [ str ] = { n = i }
end
table.insert ( edge_groups [ str ] , k )
end
local categories = { }
@ -356,24 +417,39 @@ function World:getCollisionCallbacksTable()
local collision_table = { }
for collision_class_name , collision_class in pairs ( self.collision_classes ) do
collision_table [ collision_class_name ] = { }
for _ , v in ipairs ( collision_class.enter or { } ) do table.insert ( collision_table [ collision_class_name ] , { type = ' enter ' , other = v } ) end
for _ , v in ipairs ( collision_class.exit or { } ) do table.insert ( collision_table [ collision_class_name ] , { type = ' exit ' , other = v } ) end
for _ , v in ipairs ( collision_class.pre or { } ) do table.insert ( collision_table [ collision_class_name ] , { type = ' pre ' , other = v } ) end
for _ , v in ipairs ( collision_class.post or { } ) do table.insert ( collision_table [ collision_class_name ] , { type = ' post ' , other = v } ) end
for _ , v in ipairs ( collision_class.enter or { } ) do
table.insert ( collision_table [ collision_class_name ] , { type = " enter " , other = v } )
end
for _ , v in ipairs ( collision_class.exit or { } ) do
table.insert ( collision_table [ collision_class_name ] , { type = " exit " , other = v } )
end
for _ , v in ipairs ( collision_class.pre or { } ) do
table.insert ( collision_table [ collision_class_name ] , { type = " pre " , other = v } )
end
for _ , v in ipairs ( collision_class.post or { } ) do
table.insert ( collision_table [ collision_class_name ] , { type = " post " , other = v } )
end
end
return collision_table
end
local function collEnsure ( collision_class_name1 , a , collision_class_name2 , b )
if a.collision_class == collision_class_name2 and b.collision_class == collision_class_name1 then return b , a
else return a , b end
if a.collision_class == collision_class_name2 and b.collision_class == collision_class_name1 then
return b , a
else
return a , b
end
end
local function collIf ( collision_class_name1 , collision_class_name2 , a , b )
if ( a.collision_class == collision_class_name1 and b.collision_class == collision_class_name2 ) or
( a.collision_class == collision_class_name2 and b.collision_class == collision_class_name1 ) then
if
( a.collision_class == collision_class_name1 and b.collision_class == collision_class_name2 )
or ( a.collision_class == collision_class_name2 and b.collision_class == collision_class_name1 )
then
return true
else return false end
else
return false
end
end
function World . collisionOnEnter ( fixture_a , fixture_b , contact )
@ -384,22 +460,33 @@ function World.collisionOnEnter(fixture_a, fixture_b, contact)
for _ , collision in ipairs ( a.world . collisions.on_enter . sensor ) do
if collIf ( collision.type1 , collision.type2 , a , b ) then
a , b = collEnsure ( collision.type1 , a , collision.type2 , b )
table.insert ( a.collision_events [ collision.type2 ] , { collision_type = ' enter ' , collider_1 = a , collider_2 = b , contact = contact } )
table.insert (
a.collision_events [ collision.type2 ] ,
{ collision_type = " enter " , collider_1 = a , collider_2 = b , contact = contact }
)
if collision.type1 == collision.type2 then
table.insert ( b.collision_events [ collision.type1 ] , { collision_type = ' enter ' , collider_1 = b , collider_2 = a , contact = contact } )
table.insert (
b.collision_events [ collision.type1 ] ,
{ collision_type = " enter " , collider_1 = b , collider_2 = a , contact = contact }
)
end
end
end
end
elseif not ( fixture_a : isSensor ( ) or fixture_b : isSensor ( ) ) then
if a and b then
for _ , collision in ipairs ( a.world . collisions.on_enter . non_sensor ) do
if collIf ( collision.type1 , collision.type2 , a , b ) then
a , b = collEnsure ( collision.type1 , a , collision.type2 , b )
table.insert ( a.collision_events [ collision.type2 ] , { collision_type = ' enter ' , collider_1 = a , collider_2 = b , contact = contact } )
table.insert (
a.collision_events [ collision.type2 ] ,
{ collision_type = " enter " , collider_1 = a , collider_2 = b , contact = contact }
)
if collision.type1 == collision.type2 then
table.insert ( b.collision_events [ collision.type1 ] , { collision_type = ' enter ' , collider_1 = b , collider_2 = a , contact = contact } )
table.insert (
b.collision_events [ collision.type1 ] ,
{ collision_type = " enter " , collider_1 = b , collider_2 = a , contact = contact }
)
end
end
end
@ -415,22 +502,33 @@ function World.collisionOnExit(fixture_a, fixture_b, contact)
for _ , collision in ipairs ( a.world . collisions.on_exit . sensor ) do
if collIf ( collision.type1 , collision.type2 , a , b ) then
a , b = collEnsure ( collision.type1 , a , collision.type2 , b )
table.insert ( a.collision_events [ collision.type2 ] , { collision_type = ' exit ' , collider_1 = a , collider_2 = b , contact = contact } )
table.insert (
a.collision_events [ collision.type2 ] ,
{ collision_type = " exit " , collider_1 = a , collider_2 = b , contact = contact }
)
if collision.type1 == collision.type2 then
table.insert ( b.collision_events [ collision.type1 ] , { collision_type = ' exit ' , collider_1 = b , collider_2 = a , contact = contact } )
table.insert (
b.collision_events [ collision.type1 ] ,
{ collision_type = " exit " , collider_1 = b , collider_2 = a , contact = contact }
)
end
end
end
end
elseif not ( fixture_a : isSensor ( ) or fixture_b : isSensor ( ) ) then
if a and b then
for _ , collision in ipairs ( a.world . collisions.on_exit . non_sensor ) do
if collIf ( collision.type1 , collision.type2 , a , b ) then
a , b = collEnsure ( collision.type1 , a , collision.type2 , b )
table.insert ( a.collision_events [ collision.type2 ] , { collision_type = ' exit ' , collider_1 = a , collider_2 = b , contact = contact } )
table.insert (
a.collision_events [ collision.type2 ] ,
{ collision_type = " exit " , collider_1 = a , collider_2 = b , contact = contact }
)
if collision.type1 == collision.type2 then
table.insert ( b.collision_events [ collision.type1 ] , { collision_type = ' exit ' , collider_1 = b , collider_2 = a , contact = contact } )
table.insert (
b.collision_events [ collision.type1 ] ,
{ collision_type = " exit " , collider_1 = b , collider_2 = a , contact = contact }
)
end
end
end
@ -453,7 +551,6 @@ function World.collisionPre(fixture_a, fixture_b, contact)
end
end
end
elseif not ( fixture_a : isSensor ( ) or fixture_b : isSensor ( ) ) then
if a and b then
for _ , collision in ipairs ( a.world . collisions.pre . non_sensor ) do
@ -484,7 +581,6 @@ function World.collisionPost(fixture_a, fixture_b, contact, ni1, ti1, ni2, ti2)
end
end
end
elseif not ( fixture_a : isSensor ( ) or fixture_b : isSensor ( ) ) then
if a and b then
for _ , collision in ipairs ( a.world . collisions.post . non_sensor ) do
@ -501,34 +597,36 @@ function World.collisionPost(fixture_a, fixture_b, contact, ni1, ti1, ni2, ti2)
end
function World : newCircleCollider ( x , y , r , settings )
return self.wf . Collider.new ( self , ' Circle ' , x , y , r , settings )
return self.wf . Collider.new ( self , " Circle " , x , y , r , settings )
end
function World : newRectangleCollider ( x , y , w , h , settings )
return self.wf . Collider.new ( self , ' Rectangle ' , x , y , w , h , settings )
return self.wf . Collider.new ( self , " Rectangle " , x , y , w , h , settings )
end
function World : newBSGRectangleCollider ( x , y , w , h , corner_cut_size , settings )
return self.wf . Collider.new ( self , ' BSGRectangle ' , x , y , w , h , corner_cut_size , settings )
return self.wf . Collider.new ( self , " BSGRectangle " , x , y , w , h , corner_cut_size , settings )
end
function World : newPolygonCollider ( vertices , settings )
return self.wf . Collider.new ( self , ' Polygon ' , vertices , settings )
return self.wf . Collider.new ( self , " Polygon " , vertices , settings )
end
function World : newLineCollider ( x1 , y1 , x2 , y2 , settings )
return self.wf . Collider.new ( self , ' Line ' , x1 , y1 , x2 , y2 , settings )
return self.wf . Collider.new ( self , " Line " , x1 , y1 , x2 , y2 , settings )
end
function World : newChainCollider ( vertices , loop , settings )
return self.wf . Collider.new ( self , ' Chain ' , vertices , loop , settings )
return self.wf . Collider.new ( self , " Chain " , vertices , loop , settings )
end
-- Internal AABB box2d query used before going for more specific and precise computations.
function World : _queryBoundingBox ( x1 , y1 , x2 , y2 )
local colliders = { }
local callback = function ( fixture )
if not fixture : isSensor ( ) then table.insert ( colliders , fixture : getUserData ( ) ) end
if not fixture : isSensor ( ) then
table.insert ( colliders , fixture : getUserData ( ) )
end
return true
end
self.box2d_world : queryBoundingBox ( x1 , y1 , x2 , y2 , callback )
@ -536,7 +634,7 @@ function World:_queryBoundingBox(x1, y1, x2, y2)
end
function World : collisionClassInCollisionClassesList ( collision_class , collision_classes )
if collision_classes [ 1 ] == ' All ' then
if collision_classes [ 1 ] == " All " then
local all_collision_classes = { }
for class , _ in pairs ( self.collision_classes ) do
table.insert ( all_collision_classes , class )
@ -552,25 +650,43 @@ function World:collisionClassInCollisionClassesList(collision_class, collision_c
end
end
for _ , class in ipairs ( all_collision_classes ) do
if class == collision_class then return true end
if class == collision_class then
return true
end
end
else
for _ , class in ipairs ( collision_classes ) do
if class == collision_class then return true end
if class == collision_class then
return true
end
end
end
end
function World : queryCircleArea ( x , y , radius , collision_class_names )
if not collision_class_names then collision_class_names = { ' All ' } end
if self.query_debug_drawing_enabled then table.insert ( self.query_debug_draw , { type = ' circle ' , x = x , y = y , r = radius , frames = self.draw_query_for_n_frames } ) end
if not collision_class_names then
collision_class_names = { " All " }
end
if self.query_debug_drawing_enabled then
table.insert (
self.query_debug_draw ,
{ type = " circle " , x = x , y = y , r = radius , frames = self.draw_query_for_n_frames }
)
end
local colliders = self : _queryBoundingBox ( x - radius , y - radius , x + radius , y + radius )
local outs = { }
for _ , collider in ipairs ( colliders ) do
if self : collisionClassInCollisionClassesList ( collider.collision_class , collision_class_names ) then
for _ , fixture in ipairs ( collider.body : getFixtures ( ) ) do
if self.wf . Math.polygon . getCircleIntersection ( x , y , radius , { collider.body : getWorldPoints ( fixture : getShape ( ) : getPoints ( ) ) } ) then
if
self.wf . Math.polygon . getCircleIntersection (
x ,
y ,
radius ,
{ collider.body : getWorldPoints ( fixture : getShape ( ) : getPoints ( ) ) }
)
then
table.insert ( outs , collider )
break
end
@ -581,15 +697,27 @@ function World:queryCircleArea(x, y, radius, collision_class_names)
end
function World : queryRectangleArea ( x , y , w , h , collision_class_names )
if not collision_class_names then collision_class_names = { ' All ' } end
if self.query_debug_drawing_enabled then table.insert ( self.query_debug_draw , { type = ' rectangle ' , x = x , y = y , w = w , h = h , frames = self.draw_query_for_n_frames } ) end
if not collision_class_names then
collision_class_names = { " All " }
end
if self.query_debug_drawing_enabled then
table.insert (
self.query_debug_draw ,
{ type = " rectangle " , x = x , y = y , w = w , h = h , frames = self.draw_query_for_n_frames }
)
end
local colliders = self : _queryBoundingBox ( x , y , x + w , y + h )
local outs = { }
for _ , collider in ipairs ( colliders ) do
if self : collisionClassInCollisionClassesList ( collider.collision_class , collision_class_names ) then
for _ , fixture in ipairs ( collider.body : getFixtures ( ) ) do
if self.wf . Math.polygon . isPolygonInside ( { x , y , x + w , y , x + w , y + h , x , y + h } , { collider.body : getWorldPoints ( fixture : getShape ( ) : getPoints ( ) ) } ) then
if
self.wf . Math.polygon . isPolygonInside (
{ x , y , x + w , y , x + w , y + h , x , y + h } ,
{ collider.body : getWorldPoints ( fixture : getShape ( ) : getPoints ( ) ) }
)
then
table.insert ( outs , collider )
break
end
@ -600,21 +728,35 @@ function World:queryRectangleArea(x, y, w, h, collision_class_names)
end
function World : queryPolygonArea ( vertices , collision_class_names )
if not collision_class_names then collision_class_names = { ' All ' } end
if self.query_debug_drawing_enabled then table.insert ( self.query_debug_draw , { type = ' polygon ' , vertices = vertices , frames = self.draw_query_for_n_frames } ) end
if not collision_class_names then
collision_class_names = { " All " }
end
if self.query_debug_drawing_enabled then
table.insert (
self.query_debug_draw ,
{ type = " polygon " , vertices = vertices , frames = self.draw_query_for_n_frames }
)
end
local cx , cy = self.wf . Math.polygon . getCentroid ( vertices )
local d_max = 0
for i = 1 , # vertices , 2 do
local d = self.wf . Math.line . getLength ( cx , cy , vertices [ i ] , vertices [ i + 1 ] )
if d > d_max then d_max = d end
if d > d_max then
d_max = d
end
end
local colliders = self : _queryBoundingBox ( cx - d_max , cy - d_max , cx + d_max , cy + d_max )
local outs = { }
for _ , collider in ipairs ( colliders ) do
if self : collisionClassInCollisionClassesList ( collider.collision_class , collision_class_names ) then
for _ , fixture in ipairs ( collider.body : getFixtures ( ) ) do
if self.wf . Math.polygon . isPolygonInside ( vertices , { collider.body : getWorldPoints ( fixture : getShape ( ) : getPoints ( ) ) } ) then
if
self.wf . Math.polygon . isPolygonInside (
vertices ,
{ collider.body : getWorldPoints ( fixture : getShape ( ) : getPoints ( ) ) }
)
then
table.insert ( outs , collider )
break
end
@ -625,14 +767,21 @@ function World:queryPolygonArea(vertices, collision_class_names)
end
function World : queryLine ( x1 , y1 , x2 , y2 , collision_class_names )
if not collision_class_names then collision_class_names = { ' All ' } end
if not collision_class_names then
collision_class_names = { " All " }
end
if self.query_debug_drawing_enabled then
table.insert ( self.query_debug_draw , { type = ' line ' , x1 = x1 , y1 = y1 , x2 = x2 , y2 = y2 , frames = self.draw_query_for_n_frames } )
table.insert (
self.query_debug_draw ,
{ type = " line " , x1 = x1 , y1 = y1 , x2 = x2 , y2 = y2 , frames = self.draw_query_for_n_frames }
)
end
local colliders = { }
local callback = function ( fixture , ... )
if not fixture : isSensor ( ) then table.insert ( colliders , fixture : getUserData ( ) ) end
if not fixture : isSensor ( ) then
table.insert ( colliders , fixture : getUserData ( ) )
end
return 1
end
self.box2d_world : rayCast ( x1 , y1 , x2 , y2 , callback )
@ -648,9 +797,13 @@ end
function World : addJoint ( joint_type , ... )
local args = { ... }
if args [ 1 ] . body then args [ 1 ] = args [ 1 ] . body end
if type ( args [ 2 ] ) == " table " and args [ 2 ] . body then args [ 2 ] = args [ 2 ] . body end
local joint = love.physics [ ' new ' .. joint_type ] ( unpack ( args ) )
if args [ 1 ] . body then
args [ 1 ] = args [ 1 ] . body
end
if type ( args [ 2 ] ) == " table " and args [ 2 ] . body then
args [ 2 ] = args [ 2 ] . body
end
local joint = love.physics [ " new " .. joint_type ] ( unpack ( args ) )
return joint
end
@ -665,13 +818,13 @@ function World:destroy()
collider : destroy ( )
end
local joints = self.box2d_world : getJoints ( )
for _ , joint in ipairs ( joints ) do joint : destroy ( ) end
for _ , joint in ipairs ( joints ) do
joint : destroy ( )
end
self.box2d_world : destroy ( )
self.box2d_world = nil
end
local Collider = { }
Collider.__index = Collider
@ -704,40 +857,58 @@ function Collider.new(world, collider_type, ...)
local args = { ... }
local shape , fixture
if self.type == ' Circle ' then
self.collision_class = ( args [ 4 ] and args [ 4 ] . collision_class ) or ' Default '
self.body = love.physics . newBody ( self.world . box2d_world , args [ 1 ] , args [ 2 ] , ( args [ 4 ] and args [ 4 ] . body_type ) or ' dynamic ' )
if self.type == " Circle " then
self.collision_class = ( args [ 4 ] and args [ 4 ] . collision_class ) or " Default "
self.body =
love.physics . newBody ( self.world . box2d_world , args [ 1 ] , args [ 2 ] , ( args [ 4 ] and args [ 4 ] . body_type ) or " dynamic " )
shape = love.physics . newCircleShape ( args [ 3 ] )
elseif self.type == ' Rectangle ' then
self.collision_class = ( args [ 5 ] and args [ 5 ] . collision_class ) or ' Default '
self.body = love.physics . newBody ( self.world . box2d_world , args [ 1 ] + args [ 3 ] / 2 , args [ 2 ] + args [ 4 ] / 2 , ( args [ 5 ] and args [ 5 ] . body_type ) or ' dynamic ' )
elseif self.type == " Rectangle " then
self.collision_class = ( args [ 5 ] and args [ 5 ] . collision_class ) or " Default "
self.body = love.physics . newBody (
self.world . box2d_world ,
args [ 1 ] + args [ 3 ] / 2 ,
args [ 2 ] + args [ 4 ] / 2 ,
( args [ 5 ] and args [ 5 ] . body_type ) or " dynamic "
)
shape = love.physics . newRectangleShape ( args [ 3 ] , args [ 4 ] )
elseif self.type == ' BSGRectangle ' then
self.collision_class = ( args [ 6 ] and args [ 6 ] . collision_class ) or ' Default '
self.body = love.physics . newBody ( self.world . box2d_world , args [ 1 ] + args [ 3 ] / 2 , args [ 2 ] + args [ 4 ] / 2 , ( args [ 6 ] and args [ 6 ] . body_type ) or ' dynamic ' )
elseif self.type == " BSGRectangle " then
self.collision_class = ( args [ 6 ] and args [ 6 ] . collision_class ) or " Default "
self.body = love.physics . newBody (
self.world . box2d_world ,
args [ 1 ] + args [ 3 ] / 2 ,
args [ 2 ] + args [ 4 ] / 2 ,
( args [ 6 ] and args [ 6 ] . body_type ) or " dynamic "
)
local w , h , s = args [ 3 ] , args [ 4 ] , args [ 5 ]
shape = love.physics . newPolygonShape ( {
- w / 2 , - h / 2 + s , - w / 2 + s , - h / 2 ,
w / 2 - s , - h / 2 , w / 2 , - h / 2 + s ,
w / 2 , h / 2 - s , w / 2 - s , h / 2 ,
- w / 2 + s , h / 2 , - w / 2 , h / 2 - s
- w / 2 ,
- h / 2 + s ,
- w / 2 + s ,
- h / 2 ,
w / 2 - s ,
- h / 2 ,
w / 2 ,
- h / 2 + s ,
w / 2 ,
h / 2 - s ,
w / 2 - s ,
h / 2 ,
- w / 2 + s ,
h / 2 ,
- w / 2 ,
h / 2 - s ,
} )
elseif self.type == ' Polygon ' then
self.collision_class = ( args [ 2 ] and args [ 2 ] . collision_class ) or ' Default '
self.body = love.physics . newBody ( self.world . box2d_world , 0 , 0 , ( args [ 2 ] and args [ 2 ] . body_type ) or ' dynamic ' )
elseif self.type == " Polygon " then
self.collision_class = ( args [ 2 ] and args [ 2 ] . collision_class ) or " Default "
self.body = love.physics . newBody ( self.world . box2d_world , 0 , 0 , ( args [ 2 ] and args [ 2 ] . body_type ) or " dynamic " )
shape = love.physics . newPolygonShape ( unpack ( args [ 1 ] ) )
elseif self.type == ' Line ' then
self.collision_class = ( args [ 5 ] and args [ 5 ] . collision_class ) or ' Default '
self.body = love.physics . newBody ( self.world . box2d_world , 0 , 0 , ( args [ 5 ] and args [ 5 ] . body_type ) or ' dynamic ' )
elseif self.type == " Line " then
self.collision_class = ( args [ 5 ] and args [ 5 ] . collision_class ) or " Default "
self.body = love.physics . newBody ( self.world . box2d_world , 0 , 0 , ( args [ 5 ] and args [ 5 ] . body_type ) or " dynamic " )
shape = love.physics . newEdgeShape ( args [ 1 ] , args [ 2 ] , args [ 3 ] , args [ 4 ] )
elseif self.type == ' Chain ' then
self.collision_class = ( args [ 3 ] and args [ 3 ] . collision_class ) or ' Default '
self.body = love.physics . newBody ( self.world . box2d_world , 0 , 0 , ( args [ 3 ] and args [ 3 ] . body_type ) or ' dynamic ' )
elseif self.type == " Chain " then
self.collision_class = ( args [ 3 ] and args [ 3 ] . collision_class ) or " Default "
self.body = love.physics . newBody ( self.world . box2d_world , 0 , 0 , ( args [ 3 ] and args [ 3 ] . body_type ) or " dynamic " )
shape = love.physics . newChainShape ( args [ 1 ] , unpack ( args [ 2 ] ) )
end
@ -752,9 +923,9 @@ function Collider.new(world, collider_type, ...)
sensor : setSensor ( true )
sensor : setUserData ( self )
self.shapes [ ' main ' ] = shape
self.fixtures [ ' main ' ] = fixture
self.sensors [ ' main ' ] = sensor
self.shapes [ " main " ] = shape
self.fixtures [ " main " ] = fixture
self.sensors [ " main " ] = sensor
self.shape = shape
self.fixture = fixture
@ -764,21 +935,45 @@ function Collider.new(world, collider_type, ...)
-- Points all body, fixture and shape functions to this wf.Collider object
-- This means that the user can call collider:setLinearVelocity for instance without having to say collider.body:setLinearVelocity
for k , v in pairs ( self.body . __index ) do
if k ~= ' __gc ' and k ~= ' __eq ' and k ~= ' __index ' and k ~= ' __tostring ' and k ~= ' destroy ' and k ~= ' type ' and k ~= ' typeOf ' then
if
k ~= " __gc "
and k ~= " __eq "
and k ~= " __index "
and k ~= " __tostring "
and k ~= " destroy "
and k ~= " type "
and k ~= " typeOf "
then
self [ k ] = function ( self , ... )
return v ( self.body , ... )
end
end
end
for k , v in pairs ( self.fixture . __index ) do
if k ~= ' __gc ' and k ~= ' __eq ' and k ~= ' __index ' and k ~= ' __tostring ' and k ~= ' destroy ' and k ~= ' type ' and k ~= ' typeOf ' then
if
k ~= " __gc "
and k ~= " __eq "
and k ~= " __index "
and k ~= " __tostring "
and k ~= " destroy "
and k ~= " type "
and k ~= " typeOf "
then
self [ k ] = function ( self , ... )
return v ( self.fixture , ... )
end
end
end
for k , v in pairs ( self.shape . __index ) do
if k ~= ' __gc ' and k ~= ' __eq ' and k ~= ' __index ' and k ~= ' __tostring ' and k ~= ' destroy ' and k ~= ' type ' and k ~= ' typeOf ' then
if
k ~= " __gc "
and k ~= " __eq "
and k ~= " __index "
and k ~= " __tostring "
and k ~= " destroy "
and k ~= " type "
and k ~= " typeOf "
then
self [ k ] = function ( self , ... )
return v ( self.shape , ... )
end
@ -796,7 +991,9 @@ function Collider:collisionEventsClear()
end
function Collider : setCollisionClass ( collision_class_name )
if not self.world . collision_classes [ collision_class_name ] then error ( " Collision class " .. collision_class_name .. " doesn't exist. " ) end
if not self.world . collision_classes [ collision_class_name ] then
error ( " Collision class " .. collision_class_name .. " doesn't exist. " )
end
self.collision_class = collision_class_name
for _ , fixture in pairs ( self.fixtures ) do
if self.world . masks [ collision_class_name ] then
@ -810,9 +1007,14 @@ function Collider:enter(other_collision_class_name)
local events = self.collision_events [ other_collision_class_name ]
if events and # events >= 1 then
for _ , e in ipairs ( events ) do
if e.collision_type == ' enter ' then
if not self.collision_stay [ other_collision_class_name ] then self.collision_stay [ other_collision_class_name ] = { } end
table.insert ( self.collision_stay [ other_collision_class_name ] , { collider = e.collider_2 , contact = e.contact } )
if e.collision_type == " enter " then
if not self.collision_stay [ other_collision_class_name ] then
self.collision_stay [ other_collision_class_name ] = { }
end
table.insert (
self.collision_stay [ other_collision_class_name ] ,
{ collider = e.collider_2 , contact = e.contact }
)
self.enter_collision_data [ other_collision_class_name ] = { collider = e.collider_2 , contact = e.contact }
return true
end
@ -828,11 +1030,13 @@ function Collider:exit(other_collision_class_name)
local events = self.collision_events [ other_collision_class_name ]
if events and # events >= 1 then
for _ , e in ipairs ( events ) do
if e.collision_type == ' exit ' then
if e.collision_type == " exit " then
if self.collision_stay [ other_collision_class_name ] then
for i = # self.collision_stay [ other_collision_class_name ] , 1 , - 1 do
local collision_stay = self.collision_stay [ other_collision_class_name ] [ i ]
if collision_stay.collider . id == e.collider_2 . id then table.remove ( self.collision_stay [ other_collision_class_name ] , i ) end
if collision_stay.collider . id == e.collider_2 . id then
table.remove ( self.collision_stay [ other_collision_class_name ] , i )
end
end
end
self.exit_collision_data [ other_collision_class_name ] = { collider = e.collider_2 , contact = e.contact }
@ -875,9 +1079,11 @@ function Collider:getObject()
end
function Collider : addShape ( shape_name , shape_type , ... )
if self.shapes [ shape_name ] or self.fixtures [ shape_name ] then error ( " Shape/fixture " .. shape_name .. " already exists. " ) end
if self.shapes [ shape_name ] or self.fixtures [ shape_name ] then
error ( " Shape/fixture " .. shape_name .. " already exists. " )
end
local args = { ... }
local shape = love.physics [ ' new ' .. shape_type ] ( unpack ( args ) )
local shape = love.physics [ " new " .. shape_type ] ( unpack ( args ) )
local fixture = love.physics . newFixture ( self.body , shape )
if self.world . masks [ self.collision_class ] then
fixture : setCategory ( unpack ( self.world . masks [ self.collision_class ] . categories ) )
@ -894,7 +1100,9 @@ function Collider:addShape(shape_name, shape_type, ...)
end
function Collider : removeShape ( shape_name )
if not self.shapes [ shape_name ] then return end
if not self.shapes [ shape_name ] then
return
end
self.shapes [ shape_name ] = nil
self.fixtures [ shape_name ] : setUserData ( nil )
self.fixtures [ shape_name ] : destroy ( )
@ -926,4 +1134,3 @@ wf.World = World
wf.Collider = Collider
return wf