Untitled diff

Created Diff never expires


package.path = package.path .. ";data/scripts/lib/?.lua"
package.path = package.path .. ";data/scripts/lib/?.lua"
require ("galaxy")
require ("galaxy")
require ("utility")
require ("utility")
require ("goods")
require ("goods")
require ("stringutility")
require ("stringutility")
require ("player")
require ("player")
require ("faction")
require ("faction")
require ("merchantutility")
require ("merchantutility")


local TradingManager = {}
local TradingManager = {}
TradingManager.__index = TradingManager
TradingManager.__index = TradingManager


local function new()
local function new()
local instance = {}
local instance = {}


instance.buyPriceFactor = 1
instance.buyPriceFactor = 1
instance.sellPriceFactor = 1
instance.sellPriceFactor = 1
instance.tax = 0.0 -- tax is the amount of money the owner of the entity gets from transactions
instance.tax = 0.0 -- tax is the amount of money the owner of the entity gets from transactions
instance.factionPaymentFactor = 1.0 -- the amount of money the owner of the entity pays or receives directly through transactions
instance.factionPaymentFactor = 1.0 -- the amount of money the owner of the entity pays or receives directly through transactions


instance.boughtGoods = {}
instance.boughtGoods = {}
instance.soldGoods = {}
instance.soldGoods = {}


instance.numSold = 0
instance.numSold = 0
instance.numBought = 0
instance.numBought = 0


instance.buyFromOthers = true
instance.buyFromOthers = true
instance.sellToOthers = true
instance.sellToOthers = true


instance.activelyRequest = false
instance.activelyRequest = false
instance.activelySell = false
instance.activelySell = false


instance.deliveredStations = {}
instance.deliveredStations = {}
instance.deliveringStations = {}
instance.deliveringStations = {}


instance.policies =
instance.policies =
{
{
sellsIllegal = false,
sellsIllegal = false,
buysIllegal = false,
buysIllegal = false,


sellsStolen = false,
sellsStolen = false,
buysStolen = false,
buysStolen = false,


sellsSuspicious = false,
sellsSuspicious = false,
buysSuspicious = false,
buysSuspicious = false,
}
}


-- UI
-- UI
instance.boughtLines = {}
instance.boughtLines = {}
instance.soldLines = {}
instance.soldLines = {}


instance.guiInitialized = false
instance.guiInitialized = false
instance.useTimeCounter = 0 -- time counter for using up bought products
instance.useTimeCounter = 0 -- time counter for using up bought products
instance.useUpGoodsEnabled = true
instance.useUpGoodsEnabled = true


return setmetatable(instance, TradingManager)
return setmetatable(instance, TradingManager)
end
end


function TradingManager:getBuysFromOthers()
function TradingManager:getBuysFromOthers()
return self.buyFromOthers
return self.buyFromOthers
end
end


function TradingManager:getSellsToOthers()
function TradingManager:getSellsToOthers()
return self.sellToOthers
return self.sellToOthers
end
end


function TradingManager:setBuysFromOthers(value)
function TradingManager:setBuysFromOthers(value)
self.buyFromOthers = value
self.buyFromOthers = value
end
end


function TradingManager:setSellsToOthers(value)
function TradingManager:setSellsToOthers(value)
self.sellToOthers = value
self.sellToOthers = value
end
end


-- help functions
-- help functions
function TradingManager:isSoldBySelf(good)
function TradingManager:isSoldBySelf(good)
if good.illegal and not self.policies.sellsIllegal then
if good.illegal and not self.policies.sellsIllegal then
local msg = "This station doesn't sell illegal goods."%_t
local msg = "This station doesn't sell illegal goods."%_t
return false, msg
return false, msg
end
end


if good.stolen and not self.policies.sellsStolen then
if good.stolen and not self.policies.sellsStolen then
local msg = "This station doesn't sell stolen goods."%_t
local msg = "This station doesn't sell stolen goods."%_t
return false, msg
return false, msg
end
end


if good.suspicious and not self.policies.sellsSuspicious then
if good.suspicious and not self.policies.sellsSuspicious then
local msg = "This station doesn't sell suspicious goods."%_t
local msg = "This station doesn't sell suspicious goods."%_t
return false, msg
return false, msg
end
end


return true
return true
end
end


function TradingManager:isBoughtBySelf(good)
function TradingManager:isBoughtBySelf(good)
if good.illegal and not self.policies.buysIllegal then
if good.illegal and not self.policies.buysIllegal then
local msg = "This station doesn't buy illegal goods."%_t
local msg = "This station doesn't buy illegal goods."%_t
return false, msg
return false, msg
end
end


if good.stolen and not self.policies.buysStolen then
if good.stolen and not self.policies.buysStolen then
local msg = "This station doesn't buy stolen goods."%_t
local msg = "This station doesn't buy stolen goods."%_t
return false, msg
return false, msg
end
end


if good.suspicious and not self.policies.buysSuspicious then
if good.suspicious and not self.policies.buysSuspicious then
local msg = "This station doesn't buy suspicious goods."%_t
local msg = "This station doesn't buy suspicious goods."%_t
return false, msg
return false, msg
end
end


return true
return true
end
end


function TradingManager:generateGoods(min, max)
function TradingManager:generateGoods(min, max)
min = min or 10
min = min or 10
max = max or 15
max = max or 15


local numGoods = math.random(min, max)
local numGoods = math.random(min, max)


local bought = {}
local bought = {}
local sold = {}
local sold = {}
local existingGoods = {}
local existingGoods = {}


