/* ** Command & Conquer Generals(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: Science.cpp ///////////////////////////////////////////////////////// // Created: Steven Johnson, October 2001 // Desc: @todo //----------------------------------------------------------------------------- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "Common/INI.h" #include "Common/Player.h" #include "Common/Science.h" ScienceStore* TheScienceStore = NULL; #ifdef _INTERNAL // for occasional debugging... //#pragma optimize("", off) //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes") #endif //----------------------------------------------------------------------------- void ScienceStore::init() { DEBUG_ASSERTCRASH(m_sciences.empty(), ("Hmm")); m_sciences.clear(); } //----------------------------------------------------------------------------- void ScienceStore::reset() { // nope. //m_sciences.clear(); // go through all sciences and delete any overrides for (ScienceInfoVec::iterator it = m_sciences.begin(); it != m_sciences.end(); /*++it*/) { ScienceInfo* si = *it; Overridable* temp = si->deleteOverrides(); if (!temp) { it = m_sciences.erase(it); } else { ++it; } } } //----------------------------------------------------------------------------- ScienceType ScienceStore::getScienceFromInternalName(const AsciiString& name) const { if (name.isEmpty()) return SCIENCE_INVALID; NameKeyType nkt = TheNameKeyGenerator->nameToKey(name); ScienceType st = (ScienceType)nkt; return st; } //----------------------------------------------------------------------------- AsciiString ScienceStore::getInternalNameForScience(ScienceType science) const { if (science == SCIENCE_INVALID) return AsciiString::TheEmptyString; NameKeyType nk = (NameKeyType)(science); return TheNameKeyGenerator->keyToName(nk); } //----------------------------------------------------------------------------- // return a vector of all the currently-known science names // NOTE: this is really only for use by WorldBuilder! Please // do not use it in RTS! std::vector ScienceStore::friend_getScienceNames() const { std::vector v; for (ScienceInfoVec::const_iterator it = m_sciences.begin(); it != m_sciences.end(); ++it) { const ScienceInfo* si = (const ScienceInfo*)(*it)->getFinalOverride(); NameKeyType nk = (NameKeyType)(si->m_science); v.push_back(TheNameKeyGenerator->keyToName(nk)); } return v; } //----------------------------------------------------------------------------- void ScienceInfo::addRootSciences(ScienceVec& v) const { if (m_prereqSciences.empty()) { // we're a root. add ourselves. if (std::find(v.begin(), v.end(), m_science) == v.end()) v.push_back(m_science); } else { // we're not a root. add the roots of all our prereqs. for (ScienceVec::const_iterator it = m_prereqSciences.begin(); it != m_prereqSciences.end(); ++it) { const ScienceInfo* si = TheScienceStore->findScienceInfo(*it); if (si) si->addRootSciences(v); } } } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- const ScienceInfo* ScienceStore::findScienceInfo(ScienceType st) const { for (ScienceInfoVec::const_iterator it = m_sciences.begin(); it != m_sciences.end(); ++it) { const ScienceInfo* si = (const ScienceInfo*)(*it)->getFinalOverride(); if (si->m_science == st) { return si; } } return NULL; } //----------------------------------------------------------------------------- /*static*/ void ScienceStore::friend_parseScienceDefinition( INI* ini ) { const char* c = ini->getNextToken(); NameKeyType nkt = NAMEKEY(c); ScienceType st = (ScienceType)nkt; if (TheScienceStore) { static const FieldParse myFieldParse[] = { { "PrerequisiteSciences", INI::parseScienceVector, NULL, offsetof( ScienceInfo, m_prereqSciences ) }, { "SciencePurchasePointCost", INI::parseInt, NULL, offsetof( ScienceInfo, m_sciencePurchasePointCost ) }, { "IsGrantable", INI::parseBool, NULL, offsetof( ScienceInfo, m_grantable ) }, { "DisplayName", INI::parseAndTranslateLabel, NULL, offsetof( ScienceInfo, m_name) }, { "Description", INI::parseAndTranslateLabel, NULL, offsetof( ScienceInfo, m_description) }, { 0, 0, 0, 0 } }; ScienceInfo* info = NULL; // see if the science already exists. (can't use findScienceInfo() since it is const and should remain so.) for (ScienceInfoVec::iterator it = TheScienceStore->m_sciences.begin(); it != TheScienceStore->m_sciences.end(); ++it) { // note that we don't use getFinalOverride here. this is correct and as-desired. if ((*it)->m_science == st) { info = *it; break; } } if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES) { ScienceInfo* newInfo = newInstance(ScienceInfo); if (info == NULL) { // only add if it's not overriding an existing one. info = newInfo; info->markAsOverride(); // yep, so we will get cleared on reset() TheScienceStore->m_sciences.push_back(info); } else { // copy data from final override to 'newInfo' as a set of initial default values info = (ScienceInfo*)(info->friend_getFinalOverride()); *newInfo = *info; info->setNextOverride(newInfo); newInfo->markAsOverride(); // must do AFTER the copy // use the newly created override for us to set values with etc info = newInfo; //TheScienceStore->m_sciences.push_back(info); // NO, BAD, WRONG -- don't add in this case. } } else { if (info != NULL) { DEBUG_CRASH(("duplicate science %s!\n",c)); throw INI_INVALID_DATA; } info = newInstance(ScienceInfo); TheScienceStore->m_sciences.push_back(info); } ini->initFromINI(info, myFieldParse); info->m_science = st; info->addRootSciences(info->m_rootSciences); } } //----------------------------------------------------------------------------- Int ScienceStore::getSciencePurchaseCost(ScienceType st) const { const ScienceInfo* si = findScienceInfo(st); if (si) { return si->m_sciencePurchasePointCost; } else { return 0; } } //----------------------------------------------------------------------------- Bool ScienceStore::isScienceGrantable(ScienceType st) const { const ScienceInfo* si = findScienceInfo(st); if (si) { return si->m_grantable; } else { return false; } } //----------------------------------------------------------------------------- Bool ScienceStore::getNameAndDescription(ScienceType st, UnicodeString& name, UnicodeString& description) const { const ScienceInfo* si = findScienceInfo(st); if (si) { name = si->m_name; description = si->m_description; return true; } else { return false; } } //----------------------------------------------------------------------------- Bool ScienceStore::playerHasPrereqsForScience(const Player* player, ScienceType st) const { const ScienceInfo* si = findScienceInfo(st); if (si) { for (ScienceVec::const_iterator it2 = si->m_prereqSciences.begin(); it2 != si->m_prereqSciences.end(); ++it2) { if (!player->hasScience(*it2)) { return false; } } return true; } else { return false; } } //----------------------------------------------------------------------------- Bool ScienceStore::playerHasRootPrereqsForScience(const Player* player, ScienceType st) const { const ScienceInfo* si = findScienceInfo(st); if (si) { for (ScienceVec::const_iterator it2 = si->m_rootSciences.begin(); it2 != si->m_rootSciences.end(); ++it2) { if (!player->hasScience(*it2)) { return false; } } return true; } else { return false; } } //----------------------------------------------------------------------------- /** return a list of the sciences the given player can purchase now, and a list he might be able to purchase in the future, but currently lacks prereqs or points for. (either might be an empty list) */ void ScienceStore::getPurchasableSciences(const Player* player, ScienceVec& purchasable, ScienceVec& potentiallyPurchasable) const { purchasable.clear(); potentiallyPurchasable.clear(); for (ScienceInfoVec::const_iterator it = m_sciences.begin(); it != m_sciences.end(); ++it) { const ScienceInfo* si = (const ScienceInfo*)(*it)->getFinalOverride(); if (si->m_sciencePurchasePointCost == 0) { // 0 means "cannot be purchased" continue; } if (player->hasScience(si->m_science)) { continue; } if (playerHasPrereqsForScience(player, si->m_science)) { purchasable.push_back(si->m_science); } else if (playerHasRootPrereqsForScience(player, si->m_science)) { potentiallyPurchasable.push_back(si->m_science); } } } //----------------------------------------------------------------------------- // this is intended ONLY for use by INI::scanScience. // Don't use it anywhere else. In particular, never, ever, ever // call this with a hardcoded science name. (srj) ScienceType ScienceStore::friend_lookupScience(const char* scienceName) const { NameKeyType nkt = NAMEKEY(scienceName); ScienceType st = (ScienceType)nkt; if (!isValidScience(st)) { DEBUG_CRASH(("Science name %s not known! (Did you define it in Science.ini?)",scienceName)); throw INI_INVALID_DATA; } return st; } //----------------------------------------------------------------------------- Bool ScienceStore::isValidScience(ScienceType st) const { const ScienceInfo* si = findScienceInfo(st); return si != NULL; } //----------------------------------------------------------------------------- void INI::parseScienceDefinition( INI* ini ) { ScienceStore::friend_parseScienceDefinition(ini); }