Browse Source

* spells in the guild visible again
* garrisons on adv. map again available from all directions
* working Subterranean Gates, Monoliths
* minor code reorganizations

Michał W. Urbańczyk 17 years ago
parent
commit
41acf5528c
15 changed files with 210 additions and 68 deletions
  1. 1 1
      CCallback.h
  2. 1 1
      CCastleInterface.cpp
  3. 37 3
      CGameState.cpp
  4. 3 2
      CGameState.h
  5. 66 1
      CLua.cpp
  6. 12 1
      CLua.h
  7. 14 6
      CPlayerInterface.cpp
  8. 16 7
      client/Client.cpp
  9. 1 1
      global.h
  10. 1 1
      hch/CDefObjInfoHandler.cpp
  11. 2 0
      int3.h
  12. 1 1
      lib/NetPacks.h
  13. 23 43
      server/CGameHandler.cpp
  14. 31 0
      server/CScriptCallback.cpp
  15. 1 0
      server/CScriptCallback.h

+ 1 - 1
CCallback.h

@@ -88,7 +88,7 @@ struct HeroMoveDetails
 	HeroMoveDetails(int3 Src, int3 Dst, CGHeroInstance*Ho);
 	int3 src, dst; //source and destination points
 	CGHeroInstance * ho; //object instance of this hero
-	int owner;
+	int owner, style; //style: 0 - normal move, 1 - teleport, 2 - instant jump
 	bool successful;
 };
 

+ 1 - 1
CCastleInterface.cpp

@@ -1440,7 +1440,7 @@ CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner)
 		int sp = owner->town->spellsAtLevel(i+1,false);
 		for(int j=0; j<sp; j++)
 		{
-			if(i<owner->town->mageGuildLevel() && owner->town->spells[i].size()<j)
+			if(i<owner->town->mageGuildLevel() && owner->town->spells[i].size()>j)
 			{
 				spells.push_back(Scroll(&CGI->spellh->spells[owner->town->spells[i][j]]));
 				spells[spells.size()-1].pos = positions[i][j];

+ 37 - 3
CGameState.cpp

@@ -255,7 +255,7 @@ std::vector<int> BattleInfo::getPath(int start, int dest, bool*accessibility)
 }
 
 CStack::CStack(CCreature * C, int A, int O, int I, bool AO, int S)
-	:creature(C),amount(A), baseAmount(A), owner(O), position(-1), ID(I), attackerOwned(AO), firstHPleft(C->hitPoints), slot(S), counterAttacks(0)
+	:creature(C),amount(A), baseAmount(A), owner(O), position(-1), ID(I), attackerOwned(AO), firstHPleft(C->hitPoints), slot(S), counterAttacks(1)
 {
 	abilities = C->abilities;
 	state.insert(ALIVE);
@@ -541,7 +541,7 @@ void CGameState::applyNL(IPack * pack)
 			BattleNextRound *ns = static_cast<BattleNextRound*>(pack);
 			curB->round = ns->round;
 			for(int i=0; i<curB->stacks.size();i++)
-				curB->stacks[i]->counterAttacks = 0;
+				curB->stacks[i]->counterAttacks = 1;
 			break;
 		}
 	case 3002:
@@ -582,7 +582,7 @@ void CGameState::applyNL(IPack * pack)
 		{
 			BattleAttack *br = static_cast<BattleAttack*>(pack);
 			if(br->counter())
-				curB->getStack(br->stackAttacking)->counterAttacks++;
+				curB->getStack(br->stackAttacking)->counterAttacks--;
 			applyNL(&br->bsa);
 			break;
 		}
@@ -1313,6 +1313,40 @@ float CGameState::getMarketEfficiency( int player, int mode/*=0*/ )
 	float ret = std::min(((float)mcount+1.0f)/20.0f,0.5f);
 	return ret;
 }
