1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-21 21:17:49 +02:00

Rewritten battle obstacles. New file for lib: CObstacleInstance.cpp.

Now obstacles should be placed exactly like they were in OH3. 
All problems with displaying obstacles in battlefield should be gone. They should be now matched to the single pixel. 
If there are still some discrepancies, please report them.
This commit is contained in:
Michał W. Urbańczyk 2012-04-23 19:56:37 +00:00
parent 6d03e0bf23
commit 7dc0d6878e
16 changed files with 1606 additions and 300 deletions

View File

@ -270,10 +270,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
for(int h = 0; h < bfield.size(); ++h) for(int h = 0; h < bfield.size(); ++h)
{ {
bfield[h].myNumber = h; bfield[h].myNumber = h;
bfield[h].pos = hexPosition(h);
int x = 14 + ((h/GameConstants::BFIELD_WIDTH)%2==0 ? 22 : 0) + 44*(h%GameConstants::BFIELD_WIDTH);
int y = 86 + 42 * (h/GameConstants::BFIELD_WIDTH);
bfield[h].pos = genRect(cellShade->h, cellShade->w, x + pos.x, y + pos.y);
bfield[h].accessible = true; bfield[h].accessible = true;
bfield[h].myInterface = this; bfield[h].myInterface = this;
} }
@ -341,10 +338,20 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
std::vector<CObstacleInstance> obst = curInt->cb->battleGetAllObstacles(); std::vector<CObstacleInstance> obst = curInt->cb->battleGetAllObstacles();
for(size_t t = 0; t < obst.size(); ++t) for(size_t t = 0; t < obst.size(); ++t)
{ {
idToObstacle[obst[t].ID] = CDefHandler::giveDef(CGI->heroh->obstacles.find(obst[t].ID)->second.defName); int ID = obst[t].ID;
for(size_t n = 0; n < idToObstacle[obst[t].ID]->ourImages.size(); ++n) std::string gfxName = obst[t].getInfo().defName;
if(!obst[t].isAbsoluteObstacle)
{ {
SDL_SetColorKey(idToObstacle[obst[t].ID]->ourImages[n].bitmap, SDL_SRCCOLORKEY, SDL_MapRGB(idToObstacle[obst[t].ID]->ourImages[n].bitmap->format,0,255,255)); idToObstacle[ID] = CDefHandler::giveDef(gfxName);
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
{
idToAbsoluteObstacle[ID] = BitmapHandler::loadBitmap(gfxName);
} }
} }
@ -625,10 +632,15 @@ void CBattleInterface::show(SDL_Surface * to)
//preparing obstacles to be shown //preparing obstacles to be shown
std::vector<CObstacleInstance> obstacles = curInt->cb->battleGetAllObstacles(); std::vector<CObstacleInstance> obstacles = curInt->cb->battleGetAllObstacles();
std::multimap<BattleHex, int> hexToObstacle; std::multimap<BattleHex, int> hexToObstacle;
for(size_t b = 0; b < obstacles.size(); ++b) for(size_t b = 0; b < obstacles.size(); ++b)
{ {
BattleHex position = CGI->heroh->obstacles.find(obstacles[b].ID)->second.getMaxBlocked(obstacles[b].pos); const CObstacleInstance &oi = obstacles[b];
hexToObstacle.insert(std::make_pair(position, b)); if(!oi.isAbsoluteObstacle)
{
//BattleHex position = CGI->heroh->obstacles.find(obstacles[b].ID)->second.getMaxBlocked(obstacles[b].pos);
hexToObstacle.insert(std::make_pair(oi.pos, b));
}
} }
////showing units //a lot of work... ////showing units //a lot of work...
@ -892,11 +904,15 @@ void CBattleInterface::showObstacles(std::multimap<BattleHex, int> *hexToObstacl
for(std::multimap<BattleHex, int>::const_iterator it = obstRange.first; it != obstRange.second; ++it) for(std::multimap<BattleHex, int>::const_iterator it = obstRange.first; it != obstRange.second; ++it)
{ {
CObstacleInstance & curOb = obstacles[it->second]; CObstacleInstance & curOb = obstacles[it->second];
std::pair<si16, si16> shift = CGI->heroh->obstacles.find(curOb.ID)->second.posShift;
int x = ((curOb.pos/GameConstants::BFIELD_WIDTH)%2==0 ? 22 : 0) + 44*(curOb.pos%GameConstants::BFIELD_WIDTH) + pos.x + shift.first;
int y = 86 + 42 * (curOb.pos/GameConstants::BFIELD_WIDTH) + pos.y + shift.second;
std::vector<Cimage> &images = idToObstacle[curOb.ID]->ourImages; //reference to animation of obstacle std::vector<Cimage> &images = idToObstacle[curOb.ID]->ourImages; //reference to animation of obstacle
blitAt(images[((animCount+1)/(4/getAnimSpeed()))%images.size()].bitmap, x, y, to); Rect r = hexPosition(hex);
int offset = images.front().bitmap->h % 42;
if(offset > 15) //experimental value, may need tweaking if some obstacles are shown too low/high
offset -= 42;
r.y += 42 - images.front().bitmap->h + offset;
//r.y -= cellShade->h*CGI->heroh->obstacles.find(curOb.ID)->second.height - images.front().bitmap->h;
blitAt(images[((animCount+1)/(4/getAnimSpeed()))%images.size()].bitmap, r.x, r.y, to);
} }
} }
@ -2251,6 +2267,12 @@ void CBattleInterface::redrawBackgroundWithHexes(const CStack * activeStack)
curInt->cb->battleGetStackCountOutsideHexes(stackCountOutsideHexes); curInt->cb->battleGetStackCountOutsideHexes(stackCountOutsideHexes);
//preparating background graphic with hexes and shaded hexes //preparating background graphic with hexes and shaded hexes
blitAt(background, 0, 0, backgroundWithHexes); blitAt(background, 0, 0, backgroundWithHexes);
//draw absolute obstacles (cliffs and so on)
BOOST_FOREACH(const CObstacleInstance &oi, curInt->cb->battleGetAllObstacles())
if(oi.isAbsoluteObstacle)
blitAt(idToAbsoluteObstacle[oi.ID], oi.getInfo().width, oi.getInfo().height, backgroundWithHexes);
if(settings["battle"]["cellBorders"].Bool()) if(settings["battle"]["cellBorders"].Bool())
CSDL_Ext::blit8bppAlphaTo24bpp(cellBorders, NULL, backgroundWithHexes, NULL); CSDL_Ext::blit8bppAlphaTo24bpp(cellBorders, NULL, backgroundWithHexes, NULL);
@ -3182,6 +3204,16 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber)
} }
return -1; return -1;
} }
Rect CBattleInterface::hexPosition(BattleHex hex) const
{
int x = 14 + ((hex.getY())%2==0 ? 22 : 0) + 44*hex.getX() + pos.x;
int y = 86 + 42 * hex.getY() + pos.y;
int w = cellShade->w;
int h = cellShade->h;
return Rect(x, y, w, h);
}
std::string CBattleInterface::SiegeHelper::townTypeInfixes[GameConstants::F_NUMBER] = {"CS", "RM", "TW", "IN", "NC", "DN", "ST", "FR", "EL"}; std::string CBattleInterface::SiegeHelper::townTypeInfixes[GameConstants::F_NUMBER] = {"CS", "RM", "TW", "IN", "NC", "DN", "ST", "FR", "EL"};
CBattleInterface::SiegeHelper::SiegeHelper(const CGTownInstance *siegeTown, const CBattleInterface * _owner) CBattleInterface::SiegeHelper::SiegeHelper(const CGTownInstance *siegeTown, const CBattleInterface * _owner)

