Jelajahi Sumber

* Neutral creatures can join or escape depending on hero strength / army (partially done)
* Diplomacy secondary skill support

Michał W. Urbańczyk 16 tahun lalu
induk
melakukan
de71191307
5 mengubah file dengan 258 tambahan dan 23 penghapusan
  1. 1 1
      CPlayerInterface.cpp
  2. 228 1
      hch/CObjectHandler.cpp
  3. 10 2
      hch/CObjectHandler.h
  4. 18 18
      server/CGameHandler.cpp
  5. 1 1
      server/CGameHandler.h

+ 1 - 1
CPlayerInterface.cpp

@@ -4850,7 +4850,7 @@ CGarrisonWindow::CGarrisonWindow( const CArmedInstance *up, const CGHeroInstance
 	pos.w = screen->w;
 	pos.w = screen->w;
 	pos.h = screen->h;
 	pos.h = screen->h;
 
 
-	garr = new CGarrisonInt(pos.x+92, pos.y+129, 4, 30, bg, 92, 126, up, down);
+	garr = new CGarrisonInt(pos.x+92, pos.y+129, 4, 30, bg, 92, 129, up, down);
 	split = new AdventureMapButton(CGI->generaltexth->tcommands[3],"",boost::bind(&CGarrisonInt::splitClick,garr),pos.x+88,pos.y+314,"IDV6432.DEF");
 	split = new AdventureMapButton(CGI->generaltexth->tcommands[3],"",boost::bind(&CGarrisonInt::splitClick,garr),pos.x+88,pos.y+314,"IDV6432.DEF");
 	quit = new AdventureMapButton(CGI->generaltexth->tcommands[8],"",boost::bind(&CGarrisonWindow::close,this),pos.x+399,pos.y+314,"IOK6432.DEF",SDLK_RETURN);
 	quit = new AdventureMapButton(CGI->generaltexth->tcommands[8],"",boost::bind(&CGarrisonWindow::close,this),pos.x+399,pos.y+314,"IOK6432.DEF",SDLK_RETURN);
 }
 }

+ 228 - 1
hch/CObjectHandler.cpp

@@ -761,6 +761,16 @@ void CGHeroInstance::setPropertyDer( ui8 what, ui32 val )
 		army.slots[0].second = val;
 		army.slots[0].second = val;
 }
 }
 
 
+double CGHeroInstance::getHeroStrength() const
+{
+	return sqrt((1.0 + 0.05*getPrimSkillLevel(0)) * (1.0 + 0.05*getPrimSkillLevel(1)));
+}
+
+int CGHeroInstance::getTotalStrength() const
+{
+	return getHeroStrength() * getArmyStrength();
+}
+
 int3 CGHeroInstance::getSightCenter() const
 int3 CGHeroInstance::getSightCenter() const
 {
 {
 	return getPosition(false);
 	return getPosition(false);
@@ -1240,9 +1250,53 @@ bool CArmedInstance::needsLastStack() const
 	return false;
 	return false;
 }
 }
 
 
+int CArmedInstance::getArmyStrength() const
+{
+	int ret = 0;
+	for(std::map<si32,std::pair<ui32,si32> >::const_iterator i=army.slots.begin(); i!=army.slots.end(); i++)
+		ret += VLC->creh->creatures[i->second.first].AIValue;
+	return ret;
+}
+
 void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
 void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
 {
 {
-	cb->startBattleI(h->id,army,pos,boost::bind(&CGCreature::endBattle,this,_1));
+	int action = takenAction(h);
+	switch( action ) //decide what we do...
+	{
+	case -2: //fight
+		fight(h);
+		break;
+	case -1: //flee
+		{
+			flee(h);
+			break;
+		}
+	case 0: //join for free
+		{
+			BlockingDialog ynd(true,false);
+			ynd.player = h->tempOwner;
+			ynd.text << std::pair<ui8,ui32>(11,86); 
+			ynd.text.replacements.push_back(VLC->creh->creatures[subID].namePl);
+			cb->showBlockingDialog(&ynd,boost::bind(&CGCreature::joinDecision,this,h,0,_1));
+			break;
+		}
+	default: //join for gold
+		{
+			assert(action > 0);
+
+			//ask if player agrees to pay gold
+			BlockingDialog ynd(true,false);
+			ynd.player = h->tempOwner;
+			std::string tmp = VLC->generaltexth->advobtxt[90];
+			boost::algorithm::replace_first(tmp,"%d",boost::lexical_cast<std::string>(army.slots.find(0)->second.second));
+			boost::algorithm::replace_first(tmp,"%d",boost::lexical_cast<std::string>(action));
+			boost::algorithm::replace_first(tmp,"%s",VLC->creh->creatures[subID].namePl);
+			ynd.text << tmp;
+			cb->showBlockingDialog(&ynd,boost::bind(&CGCreature::joinDecision,this,h,action,_1));
+			break;
+		}
+	}
+
 }
 }
 
 
 void CGCreature::endBattle( BattleResult *result ) const
 void CGCreature::endBattle( BattleResult *result ) const
