--8bit ring local lastDamageFrame = 0 addHook("MobjThinker", function (ring) if not ring.bit8ring then if lastDamageFrame ~= leveltime then lastDamageFrame = leveltime ring.bit8ring = true local maxinum = max(ring.momx, ring.momy) if abs(maxinum) > abs(ring.momz) then local sflip = (FixedMul(maxinum, ring.momz) > 0) * 2 - 1 if maxinum == ring.momx then ring.momx, ring.momz = ring.momz * sflip, ring.momx * sflip else ring.momy, ring.momz = ring.momz * sflip, ring.momy * sflip end end else P_RemoveMobj(ring) end else if ring.eflags & MFE_JUSTHITFLOOR then P_RemoveMobj(ring) end end end, MT_FLINGRING) addHook("TouchSpecial", function (ring, player) return true end, MT_FLINGRING) --100 ring reset addHook("PreThinkFrame", function() for player in players.iterate() if player.rings >= 100 and mapheaderinfo[gamemap].levelselect if player.lives - player.lastlives == 1 then player.lives = player.lastlives end P_GivePlayerLives(player, player.rings/100) P_PlayLivesJingle(player) player.rings = $ % 100 end player.lastlives = player.lives end end) --no boss rings addHook("PlayerSpawn", function(player) if mapheaderinfo[gamemap].levelflags & LF_WARNINGTITLE then player.rings = 0 end end) addHook("MobjThinker", function (ring) if mapheaderinfo[gamemap].levelflags & LF_WARNINGTITLE and ring.valid then P_RemoveMobj(ring) end end, MT_RING) --checkpoint monitors local postmonitors = {} addHook("MobjThinker", function (post) if not post.monitor then post.monitor = P_SpawnMobjFromMobj(post, 0, 0, 0, MT_GRAVITY_BOX) post.monitor.color = SKINCOLOR_ORANGE post.monitor.colorized = true post.monitor.post = post table.insert(postmonitors, post.monitor) post.radius = $ * 2 end end, MT_STARPOST) addHook("PostThinkFrame", function () for i, monitor in ipairs(postmonitors) if monitor.valid then monitor.post.flags2 = $ | MF2_DONTDRAW if monitor.post.state ~= S_STARPOST_IDLE then monitor.state = S_BOX_POP2 monitor.flags = $ | MF_NOCLIP end if monitor.state ~= S_BOX_POP2 then monitor.frame = $ | FF_VERTICALFLIP end end end end) addHook("MobjDeath", function (monitor, inflictor, source) local playermo = source or inflictor if monitor.post and playermo.player and monitor.post.radius >= R_PointToDist2(monitor.post.x, monitor.post.y, playermo.x, playermo.y) then monitor.state = S_BOX_POP2 monitor.flags = ($ | MF_NOCLIP) & ~MF_SOLID else monitor.post.monitor = nil P_RemoveMobj(monitor) end return true end, MT_GRAVITY_BOX) addHook("TouchSpecial", function (post, player) return (not post.monitor) or (post.monitor.state ~= S_BOX_POP2) end, MT_STARPOST) --signpost freeslot("S_SIGN_RING") states[S_SIGN_RING] = {SPR_RING, A, 1, nil, 0, 32, S_SIGN_RING} freeslot("S_SIGN_SSTAGE") states[S_SIGN_SSTAGE] = {SPR_WHAT, D, 1, nil, 0, 16, S_SIGN_SSTAGE} --note: the following is a modified lua port off of the game code for A_SignPlayer --this includes the original commented text --generally if a comment uses -- and has no space between the marker and the comment itself (such as "--hi" rather than "-- hi") it's probably from me --otherwise, it's probably from Sonic Team Jr. function A_SignPlayer(actor, var1, var2) local locvar1 = var1 local locvar2 = var2 local skin = nil local ov local facecolor, signcolor = locvar2, locvar2 local signframe = states[actor.info.raisestate].frame local numskins = 0 for i = 0, #skins - 1 do if skins[i].valid numskins = $ + 1 end end if (actor.tracer == nil or locvar1 < -3 or locvar1 >= numskins) return end --if no face overlay, spawn one if (actor.tracer.tracer == nil or not actor.tracer.tracer.valid) ov = P_SpawnMobj(actor.x, actor.y, actor.z, MT_OVERLAY) ov.target = actor.tracer actor.tracer.tracer = ov else ov = actor.tracer.tracer end if (locvar1 == -1) // set to target's skin if (not actor.target) return end if (not actor.target.player) return end --LCG RNG local function simrand(steps, seed) if seed == nil then seed = 26181 end if steps == 0 then return seed else return simrand(steps - 1, (8121 * seed + 28411) % 134456) end end if token then --show ! ov.state = S_SIGN_SSTAGE signcolor = SKINCOLOR_WHITE elseif actor.target.player.rings % 50 == ((((simrand(gamemap) % 45) * 10) / 9) + 1) then --show player face skin = skins[actor.target.skin] facecolor = actor.target.player.skincolor if (signcolor) elseif ((actor.target.player.skincolor == skin.prefcolor) and (skin.prefoppositecolor)) -- Set it as the skin's preferred oppositecolor? signcolor = skin.prefoppositecolor elseif (actor.target.player.skincolor) -- Set the sign to be an appropriate background color for this player's skincolor. signcolor = ColorOpposite(actor.target.player.skincolor) else signcolor = SKINCOLOR_NONE end for player in players.iterate() do if not player.gotsignbonus then P_GivePlayerLives(player, 1) P_PlayLivesJingle(player) player.gotsignbonus = true end end elseif actor.target.player.rings % 10 == 0 then --show ring ov.state = S_SIGN_RING signcolor = SKINCOLOR_GOLD for player in players.iterate() do player.signringbonus = true end else skin = nil end elseif (locvar1 != -3) // set to a defined skin -- I turned this function into a fucking mess. I'm so sorry. -Lach if (locvar1 == -2) // next skin local player = actor.target and actor.target.player or nil local skinnum; local function skincheck(num) if player return not R_SkinUsable(player, num) else return skins[num].availability > 0 end end if (ov.skin == nil) // pick a random skin to start with! local skincount = 0; for skincount = 0, numskins - 1 do if (not skincheck(skincount)) skincount = $ + 1 end end skinnum = P_RandomKey(skincount) + 1; for skincount = 0, numskins - 1 do if (skincount > skinnum) break end if (skincheck(skincount)) skinnum = $ + 1 end end else -- otherwise, advance 1 skin skinnum = #skins[ov.skin] skinnum = (skinnum + 1) % numskins while skincheck(skinnum) do skinnum = (skinnum + 1) % numskins end end skin = skins[skinnum] else -- specific skin skin = skins[locvar1] end facecolor = skin.prefcolor if (signcolor) elseif (skin.prefoppositecolor) signcolor = skin.prefoppositecolor elseif (facecolor) signcolor = ColorOpposite(facecolor) end end if not (ov.state == S_SIGN_RING or ov.state == S_SIGN_SSTAGE) if (skin) // player face ov.color = facecolor ov.skin = skin.name ov.state = actor.info.seestate // S_PLAY_SIGN else // Eggman face ov.color = SKINCOLOR_NONE ov.state = actor.info.meleestate // S_EGGMANSIGN if (not signcolor) signcolor = SKINCOLOR_CARBON end end end actor.tracer.color = signcolor; --[[ If you're here from the comment above Color_Opposite, the following line is the one which is dependent on the array being symmetrical. It gets the opposite of the opposite of your desired colour just so it can get the brightness frame for the End Sign. It's not a great design choice, but it's constant time array access and the idea that the colours should be OPPOSITES is kind of in the name. If you have a better idea, feel free to let me know. ~toast 2016/07/20 --]] if (signcolor and signcolor < MAXSKINCOLORS) local _, f = ColorOpposite(ColorOpposite(signcolor)) signframe = $ + 15 - f end actor.tracer.frame = signframe end addHook("PlayerSpawn", function(player) if player.signringbonus and mapheaderinfo[gamemap].levelselect then player.rings = $ + 10 player.signringbonus = false end player.gotsignbonus = false end)