+
+std::set<int3> CGameState::tilesToReveal(int3 pos, int radious, int player)
+{		
+	std::set<int3> ret;
+	int xbeg = pos.x - radious - 2;
+	if(xbeg < 0)
+		xbeg = 0;
+	int xend = pos.x + radious + 2;
+	if(xend >= map->width)
+		xend = map->width;
+	int ybeg = pos.y - radious - 2;
+	if(ybeg < 0)
+		ybeg = 0;
+	int yend = pos.y + radious + 2;
+	if(yend >= map->height)
+		yend = map->height;
+	for(int xd=xbeg; xd<xend; ++xd) //revealing part of map around heroes
+	{
+		for(int yd=ybeg; yd<yend; ++yd)
+		{
+			int deltaX = (pos.x-xd)*(pos.x-xd);
+			int deltaY = (pos.y-yd)*(pos.y-yd);
+			if(deltaX+deltaY<radious*radious)
+			{
+				if(player<0 || players[player].fogOfWarMap[xd][yd][pos.z]==0)
+				{
+					ret.insert(int3(xd,yd,pos.z));
+				}
+			}
+		}
+	}
+	return ret;
+}
+
 int BattleInfo::calculateDmg(const CStack* attacker, const CStack* defender)
 {
 	int attackDefenseBonus = attacker->creature->attack - defender->creature->defence;

+ 3 - 2
CGameState.h

@@ -87,13 +87,13 @@ public:
 	ui8 owner, slot;  //owner - player colour (255 for neutrals), slot - position in garrison (may be 255 for neutrals/called creatures)
 	ui8 attackerOwned; //if true, this stack is owned by attakcer (this one from left hand side of battle)
 	ui16 position; //position on battlefield
-	ui8 counterAttacks; //how many counter attacks has this stack perfomed in current round
+	ui8 counterAttacks; //how many counter attacks can be performed more in this turn (by default set at the beginning of the round to 1)
 
 	std::set<EAbilities> abilities;
 	std::set<ECombatInfo> state;
 
 	CStack(CCreature * C, int A, int O, int I, bool AO, int S);
-	CStack() : creature(NULL),amount(-1),owner(255), position(-1), ID(-1), attackerOwned(true), firstHPleft(-1), slot(255), baseAmount(-1), counterAttacks(0){};
+	CStack() : creature(NULL),amount(-1),owner(255), position(-1), ID(-1), attackerOwned(true), firstHPleft(-1), slot(255), baseAmount(-1), counterAttacks(1){};
 
 	template <typename Handler> void save(Handler &h, const int version)
 	{
@@ -161,6 +161,7 @@ private:
 	int battleGetStack(int pos); //returns ID of stack at given tile
 	UpgradeInfo getUpgradeInfo(CArmedInstance *obj, int stackPos);
 	float getMarketEfficiency(int player, int mode=0);
+	std::set<int3> tilesToReveal(int3 pos, int radious, int player); //if player==-1 => adds all tiles in radious
 public:
 	int getDate(int mode=0) const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month
 

+ 66 - 1
CLua.cpp

@@ -873,7 +873,72 @@ void CCreatureGen::onHeroVisit(int objid, int heroID)
 }
 std::vector<int> CCreatureGen::yourObjects() //returns IDs of objects which are handled by script
 {
-	std::vector<int> ret(1);
+	std::vector<int> ret;
 	ret.push_back(17); //cregen1
 	return ret;
 }
+
+void CTeleports::newObject(int objid)
+{
+	DEFOS;
+	objs[os->ID][os->subID].push_back(objid);
+}
+
+void CTeleports::onHeroVisit(int objid, int heroID)
+{
+	DEFOS;
+	int destinationid=-1;
+	switch(os->ID)
+	{
+	case 43: //one way - find correspong exit monolith
+		if(vstd::contains(objs,44) && vstd::contains(objs[44],os->subID) && objs[44][os->subID].size())
+			destinationid = objs[44][os->subID][rand()%objs[44][os->subID].size()];
+		else
+			tlog2 << "Cannot find corresponding exit monolith for "<<objid << std::endl;
+		break;
+	case 45: //two way monolith - pick any other one
+		if(vstd::contains(objs,45) && vstd::contains(objs[45],os->subID) && objs[45][os->subID].size()>1)
+			while ((destinationid = objs[45][os->subID][rand()%objs[45][os->subID].size()])==objid);
+		else
+			tlog2 << "Cannot find corresponding exit monolith for "<<objid << std::endl;
+		break;
+	case 103: //find nearest subterranean gate on the other level
+		{
+			std::pair<int,double> best(-1,150000); //pair<id,dist>
+			for(int i=0; i<objs[103][0].size(); i++)
+			{
+				if(cb->getObj(objs[103][0][i])->pos.z == os->pos.z) continue; //gates on our level are not interesting
+				double hlp = cb->getObj(objs[103][0][i])->pos.dist2d(os->pos);
+				if(hlp<best.second)
+				{
+					best.first = objs[103][0][i];
+					best.second = hlp;
+				}
+			}
+			if(best.first<0)
+				return;
+			else 
+				destinationid = best.first;
+			break;
+		}
+	}
+	if(destinationid < 0)
+	{
+		tlog2 << "Cannot find exit... :( \n";
+		return;
+	}
+	cb->moveHero(heroID,(os->ID!=103)
+		?(CGHeroInstance::convertPosition(cb->getObj(destinationid)->pos,true))
+		:(cb->getObj(destinationid)->pos),
+	true);
+}
+
+std::vector<int> CTeleports::yourObjects() //returns IDs of objects which are handled by script
+{
+	std::vector<int> ret;
+	ret.push_back(43); 
+	ret.push_back(44); 
+	ret.push_back(45); 
+	ret.push_back(103); 
+	return ret;
+}

+ 12 - 1
CLua.h

@@ -39,7 +39,7 @@ public:
 
 
 	//functions to be called in script
-	//virtual void init(){};
+	//virtual void init(){}; //called when game is ready
 	virtual void newObject(int objid){};
 	virtual void onHeroVisit(int objid, int heroID){};
 	virtual void onHeroLeave(int objid, int heroID){};
@@ -187,3 +187,14 @@ public:
 	void onHeroVisit(int objid, int heroID);
 	std::vector<int> yourObjects(); //returns IDs of objects which are handled by script
 };
