Skip to content

Commit

Permalink
Add town portal spell
Browse files Browse the repository at this point in the history
  • Loading branch information
grantramsay committed Mar 1, 2020
1 parent 29e2302 commit c706c56
Show file tree
Hide file tree
Showing 9 changed files with 65 additions and 8 deletions.
14 changes: 12 additions & 2 deletions apps/freeablo/faworld/gamelevel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ namespace FAWorld
actorMapInsert(mActors[i]);
}

Misc::Point GameLevel::getFreeSpotNear(Misc::Point point, int32_t radius) const
Misc::Point GameLevel::getFreeSpotNear(Misc::Point point, int32_t radius, std::function<bool(Misc::Point point)> additionalConstraints) const
{
// partially based on https://stackoverflow.com/a/398302

Expand All @@ -192,7 +192,7 @@ namespace FAWorld
Misc::Point targetPoint = point + Misc::Point{xOffset, yOffset};
if (targetPoint.x >= 0 && targetPoint.x < width() && targetPoint.y >= 0 && targetPoint.y < height())
{
if (isPassable(targetPoint, nullptr))
if (isPassable(targetPoint, nullptr) && (additionalConstraints == nullptr || additionalConstraints(targetPoint)))
return targetPoint;
}

Expand Down Expand Up @@ -355,4 +355,14 @@ namespace FAWorld
ItemMap& GameLevel::getItemMap() { return *mItemMap; }

bool GameLevel::isTown() const { return mLevelIndex == 0; }

bool GameLevel::anyMissileAtPoint(Misc::Point& point)
{
for (const auto& actor : mActors)
for (const auto& missile : actor->getMissiles())
for (const auto& g : missile->getGraphics())
if (g->mCurPos.current() == point)
return true;
return false;
}
}
4 changes: 3 additions & 1 deletion apps/freeablo/faworld/gamelevel.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ namespace FAWorld

void actorMapRefresh();

Misc::Point getFreeSpotNear(Misc::Point point, int32_t radius = std::numeric_limits<int32_t>::max()) const;
Misc::Point getFreeSpotNear(Misc::Point point, int32_t radius = std::numeric_limits<int32_t>::max(), std::function<bool(Misc::Point point)> additionalConstraints = nullptr) const;

virtual bool isPassable(const Misc::Point& point, const FAWorld::Actor* forActor) const;

Expand All @@ -108,6 +108,8 @@ namespace FAWorld

World* getWorld() { return &mWorld; }

bool anyMissileAtPoint(Misc::Point& point);

private:
GameLevel(World& world);

Expand Down
2 changes: 2 additions & 0 deletions apps/freeablo/faworld/missile/missile.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ namespace FAWorld::Missile
static void animated16Direction(Missile& missile, Misc::Point dest, GameLevel* level);
static void firewall(Missile& missile, Misc::Point dest, GameLevel* level);
static void basicAnimated(Missile& missile, Misc::Point dest, GameLevel* level);
static void townPortal(Missile& missile, Misc::Point dest, GameLevel* level);
};

class Movement
Expand All @@ -68,6 +69,7 @@ namespace FAWorld::Missile
static void none(Missile& missile, MissileGraphic& graphic, Actor& actor);
static void damageEnemy(Missile& missile, MissileGraphic& graphic, Actor& actor);
static void damageEnemyAndStop(Missile& missile, MissileGraphic& graphic, Actor& actor);
static void townPortal(Missile& missile, MissileGraphic& graphic, Actor& actor);
};

// Inner class that holds reference to all missile attributes.
Expand Down
18 changes: 18 additions & 0 deletions apps/freeablo/faworld/missile/missileactorengagement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,22 @@ namespace FAWorld::Missile
if (&actor != missile.mCreator)
graphic.stop();
}

void Missile::ActorEngagement::townPortal(Missile& missile, MissileGraphic& graphic, Actor& actor)
{
if (&actor == missile.mCreator)
{
// Teleport to other portal
auto& otherPortal = missile.mGraphics[0].get() != &graphic ? missile.mGraphics[0] : missile.mGraphics[1];
auto noMissilesAtPoint = [&](Misc::Point p) { return !otherPortal->mLevel->anyMissileAtPoint(p); };
auto pos = otherPortal->mLevel->getFreeSpotNear(otherPortal->mCurPos.current(), INT32_MAX, noMissilesAtPoint);
actor.teleport(otherPortal->mLevel, Position(pos));
// Close the portal if teleporting back through the 2nd (town located) portal
if (&graphic == missile.mGraphics[1].get())
{
graphic.stop();
otherPortal->stop();
}
}
}
}
2 changes: 2 additions & 0 deletions apps/freeablo/faworld/missile/missileattributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ namespace FAWorld::Missile
return Attributes(Creation::firewall, Movement::stationary, ActorEngagement::damageEnemy, maxRangeIgnore, World::getTicksInPeriod(8));
case MissileId::manashield:
return Attributes(Creation::basicAnimated, Movement::hoverOverCreator, ActorEngagement::none, maxRangeIgnore, World::getTicksInPeriod(8));
case MissileId::town:
return Attributes(Creation::townPortal, Movement::stationary, ActorEngagement::townPortal, maxRangeIgnore, ttlIgnore);
default:
invalid_enum(MissileId, missileId);
}
Expand Down
18 changes: 18 additions & 0 deletions apps/freeablo/faworld/missile/missilecreation.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#include "missile.h"
#include "engine/enginemain.h"
#include "faworld/gamelevel.h"
#include <misc/simplevec2.h>

