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:
parent
6d03e0bf23
commit
7dc0d6878e
@ -270,10 +270,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
|
||||
for(int h = 0; h < bfield.size(); ++h)
|
||||
{
|
||||
bfield[h].myNumber = 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].pos = hexPosition(h);
|
||||
bfield[h].accessible = true;
|
||||
bfield[h].myInterface = this;
|
||||
}
|
||||
@ -341,10 +338,20 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
|
||||
std::vector<CObstacleInstance> obst = curInt->cb->battleGetAllObstacles();
|
||||
for(size_t t = 0; t < obst.size(); ++t)
|
||||
{
|
||||
idToObstacle[obst[t].ID] = CDefHandler::giveDef(CGI->heroh->obstacles.find(obst[t].ID)->second.defName);
|
||||
for(size_t n = 0; n < idToObstacle[obst[t].ID]->ourImages.size(); ++n)
|
||||
int ID = obst[t].ID;
|
||||
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
|
||||
std::vector<CObstacleInstance> obstacles = curInt->cb->battleGetAllObstacles();
|
||||
std::multimap<BattleHex, int> hexToObstacle;
|
||||
|
||||
for(size_t b = 0; b < obstacles.size(); ++b)
|
||||
{
|
||||
BattleHex position = CGI->heroh->obstacles.find(obstacles[b].ID)->second.getMaxBlocked(obstacles[b].pos);
|
||||
hexToObstacle.insert(std::make_pair(position, b));
|
||||
const CObstacleInstance &oi = obstacles[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...
|
||||
@ -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)
|
||||
{
|
||||
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
|
||||
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);
|
||||
//preparating background graphic with hexes and shaded hexes
|
||||
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())
|
||||
CSDL_Ext::blit8bppAlphaTo24bpp(cellBorders, NULL, backgroundWithHexes, NULL);
|
||||
|
||||
@ -3182,6 +3204,16 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber)
|
||||
}
|
||||
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"};
|
||||
|
||||
CBattleInterface::SiegeHelper::SiegeHelper(const CGTownInstance *siegeTown, const CBattleInterface * _owner)
|
||||
|
@ -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, CDefHandler * > idToProjectile; //projectiles of creatures (creatureID, defhandler)
|
||||
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>
|
||||
ui8 animCount;
|
||||
const CStack * activeStack; //number of active stack; NULL - no one
|
||||
@ -261,6 +262,7 @@ public:
|
||||
void hideQueue();
|
||||
void showQueue();
|
||||
PossibleActions selectionTypeByPositiveness(const CSpell & spell);
|
||||
Rect hexPosition(BattleHex hex) const;
|
||||
|
||||
void handleHex(BattleHex myNumber, int eventType);
|
||||
bool isCastingPossibleHere (const CStack * sactive, const CStack * shere, BattleHex myNumber);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -153,7 +153,7 @@ void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool atta
|
||||
//obstacles
|
||||
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)
|
||||
{
|
||||
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 )
|
||||
{
|
||||
CMP_stack cmpst;
|
||||
@ -1577,7 +1652,8 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const
|
||||
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
|
||||
if(stacks[g]->doubleWide() && stacks[g]->attackerOwned)
|
||||
@ -1667,61 +1743,96 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const
|
||||
}
|
||||
|
||||
//randomize obstacles
|
||||
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;
|
||||
std::vector<int> possibleObstacles;
|
||||
if(town == NULL && !creatureBank) //do it only when it's not siege and not creature bank
|
||||
{
|
||||
const int ABSOLUTE_OBSTACLES_COUNT = 34, USUAL_OBSTACLES_COUNT = 91; //shouldn't be changes if we want H3-like obstacle placement
|
||||
|
||||
for(int i=0; i<GameConstants::BFIELD_SIZE; ++i)
|
||||
{
|
||||
if(i%17 < 4 || i%17 > 12)
|
||||
{
|
||||
obAv[i] = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
obAv[i] = true;
|
||||
}
|
||||
}
|
||||
RandGen r;
|
||||
auto ourRand = [&]{ return r.rand(); };
|
||||
r.srand(tile);
|
||||
const int sound = r.rand(1,8); //battle sound ID to play... can't do anything with it here
|
||||
int tilesToBlock = r.rand(5,12);
|
||||
const int specialBattlefield = battlefieldTypeToBI(terType);
|
||||
|
||||
for(std::map<int, CObstacleInfo>::const_iterator g=VLC->heroh->obstacles.begin(); g!=VLC->heroh->obstacles.end(); ++g)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
std::vector<BattleHex> blockedTiles;
|
||||
|
||||
srand(time(NULL));
|
||||
if(possibleObstacles.size() > 0) //we cannot place any obstacles when we don't have them
|
||||
auto appropriateAbsoluteObstacle = [&](int id)
|
||||
{
|
||||
int toBlock = rand()%6 + 6; //how many hexes should be blocked by obstacles
|
||||
while(toBlock>0)
|
||||
return VLC->heroh->absoluteObstacles[id].isAppropriate(terrain, specialBattlefield);
|
||||
};
|
||||
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;
|
||||
coi.isAbsoluteObstacle = true;
|
||||
coi.ID = obidgen.getSuchNumber(appropriateAbsoluteObstacle);
|
||||
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);
|
||||
for(int b=0; b<block.size(); ++b)
|
||||
{
|
||||
if(block[b] >= 0 && block[b] < GameConstants::BFIELD_SIZE)
|
||||
obAv[block[b]] = false;
|
||||
}
|
||||
toBlock -= block.size();
|
||||
|
||||
BOOST_FOREACH(BattleHex blocked, coi.getBlocked())
|
||||
blockedTiles.push_back(blocked);
|
||||
tilesToBlock -= VLC->heroh->absoluteObstacles[coi.ID].blockedTiles.size() / 2;
|
||||
}
|
||||
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;
|
||||
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)
|
||||
coveredHexes.insert(blocked[w]);
|
||||
}
|
||||
@ -2415,6 +2526,21 @@ const CStack * BattleInfo::getStackIf(boost::function<bool(const CStack*)> pred)
|
||||
: *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)
|
||||
: base(Base), ID(I), owner(O), slot(S), attackerOwned(AO),
|
||||
counterAttacks(1)
|
||||
|
@ -155,6 +155,9 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode
|
||||
|
||||
int theOtherPlayer(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
|
||||
|
@ -915,7 +915,7 @@ void CStackInstance::deserializationFix()
|
||||
{
|
||||
const CCreature *backup = type;
|
||||
type = NULL;
|
||||
setType(backup);
|
||||
setType(backup);
|
||||
const CArmedInstance *armyBackup = _armyObj;
|
||||
_armyObj = NULL;
|
||||
setArmyObj(armyBackup);
|
||||
|
@ -1589,7 +1589,7 @@ void CGameState::init(StartInfo * si)
|
||||
}
|
||||
}
|
||||
|
||||
int CGameState::battleGetBattlefieldType(int3 tile)
|
||||
int CGameState::battleGetBattlefieldType(int3 tile) const
|
||||
{
|
||||
if(tile==int3() && curB)
|
||||
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)
|
||||
) //look only for objects covering given tile
|
||||
continue;
|
||||
|
||||
switch(objs[g]->ID)
|
||||
{
|
||||
case 222: //clover field
|
||||
case Obj::CLOVER_FIELD:
|
||||
return 19;
|
||||
case 21: case 223: //cursed ground
|
||||
case Obj::CURSED_GROUND1: case Obj::CURSED_GROUND2:
|
||||
return 22;
|
||||
case 224: //evil fog
|
||||
case Obj::EVIL_FOG:
|
||||
return 20;
|
||||
case 225: //favourable winds
|
||||
case Obj::FAVORABLE_WINDS:
|
||||
return 21;
|
||||
case 226: //fiery fields
|
||||
case Obj::FIERY_FIELDS:
|
||||
return 14;
|
||||
case 227: //holy ground
|
||||
case Obj::HOLY_GROUNDS:
|
||||
return 18;
|
||||
case 228: //lucid pools
|
||||
case Obj::LUCID_POOLS:
|
||||
return 17;
|
||||
case 229: //magic clouds
|
||||
case Obj::MAGIC_CLOUDS:
|
||||
return 16;
|
||||
case 46: case 230: //magic plains
|
||||
case Obj::MAGIC_PLAINS1: case Obj::MAGIC_PLAINS2:
|
||||
return 9;
|
||||
case 231: //rocklands
|
||||
case Obj::ROCKLANDS:
|
||||
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> > ret;
|
||||
|
@ -381,7 +381,7 @@ public:
|
||||
void giveHeroArtifact(CGHeroInstance *h, int aid);
|
||||
|
||||
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);
|
||||
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
|
||||
|
@ -6,13 +6,7 @@
|
||||
#include "../lib/JsonNode.h"
|
||||
#include "GameConstants.h"
|
||||
#include <boost/version.hpp>
|
||||
#if BOOST_VERSION >= 103800
|
||||
#include <boost/spirit/include/classic.hpp>
|
||||
#else
|
||||
#include <boost/spirit.hpp>
|
||||
#endif
|
||||
|
||||
using namespace boost::spirit;
|
||||
#include "BattleHex.h"
|
||||
|
||||
extern CLodHandler * bitmaph;
|
||||
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;
|
||||
}
|
||||
|
||||
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> ret;
|
||||
int cur = hex; //currently browsed hex
|
||||
int curBeg = hex; //beginning of current line
|
||||
for(int h=0; h<blockmap.size(); ++h)
|
||||
if(isAbsoluteObstacle)
|
||||
{
|
||||
switch(blockmap[h])
|
||||
{
|
||||
case 'X':
|
||||
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;
|
||||
}
|
||||
assert(!hex.isValid());
|
||||
range::copy(blockedTiles, std::back_inserter(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
BattleHex CObstacleInfo::getMaxBlocked(BattleHex hex) const
|
||||
bool CObstacleInfo::isAppropriate(int terrainType, int specialBattlefield /*= -1*/) const
|
||||
{
|
||||
std::vector<BattleHex> blocked = getBlocked(hex);
|
||||
return *std::max_element(blocked.begin(), blocked.end());
|
||||
if(specialBattlefield != -1)
|
||||
return vstd::contains(allowedSpecialBfields, specialBattlefield);
|
||||
|
||||
return vstd::contains(allowedTerrains, terrainType);
|
||||
}
|
||||
|
||||
CHeroHandler::~CHeroHandler()
|
||||
@ -141,21 +98,27 @@ CHeroHandler::CHeroHandler()
|
||||
|
||||
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");
|
||||
|
||||
BOOST_FOREACH(const JsonNode &obs, config["obstacles"].Vector()) {
|
||||
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;
|
||||
}
|
||||
loadObstacles(config["obstacles"], false, obstacles);
|
||||
loadObstacles(config["absoluteObstacles"], true, absoluteObstacles);
|
||||
}
|
||||
|
||||
void CHeroHandler::loadPuzzleInfo()
|
||||
@ -164,12 +127,12 @@ void CHeroHandler::loadPuzzleInfo()
|
||||
|
||||
int faction = 0;
|
||||
|
||||
BOOST_FOREACH(const JsonNode &puzzle, config["puzzles"].Vector()) {
|
||||
|
||||
BOOST_FOREACH(const JsonNode &puzzle, config["puzzles"].Vector())
|
||||
{
|
||||
int idx = 0;
|
||||
|
||||
BOOST_FOREACH(const JsonNode &piece, puzzle.Vector()) {
|
||||
|
||||
BOOST_FOREACH(const JsonNode &piece, puzzle.Vector())
|
||||
{
|
||||
SPuzzleInfo spi;
|
||||
|
||||
spi.x = piece["x"].Float();
|
||||
|
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "BattleHex.h"
|
||||
#include "../lib/ConstTransitivePtr.h"
|
||||
#include "GameConstants.h"
|
||||
|
||||
@ -18,6 +17,7 @@ class CHeroClass;
|
||||
class CDefHandler;
|
||||
class CGameInfo;
|
||||
class CGHeroInstance;
|
||||
struct BattleHex;
|
||||
|
||||
struct SSpecialtyInfo
|
||||
{ si32 type;
|
||||
@ -85,22 +85,22 @@ public:
|
||||
|
||||
struct DLL_LINKAGE CObstacleInfo
|
||||
{
|
||||
int ID;
|
||||
std::string defName,
|
||||
blockmap, //blockmap: X - blocked, N - not blocked, L - description goes to the next line, staring with the left bottom hex
|
||||
allowedTerrains; /*terrains[i]: 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*/
|
||||
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
|
||||
si32 ID;
|
||||
std::string defName;
|
||||
std::vector<ui8> allowedTerrains;
|
||||
std::vector<ui8> allowedSpecialBfields;
|
||||
|
||||
ui8 isAbsoluteObstacle; //there may only one such obstacle in battle and its position is always the same
|
||||
si32 width, height; //how much space to the right and up is needed to place obstacle (affects only placement algorithm)
|
||||
std::vector<si16> blockedTiles; //offsets relative to obstacle position (that is its left bottom corner)
|
||||
|
||||
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)
|
||||
{
|
||||
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::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
|
||||
|
||||
void loadObstacles(); //loads info about obstacles
|
||||
@ -159,7 +160,7 @@ public:
|
||||
|
||||
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)
|
||||
{
|
||||
//restore class pointers
|
||||
|
@ -45,9 +45,9 @@ void CMapInfo::mapInit(const std::string &fname, const ui8 *map )
|
||||
mapHeader->initFromMemory(map, i);
|
||||
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;
|
||||
mapHeader = NULL;
|
||||
}
|
||||
|
@ -1,12 +1,23 @@
|
||||
#pragma once
|
||||
#include "BattleHex.h"
|
||||
|
||||
struct CObstacleInfo;
|
||||
|
||||
struct DLL_LINKAGE CObstacleInstance
|
||||
{
|
||||
int uniqueID;
|
||||
int ID; //ID of obstacle (defines type of it)
|
||||
int pos; //position on battlefield
|
||||
si32 uniqueID;
|
||||
si32 ID; //ID of obstacle (defines type of it)
|
||||
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)
|
||||
{
|
||||
h & ID & pos & uniqueID;
|
||||
h & ID & pos & isAbsoluteObstacle & uniqueID;
|
||||
}
|
||||
};
|
@ -187,6 +187,7 @@ namespace Obj
|
||||
BOAT = 8,
|
||||
CREATURE_BANK = 16,
|
||||
CREATURE_GENERATOR1 = 17,
|
||||
CURSED_GROUND1 = 21,
|
||||
DERELICT_SHIP = 24,
|
||||
DRAGON_UTOPIA = 25,
|
||||
GARRISON = 33,
|
||||
@ -194,6 +195,7 @@ namespace Obj
|
||||
MONOLITH1 = 43,
|
||||
MONOLITH2 = 44,
|
||||
MONOLITH3 = 45,
|
||||
MAGIC_PLAINS1 = 46,
|
||||
SCHOOL_OF_MAGIC = 47,
|
||||
MINE = 53,
|
||||
MONSTER = 54,
|
||||
@ -209,6 +211,35 @@ namespace Obj
|
||||
WHIRLPOOL = 111,
|
||||
BORDER_GATE = 212,
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,19 @@ public:
|
||||
const JsonVector & Vector() 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
|
||||
JsonNode & operator[](std::string child);
|
||||
const JsonNode & operator[](std::string child) const;
|
||||
|
@ -239,6 +239,7 @@
|
||||
<ClCompile Include="CLogger.cpp" />
|
||||
<ClCompile Include="CMapInfo.cpp" />
|
||||
<ClCompile Include="CObjectHandler.cpp" />
|
||||
<ClCompile Include="CObstacleInstance.cpp" />
|
||||
<ClCompile Include="Connection.cpp" />
|
||||
<ClCompile Include="CSpellHandler.cpp" />
|
||||
<ClCompile Include="CThreadHelper.cpp" />
|
||||
|
@ -3884,17 +3884,16 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
|
||||
case Spells::REMOVE_OBSTACLE:
|
||||
{
|
||||
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(blockedHexes, destination)) //this obstacle covers given hex
|
||||
{
|
||||
obr.obstacles.insert(gs->curB->obstacles[g].uniqueID);
|
||||
}
|
||||
if(vstd::contains(obstacle.getBlocked(), destination))
|
||||
obr.obstacles.insert(obstacle.uniqueID);
|
||||
}
|
||||
|
||||
if(!obr.obstacles.empty())
|
||||
sendAndApply(&obr);
|
||||
else
|
||||
complain("There's no obstacle to remove!");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
Loading…
x
Reference in New Issue
Block a user