diff --git a/premake5.lua b/premake5.lua index a12cd920..648a60b9 100644 --- a/premake5.lua +++ b/premake5.lua @@ -101,6 +101,7 @@ end nuget(nugetPackages) filter { "toolset:msc*" } defines { "_CRT_SECURE_NO_WARNINGS" } -- 4996: '$func': This function or variable may be unsafe. Consider using $func2 instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. + defines { "_USE_MATH_DEFINES" } -- for M_PI buildoptions { "/Zc:__cplusplus" } -- else __cplusplus would be 199711L disablewarnings { "4458", -- declaration of '$var' hides class member diff --git a/src/lib/game/data/units/unit.cpp b/src/lib/game/data/units/unit.cpp index a617d801..0b2a2d26 100644 --- a/src/lib/game/data/units/unit.cpp +++ b/src/lib/game/data/units/unit.cpp @@ -320,12 +320,12 @@ void cUnit::changeName (std::string&& newName) //------------------------------------------------------------------------------ /** rotates the unit to the given direction */ //------------------------------------------------------------------------------ -void cUnit::rotateTo (int newDir) +void cUnit::rotateTo (EDirection newDir) { - if (newDir < 0 || newDir >= 8 || newDir == dir) + if (newDir == dir) return; - int t = dir; + EDirection t = dir; int dest = 0; for (int i = 0; i < 8; ++i) @@ -336,23 +336,12 @@ void cUnit::rotateTo (int newDir) break; } ++t; - - if (t > 7) - t = 0; } if (dest < 4) ++dir; else --dir; - - if (dir < 0) - dir += 8; - else - { - if (dir > 7) - dir -= 8; - } } //------------------------------------------------------------------------------ diff --git a/src/lib/game/data/units/unit.h b/src/lib/game/data/units/unit.h index 351d0c35..2c0aa25f 100644 --- a/src/lib/game/data/units/unit.h +++ b/src/lib/game/data/units/unit.h @@ -21,6 +21,7 @@ #define game_data_units_unitH #include "game/data/units/unitdata.h" +#include "utility/direction.h" #include "utility/position.h" #include "utility/signal/signal.h" @@ -103,7 +104,7 @@ class cUnit std::optional getCustomName() const; void changeName (std::string&& newName); - void rotateTo (int newDir); + void rotateTo (EDirection newDir); /** checks if the unit can attack something at the offset. * when forceAttack is false, the function only returns true, @@ -222,7 +223,7 @@ class cUnit const cStaticUnitData& getStaticUnitData() const; cDynamicUnitData data; // basic data of the unit const unsigned int iID; // the identification number of this unit - int dir = 0; // ?Frame of the unit/current direction the unit is facing? + EDirection dir = EDirection::North; // ?Frame of the unit/current direction the unit is facing? private: std::vector storedUnitIds; // equivalent of storedUnits, for serialization only. diff --git a/src/lib/game/logic/attackjob.cpp b/src/lib/game/logic/attackjob.cpp index 5cf5e216..26078e5c 100644 --- a/src/lib/game/logic/attackjob.cpp +++ b/src/lib/game/logic/attackjob.cpp @@ -41,7 +41,7 @@ cAttackJob::cAttackJob (cUnit& aggressor, const cPosition& targetPosition, const cModel& model) : aggressorId (aggressor.getId()), targetPosition (targetPosition), - fireDir (calcFireDir (aggressor)), + fireDir (directionFromOffset (targetPosition - aggressor.getPosition()).value_or (aggressor.dir)), counter (10), state (eAJState::Rotating) { @@ -143,48 +143,6 @@ uint32_t cAttackJob::getChecksum (uint32_t crc) const //------------------------------------------------------------------------------ // private functions -int cAttackJob::calcFireDir (const cUnit& aggressor) -{ - auto dx = (float) (targetPosition.x() - aggressor.getPosition().x()); - auto dy = (float) -(targetPosition.y() - aggressor.getPosition().y()); - auto r = std::sqrt (dx * dx + dy * dy); - - int fireDir = aggressor.dir; - if (r > 0.001f) - { - // 360 / (2 * PI) = 57.29577951f; - dx /= r; - dy /= r; - r = asinf (dx) * 57.29577951f; - if (dy >= 0) - { - if (r < 0) - r += 360; - } - else - r = 180 - r; - - if (r >= 337.5f || r <= 22.5f) - fireDir = 0; - else if (r >= 22.5f && r <= 67.5f) - fireDir = 1; - else if (r >= 67.5f && r <= 112.5f) - fireDir = 2; - else if (r >= 112.5f && r <= 157.5f) - fireDir = 3; - else if (r >= 157.5f && r <= 202.5f) - fireDir = 4; - else if (r >= 202.5f && r <= 247.5f) - fireDir = 5; - else if (r >= 247.5f && r <= 292.5f) - fireDir = 6; - else if (r >= 292.5f && r <= 337.5f) - fireDir = 7; - } - - return fireDir; -} - //------------------------------------------------------------------------------ void cAttackJob::lockTarget (const cMap& map, const cUnit& aggressor) { @@ -277,31 +235,31 @@ std::unique_ptr cAttackJob::createMuzzleFx (const cUnit& aggressor) case eMuzzleType::Big: switch (fireDir) { - case 0: + case EDirection::North: offset.y() = -40; break; - case 1: + case EDirection::NorthEast: offset.x() = 32; offset.y() = -32; break; - case 2: + case EDirection::East: offset.x() = 40; break; - case 3: + case EDirection::SouthEast: offset.x() = 32; offset.y() = 32; break; - case 4: + case EDirection::South: offset.y() = 40; break; - case 5: + case EDirection::SouthWest: offset.x() = -32; offset.y() = 32; break; - case 6: + case EDirection::West: offset.x() = -40; break; - case 7: + case EDirection::NorthWest: offset.x() = -32; offset.y() = -32; break; @@ -319,31 +277,31 @@ std::unique_ptr cAttackJob::createMuzzleFx (const cUnit& aggressor) case eMuzzleType::MedLong: switch (fireDir) { - case 0: + case EDirection::North: offset.y() = -20; break; - case 1: + case EDirection::NorthEast: offset.x() = 12; offset.y() = -12; break; - case 2: + case EDirection::East: offset.x() = 20; break; - case 3: + case EDirection::SouthEast: offset.x() = 12; offset.y() = 12; break; - case 4: + case EDirection::South: offset.y() = 20; break; - case 5: + case EDirection::SouthWest: offset.x() = -12; offset.y() = 12; break; - case 6: + case EDirection::West: offset.x() = -20; break; - case 7: + case EDirection::NorthWest: offset.x() = -12; offset.y() = -12; break; diff --git a/src/lib/game/logic/attackjob.h b/src/lib/game/logic/attackjob.h index 2ba82ca4..845ee8b4 100644 --- a/src/lib/game/logic/attackjob.h +++ b/src/lib/game/logic/attackjob.h @@ -75,7 +75,6 @@ class cAttackJob } private: - int calcFireDir (const cUnit& aggressor); void lockTarget (const cMap& map, const cUnit& aggressor); void releaseTargets (const cModel& model); void fire (cModel& model); @@ -88,7 +87,7 @@ class cAttackJob int aggressorId = -1; cPosition targetPosition; std::vector lockedTargets; - int fireDir = 0; + EDirection fireDir = EDirection::North; int counter = 0; enum class eAJState { diff --git a/src/lib/game/logic/fxeffects.cpp b/src/lib/game/logic/fxeffects.cpp index 0df7213b..bd215d51 100644 --- a/src/lib/game/logic/fxeffects.cpp +++ b/src/lib/game/logic/fxeffects.cpp @@ -78,7 +78,7 @@ void cFxContainer::run() //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -cFxMuzzle::cFxMuzzle (const cPosition& position_, int dir_, sID id_) : +cFxMuzzle::cFxMuzzle (const cPosition& position_, EDirection dir_, sID id_) : cFx (false, position_), dir (dir_), id (id_) @@ -86,7 +86,7 @@ cFxMuzzle::cFxMuzzle (const cPosition& position_, int dir_, sID id_) : //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -cFxMuzzleBig::cFxMuzzleBig (const cPosition& position_, int dir_, sID id_) : +cFxMuzzleBig::cFxMuzzleBig (const cPosition& position_, EDirection dir_, sID id_) : cFxMuzzle (position_, dir_, id_) { length = 6; @@ -94,7 +94,7 @@ cFxMuzzleBig::cFxMuzzleBig (const cPosition& position_, int dir_, sID id_) : //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -cFxMuzzleMed::cFxMuzzleMed (const cPosition& position_, int dir_, sID id_) : +cFxMuzzleMed::cFxMuzzleMed (const cPosition& position_, EDirection dir_, sID id_) : cFxMuzzle (position_, dir_, id_) { length = 6; @@ -102,7 +102,7 @@ cFxMuzzleMed::cFxMuzzleMed (const cPosition& position_, int dir_, sID id_) : //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -cFxMuzzleMedLong::cFxMuzzleMedLong (const cPosition& position_, int dir_, sID id_) : +cFxMuzzleMedLong::cFxMuzzleMedLong (const cPosition& position_, EDirection dir_, sID id_) : cFxMuzzle (position_, dir_, id_) { length = 16; @@ -110,7 +110,7 @@ cFxMuzzleMedLong::cFxMuzzleMedLong (const cPosition& position_, int dir_, sID id //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -cFxMuzzleSmall::cFxMuzzleSmall (const cPosition& position_, int dir_, sID id_) : +cFxMuzzleSmall::cFxMuzzleSmall (const cPosition& position_, EDirection dir_, sID id_) : cFxMuzzle (position_, dir_, id_) { length = 6; @@ -200,7 +200,7 @@ cFxCorpse::cFxCorpse (const cPosition& position_) : //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -cFxTracks::cFxTracks (const cPosition& position_, int dir_) : +cFxTracks::cFxTracks (const cPosition& position_, EDirection dir_) : cFx (true, position_), dir (dir_) { @@ -209,7 +209,7 @@ cFxTracks::cFxTracks (const cPosition& position_, int dir_) : //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -cFxRocket::cFxRocket (const cPosition& startPosition_, const cPosition& endPosition_, int dir_, bool bottom, sID id_) : +cFxRocket::cFxRocket (const cPosition& startPosition_, const cPosition& endPosition_, EDirection dir_, bool bottom, sID id_) : cFx (bottom, startPosition_), dir (dir_), startPosition (startPosition_), diff --git a/src/lib/game/logic/fxeffects.h b/src/lib/game/logic/fxeffects.h index a62ea634..6001826a 100644 --- a/src/lib/game/logic/fxeffects.h +++ b/src/lib/game/logic/fxeffects.h @@ -21,6 +21,7 @@ #define game_logic_fxeffectsH #include "game/data/units/unitdata.h" +#include "utility/direction.h" #include "utility/position.h" #include @@ -105,41 +106,41 @@ class cFxContainer class cFxMuzzle : public cFx { public: - int getDir() const { return dir; } + EDirection getDir() const { return dir; } const sID& getId() const { return id; } protected: - cFxMuzzle (const cPosition& position, int dir_, sID id); + cFxMuzzle (const cPosition&, EDirection, sID); - int dir; + EDirection dir; const sID id; }; class cFxMuzzleBig : public cFxMuzzle { public: - cFxMuzzleBig (const cPosition& position, int dir, sID id); + cFxMuzzleBig (const cPosition&, EDirection, sID); void accept (IFxVisitor& visitor) const override { visitor.visit (*this); } }; class cFxMuzzleMed : public cFxMuzzle { public: - cFxMuzzleMed (const cPosition& position, int dir, sID id); + cFxMuzzleMed (const cPosition&, EDirection, sID); void accept (IFxVisitor& visitor) const override { visitor.visit (*this); } }; class cFxMuzzleMedLong : public cFxMuzzle { public: - cFxMuzzleMedLong (const cPosition& position, int dir, sID id); + cFxMuzzleMedLong (const cPosition&, EDirection, sID); void accept (IFxVisitor& visitor) const override { visitor.visit (*this); } }; class cFxMuzzleSmall : public cFxMuzzle { public: - cFxMuzzleSmall (const cPosition& position, int dir, sID id); + cFxMuzzleSmall (const cPosition&, EDirection, sID); void accept (IFxVisitor& visitor) const override { visitor.visit (*this); } }; @@ -241,10 +242,10 @@ class cFxTracks : public cFx public: static constexpr int alphaStart = 100; static constexpr int alphaEnd = 0; - const int dir; + const EDirection dir; public: - cFxTracks (const cPosition& position, int dir_); + cFxTracks (const cPosition&, EDirection); void accept (IFxVisitor& visitor) const override { visitor.visit (*this); } }; @@ -253,14 +254,14 @@ class cFxRocket : public cFx private: static constexpr int speed = 8; std::vector> subEffects; - int dir; + EDirection dir; int distance; const cPosition startPosition; const cPosition endPosition; const sID id; public: - cFxRocket (const cPosition& startPosition, const cPosition& endPosition, int dir_, bool bottom, sID id); + cFxRocket (const cPosition& startPosition, const cPosition& endPosition, EDirection, bool bottom, sID); void accept (IFxVisitor& visitor) const override { visitor.visit (*this); } void run() override; // return true, when the last smoke effect is finished. @@ -268,7 +269,7 @@ class cFxRocket : public cFx // the rocket has reached the destination bool isFinished() const override; const sID getId() const { return id; } - int getDir() const { return dir; } + EDirection getDir() const { return dir; } const std::vector>& getSubEffects() const { return subEffects; } }; diff --git a/src/lib/game/logic/jobs/startbuildjob.cpp b/src/lib/game/logic/jobs/startbuildjob.cpp index 43313242..e5eeb653 100644 --- a/src/lib/game/logic/jobs/startbuildjob.cpp +++ b/src/lib/game/logic/jobs/startbuildjob.cpp @@ -60,19 +60,19 @@ void cStartBuildJob::run (cModel& model) if (big) { - int deltaX = (vehicle->getPosition().x() < org.x() ? -1 : 1) * MOVE_SPEED; - int deltaY = (vehicle->getPosition().y() < org.y() ? -1 : 1) * MOVE_SPEED; - int dir = 0; - if (deltaX > 0 && deltaY > 0) dir = 3; - if (deltaX > 0 && deltaY < 0) dir = 1; - if (deltaX < 0 && deltaY > 0) dir = 5; - if (deltaX < 0 && deltaY < 0) dir = 7; + const int deltaX = (vehicle->getPosition().x() < org.x() ? -1 : 1) * MOVE_SPEED; + const int deltaY = (vehicle->getPosition().y() < org.y() ? -1 : 1) * MOVE_SPEED; + EDirection dir = EDirection::North; + if (deltaX > 0 && deltaY > 0) dir = EDirection::SouthEast; + if (deltaX > 0 && deltaY < 0) dir = EDirection::NorthEast; + if (deltaX < 0 && deltaY > 0) dir = EDirection::SouthWest; + if (deltaX < 0 && deltaY < 0) dir = EDirection::NorthWest; if (vehicle->getMovementOffset().x() == 32) { if (model.getGameTime() % 10 != 0) return; - vehicle->rotateTo (0); - if (vehicle->dir == 0) + vehicle->rotateTo (EDirection::North); + if (vehicle->dir == EDirection::North) { finished = true; vehicle->setMovementOffset (cPosition (0, 0)); @@ -99,8 +99,8 @@ void cStartBuildJob::run (cModel& model) else { if (model.getGameTime() % 10 != 0) return; - vehicle->rotateTo (0); - if (vehicle->dir == 0) + vehicle->rotateTo (EDirection::North); + if (vehicle->dir == EDirection::North) { finished = true; } diff --git a/src/lib/game/logic/movejob.cpp b/src/lib/game/logic/movejob.cpp index a9abcbbc..ea6ab2c4 100644 --- a/src/lib/game/logic/movejob.cpp +++ b/src/lib/game/logic/movejob.cpp @@ -29,43 +29,18 @@ #include "utility/narrow_cast.h" #include "utility/ranges.h" -// N, NE, E, SE, S, SW, W, NW -static const int directionDx[8] = {0, 1, 1, 1, 0, -1, -1, -1}; -static const int directionDy[8] = {-1, -1, 0, 1, 1, 1, 0, -1}; - constexpr double MOVE_ACCELERATION = 0.08; // change of vehicle speed per tick namespace { - //-------------------------------------------------------------------------- - /** - * calculates the needed rotation before the next movement - */ - std::optional calcNextDir (const cVehicle& vehicle, const cPosition& dest) - { - const cPosition diff = dest - vehicle.getPosition(); - - for (int i = 0; i != 8; ++i) - { - if (diff.x() == directionDx[i] && diff.y() == directionDy[i]) - { - return i; - } - } - return std::nullopt; - } - //-------------------------------------------------------------------------- /** * moves the vehicle by 'offset' pixel in direction of 'nextDir' */ - void changeVehicleOffset (cVehicle& vehicle, int offset, int dir) + void changeVehicleOffset (cVehicle& vehicle, int offset, EDirection dir) { - auto newOffset = vehicle.getMovementOffset(); - newOffset.x() += directionDx[dir] * offset; - newOffset.y() += directionDy[dir] * offset; - + const auto newOffset = vehicle.getMovementOffset() + offsetFromDirection (dir) * offset; vehicle.setMovementOffset (newOffset); } @@ -75,9 +50,9 @@ namespace */ bool reachedField (cVehicle& vehicle) { - const auto& offset = vehicle.getMovementOffset(); - const auto dir = vehicle.dir; - return (offset.x() * directionDx[dir] >= 0 && offset.y() * directionDy[dir] >= 0); + const auto& moveOffset = vehicle.getMovementOffset(); + const auto dirOffset = offsetFromDirection (vehicle.dir); + return moveOffset.x() * dirOffset.x() >= 0 && moveOffset.y() * dirOffset.y() >= 0; } } // namespace @@ -131,7 +106,7 @@ void cMoveJob::run (cModel& model) { startMove (model, *vehicle); } - else if (nextDir != static_cast (vehicle->dir)) + else if (nextDir != vehicle->dir) { if (timer100ms == 0) { @@ -227,7 +202,7 @@ void cMoveJob::startMove (cModel& model, cVehicle& vehicle) // start the move vehicle.setMoving (true); - nextDir = calcNextDir (vehicle, path.front()); + nextDir = directionFromOffset (path.front() - vehicle.getPosition()); vehicle.triggerLandingTakeOff (model); diff --git a/src/lib/game/logic/movejob.h b/src/lib/game/logic/movejob.h index ebb09e20..6bdd45e0 100644 --- a/src/lib/game/logic/movejob.h +++ b/src/lib/game/logic/movejob.h @@ -21,6 +21,7 @@ #define game_logic_movejobsH #include "game/logic/endmoveaction.h" +#include "utility/direction.h" #include "utility/position.h" #include @@ -172,7 +173,7 @@ class cMoveJob /** movement points, that are taken to the next turn, to prevent that the player looses movement points due to rounding issues */ unsigned int savedSpeed = 0; /** direction the vehicle must be rotated to, before moving */ - std::optional nextDir; + std::optional nextDir; /** 100 ms timer tick */ unsigned int timer100ms = 1; /** 50 ms timer tick */ diff --git a/src/lib/resources/buildinguidata.cpp b/src/lib/resources/buildinguidata.cpp index df4fbfc2..ca8939c8 100644 --- a/src/lib/resources/buildinguidata.cpp +++ b/src/lib/resources/buildinguidata.cpp @@ -183,7 +183,7 @@ void sBuildingUIData::render_simple (SDL_Surface& surface, const SDL_Rect& dest, //------------------------------------------------------------------------------ void sBuildingUIData::render_simple (SDL_Surface& surface, const SDL_Rect& dest, float zoomFactor, const cBuilding& building, unsigned long long animationTime, int alpha) const { - int frameNr = building.dir; + int frameNr = toUnderlyingType (building.dir); if (hasFrames && staticData.isAnimated && cSettings::getInstance().isAnimations() && building.isDisabled() == false) { frameNr = (animationTime % hasFrames); diff --git a/src/lib/resources/vehicleuidata.cpp b/src/lib/resources/vehicleuidata.cpp index 6cf889f2..755ec2f9 100644 --- a/src/lib/resources/vehicleuidata.cpp +++ b/src/lib/resources/vehicleuidata.cpp @@ -58,31 +58,31 @@ void sVehicleUIData::render_shadow (const cVehicle& vehicle, const cMapView& map if (vehicle.alphaEffectValue && cSettings::getInstance().isAlphaEffects()) { - SDL_SetSurfaceAlphaMod (shw[vehicle.dir].get(), narrow_cast (vehicle.alphaEffectValue / 5)); + SDL_SetSurfaceAlphaMod (shw[toUnderlyingType (vehicle.dir)].get(), narrow_cast (vehicle.alphaEffectValue / 5)); } else { - SDL_SetSurfaceAlphaMod (shw[vehicle.dir].get(), 50); + SDL_SetSurfaceAlphaMod (shw[toUnderlyingType (vehicle.dir)].get(), 50); } SDL_Rect tmp = dest; // draw shadow if (vehicle.getFlightHeight() > 0) { - int high = ((int) (Round (shw_org[vehicle.dir]->w * zoomFactor) * (vehicle.getFlightHeight() / 64.0f))); + int high = ((int) (Round (shw_org[toUnderlyingType (vehicle.dir)]->w * zoomFactor) * (vehicle.getFlightHeight() / 64.0f))); tmp.x += high; tmp.y += high; - blitWithPreScale (*shw_org[vehicle.dir], *shw[vehicle.dir], nullptr, surface, &tmp, zoomFactor); + blitWithPreScale (*shw_org[toUnderlyingType (vehicle.dir)], *shw[toUnderlyingType (vehicle.dir)], nullptr, surface, &tmp, zoomFactor); } else if (vehicle.getStaticData().animationMovement) { - const Uint16 size = narrow_cast (img_org[vehicle.dir]->h * zoomFactor); + const Uint16 size = narrow_cast (img_org[toUnderlyingType (vehicle.dir)]->h * zoomFactor); SDL_Rect r = {Sint16 (vehicle.WalkFrame * size), 0, size, size}; - blitWithPreScale (*shw_org[vehicle.dir], *shw[vehicle.dir], &r, surface, &tmp, zoomFactor); + blitWithPreScale (*shw_org[toUnderlyingType (vehicle.dir)], *shw[toUnderlyingType (vehicle.dir)], &r, surface, &tmp, zoomFactor); } else - blitWithPreScale (*shw_org[vehicle.dir], *shw[vehicle.dir], nullptr, surface, &tmp, zoomFactor); + blitWithPreScale (*shw_org[toUnderlyingType (vehicle.dir)], *shw[toUnderlyingType (vehicle.dir)], nullptr, surface, &tmp, zoomFactor); } //------------------------------------------------------------------------------ @@ -262,7 +262,7 @@ void sVehicleUIData::drawOverlayAnimation (SDL_Surface& surface, const SDL_Rect& void render_simple (const cVehicle& vehicle, SDL_Surface& surface, const SDL_Rect& dest, float zoomFactor, int alpha) { auto* uiData = UnitsUiData.getVehicleUI (vehicle.getStaticUnitData().ID); - uiData->render_simple (surface, dest, zoomFactor, vehicle.getStaticData(), vehicle.getOwner(), vehicle.dir, vehicle.WalkFrame, alpha); + uiData->render_simple (surface, dest, zoomFactor, vehicle.getStaticData(), vehicle.getOwner(), toUnderlyingType (vehicle.dir), vehicle.WalkFrame, alpha); } //------------------------------------------------------------------------------ diff --git a/src/lib/utility/direction.cpp b/src/lib/utility/direction.cpp new file mode 100644 index 00000000..f0b85c2d --- /dev/null +++ b/src/lib/utility/direction.cpp @@ -0,0 +1,91 @@ +/*************************************************************************** + * Mechanized Assault and Exploration Reloaded Projectfile * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "direction.h" + +#include "utility/position.h" + +#include + +namespace +{ + + //-------------------------------------------------------------------------- + EDirection degreeToDirection (double angle) + { + if (angle < 0) + { + angle = 360. + fmod (angle, 360.); + } + if (angle >= 360.) + { + angle = 360. + fmod (angle, 360.); + } + if (angle <= 22.5) + return EDirection::North; + else if (angle <= 67.5) + return EDirection::NorthEast; + else if (angle <= 112.5) + return EDirection::East; + else if (angle <= 157.5) + return EDirection::SouthEast; + else if (angle <= 202.5) + return EDirection::South; + else if (angle <= 247.5) + return EDirection::SouthWest; + else if (angle <= 292.5) + return EDirection::West; + else if (angle <= 337.5) + return EDirection::NorthWest; + else + return EDirection::North; + } + + //-------------------------------------------------------------------------- + EDirection radianToDirection (double angle) + { + const auto degree_by_radian = 360 / (2 * M_PI); // 57.29577951f; + return degreeToDirection (angle * degree_by_radian); + } + +} // namespace + +//------------------------------------------------------------------------------ +std::optional directionFromOffset (const cPosition& offset) +{ + if (offset == cPosition{0, 0}) { return std::nullopt; } + + return radianToDirection (atan2 ((double) offset.x(), (double) -offset.y())); +} + +//------------------------------------------------------------------------------ +cPosition offsetFromDirection (EDirection direction) +{ + static const cPosition offsets[8] = { + {0, -1}, + {1, -1}, + {1, 0}, + {1, 1}, + {0, 1}, + {-1, 1}, + {-1, 0}, + {-1, -1}}; + + return offsets[toUnderlyingType (direction)]; +} diff --git a/src/lib/utility/direction.h b/src/lib/utility/direction.h new file mode 100644 index 00000000..49bbe76c --- /dev/null +++ b/src/lib/utility/direction.h @@ -0,0 +1,57 @@ +/*************************************************************************** + * Mechanized Assault and Exploration Reloaded Projectfile * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef utility_directionH +# define utility_directionH + +#include "utility/tounderlyingtype.h" + +#include + +class cPosition; + +enum class EDirection +{ + North = 0, + NorthEast = 1, + East = 2, + SouthEast = 3, + South = 4, + SouthWest = 5, + West = 6, + NorthWest = 7 +}; + +//----------------------------------------------------------------------------- +inline EDirection& operator++ (EDirection& dir) +{ + return dir = EDirection ((toUnderlyingType (dir) + 1) % 8); +} + +//----------------------------------------------------------------------------- +inline EDirection& operator-- (EDirection& dir) +{ + return dir = EDirection ((toUnderlyingType (dir) - 1 + 8) % 8); +} + +std::optional directionFromOffset(const cPosition& offset); + +cPosition offsetFromDirection (EDirection); + +#endif diff --git a/src/ui/graphical/game/control/gameguicontroller.cpp b/src/ui/graphical/game/control/gameguicontroller.cpp index 6e223072..37d3f35c 100644 --- a/src/ui/graphical/game/control/gameguicontroller.cpp +++ b/src/ui/graphical/game/control/gameguicontroller.cpp @@ -997,29 +997,29 @@ void cGameGuiController::connectClient (cClient& client) { switch (vehicle.dir) { - case 0: - map.addEffect (std::make_shared (vehiclePixelPos + cPosition (0, -10), 0)); + case EDirection::North: + map.addEffect (std::make_shared (vehiclePixelPos + cPosition (0, -10), EDirection::North)); break; - case 4: - map.addEffect (std::make_shared (vehiclePixelPos + cPosition (0, 10), 0)); + case EDirection::South: + map.addEffect (std::make_shared (vehiclePixelPos + cPosition (0, 10), EDirection::North)); break; - case 2: - map.addEffect (std::make_shared (vehiclePixelPos + cPosition (10, 0), 2)); + case EDirection::East: + map.addEffect (std::make_shared (vehiclePixelPos + cPosition (10, 0), EDirection::East)); break; - case 6: - map.addEffect (std::make_shared (vehiclePixelPos + cPosition (-10, 0), 2)); + case EDirection::West: + map.addEffect (std::make_shared (vehiclePixelPos + cPosition (-10, 0), EDirection::East)); break; - case 1: - map.addEffect (std::make_shared (vehiclePixelPos + cPosition (10, -10), 1)); + case EDirection::NorthEast: + map.addEffect (std::make_shared (vehiclePixelPos + cPosition (10, -10), EDirection::NorthEast)); break; - case 5: - map.addEffect (std::make_shared (vehiclePixelPos + cPosition (-10, 10), 1)); + case EDirection::SouthWest: + map.addEffect (std::make_shared (vehiclePixelPos + cPosition (-10, 10), EDirection::NorthEast)); break; - case 3: - map.addEffect (std::make_shared (vehiclePixelPos + cPosition (10, 10), 3)); + case EDirection::SouthEast: + map.addEffect (std::make_shared (vehiclePixelPos + cPosition (10, 10), EDirection::SouthEast)); break; - case 7: - map.addEffect (std::make_shared (vehiclePixelPos + cPosition (-10, -10), 3)); + case EDirection::NorthWest: + map.addEffect (std::make_shared (vehiclePixelPos + cPosition (-10, -10), EDirection::SouthEast)); break; } } @@ -1027,15 +1027,14 @@ void cGameGuiController::connectClient (cClient& client) { switch (vehicle.dir) { - case 1: - case 5: - map.addEffect (std::make_shared (vehiclePixelPos, 1)); + case EDirection::NorthEast: + case EDirection::SouthWest: + map.addEffect (std::make_shared (vehiclePixelPos, EDirection::NorthEast)); break; - case 3: - case 7: - map.addEffect (std::make_shared (vehiclePixelPos, 3)); + case EDirection::SouthEast: + case EDirection::NorthWest: + map.addEffect (std::make_shared (vehiclePixelPos, EDirection::SouthEast)); break; - ; } } }); diff --git a/src/ui/graphical/game/drawfxeffect.cpp b/src/ui/graphical/game/drawfxeffect.cpp index 8d531bf2..5188312a 100644 --- a/src/ui/graphical/game/drawfxeffect.cpp +++ b/src/ui/graphical/game/drawfxeffect.cpp @@ -33,7 +33,7 @@ namespace CHECK_SCALING (*images[1], *images[0], zoom); SDL_Rect src; - src.x = (int) (images[0]->w * zoom * fx.getDir() / 8); + src.x = (int) (images[0]->w * zoom * toUnderlyingType (fx.getDir()) / 8); src.y = 0; src.w = images[1]->w / 8; src.h = images[1]->h; @@ -87,7 +87,7 @@ namespace CHECK_SCALING (*images[1], *images[0], zoom); SDL_Rect src; - src.x = fx.getDir() * images[1]->w / 8; + src.x = toUnderlyingType (fx.getDir()) * images[1]->w / 8; src.y = 0; src.h = images[1]->h; src.w = images[1]->w / 8; @@ -127,7 +127,7 @@ namespace SDL_Rect src; src.y = 0; - src.x = images[1]->w * fx.dir / 4; + src.x = images[1]->w * toUnderlyingType (fx.dir) / 4; src.w = images[1]->w / 4; src.h = images[1]->h; SDL_Rect dest = {destination.x(), destination.y(), 0, 0}; diff --git a/src/ui/graphical/game/temp/drawingcache.cpp b/src/ui/graphical/game/temp/drawingcache.cpp index e513460c..e01ec5e7 100644 --- a/src/ui/graphical/game/temp/drawingcache.cpp +++ b/src/ui/graphical/game/temp/drawingcache.cpp @@ -75,11 +75,11 @@ void sDrawingCacheEntry::init (const cVehicle& vehicle, const cMapView& map, con //determine needed size of the surface auto* uiData = UnitsUiData.getVehicleUI (vehicle.getStaticUnitData().ID); - int height = narrow_cast (std::max (uiData->img_org[vehicle.dir]->h * zoom, uiData->shw_org[vehicle.dir]->h * zoom)); - int width = narrow_cast (std::max (uiData->img_org[vehicle.dir]->w * zoom, uiData->shw_org[vehicle.dir]->w * zoom)); + int height = narrow_cast (std::max (uiData->img_org[toUnderlyingType (vehicle.dir)]->h * zoom, uiData->shw_org[toUnderlyingType (vehicle.dir)]->h * zoom)); + int width = narrow_cast (std::max (uiData->img_org[toUnderlyingType (vehicle.dir)]->w * zoom, uiData->shw_org[toUnderlyingType (vehicle.dir)]->w * zoom)); if (vehicle.getFlightHeight() > 0) { - int shwOff = narrow_cast (Round (narrow_cast (uiData->img_org[vehicle.dir]->w * zoom)) * (vehicle.getFlightHeight() / 64.0f)); + int shwOff = narrow_cast (Round (narrow_cast (uiData->img_org[toUnderlyingType (vehicle.dir)]->w * zoom)) * (vehicle.getFlightHeight() / 64.0f)); height += shwOff; width += shwOff; } diff --git a/src/ui/graphical/game/temp/drawingcache.h b/src/ui/graphical/game/temp/drawingcache.h index 3d021380..6b092fac 100644 --- a/src/ui/graphical/game/temp/drawingcache.h +++ b/src/ui/graphical/game/temp/drawingcache.h @@ -22,6 +22,7 @@ #include "SDLutility/uniquesurface.h" #include "game/data/units/unitdata.h" +#include "utility/direction.h" #include @@ -73,7 +74,7 @@ struct sDrawingCacheEntry //common properties sID id; cPlayer* owner = nullptr; - int dir = 0; + EDirection dir = EDirection::North; double zoom = 0.; unsigned long long lastUsed = 0; diff --git a/src/ui/graphical/game/widgets/debugoutputwidget.cpp b/src/ui/graphical/game/widgets/debugoutputwidget.cpp index e30f7aff..9ba0f7d0 100644 --- a/src/ui/graphical/game/widgets/debugoutputwidget.cpp +++ b/src/ui/graphical/game/widgets/debugoutputwidget.cpp @@ -57,6 +57,24 @@ namespace strStream << x; return "0x" + strStream.str(); } + + //-------------------------------------------------------------------------- + std::string asString(EDirection dir) + { + switch (dir) + { + case EDirection::North: return "North"; + case EDirection::NorthEast: return "NorthEast"; + case EDirection::East: return "East"; + case EDirection::SouthEast: return "SouthEast"; + case EDirection::South: return "South"; + case EDirection::SouthWest: return "SouthWest"; + case EDirection::West: return "West"; + case EDirection::NorthWest: return "NorthWest"; + } + return "???"; + } + } // namespace //------------------------------------------------------------------------------ @@ -461,7 +479,7 @@ void cDebugOutputWidget::traceVehicle (const cVehicle& vehicle, cPosition& drawP font->showText (drawPosition, tmpString, eUnicodeFontType::LatinSmallWhite); drawPosition.y() += 8; - tmpString = "dir: " + std::to_string (vehicle.dir) + " moving: +" + std::to_string (vehicle.isUnitMoving()) + " mjob: " + pToStr (vehicle.getMoveJob()) + " speed: " + std::to_string (vehicle.data.getSpeed()); + tmpString = "dir: " + asString (vehicle.dir) + " moving: +" + std::to_string (vehicle.isUnitMoving()) + " mjob: " + pToStr (vehicle.getMoveJob()) + " speed: " + std::to_string (vehicle.data.getSpeed()); font->showText (drawPosition, tmpString, eUnicodeFontType::LatinSmallWhite); drawPosition.y() += 8; @@ -516,7 +534,7 @@ void cDebugOutputWidget::traceBuilding (const cBuilding& building, cPosition& dr font->showText (drawPosition, s, eUnicodeFontType::LatinSmallWhite); drawPosition.y() += 8; - s = "dir: " + std::to_string (building.dir) + " on sentry: +" + std::to_string (building.isSentryActive()) + " sub_base: " + pToStr (building.subBase); + s = "dir: " + asString (building.dir) + " on sentry: +" + std::to_string (building.isSentryActive()) + " sub_base: " + pToStr (building.subBase); font->showText (drawPosition, s, eUnicodeFontType::LatinSmallWhite); drawPosition.y() += 8; diff --git a/tests/tests/utility/direction.cpp b/tests/tests/utility/direction.cpp new file mode 100644 index 00000000..f7e04751 --- /dev/null +++ b/tests/tests/utility/direction.cpp @@ -0,0 +1,120 @@ +/*************************************************************************** + * Mechanized Assault and Exploration Reloaded Projectfile * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "utility/direction.h" + +#include "utility/position.h" + +#include + +#if 1 // TODO: move it in appropriate header and fix conflict with toString +namespace doctest +{ + template + struct StringMaker> + { + static String convert (const std::optional& value) + { + if (value) + { + return toString (*value); + } + return "std::nullopt"; + } + }; + + template <> + struct StringMaker + { + static String convert (const cPosition& value) + { + String res = "{"; + res += toString (value.x()); + res += ", "; + res += toString (value.y()); + res += "}"; + return res; + } + }; +} // namespace doctest +#endif + +//------------------------------------------------------------------------------ +TEST_CASE ("IncrementDirection") +{ + EDirection dir = EDirection::North; + + CHECK (++dir == EDirection::NorthEast); + CHECK (++dir == EDirection::East); + CHECK (++dir == EDirection::SouthEast); + CHECK (++dir == EDirection::South); + CHECK (++dir == EDirection::SouthWest); + CHECK (++dir == EDirection::West); + CHECK (++dir == EDirection::NorthWest); + CHECK (++dir == EDirection::North); +} +//------------------------------------------------------------------------------ +TEST_CASE ("DecrementDirection") +{ + EDirection dir = EDirection::North; + + CHECK (--dir == EDirection::NorthWest); + CHECK (--dir == EDirection::West); + CHECK (--dir == EDirection::SouthWest); + CHECK (--dir == EDirection::South); + CHECK (--dir == EDirection::SouthEast); + CHECK (--dir == EDirection::East); + CHECK (--dir == EDirection::NorthEast); + CHECK (--dir == EDirection::North); +} + +//------------------------------------------------------------------------------ +TEST_CASE ("directionFromOffset") +{ + CHECK (directionFromOffset ({0, 0}) == std::nullopt); + + CHECK (directionFromOffset ({0, -1}) == EDirection::North); + CHECK (directionFromOffset ({1, -1}) == EDirection::NorthEast); + CHECK (directionFromOffset ({1, 0}) == EDirection::East); + CHECK (directionFromOffset ({1, 1}) == EDirection::SouthEast); + CHECK (directionFromOffset ({0, 1}) == EDirection::South); + CHECK (directionFromOffset ({-1, 1}) == EDirection::SouthWest); + CHECK (directionFromOffset ({-1, 0}) == EDirection::West); + CHECK (directionFromOffset ({-1, -1}) == EDirection::NorthWest); + + CHECK (directionFromOffset ({-1, -3}) == EDirection::North); + CHECK (directionFromOffset ({0, -3}) == EDirection::North); + CHECK (directionFromOffset ({1, -3}) == EDirection::North); + CHECK (directionFromOffset ({2, -3}) == EDirection::NorthEast); + CHECK (directionFromOffset ({3, -3}) == EDirection::NorthEast); +} + +//------------------------------------------------------------------------------ +TEST_CASE ("offsetFromDirection") +{ + // TODO: handle `toString` conflict between doctest and our own version + CHECK (static_cast (offsetFromDirection (EDirection::North) == cPosition (0, -1))); + CHECK (static_cast (offsetFromDirection (EDirection::NorthEast) == cPosition (1, -1))); + CHECK (static_cast (offsetFromDirection (EDirection::East) == cPosition (1, 0))); + CHECK (static_cast (offsetFromDirection (EDirection::SouthEast) == cPosition (1, 1))); + CHECK (static_cast (offsetFromDirection (EDirection::South) == cPosition (0, 1))); + CHECK (static_cast (offsetFromDirection (EDirection::SouthWest) == cPosition (-1, 1))); + CHECK (static_cast (offsetFromDirection (EDirection::West) == cPosition (-1, 0))); + CHECK (static_cast (offsetFromDirection (EDirection::NorthWest) == cPosition (-1, -1))); +}