From c0de6fd1f0450d961435e150bffadd53d8e94a7b Mon Sep 17 00:00:00 2001 From: cybermind <iddqd_mail@mail.ru> Date: Fri, 11 May 2012 14:19:30 +0600 Subject: [PATCH] [+] Ability to set specific rules for AI players how to build buildings. Use AiBuildingRules in CUnitType for that (same syntax as in BuildingRules) [+] Defence forces now can return to base if no more enemy units left. [+] Move CUnitType._Storing to stats, it's possible to use Storing in upgrades. [*] Don't check the builder in CBuildRestrictionDistance [*] Don't restore the saved attack order for dead units. [-] Fixed bug which didn't let the defence force to retaliate. --- src/action/action_build.cpp | 2 +- src/action/action_upgradeto.cpp | 2 +- src/ai/ai_force.cpp | 45 ++++++++++++++++++---- src/ai/ai_local.h | 5 ++- src/animation/animation_setplayervar.cpp | 4 +- src/editor/editloop.cpp | 2 +- src/include/unit.h | 2 +- src/include/unittype.h | 12 +++--- src/include/upgrade_structs.h | 2 + src/stratagus/script_player.cpp | 2 +- src/unit/build.cpp | 48 +++++++++++++++++------- src/unit/script_unittype.cpp | 37 +++++++++++++++++- src/unit/unit.cpp | 22 ++++++----- src/unit/unittype.cpp | 19 +++++++++- src/unit/upgrade.cpp | 16 ++++++++ 15 files changed, 173 insertions(+), 47 deletions(-) diff --git a/src/action/action_build.cpp b/src/action/action_build.cpp index e1ded867b..786afdc56 100644 --- a/src/action/action_build.cpp +++ b/src/action/action_build.cpp @@ -330,7 +330,7 @@ bool COrder_Build::StartBuilding(CUnit &unit, CUnit &ontop) if (&ontop != &unit) { CBuildRestrictionOnTop *b; - b = static_cast<CBuildRestrictionOnTop *>(OnTopDetails(*build, ontop.Type)); + b = static_cast<CBuildRestrictionOnTop *>(OnTopDetails(unit.Type->BuildingRules, *build, ontop.Type)); Assert(b); if (b->ReplaceOnBuild) { build->ResourcesHeld = ontop.ResourcesHeld; // We capture the value of what is beneath. diff --git a/src/action/action_upgradeto.cpp b/src/action/action_upgradeto.cpp index 70f6c1cd8..346a8a396 100644 --- a/src/action/action_upgradeto.cpp +++ b/src/action/action_upgradeto.cpp @@ -115,7 +115,7 @@ static int TransformUnitIntoType(CUnit &unit, const CUnitType &newtype) // Change resource limit for (int i = 0; i < MaxCosts; ++i) { if (player.MaxResources[i] != -1) { - player.MaxResources[i] += newtype._Storing[i] - oldtype._Storing[i]; + player.MaxResources[i] += newtype.Stats[player.Index].Storing[i] - oldtype.Stats[player.Index].Storing[i]; player.SetResource(i, player.StoredResources[i], true); } } diff --git a/src/ai/ai_force.cpp b/src/ai/ai_force.cpp index 2877d49b6..5331c68ad 100644 --- a/src/ai/ai_force.cpp +++ b/src/ai/ai_force.cpp @@ -270,6 +270,12 @@ void AiForce::Attack(const Vec2i &pos) Vec2i goalPos(pos); RemoveDeadUnit(); + // Remember the original force position so we can return there after attack + if (this->Size() > 0 && (this->Role == AiForceRoleDefend && !this->Attacking) + || (this->Role == AiForceRoleAttack && !this->Attacking && !this->State)) { + this->HomePos = this->Units[0]->tilePos; + } + Attacking = false; if (Units.size() == 0) { return; @@ -295,6 +301,20 @@ void AiForce::Attack(const Vec2i &pos) } // Send all units in the force to enemy. this->State = AiForceAttackingState_Attacking; + for (size_t i = 0; i != this->Units.size(); ++i) { + CUnit* const unit = this->Units[i]; + int delta = 0; + if (unit->Container == NULL) { + // To avoid lot of CPU consuption, send them with a small time difference. + unit->Wait = delta; + ++delta; + if (unit->Type->CanAttack) { + CommandAttack(*unit, goalPos, NULL, FlushCommands); + } else { + CommandMove(*unit, goalPos, FlushCommands); + } + } + } } AiForceManager::AiForceManager() @@ -706,13 +726,24 @@ void AiForceManager::Update() if (force.Defending) { force.RemoveDeadUnit(); - // Look if still enemies in attack range. - const CUnit *dummy = NULL; - if (!AiForceEnemyFinder<true>(force, &dummy).found()) { - DebugPrint("%d:FIXME: not written, should send force #%d home\n" - _C_ AiPlayer->Player->Index _C_ f); - force.Defending = false; - force.Attacking = false; + // Check if some unit from force reached goal point + if (Map.Info.IsPointOnMap(force.GoalPos) && + force.Units[0]->MapDistanceTo(force.GoalPos) <= 5) { + // Look if still enemies in attack range. + const CUnit *dummy = NULL; + if (!AiForceEnemyFinder<true>(force, &dummy).found()) { + if (Map.Info.IsPointOnMap(force.HomePos)) { + const Vec2i invalidPos = { -1, -1}; + + for (size_t i = 0; i != force.Units.size(); ++i) { + CUnit* const unit = force.Units[i]; + CommandMove(*unit, force.HomePos, FlushCommands); + } + force.HomePos = invalidPos; + } + force.Defending = false; + force.Attacking = false; + } } } else if (force.Attacking) { force.RemoveDeadUnit(); diff --git a/src/ai/ai_local.h b/src/ai/ai_local.h index b535b7d85..ddc6db80c 100644 --- a/src/ai/ai_local.h +++ b/src/ai/ai_local.h @@ -117,7 +117,7 @@ public: AiForce() : Completed(false), Defending(false), Attacking(false), Role(AiForceRoleDefault), State(AiForceAttackingState_Free) { - GoalPos.x = GoalPos.y = -1; + HomePos.x = HomePos.y = GoalPos.x = GoalPos.y = -1; } void Remove(CUnit &unit) { @@ -141,7 +141,7 @@ public: } Units.for_each(InternalRemoveUnit); Units.clear(); - GoalPos.x = GoalPos.y = 0; + GoalPos.x = GoalPos.y = -1; } inline size_t Size() const { return Units.size(); } @@ -178,6 +178,7 @@ public: // If attacking AiForceAttackingState State; /// Attack state Vec2i GoalPos; /// Attack point tile map position + Vec2i HomePos; /// Return after attack tile map position }; // forces diff --git a/src/animation/animation_setplayervar.cpp b/src/animation/animation_setplayervar.cpp index d06b58786..013659236 100644 --- a/src/animation/animation_setplayervar.cpp +++ b/src/animation/animation_setplayervar.cpp @@ -61,15 +61,13 @@ int GetPlayerData(int player, const char *prop, const char *arg) if (!strcmp(prop, "RaceName")) { return Players[player].Race; } else if (!strcmp(prop, "Resources")) { - const int resId = GetResourceIdByName(arg); if (resId == -1) { fprintf(stderr, "Invalid resource \"%s\"", arg); Exit(1); } - return Players[player].Resources[resId]; + return Players[player].Resources[resId] + Players[player].StoredResources[resId]; } else if (!strcmp(prop, "StoredResources")) { - const int resId = GetResourceIdByName(arg); if (resId == -1) { fprintf(stderr, "Invalid resource \"%s\"", arg); diff --git a/src/editor/editloop.cpp b/src/editor/editloop.cpp index 9244e2174..9436d50d9 100644 --- a/src/editor/editloop.cpp +++ b/src/editor/editloop.cpp @@ -329,7 +329,7 @@ static void EditorActionPlaceUnit(const Vec2i &pos, CUnitType &type, CPlayer *pl return; } - CBuildRestrictionOnTop *b = OnTopDetails(*unit, NULL); + CBuildRestrictionOnTop *b = OnTopDetails(unit->Type->BuildingRules, *unit, NULL); if (b && b->ReplaceOnBuild) { CUnitCache &unitCache = Map.Field(pos)->UnitCache; CUnitCache::iterator it = std::find_if(unitCache.begin(), unitCache.end(), HasSameTypeAs(*b->Parent)); diff --git a/src/include/unit.h b/src/include/unit.h index dae36222a..15517feaa 100644 --- a/src/include/unit.h +++ b/src/include/unit.h @@ -915,7 +915,7 @@ extern void DropOutNearest(CUnit &unit, const Vec2i &goalPos, const CUnit *conta extern void DropOutAll(const CUnit &unit); /// Return the rule used to build this building. -extern CBuildRestrictionOnTop *OnTopDetails(const CUnit &unit, const CUnitType *parent); +extern CBuildRestrictionOnTop *OnTopDetails(std::vector<CBuildRestriction *> &restr, const CUnit &unit, const CUnitType *parent); /// @todo more docu extern CUnit *CanBuildHere(const CUnit *unit, const CUnitType &type, const Vec2i &pos); /// @todo more docu diff --git a/src/include/unittype.h b/src/include/unittype.h index 2871d00fe..d4aeae9cd 100644 --- a/src/include/unittype.h +++ b/src/include/unittype.h @@ -801,7 +801,7 @@ class CBuildRestriction public: virtual ~CBuildRestriction() {} virtual void Init() {}; - virtual bool Check(const CUnitType &type, const Vec2i &pos, CUnit *&ontoptarget) const = 0; + virtual bool Check(const CUnit *builder, const CUnitType &type, const Vec2i &pos, CUnit *&ontoptarget) const = 0; }; @@ -822,7 +822,7 @@ public: (*i)->Init(); } } - virtual bool Check(const CUnitType &type, const Vec2i &pos, CUnit *&ontoptarget) const; + virtual bool Check(const CUnit *builder, const CUnitType &type, const Vec2i &pos, CUnit *&ontoptarget) const; void push_back(CBuildRestriction *restriction) { _or_list.push_back(restriction); } public: @@ -846,7 +846,7 @@ public: CBuildRestrictionAddOn() : Parent(NULL) { Offset.x = Offset.y = 0; }; virtual ~CBuildRestrictionAddOn() {}; virtual void Init() {this->Parent = UnitTypeByIdent(this->ParentName);}; - virtual bool Check(const CUnitType &type, const Vec2i &pos, CUnit *&ontoptarget) const; + virtual bool Check(const CUnit *builder, const CUnitType &type, const Vec2i &pos, CUnit *&ontoptarget) const; Vec2i Offset; /// offset from the main building to place this std::string ParentName; /// building that is unit is an addon too. @@ -869,7 +869,7 @@ public: CBuildRestrictionOnTop() : Parent(NULL), ReplaceOnDie(0), ReplaceOnBuild(0) {}; virtual ~CBuildRestrictionOnTop() {}; virtual void Init() {this->Parent = UnitTypeByIdent(this->ParentName);}; - virtual bool Check(const CUnitType &type, const Vec2i &pos, CUnit *&ontoptarget) const; + virtual bool Check(const CUnit *builder, const CUnitType &type, const Vec2i &pos, CUnit *&ontoptarget) const; std::string ParentName; /// building that is unit is an addon too. CUnitType *Parent; /// building that is unit is an addon too. @@ -883,7 +883,7 @@ public: CBuildRestrictionDistance() : Distance(0), RestrictType(NULL) {}; virtual ~CBuildRestrictionDistance() {}; virtual void Init() {this->RestrictType = UnitTypeByIdent(this->RestrictTypeName);}; - virtual bool Check(const CUnitType &type, const Vec2i &pos, CUnit *&ontoptarget) const; + virtual bool Check(const CUnit *builder, const CUnitType &type, const Vec2i &pos, CUnit *&ontoptarget) const; int Distance; /// distance to build (circle) DistanceTypeType DistanceType; @@ -948,7 +948,6 @@ public: CConstruction *Construction; /// What is shown in construction phase - int _Storing[MaxCosts]; /// How many resources the unit can store int RepairHP; /// Amount of HP per repair int RepairCosts[MaxCosts]; /// How much it costs to repair @@ -1032,6 +1031,7 @@ public: int GivesResource; /// The resource this unit gives. ResourceInfo *ResInfo[MaxCosts]; /// Resource information. std::vector<CBuildRestriction *> BuildingRules; /// Rules list for building a building. + std::vector<CBuildRestriction *> AiBuildingRules; /// Rules list for for AI to build a building. SDL_Color NeutralMinimapColorRGB; /// Minimap Color for Neutral Units. CUnitSound Sound; /// Sounds for events diff --git a/src/include/upgrade_structs.h b/src/include/upgrade_structs.h index a139dd387..707f01401 100644 --- a/src/include/upgrade_structs.h +++ b/src/include/upgrade_structs.h @@ -140,6 +140,7 @@ class CUnitStats public: CUnitStats() : Variables(NULL) { memset(Costs, 0, sizeof(Costs)); + memset(Storing, 0, sizeof(Storing)); } ~CUnitStats(); @@ -150,6 +151,7 @@ public: public: CVariable *Variables; /// user defined variable. int Costs[MaxCosts]; /// current costs of the unit + int Storing[MaxCosts]; /// storage increasing }; /** diff --git a/src/stratagus/script_player.cpp b/src/stratagus/script_player.cpp index 632f0fb45..2b8b89a71 100644 --- a/src/stratagus/script_player.cpp +++ b/src/stratagus/script_player.cpp @@ -718,7 +718,7 @@ static int CclGetPlayerData(lua_State *l) const std::string res = LuaToString(l, 3); const int resId = GetResourceIdByName(l, res.c_str()); - lua_pushnumber(l, p->Resources[resId]); + lua_pushnumber(l, p->Resources[resId] + p->StoredResources[resId]); return 1; } else if (!strcmp(data, "StoredResources")) { LuaCheckArgs(l, 3); diff --git a/src/unit/build.cpp b/src/unit/build.cpp index 7b8a2e583..db8f403fa 100644 --- a/src/unit/build.cpp +++ b/src/unit/build.cpp @@ -55,13 +55,14 @@ ** ** @return the BuildingRestrictionDetails */ -CBuildRestrictionOnTop *OnTopDetails(const CUnit &unit, const CUnitType *parent) +CBuildRestrictionOnTop *OnTopDetails(std::vector<CBuildRestriction *> &restr, + const CUnit &unit, const CUnitType *parent) { CBuildRestrictionAnd *andb; CBuildRestrictionOnTop *ontopb; - for (std::vector<CBuildRestriction *>::iterator i = unit.Type->BuildingRules.begin(); - i != unit.Type->BuildingRules.end(); ++i) { + for (std::vector<CBuildRestriction *>::const_iterator i = restr.begin(); + i != restr.end(); ++i) { if ((ontopb = dynamic_cast<CBuildRestrictionOnTop *>(*i))) { if (!parent) { // Guess this is right @@ -91,10 +92,10 @@ CBuildRestrictionOnTop *OnTopDetails(const CUnit &unit, const CUnitType *parent) /** ** Check And Restriction */ -bool CBuildRestrictionAnd::Check(const CUnitType &type, const Vec2i &pos, CUnit *&ontoptarget) const +bool CBuildRestrictionAnd::Check(const CUnit *builder, const CUnitType &type, const Vec2i &pos, CUnit *&ontoptarget) const { for (std::vector<CBuildRestriction *>::const_iterator i = _or_list.begin(); i != _or_list.end(); ++i) { - if (!(*i)->Check(type, pos, ontoptarget)) { + if (!(*i)->Check(builder, type, pos, ontoptarget)) { return false; } } @@ -104,7 +105,7 @@ bool CBuildRestrictionAnd::Check(const CUnitType &type, const Vec2i &pos, CUnit /** ** Check Distance Restriction */ -bool CBuildRestrictionDistance::Check(const CUnitType &type, const Vec2i &pos, CUnit *&) const +bool CBuildRestrictionDistance::Check(const CUnit *builder, const CUnitType &type, const Vec2i &pos, CUnit *&) const { Vec2i pos1 = {0, 0}; Vec2i pos2 = {0, 0}; @@ -133,7 +134,7 @@ bool CBuildRestrictionDistance::Check(const CUnitType &type, const Vec2i &pos, C case GreaterThan : case GreaterThanEqual : for (size_t i = 0; i != table.size(); ++i) { - if (this->RestrictType == table[i]->Type && + if (builder != table[i] && this->RestrictType == table[i]->Type && MapDistanceBetweenTypes(type, pos, *table[i]->Type, table[i]->tilePos) <= distance) { return false; } @@ -142,7 +143,7 @@ bool CBuildRestrictionDistance::Check(const CUnitType &type, const Vec2i &pos, C case LessThan : case LessThanEqual : for (size_t i = 0; i != table.size(); ++i) { - if (this->RestrictType == table[i]->Type && + if (builder != table[i] && this->RestrictType == table[i]->Type && MapDistanceBetweenTypes(type, pos, *table[i]->Type, table[i]->tilePos) <= distance) { return true; } @@ -150,7 +151,7 @@ bool CBuildRestrictionDistance::Check(const CUnitType &type, const Vec2i &pos, C return false; case Equal : for (size_t i = 0; i != table.size(); ++i) { - if (this->RestrictType == table[i]->Type && + if (builder != table[i] && this->RestrictType == table[i]->Type && MapDistanceBetweenTypes(type, pos, *table[i]->Type, table[i]->tilePos) == distance) { return true; } @@ -158,7 +159,7 @@ bool CBuildRestrictionDistance::Check(const CUnitType &type, const Vec2i &pos, C return false; case NotEqual : for (size_t i = 0; i != table.size(); ++i) { - if (this->RestrictType == table[i]->Type && + if (builder != table[i] && this->RestrictType == table[i]->Type && MapDistanceBetweenTypes(type, pos, *table[i]->Type, table[i]->tilePos) == distance) { return false; } @@ -176,7 +177,7 @@ inline bool CBuildRestrictionAddOn::functor::operator()(const CUnit *const unit) /** ** Check AddOn Restriction */ -bool CBuildRestrictionAddOn::Check(const CUnitType &, const Vec2i &pos, CUnit *&) const +bool CBuildRestrictionAddOn::Check(const CUnit *, const CUnitType &, const Vec2i &pos, CUnit *&) const { Vec2i pos1 = pos - this->Offset; @@ -218,7 +219,7 @@ private: }; -bool CBuildRestrictionOnTop::Check(const CUnitType &, const Vec2i &pos, CUnit *&ontoptarget) const +bool CBuildRestrictionOnTop::Check(const CUnit *, const CUnitType &, const Vec2i &pos, CUnit *&ontoptarget) const { Assert(Map.Info.IsPointOnMap(pos)); @@ -291,7 +292,7 @@ CUnit *CanBuildHere(const CUnit *unit, const CUnitType &type, const Vec2i &pos) for (unsigned int i = 0; i < count; ++i) { CBuildRestriction *rule = type.BuildingRules[i]; // All checks processed, did we really have success - if (rule->Check(type, pos, ontoptarget)) { + if (rule->Check(unit, type, pos, ontoptarget)) { // We passed a full ruleset return if (unit == NULL) { return ontoptarget ? ontoptarget : (CUnit *)1; @@ -302,6 +303,27 @@ CUnit *CanBuildHere(const CUnit *unit, const CUnitType &type, const Vec2i &pos) } return NULL; } + + // Check special rules for AI players + if (unit->Player->AiEnabled) { + size_t count = type.AiBuildingRules.size(); + if (count > 0) { + ontoptarget = NULL; + for (unsigned int i = 0; i < count; ++i) { + CBuildRestriction *rule = type.AiBuildingRules[i]; + // All checks processed, did we really have success + if (rule->Check(unit, type, pos, ontoptarget)) { + // We passed a full ruleset return + if (unit == NULL) { + return ontoptarget ? ontoptarget : (CUnit *)1; + } else { + return ontoptarget ? ontoptarget : (CUnit *)unit; + } + } + } + return NULL; + } + } return (unit == NULL) ? (CUnit *)1 : const_cast<CUnit *>(unit); } diff --git a/src/unit/script_unittype.cpp b/src/unit/script_unittype.cpp index 53e90b171..402370797 100644 --- a/src/unit/script_unittype.cpp +++ b/src/unit/script_unittype.cpp @@ -489,7 +489,7 @@ static int CclDefineUnitType(lua_State *l) lua_pop(l, 1); ++k; lua_rawgeti(l, -1, k + 1); - type->_Storing[res] = LuaToNumber(l, -1); + type->DefaultStat.Storing[res] = LuaToNumber(l, -1); lua_pop(l, 1); } } else if (!strcmp(value, "ImproveProduction")) { @@ -745,6 +745,25 @@ static int CclDefineUnitType(lua_State *l) ParseBuildingRules(l, type->BuildingRules); lua_pop(l, 1); } + } else if (!strcmp(value, "AiBuildingRules")) { + if (!lua_istable(l, -1)) { + LuaError(l, "incorrect argument"); + } + subargs = lua_rawlen(l, -1); + // Free any old restrictions if they are redefined + for (std::vector<CBuildRestriction *>::iterator b = type->AiBuildingRules.begin(); + b != type->AiBuildingRules.end(); ++b) { + delete *b; + } + type->AiBuildingRules.clear(); + for (k = 0; k < subargs; ++k) { + lua_rawgeti(l, -1, k + 1); + if (!lua_istable(l, -1)) { + LuaError(l, "incorrect argument"); + } + ParseBuildingRules(l, type->AiBuildingRules); + lua_pop(l, 1); + } } else if (!strcmp(value, "BuilderOutside")) { type->BuilderOutside = LuaToBoolean(l, -1); } else if (!strcmp(value, "BuilderLost")) { @@ -1160,6 +1179,22 @@ static int CclDefineUnitStats(lua_State *l) stats->Costs[resId] = LuaToNumber(l, -1); lua_pop(l, 1); } + } else if (!strcmp(value, "storing")) { + if (!lua_istable(l, j + 1)) { + LuaError(l, "incorrect argument"); + } + const int subargs = lua_rawlen(l, j + 1); + + for (int k = 0; k < subargs; ++k) { + lua_rawgeti(l, j + 1, k + 1); + value = LuaToString(l, -1); + lua_pop(l, 1); + ++k; + const int resId = GetResourceIdByName(l, value); + lua_rawgeti(l, j + 1, k + 1); + stats->Storing[resId] = LuaToNumber(l, -1); + lua_pop(l, 1); + } } else { int i = UnitTypeVar.VariableNameLookup[value];// User variables if (i != -1) { // valid index diff --git a/src/unit/unit.cpp b/src/unit/unit.cpp index 85279b81d..aef5f0d40 100644 --- a/src/unit/unit.cpp +++ b/src/unit/unit.cpp @@ -367,7 +367,11 @@ void CUnit::Init(CUnitType &type) */ bool CUnit::RestoreOrder() { - if (this->SavedOrder == NULL) { + COrder *savedOrder = this->SavedOrder; + + if (savedOrder == NULL + || (savedOrder->Action == UnitActionAttack + && (!savedOrder->HasGoal() || savedOrder->GetGoal()->IsAlive()))) { return false; } @@ -380,7 +384,7 @@ bool CUnit::RestoreOrder() //copy - this->Orders.insert(this->Orders.begin() + 1, this->SavedOrder); + this->Orders.insert(this->Orders.begin() + 1, savedOrder); this->SavedOrder = NULL; return true; @@ -966,8 +970,8 @@ void UnitLost(CUnit &unit) player.Supply -= type.Supply; // Decrease resource limit for (int i = 0; i < MaxCosts; ++i) { - if (player.MaxResources[i] != -1 && type._Storing[i]) { - const int newMaxValue = player.MaxResources[i] - type._Storing[i]; + if (player.MaxResources[i] != -1 && type.Stats[player.Index].Storing[i]) { + const int newMaxValue = player.MaxResources[i] - type.Stats[player.Index].Storing[i]; player.MaxResources[i] = std::max(0, newMaxValue); player.SetResource(i, player.StoredResources[i], true); @@ -994,7 +998,7 @@ void UnitLost(CUnit &unit) DebugPrint("%d: Lost %s(%d)\n" _C_ player.Index _C_ type.Ident.c_str() _C_ UnitNumber(unit)); // Destroy resource-platform, must re-make resource patch. - if ((b = OnTopDetails(unit, NULL)) != NULL) { + if ((b = OnTopDetails(unit.Type->BuildingRules, unit, NULL)) != NULL) { if (b->ReplaceOnDie && (type.GivesResource && unit.ResourcesHeld != 0)) { temp = MakeUnitAndPlace(unit.tilePos, *b->Parent, &Players[PlayerNumNeutral]); if (temp == NoUnitP) { @@ -1040,8 +1044,8 @@ void UpdateForNewUnit(const CUnit &unit, int upgrade) if (!upgrade) { player.Supply += type.Supply; for (int i = 0; i < MaxCosts; ++i) { - if (player.MaxResources[i] != -1 && type._Storing[i]) { - player.MaxResources[i] += type._Storing[i]; + if (player.MaxResources[i] != -1 && type.Stats[player.Index].Storing[i]) { + player.MaxResources[i] += type.Stats[player.Index].Storing[i]; } } } @@ -1559,8 +1563,8 @@ void CUnit::ChangeOwner(CPlayer &newplayer) newplayer.Supply += Type->Supply; // Increase resource limit for (int i = 0; i < MaxCosts; ++i) { - if (newplayer.MaxResources[i] != -1 && Type->_Storing[i]) { - newplayer.MaxResources[i] += Type->_Storing[i]; + if (newplayer.MaxResources[i] != -1 && Type->Stats[newplayer.Index].Storing[i]) { + newplayer.MaxResources[i] += Type->Stats[newplayer.Index].Storing[i]; } } if (Type->Building && !Type->Wall) { diff --git a/src/unit/unittype.cpp b/src/unit/unittype.cpp index 11162ce5f..bb7adc314 100644 --- a/src/unit/unittype.cpp +++ b/src/unit/unittype.cpp @@ -163,7 +163,6 @@ CUnitType::CUnitType() : #ifdef USE_MNG memset(&Portrait, 0, sizeof(Portrait)); #endif - memset(_Storing, 0, sizeof(_Storing)); memset(RepairCosts, 0, sizeof(RepairCosts)); memset(CanStore, 0, sizeof(CanStore)); memset(ResInfo, 0, sizeof(ResInfo)); @@ -184,6 +183,11 @@ CUnitType::~CUnitType() delete *b; } BuildingRules.clear(); + for (std::vector<CBuildRestriction *>::iterator b = AiBuildingRules.begin(); + b != AiBuildingRules.end(); ++b) { + delete *b; + } + AiBuildingRules.clear(); delete[] CanCastSpell; delete[] AutoCastActive; @@ -378,6 +382,13 @@ static bool SaveUnitStats(const CUnitStats &stats, const CUnitType &type, int pl } file.printf("\"%s\", %d,", DefaultResourceNames[i].c_str(), stats.Costs[i]); } + file.printf("\"storing\", {"); + for (unsigned int i = 0; i < MaxCosts; ++i) { + if (i) { + file.printf(" "); + } + file.printf("\"%s\", %d,", DefaultResourceNames[i].c_str(), stats.Storing[i]); + } file.printf("})\n"); return true; } @@ -538,6 +549,12 @@ void InitUnitTypes(int reset_player_stats) b < type->BuildingRules.end(); ++b) { (*b)->Init(); } + + // Lookup AiBuildingTypes + for (std::vector<CBuildRestriction *>::iterator b = type->AiBuildingRules.begin(); + b < type->AiBuildingRules.end(); ++b) { + (*b)->Init(); + } } // LUDO : called after game is loaded -> don't reset stats ! diff --git a/src/unit/upgrade.cpp b/src/unit/upgrade.cpp index f3443dd8d..c259e6dcd 100644 --- a/src/unit/upgrade.cpp +++ b/src/unit/upgrade.cpp @@ -91,6 +91,7 @@ const CUnitStats &CUnitStats::operator = (const CUnitStats &rhs) { for (unsigned int i = 0; i < MaxCosts; ++i) { this->Costs[i] = rhs.Costs[i]; + this->Storing[i] = rhs.Storing[i]; } delete [] this->Variables; const unsigned int size = UnitTypeVar.GetNumberVariable(); @@ -106,6 +107,9 @@ bool CUnitStats::operator == (const CUnitStats &rhs) const if (this->Costs[i] != rhs.Costs[i]) { return false; } + if (this->Storing[i] != rhs.Storing[i]) { + return false; + } } for (unsigned int i = 0; i != UnitTypeVar.GetNumberVariable(); ++i) { if (this->Variables[i] != rhs.Variables[i]) { @@ -295,6 +299,17 @@ static int CclDefineModifier(lua_State *l) lua_rawgeti(l, j + 1, 2); um->Modifier.Costs[resId] = LuaToNumber(l, -1); lua_pop(l, 1); + } else if (!strcmp(key, "storing")) { + if (!lua_istable(l, j + 1) || lua_rawlen(l, j + 1) != 2) { + LuaError(l, "incorrect argument"); + } + lua_rawgeti(l, j + 1, 1); + value = LuaToString(l, -1); + lua_pop(l, 1); + const int resId = GetResourceIdByName(l, value); + lua_rawgeti(l, j + 1, 2); + um->Modifier.Storing[resId] = LuaToNumber(l, -1); + lua_pop(l, 1); } else if (!strcmp(key, "allow-unit")) { lua_rawgeti(l, j + 1, 2); value = LuaToString(l, -1); @@ -582,6 +597,7 @@ static void ApplyUpgradeModifier(CPlayer &player, const CUpgradeModifier *um) // upgrade costs :) for (unsigned int j = 0; j < MaxCosts; ++j) { UnitTypes[z]->Stats[pn].Costs[j] += um->Modifier.Costs[j]; + UnitTypes[z]->Stats[pn].Storing[j] += um->Modifier.Storing[j]; } int varModified = 0;