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:
parent
f9aebcc4bd
commit
de71191307
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user