View File

@ -111,6 +111,7 @@ private:
std::map< int, CCreatureAnimation * > creAnims; //animations of creatures from fighting armies (order by BattleInfo's stacks' ID) std::map< int, CCreatureAnimation * > creAnims; //animations of creatures from fighting armies (order by BattleInfo's stacks' ID)
std::map< int, CDefHandler * > idToProjectile; //projectiles of creatures (creatureID, defhandler) std::map< int, CDefHandler * > idToProjectile; //projectiles of creatures (creatureID, defhandler)
std::map< int, CDefHandler * > idToObstacle; //obstacles located on the battlefield std::map< int, CDefHandler * > idToObstacle; //obstacles located on the battlefield
std::map< int, SDL_Surface * > idToAbsoluteObstacle; //obstacles located on the battlefield
std::map< int, bool > creDir; // <creatureID, if false reverse creature's animation> std::map< int, bool > creDir; // <creatureID, if false reverse creature's animation>
ui8 animCount; ui8 animCount;
const CStack * activeStack; //number of active stack; NULL - no one const CStack * activeStack; //number of active stack; NULL - no one
@ -261,6 +262,7 @@ public:
void hideQueue(); void hideQueue();
void showQueue(); void showQueue();
PossibleActions selectionTypeByPositiveness(const CSpell & spell); PossibleActions selectionTypeByPositiveness(const CSpell & spell);
Rect hexPosition(BattleHex hex) const;
void handleHex(BattleHex myNumber, int eventType); void handleHex(BattleHex myNumber, int eventType);
bool isCastingPossibleHere (const CStack * sactive, const CStack * shere, BattleHex myNumber); bool isCastingPossibleHere (const CStack * sactive, const CStack * shere, BattleHex myNumber);

File diff suppressed because it is too large Load Diff

View File

