1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-12 02:28:11 +02:00

Surrendering and related artifacts (Statesman's Medal,

Diplomat's Ring, Ambassador's Sash).
This commit is contained in:
Michał W. Urbańczyk 2011-03-05 16:38:22 +00:00
parent 7908a1a61d
commit 7dbf105f6e
10 changed files with 131 additions and 228 deletions

View File

@ -1135,3 +1135,14 @@ bool CBattleCallback::battleMakeTacticAction( BattleAction * action )
sendRequest(&ma);
return true;
}
int CBattleCallback::battleGetSurrenderCost()
{
if (!gs->curB)
{
tlog1 << "battleGetSurrenderCost called when no battle!\n";
return -1;
}
return gs->curB->getSurrenderingCost(player);
}

View File

@ -99,6 +99,7 @@ public:
virtual bool battleCanCastSpell()=0; //returns true, if caller can cast a spell
virtual SpellCasting::ESpellCastProblem battleCanCastThisSpell(const CSpell * spell)=0; //determines if given spell can be casted (and returns problem description)
virtual bool battleCanFlee()=0; //returns true if caller can flee from the battle
virtual int battleGetSurrenderCost()=0; //returns cost of surrendering battle, -1 if surrendering is not possible
virtual const CGTownInstance * battleGetDefendedTown()=0; //returns defended town if current battle is a siege, NULL instead
virtual ui8 battleGetWallState(int partOfWall)=0; //for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle
virtual int battleGetWallUnderHex(THex hex)=0; //returns part of destructible wall / gate / keep under given hex or -1 if not found
@ -237,6 +238,7 @@ public:
bool battleCanCastSpell() OVERRIDE; //returns true, if caller can cast a spell
SpellCasting::ESpellCastProblem battleCanCastThisSpell(const CSpell * spell) OVERRIDE; //determines if given spell can be casted (and returns problem description)
bool battleCanFlee() OVERRIDE; //returns true if caller can flee from the battle
int battleGetSurrenderCost() OVERRIDE; //returns cost of surrendering battle, -1 if surrendering is not possible
const CGTownInstance * battleGetDefendedTown() OVERRIDE; //returns defended town if current battle is a siege, NULL instead
ui8 battleGetWallState(int partOfWall) OVERRIDE; //for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle
int battleGetWallUnderHex(THex hex) OVERRIDE; //returns part of destructible wall / gate / keep under given hex or -1 if not found

View File

@ -1,3 +1,11 @@
0.84 -> 0.85
GENERAL:
* New artifacts supported:
- Statesman's Medal
- Diplomat's Ring
- Ambassador's Sash
0.83 -> 0.84 (Mar 01 2011)
GENERAL:
* Bonus system has been rewritten

View File

