diff --git a/CMakeLists.txt b/CMakeLists.txt index 7231ec6a8..2e38ae6db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,6 +90,7 @@ set(animation_SRCS src/animation/animation_goto.cpp src/animation/animation_ifvar.cpp src/animation/animation_label.cpp + src/animation/animation_luacallback.cpp src/animation/animation_move.cpp src/animation/animation_randomgoto.cpp src/animation/animation_randomrotate.cpp @@ -475,6 +476,7 @@ set(stratagus_animation_HDRS src/include/animation/animation_goto.h src/include/animation/animation_ifvar.h src/include/animation/animation_label.h + src/include/animation/animation_luacallback.h src/include/animation/animation_move.h src/include/animation/animation_randomgoto.h src/include/animation/animation_randomrotate.h diff --git a/src/action/action_built.cpp b/src/action/action_built.cpp index eaf101774..465daaa9d 100644 --- a/src/action/action_built.cpp +++ b/src/action/action_built.cpp @@ -346,7 +346,7 @@ void COrder_Built::Progress(CUnit &unit, int amount) Boost(unit, amount, HP_INDEX); Boost(unit, amount, SHIELD_INDEX); - this->ProgressCounter += amount * unit.Player->SpeedBuild / SPEEDUP_FACTOR; + this->ProgressCounter += std::max(1, amount * unit.Player->SpeedBuild / SPEEDUP_FACTOR); UpdateConstructionFrame(unit); } @@ -354,7 +354,7 @@ void COrder_Built::ProgressHp(CUnit &unit, int amount) { Boost(unit, amount, HP_INDEX); - this->ProgressCounter += amount * unit.Player->SpeedBuild / SPEEDUP_FACTOR; + this->ProgressCounter += std::max(1, amount * unit.Player->SpeedBuild / SPEEDUP_FACTOR); UpdateConstructionFrame(unit); } @@ -365,7 +365,7 @@ void COrder_Built::Boost(CUnit &building, int amount, int varIndex) const const int costs = building.Stats->Costs[TimeCost] * 600; const int progress = this->ProgressCounter; - const int newProgress = progress + amount * building.Player->SpeedBuild / SPEEDUP_FACTOR; + const int newProgress = progress + std::max(1, amount * building.Player->SpeedBuild / SPEEDUP_FACTOR); const int maxValue = building.Variable[varIndex].Max; int ¤tValue = building.Variable[varIndex].Value; diff --git a/src/action/action_follow.cpp b/src/action/action_follow.cpp index adc9227cf..9614bd342 100644 --- a/src/action/action_follow.cpp +++ b/src/action/action_follow.cpp @@ -38,6 +38,7 @@ #include "action/action_follow.h" #include "iolib.h" +#include "missile.h" #include "pathfinder.h" #include "script.h" #include "ui.h" @@ -168,6 +169,12 @@ enum { return ; } + // Don't follow after immobile units + if (goal && goal->CanMove() == false) { + this->Finished = true; + return; + } + if (goal->tilePos == this->goalPos) { // Move to the next order if (unit.Orders.size() > 1) { @@ -204,42 +211,51 @@ enum { // Handle Teleporter Units // FIXME: BAD HACK // goal shouldn't be busy and portal should be alive - if (goal->Type->Teleporter && goal->IsIdle() && goal->Goal - && goal->Goal->IsAlive() && unit.MapDistanceTo(*goal) <= 1) { - // Teleport the unit - unit.Remove(NULL); - unit.tilePos = goal->Goal->tilePos; - DropOutOnSide(unit, unit.Direction, NULL); -#if 0 - // FIXME: SoundForName() should be called once - PlayGameSound(SoundForName("invisibility"), MaxSampleVolume); - // FIXME: MissileTypeByIdent() should be called once - MakeMissile(MissileTypeByIdent("missile-normal-spell"), - unit.GetMapPixelPosCenter(), - unit.GetMapPixelPosCenter()); -#endif - // FIXME: we must check if the units supports the new order. - CUnit &dest = *goal->Goal; + if (goal->Type->Teleporter && goal->Goal && goal->Goal->IsAlive() && unit.MapDistanceTo(*goal) <= 1) { + if (!goal->IsIdle()) { // wait + unit.Wait = 10; + return; + } + // Check if we have enough mana + if (goal->Goal->Type->TeleportCost > goal->Variable[MANA_INDEX].Value) { + this->Finished = true; + return; + } else { + goal->Variable[MANA_INDEX].Value -= goal->Goal->Type->TeleportCost; + } + // Everything is OK, now teleport the unit + unit.Remove(NULL); + if (goal->Type->TeleportEffect.Missile) { + MakeMissile(*goal->Type->TeleportEffect.Missile, unit.GetMapPixelPosCenter(), unit.GetMapPixelPosCenter()); + } + unit.tilePos = goal->Goal->tilePos; + DropOutOnSide(unit, unit.Direction, NULL); - if (dest.NewOrder == NULL - || (dest.NewOrder->Action == UnitActionResource && !unit.Type->Harvester) - || (dest.NewOrder->Action == UnitActionAttack && !unit.Type->CanAttack) - || (dest.NewOrder->Action == UnitActionBoard && unit.Type->UnitType != UnitTypeLand)) { - this->Finished = true; - return ; - } else { - if (dest.NewOrder->HasGoal()) { - if (dest.NewOrder->GetGoal()->Destroyed) { - delete dest.NewOrder; - dest.NewOrder = NULL; - this->Finished = true; - return ; - } + // FIXME: we must check if the units supports the new order. + CUnit &dest = *goal->Goal; + if (dest.Type->TeleportEffect.Missile) { + MakeMissile(*dest.Type->TeleportEffect.Missile, unit.GetMapPixelPosCenter(), unit.GetMapPixelPosCenter()); + } + + if (dest.NewOrder == NULL + || (dest.NewOrder->Action == UnitActionResource && !unit.Type->Harvester) + || (dest.NewOrder->Action == UnitActionAttack && !unit.Type->CanAttack) + || (dest.NewOrder->Action == UnitActionBoard && unit.Type->UnitType != UnitTypeLand)) { + this->Finished = true; + return ; + } else { + if (dest.NewOrder->HasGoal()) { + if (dest.NewOrder->GetGoal()->Destroyed) { + delete dest.NewOrder; + dest.NewOrder = NULL; + this->Finished = true; + return ; } unit.Orders.insert(unit.Orders.begin() + 1, dest.NewOrder->Clone()); this->Finished = true; return ; } + } } this->goalPos = goal->tilePos; this->State = State_TargetReached; diff --git a/src/action/action_research.cpp b/src/action/action_research.cpp index 48561c49a..4c79cbfc3 100644 --- a/src/action/action_research.cpp +++ b/src/action/action_research.cpp @@ -131,7 +131,7 @@ } #endif CPlayer &player = *unit.Player; - player.UpgradeTimers.Upgrades[upgrade.ID] += player.SpeedResearch / SPEEDUP_FACTOR; + player.UpgradeTimers.Upgrades[upgrade.ID] += std::max(1, player.SpeedResearch / SPEEDUP_FACTOR); if (player.UpgradeTimers.Upgrades[upgrade.ID] >= upgrade.Costs[TimeCost]) { player.Notify(NotifyGreen, unit.tilePos, _("%s: research complete"), type.Name.c_str()); if (&player == ThisPlayer) { diff --git a/src/action/action_resource.cpp b/src/action/action_resource.cpp index 2faa19a5c..b0b221c63 100644 --- a/src/action/action_resource.cpp +++ b/src/action/action_resource.cpp @@ -470,7 +470,7 @@ int COrder_Resource::StartGathering(CUnit &unit) #endif UnitHeadingFromDeltaXY(unit, this->goalPos - unit.tilePos); if (resinfo.WaitAtResource) { - this->TimeToHarvest = resinfo.WaitAtResource / unit.Player->SpeedResourcesHarvest[resinfo.ResourceId] * SPEEDUP_FACTOR; + this->TimeToHarvest = std::max<int>(1, resinfo.WaitAtResource * SPEEDUP_FACTOR / unit.Player->SpeedResourcesHarvest[resinfo.ResourceId]); } else { this->TimeToHarvest = 1; } @@ -538,7 +538,7 @@ int COrder_Resource::StartGathering(CUnit &unit) goal->Resource.Active++; if (resinfo.WaitAtResource) { - this->TimeToHarvest = resinfo.WaitAtResource / unit.Player->SpeedResourcesHarvest[resinfo.ResourceId] * SPEEDUP_FACTOR; + this->TimeToHarvest = std::max<int>(1, resinfo.WaitAtResource * SPEEDUP_FACTOR / unit.Player->SpeedResourcesHarvest[resinfo.ResourceId]); } else { this->TimeToHarvest = 1; } @@ -671,7 +671,7 @@ int COrder_Resource::GatherResource(CUnit &unit) while (!this->DoneHarvesting && this->TimeToHarvest < 0) { //FIXME: rb - how should it look for WaitAtResource == 0 if (resinfo.WaitAtResource) { - this->TimeToHarvest += resinfo.WaitAtResource * SPEEDUP_FACTOR / unit.Player->SpeedResourcesHarvest[resinfo.ResourceId]; + this->TimeToHarvest += std::max<int>(1, resinfo.WaitAtResource * SPEEDUP_FACTOR / unit.Player->SpeedResourcesHarvest[resinfo.ResourceId]); } else { this->TimeToHarvest += 1; } @@ -954,7 +954,7 @@ int COrder_Resource::MoveToDepot(CUnit &unit) unit.CurrentResource = 0; if (unit.Wait) { - unit.Wait /= unit.Player->SpeedResourcesReturn[resinfo.ResourceId] / SPEEDUP_FACTOR; + unit.Wait /= std::max(1, unit.Player->SpeedResourcesReturn[resinfo.ResourceId] / SPEEDUP_FACTOR); if (unit.Wait) { unit.Wait--; } @@ -1103,6 +1103,9 @@ bool COrder_Resource::ActionResourceInit(CUnit &unit) unit.DeAssignWorkerFromMine(*mine); this->Resource.Mine = NULL; } + if (goal && goal->IsAlive() == false) { + return false; + } if (goal && goal->CurrentAction() != UnitActionBuilt) { unit.AssignWorkerToMine(*goal); this->Resource.Mine = goal; @@ -1133,6 +1136,7 @@ void COrder_Resource::Execute(CUnit &unit) // Let's start mining. if (this->State == SUB_START_RESOURCE) { if (ActionResourceInit(unit) == false) { + ResourceGiveUp(unit); return; } } diff --git a/src/action/action_spellcast.cpp b/src/action/action_spellcast.cpp index 28e72a918..2b1484097 100644 --- a/src/action/action_spellcast.cpp +++ b/src/action/action_spellcast.cpp @@ -333,7 +333,7 @@ bool COrder_SpellCast::SpellMoveToTarget(CUnit &unit) } // FALL THROUGH case 2: // Cast spell on the target. - if (!spell.IsCasterOnly()) { + if (!spell.IsCasterOnly() || spell.ForceUseAnimation) { AnimateActionSpellCast(unit, *this); if (unit.Anim.Unbreakable) { return ; diff --git a/src/action/action_train.cpp b/src/action/action_train.cpp index 2b388b26c..c60169f33 100644 --- a/src/action/action_train.cpp +++ b/src/action/action_train.cpp @@ -183,7 +183,7 @@ static void AnimateActionTrain(CUnit &unit) CPlayer &player = *unit.Player; const CUnitType &nType = *this->Type; const int cost = nType.Stats[player.Index].Costs[TimeCost]; - this->Ticks += player.SpeedTrain / SPEEDUP_FACTOR; + this->Ticks += std::max(1, player.SpeedTrain / SPEEDUP_FACTOR); if (this->Ticks < cost) { unit.Wait = CYCLES_PER_SECOND / 6; diff --git a/src/action/action_upgradeto.cpp b/src/action/action_upgradeto.cpp index 4a12e9511..39cf2b207 100644 --- a/src/action/action_upgradeto.cpp +++ b/src/action/action_upgradeto.cpp @@ -260,7 +260,7 @@ static void AnimateActionUpgradeTo(CUnit &unit) const CUnitType &newtype = *this->Type; const CUnitStats &newstats = newtype.Stats[player.Index]; - this->Ticks += player.SpeedUpgrade / SPEEDUP_FACTOR; + this->Ticks += std::max(1, player.SpeedUpgrade / SPEEDUP_FACTOR); if (this->Ticks < newstats.Costs[TimeCost]) { unit.Wait = CYCLES_PER_SECOND / 6; return ; diff --git a/src/action/command.cpp b/src/action/command.cpp index db456cdb3..97c963731 100644 --- a/src/action/command.cpp +++ b/src/action/command.cpp @@ -36,7 +36,10 @@ #include "stratagus.h" #include "actions.h" +#include "action/action_built.h" +#include "action/action_research.h" #include "action/action_train.h" +#include "action/action_upgradeto.h" #include "commands.h" #include "map.h" #include "pathfinder.h" @@ -63,8 +66,19 @@ static void ReleaseOrders(CUnit &unit) Assert(unit.Orders.empty() == false); // Order 0 must be stopped in the action loop. - for (size_t i = 1; i != unit.Orders.size(); ++i) { - delete unit.Orders[i]; + for (size_t i = 0; i != unit.Orders.size(); ++i) { + if (unit.Orders[i]->Action == UnitActionBuilt) { + (dynamic_cast<COrder_Built *>(unit.Orders[i]))->Cancel(unit); + } else if (unit.Orders[i]->Action == UnitActionResearch) { + (dynamic_cast<COrder_Research *>(unit.Orders[i]))->Cancel(unit); + } else if (unit.Orders[i]->Action == UnitActionTrain) { + (dynamic_cast<COrder_Train *>(unit.Orders[i]))->Cancel(unit); + } else if (unit.Orders[i]->Action == UnitActionUpgradeTo) { + (dynamic_cast<COrder_UpgradeTo *>(unit.Orders[i]))->Cancel(unit); + } + if (i > 0) { + delete unit.Orders[i]; + } } unit.Orders.resize(1); unit.Orders[0]->Finished = true; diff --git a/src/ai/ai.cpp b/src/ai/ai.cpp index 7e6508d2c..f137fa812 100644 --- a/src/ai/ai.cpp +++ b/src/ai/ai.cpp @@ -670,7 +670,7 @@ void AiHelpMe(const CUnit *attacker, CUnit &defender) return; } // Summoned unit, don't help - if (defender.GroupId == -1) { + if (defender.Summoned) { return; } diff --git a/src/ai/ai_force.cpp b/src/ai/ai_force.cpp index 71888d271..6cd69d8ab 100644 --- a/src/ai/ai_force.cpp +++ b/src/ai/ai_force.cpp @@ -448,14 +448,14 @@ void AiForce::ReturnToHome() AiForceManager::AiForceManager() { - forces.resize(3); + forces.resize(AI_MAX_FORCES); memset(script, -1, AI_MAX_FORCES * sizeof(char)); } -unsigned int AiForceManager::FindFreeForce(AiForceRole role) +unsigned int AiForceManager::FindFreeForce(AiForceRole role, int begin) { /* find free force */ - unsigned int f = 0; + unsigned int f = begin; while (f < forces.size() && (forces[f].State > AiForceAttackingState_Free)) { ++f; }; @@ -467,6 +467,30 @@ unsigned int AiForceManager::FindFreeForce(AiForceRole role) return f; } +/** +** Find unit in force +** +** @param unit Unit to search for. +** +** @return Force number, or -1 if not found +*/ + +int AiForceManager::GetForce(const CUnit &unit) +{ + for (unsigned int i = 0; i < forces.size(); ++i) { + AiForce &force = forces[i]; + + for (unsigned int j = 0; j < force.Units.size(); ++j) { + CUnit &aiunit = *force.Units[j]; + + if (UnitNumber(unit) == UnitNumber(aiunit)) { + return i; + } + } + } + return -1; +} + /** ** Cleanup units in forces. */ @@ -591,7 +615,7 @@ void AiAttackWithForceAt(unsigned int force, int x, int y) { const Vec2i pos(x, y); - if (!(force < AI_MAX_FORCES)) { + if (!(force < AI_MAX_FORCE_INTERNAL)) { DebugPrint("Force out of range: %d" _C_ force); return ; } @@ -611,7 +635,7 @@ void AiAttackWithForceAt(unsigned int force, int x, int y) */ void AiAttackWithForce(unsigned int force) { - if (!(force < AI_MAX_FORCES)) { + if (!(force < AI_MAX_FORCE_INTERNAL)) { DebugPrint("Force out of range: %d" _C_ force); return ; } @@ -620,7 +644,7 @@ void AiAttackWithForce(unsigned int force) // the first force, so we can reuse it if (!AiPlayer->Force[force].Defending) { unsigned int top; - unsigned int f = AiPlayer->Force.FindFreeForce(); + unsigned int f = AiPlayer->Force.FindFreeForce(AiForceRoleDefault, AI_MAX_FORCE_INTERNAL); AiPlayer->Force[f].Reset(); AiPlayer->Force[f].Role = AiPlayer->Force[force].Role; @@ -658,7 +682,7 @@ void AiAttackWithForces(int *forces) const Vec2i invalidPos(-1, -1); bool found = false; unsigned int top; - unsigned int f = AiPlayer->Force.FindFreeForce(); + unsigned int f = AiPlayer->Force.FindFreeForce(AiForceRoleDefault, AI_MAX_FORCE_INTERNAL); AiPlayer->Force[f].Reset(); diff --git a/src/ai/ai_local.h b/src/ai/ai_local.h index 396b29376..ba8e9474f 100644 --- a/src/ai/ai_local.h +++ b/src/ai/ai_local.h @@ -159,12 +159,12 @@ public: void ReturnToHome(); bool NewRallyPoint(const Vec2i &startPos, Vec2i *resultPos); + void Insert(CUnit &unit); private: void CountTypes(unsigned int *counter, const size_t len); bool IsBelongsTo(const CUnitType &type); - void Insert(CUnit &unit); - + void Update(); static void InternalRemoveUnit(CUnit *unit); @@ -186,7 +186,8 @@ public: }; // forces -#define AI_MAX_FORCES 50 /// How many forces are supported +#define AI_MAX_FORCES 50 /// How many forces are supported +#define AI_MAX_FORCE_INTERNAL (AI_MAX_FORCES / 2) /// The forces after AI_MAX_FORCE_INTERNAL are for internal use /** ** AI force manager. @@ -219,10 +220,11 @@ public: return script[index]; } + int GetForce(const CUnit &unit); void RemoveDeadUnit(); bool Assign(CUnit &unit); void Update(); - unsigned int FindFreeForce(AiForceRole role = AiForceRoleDefault); + unsigned int FindFreeForce(AiForceRole role = AiForceRoleDefault, int begin = 0); void CheckUnits(int *counter); private: std::vector<AiForce> forces; diff --git a/src/ai/ai_plan.cpp b/src/ai/ai_plan.cpp index dc881858c..e76818e55 100644 --- a/src/ai/ai_plan.cpp +++ b/src/ai/ai_plan.cpp @@ -116,14 +116,14 @@ class WallFinder { public: WallFinder(const CUnit &unit, int maxDist, Vec2i *resultPos) : - unit(unit), +// unit(unit), maxDist(maxDist), movemask(unit.Type->MovementMask & ~(MapFieldLandUnit | MapFieldAirUnit | MapFieldSeaUnit)), resultPos(resultPos) {} VisitResult Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from); private: - const CUnit &unit; +// const CUnit &unit; int maxDist; int movemask; Vec2i *resultPos; @@ -213,12 +213,12 @@ class ReachableTerrainMarker { public: ReachableTerrainMarker(const CUnit &unit) : - unit(unit), +// unit(unit), movemask(unit.Type->MovementMask & ~(MapFieldLandUnit | MapFieldAirUnit | MapFieldSeaUnit)) {} VisitResult Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from); private: - const CUnit &unit; +// const CUnit &unit; int movemask; }; diff --git a/src/ai/script_ai.cpp b/src/ai/script_ai.cpp index b6e07d8b9..313eb1d21 100644 --- a/src/ai/script_ai.cpp +++ b/src/ai/script_ai.cpp @@ -692,7 +692,7 @@ static int CclAiForce(lua_State *l) resetForce = LuaToBoolean(l, 3); } int force = LuaToNumber(l, 1); - if (force < 0 || force >= AI_MAX_FORCES) { + if (force < 0 || force >= AI_MAX_FORCE_INTERNAL) { LuaError(l, "Force out of range: %d" _C_ force); } AiForce &aiforce = AiPlayer->Force[AiPlayer->Force.getScriptForce(force)]; @@ -762,7 +762,7 @@ static int CclAiForceRole(lua_State *l) { LuaCheckArgs(l, 2); int force = LuaToNumber(l, 1); - if (force < 0 || force >= AI_MAX_FORCES) { + if (force < 0 || force >= AI_MAX_FORCE_INTERNAL) { LuaError(l, "Force %i out of range" _C_ force); } @@ -789,7 +789,7 @@ static int CclAiCheckForce(lua_State *l) { LuaCheckArgs(l, 1); int force = LuaToNumber(l, 1); - if (force < 0 || force >= AI_MAX_FORCES) { + if (force < 0 || force >= AI_MAX_FORCE_INTERNAL) { lua_pushfstring(l, "Force out of range: %d", force); } if (AiPlayer->Force[AiPlayer->Force.getScriptForce(force)].Completed) { @@ -809,7 +809,7 @@ static int CclAiWaitForce(lua_State *l) { LuaCheckArgs(l, 1); int force = LuaToNumber(l, 1); - if (force < 0 || force >= AI_MAX_FORCES) { + if (force < 0 || force >= AI_MAX_FORCE_INTERNAL) { lua_pushfstring(l, "Force out of range: %d", force); } if (AiPlayer->Force[AiPlayer->Force.getScriptForce(force)].Completed) { @@ -834,7 +834,7 @@ static int CclAiAttackWithForce(lua_State *l) { LuaCheckArgs(l, 1); int force = LuaToNumber(l, 1); - if (force < 0 || force >= AI_MAX_FORCES) { + if (force < 0 || force >= AI_MAX_FORCE_INTERNAL) { LuaError(l, "Force out of range: %d" _C_ force); } AiAttackWithForce(AiPlayer->Force.getScriptForce(force)); @@ -857,7 +857,7 @@ static int CclAiWaitForces(lua_State *l) for (int i = 0; i < args; ++i) { const int force = LuaToNumber(l, 1, i + 1); - if (force < 0 || force >= AI_MAX_FORCES) { + if (force < 0 || force >= AI_MAX_FORCE_INTERNAL) { lua_pushfstring(l, "Force out of range: %d", force); } if (!AiPlayer->Force[AiPlayer->Force.getScriptForce(force)].Completed) { @@ -876,7 +876,7 @@ static int CclAiWaitForces(lua_State *l) */ static int CclAiAttackWithForces(lua_State *l) { - int Forces[AI_MAX_FORCES + 1]; + int Forces[AI_MAX_FORCE_INTERNAL + 1]; LuaCheckArgs(l, 1); if (!lua_istable(l, 1)) { @@ -886,7 +886,7 @@ static int CclAiAttackWithForces(lua_State *l) for (int i = 0; i < args; ++i) { const int force = LuaToNumber(l, 1, i + 1); - if (force < 0 || force >= AI_MAX_FORCES) { + if (force < 0 || force >= AI_MAX_FORCE_INTERNAL) { lua_pushfstring(l, "Force out of range: %d", force); } Forces[i] = AiPlayer->Force.getScriptForce(force); diff --git a/src/animation/animation.cpp b/src/animation/animation.cpp index fcbcad9c5..5e6175002 100644 --- a/src/animation/animation.cpp +++ b/src/animation/animation.cpp @@ -46,6 +46,7 @@ #include "animation/animation_goto.h" #include "animation/animation_ifvar.h" #include "animation/animation_label.h" +#include "animation/animation_luacallback.h" #include "animation/animation_move.h" #include "animation/animation_randomgoto.h" #include "animation/animation_randomrotate.h" @@ -133,17 +134,17 @@ static int ParseAnimPlayer(const CUnit &unit, const char *parseint) ** @return The parsed value. */ -int ParseAnimInt(const CUnit *unit, const char *parseint) +int ParseAnimInt(const CUnit &unit, const char *parseint) { char s[100]; - const CUnit *goal = unit; + const CUnit *goal = &unit; strcpy(s, parseint); char *cur = &s[2]; - if ((s[0] == 'v' || s[0] == 't') && unit != NULL) { //unit variable detected + if (s[0] == 'v' || s[0] == 't') { //unit variable detected if (s[0] == 't') { - if (unit->CurrentOrder()->HasGoal()) { - goal = unit->CurrentOrder()->GetGoal(); + if (unit.CurrentOrder()->HasGoal()) { + goal = unit.CurrentOrder()->GetGoal(); } else { return 0; } @@ -151,7 +152,7 @@ int ParseAnimInt(const CUnit *unit, const char *parseint) char *next = strchr(cur, '.'); if (next == NULL) { fprintf(stderr, "Need also specify the variable '%s' tag \n", cur); - Exit(1); + ExitFatal(1); } else { *next = '\0'; } @@ -163,7 +164,7 @@ int ParseAnimInt(const CUnit *unit, const char *parseint) return goal->Resource.Active; } fprintf(stderr, "Bad variable name '%s'\n", cur); - Exit(1); + ExitFatal(1); } if (!strcmp(next + 1, "Value")) { return goal->Variable[index].Value; @@ -177,10 +178,10 @@ int ParseAnimInt(const CUnit *unit, const char *parseint) return goal->Variable[index].Value * 100 / goal->Variable[index].Max; } return 0; - } else if ((s[0] == 'b' || s[0] == 'g') && unit != NULL) { //unit bool flag detected + } else if (s[0] == 'b' || s[0] == 'g') { //unit bool flag detected if (s[0] == 'g') { - if (unit->CurrentOrder()->HasGoal()) { - goal = unit->CurrentOrder()->GetGoal(); + if (unit.CurrentOrder()->HasGoal()) { + goal = unit.CurrentOrder()->GetGoal(); } else { return 0; } @@ -188,11 +189,10 @@ int ParseAnimInt(const CUnit *unit, const char *parseint) const int index = UnitTypeVar.BoolFlagNameLookup[cur];// User bool flags if (index == -1) { fprintf(stderr, "Bad bool-flag name '%s'\n", cur); - Exit(1); - return 0; + ExitFatal(1); } return goal->Type->BoolFlag[index].value; - } else if ((s[0] == 's') && unit != NULL) { //spell type detected + } else if (s[0] == 's') { //spell type detected Assert(goal->CurrentAction() == UnitActionSpellCast); const COrder_SpellCast &order = *static_cast<COrder_SpellCast *>(goal->CurrentOrder()); const SpellType &spell = order.GetSpell(); @@ -200,11 +200,11 @@ int ParseAnimInt(const CUnit *unit, const char *parseint) return 1; } return 0; - } else if (s[0] == 'p' && unit != NULL) { //player variable detected + } else if (s[0] == 'p') { //player variable detected char *next = strchr(cur, '.'); if (next == NULL) { fprintf(stderr, "Need also specify the %s player's property\n", cur); - Exit(1); + ExitFatal(1); } else { *next = '\0'; } @@ -212,7 +212,7 @@ int ParseAnimInt(const CUnit *unit, const char *parseint) if (arg != NULL) { *arg = '\0'; } - return GetPlayerData(ParseAnimPlayer(*unit, cur), next + 1, arg + 1); + return GetPlayerData(ParseAnimPlayer(unit, cur), next + 1, arg + 1); } else if (s[0] == 'r') { //random value char *next = strchr(cur, '.'); if (next == NULL) { @@ -223,12 +223,72 @@ int ParseAnimInt(const CUnit *unit, const char *parseint) return min + SyncRand(atoi(next + 1) - min + 1); } } else if (s[0] == 'l') { //player number - return ParseAnimPlayer(*unit, cur); + return ParseAnimPlayer(unit, cur); } return atoi(parseint); } +/** +** Parse flags list in animation frame. +** +** @param unit Unit of the animation. +** @param parseflag Flag list to parse. +** +** @return The parsed value. +*/ +int ParseAnimFlags(const CUnit &unit, const char *parseflag) +{ + char s[100]; + int flags = 0; + + strcpy(s, parseflag); + char *cur = s; + char *next = s; + while (next && *next) { + next = strchr(cur, '.'); + if (next) { + *next = '\0'; + ++next; + } + if (unit.Anim.Anim->Type == AnimationSpawnMissile) { + if (!strcmp(cur, "none")) { + flags = SM_None; + return flags; + } else if (!strcmp(cur, "damage")) { + flags |= SM_Damage; + } else if (!strcmp(cur, "totarget")) { + flags |= SM_ToTarget; + } else if (!strcmp(cur, "pixel")) { + flags |= SM_Pixel; + } else if (!strcmp(cur, "reltarget")) { + flags |= SM_RelTarget; + } else if (!strcmp(cur, "ranged")) { + flags |= SM_Ranged; + } else if (!strcmp(cur, "setdirection")) { + flags |= SM_SetDirection; + } else { + fprintf(stderr, "Unknown animation flag: %s\n", cur); + ExitFatal(1); + } + } else if (unit.Anim.Anim->Type == AnimationSpawnUnit) { + if (!strcmp(cur, "none")) { + flags = SU_None; + return flags; + } else if (!strcmp(cur, "summoned")) { + flags |= SU_Summoned; + } else if (!strcmp(cur, "jointoai")) { + flags |= SU_JoinToAIForce; + } else { + fprintf(stderr, "Unknown animation flag: %s\n", cur); + ExitFatal(1); + } + } + cur = next; + } + return flags; +} + /** ** Show unit animation. @@ -479,10 +539,12 @@ static CAnimation *ParseAnimationFrame(lua_State *l, const char *str) anim = new CAnimation_Goto; } else if (op1 == "random-goto") { anim = new CAnimation_RandomGoto; + } else if (op1 == "lua-callback") { + anim = new CAnimation_LuaCallback; } else { LuaError(l, "Unknown animation: %s" _C_ op1.c_str()); } - anim->Init(extraArg.c_str()); + anim->Init(extraArg.c_str(), l); return anim; } diff --git a/src/animation/animation_attack.cpp b/src/animation/animation_attack.cpp index a777ea5aa..bcab1855a 100644 --- a/src/animation/animation_attack.cpp +++ b/src/animation/animation_attack.cpp @@ -47,7 +47,7 @@ } -/* virtual */ void CAnimation_Attack::Init(const char *s) +/* virtual */ void CAnimation_Attack::Init(const char *s, lua_State *) { } diff --git a/src/animation/animation_die.cpp b/src/animation/animation_die.cpp index 79515fdc5..2cdfd0cbc 100644 --- a/src/animation/animation_die.cpp +++ b/src/animation/animation_die.cpp @@ -54,7 +54,7 @@ throw AnimationDie_Exception(); } -/* virtual */ void CAnimation_Die::Init(const char *s) +/* virtual */ void CAnimation_Die::Init(const char *s, lua_State *) { this->DeathType = s; } diff --git a/src/animation/animation_exactframe.cpp b/src/animation/animation_exactframe.cpp index 73992924a..f03bf2790 100644 --- a/src/animation/animation_exactframe.cpp +++ b/src/animation/animation_exactframe.cpp @@ -45,14 +45,18 @@ unit.Frame = ParseAnimInt(&unit); } -/* virtual */ void CAnimation_ExactFrame::Init(const char *s) +/* virtual */ void CAnimation_ExactFrame::Init(const char *s, lua_State *) { this->frame = s; } int CAnimation_ExactFrame::ParseAnimInt(const CUnit *unit) const { - return ::ParseAnimInt(unit, this->frame.c_str()); + if (unit == NULL) { + return atoi(this->frame.c_str()); + } else { + return ::ParseAnimInt(*unit, this->frame.c_str()); + } } //@} diff --git a/src/animation/animation_frame.cpp b/src/animation/animation_frame.cpp index cb512cfb0..57c94ef7e 100644 --- a/src/animation/animation_frame.cpp +++ b/src/animation/animation_frame.cpp @@ -46,14 +46,18 @@ UnitUpdateHeading(unit); } -/* virtual */ void CAnimation_Frame::Init(const char *s) +/* virtual */ void CAnimation_Frame::Init(const char *s, lua_State *) { this->frame = s; } int CAnimation_Frame::ParseAnimInt(const CUnit *unit) const { - return ::ParseAnimInt(unit, this->frame.c_str()); + if (unit == NULL) { + return atoi(this->frame.c_str()); + } else { + return ::ParseAnimInt(*unit, this->frame.c_str()); + } } //@} diff --git a/src/animation/animation_goto.cpp b/src/animation/animation_goto.cpp index 0f58bc17f..4216d6d3a 100644 --- a/src/animation/animation_goto.cpp +++ b/src/animation/animation_goto.cpp @@ -46,7 +46,7 @@ unit.Anim.Anim = this->gotoLabel; } -/* virtual */ void CAnimation_Goto::Init(const char *s) +/* virtual */ void CAnimation_Goto::Init(const char *s, lua_State *) { FindLabelLater(&this->gotoLabel, s); } diff --git a/src/animation/animation_ifvar.cpp b/src/animation/animation_ifvar.cpp index 254c8b9c9..7eb6da399 100644 --- a/src/animation/animation_ifvar.cpp +++ b/src/animation/animation_ifvar.cpp @@ -61,8 +61,8 @@ static bool returnFalse(int, int) { return false; } { Assert(unit.Anim.Anim == this); - const int lop = ParseAnimInt(&unit, this->leftVar.c_str()); - const int rop = ParseAnimInt(&unit, this->rightVar.c_str()); + const int lop = ParseAnimInt(unit, this->leftVar.c_str()); + const int rop = ParseAnimInt(unit, this->rightVar.c_str()); const bool cond = this->binOpFunc(lop, rop); if (cond) { @@ -73,7 +73,7 @@ static bool returnFalse(int, int) { return false; } /* ** s = "leftOp Op rigthOp gotoLabel" */ -/* virtual */ void CAnimation_IfVar::Init(const char *s) +/* virtual */ void CAnimation_IfVar::Init(const char *s, lua_State *) { const std::string str(s); const size_t len = str.size(); diff --git a/src/animation/animation_label.cpp b/src/animation/animation_label.cpp index dfb7264ea..84bf7d371 100644 --- a/src/animation/animation_label.cpp +++ b/src/animation/animation_label.cpp @@ -44,7 +44,7 @@ Assert(unit.Anim.Anim == this); } -/* virtual */ void CAnimation_Label::Init(const char *s) +/* virtual */ void CAnimation_Label::Init(const char *s, lua_State *) { } diff --git a/src/animation/animation_luacallback.cpp b/src/animation/animation_luacallback.cpp new file mode 100644 index 000000000..055498b5c --- /dev/null +++ b/src/animation/animation_luacallback.cpp @@ -0,0 +1,84 @@ +// _________ __ __ +// / _____// |_____________ _/ |______ ____ __ __ ______ +// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/ +// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ | +// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ > +// \/ \/ \//_____/ \/ +// ______________________ ______________________ +// T H E W A R B E G I N S +// Stratagus - A free fantasy real time strategy game engine +// +/**@name animation_luacallback.cpp - The animation LuaCallback. */ +// +// (c) Copyright 2013 by cybermind +// +// 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; only version 2 of the License. +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// + +//@{ + +/*---------------------------------------------------------------------------- +-- Includes +----------------------------------------------------------------------------*/ + +#include "stratagus.h" + +#include "animation/animation_luacallback.h" + +#include "script.h" +#include "unit.h" + + +/* virtual */ void CAnimation_LuaCallback::Action(CUnit &unit, int &/*move*/, int /*scale*/) const +{ + Assert(unit.Anim.Anim == this); + Assert(cb); + + cb->pushPreamble(); + for (std::vector<std::string>::const_iterator it = cbArgs.begin(); it != cbArgs.end(); ++it) { + const std::string str = *it; + + const int arg = ParseAnimInt(unit, str.c_str()); + cb->pushInteger(arg); + } + cb->run(); +} + +/* +** s = "cbName cbArg1 [cbArgN ...]" +*/ +/* virtual */ void CAnimation_LuaCallback::Init(const char *s, lua_State *l) +{ + const std::string str(s); + const size_t len = str.size(); + + size_t begin = 0; + size_t end = str.find(' ', begin); + this->cbName.assign(str, begin, end - begin); + + lua_getglobal(l, cbName.c_str()); + cb = new LuaCallback(l, -1); + lua_pop(l, 1); + + for (size_t begin = std::min(len, str.find_first_not_of(' ', end)); + begin != std::string::npos;) { + end = std::min(len, str.find(' ', begin)); + + this->cbArgs.push_back(str.substr(begin, end - begin)); + begin = str.find_first_not_of(' ', end); + } +} + +//@} diff --git a/src/animation/animation_move.cpp b/src/animation/animation_move.cpp index a8e7077f2..88d9c034f 100644 --- a/src/animation/animation_move.cpp +++ b/src/animation/animation_move.cpp @@ -44,10 +44,10 @@ Assert(unit.Anim.Anim == this); Assert(!move); - move = ParseAnimInt(&unit, this->moveStr.c_str()); + move = ParseAnimInt(unit, this->moveStr.c_str()); } -/* virtual */ void CAnimation_Move::Init(const char *s) +/* virtual */ void CAnimation_Move::Init(const char *s, lua_State *) { this->moveStr = s; } diff --git a/src/animation/animation_randomgoto.cpp b/src/animation/animation_randomgoto.cpp index 73e51565d..7467e1cb6 100644 --- a/src/animation/animation_randomgoto.cpp +++ b/src/animation/animation_randomgoto.cpp @@ -43,7 +43,7 @@ { Assert(unit.Anim.Anim == this); - if (SyncRand() % 100 < ParseAnimInt(&unit, this->randomStr.c_str())) { + if (SyncRand() % 100 < ParseAnimInt(unit, this->randomStr.c_str())) { unit.Anim.Anim = this->gotoLabel; } } @@ -51,7 +51,7 @@ /* ** s : "percent label" */ -/* virtual */ void CAnimation_RandomGoto::Init(const char *s) +/* virtual */ void CAnimation_RandomGoto::Init(const char *s, lua_State *) { const std::string str(s); const size_t len = str.size(); diff --git a/src/animation/animation_randomrotate.cpp b/src/animation/animation_randomrotate.cpp index 4b8efde4f..f6969c90f 100644 --- a/src/animation/animation_randomrotate.cpp +++ b/src/animation/animation_randomrotate.cpp @@ -45,13 +45,13 @@ Assert(unit.Anim.Anim == this); if ((SyncRand() >> 8) & 1) { - UnitRotate(unit, -ParseAnimInt(&unit, this->rotateStr.c_str())); + UnitRotate(unit, -ParseAnimInt(unit, this->rotateStr.c_str())); } else { - UnitRotate(unit, ParseAnimInt(&unit, this->rotateStr.c_str())); + UnitRotate(unit, ParseAnimInt(unit, this->rotateStr.c_str())); } } -/* virtual */ void CAnimation_RandomRotate::Init(const char *s) +/* virtual */ void CAnimation_RandomRotate::Init(const char *s, lua_State *) { this->rotateStr = s; } diff --git a/src/animation/animation_randomsound.cpp b/src/animation/animation_randomsound.cpp index f7d4f0a5b..f28d658ef 100644 --- a/src/animation/animation_randomsound.cpp +++ b/src/animation/animation_randomsound.cpp @@ -54,7 +54,7 @@ /* ** s = "Sound1 [SoundN ...]" */ -/* virtual */ void CAnimation_RandomSound::Init(const char *s) +/* virtual */ void CAnimation_RandomSound::Init(const char *s, lua_State *) { const std::string str(s); const size_t len = str.size(); diff --git a/src/animation/animation_randomwait.cpp b/src/animation/animation_randomwait.cpp index a0f705573..341a58c61 100644 --- a/src/animation/animation_randomwait.cpp +++ b/src/animation/animation_randomwait.cpp @@ -43,8 +43,8 @@ { Assert(unit.Anim.Anim == this); - const int arg1 = ParseAnimInt(&unit, this->minWait.c_str()); - const int arg2 = ParseAnimInt(&unit, this->maxWait.c_str()); + const int arg1 = ParseAnimInt(unit, this->minWait.c_str()); + const int arg2 = ParseAnimInt(unit, this->maxWait.c_str()); unit.Anim.Wait = arg1 + SyncRand() % (arg2 - arg1 + 1); } @@ -52,7 +52,7 @@ /* ** s = "minWait MaxWait" */ -/* virtual */ void CAnimation_RandomWait::Init(const char *s) +/* virtual */ void CAnimation_RandomWait::Init(const char *s, lua_State *) { const std::string str(s); const size_t len = str.size(); diff --git a/src/animation/animation_rotate.cpp b/src/animation/animation_rotate.cpp index b46944840..b15eab44b 100644 --- a/src/animation/animation_rotate.cpp +++ b/src/animation/animation_rotate.cpp @@ -66,11 +66,11 @@ void UnitRotate(CUnit &unit, int rotate) const Vec2i pos = target.tilePos + target.Type->GetHalfTileSize() - unit.tilePos; UnitHeadingFromDeltaXY(unit, pos); } else { - UnitRotate(unit, ParseAnimInt(&unit, this->rotateStr.c_str())); + UnitRotate(unit, ParseAnimInt(unit, this->rotateStr.c_str())); } } -/* virtual */ void CAnimation_Rotate::Init(const char *s) +/* virtual */ void CAnimation_Rotate::Init(const char *s, lua_State *) { this->rotateStr = s; } diff --git a/src/animation/animation_setplayervar.cpp b/src/animation/animation_setplayervar.cpp index 7d78a2f85..80981a7c4 100644 --- a/src/animation/animation_setplayervar.cpp +++ b/src/animation/animation_setplayervar.cpp @@ -42,13 +42,6 @@ #include <stdio.h> -//Modify types -#define MOD_ADD 1 -#define MOD_SUB 2 -#define MOD_MUL 3 -#define MOD_DIV 4 -#define MOD_MOD 5 - /** ** Gets the player data. ** @@ -185,34 +178,46 @@ static void SetPlayerData(const int player, const char *prop, const char *arg, i const char *var = this->varStr.c_str(); const char *arg = this->argStr.c_str(); - const int playerId = ParseAnimInt(&unit, this->playerStr.c_str()); - int rop = ParseAnimInt(&unit, this->valueStr.c_str()); + const int playerId = ParseAnimInt(unit, this->playerStr.c_str()); + int rop = ParseAnimInt(unit, this->valueStr.c_str()); int data = GetPlayerData(playerId, var, arg); switch (this->mod) { - case MOD_ADD: + case modAdd: data += rop; break; - case MOD_SUB: + case modSub: data -= rop; break; - case MOD_MUL: + case modMul: data *= rop; break; - case MOD_DIV: + case modDiv: if (!rop) { fprintf(stderr, "Division by zero in AnimationSetPlayerVar\n"); Exit(1); } data /= rop; break; - case MOD_MOD: + case modMod: if (!rop) { fprintf(stderr, "Division by zero in AnimationSetPlayerVar\n"); Exit(1); } data %= rop; break; + case modAnd: + data &= rop; + break; + case modOr: + data |= rop; + break; + case modXor: + data ^= rop; + break; + case modNot: + data = !data; + break; default: data = rop; } @@ -223,7 +228,7 @@ static void SetPlayerData(const int player, const char *prop, const char *arg, i /* ** s = "player var mod value [arg2]" */ -/* virtual */ void CAnimation_SetPlayerVar::Init(const char *s) +/* virtual */ void CAnimation_SetPlayerVar::Init(const char *s, lua_State *) { const std::string str(s); const size_t len = str.size(); @@ -239,7 +244,29 @@ static void SetPlayerData(const int player, const char *prop, const char *arg, i begin = std::min(len, str.find_first_not_of(' ', end)); end = std::min(len, str.find(' ', begin)); const std::string modStr(str, begin, end - begin); - this->mod = atoi(modStr.c_str()); + if (modStr == "=") { + this->mod = modSet; + } else if (modStr == "+=") { + this->mod = modAdd; + } else if (modStr == "-=") { + this->mod = modSub; + } else if (modStr == "*=") { + this->mod = modMul; + } else if (modStr == "/=") { + this->mod = modDiv; + } else if (modStr == "%=") { + this->mod = modMod; + } else if (modStr == "&=") { + this->mod = modAnd; + } else if (modStr == "|=") { + this->mod = modOr; + } else if (modStr == "^=") { + this->mod = modXor; + } else if (modStr == "!") { + this->mod = modNot; + } else { + this->mod = (SetVar_ModifyTypes)(atoi(modStr.c_str())); + } begin = std::min(len, str.find_first_not_of(' ', end)); end = std::min(len, str.find(' ', begin)); diff --git a/src/animation/animation_setvar.cpp b/src/animation/animation_setvar.cpp index e42fb6312..0f7957170 100644 --- a/src/animation/animation_setvar.cpp +++ b/src/animation/animation_setvar.cpp @@ -43,13 +43,6 @@ #include <stdio.h> -//Modify types -#define MOD_ADD 1 -#define MOD_SUB 2 -#define MOD_MUL 3 -#define MOD_DIV 4 -#define MOD_MOD 5 - /* virtual */ void CAnimation_SetVar::Action(CUnit &unit, int &/*move*/, int /*scale*/) const { @@ -102,7 +95,7 @@ return; } - const int rop = ParseAnimInt(&unit, this->valueStr.c_str()); + const int rop = ParseAnimInt(unit, this->valueStr.c_str()); int value = 0; if (!strcmp(next + 1, "Value")) { value = goal->Variable[index].Value; @@ -116,16 +109,16 @@ value = goal->Variable[index].Value * 100 / goal->Variable[index].Max; } switch (this->mod) { - case MOD_ADD: + case modAdd: value += rop; break; - case MOD_SUB: + case modSub: value -= rop; break; - case MOD_MUL: + case modMul: value *= rop; break; - case MOD_DIV: + case modDiv: if (!rop) { fprintf(stderr, "Division by zero in AnimationSetVar\n"); Exit(1); @@ -133,7 +126,7 @@ } value /= rop; break; - case MOD_MOD: + case modMod: if (!rop) { fprintf(stderr, "Division by zero in AnimationSetVar\n"); Exit(1); @@ -141,6 +134,18 @@ } value %= rop; break; + case modAnd: + value &= rop; + break; + case modOr: + value |= rop; + break; + case modXor: + value ^= rop; + break; + case modNot: + value = !value; + break; default: value = rop; } @@ -161,7 +166,7 @@ /* ** s = "var mod value [unitSlot]" */ -/* virtual */ void CAnimation_SetVar::Init(const char *s) +/* virtual */ void CAnimation_SetVar::Init(const char *s, lua_State *) { const std::string str(s); const size_t len = str.size(); @@ -173,7 +178,30 @@ begin = std::min(len, str.find_first_not_of(' ', end)); end = std::min(len, str.find(' ', begin)); const std::string modStr(str, begin, end - begin); - this->mod = atoi(modStr.c_str()); + + if (modStr == "=") { + this->mod = modSet; + } else if (modStr == "+=") { + this->mod = modAdd; + } else if (modStr == "-=") { + this->mod = modSub; + } else if (modStr == "*=") { + this->mod = modMul; + } else if (modStr == "/=") { + this->mod = modDiv; + } else if (modStr == "%=") { + this->mod = modMod; + } else if (modStr == "&=") { + this->mod = modAnd; + } else if (modStr == "|=") { + this->mod = modOr; + } else if (modStr == "^=") { + this->mod = modXor; + } else if (modStr == "!") { + this->mod = modNot; + } else { + this->mod = (SetVar_ModifyTypes)(atoi(modStr.c_str())); + } begin = std::min(len, str.find_first_not_of(' ', end)); end = std::min(len, str.find(' ', begin)); diff --git a/src/animation/animation_sound.cpp b/src/animation/animation_sound.cpp index 355f5e151..16efd5d97 100644 --- a/src/animation/animation_sound.cpp +++ b/src/animation/animation_sound.cpp @@ -49,7 +49,7 @@ } } -/* virtual */ void CAnimation_Sound::Init(const char *s) +/* virtual */ void CAnimation_Sound::Init(const char *s, lua_State *) { this->sound.Name = s; } diff --git a/src/animation/animation_spawnmissile.cpp b/src/animation/animation_spawnmissile.cpp index ce001db9f..b3e22d49a 100644 --- a/src/animation/animation_spawnmissile.cpp +++ b/src/animation/animation_spawnmissile.cpp @@ -46,68 +46,17 @@ #include "pathfinder.h" #include "unit.h" -//SpawnMissile flags -#define ANIM_SM_DAMAGE 1 -#define ANIM_SM_TOTARGET 2 -#define ANIM_SM_PIXEL 4 -#define ANIM_SM_RELTARGET 8 -#define ANIM_SM_RANGED 16 -#define ANIM_SM_SETDIRECTION 32 - -/** -** Parse flags list in animation frame. -** -** @param unit Unit of the animation. -** @param parseflag Flag list to parse. -** -** @return The parsed value. -*/ -static int ParseAnimFlags(CUnit &unit, const char *parseflag) -{ - char s[100]; - int flags = 0; - - strcpy(s, parseflag); - char *cur = s; - char *next = s; - while (next) { - next = strchr(cur, '.'); - if (next) { - *next = '\0'; - ++next; - } - if (unit.Anim.Anim->Type == AnimationSpawnMissile) { - if (!strcmp(cur, "damage")) { - flags |= ANIM_SM_DAMAGE; - } else if (!strcmp(cur, "totarget")) { - flags |= ANIM_SM_TOTARGET; - } else if (!strcmp(cur, "pixel")) { - flags |= ANIM_SM_PIXEL; - } else if (!strcmp(cur, "reltarget")) { - flags |= ANIM_SM_RELTARGET; - } else if (!strcmp(cur, "ranged")) { - flags |= ANIM_SM_RANGED; - } else if (!strcmp(cur, "setdirection")) { - flags |= ANIM_SM_SETDIRECTION; - } - } - cur = next; - } - return flags; -} - - /* virtual */ void CAnimation_SpawnMissile::Action(CUnit &unit, int &/*move*/, int /*scale*/) const { Assert(unit.Anim.Anim == this); - const int startx = ParseAnimInt(&unit, this->startXStr.c_str()); - const int starty = ParseAnimInt(&unit, this->startYStr.c_str()); - const int destx = ParseAnimInt(&unit, this->destXStr.c_str()); - const int desty = ParseAnimInt(&unit, this->destYStr.c_str()); - const int flags = ParseAnimFlags(unit, this->flagsStr.c_str()); - const int offsetnum = ParseAnimInt(&unit, this->offsetNumStr.c_str()); - const CUnit *goal = flags & ANIM_SM_RELTARGET ? unit.CurrentOrder()->GetGoal() : &unit; + const int startx = ParseAnimInt(unit, this->startXStr.c_str()); + const int starty = ParseAnimInt(unit, this->startYStr.c_str()); + const int destx = ParseAnimInt(unit, this->destXStr.c_str()); + const int desty = ParseAnimInt(unit, this->destYStr.c_str()); + const SpawnMissile_Flags flags = (SpawnMissile_Flags)(ParseAnimFlags(unit, this->flagsStr.c_str())); + const int offsetnum = ParseAnimInt(unit, this->offsetNumStr.c_str()); + const CUnit *goal = flags & SM_RelTarget ? unit.CurrentOrder()->GetGoal() : &unit; const int dir = ((goal->Direction + NextDirection / 2) & 0xFF) / NextDirection; const PixelPos moff = goal->Type->MissileOffsets[dir][!offsetnum ? 0 : offsetnum - 1]; PixelPos start; @@ -120,14 +69,14 @@ static int ParseAnimFlags(CUnit &unit, const char *parseflag) if (!goal || goal->Destroyed) { return; } - if ((flags & ANIM_SM_PIXEL)) { + if ((flags & SM_Pixel)) { start.x = goal->tilePos.x * PixelTileSize.x + goal->IX + moff.x + startx; start.y = goal->tilePos.y * PixelTileSize.y + goal->IY + moff.y + starty; } else { start.x = (goal->tilePos.x + startx) * PixelTileSize.x + PixelTileSize.x / 2 + moff.x; start.y = (goal->tilePos.y + starty) * PixelTileSize.y + PixelTileSize.y / 2 + moff.y; } - if ((flags & ANIM_SM_TOTARGET)) { + if ((flags & SM_ToTarget)) { CUnit *target = goal->CurrentOrder()->GetGoal(); if (!target || target->Destroyed) { Assert(!mtype->AlwaysFire || mtype->Range); @@ -143,14 +92,14 @@ static int ParseAnimFlags(CUnit &unit, const char *parseflag) COrder_SpellCast &order = *static_cast<COrder_SpellCast *>(goal->CurrentOrder()); dest = Map.TilePosToMapPixelPos_Center(order.GetGoalPos()); } - if (flags & ANIM_SM_PIXEL) { + if (flags & SM_Pixel) { dest.x += destx; dest.y += desty; } else { dest.x += destx * PixelTileSize.x; dest.y += desty * PixelTileSize.y; } - } else if (flags & ANIM_SM_PIXEL) { + } else if (flags & SM_Pixel) { dest.x = target->GetMapPixelPosCenter().x + destx; dest.y = target->GetMapPixelPosCenter().y + desty; } else { @@ -159,7 +108,7 @@ static int ParseAnimFlags(CUnit &unit, const char *parseflag) dest += target->Type->GetPixelSize() / 2; } } else { - if ((flags & ANIM_SM_PIXEL)) { + if ((flags & SM_Pixel)) { dest.x = goal->GetMapPixelPosCenter().x + destx; dest.y = goal->GetMapPixelPosCenter().y + desty; } else { @@ -170,22 +119,22 @@ static int ParseAnimFlags(CUnit &unit, const char *parseflag) } Vec2i destTilePos = Map.MapPixelPosToTilePos(dest); const int dist = goal->MapDistanceTo(destTilePos); - if ((flags & ANIM_SM_RANGED) && !(flags & ANIM_SM_PIXEL) + if ((flags & SM_Ranged) && !(flags & SM_Pixel) && dist > goal->Stats->Variables[ATTACKRANGE_INDEX].Max && dist < goal->Type->MinAttackRange) { } else { Missile *missile = MakeMissile(*mtype, start, dest); - if (flags & ANIM_SM_SETDIRECTION) { + if (flags & SM_SetDirection) { PixelPos posd; posd.x = Heading2X[goal->Direction / NextDirection]; posd.y = Heading2Y[goal->Direction / NextDirection]; missile->MissileNewHeadingFromXY(posd); } - if (flags & ANIM_SM_DAMAGE) { + if (flags & SM_Damage) { missile->SourceUnit = &unit; } CUnit *target = goal->CurrentOrder()->GetGoal(); - if (flags & ANIM_SM_TOTARGET && target && target->IsAlive()) { + if (flags & SM_ToTarget && target && target->IsAlive()) { missile->TargetUnit = target; } } @@ -194,7 +143,7 @@ static int ParseAnimFlags(CUnit &unit, const char *parseflag) /* ** s = "missileType startX startY destX destY [flag1[.flagN]] [missileoffset]" */ -/* virtual */ void CAnimation_SpawnMissile::Init(const char *s) +/* virtual */ void CAnimation_SpawnMissile::Init(const char *s, lua_State *) { const std::string str(s); const size_t len = str.size(); diff --git a/src/animation/animation_spawnunit.cpp b/src/animation/animation_spawnunit.cpp index 107333fe4..9f69ba64b 100644 --- a/src/animation/animation_spawnunit.cpp +++ b/src/animation/animation_spawnunit.cpp @@ -37,6 +37,9 @@ #include "animation/animation_spawnunit.h" +#include "../ai/ai_local.h" + +#include "commands.h" #include "map.h" #include "unit.h" @@ -104,10 +107,12 @@ found: { Assert(unit.Anim.Anim == this); - const int offX = ParseAnimInt(&unit, this->offXStr.c_str()); - const int offY = ParseAnimInt(&unit, this->offYStr.c_str()); - const int range = ParseAnimInt(&unit, this->rangeStr.c_str()); - const int playerId = ParseAnimInt(&unit, this->playerStr.c_str()); + const int offX = ParseAnimInt(unit, this->offXStr.c_str()); + const int offY = ParseAnimInt(unit, this->offYStr.c_str()); + const int range = ParseAnimInt(unit, this->rangeStr.c_str()); + const int playerId = ParseAnimInt(unit, this->playerStr.c_str()); + const SpawnUnit_Flags flags = (SpawnUnit_Flags)(ParseAnimFlags(unit, this->flagsStr.c_str())); + CPlayer &player = Players[playerId]; const Vec2i pos(unit.tilePos.x + offX, unit.tilePos.y + offY); CUnitType *type = UnitTypeByIdent(this->unitTypeStr.c_str()); @@ -120,6 +125,17 @@ found: if (target != NULL) { target->tilePos = resPos; target->Place(resPos); + if (flags & SU_Summoned) { + target->Summoned = 1; + } + if ((flags & SU_JoinToAIForce) && unit.Player->AiEnabled) { + int force = unit.Player->Ai->Force.GetForce(unit); + if (force != -1) { + unit.Player->Ai->Force[force].Insert(*target); + target->GroupId = unit.GroupId; + CommandDefend(*target, unit, FlushCommands); + } + } //DropOutOnSide(*target, LookingW, NULL); } else { DebugPrint("Unable to allocate Unit"); @@ -128,9 +144,9 @@ found: } /* -** s = "unitType offX offY range player" +** s = "unitType offX offY range player [flags]" */ -/* virtual */ void CAnimation_SpawnUnit::Init(const char *s) +/* virtual */ void CAnimation_SpawnUnit::Init(const char *s, lua_State *) { const std::string str(s); const size_t len = str.size(); @@ -154,6 +170,12 @@ found: begin = std::min(len, str.find_first_not_of(' ', end)); end = std::min(len, str.find(' ', begin)); this->playerStr.assign(str, begin, end - begin); + + begin = std::min(len, str.find_first_not_of(' ', end)); + end = std::min(len, str.find(' ', begin)); + if (begin != end) { + this->flagsStr.assign(str, begin, end - begin); + } } //@} diff --git a/src/animation/animation_unbreakable.cpp b/src/animation/animation_unbreakable.cpp index 020967c5b..73282d279 100644 --- a/src/animation/animation_unbreakable.cpp +++ b/src/animation/animation_unbreakable.cpp @@ -47,7 +47,7 @@ unit.Anim.Unbreakable = this->state; } -/* virtual */ void CAnimation_Unbreakable::Init(const char *s) +/* virtual */ void CAnimation_Unbreakable::Init(const char *s, lua_State *) { if (!strcmp(s, "begin")) { this->state = 1; diff --git a/src/animation/animation_wait.cpp b/src/animation/animation_wait.cpp index 85da3b2d6..f4609c592 100644 --- a/src/animation/animation_wait.cpp +++ b/src/animation/animation_wait.cpp @@ -42,7 +42,7 @@ /* virtual */ void CAnimation_Wait::Action(CUnit &unit, int &/*move*/, int scale) const { Assert(unit.Anim.Anim == this); - unit.Anim.Wait = ParseAnimInt(&unit, this->wait.c_str()) << scale >> 8; + unit.Anim.Wait = ParseAnimInt(unit, this->wait.c_str()) << scale >> 8; if (unit.Variable[SLOW_INDEX].Value) { // unit is slowed down unit.Anim.Wait <<= 1; } @@ -54,7 +54,7 @@ } } -/* virtual */ void CAnimation_Wait::Init(const char *s) +/* virtual */ void CAnimation_Wait::Init(const char *s, lua_State *) { this->wait = s; } diff --git a/src/include/animation.h b/src/include/animation.h index d56577501..bab6db85c 100644 --- a/src/include/animation.h +++ b/src/include/animation.h @@ -72,7 +72,22 @@ enum AnimationType { AnimationIfVar, AnimationSetVar, AnimationSetPlayerVar, - AnimationDie + AnimationDie, + AnimationLuaCallback +}; + +//Modify types +enum SetVar_ModifyTypes { + modSet = 0, /// Set value to this + modAdd, /// Addition + modSub, /// Subtraction + modMul, /// Multiplication + modDiv, /// Division + modMod, /// Modulo + modAnd, /// Bitwise AND + modOr, /// Bitwise OR + modXor, /// Bitwise XOR + modNot, /// Bitwise NOT }; class CAnimation @@ -83,7 +98,7 @@ public: virtual ~CAnimation() {} virtual void Action(CUnit &unit, int &move, int scale) const = 0; - virtual void Init(const char *s) {} + virtual void Init(const char *s, lua_State *l = NULL) {} const AnimationType Type; CAnimation *Next; @@ -152,7 +167,8 @@ extern int UnitShowAnimationScaled(CUnit &unit, const CAnimation *anim, int scal extern int UnitShowAnimation(CUnit &unit, const CAnimation *anim); -extern int ParseAnimInt(const CUnit *unit, const char *parseint); +extern int ParseAnimInt(const CUnit &unit, const char *parseint); +extern int ParseAnimFlags(const CUnit &unit, const char *parseflag); extern void FindLabelLater(CAnimation **anim, const std::string &name); diff --git a/src/include/animation/animation_attack.h b/src/include/animation/animation_attack.h index 67b2374c6..42a49c9e5 100644 --- a/src/include/animation/animation_attack.h +++ b/src/include/animation/animation_attack.h @@ -41,7 +41,7 @@ public: CAnimation_Attack() : CAnimation(AnimationAttack) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); }; diff --git a/src/include/animation/animation_die.h b/src/include/animation/animation_die.h index 3bd359e99..2a8657338 100644 --- a/src/include/animation/animation_die.h +++ b/src/include/animation/animation_die.h @@ -41,7 +41,7 @@ public: CAnimation_Die() : CAnimation(AnimationDie) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); private: std::string DeathType; diff --git a/src/include/animation/animation_exactframe.h b/src/include/animation/animation_exactframe.h index 092db73ee..95444d88e 100644 --- a/src/include/animation/animation_exactframe.h +++ b/src/include/animation/animation_exactframe.h @@ -41,7 +41,7 @@ public: CAnimation_ExactFrame() : CAnimation(AnimationExactFrame) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); int ParseAnimInt(const CUnit *unit) const; diff --git a/src/include/animation/animation_frame.h b/src/include/animation/animation_frame.h index bc89e4458..db44c7e67 100644 --- a/src/include/animation/animation_frame.h +++ b/src/include/animation/animation_frame.h @@ -41,7 +41,7 @@ public: CAnimation_Frame() : CAnimation(AnimationFrame) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); int ParseAnimInt(const CUnit *unit) const; private: diff --git a/src/include/animation/animation_goto.h b/src/include/animation/animation_goto.h index bb5e81a3f..2ee8d7ca1 100644 --- a/src/include/animation/animation_goto.h +++ b/src/include/animation/animation_goto.h @@ -41,7 +41,7 @@ public: CAnimation_Goto() : CAnimation(AnimationGoto), gotoLabel(NULL) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); private: CAnimation *gotoLabel; diff --git a/src/include/animation/animation_ifvar.h b/src/include/animation/animation_ifvar.h index 73769ac79..a24225691 100644 --- a/src/include/animation/animation_ifvar.h +++ b/src/include/animation/animation_ifvar.h @@ -41,7 +41,7 @@ public: CAnimation_IfVar() : CAnimation(AnimationIfVar), binOpFunc(NULL), gotoLabel(NULL) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); private: typedef bool BinOpFunc(int lhs, int rhs); diff --git a/src/include/animation/animation_label.h b/src/include/animation/animation_label.h index e954a7baf..3363905a9 100644 --- a/src/include/animation/animation_label.h +++ b/src/include/animation/animation_label.h @@ -40,7 +40,7 @@ public: CAnimation_Label() : CAnimation(AnimationLabel) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); }; //@} diff --git a/src/include/animation/animation_luacallback.h b/src/include/animation/animation_luacallback.h new file mode 100644 index 000000000..92f0d7d59 --- /dev/null +++ b/src/include/animation/animation_luacallback.h @@ -0,0 +1,57 @@ +// _________ __ __ +// / _____// |_____________ _/ |______ ____ __ __ ______ +// \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/ +// / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ | +// /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ > +// \/ \/ \//_____/ \/ +// ______________________ ______________________ +// T H E W A R B E G I N S +// Stratagus - A free fantasy real time strategy game engine +// +/**@name animation_luacallback.h - The animation LuaCallback headerfile. */ +// +// (c) Copyright 2013 by cybermind +// +// 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; only version 2 of the License. +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// + +#ifndef ANIMATION_LUACALLBACK_H +#define ANIMATION_LUACALLBACK_H + +//@{ + +#include <string> +#include "animation.h" + +#include "luacallback.h" + +class CAnimation_LuaCallback : public CAnimation +{ +public: + CAnimation_LuaCallback() : CAnimation(AnimationLuaCallback), cb(NULL) {} + ~CAnimation_LuaCallback() { delete cb; } + + virtual void Action(CUnit &unit, int &move, int scale) const; + virtual void Init(const char *s, lua_State *l); + +private: + LuaCallback *cb; + std::string cbName; + std::vector<std::string> cbArgs; +}; + +//@} + +#endif // ANIMATION_LUACALLBACK_H diff --git a/src/include/animation/animation_move.h b/src/include/animation/animation_move.h index 80cbe65e2..8d3efa2a0 100644 --- a/src/include/animation/animation_move.h +++ b/src/include/animation/animation_move.h @@ -41,7 +41,7 @@ public: CAnimation_Move() : CAnimation(AnimationMove) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); private: std::string moveStr; diff --git a/src/include/animation/animation_randomgoto.h b/src/include/animation/animation_randomgoto.h index dad4b2c86..1b47b845c 100644 --- a/src/include/animation/animation_randomgoto.h +++ b/src/include/animation/animation_randomgoto.h @@ -41,7 +41,7 @@ public: CAnimation_RandomGoto() : CAnimation(AnimationRandomGoto), gotoLabel(NULL) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); private: std::string randomStr; diff --git a/src/include/animation/animation_randomrotate.h b/src/include/animation/animation_randomrotate.h index 777384f4e..77f36aefe 100644 --- a/src/include/animation/animation_randomrotate.h +++ b/src/include/animation/animation_randomrotate.h @@ -41,7 +41,7 @@ public: CAnimation_RandomRotate() : CAnimation(AnimationRandomRotate) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); private: std::string rotateStr; diff --git a/src/include/animation/animation_randomsound.h b/src/include/animation/animation_randomsound.h index 75470c5e8..ce73ffbaf 100644 --- a/src/include/animation/animation_randomsound.h +++ b/src/include/animation/animation_randomsound.h @@ -43,7 +43,7 @@ public: CAnimation_RandomSound() : CAnimation(AnimationRandomSound) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); void MapSound(); private: diff --git a/src/include/animation/animation_randomwait.h b/src/include/animation/animation_randomwait.h index 82c1b58a7..e8cfe6a7d 100644 --- a/src/include/animation/animation_randomwait.h +++ b/src/include/animation/animation_randomwait.h @@ -41,7 +41,7 @@ public: CAnimation_RandomWait() : CAnimation(AnimationRandomWait) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); private: std::string minWait; diff --git a/src/include/animation/animation_rotate.h b/src/include/animation/animation_rotate.h index f0c99b35d..990023a4c 100644 --- a/src/include/animation/animation_rotate.h +++ b/src/include/animation/animation_rotate.h @@ -41,7 +41,7 @@ public: CAnimation_Rotate() : CAnimation(AnimationRotate) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); private: std::string rotateStr; diff --git a/src/include/animation/animation_setplayervar.h b/src/include/animation/animation_setplayervar.h index edb0604e8..bb24f37c4 100644 --- a/src/include/animation/animation_setplayervar.h +++ b/src/include/animation/animation_setplayervar.h @@ -41,10 +41,10 @@ public: CAnimation_SetPlayerVar() : CAnimation(AnimationSetPlayerVar) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); private: - int mod; + SetVar_ModifyTypes mod; std::string playerStr; std::string varStr; std::string argStr; diff --git a/src/include/animation/animation_setvar.h b/src/include/animation/animation_setvar.h index f231fe0c8..af3906fc0 100644 --- a/src/include/animation/animation_setvar.h +++ b/src/include/animation/animation_setvar.h @@ -41,10 +41,10 @@ public: CAnimation_SetVar() : CAnimation(AnimationSetVar) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); private: - int mod; + SetVar_ModifyTypes mod; std::string varStr; std::string valueStr; std::string unitSlotStr; diff --git a/src/include/animation/animation_sound.h b/src/include/animation/animation_sound.h index e669319d6..09cc9a860 100644 --- a/src/include/animation/animation_sound.h +++ b/src/include/animation/animation_sound.h @@ -42,7 +42,7 @@ public: CAnimation_Sound() : CAnimation(AnimationSound) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); void MapSound(); diff --git a/src/include/animation/animation_spawnmissile.h b/src/include/animation/animation_spawnmissile.h index 18425212a..f1e461ec0 100644 --- a/src/include/animation/animation_spawnmissile.h +++ b/src/include/animation/animation_spawnmissile.h @@ -35,13 +35,25 @@ #include <string> #include "animation.h" +//SpawnMissile flags +enum SpawnMissile_Flags { + SM_None = 0, /// Clears all flags + SM_Damage = 1, /// Missile deals damage to units + SM_ToTarget = 2, /// Missile is directed to unit's target + SM_Pixel = 4, /// Missile's offsets are calculated in pixels rather than tiles + SM_RelTarget = 8, /// All calculations are relative to unit's target + SM_Ranged = 16, /// Missile can't be shot if current range between unit and it's target + /// is bigger than unit's attack range + SM_SetDirection = 32 /// Missile takes the same direction as spawner +}; + class CAnimation_SpawnMissile : public CAnimation { public: CAnimation_SpawnMissile() : CAnimation(AnimationSpawnMissile) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); private: std::string missileTypeStr; diff --git a/src/include/animation/animation_spawnunit.h b/src/include/animation/animation_spawnunit.h index a708bcdb2..7e4e5dd8c 100644 --- a/src/include/animation/animation_spawnunit.h +++ b/src/include/animation/animation_spawnunit.h @@ -35,13 +35,20 @@ #include <string> #include "animation.h" +//SpawnUnit flags +enum SpawnUnit_Flags { + SU_None = 0, /// Clears all flags + SU_Summoned = 1, /// Unit is marked as "summoned" + SU_JoinToAIForce = 2 /// Unit is included into spawner's AI force, if available +}; + class CAnimation_SpawnUnit : public CAnimation { public: CAnimation_SpawnUnit() : CAnimation(AnimationSpawnUnit) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); private: std::string unitTypeStr; @@ -49,6 +56,7 @@ private: std::string offYStr; std::string rangeStr; std::string playerStr; + std::string flagsStr; }; //@} diff --git a/src/include/animation/animation_unbreakable.h b/src/include/animation/animation_unbreakable.h index 90c18308a..b3672eb42 100644 --- a/src/include/animation/animation_unbreakable.h +++ b/src/include/animation/animation_unbreakable.h @@ -41,7 +41,7 @@ public: CAnimation_Unbreakable() : CAnimation(AnimationUnbreakable), state(0) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); private: int state; diff --git a/src/include/animation/animation_wait.h b/src/include/animation/animation_wait.h index 917be6d52..4c72b9184 100644 --- a/src/include/animation/animation_wait.h +++ b/src/include/animation/animation_wait.h @@ -41,7 +41,7 @@ public: CAnimation_Wait() : CAnimation(AnimationWait) {} virtual void Action(CUnit &unit, int &move, int scale) const; - virtual void Init(const char *s); + virtual void Init(const char *s, lua_State *l); private: std::string wait; diff --git a/src/include/particle.h b/src/include/particle.h index 9338377fa..cfee2019f 100644 --- a/src/include/particle.h +++ b/src/include/particle.h @@ -77,8 +77,8 @@ public: class CParticle { public: - CParticle(CPosition position) : - pos(position), destroyed(false) + CParticle(CPosition position, int drawlevel = 0) : + pos(position), destroyed(false), drawLevel(drawlevel) {} virtual ~CParticle() {} @@ -91,16 +91,20 @@ public: virtual CParticle *clone() = 0; + int getDrawLevel() const { return drawLevel; } + void setDrawLevel(int value) { drawLevel = value; } + protected: CPosition pos; bool destroyed; + int drawLevel; }; class StaticParticle : public CParticle { public: - StaticParticle(CPosition position, GraphicAnimation *flame); + StaticParticle(CPosition position, GraphicAnimation *flame, int drawlevel = 0); virtual ~StaticParticle(); virtual bool isVisible(const CViewport &vp) const; @@ -120,13 +124,17 @@ public: CChunkParticle(CPosition position, GraphicAnimation *smokeAnimation, GraphicAnimation *debrisAnimation, GraphicAnimation *destroyAnimation, int minVelocity = 0, int maxVelocity = 400, - int minTrajectoryAngle = 77, int maxTTL = 0); + int minTrajectoryAngle = 77, int maxTTL = 0, int drawlevel = 0); virtual ~CChunkParticle(); virtual bool isVisible(const CViewport &vp) const; virtual void draw(); virtual void update(int ticks); virtual CParticle *clone(); + int getSmokeDrawLevel() const { return smokeDrawLevel; } + int getDestroyDrawLevel() const { return destroyDrawLevel; } + void setSmokeDrawLevel(int value) { smokeDrawLevel = value; } + void setDestroyDrawLevel(int value) { destroyDrawLevel = value; } protected: CPosition initialPos; @@ -140,6 +148,8 @@ protected: int maxVelocity; int minTrajectoryAngle; float height; + int smokeDrawLevel; + int destroyDrawLevel; GraphicAnimation *debrisAnimation; GraphicAnimation *smokeAnimation; GraphicAnimation *destroyAnimation; @@ -155,7 +165,7 @@ protected: class CSmokeParticle : public CParticle { public: - CSmokeParticle(CPosition position, GraphicAnimation *animation, float speedx = 0, float speedy = -22.0f); + CSmokeParticle(CPosition position, GraphicAnimation *animation, float speedx = 0, float speedy = -22.0f, int drawlevel = 0); virtual ~CSmokeParticle(); virtual bool isVisible(const CViewport &vp) const; @@ -174,7 +184,7 @@ protected: class CRadialParticle : public CParticle { public: - CRadialParticle(CPosition position, GraphicAnimation *animation, int maxSpeed); + CRadialParticle(CPosition position, GraphicAnimation *animation, int maxSpeed, int drawlevel = 0); virtual ~CRadialParticle(); virtual bool isVisible(const CViewport &vp) const; @@ -199,7 +209,9 @@ public: static void init(); static void exit(); - void draw(const CViewport &vp); + void prepareToDraw(const CViewport &vp, std::vector<CParticle *> &table); + void endDraw(); + void update(); void add(CParticle *particle); diff --git a/src/include/spell/spell_capture.h b/src/include/spell/spell_capture.h index 16b6d55e7..92acb8e44 100644 --- a/src/include/spell/spell_capture.h +++ b/src/include/spell/spell_capture.h @@ -41,13 +41,14 @@ class Spell_Capture : public SpellActionType { public: - Spell_Capture() : SacrificeEnable(0), Damage(0), DamagePercent(0) {}; + Spell_Capture() : SacrificeEnable(false), JoinToAIForce(false), Damage(0), DamagePercent(0) {}; virtual int Cast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &goalPos); virtual void Parse(lua_State *l, int startIndex, int endIndex); private: - char SacrificeEnable; /// true if the caster dies after casting. + bool SacrificeEnable; /// true if the caster dies after casting. + bool JoinToAIForce; /// if true, captured unit is joined into caster's AI force, if available int Damage; /// damage the spell does if unable to caputre int DamagePercent; /// percent the target must be damaged for a /// capture to suceed. diff --git a/src/include/spell/spell_summon.h b/src/include/spell/spell_summon.h index ce4f4aea3..b1e416646 100644 --- a/src/include/spell/spell_summon.h +++ b/src/include/spell/spell_summon.h @@ -41,7 +41,8 @@ class Spell_Summon : public SpellActionType { public: - Spell_Summon() : SpellActionType(1), UnitType(NULL), TTL(0), RequireCorpse(0) {}; + Spell_Summon() : SpellActionType(1), UnitType(NULL), TTL(0), + RequireCorpse(false), JoinToAiForce(false) {}; virtual int Cast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &goalPos); virtual void Parse(lua_State *l, int startIndex, int endIndex); @@ -50,6 +51,7 @@ private: CUnitType *UnitType; /// Type of unit to be summoned. int TTL; /// Time to live for summoned unit. 0 means infinite int RequireCorpse; /// Corpse consumed while summoning. + bool JoinToAiForce; /// if true, captured unit is joined into caster's AI force, if available }; diff --git a/src/include/spell/spell_teleport.h b/src/include/spell/spell_teleport.h index e667e6684..3f15e1881 100644 --- a/src/include/spell/spell_teleport.h +++ b/src/include/spell/spell_teleport.h @@ -40,15 +40,10 @@ class Spell_Teleport : public SpellActionType { public: - Spell_Teleport() : SpellActionType(1), UnitType(NULL), TTL(0), RequireCorpse(0) {}; + Spell_Teleport() : SpellActionType(0) {} virtual int Cast(CUnit &caster, const SpellType &spell, CUnit *target, const Vec2i &goalPos); virtual void Parse(lua_State *l, int startIndex, int endIndex); - -private: - CUnitType *UnitType; /// Type of unit to be summoned. - int TTL; /// Time to live for summoned unit. 0 means infinite - int RequireCorpse; /// Corpse consumed while summoning. }; diff --git a/src/include/spells.h b/src/include/spells.h index 040dceb62..970477043 100644 --- a/src/include/spells.h +++ b/src/include/spells.h @@ -224,6 +224,7 @@ public: bool IsCasterOnly() const { return !Range && Target == TargetSelf; } + bool ForceUseAnimation; }; diff --git a/src/include/unit.h b/src/include/unit.h index b1aacfa10..95e567e2b 100644 --- a/src/include/unit.h +++ b/src/include/unit.h @@ -339,21 +339,23 @@ public: int ResourcesHeld; /// Resources Held by a unit unsigned char DamagedType; /// Index of damage type of unit which damaged this unit - unsigned long Attacked; /// gamecycle unit was last attacked - unsigned Blink : 3; /// Let selection rectangle blink - unsigned Moving : 1; /// The unit is moving - unsigned ReCast : 1; /// Recast again next cycle - unsigned AutoRepair : 1; /// True if unit tries to repair on still action. + unsigned long Attacked; /// gamecycle unit was last attacked + unsigned Blink : 3; /// Let selection rectangle blink + unsigned Moving : 1; /// The unit is moving + unsigned ReCast : 1; /// Recast again next cycle + unsigned AutoRepair : 1; /// True if unit tries to repair on still action. - unsigned Burning : 1; /// unit is burning - unsigned Destroyed : 1; /// unit is destroyed pending reference - unsigned Removed : 1; /// unit is removed (not on map) - unsigned Selected : 1; /// unit is selected + unsigned Burning : 1; /// unit is burning + unsigned Destroyed : 1; /// unit is destroyed pending reference + unsigned Removed : 1; /// unit is removed (not on map) + unsigned Selected : 1; /// unit is selected unsigned Constructed : 1; /// Unit is in construction unsigned Active : 1; /// Unit is active for AI unsigned Boarded : 1; /// Unit is on board a transporter. - unsigned CacheLock : 1; /// Unit is on lock by unitcache operations. + unsigned CacheLock : 1; /// Unit is on lock by unitcache operations. + + unsigned Summoned : 1; /// Unit is summoned using spells. unsigned TeamSelected; /// unit is selected by a team member. CPlayer *RescuedFrom; /// The original owner of a rescued unit. @@ -417,13 +419,15 @@ class CPreference public: CPreference() : ShowSightRange(false), ShowReactionRange(false), ShowAttackRange(false), ShowMessages(true), - BigScreen(false), ShowOrders(0), ShowNameDelay(0), ShowNameTime(0) {}; + BigScreen(false), PauseOnLeave(true), ShowOrders(0), ShowNameDelay(0), + ShowNameTime(0) {}; bool ShowSightRange; /// Show sight range. bool ShowReactionRange; /// Show reaction range. bool ShowAttackRange; /// Show attack range. bool ShowMessages; /// Show messages. bool BigScreen; /// If true, shows the big screen(without panels) + bool PauseOnLeave; /// If true, game pauses when cursor is gone int ShowOrders; /// How many second show orders of unit on map. int ShowNameDelay; /// How many cycles need to wait until unit's name popup will appear. diff --git a/src/include/unit_find.h b/src/include/unit_find.h index a3186b5bc..b5be40e81 100644 --- a/src/include/unit_find.h +++ b/src/include/unit_find.h @@ -281,7 +281,7 @@ extern CUnit *FindIdleWorker(const CPlayer &player, const CUnit *last); extern bool FindTerrainType(int movemask, int resmask, int range, const CPlayer &player, const Vec2i &startPos, Vec2i *pos); -extern void FindUnitsByType(const CUnitType &type, std::vector<CUnit *> &units); +extern void FindUnitsByType(const CUnitType &type, std::vector<CUnit *> &units, bool everybody = false); /// Find all units of this type of the player extern void FindPlayerUnitsByType(const CPlayer &player, const CUnitType &type, std::vector<CUnit *> &units); diff --git a/src/include/unittype.h b/src/include/unittype.h index 178551ce7..50e4a879d 100644 --- a/src/include/unittype.h +++ b/src/include/unittype.h @@ -467,6 +467,10 @@ public: LuaCallback *OnHit; /// lua function called when unit is hit LuaCallback *OnEachCycle; /// lua function called every cycle LuaCallback *OnEachSecond; /// lua function called every second + LuaCallback *OnInit; /// lua function called on unit init + + int TeleportCost; /// mana used for teleportation + MissileConfig TeleportEffect; /// missile created when teleported mutable std::string DamageType; /// DamageType (used for extra death animations and impacts) diff --git a/src/include/widgets.h b/src/include/widgets.h index 48988c1d6..5650f3239 100644 --- a/src/include/widgets.h +++ b/src/include/widgets.h @@ -294,8 +294,6 @@ public: int getPercent() const; private: - int width; /// width of the widget. - int height; /// height of the widget. std::string caption; /// caption of the widget. unsigned int percent; /// percent value of the widget. }; diff --git a/src/map/map_draw.cpp b/src/map/map_draw.cpp index dd3da8fbe..496e59798 100644 --- a/src/map/map_draw.cpp +++ b/src/map/map_draw.cpp @@ -332,25 +332,68 @@ void CViewport::Draw() const CurrentViewport = this; { + // Now we need to sort units, missiles, particles by draw level and draw them std::vector<CUnit *> unittable; std::vector<Missile *> missiletable; + std::vector<CParticle *> particletable; - // We find and sort units after draw level. FindAndSortUnits(*this, unittable); const size_t nunits = unittable.size(); FindAndSortMissiles(*this, missiletable); const size_t nmissiles = missiletable.size(); + ParticleManager.prepareToDraw(*this, particletable); + const size_t nparticles = particletable.size(); + size_t i = 0; size_t j = 0; + size_t k = 0; - while (i < nunits && j < nmissiles) { - if (unittable[i]->Type->DrawLevel <= missiletable[j]->Type->DrawLevel) { - unittable[i]->Draw(*this); - ++i; - } else { - missiletable[j]->DrawMissile(*this); - ++j; - } + + while ((i < nunits && j < nmissiles) || (i < nunits && k < nparticles) + || (j < nmissiles && k < nparticles)) { + if (i == nunits) { + if (missiletable[j]->Type->DrawLevel < particletable[k]->getDrawLevel()) { + missiletable[j]->DrawMissile(*this); + ++j; + } else { + particletable[k]->draw(); + ++k; + } + } else if (j == nmissiles) { + if (unittable[i]->Type->DrawLevel < particletable[k]->getDrawLevel()) { + unittable[i]->Draw(*this); + ++i; + } else { + particletable[k]->draw(); + ++k; + } + } else if (k == nparticles) { + if (unittable[i]->Type->DrawLevel < missiletable[j]->Type->DrawLevel) { + unittable[i]->Draw(*this); + ++i; + } else { + missiletable[j]->DrawMissile(*this); + ++j; + } + } else { + if (unittable[i]->Type->DrawLevel <= missiletable[j]->Type->DrawLevel) { + if (unittable[i]->Type->DrawLevel < particletable[k]->getDrawLevel()) { + unittable[i]->Draw(*this); + ++i; + } else { + particletable[k]->draw(); + ++k; + } + } else { + if (missiletable[j]->Type->DrawLevel < particletable[k]->getDrawLevel()) { + missiletable[j]->DrawMissile(*this); + ++j; + } else { + particletable[k]->draw(); + ++k; + } + } + } } for (; i < nunits; ++i) { unittable[i]->Draw(*this); @@ -358,10 +401,12 @@ void CViewport::Draw() const for (; j < nmissiles; ++j) { missiletable[j]->DrawMissile(*this); } + for (; k < nparticles; ++k) { + particletable[k]->draw(); + } + ParticleManager.endDraw(); } - ParticleManager.draw(*this); - this->DrawMapFogOfWar(); // diff --git a/src/particle/chunkparticle.cpp b/src/particle/chunkparticle.cpp index e858974a5..b886c5efa 100644 --- a/src/particle/chunkparticle.cpp +++ b/src/particle/chunkparticle.cpp @@ -46,8 +46,8 @@ static inline float deg2rad(int degrees) CChunkParticle::CChunkParticle(CPosition position, GraphicAnimation *smokeAnimation, GraphicAnimation *debrisAnimation, GraphicAnimation *destroyAnimation, - int minVelocity, int maxVelocity, int minTrajectoryAngle, int maxTTL) : - CParticle(position), initialPos(position), maxTTL(maxTTL), nextSmokeTicks(0), + int minVelocity, int maxVelocity, int minTrajectoryAngle, int maxTTL, int drawlevel) : + CParticle(position, drawlevel), initialPos(position), maxTTL(maxTTL), nextSmokeTicks(0), age(0), height(0.f) { float radians = deg2rad(MyRand() % 360); @@ -112,7 +112,7 @@ void CChunkParticle::update(int ticks) if (destroyAnimation) { CPosition p(pos.x, calculateScreenPos(pos.y, height)); GraphicAnimation *destroyanimation = destroyAnimation->clone(); - StaticParticle *destroy = new StaticParticle(p, destroyanimation); + StaticParticle *destroy = new StaticParticle(p, destroyanimation, destroyDrawLevel); ParticleManager.add(destroy); } @@ -126,7 +126,7 @@ void CChunkParticle::update(int ticks) if (age > nextSmokeTicks) { CPosition p(pos.x, calculateScreenPos(pos.y, height)); GraphicAnimation *smokeanimation = smokeAnimation->clone(); - CSmokeParticle *smoke = new CSmokeParticle(p, smokeanimation); + CSmokeParticle *smoke = new CSmokeParticle(p, smokeanimation, 0, -22.0f, smokeDrawLevel); ParticleManager.add(smoke); nextSmokeTicks += MyRand() % randSmokeTicks + minSmokeTicks; @@ -151,7 +151,10 @@ void CChunkParticle::update(int ticks) CParticle *CChunkParticle::clone() { - return new CChunkParticle(pos, smokeAnimation, debrisAnimation, destroyAnimation, minVelocity, maxVelocity, minTrajectoryAngle, maxTTL); + CChunkParticle *particle = new CChunkParticle(pos, smokeAnimation, debrisAnimation, destroyAnimation, minVelocity, maxVelocity, minTrajectoryAngle, maxTTL, drawLevel); + particle->smokeDrawLevel = smokeDrawLevel; + particle->destroyDrawLevel = destroyDrawLevel; + return particle; } //@} diff --git a/src/particle/particlemanager.cpp b/src/particle/particlemanager.cpp index 8025f3394..8e1e12270 100644 --- a/src/particle/particlemanager.cpp +++ b/src/particle/particlemanager.cpp @@ -34,6 +34,8 @@ #include "ui.h" #include "video.h" +#include <algorithm> + CParticleManager ParticleManager; @@ -70,17 +72,27 @@ void CParticleManager::clear() new_particles.clear(); } -void CParticleManager::draw(const CViewport &vp) +static inline bool DrawLevelCompare(const CParticle *lhs, const CParticle *rhs) +{ + return lhs->getDrawLevel() < rhs->getDrawLevel(); +} + +void CParticleManager::prepareToDraw(const CViewport &vp, std::vector<CParticle *> &table) { this->vp = &vp; - std::vector<CParticle *>::iterator i; - for (i = particles.begin(); i != particles.end(); ++i) { - if ((*i)->isVisible(vp)) { - (*i)->draw(); + for (std::vector<CParticle *>::iterator it = particles.begin(); it != particles.end(); ++it) { + CParticle &particle = **it; + if (particle.isVisible(vp)) { + table.push_back(&particle); } } + std::sort(table.begin(), table.end(), DrawLevelCompare); +} + +void CParticleManager::endDraw() +{ this->vp = NULL; } diff --git a/src/particle/radialparticle.cpp b/src/particle/radialparticle.cpp index e27008312..20cfb008e 100644 --- a/src/particle/radialparticle.cpp +++ b/src/particle/radialparticle.cpp @@ -34,8 +34,8 @@ #include "stratagus.h" #include "particle.h" -CRadialParticle::CRadialParticle(CPosition position, GraphicAnimation *animation, int maxSpeed) : - CParticle(position) +CRadialParticle::CRadialParticle(CPosition position, GraphicAnimation *animation, int maxSpeed, int drawlevel) : + CParticle(position, drawlevel) { Assert(animation); this->animation = animation->clone(); @@ -76,7 +76,7 @@ void CRadialParticle::update(int ticks) CParticle *CRadialParticle::clone() { - CParticle *p = new CRadialParticle(pos, animation, maxSpeed); + CParticle *p = new CRadialParticle(pos, animation, maxSpeed, drawLevel); return p; } diff --git a/src/particle/smokeparticle.cpp b/src/particle/smokeparticle.cpp index e0cbdac18..b735ebaff 100644 --- a/src/particle/smokeparticle.cpp +++ b/src/particle/smokeparticle.cpp @@ -36,8 +36,8 @@ CSmokeParticle::CSmokeParticle(CPosition position, GraphicAnimation *smoke, - float speedx, float speedy) : - CParticle(position) + float speedx, float speedy, int drawlevel) : + CParticle(position, drawlevel) { Assert(smoke); this->puff = smoke->clone(); @@ -77,7 +77,7 @@ void CSmokeParticle::update(int ticks) CParticle *CSmokeParticle::clone() { - return new CSmokeParticle(pos, puff, speedVector.x, speedVector.y); + return new CSmokeParticle(pos, puff, speedVector.x, speedVector.y, drawLevel); } //@} diff --git a/src/particle/staticparticle.cpp b/src/particle/staticparticle.cpp index e0cfa6f54..a2c32d880 100644 --- a/src/particle/staticparticle.cpp +++ b/src/particle/staticparticle.cpp @@ -33,8 +33,8 @@ #include "particle.h" -StaticParticle::StaticParticle(CPosition position, GraphicAnimation *animation) : - CParticle(position) +StaticParticle::StaticParticle(CPosition position, GraphicAnimation *animation, int drawlevel) : + CParticle(position, drawlevel) { Assert(animation); this->animation = animation->clone(); @@ -66,7 +66,7 @@ void StaticParticle::update(int ticks) CParticle *StaticParticle::clone() { - CParticle *p = new StaticParticle(pos, animation); + CParticle *p = new StaticParticle(pos, animation, drawLevel); return p; } diff --git a/src/sound/sound_server.cpp b/src/sound/sound_server.cpp index 54f40058f..9b92ec28c 100644 --- a/src/sound/sound_server.cpp +++ b/src/sound/sound_server.cpp @@ -438,14 +438,7 @@ int SetChannelStereo(int channel, int stereo) stereo = Channels[channel].Stereo; } else { SDL_LockAudio(); - - if (stereo > 127) { - stereo = 127; - } else if (stereo < -128) { - stereo = -128; - } Channels[channel].Stereo = stereo; - SDL_UnlockAudio(); } return stereo; diff --git a/src/spell/script_spell.cpp b/src/spell/script_spell.cpp index 6280a6bc3..decd53641 100644 --- a/src/spell/script_spell.cpp +++ b/src/spell/script_spell.cpp @@ -333,6 +333,9 @@ static int CclDefineSpell(lua_State *l) } else if (!strcmp(value, "repeat-cast")) { spell->RepeatCast = 1; --i; + } else if (!strcmp(value, "force-use-animation")) { + spell->ForceUseAnimation = true; + --i; } else if (!strcmp(value, "target")) { value = LuaToString(l, i + 1); if (!strcmp(value, "self")) { diff --git a/src/spell/spell_capture.cpp b/src/spell/spell_capture.cpp index 18c4354e0..09ca88cc8 100644 --- a/src/spell/spell_capture.cpp +++ b/src/spell/spell_capture.cpp @@ -34,6 +34,9 @@ #include "spell/spell_capture.h" +#include "../ai/ai_local.h" + +#include "commands.h" #include "game.h" #include "script.h" #include "unit.h" @@ -44,7 +47,9 @@ const char *value = LuaToString(l, -1, j + 1); ++j; if (!strcmp(value, "sacrifice")) { - this->SacrificeEnable = 1; + this->SacrificeEnable = true; + } else if (!strcmp(value, "join-to-ai-force")) { + this->JoinToAIForce = true; } else if (!strcmp(value, "damage")) { this->Damage = LuaToNumber(l, -1, j + 1); } else if (!strcmp(value, "percent")) { @@ -79,8 +84,7 @@ if (this->SacrificeEnable) { // No corpse. caster.Remove(NULL); - UnitLost(caster); - UnitClearOrders(caster); + caster.Release(); } return 1; } @@ -103,15 +107,24 @@ caster.Variable[KILL_INDEX].Enable = 1; } target->ChangeOwner(*caster.Player); + UnitClearOrders(*target); + if (this->JoinToAIForce && caster.Player->AiEnabled) { + int force = caster.Player->Ai->Force.GetForce(caster); + if (force != -1) { + caster.Player->Ai->Force[force].Insert(*target); + target->GroupId = caster.GroupId; + CommandDefend(*target, caster, FlushCommands); + } + } if (this->SacrificeEnable) { // No corpse. caster.Remove(NULL); - UnitLost(caster); - UnitClearOrders(caster); + caster.Release(); } else { caster.Variable[MANA_INDEX].Value -= spell.ManaCost; } - UnitClearOrders(*target); + + return 0; } diff --git a/src/spell/spell_spawnportal.cpp b/src/spell/spell_spawnportal.cpp index b444dacf7..b730f94ea 100644 --- a/src/spell/spell_spawnportal.cpp +++ b/src/spell/spell_spawnportal.cpp @@ -85,6 +85,7 @@ } else { portal = MakeUnitAndPlace(goalPos, *this->PortalType, CurrentPlayer ? caster.Player : &Players[PlayerNumNeutral]); + portal->Summoned = 1; } portal->TTL = GameCycle + this->TTL; // Goal is used to link to destination circle of power diff --git a/src/spell/spell_summon.cpp b/src/spell/spell_summon.cpp index 2033c9349..58bcfa81c 100644 --- a/src/spell/spell_summon.cpp +++ b/src/spell/spell_summon.cpp @@ -36,6 +36,8 @@ #include "spell/spell_summon.h" +#include "../ai/ai_local.h" + #include "actions.h" #include "commands.h" #include "script.h" @@ -57,7 +59,10 @@ } else if (!strcmp(value, "time-to-live")) { this->TTL = LuaToNumber(l, -1, j + 1); } else if (!strcmp(value, "require-corpse")) { - this->RequireCorpse = 1; + this->RequireCorpse = true; + --j; + } else if (!strcmp(value, "join-to-ai-force")) { + this->JoinToAiForce = true; --j; } else { LuaError(l, "Unsupported summon tag: %s" _C_ value); @@ -124,6 +129,8 @@ public: if (target != NULL) { target->tilePos = pos; DropOutOnSide(*target, LookingW, NULL); + // To avoid defending summoned unit for AI + target->Summoned = 1; // // set life span. ttl=0 results in a permanent unit. // @@ -131,13 +138,13 @@ public: target->TTL = GameCycle + ttl; } - // To avoid defending summoned unit for AI - if (caster.Player->AiEnabled) { - if (caster.GroupId) { + // Insert summoned unit to AI force so it will help them in battle + if (this->JoinToAiForce && caster.Player->AiEnabled) { + int force = caster.Player->Ai->Force.GetForce(caster); + if (force != -1) { + caster.Player->Ai->Force[force].Insert(*target); target->GroupId = caster.GroupId; CommandDefend(*target, caster, FlushCommands); - } else { - target->GroupId = -1; } } diff --git a/src/spell/spells.cpp b/src/spell/spells.cpp index 6daf10962..92a639071 100644 --- a/src/spell/spells.cpp +++ b/src/spell/spells.cpp @@ -485,7 +485,7 @@ SpellType::SpellType(int slot, const std::string &ident) : Ident(ident), Slot(slot), Target(), Action(), Range(0), ManaCost(0), RepeatCast(0), DependencyId(-1), Condition(NULL), - AutoCast(NULL), AICast(NULL) + AutoCast(NULL), AICast(NULL), ForceUseAnimation(false) { memset(Costs, 0, sizeof(Costs)); } diff --git a/src/tolua/particle.pkg b/src/tolua/particle.pkg index 3d157495d..db7d6172a 100644 --- a/src/tolua/particle.pkg +++ b/src/tolua/particle.pkg @@ -16,30 +16,35 @@ class GraphicAnimation class CParticle { virtual CParticle* clone(); + void setDrawLevel(int value); }; class StaticParticle : public CParticle { public: - StaticParticle(CPosition position, GraphicAnimation *animation); + StaticParticle(CPosition position, GraphicAnimation *animation, int drawlevel = 0); }; class CChunkParticle : public CParticle { public: - CChunkParticle(CPosition position, GraphicAnimation *smokeAnimation, GraphicAnimation *debrisAnimation, GraphicAnimation *destroyAnimation, int minVelocity = 0, int maxVelocity = 400, int minTrajectoryAngle = 77, int maxTTL = 0); + CChunkParticle(CPosition position, GraphicAnimation *smokeAnimation, GraphicAnimation *debrisAnimation, GraphicAnimation *destroyAnimation, int minVelocity = 0, int maxVelocity = 400, int minTrajectoryAngle = 77, int maxTTL = 0, int drawlevel = 0); + int getSmokeDrawLevel() const; + int getDestroyDrawLevel() const; + void setSmokeDrawLevel(int value); + void setDestroyDrawLevel(int value); }; class CSmokeParticle : public CParticle { public: - CSmokeParticle(CPosition position, GraphicAnimation *animation, float speedx = 0, float speedy = -22.0); + CSmokeParticle(CPosition position, GraphicAnimation *animation, float speedx = 0, float speedy = -22.0, int drawlevel = 0); }; class CRadialParticle : public CParticle { public: - CRadialParticle(CPosition position, GraphicAnimation *smokeAnimation, int maxSpeed); + CRadialParticle(CPosition position, GraphicAnimation *smokeAnimation, int maxSpeed, int drawlevel = 0); }; class CParticleManager diff --git a/src/tolua/unit.pkg b/src/tolua/unit.pkg index 61e996c70..e07191b96 100644 --- a/src/tolua/unit.pkg +++ b/src/tolua/unit.pkg @@ -22,6 +22,7 @@ class CPreference bool ShowAttackRange; bool ShowMessages; bool BigScreen; + bool PauseOnLeave; unsigned int ShowOrders; unsigned int ShowNameDelay; diff --git a/src/ui/botpanel.cpp b/src/ui/botpanel.cpp index ae32748b8..ceb6aa5c2 100644 --- a/src/ui/botpanel.cpp +++ b/src/ui/botpanel.cpp @@ -847,14 +847,17 @@ void UpdateStatusLineForButton(const ButtonAction &button) */ bool IsButtonAllowed(const CUnit &unit, const ButtonAction &buttonaction) { + bool res = false; if (buttonaction.Allowed) { - return buttonaction.Allowed(unit, buttonaction); + res = buttonaction.Allowed(unit, buttonaction); + if (!res) { + return false; + } else { + res = false; + } } - bool res = false; - // FIXME: we have to check and if these unit buttons are available - // i.e. if button action is ButtonTrain for example check if - // required unit is not restricted etc... + // Check button-specific cases switch (buttonaction.Action) { case ButtonStop: case ButtonStandGround: diff --git a/src/ui/mainscr.cpp b/src/ui/mainscr.cpp index 1247e3947..dc2713200 100644 --- a/src/ui/mainscr.cpp +++ b/src/ui/mainscr.cpp @@ -153,7 +153,7 @@ static void UiDrawLifeBar(const CUnit &unit, int x, int y) } f = (f * (unit.Type->Icon.Icon->G->Width)) / 100; - Video.FillRectangleClip(color, x + 1, y + 1, f - 2, 5); + Video.FillRectangleClip(color, x + 1, y + 1, f > 1 ? f - 2 : 0, 5); } } diff --git a/src/unit/script_unit.cpp b/src/unit/script_unit.cpp index 319887e59..07bc18d7b 100644 --- a/src/unit/script_unit.cpp +++ b/src/unit/script_unit.cpp @@ -377,6 +377,9 @@ static int CclUnit(lua_State *l) } else if (!strcmp(value, "selected")) { unit->Selected = 1; --j; + } else if (!strcmp(value, "summoned")) { + unit->Summoned = 1; + --j; } else if (!strcmp(value, "rescued-from")) { unit->RescuedFrom = &Players[LuaToNumber(l, 2, j + 1)]; } else if (!strcmp(value, "seen-by-player")) { diff --git a/src/unit/script_unittype.cpp b/src/unit/script_unittype.cpp index fa839ece0..9d0e87da7 100644 --- a/src/unit/script_unittype.cpp +++ b/src/unit/script_unittype.cpp @@ -551,6 +551,11 @@ static int CclDefineUnitType(lua_State *l) type->ExplodeWhenKilled = 1; type->Explosion.Name = LuaToString(l, -1); type->Explosion.Missile = NULL; + } else if (!strcmp(value, "TeleportCost")) { + type->TeleportCost = LuaToNumber(l, -1); + } else if (!strcmp(value, "TeleportEffect")) { + type->TeleportEffect.Name = LuaToString(l, -1); + type->TeleportEffect.Missile = NULL; } else if (!strcmp(value, "DeathExplosion")) { type->DeathExplosion = new LuaCallback(l, -1); } else if (!strcmp(value, "OnHit")) { @@ -559,6 +564,8 @@ static int CclDefineUnitType(lua_State *l) type->OnEachCycle = new LuaCallback(l, -1); } else if (!strcmp(value, "OnEachSecond")) { type->OnEachSecond = new LuaCallback(l, -1); + } else if (!strcmp(value, "OnInit")) { + type->OnInit = new LuaCallback(l, -1); } else if (!strcmp(value, "Type")) { value = LuaToString(l, -1); if (!strcmp(value, "land")) { diff --git a/src/unit/unit.cpp b/src/unit/unit.cpp index 56d5f42f4..d8f63cb09 100644 --- a/src/unit/unit.cpp +++ b/src/unit/unit.cpp @@ -453,6 +453,7 @@ void CUnit::Init() Moving = 0; ReCast = 0; CacheLock = 0; + Summoned = 0; memset(&Anim, 0, sizeof(Anim)); CurrentResource = 0; Orders.clear(); @@ -738,6 +739,12 @@ CUnit *MakeUnit(const CUnitType &type, CPlayer *player) unit->AssignToPlayer(*player); } + if (unit->Type->OnInit) { + unit->Type->OnInit->pushPreamble(); + unit->Type->OnInit->pushInteger(UnitNumber(*unit)); + unit->Type->OnInit->run(); + } + // fancy buildings: mirror buildings (but shadows not correct) if (type.Building && FancyBuildings && unit->Type->NoRandomPlacing == false && (MyRand() & 1) != 0) { diff --git a/src/unit/unit_find.cpp b/src/unit/unit_find.cpp index 3943f32de..5ca6b6ae9 100644 --- a/src/unit/unit_find.cpp +++ b/src/unit/unit_find.cpp @@ -310,7 +310,6 @@ public: resinfo(*worker.Type->ResInfo[resource]), deposit(deposit), movemask(worker.Type->MovementMask & ~(MapFieldLandUnit | MapFieldAirUnit | MapFieldSeaUnit)), - resource(resource), maxRange(maxRange), check_usage(check_usage), res_finder(resource, 1), @@ -348,7 +347,6 @@ private: const ResourceInfo &resinfo; const CUnit *deposit; unsigned int movemask; - int resource; int maxRange; bool check_usage; CResourceFinder res_finder; @@ -511,15 +509,16 @@ CUnit *FindIdleWorker(const CPlayer &player, const CUnit *last) /** ** Find all units of type. ** -** @param type type of unit requested -** @param units array in which we have to store the units +** @param type type of unit requested +** @param units array in which we have to store the units +** @param everybody if true, include all units */ -void FindUnitsByType(const CUnitType &type, std::vector<CUnit *> &units) +void FindUnitsByType(const CUnitType &type, std::vector<CUnit *> &units, bool everybody) { for (CUnitManager::Iterator it = UnitManager.begin(); it != UnitManager.end(); ++it) { CUnit &unit = **it; - if (unit.Type == &type && !unit.IsUnusable()) { + if (unit.Type == &type && !unit.IsUnusable(everybody)) { units.push_back(&unit); } } @@ -1008,9 +1007,7 @@ private: } // don't consider small damages... - if (sgood < 20) { - sgood = 20; - } + sgood = std::max(sgood, 20); int cost = sbad / sgood; if (cost > best_cost) { diff --git a/src/unit/unit_save.cpp b/src/unit/unit_save.cpp index a6a12b8d6..66cb15323 100644 --- a/src/unit/unit_save.cpp +++ b/src/unit/unit_save.cpp @@ -177,6 +177,9 @@ void SaveUnit(const CUnit &unit, CFile &file) if (unit.Selected) { file.printf(" \"selected\","); } + if (unit.Summoned) { + file.printf(" \"summoned\","); + } if (unit.RescuedFrom) { file.printf(" \"rescued-from\", %d,", unit.RescuedFrom->Index); } diff --git a/src/unit/unittype.cpp b/src/unit/unittype.cpp index e15fa6653..9f74572f8 100644 --- a/src/unit/unittype.cpp +++ b/src/unit/unittype.cpp @@ -611,7 +611,8 @@ CUnitType::CUnitType() : Slot(0), Width(0), Height(0), OffsetX(0), OffsetY(0), DrawLevel(0), ShadowWidth(0), ShadowHeight(0), ShadowOffsetX(0), ShadowOffsetY(0), Animations(NULL), StillFrame(0), - DeathExplosion(NULL), OnHit(NULL), OnEachCycle(NULL), OnEachSecond(NULL), + DeathExplosion(NULL), OnHit(NULL), OnEachCycle(NULL), OnEachSecond(NULL), OnInit(NULL), + TeleportCost(0), CorpseType(NULL), Construction(NULL), RepairHP(0), TileWidth(0), TileHeight(0), BoxWidth(0), BoxHeight(0), BoxOffsetX(0), BoxOffsetY(0), NumDirections(0), MinAttackRange(0), ReactRangeComputer(0), ReactRangePerson(0), Priority(0), @@ -649,6 +650,7 @@ CUnitType::~CUnitType() delete OnHit; delete OnEachCycle; delete OnEachSecond; + delete OnInit; BoolFlag.clear(); @@ -1123,6 +1125,7 @@ void LoadUnitTypes() // Lookup missiles. type.Missile.MapMissile(); type.Explosion.MapMissile(); + type.TeleportEffect.MapMissile(); // Lookup impacts for (int i = 0; i < ANIMATIONS_DEATHTYPES + 2; ++i) { diff --git a/src/unit/upgrade.cpp b/src/unit/upgrade.cpp index ed6ebb915..4c7e62fab 100644 --- a/src/unit/upgrade.cpp +++ b/src/unit/upgrade.cpp @@ -597,7 +597,7 @@ static void ApplyUpgradeModifier(CPlayer &player, const CUpgradeModifier *um) if (varModified) { std::vector<CUnit *> unitupgrade; - FindUnitsByType(*UnitTypes[z], unitupgrade); + FindUnitsByType(*UnitTypes[z], unitupgrade, true); for (size_t j = 0; j != unitupgrade.size(); ++j) { CUnit &unit = *unitupgrade[j]; diff --git a/src/video/sdl.cpp b/src/video/sdl.cpp index 48a97d57e..b7fe6cc3d 100644 --- a/src/video/sdl.cpp +++ b/src/video/sdl.cpp @@ -94,6 +94,7 @@ #include "sound_server.h" #include "translate.h" #include "ui.h" +#include "unit.h" #include "video.h" #include "widgets.h" @@ -860,7 +861,7 @@ static void SdlDoEvent(const EventCallback &callbacks, SDL_Event &event) } InMainWindow = (event.active.gain != 0); } - if (event.active.state & SDL_APPACTIVE || SDL_GetAppState() & SDL_APPACTIVE) { + if (Preference.PauseOnLeave && (event.active.state & SDL_APPACTIVE || SDL_GetAppState() & SDL_APPACTIVE)) { static bool DoTogglePause = false; if (IsSDLWindowVisible && !event.active.gain) { diff --git a/tests/network/test_netconnect.cpp b/tests/network/test_netconnect.cpp index 65fde63e2..d0cecbd17 100644 --- a/tests/network/test_netconnect.cpp +++ b/tests/network/test_netconnect.cpp @@ -53,6 +53,7 @@ void FillCustomValue(CServerSetup *obj) obj->GameTypeOption = 52; obj->Difficulty = 54; obj->MapRichness = 56; + obj->Opponents = 58; for (int i = 0; i != PlayerMax; ++i) { obj->CompOpt[i] = i + 1; }