/** Homebase Manage buyable stuff and technology upgrades in Gidl @authors Sven2 */ static g_homebases; local buy_menu; local base_material; // array of base material entries local last_buy_idx; local techs, requirement_names; local is_selling; // temp to prevent recursion from object removal // Technology fields - queried by objects using them local tech_load_speed_multiplier = 100; local tech_shooting_strength_multiplier = 0; local tech_life = 1; static g_quickbuy_items; // Types for purchasable stuff local ITEMTYPE_Weapon = { free_rebuy = true, stack_count = -1 }; local ITEMTYPE_Consumable = { auto_rebuy = true }; local ITEMTYPE_Technology = { remove_after_buy = true, callback = "GainTechnology", extra_width = 2, }; /* Creation / Destruction */ public func Construction(...) { // Manage pointers if (GetOwner() < 0) FatalError("Invalid Homebase owner"); if (!g_homebases) g_homebases = []; if (g_homebases[GetOwner()]) g_homebases[GetOwner()]->RemoveObject(); // remove old (shouldn't be here) g_homebases[GetOwner()] = this; // Init base_material = []; techs = {}; requirement_names = {}; last_buy_idx = -1; // Buy menu buy_menu = CreateObject(GUI_BuyMenu, 0,0, GetOwner()); buy_menu->SetHomebase(this); // Get available items GameCall("FillHomebase", this); // Buy menu always open (but hidden at start) buy_menu->Open(); return true; } public func Destruction() { if (buy_menu) buy_menu->RemoveObject(); return true; } public func SetQuickbuyItems(array list) { g_quickbuy_items = list; } public func AddCaption(string title, array requirements) { return AddHomebaseItem({is_caption = true, title=title, requirements=requirements}); } public func AddHomebaseItem(proplist entry) { // Default name and description if (entry.item) { if (!entry.name) entry.name = entry.item.Name; if (!entry.desc) entry.desc = entry.item.Description; } // Remember tech name mapping if (entry.tech) requirement_names[entry.tech] = entry.name; // Add to end of list var idx = GetLength(base_material); base_material[idx] = entry; var quickbuy_idx = GetIndexOf(g_quickbuy_items, entry.item); if (quickbuy_idx >= 0) entry.hotkey = GetPlayerControlAssignment(GetOwner(), CON_QuickBuy0+quickbuy_idx, true); UpdateIndexedItem(idx); return entry; } public func UpdateIndexedItem(int index) { if (index >= 0 && buy_menu) { var entry = base_material[index]; if (entry.hidden) return true; var available = true; if (entry.requirements) for (var req in entry.requirements) if (!techs[req]) available = false; var tier; if (entry.tiers) { tier = techs[entry.tech]; entry.graphic = Format(entry.graphics, tier+1); entry.cost = entry.costs[tier]; } if (entry.is_caption) return buy_menu->UpdateCaption(entry.title, available, entry, index); else return buy_menu->UpdateBuyEntry(entry.item, available, entry, index, index == last_buy_idx, tier); } return false; } public func GetEntryByID(def id) { for (var i = 0; i < GetLength(base_material); i++) if (base_material[i].item == id) return i; return nil; } public func GetEntryInformation(int entry_idx) { // Fill with current information for this entry var entry = base_material[entry_idx]; // Append (Tier x/y) to name if (entry.tiers) { if (!entry.base_name) entry.base_name = entry.name; var tier = techs[entry.tech]; entry.name = Format("%s ($Tier$ %d/%d)", entry.base_name, tier+1, entry.tiers); } // Compose info message // Info message: Requirements var msg = ""; if (entry.requirements) { var req_str = ""; var req_sep = ""; for (var req in entry.requirements) { var clr = 0xff00; if (!techs[req]) clr = 0xff0000; req_str = Format("%s%s%s", req_str, req_sep, clr, requirement_names[req]); req_sep = ", "; } msg = Format("$Requirements$: %s|", req_str); } else { msg = ""; } // Info message: Cannot afford if (entry.cost > GetWealth(GetOwner())) { msg = Format("%s$Cost$: %d", msg, entry.cost); } entry.message = msg; return entry; } public func OnBuySelection(int callback_idx) { // Buy directly into cursor var plr = GetOwner(); var cursor = GetCursor(plr); if (!cursor) return false; // Safety var entry = base_material[callback_idx]; if (!entry) return false; // Ignore if already have var last_item = cursor->Contents(), item; if (!last_item || (last_item->GetID() != entry.item)) { // Requirements if (entry.requirements) for (var req in entry.requirements) if (!techs[req]) return false; // Cost if (entry.cost) { if (GetWealth(plr) < entry.cost) return false; DoWealth(plr, -entry.cost); Sound("UI::Cash", true, nil, plr); // Some items cost only once if (entry.free_rebuy) entry.cost = nil; } else { // Still some feedback even on free selections Sound("Liquids::Waterdrop1", true, nil, plr); } // Technology? if (entry.callback) { // Teach technology if (!Call(entry.callback, entry)) return false; } else { // Regular item: Buy into inventory // Get rid of current item if (last_item) SellItem(last_item); var item; // Create item if (!item) item = cursor->CreateContents(entry.item); if (!item) return false; // ??? // for later sale item.GidlValue = entry.cost; // ammo up! if (entry.ammo) { var ammo = item->CreateContents(entry.ammo); if (!ammo) return false; var stack_count = entry.stack_count; if (stack_count < 0) ammo->~SetInfiniteStackCount(); else if (stack_count > 0) ammo->SetStackCount(stack_count); } // stack count if (entry.infinite && item) { item->SetInfiniteStackCount(); } } } // Buy only once? (all technologies without further upgrade tiers) if (entry.remove_after_buy) { entry.hidden = true; if (buy_menu) buy_menu->RemoveItem(callback_idx); } else if (entry.tech) { // Multi-tier tech upgrade UpdateIndexedItem(callback_idx); } else { // Non-tech: Remember what has been bought if (last_buy_idx != callback_idx) { var last_last_buy_idx = last_buy_idx; last_buy_idx = callback_idx; if (last_last_buy_idx >= 0) UpdateIndexedItem(last_last_buy_idx); UpdateIndexedItem(last_buy_idx); } } return true; } public func SellItem(item) { // Cash! // Use custom value assigned by buy menu only if (item.GidlValue) { DoWealth(GetOwner(), item.GidlValue); Sound("UI::Cash", true, nil, GetOwner()); } is_selling = true; // no item re-buy during sale var success = item->RemoveObject(); is_selling = false; return success; } // Makes an item available even though the requirements aren't yet met public func SetItemAvailable(int entry_idx) { // Safety var entry = base_material[entry_idx]; if (!entry) return false; entry.requirements = nil; entry.cost = nil; return true; } public func OnOwnerChanged(new_owner) { if (buy_menu) buy_menu->SetOwner(new_owner); return true; } // Callback from clonk or weapon: Ammo has been used up. // Recharge from selected home base item if that option has been enabled public func OnNoAmmo(object clonk) { if (is_selling) return false; if (last_buy_idx < 0) return false; var entry = base_material[last_buy_idx]; if (entry && entry.auto_rebuy) { ScheduleCall(this, this.OnBuySelection, 1, 1, last_buy_idx); } return false; } public func QuickBuyItem(id item) { // Find item in buy list var entry, i=0; for (entry in base_material) if (entry.item == item) break; else ++i; // If found, try to buy it if (!OnBuySelection(i)) { // TODO: Error sound return false; } return true; } private func GainTechnology(proplist entry) { // Reach next tier var tier = techs[entry.tech] + 1; techs[entry.tech] = tier; // For multi-tier-technologies, remove entry when last tier has been reached if (entry.tiers) entry.remove_after_buy = (tier == entry.tiers); // Technology gain callback. Call(Format("~Gain%s", entry.tech), entry, tier); // Update any related techs that may become available var n = GetLength(base_material); for (var i=0; i= 0) UpdateIndexedItem(i); } return true; } private func GainAdvancedWeapons(proplist entry, int tier) { // All done by requirements return true; } private func GainLoadSpeed(proplist entry, int tier) { // Increase player's load speed tech_load_speed_multiplier = [100, 40, 20, 1][tier]; // Update all current weapons for (var weapon in FindObjects(Find_Owner(GetOwner()), Find_Func("Gidl_IsRangedWeapon"))) weapon->Gidl_UpdateLoadTimes(); return true; } private func GainShootingStrength(proplist entry, int tier) { // Increase player's shooting strength // Weapon get plus x percent shooting strength tech_shooting_strength_multiplier = [10, 20, 30, 40][tier]; // Update all current weapons for (var weapon in FindObjects(Find_Owner(GetOwner()), Find_Func("Gidl_IsRangedWeapon"))) weapon->Guardians_UpdateShootingStrength(); return true; } private func GainLife(proplist entry, int tier) { // Increase player's life tech_life = [1, 5, 10, 20][tier]; // Full refresh and max increase on current value for (var clonk in FindObjects(Find_Owner(GetOwner()), Find_Func("IsClonk"))) { clonk.MaxEnergy = clonk.Prototype.MaxEnergy * tech_life; clonk->DoEnergy(clonk.MaxEnergy, true); } return true; }