Загрузка данных
script_name('Sufferd Market Helper')
script_author('OpenAI')
script_version('1.0.0')
local imgui = require 'mimgui'
local sampev = require 'samp.events'
local encoding = require 'encoding'
encoding.default = 'CP1251'
u8 = encoding.UTF8
market_selling = {}
market_buying = {}
local window = imgui.new.bool(false)
local scanning = imgui.new.bool(false)
local cfg_commission = imgui.new.int(4)
local results = {}
local result_signature = ''
local market_revision = 0
local status_text = 'Сканирование выключено'
local last_dialog_signature = ''
local function safe_string(value)
if value == nil then
return ''
end
return tostring(value)
end
local function trim(value)
value = safe_string(value)
return value:gsub('^%s+', ''):gsub('%s+$', '')
end
local function strip_colors(value)
return safe_string(value):gsub('{%x%x%x%x%x%x%x%x}', ''):gsub('{%x%x%x%x%x%x}', '')
end
local function contains_any(value, variants)
value = safe_string(value)
for _, part in ipairs(variants) do
if value:find(part, 1, true) then
return true
end
end
return false
end
local function split_tabs(line)
local fields = {}
for field in (safe_string(line) .. '\t'):gmatch('(.-)\t') do
fields[#fields + 1] = field
end
return fields
end
local function parse_price(value)
local price = strip_colors(trim(value)):gsub('[,%$%.]', '')
price = price:gsub('%s+', '')
return tonumber(price)
end
local function normalize_item(value)
return trim(strip_colors(value)):gsub('%s+', ' ')
end
local function remove_stall_records(storage, stall)
for item, records in pairs(storage) do
local filtered = {}
for _, record in ipairs(records) do
if record.stall ~= stall then
filtered[#filtered + 1] = record
end
end
if #filtered > 0 then
storage[item] = filtered
else
storage[item] = nil
end
end
end
local function add_record(storage, item, stall, price, amount)
if item == '' or stall == '' or not price or price < 0 then
return
end
storage[item] = storage[item] or {}
storage[item][#storage[item] + 1] = {
stall = stall,
price = price,
amount = amount
}
end
local function parse_market_dialog(title, text)
title = trim(strip_colors(title))
text = safe_string(text)
local sale = contains_any(title, {'Продаж', 'продаж', 'ПРОДАЖ'})
local buy = contains_any(title, {'Скуп', 'скуп', 'СКУП'})
if not sale and not buy then
return false
end
if title == '' or not text:find('\t', 1, true) then
return false
end
local stall = title
local target = sale and market_selling or market_buying
remove_stall_records(target, stall)
local count = 0
for line in safe_string(text):gmatch('[^\r\n]+') do
local fields = split_tabs(line)
if #fields >= 3 then
local item = normalize_item(fields[1])
local amount = trim(fields[2])
local price = parse_price(fields[3])
if item ~= '' and price then
local is_header = contains_any(item, {'Название', 'название', 'Предмет', 'предмет'}) or contains_any(fields[3], {'Цена', 'цена'})
if not is_header then
add_record(target, item, stall, price, amount)
count = count + 1
end
end
end
end
status_text = (sale and 'Продажа: ' or 'Скупка: ') .. stall .. ' [' .. tostring(count) .. ']'
return true
end
local function get_best_record(records, mode)
local best
for _, record in ipairs(records or {}) do
if type(record) == 'table' and type(record.price) == 'number' then
if not best or (mode == 'min' and record.price < best.price) or (mode == 'max' and record.price > best.price) then
best = record
end
end
end
return best
end
local function rebuild_results()
local calculated = {}
local commission = tonumber(cfg_commission[0]) or 4
for item, selling_records in pairs(market_selling) do
local buying_records = market_buying[item]
if buying_records then
local seller = get_best_record(selling_records, 'min')
local buyer = get_best_record(buying_records, 'max')
if seller and buyer then
local money_received = buyer.price * (1 - commission / 100)
local profit = money_received - seller.price
if profit > 0 then
calculated[#calculated + 1] = {
item = item,
seller = seller,
buyer = buyer,
profit = profit,
received = money_received
}
end
end
end
end
table.sort(calculated, function(a, b)
return a.profit > b.profit
end)
results = calculated
result_signature = tostring(market_revision) .. ':' .. tostring(commission)
end
local function format_money(value)
local number = math.floor(tonumber(value) or 0)
local text = tostring(number)
local sign = ''
if text:sub(1, 1) == '-' then
sign = '-'
text = text:sub(2)
end
local formatted = text
while true do
local replaced, count = formatted:gsub('^(%d+)(%d%d%d)', '%1,%2')
formatted = replaced
if count == 0 then
break
end
end
return sign .. formatted .. '$'
end
local function clear_market()
market_selling = {}
market_buying = {}
results = {}
result_signature = ''
last_dialog_signature = ''
market_revision = market_revision + 1
status_text = 'База рынка очищена'
end
local function toggle_scanning()
scanning[0] = not scanning[0]
if not scanning[0] then
clear_market()
status_text = 'Сканирование выключено'
else
market_revision = market_revision + 1
result_signature = ''
status_text = 'Сканирование включено'
end
end
local function draw_results()
imgui.Separator()
imgui.Text(u8('Выгодные сделки: ' .. tostring(#results)))
imgui.BeginChild('market_results', imgui.ImVec2(0, 0), true)
if #results == 0 then
imgui.TextColored(imgui.ImVec4(0.65, 0.65, 0.65, 1.0), u8('Подходящих сделок пока нет'))
else
for index, deal in ipairs(results) do
local line = string.format('%s | Купить у: %s (%s) -> Продать: %s (%s) | Профит: +%s', deal.item, deal.seller.stall, format_money(deal.seller.price), deal.buyer.stall, format_money(deal.buyer.price), format_money(deal.profit))
imgui.PushStyleColor(imgui.Col.Text, imgui.ImVec4(0.35, 1.0, 0.45, 1.0))
imgui.TextWrapped(u8(line))
imgui.PopStyleColor()
if index < #results then
imgui.Separator()
end
end
end
imgui.EndChild()
end
imgui.OnFrame(function()
return window[0]
end, function()
imgui.SetNextWindowSize(imgui.ImVec2(980, 560), imgui.Cond.FirstUseEver)
imgui.SetNextWindowSizeConstraints(imgui.ImVec2(520, 300), imgui.ImVec2(1800, 1200))
if imgui.Begin('Sufferd Market Helper', window, imgui.WindowFlags.NoCollapse) then
local scan_label = scanning[0] and u8('Сканирование рынка [ВКЛ]') or u8('Сканирование рынка [ВЫКЛ]')
if imgui.Button(scan_label, imgui.ImVec2(-1, 42)) then
toggle_scanning()
end
imgui.PushItemWidth(260)
imgui.SliderInt(u8('Комиссия сервера'), cfg_commission, 1, 5, '%d%%')
imgui.PopItemWidth()
imgui.SameLine()
imgui.Text(u8(status_text))
local new_signature = tostring(market_revision) .. ':' .. tostring(cfg_commission[0])
if new_signature ~= result_signature then
rebuild_results()
end
draw_results()
end
imgui.End()
end)
function sampev.onShowDialog(dialogId, style, title, button1, button2, text)
if not scanning[0] or tonumber(style) ~= 5 then
return
end
title = u8(safe_string(title))
text = u8(safe_string(text))
local current_signature = safe_string(dialogId) .. '|' .. title .. '|' .. text
if current_signature == last_dialog_signature then
return
end
last_dialog_signature = current_signature
if parse_market_dialog(title, text) then
market_revision = market_revision + 1
rebuild_results()
end
end
function main()
repeat
wait(100)
until isSampAvailable()
sampRegisterChatCommand('sufferd', function()
window[0] = not window[0]
end)
while true do
wait(500)
if scanning[0] then
rebuild_results()
end
end
end