Divergent/mods/Western Goods/gamedata/scripts/western_goods_flea.script

465 lines
22 KiB
Plaintext

---==================================================================================================================---
--- ---
--- Original Author(s) : NLTP_ASHES ---
--- Edited : N/A ---
--- Date : 02/06/2023 ---
--- License : Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) ---
--- ---
--- Script used to manage the Mercenary flea market system. ---
--- ---
--- You can add new sections to the item pool using a file : ---
--- ---
---=== gamedata/configs/plugins/western_goods/flea_market/flea_<some_unique_name_of_your_choice>.ltx ================---
--- ---
--- [some_unique_name_of_your_choice] ---
--- some_item_section ---
--- some_other_item_section ---
--- ---
---==================================================================================================================---
--- ---
--- Or you can use the callback to add new items : ---
--- ---
---=== gamedata/scripts/my_addon_s_script.script ====================================================================---
--- ---
--- function on_game_start() ---
--- RegisterScriptCallback("western_goods_flea_on_collect_sections", function(flags) ---
--- local items_to_add = { ---
--- "some_item_section", ---
--- "some_other_item_section", ---
--- } ---
--- for k,v in pairs(items_to_add) do ---
--- table.insert(flags.result,v) ---
--- end ---
--- end) ---
--- end ---
--- ---
---==================================================================================================================---
--- ---
--- By default, if the npc has the "western_goods_flea_offers" dialog, it will be available to the player. ---
--- If you want to have more control over this dialog, you can use the "western_goods_flea_on_dialog" callback. ---
--- ---
---=== gamedata/scripts/my_addon_s_script.script ====================================================================---
--- ---
--- function on_game_start() ---
--- RegisterScriptCallback("western_goods_flea_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 ---
--- ---
---==================================================================================================================---
--- ---
---==================================================================================================================---
-- ---------------------------------------------------------------------------------------------------------------------
-- Constants, global variables, imported functions and new callbacks
-- ---------------------------------------------------------------------------------------------------------------------
-- New callbacks
AddScriptCallback("western_goods_flea_on_dialog")
AddScriptCallback("western_goods_flea_on_collect_sections")
-- Imported functions
local dbg_printf = western_goods_utils.dbg_printf
-- Constants
local ini_sections = ini_file([[plugins\western_goods\flea_market\importer.ltx]])
local offers_per_npc = 5
local offers_exp_freq = 3600000
local min_item_count = 2
local max_item_count = 10
local min_discount = 5
local max_discount = 50
-- Variables
local current_offer = 1
local offers = {}
-- ---------------------------------------------------------------------------------------------------------------------
-- Core Functions
-- ---------------------------------------------------------------------------------------------------------------------
--- Function used to generate new offers
--- @param count number
--- @return table
function generate_offers(count)
local new_offers = {}
local viable_sections = this.get_viable_sections()
dbg_printf("[WG] Flea Market | Generating %s offers...", count)
for i = 1, count do
local price = 0
local sections = {}
for y = 1, math.random(min_item_count,max_item_count) do
local section = viable_sections[math.random(1,size_table(viable_sections))]
price = price + western_goods_utils.get_section_price(section)
table.insert(sections,section)
end
local sale = math.random(min_discount,max_discount)
local cost = math.ceil(price * ((100 - sale) / 100))
table.insert(new_offers,{ sections=sections, sale=sale, cost=cost })
end
dbg_printf("[WG] Flea Market | Generated %s offers :\n%s", count, utils_data.print_table(new_offers, false, true))
return new_offers
end
--- Function used to purchase an offer. Check if player has enough money before calling!
--- @param offer table
--- @param npc game_object
--- @return nil
function purchase_offer(offer, npc)
for _,sec in pairs(offer.sections) do
local uses = IsItem("multiuse", sec)
itms_manager.relocate_item_to_actor(db.actor, npc, sec, uses or 1)
end
dialogs.relocate_money(npc, offer.cost, "out")
dbg_printf("[WG] Flea Market | Purchased offer (qte:%s)(price:%s)(sale:%s)", size_table(offer.sections), offer.cost, offer.sale)
table.remove(offers[npc:id()].set,current_offer)
end
-- ---------------------------------------------------------------------------------------------------------------------
-- General Dialog Functions
-- ---------------------------------------------------------------------------------------------------------------------
--- Function called by dialogs, used to know if an NPC show display the western_goods_flea_offers dialog.
--- Used mainly to have per NPC conditions, while all sharing the same line of dialog.
--- @return nil
function cond_offers_dialog(actor, npc, dialog_id)
local flags = { result = true }
SendScriptCallback("western_goods_flea_on_dialog",actor,npc,flags)
return flags.result
end
--- Function called by dialogs, used to initialize offers for an NPC.
--- @param actor game_object
--- @param npc game_object
--- @return nil
function init_offers(actor, npc)
dbg_printf("[WG] Flea Market | Initializing for %s(%s)...", npc:section(), npc:id())
if not offers[npc:id()] then
offers[npc:id()] = {
set = this.generate_offers(offers_per_npc),
exp = time_global() + offers_exp_freq,
}
end
if not offers[npc:id()].exp or offers[npc:id()].exp <= time_global() then
offers[npc:id()] = {
set = this.generate_offers(offers_per_npc),
exp = time_global() + offers_exp_freq,
}
end
end
--- Function called by dialogs, used to initialize a purchase.
--- @param actor game_object
--- @param npc game_object
--- @return nil
function init_purchase(actor, npc)
current_offer = 1
end
--- Function called by dialogs, used to determine if the 'current_offer' pointer has an offer.
--- @param actor game_object
--- @param npc game_object
--- @return boolean
function exists_current_offer(actor, npc)
return offers[npc:id()].set[current_offer] ~= nil
end
--- Function called by dialogs, used to determine if the current offer has expired or not.
--- @param actor game_object
--- @param npc game_object
--- @return boolean
function not_expired_current_offer(actor, npc)
return offers[npc:id()].exp > time_global()
end
--- Function called by dialogs, used to determine if the player has enough money for the current offer.
--- @param actor game_object
--- @param npc game_object
--- @return boolean
function can_afford_offer(actor, npc)
local offer = offers[npc:id()].set[current_offer]
if offer then
return db.actor:money() >= offer.cost
end
return false
end
--- Function called by dialogs, used to list all available offers for a given NPC.
--- @param actor game_object
--- @param npc game_object
--- @return nil
function list_offers(actor, npc)
for _,offer in ipairs(offers[npc:id()].set) do
local expiration = offers[npc:id()] and offers[npc:id()].exp
local time_left = expiration and math.floor((expiration - time_global()) / 60000) or "unknown"
local caption = western_goods_utils.get_translation("st_wg_flea_offer_caption", this.get_expiration_color(time_left), time_left)
local text = western_goods_utils.get_translation("st_wg_flea_offer_text", this.get_items_text(offer.sections), size_table(offer.sections), this.get_discount_color(offer.sale), offer.sale, offer.cost)
db.actor:give_talk_message2(caption, text, "ui_inGame2_Osobiy_zakaz", "iconed_answer_item")
end
end
-- ---------------------------------------------------------------------------------------------------------------------
-- Show Current Item Dialog Functions
-- ---------------------------------------------------------------------------------------------------------------------
--- Function called by dialogs, used to return the dialog text when showing an offer.
--- @param actor game_object
--- @param npc game_object
--- @return string
function st_show_current_item(npc, dialog_id)
local offer = offers[npc:id()].set[current_offer]
local exp = offers[npc:id()].exp
if time_global() >= exp then
return western_goods_utils.get_translation("st_wg_flea_expired")
elseif offer then
return western_goods_utils.get_translation("st_wg_flea_offer", offer.sale) .. " " .. western_goods_utils.get_translation("st_wg_flea_offer_end_"..tostring(math.random(0,4)))
else
return western_goods_utils.get_translation("st_wg_flea_no_offer")
end
end
--- Function called by dialogs, used to show the current offer.
--- @param npc game_object
--- @param actor game_object
--- @return nil
function show_current_offer(npc, actor)
local offer = offers[npc:id()].set[current_offer]
local exp = offers[npc:id()].exp
if offer and time_global() < exp then
local expiration = offers[npc:id()] and offers[npc:id()].exp
local time_left = expiration and math.floor((expiration - time_global()) / 60000 * level.get_time_factor())
local hours_left = time_left and math.floor(time_left / 60)
local mins_left = time_left and math.ceil(time_left % 60)
local caption = western_goods_utils.get_translation("st_wg_flea_offer_caption", this.get_expiration_color(time_left), hours_left, mins_left)
local text = western_goods_utils.get_translation("st_wg_flea_offer_text", this.get_items_text(offer.sections), size_table(offer.sections), this.get_discount_color(offer.sale), offer.sale, offer.cost)
dbg_printf("[WG] Flea Market | Showing offer (qte:%s)(sale:%s)(price:%s)", size_table(offer.sections), offer.sale, offer.cost)
db.actor:give_talk_message2(caption, text, "ui_inGame2_Osobiy_zakaz", "iconed_answer_item")
end
end
-- ---------------------------------------------------------------------------------------------------------------------
-- Buy Current Item Dialog Functions
-- ---------------------------------------------------------------------------------------------------------------------
--- Function called by dialogs, used to return the dialog text when purchasing an offer.
--- @param actor game_object
--- @param npc game_object
--- @return string
function st_buy_current_item(npc, dialog_id)
local offer = offers[npc:id()].set[current_offer]
local exp = offers[npc:id()].exp
if offer and time_global() < exp then
return western_goods_utils.get_translation("st_wg_flea_offer_buy", offer.cost) .. " " .. western_goods_utils.get_translation("st_wg_flea_offer_buy_end_"..tostring(math.random(0,4)))
end
end
--- Function called by dialogs, used to execute a purchase for the current offer.
--- @param actor game_object
--- @param npc game_object
--- @return nil
function buy_current_item(actor, npc)
this.purchase_offer(offers[npc:id()].set[current_offer], npc)
end
-- ---------------------------------------------------------------------------------------------------------------------
-- Next Item Dialog Functions
-- ---------------------------------------------------------------------------------------------------------------------
--- Function called by dialogs, used to return the dialog text when skipping to the next offer.
--- @param actor game_object
--- @param npc game_object
--- @return string
function st_next_item(npc, dialog_id)
local offer = offers[npc:id()].set[current_offer]
if offer and db.actor:money() >= offer.cost then
return western_goods_utils.get_translation("st_wg_flea_next_offer")
else
return western_goods_utils.get_translation("st_wg_flea_poor_next_offer_"..tostring(math.random(0,2)))
end
end
--- Function called by dialogs, used to skip to the next offer.
--- @param actor game_object
--- @param npc game_object
--- @return nil
function next_item(actor, npc)
dbg_printf("[WG] Flea Market | Next offer...")
current_offer = current_offer + 1
end
-- ---------------------------------------------------------------------------------------------------------------------
-- Callbacks registration
-- ---------------------------------------------------------------------------------------------------------------------
--- Function used to register callbacks.
--- @return nil
function on_game_start()
RegisterScriptCallback("save_state", save_state)
RegisterScriptCallback("load_state", load_state)
end
-- ---------------------------------------------------------------------------------------------------------------------
-- Data persistence
-- ---------------------------------------------------------------------------------------------------------------------
--- Function used to store information in the save file.
--- @param m_data table
--- @return nil
function save_state(m_data)
-- Prepare save table
local offers_save = {}
-- Make copy of offers table
copy_table(offers_save, offers)
-- Pre-process offers table
for _,npc_offers in pairs(offers_save) do
npc_offers.exp = npc_offers.exp - time_global()
end
-- Save offers table
m_data.wg_flea_offers = offers_save
-- Debug prints
dbg_printf("[WG] Flea Market | Saved offers...\n%s",utils_data.print_table(offers_save, false, true))
end
--- Function used to load information stored in the save file.
--- @param m_data table
--- @return nil
function load_state(m_data)
-- Retrieve save table
local offers_save = m_data.wg_flea_offers or {}
-- Post-process offers table
for npc_id,data in pairs(offers_save) do
data.exp = data.exp + time_global()
end
-- Restore offers table
copy_table(offers, offers_save)
-- Debug prints
dbg_printf("[WG] Flea Market | Loaded offers...\n%s",utils_data.print_table(offers, false, true))
end
-- ---------------------------------------------------------------------------------------------------------------------
-- Service Functions
-- ---------------------------------------------------------------------------------------------------------------------
--- Function used to a copy of the offers table from another script.
--- You better not use the LUA Unlocalizer to sneak into my pipes !
--- @param npc_id number
--- @return table
function get_all_offers(npc_id)
local result = {}
if npc_id ~= nil then
copy_table(result, offers)
else
copy_table(result, offers[npc_id])
end
return result
end
--- Function used to get a table listing all possible sections that can spawn in the Flea Market system.
--- @return table
function get_viable_sections()
local viable_sections = {}
local file_count, script_count = 0, 0
-- Collect viable sections via files
ini_sections:section_for_each(function(section)
local sec_names = utils_data.collect_section(ini_sections, section, true)
for sec_name,_ in pairs(sec_names) do
file_count = file_count + 1
if ini_sys:section_exist(sec_name) then
table.insert(viable_sections,sec_name)
else
printf("![WG] ERROR | Flea Market | Section %s (module : %s) cannot be collected, as it does not exist!", sec_name, section)
end
end
end)
-- Collect viable sections via script
local flags = { result = { } }
SendScriptCallback("western_goods_flea_on_collect_sections",flags)
for _,sec_name in pairs(flags.result) do
script_count = script_count + 1
if ini_sys:section_exist(sec_name) then
table.insert(viable_sections,sec_name)
else
printf("![WG] ERROR | Flea Market | Section %s (from script) cannot be collected, as it does not exist!", sec_name)
end
end
dbg_printf("[WG] Flea Market | Collected %s viable sections from files", file_count)
dbg_printf("[WG] Flea Market | Collected %s viable sections from scripts", script_count)
dbg_printf("[WG] Flea Market | Collected total of %s viable sections", file_count+script_count)
return viable_sections
end
--- Function used to get color code for a given percentage.
--- Color code : x>=35% : green / 35%>x>=15% : yellow / 15%>x : red
--- @param percentage number
--- @return string
function get_discount_color(percentage)
if percentage >= 35 then
return "%c[green]"
elseif percentage >= 15 then
return "%c[yellow]"
else
return "%c[red]"
end
end
--- Function used to get color code for a given expiration time.
--- Color code : x>=10m : default / 10m>x>=5m : yellow / 5m>x : red
--- @param time_left number
--- @return string
function get_expiration_color(time_left)
if time_left >= 60 then
return "%c[default]"
elseif time_left >= 10 then
return "%c[yellow]"
else
return "%c[red]"
end
end
--- Function used to get a string list of the items contained in a given offer.
--- @param sections table
--- @return string
function get_items_text(sections)
local items_text = [[\n]]
for _,sec in pairs(sections) do
items_text = items_text .. [[ - ]] .. ui_item.get_sec_name(sec) .. [[\n]]
end
return items_text
end