@ -153,7 +153,7 @@ void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool atta
//obstacles //obstacles
for(ui32 b=0; b<obstacles.size(); ++b) for(ui32 b=0; b<obstacles.size(); ++b)
{ {
std::vector<BattleHex> blocked = VLC->heroh->obstacles[obstacles[b].ID].getBlocked(obstacles[b].pos); std::vector<BattleHex> blocked = obstacles[b].getBlocked();
for(ui32 c=0; c<blocked.size(); ++c) for(ui32 c=0; c<blocked.size(); ++c)
{ {
if(blocked[c] >=0 && blocked[c] < GameConstants::BFIELD_SIZE) if(blocked[c] >=0 && blocked[c] < GameConstants::BFIELD_SIZE)
@ -1501,6 +1501,81 @@ namespace CGH
} }
} }
//RNG that works like H3 one
struct RandGen
{
int seed;
void srand(int s)
{
seed = s;
}
void srand(int3 pos)
{
srand(110291 * pos.x + 167801 * pos.y + 81569);
}
int rand()
{
seed = 214013 * seed + 2531011;
return (seed >> 16) & 0x7FFF;
}
int rand(int min, int max)
{
if(min == max)
return min;
if(min > max)
return min;
return min + rand() % (max - min + 1);
}
};
struct RangeGenerator
{
class ExhaustedPossibilities : public std::exception
{
};
RangeGenerator(int _min, int _max, boost::function<int()> _myRand)
{
myRand = _myRand;
min = _min;
remainingCount = _max - _min + 1;
remaining.resize(remainingCount, true);
}
//get number fulfilling predicate. Never gives the same number twice.
int getSuchNumber(boost::function<bool(int)> goodNumberPred = 0)
{
int ret = -1;
do
{
if(!remainingCount)
throw ExhaustedPossibilities();
int n = myRand() % remainingCount;
int i = 0;
for(;;i++)
{
assert(i < (int)remaining.size());
if(!remaining[i])
continue;
if(!n)
break;
n--;
}
remainingCount--;
remaining[i] = false;
ret = i + min;
} while(goodNumberPred && !goodNumberPred(ret));
return ret;
}
int min, remainingCount;
std::vector<bool> remaining;
boost::function<int()> myRand;
};
BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town ) BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town )
{ {
CMP_stack cmpst; CMP_stack cmpst;
@ -1577,7 +1652,8 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const
stacks.push_back(stack); stacks.push_back(stack);
} }
for(unsigned g=0; g<stacks.size(); ++g) //shifting positions of two-hex creatures //shifting positions of two-hex creatures
for(unsigned g=0; g<stacks.size(); ++g)
{ {
//we should do that for creature bank too //we should do that for creature bank too
if(stacks[g]->doubleWide() && stacks[g]->attackerOwned) if(stacks[g]->doubleWide() && stacks[g]->attackerOwned)
@ -1667,61 +1743,96 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const
} }
//randomize obstacles //randomize obstacles
if(town == NULL && !creatureBank) //do it only when it's not siege and not creature bank if(town == NULL && !creatureBank) //do it only when it's not siege and not creature bank
{ {
bool obAv[GameConstants::BFIELD_SIZE]; //availability of hexes for obstacles; const int ABSOLUTE_OBSTACLES_COUNT = 34, USUAL_OBSTACLES_COUNT = 91; //shouldn't be changes if we want H3-like obstacle placement
std::vector<int> possibleObstacles;
for(int i=0; i<GameConstants::BFIELD_SIZE; ++i) RandGen r;
{ auto ourRand = [&]{ return r.rand(); };
if(i%17 < 4 || i%17 > 12) r.srand(tile);
{ const int sound = r.rand(1,8); //battle sound ID to play... can't do anything with it here
obAv[i] = false; int tilesToBlock = r.rand(5,12);
} const int specialBattlefield = battlefieldTypeToBI(terType);
else
{
obAv[i] = true;
}
}
for(std::map<int, CObstacleInfo>::const_iterator g=VLC->heroh->obstacles.begin(); g!=VLC->heroh->obstacles.end(); ++g) std::vector<BattleHex> blockedTiles;
{
if(g->second.allowedTerrains[terType-1] == '1') //we need to take terType with -1 because terrain ids start from 1 and allowedTerrains array is indexed from 0
{
possibleObstacles.push_back(g->first);
}
}
srand(time(NULL)); auto appropriateAbsoluteObstacle = [&](int id)
if(possibleObstacles.size() > 0) //we cannot place any obstacles when we don't have them
{ {
int toBlock = rand()%6 + 6; //how many hexes should be blocked by obstacles return VLC->heroh->absoluteObstacles[id].isAppropriate(terrain, specialBattlefield);
while(toBlock>0) };
auto appropriateUsualObstacle = [&](int id) -> bool
{
return VLC->heroh->obstacles[id].isAppropriate(terrain, specialBattlefield);
};
if(r.rand(1,100) <= 40) //put cliff-like obstacle
{
RangeGenerator obidgen(0, ABSOLUTE_OBSTACLES_COUNT-1, ourRand);
try
{ {
CObstacleInstance coi; CObstacleInstance coi;
coi.isAbsoluteObstacle = true;
coi.ID = obidgen.getSuchNumber(appropriateAbsoluteObstacle);
coi.uniqueID = curB->obstacles.size(); coi.uniqueID = curB->obstacles.size();
coi.ID = possibleObstacles[rand()%possibleObstacles.size()];
coi.pos = rand()%GameConstants::BFIELD_SIZE;
std::vector<BattleHex> block = VLC->heroh->obstacles[coi.ID].getBlocked(coi.pos);
bool badObstacle = false;
for(int b=0; b<block.size(); ++b)
{
if(block[b] < 0 || block[b] >= GameConstants::BFIELD_SIZE || !obAv[block[b]])
{
badObstacle = true;
break;
}
}
if(badObstacle) continue;
//obstacle can be placed
curB->obstacles.push_back(coi); curB->obstacles.push_back(coi);
for(int b=0; b<block.size(); ++b)
{ BOOST_FOREACH(BattleHex blocked, coi.getBlocked())
if(block[b] >= 0 && block[b] < GameConstants::BFIELD_SIZE) blockedTiles.push_back(blocked);
obAv[block[b]] = false; tilesToBlock -= VLC->heroh->absoluteObstacles[coi.ID].blockedTiles.size() / 2;
}
toBlock -= block.size();
} }
catch(RangeGenerator::ExhaustedPossibilities &)
{
//silently ignore, if we can't place absolute obstacle, we'll go wityh the usual ones
}
}
RangeGenerator obidgen(0, USUAL_OBSTACLES_COUNT-1, ourRand);
try
{
while(tilesToBlock > 0)
{
const int obid = obidgen.getSuchNumber(appropriateUsualObstacle);
const CObstacleInfo &obi = VLC->heroh->obstacles[obid];
auto validPosition = [&](BattleHex pos) -> bool
{
if(obi.height >= pos.getY())
return false;
if(pos.getX() == 0)
return false;
if(pos.getX() + obi.width > 15)
return false;
if(vstd::contains(blockedTiles, pos))
return false;
BOOST_FOREACH(BattleHex blocked, obi.getBlocked(pos))
{
if(vstd::contains(blockedTiles, blocked))
return false;
int x = blocked.getX();
if(x <= 2 || x >= 14)
return false;
}
return true;
};
RangeGenerator posgenerator(18, 168, ourRand);
CObstacleInstance oi;
oi.ID = obid;
oi.pos = posgenerator.getSuchNumber(validPosition);
oi.uniqueID = curB->obstacles.size();
curB->obstacles.push_back(oi);
BOOST_FOREACH(BattleHex blocked, oi.getBlocked())
blockedTiles.push_back(blocked);
tilesToBlock -= obi.blockedTiles.size();
}
}
catch(RangeGenerator::ExhaustedPossibilities &)
{
} }
} }
@ -2400,7 +2511,7 @@ bool BattleInfo::isObstacleOnTile(BattleHex tile) const
std::set<BattleHex> coveredHexes; std::set<BattleHex> coveredHexes;
BOOST_FOREACH(const CObstacleInstance &obs, obstacles) BOOST_FOREACH(const CObstacleInstance &obs, obstacles)
{ {
std::vector<BattleHex> blocked = VLC->heroh->obstacles.find(obs.ID)->second.getBlocked(obs.pos); std::vector<BattleHex> blocked = obs.getBlocked();
for(size_t w = 0; w < blocked.size(); ++w) for(size_t w = 0; w < blocked.size(); ++w)
coveredHexes.insert(blocked[w]); coveredHexes.insert(blocked[w]);
} }
@ -2415,6 +2526,21 @@ const CStack * BattleInfo::getStackIf(boost::function<bool(const CStack*)> pred)
: *stackItr; : *stackItr;
} }
int BattleInfo::battlefieldTypeToBI(int bfieldType)
{
static const std::map<int, int> theMap = boost::assign::map_list_of(19, BattlefieldBI::CLOVER_FIELD)
(22, BattlefieldBI::CURSED_GROUND)(20, BattlefieldBI::EVIL_FOG)(21, BattlefieldBI::NONE)
(14, BattlefieldBI::FIERY_FIELDS)(18, BattlefieldBI::HOLY_GROUND)(17, BattlefieldBI::LUCID_POOLS)
(16, BattlefieldBI::MAGIC_CLOUDS)(9, BattlefieldBI::MAGIC_PLAINS)(15, BattlefieldBI::ROCKLANDS)
(1, BattlefieldBI::COASTAL);
auto itr = theMap.find(bfieldType);
if(itr != theMap.end())
return itr->second;
return BattlefieldBI::NONE;
}
CStack::CStack(const CStackInstance *Base, int O, int I, bool AO, int S) CStack::CStack(const CStackInstance *Base, int O, int I, bool AO, int S)
: base(Base), ID(I), owner(O), slot(S), attackerOwned(AO), : base(Base), ID(I), owner(O), slot(S), attackerOwned(AO),
counterAttacks(1) counterAttacks(1)

