Selaa lähdekoodia

Merge pull request #1783 from vcmi/beta

Merge beta -> develop
Ivan Savenko 2 vuotta sitten
vanhempi
sitoutus
b2f5042942

+ 5 - 0
client/battle/BattleObstacleController.cpp

@@ -78,6 +78,11 @@ void BattleObstacleController::obstaclePlaced(const std::vector<std::shared_ptr<
 {
 	for (auto const & oi : obstacles)
 	{
+		auto side = owner.curInt->cb->playerToSide(owner.curInt->playerID);
+
+		if(!oi->visibleForSide(side.get(),owner.curInt->cb->battleHasNativeStack(side.get())))
+			continue;
+
 		auto spellObstacle = dynamic_cast<const SpellCreatedObstacle*>(oi.get());
 
 		if (!spellObstacle)

+ 1 - 1
config/schemas/settings.json

@@ -335,7 +335,7 @@
 				},
 				"playerAI" : {
 					"type" : "string",
-					"default" : "VCAI"
+					"default" : "Nullkiller"
 				},
 				"friendlyAI" : {
 					"type" : "string",

+ 1 - 2
config/spells/other.json

@@ -90,8 +90,7 @@
 					"damage":{
 						"type":"core:damage",
 						"optional":false,
-						"indirect":true,
-						"customEffectId" : 82
+						"indirect":true
 					}
 				}
 			},

+ 2 - 2
lib/GameConstants.h

@@ -249,8 +249,8 @@ public:
 	}
 
 	bool operator == (const Identifier & b) const { return num == b.num; }
-	bool operator <= (const Identifier & b) const { return num >= b.num; }
-	bool operator >= (const Identifier & b) const { return num <= b.num; }
+	bool operator <= (const Identifier & b) const { return num <= b.num; }
+	bool operator >= (const Identifier & b) const { return num >= b.num; }
 	bool operator != (const Identifier & b) const { return num != b.num; }
 	bool operator <  (const Identifier & b) const { return num <  b.num; }
 	bool operator >  (const Identifier & b) const { return num > b.num; }

+ 0 - 2
lib/NetPacksBase.h

@@ -308,8 +308,6 @@ public:
 		RESET_STATE,
 		UPDATE,
 		REMOVE,
-		ACTIVATE_AND_UPDATE,
-		ACTIVATE_AND_REMOVE
 	};
 
 	JsonNode data;

+ 0 - 1
lib/NetPacksLib.cpp

@@ -2402,7 +2402,6 @@ void BattleObstaclesChanged::applyBattle(IBattleState * battleState)
 		case BattleChanges::EOperation::ADD:
 			battleState->addObstacle(change);
 			break;
-		case BattleChanges::EOperation::ACTIVATE_AND_UPDATE:
 		case BattleChanges::EOperation::UPDATE:
 			battleState->updateObstacle(change);
 			break;

+ 1 - 1
lib/VCMIDirs.cpp

@@ -346,7 +346,7 @@ std::vector<bfs::path> VCMIDirsWIN32::dataPaths() const
 }
 
 bfs::path VCMIDirsWIN32::clientPath() const { return binaryPath() / "VCMI_client.exe"; }
-bfs::path VCMIDirsWIN32::mapEditorPath() const { return binaryPath() / "VCMI_editor.exe"; }
+bfs::path VCMIDirsWIN32::mapEditorPath() const { return binaryPath() / "VCMI_mapeditor.exe"; }
 bfs::path VCMIDirsWIN32::serverPath() const { return binaryPath() / "VCMI_server.exe"; }
 
 bfs::path VCMIDirsWIN32::libraryPath() const { return "."; }

+ 14 - 7
lib/battle/CBattleInfoCallback.cpp

@@ -798,19 +798,21 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::battl
 	return obstacles;
 }
 
