1
0
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:
ArseniyShestakov 2015-03-08 16:11:23 +03:00
parent 04a1df29ad
commit c9eba40fe6
7 changed files with 383 additions and 127 deletions

View File

@ -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
}

View File

@ -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);

View File

@ -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()

View File

@ -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);
}
};

View File

@ -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;

View File

@ -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);

View File

@ -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());