Divergent/mods/Western Goods/gamedata/scripts/western_goods_trade_eur_usd...

482 lines
20 KiB
Plaintext
Raw Normal View History

2024-03-17 20:18:03 -04:00
---==================================================================================================================---
--- ---
--- Original Author(s) : NLTP_ASHES ---
--- Edited : N/A ---
--- Date : 19/02/2024 ---
--- License : Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) ---
--- ---
--- Script used to manage the trade system, but with Euros/Dollars instead of Roubles. ---
--- ---
--- By default, if the npc has the "western_goods_trade_eur_usd" dialog, it will be available to the player. ---
--- If you want more control over this dialog, you can use the "western_goods_trade_eur_usd_on_dialog" callback. ---
--- ---
---=== gamedata/scripts/my_addon_s_script.script ====================================================================---
--- ---
--- function on_game_start() ---
--- RegisterScriptCallback("western_goods_trade_eur_usd_on_dialog", function(actor,npc,flags) ---
--- if npc:section() == "my_npc_section" and some_condition() then ---
--- -- The line of dialog will now be hidden ---
--- flags.result = false ---
--- end ---
--- end) ---
--- end ---
--- ---
---==================================================================================================================---
--- ---
--- By default, NPCs using the EUR/USD trade system are shielded (aka unaffected) from trader_autoinject.script. ---
--- If you want change that, you can use the "western_goods_trade_eur_usd_on_trader_autoinject" callback. ---
--- ---
---=== gamedata/scripts/my_addon_s_script.script ====================================================================---
--- ---
--- function on_game_start() ---
--- RegisterScriptCallback("western_goods_trade_eur_usd_on_trader_autoinject", function(npc,flags) ---
--- if npc:section() == "my_npc_section" and some_condition() then ---
--- -- The NPC will now receive items from trader_autoinject.script ---
--- flags.result = true ---
--- end ---
--- end) ---
--- end ---
--- ---
---==================================================================================================================---
--- ---
---==================================================================================================================---
-- ---------------------------------------------------------------------------------------------------------------------
-- Constants, global variables, imported functions and new callbacks
-- ---------------------------------------------------------------------------------------------------------------------
-- New callbacks
AddScriptCallback("western_goods_trade_eur_usd_on_dialog")
AddScriptCallback("western_goods_trade_eur_usd_on_trader_autoinject")
-- Imported functions
local dbg_printf = western_goods_utils.dbg_printf
local level_object_by_id = western_goods_utils.level_object_by_id
-- Constants
local bags_to_tint = { actor_equ = true, actor_bag = true, actor_trade = true, actor_trade_bag = true }
local eur_rouble_rate = 68
local usd_rouble_rate = 70
-- Variables
local traders_list = {}
local traders_names = {}
-- ---------------------------------------------------------------------------------------------------------------------
-- General Dialog Functions
-- ---------------------------------------------------------------------------------------------------------------------
--- Function called when the player uses the trade line of dialog.
--- @param npc game_object
--- @param actor game_object
--- @return nil
function start_trade(npc, actor)
if not this.is_eur_usd_trader(npc) then return end
if hide_hud_inventory() then
npc:start_trade(actor)
end
end
--- Function called to determine if the trade line of dialog should be shown.
--- @param npc game_object
--- @param actor game_object
--- @param dialog_id string
--- @return table
function cond_trade_dialog(actor, npc, dialog_id)
local flags = { result = true }
SendScriptCallback("western_goods_trade_eur_usd_on_dialog",actor,npc,flags)
return flags.result
end
-- ---------------------------------------------------------------------------------------------------------------------
-- Trading actions/restrictions
-- ---------------------------------------------------------------------------------------------------------------------
--- Monkey-patching the monkey-patches so I can prevent the monkey-patches to be executed on the western goods trader.
--- Got it? Me neither. In the end, it works.
--- @param npc game_object
--- @param force_refresh boolean
--- @return nil
function trade_manager_update(npc, force_refresh)
local flags = { result = false }
SendScriptCallback("western_goods_trade_eur_usd_on_trader_autoinject",npc,flags)
if this.is_eur_usd_trader(npc) and not flags.result then
trader_autoinject.TraderUpdate(npc, force_refresh)
else
western_goods_monkey_patches.trade_manager_update(npc, force_refresh)
end
end
--- Function used to allow/disallow the player from moving items from one container to another when trading with a EUR/USD trader.
--- @param flags table
--- @param npc_id number
--- @param obj game_object
--- @param mode string
--- @param bag string
--- @return nil
function check_move_item(flags, npc_id, obj, mode, bag)
if mode ~= "trade" then return flags.ret_value end
local npc = npc_id and level_object_by_id(npc_id)
-- Always allow items in NPC inventory to be moved
if utils_item.in_npc_inv(npc, obj) then return end
-- If you are talking to a EUR/USD trader and the item isn't tradable, prevent the item from moving
if this.is_eur_usd_trader(npc) and not this.is_tradable(obj:section()) then
flags.ret_value = false
end
end
--- Function used to color the untradable items in the player inventory.
--- @param cell table
--- @return fcolor
function tint_functor(cell)
local ui_instance = ui_inventory.GUI
if not ui_instance or not cell then return end
local partner = ui_instance:GetPartner()
local obj = cell.ID and level_object_by_id(cell.ID)
if (not ui_instance.mode == "trade")
or (not bags_to_tint[cell.container.ID])
or (not obj)
or (not partner)
or (utils_item.in_npc_inv(partner, obj))
or (this.is_tradable(obj:section()))
or (not this.is_eur_usd_trader(partner))
then return end
-- If you're in the trade mode with a EUR/USD trader, and the item isn't tradable, tint it in red
return GetARGB(240, 250, 0, 0)
end
--- Monkey-patching ui_inventory.UIInventory.UpdateCharacter to display custom currencies in the character wallet.
--- @param self UIInventory
--- @return nil
function ui_inventory_UIInventory_UpdateCharacter(self)
local partner = self:GetPartner()
if (not self.mode == "trade")
or (not partner)
or (not this.is_eur_usd_trader(partner))
then return end
local eur_money = 0
local usd_money = 0
western_goods_utils.inventory_iter(db.actor,function(owner, obj)
if this.is_tradable(obj:section()) then
local currency,value = this.get_money_value(obj:section())
if currency == "usd" then
usd_money = usd_money + value
end
if currency == "eur" then
eur_money = eur_money + value
end
end
end)
self.player_money:SetText(tostring(eur_money) .. " EUR | " .. tostring(usd_money) .. " USD")
-- NPC
self.npc_money:SetText("... EUR | ... USD")
end
--- Monkey-patching ui_inventory.UIInventory.UpdatePrice to display custom value for items in cart.
--- @param self UIInventory
--- @param ele_txt ?
--- @param ele_btn ?
--- @param bag string
--- @return nil
function ui_inventory_UIInventory_TMode_UpdatePrice(self, ele_txt, ele_btn, bag)
local partner = self:GetPartner()
if (not self.mode == "trade")
or (not partner)
or (not this.is_eur_usd_trader(partner))
then return end
local cc = self.CC[bag]
if (not cc) then
return
end
local tot_cost = 0
for obj_id,idx in pairs(cc.indx_id) do
local obj = level_object_by_id(obj_id)
local currency,value = this.get_money_value(obj:section())
if currency == "eur" then
tot_cost = tot_cost + value * eur_rouble_rate
end
if currency == "usd" then
tot_cost = tot_cost + value * usd_rouble_rate
end
if currency == "unknown" then
tot_cost = tot_cost + cc:GetCellCost(cc.cell[idx])
end
end
ele_txt:SetText(math.ceil(tot_cost/eur_rouble_rate) .. " EUR or " .. math.ceil(tot_cost/usd_rouble_rate) .. " USD")
ele_btn:Enable(math.ceil(tot_cost) > 0)
end
--- Monkey-patching ui_inventory.UIInventory.TMode_Sell to change the trade logic to a sort of barter system with the trader.
--- @param self UIInventory
--- @return nil
function ui_inventory_UIInventory_TMode_Sell(self)
-- If we aren't talking to a EUR/USD trader, process normally
local npc = self:GetPartner()
if not npc or not this.is_eur_usd_trader(npc) then
western_goods_monkey_patches.ui_inventory_t_mode_sell(self)
return
end
make_exchange(self, npc)
end
--- Monkey-patching ui_inventory.UIInventory.TMode_Buy to change the trade logic to a sort of barter system with the trader.
--- @param self UIInventory
--- @return nil
function ui_inventory_UIInventory_TMode_Buy(self)
-- If we aren't talking to a EUR/USD trader, process normally
local npc = self:GetPartner()
if not npc or not this.is_eur_usd_trader(npc) then
western_goods_monkey_patches.ui_inventory_t_mode_buy(self)
return
end
make_exchange(self, npc)
end
--- Function called when the player clicks Sell/Buy in the trade UI.
--- This function makes a few checks, and then proceeds to exchange the money for the items selected.
--- @param self UIInventory
--- @param npc game_object
--- @return nil
function make_exchange(self, npc)
-- Get sell cells
local cc_sell = self.CC["actor_trade"]
if (not cc_sell) then
return
end
if size_table(cc_sell.indx_id) <= 0 then
self.message_box:InitMessageBox("message_box_ok")
self.message_box:SetText(western_goods_utils.get_translation("st_wg_trader_empty_sell"))
self.message_box:ShowDialog(true)
return
end
-- Get buy sells
local cc_buy = self.CC["npc_trade"]
if (not cc_buy) then
return
end
if size_table(cc_buy.indx_id) <= 0 then
self.message_box:InitMessageBox("message_box_ok")
self.message_box:SetText(western_goods_utils.get_translation("st_wg_trader_empty_buy"))
self.message_box:ShowDialog(true)
return
end
-- Calculate full sell price
local tot_sell_cost = 0
for obj_id,idx in pairs(cc_sell.indx_id) do
local obj = level_object_by_id(obj_id)
local currency,value = this.get_money_value(obj:section())
if currency == "eur" then
tot_sell_cost = tot_sell_cost + value * eur_rouble_rate
end
if currency == "usd" then
tot_sell_cost = tot_sell_cost + value * usd_rouble_rate
end
if currency == "unknown" then
tot_sell_cost = tot_sell_cost + cc_sell:GetCellCost(cc_sell.cell[idx])
end
end
-- Calculate full buy price
local tot_buy_cost = 0
for obj_id,idx in pairs(cc_buy.indx_id) do
local obj = level_object_by_id(obj_id)
local currency,value = this.get_money_value(obj:section())
if currency == "eur" then
tot_buy_cost = tot_buy_cost + value * eur_rouble_rate
end
if currency == "usd" then
tot_buy_cost = tot_buy_cost + value * usd_rouble_rate
end
if currency == "unknown" then
tot_buy_cost = tot_buy_cost + cc_buy:GetCellCost(cc_buy.cell[idx])
end
end
-- Round values up
tot_sell_cost = math.ceil(tot_sell_cost)
tot_buy_cost = math.ceil(tot_buy_cost)
-- Don't processed the sell price < buy price
if tot_sell_cost < tot_buy_cost then
self.message_box:InitMessageBox("message_box_ok")
self.message_box:SetText(western_goods_utils.get_translation("st_wg_trader_uneven_value"))
self.message_box:ShowDialog(true)
return
end
-- Make the trade
dbg_printf("[WG] Western Goods Trader | Traded :")
for id,_ in pairs(cc_sell.indx_id) do
local obj = level_object_by_id(id)
dbg_printf("[WG] - [%s] %s", obj:id(), obj:section())
self:On_Item_Exchange(db.actor, npc, obj)
end
dbg_printf("[WG] Western Goods Trader | In exchange for :")
for id,_ in pairs(cc_buy.indx_id) do
local obj = level_object_by_id(id)
dbg_printf("[WG] - [%s] %s", obj:id(), obj:section())
self:On_Item_Exchange(npc, db.actor, obj)
end
-- Update UI
self:UpdateInfo(true)
-- Clean trader inventory
CreateTimeEvent("delete_traded_items",npc:id(),0.01,function()
dbg_printf("[WG] Western Goods Trader | Cleaning traded items :")
western_goods_utils.inventory_iter(npc,function(owner, obj)
if this.is_tradable(obj:section()) then
dbg_printf("[WG] - [%s] %s", obj:id(), obj:section())
alife_release_id(obj:id())
end
end)
return true
end)
end
-- ---------------------------------------------------------------------------------------------------------------------
-- Barter actions/restrictions
-- ---------------------------------------------------------------------------------------------------------------------
--- Monkey-patching barter_ui.GUI_on_show to prevent the Barter button from showing if the NPC is a EUR/USD trader.
--- @param name string
--- @param path string
--- @return nil
function barter_ui_gui_on_show(name, path)
local ui_instance = ui_inventory.GUI
if name ~= "UIInventory" or (not ui_instance) then
return
end
local partner = ui_instance:GetPartner()
if partner and this.is_eur_usd_trader(partner) then
return
end
western_goods_monkey_patches.barter_ui_gui_on_show(name, path)
end
-- ---------------------------------------------------------------------------------------------------------------------
-- Callbacks registration
-- ---------------------------------------------------------------------------------------------------------------------
--- Function used to register callbacks.
--- @return nil
function on_game_start()
rax_icon_tint.register("wg_tint_functor", tint_functor)
RegisterScriptCallback("ActorMenu_on_item_before_move", check_move_item)
RegisterScriptCallback("on_game_load", collect_traders_list)
end
-- ---------------------------------------------------------------------------------------------------------------------
-- Service Functions
-- ---------------------------------------------------------------------------------------------------------------------
--- Function used to know if an NPC is a EUR/USD trader or not.
--- @param section string
--- @return boolean
function is_eur_usd_trader(obj)
-- Check cache
if traders_names[obj:name()] ~= nil then
return traders_names[obj:name()]
end
-- Query and cache character_id
local character_id = western_goods_utils.get_character_id(obj)
traders_names[obj:name()] = traders_list[character_id] or false
return traders_names[obj:name()]
end
--- Function used to know if an object is tradable with the western goods trader.
--- @param obj game_object
--- @return boolean
function is_tradable(sec)
for section,data in pairs(western_goods_core.western_goods_items) do
-- Is tradable if item is in actor inventory, and is of type money
if sec == section and data.type == "money" then
return true
end
end
-- All other items are forbidden for trade
return false
end
--- Function used to get the monetary value of a section, if this section is a WG money bill.
--- @param sec string
--- @return string,number
function get_money_value(sec)
if string.find(sec, "wg_euros_") then
local value = string.sub(sec,10,#sec) or "0"
return "eur", tonumber(value)
end
if string.find(sec, "wg_dollars_") then
local value = string.sub(sec,12,#sec) or "0"
return "usd", tonumber(value)
end
return "unknown", 0
end
--- Function used to parse through all character description files.
--- This function will find and save (in 'traders_list' table) all character IDs that have the EUR/USD trade dialog.
--- @return nil
function collect_traders_list()
dbg_printf("[WG] Trade EUR/USD | Started trader collection...")
local profiler = profile_timer()
profiler:start()
local file_list = str_explode(ini_sys:r_string("profiles", "specific_characters_files"),",")
for _,file_name in pairs(file_list) do
local xml = CScriptXmlInit()
xml:ParseDirFile([[gameplay]],file_name..".xml")
local char_count = xml:GetNodesNum("", 0, "specific_character")
for i=0, char_count-1 do
local char_id = xml:ReadAttribute("specific_character", i, "id")
xml:NavigateToNode("specific_character", i)
local diag_count = xml:GetNodesNum("specific_character", i, "actor_dialog")
for y=0, diag_count-1 do
if xml:ReadValue("actor_dialog", y) == "western_goods_trade_eur_usd" then
traders_list[char_id] = true
dbg_printf("[WG] Trade EUR/USD | Found EUR/USD dialog for '%s'", char_id)
end
end
xml:NavigateToRoot()
end
xml = nil
end
profiler:stop()
dbg_printf("[WG] Trade EUR/USD | Finished trader collection in %sms :\n%s", profiler:time()/1000, utils_data.print_table(traders_list, false, true))
end