[+] 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.
This commit is contained in:
cybermind 2012-05-11 14:19:30 +06:00
parent 1944bfa6fb
commit c0de6fd1f0
15 changed files with 173 additions and 47 deletions

View file

@ -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.

View file

@ -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);
}
}

View file

@ -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();

View file

@ -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

View file

@ -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);

View file

@ -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));

View file

@ -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

View file

@ -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

View file

@ -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
};
/**

View file

@ -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);

View file

@ -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);
}

View file

@ -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

View file

@ -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) {

View file

@ -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 !

View file

@ -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;