Browse Source

* half-working boots of levitation and angel's wings

mateuszb 15 years ago
parent
commit
edd31ef41b

+ 1 - 1
client/CAdvmapInterface.cpp

@@ -1961,7 +1961,7 @@ void CAdvMapInt::tileHovered(const int3 &tile)
 		} 
 		else //no objs 
 		{
-			if(accessible)
+			if(accessible && pnode->accessible != CGPathNode::FLYABLE)
 			{
 				if (guardingCreature) {
 					CGI->curh->changeGraphic(0, 5 + turns*6);

+ 5 - 0
client/CPlayerInterface.cpp

@@ -969,6 +969,11 @@ void CPlayerInterface::heroBonusChanged( const CGHeroInstance *hero, const Bonus
 	if(bonus.type == Bonus::NONE)	return;
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
 	updateInfo(hero);
+	if (bonus.type == Bonus::FLYING_MOVEMENT || bonus.type == Bonus::WATER_WALKING && !gain)
+	{
+		//recalculate paths because hero has lost bonus influencing pathfinding
+		cb->recalculatePaths();
+	}
 }
 
 template <typename Handler> void CPlayerInterface::serializeTempl( Handler &h, const int version )

+ 2 - 2
hch/CArtHandler.cpp

@@ -479,7 +479,7 @@ void CArtHandler::addBonuses()
 
 	giveArtBonus(70,Bonus::LAND_MOVEMENT,+300);//Equestrian's Gloves
 	giveArtBonus(71,Bonus::SEA_MOVEMENT,+1000);//Necklace of Ocean Guidance
-	giveArtBonus(72,Bonus::FLYING_MOVEMENT,+1);//Angel Wings
+	giveArtBonus(72,Bonus::FLYING_MOVEMENT, 0, 1);//Angel Wings
 
 	giveArtBonus(73,Bonus::MANA_REGENERATION,+1);//Charm of Mana
 	giveArtBonus(74,Bonus::MANA_REGENERATION,+2);//Talisman of Mana
@@ -503,7 +503,7 @@ void CArtHandler::addBonuses()
 	giveArtBonus(88,Bonus::WATER_SPELLS,0);//Tome of Water Magic
 	giveArtBonus(89,Bonus::EARTH_SPELLS,0);//Tome of Earth Magic
 
-	giveArtBonus(90,Bonus::WATER_WALKING,0);//Boots of Levitation
+	giveArtBonus(90,Bonus::WATER_WALKING, 0, 1);//Boots of Levitation
 	giveArtBonus(91,Bonus::NO_SHOTING_PENALTY,0);//Golden Bow
 	giveArtBonus(92,Bonus::SPELL_IMMUNITY,35);//Sphere of Permanence
 	giveArtBonus(93,Bonus::NEGATE_ALL_NATURAL_IMMUNITIES,0);//Orb of Vulnerability

+ 3 - 2
hch/CObjectHandler.cpp

@@ -573,15 +573,16 @@ si32 CGHeroInstance::manaLimit() const
 
 bool CGHeroInstance::canWalkOnSea() const
 {
-	//TODO: write it - it should check if hero is flying, or something similar
-	return false;
+	return hasBonusOfType(Bonus::FLYING_MOVEMENT) || hasBonusOfType(Bonus::WATER_WALKING);
 }
+
 int CGHeroInstance::getPrimSkillLevel(int id) const
 {
 	int ret = valOfBonuses(Bonus::PRIMARY_SKILL, id);
 	amax(ret, id/2); //minimal value is 0 for attack and defense and 1 for spell power and knowledge
 	return ret;
 }
+
 ui8 CGHeroInstance::getSecSkillLevel(const int & ID) const
 {
 	for(size_t i=0; i < secSkills.size(); ++i)

+ 64 - 9
lib/CGameState.cpp

@@ -1771,8 +1771,8 @@ void CGameState::getNeighbours( const TerrainTile &srct, int3 tile, std::vector<
 
 		const TerrainTile &hlpt = map->getTile(hlp);
 
-		if((indeterminate(onLand)  ||  onLand == (hlpt.tertype!=8) ) 
-			&& hlpt.tertype!=9) 
+		if((indeterminate(onLand)  ||  onLand == (hlpt.tertype!=TerrainTile::water) ) 
+			&& hlpt.tertype != TerrainTile::rock) 
 		{
 			vec.push_back(hlp);
 		}
@@ -1790,6 +1790,23 @@ int CGameState::getMovementCost(const CGHeroInstance *h, const int3 &src, const
 	//get basic cost
 	int ret = h->getTileCost(d,s);
 
+	if(d.blocked)
+	{
+		bool freeFlying = h->getBonusesCount(Selector::typeSybtype(Bonus::FLYING_MOVEMENT, 1)) > 0;
+
+		if(!freeFlying)
+		{
+			ret *= 1.4f; //40% penalty for movement over blocked tile
+		}
+	}
+	else if (d.tertype == TerrainTile::water)
+	{
+		if (!h->boat && h->getBonusesCount(Selector::typeSybtype(Bonus::WATER_WALKING, 1)) > 0)
+		{
+			ret *= 1.4f; //40% penalty for water walking
+		}
+	}
+
 	if(src.x != dest.x  &&  src.y != dest.y) //it's diagonal move
 	{
 		int old = ret;
@@ -2104,6 +2121,9 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int
 
 	const std::vector<std::vector<std::vector<ui8> > > &FoW = getPlayer(hero->tempOwner)->fogOfWarMap;
 
+	bool flying = hero->hasBonusOfType(Bonus::FLYING_MOVEMENT);
+	bool waterWalk = hero->hasBonusOfType(Bonus::WATER_WALKING);
+
 	//graph initialization
 	CGPathNode ***graph = out.nodes;
 	for(size_t i=0; i < out.sizes.x; ++i)
@@ -2115,7 +2135,11 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int
 				const TerrainTile *tinfo = &map->terrain[i][j][k];
 				CGPathNode &node = graph[i][j][k];
 
-				node.accessible = (tinfo->blocked ? CGPathNode::BLOCKED : CGPathNode::ACCESSIBLE);
+				node.accessible = (tinfo->blocked ? CGPathNode::FLYABLE : CGPathNode::ACCESSIBLE);
+				if(!flying && node.accessible == CGPathNode::FLYABLE)
+				{
+					node.accessible = CGPathNode::BLOCKED;
+				}
 				node.turns = 0xff;
 				node.moveRemains = 0;
 				node.coord.x = i;
@@ -2124,9 +2148,20 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int
 				node.land = tinfo->tertype != TerrainTile::water;
 				node.theNodeBefore = NULL;
 
+				if((onLand || indeterminate(onLand)) && !node.land)//it's sea and we cannot walk on sea
+				{
+					if (waterWalk || flying)
+					{
+						node.accessible = CGPathNode::FLYABLE;
+					}
+					else
+					{
+						node.accessible = CGPathNode::BLOCKED;
+					}
+				}
+
 				if ( tinfo->tertype == TerrainTile::rock//it's rock
-					|| onLand  && !node.land		//it's sea and we cannot walk on sea
-					|| !onLand && node.land		//it's land and we cannot walk on land
+					|| !onLand && node.land		//it's land and we cannot walk on land (complementary condition is handled above)
 					|| !FoW[i][j][k]					//tile is covered by the FoW
 				)
 				{
@@ -2185,7 +2220,7 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int
 	std::queue<CGPathNode*> mq;
 	mq.push(&graph[src.x][src.y][src.z]);
 
-	ui32 curDist = 0xffffffff; //total cost of path - init with max possible val
+	//ui32 curDist = 0xffffffff; //total cost of path - init with max possible val
 
 	std::vector<int3> neighbours;
 	neighbours.reserve(8);
@@ -2248,7 +2283,7 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int
 				const bool guardedNeighbor = guardingCreaturePosition(dp.coord) != int3(-1, -1, -1);
 				const bool positionIsGuard = guardingCreaturePosition(cp->coord) == cp->coord;
 
-				if (dp.accessible == CGPathNode::ACCESSIBLE
+				if (dp.accessible == CGPathNode::ACCESSIBLE || dp.accessible == CGPathNode::FLYABLE
 					|| (guardedNeighbor && !positionIsGuard)) // Can step into a hostile tile once.
 				{
 					mq.push(&dp);
@@ -3651,13 +3686,33 @@ bool CPathsInfo::getPath( const int3 &dst, CGPath &out )
 {
 	out.nodes.clear();
 	const CGPathNode *curnode = &nodes[dst.x][dst.y][dst.z];
-	if(!curnode->theNodeBefore)
+	if(!curnode->theNodeBefore || curnode->accessible == CGPathNode::FLYABLE)
 		return false;
 
+
+	//we'll transform number of turns to conform the rule that hero cannot stop on blocked tile
+	bool transition01 = false;
 	while(curnode)
 	{
-		out.nodes.push_back(*curnode);
+		CGPathNode cpn = *curnode;
+		if(transition01)
+		{
+			if (curnode->accessible == CGPathNode::ACCESSIBLE)
+			{
+				transition01 = false;
+			}
+			else if (curnode->accessible == CGPathNode::FLYABLE)
+			{
+				cpn.turns = 1;
+			}
+		}
+		if(curnode->turns == 1 && curnode->theNodeBefore->turns == 0)
+		{
+			transition01 = true;
+		}
 		curnode = curnode->theNodeBefore;
+
+		out.nodes.push_back(cpn);
 	}
 	return true;
 }

+ 2 - 1
lib/CGameState.h

@@ -306,7 +306,8 @@ struct CGPathNode
 		ACCESSIBLE=1, //tile can be entered and passed
 		VISITABLE, //tile can be entered as the last tile in path
 		BLOCKVIS,  //visitable from neighbouring tile but not passable
-		BLOCKED //tile can't be entered nor visited
+		BLOCKED, //tile can't be entered nor visited
+		FLYABLE //if hero flies, he can pass this tile
 	};
 
 	ui8 accessible; //the enum above

+ 2 - 2
lib/HeroBonus.h

@@ -50,14 +50,14 @@ namespace PrimarySkill
 	BONUS_NAME(SECONDARY_SKILL_PREMY) /*%*/  \
 	BONUS_NAME(SURRENDER_DISCOUNT) /*%*/  \
 	BONUS_NAME(STACKS_SPEED)  /*additional info - percent of speed bonus applied after direct bonuses; >0 - added, <0 - substracted to this part*/ \
-	BONUS_NAME(FLYING_MOVEMENT) \
+	BONUS_NAME(FLYING_MOVEMENT) /*subtype 1 - without penalty, 2 - with penalty*/ \
 	BONUS_NAME(SPELL_DURATION) \
 	BONUS_NAME(AIR_SPELL_DMG_PREMY) \
 	BONUS_NAME(EARTH_SPELL_DMG_PREMY) \
 	BONUS_NAME(FIRE_SPELL_DMG_PREMY) \
 	BONUS_NAME(WATER_SPELL_DMG_PREMY) \
 	BONUS_NAME(BLOCK_SPELLS_ABOVE_LEVEL) \
-	BONUS_NAME(WATER_WALKING) \
+	BONUS_NAME(WATER_WALKING) /*subtype 1 - without penalty, 2 - with penalty*/ \
 	BONUS_NAME(NO_SHOTING_PENALTY) \
 	BONUS_NAME(DISPEL_IMMUNITY) \
 	BONUS_NAME(NEGATE_ALL_NATURAL_IMMUNITIES) \

+ 23 - 5
server/CGameHandler.cpp

@@ -1591,7 +1591,7 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255*
 	}
 
 	TerrainTile t = gs->map->terrain[hmpos.x][hmpos.y][hmpos.z];
-	int cost = gs->getMovementCost(h,h->getPosition(false),CGHeroInstance::convertPosition(dst,false),h->movement);
+	int cost = gs->getMovementCost(h, h->getPosition(false), CGHeroInstance::convertPosition(dst,false),h->movement);
 
 	//result structure for start - movement failed, no move points used
 	TryMoveHero tmh;
@@ -1605,7 +1605,7 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255*
 
 	//it's a rock or blocked and not visitable tile 
 	//OR hero is on land and dest is water and (there is not present only one object - boat)
-	if((t.tertype == TerrainTile::rock  ||  (t.blocked && !t.visitable)) 
+	if((t.tertype == TerrainTile::rock  ||  (t.blocked && !t.visitable && !h->hasBonusOfType(Bonus::FLYING_MOVEMENT) )) 
 			&& complain("Cannot move hero, destination tile is blocked!") 
 		|| (!h->boat && !h->canWalkOnSea() && t.tertype == TerrainTile::water && (t.visitableObjects.size() != 1 ||  (t.visitableObjects.front()->ID != 8 && t.visitableObjects.front()->ID != HEROI_TYPE)))  //hero is not on boat/water walking and dst water tile doesn't contain boat/hero (objs visitable from land)
 			&& complain("Cannot move hero, destination tile is on water!")
@@ -1647,7 +1647,7 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255*
 	//checks for standard movement
 	if(!instant)
 	{
-		if( distance(h->pos,dst) >= 1.5  &&  complain("Tiles are not neighbouring!")
+		if( distance(h->pos,dst) >= 1.5  &&  complain("Tiles are not neighboring!")
 			|| h->movement < cost  &&  h->movement < 100  &&  complain("Not enough move points!")) 
 		{
 			sendAndApply(&tmh);
@@ -4455,12 +4455,30 @@ bool CGameHandler::castSpell(const CGHeroInstance *h, int spellID, const int3 &p
 			sendAndApply(&tmh);
 		}
 		break;
+	case FLY: //Fly 
+		{
+			int subtype = schoolLevel >= 2 ? 1 : 2; //adv or expert
+
+			GiveBonus gb;
+			gb.id = h->id;
+			gb.bonus = Bonus(Bonus::ONE_DAY, Bonus::FLYING_MOVEMENT, Bonus::CASTED_SPELL, 0, Spells::FLY, subtype);
+			sendAndApply(&gb);
+		}
+		break;
+	case WATER_WALK: //Water Walk 
+		{
+			int subtype = schoolLevel >= 2 ? 1 : 2; //adv or expert
+
+			GiveBonus gb;
+			gb.id = h->id;
+			gb.bonus = Bonus(Bonus::ONE_DAY, Bonus::WATER_WALKING, Bonus::CASTED_SPELL, 0, Spells::FLY, subtype);
+			sendAndApply(&gb);
+		}
+		break;
 	case VISIONS: //Visions 
 	case VIEW_EARTH: //View Earth 
 	case DISGUISE: //Disguise 
 	case VIEW_AIR: //View Air 
-	case FLY: //Fly 
-	case WATER_WALK: //Water Walk 
 	case TOWN_PORTAL: //Town Portal 
 	default:
 		COMPLAIN_RET("This spell is not implemented yet!");