diff --git a/source/glest_game/type_instances/unit.cpp b/source/glest_game/type_instances/unit.cpp index cbe3c050..09f8a2fc 100644 --- a/source/glest_game/type_instances/unit.cpp +++ b/source/glest_game/type_instances/unit.cpp @@ -1951,7 +1951,17 @@ void Unit::born(const CommandType *ct) { checkItemInVault(&this->hp,this->hp); int original_hp = this->hp; - this->hp= type->getMaxHp(); + + + //set hp from start hp + checkItemInVault(&this->ep,this->ep); + if(type->getStartHpType() == UnitType::stValue) { + this->hp= type->getStartHpValue(); + } + else { + this->hp= type->getTotalMaxHp(&totalUpgrade) * 100 / type->getStartHpPercentage(); + } + if(original_hp != this->hp) { //printf("File: %s line: %d\n",extractFileFromDirectoryPath(__FILE__).c_str(),__LINE__); game->getScriptManager()->onUnitTriggerEvent(this,utet_HPChanged); @@ -1959,9 +1969,14 @@ void Unit::born(const CommandType *ct) { } addItemToVault(&this->hp,this->hp); - //set ep from start-ep tag + //set ep from start ep checkItemInVault(&this->ep,this->ep); - this->ep= type->getStartEp(); + if(type->getStartEpType() == UnitType::stValue) { + this->ep= type->getStartEpValue(); + } + else { + this->ep= type->getTotalMaxEp(&totalUpgrade) * 100 / type->getStartEpPercentage(); + } } void Unit::kill() { diff --git a/source/glest_game/types/unit_type.cpp b/source/glest_game/types/unit_type.cpp index 5eac779e..a43a2034 100644 --- a/source/glest_game/types/unit_type.cpp +++ b/source/glest_game/types/unit_type.cpp @@ -125,8 +125,13 @@ UnitType::UnitType() : ProducibleType() { epRegeneration= 0; maxUnitCount= 0; maxHp=0; + startHpValue=0; + startHpPercentage=1.0; + startHpType=stValue; maxEp=0; - startEp=0; + startEpValue=0; + startEpPercentage=0; + startEpType=stValue; armor=0; sight=0; size=0; @@ -248,13 +253,58 @@ void UnitType::loaddd(int id,const string &dir, const TechTree *techTree, } addItemToVault(&(this->epRegeneration),this->epRegeneration); - //startEp - - if(parametersNode->hasChild("start-ep")) { - //checkItemInVault(&(this->startEp),this->startEp); - startEp= parametersNode->getChild("start-ep")->getAttribute("value")->getIntValue(); + // Check that we don't use both start-value and start-percentage, as they are mutually + // exclusive + if(parametersNode->getChild("max-hp")->hasAttribute("start-value") && + parametersNode->getChild("max-hp")->hasAttribute("start-percentage")) { + throw megaglest_runtime_error("Unit " + name + + " has both start-value and start-percentage for HP", validationMode); } - addItemToVault(&(this->startEp),this->startEp); + + //startHpValue -- the *absolute* value to use for starting HP + if(parametersNode->getChild("max-hp")->hasAttribute("start-value")) { + //checkItemInVault(&(this->startEp),this->startEp); + startHpValue= parametersNode->getChild("max-hp")->getAttribute("start-value")->getIntValue(); + startHpType= stValue; + } + addItemToVault(&(this->startHpValue),this->startHpValue); + + //startHpPercentage -- the *relative* value to use for starting HP + if(parametersNode->getChild("max-hp")->hasAttribute("start-percentage")) { + startHpPercentage= parametersNode->getChild("max-hp")->getAttribute("start-percentage")->getIntValue(); + startHpType= stPercentage; + } + + // No start value set; use max HP before upgrades + if(!parametersNode->getChild("max-hp")->hasAttribute("start-value") && + !parametersNode->getChild("max-hp")->hasAttribute("start-percentage")) { + startHpValue= parametersNode->getChild("max-hp")->getAttribute("value")->getIntValue(); + startHpType= stValue; + } + addItemToVault(&(this->startHpPercentage),this->startHpPercentage); + + // Check that we don't use both start-value and start-percentage, as they are mutually + // exclusive + if(parametersNode->getChild("max-ep")->hasAttribute("start-value") && + parametersNode->getChild("max-ep")->hasAttribute("start-percentage")) { + throw megaglest_runtime_error("Unit " + name + + " has both start-value and start-percentage for EP", validationMode); + } + + //startEpValue -- the *absolute* value to use for starting EP + if(parametersNode->getChild("max-ep")->hasAttribute("start-value")) { + //checkItemInVault(&(this->startEp),this->startEp); + startEpValue= parametersNode->getChild("max-ep")->getAttribute("start-value")->getIntValue(); + startEpType= stValue; + } + addItemToVault(&(this->startEpValue),this->startEpValue); + + //startEpPercentage -- the *relative* value to use for starting EP + if(parametersNode->getChild("max-ep")->hasAttribute("start-percentage")) { + startEpPercentage= parametersNode->getChild("max-ep")->getAttribute("start-percentage")->getIntValue(); + startEpType= stPercentage; + } + addItemToVault(&(this->startEpPercentage),this->startEpPercentage); //maxUnitCount if(parametersNode->hasChild("max-unit-count")) { @@ -541,6 +591,65 @@ void UnitType::loaddd(int id,const string &dir, const TechTree *techTree, } } sortedItems.clear(); + hasDup = false; + + // Lootable resources (resources given/lost on death) + if(parametersNode->hasChild("resources-death")) { + const XmlNode *deathResourcesNode= parametersNode->getChild("resources-death"); + + for(int i=0; i < deathResourcesNode->getChildCount(); ++i){ + const XmlNode *resourceNode= deathResourcesNode->getChild("resource", i); + string name= resourceNode->getAttribute("name")->getRestrictedValue(); + + LootableResource resource; + resource.setResourceType(techTree->getResourceType(name)); + + // All attributes are optional, although nothing happens if they aren't used. They can + // be combined freely. Percentages will take affect before absolute values. + if(resourceNode->hasAttribute("amount-value")) { + resource.setAmountValue(resourceNode->getAttribute("amount-value")->getIntValue()); + } + else { + resource.setAmountValue(0); + } + + if(resourceNode->hasAttribute("amount-percentage")) { + resource.setAmountPercentage(resourceNode->getAttribute("amount-percentage")->getIntValue()); + } + else { + resource.setAmountPercentage(0); + } + + if(resourceNode->hasAttribute("loss-value")) { + resource.setLossValue(resourceNode->getAttribute("loss-value")->getIntValue()); + } + else { + resource.setLossValue(0); + } + + if(resourceNode->hasAttribute("loss-percentage")) { + resource.setLossPercentage(resourceNode->getAttribute("loss-percentage")->getIntValue()); + } + else { + resource.setLossPercentage(0); + } + + if(resourceNode->hasAttribute("allow-negative")) { + resource.setNegativeAllowed(resourceNode->getAttribute("allow-negative")->getBoolValue()); + } + else { + resource.setNegativeAllowed(false); + } + + // Figure out if there are duplicate resources. The value stored in the map is arbitrary, + // and exists solely because + if(std::find(lootableResources.begin(), lootableResources.end(), resource) != lootableResources.end()) { + printf("WARNING, unit type [%s] has one or more duplicate lootable resources\n", this->getName(false).c_str()); + } + + lootableResources.push_back(resource); + } + } //image const XmlNode *imageNode= parametersNode->getChild("image"); @@ -1109,7 +1218,8 @@ std::string UnitType::toString() const { result += " maxHp = " + intToStr(maxHp); result += " hpRegeneration = " + intToStr(hpRegeneration); result += " maxEp = " + intToStr(maxEp); - result += " startEp = " + intToStr(startEp); + result += " startEpValue = " + intToStr(startEpValue); + result += " startEpPercentage = " + intToStr(startEpPercentage); result += " epRegeneration = " + intToStr(epRegeneration); result += " maxUnitCount = " + intToStr(getMaxUnitCount()); diff --git a/source/glest_game/types/unit_type.h b/source/glest_game/types/unit_type.h index ee23d111..0342394a 100644 --- a/source/glest_game/types/unit_type.h +++ b/source/glest_game/types/unit_type.h @@ -63,6 +63,45 @@ public: static const Level * loadGame(const XmlNode *rootNode, const UnitType *ut); }; +// =============================== +// class LootResource +// +/// Stores information about a lootable resource. Lootable resources are stolen by the attacker on death. +// =============================== + +class LootableResource { +private: + const ResourceType *type; + int amountValue; + int amountPercentage; + int lossValue; + int lossPercentage; + bool negativeAllowed; + +public: + const ResourceType* getResourceType() const {return type;} + void setResourceType(const ResourceType *type) {this->type=type;} + + int getAmountValue() const {return amountValue;} + void setAmountValue(int amountValue) {this->amountValue=amountValue;} + + int getAmountPercentage() const {return amountPercentage;} + void setAmountPercentage(int amountPercentage) {this->amountPercentage=amountPercentage;} + + int getLossValue() const {return lossValue;} + void setLossValue(int lossValue) {this->lossValue=lossValue;} + + int getLossPercentage() const {return lossPercentage;} + void setLossPercentage(int lossPercentage) {this->lossPercentage=lossPercentage;} + + bool isNegativeAllowed() const {return negativeAllowed;} + void setNegativeAllowed(bool negativeAllowed) {this->negativeAllowed=negativeAllowed;} + + bool operator==(const LootableResource& other) { + return type == other.getResourceType(); + } +}; + // =============================== // class UnitType // @@ -92,6 +131,11 @@ public: pCount }; + enum StartType { + stValue, + stPercentage + }; + static const char *propertyNames[]; DamageParticleSystemTypes damageParticleSystemTypes; private: @@ -99,14 +143,20 @@ private: typedef vector CommandTypes; typedef vector StoredResources; typedef vector Levels; + typedef vector LootableResources; private: //basic int id; int maxHp; + int startHpValue; + int startHpPercentage; + StartType startHpType; int hpRegeneration; int maxEp; - int startEp; + int startEpValue; + int startEpPercentage; + StartType startEpType; int epRegeneration; int maxUnitCount; @@ -141,6 +191,7 @@ private: CommandTypes commandTypes; StoredResources storedResources; Levels levels; + LootableResources lootableResources; //meeting point bool meetingPoint; @@ -179,9 +230,14 @@ public: inline int getId() const {return id;} inline int getMaxHp() const {return maxHp;} inline int getHpRegeneration() const {return hpRegeneration;} + inline int getStartHpValue() const {return startHpValue;} + inline int getStartHpPercentage() const {return startHpPercentage;} + inline StartType getStartHpType() const {return startHpType;} inline int getMaxEp() const {return maxEp;} inline int getEpRegeneration() const {return epRegeneration;} - inline int getStartEp() const {return startEp;} + inline int getStartEpValue() const {return startEpValue;} + inline int getStartEpPercentage() const {return startEpPercentage;} + inline StartType getStartEpType() const {return startEpType;} inline int getMaxUnitCount() const {return maxUnitCount;} inline bool getField(Field field) const {return fields[field];} inline Field getField() const {return field;} @@ -205,6 +261,8 @@ public: int getHeight() const {return height;} int getStoredResourceCount() const {return (int)storedResources.size();} inline const Resource *getStoredResource(int i) const {return &storedResources[i];} + int getLootableResourceCount() const {return lootableResources.size();} + inline const LootableResource getLootableResource(int i) const {return lootableResources.at(i);} bool getCellMapCell(int x, int y, CardinalDir facing) const; inline bool getMeetingPoint() const {return meetingPoint;} inline bool getCountUnitDeathInStats() const {return countUnitDeathInStats;} diff --git a/source/glest_game/world/unit_updater.cpp b/source/glest_game/world/unit_updater.cpp index d1ea979b..099bad9f 100644 --- a/source/glest_game/world/unit_updater.cpp +++ b/source/glest_game/world/unit_updater.cpp @@ -2587,6 +2587,37 @@ void UnitUpdater::damage(Unit *attacker, const AttackSkillType* ast, Unit *attac attacker->incKills(attacked->getTeam()); } + // Perform resource looting iff the attack is from a different faction + if(attacker->getFaction() != attacked->getFaction()) { + int lootableResourceCount = attacked->getType()->getLootableResourceCount(); + for(int i = 0; i < lootableResourceCount; i++) { + LootableResource resource = attacked->getType()->getLootableResource(i); + + // Figure out how much of the resource in question that the attacked unit's faction has + int factionTotalResource = 0; + for(int j = 0; j < attacked->getFaction()->getTechTree()->getResourceTypeCount(); j++) { + if(attacked->getFaction()->getTechTree()->getResourceType(j) == resource.getResourceType()) { + factionTotalResource = attacked->getFaction()->getResource(j)->getAmount(); + break; + } + } + + if(resource.isNegativeAllowed()) { + attacked->getFaction()->incResourceAmount(resource.getResourceType(), -(factionTotalResource * resource.getLossPercentage() / 100)); + attacked->getFaction()->incResourceAmount(resource.getResourceType(), -resource.getLossValue()); + attacker->getFaction()->incResourceAmount(resource.getResourceType(), factionTotalResource * resource.getAmountPercentage() / 100); + attacker->getFaction()->incResourceAmount(resource.getResourceType(), resource.getAmountValue()); + } + // Can't take more resources than the faction has, otherwise we end up in the negatives + else { + attacked->getFaction()->incResourceAmount(resource.getResourceType(), -(std::min)(factionTotalResource * resource.getLossPercentage() / 100, factionTotalResource)); + attacked->getFaction()->incResourceAmount(resource.getResourceType(), -(std::min)(resource.getLossValue(), factionTotalResource)); + attacker->getFaction()->incResourceAmount(resource.getResourceType(), (std::min)(factionTotalResource * resource.getAmountPercentage() / 100, factionTotalResource)); + attacker->getFaction()->incResourceAmount(resource.getResourceType(), (std::min)(resource.getAmountValue(), factionTotalResource)); + } + } + } + switch(this->game->getGameSettings()->getPathFinderType()) { case pfBasic: break;