local maxNumGoods = tablelength(goodsArray)
local maxNumGoods = tablelength(goodsArray)


for i = 1, numGoods do
for i = 1, numGoods do
local index = math.random(1, maxNumGoods)
local index = math.random(1, maxNumGoods)
local g = goodsArray[index]
local g = goodsArray[index]
local good = g:good()
local good = g:good()


-- don't trade potentially illegal goods
-- don't trade potentially illegal goods
if self:isBoughtBySelf(good) then
if self:isBoughtBySelf(good) then


if existingGoods[good.name] == nil then
if existingGoods[good.name] == nil then


good.size = round(good.size, 2)
good.size = round(good.size, 2)
good.price = round(good.price)
good.price = round(good.price)
table.insert(bought, good)
table.insert(bought, good)
existingGoods[good.name] = 1
existingGoods[good.name] = 1
end
end


end
end
end
end


for i = 1, numGoods do
for i = 1, numGoods do
local index = math.random(1, maxNumGoods)
local index = math.random(1, maxNumGoods)
local g = goodsArray[index]
local g = goodsArray[index]
local good = g:good()
local good = g:good()


-- don't trade potentially illegal goods
-- don't trade potentially illegal goods
if self:isSoldBySelf(good.name) then
if self:isSoldBySelf(good.name) then


if existingGoods[good.name] == nil then
if existingGoods[good.name] == nil then
good.size = round(good.size, 2)
good.size = round(good.size, 2)
good.price = round(good.price)
good.price = round(good.price)


table.insert(sold, good)
table.insert(sold, good)
existingGoods[good.name] = 1
existingGoods[good.name] = 1
end
end
end
end
end
end


return bought, sold
return bought, sold
end
end


function TradingManager:restoreTradingGoods(data)
function TradingManager:restoreTradingGoods(data)
self.buyPriceFactor = data.buyPriceFactor
self.buyPriceFactor = data.buyPriceFactor
self.sellPriceFactor = data.sellPriceFactor
self.sellPriceFactor = data.sellPriceFactor
self.policies = data.policies
self.policies = data.policies


self.boughtGoods = {}
self.boughtGoods = {}
for _, g in pairs(data.boughtGoods) do
for _, g in pairs(data.boughtGoods) do
table.insert(self.boughtGoods, tableToGood(g))
table.insert(self.boughtGoods, tableToGood(g))
end
end


self.soldGoods = {}
self.soldGoods = {}
for _, g in pairs(data.soldGoods) do
for _, g in pairs(data.soldGoods) do
table.insert(self.soldGoods, tableToGood(g))
table.insert(self.soldGoods, tableToGood(g))
end
end


self.numBought = #self.boughtGoods
self.numBought = #self.boughtGoods
self.numSold = #self.soldGoods
self.numSold = #self.soldGoods


if data.buyFromOthers == nil then
if data.buyFromOthers == nil then
self.buyFromOthers = true
self.buyFromOthers = true
else
else
self.buyFromOthers = data.buyFromOthers
self.buyFromOthers = data.buyFromOthers
end
end


if data.sellToOthers == nil then
if data.sellToOthers == nil then
self.sellToOthers = true
self.sellToOthers = true
else
else
self.sellToOthers = data.sellToOthers
self.sellToOthers = data.sellToOthers
end
end


self.activelyRequest = data.activelyRequest or false
self.activelyRequest = data.activelyRequest or false
self.activelySell = data.activelySell or false
self.activelySell = data.activelySell or false


self.deliveredStations = data.deliveredStations or {}
self.deliveredStations = data.deliveredStations or {}
self.deliveringStations = data.deliveringStations or {}
self.deliveringStations = data.deliveringStations or {}
end
end


function TradingManager:secureTradingGoods()
function TradingManager:secureTradingGoods()
local data = {}
local data = {}
data.buyPriceFactor = self.buyPriceFactor
data.buyPriceFactor = self.buyPriceFactor
data.sellPriceFactor = self.sellPriceFactor
data.sellPriceFactor = self.sellPriceFactor
data.policies = self.policies
data.policies = self.policies


data.buyFromOthers = self.buyFromOthers
data.buyFromOthers = self.buyFromOthers
data.sellToOthers = self.sellToOthers
data.sellToOthers = self.sellToOthers
data.activelyRequest = self.activelyRequest
data.activelyRequest = self.activelyRequest
data.activelySell = self.activelySell
data.activelySell = self.activelySell


data.deliveredStations = self.deliveredStations
data.deliveredStations = self.deliveredStations
data.deliveringStations = self.deliveringStations
data.deliveringStations = self.deliveringStations




data.boughtGoods = {}
data.boughtGoods = {}
for _, g in pairs(self.boughtGoods) do
for _, g in pairs(self.boughtGoods) do
table.insert(data.boughtGoods, goodToTable(g))
table.insert(data.boughtGoods, goodToTable(g))
end
end


data.soldGoods = {}
data.soldGoods = {}
for _, g in pairs(self.soldGoods) do
for _, g in pairs(self.soldGoods) do
table.insert(data.soldGoods, goodToTable(g))
table.insert(data.soldGoods, goodToTable(g))
end
end


return data
return data
end
end


function TradingManager:initializeTrading(boughtGoodsIn, soldGoodsIn, policiesIn)
function TradingManager:initializeTrading(boughtGoodsIn, soldGoodsIn, policiesIn)


local entity = Entity()
local entity = Entity()
entity:waitUntilAsyncWorkFinished()
entity:waitUntilAsyncWorkFinished()


