--[[
UCause v1.0
by Brett "Megiddo" Smith
Part of the Spirit of UGM collection.
This script will let you chain events between entities in creative ways.
For example, you could chain a health change in a player to ignite a wooden bench,
and when the bench breaks, triggers a bowling ball to fall on a vehicle,
causing the vehicle to start and take off, hitting a trigger to turn player one purple.
Requirements:
This script requires ULib version 1.1 or higher. You can download ULib from ulyssesmod.net
Usage:
Let's say you want a crate to ignite when you pickup a bathtub with the physcannon.
Type 'ucause_start' in console or say '!ucause_start',
pick the 'causer' (the bathtub) by looking at it and pressing fire (mouse button),
pick the effected (the crate) by looking at it and pressing fire,
pick the cause (OnPhysGunPickup) from the menu,
pick the effect (Ignite) from the menu,
choose whether you want it to repeat or not (IE, will this only happen once?),
and you're done! Try it out!
UGM's cause and effect story:
Cause and effect was implemented around UGM v4.2. Sadly, the usage was complicated
and very few truly understood its full potential. This script aims to rectify that.
While this script may not be understood by someone playing GMod for the first time,
even mild novices should be able to pick up the basics of this script.
A warning:
Cause and effect is what spawned ULM, the first GMod "virus". The source engine's
input/output system is POWERFUL. Please be careful!
Changelog:
v1.0 *(09/12/06)*
* Initial version.
Known Problems:
The 'OnPlayerUse' cause can't target the activator very well.
]]
--TODO: Player addhealth input and patterns
local access = ACCESS_ALL
assert( _file.Exists( "lua/ULib/init.lua" ), "CauseAndEffect needs ULib to run!" )
_OpenScript( "ULib/init.lua" )
assert( ULib.ULIB_VERSION >= 1.1, "CauseAndEffect requires ULib version 1.1 or higher to run!" )
ioinf = {} -- I/O Information
ioinf.prop_physics = {}
ioinf.prop_physics.o = { "OnPlayerUse", "OnBreak", "OnHealthChanged", "OnIgnite", "OnPhysCannonDetach", "OnMotionEnabled",
"OnAwakened", "OnPhysGunPickup", "OnPhysGunDrop", "OnUser1", "OnUser2", "OnUser3", "OnUser4", }
ioinf.prop_physics.i = { "Break", "SetHealth", "AddHealth", "RemoveHealth", "Ignite", "Kill", "Alpha", "Color", "Sleep",
"Wake", "EnableMotion", "DisableMotion", "EnablePhyscannonPickup", "DisablePhyscannonPickup",
"DisableFloating", "physdamagescale", "EnableDamageForces", "DisableDamageForces", "DisableShadow",
"EnableShadow", "FireUser1", "FireUser2", "FireUser3", "FireUser4", }
ioinf.prop_ragdoll = ioinf.prop_physics -- Exactly the same
ioinf.prop_vehicle_airboat = {}
ioinf.prop_vehicle_airboat.o = { "PlayerOn", "PlayerOff", "PressedAttack", "PressedAttack2", "AttackAxis", "Attack2Axis",
"OnIgnite", "OnUser1", "OnUser2", "OnUser3", "OnUser4", }
ioinf.prop_vehicle_airboat.i = { "EnableGun", "ExitVehicle", "Lock", "Unlock", "TurnOn", "TurnOff", "Throttle", "Steer",
"HandBrakeOn", "HandBrakeOff", "Ignite", "Kill", "Alpha", "Color", "EnableShadow", "DisableShadow",
"FireUser1", "FireUser2", "FireUser3", "FireUser4", }
ioinf.prop_vehicle_jeep = {}
ioinf.prop_vehicle_jeep.o = { "PlayerOn", "PlayerOff", "PressedAttack", "PressedAttack2", "AttackAxis", "Attack2Axis",
"OnIgnite", "OnUser1", "OnUser2", "OnUser3", "OnUser4", }
ioinf.prop_vehicle_jeep.i = { "StartRemoveTauCannon", "FinishRemoveTauCannon", "Lock", "Unlock", "TurnOn", "TurnOff",
"Throttle", "Steer", "HandBrakeOn", "HandBrakeOff", "Ignite", "Kill", "Alpha", "Color", "EnableShadow",
"DisableShadow", "FireUser1", "FireUser2", "FireUser3", "FireUser4", }
ioinf.player = {}
ioinf.player.o = { "OnIgnite", "OnUser1", "OnUser2", "OnUser3", "OnUser4", }
ioinf.player.i = { "IgnoreFallDamage", "SetHealth", "physdamagescale", "Ignite", "Alpha", "Color", "EnableShadow",
"DisableShadow", "FireUser1", "FireUser2", "FireUser3", "FireUser4", }
ioinf.phys_thruster = {}
ioinf.phys_thruster.o = { "OnUser1", "OnUser2", "OnUser3", "OnUser4", }
ioinf.phys_thruster.i = { "Activate", "Deactivate", "scale", "Kill", "Alpha", "Color", "EnableShadow", "DisableShadow",
"FireUser1", "FireUser2", "FireUser3", "FireUser4", }
iospec = {} -- Special IO inputs
iospec.SetHealth = { 0, 1, 5, 10, 25, 50, 100, 500, 1000, 99999 }
iospec.AddHealth = iospec.SetHealth
iospec.RemoveHealth = iospec.SetHealth
iospec.Color = { "red", "blue", "green", "clear", "black", "tan", "grey", "brown", "purple", "pink", "teal", "orange", "hotpink", "gold", "yellow" }
iospec.Alpha = { 0, 50, 100, 128, 150, 200, 250, 255 }
iospec.Throttle = { 0, 0.25, 0.5, 0.75, 1, 5, 10, 100 }
iospec.Steer = { "Right", "Left" }
local users = {} -- This will hold some user information
local function chooseRepeat( userid, id, time, text )
if id == ULib.ID_EXIT then
users[ userid ] = nil
return
end
users[ userid ].menu = nil
users[ userid ].stage = 7
users[ userid ].rep = id
local t = users[ userid ]
ULib.applyColorToEnt( t.causer, "white" )
ULib.applyColorToEnt( t.effected, "white" )
ULib.addOutput( t.causer, t.cause, t.effected, t.effect, t.param, 0, t.rep )
if t.effect == "Break" then
ULib.addOutput( t.causer, t.cause, t.effected, "kill", t.param, 0.01, t.rep ) -- This removes the shell
end
if t.effect == "Alpha" then
_EntFire( t.effected, "addoutput", "rendermode 1", 0 ) -- Allows it to change alpha
end
--ULib.debugFunctionCall( "ULib.addOutput", t.causer, t.cause, t.effected, t.effect, t.param, 0, t.rep )
users[ userid ] = nil
end
local function chooseEffect( userid, id, time, text )
if id == ULib.ID_EXIT then
ULib.applyColorToEnt( users[ userid ].causer, "white" )
ULib.applyColorToEnt( users[ userid ].effected, "white" )
users[ userid ] = nil
return
end
if iospec[ text ] and users[ userid ].stage < 5 then -- They have a special step and they haven't done it yet
users[ userid ].menu = nil
users[ userid ].stage = 5
users[ userid ].effect = text
local menu = ULib.Menu:new( text .. "?", chooseEffect )
users[ userid ].menu = menu
for _, v in ipairs( iospec[ text ] ) do
menu:addOption( tostring( v ) )
end
menu:showMenu( userid )
return -- We need to wait to be processed again
elseif users[ userid ].stage == 5 then -- They just handled the special step
users[ userid ].param = text
if users[ userid ].effect == "Color" then
users[ userid ].param = "\"" .. ULib.getColorString( users[ userid ].param ) .. "\""
end
if users[ userid ].effect == "Steer" then
if users[ userid ].param == "Right" then
users[ userid ].param = 1
else
users[ userid ].param = -1
end
end
else -- No special step
users[ userid ].menu = nil
users[ userid ].effect = text
end
users[ userid ].stage = 6
local menu = ULib.Menu:new( "Repeat?", chooseRepeat )
users[ userid ].menu = menu
menu:addOption( "Yes", 999 )
menu:addOption( "No", 1 )
menu:showMenu( userid )
end
local function chooseCause( userid, id, time, text )
if id == ULib.ID_EXIT then
ULib.applyColorToEnt( users[ userid ].causer, "white" )
ULib.applyColorToEnt( users[ userid ].effected, "white" )
users[ userid ] = nil
return
end
users[ userid ].menu = nil
users[ userid ].stage = 4
users[ userid ].cause = text
local effectedt
if users[ userid ].effected ~= "!activator" then
local effectedtype = _EntGetType( users[ userid ].effected )
effectedt = ioinf[ effectedtype ].i
else
effectedt = ioinf[ "player" ].i -- We'll just assume the activator's going to be a player
end
if users[ userid ].cause == "OnPlayerUse" then
if users[ userid ].effected == "!activator" then
users[ userid ].effected = "!player" -- HACK: For some reason, OnPlayerUse does not agree with !activator but will work with !player.
end
_EntFire( users[ userid ].causer, "addoutput", "spawnflags 256", 0 ) -- If we don't do this, it won't call the output on use.
end
local menu = ULib.Menu:new( "Effect?", chooseEffect )
users[ userid ].menu = menu
for _, v in ipairs( effectedt ) do
menu:addOption( v )
end
menu:showMenu( userid )
end
local function chooseEffected( userid, in_key )
if in_key ~= IN_ATTACK and in_key ~= IN_ATTACK2 then return end -- Not listening for this
if not users[ userid ] then return end -- ERROR!
UnHookEvent( users[ userid ].hookpress )
HaltTimer( users[ userid ].reminder )
users[ userid ].hookpress = nil
users[ userid ].reminder = nil
users[ userid ].stage = 3
local entid
if in_key == IN_ATTACK then
PlayerLookTrace( userid, 4096 )
entid = _TraceGetEnt()
if entid <= 0 then
ULib.tsay( userid, "[IO] Error! Invalid or no ent chosen!" )
-- Previous Step
users[ userid ].hookpress = HookEvent( "eventKeyPressed", chooseEffected )
users[ userid ].reminder = AddTimer( 10, 0, ULib.csay, userid, "[IO] Point your gun at the effected ent and press fire. Use 'ucause_end' to cancel." )
ULib.csay( userid, "[IO] Point your gun at the effected ent and press fire.\nPress secondary fire for the activator (ent causing output) to be the effected." )
_PlayerSelectWeapon( userid, "weapon_physgun" ) -- Don't want to kill the ent
users[ userid ].stage = 2
return -- Will call this again
end
else -- They chose activator
entid = "!activator"
end
users[ userid ].effected = entid
ULib.applyColorToEnt( entid, "red" )
local causertype = _EntGetType( users[ userid ].causer )
local causert = ioinf[ causertype ].o
local menu = ULib.Menu:new( "Fire on?", chooseCause )
users[ userid ].menu = menu
for _, v in ipairs( causert ) do
menu:addOption( v )
end
menu:showMenu( userid )
end
local function chooseCauser( userid, in_key )
if in_key ~= IN_ATTACK then return end -- Not listening for this
if not users[ userid ] then return end -- ERROR!
UnHookEvent( users[ userid ].hookpress )
HaltTimer( users[ userid ].reminder )
users[ userid ].hookpress = nil
users[ userid ].reminder = nil
users[ userid ].stage = 2
PlayerLookTrace( userid, 4096 )
local entid = _TraceGetEnt()
if entid <= 0 then
ULib.tsay( userid, "[IO] Error! Invalid or no ent chosen!" )
-- Previous Step
users[ userid ].hookpress = HookEvent( "eventKeyPressed", chooseCauser )
users[ userid ].reminder = AddTimer( 10, 0, ULib.csay, userid, "[IO] Point your gun at the 'causer' ent and press fire. Use 'ucause_end' to cancel." )
ULib.csay( userid, "[IO] Point your gun at the 'causer' ent and press fire." )
_PlayerSelectWeapon( userid, "weapon_physgun" ) -- Don't want to kill the ent
users[ userid ].stage = 1
return -- Will call this again
end
users[ userid ].causer = entid
ULib.applyColorToEnt( entid, "black" )
users[ userid ].hookpress = HookEvent( "eventKeyPressed", chooseEffected )
users[ userid ].reminder = AddTimer( 10, 0, ULib.csay, userid, "[IO] Point your gun at the effected ent and press fire. Use 'ucause_end' to cancel." )
ULib.csay( userid, "[IO] Point your gun at the effected ent and press fire.\nPress secondary fire for the activator (ent causing output) to be the effected." )
_PlayerSelectWeapon( userid, "weapon_physgun" ) -- Don't want to kill the ent
end
local function cc_ucause_end( userid, args )
local stage = users[ userid ].stage
if stage >= 2 then
ULib.applyColorToEnt( users[ userid ].causer, "white" )
end
if stage >= 3 then
ULib.applyColorToEnt( users[ userid ].effected, "white" )
end
if stage < 3 then
UnHookEvent( users[ userid ].hookpress )
HaltTimer( users[ userid ].reminder )
end
users[ userid ] = nil
end
ULib.CONCOMMAND( "ucause_end", cc_ucause_end, "", access )
local function cc_ucause_start( userid, args )
if users[ userid ] then cc_ucause_end( userid ) end -- Clear crap
users[ userid ] = {}
users[ userid ].stage = 1
users[ userid ].hookpress = HookEvent( "eventKeyPressed", chooseCauser )
users[ userid ].reminder = AddTimer( 10, 0, ULib.csay, userid, "[IO] Point your gun at the 'causer' ent and press fire. Use 'ucause_end' to cancel." )
ULib.csay( userid, "[IO] Point your gun at the 'causer' ent and press fire." )
_PlayerSelectWeapon( userid, "weapon_physgun" ) -- Don't want to kill the ent
end
ULib.CONCOMMAND( "ucause_start", cc_ucause_start, "", access )