196 lines
6.2 KiB
Plaintext
196 lines
6.2 KiB
Plaintext
-- custom rocket/grenade binder based on hawu impact grenade
|
|
|
|
ini_ammo = ini_file("ammo\\importer.ltx")
|
|
|
|
--[[
|
|
Credits: Thial
|
|
]]
|
|
|
|
class "geometry_ray"
|
|
--[[
|
|
(At least one range parameter should be specified)
|
|
ray_range:
|
|
Defines the total range of the ray. If you want to attach the ray to
|
|
a fast moving object it is good to extend the ray so that you can reduce
|
|
the polling rate by using the get function.
|
|
contact_range:
|
|
Defines the distance at which the result will report being in contact.
|
|
You can skip it or set it to a value lower than the ray_range to
|
|
still be able to get the intersection position from the result while
|
|
marking the ray as not being in contact yet
|
|
distance_offset:
|
|
Defines how much the intersection position is offset.
|
|
You can use both positive and negative values or you can leave it blank.
|
|
flags (bit map = values can be added together for combined effect):
|
|
0 : None
|
|
1 : Objects
|
|
2 : Statics
|
|
4 : Shapes
|
|
8 : Obstacles
|
|
]]--
|
|
function geometry_ray:__init(ray_range, contact_range, distance_offset, flags)
|
|
if ray_range == nil and contact_range == nil then
|
|
return nil
|
|
end
|
|
self.ray_range = ray_range or contact_range
|
|
self.contact_range = contact_range or ray_range
|
|
self.distance_offset = distance_offset ~= nil and distance_offset or 0
|
|
self.ray = ray_pick()
|
|
self.ray:set_ignore_object(db.actor)
|
|
self.ray:set_flags(flags or 2)
|
|
self.ray:set_range(self.ray_range)
|
|
end
|
|
--[[
|
|
position:
|
|
position from which the ray will start
|
|
direction:
|
|
direction in which the ray will be fired
|
|
]]--
|
|
function geometry_ray:get(position, direction)
|
|
if position == nil or direction == nil then
|
|
return nil
|
|
end
|
|
self.ray:set_position(position)
|
|
self.ray:set_direction(direction)
|
|
local distance = self.ray:query() and self.ray:get_distance() or self.ray_range
|
|
local result = {}
|
|
result.in_contact = distance <= self.contact_range
|
|
result.position = position:add(direction:mul(distance + self.distance_offset))
|
|
result.distance = distance + self.distance_offset
|
|
return result
|
|
end
|
|
|
|
function bind(obj)
|
|
if (not alife_object(obj:id())) then
|
|
obj:bind_object(custom_grenade_binder(obj))
|
|
end
|
|
end
|
|
--[[
|
|
|
|
--------------------------------------------------------------------------------
|
|
Class "custom_grenade_binder"
|
|
Usage is trickier than for bullets. In order to use it the following steps are required:
|
|
1. grenade ammo section needs to define a custom fake_grenade_name
|
|
2. said fake_grenade_name needs line `script_binding = bind_grenade.bind`
|
|
3. when projectile hits the ground, custom explosion will be triggered (default does nothing)
|
|
4. Default explosion spawns item defined by `bomb_type` and immediately detonates if possible
|
|
--------------------------------------------------------------------------------
|
|
]]
|
|
|
|
local VEC_GROUND = vector():set(0, -1, 0)
|
|
class "custom_grenade_binder" (object_binder)
|
|
|
|
function custom_grenade_binder:__init(obj)
|
|
super(obj)
|
|
|
|
self.sec = self.object:section()
|
|
self.ray = geometry_ray(0.11, 0.1, nil, 1 + 2)
|
|
self.ray.ray:set_ignore_object(obj)
|
|
|
|
-- self.ray = geometry_ray(40, 2, nil, 1 + 2)
|
|
-- self.ray.ray:set_ignore_object(obj)
|
|
-- self.frags_r = SYS_GetParam(2, self.sec, "frags_r", 6)
|
|
-- self.velocity_threshold = SYS_GetParam(2, self.sec, "velocity_threshold", 0.36)
|
|
self.tg_period = 33
|
|
self.object:set_fastcall(self.fastcall, self)
|
|
end
|
|
|
|
function custom_grenade_binder:fastcall()
|
|
local obj = self.object
|
|
|
|
if obj:parent() then
|
|
return
|
|
end
|
|
-- shamelessly stolen from molotovs
|
|
if (not self.position) then
|
|
-- self.velocity = 0
|
|
self.position = obj:position()
|
|
self.direction = VEC_GROUND
|
|
self.tg = 0
|
|
self.tg_time = 0
|
|
return
|
|
end
|
|
|
|
self.tg = self.tg + device().time_delta
|
|
|
|
if self.tg < self.tg_time then
|
|
return
|
|
end
|
|
|
|
self.tg_time = self.tg + self.tg_period
|
|
|
|
local pos = obj:position()
|
|
local dir = vector():set(pos):sub(self.position)
|
|
|
|
local result = self.ray:get(pos, dir)
|
|
if result.in_contact or self.tg_time > 5000 then
|
|
res = self:explode(pos, dir)
|
|
self.object:destroy_object()
|
|
end
|
|
end
|
|
|
|
local func
|
|
local last_ammo
|
|
function custom_grenade_binder:explode(pos, dir)
|
|
if not last_ammo then
|
|
local weapon = self.object:parent() or db.actor:active_item()
|
|
local ammo = "ammo_vog-25"
|
|
if weapon ~= nil then
|
|
ammo_map = utils_item.get_ammo(nil, weapon:id())
|
|
if not is_empty(ammo_map) then
|
|
ammo = ammo_map[weapon:get_ammo_type() + 1]
|
|
end
|
|
end
|
|
last_ammo = ammo
|
|
func = ballistics_mcm.get_handler(ammo)
|
|
end
|
|
if func and func[1] and func[2] and _G[func[1]] and _G[func[1]][func[2]] then
|
|
_G[func[1]][func[2]](pos, dir, last_ammo)
|
|
end
|
|
func = nil
|
|
end
|
|
|
|
function custom_grenade_binder:net_spawn(se_abstract)
|
|
if (not object_binder.net_spawn(self, se_abstract)) then
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
-- lock the grenade handler for the next round
|
|
function actor_on_weapon_before_fire(flags)
|
|
local weapon = db.actor:active_item()
|
|
if weapon and (weapon:weapon_in_grenade_mode() or weapon:clsid() == clsid.wpn_rg6_s or weapon:clsid() == clsid.wpn_rg6) then
|
|
local ammo = "ammo_vog-25"
|
|
if weapon ~= nil then
|
|
ammo_map = utils_item.get_ammo(nil, weapon:id())
|
|
if not is_empty(ammo_map) then
|
|
ammo = ammo_map[weapon:get_ammo_type() + 1]
|
|
end
|
|
end
|
|
func = ballistics_mcm.get_handler(ammo)
|
|
end
|
|
end
|
|
|
|
function explode(pos, dir, ammo)
|
|
local lvid = db.actor:level_vertex_id()
|
|
local gvid = db.actor:game_vertex_id()
|
|
local bomb_type = ini_ammo:r_string_ex(ammo, "bomb_type") or ""
|
|
if not bomb_type then return end
|
|
local bomb = alife_create_item(bomb_type, {pos, lvid, gvid})
|
|
CreateTimeEvent(bomb.id, bomb.id, 0, function()
|
|
local bomb_obj = level.object_by_id(bomb.id)
|
|
if bomb_obj then
|
|
bomb_obj:explode(0)
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end)
|
|
end
|
|
|
|
function on_game_start()
|
|
RegisterScriptCallback("actor_on_weapon_before_fire", actor_on_weapon_before_fire)
|
|
func = nil
|
|
end |