mirror of
https://github.com/vcmi/vcmi.git
synced 2025-07-01 00:45:26 +02:00
* redone stack queue algorithm
* recalculating paths only after finished movement / switching selection * moving hero uses "new" pathfinder * moving hero by arrow keys / numpad * VCMI window should start centered * fixed pairing Subterranean Gates * fixed issues with creatures sounds after loading * several minor changes and improvements
This commit is contained in:
@ -90,6 +90,61 @@ public:
|
||||
|
||||
} *applierGs = NULL;
|
||||
|
||||
class IObjectCaller
|
||||
{
|
||||
public:
|
||||
virtual void preInit()=0;
|
||||
virtual void postInit()=0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class CObjectCaller : public IObjectCaller
|
||||
{
|
||||
public:
|
||||
void preInit()
|
||||
{
|
||||
T::preInit();
|
||||
}
|
||||
void postInit()
|
||||
{
|
||||
T::postInit();
|
||||
}
|
||||
};
|
||||
|
||||
class CObjectCallersHandler
|
||||
{
|
||||
public:
|
||||
std::vector<IObjectCaller*> apps;
|
||||
|
||||
template<typename T> void registerType(const T * t=NULL)
|
||||
{
|
||||
apps.push_back(new CObjectCaller<T>);
|
||||
}
|
||||
|
||||
CObjectCallersHandler()
|
||||
{
|
||||
registerTypes1(*this);
|
||||
}
|
||||
|
||||
~CObjectCallersHandler()
|
||||
{
|
||||
for (size_t i = 0; i < apps.size(); i++)
|
||||
delete apps[i];
|
||||
}
|
||||
|
||||
void preInit()
|
||||
{
|
||||
for (size_t i = 0; i < apps.size(); i++)
|
||||
apps[i]->preInit();
|
||||
}
|
||||
|
||||
void postInit()
|
||||
{
|
||||
for (size_t i = 0; i < apps.size(); i++)
|
||||
apps[i]->postInit();
|
||||
}
|
||||
} *objCaller = NULL;
|
||||
|
||||
void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst) const
|
||||
{
|
||||
int type = txt.first, ser = txt.second;
|
||||
@ -252,12 +307,7 @@ CStack * BattleInfo::getStack(int stackID, bool onlyAlive)
|
||||
|
||||
const CStack * BattleInfo::getStack(int stackID, bool onlyAlive) const
|
||||
{
|
||||
for(unsigned int g=0; g<stacks.size(); ++g)
|
||||
{
|
||||
if(stacks[g]->ID == stackID && (!onlyAlive || stacks[g]->alive()))
|
||||
return stacks[g];
|
||||
}
|
||||
return NULL;
|
||||
return const_cast<BattleInfo * const>(this)->getStack(stackID, onlyAlive);
|
||||
}
|
||||
|
||||
CStack * BattleInfo::getStackT(int tileID, bool onlyAlive)
|
||||
@ -279,19 +329,7 @@ CStack * BattleInfo::getStackT(int tileID, bool onlyAlive)
|
||||
|
||||
const CStack * BattleInfo::getStackT(int tileID, bool onlyAlive) const
|
||||
{
|
||||
for(unsigned int g=0; g<stacks.size(); ++g)
|
||||
{
|
||||
if(stacks[g]->position == tileID
|
||||
|| (stacks[g]->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && stacks[g]->attackerOwned && stacks[g]->position-1 == tileID)
|
||||
|| (stacks[g]->hasFeatureOfType(StackFeature::DOUBLE_WIDE) && !stacks[g]->attackerOwned && stacks[g]->position+1 == tileID))
|
||||
{
|
||||
if(!onlyAlive || stacks[g]->alive())
|
||||
{
|
||||
return stacks[g];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
return const_cast<BattleInfo * const>(this)->getStackT(tileID, onlyAlive);
|
||||
}
|
||||
|
||||
void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<int> & occupyable, bool flying, int stackToOmmit) const
|
||||
@ -749,14 +787,24 @@ ui16 CStack::MaxHealth() const
|
||||
return creature->hitPoints + valOfFeatures(StackFeature::HP_BONUS);
|
||||
}
|
||||
|
||||
bool CStack::willMove()
|
||||
bool CStack::willMove() const
|
||||
{
|
||||
return !vstd::contains(state, DEFENDING)
|
||||
&& !vstd::contains(state, MOVED)
|
||||
&& alive()
|
||||
&& !moved()
|
||||
&& canMove();
|
||||
}
|
||||
|
||||
bool CStack::canMove() const
|
||||
{
|
||||
return alive()
|
||||
&& ! hasFeatureOfType(StackFeature::NOT_ACTIVE); //eg. Ammo Cart
|
||||
}
|
||||
|
||||
bool CStack::moved() const
|
||||
{
|
||||
return vstd::contains(state, MOVED);
|
||||
}
|
||||
|
||||
CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, int player, const CTown *town, std::map<ui32,CGHeroInstance *> &available) const
|
||||
{
|
||||
CGHeroInstance *ret = NULL;
|
||||
@ -1105,6 +1153,7 @@ CGameState::CGameState()
|
||||
curB = NULL;
|
||||
scenarioOps = NULL;
|
||||
applierGs = new CGSApplier;
|
||||
objCaller = new CObjectCallersHandler;
|
||||
}
|
||||
CGameState::~CGameState()
|
||||
{
|
||||
@ -1113,6 +1162,7 @@ CGameState::~CGameState()
|
||||
delete curB;
|
||||
delete scenarioOps;
|
||||
delete applierGs;
|
||||
delete objCaller;
|
||||
}
|
||||
void CGameState::init(StartInfo * si, Mapa * map, int Seed)
|
||||
{
|
||||
@ -1456,12 +1506,14 @@ void CGameState::init(StartInfo * si, Mapa * map, int Seed)
|
||||
map->defy[i]->serial = i;
|
||||
}
|
||||
|
||||
objCaller->preInit();
|
||||
for(unsigned int i=0; i<map->objects.size(); i++)
|
||||
{
|
||||
map->objects[i]->initObj();
|
||||
if(map->objects[i]->ID == 62) //prison also needs to initialize hero
|
||||
static_cast<CGHeroInstance*>(map->objects[i])->initHero();
|
||||
}
|
||||
objCaller->postInit();
|
||||
}
|
||||
|
||||
bool CGameState::battleShootCreatureStack(int ID, int dest)
|
||||
@ -1915,11 +1967,15 @@ bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath
|
||||
|
||||
void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src, int movement)
|
||||
{
|
||||
assert(hero);
|
||||
if(src.x < 0)
|
||||
src = hero->getPosition(false);
|
||||
if(movement < 0)
|
||||
movement = hero->movement;
|
||||
|
||||
out.hero = hero;
|
||||
out.hpos = src;
|
||||
|
||||
if(!map->isInTheMap(src)/* || !map->isInTheMap(dest)*/) //check input
|
||||
{
|
||||
tlog1 << "CGameState::calculatePaths: Hero outside the map? How dare you...\n";
|
||||
@ -1953,6 +2009,7 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int
|
||||
node.coord.y = j;
|
||||
node.coord.z = k;
|
||||
node.land = tinfo->tertype != TerrainTile::water;
|
||||
node.theNodeBefore = NULL;
|
||||
|
||||
if ( tinfo->tertype == TerrainTile::rock//it's rock
|
||||
|| onLand && !node.land //it's sea and we cannot walk on sea
|
||||
@ -1988,7 +2045,7 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int
|
||||
else if(!onLand && tinfo->tertype != TerrainTile::water) //hero is moving by water
|
||||
{
|
||||
if((tinfo->siodmyTajemniczyBajt & 64) && !tinfo->blocked)
|
||||
node.accessible = CGPathNode::ACCESSIBLE; //tile is accessible if it's coastal and not blocked
|
||||
node.accessible = CGPathNode::VISITABLE; //tile is accessible if it's coastal and not blocked
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2213,7 +2270,7 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange(const CStack* attacker, cons
|
||||
affectedIds.push_back(151); //diamond dragon
|
||||
affectedIds.push_back(154); //blood dragon
|
||||
affectedIds.push_back(155); //darkness dragon
|
||||
affectedIds.push_back(156); //ghost behemot
|
||||
affectedIds.push_back(156); //ghost behemoth
|
||||
affectedIds.push_back(157); //hell hydra
|
||||
break;
|
||||
}
|
||||
@ -2412,17 +2469,17 @@ si8 CGameState::battleMaxSpellLevel()
|
||||
std::set<CStack*> BattleInfo::getAttackedCreatures(const CSpell * s, const CGHeroInstance * caster, int destinationTile)
|
||||
{
|
||||
std::set<ui16> attackedHexes = s->rangeInHexes(destinationTile, caster->getSpellSchoolLevel(s));
|
||||
std::set<CStack*> attackedCres; /*std::set to exclude multiple occurences of two hex creatures*/
|
||||
std::set<CStack*> attackedCres; /*std::set to exclude multiple occurrences of two hex creatures*/
|
||||
|
||||
bool onlyAlive = s->id != 38 && s->id != 39; //when casting resurrection or animate dead we should be allow to select dead stack
|
||||
|
||||
if(s->id == 24 || s->id == 25 || s->id == 26) //death ripple, destroy undead and armageddon
|
||||
if(s->id == 24 || s->id == 25 || s->id == 26) //death ripple, destroy undead and Armageddon
|
||||
{
|
||||
for(int it=0; it<stacks.size(); ++it)
|
||||
{
|
||||
if((s->id == 24 && !stacks[it]->creature->isUndead()) //death ripple
|
||||
|| (s->id == 25 && stacks[it]->creature->isUndead()) //destroy undead
|
||||
|| (s->id == 26) //armageddon
|
||||
|| (s->id == 26) //Armageddon
|
||||
)
|
||||
{
|
||||
attackedCres.insert(stacks[it]);
|
||||
@ -2641,92 +2698,150 @@ bool CGameState::battleCanShoot(int ID, int dest)
|
||||
return false;
|
||||
}
|
||||
|
||||
CStack * BattleInfo::getNextStack()
|
||||
const CStack * BattleInfo::getNextStack() const
|
||||
{
|
||||
CStack *current = getStack(activeStack);
|
||||
for (unsigned int i = 0; i < stacks.size(); i++) //find fastest not moved/waited stack (stacks vector is sorted by speed)
|
||||
{
|
||||
if(stacks[i]->willMove() && !vstd::contains(stacks[i]->state,WAITING))
|
||||
return stacks[i];
|
||||
}
|
||||
for (int i = stacks.size() - 1; i >= 0 ; i--) //find slowest waiting stack
|
||||
{
|
||||
if(stacks[i]->willMove())
|
||||
return stacks[i];
|
||||
}
|
||||
return NULL; //all stacks moved or defending!
|
||||
std::vector<const CStack *> hlp;
|
||||
getStackQueue(hlp, 1, 2);
|
||||
|
||||
if(hlp.size())
|
||||
return hlp[0];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::vector<CStack> BattleInfo::getStackQueue()
|
||||
static const CStack *takeStack(std::vector<const CStack *> &st, int &curside)
|
||||
{
|
||||
std::vector<CStack> ret;
|
||||
std::vector<int> taken; //if non-zero value, corresponding stack has been placed in ret
|
||||
taken.resize(stacks.size());
|
||||
for(unsigned int g=0; g<taken.size(); ++g)
|
||||
{
|
||||
taken[g] = 0;
|
||||
}
|
||||
const CStack *ret = NULL;
|
||||
unsigned i, //fastest stack
|
||||
j; //fastest stack of the other side
|
||||
for(i = 0; i < st.size(); i++)
|
||||
if(st[i])
|
||||
break;
|
||||
|
||||
for(int moved=0; moved<2; ++moved) //in first cycle we add stacks that can act in current turn, in second one the rest of them
|
||||
//no stacks left
|
||||
if(i == st.size())
|
||||
return NULL;
|
||||
|
||||
const CStack *fastest = st[i], *other = NULL;
|
||||
int bestSpeed = fastest->Speed();
|
||||
|
||||
if(fastest->attackerOwned != curside)
|
||||
{
|
||||
for(unsigned int gc=0; gc<stacks.size(); ++gc)
|
||||
ret = fastest;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(j = i + 1; j < st.size(); j++)
|
||||
{
|
||||
int id = -1, speed = -1;
|
||||
for(unsigned int i=0; i<stacks.size(); ++i) //find not waited stacks only
|
||||
{
|
||||
if((moved == 1 ||!vstd::contains(stacks[i]->state, DEFENDING))
|
||||
&& stacks[i]->alive()
|
||||
&& (moved == 1 || !vstd::contains(stacks[i]->state, MOVED))
|
||||
&& !vstd::contains(stacks[i]->state,WAITING)
|
||||
&& taken[i]==0
|
||||
&& !stacks[i]->hasFeatureOfType(StackFeature::NOT_ACTIVE)) //eg. Ammo Cart
|
||||
{
|
||||
if(speed == -1 || stacks[i]->Speed() > speed)
|
||||
{
|
||||
id = i;
|
||||
speed = stacks[i]->Speed();
|
||||
}
|
||||
}
|
||||
}
|
||||
if(id != -1)
|
||||
{
|
||||
ret.push_back(*stacks[id]);
|
||||
taken[id] = 1;
|
||||
}
|
||||
else //choose something from not moved stacks
|
||||
{
|
||||
int id = -1, speed = 10000; //infinite speed
|
||||
for(unsigned int i=0; i<stacks.size(); ++i) //find waited stacks only
|
||||
{
|
||||
if((moved == 1 ||!vstd::contains(stacks[i]->state, DEFENDING))
|
||||
&& stacks[i]->alive()
|
||||
&& (moved == 1 || !vstd::contains(stacks[i]->state, MOVED))
|
||||
&& vstd::contains(stacks[i]->state,WAITING)
|
||||
&& taken[i]==0
|
||||
&& !stacks[i]->hasFeatureOfType(StackFeature::NOT_ACTIVE)) //eg. Ammo Cart
|
||||
{
|
||||
if(stacks[i]->Speed() < speed) //slowest one
|
||||
{
|
||||
id = i;
|
||||
speed = stacks[i]->Speed();
|
||||
}
|
||||
}
|
||||
}
|
||||
if(id != -1)
|
||||
{
|
||||
ret.push_back(*stacks[id]);
|
||||
taken[id] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
break; //no stacks have been found, so none of them will be found in next iterations
|
||||
}
|
||||
}
|
||||
if(!st[j]) continue;
|
||||
if(st[j]->attackerOwned != curside || st[j]->Speed() != bestSpeed)
|
||||
break;
|
||||
}
|
||||
|
||||
if(j >= st.size())
|
||||
{
|
||||
ret = fastest;
|
||||
}
|
||||
else
|
||||
{
|
||||
other = st[j];
|
||||
if(other->Speed() != bestSpeed)
|
||||
ret = fastest;
|
||||
else
|
||||
ret = other;
|
||||
}
|
||||
}
|
||||
|
||||
assert(ret);
|
||||
if(ret == fastest)
|
||||
st[i] = NULL;
|
||||
else
|
||||
st[j] = NULL;
|
||||
|
||||
curside = ret->attackerOwned;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void BattleInfo::getStackQueue( std::vector<const CStack *> &out, int howMany, int mode, int lastMoved ) const
|
||||
{
|
||||
//we'll split creatures with remaining movement to 4 parts
|
||||
std::vector<const CStack *> phase[4]; //0 - turrets/catapult, 1 - normal (unmoved) creatures, other war machines, 2 - waited cres that had morale, 3 - rest of waited cres
|
||||
int toMove = 0; //how many stacks still has move
|
||||
|
||||
for(unsigned int i=0; i<stacks.size(); ++i)
|
||||
{
|
||||
const CStack * const s = stacks[i];
|
||||
if(mode != 1 && s->willMove()
|
||||
|| mode == 1 && s->canMove())
|
||||
{
|
||||
int p = -1; //in which phase this tack will move?
|
||||
if(!mode && vstd::contains(s->state, WAITING))
|
||||
{
|
||||
if(vstd::contains(s->state, HAD_MORALE))
|
||||
p = 2;
|
||||
else
|
||||
p = 3;
|
||||
}
|
||||
else if(vstd::contains(s->state, StackFeature::SIEGE_WEAPON) //catapult and turrets are first
|
||||
&& (s->creature->idNumber == 145 || s->creature->idNumber == 149))
|
||||
{
|
||||
p = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
p = 1;
|
||||
}
|
||||
|
||||
phase[p].push_back(s);
|
||||
toMove++;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
std::sort(phase[i].begin(), phase[i].end(), CMP_stack(i));
|
||||
|
||||
for(size_t i = 0; i < phase[0].size() && i < howMany; i++)
|
||||
out.push_back(phase[0][i]);
|
||||
|
||||
if(out.size() == howMany)
|
||||
return;
|
||||
|
||||
if(lastMoved == -1)
|
||||
{
|
||||
const CStack *current = getStack(activeStack);
|
||||
if(current)
|
||||
{
|
||||
lastMoved = !current->attackerOwned;
|
||||
if(!current->willMove() || mode == 2)
|
||||
lastMoved = !lastMoved;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastMoved = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int pi = 1;
|
||||
while(out.size() < howMany)
|
||||
{
|
||||
const CStack *hlp = takeStack(phase[pi], lastMoved);
|
||||
if(!hlp)
|
||||
{
|
||||
pi++;
|
||||
if(pi > 3)
|
||||
{
|
||||
if(mode != 2)
|
||||
getStackQueue(out, howMany, 1, lastMoved);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out.push_back(hlp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int3 CPath::startPos() const
|
||||
{
|
||||
return nodes[nodes.size()-1].coord;
|
||||
@ -2764,7 +2879,7 @@ bool CPathsInfo::getPath( const int3 &dst, CGPath &out )
|
||||
if(!curnode->theNodeBefore)
|
||||
return false;
|
||||
|
||||
while(curnode->theNodeBefore)
|
||||
while(curnode)
|
||||
{
|
||||
out.nodes.push_back(*curnode);
|
||||
curnode = curnode->theNodeBefore;
|
||||
@ -2797,4 +2912,68 @@ CPathsInfo::~CPathsInfo()
|
||||
delete [] nodes[i];
|
||||
}
|
||||
delete [] nodes;
|
||||
}
|
||||
|
||||
int3 CGPath::startPos() const
|
||||
{
|
||||
return nodes[nodes.size()-1].coord;
|
||||
}
|
||||
|
||||
int3 CGPath::endPos() const
|
||||
{
|
||||
return nodes[0].coord;
|
||||
}
|
||||
|
||||
void CGPath::convert( ui8 mode )
|
||||
{
|
||||
if(mode==0)
|
||||
{
|
||||
for(unsigned int i=0;i<nodes.size();i++)
|
||||
{
|
||||
nodes[i].coord = CGHeroInstance::convertPosition(nodes[i].coord,true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CMP_stack::operator()( const CStack* a, const CStack* b )
|
||||
{
|
||||
switch(phase)
|
||||
{
|
||||
case 0: //catapult moves after turrets
|
||||
return a->creature->idNumber < b->creature->idNumber; //catapult is 145 and turrets are 149
|
||||
//TODO? turrets order
|
||||
case 1: //fastest first, upper slot first
|
||||
{
|
||||
int as = a->Speed(), bs = b->Speed();
|
||||
if(as != bs)
|
||||
return as > bs;
|
||||
else
|
||||
return a->slot < b->slot;
|
||||
}
|
||||
case 2: //fastest last, upper slot first
|
||||
//TODO: should be replaced with order of receiving morale!
|
||||
case 3: //fastest last, upper slot first
|
||||
{
|
||||
int as = a->Speed(), bs = b->Speed();
|
||||
if(as != bs)
|
||||
return as < bs;
|
||||
else
|
||||
return a->slot < b->slot;
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
CMP_stack::CMP_stack( int Phase /*= 1*/ )
|
||||
{
|
||||
phase = Phase;
|
||||
}
|
||||
|
||||
PlayerState::PlayerState()
|
||||
: color(-1), currentSelection(0xffffffff)
|
||||
{
|
||||
|
||||
}
|
Reference in New Issue
Block a user