+
+class CTeleports : public CCPPObjectScript
+{
+public:
+	std::map<int,std::map<int, std::vector<int> > > objs; //map[ID][subID] => vector of ids
+
+	void newObject(int objid);
+	void onHeroVisit(int objid, int heroID);
+	std::vector<int> yourObjects(); //returns IDs of objects which are handled by script
+	CTeleports(CScriptCallback * CB):CCPPObjectScript(CB){};
+};

+ 14 - 6
CPlayerInterface.cpp

@@ -1101,10 +1101,23 @@ int getDir(int3 src, int3 dst)
 void CPlayerInterface::heroMoved(const HeroMoveDetails & details)
 {
 	boost::unique_lock<boost::recursive_mutex> un(*pim);
+
+	int3 buff = details.ho->pos;
+	buff.x-=11;
+	buff.y-=9;
+	buff = repairScreenPos(buff);
+	LOCPLINT->adventureInt->position = buff; //actualizing screen pos
+
+	if(adventureInt == curint)
+		adventureInt->minimap.draw();
+
+	if(details.style)
+		return;
+
 	//initializing objects and performing first step of move
 	const CGHeroInstance * ho = details.ho; //object representing this hero
 	int3 hp = details.src;
-	if (!details.successful)
+	if (!details.successful) //hero failed to move
 	{
 		ho->moveDir = getDir(details.src,details.dst);
 		ho->isStanding = true;
@@ -1123,11 +1136,6 @@ void CPlayerInterface::heroMoved(const HeroMoveDetails & details)
 		adventureInt->terrain.currentPath->nodes.erase(adventureInt->terrain.currentPath->nodes.end()-1);
 	}
 
-	int3 buff = details.ho->pos;
-	buff.x-=11;
-	buff.y-=9;
-	buff = repairScreenPos(buff);
-	LOCPLINT->adventureInt->position = buff; //actualizing screen pos
 
 	if(details.dst.x+1 == details.src.x && details.dst.y+1 == details.src.y) //tl
 	{

+ 16 - 7
client/Client.cpp

@@ -284,7 +284,16 @@ void CClient::process(int what)
 			*serv >> *th;
 			tlog5 << "HeroMove: id="<<th->id<<"\tResult: "<<(unsigned)th->result<<"\tPosition "<<th->end<<std::endl;
 
+			HeroMoveDetails hmd(th->start,th->end,static_cast<CGHeroInstance*>(gs->map->objects[th->id]));
+			hmd.style = th->result-1;
+			hmd.successful = th->result;
+			if(th->result>1)
+				CGI->mh->removeObject(hmd.ho);
+
 			gs->apply(th);
+			
+			if(th->result>1)
+				CGI->mh->printObject(hmd.ho);
 			int player = gs->map->objects[th->id]->getOwner();
 
 			if(playerint[player])
@@ -295,22 +304,22 @@ void CClient::process(int what)
 			}
 
 			//notify interfacesabout move
-			int nn=0; //number of interfece of currently browsed player
 			for(std::map<ui8, CGameInterface*>::iterator i=playerint.begin();i!=playerint.end();i++)
 			{
 				if(gs->players[i->first].fogOfWarMap[th->start.x-1][th->start.y][th->start.z] || gs->players[i->first].fogOfWarMap[th->end.x-1][th->end.y][th->end.z])
 				{
-					HeroMoveDetails hmd(th->start,th->end,static_cast<CGHeroInstance*>(gs->map->objects[th->id]));
-					hmd.successful = th->result;
 					i->second->heroMoved(hmd);
 				}
 			}
 
 			//add info for callback
-			mess.mx->lock();
-			mess.res->insert(th);
-			mess.mx->unlock();
-			mess.cv->notify_all();
+			if(th->result<2)
+			{
+				mess.mx->lock();
+				mess.res->insert(th);
+				mess.mx->unlock();
+				mess.cv->notify_all();
+			}
 			break;
 		}
 	case 502:

