166 lines
7.0 KiB
Lua
166 lines
7.0 KiB
Lua
-- AD Prices
|
|
-- Prisregel for udkørsel: hvor tæt er salgsstedets pris på årets toppris for varen?
|
|
-- Grundlaget er spillets faste sæsonkurve (fillType.economy.factors[1..12], samme data som
|
|
-- prisgrafen i Priser-menuen; metode fra Distribution Redux): kurve-% = faktor nu ÷ årets højeste.
|
|
-- Oven på det ganges stationens egen afvigelse (effektiv pris ÷ stationens grundpris nu), så
|
|
-- prisfald efter salg og stor efterspørgsel tæller med — stor efterspørgsel giver > 100 %.
|
|
-- Sværhedsgraden skalerer begge priser ens og falder ud af regnestykket. Flade kurver
|
|
-- (top/bund under FLAT_THRESHOLD) regnes som 100 %, så varer uden sæson aldrig strander.
|
|
-- De rene funktioner øverst har ingen spil-API; getQuote er spil-adapteren.
|
|
|
|
ADPrices = {}
|
|
|
|
ADPrices.LOG_PREFIX = "ADSmartPickup: "
|
|
-- top/bund under dette = ingen reel sæson (Distribution Redux bruger samme grænse)
|
|
ADPrices.FLAT_THRESHOLD = 1.02
|
|
|
|
function ADPrices.maxFactor(factors)
|
|
local best = nil
|
|
for _, factor in pairs(factors or {}) do
|
|
if type(factor) == "number" and (best == nil or factor > best) then
|
|
best = factor
|
|
end
|
|
end
|
|
return best or 1
|
|
end
|
|
|
|
function ADPrices.topPrice(baseNow, factorNow, maxFactor)
|
|
if baseNow == nil or baseNow <= 0 then
|
|
return 0
|
|
end
|
|
if factorNow == nil or factorNow <= 0 or maxFactor == nil or maxFactor <= 0 then
|
|
return baseNow
|
|
end
|
|
return baseNow * maxFactor / factorNow
|
|
end
|
|
|
|
function ADPrices.percentOfTop(price, top)
|
|
if top == nil or top <= 0 or price == nil then
|
|
return 0
|
|
end
|
|
return math.floor(100 * price / top + 0.5)
|
|
end
|
|
|
|
function ADPrices.minFactor(factors)
|
|
local best = nil
|
|
for _, factor in pairs(factors or {}) do
|
|
if type(factor) == "number" and (best == nil or factor < best) then
|
|
best = factor
|
|
end
|
|
end
|
|
return best or 1
|
|
end
|
|
|
|
-- Kurve-%: nuværende periodes faktor ÷ årets højeste, afrundet. 100 uden kurve, uden periode
|
|
-- eller når kurven er flad.
|
|
function ADPrices.curvePercent(factors, period)
|
|
if factors == nil or period == nil then
|
|
return 100
|
|
end
|
|
local now, max, min = tonumber(factors[period]), ADPrices.maxFactor(factors), ADPrices.minFactor(factors)
|
|
if now == nil or now <= 0 or max <= 0 or min <= 0 or (max / min) < ADPrices.FLAT_THRESHOLD then
|
|
return 100
|
|
end
|
|
return math.floor(100 * now / max + 0.5)
|
|
end
|
|
|
|
-- Rigtige salgssteder: SellingStation der sælger (fabrikkers aflæsningsstationer er
|
|
-- også SellingStation-objekter, men med isSellingPoint = false) og vises i Priser-menuen.
|
|
-- Fabrikkers og staldes egne indleveringer bruger også SellingStation-klassen (TMR-fabrikken
|
|
-- endda med isSellingPoint = true) — de er mål for forsyning, aldrig markeder.
|
|
function ADPrices.isSellingStation(station)
|
|
if station == nil or station.isa == nil or SellingStation == nil or station:isa(SellingStation) ~= true then
|
|
return false
|
|
end
|
|
if station.isSellingPoint ~= true or station.hideFromPricesMenu then
|
|
return false
|
|
end
|
|
-- Afleveringer til GÅRDENS EGNE fabrikker/stalde er ikke salg. En fremmed produktion (fx kortets
|
|
-- mejeri, ejer 0) køber varen og er et rigtigt salgssted. Ukendt ejer = regnes som egen.
|
|
local placeable = station.owningPlaceable
|
|
if placeable ~= nil and (placeable.spec_productionPoint ~= nil or placeable.getHusbandryFillLevel ~= nil) then
|
|
return not ADPrices.isOwnedByPlayer(placeable)
|
|
end
|
|
return true
|
|
end
|
|
|
|
-- Placeable-ejeren (nil = ukendt).
|
|
local function getOwner(placeable)
|
|
if placeable.getOwnerFarmId ~= nil then
|
|
local ok, value = pcall(placeable.getOwnerFarmId, placeable)
|
|
if ok then return value end
|
|
return nil
|
|
end
|
|
return placeable.ownerFarmId
|
|
end
|
|
|
|
-- Ejeren af jorden under en placeable (nil = ukendt).
|
|
local function getLandOwner(placeable)
|
|
local manager = g_farmlandManager
|
|
if manager == nil or manager.getFarmlandIdAtWorldPosition == nil or manager.getFarmlandOwner == nil or placeable.rootNode == nil or getWorldTranslation == nil then
|
|
return nil
|
|
end
|
|
local ok, x, _, z = pcall(getWorldTranslation, placeable.rootNode)
|
|
if not ok or x == nil then return nil end
|
|
local okId, farmlandId = pcall(manager.getFarmlandIdAtWorldPosition, manager, x, z)
|
|
if not okId or farmlandId == nil then return nil end
|
|
local okOwner, owner = pcall(manager.getFarmlandOwner, manager, farmlandId)
|
|
return okOwner and owner or nil
|
|
end
|
|
|
|
-- Gårdens bygning? Ejer = farmId, ELLER et kortanlæg der følger jorden (boughtWithFarmland / ejer 15)
|
|
-- og jorden under det er gårdens — Dakotas BGA500kw står med ejer 15 selv på købt jord.
|
|
function ADPrices.isOwnedBy(placeable, farmId)
|
|
if placeable == nil then return false end
|
|
local owner = getOwner(placeable)
|
|
if owner == farmId then return true end
|
|
if placeable.boughtWithFarmland == true or owner == 15 then
|
|
return getLandOwner(placeable) == farmId
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Spillerens gård ejer placeablen; ukendt ejer = regnes som egen (afleveringer til egne anlæg er ikke salg).
|
|
function ADPrices.isOwnedByPlayer(placeable)
|
|
local farmId = g_currentMission ~= nil and g_currentMission.getFarmId ~= nil and g_currentMission:getFarmId() or 1
|
|
if getOwner(placeable) == nil then
|
|
return true
|
|
end
|
|
return ADPrices.isOwnedBy(placeable, farmId)
|
|
end
|
|
|
|
local function getCurrentPeriod()
|
|
local environment = g_currentMission ~= nil and g_currentMission.environment or nil
|
|
return environment ~= nil and environment.currentPeriod or nil
|
|
end
|
|
|
|
-- {price, top, percent} for varen ved stationen; nil hvis stationen ikke kan prissætte den.
|
|
-- percent = kurve-% (faktor nu ÷ årets højeste). Stationens `fillTypePrices` bruges IKKE som
|
|
-- reference: in-game (23/9) gav effektiv 1602 mod grundpris 505 = 317 %, fordi grundprisen
|
|
-- hverken indeholder sværhedsgrad eller prisskala. top = prisen der svarer til 100 % af kurven.
|
|
function ADPrices.getQuote(station, fillType)
|
|
if station == nil or station.getEffectiveFillTypePrice == nil then
|
|
return nil
|
|
end
|
|
local ok, effective = pcall(station.getEffectiveFillTypePrice, station, fillType)
|
|
if not ok or type(effective) ~= "number" then
|
|
return nil
|
|
end
|
|
local fillTypeDesc = g_fillTypeManager ~= nil and g_fillTypeManager.getFillTypeByIndex ~= nil and g_fillTypeManager:getFillTypeByIndex(fillType) or nil
|
|
local factors = fillTypeDesc ~= nil and fillTypeDesc.economy ~= nil and fillTypeDesc.economy.factors or nil
|
|
local percent = ADPrices.curvePercent(factors, getCurrentPeriod())
|
|
local top = percent > 0 and (effective * 100 / percent) or effective
|
|
return {price = effective, top = top, percent = percent}
|
|
end
|
|
|
|
-- Højeste pris-%, ved lighed højeste pris. nil ved tom liste.
|
|
function ADPrices.bestQuote(quotes)
|
|
local best = nil
|
|
for _, quote in ipairs(quotes or {}) do
|
|
if best == nil or quote.percent > best.percent or (quote.percent == best.percent and quote.price > best.price) then
|
|
best = quote
|
|
end
|
|
end
|
|
return best
|
|
end
|