namespace FAWorld::Missile
Expand Down Expand Up @@ -42,4 +44,20 @@ namespace FAWorld::Missile
{
missile.mGraphics.push_back(std::make_unique<MissileGraphic>("", missile.getGraphicsPath(0), std::nullopt, Position(missile.mSrcPoint), level));
}

void Missile::Creation::townPortal(Missile& missile, Misc::Point, GameLevel* level)
{
// Add portal near player
auto noMissilesAtPoint = [&](Misc::Point p) { return !level->anyMissileAtPoint(p); };
auto point = level->getFreeSpotNear(missile.mSrcPoint, INT32_MAX, noMissilesAtPoint);
missile.mGraphics.push_back(std::make_unique<MissileGraphic>(
"", missile.getGraphicsPath(0), std::nullopt, Position(point), level, FARender::AnimationPlayer::AnimationType::FreezeAtEnd));
// Add portal in town
auto town = Engine::EngineMain::get()->mWorld->getLevel(0);
static const Misc::Point townPortalPoint = Misc::Point(60, 80);
auto noMissilesAtTownPoint = [&](Misc::Point p) { return !town->anyMissileAtPoint(p); };
point = town->getFreeSpotNear(townPortalPoint, INT32_MAX, noMissilesAtTownPoint);
missile.mGraphics.push_back(std::make_unique<MissileGraphic>(
"", missile.getGraphicsPath(0), std::nullopt, Position(point), town, FARender::AnimationPlayer::AnimationType::FreezeAtEnd));
}
}
9 changes: 6 additions & 3 deletions apps/freeablo/faworld/missile/missilegraphic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@

namespace FAWorld::Missile
{
MissileGraphic::MissileGraphic(std::string initialGraphicPath, std::string mainGraphicPath, std::optional<int32_t> singleFrame, Position position, GameLevel* level)
: mCurPos(position), mLevel(level), mMainGraphicPath(mainGraphicPath), mSingleFrame(singleFrame)
MissileGraphic::MissileGraphic(std::string initialGraphicPath, std::string mainGraphicPath, std::optional<int32_t> singleFrame, Position position, GameLevel* level,
FARender::AnimationPlayer::AnimationType animationType)
: mCurPos(position), mLevel(level), mMainGraphicPath(mainGraphicPath), mSingleFrame(singleFrame), mAnimationType(animationType)
{
playAnimation(initialGraphicPath, FARender::AnimationPlayer::AnimationType::Once);
}
Expand All @@ -18,6 +19,7 @@ namespace FAWorld::Missile
mCurPos = Position(loader);
mMainGraphicPath = loader.load<std::string>();
mSingleFrame = (mSingleFrame = loader.load<int32_t>()) == -1 ? std::nullopt : mSingleFrame;
mAnimationType = static_cast<FARender::AnimationPlayer::AnimationType>(loader.load<int32_t>());
mAnimationPlayer = FARender::AnimationPlayer(loader);
auto levelIndex = loader.load<int32_t>();
auto world = loader.currentlyLoadingWorld;
Expand All @@ -31,6 +33,7 @@ namespace FAWorld::Missile
mCurPos.save(saver);
saver.save(mMainGraphicPath);
saver.save(static_cast<int32_t>(mSingleFrame == std::nullopt ? -1 : *mSingleFrame));
saver.save(static_cast<int32_t>(mAnimationType));
mAnimationPlayer.save(saver);
saver.save(mLevel->getLevelIndex());
saver.save(mTicksSinceStarted);
Expand All @@ -45,7 +48,7 @@ namespace FAWorld::Missile
return;

if (!mAnimationPlayer.isPlaying())
playAnimation(mMainGraphicPath, FARender::AnimationPlayer::AnimationType::Looped);
playAnimation(mMainGraphicPath, mAnimationType);

mAnimationPlayer.update();
}
Expand Down
4 changes: 3 additions & 1 deletion apps/freeablo/faworld/missile/missilegraphic.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ namespace FAWorld::Missile
public:
virtual ~MissileGraphic() = default;

MissileGraphic(std::string initialGraphicPath, std::string mainGraphicPath, std::optional<int32_t> singleFrame, Position position, GameLevel* level);
MissileGraphic(std::string initialGraphicPath, std::string mainGraphicPath, std::optional<int32_t> singleFrame, Position position, GameLevel* level,
FARender::AnimationPlayer::AnimationType animationType = FARender::AnimationPlayer::AnimationType::Looped);
MissileGraphic(FASaveGame::GameLoader& loader);

virtual void save(FASaveGame::GameSaver& saver);
Expand All @@ -36,6 +37,7 @@ namespace FAWorld::Missile

std::string mMainGraphicPath;
std::optional<int32_t> mSingleFrame;
FARender::AnimationPlayer::AnimationType mAnimationType;
FARender::AnimationPlayer mAnimationPlayer;
Tick mTicksSinceStarted = 0;
bool mComplete = false;
Expand Down
2 changes: 1 addition & 1 deletion apps/freeablo/faworld/spells.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ namespace FAWorld
// Temporary quirk to only allow implemented spells to be used.
static bool isSpellImplemented(SpellId spell)
{
static const SpellId implementedSpells[] = {SpellId::firebolt, SpellId::firewall, SpellId::manashield};
static const SpellId implementedSpells[] = {SpellId::firebolt, SpellId::firewall, SpellId::manashield, SpellId::town};
for (auto sp : implementedSpells)
if (spell == sp)
return true;
Expand Down

0 comments on commit c706c56

Please sign in to comment.