From 4e6a157071b4fedaf4ad23d8cb7c2234633b3d47 Mon Sep 17 00:00:00 2001 From: Alberth Date: Sun, 4 Jan 2015 20:23:10 +0100 Subject: [PATCH] -Add: Save and load of goalies. Fixes #7366. --- company.nut | 103 +++++++++++++++++++++++++++++++++++++++++++++------- info.nut | 1 + main.nut | 88 ++++++++++++++++++++++++++++++++++++-------- 3 files changed, 164 insertions(+), 28 deletions(-) diff --git a/company.nut b/company.nut index f3393b9..f16ea0d 100644 --- a/company.nut +++ b/company.nut @@ -12,25 +12,33 @@ class CompanyGoal { displayed_string = null; displayed_count = null; + // Construct a company goal. + // @param comp_id Company owning the goal. Use \c null if no OpenTTD goal should be created. + // @param cargo Cargo that should be delivered. + // @param accept Accepting resource. + // @param wanted_amount Amount of cargo that should be delivered to fulfil the goal. constructor(comp_id, cargo, accept, wanted_amount) { this.cargo = cargo; this.accept = accept; this.wanted_amount = wanted_amount; this.timeout = 5 * 365 * 74; // About 5 years. - // Construct goal. - local destination, destination_string, goal_type; - if ("town" in this.accept) { - destination = accept.town; - destination_string = GSText(GSText.STR_TOWN_NAME, destination); - goal_type = GSGoal.GT_TOWN; - } else { - destination = accept.ind; - destination_string = GSText(GSText.STR_INDUSTRY_NAME, destination); - goal_type = GSGoal.GT_INDUSTRY; + // Construct goal if a company id was provided. + if (comp_id != null) { + local destination, destination_string, goal_type; + if ("town" in this.accept) { + destination = accept.town; + destination_string = GSText(GSText.STR_TOWN_NAME, destination); + goal_type = GSGoal.GT_TOWN; + } else { + destination = accept.ind; + destination_string = GSText(GSText.STR_INDUSTRY_NAME, destination); + goal_type = GSGoal.GT_INDUSTRY; + } + local goal_text = GSText(GSText.STR_COMPANY_GOAL, cargo.cid, + this.wanted_amount, destination_string); + this.goal_id = GSGoal.New(comp_id, goal_text, goal_type, destination); } - local goal_text = GSText(GSText.STR_COMPANY_GOAL, cargo.cid, this.wanted_amount, destination_string); - this.goal_id = GSGoal.New(comp_id, goal_text, goal_type, destination); } function AddMonitorElement(mon); @@ -38,8 +46,36 @@ class CompanyGoal { function UpdateTimeout(step); function CheckFinished(); function FinalizeGoal(); + + function SaveGoal(); + static function LoadGoal(num, loaded_data); }; +function CompanyGoal::SaveGoal() +{ + return {cid=this.cargo.cid, accept=this.accept, wanted=this.wanted_amount, + delivered=this.delivered_amount, goal=this.goal_id, timeout=this.timeout}; +} + +// Load an existing goal. +// @param loaded_data Data of the goal. +// @param cargoes Cargoes of the game. +// @return The loaded goal, if loading went ok. +function CompanyGoal::LoadGoal(loaded_data, cargoes) +{ + local goal = null; + foreach (cargo_num, cargo in cargoes) { + if (cargo.cid == loaded_data.cid) { + goal = CompanyGoal(null, cargo, loaded_data.accept, loaded_data.wanted); + goal.delivered_amount = loaded_data.delivered; + goal.goal_id = loaded_data.goal; + goal.timeout = loaded_data.timeout; + return goal; + } + } + return null; +} + // Add an entry to the collection of monitored things. // @param [inout] mon Table with 'cargo_id' to 'town' and 'ind' tables, holding ids to 'null'. function CompanyGoal::AddMonitorElement(mon) @@ -216,6 +252,7 @@ class CompanyData { function FinalizeCompany(); + function GoalsPostLoadCheck(); function GetMissingGoalCount(); function AddActiveGoal(cargo, accept, amount); function HasGoal(cargo_id, accept); @@ -226,8 +263,36 @@ class CompanyData { function UpdateDelivereds(cmon); function UpdateTimeout(step); function CheckAndFinishGoals(); + + function SaveCompany(); + static function LoadCompany(comp_id, loaded_data); }; +// Save company data. +function CompanyData::SaveCompany() +{ + local result = {}; + foreach (num, goal in this.active_goals) { + if (goal == null) continue; + result[num] <- goal.SaveGoal(); + } + return result; +} + +// Load company data from the file, constructing a new company. +// @param comp_id Company id. +// @param loaded_data Data to load for this company. +// @param cargoes Cargoes of the game. +// @return The created company. +function CompanyData::LoadCompany(comp_id, loaded_data, cargoes) +{ + local cdata = CompanyData(comp_id); + foreach(num, loaded_goal_data in loaded_data) { + cdata.active_goals[num] = CompanyGoal.LoadGoal(loaded_goal_data, cargoes); + } + return cdata; +} + // Company is about to be deleted, last chance to clean up. function CompanyData::FinalizeCompany() { @@ -239,7 +304,6 @@ function CompanyData::FinalizeCompany() } } - // Find the number of active goals that are missing for this company. // @return Number of additional goals that the company needs. function CompanyData::GetMissingGoalCount() @@ -310,6 +374,19 @@ function CompanyData::IndustryClosed(ind_id) } } +// Game data was just loaded, check whether the goals make sense. +function CompanyData::GoalsPostLoadCheck() +{ + // Check whether the industries still live. + foreach (num, goal in this.active_goals) { + if (goal == null) continue; + if ("ind" in goal.accept && !GSIndustry.IsValidIndustry(goal.accept.ind)) { + goal.FinalizeGoal(); + this.active_goals[num] = null; + } + } +} + // Add monitor elements of a company, if they exist. // @param [inout] cmon Monitors of all companies, updated in-place. // Result is 'comp_id' number to 'cargo_id' numbers to 'ind' and/or 'town' indices to 'null' diff --git a/info.nut b/info.nut index 0235890..cbd4ffe 100644 --- a/info.nut +++ b/info.nut @@ -27,6 +27,7 @@ class BusyBeeInfo extends GSInfo { function GetShortName() { return "BBEE"; } function GetAPIVersion() { return "1.5"; } function GetUrl() { return ""; } + function MinVersionToLoad() { return this.GetVersion(); } function GetSettings(); } diff --git a/main.nut b/main.nut index cfb048d..5a8808f 100644 --- a/main.nut +++ b/main.nut @@ -26,12 +26,40 @@ class BusyBeeClass extends GSController companies = null; + loaded = false; + + function Load(version, data); + function Save(); function Start(); } -// Examine and store cargo types of the game. -function BusyBeeClass::ExamineCargoes() +function BusyBeeClass::Load(version, data) { + this.loaded = true; + this.Initialize(); + + foreach (comp_id, loaded_comp_data in data) { + local cdata = CompanyData.LoadCompany(comp_id, loaded_comp_data, this.cargoes); + this.companies[comp_id] = cdata; + } +} + +function BusyBeeClass::Save() +{ + local result = {}; + foreach (comp_id, cdata in this.companies) { + if (cdata == null) continue; + result[comp_id] <- cdata.SaveCompany(); + } + return result; +} + +// Initialize core data of the script. +function BusyBeeClass::Initialize() +{ + if (this.companies != null) return; // Already initialized. + + // Examine and store cargo types of the game. this.cargoes = {}; this.num_cargoes = 0; @@ -42,6 +70,12 @@ function BusyBeeClass::ExamineCargoes() this.cargoes[this.num_cargoes] <- cargo; this.num_cargoes += 1; } + + // Construct empty companies. + this.companies = {}; + for (local comp_id = GSCompany.COMPANY_FIRST; comp_id <= GSCompany.COMPANY_LAST; comp_id++) { + this.companies[comp_id] <- null; + } } // Find cargo sources. @@ -251,7 +285,7 @@ function BusyBeeClass::ProcessEvents() } else if (event_type == GSEvent.ET_INDUSTRY_CLOSE) { local ind_id = GSEventIndustryClose.Convert(event).GetIndustryID(); - foreach (comp_id, cdata in companies) { + foreach (comp_id, cdata in this.companies) { if (cdata != null) cdata.IndustryClosed(ind_id); } } @@ -268,7 +302,7 @@ function BusyBeeClass::TryAddNewGoal() local total_missing = 0; // Total number of missing goals. local best_comp_id = null; local comp_id_missing = 0; - foreach (comp_id, cdata in companies) { + foreach (comp_id, cdata in this.companies) { if (cdata == null) continue; local missing = cdata.GetMissingGoalCount(); total_missing += missing; @@ -299,7 +333,7 @@ function BusyBeeClass::UpdateDeliveries(old_cmon) local cmon = {}; // Collect monitors that are of interest. - foreach (comp_id, cdata in companies) { + foreach (comp_id, cdata in this.companies) { if (cdata == null) continue; cdata.AddMonitorElements(cmon); } @@ -308,7 +342,7 @@ function BusyBeeClass::UpdateDeliveries(old_cmon) // Distribute the retrieved data. local finished = false; - foreach (comp_id, cdata in companies) { + foreach (comp_id, cdata in this.companies) { if (cdata == null) continue; if (cdata.UpdateDelivereds(cmon)) finished = true; } @@ -319,23 +353,47 @@ function BusyBeeClass::UpdateDeliveries(old_cmon) return {cmon=cmon, finished_goals=finished}; } +// The script data got loaded from file, check it against the game. +function BusyBeeClass::CompaniesPostLoadCheck() +{ + // Check companies. + for (local comp_id = GSCompany.COMPANY_FIRST; comp_id <= GSCompany.COMPANY_LAST; comp_id++) { + if (GSCompany.ResolveCompanyID(comp_id) == GSCompany.COMPANY_INVALID) { + if (this.companies[comp_id] != null) { + this.companies[comp_id].FinalizeCompany(); + this.companies[comp_id] = null; + GSLog.Info("Deleted company " + comp_id + " (disappeared after loading)."); + } + } else { + if (this.companies[comp_id] == null) { + this.companies[comp_id] = CompanyData(comp_id); + GSLog.Info("Created company " + comp_id + " (appeared from nowhere after loading)."); + } + } + } + + // Check industries used for goals. + foreach (comp_id, cdata in this.companies) { + if (cdata == null) continue; + cdata.GoalsPostLoadCheck(); + } +} + function BusyBeeClass::Start() { + this.Initialize(); this.Sleep(1); // Wait for the game to start. - this.ExamineCargoes(); - - // Construct empty companies. - this.companies = {}; - for (local comp_id = GSCompany.COMPANY_FIRST; comp_id <= GSCompany.COMPANY_LAST; comp_id++) { - this.companies[comp_id] <- null; + local cmonitor = null; + if (this.loaded) { // Script data was loaded. + this.CompaniesPostLoadCheck(); + cmonitor = {}; // Don't kill existing monitors after loading. } // Main event loop. local new_goal_timeout = 0; local finished_timeout = 0; local monitor_timeout = 0; - local cmonitor = null; while (true) { local result = this.ProcessEvents(); if (result.force_goal) new_goal_timeout = 0; @@ -363,7 +421,7 @@ function BusyBeeClass::Start() // Check for finished goals, and remove them if they exist. if (finished_timeout <= 0) { - foreach (comp_id, cdata in companies) { + foreach (comp_id, cdata in this.companies) { if (cdata == null) continue; cdata.CheckAndFinishGoals(); } @@ -389,7 +447,7 @@ function BusyBeeClass::Start() // Update timeout of the goals as well. if (!GSGame.IsPaused()) { - foreach (comp_id, cdata in companies) { + foreach (comp_id, cdata in this.companies) { if (cdata == null) continue; cdata.UpdateTimeout(delay_time); }