/// @brief Gets mathematically correct modulo calculation. /// @details Assumes the modulo is positive. /// @code /// SSVU_ASSERT(getMod(-2, 12) == 10); /// @endcode /// @param mVal Left side of operation. /// @param mUB Right side of operation. Must be positive. /// @return Returns the mathematically correct mA % mB. template<typename T1, typename T2> inline Common<T1, T2> getMod(const T1& mVal, const T2& mUB) { SSVU_ASSERT(mUB > 0); auto result(((mVal % mUB) + mUB) % mUB); SSVU_ASSERT(result >= 0 && result < mUB); return result; }
/// @brief Wraps a value between two other values. /// @details Assumes mUB - mLB is positive. /// @code /// SSVU_ASSERT(getMod(-2, 0, 12) == 9); /// @endcode /// @param mVal Index value to wrap. /// @param mLB Lower bound of possible indices (inclusive). /// @param mUB Upper bound of possible indices (exclusive). /// @return Returns the wrapped index value. template<typename T1, typename T2, typename T3> inline Common<T1, T2, T3> getMod(T1 mVal, const T2& mLB, const T3& mUB) noexcept { SSVU_ASSERT(mLB < mUB); const auto& rangeSize(mUB - mLB); SSVU_ASSERT(rangeSize > 0 && rangeSize + 1 != 0); if(mVal < mLB) mVal += rangeSize * ((mLB - mVal) / rangeSize + 1); return mLB + ((mVal - mLB) % rangeSize); }
inline void run() { SSVU_ASSERT(gameEngine != nullptr); while(gameEngine->isRunning()) { if(mustRecreate) recreateWindow(); renderWindow.setActive(true); this->clear(); gameEngine->refreshTimer(); auto tempMs(HRClock::now()); { runEvents(); gameEngine->runUpdate(); } msUpdate = std::chrono::duration_cast<FTDuration>( HRClock::now() - tempMs) .count(); tempMs = HRClock::now(); { gameEngine->runDraw(); renderWindow.display(); } msDraw = std::chrono::duration_cast<FTDuration>( HRClock::now() - tempMs) .count(); gameEngine->runFPS(); } }
SSVU_INLINE Wait::Wait(Timeline &mTimeline, FT mTime) noexcept : Command{mTimeline}, time{mTime}, currentTime{mTime} { SSVU_ASSERT(time >= 0); }
inline static bool SSVU_ATTRIBUTE(pure) areArrItemsOfType(const Val& mV) noexcept { SSVU_ASSERT(mV.is<Arr>()); for(const auto& v : mV.getArr()) if(!v.template isNoNum<T>()) return false; return true; }
inline static EnableIf < TI<sizeof...(TArgs), bool> isTpl(const Val& mV) noexcept { SSVU_ASSERT(mV.is<Arr>() && mV.getArr().size() > TI); if(!mV[TI].isNoNum<TplArg<TI, Tpl<TArgs...>>>()) return false; return isTpl<TI + 1, TArgs...>(mV); }
inline Value getPop() noexcept { SSVU_ASSERT(!stack.empty()); auto result(stack.back()); stack.pop_back(); --baseOffset; return result; }
inline char getEscapeSequence(char mC) noexcept { SSVU_ASSERT(isValidEscapeSequenceChar(mC)); switch(mC) { case '"': case '\\': case '/': return mC; case 'b': return '\b'; case 'f': return '\f'; case 'n': return '\n'; case 'r': return '\r'; case 't': return '\t'; default: SSVU_UNREACHABLE(); } }
inline auto refreshImplLoop(SizeT& mSizeNext, const TF1& mFAliveChk, const TF2& mFSwap, const TF3& mFDeinit) noexcept { SizeT iD{0}, iA{mSizeNext - 1}; while(true) { // Find dead item from left for(; true; ++iD) { // Order matters. if(SSVU_UNLIKELY(iD > iA)) { // No more dead items return iD; } if(SSVU_UNLIKELY(!mFAliveChk(iD))) break; } // Find alive item from right for(; true; --iA) { // Order matters. if(SSVU_UNLIKELY(mFAliveChk(iA))) break; mFDeinit(iA); if(SSVU_UNLIKELY(iA <= iD)) { // No more alive items return iD; } } SSVU_ASSERT(!mFAliveChk(iD) && mFAliveChk(iA)); mFSwap(iD, iA); mFDeinit(iA); // Move both iterators ++iD; --iA; } return iD; }
inline void runEvents() { SSVU_ASSERT(gameEngine != nullptr); sf::Event event; while(renderWindow.pollEvent(event)) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-enum" switch(event.type) { case sf::Event::Closed: gameEngine->stop(); break; case sf::Event::GainedFocus: focus = true; break; case sf::Event::LostFocus: inputState.reset(); focus = false; break; case sf::Event::KeyPressed: inputState[event.key.code] = true; break; case sf::Event::KeyReleased: inputState[event.key.code] = false; break; case sf::Event::MouseButtonPressed: inputState[event.mouseButton.button] = true; break; case sf::Event::MouseButtonReleased: inputState[event.mouseButton.button] = false; break; case sf::Event::TouchBegan: inputState.getFinger(event.touch.finger) = true; break; case sf::Event::TouchEnded: inputState.getFinger(event.touch.finger) = false; break; default: break; } gameEngine->handleEvent(event); } #pragma GCC diagnostic pop }
inline Str readStr() { // Skip opening '"' ++idx; // Find end index of the string auto end(idx); auto sz(0u); for(; true; ++end, ++sz) { // End of the string if(getC(end) == '"') break; // Skip non-escape sequences if(getC(end) != '\\') continue; // Skip escape sequences ++end; SSVU_ASSERT(isValidEscapeSequenceChar(getC(end))); } // Reserve memory for the string (BOTTLENECK) Str result; result.reserve(sz); for(; idx < end; ++idx) { // Not an escape sequence if(!isC('\\')) { result += getC(); continue; } // Escape sequence: skip '\' ++idx; // Convert escape sequence result += getEscapeSequence(getC()); } // Skip closing '"' ++idx; return result; }
inline void refreshImpl(SizeT& mSize, SizeT& mSizeNext, const TF1& mFAliveChk, const TF2& mFSwap, const TF3& mFDeinit) noexcept { if(SSVU_UNLIKELY(mSizeNext == 0)) { mSize = 0; return; } auto iD(refreshImplLoop(mSizeNext, mFAliveChk, mFSwap, mFDeinit)); mSize = mSizeNext = iD; #if defined(SSVU_DEBUG) for(auto i(0u); i < iD; ++i) { SSVU_ASSERT(mFAliveChk(i)); } #endif }
template<typename T> inline static void fromVal(T&& mV, Type& mX) { SSVU_ASSERT(mV.getArr().size() >= TS); for(auto i(0u); i < TS; ++i) mX[i] = moveIfRValue<decltype(mV)>(mV[i].template as<TItem>()); }
inline void linkTo(const NodePtr& mNode, TArgs&&... mArgs) { SSVU_ASSERT(TGraph::isNodeValid(mNode)); TGraph::StorageNodeBase::emplaceLink(mNode, FWD(mArgs)...); }
inline char getC() const noexcept { SSVU_ASSERT(idx >= 0 && idx < src.size()); return src[idx]; }
inline GraphLink(const NodePtr& mNode) noexcept : node{mNode} { SSVU_ASSERT(TGraph::isNodeValid(node)); }
inline const NodePtr& getNode() const noexcept { SSVU_ASSERT(TGraph::isNodeValid(node)); return node; }
/// @brief Clamps a numeric value. /// @param mValue Reference to the value. (will be modified) /// @param mMin Lower bound. /// @param mMax Upper bound. template<typename T1, typename T2, typename T3> inline void clamp(T1& mValue, const T2& mMin, const T3& mMax) noexcept { SSVU_ASSERT(mMin <= mMax); if(mValue < mMin) mValue = mMin; else if(mValue > mMax) mValue = mMax; }
inline const NodePtr& getLastAddedNode() noexcept { SSVU_ASSERT(!nodes.empty()); return nodes.back(); }
/// @brief Gets a random integer value between [mMin and mMax). /// @tparam T Type of integer value. (default int) /// @param mMin Lower inclusive bound. /// @param mMax Upper exclusive bound. /// @return Returns a random integer value, between [mMin and mMax). template<typename T = int> inline T getRnd(const T& mMin, const T& mMax) { SSVU_ASSERT(mMin < mMax); return RndDistributionI<T>(mMin, mMax - 1)(getRndEngine()); }
/// @brief Gets a random real value between [mMin and mMax]. /// @tparam T Type of real value. (default float) /// @param mMin Lower inclusive bound. /// @param mMax Upper inclusive bound. /// @return Returns a random real value, between [mMin and mMax]. template<typename T = float> inline T getRndR(const T& mMin, const T& mMax) { SSVU_ASSERT(mMin <= mMax); return RndDistributionR<T>{mMin, mMax}(getRndEngine()); }
inline void stop() noexcept { SSVU_ASSERT(gameEngine != nullptr); gameEngine->stop(); }
inline char getC(SizeT mIdx) const noexcept { SSVU_ASSERT(mIdx >= 0 && mIdx < src.size()); return src[mIdx]; }
/// @brief Gets a 3D index from an 1D index. /// @details Useful when dealing with "implicit 3D" arrays, that are stored as 1D arrays. /// @param mIdx 1D index. /// @param mCols Number of columns of the 2D array. /// @param mRows Number of rows of the 3D array. /// @return Returns a 3D index (under the form of an std::tuple) for an "implicit 3D" array. template<typename T1, typename T2, typename T3> inline auto get3DIdxFrom1D(const T1& mIdx, const T2& mCols, const T3& mRows) noexcept { SSVU_ASSERT(mIdx > 0 && mRows != 0 && mCols != 0); Common<T1, T2, T3> y{mIdx / mCols}; return std::make_tuple(y, y % mRows, mIdx / (mCols * mRows)); }
/// @brief Gets a 2D index from an 1D index. /// @details Useful when dealing with "implicit 2D" arrays, that are stored as 1D arrays. /// @param mIdx 1D index. /// @param mCols Number of columns of the 2D array. /// @return Returns a 2D index (under the form of an std::tuple) for a 2D array with `mCols` columns. template<typename T1, typename T2> inline auto get2DIdxFrom1D(const T1& mIdx, const T2& mCols) noexcept { SSVU_ASSERT(mIdx > 0 && mCols != 0); Common<T1, T2> y{mIdx / mCols}; return std::make_tuple(mIdx - y * mCols, y); }
inline void pop() noexcept { SSVU_ASSERT(!stack.empty()); stack.pop_back(); }
inline auto getC(SizeT mIdx) const noexcept { SSVU_ASSERT(mIdx < src.size()); return src[mIdx]; }
SSVU_INLINE auto& getLastDataRef() { SSVU_ASSERT(!getStack().empty()); return getStack().back(); }