+ 1 - 1
global.h

@@ -16,7 +16,7 @@ typedef boost::int8_t si8; //signed int 8 bits (1 byte)
 #define THC
 #endif
 
-#define NAME_VER ("VCMI 0.62b")
+#define NAME_VER ("VCMI 0.62c")
 #define CONSOLE_LOGGING_LEVEL 5
 #define FILE_LOGGING_LEVEL 6
 

+ 1 - 1
hch/CDefObjInfoHandler.cpp

@@ -66,7 +66,7 @@ void CDefObjInfoHandler::load()
 		inp>>nobj->id;
 		inp>>nobj->subid;
 		inp>>nobj->type;
-		if(nobj->type == 2 || nobj->type == 3 || nobj->type == 4 || nobj->type == 5 || nobj->id == 111) //creature, hero, artifact, resource or whripool
+		if(nobj->type == 2 || nobj->type == 3 || nobj->type == 4 || nobj->type == 5 || nobj->id == 111 || nobj->id == 33) //creature, hero, artifact, resource or whripool or garrison
 			nobj->visitDir = 0xff;
 		else
 			nobj->visitDir = (8|16|32|64|128); //disabled visiting from the top

+ 2 - 0
int3.h

@@ -42,6 +42,8 @@ public:
 		{return int3(x-i,y-i,z-i);}
 	inline int3 operator-() const //increases all components by si32
 		{return int3(-x,-y,-z);}