self.policies = policiesIn or self.policies
self.policies = policiesIn or self.policies


-- generate goods only once, this adds physical goods to the entity
-- generate goods only once, this adds physical goods to the entity
local generated = entity:getValue("goods_generated")
local generated = entity:getValue("goods_generated")
if not generated or generated ~= 1 then
if not generated or generated ~= 1 then
entity:setValue("goods_generated", 1)
entity:setValue("goods_generated", 1)
generated = false
generated = false
else
else
generated = true
generated = true
end
end


boughtGoodsIn = boughtGoodsIn or {}
boughtGoodsIn = boughtGoodsIn or {}
soldGoodsIn = soldGoodsIn or {}
soldGoodsIn = soldGoodsIn or {}


self.numBought = #boughtGoodsIn
self.numBought = #boughtGoodsIn
self.numSold = #soldGoodsIn
self.numSold = #soldGoodsIn


self.boughtGoods = {}
self.boughtGoods = {}


for i, v in ipairs(boughtGoodsIn) do
for i, v in ipairs(boughtGoodsIn) do
if not generated then
if not generated then
local maxStock = self:getMaxStock(v.size)
local maxStock = self:getMaxStock(v.size)
if maxStock > 0 then
if maxStock > 0 then


-- generate a random amount of things
-- generate a random amount of things
local amount
local amount
if math.random() < 0.65 then -- what they buy is most likely not available
if math.random() < 0.65 then -- what they buy is most likely not available
amount = 0
amount = 0
else
else
amount = math.random(1, maxStock)
amount = math.random(1, maxStock)


-- limit to a max value
-- limit to a max value
local maxValue = 300 * 1000 * Balancing_GetSectorRichnessFactor(Sector():getCoordinates())
local maxValue = 300 * 10000 * Balancing_GetSectorRichnessFactor(Sector():getCoordinates())
amount = math.min(maxStock, math.floor(maxValue / v.price))
amount = math.min(maxStock, math.floor(maxValue / v.price))


-- but have at least 7 - 10
-- but have at least 7 - 10
amount = math.max(amount, math.random(7, 10))
amount = math.max(amount, math.random(7, 10))


-- ... if max stock allows it
-- ... if max stock allows it
amount = math.min(amount, maxStock)
amount = math.min(amount, maxStock)
end
end


entity:addCargo(v, amount)
entity:addCargo(v, amount)
end
end
end
end


table.insert(self.boughtGoods, v)
table.insert(self.boughtGoods, v)
end
end


self.soldGoods = {}
self.soldGoods = {}


for i, v in ipairs(soldGoodsIn) do
for i, v in ipairs(soldGoodsIn) do
if not generated then
if not generated then
local maxStock = self:getMaxStock(v.size)
local maxStock = self:getMaxStock(v.size)
if maxStock > 0 then
if maxStock > 0 then


-- generate a random amount of things
-- generate a random amount of things
local amount = 0
local amount = 0
if math.random() < 0.35 then -- what they sell is most likely available
if math.random() < 0.35 then -- what they sell is most likely available
amount = 0
amount = 0
else
else
amount = math.random(1, maxStock)
amount = math.random(1, maxStock)


-- limit to 500k value at max
-- limit to 500k value at max
local maxValue = 500 * 1000
local maxValue = 500 * 10000
amount = math.min(maxStock, math.floor(maxValue / v.price))
amount = math.min(maxStock, math.floor(maxValue / v.price))


-- but have at least a few
-- but have at least a few
amount = math.max(amount, math.random(2, 5))
amount = math.max(amount, math.random(2, 5))


-- ... if max stock allows it
-- ... if max stock allows it
amount = math.min(amount, maxStock)
amount = math.min(amount, maxStock)
end
end


entity:addCargo(v, amount)
entity:addCargo(v, amount)
end
end
end
end


table.insert(self.soldGoods, v)
table.insert(self.soldGoods, v)
end
end


self.numBought = #self.boughtGoods
self.numBought = #self.boughtGoods
self.numSold = #self.soldGoods
self.numSold = #self.soldGoods


end
end


function TradingManager:requestGoods()
function TradingManager:requestGoods()
self.boughtGoods = {}
self.boughtGoods = {}
self.soldGoods = {}
self.soldGoods = {}


self.numBought = 0
self.numBought = 0
self.numSold = 0
self.numSold = 0


invokeServerFunction("sendGoods", Player().index)
invokeServerFunction("sendGoods", Player().index)
end
end


function TradingManager:sendGoods(playerIndex)
function TradingManager:sendGoods(playerIndex)


local player = Player(playerIndex)
local player = Player(playerIndex)


invokeClientFunction(player, "receiveGoods", self.buyPriceFactor, self.sellPriceFactor, self.boughtGoods, self.soldGoods, self.policies)
invokeClientFunction(player, "receiveGoods", self.buyPriceFactor, self.sellPriceFactor, self.boughtGoods, self.soldGoods, self.policies)
end
end


function TradingManager:receiveGoods(buyFactor, sellFactor, boughtGoods_in, soldGoods_in, policies_in)
function TradingManager:receiveGoods(buyFactor, sellFactor, boughtGoods_in, soldGoods_in, policies_in)


self.buyPriceFactor = buyFactor
self.buyPriceFactor = buyFactor
self.sellPriceFactor = sellFactor
self.sellPriceFactor = sellFactor


self.policies = policies_in
self.policies = policies_in


self.boughtGoods = boughtGoods_in
self.boughtGoods = boughtGoods_in
self.soldGoods = soldGoods_in
self.soldGoods = soldGoods_in


