mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Updated CGTeleport and new CGMonolith / CGSubterraneanGate / CGWhirlpool
Now CGTeleport is not publicly available handler, but generic class for teleport channels usage. Teleport channels are stored as part of information about the map.
This commit is contained in:
parent
04a1df29ad
commit
c9eba40fe6
@ -1867,7 +1867,7 @@ void CGameState::initMapObjects()
|
||||
}
|
||||
}
|
||||
}
|
||||
CGTeleport::postInit(); //pairing subterranean gates
|
||||
CGSubterraneanGate::postInit(gs); //pairing subterranean gates
|
||||
|
||||
map->calculateGuardingGreaturePositions(); //calculate once again when all the guards are placed and initialized
|
||||
}
|
||||
|
@ -83,7 +83,9 @@ CObjectClassesHandler::CObjectClassesHandler()
|
||||
SET_HANDLER("shrine", CGShrine);
|
||||
SET_HANDLER("sign", CGSignBottle);
|
||||
SET_HANDLER("siren", CGSirens);
|
||||
SET_HANDLER("teleport", CGTeleport);
|
||||
SET_HANDLER("monolith", CGMonolith);
|
||||
SET_HANDLER("subterraneanGate", CGSubterraneanGate);
|
||||
SET_HANDLER("whirlpool", CGWhirlpool);
|
||||
SET_HANDLER("university", CGUniversity);
|
||||
SET_HANDLER("oncePerHero", CGVisitableOPH);
|
||||
SET_HANDLER("oncePerWeek", CGVisitableOPW);
|
||||
|
@ -21,8 +21,6 @@
|
||||
#include "../IGameCallback.h"
|
||||
#include "../CGameState.h"
|
||||
|
||||
std::map<Obj, std::map<int, std::vector<ObjectInstanceID> > > CGTeleport::objs;
|
||||
std::vector<std::pair<ObjectInstanceID, ObjectInstanceID> > CGTeleport::gates;
|
||||
std::map <si32, std::vector<ObjectInstanceID> > CGMagi::eyelist;
|
||||
ui8 CGObelisk::obeliskCount; //how many obelisks are on map
|
||||
std::map<TeamID, ui8> CGObelisk::visited; //map: team_id => how many obelisks has been visited
|
||||
@ -743,135 +741,272 @@ void CGResource::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer)
|
||||
cb->startBattleI(hero, this);
|
||||
}
|
||||
|
||||
void CGTeleport::onHeroVisit( const CGHeroInstance * h ) const
|
||||
CGTeleport::CGTeleport() :
|
||||
type(UNKNOWN), channel(TeleportChannelID())
|
||||
{
|
||||
ObjectInstanceID destinationid;
|
||||
switch(ID)
|
||||
}
|
||||
|
||||
bool CGTeleport::isEntrance() const
|
||||
{
|
||||
return type == BOTH || type == ENTRANCE;
|
||||
}
|
||||
|
||||
bool CGTeleport::isExit() const
|
||||
{
|
||||
return type == BOTH || type == EXIT;
|
||||
}
|
||||
|
||||
bool CGTeleport::isChannelEntrance(ObjectInstanceID id) const
|
||||
{
|
||||
if(vstd::contains(getAllEntrances(), id))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CGTeleport::isChannelExit(ObjectInstanceID id) const
|
||||
{
|
||||
if(vstd::contains(getAllExits(), id))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<ObjectInstanceID> CGTeleport::getAllEntrances(bool excludeCurrent) const
|
||||
{
|
||||
std::vector<ObjectInstanceID> ret = cb->getTeleportChannelEntraces(channel);
|
||||
if(excludeCurrent)
|
||||
ret.erase(std::remove(ret.begin(), ret.end(), id), ret.end());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<ObjectInstanceID> CGTeleport::getAllExits(bool excludeCurrent) const
|
||||
{
|
||||
std::vector<ObjectInstanceID> ret = cb->getTeleportChannelExits(channel);
|
||||
if(excludeCurrent)
|
||||
ret.erase(std::remove(ret.begin(), ret.end(), id), ret.end());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ObjectInstanceID CGTeleport::getRandomExit(const CGHeroInstance * h) const
|
||||
{
|
||||
auto passableExits = getPassableExits(cb->gameState(), h, getAllExits(true));
|
||||
if(passableExits.size())
|
||||
return *RandomGeneratorUtil::nextItem(passableExits, cb->gameState()->getRandomGenerator());
|
||||
|
||||
return ObjectInstanceID();
|
||||
}
|
||||
|
||||
bool CGTeleport::isTeleport(const CGObjectInstance * obj)
|
||||
{
|
||||
auto teleportObj = dynamic_cast<const CGTeleport *>(obj);
|
||||
if(teleportObj)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CGTeleport::isConnected(const CGTeleport * src, const CGTeleport * dst)
|
||||
{
|
||||
if(src && dst && src->isChannelExit(dst->id) && src != dst)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CGTeleport::isConnected(const CGObjectInstance * src, const CGObjectInstance * dst)
|
||||
{
|
||||
auto srcObj = dynamic_cast<const CGTeleport *>(src);
|
||||
auto dstObj = dynamic_cast<const CGTeleport *>(dst);
|
||||
return isConnected(srcObj, dstObj);
|
||||
}
|
||||
|
||||
bool CGTeleport::isExitPassable(CGameState * gs, const CGHeroInstance * h, const CGObjectInstance * obj)
|
||||
{
|
||||
auto objTopVisObj = gs->map->getTile(obj->visitablePos()).topVisitableObj();
|
||||
if(objTopVisObj->ID == Obj::HERO)
|
||||
{
|
||||
case Obj::MONOLITH_ONE_WAY_ENTRANCE: //one way - find corresponding exit monolith
|
||||
if(h->id == objTopVisObj->id) // Just to be sure it's won't happen.
|
||||
return false;
|
||||
|
||||
// Check if it's friendly hero or not
|
||||
if(gs->getPlayerRelations(h->tempOwner, objTopVisObj->tempOwner))
|
||||
{
|
||||
if(vstd::contains(objs,Obj::MONOLITH_ONE_WAY_EXIT) && vstd::contains(objs[Obj::MONOLITH_ONE_WAY_EXIT],subID) && objs[Obj::MONOLITH_ONE_WAY_EXIT][subID].size())
|
||||
// Exchange between heroes only possible via subterranean gates
|
||||
if(!dynamic_cast<const CGSubterraneanGate *>(obj))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<ObjectInstanceID> CGTeleport::getPassableExits(CGameState * gs, const CGHeroInstance * h, std::vector<ObjectInstanceID> exits)
|
||||
{
|
||||
vstd::erase_if(exits, [&](ObjectInstanceID exit) -> bool
|
||||
{
|
||||
destinationid = *RandomGeneratorUtil::nextItem(objs[Obj::MONOLITH_ONE_WAY_EXIT][subID], cb->gameState()->getRandomGenerator());
|
||||
return !isExitPassable(gs, h, gs->getObj(exit));
|
||||
});
|
||||
return exits;
|
||||
}
|
||||
|
||||
void CGTeleport::addToChannel(std::map<TeleportChannelID, shared_ptr<TeleportChannel> > &channelsList, const CGTeleport * obj)
|
||||
{
|
||||
shared_ptr<TeleportChannel> tc;
|
||||
if(channelsList.find(obj->channel) == channelsList.end())
|
||||
{
|
||||
tc = make_shared<TeleportChannel>();
|
||||
channelsList.insert(std::make_pair(obj->channel, tc));
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->warnStream() << "Cannot find corresponding exit monolith for "<< id;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Obj::MONOLITH_TWO_WAY://two way monolith - pick any other one
|
||||
case Obj::WHIRLPOOL: //Whirlpool
|
||||
if(vstd::contains(objs,ID) && vstd::contains(objs[ID],subID) && objs[ID][subID].size()>1)
|
||||
{
|
||||
//choose another exit
|
||||
do
|
||||
{
|
||||
destinationid = *RandomGeneratorUtil::nextItem(objs[ID][subID], cb->gameState()->getRandomGenerator());
|
||||
} while(destinationid == id);
|
||||
tc = channelsList[obj->channel];
|
||||
|
||||
if (ID == Obj::WHIRLPOOL)
|
||||
{
|
||||
if (!h->hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION))
|
||||
{
|
||||
if (h->Slots().size() > 1 || h->Slots().begin()->second->count > 1)
|
||||
{ //we can't remove last unit
|
||||
SlotID targetstack = h->Slots().begin()->first; //slot numbers may vary
|
||||
for(auto i = h->Slots().rbegin(); i != h->Slots().rend(); i++)
|
||||
{
|
||||
if (h->getPower(targetstack) > h->getPower(i->first))
|
||||
{
|
||||
targetstack = (i->first);
|
||||
}
|
||||
}
|
||||
if(obj->isEntrance() && !vstd::contains(tc->entrances, obj->id))
|
||||
tc->entrances.push_back(obj->id);
|
||||
|
||||
TQuantity countToTake = h->getStackCount(targetstack) * 0.5;
|
||||
vstd::amax(countToTake, 1);
|
||||
if(obj->isExit() && !vstd::contains(tc->exits, obj->id))
|
||||
tc->exits.push_back(obj->id);
|
||||
|
||||
if(tc->entrances.size() && tc->exits.size()
|
||||
&& (tc->entrances.size() != 1 || tc->entrances != tc->exits))
|
||||
{
|
||||
tc->passability = TeleportChannel::PASSABLE;
|
||||
}
|
||||
}
|
||||
|
||||
InfoWindow iw;
|
||||
iw.player = h->tempOwner;
|
||||
iw.text.addTxt (MetaString::ADVOB_TXT, 168);
|
||||
iw.components.push_back (Component(CStackBasicDescriptor(h->getCreature(targetstack), countToTake)));
|
||||
cb->showInfoDialog(&iw);
|
||||
cb->changeStackCount(StackLocation(h, targetstack), -countToTake);
|
||||
}
|
||||
}
|
||||
TeleportChannelID CGMonolith::findMeChannel(std::vector<Obj> IDs, int SubID) const
|
||||
{
|
||||
for(auto obj : cb->gameState()->map->objects)
|
||||
{
|
||||
auto teleportObj = dynamic_cast<const CGTeleport *>(cb->getObj(obj->id));
|
||||
if(teleportObj && vstd::contains(IDs, teleportObj->ID) && teleportObj->subID == SubID)
|
||||
return teleportObj->channel;
|
||||
}
|
||||
return TeleportChannelID();
|
||||
}
|
||||
|
||||
void CGMonolith::onHeroVisit( const CGHeroInstance * h ) const
|
||||
{
|
||||
TeleportDialog td(h, channel);
|
||||
if(isEntrance())
|
||||
{
|
||||
if(ETeleportChannelType::BIDIRECTIONAL == cb->getTeleportChannelType(channel)
|
||||
&& cb->getTeleportChannelExits(channel).size() > 1)
|
||||
{
|
||||
td.exits = cb->getTeleportChannelExits(channel);
|
||||
}
|
||||
else
|
||||
logGlobal->warnStream() << "Cannot find corresponding exit monolith for "<< id;
|
||||
break;
|
||||
case Obj::SUBTERRANEAN_GATE: //find nearest subterranean gate on the other level
|
||||
td.exits.push_back(getRandomExit(h));
|
||||
|
||||
if(ETeleportChannelType::IMPASSABLE == cb->getTeleportChannelType(channel))
|
||||
{
|
||||
destinationid = getMatchingGate(id);
|
||||
if(destinationid == ObjectInstanceID()) //no exit
|
||||
logGlobal->warnStream() << "Cannot find corresponding exit monolith for "<< id << " (obj at " << pos << ") :(";
|
||||
td.impassable = true;
|
||||
}
|
||||
else if(getRandomExit(h) == ObjectInstanceID())
|
||||
logGlobal->warnStream() << "All exits blocked for monolith "<< id << " (obj at " << pos << ") :(";
|
||||
}
|
||||
|
||||
cb->showTeleportDialog(&td);
|
||||
}
|
||||
|
||||
void CGMonolith::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector<ObjectInstanceID> exits) const
|
||||
{
|
||||
ObjectInstanceID objId = ObjectInstanceID(answer);
|
||||
auto realExits = getAllExits(true);
|
||||
if(!isEntrance() // Do nothing if hero visited exit only object
|
||||
|| (!exits.size() && !realExits.size()) // Do nothing if there no exits on this channel
|
||||
|| (!exits.size() && ObjectInstanceID() == getRandomExit(hero))) // Do nothing if all exits are blocked by friendly hero and it's not subterranean gate
|
||||
{
|
||||
showInfoDialog(h,153,0);//Just inside the entrance you find a large pile of rubble blocking the tunnel. You leave discouraged.
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(destinationid == ObjectInstanceID())
|
||||
{
|
||||
logGlobal->warnStream() << "Cannot find exit... (obj at " << pos << ") :( ";
|
||||
return;
|
||||
}
|
||||
if (ID == Obj::WHIRLPOOL)
|
||||
else if(objId == ObjectInstanceID())
|
||||
objId = getRandomExit(hero);
|
||||
else
|
||||
assert(vstd::contains(exits, objId)); // Likely cheating attempt: not random teleporter choosen, but it's not from provided list
|
||||
|
||||
auto obj = cb->getObj(objId);
|
||||
if(obj)
|
||||
cb->moveHero(hero->id,CGHeroInstance::convertPosition(obj->pos,true) - getVisitableOffset(), true);
|
||||
}
|
||||
|
||||
void CGMonolith::initObj()
|
||||
{
|
||||
std::vector<Obj> IDs;
|
||||
IDs.push_back(ID);
|
||||
switch(ID)
|
||||
{
|
||||
std::set<int3> tiles = cb->getObj(destinationid)->getBlockedPos();
|
||||
auto & tile = *RandomGeneratorUtil::nextItem(tiles, cb->gameState()->getRandomGenerator());
|
||||
cb->moveHero(h->id, tile + int3(1,0,0), true);
|
||||
case Obj::MONOLITH_ONE_WAY_ENTRANCE:
|
||||
type = ENTRANCE;
|
||||
IDs.push_back(Obj::MONOLITH_ONE_WAY_EXIT);
|
||||
break;
|
||||
case Obj::MONOLITH_ONE_WAY_EXIT:
|
||||
type = EXIT;
|
||||
IDs.push_back(Obj::MONOLITH_ONE_WAY_ENTRANCE);
|
||||
break;
|
||||
case Obj::MONOLITH_TWO_WAY:
|
||||
default:
|
||||
type = BOTH;
|
||||
break;
|
||||
}
|
||||
|
||||
channel = findMeChannel(IDs, subID);
|
||||
if(channel == TeleportChannelID())
|
||||
channel = TeleportChannelID(cb->gameState()->map->teleportChannels.size());
|
||||
|
||||
addToChannel(cb->gameState()->map->teleportChannels, this);
|
||||
}
|
||||
|
||||
void CGSubterraneanGate::onHeroVisit( const CGHeroInstance * h ) const
|
||||
{
|
||||
TeleportDialog td(h, channel);
|
||||
if(ETeleportChannelType::IMPASSABLE == cb->getTeleportChannelType(channel)) //no exit
|
||||
{
|
||||
showInfoDialog(h,153,0);//Just inside the entrance you find a large pile of rubble blocking the tunnel. You leave discouraged.
|
||||
logGlobal->debugStream() << "Cannot find exit subterranean gate for "<< id << " (obj at " << pos << ") :(";
|
||||
td.impassable = true;
|
||||
}
|
||||
else
|
||||
cb->moveHero (h->id,CGHeroInstance::convertPosition(cb->getObj(destinationid)->pos,true) - getVisitableOffset(), true);
|
||||
td.exits.push_back(getRandomExit(h));
|
||||
|
||||
cb->showTeleportDialog(&td);
|
||||
}
|
||||
|
||||
void CGTeleport::initObj()
|
||||
void CGSubterraneanGate::initObj()
|
||||
{
|
||||
int si = subID;
|
||||
switch (ID)
|
||||
{
|
||||
case Obj::SUBTERRANEAN_GATE://ignore subterranean gates subid
|
||||
case Obj::WHIRLPOOL:
|
||||
{
|
||||
si = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
objs[ID][si].push_back(id);
|
||||
type = BOTH;
|
||||
}
|
||||
|
||||
void CGTeleport::postInit() //matches subterranean gates into pairs
|
||||
void CGSubterraneanGate::postInit( CGameState * gs ) //matches subterranean gates into pairs
|
||||
{
|
||||
//split on underground and surface gates
|
||||
std::vector<const CGObjectInstance *> gatesSplit[2]; //surface and underground gates
|
||||
for(auto & elem : objs[Obj::SUBTERRANEAN_GATE][0])
|
||||
std::vector<CGSubterraneanGate *> gatesSplit[2]; //surface and underground gates
|
||||
for(auto & obj : cb->gameState()->map->objects)
|
||||
{
|
||||
const CGObjectInstance *hlp = cb->getObj(elem);
|
||||
auto hlp = dynamic_cast<CGSubterraneanGate *>(gs->getObjInstance(obj->id));
|
||||
if(hlp)
|
||||
gatesSplit[hlp->pos.z].push_back(hlp);
|
||||
}
|
||||
|
||||
//sort by position
|
||||
std::sort(gatesSplit[0].begin(), gatesSplit[0].end(), [](const CGObjectInstance * a, const CGObjectInstance * b)
|
||||
std::sort(gatesSplit[0].begin(), gatesSplit[0].end(), [](CGSubterraneanGate * a, CGSubterraneanGate * b)
|
||||
{
|
||||
return a->pos < b->pos;
|
||||
});
|
||||
|
||||
for(size_t i = 0; i < gatesSplit[0].size(); i++)
|
||||
{
|
||||
const CGObjectInstance *cur = gatesSplit[0][i];
|
||||
CGSubterraneanGate * objCurrent = gatesSplit[0][i];
|
||||
|
||||
//find nearest underground exit
|
||||
std::pair<int, si32> best(-1, std::numeric_limits<si32>::max()); //pair<pos_in_vector, distance^2>
|
||||
for(int j = 0; j < gatesSplit[1].size(); j++)
|
||||
{
|
||||
const CGObjectInstance *checked = gatesSplit[1][j];
|
||||
CGSubterraneanGate *checked = gatesSplit[1][j];
|
||||
if(!checked)
|
||||
continue;
|
||||
si32 hlp = checked->pos.dist2dSQ(cur->pos);
|
||||
si32 hlp = checked->pos.dist2dSQ(objCurrent->pos);
|
||||
if(hlp < best.second)
|
||||
{
|
||||
best.first = j;
|
||||
@ -879,28 +1014,86 @@ void CGTeleport::postInit() //matches subterranean gates into pairs
|
||||
}
|
||||
}
|
||||
|
||||
if(objCurrent->channel == TeleportChannelID())
|
||||
{ // if object not linked to channel then create new channel
|
||||
objCurrent->channel = TeleportChannelID(gs->map->teleportChannels.size());
|
||||
addToChannel(cb->gameState()->map->teleportChannels, objCurrent);
|
||||
}
|
||||
|
||||
if(best.first >= 0) //found pair
|
||||
{
|
||||
gates.push_back(std::make_pair(cur->id, gatesSplit[1][best.first]->id));
|
||||
gatesSplit[1][best.first] = nullptr;
|
||||
gatesSplit[1][best.first]->channel = objCurrent->channel;
|
||||
addToChannel(cb->gameState()->map->teleportChannels, gatesSplit[1][best.first]);
|
||||
}
|
||||
else
|
||||
gates.push_back(std::make_pair(cur->id, ObjectInstanceID()));
|
||||
}
|
||||
objs.erase(Obj::SUBTERRANEAN_GATE);
|
||||
}
|
||||
|
||||
ObjectInstanceID CGTeleport::getMatchingGate(ObjectInstanceID id)
|
||||
void CGWhirlpool::onHeroVisit( const CGHeroInstance * h ) const
|
||||
{
|
||||
for(auto & gate : gates)
|
||||
TeleportDialog td(h, channel);
|
||||
if(ETeleportChannelType::IMPASSABLE == cb->getTeleportChannelType(channel))
|
||||
{
|
||||
if(gate.first == id)
|
||||
return gate.second;
|
||||
if(gate.second == id)
|
||||
return gate.first;
|
||||
logGlobal->warnStream() << "Cannot find exit whirlpool for "<< id << " (obj at " << pos << ") :(";
|
||||
td.impassable = true;
|
||||
}
|
||||
else if(getRandomExit(h) == ObjectInstanceID())
|
||||
logGlobal->warnStream() << "All exits are blocked for whirlpool "<< id << " (obj at " << pos << ") :(";
|
||||
|
||||
if(!isProtected(h))
|
||||
{
|
||||
SlotID targetstack = h->Slots().begin()->first; //slot numbers may vary
|
||||
for(auto i = h->Slots().rbegin(); i != h->Slots().rend(); i++)
|
||||
{
|
||||
if(h->getPower(targetstack) > h->getPower(i->first))
|
||||
targetstack = (i->first);
|
||||
}
|
||||
|
||||
return ObjectInstanceID();
|
||||
TQuantity countToTake = h->getStackCount(targetstack) * 0.5;
|
||||
vstd::amax(countToTake, 1);
|
||||
|
||||
InfoWindow iw;
|
||||
iw.player = h->tempOwner;
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 168);
|
||||
iw.components.push_back(Component(CStackBasicDescriptor(h->getCreature(targetstack), countToTake)));
|
||||
cb->showInfoDialog(&iw);
|
||||
cb->changeStackCount(StackLocation(h, targetstack), -countToTake);
|
||||
}
|
||||
else
|
||||
td.exits = getAllExits(true);
|
||||
|
||||
cb->showTeleportDialog(&td);
|
||||
}
|
||||
|
||||
void CGWhirlpool::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector<ObjectInstanceID> exits) const
|
||||
{
|
||||
ObjectInstanceID objId = ObjectInstanceID(answer);
|
||||
auto realExits = getAllExits();
|
||||
if(!exits.size() && !realExits.size())
|
||||
return;
|
||||
else if(objId == ObjectInstanceID())
|
||||
objId = getRandomExit(hero);
|
||||
else
|
||||
assert(vstd::contains(exits, objId)); // Likely cheating attempt: not random teleporter choosen, but it's not from provided list
|
||||
|
||||
auto obj = cb->getObj(objId);
|
||||
if(obj)
|
||||
{
|
||||
std::set<int3> tiles = obj->getBlockedPos();
|
||||
auto & tile = *RandomGeneratorUtil::nextItem(tiles, cb->gameState()->getRandomGenerator());
|
||||
cb->moveHero(hero->id, tile + int3(1,0,0), true);
|
||||
|
||||
cb->moveHero(hero->id,CGHeroInstance::convertPosition(obj->pos,true) - getVisitableOffset(), true);
|
||||
}
|
||||
}
|
||||
|
||||
bool CGWhirlpool::isProtected( const CGHeroInstance * h )
|
||||
{
|
||||
if(h->hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION)
|
||||
|| (h->Slots().size() == 1 && h->Slots().begin()->second->count == 1)) //we can't remove last unit
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CGArtifact::initObj()
|
||||
|
@ -263,19 +263,76 @@ struct DLL_LINKAGE TeleportChannel
|
||||
}
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CGTeleport : public CGObjectInstance //teleports and subterranean gates
|
||||
class DLL_LINKAGE CGTeleport : public CGObjectInstance
|
||||
{
|
||||
public:
|
||||
static std::map<Obj, std::map<int, std::vector<ObjectInstanceID> > > objs; //teleports: map[ID][subID] => vector of ids
|
||||
static std::vector<std::pair<ObjectInstanceID, ObjectInstanceID> > gates; //subterranean gates: pairs of ids
|
||||
void onHeroVisit(const CGHeroInstance * h) const override;
|
||||
void initObj() override;
|
||||
static void postInit();
|
||||
static ObjectInstanceID getMatchingGate(ObjectInstanceID id); //receives id of one subterranean gate and returns id of the paired one, -1 if none
|
||||
enum EType {UNKNOWN, ENTRANCE, EXIT, BOTH};
|
||||
|
||||
EType type;
|
||||
TeleportChannelID channel;
|
||||
|
||||
CGTeleport();
|
||||
bool isEntrance() const;
|
||||
bool isExit() const;
|
||||
bool isChannelEntrance(ObjectInstanceID id) const;
|
||||
bool isChannelExit(ObjectInstanceID id) const;
|
||||
std::vector<ObjectInstanceID> getAllEntrances(bool excludeCurrent = false) const;
|
||||
std::vector<ObjectInstanceID> getAllExits(bool excludeCurrent = false) const;
|
||||
ObjectInstanceID getRandomExit(const CGHeroInstance * h) const;
|
||||
|
||||
virtual void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector<ObjectInstanceID> exits) const = 0;
|
||||
|
||||
static bool isTeleport(const CGObjectInstance * dst);
|
||||
static bool isConnected(const CGTeleport * src, const CGTeleport * dst);
|
||||
static bool isConnected(const CGObjectInstance * src, const CGObjectInstance * dst);
|
||||
static bool isExitPassable(CGameState * gs, const CGHeroInstance * h, const CGObjectInstance * obj);
|
||||
static std::vector<ObjectInstanceID> getPassableExits(CGameState * gs, const CGHeroInstance * h, std::vector<ObjectInstanceID> exits);
|
||||
static void addToChannel(std::map<TeleportChannelID, shared_ptr<TeleportChannel> > &channelsList, const CGTeleport * obj);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CGObjectInstance&>(*this);
|
||||
h & type & channel & static_cast<CGObjectInstance&>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CGMonolith : public CGTeleport
|
||||
{
|
||||
TeleportChannelID findMeChannel(std::vector<Obj> IDs, int SubID) const;
|
||||
|
||||
public:
|
||||
void onHeroVisit(const CGHeroInstance * h) const override;
|
||||
void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector<ObjectInstanceID> exits) const override;
|
||||
void initObj() override;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CGTeleport&>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CGSubterraneanGate : public CGMonolith
|
||||
{
|
||||
public:
|
||||
void onHeroVisit(const CGHeroInstance * h) const override;
|
||||
void initObj() override;
|
||||
static void postInit(CGameState * gs);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CGMonolith&>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CGWhirlpool : public CGMonolith
|
||||
{
|
||||
public:
|
||||
void onHeroVisit(const CGHeroInstance * h) const override;
|
||||
void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector<ObjectInstanceID> exits) const override;
|
||||
static bool isProtected( const CGHeroInstance * h );
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CGMonolith&>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -432,6 +432,7 @@ public:
|
||||
|
||||
//Helper lists
|
||||
std::vector< ConstTransitivePtr<CGHeroInstance> > heroesOnMap;
|
||||
std::map<TeleportChannelID, shared_ptr<TeleportChannel> > teleportChannels;
|
||||
|
||||
/// associative list to identify which hero/creature id belongs to which object id(index for objects)
|
||||
std::map<si32, ObjectInstanceID> questIdentifierToId;
|
||||
@ -499,11 +500,9 @@ public:
|
||||
}
|
||||
|
||||
h & objects;
|
||||
h & heroesOnMap & towns & artInstances;
|
||||
h & heroesOnMap & teleportChannels & towns & artInstances;
|
||||
|
||||
// static members
|
||||
h & CGTeleport::objs;
|
||||
h & CGTeleport::gates;
|
||||
h & CGKeys::playerKeyMap;
|
||||
h & CGMagi::eyelist;
|
||||
h & CGObelisk::obeliskCount & CGObelisk::visited;
|
||||
|
@ -36,6 +36,9 @@ void registerTypesMapObjects1(Serializer &s)
|
||||
|
||||
// Non-armed objects
|
||||
s.template registerType<CGObjectInstance, CGTeleport>();
|
||||
s.template registerType<CGTeleport, CGMonolith>();
|
||||
s.template registerType<CGMonolith, CGSubterraneanGate>();
|
||||
s.template registerType<CGMonolith, CGWhirlpool>();
|
||||
s.template registerType<CGObjectInstance, CGSignBottle>();
|
||||
s.template registerType<CGObjectInstance, CGScholar>();
|
||||
s.template registerType<CGObjectInstance, CGMagicWell>();
|
||||
@ -121,7 +124,9 @@ void registerTypesMapObjectTypes(Serializer &s)
|
||||
REGISTER_GENERIC_HANDLER(CGShrine);
|
||||
REGISTER_GENERIC_HANDLER(CGSignBottle);
|
||||
REGISTER_GENERIC_HANDLER(CGSirens);
|
||||
REGISTER_GENERIC_HANDLER(CGTeleport);
|
||||
REGISTER_GENERIC_HANDLER(CGMonolith);
|
||||
REGISTER_GENERIC_HANDLER(CGSubterraneanGate);
|
||||
REGISTER_GENERIC_HANDLER(CGWhirlpool);
|
||||
REGISTER_GENERIC_HANDLER(CGTownInstance);
|
||||
REGISTER_GENERIC_HANDLER(CGUniversity);
|
||||
REGISTER_GENERIC_HANDLER(CGVisitableOPH);
|
||||
|
@ -381,11 +381,11 @@ void CMapGenerator::createConnections()
|
||||
|
||||
if (withinZone)
|
||||
{
|
||||
auto gate1 = new CGTeleport;
|
||||
auto gate1 = new CGSubterraneanGate;
|
||||
gate1->ID = Obj::SUBTERRANEAN_GATE;
|
||||
gate1->subID = 0;
|
||||
zoneA->placeAndGuardObject(this, gate1, tile, connection.getGuardStrength());
|
||||
auto gate2 = new CGTeleport(*gate1);
|
||||
auto gate2 = new CGSubterraneanGate(*gate1);
|
||||
zoneB->placeAndGuardObject(this, gate2, otherTile, connection.getGuardStrength());
|
||||
|
||||
stop = true; //we are done, go to next connection
|
||||
@ -398,11 +398,11 @@ void CMapGenerator::createConnections()
|
||||
}
|
||||
if (!guardPos.valid())
|
||||
{
|
||||
auto teleport1 = new CGTeleport;
|
||||
auto teleport1 = new CGMonolith;
|
||||
teleport1->ID = Obj::MONOLITH_TWO_WAY;
|
||||
teleport1->subID = getNextMonlithIndex();
|
||||
|
||||
auto teleport2 = new CGTeleport(*teleport1);
|
||||
auto teleport2 = new CGMonolith(*teleport1);
|
||||
|
||||
zoneA->addRequiredObject (teleport1, connection.getGuardStrength());
|
||||
zoneB->addRequiredObject (teleport2, connection.getGuardStrength());
|
||||
|
Loading…
Reference in New Issue
Block a user