Divergent/mods/Hideout Furniture/gamedata/scripts/placeable_radio.script

617 lines
24 KiB
Plaintext
Raw Normal View History

2024-03-17 20:18:03 -04:00
--[[
Tronex
Last edit: 2018/5/22
PDA radio/music player
==============================================================================================
Codes/comments with ( --< can be expanded for more channels >-- ) tag can be edited to add more radio channels
Creating new radio channel require editing the other XML and DDS files as well.
Using (radio_vol = soundObject.volume) will cause a crash if you didn't give the object a volume value
Sounds start always at maxed volume before going down if you declare a volume value before playing the sound
Don't use (play_no_feedback) method, it has no controls
volume values changes give fractions (for example 0.2 change is actually 0.20000003278255)
Codes/comments with ( --< can be expanded for more channels >-- ) tag can be edited to add more radio channels
==============================================================================================
--]]
------------------------------------------------------------
-- Controls
------------------------------------------------------------
-- Files links & controls
local ini_radio = ini_file("plugins\\radio_zone_fm.ltx") -- File control: reading the ltx file that controls the hotkeys
local num_of_ch = 0 --< can be expanded for more channels >--- Number of Radio channels
local num_of_noise = 8 -- File control: number of files with name (white_noise_heavy_horror_)
local num_of_mixdown = 4 -- File control: number of files with name (radio_mixdown)
local path_radio_ch = {} -- File control: the path to channel x tracks
local path_radio_session = {} -- File control: the path to channel x sessions
local radio_ch_names = {}
local radio_frequencies = {}
local ini_channels = ini_file_ex("plugins\\placeable_radio\\base.ltx")
local i = 1
for _, section in pairs(ini_channels:get_sections()) do
path_radio_ch[i] = ini_channels:r_string_ex(section, "program")
path_radio_session[i] = ini_channels:r_string_ex(section, "intermission")
radio_ch_names[i] = ini_channels:r_string_ex(section, "name")
radio_frequencies[i] = ini_channels:r_string_ex(section, "frequency")
num_of_ch = num_of_ch + 1
i = i + 1
end
local fileList, count, file -- Variables for file reading
-- Volume controls
local radio_vol = 0.5 -- In-game control: Radio volume - first value
local vol_max = 0.98 -- In-game control: Max volume value (1.1 - anything beyond 1.0 has no additional effect)
local vol_min = 0.12 -- In-game control: Min volume value (0.1 - still hearable)
local vol_step = 0.2 -- In-game control: Volume change per click
local psi_vol = 0 -- This value get subtracted from the main volume value, it become >0 on emissions to reduce the total radio volume
-- Others
local safety_lock = false -- become "true" after making change. while true, it prevents any possible additional changes done by user for a brief amount of time until the code is over. basically a safety procedure to prevent codes from interfering with each others (locked = true, free to go = false)
local white_noise_now = 1 -- Current index of the white noise
local psi_start_lock = false -- used to separate stages of for emission noise effects
local psi_time = time_global() or 0 -- period between radio volume shifts for emission/underground noise effects
local indoor_lvls = {} -- Table: contains names of underground levels
local gini = game_ini()
local levels = utils_data.collect_section(gini,"level_maps_single")
for i=1,#levels do
wthr = gini:r_string_ex(levels[i],"weathers")
if (wthr == "indoor") then
indoor_lvls[levels[i]] = true
end
end
-- Hotkeys
local opt = {}
function update_settings()
opt.emission_noise = ui_options.get("sound/radio/emission_intereferences")
opt.underground_noise = ui_options.get("sound/radio/underground_intereferences")
opt.display_names = ui_options.get("sound/radio/display_tracks")
end
------------------------------------------------------------
-- Binder
------------------------------------------------------------
function init(obj)
obj:bind_object(placeable_radio_wrapper(obj).binder)
end
--------------------------------------------------------------------------------
-- Class "placeable_radio_wrapper"
--------------------------------------------------------------------------------
class "placeable_radio_wrapper" (bind_hf_base.hf_binder_wrapper)
-- Class constructor
function placeable_radio_wrapper:__init(obj) super(obj)
-- Radio
self.radio_ch = {} -- Multi-dimensional table: Radio channels, every channel has its tracks
self.radio_ch_out = {} -- Similar, used for outer radios
self.radio_index = {} -- Multi-dimensional table: Radio channels indexes, every channel index has its track indexes
self.radio_now = {} -- Table: to determine the current track for each channel
for x = 1, num_of_ch do -- Reading the files for radio channels
self.radio_ch[x] = {}
self.radio_ch_out[x] = {}
self.radio_index[x] = {}
self.radio_now[x] = 1
fileList = getFS():file_list_open("$game_sounds$", path_radio_ch[x], bit_or(FS.FS_ListFiles, FS.FS_RootOnly)) -- Reading the file list in channel x
count = fileList and fileList:Size() or 0 -- Get the number of files per channel x
if (count > 0) then
for i = 1, count do
file = fileList:GetAt(i - 1):sub(1, -5) -- Get red off the extension
self.radio_ch[x][i] = sound_object(path_radio_ch[x] .. file) -- Creating sound objects, every track in every channel
self.radio_ch_out[x][i] = sound_object(path_radio_ch[x] .. file)
self.radio_index[x][i] = i -- Creating track index
end
else -- if no files found
self.radio_ch[x][1] = sound_object("radio\\no_sound") -- assign no sound, a small hack to prevent the game from crashing
self.radio_ch_out[x][1] = sound_object("radio\\no_sound")
self.radio_index[x][1] = 1
end
end
self.radio_state = false -- Radio on/off state (off = false, on = true)
self.radio_selected = 1 -- Selected Radio channel (0 means nothing is selected)
self.radio_hotkey_switch = false -- Safety switch for (Play/Stop Radio) Hotkey
-- Radio sessions
self.radio_session = {} -- Multi-dimensional table: Radio channels commercial sessions, every channel has its sessions
self.radio_session_out = {} -- Similar, used for outer radios
self.radio_session_now = {} -- Current session per channel
self.radio_session_lock = {} -- Boolean table: used to make sure that only 1 session is being played between tracks
for x = 1, num_of_ch do -- assign sound files for sessions
self.radio_session[x] = {}
self.radio_session_out[x] = {}
self.radio_session_now[x] = 1
self.radio_session_lock[x] = false
fileList = getFS():file_list_open("$game_sounds$", path_radio_session[x], bit_or(FS.FS_ListFiles, FS.FS_RootOnly)) -- Reading the file list in channel x sessions
count = fileList and fileList:Size() or 0 -- Get the number of files per channel x
if (count > 0) then
for i = 1, count do
file = fileList:GetAt(i - 1):sub(1, -5)
self.radio_session[x][i] = sound_object(path_radio_session[x] .. file)
self.radio_session_out[x][i] = sound_object(path_radio_session[x] .. file)
end
else
self.radio_session[x][1] = sound_object("radio\\no_sound")
self.radio_session_out[x][1] = sound_object("radio\\no_sound")
end
end
-- Sound effects
self.snd_radio_mixdown = {}
self.snd_white_noise_heavy_horror = {}
self.snd_radio_on = sound_object("radio\\interact\\radio_on")
self.snd_radio_off = sound_object("radio\\interact\\radio_off")
self.snd_white_noise_light = sound_object("radio\\white_noise\\white_noise_light")
self.snd_white_noise_heavy = sound_object("radio\\white_noise\\white_noise_heavy")
self.snd_white_noise_heavy_start = sound_object("radio\\white_noise\\white_noise_heavy_fade_in")
self.snd_white_noise_heavy_end = sound_object("radio\\white_noise\\white_noise_heavy_fade_out")
for i = 1, num_of_mixdown do
self.snd_radio_mixdown[i] = sound_object("radio\\interact\\radio_mixdown_" .. tostring(i))
self.snd_radio_mixdown[i].volume = 2
end
for i = 1, num_of_noise do
self.snd_white_noise_heavy_horror[i] = sound_object("radio\\white_noise\\White_noise_heavy_horror_" .. tostring(i))
end
self.snd_radio_on.volume = 2
self.snd_radio_off.volume = 2
self.object:set_tip_text(game.translate_string("st_interact"))
end
-- Class update
function placeable_radio_wrapper:update(delta)
-- self.object:set_callback(callback.use_object, placeable_radio_wrapper.use_callback, self)
if not self.init then
local phys = self.object:get_physics_shell()
if phys then
phys:apply_force(0, 1, 0)
self.init = true
end
end
-- Radio (no channel) part
if self.radio_state and (self.radio_selected == 0) then
if not self.snd_white_noise_light:playing() then
self.snd_white_noise_light:play_at_pos(self.object, self.object:position(), 0, sound_object.s3d)
self.snd_white_noise_light.volume = 0.4
end
else
self.snd_white_noise_light:stop()
end
-- Consume power from PDA if radio/player is ON
-- change obj:condition()
if (not safety_lock) then
-- Radio part
if (not self:is_snd_playing()) then
for i=1,num_of_ch do
if (not self.radio_ch[i][self.radio_now[i]]:playing()) and (not self.radio_session[i][self.radio_session_now[i]]:playing()) then -- if no track and no session is playing in channel (i)
if (math.random(3) == 1) and (not self.radio_session_lock[i]) then -- chance of playing a session between tracks (%30)
self.radio_session_now[i] = math.random(#self.radio_session[i]) -- pick a session from channel i
self.radio_session[i][self.radio_session_now[i]]:play_at_pos(self.object, self.object:position(), 0, sound_object.s3d) -- play it
self.radio_session_lock[i] = true
else -- if no session is picked, go for radio tracks
self.radio_now[i] = self:radio_pick (self.radio_index[i], #self.radio_ch[i]) -- pick a track from channel i
self.radio_ch[i][self.radio_now[i]]:play_at_pos(self.object, self.object:position(), 0, sound_object.s3d) -- play it
self.radio_session_lock[i] = false
end
end
if self.radio_state and (self.radio_selected == i) then
self.radio_ch[i][self.radio_now[i]].volume = radio_vol - psi_vol
self.radio_session[i][self.radio_session_now[i]].volume = radio_vol - psi_vol
else
self.radio_ch[i][self.radio_now[i]].volume = 0
self.radio_session[i][self.radio_session_now[i]].volume = 0
end
end
end
end
-- Emission part
if opt.emission_noise then
if xr_conditions.surge_started() or psi_storm_manager.is_started() then
if not psi_start_lock then -- lock to make sure this condition happen once per emission
psi_start_lock = true
self.snd_white_noise_heavy_start:play_at_pos(self.object, self.object:position(), 0, sound_object.s3d)
end
if (not self.snd_white_noise_heavy_start:playing()) and (not self.snd_white_noise_heavy_horror[white_noise_now]:playing()) then
white_noise_now = math.random(#self.snd_white_noise_heavy_horror)
self.snd_white_noise_heavy_horror[white_noise_now]:play_at_pos(self.object, self.object:position(), 0, sound_object.s3d)
end
if (math.floor(psi_time + 1500) < time_global()) then
psi_time = time_global()
psi_vol = math.random(2,8)/10
end
elseif psi_start_lock then
if (xr_conditions.surge_complete() or psi_storm_manager.is_finished()) and (not self.snd_white_noise_heavy_horror[white_noise_now]:playing()) and (not self.snd_white_noise_heavy_start:playing()) and (not self.snd_white_noise_heavy_end:playing()) then -- if emission ended AND start/end sound are not playing AND emission lock is on
self.snd_white_noise_heavy_end:play_at_pos(self.object, self.object:position(), 0, sound_object.s3d)
psi_start_lock = false
psi_vol = 0
end
end
else
self.snd_white_noise_heavy_horror[white_noise_now]:stop()
self.snd_white_noise_heavy_start:stop()
self.snd_white_noise_heavy_end:stop()
psi_start_lock = false
psi_vol = 0
end
-- Underground part
if indoor_lvls[level.name()] then
if opt.underground_noise then
if (not self.snd_white_noise_heavy:playing()) then
self.snd_white_noise_heavy:play_at_pos(self.object, self.object:position(), 0, sound_object.s3d)
end
if (math.floor(psi_time + 1500) < time_global()) then
psi_time = time_global()
psi_vol = math.random(5,8)/10
end
else
psi_vol = 0
end
end
if self.radio_state then
self.snd_white_noise_heavy_horror[white_noise_now].volume = radio_vol
self.snd_white_noise_heavy_start.volume = radio_vol
self.snd_white_noise_heavy_end.volume = radio_vol
self.snd_white_noise_heavy.volume = radio_vol
else
self.snd_white_noise_heavy_horror[white_noise_now].volume = 0
self.snd_white_noise_heavy_start.volume = 0
self.snd_white_noise_heavy_end.volume = 0
self.snd_white_noise_heavy.volume = 0
end
end
function placeable_radio_wrapper:use_callback()
hide_hud_inventory()
start_radio_dialog(self.object:id())
end
function placeable_radio_wrapper:use_callback_simple()
if self.radio_state then
self:action_radio_stop()
else
self:action_radio_start()
end
end
------------------------------------------------------------
-- Functionality
------------------------------------------------------------
function placeable_radio_wrapper:action_radio_ch(n) -- Rule: Switch to Channel (n)
if self.radio_state and (not safety_lock) and (not self:is_snd_playing()) and (self.radio_selected ~= n) then
safety_lock = true
-- get_ui():SwitchChannel(n)
self:radio_setVolume(0) -- set volume 0 to the previous channel
self.radio_selected = n
--snd_click:play_at_pos(self.object, self.object:position(), 0, sound_object.s3d)
self.snd_radio_mixdown[math.random(#self.snd_radio_mixdown)]:play_at_pos(self.object, self.object:position(), 0, sound_object.s3d)
safety_lock = false
return true
end
return false
end
function placeable_radio_wrapper:action_radio_v_down() -- Rule: Reduce the radio volume
if (radio_vol > vol_min) and self.radio_state and (not safety_lock) and (not self:is_snd_playing()) then
safety_lock = true
--snd_click:play_at_pos(self.object, self.object:position(), 0, sound_object.s3d)
radio_vol = radio_vol - vol_step
self:radio_setVolume(radio_vol - psi_vol)
safety_lock = false
end
end
function placeable_radio_wrapper:action_radio_v_up() -- Rule: Increase the radio volume
if (radio_vol < vol_max) and self.radio_state and (not safety_lock) and (not self:is_snd_playing()) then
safety_lock = true
--snd_click:play_at_pos(self.object, self.object:position(), 0, sound_object.s3d)
radio_vol = radio_vol + vol_step
self:radio_setVolume(radio_vol - psi_vol)
safety_lock = false
end
end
function placeable_radio_wrapper:action_radio_stop() -- Rule: Stop the radio
if self.radio_state and (not safety_lock) and (not self:is_snd_playing()) then
safety_lock = true
self.radio_state = false
--snd_click:play_at_pos(self.object, self.object:position(), 0, sound_object.s3d)
self.snd_radio_off:play_at_pos(self.object, self.object:position(), 0, sound_object.s3d)
self:radio_setVolume(0)
safety_lock = false
end
end
function placeable_radio_wrapper:action_radio_start() -- Rule: Start the radio
if (not self.radio_state)
and (not safety_lock)
and (not self:is_snd_playing())
then
safety_lock = true
self.radio_state = true
--snd_click:play_at_pos(self.object, self.object:position(), 0, sound_object.s3d)
self.snd_radio_on:play_at_pos(self.object, self.object:position(), 0, sound_object.s3d)
self:radio_setVolume(radio_vol - psi_vol)
safety_lock = false
end
end
----------------------------------------------------------------------------------------
function placeable_radio_wrapper:is_snd_playing() -- Rule: check if some interacting sound is currently playing | it prevents any changes done by the player until that sound is over (no sound interfering)
if self.snd_radio_on:playing() or self.snd_radio_off:playing() then
return true
end
for j = 1, #self.snd_radio_mixdown do
if self.snd_radio_mixdown[j]:playing() then
return true
end
end
return false
end
function placeable_radio_wrapper:radio_setVolume(radio_vol) -- Rule: set volume for selected channel
-- This might seem unnecessary at first glance since the volume get modified always through (actor_on_update) callback
-- but tests showed that volume changes slowly, not instantly.
-- with bigger code snippets between the sound play and volume change, its even slower.
-- This function make things faster for volume changes.
if (self.radio_selected ~= 0) then
self.radio_ch[self.radio_selected][self.radio_now[self.radio_selected]].volume = radio_vol
self.radio_session[self.radio_selected][self.radio_session_now[self.radio_selected]].volume = radio_vol
end
end
function placeable_radio_wrapper:radio_pick(radio_index_i, number_of_tracks) -- Rule: pick a random index for radio channels, get rid of the picked index afterwards so it doesn't get chosen again until a new cycle is called
if (#radio_index_i == 0) then
for i=1,number_of_tracks do
radio_index_i[i] = i
end
end
local j = math.random(#radio_index_i)
local k = radio_index_i[j]
table.remove(radio_index_i, j)
return k
end
function placeable_radio_wrapper:get_playing_object(i) -- called from ph_sound to play current track on the radio on radio objects
local sound_obj = self.radio_session_out[i][self.radio_session_now[i]]
if (self.radio_session[i][self.radio_session_now[i]]:playing()) then
return sound_obj
end
sound_obj = self.radio_ch_out[i][self.radio_now[i]]
if (self.radio_ch[i][self.radio_now[i]]:playing()) then
return sound_obj
end
return
end
------------------------------------------------------------
-- GUI
------------------------------------------------------------
local SINGLETON = nil
function start_radio_dialog(obj_id) -- get called from pressing the tab
SINGLETON = SINGLETON or UIFurnitureRadio()
SINGLETON:TestAndShow(obj_id)
end
class "UIFurnitureRadio" (CUIScriptWnd)
function UIFurnitureRadio:__init() super()
self.snd_click = sound_object("radio\\interact\\click")
self.radio_display_ch = {}
-- for i=1,num_of_ch do
-- self.radio_display_ch[i] = false -- If true, it will display channel i red marker
-- end
self.frequency_min = 88
self.frequency_max = 110
self.frequency_min_x = 14
self.frequency_max_x = 397
self.frequency = 88
self:InitControls()
self:InitCallbacks()
end
function UIFurnitureRadio:__finalize()
SINGLETON = nil
end
function UIFurnitureRadio:TestAndShow(obj_id)
self.obj_id = obj_id
self:ShowDialog(true)
Register_UI("UIFurnitureRadio","ui_radio_dialog")
end
function UIFurnitureRadio:Pickup()
local obj = get_object_by_id(self.obj_id)
local item_section = ini_sys:r_string_ex(obj:section(), "item_section") or "device_gas_lamp"
-- local condition = obj:binded_object().fuel
alife_create_item(item_section, db.actor)
-- local item = alife():create(item_section, db.actor:position(), 1, db.actor:game_vertex_id(), db.actor:id())
-- get_object_by_id(item.id):set_condition(obj:binded_object().fuel)
alife_release(obj)
self:Close()
end
function UIFurnitureRadio:InitControls() -- Rule: Prepare the UI
local xml = CScriptXmlInit()
xml:ParseFile("ui_furniture_radio.xml")
-- Main frame
self:SetWndRect(Frect():set(0, 0, 1024, 768))
xml:InitFrame("background", self)
self.btn_pickup = xml:Init3tButton("btn_pickup", self)
self:Register(self.btn_pickup, "btn_pickup")
self.btn_close = xml:Init3tButton("btn_close", self)
self:Register(self.btn_close, "btn_close")
-- Radio channels
self.frequency_bg = xml:InitStatic("interface_radio_bg", self)
self.frequency_needle = xml:InitStatic("interface_radio_needle", self.frequency_bg)
self.btn_radio_ch = {}
self.list_channel = xml:InitComboBox("list_channel", self)
self:Register(self.list_channel, "list_channel")
for i=1,num_of_ch do
self.list_channel:AddItem(game.translate_string(radio_ch_names[i]), i)
end
local index = prevChannel or 1
self.list_channel:SetText(game.translate_string(radio_ch_names[index]))
-- Radio buttons
self.btn_radio_v_down = xml:Init3tButton("btn_radio_v_down", self)
self:Register(self.btn_radio_v_down, "btn_radio_v_down")
self.btn_radio_v_up = xml:Init3tButton("btn_radio_v_up", self)
self:Register(self.btn_radio_v_up, "btn_radio_v_up")
self.btn_radio_stop = xml:Init3tButton("btn_radio_stop", self)
self:Register(self.btn_radio_stop, "btn_radio_stop")
self.btn_radio_start = xml:Init3tButton("btn_radio_start", self)
self:Register(self.btn_radio_start, "btn_radio_start")
end
function UIFurnitureRadio:InitCallbacks()
-- Radio
-- for i=1,num_of_ch do
-- self:AddCallback("btn_radio_ch_" .. i, ui_events.BUTTON_CLICKED, self["On_Radio_Channel_" .. i], self)
-- end
self:AddCallback("btn_close", ui_events.BUTTON_CLICKED, self.Close, self)
self:AddCallback("btn_pickup", ui_events.BUTTON_CLICKED, self.Pickup, self)
self:AddCallback("list_channel", ui_events.LIST_ITEM_SELECT, self.OnSelectChannel, self)
self:AddCallback("btn_radio_stop", ui_events.BUTTON_CLICKED, self.On_Radio_Stop, self)
self:AddCallback("btn_radio_start", ui_events.BUTTON_CLICKED, self.On_Radio_Start, self)
self:AddCallback("btn_radio_v_down", ui_events.BUTTON_CLICKED, self.On_Radio_Volume_Down, self)
self:AddCallback("btn_radio_v_up", ui_events.BUTTON_CLICKED, self.On_Radio_Volume_Up, self)
end
function UIFurnitureRadio:Update() -- Rule: called automatically to update the PDA UI
CUIScriptWnd.Update(self)
local pos = vector2():set(0, 0)
local range_freq = self.frequency_max - self.frequency_min
local percent = (self.frequency - self.frequency_min) / range_freq
local ui_range = self.frequency_max_x - self.frequency_min_x
pos.x = self.frequency_min_x + (ui_range * percent) - (self.frequency_needle:GetWidth()/2)
self.frequency_needle:SetWndPos(pos)
end
function UIFurnitureRadio:SwitchChannel(freq)
self.frequency = freq
end
function UIFurnitureRadio:OnKeyboard(dik, keyboard_action)
local res = CUIScriptWnd.OnKeyboard(self,dik,keyboard_action)
if (res == false) then
local bind = dik_to_bind(dik)
if keyboard_action == ui_events.WINDOW_KEY_PRESSED then
if dik == DIK_keys.DIK_ESCAPE then
self:Close()
end
end
end
return res
end
function UIFurnitureRadio:Close()
if (self:IsShown()) then
self:HideDialog()
end
--self.object:give_info_portion("tutorial_sleep")
Unregister_UI("UIFurnitureRadio")
end
-- Callbacks
function UIFurnitureRadio:OnSelectChannel()
local channel_id = self.list_channel:CurrentID()
local wrapper = bind_hf_base.get_wrapper(self.obj_id)
local result = wrapper:action_radio_ch(channel_id)
if result then
self:SwitchChannel(radio_frequencies[channel_id])
self.snd_click:play(db.actor, 0, sound_object.s2d)
end
end
function UIFurnitureRadio:On_Radio_Channel_1()
local wrapper = bind_hf_base.get_wrapper(self.obj_id)
local result = wrapper:action_radio_ch(1)
if result then
self:SwitchChannel(1)
self.snd_click:play(db.actor, 0, sound_object.s2d)
end
end
function UIFurnitureRadio:On_Radio_Channel_2()
local wrapper = bind_hf_base.get_wrapper(self.obj_id)
local result = wrapper:action_radio_ch(2)
if result then
self:SwitchChannel(2)
self.snd_click:play(db.actor, 0, sound_object.s2d)
end
end
--< can be expanded for more channels >-- you need to make new function for each new channel
function UIFurnitureRadio:On_Radio_Volume_Down()
local wrapper = bind_hf_base.get_wrapper(self.obj_id)
wrapper:action_radio_v_down()
self.snd_click:play(db.actor, 0, sound_object.s2d)
end
function UIFurnitureRadio:On_Radio_Volume_Up()
local wrapper = bind_hf_base.get_wrapper(self.obj_id)
wrapper:action_radio_v_up()
self.snd_click:play(db.actor, 0, sound_object.s2d)
end
function UIFurnitureRadio:On_Radio_Stop()
local wrapper = bind_hf_base.get_wrapper(self.obj_id)
wrapper:action_radio_stop()
self.snd_click:play(db.actor, 0, sound_object.s2d)
end
function UIFurnitureRadio:On_Radio_Start()
local wrapper = bind_hf_base.get_wrapper(self.obj_id)
wrapper:action_radio_start()
self.snd_click:play(db.actor, 0, sound_object.s2d)
end
function on_game_start()
local function on_game_load()
update_settings()
end
RegisterScriptCallback("on_option_change",update_settings)
RegisterScriptCallback("on_game_load",on_game_load)
end