NS/main/source/mod/AvHTechTree.cpp

604 lines
17 KiB
C++
Raw Normal View History

//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. =========
//
// The copyright to the contents herein is the property of Charles G. Cleveland.
// The contents may be used and/or copied only with the written permission of
// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose:
//
// $Workfile: AvHTechTree.cpp $
// $Date: 2002/09/23 22:36:08 $
//
//===============================================================================
#include "mod/AvHTechTree.h"
bool AvHSHUGetIsResearchTech(AvHMessageID inMessageID);
AvHTechTree::AvHTechTree(void) { Init(); }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
AvHTechTree::AvHTechTree(const AvHTechTree& other)
{
//because pointers in mNodesByMsg are deleted by
//destruction, we need to make our own copy on
//construction
TechNodeMap::const_iterator current, end = other.mNodesByMsg.end();
for( current = other.mNodesByMsg.begin(); current != end; ++current )
{
mNodesByMsg[current->first] = current->second->clone();
}
Init();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
AvHTechTree::~AvHTechTree(void) { Clear(); }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void AvHTechTree::Init(void)
{
#ifdef SERVER
BALANCE_LISTENER(this);
compoundChangeInProgress = false;
#endif
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void AvHTechTree::swap(AvHTechTree& other)
{
TechNodeMap tempMap = mNodesByMsg; mNodesByMsg = other.mNodesByMsg; other.mNodesByMsg = tempMap;
TechIDMap tempIDMap = mNodesByTech; mNodesByTech = other.mNodesByTech; other.mNodesByTech = tempIDMap;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void AvHTechTree::InsertNode(const AvHTechNode* inTechNode)
{
this->RemoveNode(inTechNode->getMessageID()); //remove old node to prevent memory leak
this->mNodesByMsg[inTechNode->getMessageID()] = inTechNode->clone();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void AvHTechTree::RemoveNode(const AvHMessageID inMessageID)
{
AvHTechNode* node = GetNode(inMessageID);
if( node )
{
mNodesByMsg.erase(node->getMessageID());
//remove from tech map if it's a match before we delete it!
TechIDMap::iterator item = mNodesByTech.find(node->getTechID());
if( item != mNodesByTech.end() && item->second == node )
{ mNodesByTech.erase(item); }
delete node;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void AvHTechTree::Clear(void)
{
TechNodeMap::iterator current, end = mNodesByMsg.end();
for( current = mNodesByMsg.begin(); current != end; ++current )
{
delete current->second;
}
mNodesByMsg.clear();
mNodesByTech.clear();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
AvHTechNode* AvHTechTree::GetNode(const AvHMessageID message)
{
AvHTechNode* returnVal = NULL;
TechNodeMap::iterator item = mNodesByMsg.find(message);
if( item != mNodesByMsg.end() )
{ returnVal = item->second; }
return returnVal;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const AvHTechNode* AvHTechTree::GetNode(const AvHMessageID message) const
{
const AvHTechNode* returnVal = NULL;
TechNodeMap::const_iterator item = mNodesByMsg.find(message);
if( item != mNodesByMsg.end() )
{ returnVal = item->second; }
return returnVal;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
AvHTechNode* AvHTechTree::GetNodeByTech(const AvHTechID inTech)
{
AvHTechNode* returnVal = NULL;
TechIDMap::iterator item = mNodesByTech.find(inTech);
if( item != mNodesByTech.end() )
{ returnVal = item->second; }
else
{
TechNodeMap::iterator current, end = mNodesByMsg.end();
for( current = mNodesByMsg.begin(); current != end; ++current )
{
if( current->second->getTechID() == inTech )
{
mNodesByTech[inTech] = current->second;
returnVal = current->second;
break;
}
}
}
return returnVal;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const AvHTechNode* AvHTechTree::GetNodeByTech(const AvHTechID inTech) const
{
const AvHTechNode* returnVal = NULL;
TechIDMap::const_iterator item = mNodesByTech.find(inTech);
if( item != mNodesByTech.end() )
{ returnVal = item->second; }
else
{
TechNodeMap::const_iterator current, end = mNodesByMsg.end();
for( current = mNodesByMsg.begin(); current != end; ++current )
{
if( current->second->getTechID() == inTech )
{
mNodesByTech[inTech] = current->second;
returnVal = current->second;
break;
}
}
}
return returnVal;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const int AvHTechTree::GetSize(void) const
{
return (int)mNodesByTech.size();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool AvHTechTree::GetAllowMultiples(const AvHMessageID inMessageID) const
{
const AvHTechNode* Node = GetNode(inMessageID);
return (Node != NULL) && Node->getAllowMultiples();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool AvHTechTree::GetIsMessageInTechTree(const AvHMessageID inMessageID) const
{
const AvHTechNode* Node = GetNode(inMessageID);
return (Node != NULL);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool AvHTechTree::GetIsMessageAvailable(const AvHMessageID inMessageID) const
{
bool returnVal = true;
const AvHTechNode* Subnode;
const AvHTechNode* Node = GetNode(inMessageID);
if( Node == NULL ) //not found
{ returnVal = false; }
else if( Node->getIsResearched() && !Node->getAllowMultiples() ) //can only research once?
{ returnVal = false; }
else
{
AvHTechID prereq = Node->getPrereqTechID1();
if( prereq != TECH_NULL && ( (Subnode = GetNodeByTech(prereq)) == NULL || !Subnode->getIsResearched() ) )
{ returnVal = false; }
else
{
prereq = Node->getPrereqTechID2();
if( prereq != TECH_NULL && ( (Subnode = GetNodeByTech(prereq)) == NULL || !Subnode->getIsResearched() ) )
{ returnVal = false; }
}
}
return returnVal;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool AvHTechTree::GetIsMessageAvailableForSelection(const AvHMessageID inMessageID, const EntityListType& inSelection) const
{
return GetIsMessageAvailable(inMessageID);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool AvHTechTree::GetIsTechResearched(AvHTechID inTech) const
{
const AvHTechNode* Node = GetNodeByTech(inTech);
return (Node != NULL) && Node->getIsResearched();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool AvHTechTree::SetFirstNodeWithTechResearchState(const AvHTechID inTech, bool inState)
{
bool returnVal = false;
AvHTechNode* Node = GetNodeByTech(inTech);
if( Node != NULL )
{
Node->setResearchState(inState);
returnVal = true;
}
return returnVal;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool AvHTechTree::GetMessageForTech(const AvHTechID inTech, AvHMessageID& outMessageID) const
{
bool returnVal = false;
const AvHTechNode* Node = GetNodeByTech(inTech);
if( Node != NULL )
{
outMessageID = Node->getMessageID();
returnVal = true;
}
return returnVal;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
AvHMessageID AvHTechTree::GetNextMessageNeededFor(const AvHMessageID inMessageID) const
{
//perform depth first search for item that hasn't been researched but can be; returns
//original ID if no match is found... would be better to return boolean false with an
//output AvHMessageID specified as a parameter.
AvHMessageID returnVal = inMessageID;
const AvHTechNode* Node = GetNode(inMessageID);
bool prereq1_researched = false;
if( Node != NULL && !Node->getIsResearched() )
{
vector<const AvHTechNode*> ParentStack;
ParentStack.push_back(Node);
const AvHTechNode* Child;
AvHTechID prereq;
while(!ParentStack.empty())
{
Node = ParentStack.back();
ParentStack.pop_back();
//First child
prereq1_researched = false;
prereq = Node->getPrereqTechID1();
if( prereq == TECH_NULL )
{ prereq1_researched = true; }
else
{
Child = GetNodeByTech(prereq);
if( Child != NULL )
{
if( Child->getIsResearched() )
{ prereq1_researched = true; }
else
{ ParentStack.push_back(Child); }
}
}
//Second child
prereq = Node->getPrereqTechID2();
if( prereq == TECH_NULL && prereq1_researched )
{
returnVal = Node->getMessageID();
break;
}
else
{
Child = GetNodeByTech(prereq);
if( Child != NULL )
{
if( Child->getIsResearched() )
{
if( prereq1_researched )
{
returnVal = Node->getMessageID();
break;
}
}
else
{ ParentStack.push_back(Child); }
}
}
}
}
return returnVal;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void AvHTechTree::GetResearchNodesDependentOn(AvHTechID inTechID, TechNodeMap& outTechNodes) const
{
//move up tree from supplied base tech; this won't be a cheap operation - worst case is (tree size)^2
if(inTechID != TECH_NULL)
{
TechNodeMap::const_iterator current, end = mNodesByMsg.end();
for( current = mNodesByMsg.begin(); current != end; ++current )
{
if( !AvHSHUGetIsResearchTech(current->first) || current->second->getTechID() == TECH_NULL )
{ continue; }
if( current->second->getPrereqTechID1() == inTechID || current->second->getPrereqTechID2() == inTechID )
{
outTechNodes[current->first] = current->second; //don't clone here, caller not responsible for deletion
GetResearchNodesDependentOn(current->second->getTechID(), outTechNodes); //recurse
}
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool AvHTechTree::GetTechForMessage(const AvHMessageID inMessageID, AvHTechID& outTechID) const
{
bool returnVal = false;
const AvHTechNode* Node = GetNode(inMessageID);
if( Node != NULL )
{
outTechID = Node->getTechID();
returnVal = true;
}
return returnVal;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool AvHTechTree::GetPrequisitesForMessage(const AvHMessageID inMessageID, AvHTechID& outTech1, AvHTechID& outTech2) const
{
bool returnVal = false;
const AvHTechNode* Node = GetNode(inMessageID);
if( Node != NULL )
{
outTech1 = Node->getPrereqTechID1();
outTech2 = Node->getPrereqTechID2();
returnVal = (outTech1 != TECH_NULL) || (outTech2 != TECH_NULL);
}
return returnVal;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool AvHTechTree::GetResearchInfo(AvHMessageID inMessageID, bool& outIsResearchable, int& outCost, float& outTime) const
{
bool returnVal = false;
const AvHTechNode* Node = GetNode(inMessageID);
if( Node != NULL )
{
outIsResearchable = Node->getIsResearchable();
outCost = Node->getCost();
outTime = Node->getBuildTime();
returnVal = true;
}
else
{
outIsResearchable = false;
outCost = -1;
outTime = -1;
}
return returnVal;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool AvHTechTree::SetIsResearchable(AvHMessageID inMessageID, bool inState)
{
bool returnVal = false;
AvHTechNode* Node = GetNode(inMessageID);
if( Node != NULL )
{
Node->setResearchable(inState);
returnVal = true;
}
return returnVal;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool AvHTechTree::SetResearchDone(AvHMessageID inMessageID, bool inState)
{
bool returnVal = false;
AvHTechNode* Node = GetNode(inMessageID);
if( Node != NULL )
{
Node->setResearchState(inState);
returnVal = true;
}
return returnVal;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void AvHTechTree::TriggerAddTech(AvHTechID inTechID)
{
AvHTechNode* Node = GetNodeByTech(inTechID);
if( Node != NULL )
{
Node->setResearchState(true);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void AvHTechTree::TriggerRemoveTech(AvHTechID inTechID)
{
AvHTechNode* Node = GetNodeByTech(inTechID);
if( Node != NULL )
{
Node->setResearchState(false);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void AvHTechTree::GetDelta(const AvHTechTree& other, MessageIDListType& delta) const
{
delta.clear();
TechNodeMap::const_iterator current = mNodesByMsg.begin();
TechNodeMap::const_iterator end = mNodesByMsg.end();
TechNodeMap::const_iterator other_current = other.mNodesByMsg.begin();
TechNodeMap::const_iterator other_end = other.mNodesByMsg.end();
while( current != end && other_current != other_end )
{
if( current->first < other_current->first )
{
delta.push_back(current->first);
++current;
continue;
}
if( current->first > other_current->first )
{
delta.push_back(current->first);
++other_current;
continue;
}
if( *current->second != *other_current->second )
{
delta.push_back(current->first);
}
++current;
++other_current;
}
while( current != end )
{
delta.push_back(current->first);
++current;
}
while( other_current != other_end )
{
delta.push_back(other_current->first);
++other_current;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool AvHTechTree::operator!=(const AvHTechTree& inTree) const
{
return !this->operator==(inTree);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool AvHTechTree::operator==(const AvHTechTree& inTree) const
{
return mNodesByMsg == inTree.mNodesByMsg;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
AvHTechTree& AvHTechTree::operator=(const AvHTechTree& inTree)
{
AvHTechTree temp(inTree);
swap(temp);
return *this;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#ifdef AVH_SERVER
bool AvHTechTree::shouldNotify(const string& name, const BalanceValueType type) const { return true; }
void AvHTechTree::balanceCleared(void) const {}
void AvHTechTree::balanceValueInserted(const string& name, const float value) const
{
if( !compoundChangeInProgress )
{ const_cast<AvHTechTree*>(this)->processBalanceChange(); }
}
void AvHTechTree::balanceValueInserted(const string& name, const int value) const
{
if( !compoundChangeInProgress )
{ const_cast<AvHTechTree*>(this)->processBalanceChange(); }
}
void AvHTechTree::balanceValueInserted(const string& name, const string& value) const
{
if( !compoundChangeInProgress )
{ const_cast<AvHTechTree*>(this)->processBalanceChange(); }
}
void AvHTechTree::balanceValueRemoved(const string& name, const float old_value) const
{
if( !compoundChangeInProgress )
{ const_cast<AvHTechTree*>(this)->processBalanceChange(); }
}
void AvHTechTree::balanceValueRemoved(const string& name, const int old_value) const
{
if( !compoundChangeInProgress )
{ const_cast<AvHTechTree*>(this)->processBalanceChange(); }
}
void AvHTechTree::balanceValueRemoved(const string& name, const string& old_value) const
{
if( !compoundChangeInProgress )
{ const_cast<AvHTechTree*>(this)->processBalanceChange(); }
}
void AvHTechTree::balanceValueChanged(const string& name, const float old_value, const float new_value) const
{
if( !compoundChangeInProgress )
{ const_cast<AvHTechTree*>(this)->processBalanceChange(); }
}
void AvHTechTree::balanceValueChanged(const string& name, const int old_value, const int new_value) const
{
if( !compoundChangeInProgress )
{ const_cast<AvHTechTree*>(this)->processBalanceChange(); }
}
void AvHTechTree::balanceValueChanged(const string& name, const string& old_value, const string& default_value) const
{
if( !compoundChangeInProgress )
{ const_cast<AvHTechTree*>(this)->processBalanceChange(); }
}
void AvHTechTree::balanceStartCompoundChange(void) const
{ compoundChangeInProgress = true; }
void AvHTechTree::balanceEndCompoundChange(void) const
{ compoundChangeInProgress = false; }
#include "dlls/extdll.h"
#include "dlls/util.h"
#include "mod/AvHGamerules.h"
void AvHTechTree::processBalanceChange(void)
{
// Run through our tech nodes and update cost and build time
TechNodeMap::iterator current, end = mNodesByMsg.end();
for( current = mNodesByMsg.begin(); current != end; ++current )
{
current->second->setBuildTime(GetGameRules()->GetBuildTimeForMessageID(current->first));
current->second->setCost(GetGameRules()->GetCostForMessageID(current->first));
}
}
#endif