|
bobby_tier_print = CreatePrint{ |
|
|
|
} |
|
bobby_restock_print = CreatePrint{ |
|
|
|
} |
|
|
|
bobby_mod_print = CreatePrint{ |
|
|
|
} |
|
|
|
bobby_cost_print = CreatePrint{ |
|
|
|
} |
|
|
|
if FirstLoad then |
|
g_BobbyRayShopOpen = false |
|
end |
|
|
|
function BobbyRayRolloverOverride() |
|
return g_BobbyRayShopOpen |
|
end |
|
|
|
|
|
|
|
function BobbyRayShopGetUnlockedTier() |
|
return GetQuestVar("BobbyRayQuest", "UnlockedTier") |
|
end |
|
|
|
function BobbyRayShopIsUnlocked() |
|
if not gv_Quests["BobbyRayQuest"] then return end |
|
return (GetQuestVar("BobbyRayQuest", "UnlockedTier") or 0) > 0 |
|
end |
|
|
|
function BobbyRayShopIsOpening() |
|
return GetQuestVar("BobbyRayQuest", "TCE_PreparingToOpen") == "done" and not GetQuestVar("BobbyRayQuest", "TCE_StoreNowOpen") == "done" |
|
end |
|
|
|
function BobbyRayShopGetRestockTime() |
|
return GetQuestVar("BobbyRayQuest", "RestockTimer") |
|
end |
|
|
|
function NetSyncEvents.Cheat_BobbyRaySetTier(tier) |
|
if not gv_Quests["BobbyRayQuest"] then return end |
|
SetQuestVar(QuestGetState("BobbyRayQuest"), "UnlockedTier", tier) |
|
ObjModified("g_BobbyRayShop_UnlockedTier") |
|
end |
|
|
|
function NetSyncEvents.Cheat_BobbyRayToggleLock() |
|
if not gv_Quests["BobbyRayQuest"] then return end |
|
if BobbyRayShopGetUnlockedTier() > 0 then |
|
SetQuestVar(QuestGetState("BobbyRayQuest"), "UnlockedTier", 0) |
|
SetQuestVar(QuestGetState("BobbyRayQuest"), "RestockTimer", 0) |
|
else |
|
SetQuestVar(QuestGetState("BobbyRayQuest"), "UnlockedTier", 1) |
|
SetQuestVar(QuestGetState("BobbyRayQuest"), "RestockTimer", Game.CampaignTime + 1 * const.Scale.h) |
|
end |
|
|
|
ObjModified("g_BobbyRayShop_UnlockedTier") |
|
end |
|
|
|
function TFormat.GetShopTime(context, time) |
|
local daysLeft = DivCeil(time, const.Scale.day) |
|
if daysLeft > 2 then return daysLeft .. " " .. T(569233738707, "days") end |
|
|
|
local hoursLeft = DivCeil(time, const.Scale.h) |
|
if hoursLeft > 1 then |
|
return T{292118944563, "<hours> hours", hours = hoursLeft} |
|
else |
|
return T(882114309389, "1 hour") |
|
end |
|
end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
GameVar("g_BobbyRayStore", { |
|
used = {}, |
|
standard = {}, |
|
standard_ids = {}, |
|
}) |
|
|
|
GameVar("g_BobbyRayCart", { |
|
units = {} |
|
}) |
|
|
|
|
|
|
|
|
|
if FirstLoad then |
|
lBobbyRayCartNextOrdinal = 1 |
|
lBobbyRayCartOrdinalUnits = {} |
|
end |
|
local function lClearOrdinals() |
|
lBobbyRayCartNextOrdinal = 1 |
|
lBobbyRayCartOrdinalUnits = lBobbyRayCartOrdinalUnits or {} |
|
table.clear(lBobbyRayCartOrdinalUnits) |
|
end |
|
|
|
local function lGetNextOrdinal() |
|
lBobbyRayCartNextOrdinal = lBobbyRayCartNextOrdinal + 1 |
|
return lBobbyRayCartNextOrdinal - 1 |
|
end |
|
|
|
local function lRebuildOrdinals(units) |
|
lClearOrdinals() |
|
for item_id, item in sorted_pairs(units) do |
|
lBobbyRayCartOrdinalUnits[item_id] = lGetNextOrdinal() |
|
end |
|
end |
|
|
|
local function lHasItemOrdinal(item_id) |
|
return lBobbyRayCartOrdinalUnits[item_id] and true or false |
|
end |
|
local function lGetItemOrdinal(item_id) |
|
assert(lBobbyRayCartOrdinalUnits[item_id]) |
|
return lBobbyRayCartOrdinalUnits[item_id] |
|
end |
|
|
|
local function lCheckShopIdInconsistency() |
|
for class, item in pairs(g_BobbyRayStore.standard) do |
|
if g_BobbyRayStore.standard_ids[item.id] ~= item.class then |
|
return true |
|
end |
|
end |
|
return false |
|
end |
|
|
|
local function lFixShopIds() |
|
table.clear(g_BobbyRayStore.standard_ids) |
|
for class, item in pairs(g_BobbyRayStore.standard) do |
|
g_BobbyRayStore.standard_ids[item.id] = item.class |
|
end |
|
end |
|
|
|
function OnMsg.LoadSessionData() |
|
if lCheckShopIdInconsistency() then |
|
BobbyRayCartClearEverything() |
|
lFixShopIds() |
|
end |
|
lRebuildOrdinals(BobbyRayCartGetUnits()) |
|
end |
|
|
|
|
|
|
|
GameVar("g_BobbyRayItemsDirty", false) |
|
function NetSyncEvents.BobbyRayMarkItemsAsSeen(category, subcategory) |
|
category = BobbyRayShopGetCategory(category) |
|
if subcategory then subcategory = BobbyRayShopGetSubCategory(subcategory) end |
|
|
|
g_BobbyRayItemsDirty = true |
|
|
|
for item_id, item in pairs(g_BobbyRayStore.used) do |
|
if (subcategory and item:GetSubCategory() == subcategory) or (not subcategory and item:GetCategory() == category) then |
|
item.Seen = true |
|
end |
|
end |
|
for id, item in pairs(g_BobbyRayStore.standard) do |
|
if (subcategory and item:GetSubCategory() == subcategory) or (not subcategory and item:GetCategory() == category) then |
|
item.Seen = true |
|
end |
|
end |
|
end |
|
|
|
function NetSyncEvents.BobbyRayUpdateNew() |
|
if not g_BobbyRayItemsDirty then return end |
|
g_BobbyRayItemsDirty = false |
|
for item_id, item in pairs(g_BobbyRayStore.used) do |
|
if item.Seen then item.New = false end |
|
end |
|
|
|
for item_class, item in pairs(g_BobbyRayStore.standard) do |
|
if item.Seen then item.New = false end |
|
end |
|
end |
|
|
|
|
|
|
|
function lGetShopItemFromId(id) |
|
if g_BobbyRayStore.used[id] then |
|
return g_BobbyRayStore.used[id] |
|
end |
|
return g_BobbyRayStore.standard[g_BobbyRayStore.standard_ids[id]] |
|
end |
|
|
|
function BobbyRayStoreGetEntry(entry) |
|
return entry.Used and entry or g_BobbyRayStore.standard[entry.class] |
|
end |
|
|
|
function BobbyRayStoreGetEntryCost(entry) |
|
local br_entry = BobbyRayStoreGetEntry(entry) |
|
local cost = br_entry and br_entry.Cost or 0 |
|
if IsGameRuleActive("BobbyPays") then |
|
local percent = GameRuleDefs.BobbyPays:ResolveValue("PricePercentMultiplier") or 0 |
|
cost = MulDivRound(cost, percent, 100) |
|
end |
|
return cost |
|
end |
|
|
|
function BobbyRayStoreDeliveryPrice(delivery_option) |
|
if not delivery_option then |
|
g_BobbyRayCart.delivery_option = g_BobbyRayCart.delivery_option or "Standard" |
|
delivery_option = g_BobbyRayCart.delivery_option |
|
end |
|
local price = FindPreset("BobbyRayShopDeliveryDef", delivery_option).Price |
|
if IsGameRuleActive("BobbyPays") then |
|
local percent = GameRuleDefs.BobbyPays:ResolveValue("PricePercentMultiplier") or 0 |
|
price = MulDivRound(price,percent, 100) |
|
end |
|
return price |
|
end |
|
|
|
function BobbyRayStoreClear() |
|
table.clear(g_BobbyRayStore) |
|
g_BobbyRayStore.used = {} |
|
g_BobbyRayStore.standard = {} |
|
g_BobbyRayStore.standard_ids = {} |
|
|
|
BobbyRayCartClearEverything() |
|
end |
|
|
|
function BobbyRayStoreToArray(categoryId, subcategoryId, context) |
|
local category = BobbyRayShopGetCategory(categoryId) |
|
local subcategory = BobbyRayShopGetSubCategory(subcategoryId) |
|
local array = {} |
|
local min_entries = 7 |
|
for item_id, item in pairs(g_BobbyRayStore.used) do |
|
if (subcategory and subcategory:BelongsInSubCategory(item)) or (not subcategory and category:BelongsInCategory(item)) then |
|
table.insert(array, item) |
|
end |
|
end |
|
for id, item in pairs(g_BobbyRayStore.standard) do |
|
if (subcategory and item:GetSubCategory() == subcategory) or (not subcategory and item:GetCategory() == category) then |
|
table.insert(array, item) |
|
end |
|
end |
|
|
|
table.sort(array, function(a,b) |
|
local aEntry = BobbyRayStoreGetEntry(a) |
|
local bEntry = BobbyRayStoreGetEntry(b) |
|
|
|
|
|
local units = g_BobbyRayCart.units |
|
local aInCart = units[a.id] and units[a.id] ~= 0 |
|
local bInCart = units[b.id] and units[b.id] ~= 0 |
|
if aInCart and not bInCart then return true |
|
elseif bInCart and not aInCart then return false end |
|
|
|
|
|
local aEntrySubCat = aEntry:GetSubCategory() |
|
local bEntrySubCat = bEntry:GetSubCategory() |
|
if aEntrySubCat.SortKey < bEntrySubCat.SortKey then return true end |
|
if aEntrySubCat.SortKey > bEntrySubCat.SortKey then return false end |
|
|
|
|
|
if aEntry.class < bEntry.class then return true end |
|
if aEntry.class > bEntry.class then return false end |
|
|
|
|
|
if aEntry.Condition < bEntry.Condition then return false end |
|
if aEntry.Condition > bEntry.Condition then return true end |
|
|
|
|
|
return aEntry.id < bEntry.id |
|
end) |
|
|
|
for i = table.count(array), min_entries - 1 do |
|
table.insert(array, empty_table) |
|
end |
|
|
|
NetSyncEvent("BobbyRayMarkItemsAsSeen", category.id, subcategory and subcategory.id) |
|
|
|
return array |
|
end |
|
|
|
function BobbyRayCartUnitsToOrders() |
|
local orders = {} |
|
local min_entries = 12 |
|
for item_id, count in pairs(BobbyRayCartGetUnits()) do |
|
local entry = lGetShopItemFromId(item_id) |
|
table.insert(orders, entry) |
|
end |
|
table.sort(orders, function(a,b) return lGetItemOrdinal(a.id) < lGetItemOrdinal(b.id) end) |
|
for i=table.count(orders), min_entries - 1 do |
|
table.insert(orders, empty_table) |
|
end |
|
return orders |
|
end |
|
|
|
function BobbyRayCartGetAggregate() |
|
local acc = MulDivRound(BobbyRayStoreDeliveryPrice(), gv_Sectors[BobbyRayCartGetDeliverySector()].BobbyRayDeliveryCostMultiplier, 100) |
|
local count = 0 |
|
for item_id, number in pairs(BobbyRayCartGetUnits()) do |
|
local entry = lGetShopItemFromId(item_id) |
|
local br_entry_cost = BobbyRayStoreGetEntryCost(entry) |
|
acc = acc + br_entry_cost * number |
|
count = count + number |
|
end |
|
return count, acc |
|
end |
|
|
|
function BobbyRayCartHasEnoughMoney(entry) |
|
local cart_count, cart_cost = BobbyRayCartGetAggregate() |
|
local entry_cost = BobbyRayStoreGetEntryCost(entry) |
|
return Game.Money - cart_cost - entry_cost >= 0 |
|
end |
|
|
|
function BobbyRayCartHasEnoughStock(entry) |
|
local max_stock = BobbyRayStoreGetEntry(entry).Stock |
|
local cur_stock = BobbyRayCartGetUnits()[entry.id] or 0 |
|
return cur_stock < max_stock |
|
end |
|
|
|
function CanAddToCart(item_id) |
|
local item = lGetShopItemFromId(item_id) |
|
return BobbyRayCartHasEnoughMoney(item) and BobbyRayCartHasEnoughStock(item) |
|
end |
|
|
|
function CanRemoveFromCart(item_id) |
|
return g_BobbyRayCart.units[item_id] and g_BobbyRayCart.units[item_id] > 0 |
|
end |
|
|
|
function BobbyRayCartClearEverything() |
|
g_BobbyRayCart.delivery_option = nil |
|
BobbyRayCartClearSectorDelivery() |
|
BobbyRayCartClearUnits() |
|
end |
|
|
|
|
|
|
|
local function lForgetBobbyRayOrderTabState() |
|
if PDABrowserTabState["bobby_ray_shop"] and PDABrowserTabState["bobby_ray_shop"].mode_param == "cart" and table.count(g_BobbyRayCart.units) == 0 then |
|
PDABrowserTabState["bobby_ray_shop"].mode_param = "front" |
|
end |
|
end |
|
|
|
local function lBobbyRayCartAdd(item_id) |
|
g_BobbyRayCart.units[item_id] = g_BobbyRayCart.units[item_id] and g_BobbyRayCart.units[item_id] + 1 or 1 |
|
if not lHasItemOrdinal(item_id) then lBobbyRayCartOrdinalUnits[item_id] = lGetNextOrdinal() end |
|
end |
|
|
|
local function lBobbyRayCartRemove(item_id) |
|
g_BobbyRayCart.units[item_id] = g_BobbyRayCart.units[item_id] and Max(0, g_BobbyRayCart.units[item_id] - 1) or 0 |
|
end |
|
|
|
function BobbyRayCartGetUnits() |
|
g_BobbyRayCart.units = g_BobbyRayCart.units or {} |
|
return g_BobbyRayCart.units |
|
end |
|
|
|
function BobbyRayCartClearUnits() |
|
table.clear(g_BobbyRayCart.units) |
|
lClearOrdinals() |
|
end |
|
|
|
function NetSyncEvents.BobbyRayCartAdd(item_id) |
|
if not CanAddToCart(item_id) then return end |
|
lBobbyRayCartAdd(item_id) |
|
ObjModified(g_BobbyRayCart) |
|
end |
|
|
|
function NetSyncEvents.BobbyRayCartRemove(item_id) |
|
lBobbyRayCartRemove(item_id) |
|
ObjModified(g_BobbyRayCart) |
|
end |
|
|
|
|
|
|
|
|
|
|
|
if FirstLoad then |
|
BobbyRayOrderFormOpenedBySelf = false |
|
BobbyRayOrderFormOpenedByOther = false |
|
end |
|
|
|
function BobbyRayCheckClearEmptyCartEntries() |
|
if not g_BobbyRayCart then return end |
|
if not BobbyRayOrderFormOpenedByOther and not BobbyRayOrderFormOpenedBySelf then |
|
for item, amount in pairs(g_BobbyRayCart.units) do |
|
if amount == 0 then |
|
g_BobbyRayCart.units[item] = nil |
|
lBobbyRayCartOrdinalUnits[item] = nil |
|
end |
|
end |
|
lForgetBobbyRayOrderTabState() |
|
end |
|
end |
|
|
|
function GetBobbyRayOrderFormOpenId() |
|
return netInGame and netUniqueId or "self" |
|
end |
|
|
|
function NetSyncEvents.SetBobbyRayOrderFormOpened(player_id, open) |
|
if player_id == GetBobbyRayOrderFormOpenId() then |
|
BobbyRayOrderFormOpenedBySelf = open |
|
else |
|
BobbyRayOrderFormOpenedByOther = open |
|
end |
|
BobbyRayCheckClearEmptyCartEntries() |
|
end |
|
|
|
function OnMsg.NetGameLeft() |
|
BobbyRayOrderFormOpenedByOther = false |
|
BobbyRayCheckClearEmptyCartEntries() |
|
end |
|
|
|
function OnMsg.NetPlayerLeft(player_id) |
|
BobbyRayOrderFormOpenedByOther = false |
|
BobbyRayCheckClearEmptyCartEntries() |
|
end |
|
|
|
function OnMsg.ChangeMap() |
|
BobbyRayOrderFormOpenedBySelf = false |
|
BobbyRayOrderFormOpenedByOther = false |
|
end |
|
|
|
|
|
|
|
function BobbyRayCartGetDefaultDeliveryOption() |
|
return Presets.BobbyRayShopDeliveryDef.Default.Standard |
|
end |
|
|
|
function BobbyRayCartGetDeliveryOption() |
|
g_BobbyRayCart.delivery_option = g_BobbyRayCart.delivery_option or "Standard" |
|
return FindPreset("BobbyRayShopDeliveryDef", g_BobbyRayCart.delivery_option) |
|
end |
|
|
|
local function lBobbyRaySetDeliveryOption(option_id) |
|
g_BobbyRayCart.delivery_option = option_id |
|
end |
|
|
|
function NetSyncEvents.BobbyRaySetDeliveryOption(option_id) |
|
lBobbyRaySetDeliveryOption(option_id) |
|
ObjModified(g_BobbyRayCart) |
|
end |
|
|
|
|
|
|
|
function BobbyRayGetAvailableDeliverySectors() |
|
local initial_sector = GetCurrentCampaignPreset().InitialSector |
|
local sectors = { initial_sector } |
|
for id, sector in pairs(gv_Sectors) do |
|
if sector.Side == "player1" and sector.CanBeUsedForArrival and not sector.PortLocked and sector.last_own_campaign_time ~= 0 and id ~= initial_sector then |
|
table.insert(sectors, id) |
|
end |
|
end |
|
if #sectors == 0 then |
|
table.insert(sectors, initial_sector) |
|
end |
|
return sectors |
|
end |
|
|
|
function BobbyRayCartSetSectorDelivery(sectorId) |
|
g_BobbyRayCart.delivery_destination = sectorId |
|
end |
|
|
|
function NetSyncEvents.BobbyRayCartSetSectorDelivery(sectorId) |
|
BobbyRayCartSetSectorDelivery(sectorId) |
|
ObjModified(g_BobbyRayCart) |
|
ObjModified("DeliverySectorChanged") |
|
end |
|
|
|
function BobbyRayGetDefaultDeliverySector() |
|
return BobbyRayGetAvailableDeliverySectors()[1] |
|
end |
|
|
|
function BobbyRayCartGetDeliverySector() |
|
return g_BobbyRayCart.delivery_destination or BobbyRayGetDefaultDeliverySector() |
|
end |
|
|
|
function BobbyRayCartClearSectorDelivery() |
|
g_BobbyRayCart.delivery_destination = nil |
|
end |
|
|
|
|
|
|
|
function BobbyRayStoreGetStats_Firearm(item) |
|
return { |
|
{ T(467324314141, "DMG"), Untranslated(item.Damage) }, |
|
{ T(788999452116, "RANGE"), Untranslated(item.WeaponRange) }, |
|
{ T(921500948697, "CRIT"), T{580888120593, "<percent(number)>", number = Presets.WeaponPropertyDef.Default.MaxCritChance:GetProp(item)} }, |
|
{ T(842354777573, "PEN"), GetPenetrationClassUIText(item.PenetrationClass) }, |
|
} |
|
end |
|
|
|
function BobbyRayStoreGetStats_MeleeWeapon(item) |
|
return { |
|
{ T(467324314141, "DMG"), Untranslated(item.Damage and item.Damage or item.BaseDamage) }, |
|
{ T(788999452116, "RANGE"), Untranslated(item.WeaponRange) }, |
|
{ T(921500948697, "CRIT"), T{580888120593, "<percent(number)>", number = Presets.WeaponPropertyDef.Default.MaxCritChance:GetProp(item)} }, |
|
{ T(842354777573, "PEN"), GetPenetrationClassUIText(item.PenetrationClass) }, |
|
} |
|
end |
|
|
|
function BobbyRayStoreGetStats_Armor(item) |
|
return { |
|
{ T(113963825061, "DR"), T{580888120593, "<percent(number)>", number = item.DamageReduction + item.AdditionalReduction } }, |
|
{ T(260685017729, "SLOT"), Presets.TargetBodyPart.Default[item.Slot].display_name }, |
|
{ T(842354777573, "PEN"), GetPenetrationClassUIText(item.PenetrationClass) }, |
|
} |
|
end |
|
|
|
function BobbyRayStoreGetStats_Ammo(item) |
|
return { |
|
{ T(196962828215, "Cal"), FindPreset("Caliber", item.Caliber).Name }, |
|
item.PenetrationClass and { T(314470590373, "Pen"), GetPenetrationClassUIText(item.PenetrationClass) } or nil |
|
} |
|
end |
|
|
|
function BobbyRayStoreGetStats_Other(item) |
|
return nil |
|
end |
|
|
|
function PickRandomWeightItems(num, items_array, max_weight) |
|
local picked_items = {} |
|
local picked_items_set = {} |
|
for i=1, num do |
|
local rand_weight = InteractionRand(max_weight, "BobbyRayShop") |
|
local cur_weight = 0 |
|
local cur_index = 1 |
|
while true do |
|
local item = items_array[cur_index] |
|
while picked_items_set[item] do |
|
cur_index = cur_index + 1 |
|
item = items_array[cur_index] |
|
end |
|
cur_weight = cur_weight + item.RestockWeight |
|
cur_index = cur_index + 1 |
|
if cur_weight > rand_weight then |
|
table.insert(picked_items, item.class) |
|
picked_items_set[item] = true |
|
max_weight = max_weight - item.RestockWeight |
|
break |
|
end |
|
end |
|
end |
|
return picked_items |
|
end |
|
|
|
function PrepareShopItemsForRestock(unlocked_tier, used) |
|
local category_weights = {} |
|
local category_count = {} |
|
local category_items = {} |
|
local category_items_set = {} |
|
NetUpdateHash("PrepareShopItemsForRestock1", unlocked_tier, used) |
|
|
|
|
|
ForEachPreset("InventoryItemCompositeDef", function(preset) |
|
local item = g_Classes[preset.id] |
|
local usedOrStandard = false |
|
if used then |
|
usedOrStandard = item.CanAppearUsed |
|
else |
|
usedOrStandard = item.CanAppearStandard |
|
end |
|
if item.CanAppearInShop and usedOrStandard and item.Tier <= unlocked_tier and item.RestockWeight > 0 then |
|
local cat = item:GetCategory().id |
|
if not category_weights[cat] then |
|
category_weights[cat] = 0 |
|
category_count[cat] = 0 |
|
category_items[cat] = {} |
|
category_items_set[cat] = {} |
|
end |
|
table.insert(category_items[cat], item) |
|
category_items_set[cat][item] = true |
|
category_weights[cat] = category_weights[cat] + item.RestockWeight |
|
category_count[cat] = category_count[cat] + 1 |
|
NetUpdateHash("PrepareShopItemsForRestock2", cat, item.class, item.RestockWeight, category_count[cat], category_weights[cat]) |
|
end |
|
end) |
|
return category_items, category_count, category_weights, category_items_set |
|
end |
|
|
|
function RandomlyModifyWeapon(weapon) |
|
local weapon_component_chance = const.BobbyRay.Restock_UsedWeaponComponentPercentage |
|
local weapon_component_price_modifier = const.BobbyRay.Restock_UsedWeaponComponentPriceMod |
|
|
|
local shuffledComponents = {} |
|
for i, slotDef in ipairs(weapon.ComponentSlots) do |
|
table.insert(shuffledComponents, slotDef) |
|
end |
|
table.shuffle(shuffledComponents, InteractionRand(nil, "BobbyRayShop")) |
|
|
|
local blocked_slots = {} |
|
local applied_mods = {} |
|
for i, slotDef in ipairs(shuffledComponents) do |
|
if InteractionRand(100, "BobbyRayShop") <= weapon_component_chance and not blocked_slots[slotDef.SlotType] then |
|
local comp_num = #slotDef.AvailableComponents |
|
if slotDef.DefaultComponent and slotDef.DefaultComponent ~= "" then comp_num = comp_num - 1 end |
|
assert(comp_num >= 0) |
|
if comp_num == 0 then goto continue end |
|
local index = InteractionRand(comp_num) + 1 |
|
local comp_id = slotDef.AvailableComponents[index] |
|
local component = WeaponComponents[comp_id] |
|
if comp_id == slotDef.DefaultComponent then component = slotDef.AvailableComponents[comp_num] end |
|
applied_mods[slotDef.SlotType] = comp_id |
|
if component.BlockSlots and next(component.BlockSlots) ~= nil then |
|
for _, blockSlotType in ipairs(component.BlockSlots) do |
|
blocked_slots[blockSlotType] = true |
|
end |
|
end |
|
bobby_mod_print("Applied", comp_id,"to", slotDef.SlotType) |
|
elseif blocked_slots[slotDef.SlotType] then |
|
bobby_mod_print("Skipped due to blocked slot:", slotDef.SlotType) |
|
else |
|
bobby_mod_print("Skipped due to low chance:", slotDef.SlotType) |
|
end |
|
::continue:: |
|
end |
|
|
|
local cost_modifier = 0 |
|
for slot, component in pairs(applied_mods) do |
|
cost_modifier = cost_modifier + weapon_component_price_modifier |
|
weapon:SetWeaponComponent(slot, component) |
|
end |
|
|
|
return cost_modifier |
|
end |
|
|
|
function RestockStandardItem(item_class) |
|
local item = g_BobbyRayStore.standard[item_class] |
|
if not item then |
|
item = PlaceInventoryItem(item_class) |
|
item.Stock = 0 |
|
g_BobbyRayStore.standard[item_class] = item |
|
g_BobbyRayStore.standard_ids[item.id] = item_class |
|
end |
|
|
|
item.LastRestock = Game.CampaignTime |
|
local rand_stock = InteractionRand(item.MaxStock + 1, "BobbyRayShop") |
|
item.Stock = Max(1, item.Stock, rand_stock) |
|
item.New = true |
|
end |
|
|
|
function RestockUsedArmor(armor_id) |
|
local used_price_min = const.BobbyRay.Restock_UsedPriceModMin |
|
local used_price_max = const.BobbyRay.Restock_UsedPriceModMax |
|
local used_condition_min = const.BobbyRay.Restock_UsedConditionMin |
|
local used_condition_max = const.BobbyRay.Restock_UsedConditionMax |
|
|
|
local item = PlaceInventoryItem(armor_id) |
|
item.Used = true |
|
item.Condition = used_condition_min + InteractionRand(used_condition_max - used_condition_min, "BobbyRayShop") |
|
local usedCostMod = used_price_min + InteractionRand(used_price_max - used_price_min, "BobbyRayShop") |
|
item.Stock = 1 |
|
item.LastRestock = Game.CampaignTime |
|
g_BobbyRayStore.used[item.id] = item |
|
local baseCost = item.Cost |
|
item.Cost = MulDivRound(item.Cost, usedCostMod, 100) |
|
bobby_cost_print(item.class, "\n\tbase price:", baseCost, "$", "\n\tcondition price mod:", usedCostMod, "%", "\n\tfinal price:", item.Cost, "$") |
|
item.New = true |
|
end |
|
|
|
function RestockUsedWeapon(weapon_id) |
|
local used_price_min = const.BobbyRay.Restock_UsedPriceModMin |
|
local used_price_max = const.BobbyRay.Restock_UsedPriceModMax |
|
local used_condition_min = const.BobbyRay.Restock_UsedConditionMin |
|
local used_condition_max = const.BobbyRay.Restock_UsedConditionMax |
|
|
|
local item = PlaceInventoryItem(weapon_id) |
|
item.Used = true |
|
item.Condition = used_condition_min + InteractionRand(used_condition_max - used_condition_min, "BobbyRayShop") |
|
local usedCostMod = used_price_min + InteractionRand(used_price_max - used_price_min, "BobbyRayShop") |
|
item.Stock = 1 |
|
item.LastRestock = Game.CampaignTime |
|
local compCostMod = RandomlyModifyWeapon(item) |
|
g_BobbyRayStore.used[item.id] = item |
|
local baseCost = item.Cost |
|
item.Cost = MulDivRound(item.Cost, usedCostMod + compCostMod, 100) |
|
bobby_cost_print(item.class, "\n\tbase price:", baseCost, "%", "\n\tcondition price mod:", usedCostMod, "%", "\n\tmodification price mod:", compCostMod, "%", "\n\tfinal price:", item.Cost, "$") |
|
item.New = true |
|
end |
|
|
|
function BobbyRayStoreRestock(restock_modifier_standard, restock_modifier_used) |
|
if not BobbyRayShopIsUnlocked() then return end |
|
|
|
local restock_min_percent_used = MulDivRound(const.BobbyRay.Restock_UsedPercentageMin, restock_modifier_used or 100, 100) |
|
local restock_max_percent_used = MulDivRound(const.BobbyRay.Restock_UsedPercentageMax, restock_modifier_used or 100, 100) |
|
local restock_min_percent_standard = MulDivRound(const.BobbyRay.Restock_StandardPercentageMin, restock_modifier_standard or 100, 100) |
|
local restock_max_percent_standard = MulDivRound(const.BobbyRay.Restock_StandardPercentageMax, restock_modifier_standard or 100, 100) |
|
|
|
local category_items, category_count, category_weights, category_items_set = PrepareShopItemsForRestock(BobbyRayShopGetUnlockedTier(), "used") |
|
|
|
|
|
local total_items = category_count["Armor"] |
|
local restock_items = Max(1, MulDivRound(total_items, restock_min_percent_used + InteractionRand(restock_max_percent_used - restock_min_percent_used + 1, "BobbyRayShop"), 100)) |
|
restock_items = Min(restock_items, #category_items["Armor"]) |
|
local picked_items = PickRandomWeightItems(restock_items, category_items["Armor"], category_weights["Armor"]) |
|
if total_items > 0 then bobby_restock_print("Restocking", restock_items, "out of", total_items, "used", "Armors", "or ", MulDivRound(restock_items, 100, total_items)) end |
|
for _, item in ipairs(picked_items) do |
|
RestockUsedArmor(item) |
|
end |
|
|
|
|
|
|
|
local total_items = category_count["Weapons"] |
|
local restock_items = Max(1, MulDivRound(total_items, restock_min_percent_used + InteractionRand(restock_max_percent_used - restock_min_percent_used + 1, "BobbyRayShop"), 100)) |
|
restock_items = Min(restock_items, #category_items["Weapons"]) |
|
local picked_items = PickRandomWeightItems(restock_items, category_items["Weapons"], category_weights["Weapons"]) |
|
if total_items > 0 then bobby_restock_print("Restocking", restock_items, "out of", total_items, "used", "Weapons", "or ", MulDivRound(restock_items, 100, total_items)) end |
|
for _, item in ipairs(picked_items) do |
|
RestockUsedWeapon(item) |
|
end |
|
|
|
|
|
|
|
local category_items, category_count, category_weights, category_items_set = PrepareShopItemsForRestock(BobbyRayShopGetUnlockedTier()) |
|
for cat, _ in sorted_pairs(category_weights) do |
|
local total_items = category_count[cat] |
|
local restock_items = Max(1, MulDivRound(total_items, restock_min_percent_standard + InteractionRand(restock_max_percent_standard - restock_min_percent_standard + 1, "BobbyRayShop"), 100)) |
|
restock_items = Min(restock_items, #category_items[cat]) |
|
if total_items > 0 then bobby_restock_print("Restocking", restock_items, "out of", total_items, cat, "or ", MulDivRound(restock_items, 100, total_items)) end |
|
local picked_items = PickRandomWeightItems(restock_items, category_items[cat], category_weights[cat]) |
|
for _, item in ipairs(picked_items) do |
|
RestockStandardItem(item) |
|
end |
|
end |
|
CombatLog("important", T(938586124784, "Inventory restock at Bobby Ray's Guns 'n Things.")) |
|
end |
|
|
|
function BobbyRayStoreConsumeRandomStock(pick_probability, stock_min_percent, stock_max_percent) |
|
pick_probability = pick_probability or const.BobbyRay.FakePurchase_PickProbability |
|
stock_min_percent = stock_min_percent or const.BobbyRay.FakePurchase_StockConsumedMin |
|
stock_max_percent = stock_max_percent or const.BobbyRay.FakePurchase_StockConsumedMax |
|
|
|
local consumed_items = 0 |
|
local total_items = 0 |
|
for _, item in sorted_pairs(g_BobbyRayStore.standard) do |
|
total_items = total_items + 1 |
|
if item.CanBeConsumed and InteractionRand(100, "BobbyRayShop") < pick_probability then |
|
consumed_items = consumed_items + 1 |
|
local current_stock = item.Stock |
|
local stock_purchased = Max(1, MulDivRound(current_stock, stock_min_percent, 100) + MulDivRound(current_stock, InteractionRand(stock_max_percent - stock_min_percent + 1, "BobbyRayShop"), 100)) |
|
assert(current_stock >= stock_purchased) |
|
local new_stock = current_stock - stock_purchased |
|
item.Stock = new_stock |
|
bobby_restock_print("Consumed", stock_purchased, "out of", current_stock, "of", item.class, "(Standard)", "or", MulDivRound(stock_purchased, 100, current_stock)) |
|
|
|
if new_stock <= 0 then |
|
g_BobbyRayStore.standard[item.class] = nil |
|
g_BobbyRayStore.standard_ids[item.id] = nil |
|
end |
|
end |
|
end |
|
if total_items > 0 then bobby_restock_print(" --------------------------- Consumed", consumed_items, "out of", total_items, "standard items", "or", MulDivRound(consumed_items, 100, total_items)) end |
|
|
|
|
|
consumed_items = 0 |
|
total_items = 0 |
|
for item_id, item in sorted_pairs(g_BobbyRayStore.used) do |
|
total_items = total_items + 1 |
|
if item.CanBeConsumed and InteractionRand(100, "BobbyRayShop") < pick_probability then |
|
consumed_items = consumed_items + 1 |
|
bobby_restock_print("Consumed", item.class, "(Used)") |
|
g_BobbyRayStore.used[item.id] = nil |
|
end |
|
end |
|
if total_items > 0 then bobby_restock_print(" --------------------------- Consumed", consumed_items, "out of", total_items, "used items", "or", MulDivRound(consumed_items, 100, total_items)) end |
|
end |
|
|
|
function BobbyRayShopSetCategory(category, subcategory) |
|
PDABrowserTabState["bobby_ray_shop"].category = category or "Weapons" |
|
PDABrowserTabState["bobby_ray_shop"].subcategory = subcategory |
|
end |
|
|
|
function BobbyRayShopGetActiveCategoryPair(category) |
|
PDABrowserTabState["bobby_ray_shop"].category = PDABrowserTabState["bobby_ray_shop"].category or "Weapons" |
|
return PDABrowserTabState["bobby_ray_shop"].category, PDABrowserTabState["bobby_ray_shop"].subcategory |
|
end |
|
|
|
function BobbyRayShopGetCategory(category) |
|
return FindPreset("BobbyRayShopCategory", category) |
|
end |
|
|
|
function BobbyRayShopGetSubCategory(subcategory) |
|
return FindPreset("BobbyRayShopSubCategory", subcategory) |
|
end |
|
|
|
|
|
function TFormat.GetWeaponModificationRolloverTitle(ctx) |
|
local component = WeaponComponents[WeaponGetComponentAt(ctx.weapon, ctx.index)] |
|
return component.DisplayName |
|
end |
|
|
|
function TFormat.GetWeaponModificationRolloverText(ctx) |
|
local component = WeaponComponents[WeaponGetComponentAt(ctx.weapon, ctx.index)] |
|
return GetWeaponComponentDescription(component) |
|
end |
|
|
|
function WeaponCountComponents(weapon) |
|
if not weapon.components then return 0 end |
|
|
|
local count = 0 |
|
for slot, component in pairs(weapon.components) do |
|
local componentSlot = table.find_value(weapon.ComponentSlots, "SlotType", slot) |
|
|
|
if component and component ~= "" then count = count + 1 end |
|
end |
|
return count |
|
end |
|
|
|
function WeaponGetComponentAt(weapon, index) |
|
if not weapon.ComponentSlots or not weapon.ComponentSlots[index] then return nil end |
|
|
|
local count = 0 |
|
for _, slot in ipairs(weapon.ComponentSlots) do |
|
local comp = weapon.components[slot.SlotType] |
|
|
|
if comp and comp ~= "" then |
|
count = count + 1 |
|
end |
|
if count == index then |
|
return comp |
|
end |
|
end |
|
return nil |
|
end |
|
|
|
|
|
|
|
GameVar("g_BobbyRay_CurrentShipments", {}) |
|
|
|
local function lBobbyRayAddShipment(departure_time, due_time, order_id, items, sector_id, total_cost, delivery_option) |
|
assert(not g_BobbyRay_CurrentShipments[order_id]) |
|
g_BobbyRay_CurrentShipments[order_id] = { order_id = order_id, departure_time = departure_time, due_time = due_time, items = items, sector_id = sector_id, total_cost = total_cost, delivery_option = delivery_option.id } |
|
return g_BobbyRay_CurrentShipments[order_id] |
|
end |
|
|
|
local function lBobbyRayRemoveShipment(order_id) |
|
assert(g_BobbyRay_CurrentShipments[order_id]) |
|
g_BobbyRay_CurrentShipments[order_id] = nil |
|
end |
|
|
|
local function lBobbyRayClearShipments() |
|
table.clear(g_BobbyRay_CurrentShipments) |
|
end |
|
|
|
local function lCheckShipments() |
|
local due_shipments = {} |
|
for order_id, shipment in pairs(g_BobbyRay_CurrentShipments) do |
|
if Game.CampaignTime >= shipment.due_time then |
|
table.insert(due_shipments, shipment) |
|
end |
|
end |
|
|
|
table.sort(due_shipments, function(a,b) |
|
if a.due_time > b.due_time then return false; |
|
elseif b.due_time > a.due_time then return true; |
|
end |
|
|
|
assert(a == b or a.order_id ~= b.order_id) |
|
if a.order_id >= b.order_id then return false; |
|
elseif b.order_id > a.order_id then return true; |
|
end |
|
end) |
|
|
|
for _, shipment in ipairs(due_shipments) do |
|
Msg("BobbyRayShopShipmentArrived", shipment) |
|
lBobbyRayRemoveShipment(shipment.order_id) |
|
end |
|
end |
|
|
|
function GetClosestShipment() |
|
if table.count(g_BobbyRay_CurrentShipments) == 0 then return nil end |
|
|
|
local closest_shipment = nil |
|
for id, shipment in pairs(g_BobbyRay_CurrentShipments) do |
|
if closest_shipment == nil or shipment.due_time < closest_shipment.due_time or shipment.order_id < closest_shipment.order_id then |
|
closest_shipment = shipment |
|
end |
|
end |
|
return closest_shipment |
|
end |
|
|
|
local function lGenerateShipmentId(num_attempts) |
|
local order_id = InteractionRand(2147483647, "BobbyRayShop") |
|
local count = 0 |
|
num_attempts = num_attempts or 20 |
|
while g_BobbyRay_CurrentShipments[order_id] do |
|
order_id = InteractionRand(2147483647, "BobbyRayShop") |
|
count = count + 1 |
|
if count > num_attempts then return -1 end |
|
end |
|
return order_id |
|
end |
|
|
|
function BobbyRayShopFinishPurchase() |
|
|
|
local order_id = lGenerateShipmentId() |
|
assert(order_id ~= -1, "Failed to generate shipment id (too many collisions)") |
|
|
|
local due_time = Game.CampaignTime + BobbyRayCartGetDeliveryOption().MinTime * const.Scale.day + InteractionRand((BobbyRayCartGetDeliveryOption().MaxTime - BobbyRayCartGetDeliveryOption().MinTime) * const.Scale.day) |
|
local sector_id = BobbyRayCartGetDeliverySector() |
|
local delivery_option = BobbyRayCartGetDeliveryOption() |
|
|
|
local items_number, total_cost = BobbyRayCartGetAggregate() |
|
|
|
|
|
local shipment_items = {} |
|
local units = BobbyRayCartGetUnits() |
|
|
|
for unit, amount in sorted_pairs(units) do |
|
if amount ~= 0 then |
|
local actual_unit = lGetShopItemFromId(unit) |
|
for _, item in sorted_pairs(actual_unit:GenerateInventoryEntries(amount)) do |
|
table.insert(shipment_items, item) |
|
end |
|
end |
|
end |
|
|
|
local gossip_table = {} |
|
for unit, amount in pairs(units) do |
|
if amount ~= 0 then |
|
local actual_unit = lGetShopItemFromId(unit) |
|
|
|
table.insert(gossip_table,{ |
|
item = actual_unit.class, |
|
cost = BobbyRayStoreGetEntryCost(actual_unit), |
|
used = actual_unit.Used and true or false, |
|
amount = amount, |
|
shop_stack = actual_unit.ShopStackSize and actual_unit.ShopStackSize or 1, |
|
}) |
|
end |
|
end |
|
NetGossip("BobbyRayPurchase", order_id, gossip_table, GetCurrentPlaytime(), Game and Game.CampaignTime) |
|
|
|
|
|
local shipment_context = { sectorId = sector_id, items = shipment_items, order_id = order_id } |
|
AddTimelineEvent( |
|
"bobby_ray_shipment_" .. tostring(order_id), |
|
due_time, |
|
"store_shipment", |
|
shipment_context |
|
) |
|
|
|
|
|
for item_id, amount in pairs(units) do |
|
if amount > 0 then |
|
local item = lGetShopItemFromId(item_id) |
|
if item.Used then |
|
assert(g_BobbyRayStore.used[item.id]) |
|
g_BobbyRayStore.used[item.id] = nil |
|
else |
|
assert(g_BobbyRayStore.standard[item.class] and g_BobbyRayStore.standard[item.class].Stock >= amount) |
|
g_BobbyRayStore.standard[item.class].Stock = g_BobbyRayStore.standard[item.class].Stock - amount |
|
if g_BobbyRayStore.standard[item.class].Stock == 0 then |
|
g_BobbyRayStore.standard[item.class] = nil |
|
g_BobbyRayStore.standard_ids[item.id] = nil |
|
end |
|
end |
|
end |
|
end |
|
|
|
|
|
BobbyRayCartClearEverything() |
|
|
|
|
|
AddMoney(-total_cost, "expense") |
|
|
|
local shipment_details = lBobbyRayAddShipment(Game.CampaignTime, due_time, order_id, shipment_items, sector_id, total_cost, delivery_option) |
|
CombatLog("important",T{624146592949, "<em>Bobby Ray's</em> shipment sent. It will arrive in <em><timeDuration(due_time)></em> in <em><SectorName(sector_id)></em>",order_id = order_id, due_time = due_time - Game.CampaignTime, sector_id = sector_id}) |
|
Msg("BobbyRayShopShipmentSent", shipment_details) |
|
end |
|
|
|
function OnMsg.BobbyRayShopShipmentArrived(shipment_details) |
|
local sectorStash = GetSectorInventory(shipment_details.sector_id) |
|
local itemsCopy = table.copy(shipment_details.items) |
|
if sectorStash then |
|
AddItemsToInventory(sectorStash, itemsCopy) |
|
end |
|
end |
|
|
|
|
|
|
|
function TFormat.BobbyRayEmailItemList(context, items) |
|
return table.concat(table.map(items, function(item) return T{757479034237, "\t<bullet_point> <DisplayName> x <Amount>\n", DisplayName = item.DisplayName, Amount = (item.Amount or 1)} end )) |
|
end |
|
|
|
|
|
|
|
function OnMsg.StartSatelliteGameplay() |
|
lCheckShipments() |
|
BobbyRayCartClearEverything() |
|
end |
|
|
|
function OnMsg.SatelliteTick() |
|
lCheckShipments() |
|
BobbyRayCartClearEverything() |
|
lForgetBobbyRayOrderTabState() |
|
NetSyncEvents.BobbyRayUpdateNew() |
|
end |
|
|
|
function OnMsg.MoneyChanged(amount, logReason, previousBalance) |
|
ObjModified(g_BobbyRayCart) |
|
ObjModified(g_BobbyRayStore) |
|
end |
|
|
|
|
|
local function lCloseBobbyCountdowns() |
|
for d, _ in pairs(g_OpenMessageBoxes) do |
|
if d and d.window_state == "open" and d.context.obj == "bobby-countdown" then |
|
d:Close() |
|
end |
|
end |
|
end |
|
|
|
function NetSyncEvents.FinishBBROrder(mode) |
|
if mode == "clear-store" then |
|
if IsBobbyRayOpen("cart") then OpenBobbyRayPage() end |
|
BobbyRayStoreClear() |
|
ObjModified(g_BobbyRayStore) |
|
elseif mode == "clear-order" then |
|
if IsBobbyRayOpen("cart") then OpenBobbyRayPage() end |
|
BobbyRayCartClearEverything() |
|
ObjModified(g_BobbyRayCart) |
|
elseif mode == "finish-order" then |
|
if IsBobbyRayOpen() then OpenBobbyRayPage() end |
|
ObjModified("BobbyRayShopFinishPurchaseUI") |
|
BobbyRayShopFinishPurchase() |
|
elseif mode == "consume-stock" then |
|
BobbyRayStoreConsumeRandomStock() |
|
ObjModified(g_BobbyRayStore) |
|
ObjModified(g_BobbyRayCart) |
|
elseif mode == "restock" then |
|
BobbyRayStoreRestock() |
|
ObjModified(g_BobbyRayStore) |
|
ObjModified(g_BobbyRayCart) |
|
else |
|
assert("unknown mode:", mode) |
|
end |
|
end |
|
|
|
function NetSyncEvents.CreateTimerBeforeAction(mode) |
|
if not CanYield() then |
|
CreateGameTimeThread(NetSyncEvents.CreateTimerBeforeAction, mode) |
|
return |
|
end |
|
|
|
lCloseBobbyCountdowns() |
|
|
|
local dialog = CreateMessageBox(terminal.desktop, "", "", T(739643427177, "Cancel"), "bobby-countdown") |
|
local reason = "bobby-countdown" |
|
Pause(reason) |
|
PauseCampaignTime(reason) |
|
dialog.OnDelete = function() |
|
Resume(reason) |
|
ResumeCampaignTime(reason) |
|
end |
|
|
|
local countdown_seconds = 3 |
|
dialog:CreateThread("bobby-countdown", function() |
|
if netInGame and table.count(netGamePlayers) > 1 then |
|
local idText = dialog.idMain.idText |
|
local currentCountdown = countdown_seconds |
|
for i = 1, countdown_seconds do |
|
if idText.window_state == "open" then |
|
if mode == "clear-order" then |
|
idText:SetText(T{575279476730, "<center>Clearing order in <u(currentCountdown)>", currentCountdown = currentCountdown}) |
|
elseif mode == "clear-store" then |
|
idText:SetText(T{332391120755, "<center>(DEV-DEBUG)Clearing store in <u(currentCountdown)>", currentCountdown = currentCountdown}) |
|
elseif mode == "finish-order" then |
|
idText:SetText(T{533359561728, "<center>Finishing order in <u(currentCountdown)>", currentCountdown = currentCountdown}) |
|
elseif mode == "restock" then |
|
idText:SetText(T{852189704117, "<center>(DEV-DEBUG)Restocking shop in <u(currentCountdown)>", currentCountdown = currentCountdown}) |
|
elseif mode == "consume-stock" then |
|
idText:SetText(T{202941315408, "<center>(DEV-DEBUG)Consuming random shop stock in <u(currentCountdown)>", currentCountdown = currentCountdown}) |
|
else |
|
idText:SetText(T{233587337306, "<center>Unknown action in <u(currentCountdown)>", currentCountdown = currentCountdown}) |
|
end |
|
else |
|
break |
|
end |
|
|
|
Sleep(1000) |
|
currentCountdown = currentCountdown - 1 |
|
end |
|
end |
|
dialog:Close() |
|
|
|
FireNetSyncEventOnHost("FinishBBROrder", mode) |
|
end) |
|
|
|
WaitMsg(dialog) |
|
local res = dialog.result |
|
if res == "ok" then |
|
NetSyncEvent("CancelBobbyCountdown", mode, netUniqueId) |
|
end |
|
end |
|
|
|
function NetSyncEvents.CancelBobbyCountdown(mode, player_id) |
|
lCloseBobbyCountdowns() |
|
end |
|
|
|
function SavegameSessionDataFixups.BobbyRayTabState(data, meta) |
|
assert(data.gvars.PDABrowserTabState) |
|
if not data.gvars.PDABrowserTabState.bobby_ray_shop then |
|
data.gvars.PDABrowserTabState.bobby_ray_shop = { locked = true } |
|
end |
|
end |
|
|
|
|
|
|
|
DefineClass.ShipmentWindowClass = { |
|
__parents = { "XMapObject", "XContextWindow" }, |
|
ZOrder = 3, |
|
IdNode = true, |
|
ContextUpdateOnOpen = true, |
|
ScaleWithMap = false, |
|
FXMouseIn = "SatelliteBadgeRollover", |
|
FXPress = "SatelliteBadgePress", |
|
FXPressDisabled = "SatelliteBadgeDisabled", |
|
RolloverOffset = box(30, 24, 0, 0), |
|
RolloverBackground = RGBA(255, 255, 255, 0), |
|
PressedBackground = RGBA(255, 255, 255, 0), |
|
|
|
routes_displayed = false, |
|
|
|
route_visible = true |
|
} |
|
|
|
function ShipmentWindowClass:UpdateZoom(prevZoom, newZoom, time) |
|
local map = self.map |
|
local maxZoom = map:GetScaledMaxZoom() |
|
local minZoom = Max(1000 * map.box:sizex() / map.map_size:x(), 1000 * map.box:sizey() / map.map_size:y()) |
|
newZoom = Clamp(newZoom, minZoom + 120, maxZoom) |
|
|
|
XMapWindow.UpdateZoom(self, prevZoom, newZoom, time) |
|
end |
|
|
|
function ShipmentWindowClass:GetTravelPos() |
|
return self:GetVisualPos() |
|
end |
|
|
|
function ShipmentWindowClass:SetVisible(visible) |
|
XMapObject.SetVisible(self, visible) |
|
XContextWindow.SetVisible(self, visible) |
|
|
|
if not self.routes_displayed then return end |
|
self.routes_displayed["main"][1]:SetVisible(visible) |
|
for _1, decoration in pairs(self.routes_displayed["main"].decorations) do |
|
decoration:SetVisible(visible) |
|
end |
|
end |
|
|
|
function ShipmentWindowClass:Close() |
|
XMapObject.Close(self) |
|
if self.window_state == "open" or self.window_state == "closing" then XContextWindow.Close(self) end |
|
|
|
if not self.routes_displayed then return end |
|
self.routes_displayed["main"][1]:Close() |
|
for _1, decoration in pairs(self.routes_displayed["main"].decorations) do |
|
decoration:Close() |
|
end |
|
end |
|
|
|
function CreateBobbyRayShipmentSquad(shipment_details) |
|
local predef_props = { |
|
Side = "player1", |
|
arrival_squad = true, arrival_shipment = true, |
|
shipment = shipment_details, |
|
Name = "Bobby Ray's shipment " .. tostring(shipment_details.order_id), |
|
CurrentSector = shipment_details.sector_id |
|
} |
|
return XTemplateSpawn("BobbyRaySquadWindow", g_SatelliteUI, predef_props) |
|
end |
|
|
|
function ShipmentUIUpdateMovement(shipment_window) |
|
local lateLayoutThread = shipment_window:GetThread("late-layout") |
|
if lateLayoutThread and CurrentThread() ~= lateLayoutThread then |
|
shipment_window:DeleteThread(lateLayoutThread) |
|
end |
|
|
|
shipment_window:DeleteThread("sat-movement") |
|
shipment_window:CreateThread("sat-movement", ArrivingShipmentTravelThread, shipment_window) |
|
end |
|
|
|
function ArrivingShipmentTravelThread(shipment_window) |
|
local shipment = shipment_window.context.shipment |
|
local sectorId = shipment.sector_id |
|
local sY, sX = sector_unpack(sectorId) |
|
local sectorPos = gv_Sectors[sectorId].XMapPosition |
|
local leftMostSectorId = sector_pack(sY, 1) |
|
|
|
local positions, routeSegments = ComputeArrivingPath(leftMostSectorId, sectorId) |
|
|
|
if not shipment.departure_time then |
|
local preset = FindPreset("BobbyRayShopDeliveryDef", shipment.delivery_option) |
|
assert(preset) |
|
shipment.departure_time = shipment.due_time - preset.MaxTime * const.Scale.day |
|
end |
|
|
|
local routeEndDecoration = XTemplateSpawn("SquadRouteDecoration", g_SatelliteUI) |
|
if g_SatelliteUI.window_state == "open" then |
|
routeEndDecoration:Open() |
|
end |
|
routeEndDecoration:SetRouteEnd(point(0, sY), sectorId) |
|
routeEndDecoration:SetColor(GameColors.Player) |
|
|
|
if not shipment_window.routes_displayed then shipment_window.routes_displayed = {} end |
|
shipment_window.routes_displayed["main"] = routeSegments |
|
routeSegments.decorations = { routeEndDecoration } |
|
|
|
local totalTime = shipment.due_time - shipment.departure_time |
|
local timeLeft = shipment.due_time - Game.CampaignTime |
|
DisplayArrivingPathRemainder(totalTime, timeLeft, routeSegments, positions, shipment_window) |
|
end |
|
|
|
|
|
|
|
DefineClass.PDABobbyRayPopupButtonClass = { |
|
__parents = { "PDACommonButtonClass" }, |
|
has_lost_rollover = false |
|
} |
|
|
|
function PDABobbyRayPopupButtonClass:OnLayoutComplete() |
|
if not self.has_lost_rollover then |
|
if not self:MouseInWindow(terminal.GetMousePos()) then |
|
self.has_lost_rollover = true |
|
end |
|
end |
|
PDACommonButtonClass.OnLayoutComplete(self) |
|
end |
|
|
|
function PDABobbyRayPopupButtonClass:SetupCategoryButton() |
|
local dlg = GetDialog(self) |
|
local alignMenuTo = self |
|
local category = BobbyRayShopGetCategory(self:GetContext()) |
|
local categoryId, subcategoryId = BobbyRayShopGetActiveCategoryPair() |
|
local active_category = BobbyRayShopGetCategory(categoryId) |
|
local active_subcategory = BobbyRayShopGetSubCategory(subcategoryId) |
|
local ctxMenu = XTemplateSpawn("PDABrowserBobbyRay_Store_SubCategoryMenu", dlg, { category = category, active_category = active_category, active_subcategory = active_subcategory }) |
|
ctxMenu:SetAnchor(alignMenuTo.box) |
|
ctxMenu:SetMinWidth(self.measure_width) |
|
ctxMenu.button = self |
|
ctxMenu:Open() |
|
self:OnOpenPopupMenu() |
|
self.desktop:SetModalWindow(ctxMenu) |
|
self.has_lost_rollover = false |
|
end |
|
|
|
function PDABobbyRayPopupButtonClass:RolloverCategoryButton(rollover) |
|
if GetUIStyleGamepad() then return end |
|
if rollover then |
|
CreateRealTimeThread(function() |
|
Sleep(1) |
|
self:SetupCategoryButton() |
|
end) |
|
end |
|
end |
|
|
|
function PDABobbyRayPopupButtonClass:OnSetRollover(rollover) |
|
if rollover and self.has_lost_rollover then |
|
self:RolloverCategoryButton(rollover) |
|
elseif not rollover then |
|
self.has_lost_rollover = true |
|
end |
|
PDACommonButtonClass.OnSetRollover(self, rollover) |
|
end |
|
|
|
function PDABobbyRayPopupButtonClass:OnPress(gamepad) |
|
self:SetupCategoryButton() |
|
end |
|
|
|
function PDABobbyRayPopupButtonClass:OnClosePopupMenu() |
|
self:SetColumnsUse("abccd") |
|
if not self:MouseInWindow(terminal.GetMousePos()) then |
|
self.has_lost_rollover = true |
|
end |
|
end |
|
|
|
function PDABobbyRayPopupButtonClass:OnOpenPopupMenu() |
|
self:SetColumnsUse("ccccd") |
|
end |
|
|
|
|
|
|
|
if FirstLoad then |
|
g_PrevRes = point(GetResolution()) |
|
end |
|
function OnMsg.SystemSize(pt) |
|
if g_PrevRes ~= pt then |
|
g_PrevRes = pt |
|
if IsBobbyRayOpen() then |
|
OpenBobbyRayPage() |
|
end |
|
end |
|
end |
|
|