Capsules are used by the game for collision detection. All entities have a collision capsule whose shape is defined by their XML markup. Additionally, you can use a null layer frame's size and position in a sprite's animation to generate a null capsule.
Null layers are added in the sprite's .anm2 file by setting the type to "Null" when adding a layer. The size and relative position of the null layer is dependant on the position and scale of the frame.
You can use Entity:GetNullCapsule() to generate a capsule of the current frame in a specified null layer.
localexampleMod=RegisterMod("Repentogon Null Capsule Example",1)localsfx=SFXManager()-- Setup some constants.localPIPE_ITEM_ID=Isaac.GetItemIdByName("Swinging Pipe")localPIPE_EFFECT_ID=Isaac.GetEntityVariantByName("Pipe Swing")localDAMAGE_MULTIPLIER=2.5---@param player EntityPlayerfunctionexampleMod:ActivatePipeItem(_,_,player,flags)-- Don't run a second time for Car Battery, because it would be strange for an item like this.ifflags&UseFlag.USE_CARBATTERY==UseFlag.USE_CARBATTERYthenreturnend-- Spawn the pipe effect. We will be checking for things to damage in the update callback.localeffect=Isaac.Spawn(EntityType.ENTITY_EFFECT,PIPE_EFFECT_ID,0,player.Position,Vector.Zero,player):ToEffect()effect:FollowParent(player)-- Sound effects.sfx:Play(SoundEffect.SOUND_SWORD_SPIN)end-- Connect the callback, only for our item.exampleMod:AddCallback(ModCallbacks.MC_USE_ITEM,exampleMod.ActivatePipeItem,PIPE_ITEM_ID)-- Now, let's handle capsules.-- Capsules are our hitboxes.---@param pipe EntityEffectfunctionexampleMod:PipeEffectUpdate(pipe)localsprite=pipe:GetSprite()localplayer=pipe.Parent:ToPlayer()localdata=pipe:GetData()-- We are going to use this table as a way to make sure enemies are only hurt once in a swing.-- This line will either set the hit blacklist to itself, or create one if it doesn't exist.data.HitBlacklist=data.HitBlacklistor{}-- Handle removing the pipe when the spin is done.ifsprite:IsFinished("Spin")thenpipe:Remove()returnend-- We're doing a for loop before because the effect is based off of Spirit Sword's anm2.-- Spirit Sword's anm2 has two hitboxes with the same name with a different number at the ending, so we use a for loop to avoid repeating code.fori=1,2do-- Get the "null capsule", which is the hitbox defined by the null layer in the anm2.localcapsule=pipe:GetNullCapsule("Hit"..i)-- Search for all enemies within the capsule.for_,enemyinipairs(Isaac.FindInCapsule(capsule,EntityPartition.ENEMY))do-- Make sure it can be hurt.ifenemy:IsVulnerableEnemy()andenemy:IsActiveEnemy()andnotdata.HitBlacklist[GetPtrHash(enemy)]then-- Now hurt it.enemy:TakeDamage(player.Damage*DAMAGE_MULTIPLIER,0,EntityRef(player),0)-- Add it to the blacklist, so it can't be hurt again.data.HitBlacklist[GetPtrHash(enemy)]=true-- Do some fancy effects, while we're at it.enemy:BloodExplode()enemy:MakeBloodPoof(enemy.Position,nil,0.5)sfx:Play(SoundEffect.SOUND_DEATH_BURST_LARGE)endendendend-- Connect the callback, only for our effect.exampleMod:AddCallback(ModCallbacks.MC_POST_EFFECT_UPDATE,exampleMod.PipeEffectUpdate,PIPE_EFFECT_ID)