diff --git a/LuaScriptsLibExt/HealthPoint.lua b/LuaScriptsLibExt/HealthPoint.lua index 392f5b732..678f4d5d0 100644 --- a/LuaScriptsLibExt/HealthPoint.lua +++ b/LuaScriptsLibExt/HealthPoint.lua @@ -1,7 +1,7 @@ -- HealthPoint -- Made by WasabiJellyfish -- v1.0.3 - + local HealthPoint = {} local pNPC = loadSharedAPI("pnpc") local graphX = loadSharedAPI("graphX") @@ -9,49 +9,49 @@ local colliders = loadSharedAPI("colliders") function HealthPoint.onInitAPI() --Is called when the api is loaded by loadAPI. registerEvent(HealthPoint, "onLoop", "assign", true) --Register the loop event - registerEvent(HealthPoint, "onNPCKill", "onNPCKill", true) --Register the damage event + registerEvent(HealthPoint, "onNPCKill", "onNPCKill", true) --Register the damage event end ---------------------------------------------------------------------------------------------- --- DOCUMENTATION AND FUNCTIONS --- ---------------------------------------------------------------------------------------------- --[[ - Damage types + Damage types 1 = jump - 2 = spinjump - 3 = fireball - 4 = hammer - 5 = shell - 6 = tail - 7 = link - 8 = thrown - 9 = boomerang - 10 = peachbomb - 11 = swordbeam - 12 = linkfire - 13 = iceblock - 14 = yoshifire - 15 = yoshiice - - HealthPoint.setNPCHealth(id, healthamount) + 2 = spinjump + 3 = fireball + 4 = hammer + 5 = shell + 6 = tail + 7 = link + 8 = thrown + 9 = boomerang + 10 = peachbomb + 11 = swordbeam + 12 = linkfire + 13 = iceblock + 14 = yoshifire + 15 = yoshiice + + HealthPoint.setNPCHealth(id, healthamount) - HealthPoint.setNPCDamage(id, damagetype, damageamount) + HealthPoint.setNPCDamage(id, damagetype, damageamount) - HealthPoint.setGlobalDamage(damagetype, damageamount) + HealthPoint.setGlobalDamage(damagetype, damageamount) - HealthPoint.makeNPCInvincible(id) + HealthPoint.makeNPCInvincible(id) - HealthPoint.makeNPCNormal(id) + HealthPoint.makeNPCNormal(id) --]] ---------------------------------------------------------------------------------------------- --- THE ACTUAL CODE STUFF --- ---------------------------------------------------------------------------------------------- - + local bingo = {1, 2, 3, 4, 5, 6, 7, 8, 12, 15, 17, 18, 19, 20, 23, 24, 25, 27, 28, 29, 36, 37, 38, 39, 42, 43, 44, 47, 48, 49, 50, 51, 52, 53, 54, 55, 59, 61, 63, 65, 71, 72, 73, 74, 76, 77, 86, 89, 93, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 135, 136, 137, 161, 162, 163, 164, 165, 166, 167, 168, 172, 173, 174, 175, 176, 177, 180, 189, 194, 195, 199, 200, 201, 203, 204, 205, 206, 207, 208, 209, 210, 229, 230, 231, 232, 233, 234, 235, 236, 242, 243, 244, 245, 247, 256, 257, 261, 262, 263, 267, 268, 270, 271, 272, 275, 280, 281, 284, 285, 286} -- Health -- -- Change these values corresponding to the id inside the [] to change the enemy's health -- @@ -59,7 +59,7 @@ local bingo = {1, 2, 3, 4, 5, 6, 7, 8, 12, 15, 17, 18, 19, 20, 23, 24, 25, 27, 2 for _,v in pairs(bingo) do table.insert(HealthPoint.npcHealth, v, 3) end - + -- Jump Damage -- HealthPoint.jumpDamage = {} @@ -71,7 +71,7 @@ local bingo = {1, 2, 3, 4, 5, 6, 7, 8, 12, 15, 17, 18, 19, 20, 23, 24, 25, 27, 2 for _,v in pairs(bingo) do table.insert(HealthPoint.spinjumpDamage, v, 1) end - + -- Fireball Damage -- HealthPoint.fireballDamage = {} for _,v in pairs(bingo) do @@ -83,47 +83,47 @@ local bingo = {1, 2, 3, 4, 5, 6, 7, 8, 12, 15, 17, 18, 19, 20, 23, 24, 25, 27, 2 for _,v in pairs(bingo) do table.insert(HealthPoint.hammerDamage, v, 1) end - + -- Projectile Damage -- HealthPoint.projectileDamage = {} for _,v in pairs(bingo) do table.insert(HealthPoint.projectileDamage, v, 1) end - + -- Tail Damage -- HealthPoint.tailDamage = {} for _,v in pairs(bingo) do table.insert(HealthPoint.tailDamage, v, 1) end - + -- Link Attack Damage -- HealthPoint.linkDamage = {} for _,v in pairs(bingo) do table.insert(HealthPoint.linkDamage, v, 1) end - + -- Thrown Damage -- HealthPoint.thrownDamage = {} for _,v in pairs(bingo) do table.insert(HealthPoint.thrownDamage, v, 1) end - + -- Boomerang Damage-- HealthPoint.boomerangDamage = {} for _,v in pairs(bingo) do table.insert(HealthPoint.boomerangDamage, v, 1) end - + HealthPoint.peachbombDamage = {} for _,v in pairs(bingo) do table.insert(HealthPoint.peachbombDamage, v, 1) end - + HealthPoint.swordbeamDamage = {} for _,v in pairs(bingo) do table.insert(HealthPoint.swordbeamDamage, v, 1) end - + HealthPoint.yoshifireDamage = {} for _,v in pairs(bingo) do table.insert(HealthPoint.yoshifireDamage, v, 1) @@ -171,7 +171,7 @@ end for l,_ in pairs(HealthPoint.npcHealth) do damevaeg[cause][l] = number end - end + end function HealthPoint.makeNPCInvincible(id) for r,_ in pairs(damevaeg) do @@ -190,216 +190,216 @@ end ---------------------------------------------------------------------------------------------- function HealthPoint.onNPCKill(eventObj, killedNPC, killReason) - if killedNPC.id == 13 and killReason == 4 then - fire = true - hammer = false - shell = false - end - if HealthPoint.npcHealth[killedNPC.id] ~= nil then - local wrappedNPC = pNPC.wrap(killedNPC) - - - if colliders.collideNPC(wrappedNPC, 171) then - hammer = true - fire = false - shell = false - elseif colliders.collideNPC(wrappedNPC, 292) then - boomerang = true - hammer = false - fire = false - shell = false - elseif colliders.collideNPC(wrappedNPC, 108) then - yoshifire = true - hammer = false - fire = false - shell = false - - elseif colliders.collideNPC (wrappedNPC, 263) then - Text.print ("ICE", 100, 100) - ice = true - shell = false - hammer = false - fire = false - - elseif colliders.collideNPC (wrappedNPC, 291) then - peachbomb = true - shell = false - - elseif colliders.collideNPC (wrappedNPC, 266) then - laser = true - shell = false - elseif colliders.collideNPC(wrappedNPC, 237) then - yoshiice = true - shell = false - else - shell = true - - end - - - if wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.jumpDamage[wrappedNPC.id] and killReason == 1 then - eventObj.cancelled = true - wrappedNPC:mem(0x156, FIELD_WORD, 20) - wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.jumpDamage[wrappedNPC.id] - playSFX(2) - wrappedNPC.speedX = 0 - - elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.fireballDamage[wrappedNPC.id] and killReason == 3 and fire then - eventObj.cancelled = true - wrappedNPC:mem(0x156, FIELD_WORD, 20) - wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.fireballDamage[wrappedNPC.id] - playSFX(3) - wrappedNPC.speedX = 0 - fire = false - - elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.iceblockDamage[wrappedNPC.id] and killReason == 3 and ice then - eventObj.cancelled = true - wrappedNPC:mem(0x156, FIELD_WORD, 20) - wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.iceblockDamage[wrappedNPC.id] - playSFX(3) - wrappedNPC.speedX = 0 - - - elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.hammerDamage[wrappedNPC.id] and killReason == 3 and hammer then - eventObj.cancelled = true - wrappedNPC:mem(0x156, FIELD_WORD, 20) - wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.hammerDamage[wrappedNPC.id] - playSFX(3) - wrappedNPC.speedX = 0 - - elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.boomerangDamage[wrappedNPC.id] and killReason == 3 and boomerang then - eventObj.cancelled = true - wrappedNPC:mem(0x156, FIELD_WORD, 20) - wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.boomerangDamage[wrappedNPC.id] - playSFX(3) - wrappedNPC.speedX = 0 - - elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.yoshifireDamage[wrappedNPC.id] and killReason == 3 and yoshifire then - eventObj.cancelled = true - wrappedNPC:mem(0x156, FIELD_WORD, 20) - wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.yoshifireDamage[wrappedNPC.id] - playSFX(3) - wrappedNPC.speedX = 0 - - elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.yoshifireDamage[wrappedNPC.id] and killReason == 3 and yoshiice then - eventObj.cancelled = true - wrappedNPC:mem(0x156, FIELD_WORD, 20) - wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.yoshiiceDamage[wrappedNPC.id] - playSFX(3) - wrappedNPC.speedX = 0 - - elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.peachbombDamage[wrappedNPC.id] and killReason == 3 and peachbomb then - eventObj.cancelled = true - wrappedNPC:mem(0x156, FIELD_WORD, 20) - wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.peachbombDamage[wrappedNPC.id] - playSFX(3) - wrappedNPC.speedX = 0 - - elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.swordbeamDamage[wrappedNPC.id] and killReason == 10 and laser then - eventObj.cancelled = true - wrappedNPC:mem(0x156, FIELD_WORD, 20) - wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.swordbeamDamage[wrappedNPC.id] - playSFX(89) - wrappedNPC.speedX = 0 - - - - elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.projectileDamage[wrappedNPC.id] and killReason == 3 and shell and not fire and not ice then - eventObj.cancelled = true - wrappedNPC:mem(0x156, FIELD_WORD, 20) - wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.projectileDamage[wrappedNPC.id] - playSFX(3) - wrappedNPC.speedX = 0 - shell = false - - elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.thrownDamage[wrappedNPC.id] and (killReason == 4 or killReason == 5) then - eventObj.cancelled = true - wrappedNPC:mem(0x156, FIELD_WORD, 20) - wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.thrownDamage[wrappedNPC.id] - - - elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.tailDamage[wrappedNPC.id] and killReason == 7 then - eventObj.cancelled = true - wrappedNPC:mem(0x156, FIELD_WORD, 20) - wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.tailDamage[wrappedNPC.id] - playSFX(3) - wrappedNPC.speedX = 0 - - elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.spinjumpDamage[wrappedNPC.id] and killReason == 8 then - eventObj.cancelled = true - wrappedNPC:mem(0x156, FIELD_WORD, 20) - wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.spinjumpDamage[wrappedNPC.id] - playSFX(2) - wrappedNPC.speedX = 0 - - elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.linkDamage[wrappedNPC.id] and killReason == 10 and not fire then - eventObj.cancelled = true - wrappedNPC:mem(0x156, FIELD_WORD, 20) - wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.linkDamage[wrappedNPC.id] - playSFX(89) - wrappedNPC.speedX = 0 - - - elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.linkfireDamage[wrappedNPC.id] and killReason == 10 and fire then - eventObj.cancelled = true - wrappedNPC:mem(0x156, FIELD_WORD, 20) - wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.linkfireDamage[wrappedNPC.id] - playSFX(89) - wrappedNPC.speedX = 0 - - end - end + if killedNPC.id == 13 and killReason == 4 then + fire = true + hammer = false + shell = false + end + if HealthPoint.npcHealth[killedNPC.id] ~= nil then + local wrappedNPC = pNPC.wrap(killedNPC) + + + if colliders.collideNPC(wrappedNPC, 171) then + hammer = true + fire = false + shell = false + elseif colliders.collideNPC(wrappedNPC, 292) then + boomerang = true + hammer = false + fire = false + shell = false + elseif colliders.collideNPC(wrappedNPC, 108) then + yoshifire = true + hammer = false + fire = false + shell = false + + elseif colliders.collideNPC (wrappedNPC, 263) then + Text.print ("ICE", 100, 100) + ice = true + shell = false + hammer = false + fire = false + + elseif colliders.collideNPC (wrappedNPC, 291) then + peachbomb = true + shell = false + + elseif colliders.collideNPC (wrappedNPC, 266) then + laser = true + shell = false + elseif colliders.collideNPC(wrappedNPC, 237) then + yoshiice = true + shell = false + else + shell = true + + end + + + if wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.jumpDamage[wrappedNPC.id] and killReason == 1 then + eventObj.cancelled = true + wrappedNPC:mem(0x156, FIELD_WORD, 20) + wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.jumpDamage[wrappedNPC.id] + playSFX(2) + wrappedNPC.speedX = 0 + + elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.fireballDamage[wrappedNPC.id] and killReason == 3 and fire then + eventObj.cancelled = true + wrappedNPC:mem(0x156, FIELD_WORD, 20) + wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.fireballDamage[wrappedNPC.id] + playSFX(3) + wrappedNPC.speedX = 0 + fire = false + + elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.iceblockDamage[wrappedNPC.id] and killReason == 3 and ice then + eventObj.cancelled = true + wrappedNPC:mem(0x156, FIELD_WORD, 20) + wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.iceblockDamage[wrappedNPC.id] + playSFX(3) + wrappedNPC.speedX = 0 + + + elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.hammerDamage[wrappedNPC.id] and killReason == 3 and hammer then + eventObj.cancelled = true + wrappedNPC:mem(0x156, FIELD_WORD, 20) + wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.hammerDamage[wrappedNPC.id] + playSFX(3) + wrappedNPC.speedX = 0 + + elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.boomerangDamage[wrappedNPC.id] and killReason == 3 and boomerang then + eventObj.cancelled = true + wrappedNPC:mem(0x156, FIELD_WORD, 20) + wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.boomerangDamage[wrappedNPC.id] + playSFX(3) + wrappedNPC.speedX = 0 + + elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.yoshifireDamage[wrappedNPC.id] and killReason == 3 and yoshifire then + eventObj.cancelled = true + wrappedNPC:mem(0x156, FIELD_WORD, 20) + wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.yoshifireDamage[wrappedNPC.id] + playSFX(3) + wrappedNPC.speedX = 0 + + elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.yoshifireDamage[wrappedNPC.id] and killReason == 3 and yoshiice then + eventObj.cancelled = true + wrappedNPC:mem(0x156, FIELD_WORD, 20) + wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.yoshiiceDamage[wrappedNPC.id] + playSFX(3) + wrappedNPC.speedX = 0 + + elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.peachbombDamage[wrappedNPC.id] and killReason == 3 and peachbomb then + eventObj.cancelled = true + wrappedNPC:mem(0x156, FIELD_WORD, 20) + wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.peachbombDamage[wrappedNPC.id] + playSFX(3) + wrappedNPC.speedX = 0 + + elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.swordbeamDamage[wrappedNPC.id] and killReason == 10 and laser then + eventObj.cancelled = true + wrappedNPC:mem(0x156, FIELD_WORD, 20) + wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.swordbeamDamage[wrappedNPC.id] + playSFX(89) + wrappedNPC.speedX = 0 + + + + elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.projectileDamage[wrappedNPC.id] and killReason == 3 and shell and not fire and not ice then + eventObj.cancelled = true + wrappedNPC:mem(0x156, FIELD_WORD, 20) + wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.projectileDamage[wrappedNPC.id] + playSFX(3) + wrappedNPC.speedX = 0 + shell = false + + elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.thrownDamage[wrappedNPC.id] and (killReason == 4 or killReason == 5) then + eventObj.cancelled = true + wrappedNPC:mem(0x156, FIELD_WORD, 20) + wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.thrownDamage[wrappedNPC.id] + + + elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.tailDamage[wrappedNPC.id] and killReason == 7 then + eventObj.cancelled = true + wrappedNPC:mem(0x156, FIELD_WORD, 20) + wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.tailDamage[wrappedNPC.id] + playSFX(3) + wrappedNPC.speedX = 0 + + elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.spinjumpDamage[wrappedNPC.id] and killReason == 8 then + eventObj.cancelled = true + wrappedNPC:mem(0x156, FIELD_WORD, 20) + wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.spinjumpDamage[wrappedNPC.id] + playSFX(2) + wrappedNPC.speedX = 0 + + elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.linkDamage[wrappedNPC.id] and killReason == 10 and not fire then + eventObj.cancelled = true + wrappedNPC:mem(0x156, FIELD_WORD, 20) + wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.linkDamage[wrappedNPC.id] + playSFX(89) + wrappedNPC.speedX = 0 + + + elseif wrappedNPC.data.hit > 1 and wrappedNPC.data.hit > HealthPoint.linkfireDamage[wrappedNPC.id] and killReason == 10 and fire then + eventObj.cancelled = true + wrappedNPC:mem(0x156, FIELD_WORD, 20) + wrappedNPC.data.hit = wrappedNPC.data.hit - HealthPoint.linkfireDamage[wrappedNPC.id] + playSFX(89) + wrappedNPC.speedX = 0 + + end + end end - - - + + + ---------------------------------------------------------------------------------------------- --- HEALTH ASSIGNMENT --- ---------------------------------------------------------------------------------------------- function HealthPoint.assign() - for k,v in pairs(NPC.get()) do - - local n = pNPC.wrap(v); - - if n.data.hit == nil then - if HealthPoint.npcHealth[n.id] ~= nil then - n.data.hit = HealthPoint.npcHealth[n.id] - - end - end - - if n.data.hp == nil then - if HealthPoint.npcHealth[n.id] ~= nil then - n.data.hp = HealthPoint.npcHealth[n.id] - - end - end - - if n.data.hpmax == nil then - if HealthPoint.npcHealth[n.id] ~= nil then - n.data.hpmax = HealthPoint.npcHealth[n.id] - end - end - - if n ~= nil and HealthPoint.healthbar then - if n:mem(0x12A, FIELD_WORD) ~= 0 and n.data.hp ~= nil then - graphX.boxLevel (n.x-8,n.y-6, 32, 8, 0xFFFFFFF55) - graphX.boxLevel (n.x-7,n.y-5, 30, 6, 0x00000055) - graphX.boxLevel (n.x-7,n.y-5, n.data.hp/n.data.hpmax*30, 6, 0xFFF05BFF) - graphX.boxLevel (n.x-7,n.y-5, n.data.hit/n.data.hpmax*30, 6, 0xF61818FF) - - end - end - - if n.data.hp ~= nil then - if n.data.hp > n.data.hit then - n.data.hp = n.data.hp -0.02 - end - if n.data.hit > n.data.hpmax then - n.data.hit = n.data.hpmax - end - end - end + for k,v in pairs(NPC.get()) do + + local n = pNPC.wrap(v); + + if n.data.hit == nil then + if HealthPoint.npcHealth[n.id] ~= nil then + n.data.hit = HealthPoint.npcHealth[n.id] + + end + end + + if n.data.hp == nil then + if HealthPoint.npcHealth[n.id] ~= nil then + n.data.hp = HealthPoint.npcHealth[n.id] + + end + end + + if n.data.hpmax == nil then + if HealthPoint.npcHealth[n.id] ~= nil then + n.data.hpmax = HealthPoint.npcHealth[n.id] + end + end + + if n ~= nil and HealthPoint.healthbar then + if n:mem(0x12A, FIELD_WORD) ~= 0 and n.data.hp ~= nil then + graphX.boxLevel (n.x-8,n.y-6, 32, 8, 0xFFFFFFF55) + graphX.boxLevel (n.x-7,n.y-5, 30, 6, 0x00000055) + graphX.boxLevel (n.x-7,n.y-5, n.data.hp/n.data.hpmax*30, 6, 0xFFF05BFF) + graphX.boxLevel (n.x-7,n.y-5, n.data.hit/n.data.hpmax*30, 6, 0xF61818FF) + + end + end + + if n.data.hp ~= nil then + if n.data.hp > n.data.hit then + n.data.hp = n.data.hp -0.02 + end + if n.data.hit > n.data.hpmax then + n.data.hit = n.data.hpmax + end + end + end end - return HealthPoint + return HealthPoint \ No newline at end of file diff --git a/LuaScriptsLibExt/bgofix.lua b/LuaScriptsLibExt/bgofix.lua new file mode 100644 index 000000000..7a13f0f17 --- /dev/null +++ b/LuaScriptsLibExt/bgofix.lua @@ -0,0 +1,209 @@ +--***************************************************************************-- +--*██████╗ ██████╗ ██████╗ ███████╗██╗██╗ ██╗ ██╗ ██╗ ██╗ █████╗ *-- +--*██╔══██╗██╔════╝ ██╔═══██╗██╔════╝██║╚██╗██╔╝ ██║ ██║ ██║██╔══██╗*-- +--*██████╔╝██║ ███╗██║ ██║█████╗ ██║ ╚███╔╝ ██║ ██║ ██║███████║*-- +--*██╔══██╗██║ ██║██║ ██║██╔══╝ ██║ ██╔██╗ ██║ ██║ ██║██╔══██║*-- +--*██████╔╝╚██████╔╝╚██████╔╝██║ ██║██╔╝ ██╗██╗███████╗╚██████╔╝██║ ██║*-- +--*╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝*-- +--***************************************************************************-- +--* A small library for rendering BGOs of custom sizes and changing render order +--* Coded by Sambo, Oct. 2016. +--* NOTE: You must use PNG CGFX for this to work. LunaLua currently doesn't support drawing of masked GIFs. +--***************************************************************************-- + +local bgofix = {} + +local defaults = {} +defaults.foreground = {23,24,25,36,45,46,49,50,51,68,69,187,188,189,190} +defaults.veryBackBgos = {11,12,75,76,77,78,79} +defaults.specialBGOs = {98,160} + +local bgos = {} -- Table of unique BGO IDs and their custom properties +local blank = Graphics.loadImage(Misc.resolveFile("bgofix/blank.png")) +local animData = {} + +function bgofix.onInitAPI() + registerEvent(bgofix, "onStart", "initialize") + registerEvent(bgofix, "onDraw", "draw") +end + +--[[******************************************** + * Load the custom image for each BGO if it exists in .PNG format + * This will rather redundantly reload images that are already in the memory of SMBX; however, there is no workaround for this issue right now. + ********************************************]] +local function loadImg(id) + local imageName = Misc.resolveFile("background-" .. id .. ".png") -- Search level and episode directorties + local image; + if imageName then + image = Graphics.loadImage(imageName) + end + if image then return image; + else return nil; end; +end + +--[[******************************************** + * Set the given BGO to be drawn over if a valid custom image exists + * and it is larger than the original in either dimension. + ********************************************]] +local function setShouldDraw(bgo, image) + if image then + if (image.width ~= bgo.width) or (image.height ~= image.height) then + return true; + else + return false; + end + end +end + +--[[******************************************** + * Get the default render priority for the BGO with the given ID + ********************************************]] +local function getDefault(id) -- Test sets from most likely to least likely, except for the largest set, to avoid as many tests as possible. + for _,v in pairs(defaults.foreground) do + if id == v then + return -20; + end + end + for _,v in pairs(defaults.veryBackBgos) do + if id == v then + return -95; + end + end + for _,v in pairs(defaults.specialBGOs) do + if id == v then + return -80; + end + end + return -85; +end + +--[[******************************************** + * Set the render priority to the default value or to a custom value inputted by the user + * Note: This uses LunaLua render priorities, which are not equivalent to SMBX sort-order priorities! + * This could change the render order from how it is normally. If this occurs, use setPriority to fix it. + * This will only work if called in onStart! + ********************************************]] +function bgofix.setPriority(id, priority) + assert(bgos[id].image, "Cannot change the render priority of BGO-" .. id .. " because it doesn't have a .PNG custom image.") -- Make sure the user didn't break the rules. + if (priority) and not (bgos[id].shouldDraw) then + bgos[id].shouldDraw = true -- Handling for the user changing the render priority of BGOs which are the same size as the original + end + + bgos[id].priority = priority or getDefault(id) -- fetch default setting if custom setting is not given. + Graphics.sprites.background[id].img = blank -- Make the actual BGO invisible. This is to allow the user to make the BGO's render priority lower. +end + +--[[******************************************** + * Resize the given BGO to have the same dimensions as its image + ********************************************]] +local function resize(bgo, image) + if ((image) and (bgo.width ~= image.width or bgo.height ~= image.height)) then + bgo.width = image.width + bgo.height = image.height + end +end + +--[[******************************************** + * Initialization + ********************************************]] +function bgofix.initialize() + + for _,bgo in ipairs(BGO.get()) do + if not bgos[bgo.id] then -- Do this only once for each BGO with a unique ID. + bgos[bgo.id] = {} + bgos[bgo.id].image = loadImg(bgo.id) -- custom image (nil if no valid image) + bgos[bgo.id].shouldDraw = setShouldDraw(bgo, bgos[bgo.id].image); -- Does the BGO need to be rendered over? + + + if bgos[bgo.id].shouldDraw then + bgofix.setPriority(bgo.id) -- LunaLua render priority + end + + end + + if bgos[bgo.id] then + resize(bgo, bgos[bgo.id].image); + end + + end +end + +--[[******************************************** + * Get all BGOs on the screen + * This is why I bothered to resize any BGOs so they were bigger. + * If I hadn't done this, BGOs that still appeared to be onscreen would disappear because the object isn't on the screen. + ********************************************]] +local function getBgosOnScreen() + local bgosOnScreen = {} + for _,cam in pairs(Camera.get()) do -- Check this way so it works in 2P mode + for __,bgo in pairs(BGO.getIntersecting(cam.x, cam.y, cam.x + cam.width, cam.y + cam.height)) do + if not bgo.isHidden then -- Don't insert BGOs that are on hidden layers! + table.insert(bgosOnScreen, bgo) + end + end + end + return bgosOnScreen; +end + +--[[******************************************** + * Set a custom animation + * Animation image is a separate image from the BGO image with the naming style "bgo-anim-*.png," where '*' is the ID of the BGO. + * This may seem like a weird way to do it, but it is so they will display better in the PGE editor. + ********************************************]] +function bgofix.setAnimation(id, numFrames, frameSpeed) + + if not bgos[id].shouldDraw then + bgos[id].shouldDraw = true + bgos[id].priority = getDefault(id) + Graphics.sprites.background[id].img = blank + end + + animData[id] = {} + + local imageName = Misc.resolveFile("bgo-anim-" .. id .. ".png") + if imageName then + animData[id].image = Graphics.loadImage(imageName) + else + error("Animation image for background-" .. id .. " not found.") + end + + animData[id].frames = numFrames or 4 + animData[id].frameSpeed = frameSpeed or 8 -- The number of ticks per frame for the BGO. Default = 8 + animData[id].frame = 0 -- Current frame. Reset to 0 when this == numFrames - 1 + animData[id].tick = 0 -- Tick counter. Reset to 0 and move to the next frame when this == frameSpeed + + assert(animData[id].frames > 0, "Invalid number of frames for BGO-" .. id .. ". Value must be greater than 0.") + assert(animData[id].frameSpeed > 0, "Invalid frame speed value for BGO-" .. id .. ". Value must be greater than 0.") +end + +--[[******************************************** + * Update the frame numbers of the BGOs with custom animations + ********************************************]] +local function updateAnimations() + for id,_ in pairs(animData) do + -- Compact frame controller + -- Why doesn't anyone use the modulus operator?? + animData[id].frame = math.floor(animData[id].tick / animData[id].frameSpeed) + animData[id].tick = (animData[id].tick + 1) % (animData[id].frames * animData[id].frameSpeed) + end +end + +--[[******************************************** + * Draw over the BGOs that are on the screen + ********************************************]] +function bgofix.draw() + -- Draw all BGOs on the screen that are listed for drawing + for _,bgo in pairs(getBgosOnScreen()) do + if bgos[bgo.id].shouldDraw then + if animData[bgo.id] then -- Draw animations for BGOs that have them + Graphics.drawImageToSceneWP(animData[bgo.id].image, bgo.x, bgo.y, 0, bgo.height * animData[bgo.id].frame, bgo.width, bgo.height, bgos[bgo.id].priority) + -- image position source image position dimensions priority + else + Graphics.drawImageToSceneWP(bgos[bgo.id].image, bgo.x, bgo.y, bgos[bgo.id].priority) + end + end + end + updateAnimations(); +end + +return bgofix \ No newline at end of file diff --git a/LuaScriptsLibExt/bgofix/blank.png b/LuaScriptsLibExt/bgofix/blank.png new file mode 100644 index 000000000..65b943f84 Binary files /dev/null and b/LuaScriptsLibExt/bgofix/blank.png differ diff --git a/LuaScriptsLibExt/booBuddies.lua b/LuaScriptsLibExt/booBuddies.lua new file mode 100644 index 000000000..0b79e5344 --- /dev/null +++ b/LuaScriptsLibExt/booBuddies.lua @@ -0,0 +1,325 @@ +--booBuddies.lua +--v1.0.2 +--Created by S1eth, 2016 + +local colliders = API.load("colliders"); + +local booBuddies = {}; + +function booBuddies.onInitAPI() + registerEvent(booBuddies, "onTick", "onTick", true); --Register the loop event + registerEvent(booBuddies, "onDraw", "onDraw", true); --Register the draw event +end + + +--*************************************************************************************************** +-- * +-- UTILITY FUNCTIONS * +-- * +--*************************************************************************************************** + +local function round(x) + return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5); +end + +-- Finds the corresponding section for a point (x,y). (If the point is within 1000 pixel of the section boundary) +local function getSection(x, y) + for index,section in pairs(Section.get()) do + local boundary = section.boundary; + local sectionBox = colliders.Box( + boundary.left - 1000, + boundary.top - 1000, + boundary.right - boundary.left + 1000, + boundary.bottom - boundary.top + 1000 + ) + + if colliders.collide(sectionBox, colliders.Point(x, y)) then + return index-1; -- one-indexed --> zero-indexed + end + end + + return nil; +end + + +--*************************************************************************************************** +-- * +-- PRIVATE CONSTANTS * +-- * +--*************************************************************************************************** + +-- boo types used in SMW's boo buddies +local BOO_TYPES_SMW = {0, 1, 2, 0}; --repeats when more than 4 boos are used + +-- load spritesheet for the boos +local BOO_IMAGE_RESOURCE = Graphics.loadImage(Misc.resolveFile("booBuddies/boos.png")); + +local MOUNT_TYPES = {shoe = 1, yoshi = 3}; + +local style = {SMW = 0, SMB3 = 1, SMB1 = 2, SMM_SMW = 3}; + +local direction = {right = 0, left = 1}; + +local angularDirection = {clockwise = 1, CW = 1, counterclockwise = -1, CCW = -1}; + +local _preset = {}; +_preset.SMW = {}; +_preset.SMW.style = "SMW"; +_preset.SMW.numberOfBoos = 10; +_preset.SMW.booSpacing = 0.483321947; +_preset.SMW.circleRadius = 5 * 32; +_preset.SMW.angularSpeed = math.pi / 275; +_preset.SMW.angularDirection = angularDirection.clockwise; + +_preset.SMM = {}; +_preset.SMM.style = "SMM_SMW"; +_preset.SMM.numberOfBoos = 8; +_preset.SMM.booSpacing = math.pi / 4.5; +_preset.SMM.circleRadius = 3 * 32; +_preset.SMM.angularSpeed = math.pi / 240; +_preset.SMM.angularDirection = angularDirection.clockwise; + + +--*************************************************************************************************** +-- * +-- PUBLIC MEMBERS * +-- * +--*************************************************************************************************** + +booBuddies.diesTo = {shoe = false, starman = false}; + +booBuddies.booNPCID = {SMW = 43, SMB3 = 38, SMB1 = 38, SMM_SMW = 43} + +booBuddies.canSpinjump = false; + +--*************************************************************************************************** +-- * +-- CLASS: Boo * +-- * +--*************************************************************************************************** + +local Boo = {}; +Boo.__index = Boo; +Boo.isAlive = true; + +function Boo.create() + local newInstance = {}; + setmetatable(newInstance, Boo); + + return newInstance; +end + +-- get the graphic rectangle for a specific boo animation frame +function getBooRectangle(styleIndex, direction, booIndex, frameIndex) + local rectangle = { width = 32, height = 32}; + rectangle.left = style[styleIndex] * 96 + ((styleIndex == "SMW") and booIndex * 32 or 0); -- only SMW has multiple boo types (for now?) + rectangle.top = direction * 64 + ((styleIndex == "SMW") and frameIndex * 32 or 0); -- only SMW has animation frames (for now?) + return rectangle; +end + +--*************************************************************************************************** +-- * +-- CLASS: BooCircle * +-- * +--*************************************************************************************************** + +local booCircleTable = {}; + +local BooCircle = {} +BooCircle.__index = BooCircle; +BooCircle.isActive = false; +BooCircle.toBeDeleted = false; +BooCircle.toBeKilled = false; +BooCircle.initial = {}; + +function BooCircle.create(args) + local newInstance = {} -- our new object + setmetatable (newInstance, BooCircle) -- make BooCircle handle lookup + + local preset; + if(args.preset == nil) then + preset = _preset.SMW; + else + assert(_preset[args.preset] ~= nil, "preset "..args.preset.." does not exist"); + preset = _preset[args.preset]; + end + + newInstance.style = args.style or preset.style; + + assert(args.x ~= nil and type(args.x) == "number", "x coordinate must be a number"); + assert(args.y ~= nil and type(args.y) == "number", "y coordinate must be a number"); + newInstance.x = args.x + newInstance.y = args.y + + newInstance.section = args.section or getSection(newInstance.x,newInstance.y); + newInstance.layer = args.layer or "Default"; + + newInstance.circleRadius = args.circleRadius or preset.circleRadius; + newInstance.angularSpeed = args.angularSpeed or preset.angularSpeed; + newInstance.angularDirection = angularDirection[args.angularDirection] or preset.angularDirection; + newInstance.numberOfBoos = args.numberOfBoos or preset.numberOfBoos; + newInstance.booRadius = args.booRadius or 10; + newInstance.frameTime = args.frameTime or 8; + newInstance.frame = args.frame or 0; + newInstance.booSpacing = args.booSpacing or preset.booSpacing; + newInstance.angle = args.angle or math.pi*1.5 - (newInstance.booSpacing * (newInstance.numberOfBoos-1)/2); + + -- reset to these initial values when entering/leaving a section + newInstance.initial.angle = newInstance.angle; + newInstance.initial.frame = newInstance.frame; + + newInstance.boos = {}; + for i=1,newInstance.numberOfBoos,1 do + table.insert(newInstance.boos, Boo.create(booBuddies.diesTo)); + end + + table.insert (booCircleTable, newInstance) + return newInstance; +end + +function BooCircle:update() + + -- Deactivate if not on a visible layer or current section + -- reset the cycle + if Layer.get(self.layer).isHidden + or not(player.section == self.section + or (player2 ~= nil and player2.section == self.section)) + then + if (self.isActive) then + self.isActive = false; + self.angle = self.initial.angle; + self.frame = self.initial.frame; + end + return; + end + + -- Reactivate + if not self.isActive then + self.isActive = true; + end + + --update position + self.angle = self.angle + self.angularSpeed * self.angularDirection; + self.frame = (self.frame + 1) % (self.frameTime * 2); + + --check collision with player + for i,boo in pairs(self.boos) do + + if(boo.isAlive) then + local booX = round(self.x + self.circleRadius * math.cos(self.angle + (i-1) * self.booSpacing)); + local booY = round(self.y + self.circleRadius * math.sin(self.angle + (i-1) * self.booSpacing)); + + local hitbox = colliders.Circle(booX, booY, self.booRadius); + for _,p in pairs({player, player2}) do + + local bounce, spinjump = colliders.bounce(p,hitbox); + local downSlash = colliders.downSlash(p,hitbox); + + if(bounce and p.DeathTimer == 0 and not inStar) then + if(booBuddies.canSpinjump and (spinjump or downSlash or p.MountType == MOUNT_TYPES.yoshi or p.MountType == MOUNT_TYPES.shoe) ) then + colliders.bounceResponse(p); + if(p.MountType == MOUNT_TYPES.shoe and booBuddies.diesTo["shoe"]) then + self:killBoo(boo, booX, booY); + end + else + p:harm(); + end + elseif(colliders.collide(p,hitbox)) then + --check for starman + if(booBuddies.diesTo["starman"] and inStar) then + self:killBoo(boo, booX, booY); + else + p:harm(); + end + end + + end + end + end -- for i,boo in pairs(self.boos) + +end -- BooCircle:update() + +function BooCircle:draw() + + if not self.isActive then return end + + for i,boo in pairs(self.boos) do + if(boo.isAlive) then + local booX = round(self.x + self.circleRadius * math.cos(self.angle + (i-1) * self.booSpacing)); + local booY = round(self.y + self.circleRadius * math.sin(self.angle + (i-1) * self.booSpacing)); + + local dir = direction.right; + if(self.style ~= "SMW") and (player.x + player.width/2 < booX) then + dir = direction.left; + end + + local booRect; + if (self.frame < self.frameTime) then + booRect = getBooRectangle(self.style, dir, BOO_TYPES_SMW[1+((i-1) % 4)], 0); + else + booRect = getBooRectangle(self.style, dir, BOO_TYPES_SMW[1+((i-1) % 4)], 1); + end + + Graphics.drawImageToSceneWP(BOO_IMAGE_RESOURCE, booX-16, booY-16, booRect.left, booRect.top, booRect.width, booRect.height, -45.0); + end + end -- for i,boo in pairs(self.boos) + +end -- BooCircle:draw() + +function BooCircle:delete() + self.toBeDeleted = true; -- delete during next onTick() +end + +function BooCircle:kill() + self.toBeKilled = true; -- kill during next onTick() +end + +function BooCircle:killBoos() + for i,boo in pairs(self.boos) do + if(boo.isAlive) then + local booX = round(self.x + self.circleRadius * math.cos(self.angle + (i-1) * self.booSpacing)); + local booY = round(self.y + self.circleRadius * math.sin(self.angle + (i-1) * self.booSpacing)); + + self:killBoo(boo, booX, booY); + end + end -- for i,boo in pairs(self.boos) +end + +function BooCircle:killBoo(boo, x, y) + boo.isAlive = false; + + NPC.spawn(booBuddies.booNPCID[self.style], x-16, y-16, self.section):kill(); +end + +function booBuddies.create(args) + return BooCircle.create(args); +end + +function booBuddies.onTick() + + --delete circles with the toBeKilled flag + for i=#booCircleTable,1,-1 do + if(booCircleTable[i].toBeKilled == true) then + booCircleTable[i]:killBoos(); + end + end + + --delete instances with the toBeDeleted flag + for i=#booCircleTable,1,-1 do + if(booCircleTable[i].toBeDeleted == true) then + table.remove(booCircleTable, i); + end + end + + for _,booCircle in pairs (booCircleTable) do + booCircle:update(); + end +end + +function booBuddies.onDraw() + for _,booCircle in pairs (booCircleTable) do + booCircle:draw(); + end +end + +return booBuddies; \ No newline at end of file diff --git a/LuaScriptsLibExt/booBuddies/boos.png b/LuaScriptsLibExt/booBuddies/boos.png new file mode 100644 index 000000000..7d80ce7d7 Binary files /dev/null and b/LuaScriptsLibExt/booBuddies/boos.png differ diff --git a/LuaScriptsLibExt/booBuddies/effect-94.png b/LuaScriptsLibExt/booBuddies/effect-94.png new file mode 100644 index 000000000..22407492d Binary files /dev/null and b/LuaScriptsLibExt/booBuddies/effect-94.png differ diff --git a/LuaScriptsLibExt/characters.lua b/LuaScriptsLibExt/characters.lua new file mode 100644 index 000000000..73b7be68d --- /dev/null +++ b/LuaScriptsLibExt/characters.lua @@ -0,0 +1,45 @@ +--characters.lua +--v1.1.2 +--Created by Horikawa Otane, 2015 + +local characters = {} + +local playerCharacters = {} +playerCharacters[1] = "mario" +playerCharacters[2] = "luigi" +playerCharacters[3] = "peach" +playerCharacters[4] = "toad" +playerCharacters[5] = "link" + +local intIds = {} +intIds["mario"] = 1 +intIds["luigi"] = 2 +intIds["peach"] = 3 +intIds["toad"] = 4 +intIds["link"] = 5 + +local playerCharacters = {"mario", "luigi", "peach", "toad", "link"} + + +local function resetCharacter(characterId, powerupId) + loadHitboxes(characterId, powerupId, Misc.resolveFile("character_defaults\\" .. playerCharacters[characterId] .. "-" .. tostring(powerupId) .. ".ini")) +end + +local function loadAllHitBoxes() + for _, butts in pairs(playerCharacters) do + for i = 1, 7, 1 do + local theIniFile = Misc.resolveFile(butts .. "-" .. i .. ".ini") + if theIniFile ~= nil then + loadHitboxes(intIds[butts], i, theIniFile) + else + resetCharacter(intIds[butts], i) + end + end + end +end + +function characters.onInitAPI() + loadAllHitBoxes() +end + +return characters \ No newline at end of file diff --git a/LuaScriptsLibExt/colliders.lua b/LuaScriptsLibExt/colliders.lua index bfebb66da..348ac5139 100644 --- a/LuaScriptsLibExt/colliders.lua +++ b/LuaScriptsLibExt/colliders.lua @@ -1,16 +1,16 @@ --*********************************************-- ---** _____ _ _ _ _ **-- ---** / ____| | | (_) | | **-- ---** | | ___ | | |_ __| | ___ _ __ ___ **-- ---** | | / _ \| | | |/ _` |/ _ \ '__/ __| **-- +--** _____ _ _ _ _ **-- +--** / ____| | | (_) | | **-- +--** | | ___ | | |_ __| | ___ _ __ ___ **-- +--** | | / _ \| | | |/ _` |/ _ \ '__/ __| **-- --** | |___| (_) | | | | (_| | __/ | \__ \ **-- --** \_____\___/|_|_|_|\__,_|\___|_| |___/ **-- ---** **-- ---*********************************************-- +--** **-- +--*********************************************-- -------------Created by Hoeloe - 2015------------ -----Open-Source Collision Detection Library----- --------------For Super Mario Bros X------------- ----------------------v2.1.4g--------------------- +---------------------v2.1.4---------------------- ---------------REQUIRES VECTR.lua---------------- local colliders = {} @@ -25,10 +25,6 @@ local TYPE_CIRCLE = 6; local TYPE_POINT = 7; local TYPE_POLY = 8; local TYPE_TRI = 9; -local COLLIDERS_TYPES = { - TYPE_PLAYER, TYPE_NPC, TYPE_BLOCK, TYPE_ANIM, - TYPE_BOX, TYPE_CIRCLE, TYPE_POINT, TYPE_POLY, TYPE_TRI -}; colliders.BLOCK_SOLID = {}; colliders.BLOCK_SEMISOLID = {8,25,26,27,28,38,69,121,122,123,130,161,168,240,241,242,243,244,245,259,260,261,287,288,289,290,372,373,374,375,379,380,381,382,389,391,392,437,438,439,440,441,442,443,444,445,446,447,448,506,507,508,568,572,579}; @@ -37,24 +33,6 @@ colliders.BLOCK_LAVA = {30,371,404,405,406,420,459,460,461,462,463,464,465,466,4 colliders.BLOCK_HURT = {109,110,267,268,269,407,408,428,429,430,431,511,598}; colliders.BLOCK_PLAYER = {626,627,628,629,632}; -local function MakeBlockIdTestMap(src) - local ret = {} - for i=1,638 do - ret[i] = false - end - for i=1,#src do - ret[src[i]] = true - end - return ret -end - -colliders.BLOCK_SOLID_MAP = {} -colliders.BLOCK_SEMISOLID_MAP = MakeBlockIdTestMap(colliders.BLOCK_SEMISOLID) -colliders.BLOCK_NONSOLID_MAP = MakeBlockIdTestMap(colliders.BLOCK_NONSOLID) -colliders.BLOCK_LAVA_MAP = MakeBlockIdTestMap(colliders.BLOCK_LAVA) -colliders.BLOCK_HURT_MAP = MakeBlockIdTestMap(colliders.BLOCK_HURT) -colliders.BLOCK_PLAYER_MAP = MakeBlockIdTestMap(colliders.BLOCK_PLAYER) - local blockscache = {}; local blockmt = {} @@ -118,7 +96,6 @@ function colliders.onInitAPI() table.insert(colliders.BLOCK_SOLID,i); end end - colliders.BLOCK_SOLID_MAP = MakeBlockIdTestMap(colliders.BLOCK_SOLID) registerEvent(colliders, "onLoop", "update", false) --Register the loop event end @@ -127,8 +104,8 @@ local function getScreenBounds() local h = (player:mem(0xD0, FIELD_DFLOAT)); local b = { left = player.x-400+player.speedX, right = player.x+400+player.speedX, top = player.y-260+player.speedY, bottom = player.y+340+player.speedY }; - local sect = Section(player.section); - local bounds = sect.boundary; + local sect = Section(player.section); + local bounds = sect.boundary; if(b.left < bounds.left - 10) then b.left = bounds.left - 10; @@ -163,28 +140,28 @@ end local colliderList = {}; -local debugList = {}; - -local function colliderSetDebug(object, bool) - if(debugList[object] ~= nil and not bool) then - debugList[object] = nil; - elseif(debugList[object] == nil and bool) then - debugList[object] = object; - end +local debugList = {} + +local function createMeta(t) + local mt = {} + mt.__index = function(obj, key) + if(key=="TYPE") then return t; end; + if(key=="Debug") then + return function(object, bool) + if(debugList[object] ~= nil and not bool) then + debugList[object] = nil; + elseif(debugList[object] == nil and bool) then + debugList[object] = object; + end + end; + end; + end; + return mt; end -local collidersTypeMetatables = {}; -for _,t in ipairs(COLLIDERS_TYPES) do - local mt = {}; - mt.__index = { - TYPE = t, - Debug = colliderSetDebug - }; - collidersTypeMetatables[t] = mt; -end - -do -- Declare Box methods - collidersTypeMetatables[TYPE_BOX].__index.Draw = function(obj, c) +function colliders.Box(x,y,width,height) + local b = { x = x, y = y, width = width, height = height }; + b.Draw = function(obj, c) c = c or 0xFF000099; Graphics.glSetTextureRGBA(nil, c); local pts = {}; @@ -199,10 +176,7 @@ do -- Declare Box methods Graphics.glDrawTriangles(pts, {}, 6); Graphics.glSetTextureRGBA(nil, 0xFFFFFFFF); end -end -function colliders.Box(x,y,width,height) - local b = { x = x, y = y, width = width, height = height }; - setmetatable(b,collidersTypeMetatables[TYPE_BOX]); + setmetatable(b,createMeta(TYPE_BOX)); return b; end @@ -249,8 +223,9 @@ local function circleToTris(obj) return pts; end -do -- Declare Circle methods - collidersTypeMetatables[TYPE_CIRCLE].__index.Draw = function(obj, c) +function colliders.Circle(x,y,radius) + local c = { x = x, y = y, radius = radius }; + c.Draw = function(obj, co) co = co or 0xFF00FF99; Graphics.glSetTextureRGBA(nil, co); local x1,y1 = worldToScreen(obj.x - 10, obj.y + 10); @@ -268,15 +243,13 @@ do -- Declare Circle methods Graphics.glDrawTriangles(pts, {}, (#pts + 1)/2); Graphics.glSetTexture(nil, 0xFFFFFFFF); end -end -function colliders.Circle(x,y,radius) - local c = { x = x, y = y, radius = radius }; - setmetatable(c,collidersTypeMetatables[TYPE_CIRCLE]); + setmetatable(c,createMeta(TYPE_CIRCLE)); return c; end -do -- Declare Point methods - collidersTypeMetatables[TYPE_POINT].__index.Draw = function(obj, c) +function colliders.Point(x,y) + local p = { x = x, y = y }; + p.Draw = function(obj, c) c = c or 0x0099FF99; Graphics.glSetTextureRGBA(nil, c); local pts = {}; @@ -291,22 +264,55 @@ do -- Declare Point methods Graphics.glDrawTriangles(pts, {}, 6); Graphics.glSetTextureRGBA(nil, 0xFFFFFFFF); end -end -function colliders.Point(x,y) - local p = { x = x, y = y }; - setmetatable(p,collidersTypeMetatables[TYPE_POINT]); + setmetatable(p,createMeta(TYPE_POINT)); return p; end -do -- Declare Tri methods - collidersTypeMetatables[TYPE_TRI].__index.Get = function(obj, index) +function colliders.Tri(x,y,p1,p2,p3) + local p = { x=x, y=y, v={p1,p2,p3} }; + + local winding = 0; + for k,v in ipairs(p.v) do + if(v[1] == nil or v[2] == nil) then + error("Invalid polygon definition.", 2); + end + + --Calculate winding order. + local n = k+1; + local pr = k-1; + if(n > 3) then n = 1; end + if(pr <= 0) then pr = 3; end + winding = winding + (v[1]+p.v[n][1])*(v[2]-p.v[n][2]); + + if(p.minX == nil or v[1] < p.minX) then + p.minX = v[1]; + end + if(p.maxX == nil or v[1] > p.maxX) then + p.maxX = v[1]; + end + if(p.minY == nil or v[2] < p.minY) then + p.minY = v[2]; + end + if(p.maxY == nil or v[2] > p.maxY) then + p.maxY = v[2]; + end + end + + --If winding order is anticlockwise, triangulation will fail, so reverse vertex list in that case. + if(winding > 0) then + local pv = p.v[1]; + p.v[1] = p.v[3]; + p.v[3] = pv; + end + + p.Get = function(obj, index) if(index < 1 or index >= 4) then error("Invalid triangle index.", 2); end return { obj.v[index][1]+obj.x, obj.v[index][2]+obj.y }; end - collidersTypeMetatables[TYPE_TRI].__index.Rotate = function(obj, angle) + p.Rotate = function(obj, angle) local s = math.sin(math.pi*angle/180); local c = math.cos(math.pi*angle/180); @@ -322,7 +328,7 @@ do -- Declare Tri methods obj.maxY = t.maxY; end - collidersTypeMetatables[TYPE_TRI].__index.Translate = function(obj, x, y) + p.Translate = function(obj, x, y) for i=1,3 do obj.v[i] = {obj.v[i][1]+x, obj.v[i][2]+y}; end @@ -332,7 +338,7 @@ do -- Declare Tri methods obj.maxY = obj.maxY + y; end - collidersTypeMetatables[TYPE_TRI].__index.Scale = function(obj, x, y) + p.Scale = function(obj, x, y) y = y or x; for i=1,3 do obj.v[i] = {obj.v[i][1]*x, obj.v[i][2]*y}; @@ -343,7 +349,7 @@ do -- Declare Tri methods obj.maxY = obj.maxY*y; end - collidersTypeMetatables[TYPE_TRI].__index.Draw = function(obj, c) + p.Draw = function(obj, c) c = c or 0x00FF0099; Graphics.glSetTextureRGBA(nil, c); local pts = {}; @@ -355,61 +361,25 @@ do -- Declare Tri methods Graphics.glDrawTriangles(pts, {}, 3); Graphics.glSetTextureRGBA(nil, 0xFFFFFFFF); end -end -function colliders.Tri(x,y,p1,p2,p3) - local p = { x=x, y=y, v={p1,p2,p3} }; - - local winding = 0; - for k,v in ipairs(p.v) do - if(v[1] == nil or v[2] == nil) then - error("Invalid polygon definition.", 2); - end - - --Calculate winding order. - local n = k+1; - local pr = k-1; - if(n > 3) then n = 1; end - if(pr <= 0) then pr = 3; end - winding = winding + (v[1]+p.v[n][1])*(v[2]-p.v[n][2]); - - if(p.minX == nil or v[1] < p.minX) then - p.minX = v[1]; - end - if(p.maxX == nil or v[1] > p.maxX) then - p.maxX = v[1]; - end - if(p.minY == nil or v[2] < p.minY) then - p.minY = v[2]; - end - if(p.maxY == nil or v[2] > p.maxY) then - p.maxY = v[2]; - end - end - - --If winding order is anticlockwise, triangulation will fail, so reverse vertex list in that case. - if(winding > 0) then - local pv = p.v[1]; - p.v[1] = p.v[3]; - p.v[3] = pv; - end - setmetatable(p,collidersTypeMetatables[TYPE_TRI]); + setmetatable(p,createMeta(TYPE_TRI)); return p; end -local __typeStringMap = {Player=TYPE_PLAYER, Block=TYPE_BLOCK, Animation=TYPE_ANIM, NPC=TYPE_NPC} local function getType(obj) if(obj.TYPE ~= nil) then return obj.TYPE; + elseif(obj.powerup ~= nil) then + return TYPE_PLAYER; + elseif(obj.slippery ~= nil) then + return TYPE_BLOCK; + elseif(obj.timer ~= nil) then + return TYPE_ANIM; + elseif(obj.id ~= nil) then + return TYPE_NPC; + else + error("Unknown collider type.", 2); end - local t1 = obj.__type - if (t1 ~= nil) then - local t2 = __typeStringMap[t1] - if (t2 ~= nil) then - return t2 - end - end - error("Unknown collider type.", 2); end local function convertPoints(p) @@ -566,48 +536,6 @@ local function testTriPoly(a,b) return false; end -do -- Declare Poly methods - collidersTypeMetatables[TYPE_POLY].__index.Rotate = function(obj, angle) - for k,v in ipairs(obj.tris) do - v:Rotate(angle); - if(v.minX < obj.minX) then obj.minX = v.minX; end - if(v.maxX > obj.maxX) then obj.maxX = v.maxX; end - if(v.minY < obj.minY) then obj.minY = v.minY; end - if(v.maxY > obj.maxY) then obj.maxY = v.maxY; end - end - end - - collidersTypeMetatables[TYPE_POLY].__index.Translate = function(obj, x, y) - for k,v in ipairs(obj.tris) do - v:Translate(x,y); - end - obj.minX = obj.minX + x; - obj.maxX = obj.maxX + x; - obj.minY = obj.minY + y; - obj.maxY = obj.maxY + y; - end - - collidersTypeMetatables[TYPE_POLY].__index.Scale = function(obj, x, y) - y = y or x; - for k,v in ipairs(obj.tris) do - v:Scale(x,y); - end - obj.minX = obj.minX*x; - obj.maxX = obj.maxX*x; - obj.minY = obj.minY*y; - obj.maxY = obj.maxY*y; - end - - collidersTypeMetatables[TYPE_POLY].__index.Draw = function(obj, c) - c = c or 0x0000FF99; - for _,v in ipairs(obj.tris) do - v.x = obj.x; - v.y = obj.y; - Graphics.glSetTextureRGBA(nil, c); - v:Draw(c); - end - end -end function colliders.Poly(x,y,...) local arg = {...}; @@ -706,7 +634,48 @@ function colliders.Poly(x,y,...) p.tris = trilist; - setmetatable(p,collidersTypeMetatables[TYPE_POLY]) + p.Rotate = function(obj, angle) + for k,v in ipairs(obj.tris) do + v:Rotate(angle); + if(v.minX < obj.minX) then obj.minX = v.minX; end + if(v.maxX > obj.maxX) then obj.maxX = v.maxX; end + if(v.minY < obj.minY) then obj.minY = v.minY; end + if(v.maxY > obj.maxY) then obj.maxY = v.maxY; end + end + end + + p.Translate = function(obj, x, y) + for k,v in ipairs(obj.tris) do + v:Translate(x,y); + end + obj.minX = obj.minX + x; + obj.maxX = obj.maxX + x; + obj.minY = obj.minY + y; + obj.maxY = obj.maxY + y; + end + + p.Scale = function(obj, x, y) + y = y or x; + for k,v in ipairs(obj.tris) do + v:Scale(x,y); + end + obj.minX = obj.minX*x; + obj.maxX = obj.maxX*x; + obj.minY = obj.minY*y; + obj.maxY = obj.maxY*y; + end + + p.Draw = function(obj, c) + c = c or 0x0000FF99; + for _,v in ipairs(obj.tris) do + v.x = obj.x; + v.y = obj.y; + Graphics.glSetTextureRGBA(nil, c); + v:Draw(c); + end + end + + setmetatable(p,createMeta(TYPE_POLY)) return p; end @@ -804,27 +773,25 @@ local function testPolyCircle(a,b) return false; end - -local function linecast_internal(a,sp,v1,b,ep,v2,dx,dy,aabb,collider,maxSqrDist) +function colliders.linecast(startPoint,endPoint,collider) + + local a,sp,v1 = convertPoints(startPoint); + local b,ep,v2 = convertPoints(endPoint); + local c1n = collider[1] ~= nil; local c0n = collider[0] ~= nil; + if(collider[1] ~= nil) then local hit; local norm; local col; - for _,v in pairs(collider) do - local bl,pt,nm,_,sqrDist = linecast_internal(a,sp,v1,b,ep,v2,dx,dy,aabb,v,maxSqrDist); - if(bl) then - if (sqrDist == nil) then - sqrDist = (pt-v1).sqrlength; - end - if(sqrDist < maxSqrDist) then - maxSqrDist = sqrDist; - hit = pt; - norm = nm; - col = v; - end + for _,v in pairs(collider) do + local bl,pt,nm = colliders.linecast(startPoint,endPoint,v); + if(bl and (hit == nil or (pt-v1).sqrlength < (hit-v1).sqrlength)) then + hit = pt; + norm = nm; + col = v; end end if(hit ~= nil) then @@ -836,7 +803,7 @@ local function linecast_internal(a,sp,v1,b,ep,v2,dx,dy,aabb,collider,maxSqrDist) local c = colliders.getHitbox(collider); - + local aabb = colliders.Box(math.min(v1.x,v2.x),math.min(v1.y,v2.y),math.abs(v2.x-v1.x),math.abs(v2.y-v1.y)); local cbb = colliders.getAABB(collider); if(not colliders.collide(aabb,cbb)) then @@ -846,7 +813,7 @@ local function linecast_internal(a,sp,v1,b,ep,v2,dx,dy,aabb,collider,maxSqrDist) if(sp.x == ep.x and sp.y == ep.y) then local b = colliders.collide(sp,c); if(b) then - return true, v1, vect.zero2, collider; + return true, vect.v2(sp), vect.zero2, collider; else return false, nil, nil, nil; end @@ -864,68 +831,41 @@ local function linecast_internal(a,sp,v1,b,ep,v2,dx,dy,aabb,collider,maxSqrDist) or intersect(a,b,{c.x+c.width,c.y},{c.x+c.width,c.y+c.height}) or intersect(a,b,{c.x+c.width,c.y+c.height},{c.x,c.y+c.height}) or intersect(a,b,{c.x,c.y+c.height},{c.x,c.y}));--]] - - local hit,nm,col,p; - local top = c.y - local right = c.x + c.width - local bottom = c.y + c.height - local left = c.x - if (dy > 0) and ((top - a[2])*(top - a[2]) < maxSqrDist) then - col,p = intersectpoint(a,b,{left,top},{right,top}); -- Top - if(col) then - local sqrDist = (p-v1).sqrlength - if (hit == nil) or (sqrDist < maxSqrDist) then - maxSqrDist = sqrDist; - hit = p; - nm = -vect.up2; - end - end + local hit,nm; + local col,p = intersectpoint(a,b,{c.x,c.y},{c.x+c.width,c.y}); + if(col) then + hit = p; + nm = -vect.up2; end - if (dx < 0) and ((right - a[1])*(right - a[1]) < maxSqrDist) then - col,p = intersectpoint(a,b,{right,top},{right,bottom}) -- Right - if(col) then - local sqrDist = (p-v1).sqrlength - if (hit == nil) or (sqrDist < maxSqrDist) then - maxSqrDist = sqrDist; - hit = p; - nm = vect.right2; - end - end + col,p = intersectpoint(a,b,{c.x+c.width,c.y},{c.x+c.width,c.y+c.height}) + if(col and (hit == nil or (p-v1).sqrlength < (hit-v1).sqrlength)) then + hit = p; + nm = vect.right2; end - if (dy < 0) and ((bottom - a[2])*(bottom - a[2]) < maxSqrDist) then - col,p = intersectpoint(a,b,{left,bottom},{right,bottom}) -- Bottom - if(col) then - local sqrDist = (p-v1).sqrlength - if (hit == nil) or (sqrDist < maxSqrDist) then - maxSqrDist = sqrDist; - hit = p; - nm = vect.up2; - end - end + col,p = intersectpoint(a,b,{c.x+c.width,c.y+c.height},{c.x,c.y+c.height}) + if(col and (hit == nil or (p-v1).sqrlength < (hit-v1).sqrlength)) then + hit = p; + nm = vect.up2; end - if (dx > 0) and ((left - a[1])*(left - a[1]) < maxSqrDist) then - col,p = intersectpoint(a,b,{left,top},{left,bottom}) -- Left - if(col) then - local sqrDist = (p-v1).sqrlength - if (hit == nil) or (sqrDist < maxSqrDist) then - maxSqrDist = sqrDist; - hit = p; - nm = -vect.right2; - end - end + col,p = intersectpoint(a,b,{c.x,c.y+c.height},{c.x,c.y}) + if(col and (hit == nil or (p-v1).sqrlength < (hit-v1).sqrlength)) then + hit = p; + nm = -vect.right2; end if(hit ~= nil) then - return true, hit, nm, collider, maxSqrDist; + return true, hit, nm, collider; else - return false, nil, nil, nil, nil; + return false, nil, nil, nil; end elseif(t == TYPE_CIRCLE) then local centre = vect.v2(c.x,c.y); local t1 = v1-centre; local t2 = v2-centre; - local drsqr = dx*dx + dy*dy; + local dx = t2.x-t1.x; + local dy = t2.y-t1.y; + local dr = math.sqrt(dx*dx + dy*dy); local D = t1.x*t2.y - t2.x*t1.y; - local delta = c.radius*c.radius*drsqr - D*D; + local delta = c.radius*c.radius*dr*dr - D*D; if(delta < 0) then return false, nil, nil, nil; else @@ -933,10 +873,10 @@ local function linecast_internal(a,sp,v1,b,ep,v2,dx,dy,aabb,collider,maxSqrDist) if(dy < 0) then sdy=-1; else sdy = 1; end local qx = sdy*dx*math.sqrt(delta); local qy = math.abs(dy)*math.sqrt(delta); - local px1 = (D*dy + qx)/drsqr; - local px2 = (D*dy - qx)/drsqr; - local py1 = (-D*dx + qy)/drsqr; - local py2 = (-D*dx - qy)/drsqr; + local px1 = (D*dy + qx)/(dr*dr); + local px2 = (D*dy - qx)/(dr*dr); + local py1 = (-D*dx + qy)/(dr*dr); + local py2 = (-D*dx - qy)/(dr*dr); local p1 = vect.v2(px1,py1); local p2 = vect.v2(px2,py2); if((p2-t1).sqrlength < (p1-t1).sqrlength) then @@ -980,13 +920,13 @@ local function linecast_internal(a,sp,v1,b,ep,v2,dx,dy,aabb,collider,maxSqrDist) end return false;]] local bb = colliders.Box(c.minX+c.x, c.minY+c.y, c.maxX-c.minX, c.maxY-c.minY); - if(not linecast_internal(a,sp,v1,b,ep,v2,dx,dy,aabb,bb,maxSqrDist)) then return false,nil,nil,nil; end + if(not colliders.linecast(startPoint, endPoint, bb)) then return false,nil,nil,nil; end local hit; local norm; for k,v in ipairs(c.tris) do - local ht,pt,nm = linecast_internal(a,sp,v1,b,ep,v2,dx,dy,aabb,v,maxSqrDist); + local ht,pt,nm = colliders.linecast(startPoint,endPoint,v); if(ht and (hit == nil or (pt-v1).sqrlength < (hit-v1).sqrlength)) then hit = pt; norm = nm; @@ -1001,7 +941,7 @@ local function linecast_internal(a,sp,v1,b,ep,v2,dx,dy,aabb,collider,maxSqrDist) elseif(t == TYPE_TRI) then local bb = colliders.Box(c.minX+c.x, c.minY+c.y, c.maxX-c.minX, c.maxY-c.minY); - if(not linecast_internal(a,sp,v1,b,ep,v2,dx,dy,aabb,bb,maxSqrDist)) then return false,nil,nil,nil; end + if(not colliders.linecast(startPoint, endPoint, bb)) then return false,nil,nil,nil; end local hit; local dir; @@ -1026,63 +966,57 @@ local function linecast_internal(a,sp,v1,b,ep,v2,dx,dy,aabb,collider,maxSqrDist) end end -function colliders.linecast(startPoint,endPoint,collider) - local a,sp,v1 = convertPoints(startPoint); - local b,ep,v2 = convertPoints(endPoint); - local aabb = colliders.Box(math.min(v1.x,v2.x),math.min(v1.y,v2.y),math.abs(v2.x-v1.x),math.abs(v2.y-v1.y)); - local dx = b[1] - a[1]; - local dy = b[2] - a[2]; - return linecast_internal(a,sp,v1,b,ep,v2,dx,dy,aabb,collider,dx*dx+dy*dy); -end - function colliders.raycast(startPoint,direction,collider) local _,_,sp = convertPoints(startPoint); local _,_,dir = convertPoints(direction); return colliders.linecast(sp,sp-dir,collider); end --- /| --- /_| ---Slope bottomleft to topright floor -local getBlockHitbox_lrslope_floor = {452,321,365,316,357,358,306,305,302,616,299,340,341,472,480,636,635,326,324,604,600,332}; - --- |\ --- |_\ ---Slope topleft to bottomright floor -local getBlockHitbox_rlslope_floor = {451,319,366,315,359,360,308,307,301,617,300,343,342,474,482,638,637,327,325,601,605,333}; - --- |-/ --- |/ ---Slope bottomleft to topright ceil -local getBlockHitbox_lrslope_ceil = {318,367,363,364,314,313,310,479,485,328,614,613,334}; - --- \-| --- \| ---Slope topleft to bottomright ceil -local getBlockHitbox_rlslope_ceil = {317,368,361,362,312,311,309,476,486,329,77,78,335}; - --- [][] --- ---Vertical half block -local getBlockHitbox_uphalf = {289,168,69}; - --- Create mappings for fast access -local getBlockHitbox_lrslope_floor_map = MakeBlockIdTestMap(getBlockHitbox_lrslope_floor) -local getBlockHitbox_rlslope_floor_map = MakeBlockIdTestMap(getBlockHitbox_rlslope_floor) -local getBlockHitbox_lrslope_ceil_map = MakeBlockIdTestMap(getBlockHitbox_lrslope_ceil) -local getBlockHitbox_rlslope_ceil_map = MakeBlockIdTestMap(getBlockHitbox_rlslope_ceil) -local getBlockHitbox_uphalf_map = MakeBlockIdTestMap(getBlockHitbox_uphalf) - local function getBlockHitbox(id, x, y, wid, hei) - if getBlockHitbox_lrslope_floor_map[id] then --Slope bottomleft to topright floor + + -- /| + -- /_| + --Slope bottomleft to topright floor + local lrslope_floor = {452,321,365,316,357,358,306,305,302,616,299,340,341,472,480,636,635,326,324,604,600,332}; + + -- |\ + -- |_\ + --Slope topleft to bottomright floor + local rlslope_floor = {451,319,366,315,359,360,308,307,301,617,300,343,342,474,482,638,637,327,325,601,605,333}; + + -- |-/ + -- |/ + --Slope bottomleft to topright ceil + local lrslope_ceil = {318,367,363,364,314,313,310,479,485,328,614,613,334}; + + -- \-| + -- \| + --Slope topleft to bottomright ceil + local rlslope_ceil = {317,368,361,362,312,311,309,476,486,329,77,78,335}; + + -- [][] + -- + --Vertical half block + local uphalf = {289,168,69}; + + local isAny = function(tbl, val) + for _,v in ipairs(tbl) do + if v == val then + return true; + end + end + return false; + end + + if isAny(lrslope_floor,id) then --Slope bottomleft to topright floor return colliders.Tri(x,y,{0,hei},{wid,0},{wid,hei}); - elseif getBlockHitbox_rlslope_floor_map[id] then --Slope topleft to bottomright floor + elseif isAny(rlslope_floor,id) then --Slope topleft to bottomright floor return colliders.Tri(x,y,{0,0},{wid,hei},{0,hei}); - elseif getBlockHitbox_lrslope_ceil_map[id] then --Slope bottomleft to topright ceil + elseif isAny(lrslope_ceil,id) then --Slope bottomleft to topright ceil return colliders.Tri(x,y,{0,0},{wid,0},{0,hei}); - elseif getBlockHitbox_rlslope_ceil_map[id] then --Slope topleft to bottomright ceil + elseif isAny(rlslope_ceil,id) then --Slope topleft to bottomright ceil return colliders.Tri(x,y,{0,0},{wid,0},{wid,hei}); - elseif getBlockHitbox_uphalf_map[id] then --Vertical half block + elseif isAny(uphalf,id) then --Vertical half block return colliders.Box(x,y,wid,hei/2); else return colliders.Box(x,y,wid,hei); @@ -1090,11 +1024,12 @@ local function getBlockHitbox(id, x, y, wid, hei) end function colliders.getAABB(a) + local ta = getType(a); + + if(a.TYPE == TYPE_BOX) then return a; end - - local ta = getType(a); if(ta == TYPE_BLOCK or ta == TYPE_PLAYER or ta == TYPE_NPC or ta == TYPE_ANIM) then return colliders.getHitbox(a); @@ -1108,12 +1043,12 @@ function colliders.getAABB(a) end function colliders.getHitbox(a) + local ta = getType(a); + if(a.TYPE ~= nil) then return a; end - local ta = getType(a); - if(ta == TYPE_BLOCK) then return getBlockHitbox(a.id, a.x, a.y, a.width, a.height); elseif(ta == TYPE_PLAYER) then diff --git a/LuaScriptsLibExt/deathTracker.lua b/LuaScriptsLibExt/deathTracker.lua new file mode 100644 index 000000000..b1d7523aa --- /dev/null +++ b/LuaScriptsLibExt/deathTracker.lua @@ -0,0 +1,102 @@ +--[[ +·▄▄▄▄ ▄▄▄ . ▄▄▄· ▄▄▄▄▄ ▄ .▄▄▄▄▄▄▄▄▄ ▄▄▄· ▄▄· ▄ •▄ ▄▄▄ .▄▄▄ ▄▄▌ ▄• ▄▌ ▄▄▄· +██▪ ██ ▀▄.▀·▐█ ▀█ •██ ██▪▐█•██ ▀▄ █·▐█ ▀█ ▐█ ▌▪█▌▄▌▪▀▄.▀·▀▄ █· ██• █▪██▌▐█ ▀█ +▐█· ▐█▌▐▀▀▪▄▄█▀▀█ ▐█.▪██▀▐█ ▐█.▪▐▀▀▄ ▄█▀▀█ ██ ▄▄▐▀▀▄·▐▀▀▪▄▐▀▀▄ ██▪ █▌▐█▌▄█▀▀█ +██. ██ ▐█▄▄▌▐█ ▪▐▌ ▐█▌·██▌▐▀ ▐█▌·▐█•█▌▐█ ▪▐▌▐███▌▐█.█▌▐█▄▄▌▐█•█▌ ▐█▌▐▌▐█▄█▌▐█ ▪▐▌ +▀▀▀▀▀• ▀▀▀ ▀ ▀ ▀▀▀ ▀▀▀ · ▀▀▀ .▀ ▀ ▀ ▀ ·▀▀▀ ·▀ ▀ ▀▀▀ .▀ ▀ ▀ .▀▀▀ ▀▀▀ ▀ ▀ +--V. 1.0 +--written by Enjl +]] +local lvlDeath = Data(Data.DATA_LEVEL, "deathTracker", true) + +local lastPlayerX = 0 +local lastPlayerY = 0 +local deathCounter = 0 + +local hasDied = false + +local deathTracker = {} + +local shownIcons = {} + +deathTracker.ICON_STANDARD = Graphics.loadImage(Misc.resolveFile("deathTracker\\deathIcon.png")) +deathTracker.ICON_STANDARD2X = Graphics.loadImage(Misc.resolveFile("deathTracker\\deathIcon2x.png")) +deathTracker.ICON_ARROW = Graphics.loadImage(Misc.resolveFile("deathTracker\\arrowIcon.png")) +deathTracker.ICON_ARROW2X = Graphics.loadImage(Misc.resolveFile("deathTracker\\arrowIcon2x.png")) +deathTracker.ICON_PLAYER1 = Graphics.loadImage(Misc.resolveFile("deathTracker\\player1.png")) +deathTracker.ICON_PLAYER2 = Graphics.loadImage(Misc.resolveFile("deathTracker\\player2.png")) +deathTracker.ICON_PLAYER3 = Graphics.loadImage(Misc.resolveFile("deathTracker\\player3.png")) +deathTracker.ICON_PLAYER4 = Graphics.loadImage(Misc.resolveFile("deathTracker\\player4.png")) +deathTracker.ICON_PLAYER5 = Graphics.loadImage(Misc.resolveFile("deathTracker\\player5.png")) +deathTracker.ICON_TRUMP = Graphics.loadImage(Misc.resolveFile("deathTracker\\trump.png")) +deathTracker.ICON_SWEAT = Graphics.loadImage(Misc.resolveFile("deathTracker\\sweat smile.png")) +deathTracker.ICON_CAT = Graphics.loadImage(Misc.resolveFile("deathTracker\\cat.png")) + +deathTracker.iconSprite = deathTracker.ICON_STANDARD2X + +local function iconHandler() + for i=1, deathCounter do + local entry = {} + entry.x = tonumber(lvlDeath:get("deathX" .. tostring(i))) + entry.y = tonumber(lvlDeath:get("deathY" .. tostring(i))) + entry.timer = 0 + entry.opacity = 0 + entry.sprite = deathTracker.iconSprite + table.insert(shownIcons, entry) + end +end + +function deathTracker.onInitAPI() + registerEvent(deathTracker, "onTick", "onTick", false) + registerEvent(deathTracker, "onDraw", "onDraw", false) + registerEvent(deathTracker, "onStart", "onStart", false) +end + +function deathTracker.onStart() + if lvlDeath:get("deaths") == nil then + lvlDeath:set("deaths", 0) + lvlDeath:save() + end + deathCounter = tonumber(lvlDeath:get("deaths")) or 0 +end + +function deathTracker.onDraw() + local cam = Camera.get()[1] + for k,v in ipairs(shownIcons) do + if v.x + 0.5 * v.sprite.width > cam.x and v.x -0.5 * v.sprite.width < cam.x + 800 and v.y + 0.5 * v.sprite.height > cam.y and v.y -0.5 * v.sprite.height < cam.y + 600 then + Graphics.drawImageToScene(v.sprite, v.x - 0.5 * v.sprite.width, v.y - v.sprite.height, v.opacity) + end + end +end + +function deathTracker.onTick() + local cam = Camera.get()[1] + --track player position for onscreen depiction of offscreen deaths + if player.x + player.width > cam.x and player.x < cam.x + 800 then + lastPlayerX = player.x + 0.5 * player.width + end + if player.y + player.height > cam.y and player.y < cam.y + 600 then + lastPlayerY = player.y + 0.5 * player.height + end + --add to counter + if player:mem(0x13E, FIELD_WORD) > 0 then + if hasDied == false then + hasDied = true + deathCounter = deathCounter + 1 + lvlDeath:set("deaths", tostring(deathCounter)) + lvlDeath:set("deathX" .. tostring(deathCounter), tostring(lastPlayerX)) + lvlDeath:set("deathY" .. tostring(deathCounter), tostring(lastPlayerY)) + lvlDeath:save() + iconHandler() + end + for k,v in ipairs(shownIcons) do + v.timer = v.timer + 1 + if v.timer > 50 then + v.opacity = v.opacity + 0.1 + end + end + end +end + +return deathTracker; +--cat planet cat planet \ No newline at end of file diff --git a/LuaScriptsLibExt/deathTracker/arrowIcon.png b/LuaScriptsLibExt/deathTracker/arrowIcon.png new file mode 100644 index 000000000..21c3cc3aa Binary files /dev/null and b/LuaScriptsLibExt/deathTracker/arrowIcon.png differ diff --git a/LuaScriptsLibExt/deathTracker/arrowIcon2x.png b/LuaScriptsLibExt/deathTracker/arrowIcon2x.png new file mode 100644 index 000000000..df3d99ddc Binary files /dev/null and b/LuaScriptsLibExt/deathTracker/arrowIcon2x.png differ diff --git a/LuaScriptsLibExt/deathTracker/cat.png b/LuaScriptsLibExt/deathTracker/cat.png new file mode 100644 index 000000000..74426b554 Binary files /dev/null and b/LuaScriptsLibExt/deathTracker/cat.png differ diff --git a/LuaScriptsLibExt/deathTracker/deathIcon.png b/LuaScriptsLibExt/deathTracker/deathIcon.png new file mode 100644 index 000000000..19a98bad8 Binary files /dev/null and b/LuaScriptsLibExt/deathTracker/deathIcon.png differ diff --git a/LuaScriptsLibExt/deathTracker/deathIcon2x.png b/LuaScriptsLibExt/deathTracker/deathIcon2x.png new file mode 100644 index 000000000..1ed7aef72 Binary files /dev/null and b/LuaScriptsLibExt/deathTracker/deathIcon2x.png differ diff --git a/LuaScriptsLibExt/deathTracker/player1.png b/LuaScriptsLibExt/deathTracker/player1.png new file mode 100644 index 000000000..2523f35c2 Binary files /dev/null and b/LuaScriptsLibExt/deathTracker/player1.png differ diff --git a/LuaScriptsLibExt/deathTracker/player2.png b/LuaScriptsLibExt/deathTracker/player2.png new file mode 100644 index 000000000..0162005fe Binary files /dev/null and b/LuaScriptsLibExt/deathTracker/player2.png differ diff --git a/LuaScriptsLibExt/deathTracker/player3.png b/LuaScriptsLibExt/deathTracker/player3.png new file mode 100644 index 000000000..2176a24a5 Binary files /dev/null and b/LuaScriptsLibExt/deathTracker/player3.png differ diff --git a/LuaScriptsLibExt/deathTracker/player4.png b/LuaScriptsLibExt/deathTracker/player4.png new file mode 100644 index 000000000..9467dbcd1 Binary files /dev/null and b/LuaScriptsLibExt/deathTracker/player4.png differ diff --git a/LuaScriptsLibExt/deathTracker/player5.png b/LuaScriptsLibExt/deathTracker/player5.png new file mode 100644 index 000000000..164371c8c Binary files /dev/null and b/LuaScriptsLibExt/deathTracker/player5.png differ diff --git a/LuaScriptsLibExt/deathTracker/sweat smile.png b/LuaScriptsLibExt/deathTracker/sweat smile.png new file mode 100644 index 000000000..b8848b2b7 Binary files /dev/null and b/LuaScriptsLibExt/deathTracker/sweat smile.png differ diff --git a/LuaScriptsLibExt/deathTracker/trump.png b/LuaScriptsLibExt/deathTracker/trump.png new file mode 100644 index 000000000..11edbed30 Binary files /dev/null and b/LuaScriptsLibExt/deathTracker/trump.png differ diff --git a/LuaScriptsLibExt/debug.lua b/LuaScriptsLibExt/debug.lua new file mode 100644 index 000000000..61a8635c7 --- /dev/null +++ b/LuaScriptsLibExt/debug.lua @@ -0,0 +1,68 @@ +--debug.lua +--By PixelPest +--1.2 + + +local debug = {}; +local debugTable = {}; + + +function debug.onInitAPI() + registerEvent(debug, "onTickEnd", "onTickEnd", false); +end + + +function debug.type(obj, heading) + local h = heading or ""; + table.insert(debugTable, {h, type(obj)}); +end + + +function debug.simple(obj, heading) + local h = heading or ""; + + if (type(obj) ~= "string") and (type(obj) ~= "number") and (type(obj) ~= "boolean") and (type(obj) ~= "nil") then + error("Variable defined by "..h.." is not considered simple (string nor boolean nor number nor nil)."); + end + + table.insert(debugTable, {h, obj}); +end + +function debug.table(obj, heading) + local h = heading or ""; + + if obj == nil then + table.insert(debugTable, {h, "nil"}); + elseif type(obj) ~= "table" then + error("Variable defined by "..h.." is not a table."); + else + for k, v in pairs(obj) do + table.insert(debugTable, {h.."["..k.."]", v}) + end + end +end + +function debug.onTickEnd() + if #debugTable > 30 then + error("Too many lines (>30)."); + end + + local yCounter = 0; + + for _, v in pairs(debugTable) do + if v[1] == "" then + Text.printWP(tostring(v[2]), 0, yCounter, 10); + else + Text.printWP(v[1]..": "..tostring(v[2]), 0, yCounter, 10); + end + + yCounter = yCounter + 20; + end + + debugTable = {}; +end + + +return debug + + diff --git a/LuaScriptsLibExt/eventu.lua b/LuaScriptsLibExt/eventu.lua index fd65b9a8e..0dbd21259 100644 --- a/LuaScriptsLibExt/eventu.lua +++ b/LuaScriptsLibExt/eventu.lua @@ -1,6 +1,20 @@ ---eventu.lua ---v1.0.4 ---Created by Hoeloe, 2015 +----------------------------------------- +----------------------------------------- +--*************************************-- +--* ______ _ _ _ *-- +--* | ____| | | | | | | *-- +--* | |____ _____ _ __ | |_| | | | *-- +--* | __\ \ / / _ \ '_ \| __| | | | *-- +--* | |___\ V / __/ | | | |_| |__| | *-- +--* |______\_/ \___|_| |_|\__|\____/ *-- +--* *-- +--*************************************-- +----------------------------------------- +----------------------------------------- +--------Created by Hoeloe - 2015--------- +------Open-Source Coroutine Library------ +---------For Super Mario Bros X---------- +-----------------v1.1.0------------------ local eventu = {} local secondsQueue = {} @@ -163,6 +177,7 @@ function eventu.waitEvent(name) end function eventu.signal(name) + if(signalQueue[name] == nil) then return end; local waketable = {} for k,v in pairs(signalQueue[name]) do signalQueue[name][k] = nil; @@ -175,9 +190,12 @@ function eventu.signal(name) end end -function eventu.setTimer(secs, func, repeated) - repeated = repeated or false; - local _,c = eventu.run(function() +function eventu.setTimer(secs, func, repeated) + local f; + if(repeated == nil or type(repeated) ~= "number") then + repeated = repeated or false; + + f = function() repeat eventu.waitSeconds(secs); func(); @@ -187,13 +205,30 @@ function eventu.setTimer(secs, func, repeated) repeated = false; end until repeated == false; - end); + end + else + f = function() + for i=1,repeated do + eventu.waitSeconds(secs); + func(i); + local co = getCurrentCoroutine(); + if(breakQueue[co] == true) then + breakQueue[co] = nil; + break; + end + end + end + end + local _,c = eventu.run(f); return c; end function eventu.setFrameTimer(frames, func, repeated) - repeated = repeated or false; - local _,c = eventu.run(function() + local f; + if(repeated == nil or type(repeated) ~= "number") then + repeated = repeated or false; + + f = function() repeat eventu.waitFrames(frames); func(); @@ -203,7 +238,22 @@ function eventu.setFrameTimer(frames, func, repeated) repeated = false; end until repeated == false; - end); + end + + else + f = function() + for i = 1,repeated do + eventu.waitFrames(frames); + func(i); + local co = getCurrentCoroutine(); + if(breakQueue[co] == true) then + breakQueue[co] = nil; + break; + end + end + end + end + local _,c = eventu.run(f); return c; end @@ -230,9 +280,34 @@ function eventu.breakTimer() breakQueue[co] = true; end +function eventu.abort(co) + if(co ~= nil) then + secondsQueue[co] = nil; + framesQueue[co] = nil; + inputQueue[co] = nil; + for k,v in pairs(signalQueue) do + for l,w in ipairs(v) do + if(w == co) then + table.remove(v,l); + break; + end + end + end + for k,v in pairs(eventQueue) do + for l,w in ipairs(v) do + if(w == co) then + table.remove(v,l); + break; + end + end + end + pausedQueue[co] = nil; + end +end + function eventu.registerKeyEvent(key, func, consume) consume = consume or false; - eventu.run(function() + local _,c = eventu.run(function() repeat eventu.waitForInput(key); func(); @@ -243,11 +318,12 @@ function eventu.registerKeyEvent(key, func, consume) end until consume == true; end); + return c; end function eventu.registerSMBXEvent(event, func, repeated) repeated = repeated or false; - eventu.run(function() + local _,c = eventu.run(function() repeat eventu.waitEvent(event); func(); @@ -258,6 +334,7 @@ function eventu.registerSMBXEvent(event, func, repeated) end until repeated == false; end); + return c; end return eventu; \ No newline at end of file diff --git a/LuaScriptsLibExt/metaluna.lua b/LuaScriptsLibExt/metaluna.lua new file mode 100644 index 000000000..4e58a2e5e --- /dev/null +++ b/LuaScriptsLibExt/metaluna.lua @@ -0,0 +1,84 @@ +--***************** +--* metaluna.lua * +--* v1.0 * +--* * +--***************** + +local metaluna = {} +local sectionIds = {} + + +if Animation.__tostring == nil then +function Animation.__tostring(obj) + return "Animation (id="..obj.id..", x="..obj.x..", y="..obj.y..")" +end; end; + +if BGO.__tostring == nil then +function BGO.__tostring(obj) + return "BGO (id="..obj.id..", x="..obj.x..", y="..obj.y..")" +end; end; + +if Block.__tostring == nil then +function Block.__tostring(obj) + return "Block (id="..obj.id..", x="..obj.x..", y="..obj.y..")" +end; end; + +if Camera.__tostring == nil then +function Camera.__tostring(obj) + return "Camera (x="..obj.x..", y="..obj.y..", w="..obj.width..", h="..obj.height..")" +end; end; + +if Layer.__tostring == nil then +function Layer.__tostring(obj) + return "Layer ("..obj.layerName..")" +end; end; + +if Level.__tostring == nil then +function Level.__tostring(obj) + return "Level (title="..obj.title..")" +end; end; + +if NPC.__tostring == nil then +function NPC.__tostring(obj) + return "NPC (id="..obj.id..")" +end; end; + +if Player.__tostring == nil then +function Player.__tostring(obj) + return "Player (char="..obj.character..")" +end; end; + +if RECT.__tostring == nil then +function RECT.__tostring(obj) + return "RECT (l="..tostring(obj.left)..",r="..tostring(obj.right)..",".."t="..tostring(obj.top)..",b="..tostring(obj.bottom)..")" +end; end; + +if RECTd.__tostring == nil then +function RECTd.__tostring(obj) + return "RECTd (l="..tostring(obj.left)..",r="..tostring(obj.right)..",".."t="..tostring(obj.top)..",b="..tostring(obj.bottom)..")" +end; end; + +if Section.__tonumber == nil then +function Section.__tonumber(obj) + local secId = 1 + for k,v in pairs (Section.get()) do + if v == obj then + secId = k + break; + end + end + + return secId +end; end; + +if Section.__tostring == nil then +function Section.__tostring(obj) + return "Section ("..tostring(tonumber(obj))..")" +end; end; + +if Warp.__tostring == nil then +function Warp.__tostring(obj) + return "Warp ("..tostring(obj:mem (0x5A, FIELD_WORD))..")" +end; end; + +return metaluna; \ No newline at end of file diff --git a/LuaScriptsLibExt/npcParse.lua b/LuaScriptsLibExt/npcParse.lua new file mode 100644 index 000000000..3f1bf1d82 --- /dev/null +++ b/LuaScriptsLibExt/npcParse.lua @@ -0,0 +1,278 @@ +-- npcParse.lua +-- v1.2 + +local lunajson = API.load("ext/lunajson") +local pnpc = API.load("pnpc") +local npcParse = {} + +local textblox +local textbloxActive = false + +if pcall(function() textblox = API.load("textblox") end) then + textbloxActive = true; +end + + + +npcParse.debug = false + + + +npcParse.externalJson = {} +npcParse.idGroups = {} + + +local areAllLoaded = false +npcParse.active = true +npcParse.clearAfter = true +local useLoadstring = true + + +function npcParse.onInitAPI () + registerEvent(npcParse, "onStart", "onStartStart", true) + registerEvent(npcParse, "onStart", "onStartEnd", false) + registerEvent(npcParse, "onTick", "onTickStart", true) + registerEvent(npcParse, "onTick", "onTickEnd", false) + registerEvent(npcParse, "onCameraUpdate", "onCameraUpdate", false) +end + + +-- This function loads the data from the npcdata.json file +local function loadExternalJson () + -- Get the full file path + local p = Misc.resolveFile("npcdata.json"); + + -- If an invalid path, end now + if (p == nil) then + return + end + + -- Create the table and open the file + local f = io.open (p, "r"); + local str = f:read ("*all") + + -- Parse the data on a line-by-line basis + pcall(function () npcParse.externalJson = lunajson.decode(str) end) +end + + +-- Call this function to check whether a given NPC has been parsed yet and whether it was successful; +-- returns nil if not parsed, false if unsuccessful attempt and true if successful parsing +function npcParse.getSuccessful (pnpcRef) + if pnpcRef.data.npcParse ~= nil then + return pnpcRef.data.npcParse.success + else + return nil + end +end + + +-- Call this function to parse non-NPC.msg strings +local function getStringTable (str) + local data = nil + + -- Parse with the configured method + if useLoadstring then + + -- Ensure the string starts with a curly bracket, otherwise leave data nil + if string.sub(str, 1,1) == "{" then + + -- Load the table string + local f, errorStr = loadstring ("return "..str); + + -- Does the parsed table return any errors? + if f == nil then + return nil, errorStr + end + + -- Get the table + data = f() + end + + else + -- If parsing the table would cause an error, leave data nil + pcall(function () data = lunajson.decode(str) end) + end + + + -- Validation check 2: make sure the parsed code functions as a table + if type(data) == "table" then + + -- Return the table + return data, "TABLE SUCCESSFULLY PARSED" + else + return nil, "ERROR: NOT RECOGNIZED AS A TABLE" + end +end + +-- Call this function to parse a specific NPC +function npcParse.loadMsgData (npc, force) + + -- Only wrap non-generators until that pnpc bug is fixed + if npc:mem(0x64, FIELD_BOOL) == false then + + -- Wrap for the reference + local pnpcRef = pnpc.wrap (npc) + + -- Initialize the parse data + local parseData = {} + + -- Determine where the message string should be copied from and whether it should be parsed and loaded + parseData.success = npcParse.getSuccessful(pnpcRef) + local shouldLoad = true + + if parseData.success == nil then + areAllLoaded = false + parseData.msg = npc.msg.str + else + parseData.msg = pnpcRef.data.npcParse.msg or "" + parseData.success = pnpcRef.data.npcParse.success + shouldLoad = force + end + + + -- Parse and load the table if necessary/forced + if shouldLoad then + + -- Get data table + local dataTable = getStringTable (parseData.msg) + + -- Get other data from external json (all NPCs) + if npcParse.externalJson["_ALL"] ~= nil then + local extData = npcParse.externalJson["_ALL"] + for k,v in pairs (extData) do + pnpcRef.data[k] = v + end + end + + -- If a valid table, split master data table up into separate pnpc data tables and load data from external JSON + if dataTable ~= nil then + + -- In-message properties + for k,v in pairs (dataTable) do + pnpcRef.data[k] = v + end + + -- Get other data from external json (all IDed NPCs) + if npcParse.externalJson["_ID"] ~= nil and dataTable.id ~= nil then + local extData = npcParse.externalJson["_ID"] + for k,v in pairs (extData) do + pnpcRef.data[k] = v + end + end + + -- Get other data from external json + if npcParse.externalJson [dataTable.id] ~= nil then + local extData = npcParse.externalJson [dataTable.id] + for k,v in pairs (extData) do + pnpcRef.data[k] = v + end + end + parseData.success = true + + -- Store reference in the appropriate id group + if dataTable.id ~= nil then + if npcParse.idGroups[dataTable.id] == nil then + npcParse.idGroups[dataTable.id] = {} + end + local alreadyIndexed = false + for _, value in pairs(npcParse.idGroups[dataTable.id]) do + if value == pnpcRef then + alreadyIndexed = true + end + end + if not alreadyIndexed then + table.insert(npcParse.idGroups[dataTable.id], pnpcRef) + end + end + + + -- Clear the NPC's message string after successful loading if configured to do so + if npcParse.clearAfter == true then + + if pnpcRef.data.newMsg ~= nil then + npc.msg = pnpcRef.data.newMsg + else + npc.msg = "" + end + end + end + + -- add parseData as this library's data + pnpcRef.data.npcParse = parseData + end + end +end + +-- Call this function to parse all NPCs +function npcParse.loadAllMsgData (force) + for k,v in pairs(NPC.get()) do + npcParse.loadMsgData (v, force) + end + + areAllLoaded = true +end + +-- Check whether all NPCs are loaded +function npcParse.allLoaded () + return areAllLoaded; +end + + + +-- Update npcParse +local function updateParsing () + for k,v in pairs(NPC.get()) do + if npcParse.getSuccessful (pnpc.wrap(v)) == nil then + areAllLoaded = false + break; + end + end + + if npcParse.active == true and areAllLoaded == false then + npcParse.loadAllMsgData () + end +end + +-- Call update in onStart and then at the beginning and end of every tick +function npcParse.onStartStart () + loadExternalJson () + updateParsing () +end +function npcParse.onStartEnd () + updateParsing () +end +function npcParse.onTickStart () + --updateParsing () +end +function npcParse.onTickEnd () + --updateParsing () +end + + +-- Debug stuff +function npcParse.onCameraUpdate (eventObj, cameraIndex) + + if npcParse.debug ~= true then return; end; + + -- Draw the tables + local cam = Camera.get()[cameraIndex] + + for k,v in pairs (NPC.get()) do + local pnpcRef = pnpc.wrap (v) + local within = v.x > cam.x+128 and v.x < cam.x+cam.width-128 and v.y > cam.y+96 and v.y < cam.y+cam.height-96 + + if pnpcRef.data ~= nil and textbloxActive and within then + textblox.printTable (pnpcRef.data, + {x = pnpcRef.x, + y = math.floor(pnpcRef.y/16)*16 - 32, + bind = textblox.BIND_LEVEL, + halign = textblox.ALIGN_LEFT, + valign = textblox.ALIGN_BOTTOM, + font = textblox.FONT_SPRITEDEFAULT4}) + end + end +end + + +return npcParse \ No newline at end of file diff --git a/LuaScriptsLibExt/raocoin2.lua b/LuaScriptsLibExt/raocoin2.lua index 598429230..d3ede494d 100644 --- a/LuaScriptsLibExt/raocoin2.lua +++ b/LuaScriptsLibExt/raocoin2.lua @@ -34,7 +34,7 @@ --------------------Created by Hoeloe - 2015------------------- -------------Open-Source Currency and Shop Library------------- ---------------------For Super Mario Bros X-------------------- -----------------------------v2.0.8----------------------------- +---------------------------v2.0.10----------------------------- ----------------------REQUIRES ENCRYPT.dll--------------------- ---------------------REQUIRES COLLIDERS.lua-------------------- -----------------------REQUIRES PNPC.lua----------------------- @@ -354,8 +354,9 @@ function raocoin.update() end]] if(memcurrencyCache[v.id] < mem(v.id,v.type)) then - raocoin.onCollect(v, currencyCache[k].lastCount - currencyCache[k].count); - v:onCollect(currencyCache[k].lastCount - currencyCache[k].count); + + raocoin.onCollect(v, mem(v.id,v.type) - memcurrencyCache[k]); + v:onCollect(mem(v.id,v.type) - memcurrencyCache[k]); end memcurrencyCache[v.id] = mem(v.id,v.type); end @@ -383,7 +384,7 @@ function raocoin.update() if(doBuyAnim) then player:mem(0x122,FIELD_WORD,7); --Set state to "going through door" - player.y = sourceY+35-player.height; + player.y = sourceY+32-player.height; if(player:mem(0x12A,FIELD_WORD) >= 16440) then --Warp timer counts from 16368 to 16444 by twos. 16440 is an acceptable buffer to ensure it doesn't try to warp, which will crash the game. player:mem(0x122,FIELD_WORD,0); player:mem(0x12A,FIELD_WORD,0); @@ -398,16 +399,15 @@ function raocoin.update() if(not k:isBought()) then --npc icon - if(v.npc == nil and v.npcID ~= nil and v.enableTimer <= 0) then - n = pnpc.wrap(spawnNPC(v.npcID,v.x,v.y-70-64,v.section)); - n:mem(0x46,FIELD_WORD,0xFFFF); - n:mem(0x48,FIELD_WORD,0xFFFF); - n:mem(0x12A,FIELD_WORD,100); - n.speedX = 0; - n.speedY = 0; - v.npc = n; + if((v.npc == nil or not v.npc.isValid) and v.npcID ~= nil and v.enableTimer <= 0) then + v.npc = pnpc.wrap(NPC.spawn(v.npcID,v.x,v.y-70-64,v.section)); + v.npc:mem(0x46,FIELD_WORD,0xFFFF); + v.npc:mem(0x48,FIELD_WORD,0xFFFF); + v.npc:mem(0x12A,FIELD_WORD,100); + v.npc.speedX = 0; + v.npc.speedY = 0; end - if(v.npc ~= nil) then + if(v.npc ~= nil and v.npc.isValid) then v.npc.x = v.npc:mem(0xA8,FIELD_DFLOAT); v.npc.y = v.npc:mem(0xB0,FIELD_DFLOAT); v.npc.speedX = 0; @@ -424,14 +424,14 @@ function raocoin.update() --bmp icon if(v.image ~= nil and v.enableTimer <= 0) then - Graphics.drawImageWP(v.image,v.x,v.y-70-64,1); + Graphics.drawImageWP(v.image,v.x,v.y-70-64,-1); end --text if(v.text ~= nil and v.enableTimer <= 0) then table.insert(drawQueue, function() local x1,y1 = raocoin.worldToScreen(v.x+12,v.y-72); - Text.printWP(v.text,x1+8-string.len(v.text)*9,y1-26,1); + Text.printWP(v.text,x1+8-string.len(v.text)*9,y1-26,-1); end); end @@ -456,7 +456,7 @@ function raocoin.update() Audio.playSFX(getSMBXPath().."\\LuaScriptsLib\\raocoin\\buy.ogg"); spawnEffect(132,v.x,v.y); v.enableTimer = 50; - if(v.npc ~= nil) then + if(v.npc ~= nil and v.npc.isValid) then v.npc:mem(0x40,FIELD_WORD,0xFFFF); v.npc = nil; end diff --git a/LuaScriptsLibExt/smoovement.lua b/LuaScriptsLibExt/smoovement.lua new file mode 100644 index 000000000..ba9cfa401 --- /dev/null +++ b/LuaScriptsLibExt/smoovement.lua @@ -0,0 +1,229 @@ +--smoovement.lua +--by Spinda +--v1.1 + +local vectr = API.load("vectr"); + +local smoovement = {}; + +smoovement.customDraw = false; + +smoovement.customTick = false; + +smoovement.world = {transform = vectr.id3, children = {}, actualPriority = 0}; + +function smoovement.translateM(x, y) -- creates a matrix for translation + return vectr.mat3( {1, 0, x}, + {0, 1, y}, + {0, 0, 1}); +end; + +function smoovement.rotateM(s1,c1) -- creates a matrix for rotation + return vectr.mat3( {c1, -s1, 0}, + {s1, c1, 0}, + {0, 0, 1}); +end; + +function smoovement.scaleM(x,y) -- creates a matrix for scaling + return vectr.mat3( {x, 0, 0}, + {0, y, 0}, + {0, 0, 1}); +end; + +function smoovement.vectorTransToXY(vector, trans) -- turns a vector2 and a transformation into an x and y value + local v3 = trans * vectr.v3(vector.x,vector.y,1); + return v3.x, v3.y; +end; + +local function drawImageTransformed(t) + local img = t.img; -- setting values + local trans = t.transform or vectr.id3; + local p = t.actualPriority; + local w = img.width; + local h = img.height; + local pivX = t.pivotX or .5; + local pivY = t.pivotY or .5; + local o = t.opacity or 1; + local sceneC = t.usesSceneCoords; + local vertCoords = {}; + local w1 = - pivX * w; -- setting values so that the pivot is the center + local w2 = w + w1; + local h1 = -pivY * h; + local h2 = h + h1; + vertCoords[0], vertCoords[1] = smoovement.vectorTransToXY(vectr.v2(w1,h1), trans); -- setting vertex coordinates + vertCoords[2], vertCoords[3] = smoovement.vectorTransToXY(vectr.v2(w1,h2), trans); + vertCoords[4], vertCoords[5] = smoovement.vectorTransToXY(vectr.v2(w2,h1), trans); + vertCoords[6], vertCoords[7] = smoovement.vectorTransToXY(vectr.v2(w2,h2), trans); + vertCoords[8], vertCoords[9] = smoovement.vectorTransToXY(vectr.v2(w1,h2), trans); + vertCoords[10], vertCoords[11] = smoovement.vectorTransToXY(vectr.v2(w2,h1), trans); + local textCoords = {}; + textCoords[0] = 0; textCoords[1] = 0; -- setting texture coordinates + textCoords[2] = 0; textCoords[3] = 1; + textCoords[4] = 1; textCoords[5] = 0; + textCoords[6] = 1; textCoords[7] = 1; + textCoords[8] = 0; textCoords[9] = 1; + textCoords[10] = 1; textCoords[11] = 0; + Graphics.glDraw{texture = img, + vertexCoords = vertCoords, + textureCoords = textCoords, + color = {1, 1, 1, o}, + priority = p, + sceneCoords = sceneC}; -- drawing the image +end + +local function deleteChildren(sprite, delete) -- deleting sprites recursively + if deleted then sprite.deleted = true; end; + for i = #sprite.children, 1, -1 do + deleteChildren(sprite.children[i],delete or sprite.deleted); + if delete or sprite.deleted then + table.remove(sprite.children,i); + end; + end; +end; + +local function transformChildren(sprite) -- transforming sprites recursively so the transform of its parents get taken over for their children + for _,v in pairs(sprite.children) do + v.transform = sprite.transform * smoovement.translateM(v.x, v.y) * smoovement.scaleM(v.scaleX, v.scaleY) * smoovement.rotateM(math.sin(v.rotation * math.pi/180), math.cos(v.rotation * math.pi/180)); + v.actualPriority = v.priority + sprite.actualPriority; + transformChildren(v); + end; +end; + +local function drawChildren(sprite) -- drawing the sprites + for _,v in pairs(sprite.children) do + if v.hasAnimation then + v.counter = (v.counter + 1) % v.dTime; + if v.counter == 0 then + v.frame = v.frame % #v.frames + 1; + v.img = v.frames[v.frame]; + v.width = v.img.width; + v.height = v.img.height; + end; + end; + if v.img ~= nil and v.visible then + drawImageTransformed(v); + end; + drawChildren(v); + end; +end; + +local function contains(t,val) + for _, v in pairs(t) do + if v == val then return true; end; + end; + return false; +end; + +function smoovement.tick() + deleteChildren(smoovement.world,false); + transformChildren(smoovement.world); +end; + +function smoovement.draw() + drawChildren(smoovement.world); +end; + +function smoovement.onInitAPI() + registerEvent(smoovement,"onDraw","onDraw",false); + registerEvent(smoovement,"onTick","onTick",false); +end + +function smoovement.onDraw() + if not smoovement.customDraw then + smoovement.draw(); + end; +end; + +function smoovement.onTick() + if not smoovement.customTick then + smoovement.tick(); + end; +end; + +function smoovement.createSprite(t) + local newSpr = {img = t.img, + x = t.x or 0, + y = t.y or 0, + rotation = t.rotation or 0, + pivotX = t.pivotX or .5, + pivotY = t.pivotY or .5, + scaleX = t.scaleX or 1, + scaleY = t.scaleY or 1, + visible = t.visible, + usesSceneCoords = usesSceneCoords, + hasParent = false, + hasAnimation = false, + dTime = 0, + counter = 0, + frame = 1, + priority = t.priority or 0, + opacity = t.opacity, + parent = smoovement.world, + frames = {}, + children = {}, + actualPriority = 0, + transform = vectr.id3, + deleted = false, + _isSprite = true}; + if newSpr.visible == nil then newSpr.visible = true; end; + if newSpr.usesSceneCoords == nil then newSpr.usesSceneCoords = false; end; + if newSpr.img ~= nil then + newSpr.height = t.height or newSpr.img.height; + newSpr.width = t.width or newSpr.img.width; + else + newSpr.height = t.height or 0; + newSpr.width = t.width or 0; + end; + + newSpr.setScale = function(self, scaleX, scaleY) -- defining methods to make setting certain values faster + self.scaleX = scaleX; self.scaleY = scaleY; + end; + newSpr.setPivot = function(self, pivotX, pivotY) + self.pivotX = pivotX; self.pivotY = pivotY; + end; + newSpr.moveTo = function(self, x, y) + self.x = x; self.y = y; + end; + newSpr.setParent = function(self, parent) -- method to set a parent + for i = #self.parent.children, 1, -1 do -- removing sprite from children table of the previous parent + if self == self.parent.children[i] then + table.remove(self.parent.children,i); + break; + end; + end; + self.parent = parent; + table.insert(self.parent.children,self); + self.hasParent = true; + end; + newSpr.removeParent = function(self) + self.hasParent = false; + for i = #self.parent.children, 1, -1 do -- removing sprite from childen table of the previous paeent + if self == self.parent.children[i] then + table.remove(self.parent.children,i); + break; + end; + end; + end; + newSpr.setAnimation = function(self, tab, dTime) + self.hasAnimation = true; + self.frames = tab; + self.dTime = dTime; + self.counter = 0; + self.frame = 1; + self.width = self.frames[1].width; + self.height = self.frames[1].height; + end; + newSpr.setSprite = function(self, sprite) + self.hasAnimation = false; + self.img = sprite; + self.height = self.img.height; + self.width = self.img.width; + end; + newSpr.delete = function(self) + self.deleted = true; + end; + table.insert(smoovement.world.children,newSpr); + return newSpr; +end; + +return smoovement; \ No newline at end of file diff --git a/LuaScriptsLibExt/vectr.lua b/LuaScriptsLibExt/vectr.lua index 35eea9f5a..b1ac88de2 100644 --- a/LuaScriptsLibExt/vectr.lua +++ b/LuaScriptsLibExt/vectr.lua @@ -9,7 +9,7 @@ ------Created by Hoeloe - 2015------ -----Open-Source Vector Library----- -------For Super Mario Bros X------- -----------------v1.0a--------------- +----------------v1.0---------------- local vectr = {}; local version = "1.0"; @@ -27,56 +27,14 @@ end do --Vector 2 ---CLASS DEF -local vect2 = {}; +--METATABLE +local createmt2; local v2mt = {}; - -function vect2.normalise(a) - if(a.sqrlength == 0) then - return vectr.v2(0,0) - elseif(a.sqrlength == 1) then - return a; - else - return vectr.v2(a.x/a.length,a.y/a.length); - end -end - -function vect2.rotate(a,d) - local r = d*0.0174534; --deg2rad - local sr = math.sin(r); - local cr = math.cos(r); - return vectr.v2(a.x*cr - a.y*sr, a.x*sr + a.y*cr); -end - -function vect2.lookat(a,x1,y1) - local v; - local t = v2mt_typecheck(x1); - if(t == "number") then - v = vectr.v2(x1,y1):normalise(); - else - v = x1:normalise(); - end - return v*a.length; -end - -function vect2.tov3(a) - return vectr.v3(a.x,a.y,0); -end - -function vect2.tov4(a) - return vectr.v3(a.x,a.y,0,0); -end - -function vect2.dot(a,b) +function v2mt.dot(a,b) return a.x*b.x + a.y*b.y end -vect2.normalize = vect2.normalise; -vect2.project = proj; - ---METATABLE - -local function v2mt_typecheck(a) +function v2mt.typecheck(a) if(type(a) == "number") then return "number"; elseif(a._type ~= nil and a._type == "vector2") then @@ -86,23 +44,9 @@ local function v2mt_typecheck(a) end end -function v2mt.__index(obj,key) - if(key == "x") then return rawget(obj, "x") - elseif(key == "y") then return rawget(obj, "y") - elseif(key == "sqrlength") then - return obj.x*obj.x + obj.y*obj.y - elseif(key == "length") then - return math.sqrt(obj.x*obj.x + obj.y*obj.y) - elseif(key == "_type") then - return "vector2"; - else - return vect2[key] - end -end - -function v2mt.__newindex(obj,key,val) - if(key == "x") then rawset(obj, "x", val); - elseif(key == "y") then rawset(obj, "y", val); +function v2mt.newindex(obj,key,val) + if(key == "x") then setmetatable(obj,createmt2(val,obj.y)) + elseif(key == "y") then setmetatable(obj,createmt2(obj.x,val)) elseif(key == "length" or key == "sqrlength") then error("Cannot set the length of a vector directly. Try changing the component values.",2) elseif(key == "_type") then @@ -112,13 +56,13 @@ function v2mt.__newindex(obj,key,val) end end -function v2mt.__tostring(obj) +function v2mt.tostring(obj) return "("..tostring(obj.x)..", "..tostring(obj.y)..")" end -function v2mt.__add(a,b) - local ta = v2mt_typecheck(a); - local tb = v2mt_typecheck(b); +function v2mt.add(a,b) + local ta = v2mt.typecheck(a); + local tb = v2mt.typecheck(b); if(ta == "number") then if(tb == "number") then return a+b; @@ -132,9 +76,9 @@ function v2mt.__add(a,b) end end -function v2mt.__sub(a,b) - local ta = v2mt_typecheck(a); - local tb = v2mt_typecheck(b); +function v2mt.sub(a,b) + local ta = v2mt.typecheck(a); + local tb = v2mt.typecheck(b); if(ta == "number") then if(tb == "number") then return a-b; @@ -148,13 +92,13 @@ function v2mt.__sub(a,b) end end -function v2mt.__unm(a) +function v2mt.neg(a) return vectr.v2(-a.x,-a.y) end -function v2mt.__mul(a,b) - local ta = v2mt_typecheck(a); - local tb = v2mt_typecheck(b); +function v2mt.mul(a,b) + local ta = v2mt.typecheck(a); + local tb = v2mt.typecheck(b); if(ta == "number") then if(tb == "number") then return a*b; @@ -168,9 +112,9 @@ function v2mt.__mul(a,b) end end -function v2mt.__div(a,b) - local ta = v2mt_typecheck(a); - local tb = v2mt_typecheck(b); +function v2mt.div(a,b) + local ta = v2mt.typecheck(a); + local tb = v2mt.typecheck(b); if(ta == "number") then if(tb == "number") then return a/b; @@ -184,7 +128,7 @@ function v2mt.__div(a,b) end end -function v2mt.__eq(a,b) +function v2mt.equals(a,b) if(a == nil or b == nil or typecheck(a) ~= "vector2" or typecheck(b) ~= "vector2") then return false; else @@ -192,26 +136,115 @@ function v2mt.__eq(a,b) end end -function v2mt.__len(a) +function v2mt.len(a) return 2; end -v2mt.__concat = vect2.dot; -v2mt.__mod = proj; +createmt2 = function(x,y,sl,l) + local mt = {} + mt.__index = + function(obj,key) + if(key == "x") then return x + elseif(key == "y") then return y + elseif(key == "sqrlength") then + if(sl == nil) then + local sm = x*x + y*y; + setmetatable(obj,createmt2(x,y,sm)); + return sm; + else + return sl; + end + elseif(key == "length") then + if(l == nil) then + local sm = sl or (x*x + y*y); + local mag = math.sqrt(sm); + setmetatable(obj,createmt2(x,y,sm,mag)); + return mag; + else + return l; + end + elseif(key == "_type") then + return "vector2"; + else + return nil; + end + end + mt.__newindex = v2mt.newindex; + mt.__tostring = v2mt.tostring; + mt.__add = v2mt.add; + mt.__sub = v2mt.sub; + mt.__unm = v2mt.neg; + mt.__mul = v2mt.mul; + mt.__div = v2mt.div; + mt.__concat = v2mt.dot; + mt.__mod = proj; + mt.__eq = v2mt.equals; + mt.__len = v2mt.len; + --mt.__metatable = mthide; + + return mt; +end + +--CLASS DEF +local vect2 = {}; +function vect2.normalise(a) + if(a.sqrlength == 0) then + return vectr.v2(0,0) + elseif(a.sqrlength == 1) then + return a; + else + return vectr.v2(a.x/a.length,a.y/a.length); + end +end + +function vect2.rotate(a,d) + local r = d*0.0174534; --deg2rad + local sr = math.sin(r); + local cr = math.cos(r); + return vectr.v2(a.x*cr - a.y*sr, a.x*sr + a.y*cr); +end + +function vect2.lookat(a,x1,y1) + local v; + local t = v2mt.typecheck(x1); + if(t == "number") then + v = vectr.v2(x1,y1):normalise(); + else + v = x1:normalise(); + end + return v*a.length; +end + +function vect2.tov3(a) + return vectr.v3(a.x,a.y,0); +end + +function vect2.tov4(a) + return vectr.v3(a.x,a.y,0,0); +end + +vect2.normalize = vect2.normalise; +vect2.dot = v2mt.dot; +vect2.project = proj; --CONSTRUCTOR function vectr.v2(x,y) + local v = {}; + + for m,n in pairs(vect2) do + v[m] = n; + end + if(type(x) == "number") then y = y or x; elseif(x ~= nil and x._type ~= nil and x._type == "vector2") then y = x.y; x = x.x; else - error("Invalid vector definition.",2); + error("Invalid vector definition.",2) end - local v = {x=x, y=y}; - setmetatable(v, v2mt); + setmetatable(v,createmt2(x,y)); return v; end