465 lines
22 KiB
Plaintext
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
|