self.numBought = #self.boughtGoods
self.numBought = #self.boughtGoods
self.numSold = #self.soldGoods
self.numSold = #self.soldGoods


local player = Player()
local player = Player()
local playerCraft = player.craft
local playerCraft = player.craft
if playerCraft.factionIndex == player.allianceIndex then
if playerCraft.factionIndex == player.allianceIndex then
player = player.alliance
player = player.alliance
end
end


for i, good in ipairs(self.boughtGoods) do
for i, good in ipairs(self.boughtGoods) do
self:updateBoughtGoodGui(i, good, self:getBuyPrice(good.name, player.index))
self:updateBoughtGoodGui(i, good, self:getBuyPrice(good.name, player.index))
end
end


for i, good in ipairs(self.soldGoods) do
for i, good in ipairs(self.soldGoods) do
self:updateSoldGoodGui(i, good, self:getSellPrice(good.name, player.index))
self:updateSoldGoodGui(i, good, self:getSellPrice(good.name, player.index))
end
end


end
end


function TradingManager:updateBoughtGoodGui(index, good, price)
function TradingManager:updateBoughtGoodGui(index, good, price)


if not self.guiInitialized then return end
if not self.guiInitialized then return end


local maxAmount = self:getMaxStock(good.size)
local maxAmount = self:getMaxStock(good.size)
local amount = self:getNumGoods(good.name)
local amount = self:getNumGoods(good.name)


local line = self.boughtLines[index]
local line = self.boughtLines[index]


line.name.caption = good.displayName
line.name.caption = good.displayName
line.stock.caption = amount .. "/" .. maxAmount
line.stock.caption = amount .. "/" .. maxAmount
line.price.caption = createMonetaryString(price)
line.price.caption = createMonetaryString(price)
line.size.caption = round(good.size, 2)
line.size.caption = round(good.size, 2)
line.icon.picture = good.icon
line.icon.picture = good.icon


local ownCargo = 0
local ownCargo = 0
local ship = Entity(Player().craftIndex)
local ship = Entity(Player().craftIndex)
if ship then
if ship then
ownCargo = ship:getCargoAmount(good) or 0
ownCargo = ship:getCargoAmount(good) or 0
end
end
if ownCargo == 0 then ownCargo = "-" end
if ownCargo == 0 then ownCargo = "-" end
line.you.caption = tostring(ownCargo)
line.you.caption = tostring(ownCargo)


line:show()
line:show()
end
end


function TradingManager:updateSoldGoodGui(index, good, price)
function TradingManager:updateSoldGoodGui(index, good, price)


if not self.guiInitialized then return end
if not self.guiInitialized then return end


local maxAmount = self:getMaxStock(good.size)
local maxAmount = self:getMaxStock(good.size)
local amount = self:getNumGoods(good.name)
local amount = self:getNumGoods(good.name)


local line = self.soldLines[index]
local line = self.soldLines[index]


line.icon.picture = good.icon
line.icon.picture = good.icon
line.name.caption = good.displayName
line.name.caption = good.displayName
line.stock.caption = amount .. "/" .. maxAmount
line.stock.caption = amount .. "/" .. maxAmount
line.price.caption = createMonetaryString(price)
line.price.caption = createMonetaryString(price)
line.size.caption = round(good.size, 2)
line.size.caption = round(good.size, 2)


for i, good in pairs(self.soldGoods) do
for i, good in pairs(self.soldGoods) do
local line = self.soldLines[i]
local line = self.soldLines[i]


local ownCargo = 0
local ownCargo = 0
local ship = Entity(Player().craftIndex)
local ship = Entity(Player().craftIndex)
if ship then
if ship then
ownCargo = math.floor((ship.freeCargoSpace or 0) / good.size)
ownCargo = math.floor((ship.freeCargoSpace or 0) / good.size)
end
end


if ownCargo == 0 then ownCargo = "-" end
if ownCargo == 0 then ownCargo = "-" end
line.you.caption = tostring(ownCargo)
line.you.caption = tostring(ownCargo)
end
end


line:show()
line:show()


end
end


function TradingManager:updateBoughtGoodAmount(index)
function TradingManager:updateBoughtGoodAmount(index)


local good = self.boughtGoods[index];
local good = self.boughtGoods[index];


if good ~= nil then -- it's possible that the production may start before the initialization of the client version of the factory
if good ~= nil then -- it's possible that the production may start before the initialization of the client version of the factory
local player = Player()
local player = Player()
local playerCraft = player.craft
local playerCraft = player.craft
if playerCraft.factionIndex == player.allianceIndex then
if playerCraft.factionIndex == player.allianceIndex then
player = player.alliance
player = player.alliance
end
end


self:updateBoughtGoodGui(index, good, self:getBuyPrice(good.name, player.index))
self:updateBoughtGoodGui(index, good, self:getBuyPrice(good.name, player.index))
end
end


end
end


function TradingManager:updateSoldGoodAmount(index)
function TradingManager:updateSoldGoodAmount(index)


local good = self.soldGoods[index];
local good = self.soldGoods[index];


if good ~= nil then -- it's possible that the production may start before the initialization of the client version of the factory
if good ~= nil then -- it's possible that the production may start before the initialization of the client version of the factory
local player = Player()
local player = Player()
local playerCraft = player.craft
local playerCraft = player.craft
if playerCraft.factionIndex == player.allianceIndex then
if playerCraft.factionIndex == player.allianceIndex then
player = player.alliance
player = player.alliance
end
end