+	inline double dist2d(const int3 other) const //distance (z coord is not used)
+		{return std::sqrt((double)(x-other.x)*(x-other.x) + (y-other.y)*(y-other.y));}
 	inline void operator+=(const int3 & i)
 	{
 		x+=i.x;

+ 1 - 1
lib/NetPacks.h

@@ -124,7 +124,7 @@ struct TryMoveHero : public CPack<TryMoveHero> //501
 	TryMoveHero(){type = 501;};
 
 	ui32 id, movePoints;
-	ui8 result;
+	ui8 result; //0 - failed; 1- succes -normal move; 2 - teleportation, 3 - instant jump
 	int3 start, end;
 	std::set<int3> fowRevealed; //revealed tiles
 

+ 23 - 43
server/CGameHandler.cpp

@@ -397,6 +397,7 @@ void CGameHandler::handleConnection(std::set<int> players, CConnection &c)
 					int3 start, end;
 					si32 id;
 					c >> id >> start >> end;
+					tlog5 << "Interface wants to move hero "<<id << " from "<<start << " to " << end << std::endl;
 					int3 hmpos = end + int3(-1,0,0);
 					TerrainTile t = gs->map->terrain[hmpos.x][hmpos.y][hmpos.z];
 					CGHeroInstance *h = static_cast<CGHeroInstance *>(gs->map->objects[id]);
@@ -445,6 +446,7 @@ void CGameHandler::handleConnection(std::set<int> players, CConnection &c)
 									obj->state->onHeroVisit(obj->id,h->id);
 							}
 						}
+						tlog5 << "Blocing visit at " << hmpos << std::endl;
 						break;
 					}
 					else //normal move
@@ -457,38 +459,9 @@ void CGameHandler::handleConnection(std::set<int> players, CConnection &c)
 							if(obj->state) //hard-coded function
 								obj->state->onHeroLeave(obj->id,h->id);
 						}
-
-						//reveal fog of war
-						int heroSight = h->getSightDistance();
-						int xbeg = start.x - heroSight - 2;
-						if(xbeg < 0)
-							xbeg = 0;
-						int xend = start.x + heroSight + 2;
-						if(xend >= gs->map->width)
-							xend = gs->map->width;
-						int ybeg = start.y - heroSight - 2;
-						if(ybeg < 0)
-							ybeg = 0;
-						int yend = start.y + heroSight + 2;
-						if(yend >= gs->map->height)
-							yend = gs->map->height;
-						for(int xd=xbeg; xd<xend; ++xd) //revealing part of map around heroes
-						{
-							for(int yd=ybeg; yd<yend; ++yd)
-							{
-								int deltaX = (hmpos.x-xd)*(hmpos.x-xd);
-								int deltaY = (hmpos.y-yd)*(hmpos.y-yd);
-								if(deltaX+deltaY<h->getSightDistance()*h->getSightDistance())
-								{
-									if(gs->players[h->getOwner()].fogOfWarMap[xd][yd][hmpos.z] == 0)
-									{
-										tmh.fowRevealed.insert(int3(xd,yd,hmpos.z));
-									}
-								}
-							}
-						}
-
+						tmh.fowRevealed = gs->tilesToReveal(h->getPosition(false),h->getSightDistance(),h->tempOwner);
 						sendAndApply(&tmh);
+						tlog5 << "Moved to " <<tmh.end<<std::endl;
 
 						BOOST_FOREACH(CGObjectInstance *obj, t.visitableObjects)//call objects if they are visited
 						{
@@ -498,8 +471,10 @@ void CGameHandler::handleConnection(std::set<int> players, CConnection &c)
 								obj->state->onHeroVisit(obj->id,h->id);
 						}
 					}
+					tlog5 << "Movement end!\n";
 					break;
 				fail:
+					tlog2 << "Movement failed to " << tmh.end << std::endl;
 					sendAndApply(&tmh);
 					break;
 				}
@@ -577,8 +552,17 @@ void CGameHandler::handleConnection(std::set<int> players, CConnection &c)
 					for(int i=0;i<RESOURCE_QUANTITY;i++)
 						if(b->resources[i] > gs->players[t->tempOwner].resources[i])
 							break; //no res
-					//TODO: check requirements
-					//TODO: check if building isn't forbidden
+
+					for( std::set<int>::iterator ri  =  VLC->townh->requirements[t->subID][bid].begin();
+						 ri != VLC->townh->requirements[t->subID][bid].end();
+						 ri++ )
+					{
+						if(!vstd::contains(t->builtBuildings,*ri))
+							break; //lack of requirements - cannot build
+					}
+
+					if(vstd::contains(t->forbiddenBuildings,bid))
+						break; //this building is forbidden
 
 					NewStructures ns;
 					ns.tid = tid;