-std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::getAllAffectedObstaclesByStack(const battle::Unit * unit) const
+std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::getAllAffectedObstaclesByStack(const battle::Unit * unit, const std::set<BattleHex> & passed) const
 {
-	std::vector<std::shared_ptr<const CObstacleInstance>> affectedObstacles = std::vector<std::shared_ptr<const CObstacleInstance>>();
+	auto affectedObstacles = std::vector<std::shared_ptr<const CObstacleInstance>>();
 	RETURN_IF_NOT_BATTLE(affectedObstacles);
 	if(unit->alive())
 	{
-		affectedObstacles = battleGetAllObstaclesOnPos(unit->getPosition(), false);
+		if(!passed.count(unit->getPosition()))
+			affectedObstacles = battleGetAllObstaclesOnPos(unit->getPosition(), false);
 		if(unit->doubleWide())
 		{
-			BattleHex otherHex = unit->occupiedHex(unit->getPosition());
-			if(otherHex.isValid())
+			BattleHex otherHex = unit->occupiedHex();
+			if(otherHex.isValid() && !passed.count(otherHex))
 				for(auto & i : battleGetAllObstaclesOnPos(otherHex, false))
-					affectedObstacles.push_back(i);
+					if(!vstd::contains(affectedObstacles, i))
+						affectedObstacles.push_back(i);
 		}
 		for(auto hex : unit->getHexes())
 			if(hex == ESiegeHex::GATE_BRIDGE)
@@ -932,6 +934,8 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi
 		return ret;
 
 	const std::set<BattleHex> obstacles = getStoppers(params.perspective);
+	auto checkParams = params;
+	checkParams.ignoreKnownAccessible = true; //Ignore starting hexes obstacles
 
 	std::queue<BattleHex> hexq; //bfs queue
 
@@ -949,7 +953,7 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi
 		hexq.pop();
 
 		//walking stack can't step past the obstacles
-		if(curHex != params.startPosition && isInObstacle(curHex, obstacles, params))
+		if(isInObstacle(curHex, obstacles, checkParams))
 			continue;
 
 		const int costToNeighbour = ret.distances[curHex.hex] + 1;
@@ -982,6 +986,9 @@ bool CBattleInfoCallback::isInObstacle(
 
 	for(auto occupiedHex : occupiedHexes)
 	{
+		if(params.ignoreKnownAccessible && vstd::contains(params.knownAccessible, occupiedHex))
+			continue;
+
 		if(vstd::contains(obstacles, occupiedHex))
 		{
 			if(occupiedHex == ESiegeHex::GATE_BRIDGE)

+ 1 - 1
lib/battle/CBattleInfoCallback.h

@@ -60,7 +60,7 @@ public:
 	boost::optional<int> battleIsFinished() const override; //return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw
 
 	std::vector<std::shared_ptr<const CObstacleInstance>> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const override;
-	std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const battle::Unit * unit) const override;
+	std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const battle::Unit * unit, const std::set<BattleHex> & passed) const override;
 
 	const CStack * battleGetStackByPos(BattleHex pos, bool onlyAlive = true) const;
 

+ 1 - 4
lib/battle/CBattleInfoEssentials.cpp

@@ -47,10 +47,7 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoEssentials::bat
 	else
 	{
 		if(!!player && *perspective != battleGetMySide())
-		{
-			logGlobal->error("Unauthorized obstacles access attempt!");
-			return ret;
-		}
+			logGlobal->warn("Unauthorized obstacles access attempt, assuming massive spell");
 	}
 
 	for(const auto & obstacle : getBattle()->getAllObstacles())

+ 1 - 1
lib/battle/IBattleInfoCallback.h

@@ -72,7 +72,7 @@ public:
 
 	//blocking obstacles makes tile inaccessible, others cause special effects (like Land Mines, Moat, Quicksands)
 	virtual std::vector<std::shared_ptr<const CObstacleInstance>> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const = 0;
-	virtual std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const battle::Unit * unit) const = 0;
+	virtual std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const battle::Unit * unit, const std::set<BattleHex> & passed) const = 0;
 };
 
 

+ 1 - 0
lib/battle/ReachabilityInfo.h

@@ -28,6 +28,7 @@ struct DLL_LINKAGE ReachabilityInfo
 		ui8 side = 0;
 		bool doubleWide = false;
 		bool flying = false;
+		bool ignoreKnownAccessible = false; //Ignore obstacles if it is in accessible hexes
 		std::vector<BattleHex> knownAccessible; //hexes that will be treated as accessible, even if they're occupied by stack (by default - tiles occupied by stack we do reachability for, so it doesn't block itself)
 
 		BattleHex startPosition; //assumed position of stack

+ 11 - 1
lib/rmg/Functions.cpp

