• Do not use Works in Progress as a way of avoiding the releases system! Works in Progress can be used for sharing early betas and for getting suggestions for improvement. Releases of finished content are not allowed in this forum! If you would like to submit a finished addon, click here for instructions on how to do so.

Useful general-purpose BLUA libraries

Status
Not open for further replies.

fickleheart

ms reflec beat stan
Is this in the right board? It can be moved if it isn't.

I've been writing a few handy BLUA scripts that other modders/mappers might find helpful. I'll edit this post as I write new ones. Feel free to use and edit these for whatever purpose you wish, as long as my name is left in the comments. Also feel free to post your own as replies; it'd be cool to have a resource thread for modders and mappers to use.

Mobj Sector Iterator
red.func.mobjsectoriterate

Type: BLUA function
Purpose: Provide an easy means to check functions for every sector (including FOFs) a mobj is in. Sort of like P_PlayerTouchingSectorSpecial, but it works for any arbitrary mobj.
Usage: Paste the code block at the top of any script where you wish to use the function. Function is executed as P_IterateMobjSectors(mo, func), where mo is the map object you wish to test, and func is a function(mo, sector) to execute.
Code:
-- LIBRARY red.func.mobjsectoriterate - get all sectors an object is "in" (including FOFs) and execute a function on them
-- (I realize P_PlayerTouchingSectorSpecial exists, but this works with arbitrary objects too)
-- @author RedEnchilada
-- @param mo - object to test
-- @param func - function(mo, sector) to execute
    local function P_IterateMobjSectors(mo, func)
        local sec = mo.subsector.sector
        func(mo, sec)
        local tag = sec.tag
        local foftypes = { -- Linedef types of all FOF specials
            100, 101, 102, 103, 104, 105, 140, 141, 142, 143, 144, 145, 146, -- FOF (solid)
            120, 121, 122, 123, 124, 125, 220, 221, 222, 223, -- FOF (intangible)
            170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, -- FOF (crumbling)
            150, 151, 152, 160, 190, 191, 192, 193, 194, 195, -- FOF (bobbing)
            200, 201, 202, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, -- FOF (special)
            } -- Referenced from the SRB2DB 2.1 config
        for i = 1, #foftypes do
            local foftype = foftypes[i]
            local linedefnum = P_FindSpecialLineFromTag(foftype, tag, -1)
            local last = -1
            while linedefnum ~= last do
                local fof = lines[linedefnum]
                if not fof then continue end
                fof = fof.frontsector
                if mo.z <= fof.ceilingheight and mo.z+mo.height >= fof.floorheight then
                    func(mo, fof)
                end
                --last = linedefnum
                linedefnum = P_FindSpecialLineFromTag(foftype, tag, linedefnum)
            end
        end
    end
-- /LIBRARY red.func.mobjsectoriterate
Linedef executor based on pushing a button
red.linedefexec.buttontrigger

Type: Linedef Executor Trigger
Purpose: Provides an easy-to-use linedef executor triggered by the press of an arbitrary button while in a sector.
Usage: Paste the code block into a blank script, then import said script into a WAD file. In your level editor, use Sector Type 2048 for a trigger by holding the button, and Sector Type 2304 for a trigger by tapping the button. (Tag it to a trigger linedef like you would any other trigger sector.) The configuration options block can be edited to change which button is used (Toss Flag by default), as well as which sector types are the triggers (in case you have other libraries using those sector types - can also set both to -1 to disable) and the string that appears in the lower-right corner when the trigger can be used.
Example
Code:
-- LIBRARY red.linedefexec.buttontrigger - Create linedef executors triggered by the Toss Flag key
-- (Sector type 2048 - Trigger Linedef Executor (holding Toss Flag))
-- (Sector type 2304 - Trigger Linedef Executor (tapped Toss Flag))
-- @author RedEnchilada
do
    -- Configuration options
    local actionkey = BT_TOSSFLAG -- A button flag used to trigger the action
    local actionhold = {
        section = 3, -- Section number to use for sector type
        number = 8, -- Special number to use for sector type
        str = "Hold TOSS FLAG to use!"
    }
    local actiontap = {
        section = 3,
        number = 9,
        str = "Press TOSS FLAG to use!"
    }
    -- /Configuration options
    
    addHook("ThinkFrame", do
        for player in players.iterate do
            -- Helper for below
            if not (player.cmd.buttons & actionkey) then
                player.cantap = true
                player.tapped = false
            elseif player.cantap then
                player.tapped = true
                player.cantap = false
            else
                player.tapped = false
            end
            
            -- (Sector type 2048 - Trigger Linedef Executor (holding Toss Flag))
            if actionhold.number ~= -1 then
                local s = P_PlayerTouchingSectorSpecial(player, actionhold.section, actionhold.number)
                if s then
                    player.canexecute = leveltime
                    player.canexecutehold = true
                    if (player.cmd.buttons & actionkey) then
                        P_LinedefExecute(s.tag, momo, s)
                    end
                end
            end
            
            -- (Sector type 2304 - Trigger Linedef Executor (tap Toss Flag))
            if actiontap.numer ~= -1 then
                local s = P_PlayerTouchingSectorSpecial(player, actiontap.section, actiontap.number)
                if s then
                    player.canexecute = leveltime
                    player.canexecutehold = false
                    if player.tapped then
                        P_LinedefExecute(s.tag, momo, s)
                    end
                end
            end
        end
    end)

    -- HUD thinker
    hud.add(function(v, player)
        if player.canexecute > leveltime-2 then
            local str
            if player.canexecutehold then
                str = actionhold.str
            else
                str = actiontap.str
            end
            v.drawString(312, 184, str, nil, "right")
        end
    end, "game")

    -- Reset on map load
    addHook("MapLoad", function()
        for player in players.iterate do
            player.canexecute = -10
        end
    end)