self:updateSoldGoodGui(index, good, self:getSellPrice(good.name, player.index))
self:updateSoldGoodGui(index, good, self:getSellPrice(good.name, player.index))
end
end
end
end


function TradingManager:buildBuyGui(window)
function TradingManager:buildBuyGui(window)
self:buildGui(window, 1)
self:buildGui(window, 1)
end
end


function TradingManager:buildSellGui(window)
function TradingManager:buildSellGui(window)
self:buildGui(window, 0)
self:buildGui(window, 0)
end
end


function TradingManager:buildGui(window, guiType)
function TradingManager:buildGui(window, guiType)


local buttonCaption = ""
local buttonCaption = ""
local buttonCallback = ""
local buttonCallback = ""
local textCallback = ""
local textCallback = ""


if guiType == 1 then
if guiType == 1 then
buttonCaption = "Buy"%_t
buttonCaption = "Buy"%_t
buttonCallback = "onBuyButtonPressed"
buttonCallback = "onBuyButtonPressed"
textCallback = "onBuyTextEntered"
textCallback = "onBuyTextEntered"
else
else
buttonCaption = "Sell"%_t
buttonCaption = "Sell"%_t
buttonCallback = "onSellButtonPressed"
buttonCallback = "onSellButtonPressed"
textCallback = "onSellTextEntered"
textCallback = "onSellTextEntered"
end
end


local size = window.size
local size = window.size


-- window:createFrame(Rect(size))
-- window:createFrame(Rect(size))


local pictureX = 270
local pictureX = 270
local nameX = 10
local nameX = 10
local stockX = 310
local stockX = 310
local volX = 460
local volX = 460
local priceX = 530
local priceX = 530
local youX = 630
local youX = 630
local textBoxX = 720
local textBoxX = 720
local buttonX = 790
local buttonX = 790


local buttonSize = 70
local buttonSize = 70


-- header
-- header
window:createLabel(vec2(nameX, 0), "Name"%_t, 15)
window:createLabel(vec2(nameX, 0), "Name"%_t, 15)
window:createLabel(vec2(stockX, 0), "Stock"%_t, 15)
window:createLabel(vec2(stockX, 0), "Stock"%_t, 15)
window:createLabel(vec2(priceX, 0), "Cr"%_t, 15)
window:createLabel(vec2(priceX, 0), "Cr"%_t, 15)
window:createLabel(vec2(volX, 0), "Vol"%_t, 15)
window:createLabel(vec2(volX, 0), "Vol"%_t, 15)


if guiType == 1 then
if guiType == 1 then
window:createLabel(vec2(youX, 0), "Max"%_t, 15)
window:createLabel(vec2(youX, 0), "Max"%_t, 15)
else
else
window:createLabel(vec2(youX, 0), "You"%_t, 15)
window:createLabel(vec2(youX, 0), "You"%_t, 15)
end
end


local y = 25
local y = 25
for i = 1, 15 do
for i = 1, 15 do


local yText = y + 6
local yText = y + 6


local frame = window:createFrame(Rect(0, y, textBoxX - 10, 30 + y))
local frame = window:createFrame(Rect(0, y, textBoxX - 10, 30 + y))


local icon = window:createPicture(Rect(pictureX, yText - 5, 29 + pictureX, 29 + yText - 5), "")
local icon = window:createPicture(Rect(pictureX, yText - 5, 29 + pictureX, 29 + yText - 5), "")
local nameLabel = window:createLabel(vec2(nameX, yText), "", 15)
local nameLabel = window:createLabel(vec2(nameX, yText), "", 15)
local stockLabel = window:createLabel(vec2(stockX, yText), "", 15)
local stockLabel = window:createLabel(vec2(stockX, yText), "", 15)
local priceLabel = window:createLabel(vec2(priceX, yText), "", 15)
local priceLabel = window:createLabel(vec2(priceX, yText), "", 15)
local sizeLabel = window:createLabel(vec2(volX, yText), "", 15)
local sizeLabel = window:createLabel(vec2(volX, yText), "", 15)
local youLabel = window:createLabel(vec2(youX, yText), "", 15)
local youLabel = window:createLabel(vec2(youX, yText), "", 15)
local numberTextBox = window:createTextBox(Rect(textBoxX, yText - 6, 60 + textBoxX, 30 + yText - 6), textCallback)
local numberTextBox = window:createTextBox(Rect(textBoxX, yText - 6, 60 + textBoxX, 30 + yText - 6), textCallback)
local button = window:createButton(Rect(buttonX, yText - 6, window.size.x, 30 + yText - 6), buttonCaption, buttonCallback)
local button = window:createButton(Rect(buttonX, yText - 6, window.size.x, 30 + yText - 6), buttonCaption, buttonCallback)


button.maxTextSize = 16
button.maxTextSize = 16


numberTextBox.text = "0"
numberTextBox.text = "0"
numberTextBox.allowedCharacters = "0123456789"
numberTextBox.allowedCharacters = "0123456789"
numberTextBox.clearOnClick = 1
numberTextBox.clearOnClick = 1


icon.isIcon = 1
icon.isIcon = 1


local show = function (self)
local show = function (self)
self.icon:show()
self.icon:show()
self.frame:show()
self.frame:show()
self.name:show()
self.name:show()
self.stock:show()
self.stock:show()
self.price:show()
self.price:show()
self.size:show()
self.size:show()
self.number:show()
self.number:show()
self.button:show()
self.button:show()
self.you:show()
self.you:show()
end
end
local hide = function (self)
local hide = function (self)
self.icon:hide()
self.icon:hide()
self.frame:hide()
self.frame:hide()
self.name:hide()
self.name:hide()
self.stock:hide()
self.stock:hide()
self.price:hide()
self.price:hide()
self.size:hide()
self.size:hide()
self.number:hide()
self.number:hide()
self.button:hide()
self.button:hide()
self.you:hide()
self.you:hide()
end
end


