--[[
UDodge v1.1
by Brett "Megiddo" Smith
This script will create a "pylon" that shoots full-sized combine balls at you.
Requirements:
This script requires ULib version 1.1 or higher. You can download ULib from ulyssesmod.net
Usage:
Just use the command "makepylon" in console, a pylon will appear in front of you and start shooting!
For more advanced users, the full command is makepylon [area_size] [ball_speed] [passes] [update_rate] [timeout] [ball_size] [charge]
All arguments are optional
area_size is how far away it will shoot at you from, default is 800
ball_speed is how fast the ball moves, default is 1000. Caps at ~2000, don't set it higher or the AI will be screwy.
passes is the accuracy with which the pylon will fire. Default is 2. Must be an integer. CPU usage increases with each pass, so use with caution.
update_rate is how often it checks for enemies, default is 1/2
timeout is how long it must wait between each fire before it can fire again, default is 1.5
ball_size is how large the ball is, default is 15
charge is the time between when it wants to shoot you and when it does (thus, charge time), default is 0
If you want to restrict access to the command, change the 'access' variable below to something like ACCESS_SLAY.
If you want to be able to move the pylons, change pylons_movable to true.
Changelog:
v1.1 *(09/12/06)*
[CHANGE] Now uses ULib v1.1
[ADD] ball_size parameter
[ADD] passes parameter
[ADD] Can now shoot at players in vehicles
[FIX] Deletion problem
[FIX] Doesn't target noclipped players
v1.0 *(09/09/06)*
*Initial version.
]]
assert( _file.Exists( "lua/ULib/init.lua" ), "UDodge needs ULib to run!" )
_OpenScript( "ULib/init.lua" )
assert( ULib.ULIB_VERSION >= 1.1, "UDodge requires ULib version 1.1 or higher to run!" )
local access = ACCESS_ALL
local pylons_movable = true
local offset = 50
local offset_up = vector3( 0, 0, 20 )
local pylons = {}
local maxplayers = _MaxPlayers()
local vehicles -- This will let us know if a vehicle is occupied
local vehicle_pos = {}
function updateVehicles()
vehicles = {} -- Regenerate every time.
for i=1, maxplayers do
if _PlayerInfo( i, "connected" ) and ULib.isInVehicle( i ) then
vehicles[ _EntGetParent( i ) ] = i
end
end
end
AddTimer( 2.05, 0, updateVehicles )
local function enemyFire( cball, target, speed, passes, time )
if time > 0 then -- We want to wait for charging sound to end.
AddTimer( time, 1, enemyFire, cball, target, speed, passes, 0 )
return
end
local targetpos = _EntGetPos( target )
targetpos = vecAdd( targetpos, offset_up )
local cballpos = _EntGetPos( cball )
local targetvel = _EntGetVelocity( target )
if vehicles[ target ] then -- If it's a vehicle, we handle velocity a special way
local oldpos = vehicle_pos[ target ].pos
local oldtime = vehicle_pos[ target ].time
local newpos = targetpos
local newtime = _CurTime()
-- M = (newpos-oldpos)/(newtime-oldtime)
if newtime-oldtime > 0 then -- Nothing we can do, divide by 0.
targetvel = vecSub( newpos, oldpos )
targetvel = vecMul( targetvel, 1/(newtime-oldtime) )
end
end
-- Initialize loop parameters
local futurepos = targetpos
for pass=0, passes do
local dist = vecSub( futurepos, cballpos )
local timetotarget = vecLength( dist ) / speed
local distcovered = vecMul( targetvel, timetotarget )
futurepos = vecAdd( targetpos, distcovered )
end
local dir = vecNormalize( vecSub( futurepos, cballpos ) )
ULib.play3DSound( "weapons/irifle/irifle_fire2.wav", cballpos )
ULib.applyAccel( cball, speed, dir )
end
local function enemyCheck( pylonid )
local pylon = pylons[ pylonid ]
if not _EntExists( pylon.entid ) or not _EntGetName( pylon.entid ) == "pylon" then -- Some error checking
HaltTimer( pylon.timerid )
pylons[ pylonid ] = nil -- Recycle so another pylon can take the place
end
if pylon.timein > _CurTime() then return end -- Not past timeout
local targets = {}
local pos = _EntGetPos( pylon.entid )
pos = vecAdd( pos, offset_up )
local ents = _EntitiesFindInSphere( pos, pylon.area_size )
for _, entid in ipairs( ents ) do
if (entid > 0 and entid <= maxplayers and _PlayerInfo( entid, "alive" ) and
_EntGetMoveType( entid ) ~= MOVETYPE_NOCLIP) or vehicles[ entid ] then -- Player or vehicle
-- We're going to do a trace to make sure the weapon can see the player.
local dir = vecSub( vecAdd( _EntGetPos( entid ), offset_up ), pos )
_TraceLine( pos, dir, pylon.area_size, pylon.entid )
if _TraceGetEnt() == entid then -- It can see them!
table.insert( targets, entid )
end
end
end
if table.getn( targets ) > 0 then
local entid = targets[ math.random( table.getn( targets ) ) ] -- Get a random target
pylons[ pylonid ].timein = _CurTime() + pylon.timeout
local off = vecNormalize( vecSub( _EntGetPos( entid ), pos ) )
off = vecMul( off, offset )
local spawnpos = vecAdd( pos, off )
spawnpos = vecAdd( spawnpos, offset_up )
ULib.play3DSound( "weapons/cguard/charging.wav", spawnpos )
if vehicles[ entid ] then -- If it's a vehicle
vehicle_pos[ entid ] = { pos=vecAdd( _EntGetPos( entid ), offset_up ), time=_CurTime() } -- We have to do this because _EntGetVelocity doesn't work on vehicles.
end
ULib.makeCball( enemyFire, spawnpos, pylon.size, entid, pylon.speed, pylon.passes, pylon.charge )
end
return
end
local function cc_makePylon( userid, args, argv, argc )
if not ULib.mainUcl:query( userid, access ) then
ULib.tsay( userid, "You do not have access to this command." )
return
end
local area_size = tonumber( argv[ 1 ] ) or 800
local speed = tonumber( argv[ 2 ] ) or 1000
local passes = tonumber( argv[ 3 ] ) or 2
local update = tonumber( argv[ 4 ] ) or 1/2
local timeout = tonumber( argv[ 5 ] ) or 1.5
local size = tonumber( argv[ 6 ] ) or 15
local charge = tonumber( argv[ 7 ] ) or 0
PlayerLookTrace( userid, 4096 )
local entid = ULib.makeProp( "models/props_c17/oildrum001.mdl", _TraceEndPos(), vector3( 1, 0, 0 ) )
_EntSetName( entid, "pylon" )
if not pylons_movable then
_EntSetMoveType( entid, MOVETYPE_NONE )
else
ULib.keepUpright( entid, 100 )
end
local pylonid
for i=1, 512 do -- 512 limit on pylons
if not pylons[ i ] then
pylonid = i -- Take the first open slot
end
end
local timerid = AddTimer( update, 0, enemyCheck, pylonid )
pylons[ pylonid ] = { entid=entid, passes=passes, area_size=area_size, timeout=timeout, speed=speed, timein=0, size=size, charge=charge, timerid=timerid }
ULib.callOnDelete( entid, function()
HaltTimer( timerid )
pylons[ pylonid ] = nil -- Recycle so another pylon can take the place
end )
end
ULib.CONCOMMAND( "makepylon", cc_makePylon )