|
| 1 | +if SERVER then |
| 2 | + local function GetInterceptDirection(origin, targetPosition, missileSpeed, targetVelocity) |
| 3 | + local los = targetPosition - origin |
| 4 | + local distance = los:Length() |
| 5 | + local alpha = math.acos(los:Dot(targetVelocity) / (distance * targetVelocity:Length())) |
| 6 | + local vt = targetVelocity:Length() |
| 7 | + local vRatio = vt / missileSpeed |
| 8 | + |
| 9 | + local a = 1 - (vRatio * vRatio) |
| 10 | + local b = 2 * vRatio * distance * math.cos(alpha) |
| 11 | + local c = -distance * distance |
| 12 | + |
| 13 | + local discriminant = b * b - 4 * a * c |
| 14 | + |
| 15 | + if discriminant < 0 then |
| 16 | + return Vector(0, 0, 0), false |
| 17 | + end |
| 18 | + |
| 19 | + local root1 = (-b + math.sqrt(discriminant)) / (2 * a) |
| 20 | + local root2 = (-b - math.sqrt(discriminant)) / (2 * a) |
| 21 | + |
| 22 | + local interceptVectorMagnitude = math.max(root1, root2) |
| 23 | + local time = interceptVectorMagnitude / missileSpeed |
| 24 | + local estimatedPos = targetPosition + targetVelocity * time |
| 25 | + local result = (estimatedPos - origin):GetNormalized() |
| 26 | + |
| 27 | + return result, true |
| 28 | + end |
| 29 | + |
| 30 | + local function SpawnRocketEntity(ply) |
| 31 | + if IsValid(ply) then |
| 32 | + local pos = ply:GetPos() |
| 33 | + local ang = ply:EyeAngles() |
| 34 | + local dir = ang:Forward() |
| 35 | + pos = pos + dir * 300 |
| 36 | + pos.z = pos.z + 250 |
| 37 | + |
| 38 | + local ent = ents.Create("prop_physics") |
| 39 | + ent:SetModel("models/props_junk/PropaneCanister001a.mdl") |
| 40 | + ent:SetPos(pos) |
| 41 | + ent:Spawn() |
| 42 | + |
| 43 | + util.SpriteTrail(ent, 0, Color(0, 0, 0), false, 15, 1, 4, 1 / (15 + 1) * 0.5, "trails/smoke.vmt") |
| 44 | + |
| 45 | + local physObj = ent:GetPhysicsObject() |
| 46 | + if IsValid(physObj) then |
| 47 | + physObj:SetInertia(Vector(0.1, 0.1, 0.1)) |
| 48 | + end |
| 49 | + |
| 50 | + local cone = ents.Create("prop_physics") |
| 51 | + cone:SetModel("models/props_junk/TrafficCone001a.mdl") |
| 52 | + cone:Spawn() |
| 53 | + cone:SetCollisionGroup(COLLISION_GROUP_WORLD) |
| 54 | + |
| 55 | + local timeCounter = 0 |
| 56 | + local lastPos = ent:GetPos() |
| 57 | + local missileSpeeds = {} |
| 58 | + |
| 59 | + timer.Create("RocketPushTimer" .. tostring(ent:EntIndex()), 0.05, 0, function() |
| 60 | + if IsValid(ent) then |
| 61 | + if IsValid(physObj) then |
| 62 | + timeCounter = timeCounter + 50 |
| 63 | + if timeCounter < 3000 then |
| 64 | + physObj:ApplyForceCenter(Vector(0, 0, 10000)) |
| 65 | + else |
| 66 | + local currentPos = ent:GetPos() |
| 67 | + table.insert(missileSpeeds, (currentPos - lastPos):Length() / 0.05) |
| 68 | + if #missileSpeeds > 20 then -- keep the last 20 speed measurements |
| 69 | + table.remove(missileSpeeds, 1) |
| 70 | + end |
| 71 | + local missileSpeed = 0 |
| 72 | + for _, speed in ipairs(missileSpeeds) do |
| 73 | + missileSpeed = missileSpeed + speed |
| 74 | + end |
| 75 | + missileSpeed = missileSpeed / #missileSpeeds |
| 76 | + |
| 77 | + local targetPos = ply:GetPos() |
| 78 | + local playerVelocity = ply:GetVelocity() |
| 79 | + local distance = ent:GetPos():Distance(targetPos) |
| 80 | + |
| 81 | + local forceDir, interceptPossible = GetInterceptDirection(ent:GetPos(), targetPos, missileSpeed, playerVelocity) |
| 82 | + |
| 83 | + if interceptPossible then |
| 84 | + physObj:ApplyForceCenter(forceDir * 15000) |
| 85 | + end |
| 86 | + |
| 87 | + cone:SetPos(targetPos + Vector(0, 0, 50)) |
| 88 | + |
| 89 | + if distance < 150 then |
| 90 | + local effectdata = EffectData() |
| 91 | + effectdata:SetOrigin(ent:GetPos()) |
| 92 | + util.Effect("Explosion", effectdata) |
| 93 | + util.Effect("cball_explode", effectdata) |
| 94 | + ent:EmitSound("ambient/explosions/explode_4.wav") |
| 95 | + ent:Remove() |
| 96 | + |
| 97 | + if IsValid(cone) then |
| 98 | + cone:Remove() |
| 99 | + end |
| 100 | + end |
| 101 | + end |
| 102 | + lastPos = ent:GetPos() |
| 103 | + end |
| 104 | + else |
| 105 | + timer.Remove("RocketPushTimer" .. tostring(ent:EntIndex())) |
| 106 | + end |
| 107 | + end) |
| 108 | + end |
| 109 | + end |
| 110 | + |
| 111 | + local scriptPlayer = Entity(1) |
| 112 | + timer.Create("SpawnRocketTimer", 1, 4, function() |
| 113 | + SpawnRocketEntity(scriptPlayer) |
| 114 | + end) |
| 115 | +end |
0 commit comments