1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-05-13 22:06:58 +02:00

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

* Diplomacy secondary skill support
This commit is contained in:
Michał W. Urbańczyk 2009-04-12 01:48:50 +00:00
parent f9aebcc4bd
commit de71191307
5 changed files with 258 additions and 23 deletions

View File

@ -4850,7 +4850,7 @@ CGarrisonWindow::CGarrisonWindow( const CArmedInstance *up, const CGHeroInstance
pos.w = screen->w;
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");
quit = new AdventureMapButton(CGI->generaltexth->tcommands[8],"",boost::bind(&CGarrisonWindow::close,this),pos.x+399,pos.y+314,"IOK6432.DEF",SDLK_RETURN);
}

View File

@ -761,6 +761,16 @@ void CGHeroInstance::setPropertyDer( ui8 what, ui32 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
{
return getPosition(false);
@ -1240,9 +1250,53 @@ bool CArmedInstance::needsLastStack() const
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
{
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
@ -1270,6 +1324,25 @@ void CGCreature::endBattle( BattleResult *result ) const
void CGCreature::initObj()
{
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;
si32 &amount = army.slots[0].second;
CCreature &c = VLC->creh->creatures[subID];
@ -1286,6 +1359,160 @@ void CGCreature::initObj()
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
{
if(subID == 7) //TODO: support for abandoned mine

View File

@ -155,6 +155,7 @@ class DLL_EXPORT CArmedInstance: public CGObjectInstance
public:
CCreatureSet army; //army
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)
{
@ -249,7 +250,8 @@ public:
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
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:
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::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
ui8 neverFlees; //if true, the troops will never flee
ui8 notGrowingTeam; //if true, number of units won't grow
void fight(const CGHeroInstance *h) const;
void onHeroVisit(const CGHeroInstance * h) const;
void flee( const CGHeroInstance * h ) 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();
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)
{

View File

@ -723,9 +723,10 @@ void CGameHandler::run(bool resume)
for(int i=0;i<quantity;i++)
{
(*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 )
{
gsm.lock();
boost::unique_lock<boost::recursive_mutex> lock(gsm);
sel->id = QID;
callbacks[QID] = callback;
states.addQuery(player,QID);
QID++;
sendAndApply(sel);
gsm.unlock();
}
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;
callbacks[QID] = callback;
states.addQuery(player,QID);
sendToAllClients(sel);
QID++;
gsm.unlock();
}
void CGameHandler::sendToAllClients( CPackForClient * info )
@ -1927,7 +1926,7 @@ void CGameHandler::hireHero( ui32 tid, ui8 hid )
void CGameHandler::queryReply( ui32 qid, ui32 answer )
{
gsm.lock();
boost::unique_lock<boost::recursive_mutex> lock(gsm);
if(vstd::contains(callbacks,qid))
{
CFunctionList<void(ui32)> callb = callbacks[qid];
@ -1946,7 +1945,6 @@ void CGameHandler::queryReply( ui32 qid, ui32 answer )
{
tlog1 << "Unknown query reply...\n";
}
gsm.unlock();
}
void CGameHandler::makeBattleAction( BattleAction &ba )
@ -2424,14 +2422,16 @@ void CGameHandler::showGarrisonDialog( int upobj, int hid, const boost::function
GarrisonDialog gd;
gd.hid = hid;
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 )
@ -2440,7 +2440,7 @@ bool CGameHandler::isAllowedExchange( int id1, int id2 )
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++)
if(id1 == i->second.first && id2 == i->second.second || id2 == i->second.first && id1 == i->second.second)
return true;

View File

@ -64,7 +64,7 @@ public:
std::set<CConnection*> conns;
//queries stuff
boost::mutex gsm;
boost::recursive_mutex gsm;
ui32 QID;
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