@@ -948,7 +932,7 @@ upgend:
 							//counterattack
 							if(!vstd::contains(curStack->abilities,NO_ENEMY_RETALIATION)
 								&& stackAtEnd->alive()
-								&& !stackAtEnd->counterAttacks	) //TODO: support for multiple retaliatons per turn
+								&& stackAtEnd->counterAttacks	) //TODO: support for multiple retaliatons per turn
 							{
 								prepareAttack(bat,stackAtEnd,curStack);
 								bat.flags |= 2;
@@ -991,23 +975,18 @@ upgend:
 					break;
 				}
 			default:
-#ifndef __GNUC__
-				throw std::exception("Not supported client message!");
-#else
-				throw std::exception();
-#endif
-				break;
+				throw std::string("Not supported client message!");
 			}
 		}
 	}
 	catch (const std::exception& e)
 	{
-		tlog1 << e.what() << std::endl;
+		tlog1 << "Exception during handling connection: " << e.what() << std::endl;
 		end2 = true;
 	}
 	catch (const std::exception * e)
 	{
-		tlog1 << e->what()<< std::endl;	
+		tlog1 << "Exception during handling connection: " << e->what()<< std::endl;	
 		end2 = true;
 		delete e;
 	}
@@ -1016,7 +995,7 @@ upgend:
 		end2 = true;
 	}
 handleConEnd:
-	;
+	tlog1 << "Ended handling connection\n";
 }
 void CGameHandler::moveStack(int stack, int dest)
 {							
@@ -1214,6 +1193,7 @@ void CGameHandler::run()
 	handleCPPObjS(&scripts,new CHeroScript(csc));
 	handleCPPObjS(&scripts,new CMonsterS(csc));
 	handleCPPObjS(&scripts,new CCreatureGen(csc));
+	handleCPPObjS(&scripts,new CTeleports(csc));
 
 	/****************************INITIALIZING OBJECT SCRIPTS************************************************/
 	//std::string temps("newObject");

+ 31 - 0
server/CScriptCallback.cpp

@@ -39,6 +39,37 @@ void CScriptCallback::setAmount(int objid, ui32 val)
 	gh->sendAndApply(&sop);
 }
 
+void CScriptCallback::moveHero(int hid, int3 pos, bool instant)
+{
+	if(!instant)
+	{
+		tlog1 << "Not supported call to CScriptCallback::moveHero\n";
+		return;
+	}
+	CGHeroInstance *h = const_cast<CGHeroInstance *>(getHero(hid));
+	//check if destination tile is free
+	BOOST_FOREACH(CGObjectInstance* obj, gh->gs->map->terrain[pos.x-1][pos.y][pos.z].blockingObjects)
+	{
+		if(obj->ID==34)
+		{
+			if(obj->tempOwner==h->tempOwner) 
+				return;//TODO: exchange
+			//TODO: check for ally
+			CGHeroInstance *dh = static_cast<CGHeroInstance *>(obj);
+			startBattle(&h->army,&dh->army,pos,h,dh,0);
+			return;
+		}
+	}
+	TryMoveHero tmh;
+	tmh.start = h->pos;
+	tmh.end = pos;
+	tmh.id = hid;
+	tmh.movePoints = h->movement;
+	tmh.result = instant+1;
+	tmh.fowRevealed = gh->gs->tilesToReveal(CGHeroInstance::convertPosition(pos,false),h->getSightDistance(),h->tempOwner);
+	gh->sendAndApply(&tmh);
+}
+
 void CScriptCallback::setOwner(int objid, ui8 owner)
 {
 	SetObjectProperty sop(objid,1,owner);

+ 1 - 0
server/CScriptCallback.h

@@ -56,6 +56,7 @@ public:
 	void startBattle(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, boost::function<void(BattleResult*)> cb); //use hero=NULL for no hero
 	void startBattle(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb); //for hero<=>neutral army
 	void setAmount(int objid, ui32 val);
+	void moveHero(int hid, int3 pos, bool instant);
 
 	//friends
 	friend class CGameHandler;