Index: binaries/data/mods/public/maps/scripts/Tutorial.js =================================================================== --- binaries/data/mods/public/maps/scripts/Tutorial.js +++ binaries/data/mods/public/maps/scripts/Tutorial.js @@ -1,6 +1,5 @@ Trigger.prototype.InitTutorial = function(data) { - this.count = 0; this.index = 0; this.fullText = ""; this.tutorialEvents = []; @@ -16,6 +15,10 @@ { if (typeof goal[key] !== "function" || this.tutorialEvents.indexOf(key) != -1) continue; + if (key === "Init") + continue; + if (key === "IsDone") + continue; let action = key + "Trigger"; this.RegisterTrigger(key, action, { "enabled": false }); this.tutorialEvents.push(key); @@ -32,16 +35,23 @@ let goal = this.tutorialGoals[this.index]; let needDelay = true; let readyButton = false; + + Trigger.prototype.Init = goal.Init || null; + if (!deserializing && this.Init) + this.Init(); + + Trigger.prototype.IsDone = goal.IsDone || (() => false); + let goalAlreadyDone = this.IsDone(); + for (let event of this.tutorialEvents) { let action = event + "Trigger"; - if (goal[event]) + if (!goalAlreadyDone && goal[event]) { Trigger.prototype[action] = goal[event]; this.EnableTrigger(event, action); needDelay = false; - if (!deserializing) - this.count = 0; + } else this.DisableTrigger(event, action); @@ -54,7 +64,7 @@ { this.EnableTrigger("OnPlayerCommand", "OnPlayerCommandTrigger"); Trigger.prototype.OnPlayerCommandTrigger = function(msg) - { + { if (msg.cmd.type == "dialog-answer" && msg.cmd.tutorial && msg.cmd.tutorial == "ready") this.NextGoal(); }; Index: binaries/data/mods/public/maps/tutorials/starting_economy_walkthrough.js =================================================================== --- binaries/data/mods/public/maps/tutorials/starting_economy_walkthrough.js +++ binaries/data/mods/public/maps/tutorials/starting_economy_walkthrough.js @@ -118,14 +118,26 @@ }, { "instructions": markForTranslation("Let's wait for the units to be trained.\nIn the meantime, we seem to have enough workers gathering Wood. We should remove the current rally-point of the Civic Center away from gathering Wood. For that purpose, right-click on the Civic Center when it is selected (and the flag icon indicating the rally-point is crossed out)."), + "Init": function() + { + this.trainingDone = false; + }, "OnPlayerCommand": function(msg) { if (msg.cmd.type == "unset-rallypoint") this.NextGoal(); + }, + "OnTrainingFinished": function(msg) + { + this.trainingDone = true; } }, { "instructions": markForTranslation("The units should be ready soon.\nIn the meantime, direct your attention to your population count on the top panel. It is the fifth item from the left, after the resources. It would be prudent to keep an eye on it. It indicates your current population (including those being trained) and the current population limit, which is determined by your built structures."), + "IsDone": function(msg) + { + return this.trainingDone; + }, "OnTrainingFinished": function(msg) { this.NextGoal(); @@ -136,11 +148,26 @@ }, { "instructions": markForTranslation("Select two of your newly trained Female Citizens and ask them to build these houses in the empty space to the east of the Civic Center. To do so, after selecting the Female Citizens, click on the house icon in the bottom right panel and shift-click on the position in the map where you want to build the first house followed by a shift-click on the position of the second house (when giving orders, shift-click put the order in the queue and the units will automatically switch to the next order in their queue when they finish their work). Press Escape to get rid of the house cursor so you don't spam houses all over the map.\nReminder: to select only two Female Citizens, click on the first one and then shift-click on the second one to add her to the selection."), + "Init": function() + { + this.houseGoal = new Set(); + }, + "IsDone": function() + { + return this.houseGoal.size > 1; + }, "OnPlayerCommand": function(msg) { - if (msg.cmd.type == "construct" && msg.cmd.template == "structures/athen_house" && - ++this.count == 2) + if (msg.cmd.type == "repair" && msg.cmd.target && Engine.QueryInterface(+msg.cmd.target, IID_Foundation) && + TriggerHelper.EntityHasClass(+msg.cmd.target, "House")) + this.houseGoal.add(+msg.cmd.target); + if (this.IsDone()) this.NextGoal(); + }, + "OnStructureBuilt": function(msg) + { + if (this.houseGoal.has(+msg.foundation)) + this.houseGoal.delete(+msg.foundation); } }, { @@ -152,15 +179,26 @@ { if (msg.cmd.type == "construct" && msg.cmd.template == "structures/athen_farmstead") this.NextGoal(); + }, + "OnStructureBuilt": function(msg) + { + if (this.houseGoal.has(+msg.foundation)) + this.houseGoal.delete(+msg.foundation); } }, { "instructions": markForTranslation("When the farmstead construction is finished, its builders will automatically look for food, and in this case, they will go after the nearby goats.\nBut your house builders will only look for something else to build and, if nothing found, become idle. Let's wait for them to build the houses."), + "IsDone": function() + { + return !this.houseGoal.size; + }, "OnStructureBuilt": function(msg) { - if (TriggerHelper.EntityHasClass(msg.building, "House") && ++this.count == 2) + if (this.houseGoal.has(+msg.foundation)) + this.houseGoal.delete(+msg.foundation); + if (this.IsDone()) this.NextGoal(); - }, + } }, { "name": "buildField", @@ -172,11 +210,11 @@ } }, { - "instructions": markForTranslation("When the field is constructed, the builders will automatically start gathering it.\nThe cavalry unit should have slaughtered all chicken by now. Select it and right-click on one of the goats in the west to start hunting them for food."), + "instructions": markForTranslation("When the field is constructed, the builders will automatically start gathering it.\nThe cavalry unit should have slaughtered all chicken by now. Select it and explore the south-west area: there is a lake with some camels around. Move your cavalry by right-clicking on the point you want to go, and when you see a herd of camels, right-click on one of them to start hunting for food."), "OnPlayerCommand": function(msg) { if (msg.cmd.type == "gather" && msg.cmd.target && - TriggerHelper.GetResourceType(msg.cmd.target).specific == "meat") + TriggerHelper.GetResourceType(msg.cmd.target).specific == "meat") this.NextGoal(); } }, @@ -196,6 +234,10 @@ }, { "instructions": markForTranslation("Now click three times on the female icon in the bottom right panel to train three additional farmers."), + "Init": function(msg) + { + this.femaleCount = 0; + }, "OnTrainingQueued": function(msg) { if (msg.unitTemplate != "units/athen_support_female_citizen" || +msg.count != 1) @@ -203,24 +245,35 @@ let entity = msg.trainerEntity; let cmpProductionQueue = Engine.QueryInterface(entity, IID_ProductionQueue); cmpProductionQueue.ResetQueue(); - let txt = +msg.count == 1 ? - markForTranslation("Do a simple left-click to produce a single unit.") : + let txt = +msg.count != 1 ? + markForTranslation("Do not shift-click to produce a single unit.") : markForTranslation("Click on the FEMALE CITIZEN icon."); this.WarningMessage(txt); return; } - if (++this.count < 3) - return; - this.NextGoal(); + if (++this.femaleCount == 3) + this.NextGoal(); } }, { "instructions": markForTranslation("You can increase the gather rates of your workers by researching new technologies available in some buildings.\nThe farming rate, for example, can be improved with a researchable technology in the farmstead. Select the farmstead and look at its production panel on the bottom right. You will see several researchable technologies. Hover the mouse over them to see their costs and effects and click on the one you want to research."), + "IsDone": function() + { + let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); + let playerEnt = cmpPlayerManager.GetPlayerByID(1); + let cmpTechnologyManager = Engine.QueryInterface(playerEnt, IID_TechnologyManager); + return cmpTechnologyManager && (cmpTechnologyManager.IsTechnologyQueued("gather_wicker_baskets") || + cmpTechnologyManager.IsTechnologyStarted("gather_wicker_baskets") || + cmpTechnologyManager.IsTechnologyResearched("gather_wicker_baskets")) + && (cmpTechnologyManager.IsTechnologyQueued("gather_farming_plows") || + cmpTechnologyManager.IsTechnologyStarted("gather_farming_plows") || + cmpTechnologyManager.IsTechnologyResearched("gather_farming_plows")); + }, "OnResearchQueued": function(msg) { if (msg.technologyTemplate && TriggerHelper.EntityHasClass(msg.researcherEntity, "Farmstead")) this.NextGoal(); - } + }, }, { "instructions": markForTranslation("We should start preparing to phase up into 'Town Phase', which will unlock many more units and buildings. Select the Civic Center and hover the mouse over the 'Town Phase' icon to see what is still needed.\nWe now have enough resources, but one structure is missing. Although this is an economic tutorial, it is nonetheless useful to be prepared for defense in case of attack, so let's build barracks.\nSelect four of your soldiers and ask them to build a barracks: as before, start selecting the soldiers, click on the barracks icon in the production panel and then lay down a foundation not far from your Civic Center where you want to build."), @@ -241,9 +294,23 @@ }, { "instructions": markForTranslation("You should now be able research 'Town Phase'. Select the Civic Center and click on the technology icon.\nIf you still miss some resources (icon with red overlay), wait for them to be gathered by your workers."), + "IsDone": function() + { + let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); + let playerEnt = cmpPlayerManager.GetPlayerByID(1); + let cmpTechnologyManager = Engine.QueryInterface(playerEnt, IID_TechnologyManager); + return cmpTechnologyManager && (cmpTechnologyManager.IsTechnologyQueued("phase_town_athen") || + cmpTechnologyManager.IsTechnologyStarted("phase_town_athen") || + cmpTechnologyManager.IsTechnologyResearched("phase_town_athen")); + }, "OnResearchQueued": function(msg) { - if (msg.technologyTemplate && TriggerHelper.EntityHasClass(msg.researcherEntity, "CivilCentre")) + if (msg.technologyTemplate && TriggerHelper.EntityHasClass(msg.researcherEntity, "CivilCentre") || this.IsDone()) + this.NextGoal(); + }, + "OnResearchFinished": function(msg) + { + if (msg.tech == "phase_town_athen") this.NextGoal(); } }, @@ -252,20 +319,36 @@ }, { "instructions": markForTranslation("Thus, we should order them to deposit their Wood in the Civic Center along the way. To do so, we will queue orders with shift-click: select your soldiers, shift-right-click on the Civic Center to deposit their Wood and then shift-right-click on the Stone mine to gather it.\nPerform a similar order queue with the remaining soldiers and the Metal mine in the west."), + "Init": function() + { + this.stone = false; + this.metal = false; + }, + "IsDone": function() + { + if (!this.stone || !this.metal) + return false; + let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); + let playerEnt = cmpPlayerManager.GetPlayerByID(1); + let cmpTechnologyManager = Engine.QueryInterface(playerEnt, IID_TechnologyManager); + return cmpTechnologyManager && cmpTechnologyManager.IsTechnologyResearched("phase_town_athen"); + + }, "OnPlayerCommand": function(msg) { - if (msg.cmd.type == "gather" && msg.cmd.target && - TriggerHelper.GetResourceType(msg.cmd.target).generic == "stone" && - ++this.count == 3) - this.NextGoal(); - if (msg.cmd.type == "gather" && msg.cmd.target && - TriggerHelper.GetResourceType(msg.cmd.target).generic == "metal" && - ++this.count == 3) + if (msg.cmd.type == "gather" && msg.cmd.target) + { + if (TriggerHelper.GetResourceType(msg.cmd.target).generic == "stone") + this.stone = true; + else if (TriggerHelper.GetResourceType(msg.cmd.target).generic == "metal") + this.metal = true; + } + if (this.IsDone()) this.NextGoal(); }, "OnResearchFinished": function(msg) { - if (msg.tech == "phase_town_athen" && ++this.count == 3) + if (this.IsDone()) this.NextGoal(); } }, Index: binaries/data/mods/public/simulation/components/TechnologyManager.js =================================================================== --- binaries/data/mods/public/simulation/components/TechnologyManager.js +++ binaries/data/mods/public/simulation/components/TechnologyManager.js @@ -89,9 +89,19 @@ return true; }; +TechnologyManager.prototype.IsTechnologyQueued = function(tech) +{ + return this.researchQueued[tech] !== undefined; +}; + TechnologyManager.prototype.IsTechnologyResearched = function(tech) { - return (this.researchedTechs[tech] !== undefined); + return this.researchedTechs[tech] !== undefined; +}; + +TechnologyManager.prototype.IsTechnologyStarted = function(tech) +{ + return this.researchStarted[tech] !== undefined; }; // Checks the requirements for a technology to see if it can be researched at the current time Index: binaries/data/mods/public/simulation/components/Trigger.js =================================================================== --- binaries/data/mods/public/simulation/components/Trigger.js +++ binaries/data/mods/public/simulation/components/Trigger.js @@ -229,7 +229,7 @@ Trigger.prototype.OnGlobalConstructionFinished = function(msg) { - this.CallEvent("StructureBuilt", { "building": msg.newentity }); + this.CallEvent("StructureBuilt", { "building": msg.newentity, "foundation": msg.entity }); }; Trigger.prototype.OnGlobalTrainingFinished = function(msg)