local line = {icon = icon, frame = frame, name = nameLabel, stock = stockLabel, price = priceLabel, you = youLabel, size = sizeLabel, number = numberTextBox, button = button, show = show, hide = hide}
local line = {icon = icon, frame = frame, name = nameLabel, stock = stockLabel, price = priceLabel, you = youLabel, size = sizeLabel, number = numberTextBox, button = button, show = show, hide = hide}
line:hide()
line:hide()


if guiType == 1 then
if guiType == 1 then
table.insert(self.soldLines, line)
table.insert(self.soldLines, line)
else
else
table.insert(self.boughtLines, line)
table.insert(self.boughtLines, line)
end
end


y = y + 35
y = y + 35
end
end


end
end


function TradingManager:onBuyTextEntered(textBox)
function TradingManager:onBuyTextEntered(textBox)


local enteredNumber = tonumber(textBox.text)
local enteredNumber = tonumber(textBox.text)
if enteredNumber == nil then
if enteredNumber == nil then
enteredNumber = 0
enteredNumber = 0
end
end


local newNumber = enteredNumber
local newNumber = enteredNumber


local goodIndex = nil
local goodIndex = nil
for i, line in pairs(self.soldLines) do
for i, line in pairs(self.soldLines) do
if line.number.index == textBox.index then
if line.number.index == textBox.index then
goodIndex = i
goodIndex = i
break
break
end
end
end
end


if goodIndex == nil then return end
if goodIndex == nil then return end


local good = self.soldGoods[goodIndex]
local good = self.soldGoods[goodIndex]


if not good then
if not good then
print ("good with index " .. goodIndex .. " isn't sold.")
print ("good with index " .. goodIndex .. " isn't sold.")
printEntityDebugInfo()
printEntityDebugInfo()
return
return
end
end


-- make sure the player can't buy more than the station has in stock
-- make sure the player can't buy more than the station has in stock
local stock = self:getNumGoods(good.name)
local stock = self:getNumGoods(good.name)


if stock < newNumber then
if stock < newNumber then
newNumber = stock
newNumber = stock
end
end


local player = Player()
local player = Player()
local ship = player.craft
local ship = player.craft
local shipFaction
local shipFaction
if ship.factionIndex == player.allianceIndex then
if ship.factionIndex == player.allianceIndex then
shipFaction = player.alliance
shipFaction = player.alliance
end
end
if shipFaction == nil then
if shipFaction == nil then
shipFaction = player
shipFaction = player
end
end
if ship.freeCargoSpace == nil then return end --> no cargo bay
if ship.freeCargoSpace == nil then return end --> no cargo bay


-- make sure the player does not buy more than he can have in his cargo bay
-- make sure the player does not buy more than he can have in his cargo bay
local maxShipHold = math.floor(ship.freeCargoSpace / good.size)
local maxShipHold = math.floor(ship.freeCargoSpace / good.size)
local msg
local msg


if maxShipHold < newNumber then
if maxShipHold < newNumber then
newNumber = maxShipHold
newNumber = maxShipHold
if newNumber == 0 then
if newNumber == 0 then
msg = "Not enough space in your cargo bay!"%_t
msg = "Not enough space in your cargo bay!"%_t
else
else
msg = "You can only store ${amount} of this good!"%_t % {amount = newNumber}
msg = "You can only store ${amount} of this good!"%_t % {amount = newNumber}
end
end
end
end


