-- LUA Script - precede every function and global member with lowercase name of script + '_main' local U = require "scriptbank\\utillib" local Q = require "scriptbank\\quatlib" local rad = math.rad local modf = math.modf local random = math.random local hurtAmount = 10 local soundBitRate = 11025 local spikeVars = { baseAng = -90, framestart = 1, frameend = 80, spikelen = 60 } local swordVars = { baseAng = -90, framestart = 1, frameend = 78, spikelen = 45 } local spikes ={} local playerLives = nil function spikes_init_name( e, name ) Include( "utillib.lua" ) Include( "quatlib.lua" ) spikes[ e ] = { name = name, state = 'init', npcTimer = 0 } end local function gotPlayer( x, y, z, dist ) ppx, ppy, ppz = GetCameraPositionX(0) , GetCameraPositionY(0) , GetCameraPositionZ(0) return U.CloserThan( ppx, ppy, ppz, x, y, z, dist ) or U.CloserThan( ppx, ppy - 20, ppz, x, y, z, dist ) end local function HurtOthers( e, s, pos, dist ) for _, v in pairs( s.npcList ) do if spikes[ v ] == nil then local Ent = g_Entity[ v ] if U.CloserThan( Ent.x, Ent.y + 10, Ent.z, pos.x, pos.y, pos.z, dist ) or U.CloserThan( Ent.x, Ent.y + 40, Ent.z, pos.x, pos.y, pos.z, dist ) then SetEntityHealth( v, 0 ) end end end end local function InitSound( e, spike, speed ) SetSound( e, 1 ) SetSoundSpeed( soundBitRate * speed ) spike.soundPlaying = 0 spike.sndDelay = g_Time + 2000 end local function InitialiseSwinger( e, spike, speed, height, baseAng ) SetAnimationSpeed( e, speed ) spike.obj = g_Entity[ e ].obj local x, y, z, xA, yA, zA = GetObjectPosAng( spike.obj ) xA, yA, zA = rad( xA ), rad( yA ), rad( zA ) local baseRotQ = Q.FromEuler( 0, rad( baseAng ), 0 ) spike.quat = Q.Mul( Q.FromEuler( xA, yA, zA ), baseRotQ ) local xo, yo, zo = U.Rotate3D( 0, height, 0, xA, yA, zA ) spike.pos = { x = x + xo, y = y + yo, z = z + zo } CollisionOff( e ) spike.state = 'ready' InitSound( e, spike, speed ) end local function getSwingAngle( frame, fmin, fmax, amin, amax ) local numf = modf( ( fmax - fmin ) / 2 ) local fcentre = fmin + numf if frame < fcentre then return amin + ( fcentre - frame ) * ( amax - amin ) / numf else return amin + ( frame - fcentre ) * ( amax - amin ) / numf end end local function getSwingEuler( spike, frame, fmin, fmax, amin, amax ) local quat = Q.Mul( spike.quat, Q.FromEuler( rad( getSwingAngle( frame, fmin, fmax, amin, amax ) ), 0, 0 ) ) return Q.ToEuler( quat ) end local function closestTrap() closest = { dist = math.huge, ent = 0 } for k, v in pairs( spikes ) do if v.pDist ~= nil and v.pDist < closest.dist then closest.dist = v.pDist closest.ent = k end end return closest end local function handleContact( e, spike, xo, yo, length, xa, ya, za, distp, disto ) local xo, yo, zo = U.Rotate3D( xo, yo, length, xa, ya, za ) local x, y, z = spike.pos.x, spike.pos.y, spike.pos.z if gotPlayer( x + xo, y + yo, z + zo, distp ) then HurtPlayer( e, hurtAmount ) end HurtOthers( e, spike, { x = x + xo, y = y + yo, z = z + zo }, disto ) if ShowDecal1 ~= nil then local closest = closestTrap() if closest.dist < 500 and closest.ent == e then ShowDecal1( x + xo, y + yo, z + zo ) end end end function spikes_main( e ) if playerLives == nil then playerLives = g_PlayerLives else -- when the player dies all sound seems to stop so this -- ugly code is here to detect this and restart the sounds if playerLives > g_PlayerLives then for _, v in pairs( spikes ) do v.state = 'init' end end playerLives = g_PlayerLives end local spike = spikes[ e ] if spike == nil then return end local frame = GetAnimationFrame( e ) if spike.state == 'ready' then if g_Time > spike.npcTimer then spike.npcList = U.ClosestEntities( 200, 10, spike.pos.x, spike.pos.z ) spike.npcTimer = g_Time + random( 400, 800 ) end spike.pDist = GetPlayerDistance( e ) end if spike.name == 'rotatespikes' then if spike.state == 'init' then local speed = 0.5 + random() SetAnimationSpeed( e, speed ) spike.obj = g_Entity[ e ].obj local x, y, z, xA, yA, zA = GetObjectPosAng( spike.obj ) spike.pos = { x = x, y = y, z = z } local baseRotQ = Q.FromEuler( 0, rad( spikeVars.baseAng ), 0 ) spike.quat = Q.Mul( Q.FromEuler( rad( xA ), rad( yA ), rad( zA ) ), baseRotQ ) spike.state = 'ready' InitSound( e, spike, speed ) elseif spike.state == 'ready' and ( U.PlayerCloserThanPos( spike.pos.x, spike.pos.y, spike.pos.z, 200 ) or #spike.npcList > 1 ) then local angle = ( frame - spikeVars.framestart ) * ( -360 / ( spikeVars.frameend - spikeVars.framestart ) ) local quat = Q.Mul( spike.quat, Q.FromEuler( 0, rad( angle ), 0 ) ) local xa, ya, za = Q.ToEuler( quat ) handleContact( e, spike, 0, 20, spikeVars.spikelen, xa, ya, za, 30, 40 ) handleContact( e, spike, 0, 60, spikeVars.spikelen, xa, ya, za, 30, 40 ) end elseif spike.name == 'swordtrap' then local length = frame if spike.state == 'init' then local speed = 0.5 + random() SetAnimationSpeed( e, speed ) spike.obj = g_Entity[ e ].obj local x, y, z, xA, yA, zA = GetObjectPosAng( spike.obj ) spike.pos = { x = x, y = y, z = z } spike.quat = Q.FromEuler( rad( xA ), rad( yA ), rad( zA ) ) spike.state = 'ready' InitSound( e, spike, speed ) elseif spike.state == 'ready' then if length > 100 then length = 200 - length end local xa, ya, za = Q.ToEuler( spike.quat ) handleContact( e, spike, -30, 50, 70 - length * 1.4, xa, ya, za, 30, 40 ) end elseif spike.name == 'big_pendulum' then if spike.state == 'init' then InitialiseSwinger( e, spike, 0.5 + random() / 2, 300, 0 ) elseif spike.state == 'ready' and ( U.PlayerCloserThanPos( spike.pos.x, spike.pos.y, spike.pos.z, 300 ) or #spike.npcList > 1 )then local xa, ya, za = getSwingEuler( spike, frame, 1, 119, 25, 155 ) handleContact( e, spike, 0, 0, 250, xa, ya, za, 30, 40 ) handleContact( e, spike, 0, 0, 200, xa, ya, za, 30, 40 ) end elseif spike.name == 'pendulum_animated' then if spike.state == 'init' then SetAnimationFrames( 951, 1051 ) LoopAnimation( e ) InitialiseSwinger( e, spike, 0.5 + random() / 2, 100, 90 ) elseif spike.state == 'ready' and ( U.PlayerCloserThanPos( spike.pos.x, spike.pos.y, spike.pos.z, 300 ) or #spike.npcList > 1 )then local xa, ya, za = getSwingEuler( spike, frame, 951, 1051, 25, 155 ) handleContact( e, spike, 0, 0, 80, xa, ya, za, 30, 40 ) handleContact( e, spike, 0, 0, 50, xa, ya, za, 30, 40 ) end elseif spike.name == 'rotatingswordtrap' then if spike.state == 'init' then spike.obj = g_Entity[ e ].obj local speed = 0.5 + random() SetAnimationSpeed( e, speed ) local x, y, z, xA, yA, zA = GetObjectPosAng( spike.obj ) spike.pos = { x = x, y = y, z = z } local baseRotQ = Q.FromEuler( 0, rad( swordVars.baseAng ), 0 ) spike.quat = Q.Mul( Q.FromEuler( rad( xA ), rad( yA ), rad( zA ) ), baseRotQ ) spike.state = 'ready' InitSound( e, spike, speed ) elseif spike.state == 'ready' and ( U.PlayerCloserThanPos( spike.pos.x, spike.pos.y, spike.pos.z, 200 ) or #spike.npcList > 1 ) then local angle = ( frame - swordVars.framestart ) * ( -360 / ( swordVars.frameend - swordVars.framestart ) ) local rotquat1 = Q.FromEuler( 0, rad( angle ), 0 ) local quat = Q.Mul( spike.quat, rotquat1 ) local xa, ya, za = Q.ToEuler( quat ) handleContact( e, spike, 0, 55, swordVars.spikelen, xa, ya, za, 30, 40 ) local rotquat2 = Q.FromEuler( 0, rad( angle -180 ), 0 ) quat = Q.Mul( spike.quat, rotquat2 ) xa, ya, za = Q.ToEuler( quat ) handleContact( e, spike, 0, 20, swordVars.spikelen, xa, ya, za, 30, 40 ) end elseif spike.name == 'rotatespikes2_ani' then if spike.state == 'init' then InitialiseSwinger( e, spike, 0.5 + random() / 2, 100, 0 ) elseif spike.state == 'ready' and ( U.PlayerCloserThanPos( spike.pos.x, spike.pos.y, spike.pos.z, 200 ) or #spike.npcList > 1 )then local xa, ya, za = getSwingEuler( spike, frame, 1, 101, 0, 180 ) handleContact( e, spike, -45, 0, 70, xa, ya, za, 40, 40 ) handleContact( e, spike, 45, 0, 70, xa, ya, za, 40, 40 ) handleContact( e, spike, 0, 0, 70, xa, ya, za, 40, 40 ) end elseif spike.name == 'spikefloor' then if spike.state == 'init' then SetAnimationSpeed( e, 1.0 ) spike.obj = g_Entity[ e ].obj local x, y, z = GetObjectPosAng( spike.obj ) spike.pos = { x = x, y = y, z = z } spike.rate = 800 + random() * 1600 spike.timer = g_Time + spike.rate spike.state = 'ready' StopAnimation( e ) SetAnimationFrame( e, 25 ) CollisionOff( e ) spike.sndDelay = g_Time + 2000 elseif spike.state == 'ready' then if g_Time > spike.timer then SetAnimationFrames( 25, 46 ) PlayAnimation( e ) spike.state = 'strike' if g_Time > spike.sndDelay then PlaySound( e, 1 ) end spike.soundPlaying = 1 end elseif spike.state == 'strike' then if frame == 46 then spike.timer = g_Time + spike.rate SetAnimationFrame( e, 25 ) spike.state = 'ready' spike.soundPlaying = false elseif frame > 27 and frame < 44 then if U.PlayerCloserThan( e, 70 ) then HurtPlayer( e, hurtAmount ) end HurtOthers( e, spike, spike.pos, 70 ) end end end if spike.soundPlaying == 0 then if g_Time > spike.sndDelay then spike.sndDelay = math.huge LoopSound( e, 1 ) SetSound( e, 1 ) SetSoundVolume( 0 ) spike.soundPlaying = 1 end elseif spike.soundPlaying == 1 then SetSound( e, 1 ) SetSoundVolume( 99 - ( GetPlayerDistance( e ) / 30 ) ) end end