@ -37,6 +37,7 @@ const double M_PI = 3.14159265358979323846;
#define _USE_MATH_DEFINES
#include <cmath>
#endif
#include <boost/format.hpp>
/*
* CBattleInterface.cpp, part of VCMI engine
@ -1198,8 +1199,8 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
bOptions = new AdventureMapButton (CGI->generaltexth->zelp[381].first, CGI->generaltexth->zelp[381].second, boost::bind(&CBattleInterface::bOptionsf,this), 3 + pos.x, 561 + pos.y, "icm003.def", SDLK_o);
bSurrender = new AdventureMapButton (CGI->generaltexth->zelp[379].first, CGI->generaltexth->zelp[379].second, boost::bind(&CBattleInterface::bSurrenderf,this), 54 + pos.x, 561 + pos.y, "icm001.def", SDLK_s);
bFlee = new AdventureMapButton (CGI->generaltexth->zelp[380].first, CGI->generaltexth->zelp[380].second, boost::bind(&CBattleInterface::bFleef,this), 105 + pos.x, 561 + pos.y, "icm002.def", SDLK_r);
bSurrender->block(!curInt->cb->battleCanFlee());
bFlee->block(!curInt->cb->battleCanFlee());
bSurrender->block(curInt->cb->battleGetSurrenderCost() < 0);
bAutofight = new AdventureMapButton (CGI->generaltexth->zelp[382].first, CGI->generaltexth->zelp[382].second, boost::bind(&CBattleInterface::bAutofightf,this), 157 + pos.x, 561 + pos.y, "icm004.def", SDLK_a);
bSpell = new AdventureMapButton (CGI->generaltexth->zelp[385].first, CGI->generaltexth->zelp[385].second, boost::bind(&CBattleInterface::bSpellf,this), 645 + pos.x, 561 + pos.y, "icm005.def", SDLK_c);
bSpell->block(true);
@ -2160,6 +2161,15 @@ void CBattleInterface::bSurrenderf()
{
if(spellDestSelectMode) //we are casting a spell
return;
int cost = curInt->cb->battleGetSurrenderCost();
if(cost >= 0)
{
const CGHeroInstance *opponent = curInt->cb->battleGetFightingHero(1);
std::string enemyHeroName = opponent ? opponent->name : "#ENEMY#"; //TODO: should surrendering without enemy hero be enabled?
std::string surrenderMessage = boost::str(boost::format(CGI->generaltexth->allTexts[32]) % enemyHeroName % cost); //%s states: "I will accept your surrender and grant you and your troops safe passage for the price of %d gold."
curInt->showYesNoDialog(surrenderMessage, std::vector<SComponent*>(), boost::bind(&CBattleInterface::reallySurrender,this), 0, false);
}
}
void CBattleInterface::bFleef()
@ -2170,7 +2180,7 @@ void CBattleInterface::bFleef()
if( curInt->cb->battleCanFlee() )
{
CFunctionList<void()> ony = boost::bind(&CBattleInterface::reallyFlee,this);
curInt->showYesNoDialog(CGI->generaltexth->allTexts[28],std::vector<SComponent*>(), ony, 0, false);
curInt->showYesNoDialog(CGI->generaltexth->allTexts[28],std::vector<SComponent*>(), ony, 0, false); //Are you sure you want to retreat?
}
else
{
@ -2185,7 +2195,7 @@ void CBattleInterface::bFleef()
heroName = defendingHeroInstance->name;
//calculating text
char buffer[1000];
sprintf(buffer, CGI->generaltexth->allTexts[340].c_str(), heroName.c_str());
sprintf(buffer, CGI->generaltexth->allTexts[340].c_str(), heroName.c_str()); //The Shackles of War are present. %s can not retreat!
//printing message
curInt->showInfoDialog(std::string(buffer), comps);
@ -2194,10 +2204,23 @@ void CBattleInterface::bFleef()
void CBattleInterface::reallyFlee()
{
giveCommand(4,0,0);
giveCommand(BattleAction::RETREAT,0,0);
CCS->curh->changeGraphic(0, 0);
}
void CBattleInterface::reallySurrender()
{
if(curInt->cb->getResourceAmount(Res::GOLD) < curInt->cb->battleGetSurrenderCost())
{
curInt->showInfoDialog(CGI->generaltexth->allTexts[29]); //You don't have enough gold!
}
else
{
giveCommand(BattleAction::SURRENDER,0,0);
CCS->curh->changeGraphic(0, 0);
}
}
void CBattleInterface::bAutofightf()
{
if(spellDestSelectMode) //we are casting a spell
@ -3013,6 +3036,7 @@ void CBattleInterface::activateStack()
bSpell->block(!curInt->cb->battleCanCastSpell());
bSurrender->block((curInt == attackerInt ? defendingHeroInstance : attackingHeroInstance) == NULL);
bFlee->block(!curInt->cb->battleCanFlee());
bSurrender->block(curInt->cb->battleGetSurrenderCost() < 0);
GH.fakeMouseMove();

View File

@ -496,6 +496,7 @@ public:
void bSurrenderf();
void bFleef();
void reallyFlee(); //performs fleeing without asking player
void reallySurrender(); //performs surrendering without asking player
void bAutofightf();
void bSpellf();
void bWaitf();

View File

@ -1883,6 +1883,42 @@ std::vector<ui32> BattleInfo::calculateResistedStacks( const CSpell * sp, const
return ret;
}
int BattleInfo::getSurrenderingCost(int player) const
{
if(!battleCanFlee(player)) //to surrender, conditions of fleeing must be fulfilled
return -1;
if(!getHero(theOtherPlayer(player))) //additionally, there must be an enemy hero
return -2;
int ret = 0;
double discount = 0;
BOOST_FOREACH(const CStack *s, stacks)
if(s->owner == player && s->base) //we pay for our stack that comes from our army (the last condition eliminates summoned cres and war machines)
ret += s->getCreature()->cost[Res::GOLD] * s->count;
if(const CGHeroInstance *h = getHero(player))
discount += h->valOfBonuses(Bonus::SURRENDER_DISCOUNT);
ret *= (100.0 - discount) / 100.0;
amax(ret, 0); //no negative costs for >100% discounts (impossible in original H3 mechanics, but some day...)
return ret;
}
int BattleInfo::theOtherPlayer(int player) const
{
return sides[!whatSide(player)];
}
ui8 BattleInfo::whatSide(int player) const
{
for(int i = 0; i < ARRAY_COUNT(sides); i++)
if(sides[i] == player)
return i;
tlog1 << "BattleInfo::whatSide: Player " << player << " is not in battle!\n";
return -1;
}
CStack::CStack(const CStackInstance *Base, int O, int I, bool AO, int S)
: base(Base), ID(I), owner(O), slot(S), attackerOwned(AO),
counterAttacks(1)

View File

@ -131,6 +131,10 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode
void localInit();
static BattleInfo * setupBattle( int3 tile, int terrain, int terType, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town );
bool isInTacticRange( THex dest ) const;
int getSurrenderingCost(int player) const;
int theOtherPlayer(int player) const;
ui8 whatSide(int player) const;
};
class DLL_EXPORT CStack : public CBonusSystemNode, public CStackBasicDescriptor

View File

@ -1177,6 +1177,14 @@ void CGHeroInstance::updateSkill(int which, int val)
else
b->val = +val;
}
else if(which == DIPLOMACY) //surrender discount: 20% per level
{
if(Bonus *b = getBonus(Selector::type(Bonus::SURRENDER_DISCOUNT) && Selector::sourceType(Bonus::SECONDARY_SKILL)))
b->val = +val;
else
addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::SURRENDER_DISCOUNT, Bonus::SECONDARY_SKILL, val * 20, which));
}
int skillVal = 0;
switch (which)

View File

@ -1254,11 +1254,7 @@ void CGameHandler::checkForBattleEnd( std::vector<CStack*> &stacks )
}
if(!hasStack[0] || !hasStack[1]) //somebody has won
{
BattleResult *br = new BattleResult; //will be deleted at the end of startBattle(...)
br->result = 0;
br->winner = hasStack[1]; //fleeing side loses
gs->curB->calculateCasualties(br->casualties);
battleResult.set(br);
setBattleResult(0, hasStack[1]);
}
}
@ -1665,40 +1661,6 @@ void CGameHandler::stopHeroVisitCastle(int obj, int heroID)
sendAndApply(&vc);
}
// bool CGameHandler::removeArtifact(const CArtifact* art, int hid)
// {
// const CGHeroInstance* h = getHero(hid);
//
// SetHeroArtifacts sha;
// sha.hid = hid;
// sha.artifacts = h->artifacts;
// sha.artifWorn = h->artifWorn;
//
// std::vector<const CArtifact*>::iterator it;
// if ((it = std::find(sha.artifacts.begin(), sha.artifacts.end(), art)) != sha.artifacts.end()) //it is in backpack
// sha.artifacts.erase(it);
// else //worn
// {
// std::map<ui16, const CArtifact*>::iterator itr;
// for (itr = sha.artifWorn.begin(); itr != sha.artifWorn.end(); ++itr)
// {
// if (itr->second == art)
// {
// VLC->arth->unequipArtifact(sha.artifWorn, itr->first);
// break;
// }
// }
//
// if(itr == sha.artifWorn.end())
// {
// tlog2 << "Cannot find artifact to remove!\n";
// return false;
// }
// }
// sendAndApply(&sha);
// return true;
// }
void CGameHandler::removeArtifact(const ArtifactLocation &al)
{
assert(al.getArt());
@ -1737,13 +1699,6 @@ void CGameHandler::startBattleI( const CArmedInstance *army1, const CArmedInstan
startBattleI(army1, army2, army2->visitablePos(), cb, creatureBank);
}
//void CGameHandler::startBattleI(int heroID, CCreatureSet army, int3 tile, boost::function<void(BattleResult*)> cb) //for hero<=>neutral army
//{
// CGHeroInstance* h = const_cast<CGHeroInstance*>(getHero(heroID));
// startBattleI(&h->army,&army,tile,h,NULL,cb);
// //battle(&h->army,army,tile,h,NULL);
//}
void CGameHandler::changeSpells( int hid, bool give, const std::set<ui32> &spells )
{
ChangeSpells cs;
@ -2534,17 +2489,7 @@ bool CGameHandler::moveArtifact(si32 srcHeroID, si32 destHeroID, ui16 srcSlot, u
COMPLAIN_RET("No artifact to move!");
if (destArtifact && srcHero->tempOwner != destHero->tempOwner)
COMPLAIN_RET("Can't touch artifact on hero of another player!");
// // Combinational artifacts needs to be removed first so they don't get denied movement because of their own locks.
// if (srcHeroID == destHeroID && srcSlot < 19 && destSlot < 19)
// {
// sha.setArtAtPos(srcSlot, NULL);
// if (!vstd::contains(sha.artifWorn, destSlot))
// destArtifact = NULL;
// }
// Check if src/dest slots are appropriate for the artifacts exchanged.
// Moving to the backpack is always allowed.
if ((!srcArtifact || destSlot < Arts::BACKPACK_START)
@ -2562,10 +2507,6 @@ bool CGameHandler::moveArtifact(si32 srcHeroID, si32 destHeroID, ui16 srcSlot, u
if(dst.slot >= Arts::BACKPACK_START)
amin(dst.slot, Arts::BACKPACK_START + dst.hero->artifactsInBackpack.size());
// // Correction for destination from removing source artifact in backpack.
// if (src.slot >= 19 && dst.slot >= 19 && src.slot < dst.slot)
// dst.slot--;
if (src.slot == dst.slot && src.hero == dst.hero)
COMPLAIN_RET("Won't move artifact: Dest same as source!");
@ -2583,37 +2524,6 @@ bool CGameHandler::moveArtifact(si32 srcHeroID, si32 destHeroID, ui16 srcSlot, u
moveArtifact(src, dst);
}
//
// // If dest does not fit in src, put it in dest's backpack instead.
// if (srcHeroID == destHeroID) // To avoid stumbling on own locks, remove artifact first.
// sha.setArtAtPos(destSlot, NULL);
// const bool destFits = !destArtifact || srcSlot >= 19 || destSlot >= 19 || destArtifact->fitsAt(sha.artifWorn, srcSlot);
// if (srcHeroID == destHeroID && destArtifact)
// sha.setArtAtPos(destSlot, destArtifact);
//
// sha.setArtAtPos(srcSlot, NULL);
// if (destSlot < 19 && (destArtifact || srcSlot < 19) && destFits)
// sha.setArtAtPos(srcSlot, destArtifact ? destArtifact : NULL);
//
// // Internal hero artifact arrangement.
// if(srcHero == destHero)
// {
//
// sha.setArtAtPos(destSlot, srcHero->getArtAtPos(srcSlot));
// }
// if (srcHeroID != destHeroID)
// {
// // Exchange between two different heroes.
// SetHeroArtifacts sha2;
// sha2.hid = destHeroID;
// sha2.artifacts = destHero->artifacts;
// sha2.artifWorn = destHero->artifWorn;
// sha2.setArtAtPos(destSlot, srcArtifact ? srcArtifact : NULL);
// if (!destFits)
// sha2.setArtAtPos(sha2.artifacts.size() + 19, destHero->getArtAtPos(destSlot));
// sendAndApply(&sha2);
// }
// sendAndApply(&sha);
return true;
}
@ -2656,118 +2566,7 @@ bool CGameHandler::assembleArtifacts (si32 heroID, ui16 artifactSlot, bool assem
da.al = ArtifactLocation(hero, artifactSlot);
sendAndApply(&da);
}
/*
SetHeroArtifacts sha;
sha.hid = heroID;
sha.artifacts = hero->artifacts;
sha.artifWorn = hero->artifWorn;
if (assemble)
{
if (VLC->arth->artifacts.size() < assembleTo)
{
complain("Illegal artifact to assemble to.");
return false;
}
if (!destArtifact->canBeAssembledTo(hero->artifWorn, assembleTo))
{
complain("Artifact cannot be assembled.");
return false;
}
const CArtifact &artifact = *VLC->arth->artifacts[assembleTo];
if (artifact.constituents == NULL)
{
complain("Not a combinational artifact.");
return false;
}
// Perform assembly.
bool destConsumed = false; // Determines which constituent that will be counted for together with the artifact.
const bool destSpecific = vstd::contains(artifact.possibleSlots, artifactSlot); // Prefer the chosen slot as the location for the assembled artifact.
BOOST_FOREACH(ui32 constituentID, *artifact.constituents)
{
if (destSpecific && constituentID == destArtifact->id)
{
sha.artifWorn[artifactSlot] = VLC->arth->artifacts[assembleTo];
destConsumed = true;
continue;
}
bool found = false;
for (std::map<ui16, const CArtifact*>::iterator it = sha.artifWorn.begin(); it != sha.artifWorn.end(); ++it)
{
if (it->second->id == constituentID)
{ // Found possible constituent to substitute.
if (destSpecific && !destConsumed && it->second->id == destArtifact->id)
{
// Find the specified destination for assembled artifact.
if (it->first == artifactSlot)
{
it->second = VLC->arth->artifacts[assembleTo];
destConsumed = true;
found = true;
break;
}
}
else
{
// Either put the assembled artifact in a fitting spot, or put a lock.
if (!destSpecific && !destConsumed && vstd::contains(artifact.possibleSlots, it->first))
{
it->second = VLC->arth->artifacts[assembleTo];
destConsumed = true;
}
else
{
it->second = VLC->arth->artifacts[145];
}
found = true;
break;
}
}
}
if (!found) {
complain("Constituent missing.");
return false;
}
}
}
else
{
// Perform disassembly.
bool destConsumed = false; // Determines which constituent that will be counted for together with the artifact.
BOOST_FOREACH(ui32 constituentID, *destArtifact->constituents)
{
const CArtifact &constituent = *VLC->arth->artifacts[constituentID];
if (!destConsumed && vstd::contains(constituent.possibleSlots, artifactSlot))
{
sha.artifWorn[artifactSlot] = VLC->arth->artifacts[constituentID];
destConsumed = true;
}
else
{
BOOST_REVERSE_FOREACH(ui16 slotID, constituent.possibleSlots)
{
if (vstd::contains(sha.artifWorn, slotID) && sha.artifWorn[slotID]->id == 145)
{
const_cast<CArtifact*>(sha.artifWorn[slotID])->id = constituentID;
break;
}
}
}
}
}
sendAndApply(&sha);
return true;*/
return false;
}
@ -3142,17 +2941,28 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
}
case BattleAction::RETREAT: //retreat/flee
{
if( !gs->curB->battleCanFlee(ba.side ? gs->curB->sides[1] : gs->curB->sides[0]) )
break;
//TODO: remove retreating hero from map and place it in recruitment list
BattleResult *br = new BattleResult;
br->result = 1;
br->winner = !ba.side; //fleeing side loses
gs->curB->calculateCasualties(br->casualties);
giveExp(*br);
battleResult.set(br);
if(!gs->curB->battleCanFlee(gs->curB->sides[ba.side]))
complain("Cannot retreat!");
else
setBattleResult(1, !ba.side); //surrendering side loses
break;
}
case BattleAction::SURRENDER:
{
int player = gs->curB->sides[ba.side];
int cost = gs->curB->getSurrenderingCost(player);
if(cost < 0)
complain("Cannot surrender!");
else if(getResource(player, Res::GOLD) < cost)
complain("Not enough gold to surrender!");
else
{
giveResource(player, Res::GOLD, -cost);
setBattleResult(2, !ba.side); //surrendering side loses
}
break;
}
break;
case BattleAction::WALK_AND_ATTACK: //walk or attack
{
sendAndApply(&StartAction(ba)); //start movement and attack
@ -3538,17 +3348,6 @@ void CGameHandler::playerMessage( ui8 player, const std::string &message )
if(!hero) return;
for (int g=7; g<=140; ++g)
giveHeroNewArtifact(hero, VLC->arth->artifacts[g], -1);
// SetHeroArtifacts sha;
// sha.hid = hero->id;
// sha.artifacts = hero->artifacts;
// sha.artifWorn = hero->artifWorn;
// sha.artifacts.push_back(VLC->arth->artifacts[2]); //grail
// for (int g=7; g<=140; ++g)
// {
// sha.artifacts.push_back(VLC->arth->artifacts[g]);
// }
// sendAndApply(&sha);
}
else
cheated = false;
@ -3730,7 +3529,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
{
switch(ba.actionType)
{
case 1: //hero casts spell
case BattleAction::HERO_SPELL: //hero casts spell
{
const CGHeroInstance *h = gs->curB->heroes[ba.side];
const CGHeroInstance *secondHero = gs->curB->heroes[!ba.side];
@ -5062,6 +4861,15 @@ void CGameHandler::giveHeroNewArtifact(const CGHeroInstance *h, const CArtifact
giveHeroArtifact(h, a, pos);
}
void CGameHandler::setBattleResult(int resultType, int victoriusSide)
{
BattleResult *br = new BattleResult;
br->result = resultType;
br->winner = victoriusSide; //surrendering side loses
gs->curB->calculateCasualties(br->casualties);
battleResult.set(br);
}
CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleInfo *bat)
{
int color = army->tempOwner;

View File

@ -120,6 +120,7 @@ public:
void prepareAttack(BattleAttack &bat, const CStack *att, const CStack *def, int distance); //distance - number of hexes travelled before attacking
void checkForBattleEnd( std::vector<CStack*> &stacks );
void setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town);
void setBattleResult(int resultType, int victoriusSide);
CGameHandler(void);
~CGameHandler(void);