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.
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
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.
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).
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
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
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
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: