|
|
|
|
/**
|
|
|
|
|
* @file portal.cpp
|
|
|
|
|
*
|
|
|
|
|
* Implementation of functionality for handling town portals.
|
|
|
|
|
*/
|
|
|
|
|
#include "portal.h"
|
|
|
|
|
|
|
|
|
|
#include "lighting.h"
|
|
|
|
|
#include "misdat.h"
|
|
|
|
|
#include "missiles.h"
|
|
|
|
|
#include "multi.h"
|
|
|
|
|
#include "player.h"
|
|
|
|
|
|
|
|
|
|
namespace devilution {
|
|
|
|
|
|
|
|
|
|
/** In-game state of portals. */
|
|
|
|
|
Portal Portals[MAXPORTAL];
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
/** Current portal number (a portal array index). */
|
|
|
|
|
size_t portalindex;
|
|
|
|
|
|
|
|
|
|
/** Coordinate of each players portal in town. */
|
|
|
|
|
Point PortalTownPosition[MAXPORTAL] = {
|
|
|
|
|
{ 57, 40 },
|
|
|
|
|
{ 59, 40 },
|
|
|
|
|
{ 61, 40 },
|
|
|
|
|
{ 63, 40 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
void InitPortals()
|
|
|
|
|
{
|
|
|
|
|
for (auto &portal : Portals) {
|
|
|
|
|
portal.open = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetPortalStats(int i, bool o, Point position, int lvl, dungeon_type lvltype, bool isSetLevel)
|
|
|
|
|
{
|
|
|
|
|
Portals[i].open = o;
|
|
|
|
|
Portals[i].position = position;
|
|
|
|
|
Portals[i].level = lvl;
|
|
|
|
|
Portals[i].ltype = lvltype;
|
|
|
|
|
Portals[i].setlvl = isSetLevel;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AddPortalMissile(int i, Point position, bool sync)
|
|
|
|
|
{
|
|
|
|
|
auto *missile = AddMissile({ 0, 0 }, position, Direction::South, MissileID::TownPortal, TARGET_MONSTERS, i, 0, 0, /*parent=*/nullptr, SFX_NONE);
|
|
|
|
|
if (missile != nullptr) {
|
|
|
|
|
// Don't show portal opening animation if we sync existing portals
|
|
|
|
|
if (sync)
|
|
|
|
|
SetMissDir(*missile, 1);
|
|
|
|
|
|
|
|
|
|
if (leveltype != DTYPE_TOWN)
|
|
|
|
|
missile->_mlid = AddLight(missile->position.tile, 15);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SyncPortals()
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < MAXPORTAL; i++) {
|
|
|
|
|
if (!Portals[i].open)
|
|
|
|
|
continue;
|
|
|
|
|
if (leveltype == DTYPE_TOWN)
|
|
|
|
|
AddPortalMissile(i, PortalTownPosition[i], true);
|
|
|
|
|
else {
|
|
|
|
|
int lvl = currlevel;
|
|
|
|
|
if (setlevel)
|
|
|
|
|
lvl = setlvlnum;
|
|
|
|
|
if (Portals[i].level == lvl && Portals[i].setlvl == setlevel)
|
|
|
|
|
AddPortalMissile(i, Portals[i].position, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AddPortalInTown(int i)
|
|
|
|
|
{
|
|
|
|
|
AddPortalMissile(i, PortalTownPosition[i], false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ActivatePortal(int i, Point position, int lvl, dungeon_type dungeonType, bool isSetLevel)
|
|
|
|
|
{
|
|
|
|
|
Portals[i].open = true;
|
|
|
|
|
|
|
|
|
|
if (lvl != 0) {
|
|
|
|
|
Portals[i].position = position;
|
|
|
|
|
Portals[i].level = lvl;
|
|
|
|
|
Portals[i].ltype = dungeonType;
|
|
|
|
|
Portals[i].setlvl = isSetLevel;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DeactivatePortal(int i)
|
|
|
|
|
{
|
|
|
|
|
Portals[i].open = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool PortalOnLevel(size_t i)
|
|
|
|
|
{
|
|
|
|
|
if (Portals[i].setlvl == setlevel && Portals[i].level == setlevel ? static_cast<int>(setlvlnum) : currlevel)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return leveltype == DTYPE_TOWN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RemovePortalMissile(int id)
|
|
|
|
|
{
|
|
|
|
|
Missiles.remove_if([id](Missile &missile) {
|
|
|
|
|
if (missile._mitype == MissileID::TownPortal && missile._misource == id) {
|
|
|
|
|
dFlags[missile.position.tile.x][missile.position.tile.y] &= ~DungeonFlag::Missile;
|
|
|
|
|
|
|
|
|
|
if (Portals[id].level != 0)
|
|
|
|
|
AddUnLight(missile._mlid);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetCurrentPortal(size_t p)
|
|
|
|
|
{
|
|
|
|
|
portalindex = p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GetPortalLevel()
|
|
|
|
|
{
|
|
|
|
|
if (leveltype != DTYPE_TOWN) {
|
|
|
|
|
setlevel = false;
|
|
|
|
|
currlevel = 0;
|
|
|
|
|
MyPlayer->setLevel(0);
|
|
|
|
|
leveltype = DTYPE_TOWN;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Portals[portalindex].setlvl) {
|
|
|
|
|
setlevel = true;
|
|
|
|
|
setlvlnum = (_setlevels)Portals[portalindex].level;
|
|
|
|
|
currlevel = Portals[portalindex].level;
|
|
|
|
|
MyPlayer->setLevel(setlvlnum);
|
|
|
|
|
setlvltype = leveltype = Portals[portalindex].ltype;
|
|
|
|
|
} else {
|
|
|
|
|
setlevel = false;
|
|
|
|
|
currlevel = Portals[portalindex].level;
|
|
|
|
|
MyPlayer->setLevel(currlevel);
|
|
|
|
|
leveltype = Portals[portalindex].ltype;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (portalindex == MyPlayerId) {
|
|
|
|
|
NetSendCmd(true, CMD_DEACTIVATEPORTAL);
|
|
|
|
|
DeactivatePortal(portalindex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GetPortalLvlPos()
|
|
|
|
|
{
|
|
|
|
|
if (leveltype == DTYPE_TOWN) {
|
|
|
|
|
ViewPosition = PortalTownPosition[portalindex] + Displacement { 1, 1 };
|
|
|
|
|
} else {
|
|
|
|
|
ViewPosition = Portals[portalindex].position;
|
|
|
|
|
|
|
|
|
|
if (portalindex != MyPlayerId) {
|
|
|
|
|
ViewPosition.x++;
|
|
|
|
|
ViewPosition.y++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool PosOkPortal(int lvl, Point position)
|
|
|
|
|
{
|
|
|
|
|
for (auto &portal : Portals) {
|
|
|
|
|
if (portal.open
|
|
|
|
|
&& portal.level == lvl
|
|
|
|
|
&& ((portal.position == position)
|
|
|
|
|
|| (portal.position == position - Displacement { 1, 1 })))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace devilution
|