end
-- /LIBRARY red.linedefexec.buttontrigger
Screen coordinate finder
red.func.screencoords

Type: BLUA function
Purpose: When given a set of map coordinates, find the on-screen position that corresponds to it. (Y position may not be accurate in third-person view currently; position is exact in first-person.) This is intended for HUD drawing purposes only.
Usage: Paste the code block at the top of any script where you wish to use the function. Function is executed as R_GetScreenCoords(player, mx, my, mz), where player is the current player, and mx, my, and mz are the map coordinates to test. Returns x, y, and scale, all in fixed_t form.
Code:
-- LIBRARY red.func.screencoords - get screen coords of map position
-- (The idea is if you feed these values into v.drawScaled, you'll draw a graphic that looks like it's on the map)
-- @author RedEnchilada
-- @param player - Player object (feed it the player argument from the HUD rendering hook)
-- @param mx - Map X point to test
-- @param my - Map Y point to test 
-- @param my - Map Z point to test
-- @return x - Screen X position
-- @return y - Screen Y position
-- @return scale - Scale of object

-- Helper function for HUD rendering
addHook("ThinkFrame", do
    for player in players.iterate do
        if player.tweenedaiming then
            player.tweenedaiming = $1+(player.aiming-$1)/8
            player.tweenedcamz = $1+(player.viewz+20<<FRACBITS-$1)
        else
            player.tweenedaiming = player.aiming
            player.tweenedcamz = player.viewz+20<<FRACBITS
        end
    end
end)

-- Actual function
local function R_GetScreenCoords(p, mx, my, mz)
    -- Get camera angle
    local camangle = R_PointToAngle(p.mo.x+FixedMul(cos(p.mo.angle), 64<<FRACBITS), p.mo.y+FixedMul(sin(p.mo.angle), 64<<FRACBITS))
    local camheight = p.viewz
    local camaiming = FixedMul(160<<FRACBITS, tan(p.aiming+ANGLE_90))
    if(R_PointToDist(p.mo.x, p.mo.y) > FRACUNIT) then
        camheight = p.tweenedcamz
        camaiming = p.tweenedaiming/128
        --print(camheight)
    end
    
    local x = camangle-R_PointToAngle(mx, my)
    local distfact = FixedMul(FRACUNIT, cos(x))
    if x > ANGLE_90 or x < ANGLE_270 then
        x = 9999
    else
        x = FixedMul(tan(x+ANGLE_90), 160<<FRACBITS)+160<<FRACBITS
    end
    
    local y = camheight-mz
    --print(y/FRACUNIT)
    y = FixedDiv(y, FixedMul(distfact, R_PointToDist(mx, my)))
    y = (y*160)+100<<FRACBITS
    y = y+camaiming
    
    local scale = FixedDiv(160*FRACUNIT, FixedMul(distfact, R_PointToDist(mx, my)))
    --print(scale)
    
    return x, y, scale
end
-- /LIBRARY red.func.screencoords
Sprite finder for HUD rendering
red.func.hudsprite

Type: BLUA function
Purpose: Grab a particular sprite, frame, and angle as a patch. Useful when you want, say, PLAYA2 and it might actually be PLAYA2A8 instead.
Usage: Paste the code block at the top of any script where you wish to use the function. Function is executed as V_GetSprite(v, spriteindex, number, angle), where v is the hud drawing object passed into the hook, spriteindex is the sprite_t to look for, number is the sprite's number, and angle is the desired angle (1 through 8).
Code:
-- LIBRARY red.func.hudsprite - Acquires a particular sprite frame at a particular angle and turns it into a patch_t
-- (thanks Inu for giving me v.patchExists(str) <3)
-- @author RedEnchilada
-- @param v - The v supplied to a HUD hook
-- @param spriteindex - The sprite_t you want the graphic from
-- @param number - The sprite frame to grab (0 = A)
-- @param angle - The sprite angle to grab (MUST be 1 through 8)
-- @param quick - Can the function save time by not checking for different frames assigned to same graphics? (Generally try this with true, and if you get errors or can't guarantee a certain sprite structure set it to false)
-- @return patch - The patch, suitable for HUD rendering
-- @return flip - Whether the patch needs to be rendered with the V_FLIP flag to get the desired result
local spritecache = {}
local function V_GetSprite(v, spriteindex, number, angle, quick)
    -- Grab sprite from cache if it exists, in a desparate attempt to improve performance
    while number >= 60
        number = $1-60
    end
    local key = spriteindex.."-"..number.."-"..angle
    local step = 0
    local first = true
    if spritecache[key] and spritecache[key].patch then
        return spritecache[key].patch, spritecache[key].flipped
    elseif quick then
        step = 60
    elseif spritecache[key] and spritecache[key].step then
        step = spritecache[key].step
        first = false
    end
        
    -- Okay, it's not in the cache. Let's go, sprite finding technique!
    local spriteletter = string.char(number+string.byte("A"))
    local spriteprefix = sprnames[spriteindex]
    local patch
    if first and v.patchExists(spriteprefix..spriteletter..angle)
        patch = v.cachePatch(spriteprefix..spriteletter..angle)
        spritecache[key] = {patch = patch, flipped = false}
        return patch, false
    end
    for i=0,8 do
        if first and v.patchExists(spriteprefix..spriteletter..angle..spriteletter..i)
            patch = v.cachePatch(spriteprefix..spriteletter..angle..spriteletter..i)
            spritecache[key] = {patch = patch, flipped = false}
            return patch, false
        end
        if first and v.patchExists(spriteprefix..spriteletter..i..spriteletter..angle)
            patch = v.cachePatch(spriteprefix..spriteletter..i..spriteletter..angle)
            spritecache[key] = {patch = patch, flipped = true}
            return patch, true
        end
        if not quick then
            for j = string.byte("A")+step, string.byte("A")+step+5 do
                local oth = string.char(j)
                if v.patchExists(spriteprefix..spriteletter..angle..oth..i)
                    patch = v.cachePatch(spriteprefix..spriteletter..angle..oth..i)
                    spritecache[key] = {patch = patch, flipped = false}
                    return patch, false
                end
                if v.patchExists(spriteprefix..oth..i..spriteletter..angle)
                    patch = v.cachePatch(spriteprefix..oth..i..spriteletter..angle)
                    spritecache[key] = {patch = patch, flipped = true}
                    return patch, true
                end
                if v.patchExists(spriteprefix..spriteletter.."0"..oth..i)
                    patch = v.cachePatch(spriteprefix..spriteletter.."0"..oth..i)
                    spritecache[key] = {patch = patch, flipped = false}
                    return patch, false
                end
                if v.patchExists(spriteprefix..oth..i..spriteletter.."0")
                    patch = v.cachePatch(spriteprefix..oth..i..spriteletter.."0")
                    spritecache[key] = {patch = patch, flipped = true}
                    return patch, true
                end
            end
        end
    end
    if not quick then
        for j = string.byte("A")+step, string.byte("A")+step+5 do
            local oth = string.char(j)
            if v.patchExists(spriteprefix..spriteletter.."0"..oth.."0")
                patch = v.cachePatch(spriteprefix..spriteletter.."0"..oth.."0")
                for i = 1,8 do -- Cache this sprite for all angles, since it applies to all angles!
                    spritecache[spriteindex.."-"..number.."-"..i] = {patch = patch, flipped = false}
                    spritecache[spriteindex.."-"..(j-string.byte("A")).."-"..i] = {patch = patch, flipped = true}
                end
                return patch, false
            end
            if v.patchExists(spriteprefix..oth.."0"..spriteletter.."0")
                patch = v.cachePatch(spriteprefix..oth.."0"..spriteletter.."0")
                for i = 1,8 do -- Cache this sprite for all angles, since it applies to all angles!
                    spritecache[spriteindex.."-"..number.."-"..i] = {patch = patch, flipped = true}
                    spritecache[spriteindex.."-"..(j-string.byte("A")).."-"..i] = {patch = patch, flipped = false}
                end
                return patch, true
            end
        end
    end
    if first and v.patchExists(spriteprefix..spriteletter.."0")
        patch = v.cachePatch(spriteprefix..spriteletter.."0")
        for i = 1,8 do -- Cache this sprite for all angles, since it applies to all angles!
            spritecache[spriteindex.."-"..number.."-"..i] = {patch = patch, flipped = false}
        end
        return patch, false
    end
    step = step+6
    if step < 60
        spritecache[key] = {step = step}
    else
        spritecache[key] = {patch = true, flipped = false}
    end
    return nil, false
end
-- /LIBRARY red.func.hudsprite
 
Last edited:
Status
Not open for further replies.

Who is viewing this thread (Total: 1, Members: 0, Guests: 1)

Back
Top