Allow ulx curse to specify an amount of random effects to give (#144)

* Allow ulx curse to specify an amount of random effects to give

* Filter out incompatible effects between each draw

* Safety checks

* Clamp instead of abort

* A smidge of pazazz

* Clarity
This commit is contained in:
legokidlogan 2024-06-28 15:57:18 -05:00 committed by GitHub
parent 5068562a71
commit ae0b65eb75
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,5 +1,6 @@
CFCUlxCommands.curse = CFCUlxCommands.curse or {}
local cmd = CFCUlxCommands.curse
local multiCurseLimit = 100
CATEGORY_NAME = "Fun"
@ -9,6 +10,36 @@ do -- Load curse effects
end
local function getDurationStrings( durationSeconds, effectOverride )
local hasCustomDuration = durationSeconds > 0 and ( not effectOverride or not effectOverride.blockCustomDuration )
local durationAppend = ""
local briefly = " briefly"
if hasCustomDuration then
local durationStr = durationSeconds >= 60 and
ULib.secondsToStringTime( durationSeconds ) or
math.Round( durationSeconds ) .. " seconds"
durationAppend = " for " .. durationStr
briefly = ""
end
return durationAppend, briefly
end
local function getOneTimeEffects()
local out = {}
for _, effect in ipairs( CFCUlxCurse.Effects ) do
if not effect.excludeFromOnetime then
table.insert( out, effect )
end
end
return out
end
-- Returns true if the curse effect could not be applied/removed.
function cmd.curse( ply, effectOverride, durationSeconds, shouldUncurse )
if shouldUncurse then
@ -36,11 +67,28 @@ function cmd.cursePlayers( callingPlayer, targetPlayers, effectName, durationMin
local effectOverride
isSilent = isSilent or false
local isSpecificEffect =
local isSpecificEffect = -- Gave a non-random effect name AND is either cursing or not doing 'uncurse all'
type( effectName ) == "string" and
effectName ~= "random" and
( not shouldUncurse or effectName ~= "all" )
local amount = -- Cursing and gave a specific number of effects to apply
isSpecificEffect and
not shouldUncurse and
tonumber( effectName ) or nil
if amount == 1 then
amount = false
isSpecificEffect = false
effectName = "random"
end
if amount then
amount = math.floor( amount )
amount = math.Clamp( amount, 1, multiCurseLimit )
isSpecificEffect = false
end
if isSpecificEffect then
effectOverride = CFCUlxCurse.GetEffectByName( effectName )
@ -52,13 +100,54 @@ function cmd.cursePlayers( callingPlayer, targetPlayers, effectName, durationMin
end
local durationSeconds = durationMinutes and durationMinutes * 60 or 0
local smallestAmount = amount
local largestAmount = 0
for i = #targetPlayers, 1, -1 do
local ply = targetPlayers[i]
local applyFailed = cmd.curse( ply, effectOverride, durationSeconds, shouldUncurse )
if amount then
-- Curse each person with multiple random effects. Doesn't allow uncursing.
for i = #targetPlayers, 1, -1 do
local plyAmount = amount
local ply = targetPlayers[i]
local availableEffects = CFCUlxCurse.FilterCompatibleEffects( ply, getOneTimeEffects() )
if applyFailed then
table.remove( targetPlayers, i )
-- Only allow a given effect to be applied at most one time during the following loop.
-- This rule doesn't count for effects the player already has, allowing them to be refreshed one time. (new duration, new seed, etc.)
for _ = 1, amount do
local effectsLeft = #availableEffects
if effectsLeft == 0 then
plyAmount = plyAmount - 1
else
local effectInd = math.random( 1, effectsLeft )
local effect = availableEffects[effectInd]
local applyFailed = cmd.curse( ply, effect, durationSeconds, shouldUncurse )
if applyFailed then
plyAmount = plyAmount - 1
else
table.remove( availableEffects, effectInd )
availableEffects = CFCUlxCurse.FilterCompatibleEffects( ply, availableEffects ) -- Filter out effects that might be incompatible with the new one.
end
end
end
if plyAmount == 0 then
table.remove( targetPlayers, i )
else
smallestAmount = math.min( smallestAmount, plyAmount )
largestAmount = math.max( largestAmount, plyAmount )
end
end
else
-- (un)curse each person with a single effect.
for i = #targetPlayers, 1, -1 do
local ply = targetPlayers[i]
local applyFailed = cmd.curse( ply, effectOverride, durationSeconds, shouldUncurse )
if applyFailed then
table.remove( targetPlayers, i )
end
end
end
@ -72,6 +161,26 @@ function cmd.cursePlayers( callingPlayer, targetPlayers, effectName, durationMin
return
end
if smallestAmount == largestAmount and smallestAmount == 1 then
amount = false
end
if amount then
local amountStr
if smallestAmount == largestAmount then
amountStr = tostring( smallestAmount )
else
amountStr = smallestAmount .. " to " .. largestAmount
end
local durationAppend, briefly = getDurationStrings( durationSeconds, false )
ulx.fancyLogAdmin( callingPlayer, isSilent, "#A" .. briefly .. " afflicted #T with " .. amountStr .. " random curses" .. durationAppend, targetPlayers )
return
end
local onetimeCursedPlayers = {}
local longCursedPlayers = {}
@ -100,25 +209,14 @@ function cmd.cursePlayers( callingPlayer, targetPlayers, effectName, durationMin
end
end
else
local hasCustomDuration = durationSeconds > 0 and ( not effectOverride or not effectOverride.blockCustomDuration )
local durationAppend = ""
local briefly = " briefly"
if hasCustomDuration then
local durationStr = durationSeconds >= 60 and
ULib.secondsToStringTime( durationSeconds ) or
math.Round( durationSeconds ) .. " seconds"
durationAppend = " for " .. durationStr
briefly = ""
end
local durationAppend, briefly = getDurationStrings( durationSeconds, effectOverride )
if effectOverride then -- Manually selected effect
local effectPrettyName = effectOverride.nameUpper
ulx.fancyLogAdmin( callingPlayer, isSilent, "#A" .. briefly .. " cursed #T with " .. effectPrettyName .. durationAppend, targetPlayers )
else -- Random effect
ulx.fancyLogAdmin( callingPlayer, isSilent, "#A" .. briefly .. " cursed #T with a random effect" .. durationAppend, targetPlayers )
ulx.fancyLogAdmin( callingPlayer, isSilent, "#A" .. briefly .. " afflicted #T with a random curse" .. durationAppend, targetPlayers )
end
end
end
@ -129,9 +227,17 @@ local function silentCursePlayers( callingPlayer, targetPlayers, effectName, dur
end
local curseOptions = table.Copy( CFCUlxCurse.GetEffectNames() )
table.insert( curseOptions, "random" )
for i = 1, 10 do
table.insert( curseOptions, tostring( i ) )
end
local curseCommand = ulx.command( CATEGORY_NAME, "ulx curse", cmd.cursePlayers, "!curse" )
curseCommand:addParam{ type = ULib.cmds.PlayersArg }
curseCommand:addParam{ type = ULib.cmds.StringArg, default = "random", ULib.cmds.optional, completes = CFCUlxCurse.GetEffectNames() }
curseCommand:addParam{ type = ULib.cmds.StringArg, default = "random", ULib.cmds.optional, completes = curseOptions }
curseCommand:addParam{ type = ULib.cmds.NumArg, min = 0, max = 24 * 60, default = 0, ULib.cmds.optional, ULib.cmds.allowTimeString, hint = "duration" }
curseCommand:addParam{ type = ULib.cmds.BoolArg, invisible = true }
curseCommand:defaultAccess( ULib.ACCESS_ADMIN )
@ -140,7 +246,7 @@ curseCommand:setOpposite( "ulx uncurse", { _, _, _, _, true }, "!uncurse" )
local silentCurseCommand = ulx.command( CATEGORY_NAME, "ulx scurse", silentCursePlayers, "!scurse" )
silentCurseCommand:addParam{ type = ULib.cmds.PlayersArg }
silentCurseCommand:addParam{ type = ULib.cmds.StringArg, default = "random", ULib.cmds.optional, completes = CFCUlxCurse.GetEffectNames() }
silentCurseCommand:addParam{ type = ULib.cmds.StringArg, default = "random", ULib.cmds.optional, completes = curseOptions }
silentCurseCommand:addParam{ type = ULib.cmds.NumArg, min = 0, max = 24 * 60, default = 0, ULib.cmds.optional, ULib.cmds.allowTimeString, hint = "duration" }
silentCurseCommand:addParam{ type = ULib.cmds.BoolArg, invisible = true }
silentCurseCommand:defaultAccess( ULib.ACCESS_ADMIN )