View File

@ -155,6 +155,9 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode
int theOtherPlayer(int player) const; int theOtherPlayer(int player) const;
ui8 whatSide(int player) const; ui8 whatSide(int player) const;
static int battlefieldTypeToBI(int bfieldType); //converts above to ERM BI format
static int battlefieldTypeToTerrain(int bfieldType); //converts above to ERM BI format
}; };
class DLL_LINKAGE CStack : public CBonusSystemNode, public CStackBasicDescriptor class DLL_LINKAGE CStack : public CBonusSystemNode, public CStackBasicDescriptor

View File

@ -915,7 +915,7 @@ void CStackInstance::deserializationFix()
{ {
const CCreature *backup = type; const CCreature *backup = type;
type = NULL; type = NULL;
setType(backup); setType(backup);
const CArmedInstance *armyBackup = _armyObj; const CArmedInstance *armyBackup = _armyObj;
_armyObj = NULL; _armyObj = NULL;
setArmyObj(armyBackup); setArmyObj(armyBackup);

View File

@ -1589,7 +1589,7 @@ void CGameState::init(StartInfo * si)
} }
} }
int CGameState::battleGetBattlefieldType(int3 tile) int CGameState::battleGetBattlefieldType(int3 tile) const
{ {
if(tile==int3() && curB) if(tile==int3() && curB)
tile = curB->tile; tile = curB->tile;
@ -1609,27 +1609,28 @@ int CGameState::battleGetBattlefieldType(int3 tile)
|| !objs[g]->coveringAt(objs[g]->pos.x - tile.x, tile.y - objs[g]->pos.y + 5) || !objs[g]->coveringAt(objs[g]->pos.x - tile.x, tile.y - objs[g]->pos.y + 5)
) //look only for objects covering given tile ) //look only for objects covering given tile
continue; continue;
switch(objs[g]->ID) switch(objs[g]->ID)
{ {
case 222: //clover field case Obj::CLOVER_FIELD:
return 19; return 19;
case 21: case 223: //cursed ground case Obj::CURSED_GROUND1: case Obj::CURSED_GROUND2:
return 22; return 22;
case 224: //evil fog case Obj::EVIL_FOG:
return 20; return 20;
case 225: //favourable winds case Obj::FAVORABLE_WINDS:
return 21; return 21;
case 226: //fiery fields case Obj::FIERY_FIELDS:
return 14; return 14;
case 227: //holy ground case Obj::HOLY_GROUNDS:
return 18; return 18;
case 228: //lucid pools case Obj::LUCID_POOLS:
return 17; return 17;
case 229: //magic clouds case Obj::MAGIC_CLOUDS:
return 16; return 16;
case 46: case 230: //magic plains case Obj::MAGIC_PLAINS1: case Obj::MAGIC_PLAINS2:
return 9; return 9;
case 231: //rocklands case Obj::ROCKLANDS:
return 15; return 15;
} }
} }
@ -1664,6 +1665,7 @@ int CGameState::battleGetBattlefieldType(int3 tile)
} }
} }
std::set<std::pair<int, int> > costDiff(const std::vector<ui32> &a, const std::vector<ui32> &b, const int modifier = 100) //modifer % std::set<std::pair<int, int> > costDiff(const std::vector<ui32> &a, const std::vector<ui32> &b, const int modifier = 100) //modifer %
{ {
std::set<std::pair<int, int> > ret; std::set<std::pair<int, int> > ret;

View File

@ -381,7 +381,7 @@ public:
void giveHeroArtifact(CGHeroInstance *h, int aid); void giveHeroArtifact(CGHeroInstance *h, int aid);
void apply(CPack *pack); void apply(CPack *pack);
int battleGetBattlefieldType(int3 tile = int3());// 1. sand/shore 2. sand/mesas 3. dirt/birches 4. dirt/hills 5. dirt/pines 6. grass/hills 7. grass/pines 8. lava 9. magic plains 10. snow/mountains 11. snow/trees 12. subterranean 13. swamp/trees 14. fiery fields 15. rock lands 16. magic clouds 17. lucid pools 18. holy ground 19. clover field 20. evil fog 21. "favourable winds" text on magic plains background 22. cursed ground 23. rough 24. ship to ship 25. ship int battleGetBattlefieldType(int3 tile) const;// 1. sand/shore 2. sand/mesas 3. dirt/birches 4. dirt/hills 5. dirt/pines 6. grass/hills 7. grass/pines 8. lava 9. magic plains 10. snow/mountains 11. snow/trees 12. subterranean 13. swamp/trees 14. fiery fields 15. rock lands 16. magic clouds 17. lucid pools 18. holy ground 19. clover field 20. evil fog 21. "favourable winds" text on magic plains background 22. cursed ground 23. rough 24. ship to ship 25. ship
UpgradeInfo getUpgradeInfo(const CStackInstance &stack); UpgradeInfo getUpgradeInfo(const CStackInstance &stack);
int getPlayerRelations(ui8 color1, ui8 color2);// 0 = enemy, 1 = ally, 2 = same player int getPlayerRelations(ui8 color1, ui8 color2);// 0 = enemy, 1 = ally, 2 = same player
bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if src tile is visitable from dst tile bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if src tile is visitable from dst tile

View File

@ -6,13 +6,7 @@
#include "../lib/JsonNode.h" #include "../lib/JsonNode.h"
#include "GameConstants.h" #include "GameConstants.h"
#include <boost/version.hpp> #include <boost/version.hpp>
#if BOOST_VERSION >= 103800 #include "BattleHex.h"
#include <boost/spirit/include/classic.hpp>
#else
#include <boost/spirit.hpp>
#endif
using namespace boost::spirit;
extern CLodHandler * bitmaph; extern CLodHandler * bitmaph;
void loadToIt(std::string &dest, const std::string &src, int &iter, int mode); void loadToIt(std::string &dest, const std::string &src, int &iter, int mode);
@ -57,74 +51,37 @@ EAlignment::EAlignment CHeroClass::getAlignment()
return (EAlignment::EAlignment)alignment; return (EAlignment::EAlignment)alignment;
} }
int CObstacleInfo::getWidth() const
{
int ret = 1;
int line = 1;
for(int h=0; h<blockmap.size(); ++h)
{
int cur = - line/2;
switch(blockmap[h])
{
case 'X' : case 'N':
++cur;
break;
case 'L':
if(cur > ret)
ret = cur;
++line;
break;
}
}
return ret;
}
int CObstacleInfo::getHeight() const
{
int ret = 1;
for(int h=0; h<blockmap.size(); ++h)
{
if(blockmap[h] == 'L')
{
++ret;
}
}
return ret;
}
std::vector<BattleHex> CObstacleInfo::getBlocked(BattleHex hex) const std::vector<BattleHex> CObstacleInfo::getBlocked(BattleHex hex) const
{ {
std::vector<BattleHex> ret; std::vector<BattleHex> ret;
int cur = hex; //currently browsed hex if(isAbsoluteObstacle)
int curBeg = hex; //beginning of current line
for(int h=0; h<blockmap.size(); ++h)
{ {
switch(blockmap[h]) assert(!hex.isValid());
{ range::copy(blockedTiles, std::back_inserter(ret));
case 'X': return ret;
ret.push_back(cur);
++cur;
break;
case 'L':
cur = curBeg + GameConstants::BFIELD_WIDTH;
if((cur/GameConstants::BFIELD_WIDTH)%2 != 1)
{
cur--;
}
curBeg = cur;
break;
case 'N':
++cur;
break;
}
} }
BOOST_FOREACH(int offset, blockedTiles)
{
BattleHex toBlock = hex + offset;
if((hex.getY() & 1) && !(toBlock.getY() & 1))
toBlock += BattleHex::LEFT;
if(!toBlock.isValid())
tlog1 << "Misplaced obstacle!\n";
else
ret.push_back(toBlock);
}
return ret; return ret;
} }
BattleHex CObstacleInfo::getMaxBlocked(BattleHex hex) const bool CObstacleInfo::isAppropriate(int terrainType, int specialBattlefield /*= -1*/) const
{ {
std::vector<BattleHex> blocked = getBlocked(hex); if(specialBattlefield != -1)
return *std::max_element(blocked.begin(), blocked.end()); return vstd::contains(allowedSpecialBfields, specialBattlefield);
return vstd::contains(allowedTerrains, terrainType);
} }
CHeroHandler::~CHeroHandler() CHeroHandler::~CHeroHandler()
@ -141,21 +98,27 @@ CHeroHandler::CHeroHandler()
void CHeroHandler::loadObstacles() void CHeroHandler::loadObstacles()
{ {
auto loadObstacles = [](const JsonNode &node, bool absolute, std::map<int, CObstacleInfo> &out)
{
BOOST_FOREACH(const JsonNode &obs, node.Vector())
{
int ID = obs["id"].Float();
CObstacleInfo & obi = out[ID];
obi.ID = ID;
obi.defName = obs["defname"].String();
obi.width = obs["width"].Float();
obi.height = obs["height"].Float();
obi.allowedTerrains = obs["allowedTerrain"].StdVector<ui8>();
obi.allowedSpecialBfields = obs["specialBattlefields"].StdVector<ui8>();
obi.blockedTiles = obs["blockedTiles"].StdVector<si16>();
obi.isAbsoluteObstacle = absolute;
}
};
const JsonNode config(GameConstants::DATA_DIR + "/config/obstacles.json"); const JsonNode config(GameConstants::DATA_DIR + "/config/obstacles.json");
loadObstacles(config["obstacles"], false, obstacles);
BOOST_FOREACH(const JsonNode &obs, config["obstacles"].Vector()) { loadObstacles(config["absoluteObstacles"], true, absoluteObstacles);
CObstacleInfo obi;
obi.ID = obs["id"].Float();
obi.defName = obs["defname"].String();
obi.blockmap = obs["blockmap"].String();
obi.allowedTerrains = obs["terrains"].String();
assert(obi.allowedTerrains.size() >= 25);
obi.posShift.first = obs["shift_x"].Float();
obi.posShift.second = obs["shift_y"].Float();
obstacles[obi.ID] = obi;
}
} }
void CHeroHandler::loadPuzzleInfo() void CHeroHandler::loadPuzzleInfo()
@ -164,12 +127,12 @@ void CHeroHandler::loadPuzzleInfo()
int faction = 0; int faction = 0;
BOOST_FOREACH(const JsonNode &puzzle, config["puzzles"].Vector()) { BOOST_FOREACH(const JsonNode &puzzle, config["puzzles"].Vector())
{
int idx = 0; int idx = 0;
BOOST_FOREACH(const JsonNode &piece, puzzle.Vector()) { BOOST_FOREACH(const JsonNode &piece, puzzle.Vector())
{
SPuzzleInfo spi; SPuzzleInfo spi;
spi.x = piece["x"].Float(); spi.x = piece["x"].Float();

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "BattleHex.h"
#include "../lib/ConstTransitivePtr.h" #include "../lib/ConstTransitivePtr.h"
#include "GameConstants.h" #include "GameConstants.h"
@ -18,6 +17,7 @@ class CHeroClass;
class CDefHandler; class CDefHandler;
class CGameInfo; class CGameInfo;
class CGHeroInstance; class CGHeroInstance;
struct BattleHex;
struct SSpecialtyInfo struct SSpecialtyInfo
{ si32 type; { si32 type;
@ -85,22 +85,22 @@ public:
struct DLL_LINKAGE CObstacleInfo struct DLL_LINKAGE CObstacleInfo
{ {
int ID; si32 ID;
std::string defName, std::string defName;
blockmap, //blockmap: X - blocked, N - not blocked, L - description goes to the next line, staring with the left bottom hex std::vector<ui8> allowedTerrains;
allowedTerrains; /*terrains[i]: 1. sand/shore 2. sand/mesas 3. dirt/birches 4. dirt/hills 5. dirt/pines 6. grass/hills std::vector<ui8> allowedSpecialBfields;
7. grass/pines 8. lava 9. magic plains 10. snow/mountains 11. snow/trees 12. subterranean 13. swamp/trees
14. fiery fields 15. rock lands 16. magic clouds 17. lucid pools 18. holy ground 19. clover field ui8 isAbsoluteObstacle; //there may only one such obstacle in battle and its position is always the same
20. evil fog 21. "favourable winds" text on magic plains background 22. cursed ground 23. rough si32 width, height; //how much space to the right and up is needed to place obstacle (affects only placement algorithm)
24. ship to ship 25. ship*/ std::vector<si16> blockedTiles; //offsets relative to obstacle position (that is its left bottom corner)
std::pair<si16, si16> posShift; //shift of obstacle's position in the battlefield <x shift, y shift>, eg. if it's <-1, 2> obstacle will be printed one pixel to the left and two to the bottom
int getWidth() const; //returns width of obstacle in hexes
int getHeight() const; //returns height of obstacle in hexes
std::vector<BattleHex> getBlocked(BattleHex hex) const; //returns vector of hexes blocked by obstacle when it's placed on hex 'hex' std::vector<BattleHex> getBlocked(BattleHex hex) const; //returns vector of hexes blocked by obstacle when it's placed on hex 'hex'
BattleHex getMaxBlocked(BattleHex hex) const; //returns maximal hex (max number) covered by this obstacle
bool isAppropriate(int terrainType, int specialBattlefield = -1) const;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & ID & defName & blockmap & allowedTerrains & posShift; h & ID & defName & allowedTerrains & allowedSpecialBfields & isAbsoluteObstacle & width & height & blockedTiles;
} }
}; };
@ -140,6 +140,7 @@ public:
std::vector<SBallisticsLevelInfo> ballistics; //info about ballistics ability per level; [0] - none; [1] - basic; [2] - adv; [3] - expert std::vector<SBallisticsLevelInfo> ballistics; //info about ballistics ability per level; [0] - none; [1] - basic; [2] - adv; [3] - expert
std::map<int, CObstacleInfo> obstacles; //info about obstacles that may be placed on battlefield std::map<int, CObstacleInfo> obstacles; //info about obstacles that may be placed on battlefield
std::map<int, CObstacleInfo> absoluteObstacles; //info about obstacles that may be placed on battlefield
std::vector<int> nativeTerrains; //info about native terrains of different factions std::vector<int> nativeTerrains; //info about native terrains of different factions
void loadObstacles(); //loads info about obstacles void loadObstacles(); //loads info about obstacles
@ -159,7 +160,7 @@ public:
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & heroClasses & heroes & expPerLevel & ballistics & obstacles & nativeTerrains & puzzleInfo; h & heroClasses & heroes & expPerLevel & ballistics & obstacles & absoluteObstacles & nativeTerrains & puzzleInfo;
if(!h.saving) if(!h.saving)
{ {
//restore class pointers //restore class pointers

View File

@ -45,9 +45,9 @@ void CMapInfo::mapInit(const std::string &fname, const ui8 *map )
mapHeader->initFromMemory(map, i); mapHeader->initFromMemory(map, i);
countPlayers(); countPlayers();
} }
catch (const std::string &e) catch (const std::exception &e)
{ {
tlog1 << "\t\tWarning: evil map file: " << fname << ": " << e << std::endl; tlog1 << "\t\tWarning: evil map file: " << fname << ": " << e.what() << std::endl;
delete mapHeader; delete mapHeader;
mapHeader = NULL; mapHeader = NULL;
} }

View File

@ -1,12 +1,23 @@
#pragma once #pragma once
#include "BattleHex.h"
struct CObstacleInfo;
struct DLL_LINKAGE CObstacleInstance struct DLL_LINKAGE CObstacleInstance
{ {
int uniqueID; si32 uniqueID;
int ID; //ID of obstacle (defines type of it) si32 ID; //ID of obstacle (defines type of it)
int pos; //position on battlefield BattleHex pos; //position on battlefield
ui8 isAbsoluteObstacle; //if true, then position is meaningless
CObstacleInstance();
const CObstacleInfo &getInfo() const;
std::vector<BattleHex> getBlocked() const;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & ID & pos & uniqueID; h & ID & pos & isAbsoluteObstacle & uniqueID;
} }
}; };

View File

@ -187,6 +187,7 @@ namespace Obj
BOAT = 8, BOAT = 8,
CREATURE_BANK = 16, CREATURE_BANK = 16,
CREATURE_GENERATOR1 = 17, CREATURE_GENERATOR1 = 17,
CURSED_GROUND1 = 21,
DERELICT_SHIP = 24, DERELICT_SHIP = 24,
DRAGON_UTOPIA = 25, DRAGON_UTOPIA = 25,
GARRISON = 33, GARRISON = 33,
@ -194,6 +195,7 @@ namespace Obj
MONOLITH1 = 43, MONOLITH1 = 43,
MONOLITH2 = 44, MONOLITH2 = 44,
MONOLITH3 = 45, MONOLITH3 = 45,
MAGIC_PLAINS1 = 46,
SCHOOL_OF_MAGIC = 47, SCHOOL_OF_MAGIC = 47,
MINE = 53, MINE = 53,
MONSTER = 54, MONSTER = 54,
@ -209,6 +211,35 @@ namespace Obj
WHIRLPOOL = 111, WHIRLPOOL = 111,
BORDER_GATE = 212, BORDER_GATE = 212,
GARRISON2 = 219, GARRISON2 = 219,
CLOVER_FIELD = 222,
CURSED_GROUND2 = 223,
EVIL_FOG = 224,
FAVORABLE_WINDS = 225,
FIERY_FIELDS = 226,
HOLY_GROUNDS = 227,
LUCID_POOLS = 228,
MAGIC_CLOUDS = 229,
MAGIC_PLAINS2 = 230,
ROCKLANDS = 231,
};
}
//follows ERM BI (battle image) format
namespace BattlefieldBI
{
enum
{
NONE = -1,
COASTAL,
CURSED_GROUND,
MAGIC_PLAINS,
HOLY_GROUND,
EVIL_FOG,
CLOVER_FIELD,
LUCID_POOLS,
FIERY_FIELDS,
ROCKLANDS,
MAGIC_CLOUDS,
}; };
} }

