Thursday, April 17, 2014

Spawning the Trees

Tree Seeds and Simulation

In the real world a tree grows new saplings in a number of ways: seeds carried off by wind, dropping seeds nearby, birds eating seeds and dropping them elsewhere or fruit eaten by animals and pooped somewhere else. Poop is really really important. In fact, human caused extinction of megafauna has caused serious ecological problems due to lack of pooping by the big animals. Anyway, rather than a full simulation in Cultura (although one day I hope that there is a checkbox in the game to have it run in full simulation mode versus game simulation mode), it has a very simple heat map to decide where the next tree grows.

But wait! How do we decide when something grows in the first place? Well, we head off into XML land where all the resources are defined. A particular material such as Pine has stats that say "you grow during winter, at a rate of 100 wood per season and start off with 200 wood". Then when the game is in winter, every Pine tree grows at 100 wood up to 200 wood. That's for level one pine trees. Resource nodes can be upgraded if you get the right technology thereby increasing the ecosystems carrying capacity for your society's greater consumption at its later stages. So then when a Pine tree reaches its maximum capacity for wood, it then adds any additional growth to a tracked spillover variable. When the spillover goes over a certain threshold a new tree is born!

Okay so where to put the tree? Well here's the simplest way. We put the tree at any square with the highest number of similar neighbouring resource nodes. We'll want to track the objects via what is basically a priority queue, with the most important queue being the list of locations with the highest number of neighbouring resource nodes.

  std::unordered_map<std::pair<int,int>, std::unordered_map<int,int>, hash_int_pair> m_heatMapByLocation; //location to (material id to neighbour node count)
  std::vector<std::unordered_map<int, std::unordered_set<std::pair<int,int>, hash_int_pair> > > m_heatMap; // neighbour node count to (material id to location)

And it's very simple, we just choose the first location with the highest number of neighbours because "good enough". Really, it's just a matter of whether it looks right to the player and only in full simulation mode (which probably won't be coded for a long time) will the game care about doing it how it is done in real life.

std::pair<int,int> CLandRegion::getFirstHottestSquare(CGameState* gameState, int materialId)
{
 for (int i = m_heatMap.size() - 1; i >= 0; i--)
 {
  if (m_heatMap[i].count(materialId) < 1 || m_heatMap[i][materialId].size() < 1)
  {
   continue;
  }
  else
  {
   for (std::unordered_set<std::pair<int,int>, hash_int_pair>::iterator iter = m_heatMap[i][materialId].begin(); iter != m_heatMap[i][materialId].end(); iter++)
   {
    //todo: different passability for special resource nodes, such as in rock or in water or shallow water
    if (false == gameState->isPassable(*iter))
    {
     continue;
    }

    return *iter;
   }   
  }
 }

 return std::pair<int,int>(-1,-1);
}

No comments:

Post a Comment