Divergent/mods/Anomaly Ballistics Overhaul/gamedata/scripts/bind_grenade.script

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