@@ -1270,6 +1324,25 @@ void CGCreature::endBattle( BattleResult *result ) const
 void CGCreature::initObj()
 void CGCreature::initObj()
 {
 {
 	blockVisit = true;
 	blockVisit = true;
+	switch(character)
+	{
+	case 0:
+		character = 0;
+		break;
+	case 1:
+		character = 1 + ran()%7;
+		break;
+	case 2:
+		character = 1 + ran()%10;
+		break;
+	case 3:
+		character = 4 + ran()%7;
+		break;
+	case 4:
+		character = 10;
+		break;
+	}
+
 	army.slots[0].first = subID;
 	army.slots[0].first = subID;
 	si32 &amount = army.slots[0].second;
 	si32 &amount = army.slots[0].second;
 	CCreature &c = VLC->creh->creatures[subID];
 	CCreature &c = VLC->creh->creatures[subID];
@@ -1286,6 +1359,160 @@ void CGCreature::initObj()
 	hoverName = toString(ms);
 	hoverName = toString(ms);
 }
 }
 
 
+int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
+{
+	double hlp = h->getTotalStrength() / getArmyStrength();
+
+	if(!character) //compliant creatures will always join
+		return 0;
+	else if(allowJoin)//test for joining
+	{
+		int factor;
+		if(hlp >= 7)
+			factor = 11;
+		else if(hlp >= 1)
+			factor = 2*(hlp-1);
+		else if(hlp >= 0.5)
+			factor = -1;
+		else if(hlp >= 0.333)
+			factor = -2;
+		else
+			factor = -3;
+
+		int sympathy = 0;
+
+		std::set<ui32> myKindCres; //what creatures are the same kind as we
+		myKindCres.insert(subID); //we
+		myKindCres.insert(VLC->creh->creatures[subID].upgrades.begin(),VLC->creh->creatures[subID].upgrades.end()); //our upgrades
+		for(std::vector<CCreature>::iterator i=VLC->creh->creatures.begin(); i!=VLC->creh->creatures.end(); i++)
+			if(vstd::contains(i->upgrades,id)) //it's our base creatures
+				myKindCres.insert(i->idNumber);
+
+		int count = 0, //how many creatures of our kind has hero
+			totalCount = 0;
+		for (std::map<si32,std::pair<ui32,si32> >::const_iterator i = h->army.slots.begin(); i != h->army.slots.end(); i++)
+		{
+			if(vstd::contains(myKindCres,i->second.first))
+				count += i->second.second;
+			totalCount += i->second.second;
+		}
+
+		if(count*2 > totalCount)
+			sympathy++;
+		if(count)
+			sympathy++;
+		
+
+		int charisma = factor + h->getSecSkillLevel(4) + sympathy;
+		if(charisma >= character) //creatures might join...
+		{
+			if(h->getSecSkillLevel(4) + sympathy + 1 >= character)
+				return 0; //join for free
+			else if(h->getSecSkillLevel(4) * 2  +  sympathy  +  1 >= character)
+				return VLC->creh->creatures[subID].cost[6] * army.slots.find(0)->second.second; //join for gold
+		}
+	}
+
+	//we are still here - creatures not joined heroes, test for fleeing
+
+	//TODO: it's provisional formula, should be replaced with original one (or something closer to it)
+	//TODO: should be deterministic (will be needed for Vision spell)
+	int hlp2 = (hlp - 2)*1000;
+	if(!neverFlees   
+		&& hlp2 >= 0 
+		&& rand()%2000 < hlp2
+	)
+		return -1;
+	else
+		return -2;
+
+}
+
+void CGCreature::fleeDecision(const CGHeroInstance *h, ui32 pursue) const
+{
+	if(pursue)
+	{
+		fight(h);
+	}
+	else
+	{
+		cb->removeObject(id);
+	}
+}
+
+void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const
+{
+	if(!accept)
+	{
+		if(takenAction(h,false) == -1) //they flee
+		{
+			flee(h);
+		}
+		else //they fight
+		{
+			InfoWindow iw;
+			iw.player = h->tempOwner;
+			iw.text << std::pair<ui8,ui32>(11,87);  //Insulted by your refusal of their offer, the monsters attack!
+			cb->showInfoDialog(&iw);
+			fight(h);
+		}
+	}
+	else //accepted
+	{
+		if (cb->getResource(h->tempOwner,6) < cost) //player don't have enough gold!
+		{
+			InfoWindow iw;
+			iw.player = h->tempOwner;
+			iw.text << std::pair<ui8,ui32>(1,29);  //You don't have enough gold
+			cb->showInfoDialog(&iw);
+
+			//act as if player refused
+			joinDecision(h,cost,true);
+			return;
+		}
+
+		//take gold
+		if(cost)
+			cb->giveResource(h->tempOwner,6,-cost);
+
+		int slot = h->army.getSlotFor(subID);
+		if(slot >= 0) //there is place
+		{
+			//add creatures
+			SetGarrisons sg;
+			sg.garrs[h->id] = h->army;
+			if(vstd::contains(h->army.slots,slot)) //add to already present stack
+			{
+				sg.garrs[h->id].slots[slot].second += army.slots.find(0)->second.second;
+			}
+			else //add as a new stack
+			{
+				sg.garrs[h->id].slots[slot] = army.slots.find(0)->second;
+			}
+			cb->sendAndApply(&sg);
+			cb->removeObject(id);
+		}
+		else
+		{
+			cb->showGarrisonDialog(id,h->id,boost::bind(&IGameCallback::removeObject,cb,id)); //show garrison window and remove ourselves from map when player ends
+		}
+	}
+}
+
+void CGCreature::fight( const CGHeroInstance *h ) const
+{
+	cb->startBattleI(h->id,army,pos,boost::bind(&CGCreature::endBattle,this,_1));
+}
+
+void CGCreature::flee( const CGHeroInstance * h ) const
+{
+	BlockingDialog ynd(true,false);
+	ynd.player = h->tempOwner;
+	ynd.text << std::pair<ui8,ui32>(11,91); 
+	ynd.text.replacements.push_back(VLC->creh->creatures[subID].namePl);
+	cb->showBlockingDialog(&ynd,boost::bind(&CGCreature::fleeDecision,this,h,_1));
+}
+
 void CGMine::onHeroVisit( const CGHeroInstance * h ) const
 void CGMine::onHeroVisit( const CGHeroInstance * h ) const
 {
 {
 	if(subID == 7) //TODO: support for abandoned mine
 	if(subID == 7) //TODO: support for abandoned mine

+ 10 - 2
hch/CObjectHandler.h

@@ -155,6 +155,7 @@ class  DLL_EXPORT CArmedInstance: public CGObjectInstance
 public:
 public:
 	CCreatureSet army; //army
 	CCreatureSet army; //army
 	virtual bool needsLastStack() const; //true if last stack cannot be taken
 	virtual bool needsLastStack() const; //true if last stack cannot be taken
+	int getArmyStrength() const; //sum of AI values of creatures
 
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{
@@ -249,7 +250,8 @@ public:
 	const CArtifact * getArt(int pos) const;
 	const CArtifact * getArt(int pos) const;
 	int getSpellSecLevel(int spell) const; //returns level of secondary ability (fire, water, earth, air magic) known to this hero and applicable to given spell; -1 if error
 	int getSpellSecLevel(int spell) const; //returns level of secondary ability (fire, water, earth, air magic) known to this hero and applicable to given spell; -1 if error
 	static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
 	static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
-
+	double getHeroStrength() const;
+	int getTotalStrength() const;
 
 
 	//////////////////////////////////////////////////////////////////////////
 	//////////////////////////////////////////////////////////////////////////
 
 
@@ -399,16 +401,22 @@ class DLL_EXPORT CGCreature : public CArmedInstance //creatures on map
 {
 {
 public:
 public:
 	ui32 identifier; //unique code for this monster (used in missions)
 	ui32 identifier; //unique code for this monster (used in missions)
-	ui8 character; //chracter of this set of creatures (0 - the most friendly, 4 - the most hostile)
+	si8 character; //chracter of this set of creatures (0 - the most friendly, 4 - the most hostile) => on init changed to 0 (compliant) - 10 value (savage)
 	std::string message; //message printed for attacking hero
 	std::string message; //message printed for attacking hero
 	std::vector<ui32> resources; //[res_id], resources given to hero that has won with monsters
 	std::vector<ui32> resources; //[res_id], resources given to hero that has won with monsters
 	si32 gainedArtifact; //ID of artifact gained to hero, -1 if none
 	si32 gainedArtifact; //ID of artifact gained to hero, -1 if none
 	ui8 neverFlees; //if true, the troops will never flee
 	ui8 neverFlees; //if true, the troops will never flee
 	ui8 notGrowingTeam; //if true, number of units won't grow
 	ui8 notGrowingTeam; //if true, number of units won't grow
 
 
+	void fight(const CGHeroInstance *h) const;
 	void onHeroVisit(const CGHeroInstance * h) const;
 	void onHeroVisit(const CGHeroInstance * h) const;
+
+	void flee( const CGHeroInstance * h ) const;
 	void endBattle(BattleResult *result) const;
 	void endBattle(BattleResult *result) const;
+	void fleeDecision(const CGHeroInstance *h, ui32 pursue) const;
+	void joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const;
 	void initObj();
 	void initObj();
+	int takenAction(const CGHeroInstance *h, bool allowJoin=true) const; //action on confrontation: -2 - fight, -1 - flee, >=0 - will join for given value of gold (may be 0)
 
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 	{

+ 18 - 18
server/CGameHandler.cpp

@@ -723,9 +723,10 @@ void CGameHandler::run(bool resume)
 		for(int i=0;i<quantity;i++)
 		for(int i=0;i<quantity;i++)
 		{
 		{
 			(*cc) >> pom; //read player color
 			(*cc) >> pom; //read player color
-			gsm.lock();
-			connections[pom] = cc;
-			gsm.unlock();
+			{
+				boost::unique_lock<boost::recursive_mutex> lock(gsm);
+				connections[pom] = cc;
+			}
 		}	
 		}	
 	}
 	}
 	
 	
@@ -1385,24 +1386,22 @@ void CGameHandler::changeObjPos( int objid, int3 newPos, ui8 flags )
 
 
 void CGameHandler::applyAndAsk( Query * sel, ui8 player, boost::function<void(ui32)> &callback )
 void CGameHandler::applyAndAsk( Query * sel, ui8 player, boost::function<void(ui32)> &callback )
 {
 {
-	gsm.lock();
+	boost::unique_lock<boost::recursive_mutex> lock(gsm);
 	sel->id = QID;
 	sel->id = QID;
 	callbacks[QID] = callback;
 	callbacks[QID] = callback;
 	states.addQuery(player,QID);
 	states.addQuery(player,QID);
 	QID++; 
 	QID++; 
 	sendAndApply(sel);
 	sendAndApply(sel);
-	gsm.unlock();
 }
 }
 
 
 void CGameHandler::ask( Query * sel, ui8 player, const CFunctionList<void(ui32)> &callback )
 void CGameHandler::ask( Query * sel, ui8 player, const CFunctionList<void(ui32)> &callback )
 {
 {
-	gsm.lock();
+	boost::unique_lock<boost::recursive_mutex> lock(gsm);
 	sel->id = QID;
 	sel->id = QID;
 	callbacks[QID] = callback;
 	callbacks[QID] = callback;
 	states.addQuery(player,QID);
 	states.addQuery(player,QID);
 	sendToAllClients(sel);
 	sendToAllClients(sel);
 	QID++; 
 	QID++; 
-	gsm.unlock();
 }
 }
 
 
 void CGameHandler::sendToAllClients( CPackForClient * info )
 void CGameHandler::sendToAllClients( CPackForClient * info )
@@ -1927,7 +1926,7 @@ void CGameHandler::hireHero( ui32 tid, ui8 hid )
 
 
 void CGameHandler::queryReply( ui32 qid, ui32 answer )
 void CGameHandler::queryReply( ui32 qid, ui32 answer )
 {
 {
-	gsm.lock();
+	boost::unique_lock<boost::recursive_mutex> lock(gsm);
 	if(vstd::contains(callbacks,qid))
 	if(vstd::contains(callbacks,qid))
 	{
 	{
 		CFunctionList<void(ui32)> callb = callbacks[qid];
 		CFunctionList<void(ui32)> callb = callbacks[qid];
@@ -1946,7 +1945,6 @@ void CGameHandler::queryReply( ui32 qid, ui32 answer )
 	{
 	{
 		tlog1 << "Unknown query reply...\n";
 		tlog1 << "Unknown query reply...\n";
 	}
 	}
-	gsm.unlock();
 }
 }
 
 
 void CGameHandler::makeBattleAction( BattleAction &ba )
 void CGameHandler::makeBattleAction( BattleAction &ba )
@@ -2424,14 +2422,16 @@ void CGameHandler::showGarrisonDialog( int upobj, int hid, const boost::function
 	GarrisonDialog gd;
 	GarrisonDialog gd;
 	gd.hid = hid;
 	gd.hid = hid;
 	gd.objid = upobj;
 	gd.objid = upobj;
-	gsm.lock();
-	gd.id = QID;
-	garrisonCallbacks[QID] = cb;
-	allowedExchanges[QID] = std::pair<si32,si32>(upobj,hid);
-	states.addQuery(player,QID);
-	QID++; 
-	sendAndApply(&gd);
-	gsm.unlock();
+
+	{
+		boost::unique_lock<boost::recursive_mutex> lock(gsm);
+		gd.id = QID;
+		garrisonCallbacks[QID] = cb;
+		allowedExchanges[QID] = std::pair<si32,si32>(upobj,hid);
+		states.addQuery(player,QID);
+		QID++; 
+		sendAndApply(&gd);
+	}
 }
 }
 
 
 bool CGameHandler::isAllowedExchange( int id1, int id2 )
 bool CGameHandler::isAllowedExchange( int id1, int id2 )
@@ -2440,7 +2440,7 @@ bool CGameHandler::isAllowedExchange( int id1, int id2 )
 		return true;
 		return true;
 
 
 	{
 	{
-		boost::unique_lock<boost::mutex> lock(gsm);
+		boost::unique_lock<boost::recursive_mutex> lock(gsm);
 		for(std::map<ui32, std::pair<si32,si32> >::const_iterator i = allowedExchanges.begin(); i!=allowedExchanges.end(); i++)
 		for(std::map<ui32, std::pair<si32,si32> >::const_iterator i = allowedExchanges.begin(); i!=allowedExchanges.end(); i++)
 			if(id1 == i->second.first && id2 == i->second.second   ||   id2 == i->second.first && id1 == i->second.second)
 			if(id1 == i->second.first && id2 == i->second.second   ||   id2 == i->second.first && id1 == i->second.second)
 				return true;
 				return true;

+ 1 - 1
server/CGameHandler.h

@@ -64,7 +64,7 @@ public:
 	std::set<CConnection*> conns;
 	std::set<CConnection*> conns;
 
 
 	//queries stuff
 	//queries stuff
-	boost::mutex gsm;
+	boost::recursive_mutex gsm;
 	ui32 QID;
 	ui32 QID;
 	std::map<ui32, CFunctionList<void(ui32)> > callbacks; //query id => callback function - for selection and yes/no dialogs
 	std::map<ui32, CFunctionList<void(ui32)> > callbacks; //query id => callback function - for selection and yes/no dialogs
 	std::map<ui32, boost::function<void()> > garrisonCallbacks; //query id => callback - for garrison dialogs
 	std::map<ui32, boost::function<void()> > garrisonCallbacks; //query id => callback - for garrison dialogs