BuildFinished event fires twice

PawelS

Ancient Druid
Joined
Dec 11, 2003
Messages
2,811
Location
Poland
I created a function that generates a new resource, when a Great Person constructs a special improvement. That improvement is removed and the resource appears somewhere on the map.

I used the BuildFinished event for this, but it behaves in a weird way: it fires twice, and both instances seem to be executed at the same time. So when I added a check if the improvement still exists, both instances of the event detect that it does. The result is that two resources instead of one are created, which is not what I wanted to do.

Or perhaps they aren't really executed at the same time, but Plot:SetImprovementType has a delayed effect, so it doesn't change the improvement on the plot before the event fires again. Anyway, both events report that the improvement still exists on the plot, despite the fact that they both remove it.

Is there a way to avoid this problem? (I can post my Lua code if needed, although it will require some changes to run without the entire mod, as it uses some new tables in the database, and new columns in existing tables.)
 
BuildFinished does indeed fire twice for every single Great Person created improvement. I've tested and seen this happening for Academies, Manufactories, Customs Houses, Citadels, etc. I'm not too sure why, but it's simple enough to work around it.

All you need to do is add a check/toggle to your Lua function to switch a global back and forth.

In the case of my Civ, I have my function check for the toggle being false, run its instructions, then toggle it to "true" at the end. When it fires a second time, it'll skip the whole block, and set it back to false. This prepares it for the next time it's triggered.
 
Thanks for the idea :) It will work properly only if the event always fires twice, but all my tests so far suggest that it does, so it looks like a good solution.
 
It fires twice only for great person improvements, as far as I can tell.
It will fire a single time for all other normally-built improvements by workers.

It should be enough to capture when BuildFinished has fired for your specific great person's improvement ID and have that be the wrapper around the toggle check. That way, when it fires twice for the great person improvement, it'll toggle the variable, but farms and such won't touch it. The way I have it done is that if BuildFinished fires for a Citadel, it runs a separate Citadel Handler function which does the actual toggle check, plus instructions.
 
Thanks again, I did something similar as you described and it works properly now.
 
Or perhaps they aren't really executed at the same time

They are, the BuildFinished event fires at the end of the CvvPlot::changeBuildProgress() method and the code in the CvUnit::build() method will call it twice

Code:
  // if we are starting something new wipe out the old thing immediately
  int iStartedYet = pPlot->getBuildProgress(eBuild);
  if(iStartedYet == 0) {
    // SNIP!
	
    // wipe out all build progress also
    bFinished = pPlot->changeBuildProgress(eBuild, workRate(false), getOwner());
  }

  bFinished = pPlot->changeBuildProgress(eBuild, workRate(false), getOwner());
 
That would explain why the second event "sees" that the improvement still exists after the first event removes it - it seems that every call to changeBuildProgress (in case of builds that require no time) "completes" the build (places the improvement on the map), and then fires the event.
 
Top Bottom