-- make sure the player does not buy more than he can afford (if this isn't his station)
-- make sure the player does not buy more than he can afford (if this isn't his station)
if Faction().index ~= shipFaction.index then
if Faction().index ~= shipFaction.index then
local maxAffordable = math.floor(shipFaction.money / self:getSellPrice(good.name, shipFaction.index))
local maxAffordable = math.floor(shipFaction.money / self:getSellPrice(good.name, shipFaction.index))
if shipFaction.infiniteResources then maxAffordable = math.huge end
if shipFaction.infiniteResources then maxAffordable = math.huge end


if maxAffordable < newNumber then
if maxAffordable < newNumber then
newNumber = maxAffordable
newNumber = maxAffordable


if newNumber == 0 then
if newNumber == 0 then
msg = "You can't afford any of this good!"%_t
msg = "You can't afford any of this good!"%_t
else
else
msg = "You can only afford ${amount} of this good!"%_t % {amount = newNumber}
msg = "You can only afford ${amount} of this good!"%_t % {amount = newNumber}
end
end
end
end
end
end


if msg then
if msg then
self:sendError(nil, msg)
self:sendError(nil, msg)
end
end


if newNumber ~= enteredNumber then
if newNumber ~= enteredNumber then
textBox.text = newNumber
textBox.text = newNumber
end
end
end
end


function TradingManager:onSellTextEntered(textBox)
function TradingManager:onSellTextEntered(textBox)


local enteredNumber = tonumber(textBox.text)
local enteredNumber = tonumber(textBox.text)
if enteredNumber == nil then
if enteredNumber == nil then
enteredNumber = 0
enteredNumber = 0
end
end


local newNumber = enteredNumber
local newNumber = enteredNumber


local goodIndex = nil
local goodIndex = nil
for i, line in pairs(self.boughtLines) do
for i, line in pairs(self.boughtLines) do
if line.number.index == textBox.index then
if line.number.index == textBox.index then
goodIndex = i
goodIndex = i
break
break
end
end
end
end
if goodIndex == nil then return end
if goodIndex == nil then return end


local good = self.boughtGoods[goodIndex]
local good = self.boughtGoods[goodIndex]
if not good then
if not good then
print ("good with index " .. goodIndex .. " isn't bought")
print ("good with index " .. goodIndex .. " isn't bought")
printEntityDebugInfo();
printEntityDebugInfo();
return
return
end
end


local stock = self:getNumGoods(good.name)
local stock = self:getNumGoods(good.name)


local maxAmountPlaceable = self:getMaxStock(good.size) - stock;
local maxAmountPlaceable = self:getMaxStock(good.size) - stock;
if maxAmountPlaceable < newNumber then
if maxAmountPlaceable < newNumber then
newNumber = maxAmountPlaceable
newNumber = maxAmountPlaceable
end
end




local ship = Player().craft
local ship = Player().craft


local msg
local msg


-- make sure the player does not sell more than he has in his cargo bay
-- make sure the player does not sell more than he has in his cargo bay
local amountOnPlayerShip = ship:getCargoAmount(good)
local amountOnPlayerShip = ship:getCargoAmount(good)
if amountOnPlayerShip == nil then return end --> no cargo bay
if amountOnPlayerShip == nil then return end --> no cargo bay


if amountOnPlayerShip < newNumber then
if amountOnPlayerShip < newNumber then
newNumber = amountOnPlayerShip
newNumber = amountOnPlayerShip
if newNumber == 0 then
if newNumber == 0 then
msg = "You don't have any of this!"%_t
msg = "You don't have any of this!"%_t
end
end
end
end


if msg then
if msg then
self:sendError(nil, msg)
self:sendError(nil, msg)
end
end


-- maximum number of sellable things is the amount the player has on his ship
-- maximum number of sellable things is the amount the player has on his ship
if newNumber ~= enteredNumber then
if newNumber ~= enteredNumber then
textBox.text = newNumber
textBox.text = newNumber
end
end
end
end


function TradingManager:onBuyButtonPressed(button)
function TradingManager:onBuyButtonPressed(button)


local shipIndex = Player().craftIndex
local shipIndex = Player().craftIndex
local goodIndex = nil
local goodIndex = nil


for i, line in ipairs(self.soldLines) do
for i, line in ipairs(self.soldLines) do
if line.button.index == button.index then
if line.button.index == button.index then
goodIndex = i
goodIndex = i
end
end
end
end


if goodIndex == nil then
if goodIndex == nil then
print("internal error, good matching 'Buy' button doesn't exist.")
print("internal error, good matching 'Buy' button doesn't exist.")
return
return
end
end


local amount = self.soldLines[goodIndex].number.text
local amount = self.soldLines[goodIndex].number.text
if amount == "" then
if amount == "" then
amount = 0
amount = 0
else
else
amount = tonumber(amount)
amount = tonumber(amount)
end
end


local good = self.soldGoods[goodIndex]
local good = self.soldGoods[goodIndex]
if not good then
if not good then
print ("internal error, good with index " .. goodIndex .. " of buy button not found.")
print ("internal error, good with index " .. goodIndex .. " of buy button not found.")
printEntityDebugInfo()
printEntityDebugInfo()
return
return
end
end


invokeServerFunction("sellToShip", shipIndex, good.name, amount)
invokeServerFunction("sellToShip", shipIndex, good.name, amount)
end
end


function TradingManager:onSellButtonPressed(button)
function TradingManager:onSellButtonPressed(button)


local shipIndex = Player().craftIndex
local shipIndex = Player().craftIndex
local goodIndex = nil
local goodIndex = nil


for i, line in ipairs(self.boughtLines) do
for i, line in ipairs(self.boughtLines) do
if line.button.index == button.index then
if line.button.index == button.index then
goodIndex = i
goodIndex = i
end
end
end
end


if goodIndex == nil then
if goodIndex == nil then
return
return
end
end


local amount = self.boughtLines[goodIndex].number.text
local amount = self.boughtLines[goodIndex].number.text
if amount == "" then
if amount == "" then
amount = 0
amount = 0
else
else
amount = tonumber(amount)
amount = tonumber(amount)
end
end


local good = self.boughtGoods[goodIndex]
local good = self.boughtGoods[goodIndex]
if not good then
if not good then
print ("internal error, good with index " .. goodIndex .. " of sell button not found.")
print ("internal error, good with index " .. goodIndex .. " of sell button not found.")
printEntityDebugInfo()
printEntityDebugInfo()
return
return
end
end


invokeServerFunction("buyFromShip", shipIndex, good.name, amount)
invokeServerFunction("buyFromShip", shipIndex, good.name, amount)


end
end


function TradingManager:sendError(faction, msg, ...)
function TradingManager:sendError(faction, msg, ...)
if onServer() then
if onServer() then
if faction.isPlayer then
if faction.isPlayer then
Player(faction.index):sendChatMessage(Entity().title, 1, msg, ...)
Player(faction.index):sendChatMessage(Entity().title, 1, msg, ...)
end
end
elseif onClient() then
elseif onClient() then
displayChatMessage(msg, Entity().title, 1)
displayChatMessage(msg, Entity().title, 1)
end
end
end
end


function TradingManager:transferMoney(owner, from, to, amount, fromDescription, toDescription)
function TradingManager:transferMoney(owner, from, to, amount, fromDescription, toDescription)
if from.index == to.index then return end
if from.index == to.index then return end


local ownerMoney = amount * self.factionPaymentFactor
local ownerMoney = amount * self.factionPaymentFactor


if owner.index == from.index then
if owner.index == from.index then
from:pay(fromDescription or "", ownerMoney)
from:pay(fromDescription or "", ownerMoney)
to:receive(toDescription or "", amount)
to:receive(toDescription or "", amount)
elseif owner.index == to.index then
elseif owner.index == to.index then
from:pay(fromDescription or "", amount)
from:pay(fromDescription or "", amount)
to:receive(toDescription or "", ownerMoney)
to:receive(toDescription or "", ownerMoney)
else
else
from:pay(fromDescription or "", amount)
from:pay(fromDescription or "", amount)
to:receive(toDescription or "", amount)
to:receive(toDescription or "", amount)
end
end


receiveTransactionTax(Entity(), amount * self.tax)
receiveTransactionTax(Entity(), amount * self.tax)
end
end


function TradingManager:buyFromShip(shipIndex, goodName, amount, noDockCheck)
function TradingManager:buyFromShip(shipIndex, goodName, amount, noDockCheck)


-- check if the good can be bought
-- check if the good can be bought
if not self:getBoughtGoodByName(goodName) == nil then
if not self:getBoughtGoodByName(goodName) == nil then
self:sendError(shipFaction, "%s isn't bought."%_t, goodName)
self:sendError(shipFaction, "%s isn't bought."%_t, goodName)
return
return
end
end


local shipFaction, ship = getInteractingFactionByShip(shipIndex, callingPlayer, AlliancePrivilege.SpendResources)
local shipFaction, ship = getInteractingFactionByShip(shipIndex, callingPlayer, AlliancePrivilege.SpendResources)
if not shipFaction then return end
if not shipFaction then return end


if ship.freeCargoSpace == nil then
if ship.freeCargoSpace == nil then
self:sendError(shipFaction, "Your ship has no cargo bay!"%_t)
self:sendError(shipFaction, "Your ship has no cargo bay!"%_t)
return
return
end
end


-- check if the specific good from the player can be bought (ie. it's not illegal or something like that)
-- check if the specific good from the player can be bought (ie. it's not illegal or something like that)
local cargos = ship:findCargos(goodName)
local cargos = ship:findCargos(goodName)
local good = nil
local good = nil
local msg = "You don't have any %s that the station buys!"%_t
local msg = "You don't have any %s that the station buys!"%_t
local args = {goodName}
local args = {goodName}


for g, amount in pairs(cargos) do
for g, amount in pairs(cargos) do
local ok
local ok
ok, msg = self:isBoughtBySelf(g)
ok, msg = self:isBoughtBySelf(g)
args = {}
args = {}
if ok then
if ok then
good = g
good = g
break
break
end
end
end
end


if not good then
if not good then
self:sendError(shipFaction, msg, unpack(args))
self:sendError(shipFaction, msg, unpack(args))
return
return
end
end


local station = Entity()
local station = Entity()
local stationFaction = Faction()
local stationFaction = Faction()


-- make sure the ship can not sell more than the station can have in stock
-- make sure the ship can not sell more than the station can have in stock
local maxAmountPlaceable = self:getMaxStock(good.size) - self:getNumGoods(good.name);
local maxAmountPlaceable = self:getMaxStock(good.size) - self:getNumGoods(good.name);


if maxAmountPlaceable < amount then
if maxAmountPlaceable < amount then
amount = maxAmountPlaceable
amount = maxAmountPlaceable


if maxAmountPlaceable == 0 then
if maxAmountPlaceable == 0 then
self:sendError(shipFaction, "This station is not able to take any more %s."%_t, good.translatablePlural)
self:sendError(shipFaction, "This station is not able to take any more %s."%_t, good.translatablePlural)
end
end
end
end


-- make sure the player does not sell more than he has in his cargo bay
-- make sure the player does not sell more than he has in his cargo bay
local amountOnShip = ship:getCargoAmount(good)
local amountOnShip = ship:getCargoAmount(good)


if amountOnShip < amount then
if amountOnShip < amount then
amount = amountOnShip
amount = amountOnShip


if amountOnShip == 0 then
if amountOnShip == 0 then
self:sendError(shipFaction, "You don't have any %s on your ship"%_t, good.translatablePlural)
self:sendError(shipFaction, "You don't have any %s on your ship"%_t, good.translatablePlural)
end
end
end
end


if amount <= 0 then
if amount <= 0 then
return
return
end
end


-- begin transaction
-- begin transaction
-- calculate price. if the seller is the owner of the station, the price is 0
-- calculate price. if the seller is the owner of the station, the price is 0
local price = self:getBuyPrice(good.name, shipFaction.index) * amount
local price = self:getBuyPrice(good.name, shipFaction.index) * amount


local canPay, msg, args = stationFaction:canPay(price);
local canPay, msg, args = stationFaction:canPay(price);
if not canPay then
if not canPay then
self:sendError(shipFaction, "This station's faction doesn't have enough money."%_t)
self:sendError(shipFaction, "This station's faction doesn't have enough money."%_t)
return
return
end
end


if not noDockCheck then
if not noDockCheck then
-- test the docking last so the player can know what he can buy from afar already
-- test the docking last so the player can know what he can buy from afar already
local errors = {}
local errors = {}
errors[EntityType.Station] = "You must be docked to the station to trade
errors[EntityType.Station] = "You must be docked to the station to tra