1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-07-15 01:24:45 +02:00

* Implemented Moat functionality during siege (stops movement and deals dmg)

* Mostly implemented battle spells:
 - Fire Wall
 - Force Field
 - Land Mine
 - Quicksands
This commit is contained in:
Michał W. Urbańczyk
2012-05-18 20:50:16 +00:00
parent 5435101182
commit d168f3eac2
27 changed files with 691 additions and 241 deletions

View File

@ -335,28 +335,31 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
backgroundWithHexes = CSDL_Ext::newSurface(background->w, background->h, screen);
//preparing obstacle defs
std::vector<CObstacleInstance> obst = curInt->cb->battleGetAllObstacles();
auto obst = curInt->cb->battleGetAllObstacles();
for(size_t t = 0; t < obst.size(); ++t)
{
int ID = obst[t].ID;
std::string gfxName = obst[t].getInfo().defName;
if(obst[t].obstacleType == CObstacleInstance::USUAL)
const int ID = obst[t]->ID;
if(obst[t]->obstacleType == CObstacleInstance::USUAL)
{
idToObstacle[ID] = CDefHandler::giveDef(gfxName);
idToObstacle[ID] = CDefHandler::giveDef(obst[t]->getInfo().defName);
for(size_t n = 0; n < idToObstacle[ID]->ourImages.size(); ++n)
{
SDL_SetColorKey(idToObstacle[ID]->ourImages[n].bitmap, SDL_SRCCOLORKEY, SDL_MapRGB(idToObstacle[ID]->ourImages[n].bitmap->format,0,255,255));
}
}
else
else if(obst[t]->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
{
idToAbsoluteObstacle[ID] = BitmapHandler::loadBitmap(gfxName);
idToAbsoluteObstacle[ID] = BitmapHandler::loadBitmap(obst[t]->getInfo().defName);
}
}
quicksand = CDefHandler::giveDef("C17SPE1.DEF");
landMine = CDefHandler::giveDef("C09SPF1.DEF");
fireWall = CDefHandler::giveDef("C07SPF61");
bigForceField[0] = CDefHandler::giveDef("C15SPE10.DEF");
bigForceField[1] = CDefHandler::giveDef("C15SPE7.DEF");
smallForceField[0] = CDefHandler::giveDef("C15SPE1.DEF");
smallForceField[1] = CDefHandler::giveDef("C15SPE4.DEF");
for (int i = 0; i < bfield.size(); i++)
{
@ -428,6 +431,11 @@ CBattleInterface::~CBattleInterface()
delete quicksand;
delete landMine;
delete fireWall;
delete smallForceField[0];
delete smallForceField[1];
delete bigForceField[0];
delete bigForceField[1];
delete siegeH;
@ -565,9 +573,9 @@ void CBattleInterface::show(SDL_Surface * to)
CSDL_Ext::blit8bppAlphaTo24bpp(cellBorders, NULL, to, &pos);
}
//Blit absolute obstacles
BOOST_FOREACH(const CObstacleInstance &oi, curInt->cb->battleGetAllObstacles())
if(oi.obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
blitAt(imageOfObstacle(oi), pos.x + oi.getInfo().width, pos.y + oi.getInfo().height, to);
BOOST_FOREACH(auto &oi, curInt->cb->battleGetAllObstacles())
if(oi->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
blitAt(imageOfObstacle(*oi), pos.x + oi->getInfo().width, pos.y + oi->getInfo().height, to);
}
//printing hovered cell
for(int b=0; b<GameConstants::BFIELD_SIZE; ++b)
@ -597,14 +605,15 @@ void CBattleInterface::show(SDL_Surface * to)
if (defendingHeroInstance)
schoolLevel = defendingHeroInstance->getSpellSchoolLevel(&spToCast);
}
//obtaining range and printing it
std::set<ui16> shaded = spToCast.rangeInHexes(b, schoolLevel);
for(std::set<ui16>::iterator it = shaded.begin(); it != shaded.end(); ++it) //for spells with range greater then one hex
auto shaded = spToCast.rangeInHexes(b, schoolLevel, curInt->cb->battleGetMySide());
BOOST_FOREACH(auto shadedHex, shaded) //for spells with range greater then one hex
{
if(settings["battle"]["mouseShadow"].Bool() && (*it % GameConstants::BFIELD_WIDTH != 0) && (*it % GameConstants::BFIELD_WIDTH != 16))
if(settings["battle"]["mouseShadow"].Bool() && (shadedHex % GameConstants::BFIELD_WIDTH != 0) && (shadedHex % GameConstants::BFIELD_WIDTH != 16))
{
int x = 14 + ((*it/GameConstants::BFIELD_WIDTH)%2==0 ? 22 : 0) + 44*(*it%GameConstants::BFIELD_WIDTH) + pos.x;
int y = 86 + 42 * (*it/GameConstants::BFIELD_WIDTH) + pos.y;
int x = 14 + ((shadedHex/GameConstants::BFIELD_WIDTH)%2==0 ? 22 : 0) + 44*(shadedHex%GameConstants::BFIELD_WIDTH) + pos.x;
int y = 86 + 42 * (shadedHex/GameConstants::BFIELD_WIDTH) + pos.y;
SDL_Rect temp_rect = genRect(cellShade->h, cellShade->w, x, y);
CSDL_Ext::blit8bppAlphaTo24bpp(cellShade, NULL, to, &temp_rect);
}
@ -640,16 +649,16 @@ void CBattleInterface::show(SDL_Surface * to)
SDL_SetClipRect(to, &pos);
//preparing obstacles to be shown
std::vector<CObstacleInstance> obstacles = curInt->cb->battleGetAllObstacles();
auto obstacles = curInt->cb->battleGetAllObstacles();
std::multimap<BattleHex, int> hexToObstacle;
for(size_t b = 0; b < obstacles.size(); ++b)
{
const CObstacleInstance &oi = obstacles[b];
if(oi.obstacleType != CObstacleInstance::ABSOLUTE_OBSTACLE)
const auto &oi = obstacles[b];
if(oi->obstacleType != CObstacleInstance::ABSOLUTE_OBSTACLE && oi->obstacleType != CObstacleInstance::MOAT)
{
//BattleHex position = CGI->heroh->obstacles.find(obstacles[b].ID)->second.getMaxBlocked(obstacles[b].pos);
hexToObstacle.insert(std::make_pair(oi.pos, b));
hexToObstacle.insert(std::make_pair(oi->pos, b));
}
}
@ -706,13 +715,26 @@ void CBattleInterface::show(SDL_Surface * to)
if(!active)
activate();
bool changedStack = false;
//activation of next stack
if(pendingAnims.size() == 0 && stackToActivate != NULL)
{
activateStack();
changedStack = true;
}
//anims ended
animsAreDisplayed.setn(false);
if(changedStack)
{
//we may have changed active interface (another side in hot-seat),
// so we can't continue drawing with old setting. So we call ourselves again and end.
SDL_SetClipRect(to, &buf); //restoring previous clip_rect
show(to);
return;
}
}
for(int b=0; b<GameConstants::BFIELD_SIZE; ++b) //showing dead stacks
@ -906,14 +928,14 @@ void CBattleInterface::showAliveStacks(std::vector<const CStack *> *aliveStacks,
}
}
void CBattleInterface::showObstacles(std::multimap<BattleHex, int> *hexToObstacle, std::vector<CObstacleInstance> &obstacles, int hex, SDL_Surface *to)
void CBattleInterface::showObstacles(std::multimap<BattleHex, int> *hexToObstacle, std::vector<shared_ptr<const CObstacleInstance> > &obstacles, int hex, SDL_Surface *to)
{
std::pair<std::multimap<BattleHex, int>::const_iterator, std::multimap<BattleHex, int>::const_iterator> obstRange =
hexToObstacle->equal_range(hex);
for(std::multimap<BattleHex, int>::const_iterator it = obstRange.first; it != obstRange.second; ++it)
{
CObstacleInstance & curOb = obstacles[it->second];
const CObstacleInstance & curOb = *obstacles[it->second];
SDL_Surface *toBlit = imageOfObstacle(curOb);
Point p = whereToBlitObstacleImage(toBlit, curOb);
blitAt(toBlit, p.x, p.y, to);
@ -1336,7 +1358,7 @@ void CBattleInterface::stacksAreAttacked(std::vector<StackAttackedInfo> attacked
for(size_t h = 0; h < attackedInfos.size(); ++h)
{
++targets;
killed += attackedInfos[h].killed;
killed += attackedInfos[h].amountKilled;
damage += attackedInfos[h].dmg;
}
if (attackedInfos.front().cloneKilled) //FIXME: cloned stack is already removed
@ -1855,6 +1877,11 @@ void CBattleInterface::castThisSpell(int spellID)
spellSelMode = ANY_LOCATION;
}
if(spellID == Spells::FIRE_WALL || spellID == Spells::FORCE_FIELD)
{
spellSelMode = FREE_LOCATION;
}
if (spellSelMode == NO_LOCATION) //user does not have to select location
{
spellToCast->destinationTile = -1;
@ -2282,9 +2309,11 @@ void CBattleInterface::redrawBackgroundWithHexes(const CStack * activeStack)
blitAt(background, 0, 0, backgroundWithHexes);
//draw absolute obstacles (cliffs and so on)
BOOST_FOREACH(const CObstacleInstance &oi, curInt->cb->battleGetAllObstacles())
if(oi.obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
blitAt(imageOfObstacle(oi), oi.getInfo().width, oi.getInfo().height, backgroundWithHexes);
BOOST_FOREACH(auto &oi, curInt->cb->battleGetAllObstacles())
{
if(oi->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE/* || oi.obstacleType == CObstacleInstance::MOAT*/)
blitAt(imageOfObstacle(*oi), oi->getInfo().width, oi->getInfo().height, backgroundWithHexes);
}
if(settings["battle"]["cellBorders"].Bool())
CSDL_Ext::blit8bppAlphaTo24bpp(cellBorders, NULL, backgroundWithHexes, NULL);
@ -2307,7 +2336,7 @@ void CBattleInterface::redrawBackgroundWithHexes(const CStack * activeStack)
void CBattleInterface::printConsoleAttacked( const CStack * defender, int dmg, int killed, const CStack * attacker, bool multiple )
{
char tabh[200];
char tabh[200] = {0};
int end = 0;
if (attacker) //ignore if stacks were killed by spell
{
@ -2800,6 +2829,33 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
else
notLegal = true;
break;
case FREE_LOCATION:
{
ui8 side = curInt->cb->battleGetMySide();
auto hero = curInt->cb->battleGetFightingHero(side);
assert(!creatureCasting); //we assume hero casts this spell
assert(hero);
legalAction = true;
bool hexesOutsideBattlefield = false;
auto tilesThatMustBeClear = sp->rangeInHexes(myNumber, hero->getSpellSchoolLevel(sp), side, &hexesOutsideBattlefield);
BOOST_FOREACH(BattleHex hex, tilesThatMustBeClear)
{
if(curInt->cb->battleGetStackByPos(hex) || !!curInt->cb->battleGetObstacleOnPos(hex, false)
|| !hex.isAvailable())
{
legalAction = false;
notLegal = true;
}
}
if(hexesOutsideBattlefield)
{
legalAction = false;
notLegal = true;
}
}
break;
case CATAPULT:
if (isCatapultAttackable(myNumber))
legalAction = true;
@ -2930,7 +2986,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
break;
case ANY_LOCATION:
sp = CGI->spellh->spells[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo]; //necessary if creature has random Genie spell at same time
consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[26]) % sp->name); //Cast %s on %s
consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[26]) % sp->name); //Cast %s
isCastingPossible = true;
break;
case RANDOM_GENIE_SPELL: //we assume that teleport / sacrifice will never be avaliable as random spell
@ -2952,6 +3008,11 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
spellToCast->selectedStack = shere->ID; //sacrificed creature is selected
isCastingPossible = true;
break;
case FREE_LOCATION:
//cursorFrame = ECursor::SPELLBOOK;
consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[26]) % sp->name); //Cast %s
isCastingPossible = true;
break;
case HEAL:
cursorFrame = ECursor::COMBAT_HEAL;
consoleMsg = (boost::format(CGI->generaltexth->allTexts[419]) % shere->getName()).str(); //Apply first aid to the %s
@ -3006,6 +3067,10 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
case SACRIFICE:
consoleMsg = CGI->generaltexth->allTexts[543]; //choose army to sacrifice
break;
case FREE_LOCATION:
cursorFrame = ECursor::COMBAT_BLOCKED;
consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[181]) % sp->name); //No room to place %s here
break;
default:
cursorFrame = ECursor::COMBAT_BLOCKED;
break;
@ -3275,6 +3340,20 @@ SDL_Surface * CBattleInterface::imageOfObstacle(const CObstacleInstance &oi) con
return vstd::circularAt(quicksand->ourImages, frameIndex).bitmap;
case CObstacleInstance::LAND_MINE:
return vstd::circularAt(landMine->ourImages, frameIndex).bitmap;
case CObstacleInstance::FIRE_WALL:
return vstd::circularAt(fireWall->ourImages, frameIndex).bitmap;
case CObstacleInstance::FORCE_FIELD:
{
auto &forceField = dynamic_cast<const SpellCreatedObstacle &>(oi);
if(forceField.getAffectedTiles().size() > 2)
return vstd::circularAt(bigForceField[forceField.casterSide]->ourImages, frameIndex).bitmap;
else
return vstd::circularAt(smallForceField[forceField.casterSide]->ourImages, frameIndex).bitmap;
}
case CObstacleInstance::MOAT:
//moat is blitted by SiegeHelper, this shouldn't be called
assert(0);
default:
assert(0);
}
@ -3286,16 +3365,47 @@ void CBattleInterface::obstaclePlaced(const CObstacleInstance & oi)
waitForAnims();
int effectID = -1;
soundBase::soundID sound = soundBase::invalid;
std::string defname;
switch(oi.obstacleType)
{
case CObstacleInstance::QUICKSAND:
effectID = 55;
sound = soundBase::QUIKSAND;
break;
case CObstacleInstance::LAND_MINE:
effectID = 47;
sound = soundBase::LANDMINE;
break;
case CObstacleInstance::FORCE_FIELD:
{
auto &spellObstacle = dynamic_cast<const SpellCreatedObstacle&>(oi);
if(spellObstacle.casterSide)
{
if(oi.getAffectedTiles().size() < 3)
defname = "C15SPE0.DEF"; //TODO cannot find def for 2-hex force field \ appearing
else
defname = "C15SPE6.DEF";
}
else
{
if(oi.getAffectedTiles().size() < 3)
defname = "C15SPE0.DEF";
else
defname = "C15SPE9.DEF";
}
}
sound = soundBase::FORCEFLD;
break;
case CObstacleInstance::FIRE_WALL:
if(oi.getAffectedTiles().size() < 3)
effectID = 43; //small fire wall appearing
else
effectID = 44; //and the big one
sound = soundBase::fireWall;
break;
//TODO firewall, force field
default:
tlog1 << "I don't know how to animate appearing obstacle of type " << (int)oi.obstacleType << std::endl;
return;
@ -3307,12 +3417,17 @@ void CBattleInterface::obstaclePlaced(const CObstacleInstance & oi)
return;
}
std::string defname = graphics->battleACToDef[effectID].front();
if(defname.empty() && effectID >= 0)
defname = graphics->battleACToDef[effectID].front();
assert(!defname.empty());
//we assume here that effect graphics have the same size as the usual obstacle image
// -> if we know how to blit obstacle, let's blit the effect in the same place
Point whereTo = whereToBlitObstacleImage(imageOfObstacle(oi), oi);
addNewAnim(new CSpellEffectAnimation(this, defname, whereTo.x, whereTo.y));
//TODO we need to wait after playing sound till it's finished, otherwise it overlaps and sounds really bad
//CCS->soundh->playSound(sound);
}
Point CBattleInterface::whereToBlitObstacleImage(SDL_Surface *image, const CObstacleInstance &obstacle) const