@@ -130,7 +130,17 @@ void initTerrainType(Zone & zone, CMapGenerator & gen)
 	{
 		if(zone.isMatchTerrainToTown() && zone.getTownType() != ETownType::NEUTRAL)
 		{
-			zone.setTerrainType((*VLC->townh)[zone.getTownType()]->nativeTerrain);
+			auto terrainType = (*VLC->townh)[zone.getTownType()]->nativeTerrain;
+
+			if (terrainType <= ETerrainId::NONE)
+			{
+				logGlobal->warn("Town %s has invalid terrain type: %d", zone.getTownType(), terrainType);
+				zone.setTerrainType(ETerrainId::DIRT);
+			}
+			else
+			{
+				zone.setTerrainType(terrainType);
+			}
 		}
 		else
 		{

+ 4 - 6
lib/spells/effects/Obstacle.cpp

@@ -121,6 +121,9 @@ void Obstacle::adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics
 
 bool Obstacle::applicable(Problem & problem, const Mechanics * m) const
 {
+	if(hidden && m->battle()->battleHasNativeStack(m->battle()->otherSide(m->casterSide)))
+		return m->adaptProblem(ESpellCastProblem::NO_APPROPRIATE_TARGET, problem);
+
 	return LocationEffect::applicable(problem, m);
 }
 
@@ -270,12 +273,7 @@ void Obstacle::placeObstacles(ServerCallback * server, const Mechanics * m, cons
 
 	BattleObstaclesChanged pack;
 
-	boost::optional<BattlePerspective::BattlePerspective> perspective;
-
-	if(!m->battle()->getPlayerID())
-		perspective = boost::make_optional(BattlePerspective::ALL_KNOWING);
-
-	auto all = m->battle()->battleGetAllObstacles(perspective);
+	auto all = m->battle()->battleGetAllObstacles(BattlePerspective::ALL_KNOWING);
 
 	int obstacleIdToGive = 1;
 	for(auto & one : all)

+ 2 - 2
lib/spells/effects/RemoveObstacle.cpp

@@ -72,7 +72,7 @@ bool RemoveObstacle::canRemove(const CObstacleInstance * obstacle) const
 		return true;
 	const auto *spellObstacle = dynamic_cast<const SpellCreatedObstacle *>(obstacle);
 
-	if(removeAllSpells && spellObstacle)
+	if(removeAllSpells && obstacle->obstacleType == CObstacleInstance::SPELL_CREATED)
 		return true;
 
 	if(spellObstacle && !removeSpells.empty())
@@ -89,7 +89,7 @@ std::set<const CObstacleInstance *> RemoveObstacle::getTargets(const Mechanics *
 	std::set<const CObstacleInstance *> possibleTargets;
 	if(m->isMassive() || alwaysMassive)
 	{
-		for(const auto & obstacle : m->battle()->battleGetAllObstacles())
+		for(const auto & obstacle : m->battle()->battleGetAllObstacles(BattlePerspective::ALL_KNOWING))
 			if(canRemove(obstacle.get()))
 				possibleTargets.insert(obstacle.get());
 	}

+ 4 - 4
lib/spells/effects/UnitEffect.cpp

@@ -40,7 +40,7 @@ bool UnitEffect::applicable(Problem & problem, const Mechanics * m) const
 {
 	//stack effect is applicable in general if there is at least one smart target
 
-	auto mainFilter = std::bind(&UnitEffect::getStackFilter, this, m, true, _1);
+	auto mainFilter = std::bind(&UnitEffect::getStackFilter, this, m, false, _1);
 	auto predicate = std::bind(&UnitEffect::eraseByImmunityFilter, this, m, _1);
 
 	auto targets = m->battle()->battleGetUnitsIf(mainFilter);
@@ -53,12 +53,12 @@ bool UnitEffect::applicable(Problem & problem, const Mechanics * m) const
 
 bool UnitEffect::applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const
 {
-	//stack effect is applicable if it affects at least one smart target
-	//assume target correctly transformed, just reapply smart filter
+	//stack effect is applicable if it affects at least one target (smartness should not be checked)
+	//assume target correctly transformed, just reapply filter
 
 	for(const auto & item : target)
 		if(item.unitValue)
-			if(getStackFilter(m, true, item.unitValue))
+			if(getStackFilter(m, false, item.unitValue))
 				return true;
 
 	return false;

+ 25 - 13
server/CGameHandler.cpp

@@ -1401,6 +1401,11 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
 
 	//initing necessary tables
 	auto accessibility = getAccesibility(curStack);
+	std::set<BattleHex> passed;
+	//Ignore obstacles on starting position
+	passed.insert(curStack->getPosition());
+	if(curStack->doubleWide())
+		passed.insert(curStack->occupiedHex());
 
 	//shifting destination (if we have double wide stack and we can occupy dest but not be exactly there)
 	if(!stackAtEnd && curStack->doubleWide() && !accessibility.accessible(dest, curStack))
@@ -1593,10 +1598,12 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
 						if(otherHex.isValid() && !obstacle2.empty())
 							obstacleHit = true;
 					}
+					if(!obstacleHit)
+						passed.insert(hex);
 				}
 			}
 
-			if (tiles.size() > 0)
+			if (!tiles.empty())
 			{
 				//commit movement
 				BattleStackMoved sm;
@@ -1612,7 +1619,12 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
 			if (curStack->getPosition() != dest)
 			{
 				if(stackIsMoving && start != curStack->getPosition())
-					stackIsMoving = handleDamageFromObstacle(curStack, stackIsMoving);
+				{
+					stackIsMoving = handleDamageFromObstacle(curStack, stackIsMoving, passed);
+					passed.insert(curStack->getPosition());
+					if(curStack->doubleWide())
+						passed.insert(curStack->occupiedHex());
+				}
 				if (gateStateChanging)
 				{
 					if (curStack->getPosition() == openGateAtHex)
@@ -1640,7 +1652,7 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
 	}
 
 	//handling obstacle on the final field (separate, because it affects both flying and walking stacks)
-	handleDamageFromObstacle(curStack);
+	handleDamageFromObstacle(curStack, false, passed);
 
 	return ret;
 }
@@ -5301,13 +5313,13 @@ void CGameHandler::stackTurnTrigger(const CStack *st)
 	}
 }
 
-bool CGameHandler::handleDamageFromObstacle(const CStack * curStack, bool stackIsMoving)
+bool CGameHandler::handleDamageFromObstacle(const CStack * curStack, bool stackIsMoving, const std::set<BattleHex> & passed)
 {
 	if(!curStack->alive())
 		return false;
 	bool containDamageFromMoat = false;
-	bool movementStoped = false;
-	for(auto & obstacle : getAllAffectedObstaclesByStack(curStack))
+	bool movementStopped = false;
+	for(auto & obstacle : getAllAffectedObstaclesByStack(curStack, passed))
 	{
 		if(obstacle->obstacleType == CObstacleInstance::SPELL_CREATED)
 		{
@@ -5318,7 +5330,7 @@ bool CGameHandler::handleDamageFromObstacle(const CStack * curStack, bool stackI
 			if(!spellObstacle)
 				COMPLAIN_RET("Invalid obstacle instance");
 
-			if(spellObstacle->trigger)
+			if(spellObstacle->triggersEffects())
 			{
 				const bool oneTimeObstacle = spellObstacle->removeOnTrigger;
 
@@ -5336,9 +5348,9 @@ bool CGameHandler::handleDamageFromObstacle(const CStack * curStack, bool stackI
 					ObstacleChanges changeInfo;
 					changeInfo.id = spellObstacle->uniqueID;
 					if (oneTimeObstacle)
-						changeInfo.operation = ObstacleChanges::EOperation::ACTIVATE_AND_REMOVE;
+						changeInfo.operation = ObstacleChanges::EOperation::REMOVE;
 					else
-						changeInfo.operation = ObstacleChanges::EOperation::ACTIVATE_AND_UPDATE;
+						changeInfo.operation = ObstacleChanges::EOperation::UPDATE;
 
 					SpellCreatedObstacle changedObstacle;
 					changedObstacle.uniqueID = spellObstacle->uniqueID;
@@ -5382,13 +5394,13 @@ bool CGameHandler::handleDamageFromObstacle(const CStack * curStack, bool stackI
 			return false;
 
 		if((obstacle->stopsMovement() && stackIsMoving))
-			movementStoped = true;
+			movementStopped = true;
 	}
 
 	if(stackIsMoving)
-		return curStack->alive() && !movementStoped;
-	else
-		return curStack->alive();
+		return curStack->alive() && !movementStopped;
+	
+	return curStack->alive();
 }
 
 void CGameHandler::handleTimeEvents()

+ 1 - 1
server/CGameHandler.h

@@ -231,7 +231,7 @@ public:
 	bool makeCustomAction(BattleAction &ba);
 	void stackEnchantedTrigger(const CStack * stack);
 	void stackTurnTrigger(const CStack *stack);
-	bool handleDamageFromObstacle(const CStack * curStack, bool stackIsMoving = false); //checks if obstacle is land mine and handles possible consequences
+	bool handleDamageFromObstacle(const CStack * curStack, bool stackIsMoving = false, const std::set<BattleHex> & passed = {}); //checks if obstacle is land mine and handles possible consequences
 
 	void removeObstacle(const CObstacleInstance &obstacle);
 	bool queryReply( QueryID qid, const JsonNode & answer, PlayerColor player );