View File

@ -75,6 +75,19 @@ public:
const JsonVector & Vector() const; const JsonVector & Vector() const;
const JsonMap & Struct() const; const JsonMap & Struct() const;
template<typename T>
std::vector<T> StdVector() const
{
static_assert(typename std::is_arithmetic<T>::value, "This works with numbers only.");
std::vector<T> ret;
BOOST_FOREACH(const JsonNode &node, Vector())
{
ret.push_back(node.Float());
}
return ret;
}
//operator [], for structs only - get child node by name //operator [], for structs only - get child node by name
JsonNode & operator[](std::string child); JsonNode & operator[](std::string child);
const JsonNode & operator[](std::string child) const; const JsonNode & operator[](std::string child) const;

View File

@ -239,6 +239,7 @@
<ClCompile Include="CLogger.cpp" /> <ClCompile Include="CLogger.cpp" />
<ClCompile Include="CMapInfo.cpp" /> <ClCompile Include="CMapInfo.cpp" />
<ClCompile Include="CObjectHandler.cpp" /> <ClCompile Include="CObjectHandler.cpp" />
<ClCompile Include="CObstacleInstance.cpp" />
<ClCompile Include="Connection.cpp" /> <ClCompile Include="Connection.cpp" />
<ClCompile Include="CSpellHandler.cpp" /> <ClCompile Include="CSpellHandler.cpp" />
<ClCompile Include="CThreadHelper.cpp" /> <ClCompile Include="CThreadHelper.cpp" />

View File

@ -3884,17 +3884,16 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
case Spells::REMOVE_OBSTACLE: case Spells::REMOVE_OBSTACLE:
{ {
ObstaclesRemoved obr; ObstaclesRemoved obr;
for(int g=0; g<gs->curB->obstacles.size(); ++g) BOOST_FOREACH(const CObstacleInstance &obstacle, battleGetAllObstacles())
{ {
std::vector<BattleHex> blockedHexes = VLC->heroh->obstacles[gs->curB->obstacles[g].ID].getBlocked(gs->curB->obstacles[g].pos); if(vstd::contains(obstacle.getBlocked(), destination))
obr.obstacles.insert(obstacle.uniqueID);
if(vstd::contains(blockedHexes, destination)) //this obstacle covers given hex
{
obr.obstacles.insert(gs->curB->obstacles[g].uniqueID);
}
} }
if(!obr.obstacles.empty()) if(!obr.obstacles.empty())
sendAndApply(&obr); sendAndApply(&obr);
else
complain("There's no obstacle to remove!");
break; break;
} }
break; break;