From 9c7004c8cc4e8748ba35adbb29f9f659806efca7 Mon Sep 17 00:00:00 2001
From: cybermind <iddqd_mail@mail.ru>
Date: Tue, 30 Apr 2013 19:34:46 +0600
Subject: [PATCH] [*] Spawn-portal spell improvement

---
 src/action/action_follow.cpp          | 62 ++++++++++++++-------------
 src/include/spell/spell_spawnportal.h |  4 +-
 src/spell/spell_spawnportal.cpp       | 11 ++++-
 src/ui/mouse.cpp                      |  6 +--
 4 files changed, 47 insertions(+), 36 deletions(-)

diff --git a/src/action/action_follow.cpp b/src/action/action_follow.cpp
index c80340b0b..adc9227cf 100644
--- a/src/action/action_follow.cpp
+++ b/src/action/action_follow.cpp
@@ -203,41 +203,43 @@ enum {
 			}
 			// Handle Teleporter Units
 			// FIXME: BAD HACK
-			if (goal->Type->Teleporter && goal->Goal && unit.MapDistanceTo(*goal) <= 1) {
-				// Teleport the unit
-				unit.Remove(NULL);
-				unit.tilePos = goal->Goal->tilePos;
-				DropOutOnSide(unit, unit.Direction, NULL);
+			// 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());
+					// 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;
+					// FIXME: we must check if the units supports the new order.
+					CUnit &dest = *goal->Goal;
 
-				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 ;
+					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 ;
 					}
-					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/include/spell/spell_spawnportal.h b/src/include/spell/spell_spawnportal.h
index 0c781560e..39ebd42db 100644
--- a/src/include/spell/spell_spawnportal.h
+++ b/src/include/spell/spell_spawnportal.h
@@ -41,13 +41,15 @@
 class Spell_SpawnPortal : public SpellActionType
 {
 public:
-	Spell_SpawnPortal() : PortalType(0) {};
+	Spell_SpawnPortal() : PortalType(0), TTL(0), CurrentPlayer(false) {};
 	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 *PortalType;   /// The unit type spawned
+	int TTL;                 /// Time to live for summoned portal. 0 means infinite
+	bool CurrentPlayer;      /// If true, summon portal for caster's player rather than neutral 
 };
 
 
diff --git a/src/spell/spell_spawnportal.cpp b/src/spell/spell_spawnportal.cpp
index 59fa020c4..b444dacf7 100644
--- a/src/spell/spell_spawnportal.cpp
+++ b/src/spell/spell_spawnportal.cpp
@@ -49,6 +49,11 @@
 				this->PortalType = 0;
 				DebugPrint("unit type \"%s\" not found for spawn-portal.\n" _C_ value);
 			}
+		} else if (!strcmp(value, "time-to-live")) {
+			this->TTL = LuaToNumber(l, -1, j + 1);
+		} else if (!strcmp(value, "current-player")) {
+			this->CurrentPlayer = true;
+			--j;
 		} else {
 			LuaError(l, "Unsupported spawn-portal tag: %s" _C_ value);
 		}
@@ -75,11 +80,13 @@
 	CUnit *portal = caster.Goal;
 
 	DebugPrint("Spawning a portal exit.\n");
-	if (portal) {
+	if (portal && portal->IsAlive()) {
 		portal->MoveToXY(goalPos);
 	} else {
-		portal = MakeUnitAndPlace(goalPos, *this->PortalType, &Players[PlayerNumNeutral]);
+		portal = MakeUnitAndPlace(goalPos, *this->PortalType,
+			CurrentPlayer ? caster.Player : &Players[PlayerNumNeutral]);
 	}
+	portal->TTL = GameCycle + this->TTL;
 	//  Goal is used to link to destination circle of power
 	caster.Goal = portal;
 	//FIXME: setting destination circle of power should use mana
diff --git a/src/ui/mouse.cpp b/src/ui/mouse.cpp
index 656bcbf64..814765c00 100644
--- a/src/ui/mouse.cpp
+++ b/src/ui/mouse.cpp
@@ -259,7 +259,7 @@ static bool DoRightButton_Harvest(CUnit &unit, CUnit *dest, const Vec2i &pos, in
 			PlayUnitSound(unit, VoiceAcknowledging);
 			acknowledged = 1;
 		}
-		if (dest->Type->CanMove() == false) {
+		if (dest->Type->CanMove() == false && !dest->Type->Teleporter) {
 			SendCommandMove(unit, pos, flush);
 		} else {
 			SendCommandFollow(unit, *dest, flush);
@@ -309,7 +309,7 @@ static void DoRightButton_Attack(CUnit &unit, CUnit *dest, const Vec2i &pos, int
 				PlayUnitSound(unit, VoiceAcknowledging);
 				acknowledged = 1;
 			}
-			if (dest->Type->CanMove() == false) {
+			if (dest->Type->CanMove() == false && !dest->Type->Teleporter) {
 				SendCommandMove(unit, pos, flush);
 			} else {
 				SendCommandFollow(unit, *dest, flush);
@@ -369,7 +369,7 @@ static bool DoRightButton_Follow(CUnit &unit, CUnit &dest, int flush, int &ackno
 			PlayUnitSound(unit, VoiceAcknowledging);
 			acknowledged = 1;
 		}
-		if (dest.Type->CanMove() == false) {
+		if (dest.Type->CanMove() == false && !dest.Type->Teleporter) {
 			SendCommandMove(unit, dest.tilePos, flush);
 		} else {
 			SendCommandFollow(unit, dest, flush);