From 3f6e04b751c305b657fe46b031017ac347ee5878 Mon Sep 17 00:00:00 2001 From: sam-astro <77079540+sam-astro@users.noreply.github.com> Date: Sat, 15 Jan 2022 12:56:44 -0500 Subject: [PATCH] Finished pong clone, added full text functionality, added icon, and completed v1.0.0 of Slang --- Slang/Main.cpp | 247 +- Slang/{ => Pong-Project}/arial.ttf | Bin Slang/{ => Pong-Project}/circle.png | Bin Slang/Pong-Project/net.png | Bin 0 -> 93 bytes Slang/{ => Pong-Project}/script.slg | 38 +- Slang/{ => Pong-Project}/square.png | Bin Slang/{builtin.slg => SLB.h} | 11 +- Slang/Slang.rc | 71 + Slang/Slang.vcxproj | 8 +- Slang/Slang.vcxproj.filters | 18 +- Slang/builtin.h | 26 +- Slang/graphics.h | 63 +- Slang/icon.ico | Bin 0 -> 819262 bytes Slang/olcPixelGameEngine.h | 5603 --------------------------- Slang/resource.h | 16 + 15 files changed, 357 insertions(+), 5744 deletions(-) rename Slang/{ => Pong-Project}/arial.ttf (100%) rename Slang/{ => Pong-Project}/circle.png (100%) create mode 100644 Slang/Pong-Project/net.png rename Slang/{ => Pong-Project}/script.slg (85%) rename Slang/{ => Pong-Project}/square.png (100%) rename Slang/{builtin.slg => SLB.h} (89%) create mode 100644 Slang/Slang.rc create mode 100644 Slang/icon.ico delete mode 100644 Slang/olcPixelGameEngine.h create mode 100644 Slang/resource.h diff --git a/Slang/Main.cpp b/Slang/Main.cpp index b5ed737..4c3489e 100644 --- a/Slang/Main.cpp +++ b/Slang/Main.cpp @@ -9,6 +9,14 @@ #include #include #include +#include +#include + +#if defined(__unix__) +#include +#elif defined(_MSC_VER) +#include +#endif #include "eval.h" #include "strops.h" @@ -16,6 +24,11 @@ #include "main.h" #include "anyops.h" +#include "SLB.h" + +#define DEVELOPER_MESSAGES false +#define EXAMPLE_PROJECT false + using namespace std; using namespace boost; @@ -80,7 +93,6 @@ vector VarValues(const vector& varNames, unordered_map& v string expression = trim(ex); bool inQuotes = false; +#if DEVELOPER_MESSAGES == true InterpreterLog("OLDEXPRESSION: |" + expression + "|"); +#endif bool isFunc = IsFunction(split(expression, '(')[0]); bool isSLB = split(expression, '.')[0] == "SLB"; @@ -173,10 +187,8 @@ boost::any EvalExpression(const string& ex, unordered_map& v i++; } - //InterpreterLog(split(expression, '(')[0] + " " + AnyAsString(GetVariableValue(split(argContents, ',')[0], variableValues))); string returnVal = AnyAsString(ExecuteFunction(name, VarValues(split(argContents, ','), variableValues))); newExpression += returnVal; - //cout << newExpression << endl; } else if (split(name, '.')[0] == "SLB" && !inQuotes) { @@ -207,7 +219,9 @@ boost::any EvalExpression(const string& ex, unordered_map& v newExpression += expression[i]; } } +#if DEVELOPER_MESSAGES == true InterpreterLog("NEW EXPRESSION: |" + newExpression + "|"); +#endif bool addStrings = false; for (int i = 0; i < (int)newExpression.size(); i++) @@ -244,7 +258,9 @@ bool BooleanLogic(const string& valA, const string& determinant, const string& v { boost::any valARealValue = EvalExpression(valA, variableValues); boost::any valBRealValue = EvalExpression(valB, variableValues); - //InterpreterLog(AnyAsString(valARealValue) + " " + determinant + " " + AnyAsString(valBRealValue) + " : " + AnyAsString(valA) + " " + determinant + " " + AnyAsString(valB) + " : " + to_string(AnyAsString(valARealValue) == AnyAsString(valBRealValue))); +#if DEVELOPER_MESSAGES == true + InterpreterLog(AnyAsString(valARealValue) + " " + determinant + " " + AnyAsString(valBRealValue) + " : " + AnyAsString(valA) + " " + determinant + " " + AnyAsString(valB) + " : " + to_string(AnyAsString(valARealValue) == AnyAsString(valBRealValue))); +#endif if (determinant == "==") return any_compare(valARealValue, valBRealValue); else if (determinant == "!=") @@ -265,89 +281,86 @@ bool BooleanLogic(const string& valA, const string& determinant, const string& v int varOperation(const vector& str, unordered_map& variableValues) { - if (IsVar(str[0], variableValues)) + if (IsVar(str[0], variableValues)) + { + // Checks if type is simple, like int or string + if (any_type(variableValues[str[0]]) <= 3) { - // Checks if type is simple, like int or string - if (any_type(variableValues[str[0]]) <= 3) - { - if (str[1] == "=") - variableValues[str[0]] = EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues); - else if (str[1] == "+=") - variableValues[str[0]] = EvalExpression(str[0] + "+(" + unWrapVec(vector(str.begin() + 2, str.end())) + ")", variableValues); - else if (str[1] == "-=") - variableValues[str[0]] = AnyAsFloat(variableValues[str[0]]) - AnyAsFloat(EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues)); - else if (str[1] == "*=") - variableValues[str[0]] = AnyAsFloat(variableValues[str[0]]) * AnyAsFloat(EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues)); - else if (str[1] == "/=") - variableValues[str[0]] = AnyAsFloat(variableValues[str[0]]) / AnyAsFloat(EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues)); - else - LogWarning("unrecognized operator \'" + str[1] + "\'"); - } - // Else it is a Vec2. No other complex class can be operated on it's base form (ex. you can't do: Sprite += Sprite) - else if(any_type(variableValues[str[0]]) == 5) - { - boost::any otherExpression = EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues); - if (str[1] == "=") - variableValues[str[0]] = otherExpression; - else if (str[1] == "+=") - variableValues[str[0]] = AnyAsVec2(variableValues[str[0]]) + AnyAsVec2(otherExpression); - else if (str[1] == "-=") - variableValues[str[0]] = AnyAsVec2(variableValues[str[0]]) - AnyAsVec2(otherExpression); - else if (str[1] == "*=") - variableValues[str[0]] = AnyAsVec2(variableValues[str[0]]) * AnyAsFloat(otherExpression); - else if (str[1] == "/=") - variableValues[str[0]] = AnyAsVec2(variableValues[str[0]]) / AnyAsFloat(otherExpression); - else - LogWarning("unrecognized operator \'" + str[1] + "\'"); - } - return 0; + if (str[1] == "=") + variableValues[str[0]] = EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues); + else if (str[1] == "+=") + variableValues[str[0]] = EvalExpression(str[0] + "+(" + unWrapVec(vector(str.begin() + 2, str.end())) + ")", variableValues); + else if (str[1] == "-=") + variableValues[str[0]] = AnyAsFloat(variableValues[str[0]]) - AnyAsFloat(EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues)); + else if (str[1] == "*=") + variableValues[str[0]] = AnyAsFloat(variableValues[str[0]]) * AnyAsFloat(EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues)); + else if (str[1] == "/=") + variableValues[str[0]] = AnyAsFloat(variableValues[str[0]]) / AnyAsFloat(EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues)); + else + LogWarning("unrecognized operator \'" + str[1] + "\'"); } - else if (IsVar(str[0], globalVariableValues)) + // Else it is a Vec2. No other complex class can be operated on it's base form (ex. you can't do: Sprite += Sprite) + else if (any_type(variableValues[str[0]]) == 5) { - // Checks if type is simple, like int or string - if (any_type(globalVariableValues[str[0]]) <= 3) - { - if (str[1] == "=") - globalVariableValues[str[0]] = EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues); - else if (str[1] == "+=") - globalVariableValues[str[0]] = EvalExpression(str[0] + "+(" + unWrapVec(vector(str.begin() + 2, str.end())) + ")", variableValues); - else if (str[1] == "-=") - globalVariableValues[str[0]] = AnyAsFloat(globalVariableValues[str[0]]) - AnyAsFloat(EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues)); - else if (str[1] == "*=") - globalVariableValues[str[0]] = AnyAsFloat(globalVariableValues[str[0]]) * AnyAsFloat(EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues)); - else if (str[1] == "/=") - globalVariableValues[str[0]] = AnyAsFloat(globalVariableValues[str[0]]) / AnyAsFloat(EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues)); - else - LogWarning("unrecognized operator \'" + str[1] + "\'"); - } - // Else it is a Vec2. No other complex class can be operated on it's base form (ex. you can't do: Sprite += Sprite) - else if (any_type(globalVariableValues[str[0]]) == 5) - { - boost::any otherExpression = EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues); - if (str[1] == "=") - globalVariableValues[str[0]] = otherExpression; - else if (str[1] == "+=") - globalVariableValues[str[0]] = AnyAsVec2(globalVariableValues[str[0]]) + AnyAsVec2(otherExpression); - else if (str[1] == "-=") - globalVariableValues[str[0]] = AnyAsVec2(globalVariableValues[str[0]]) - AnyAsVec2(otherExpression); - else if (str[1] == "*=") - globalVariableValues[str[0]] = AnyAsVec2(globalVariableValues[str[0]]) * AnyAsFloat(otherExpression); - else if (str[1] == "/=") - globalVariableValues[str[0]] = AnyAsVec2(globalVariableValues[str[0]]) / AnyAsFloat(otherExpression); - else - LogWarning("unrecognized operator \'" + str[1] + "\'"); - } - return 0; + boost::any otherExpression = EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues); + if (str[1] == "=") + variableValues[str[0]] = otherExpression; + else if (str[1] == "+=") + variableValues[str[0]] = AnyAsVec2(variableValues[str[0]]) + AnyAsVec2(otherExpression); + else if (str[1] == "-=") + variableValues[str[0]] = AnyAsVec2(variableValues[str[0]]) - AnyAsVec2(otherExpression); + else if (str[1] == "*=") + variableValues[str[0]] = AnyAsVec2(variableValues[str[0]]) * AnyAsFloat(otherExpression); + else if (str[1] == "/=") + variableValues[str[0]] = AnyAsVec2(variableValues[str[0]]) / AnyAsFloat(otherExpression); + else + LogWarning("unrecognized operator \'" + str[1] + "\'"); } - LogWarning("uninitialized variable or typo in \'" + str[0] + "\'"); - return 1; + return 0; + } + else if (IsVar(str[0], globalVariableValues)) + { + // Checks if type is simple, like int or string + if (any_type(globalVariableValues[str[0]]) <= 3) + { + if (str[1] == "=") + globalVariableValues[str[0]] = EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues); + else if (str[1] == "+=") + globalVariableValues[str[0]] = EvalExpression(str[0] + "+(" + unWrapVec(vector(str.begin() + 2, str.end())) + ")", variableValues); + else if (str[1] == "-=") + globalVariableValues[str[0]] = AnyAsFloat(globalVariableValues[str[0]]) - AnyAsFloat(EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues)); + else if (str[1] == "*=") + globalVariableValues[str[0]] = AnyAsFloat(globalVariableValues[str[0]]) * AnyAsFloat(EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues)); + else if (str[1] == "/=") + globalVariableValues[str[0]] = AnyAsFloat(globalVariableValues[str[0]]) / AnyAsFloat(EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues)); + else + LogWarning("unrecognized operator \'" + str[1] + "\'"); + } + // Else it is a Vec2. No other complex class can be operated on it's base form (ex. you can't do: Sprite += Sprite) + else if (any_type(globalVariableValues[str[0]]) == 5) + { + boost::any otherExpression = EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues); + if (str[1] == "=") + globalVariableValues[str[0]] = otherExpression; + else if (str[1] == "+=") + globalVariableValues[str[0]] = AnyAsVec2(globalVariableValues[str[0]]) + AnyAsVec2(otherExpression); + else if (str[1] == "-=") + globalVariableValues[str[0]] = AnyAsVec2(globalVariableValues[str[0]]) - AnyAsVec2(otherExpression); + else if (str[1] == "*=") + globalVariableValues[str[0]] = AnyAsVec2(globalVariableValues[str[0]]) * AnyAsFloat(otherExpression); + else if (str[1] == "/=") + globalVariableValues[str[0]] = AnyAsVec2(globalVariableValues[str[0]]) / AnyAsFloat(otherExpression); + else + LogWarning("unrecognized operator \'" + str[1] + "\'"); + } + return 0; + } + LogWarning("uninitialized variable or typo in \'" + str[0] + "\'"); + return 1; } boost::any ProcessLine(const vector>& words, int lineNum, unordered_map& variableValues) { - //InterpreterLog(unWrapVec(words[lineNum])); - //InterpreterLog(AnyAsString(GetVariableValue("out", variableValues))); - if (words[lineNum][0][0] == '/' && words[lineNum][0][1] == '/') return nullType; @@ -369,10 +382,10 @@ boost::any ProcessLine(const vector>& words, int lineNum, unorder // Check if it is function else if (IsFunction(trim(split(words[lineNum][0], '(')[0]))) { - if(count(words[lineNum][0], '(') >0 && count(words[lineNum][0], ')') > 0) - ExecuteFunction(trim(split(words[lineNum][0], '(')[0]), vector()); + if (count(words[lineNum][0], '(') > 0 && count(words[lineNum][0], ')') > 0) + ExecuteFunction(trim(split(words[lineNum][0], '(')[0]), vector()); else - ExecuteFunction(trim(split(words[lineNum][0], '(')[0]), VarValues(split(RMParenthesis("(" + split(unWrapVec(rangeInVec(words[lineNum], 0, (int)words[lineNum].size()-1)), '(')[1]), ','), variableValues)); + ExecuteFunction(trim(split(words[lineNum][0], '(')[0]), VarValues(split(RMParenthesis("(" + split(unWrapVec(rangeInVec(words[lineNum], 0, (int)words[lineNum].size() - 1)), '(')[1]), ','), variableValues)); return nullType; } @@ -527,7 +540,9 @@ boost::any ExecuteFunction(const string& functionName, const vector& for (int i = 0; i < (int)inputVarVals.size(); i++) { variableValues[args[i]] = inputVarVals[i]; - //cout << functionName + " \x1B[33m" << args[i] << " == " << AnyAsString(inputVarVals[i]) << "\033[0m\t\t" << endl; +#if DEVELOPER_MESSAGES == true + cout << functionName + " \x1B[33m" << args[i] << " == " << AnyAsString(inputVarVals[i]) << "\033[0m\t\t" << endl; +#endif } //Iterate through all lines in function @@ -575,7 +590,6 @@ int parseSlang(string script) } args = trim(replace(args, functName + " ", "")); - //InterpreterLog(args); functionContents.push_back(split(args, ',')); int numOfBrackets = 1; @@ -605,32 +619,71 @@ int parseSlang(string script) } // Executes main, which is the starting function - ExecuteFunction("Main", vector {"hi", 0}); + ExecuteFunction("Main", vector {}); return 0; } int main(int argc, char* argv[]) { - // Get builtin script contents - ifstream builtin("../Slang/builtin.slg"); - stringstream builtinString; - builtinString << builtin.rdbuf(); - // Gathers builtin functions and variables - GetBuiltins(builtinString.str()); + GetBuiltins(SLBContents); functionValues = builtinFunctionValues; globalVariableValues = builtinVarVals; - // Get default script contents - ifstream script("../Slang/script.slg"); stringstream scriptString; - scriptString << script.rdbuf(); +#if EXAMPLE_PROJECT == false + // If scriptname is supplied and not in developer mode + if (argc > 1) + { + std::string scriptPath = argv[1]; +#if DEVELOPER_MESSAGES == true + cout << scriptPath << endl; +#endif + std::string projectDirectory = scriptPath.substr(0, scriptPath.find_last_of("/\\")); + std::wstring_convert> converter; + std::wstring wide = converter.from_bytes(projectDirectory); - while (true) { - system("pause"); - break; +#if defined(__unix__) + chdir(projectDirectory); +#elif defined(_MSC_VER) + LPCWSTR s = wide.c_str(); + SetCurrentDirectory(s); +#endif + + // Get script contents + ifstream script(scriptPath); + scriptString << script.rdbuf(); } + else + { + LogCriticalError("No script provided! Please drag and drop .SLG file over interpreter executable file, or provide it's path as a command-line argument."); + system("pause"); + exit(1); + } +#else + // If in developer mode + std::string scriptPath = "./script.slg"; +#if DEVELOPER_MESSAGES == true + cout << scriptPath << endl; +#endif + std::string projectDirectory = scriptPath.substr(0, scriptPath.find_last_of("/\\")); + std::wstring_convert> converter; + std::wstring wide = converter.from_bytes(projectDirectory); + +#if defined(__unix__) + chdir(projectDirectory); +#elif defined(_MSC_VER) + LPCWSTR s = wide.c_str(); + SetCurrentDirectory(s); +#endif + // Get script contents + ifstream script(scriptPath); + scriptString << script.rdbuf(); +#endif + + system("pause"); + parseSlang(scriptString.str()); return 0; diff --git a/Slang/arial.ttf b/Slang/Pong-Project/arial.ttf similarity index 100% rename from Slang/arial.ttf rename to Slang/Pong-Project/arial.ttf diff --git a/Slang/circle.png b/Slang/Pong-Project/circle.png similarity index 100% rename from Slang/circle.png rename to Slang/Pong-Project/circle.png diff --git a/Slang/Pong-Project/net.png b/Slang/Pong-Project/net.png new file mode 100644 index 0000000000000000000000000000000000000000..4e3ca5676f11559efb6685b26e317cf5a87335fa GIT binary patch literal 93 zcmeAS@N?(olHy`uVBq!ia0vp^j6m$b!3HF63(7ix1d4;)ofy`glX(f`h= SCREENH { @@ -157,8 +161,6 @@ func HandleBallBounce() ballSpr.position = ballPos ballVelocity = NVec2(ballSpeed, 0) scoreTextTwo.content = Round(scoreTwo) - print scoreTextTwo.content - SLB.Graphics.LoadText(scoreTextTwo) } // Checks if ball is in player 2 goal if ballX > SCREENW @@ -170,8 +172,6 @@ func HandleBallBounce() ballSpr.position = ballPos ballVelocity = NVec2(-ballSpeed, 0) scoreTextOne.content = Round(scoreOne) - print scoreTextOne.content - SLB.Graphics.LoadText(scoreTextOne) } // Checks if colliding with left paddle diff --git a/Slang/square.png b/Slang/Pong-Project/square.png similarity index 100% rename from Slang/square.png rename to Slang/Pong-Project/square.png diff --git a/Slang/builtin.slg b/Slang/SLB.h similarity index 89% rename from Slang/builtin.slg rename to Slang/SLB.h index 369fbb9..2d710be 100644 --- a/Slang/builtin.slg +++ b/Slang/SLB.h @@ -1,3 +1,8 @@ +#include + +using namespace std; + +std::string SLBContents = R"( // Default variables, can be overwritten // if re-initialized or changed float PI = 3.14159265358979323846264338 @@ -27,14 +32,14 @@ func Tan(input) // Sigmoid activation function func Sigmoid(input) { - float out = 1 / (1+EulersNumber^-input) + float out = 1 / (1 + EulersNumber ^ -input) return out } // Hyperbolic tangent activation function func Tanh(input) { - float out = ((EulersNumber^input)-(EulersNumber^-input))/((EulersNumber^input)+(EulersNumber^-input)) + float out = ((EulersNumber ^ input) - (EulersNumber ^ -input)) / ((EulersNumber ^ input) + (EulersNumber ^ -input)) return out } @@ -132,3 +137,5 @@ func GetKey(keyName) bool b = SLB.Input.GetKey(keyName) return b } +)" +; \ No newline at end of file diff --git a/Slang/Slang.rc b/Slang/Slang.rc new file mode 100644 index 0000000..543dc7a --- /dev/null +++ b/Slang/Slang.rc @@ -0,0 +1,71 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON "icon.ico" + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Slang/Slang.vcxproj b/Slang/Slang.vcxproj index 75335ed..fea4e71 100644 --- a/Slang/Slang.vcxproj +++ b/Slang/Slang.vcxproj @@ -158,19 +158,23 @@ - + + - + + + + diff --git a/Slang/Slang.vcxproj.filters b/Slang/Slang.vcxproj.filters index 75811a9..5773c0f 100644 --- a/Slang/Slang.vcxproj.filters +++ b/Slang/Slang.vcxproj.filters @@ -26,9 +26,6 @@ - - Header Files - Source Files @@ -44,10 +41,15 @@ Source Files + + Source Files + + + Header Files + - @@ -56,5 +58,13 @@ Source Files + + Resource Files + + + + + Resource Files + \ No newline at end of file diff --git a/Slang/builtin.h b/Slang/builtin.h index 929f3eb..10e5077 100644 --- a/Slang/builtin.h +++ b/Slang/builtin.h @@ -15,6 +15,8 @@ #include #include +#define DEVELOPER_MESSAGES false + using namespace std; using namespace boost; @@ -144,11 +146,11 @@ boost::any EditClassSubComponent(boost::any value, string oper, boost::any other bool AxisAlignedCollision(const Sprite& a, const Sprite& b) // AABB - AABB collision { // collision x-axis? - bool collisionX = a.position.x + a.scale.x >= b.position.x && - b.position.x + b.scale.x >= a.position.x; + bool collisionX = a.position.x + a.scale.x / 2 >= b.position.x - b.scale.x / 2 && + b.position.x + b.scale.x / 2 >= a.position.x - a.scale.x; // collision y-axis? - bool collisionY = a.position.y + a.scale.y >= b.position.y && - b.position.y + b.scale.y >= a.position.y; + bool collisionY = a.position.y + a.scale.y / 2 >= b.position.y - b.scale.y / 2 && + b.position.y + b.scale.y / 2 >= a.position.y - a.scale.y / 2; //// collision x-axis? //bool collisionX = a.position.x - a.scale.x / 2 >= b.position.x + b.scale.x / 2 || @@ -162,9 +164,9 @@ bool AxisAlignedCollision(const Sprite& a, const Sprite& b) // AABB - AABB colli } // Initial script processing, which loads variables and functions from builtin -int GetBuiltins(const string& s) +int GetBuiltins(std::string s) { - string script = replace(s, " ", "\t"); + std::string script = replace(s, " ", "\t"); vector lines = split(script, '\n'); vector> words; @@ -184,7 +186,9 @@ int GetBuiltins(const string& s) string functName = split(words[lineNum][1], '(')[0]; +#if DEVELOPER_MESSAGES == true InterpreterLog("Load builtin function " + functName); +#endif string args = ""; for (int w = 1; w < (int)words[lineNum].size(); w++) // Get all words from the instantiation line: these are the args @@ -211,22 +215,30 @@ int GetBuiltins(const string& s) if (words[lineNum][0] == "string") { builtinVarVals[words[lineNum][1]] = StringRaw(words[lineNum][3]); +#if DEVELOPER_MESSAGES == true InterpreterLog("Load builtin variable " + words[lineNum][1]); +#endif } else if (words[lineNum][0] == "int") { builtinVarVals[words[lineNum][1]] = stoi(words[lineNum][3]); +#if DEVELOPER_MESSAGES == true InterpreterLog("Load builtin variable " + words[lineNum][1]); +#endif } else if (words[lineNum][0] == "float") { builtinVarVals[words[lineNum][1]] = stof(words[lineNum][3]); +#if DEVELOPER_MESSAGES == true InterpreterLog("Load builtin variable " + words[lineNum][1]); +#endif } else if (words[lineNum][0] == "bool") { builtinVarVals[words[lineNum][1]] = stob(words[lineNum][3]); +#if DEVELOPER_MESSAGES == true InterpreterLog("Load builtin variable " + words[lineNum][1]); +#endif } //else // LogWarning("unrecognized type \'" + words[lineNum][0] + "\' on line: " + to_string(lineNum)); @@ -253,7 +265,9 @@ boost::any SLBFunction(const string& name, const vector& args) return abs(AnyAsFloat(args[0])); else if (name == "SLB.Graphics.Init") { +#if DEVELOPER_MESSAGES == true InterpreterLog("Init graphics"); +#endif initGraphics(StringRaw(AnyAsString(args[0])), AnyAsInt(args[1]), AnyAsInt(args[2])); } else if (name == "SLB.Graphics.Sprite") diff --git a/Slang/graphics.h b/Slang/graphics.h index df95bf4..f079253 100644 --- a/Slang/graphics.h +++ b/Slang/graphics.h @@ -372,11 +372,20 @@ public: return 0; } + int Update() + { + SDL_Surface* surface = loadSurface(path); + SDL_DestroyTexture(texture); + texture = SDL_CreateTextureFromSurface(gRenderer, surface); + SDL_FreeSurface(surface); + return 0; + } + int Draw() { - // rect.x = static_cast(position.x); - rect.x = position.x; - rect.y = position.y; + // Centers + rect.x = position.x - (rect.w / 2); + rect.y = position.y - (rect.h / 2); SDL_RenderCopy(gRenderer, texture, NULL, &rect); return 0; } @@ -504,25 +513,52 @@ public: { rect.x = position.x; rect.y = position.y; - // rect.y = static_cast(position.y); + + font = TTF_OpenFont(pathToFont.c_str(), fontSize); Load(); } int Load() { - TTF_Font* font = TTF_OpenFont(pathToFont.c_str(), fontSize); - - SDL_Color color = {r, g, b}; + SDL_Color color = { r, g, b }; SDL_Surface* surface = TTF_RenderText_Solid(font, content.c_str(), color); + SDL_DestroyTexture(texture); texture = SDL_CreateTextureFromSurface(gRenderer, surface); TTF_SizeText(font, content.c_str(), &rect.w, &rect.h); + scale.x = rect.w; scale.y = rect.h; - + + // Centers + position.x = rect.x - (rect.w / 2); + position.y = rect.y - (rect.h / 2); + + SDL_FreeSurface(surface); + return 0; + } + + int Update() + { + SDL_Color color = { r, g, b }; + + SDL_Surface* surface = TTF_RenderText_Solid(font, content.c_str(), color); + + SDL_DestroyTexture(texture); + texture = SDL_CreateTextureFromSurface(gRenderer, surface); + + TTF_SizeText(font, content.c_str(), &rect.w, &rect.h); + + scale.x = rect.w; + scale.y = rect.h; + + // Centers + position.x = rect.x - (rect.w / 2); + position.y = rect.y - (rect.h / 2); + SDL_FreeSurface(surface); return 0; } @@ -612,7 +648,7 @@ public: else if (oper == "/=") fontSize /= AnyAsFloat(otherVal); } - + else if (componentName == "r") { if (oper == "=") @@ -652,7 +688,7 @@ public: else if (oper == "/=") b /= AnyAsFloat(otherVal); } - + else if (componentName == "content") { if (oper == "=") @@ -660,6 +696,9 @@ public: else if (oper == "+=") content += AnyAsString(otherVal); } + + // Updates changes to text + Update(); return *this; } @@ -673,6 +712,8 @@ public: std::string content; std::string pathToFont; + TTF_Font* font; + std::string path; SDL_Rect rect{}; SDL_Texture* texture; @@ -980,7 +1021,7 @@ int initGraphics(std::string windowTitle, int width, int height) //Get window surface gScreenSurface = SDL_GetWindowSurface(gWindow); - + ExecuteFunction("Start", vector {}); updateLoop(); diff --git a/Slang/icon.ico b/Slang/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c9ad4c8c6b0765a3c596d66c701877c6cfdca9cc GIT binary patch literal 819262 zcmeF)d4Ob9dH;Vw5Jf=QVVGeb7+{8d-$B{;b=Zb|Q4}$vXj~Ey(L`hsQ8W+~#II3| zOBC0L8jX7lxJ6@3eqT-E63u4f_ut>E>N9=3RCib3n(nIU={XP0om;ogUCy(-pXZ!= ztLiCFS;K!FS>q{NJY~^M>pf*N>x()Y*7W~c+1`2Cd9O;-nk%R!9Tuth3Gxd3`-h)j-w2at%E2zym83j&-N1fzfGT zt+m$r(1$)$gSKj*YGB4S;9EsU7s>TZRRe=-V8(sE91xqlRkLK-IwTG%%t_uBWLQ7+eF#9((NYJii{fYM^T1$Rm#&JTB`VRRg2Yz)ybi zlNzB_162dV)4-2@^rKNkay>`YzV|A)@9dV;|zoMyXU<4Yd{2w~Y>InwdK;{1kV!xiEYM}Cea5&XHhOU9i|B9xnfe~n+@_*e{eX}J%+A<%KwU{s(}$`pz?p{EUPCNTmzN=BZ&Qa zimHLi|H0u@_ZYecD*r2*ss=`&fy)1(v#g$Aa1B)ck0AEzDXIo4{|ARt-DBt)sQj;J zsu~!91}guD&a!%f!8K6%KZ4k=r>GjJ{2v@nb&sKIpz^<>scK*Z8mRmqI?L(_2G>C4 z{|I8go}y}?@_%qR)jfu;fy)1irmBGvXrS_c=q#%z7+eFD|09U~dWx!n%KyRPRQDLV z1}gt6nyLmypn=N&p|h->U~mmo{*NH`>nW-RD*p$EQ{7|e8mRoQXsQ|*fd(r7ht9Hk zg26RV`9Ff#ucxRQsQe!sPIZr=YoPMKqN!?N1RALPA3DqG2?p0d<^Kp`zn-FMpz?ok zIMqFdu7S$`il(Z85onV|A)@9dV;|zoMyXU<4Yd{2w~Y>InwdK;{1kV!xiEYM}Cea5&XHhOU9i|B9xn zfe~n+@_*e{eX}J%+A<%KwU{s(}$`pz?p{EUPCN zTmzN=BZ&QaimHLi|H0u@_ZYecD*r2*ss=`&fy)1(v#g$Aa1B)ck0AEzDXIo4{|ARt z-DBt)sQj;Jsu~!91}guD&a!%f!8K6%KZ4k=r>GjJ{2v@nb&sKIpz^<>scK*Z8mRmq zI?L(_2G>C4{|I8go}y}?@_%qR)jfu;fy)1irmBGvXrS_c=q#%z7+eFD|09U~dWx!n z%KyRPRQDLV24*M!fAgE)TzcuHix)3mYpu1OTBT~Bg$B6Fk|j$nzx;B}GYivA<^RxG zR!=av24)-ofBy5I58jEoN7X>D27dX=U(Tv5Q2F0$q566jXkb?H|7Sn@+3vgVJ`1!` zFJYC}z}|cB{fl4xV)mqf%Kuf~rRHJ>W)=UBJo3o7sN>qFm7{@Uk3F{Xf90sYo^=(` z!0h3Fo%vZsTxM0e%wPTLSFT4xXc@3`ZR)kf>Jqa)J5J@?#G`9C6E z*VC+m8kjx&Km72+S3&*NOPxjdK%M`YvmUl8cWjpM|A{A_Se3O{uYLAv;QQbIe&zq{HBY^=m8yYR!vDt~e|)8? zp`LusYT&!y{ch#|oV9N4*z`0|`9D4V)xBrC1}gt&yM^lYtvn6P68;O)m8XMx=DDna zAO7%%wf>*W2CjXZh6ZK{|Lep5)39OPwVwul{p(*>{`XT*eQ!0^z%1dv2;FL{tyW_@ z)UFRj1KV%E{cM20YpBov48_3ppwrX9?BV~XKJ}^Tb*1iIHPEYp&wcK5mH)j;sjsUB z#xyW{_%BY!E~@@pH83+8m?iM9_5aM6vmRxpH889AzxLW|&vgIl@m60Ath3HKmH(@+ z`D@QxXkb?HU$owK+ifkbx;CjAm_h@0-F4UOfq$+4r?6IidnIXLw((!2Y`Nu@D@hsk zv~xrQo+X%7@UQ%zBaSuax-kp+|8M{HZ@1iX%be46?a+uc@cP%ke)fFrb4f$>`JWM) zzn*3g4a`FRmyoF|RReQI1C{@SV5znpkp?RN=L}(LM`p7ID*s1Bw0fFBG*J0Jo1sy! zZO&+*@_!I4)wUzjK;{3OAx!PaY}P>K|A>fIPcw)HD*tCQH0rg@84Xna4}zuIc0?Me z{GT&~sU4Zk8mRmq5z*>t2GKy}|7?avy|y`{fy)0uuvFWQNCTDsbA~XrBePipmH#6m zT0PAm8mRoA&CsaVHfJ8mH)FD8ui-d zj0P(I2f|8E=ezZ1#-JXt1c^1qE^bXMm~*w&7$h8k!(^Mu~sXJd2Urs^vH zM?|!Gnn5&x|4*j-?_T|;h5W8{W33q-B^;WuP}i#lW|antm&SD)zWk)s$pYd@jmBP- zD9FJp{|CWRZ95_j9C+Y?y!yY#PCF17^qOX>8^3$xb&a_k-f%;K;{34h*nQChz9VVhyQ0+{$rf{dtVvX{6BJ>X2?vK z4pz}z&_sISM$bwUBdf!DPxs5qrmjW|_CBYZjQOPmYP#-ZLBZ`47UTWjmdQe3-+lKT zBra>)s)12x;D7@T@O}Hg?8-03gQwo{UXP4KwrQf`Sq`VEm#sM&Cn=i zQh{X9kqGS*!XtRANr`fKR;E)HHo>wwtjE5|{!JbBB#7Zzvm_vAY+gVvEGFQPh2gxE zuf}OPFqteUxP8K+_`j7B0#@v3_pu20RYIWhe-t>Y=NM1}`|rO$Xa3hQ`IoNz^ONPL zPnIXY*?DEBu>*Aeb-)r4%~(hvBpnF7g25h2tgJ*ZAu+~RhRI@u8)YRm;cB}qddNz& zG~UYe85$4I$|Zdl>^F_rG#`f~0X0W03=iJp(uoMm@owL7*MN+P^B~ycS!_r zQ7Ewz69H(1NP$L^81j@W4Az480*zARb?U`&lK#%U<+zS^HWXE4r32Q6iF>Tf&;`!a z9%4^_EHK`Pv12S4BeI|i>tVifCET_Q>Zc5fo+#)Efkc5rm2|N8-g^%{nCl6u1_soC zVQHcJkAM8*e;AV=b>;8-O4?)(`;r_=Xq2STOb242nF|Uu+9ZORLPQT_a##o|I6{mv zFliDa&tC0lK{VV<_hmTHW1u!KCp4mi=PXSl z!oEu?<_nw)@Jk8AflE+~1w`lHIQ;l!vrXBtgn+}Ui1ylRuK`1|Hm@2Ox(1Xb-y4(f ze)qfIZpaf)Jn{JC;$2djf9E^jv3rj6cU}3x4}S2&uE-%mMvNph5*G;yaUuGeNugAa zl7h%BD2C;N=ozUPQ!AEgzlRmM46HyiWOIdBk#S8b!&2|*4k^`O3IEk(rF1-cu#9Fh z5^R>ibePnTq|lS}**7L`Y1|&>XZNNNrJH7v9}C8at|}K98-?ESRZD)$f4~3z@5h-r zc5(8q%TKT<4K&VPc6X2x0{nl^J@*`XFxL}Q4GgFOWeLXrZCAeat#AEJSH9VmZ+zn$ zU+*g#q-FN9dyZ4`Lc*aL3(Z_mh6&+nim@Ob!5rZ^1hal>GzRDCH3dxlDdCv$bBr@v7`bkZCNmUNI@&&o*z$pSHP zv2RP`_T+Bn3;8p?2>DawCwG?$-^HZ1kNIMn06*ML3B-X*P{g+_xjKiBBrL{Wb)uj& zO9}3=FjuUj?k3>_r;nc1n&_ImBd{>KdxL3Flbck-EYmty-by$w|xbtt6U|EV@TB56% zSjJ>!$}A%SN|s@h=$l!-7`F@rVzRQdy^I7kZjyg|!JpD1MoiXb!D7DHC>4IVof3!x zPZ$(E&R<@Nf`mXb3K9Y-iX@aJOO^~hnCl6u1_soCvIO1#sw-dk!WTaO`Okl@D~~<) z*k{M&FULvV(T1eGeTl<3nz$$tkxD7CFkvp}p@dK^Q$rx!qy#>vz)Q%Nu>c?te3*>+ z&FyI%fb^gv6{G4DG%`hi{3#RzjnE$Ja6?@eZYcB0jDSp6Bs`hJ%hFRT(Lk97NzqG7 zbXC#;9a1I;(<&=dav2L`tXGU%ESvlula*y`S8{i==Fi}f1s&P(Gh~x@lDLC?87k&; zwXU3jQridkDS-m^1ciub1_g&73yaRbod6gM0fC@p_DTrkXo?~UW%1(01BPa8UNtau z4eYktZXf^n#~*$4(T{!XV;}wKNB`n4{^BED`EXZ0)RhlDS+dbGZ8W!+eI3DZ$R|GW ziBEp=lb`z3r#}7ZPfH!bBsoH8l(@(+LFg%v&_gf=8o@>C##D?+2Za^Qic#!ic)>*w zkwUS_eCye$0eUpiQEL7WBiM(j0(`8%c~{s3g^N#UtNKWAQI?I-(i~qcUCv69K82(V zPU??|X^#$@86mkKBLd1#qL&DoV}V$fy^7ev;?2prlC>SAWNlfdSP<}&snMRZrMAy# zH;d(1W?2plV=14RA|z z|MRYV-~%6c|NGznzOMXPSKix||1(aqJ#93%*CUTSV)q;;p^OqVtkaBuf3w=>DkXOp3(7mZ%Lr6Hg8i6JDWWD}sW4xy65xmS99Vf1XXe|a85G>U z>Hp0r&@y|8sZtjS0l8q&qD2FSW^G(k^ziyW@~VgjkRighNIO(t$uIb&=2uF|a~NN(5q4gbLMA z!ib>E9sq^V&b$FulyVWho0CFiAW-HFn&eM*DDYQjrgTi@pQ)Y{jS^5M2V9K7d$5oB z0Y2Qh_>^|IA+(7hPecbJIy<*ztfL=K$D;?!jQf)7>59_8miDC1%!JUYQk;?@I$417 z%P6lWxl7Z4Ppm{3F62+*&ODDJ@DK$<$=zkhChwHQ?MirEF`q855$0ocsvsWAZ#i%Y z3XV;hK_M=hk|;nLVCxAYi&6v~MnLlpsQqFvOfg;BOYf!3#Q4^Gil9CZ&FAMa&QC1C25> zknxVL5g6%_6ohkT+0(o9+A>OL7QM72H6&3&E*&Nd&??6rh=7NA-YEE*O z-x-T!rVjbbkS#apL;jdg!C)5AVI3UyOYT-`|i8n@|L$qAc94X5E{+YAl#A^ z%o;(5U=)(-F}T1A8A07>T#$5N@UCJkxG3{NAEv&VoRN!btn!AMF+Ly46A4hTfj zjrn0{a$dx64T@Nol==|jQW;8Tl3M$*Qqt>OU1lU0+?7h0a#9Ag7A#7(98S=+` z+(~s&Ea*yDkng3iciHR`=5th45Ps6s02K z{{n5m(5%g?28OPIt+(EqGXwl=A~gC0SLdR4gy`!bZ-T_^+m&>jW@4RK8*IHGq=0h75c z&CXhYN{X(oS@*(#kcu6Z*Tg4))N9H3K9Z#Pf_H6 z2?2pN^kA+hs2Uhh0}B=`;Ku0w@~*t>WiNZ_nA~u~4KEp!-y4&$4V`~A7;F2|8*jWZ zM{uZSQV)|;2~!7|8WOq50vQhEPNK1Z(LGk1s7V&cKmZdI^Xfx| zqFDlv&wT-jpl9B=O!%uI?Hl+zn!^H-v&cA^88&g@O5~Ym{k^FS(YN!N$&7!BV}jE z7V>Alu37UtMakW23^P2g!IdOmyF3?o#^IFwZ~ zf?Ocb1`N&GylP6sB%lum|Tzv` zgPIhF&EZmTk!mo>6aiKTDP4|bOWqIklKN8s$HbJGbWgV|Yu+z+e{j2u@|qKUIA0_; zb|rl|LYsp9GE*m^g!xJciN?4C@H4$56R}`sbz;F3_A#8g1-cDH)9ewS)Nn@uHOBzIFF zV}X(dSXP}+jM3ODcX+i)!EYCX2!@lj9Va(vlDIQi3;B(_3_lg&u0;94f-cWRy5QIg z^8@^pKn3#6py1>jetb-nC}@U&C}^ewxj>)|7@DaRFk8zJNkVh7uxFsH)R|LL$i2L1u?C7651SLb->S zsRNTXrfz}@v&ZGuuz`RXA!C6ud+eo+5`>Wg_^TuhACLt6VZ`{tkxflFku#dUHW+ak z5|gV+x#(&!KhV$x&=A+8l#5}=>bcCIFAAZYQuk7tM8L#!NSQIN6mOI#$x80Z^^5{C z(U-JdvH;6Q7)#A>&}R;?d4oRWXE^Y8K(I*NZCUO-W)cY#iUbZCXr{5f3Lg}Nun;CF|C~3mucu3wE`^BPCohM6UXF)6HYk3N>>9^L86$GPCAK}=!(+LIO7c3JNxXjDU+Y@5RMVE znk`}0I6!RL91Jp05s8DaS28lBa{#;8R}If_jw4f>-cQY+AzPYpBtFBwoTp6haKhC2 zVScHCnw;m-F1YFEV=?i&f1B?n3Q7o+ibyUHXak03ZC*7nbPa5>$tK`Ngr^`q^T{G| z`$)5`eTjqQXy=}LE^HEUk^o8I3k~rlF3K1I2UJIyH5I~QS)6LK?z-y^z4P@1b4~;6 zt+yVkOAQjb!5ZKz^%V~(@;RH#p7Nd(2GoH(M^)y;Y2(9_1qHV+LD38W>l226!wLa` zHs_RGJG9DcVB?K9jx(R$71df1wQfWe*Oal%oqtPXD>;IrB`CTH;jCslD7iqyl|-QK z5|SAvr_SGDhaFaVwbl--R1NsD1%*UPeWeBLb1+3XXDj7BCxm4_*<^mR3gXFJy6NFf z&u^|b+t{TU0;!0ULjrB3s-&L0YG48la7&&Xyc@7L#SnfEV-w zL$i%tvTvfGguq4{Z8TsHYV)drp=*F!#;eEXvzPyyx!f#!rnj4KmHFPXyQ7tGNGz0e zQ0k&m6v+if$7R;^8P9me&^ugDu-a(gna_NtsUGBq{Z#v^4lbt}&)iOCa#G~?0DsD$ zXhuPC{;Xu*L_rCG%Kz2I?^dObZn)uw$BoIcV{*)ZGPd8bV@F#4uf)+z5G6GTie`0G zW=&Tm#no$F6*K_&3XIU6n*n|t5A8W2MDxi&J3LRR({$tF+x=srG;8jgqa_5M{p@G2 z3JR;&xO!;-|BGWkciK`8m2l{Zg=Q}3sg4W{cG_vD)l2oYx5L-KuDk9E{;Bgr{y1I} z`#sE$>lRNQG$zW9Jt0u}KYVM~GYqT&ZaJBUKW8eLY{#-avCtC;3AZw2&Y(asw!sD) z41D!}8HoAKuqduR;Yu?XRQ~rhRDE;hXrS;vUj6(- zPWE@}erMge?>YH<|Lzxkbn(SKe-G|?L22;n1JAI_xknuD2opv@Hjd8)&C#*-HP%>T z<*1;Zb?$0l%{ABTndg}d`;)mY-+sX%rxb6WFdXu(sL}Y;qmLbbD9-<^XFY4~s=4-T z)zH8$Puuv+4R-$hO%DFSj@OLlPvW?D@AKq{^N&2CCoW1xP=+0T_~EOD2J2O>Y8p8D z=%Wks)%o$d9#_2L@RQ%Y=Y?NhbonrX;;~&X`~8E?ShP|7{-0INb>_tNZur!vpSJ!^ zw`_LAFnoKUV}4+dWq-Wi+1DI)a!>RL$wLk~WK~i2)@yGdhhDeQzV)xa255<{R=Qaa zJM1t8#(44{|D0p)*zcT=?0(627mlbFdTg5u-?aI2wqJX=H-e8}Z~H&o^sxN>!KQ~! z_?LIJe8!q21y*2+^Zy{C7KEbN9aIUVq4GCq8%iVEN#K z4<3dRX7HH1J#CY_wm9ZX+btW7issB&DDC2BFQ&a2w8cP&I`q&(n>9bLyJw%JpW5xx zm8jvr-fr2Qn?LuwXYDdjNBT6Lx7PaSZm`Q6H$VF0TQ427U-q)EBlJ0beQQ_F$#q4zOa_oBB&q${}zwJd&eM-FtHyu-~gnbWNcfqZ*U~Xqd zJkUYFb_VOBY1-rFO%I!?e%0d?4cxTpq0_WW%iZ@}XS3ICcKG*3HN8_hxMC}6wpVO& zP|L1qw&T+_eBb=jSL|H<_i}%*{bkgaD@y~sWfiLP`@3AvyWigaN(1DTrP!SJBMZ*y zxvKhdrZn)81!oW2xC)7xy4q15=es*x_40OE;PD-XI3|7X{L_bRpnCij)_^hN`{ti9 zO1E4o=laCfORwH=&lT3vbauR{R{leNLTozL>A&y!&)SvetQ3!(sgnxu{(bcBOr2}8 z<4k!0wZZ5$pyvPj_AB$&)nwi>yzRyUFWvZnQL6gX^%f4VJN3xXz-j9*oN=2T@Qit@ zI{Axx7}}fIpG-xs8~&7UpR-SPRdxF?G~h`dw{(kRtj;SY}%D%i~wvBz4c3&;a<4#@4IKSqukeLa&n^ zW7DUuyQ-*S4qPW~Pil#FZ**(^^X7mm`gGy8&5th1cwpX1eOk`!TVLFESvhZA8NCL4 zOk-vZIya7HjBk#2Nai^HnLEr4Ev*v0IAQWBj+XXaCAvBNi{$eMue+5eMW=tLfsWA9 z3bonHYTyMM?X$Yg^AF^f8vESJGpG09^n@mR)d;^FW6&cieHug$oz@(LHk2&WGH+=;Zqs zo&JfPE_i(5mE+Glvs)h6_1wevJMe(69C+Y?2X)1#$$X)N5B~V}TAq7j6^iuJVRUc&=jJ#vo* zd|ArFTb$n{_2VAGdEN0&Y*ZeMBCJ^KZA^ zcJe?%Kq3$Yixw@~WtUx+2g2eb-niRI|7VwTzPi(8D|XFO?)BG;uDJfN)AL=vzUeE! zGSK|38Q=Yz?|yGC%P)YG9|5uBiw_^l2WqD5!s^`MMTef&^LJk?(pLfXN zo9^J}P>YlRNnBW}k4i)&1tbcD01*PIg82V#yY05e9((M$=broQv(Ke_ANk?kE_h<6 zt7iPNM{vL%M%9+eg_^dB18p_iPf3nZ{B?ZU=J-;uI?|!%RZNT|L z;9$S!cR=pmuhnzo4{dwe?F){%eg2dGZ1K1Cv3KNen`7_rx9xHHyK}qax_`&txn1{f z`{VDN-G6uOaKc?Xo_N>76W_e>#JhJs`R+xhyk*x@)wFNj{fxKmdFH))pZ)fI&$;iw z^Y1_8f z;FKG;Jb2fQ=J^(M;!mUU!)`m zZOZopKf0vVXDgO%u_py~kkOE+w3$Xxf@I#HGj{6gEvg$EMa{Z@88kLcPESpXW{en` z?Z5y2Cdy2m9eL!D^d^n^{aioj>_?n0zWCz&(t3Vu_oh=@eOI1u2yYU;&$=%XA2b9e zZ2z}AT!H-0-)tX0IYfLje{)$(T4F?i#H@m%ghnZc!v9o3l$IfICW4CFo7qYVFyHv< zgHC<_-b4I)t`F|CRO(T2i8XVZ%tWP}-0$G{DKtNP&pLh<`I1X6k)tQnPjh3FQFVRB zHSl{gI@=HUZ<_ZU&bvueROn7*axOd8%@PB-T6Zi*Q#m}1r3nj8F#SiBvWiBXL zKnf^X06@5#l1f%~LfcqetO}luD%}>7bcUqu$xGELLu20X1z&0n)eo zEWqM+3hg!dF9pDhUsv}_yW}+|w)*Z_{BQoE)`aWFKj*pAaf$5v;QUh#dG^+9R3s+L z%k|btad@h82RvciRz5zrO1X z-y9VZ@> zu?)~DazcJy9mfuxO1;_yRAIi_CRJ;xQw#Zdds8z1lcf@fg#YozayuY3WCq2YQ#cqG ziwIT9vLlaw$Nn=jI(}%+3-gt~B@s-R3&Q*?(;kc3DEy*LmjM3Lp7yl0$He+N>#SoV zn9x17Q#d_Sa`YT}tU@V(#mmfIlmALtR+F_;|C>xJg=NU^r4fPUtBz}B%_;d0{-51- zYEtzv+-+MvXQ%ZyWg5U|BVr{|azP@d7_J#3Wkj6Bow}$Q7YT$EKgIJ4|8Y1Y-_$%A zI{8%^E~hLvAid|X^PgBW^;Ars4mRIntdasG9VM1bE#0tG;lDIZ-gWfUqunN~>iW!T z;2rZ$Xc}(C*46wsZ+W=tWxO<9x)i0E_9-=gscM>D4f_NEN`;>ypPzS1{3lBxzse`Y zPsndsTrshbdjUxSnFvZn1SX{@dgEc|d~wMP{0vk18NQMTnz^9K{4~h4LreZkwQ7It z_w#~&w$k#cPkriIYpo>U*}cg&IK4~3o&PuZj97Qre4|= zw*#hR&dd_89xWR1vpb)e_Aj%1Y@2gmw)ug)x709MDT$yIqRlyq3 zweXK)hwYupew-BG$M>>K6d*r8BzYXD+!W)P!XXurA*zT_rSMW@78I{I`mDd)b6O?e zR~B9I{6i=Bp|NH<2=klE*8E?r+6R2(k3o|KY$>AvR%pVc)R2Yz{ZSv(tdwQ(grs|R zS5L-#rtg?6^ZzC>+(R*|fcz$wLCnkCOtB;{Kf2Y!`F;35P58fm!5If{u%O9&)+zi) z{t_=DrCA^~2gS@Z9{Z&bD$$Xch|iZXFZieCY4V@9XTZzbO`uT}GSlAgop8<LKVxO4F64K=hPZ3r|7hN=#GY{tOq2Yuf5AqJGkWFQkvMb6 z-^6~YWKwyi)a1_!m~Qd1{4xHpJ=`wdE98st#2oRod zQ6dE>ijo41i`A2NJomzHES}bbbK|o>6Zk#rZTY`(hyQB+@IS?W)(r|u3TX1bRQ^mr z|6wdEG^Na~t;CfcuLwyP(IiG@?F#&5Qq*E2mclVK0~wP-#0q)IAtz6X7Y+LGzs>Fw zwz+MKqglalEE1#GLc~jA2mG7ZFLjXw9Ro29ZK&-7d4vz%EfXVZo0K=j=dJMb2#t}9 zc1yLx+e@`WMRcgzkpk$DGCiM6pAx}@otkpvS9ZKK0Aw;%mMK<7k*TF=*f$Gg zop^R@%45zr{Z{h>s{E(ad7lOsY_uEi9i#s5{E}aUWOA~&Q056^_ z(u?5Pu#;JRE{!?iOiKOKFe%Y_au^{Fjd5#m(Q${4~iQLj*4|M`jO_j%DPPD^1@><M>0c*MOj$buC}C@3DQ7a(Qb){;$|`{_ooAJBgmQB0oW=}wsV=VG z90EwG$pZHuGVn8^`)<2av;LQ@TmW+jE@5SwPBb~VMB|Js=yL(Z3X`qOEC5&YqkUxS zvnSJY|LtZn7d~_2z@Hn(lbi5wj${Lz)H9{jL>p8oE}giJGsjEOI9{46g`*NtoTZgW zcXKDwtt8mBw`1~xC5MmmCmfO#LjICpY}5==az=;ZqN$4n1-xWjoDp%Rmp!!hmXj{~ z_Ab}-SJZ^>dV0i6@|5t`U8~cfSpcVOquL7wH{@=kr{P#K~ zlXzj5kU!%xcfnF1^;AdEe^CN=O^oPN{M<@Y=%@KIZOYPHVuVnXAj&wm@V_|)gv^B7 z6Dyt$ozR~r-1zPt&H(?ye;(!fU4xT4qqAh%N7hzZc`5b0W520COt+d7d#1X-!oMVo z{Jgh|{NF87`DX%?KdbpuXY%Ku#w1VXEdf76c+L>~icns`8$~p46u+e!Q6z|N%Lo(~ z8Qa!rKsb+OyvT}o`Lo#@o|aNDVCVVHRhW>KEE6x9l`9GV4OAU1brD}JLjaGf`P|AS zmt1oBSXPQUI|+QfG$$j1u^pPc8#<*e@1oR+Y)c zC26j#6`(7uV;%J1CqWixU>DY5XvXrEmYhmqtRu~w#o27}9XJz`s%K*l|KGd&z;&-V zpB0ko*r>g{u$>?jL?8E;hd3#16RsQ!o*vmKr@Yn z6}?4LIN3tzjrKIt*j|nhSC>pp0TDhQ$xF*8cAonF*XMRzrofAEF!0wMcF_eVbt1=+ zP?Y^n=cKNI+`HFVJ+>JCa;5;dnjdGpV%u!FY4gJZ|4vQQl~VbKFlB~0VKpcqbA!Odt5a7J?&clD7FunBBOJ8;F3JwHr-Ty3?+DF>*f0MFHh*1jmOJP}3WYJ7ky2=~%f<>A}VSOIL*7RnDO_AT_xp*DH z8R-mgV5iJqWjJ&7E{C>p@*e)T*`?X$V_TmC{-He+6hkCl*dmd_9I5#gNU;d`3zJ-G z!lWcV+}2GU=j}Ya1PI42l8u%cL^+(F2!uG8w-QYPA<_w??lB!CJ8GH0!GC2r;q#=V z09&p-@{}pRJLpwA9<48tX5gpzcU9uZp`6R5I=k!9LEq!i)Z~Qgt2wu4r2D(Bv&n?I z?X&SK+g(y_V}&#EXZ+7FHq*qs-0SW znQD{&D=_mD6O2#9i4?&iQgTx)ks=pobnLP-5K|z@Wx|9NbYD-!2noH@aoXs5=<Ar87D7(u5co`3y>605`m50dj1hp{N~F+JMEVID+M!3 zVxO5fkCQrrakQl?Nv9Imz949Pfb?vc5df~{2l?{_Q|H+pwP!pVq!hL^;jf+n|4j9R ze;LVwzt}_=E-h3AESKi1;cD?}er$W(0CvJ3AQA<{j#;KI&C5dXf+_jh(Ta&I-vD=1`(3*J&@YBMQQQ_pLm)>=r=hQ;TCFBYfzPf#-q} zo0BaE;y-gV4dkV26K%}=vyR(~tYdl^a9AgS3I1y4m?qc{>UEz)nH{n;*g_}a#weEo znln1Pt8$U9mK(V8V4s@-jzJCP>m8!%CXwlgjncstI~>^K)f2uf{6GDuW52QUV0Zc5 z@D&}Y`7N_z6u>SSg)v8ol#In(8}g?{Dq+I^>74HoeYYN8jl1$v4YEw6bR4%kgQ`I> zTrA}XB@p6fJXtK1ib#Vl`}k7YQ+UV7nT9K?Xa z#W=N#bisPTp!Y1E`rB?-b97I$`=_kGa6+H&vyn>|s_^8%A6tlGhBIf*M=^sfVomf3 z!zBGwGCY%s8Jg)?Tom)?crv2{dzrT7|8SIAPF#~Ri>ov$>v>BvVwW;gUGhTC=%fKx zLZDgRWO$np$W>{nJ+KhhM!e*%u2zHWD(*!vX7A-tnA)r;Bem=(K6` ze~CIDVPY3c>lv4^PI<7zR6}fpIi)ItP|>H?IvdVp=_QF0ETfarFS`cmJi`-2Ffngo z@iNsD=5sugjd};F?r{(fNm7v5?%m6_-LH?Im$$vcNL?Gq+hv0kD;vt)Bevu`sH zc(NlW7RsPN2%xv4WzhfwKPjoi0vk2_Y1M-$#PALbOY-O4NT%C7+)>GH{(sr%B0CxwC7_b3b;^le&=-} zrvtHgelX6rQ{M38c(s=)MKmi}pviwu%hh#32$U%xLjdAlfTbBBE0$Tg{ivxQV=fxD z^36q8o_N&qN!p%7(7OI#!WN8D1pIBmoMNCu&h& z@bnFZMrwZW5C6fRMM@%eG4fF&g$Z1|Ug(kD(Jd3C)VD(UHkP`GhwE};gy)kI0@fYG zTcpH-V~T}L=p_rVEZ=U92>3JU!oIT*1(C82(PVrINHdOV_b==^!RG>QT6`SWA_>ey zr4I5dF_@sUyTH^O+_=Afc_7ytu*`lMmXrz@eyC^{S3H#yIOMKr@+LbszA zu0py*>D=B0x{zNl^rZ=Z$9}=QtP?IBws2|T1}y3sF{ljVrF@U$ zu_@zXKBAUUUXNERTAD6x_lW8=(wVpI%Gid^&wE8gy)2L@D9-PITqr4kM`4#UCIxW& zQu(-~;@gG)NderFfAUNLVi)d8#EULu2%te30>}cyjuBN&q#X0UgQos4dsFi_A8=-Z zvAG=J2$AUkU&r}pkyI&3f__xAsqo3xTk5>*?m7PiNZ_9#fGxs@Edu{UN~V2SK`(SF zUK$`<3K{wj4=Gt^gMFQ4HJv5#f4#PnWwsZyCJ{7a!2umMS;i@n0{AmO7YkBA!hvs- z)J0`!OHu%@wo=6Q@ZavQKJZDCHV-U0+qs%vlrVH^eYBT@*h`3sP0c7lXDNjEWuKze z$yWLSuD&<92JYE%fFW(hvpks_$DafL3}!_cf6lNPp?M$(b2*@hn=x(*ekSI7(81fq zFMlqP%Yp5i;!RwrPKi_!QLSYq+by*rMj6D8#rbi-Fzb;J(6kz4_9X$$9177!yk!fA;W`)0>Pfo2X8;nfiha(pYce0uk(zt6Dg=l1H6 zmMl{L16_fh6)&3!n$l>DqL_fCXo*_D-%@x6^&vd%F_vZFEv*JbQL=*!B-v}lmdz7E z90>zCfF8wR)b{YsA@vt;)hD5`Wy%#!>n(FZenPjY?a9_#>O8x@ZOh}FC9^%L{Bz#G zKLZevqD*qluAd(0pTb27x8zru1L3apA64r$uV$|VNP60gjuILP3PD7)ryQrSuPoqD z$pQ|WSa3$?N(x9xlOX^{<;Gz?--h~#EqSZFYl{YHX`^@JuBRV)%-445d{f+~cDq=x z=$8`_-bHdoJ(X_kWeE;^j}nsRTxtyMC*CGAO4&X+;p#h+YG9A2Z(bBQ<%)Xdz;);G zYMvbUTPBoJ&jkKln)~rL&c@TY8Cx@b>SxvuSZw!hA%DD@OWQXt4fVv)CKPWKZ?{)O zTo8wnMB?!LCq7^J&%5y7HE{dX9=s0H@i|`2lhGdY3HXI*f6ME@x>)XfUppq!|&- za9~--PsS0I&dBr=@;~tB|3z$xtCti|_%9121!%xyZ#c9w3h={8DVdzYi88uO=_Qmj z6H`v@U96x7+DK^;&+!YSQ)+%v#??0_*T5r#Jn(t`vv*Apab_+pnuxMW9R(~xNx8-M z;&KoXmj+vqWMxaH=Wo1DB^j?~UyjX1yRO9afu&mO_Ad zY5UcOOzqOe=O5VX{3iUZuQ2~pc>-hfMqNxG*60Ti6og_k@MozM{7KURz9ojlY9Ug< z%YsC;nU$WUHbat})Ok4oH_)k2k=y8Fihn7Oix&QihBemceEpFv-qL2-7JC#`Pk$w4 zlH(^#0)Oj?6hp8?3TICK<)0uAdLo5;a7ZRlpD;%-NawVdKH7!nCp2V`@uK72rG!KW zox~`Pz+T}$bF=AW{7;p_#}W<&{t1dKB?6pJ3b52NjWY!3dCQFeM@uZ|9qbb;I%}h- zv9A?dq#;=^#fDxBVJVMP{!fnZX}#0l+kH{3cxe{@Jlx9yezt&###$=f$b&r8Q4admHDevzCj z{%@nb?%ZeUPqh>S^$Y>p^B-+wXV@W0(}*WiQype9fha=*%8{2Lh*~m0^O6yzPJl`< zGAn6fzknBK`be_#_@&)fSjy3ymk$s;G#u+_QuU_=o?t z;J@NO<1Y^Ef+a8dq~xM`PNFPH)}brpPa5GmoEOyZ#lDoEs{A3FKv%wE7Bg_cIo3gO!tQ;nmsiw z`SyLzx#rN$#11Fq=R6$t(H`?X!%xoXif8z3@ov-eQ}FX@QA0v?%RpW%XSB<)sRCDp zHW6-?UfOu>0Y^5kr~i601q{l6h$$u)Pe^v++*eeKfVKJ7MGD0DLiA1{~Hl!5l zG9uGA#g-zdMY?$IT{yxtr5w>Iaz=P002q(rMf%G{=eWKV`r{P}iZqMnh~nYHQF_T{&P50m20d5Qn8-Rg)(w(0kY z!j@g=-^^E6`4K2C&D(jn4{f3Tx#ym{bm`Lb&O6VFM1cR^6%pK?-xkm3%zBFdLU=s+ zId%v7UF?hWA~2gVNdF6KzWc`g&K6~n>F}Sn@kCgOub2$#)m^TQ)eHfwCt0(X27(cb zLjETFL){K=yU0b|tY;}B^kjj_5<$WEimf;-$+?^~7gH*VI2HCL1@K#0AY$FSTDE-t zr)i7H)^FPEP{8luzo>C8xaQAJ=TZZIkzzm^_^0}1h=hp-!bHFH$S!ttsg6J-@rZTt zk`7vml*hIk_%nvhj(BL_Z(DQhz(3(oM!`xTA~YdU(z=?Q4tON!V_fK&)q@ z21RTXO7@a>Qgx*J8S=!_;u4kr)1v(3J2$OA*}9+c5K+m&xS;=+s1I6K%s z|NQeW=!%#4&+YlVfpGXA4l83gzXC>o(`9A9SSI2+@W*-XjjKd~S0uUMYdg37Y6u?( z%SkO~mj7sv`7Y8myGRxkiK@_2i52XpV(${Svnae}%?SM3NCM=vEMU*>WeIW9lyaz? z!s+5&F2MC5X9|o!GT7wI5ifb53vT)R|731cw&$b1fxjh@3&3C0$Ud&Y#kf4zoH;jY zr)^~eZ=rl92>fMR3Qreu{bD2+Z&@zhy6uaez4^>W%3m$M%x_ySJ)txInQA{{;qExN zvycK(3rP^A5E24q6oBxdeNsSKW@A|8&P(`{K zVEqRdoMIf^=aPo{))R}azH({j3*5-EWy@rM@IO8u{wrxXIS%*mKe60|zqrPEdVqZa zZYadhBi3zv!5&Aq@6L4xpOO?XJo!l#NBH#AJ4cD}W8sgZEf-jQyQ|`2(>% z4GRAYs0;A<%#275nB%+1a(S-7l_U$`JYOYA1h8qvOYgeQOSf+I`QItcv4tjNw+dAor@9PLi5%$^%3}MCV71}%zG6?vi*Z?! zu80*c<$~ugZhIHtdke3bDgMKzxj1>*(9AWkoojaGK;%+go9&tQVLQPXoOa;e#rrGf zA8L}h^SAAj|FW?xw?FUE#^1-cKkwr^4D-+N8>d@->T)sTR&qVQlzy7-1j6}lXElFr z_@?+*FY#^O&h7a>ol~hofU1RwWMQv@eaA@wX6b-_T-EpPPV<^u z?(SEuZ#q>SwCv)pTzKJy(t*TaXka*;8XoGAU*MYsRnIpQfifxBmkVHDdm@PB4lQXQ zxi9{fDzWKrRKf9XUvSKnSKAMB^}R_n&|j<4u3%3c!husVS+T`-{>)N=zj_8k07Tgj zfL6SWOKTAL=Q!YBygJUzpUIHQnEoA{@?AgUWfsW%fB0V}e>i;j&xZ-}^L$I|qM-1< zS@t*K53Lb;lLGMe6Faw^xt($Ty9PLts^o4WTV?VGj&idm@E3_Jm?i7C3CStdJ7*fa zbkhUO^E7AvbK94P^LBxQ=b|79m%=%QvOi;F*0&|IqXqtUNr4pXmw8u-;BE7!ei&xP z6?^TnONk6n_@7$LNR> zw~5~l>whIH@!j_xepx2}IedzLl}rkrke{~~$(Ce51zz}%t~@-IzewlmCdBBCSg}R% zbImS?wd~`}@*j3wK;b{zS$C;1Um3XZfv)|RFIe;T`Oj^6!K*_vd^Xk7Q~b*a3L6EF z!7>F>ruj71mtIpTsS~7K*5?VC_KBW_iQ?bBXips6p)9SR^sF6Mia!5(pQ~SU-W8Wz za!KKTPyNp=MFIFrYQSE~Az46nAejqF@t@(d3@Ao~28Gao;y?99VGcJVvOt?+qaWt# zdy{COzb~1d&4enqWCdcS34i_^_-FP2|2ejG=^*t?j=+eSL;`1!pr(xLwRck`HwkG{%6>N|79@Fe2Otfj|C0=BCPqQ%Ksg&DMrEPQvAyTBs2dh z{%H@>GV>$1(lAJ(ANX5`K6P&*MSczZ75|0&4o7E=5JPH0o=4cfbXex3iBgy7TLTpW1( z%EJrKgm+G>ZXv3DVkKi(PK#!onGf@1#DhU)KhULg3_2tWc2ojQIG7!O(%HP_k$smH zSLfBb99QS<5ie~N1>UtKV_N~93McRn`MEm9(j>9Y`RUV7Bem&%z7B5s_kUTl@Lw3p zR_tgp*4470ti<}JEXvp|cWuw#z+a+l78@!JYH2groE`{Q)4lsR)k%x?$)F4e!YfMxRy>*E%A60}u`inj z9T3NwsTB}jtwP@q8-xM4{u|A&{HCUYmZBXIz- zpq_OIs#|8V0?kUr$n*wxOwZN~sts;nODg{iW>e4b>WGo7C*@Y+NOrPbPr3ai2cOi0 ze{u5od9pwm1qgFBUgm#jESk(Xm$y?9hbPGb_#n?tc>x-HWziKg%YW6riwytKDXA)U zOgxskJEbW=S;_*n{*SQ~|Gc`%f9U4n(Gsf5p-jx~0!lr=Rrm>o3l`BNSSVRDN|seK z=kI`p|5j#I@tk&c%M3hpiqpt7m0>|0h;sQG_j6L_mo$Cr$m&thmH{y3^#pcbjOk zFFKXuRQ@wcivK46B?2!MJQPLM3VVj8N%>Uq&sBH@JuChN3uj>WGDk|oB}`KGimw&eVd9fffAs9Fi>p=uN8ppxD(Vqu^t=qz}<%{wDoO-4U{^bkbBRAnm+i(2V zZr6PNz!yAt=Jj`7e#0BDdFjg=^3s>S^oAR5_`Tozy%)du#V>r}3!ne|=Rfax&%5fX zt1i3jGLNo#hS}Y#-#_8Pdk?+jqkFZw**gBJ{EX4D`z`;HF3KrFKj29ZQUE7U!4tIz z_QHRzjs~#L+tC#tEQyyw;MVP1JydhkriTri|4ivRGZ`x$lSEiEf>f3N1^!swocRy@ zflCQg;ID8JJ|YGE_>5wJCxAb@r}CFC>?lYaO%e(JeV}-}2M)C5(cLb&cklD=-1nTG zzq|H5*A(L8J6%1{(Rv#Dj9AE@Y6mn3|0kVds4GbVc`KmM{mgw)Lg3HS=pmC}v=R7o zIU)sczcvpW43(2}_24}Q&C6FEa0+~Z7~3Hjzh%iVO_m3IkHL5t#`BO;fGvy*eTKIx zTMqB`J(HzeRL>}f;?JVgVjy5RkTQVj;jRQrBE@ltuM%n*7vr1Vv(M724m;UA;gq78 z4;79-Ldz}2{s@5o*q#o82DirzV#UjDLgDFqk>c$bOgblhM{5W_SsjS}jaGM?qw zWQF;E|GX2g+4`Utz34>>`^oq(Bb4G_%WJQ_R`DPHm*U@txqN!j(`&9@{xXMo>mf_O zxa(lwHSyJ*E)&A2E~tr)TPILY4@s9|K?>mcsdiHTr`iesp_Rj%hat%VLI5rv-?7!j zRQ`{5-4So$-uxM;Gx?vQ-Ink_cYZ-0>J|TdgFl3R97Y*n{*A?1ALUZ~=V)E_cNQx) zNBUR0Tt49shW1YccCq6MDDdt3oL%7GB)=#iNde)%?&C>eMfhKChxPCu{0YsFPW`{t z)Z^k|qjF9jf4s#X+2^9%(+B(r&q02l-Bayj{uyVS0ih}W-Th%!R_5NE9NCQHxHOXm z{#=@b{8rB2aM$r??7!vyEl=9>>6=r&Bcr;)KKxhezwBi%!+c_e*pita%}o9j_?!Gu z`KS2zWuEykm#3G*f1Q%iUU%?d1MO>eJzNx{x?Wq7WC3|WfyeXtc~U^?J!zthcB3Wm zhy4!yyJ9^<0N+T^UmpeNP-iUXd@)j2#4@O*tN(o6wBp;n}Xj4=!UrAXSK zD=r&t5v^9 zn0Y*QWHN!!6dvwLiSeiFv*kq_?c3})4?*?ouDh;e0knTfSF8}J!MkPJ-?Y!=mtS5! zw4Sf=k-79G@Ylz{-)$_}?d0Q*|J-hEOQ26Iyuj#{sO~JWU6>~q7~b;#q<{>LY{7pG z)c=M^iu`yx@C*MUUP=KkTyS6?os_pm&wn+lF`Xq5p>u5h|8KUx{COMhb>0ST>l5Eq zrJiZR-?}YS1pYY2#43D_!Su#}B}{OS&ln~L{^qwcj<7H0JBXzRB)+`e#l_ZbR$g+@ zX-$$(XZ@7T28&ad_u4&A;_$jjczY2q(OzCiIh6SXFO^AxMc~Mfpdli7K5mEwQwQNc zH_H&fmRkQeNjQV+6S~&dcf0D*{VsdkQ5Pwd0TcO`E?w%&Fn#)46)g+kCvqAl7$C4L z6XQR}X399W5D*B3|KY|!U+;GM`aAV>Q$U%|H>=;w0+>%^1Ooo%e6+ddnrlq^6!@p| z*99HX9mz{ZliH*(l|KQ0xlDIXJm%Q;b3t6UNU}gu099~%DZpSK|CL5wrvBF~cm2YD zl;H4kC3O+&m~h5cOC~f*&&CfgIICyNgfB1JYo(eV|7XlQp!>J~(>p7czuWSREv@HeXW4C##?57Cmx=zGZF<(rF|*>` z50e)zHJp6TC$N)RMgM6}pL{P*Q z+cW3mfuAd`xT4(ib0py zW=n0P*-eX&*FnV(S0`DqMJ%X=)ciCc1>|uN6;b$48-l2({-=}h-x5Y#Pp$ul;_**; zN_!Lg_K(N*zv{u~T=s?&F6!Yw(6O@GMm@}w83M2x+r^Gn{6t0jb4}P;F$L@jdszVI zC4%IFtk`0^2ATG0KJZy)e#&j%6#tq0N%3F!?@mX{8(}a`^w*LPCk3dS>4DpedTQbD z#=KU9gVZ2FU_!hd>QnqvR3v4&A;4F5n6a<+odx{gXF_Yv!i^`b*Xr}v-1udi9jIc5 z>zejc zd{b;`PWWWb$5d6OeM0`s{G{@q4F34v@~agL@QHHWQn4rAbzI1s2jlz#Uy);S4F5CX znfd?B|3o<{{&_fo0wN<&+C$r%HeM|&wq=O?Z}CWmYrbxyRfhlH-u}uqO7>l~Dt4d_ z{4G@51%a{)tC*zK3?krOAIc%K~hmXYJcibfl_7M(m-=^(twFQ$D|Mb8;ustFX z^hmac{DFV&_NMaB13x|RA3yUG{`h5&Pl{~N9{^Z#w^?wR}$yE*xzdbg1sW4_G%BbzdS=}meleK|wsy46J- zMoaDORqp&|{-@0~q|F=mJA1~-p9A*rf6b>nrRB)z4GWLe$0ErBB^C?;az{-4PbboW z)IdwdM3w);=KN&GZ|`r@Y1#5KO^+-%bF%9U|AF1pryx(pQbFfmFKzd(37?ex;GeR; z`Lw4SzQmpDa?e-spUS@!|Lq@v25O>QCn8z_BeV_t`G0&~*^}_MRR1*juf4re1mb{s zPEr8U=G)F&nq%eH|8tA~@0x#dh}|syp*u7G45}pH18Q16V2WFzs|HZ(TF=ZsKhZ|6 zPw}6jL}%XrX#;#Z(k-eX!QWwf_%HOX-mulj(*2AknCoW5dQyNqsQW@d9z=eRLJ{k> z8zYDpgJNn?6bYzgM&VHB|NFpjrrv6akr6ce?haS=@06%Pbz)YhM{G3DQ>J<_KU4kX zIiJk-_b0#Nzdii@LOn-~hPsena6l1Acst1u zVA77=6TS*6#tq0$8fyL?HeP(IDq07@tRLZ%L^&~nZ6YN zP5!t1p3gzyf5uVAz4V~d-nRGBPw#qZn?6pb%`3M%LN_DE!c@WMd45uWJeWITo&u5< z1xSh_`NAwg85E}?5)QPQh^Wv1^k?G=zdIdU&+NVr%saV%FBHrsb5!*tv=8vT%p^~+ zpQ)Zgeg%KvpPIip=hK|?9|-s*+U0RzZk+Jv^QM3dayfkXuhePszfAss zKdK}JIG~&Y+B2owA@Scuwh2kSxNgH$ga0>d-0BupaE<@@k#{T7fsG8upnO0bi)OKY zZU9;G1Y63Z(OQBVi`y}ke~SN>zk;etXt;k2ND45?lX6Hx z%#3KJfDDk!6p+j)b(TSKt^fPOaHig!DZ5YqIH#_^uz#ochN_<6d{b<}{9xY+B)V-iC`RR7kmRtEw0NCNnbyq~;0#jRH*n7XEW}EkOV@90dGv zASob6tNicp3B!2z{ac;h#)pBrr}(#p?U+&xz^n=Y)`vTAWA{87%@Yg>bLZDkG5n9+ z!+#F2aNUjDT!1$1;eY$SgMF(>%M0k!>?=E5%Dsus5-C79=zE3$qChQV3JCw*2(J7e zh9?hn%s!?Z@muSzxpx067dWc=@e<}whW+sce-ry5f6E8{i$Z+-2e*rWwt6_>uZ9W# z(LVfF@Pz-WAeEttNOZ_$cCn*_h<${Z)ZIv z$pTVu!hh`AjC^*yu){Ob=n?Y(;}`{trn4||WlPEUaBwB9DoNgL44 z6cF!40EEZ+CjUM8V5$=qqy|G{V+4J%iya+D_~+RV5a9DX-;cJ8Kktfd8OHq2VEkVd zKL6{RrrN0YZS#&}spg$uwu5n2W6(SU0C#B5+>GwlPY{QGW(C7H#XkjR@`uHf0=#Vb zZJ!@na&a5}x9QH1+T6bNv3g4QIgt>{h}b-sglI<8Lvd~aa+FZO~r>mpyy;Kbd#p<2$ta{^6N*9_Q_UZQd1PUCejBtF0+x$nX5y2m8L~{I>nh z5kAwrav#qO{8O}B=cQmSu2uEi9{`z|S>q@;?v=|2cfi(z;DYD*vbS zD-9mp>f|=cyk?`la`RX5Pm;AWYE#uq{fb!DFOxsvzq(g_i-8(wmT{lG3jD2HuwmP; zT)XbzQ>Ntq=%;;}+Iwi5Q}r~DLvVEi0eu${&58*BeF{K|G$Nobp^>`CUcx-}e;5Ci z|84wthT9yo-gbUr%F~!lvkq_F=;K*WU1!QZaEvDEvmW4=Qh$84f6);qx?eV(U$|&? zT#>f$nHwi~Ey15C|5E>l|M7NJO{yL9Kc+p6b3q`JKXL>JaB@33Na}wu!2w589Jc&w zo#us2w!YH&|Kd##&@ZB=q^q#y4?78Poh4L3=rBS#D<)$H*dPbY!7ZJLs!7i7GIB|l z$-LibH~3|G6F#Z`oBVeQU}sjzU7Zj3EN-HM>C5B~(+6%Y()G9or_KdfZ~2`M+;~d( zkNAe~c*lEmyQO+61rR#H|5QYo0>XT^0Gj-#E^pz#mX-fgVsOL_&fQ>_yS6yy&xe@{ zPFjD*Hl4!%W_jP8+F8CZtJ5QA9d+DvCV9qtb+(ApLXWI8`ET^f;qhNR6aI5`8~_1M zZc@nP04En0AOITnMSzr)WhUA7xy1hmo^z$IZ1U~guXx2P{@@S(;P%^Zf9qS{`lo;T zr|)^sdmerC(ZBr5zx?7Czxeg9fBk#k``!v9U;5IQ zKKt3ve&Q3K`0$56{LXj2^X+ec`(1b4^+$j7N3VS4EB#c2Z;SPXQ9eBKvQsab%-qfG zpWXHX7H6z7|C5w0tuPtWt93z)mF4|B`N{ewg4De+y*6ds7w1U|u>P^F&uyM-vi0}w zKJ^zq404BqhAZdPt13V~5AQ0#xVj=x z1(+FN=|B~vJ`@y&2@2G}-*NH`IsQ8=6gZk6mL0FF72C4X>;DzzKdQInzfAG#U;kRx z_}72^R~bYekxl;gZ~ykM{_3yf7g^@d|NPHooVUI0ZFk&p$LnAJdYMQ*^3Aal6aIf> zzg82TJzx7!@-_a;=FDpHN9s4q!!%?w`D6A^-Y@fhJcJK3y7GYU2rIs)vMPWgCFVm3>WD~eP8 zxi(Y}1j+5H|xppzvZpKa$$W|Rv-TV;upUd&;QSS<})Ar*vIhy z{`>F0=bn4s^rknx?sczw`O9C9|GqHXx5fJ8_}6x8^&8L!uRou~VV}fgUNWNMAD}Y% zlc8TGf86E7w@hvWpzMHbT58W{>|3Pe5%Ks+lR^4^=yl;zoc&k&tyxpWR8 z>9-6D@E5R!|JJcO#lI58+*D=&RQT#Wes18e)IorJ_D5OhEtJS40RE=}3`|TWce%2b z1Mal`rfn|i@ts;xYjxoNfBeUP{OVV~3jcrn;~%U4zx&7 zY*}#39a|r}{BN6M?->8refM`q7~5<40kiKm#~wX@k#Hb7P6UPVuT&@;5*L;KbBx$C z(j6ZGK6(8eZ{6&OHW4w@Hpp*Q*7JU1yodjZS=$TzmGnv--k#^&IJvSP1n^%jKma$t zL_xW?g#ejJ2>kJ%l={DAHCXu{{<}w<`ak^l@K5;vjc9Aw}<~M7#~1&{14@nxU6q}JCpy)0Nj)M`GyK1sh{Jj`Y9tdl-9r9J3){z|3ZtCF1^#>fr|14}wB$1C{ofhE=_+&(-@f>0yGP(!I{9>(fxl1q8l;QUM14|# zWr4qUd8vpNI|7R9rugUQnfV9*O#g-eA|f;YDnR=-i+}K!2#Dc;n~ZI){2M!8S^3}O zzlT4Y{P*x@5C8AI_uf12yz>ojcmw|5aKjCG_|xwPwb8aVOvd!d+RVg!oFP5{p)3n^ zD5u1AyNBsB`Ogzr9{@A;7yjo^@hHdivduNzy5Ctd#s3UK`JnfqQ;2;>H;KzLMr!Fh0|66_u%zO8^sPez?zdZbJ{^#%i{_j2f|M=sN z`%FO|{{PU2KJ?!AzPCL5`GMJzuf%QrtqHwlv=_;+W6=54>@_J_#eP#7~)EZb^e?z5U)-aSWfn_C;1Ei%SsYK z$qwV;FDRr%@K5niAsHO`@;BfApgg5-pmq=clbc*sE=+E!{BIA{S>E!2c_;a* z|2959f;PU!|7DvWwCGuz^Z)ppcoY$Wopgg^`Ij?W_f|7ZLVjaj@Q;)8WEBqh6Rz%M zW`4Bc*xcAD97pV#gB`9{%^6(QOoNNSLf0+sOjNI)m~A$l?Zw$J~t0^!emh;AXq>K!fm~7saCj zfBtaNh6~$V!wmAj)c<;s6kw$!qwv4f|EcZc=R8?o2+sEiKPQr4$S>=K{NS$&QVY>W z9)~vb@5jFs{}MrJBrRo%@L!lvsX(Ks5_FaSZ2&u)+q`f78Eu?=sBMD%*UmcvzUo8bSV*;#;Vmau;iT~6`Mg?##J;_pLX?Fe-KxJc z^JB$QI#7uT!!{boH$YbYhyVZm-~U}c{QHxi{G`o+Xn?~jgz%gy_`jeil|T5?iD6M@exv|XWYndZ zT^z@WGWnBh>qg%4=&Ssn&1~;~ZLiqmpp{V3<@g$oBYp*e^dYG!@pns>Q{aE%a=cV-~%7P|MKDA zSHJqz<-@<%FKspd!&M3@84)T8H1Q3Nf$aa)@^nnGIF!^2BlxG?K_l(Iig4){D-*Z4VB>yCP!06Wpa2%nD_`w*o* zDG?FuXW9q()&IgovE+av$SI60sK|MZkzIfYq#22c&{8g}%{L-8W2&yNEDiWBpwSon zU)lZ=cXDss@?ic$IJPB!Cfq4YD|{(SD`h=fx@|j+gMC#H;gAHw)$l?%V`1)^LdYOj zL(l4K&cu-Gj)Dj%K~#c*;~2)7ae~=V=ll2jo%41t%X355xqUm(ob~imy>Fde zb*lD$*Sp@m_O3e0;eXfw41FCX007~C1c)0^To3;{iiP|Y0Hfu*{zjqw`l!vlsDo@F z6Qp4~IPdsOcUH?DAE^+KV}2kH^M~-=AnNY$`vx6`FEid@L_RM-__k)j7@Z&HC;R36 znNP(CLByGh{CO5naWULbYj>!>J1|K@**W0<`NijbeXlqFv;^U}RP(0U9@>wjDZ)>o z!}UX$KeTVFPOOLdxi(G0O?U)+MiFsmxylTFBZRmEl;mcTa5lQ>_F3ABq2e{_~&L%YPnw?6G?J&rtqk=PEKpC`)rS1Z7#S^kMkYrr3+*HP z++cVfnX^#6t@IAU59eFn!>Ii*{|-CsK>UjZ)iQ#MFYrvoZ3cyr75kll&S)K*Y&gQhforWPAkw4B)zm)#>r?QXQlfTK|=)FPyXQ=*f z$^Z58pHJ=BYPBu!ME@5l`BTqQ%Q#baLx|qb98mGUKcS`gAHeWf@DN$6Y)?xeR3g6$ zGk^Nxjc=?yEl*{r0?cHLYs0`XWxAyjKCWCi3+LyKuzL91)Ga1wT{*u!VlUgVYdh_< zlmBg1P!0d1eW5|f;3>i)XUR2GWaKFtIgO){`HR6aQ`S~K8u0%-ru6n-clhhpdTLVJ z=O|l3v{_9`AMIxXH?+@dLg2;f0T2AJD%Yp#H_fr|7V_u9h&mS9kJ=~T`30vakAT=X zMG`*9KN2WOvlC43SBG_vxPMcZb6G>cz481Vlb>i@Cv zzpl&>z*ZU3P&&*YG5ro3fPpYc0;Yifp#p@Wv>^=$9pzX?)+*c6g!(AuBKe~Ip@mTn zZ+>J{;=;&pQJ^eCS|G1%Pwpi-9LyiW53z^gm5ccm5)O*Q!*?nXW=hQAkbDEdBFee33(pZY(i@s%ToHCncSr}h7A@1hXjQt!#4?E zIabV{`VPrSa6W@K#(7!BycnB7BVP{lM++s@1FpQAXo(_1_>c_Gbdm8sH|H|^2%a75 z3`)!J$)y{GHGlJb2iq>%Hr{$@aUBU~NvNP84`Xtgsz; zhxQ@72DX?#QXkJJ)66!Oxr|vR}(^7GJHQgr1B(v>xfjB{fBw zH%f`WF#PXN`!(8t|K~BQxB0r)y!;iPTYmgsIcrADlUZTxuswLhAb}~T(6hC_>KErb z4}ekQEVK`Dq5aD9VE)W`MesK@Kko?hN8NF4%umliHBSloQ%w$ur-QUF@}(|@j~AYc z{3HI0{DHis+p6aG>-*;arPkFH)lja=wP^<};DTK?XLN%Z&Tv>aSWYSk8VzYKca)R3 zqqM&!ZL=iLHP5}@|C;ywAC9us4jQERzx02aG5il3z!UwiwN(xz9Lv&-)0S-YuJ3!+ zosR#D^>-V=S^_Cl9Q3+KrpNs$X88!Vu{V0h^S*TV-FNrB_&>rvw68)p&A&yL<7aMA zeasmc2Z=_C?00_O0%@FuSkhwP$5}K>_@AdRw9i4x>=AIa)qwxo5b`{$;|Jnff~`$8 zPvd$C1Mi4a*s^ky6l$AKvvmmLiMD7>p{Tye_81J+hwTv>J;rEtR)kNGU_#)G93q6` z7eouEj^Kyt0<%xmEG6TF5i`Em#kf2yVe|^mG=H&u>3;~>IzzK?kQTiFh zfl5FEHXsBcSgx`h&eMdkKc_9ZZktZj*GEJJ9@^f^*3-h55-XO^5s=Ei?;X z;1ThZ@E9NAKKS3K1OA_9+}`GkS|V+>)ja&N`l3sRo#Gl99oJSN<$P+}q%t1F4H@zW zB!<}-$7Dno&PU5ExgDruToUcWVRU|FKB1Hshxwrzyx|<$;~?>C@Q#d8*@4dBRL+B`y+6WX>0)dpO^pS~%o?!eZ;swu!33p}Xu=Bp?b5_u4v_0eZGoR+RH=HIJph`P*7*f4~gJ`mXpth3x4o^TVB|?6z+dU!s5zFyQ~u z`G45=|J3^*^8UBcd9prDx9ZsfBs3c6;ePeRL;D;DPFN8>@L*kT1xt*JG3X*NSTnZ+qSzj287F1Y z`8BLJ+d5Oex1t@ zzAk37xPRvVUHYHN|1=WQ?_qu*3G;{c`5g##B>u9U6`ODsz=0xU(jg#v z&d2<`725+j=1+Q)_z&|(?QmhR5A!>-^N0C;uM05lDA^CaA%8d1(Jo#7hk?L2g(Ckm zUcXtJcB?uUZ9lE+@%tqTa9*A};Q#3V@c*xV^{Y|d|9{g>H+ld6hd=z`p7;M(3y)ra z!{UFi412>Q`BD5&CxriLX1F3PM&f^m=L~qSCI5eP^TQMRQ8x?}^B;83L5DPK^t-4} z9S%;;V~g_-KKS4%Q>NI8Ccec2vmJKWLE2yDhj(z3s37vG$UmIV=|bX z<2d1eUd(;?wU*@O8n&D${DAxw?DT1yhFbV2cY(U2!cSj?TUR(>^54H6D`NiYRy~`} ztq|bTh=1K3<`4OY`7td6n5N(dssIlfsgVpnL;HkEkJ^V}?g`9{dv?}6bXPvuM(|^P zKC2qz%bYiWcfv3~v_o=Jz0~NHJ4d>>9ruXmW7U26Gq-%klKqkYmp3_vZ}L;_nJBM64g2wNEwL~)h(ZwOCVfi2S<^lhg@&|@u{=*v9 z@^&2S3=FOuJ$33-TRWi(u4IJF4kYu>Q+N%JNG^*nxYKfncwd*`N81$nhyUY29GN?F z>IwnPARBM$h{adEO3QZw1Tw((~7*_03e(xr6 zL7k9)IhP&}^MeP?4=pqUi`s`a*o)ea;4j)I*HL%WewWKTw9k^&NA(ClYCp_hfD|A+je{DFb$5AkE~vZnWqQg)!jrF^DMn|9=pM;>+5Q6uqxQ~y`; z$IZIsO7SELjJ{2Hs6b#8APTTV0S+Y))IcC^rIBOW%14(D_9v7`7*7m`_XRI|SQ1gY$s5AX2%_aK5k3h&8kirF_;Eh7DYHQ}(;5y4l=s z-KnNn<<*N1+fbwIynD0b`6dr#hacXF0^FAGXCQFz`Y-kTfB64TfBMt!e)qdy|N7VY z{r4aL_{T52@WQjtKKs;DPksICU%&tU`|r5pj?aJo^VeK+&E=P0{=pA^(C@#WamE>^ zoN~&W-t?wX8o&8b@qg(Pd!TeE|AX8}`5)jZ+kgDb$58v+S{GD=C;SLw)Jt7w>)muEsveF#W5>yHe;pwx9NqUk6wPG&cEe0I%>HMk9xxf{vEljf9oH)%&dRP%XaqPxZ>=eGi}Xh zgNl8$z)4GN()5KkWhxirg%2ZuZGC0RA%7J@{-vo)boRCGS(cPd#;r=@CYp@MJ|=8r^)K5=2LFjRmCT=wYX?>2L% zZgbB?S84x}hic)sA9>s{#~d?b#tc#5h$D{R@*JOR%P3&L|2_QgCjjXG;{We_=R5fS zD_{A_m%sewTW+}p|9|8oAGzqFi!QwI!hZQbyp6Pn@;{t3^*`**mj9iH0TMX1)c^P& z`Rn)Sc1VB+ssJ#+d8Le?KYJB!~EF_ z0a!8Qk3Y+yxEO&{vLEv!;V?hVAHk2Pd(u0heauhT7wsee(7qu+)V^xML}(xLcZLnC zbtbbI+IN1;&wL$sX1-kjs==wKRI=X%vmWv{ka1H@@ox{yZN6*iZO7(l5)_PIivpZI zAs|zAs9?bVL;3#~zxW0I|KSgRNdJfby#o5qJMX;p)>}XEiBDW{#T6g=(1+gr?suPg z=9%>Wo8SEAmi;mk{{soMH2EJ6i~k{wx&SJ0f~DyH=m%Q{S1tMfs;!RDZ1id(P!od`Qtrg7{Q-!itJZbK$1oNShR!? zb-QZd|oMiRL@HAbHLEP3ZZ@Vd7{G^+IOntdBXg@#dmR1 z;l}sY`h?Q_oHX9*QY9o^bJ9K$|6Q8z+;7v0`PI35;-O=6qFZ);vo-2Cif8k2qxmf10R?+|`%i}{fx@=vy(AMt{k-c6n#57XfUdGb6MTargs zKZGBhAGXgNF3sOb^fwl5M&x(NzLVQwPQ^;+BmA&^N9fN=?{m+}@Vg|AnN&}_HJaaL zv(UbjjvFZp;rr8mrkaNxTK<^h&=!7fjW=ERmeb`ZPCM;1PpVR?yjut`3gGp8-w;3+ zXu$vYKTCjq|NGzn<~P6jW%m)_pZ@fx_&<*TKm72+9s$1Ro_jn3@X1ep@~W$@y7baZ z{RI43XPtHGsi$hyZ?}8@Lo@)|XTfdQJD`Oq<5R#RIRMi0zqSyYk)}^c3pMe7Mv*|p zX7YcFX-x>ZYU?8e{iOYgf3hF*ll_<fDdjC)KK!^Qj@jsBz3Gf3>!}335!X*9Q>02NqZW3q(2{6 zajLi3koQg;=I35Cmby>ed`{GN{zK9cRCVAqUF0AY29ht{udy?=8|J%-@`ObriRnDhs zp1JfEEq~5+q?Wwm_;W^(34L-xi9|5+rfrH@qcs+ z{ZH*T^?&#ul8gVzjKUb00~fAFk-90^(r z_kM}icC0q2)v5*FxzyGz0b`y29{(^2!@|~^ARc?d-E55 zgwioQ<~NS6JWmq)hSf}TDBABy>38OFSM0o#lc9a)D;Tvui}jZZ?Yn?V^>D^w{%E|& ze%oUH6vFul1V#I*VSYbY)3U`zspeE=o!B$WnwODl_-wmhUYPIs|5KOly!Rvb;nosZ zL!{cAX?%tonI2)fW^9(jb1GY>%~uq4`*zomi*Z#g+c2l9ll1UEq=o+>6G~|niUok7 z6gZ&~2#s*TuX#zi<##W)!zf^=kFMLYvqayxFPZ;{fAP!M5c!h||M-vp=(&dXbnT8q z-cbj>L<-NXa{NZGUbYpl4Jx&0f%fDNU`hKlIX{8G_h5c;K?T0X?|41fqhAB(S4J~@+FI8?_Zl5}Ql)`-He_6!8T=W7~YMJ4L zpC)V)vtS9#n)*NdAH@R6Kuga+xcMfSg!%A)PNnP&7P^npclD969{x}AM-I?nQ052u z6aQqtCmImHS6f_s@x>-MJmz-og%{VcFNMMa2CEnS zDo2|*ljFdA$RAr1PH3D)LhPJ{;K%%NA7#uRsUO-`tx5ab%Hf&Gb*kCYK6i`T;{39x z&?5g_q(4LQ&280jh{NBs)W$cg(29QBG~=BU4_^Kii}VYc0sr^gVnge<=l_!KdVduD z4~C=v=@!tHi3C*m1pCMj{Lg0c|FUiQKgs_r_{aY;KQjN)gK|D0e@k0sw>;xunUk6B zHo#jr>O8d45kq@+uxHFIaKNIgw+BY8`SEp_pYo6RS1$4={?R6{fq812FW_xCAFh#v zPn3T%!{5~Wp?$*yoNqMkz%YL^rO&yd_LDc@Xw28Kc&jZ7^N03p8*cY!kv!(lpV>M} zA&!ss+rPBGWiyVgrw05#wsvbCb@c06{rnbC0TR*cyZn{12-t z7bcxb*=arN+6E;5w861W{(s9`-h%&4VMzWE|5h}4u8s1?{N}i?zWQnpJKXiQ4?e$U z+Xo!lT*HD{#{%_chYref-U7F;aA3>6#)PJQ5k9u}y;1&E#{Y8u$Y0uo%K$v`N7Upx z8s`^?T^gU8Bx`CLuZeIH8svr43GIjZyE&ej59c}+8-@16*zs8eUWVtDi|tdcPZb=< zQgHa1i!bYUI_3uRYv-e0x9-@wY{36xYq#c6o4sa28>q`zhPr_v76J!9#7>O>GpdK% z=zk$VAuw+8Kf-sOi2pJP)>FVP_nfS`rHT!W;9>*4F3CMcX*H;f!{+aW|z5E5*DW#A^)fa@ITp)|79{GX~O@+Ob`Eu{KNnEtbACTYpC@<=6{>|-!GaK|9i&m z)1UsdAG^Q#=9@9U7hik#(Oq}lSfxX(IZX~|CQ~jf^-n_m-&%JN49ts&Xe;;{^*@j zLjK&uFquX&3W)3v^Gok@7WK1aq6EK_FlS>;&i55P+I|$;ccA*Vabc$-_2(Jwr(q%a zZ(i0(m(8IK_-R{{yZV9)=;gssEMjk-$s}2&AL`9T@(1 zPJ3GNzxDrF|C#ka^8d;IME^_vTzTb{N&EP*Gw(YM`QLr_-I(7?P`w8)cHe#X*&`@; zEvnZdscEnva7Y|EgIACbv6LpVIN-=uow`m+kE#S$_~ zxpioNd1}9v^lH-}ZR*^=;$d67X2N(uV!;37(vodD&ijW$-GxI19A5e#|C_F&_F>zQ z)+^w^I|JKHV8OP*Z1Bt@V$R8KTyftv*Ko)ohj{+W`hO1qX8n)(U-^Ih@1@rAKjHry zZ@dxzBX;zT!NT>|UoUs$`8Y`=XEXYq!?xYD&&AAh z_;rKB$EyWeKJ!c76Zy5&$G0a62nv<<=OExMM+*5IBsu?(Kjt?Cknqnke%^uhOYr0T zusyoQ+s!mSVmGYl5d9EqX7!8hL-^Q!-XwNLa^w>h-*CJ-sLnIs|7wakDU8%*&HW#q zxNDCTfo&H4rK$TTnOwIl)s?0P1%szw;A9OxbDAUVfYAca2o`^3R$A z@cdWS|9jz&^*{?WSbB5x$3@y$U<_;DZl(Cx)Gd0~rO}e*5iG zMhO9CkWD4`%m43NdVATSk*+-ZrS3no+`i{8wau!pT6|9WYjEt`Yk{3#yVB^LSf57x z*UBH4g#Z1S1wYCBKzJMky;DI1l01L;1!v(J;zG!u<}Y=G{6pZRdyzlBcUsmgR1io_ zTL{R143l}c6-QWGqU#7^%vF`@_KQ4{g?5vT>QZ%7%kPh?P((n)qw>|ui z_VIsB0f2pm0IJmj2Fzdl@25{j`rhV#@Bge6ke3106oAKmt^Y*YR{qoQ^8fhX44^Swk+SV!oeR}W~Vp4cIDUq?Q6!R9|tFy z^A>3N*stV&#DC@dBmS)giugy`7?{gs&NriDsUAaP3w~&%kUw2SX&4A8RUZ(U1N|n>I=H#Uip^g_aD8d^6AGW>{k3A=8yQdQ`#Rb<6%rr?Zh>t{Sm%# zahyURp#0+$mG&WrBKxD{i|9M=T=;9tMplu{`f0w@>%XM~IDjc}9y$rOOHB&X281;hx01?O>;fkP~3Xmbq*9dJNW zKxTnG0#GTSy#FC7APM|R0m7qE4`$dKm9x4 z|FPk`=coe~Y4x^~8&}#d*&B#u;eYDIPOL7CX*^5+gLhUq;Qt!!+8@mm%sV8b09)3Q z|3A6;5&Q4Ie`W!)3P={nLZGYyN){+jfqDOfQ9xz^5(2FD$r8{ozi^OMprV7xU&*3M z{)ixk60WnSm3sc{>-}Kk7zJ{w(_X)M&-D!cobwjAe!0Ed^wb$kZH}3H_+REHGrwp& z;vcGse6R<8nx7(y`1hyjfH(sE6Xh?5L)C};L;KFgQV#zk|E*rPLYpg`H+7`<3I4e> zx7(&S2mIfrm1bLK%~!R01H*%pjsR(h4gY8BPdn*<{9jVX6o5qzTWz&f_#gPCfU*im zwHg8x`BOn{5c}12kJ)d({W1jbBG4KF_=UJL&pgwEz$Sw83OEZvDfuh}$|!)&Hy0q= zFAy*Vf7&TJs31~A{5wztF`F=3{%>=^bG44-=WKg+P@%6D_}0p;USGe}BFh*5Cj}%H zg#Qt^96o8pFE~XqKSsouzr;UBFfPcPuPHEt1tG(z0lnjSXAbjc>(7w?)FsxQ!{1dI zFEl>9<}p`peAvlb?PK86SM!d0GVq@8(N{I4*S|4}`{m+hqgjd=wE z;h<7LkdIoO2j>eMv6lSrA`jYozYGELAb=kOH3TpVeCnyECJRKmTL|PCt~?8nwLs*3 zMggXJaXwR^{O$A+@|XPazBq{^;@{yHUU;D(HA>U&@Y;zjTWqdYoASE3KE*alBP?Ve z?Z3!sZ8{4HBl++@+Gj#QGXM6Y@gjdvPv$4f;qok`J(550A00*fJB0EN`8S!r#J>m4 z+FaONs)OyX+wkxMci463;mp0vM;HwFf4mz1v?VrcgP#wtv|FGn?UN{=AR^dF*aHK2 zbg=}x z-lBkkfK0yWU~>VqJ%%^Ki|Re<#csOkCS`l9`OlNTo*kh4y+^{^B%H)0jncNawSGTj zn^v8ZI!{iTKB4~?r;l=LFWHYiuym{6A-ebtJL7reob^A*UkJeeA_5La=KO+3fVb5S zroCt(5{M9z6y*Y02o3Z2U!*Wt2>ClFoge;BG;zW`7hUCnm8Q-KJjQ%1+COWH{YRqx zW+-sA^9wTr{vWTF-=*DSFSk!R7%_q_+K)y5!!t_9|Cs^^|AT#2fg1c80@x|@sSdwL z7Rad>Phsb-w0%pg9Y3O4F{OK zkS*pH7s!4wLLHSo_KW;IO=c+Jjz(%*)2}Trb>39_-17U4bg6^=?OUMb%Fj0~xl!~# z{_o2F&IWH|Z%i(e!!Ljb!X^0)dx@Vcgb)a_3IhH!h)(9enf4F=+amunmfU>YU$Z}3 zK$7}rZ+XB$JMOm2F1s{Iew6Zd+imBL+PCvK*1T~-tM>*B*8j}5%@=$5t7nT16`o%8 zSUfLjQy~D7{fWlyr|t1SkSF08{s(^J-wb~7zlOI`7;1u)X)* z`+&w;P+1@$AU_A}jX+5RnG48Ep{e>T3K9iO3LtzV0$XK!FuzaLQCZsGQNo9lxT%pE zt!1L1`Q0=Jri5K?s)1F>lFVd1lXc$JBfdKo@>CfLxiA|^gEP0=*8&|}R{bI7a{bP@=+Oq$IT_qqM~&i@xo-c8pN17t8J%-YDU zx87Rl)YQA~y6YZ$?19r{w$U18E4g8bBXMh?yA+4Vo!XoFj&iK$q@2Wpc@w5iD`&Fj zo_p@T`|iF@&u+Wzw&rIIOOu~*VP9LV)eqhsIB|XQ2JOQz8|J z`ID;&`Df)1mD z*c)frEaLww{(s|6JBtpZG|o5Hn$Za#TkM~B(Q^`i=Jffl-MWT_FdjOJ?X}llJbDyU zAvGQ4SeEnLv+nd!F04M9@BDw=&c~r9bCj|ZNCxLzb=6gp4^q$;qF9ms$Rm$D`Q(#7 z_`whU`Jey!r$7DazyJHc|MHi={Oxal`-eaL;g5g(MMjX zDpLgbgn*ELQ~zfP5U4{w%x4H7#E=I9pD1H=U`3p297L>7d{+<=-ZrV#TYy~K$2Xk{ zEwVr&rsyUF2zjD_6+zw$VMVA{0|^8c1!-`Uo{W&6iE*b)P+$u75d0R7s(|*bBej&s zY_iT0+J@zg{o2hEy2u?TtiIv$lcrDH_f)W2$r)a+}e!%}j!-8NTWC$F;``zz;{p(-j|9}10fBo@~fBeD=FMQ`a z-+An@$2|RN4ZN3Lni7=>kpPh@7nR<3(wU=lZu27oCLsXxGqJ#*cH;1%{2%-a1j0e4 z!1zUt0>b+9t2Elp>vaOb5!>(CbJ|1!TNhA) zAccg3k+xMiSDy0>_(mtS}rz&;R*rx-=D?*dEkZAZ>E4U1h7X6h$B=8FbZJ&g!0=yj>f#(Hr83^EZQ2@z%StzzI^C!*kQ;`Dm3mF23@L?Q5p$(;u z_A|dXPTUt27~^L(h_-T%LI6(CGFn6O?d@Caarx#4e|3Wy&#ZC6P^#&7&(NzK?CGlo z+B7)+$Jm*%?I!6v8G8kL>__Rkn$sR<1A(syhu4+#T;b3^ zo6MgJzH6PMW_tlDyrl9!BEp~k^rxhNe)F5(;D6a9IVCBer=NaWmPrc8%aE;puN06) zk1`?j@O?X+)TTXQxP|}`|4sdm|1o^{pZ=lz4FR}7h5%;45(1J1Li;hU!QMW3yEYe~ zPR5K00m%ZH>8Q7ZB5(r%A;6?CR>$qMJ(8E+!S*p_dt`gW2+K#q{Ejk}U=9U)4`mP2 ztSv9^Ez52~4N3?^6B-CqPQdMl7EHnHxZ{p)!R*Xi_CM@`eU7+)tK+}5@fc&lZvQYp zS7Hxss~6p#Ha+XTnqh2OJ^bIz*}Pg(NM8&0I=YhoIvVVOAM%U;4S4yEv7yRAh-1sz zT*I(+(CqQs?j2TaYV2ayp@piJqng|EH*YJ$hP{~4cP2au3e9gETY13$L-l|7zfwT| z^FROdi(mXALxAsp|NGzl?sqc;@bqg90kZnV5a6PdTK!bIL_HY8d7=RFkN&q4nNvRa zAJ5D7WC&oUyRtx;1@I|#WE3C@Fo)Rs(}AB`?V!=|lTW393>6avG8Z5hgh=IrM1kgv z06xd+76=jcA_7D8Ou-)eeQL;H;J`9%sYyE)WhhW41SBnB zT5y|fw%KW?om_k_JNYYH6ZdYxGJs6nAGu#EXY~5ep%*;Z)4m1TbZ+rKxgO@X6$^AR zG7tZ!mmwMd>*eBqd*IwKkk<$k1co7iqml)(U1PCUzdPA;onKn_sJCssUz0zJ*cFD- ze)cr$G>=u5w0U9ji9O#kH~y?ZFf>inb4erpIpF_c`QH?=@eo&A`l4hJVW^5d}aH)#6Oey z1rFrzBy+<5XD+ivm-zp(Ar8%zi7{EI1aMCFj%8cD zhOv2_rTLo-+2rSdFq)S!=jF^7m^e1+JoJ*9dsHYJ@c$_MpA^uq ze)X&W{_p?JEU+oyXPEf7fQ zBoKt{v$egapxY2n6hZw3tMfGrBFXwNKzMFP-W_KiIkJovM(7HHGR_@82@_S3)B#rhXuWc5w=^Na95 z2?Y7!e?1P{x}M|k8r3oc$SAUd01xg~Q_#gPg|3toQ53P&#nc{woJ?});#DdT?@{ud%C z!{qpuPA>$(?s2=aIRZii!ny{Q3PUg^&^-#hJf>-1dk;LpBVpiLvAXuzP=69ROu zK%jH&F<>O=5xXu9|HD7>r+mbE{14c=-VlJ}Py+IS83i~>;IM6T0qX3tV+S6wp#uDkBK z?Y7&z0LLmIvjBzw4{Y6P3K#%3U$yfZb7H0}`Ty`gJ_q2>Ig*{6Oy+C227u9SH%nK>WLG7ZBP9aZwUu5RK7|>ezGqR=c(N>blxNVQekX zrjhSi=`cK>qe=(MYVE$~w z|B?gF#=*n?NI(#j{NZlCPI^aShaGl6^;${GXf9i24J90WY=c%4;$tJd!BN!$ZMxX6 zU?*dP@VoTi*<|w9{$crFx5B&*g>A#z3<3B{g#ZHqW)Vf33s9#?fXbuud0(Htb(>bN z(`k8~<5g{x5HR5XQTRVY0Q_$iXl8*u2>dVq@-Joqo_Xe(Z+`Qe9t86sus0#sgJ5O> zEh$pcIEDc4{A_s*^Rn7~Yac!Ima}KT|Fh-)ya>W9 zfLUOxK&61p0zCKJa~=dX1dsx<5Y&TU-i?`A0I$LM`Zg!G?BC;7+lt!nvi{7C{6_S@ zEC;nCt3%a?%D4dipILy60`OZBLE$~yPCV$oHA3ZnACf(NtNk+0ZH@&J2v9ni9V@mE z;b&(!Kf?!GXCUxGBQ2XE)x5bS`5*dU`X4zYGa$Rm!7+p3zXs6`Xx~@6U=2d&YX|(0 zBp8-ps@b#C2Aj6LfU%L@;HYYWmhDXBllqkfGPp|@iwiyy<@GWbp!4Bh&kF7t_Es0$ zZ-T5GC)$bQw7h^}$*?b-KeRI6Y5DD{xkmx3^J0^gcW?Q?e*2S;RP*#L_ZxcS1O6Y0 z|LvCoG6cvfAVUDN07Kseky(J-Z@;~s1@629`TTc{)C)uRBQ$C!v~SD&3Esv3hP@~~ zQkEPt9Oe$ma`O&6Z)m_$A!Yjo2*mj1b^891(-&qFn|>u7+7ZH^$U!+_|(N2B!9|L;)YgLjL@PJcVA8>3ptI@We|?AH;a~qka)tolE%Y1Y3IRS+0VJ5N zxAkYsCm&tI$y@I`lniT6Ur={co4c+$Pp@*Un+5EQcMJq1vZ8IKt-aO7>+F5UT8BTq z+VNF?91C@WRT%L9==|S&7n~u$fBn~g83JS$z$&1po_flI0A>NQ3h1++{jApkU3S@J zh5*-`a8ApP+j`CQ5(2ssX67dRPydAfVIK17A1;8^;|>UrYtZ=>1yYFek7^PuT3$v^ zHSa$e^FxK)$HT=vC!Fn{XDqdO$Y0tgYXW5kq6zs3O6T&(Upu%#cPGp#qXLev)shgF zBZ`R^-0Hkl+Ri&~^Y<$Zczh09;L>GVF`=KpBYlN+G$;fxWT@Pf#P}bj>v713YA{ZZ zX9!@#$1i-!_)ZG8=daOfp>Lpg*(Qe;4US%DhJ&7(Z`pdEeslgeOx!n7FIg{50kN>^ zmfe(FbB_>y`KAZmxxtaotUjKBpy%TKIpF`<^1lZGk^-_2v>pVo5Xgf7RslWn#1m!# z{2qD@0lXDILxAVkX!S0*E7sTt&qu+c{TT2E*dQUN4HY#6fQ<|Rctk=#qJaHOnT7m? ziS4)F{>F`44T^{5X9Izum>Ffy9y`DBLz^Bv2J*jq;-Mjb+mOGRL2g1Q8Y7$b%pdai zr=4QGP#*s00=gIHQ*7>~Zw`yaeZXOu-UO~4!3C^}@x3 z?%L+7)tRx_dIJI0u_LWuR9W8r&q-yu9eRL3h!v<3?+vE`cnt&hasFwR>I9FeEY4QfhnF51zjUXTz z0u=wd$9S$K7M`r}prr@xy=x_JRy^x~Bd*@%=zBNs+wTt8akrij;O^R7{7dcqpUt3< zRiMphf%7_`{348npneoa3drjqk_E~ep_2t_)3)k7K6%D=>vSG(1OS|Gn*xdp|C2YM z9+1;NLI7XDgm_*ENEAT(DTn-hivK+!WyY3@IQfkSJhS>3UyRxEF2-EVJ9~@#d4#l2 z$R8nOT&y+dBEIj|+@{;tEJ7smR{?48ARHo%kdd{+mHmQ1n@jq^D#wot^$kv<1zJu4 zoU+nZiQ1h2Fw2O4G-$tw-_-w*j}P&`u1^R60Ac{!c%#Sdy>3MaQ1Co{J!pUG9*2Bv`)PM?GQH*d&(9dL z1ZcqjO{yQZoCm=S0n7sTJPVv5fH#8XS+Hb*{4jck0GC{HNqgk@O%oJ{@bP~_K#_m) zKc)Y}|2zWQa|)EmlvBtS`O^;|Bz0*uCKUll@7ZbYE-+|+v(1_htTffMFJE90ML3*> z#|qfMycnL>0%Bk8*Nt;|o`NC}1-ejEeVp&WEGEqGq4_PNl)tdTfdlFuuNHXk(%X;H z)b){Hb3=DXkD+~m-d5S5K!;ZTha>2E$Zt-284Iw40G#h5=1;(||J+UYs&84K3PcWs zV}8~J`%J*YYLovdJ+X|`;k?#Vd%VSi_L4t_0C~lVbda6gxgNby@MoKw{rE;x2Ks+k zk{@MfR)Ho3)O;2=zX2muwy z@MwxKfZ+@W*8+S*a)9zj7pfuuFn=e$9RLD#N}v-1W|=`j2EXtR`TOs<>zrphTY{%$H8{`n z?BV}}fB+tJphezS>Kn+lK%>i0Ggl=2@6O<)Qo^ z!2>4&Lvsru#Do5Y3dtYjPa%*E_}@g*2lqPa*)>~vXd6V$Ngd431)_xvgz38wHhyO`^+z#gz0&sqe7ex6$K8A$(x9+rHy@$b1661U!AO`aQ52c`+p|0E4 z8l0WH5!T!z!soC|6yV4_I-KuQqXWLc1U~n!@;kR|b-x;?#|HVI9;zSd&&&dZ|La+> z{3whpP!tXv>A5;P=)IcGkZP=F6ah75^va z*^U35pGE?VV*ZdnM?n5M7jp1_(t}nJ6#x6hmbV_&YMsVh@`%S)K6*q%E)fCC=4zR&z*@3fcWV1W=J9n9 znHfLt(j6f-{$x8d;fwrZ0AD$upq=th+TUGk1n}m|Tp0PgBgp6Jyg#t-w$NWT86Ty69$LNC z8qV*pb|OM>@-pFIp#R&407C$?z!d_#5w0EwlLE>_aMprmBG^QLS%AD3`nDOZeit%K zJ7d$G&|LySZ53irQQ|-R4>m>tkQ0kL=-lvsCVxx+TSC_S(XQhToqF@O)5n+LM>-qk zm-8&c-i(H)g2McW9r@#gXdAc5+1;b4XAjJiNy+>;O4Q&`oGZ+q?HNmMKGL@j-T%44 zJAdX*th#iWRu2Mybj^J*7@pVTIA8y=B7fpP{DuRgeBeKWL8u7S6AJ(y+R<9nj#MYK zn>NM&R?fWT$m#cQGfzVR*>9;QaF)m^&7W~@mNy`ODKDHJ@^_nbjvFC(xO134EkpD0 z3*fIIl0Bk^PfuKP+o8Z7cF&jBJ6ikTgjk}$J?pme{g?9k|7L*5EI_hA_}@#R^G1jt zcV7ycCxSBzkY5M>%x6B6pN8=`z_6V%?9OkjIUVU?KT!bNGssWDHso*17s>!8+0l4J zGyfC*ulzr!5CWL705c!^UL99{X@u~I~dy< z?6`Mg&waUNzJLw50DrMcP<@Ao{E2_e|H@asGW^f=+FVcSto?UO%^m)Gsh@TGzn|Eq z7p!^Pe%+aC-Vy~cp#sz0$#UrW8u@|**k`oc3;`j3v?lpO{0HjzAILM*HQJqLqX19% zuJO8Mh#dHgMes!GQ2qd)oDVWb<8IcSx=lXrrsTNXrdtm4BYCjr8nb4|JOA?ndxQhw z^1jJa+cdR0@7?MU{9l$R@Z#lvsTEIJS#0SSSUi4SY8Tu?X}l>BDiHYrP{|gJg7oI zqCmhul>9Mdj9*j=s1%W24*yF5SxaCEaX3G;U(b@zOK;otz}q)DvagG5S=q001Amxb zAr|tN^DzoW>F~@K;F&uRdv2B^(0K6$IuJ9c6Y>`|oQ-oO^Mm}u|B^o~FG;n*3qR*- zlxo=#A6~#?6^m4Pvp2S-fgI>L9_-T z#3PV=G!Nv98ce~!!6?}NV<&CVa}A^Xx$o|KijS;9fWmaW9-ha0W%`|=eIJGS zp`wrv_LcuB{>Ns4I{gpQ;eVsu2p)ezJDo!a!TAN-9B}J~)1Fvkj-c*Mo)G@-<#qr_ zsE)yTEPui|L;I*3Z-YA7ADsi(?ic6BdEFrJi-3XxcTiJ(4#G`xl58_xw%-YxzBYRK z{o70t8TmLh80$oW-FI)x|4XkYiq)Vd5iR$XJK~<4G@}@Iq~nAzHzLN7ZLZd3YY+$W z+)RZiXsAn;YxRqn*X?}V2`8Lz>ZzxmefHVspMSoe*_y5X$IRod{{@Q*0Y(;?2=+xL zg8%K`{w zKfzzh|6^}x^&`J&;dKU?pSjBZFMHYj?S2a&-7#a69q~NUDB3UPuY@NFg#XJCKr|q? ztsGz7dVWU&)H8Kt=(1DNsfMh5)iaPi@@x*Wq=j z$2!0pN;~BK*9QM(Dkx{0TFUVbAU6cS?fP1$>wPXz7BE0Uk$;@RNnnz@=}nxhTaokL zYi)AE8rvMd+SbQS-qOJHm`R(@SZT8vD{Z>-3TwhPtpenTodIo!{fC(SK2=R+hk#9F zKjiQQ48X^^gKrP{8yd$QL<;V}p$bBnzhuAiiA(hH;>YVI>@}CkZ*cmTov`?ZqcBp> zM_*a%NTk8wEs#sW781hz)Cf!l9szkU59dHjw=ggUW~fGY2DZ`s8RSOoZ~=~>rb8U) z3_$KHfE;DP+lgy!2jqU0@12u(x_s3=E?;F22%oXyrhcny`U;x>A@owS46DO43(Pw) zplgMCA2rdQY(GjQX?T;bbHBdbeff6yHyjWeGReh39PWJBKA6Xx4EKH;R0NSSRmNBQ;kp(F8tr^#?JhOKRGW4S3x_d zoHNv%IQ{QDW7Gdl{=fI$dz<|4wQR}%&u#v%g+N9b76R4d0A>N6d+s?e1$zAP$6Z){ zAo$i>Z#7%tr4XMv4!?6P%o<`#Rt20zfFv=<9q-$lu|?57@~3H2Htae^{=sY2N`m&zlx_eC1Y( zI{1e-WC#P;IHt_(kA?YxGX~_dik*Q#LI4cIGhkCV2>~HJnCODHL?7P$&qwq%0N7MH^G@-CsBVz$H(31a;TxNY=>Z@bQF3;_{ zP7I)x#5K2&pr$?DQTRXMOq+;hj;ed%#mw3I;{Vf5JMElv&XNBaiT^FaoCo}$AwXsU z@;I1Slg9yMfn0#gkOlHYn2F%8e)X$HC|(VnCxQ)I?mw`PCxRbdd&Yq~?*`rw8-tly z#hL#%KebDR2qar>Vox&!#6kFo0^<(&pW~=gq6jiM9XAaBa}5*?y2bxoL(hUM8jouv z1e7|eSfF4hNUOkupe3>&`8%70CoRMUd>wao0lpfF?B@>LkUKaK%;*PbfuEkVzd+y( zay@__{m*&BeuHn(0_{VV+(mJop1B61P}3hF^SRI|WaAqZDT<=~u~I1kzf zbvXAG(K)$nUk&QqN(D6?;#8qZ>KZ*Fe0fd=mj(La4}WMB;MZX+ zOnTsf2aE!e1-kzF>m`Dk9n_{AiB6?QsKans&mQ>q5qQHG4}x%8P`3x{edJUz%+I=~ z?4f-tTdc_=qjz2uualxROKS3r;1VKnoWD7~ja0 zxuGwroWC=K`2!x9iSp+QN&YDGwcuY<@4v`u17@CQE%5%O`}q0MTQ{5Vc{NpT6=s=Kj6p$NlM(4m}Kn|q|0dQVA2aJPf+YY$9 zBZ3sNAAzfb@f-r`j)i0{Q)u@^+{*Xb)*x4a;2Mqo*R1?7xL3t@e-VW`(K(as{3YG}^%0X?P2u^hP z&~}GX>`mTQ4uRWNxeDzrH1r#`x{X#$;82-1X$^SXZA>|sSjv%=ykP-!;oq;{@ zm-#am+K&q24$crEk(Ei|s@B@EOD2L2Ll5Cgeah6u}Zw4an_uphLJ! z$lq8X;y?Uf-hql8SVI8FOb8&?VKpHjItPpc@}RnKu8=)lC+LVAPLKc!e_L+d11d?5Qw14sV>$YAA8^avK?# z2mvu=S1Vv~(u6sF?T#=8Q}CyDaBJno32HT>NeF14|2cVyO-8?w7u~0~uW(>bPmTWP z**hN;Fhezi=M=XqejesqK++n>Vt`v{pN{nCd9fw33K`hsZI*Kxncc5tSZfYbL zVDXgN+SBv}M>#yc5ZUjGe4}kJ6L;V{e2T0x7N#-ChNOKUr_23+A^$xWU8S$IKO%b3 zfn*`LU<0nvf!*`zncMFVlVzd$2pd}nFOXySFnJU;kTYe7f_zAJ5{DrF_()D-C-3lS zs2Io{6_5ib?t)TSWr+eX%(?)d`G$%}n!>q)PvMqviU@vS7EO3f5wnHRRUNzPn%p{$Ka+mKwe9dOr2*U2og%piJCkU<=qHLRsyE^N@Lsj){4| zL$d@D2Z2NQ=RVC4z$sW9N`-^~M__ zzh(9wMXc0K;O$Syk4aoJl#a=R^C%WrD!78WKkd)x60kXiz>yHp(K^Ga9o)qV>JSUJ zm^>ED18TFN>{Hd4`XY$KBX3+l9L+Uwz??R|BcKQ(p+|I@K`xyWc^r`c?ce?_=8xh3 zRbR72zw=z1zh=U0JM_loTIqi){lqDb8B=!gT$RIW5cgiLtp&7+ppd&Wgy+c3xW;)| zLV%2r$EACSZrDE;KK-Ar`AEB$cp|?!sTi(Vpl~ZLoJwC!Ua68N+dDts;#3@4N zwuuFC5S*`m6c@ip6exk@aOh z@|`e0EExrp{QyG;mF4ND|s9A{r@Z$E9=*ZCry~tO(xNo)!Nqll}dY|G7(U)uuyR z^1sF2_?(Bvl*O-%dPFp>;r6vQ(_(JZJ*sJpka3OBmdt;=IF3xw$#lKM`G4`h?K`1& z#y*E<9zPGZ*w&vM1pjl5l1AZwAPlhM8nxA5J_1+OFmoKlmid#}&*B{jyz3t`e}fy^ zV?n;MLX|m@ldJFi4i`oW2cyhd3Cp#eQ2|1dw!#GG_aaU*RUsFONT z{s_Nhzp|bE4t2k)({5CyD4|)9yNm7w-pWPe;*~f8QlnQ z5prDEy|^Q$%(dI||49p7{=a;!FTYTkIy!T|sZ*wO9&C>tdg!4F9E3C?mJFG>Mz9Ty z=r{Zi(oNUUYb-DZ;Gh?7Ih12#84JWg5(p|5IA>1lSZ9t4WM@vLrbApnU4w52j|d^; zkNLR+M3D+)Kd4at&{7HiWDCRm(F|iCe=T$j-Z9u($F3ru*$B2P_fwbZ2Cp@Z{% z4~OkopbqwMj{k|AD0x~XWQ%Ws5XKl(BkCZW*if)DC-@e8HY(u*LYO~ELRu)266 zSNUttPYs^-oiB_(+?RUtx;?^E^OhZswRX`8#-olpN&)|82tedy2!Oo-GDvHJ94~=f zQZ#_4V4H&dJ}toWj$qz-7=%N+({_VHXNsZB&H!7Te4}HF?VZ!NB&!+DkHVpx(LO#T za^yom-3YgGJ`p*f9-O0n+{j3sPcPT%SKWJ>^rEGA9Iow$-`A4=-?Yy5?g-&C1zt?W zxmmXy=1(tZ(v0ifc=+F)B6)Y0p!V{vNp0med3x*o&&I>wz{0Zk&OQgo>2&WlKlfSbk~Zsr62L|Dnf)|D$t?|Ect0`5#;Z$Z&qJJrv;ua+P72 z&BFCx{zr5%yHcmzC;pOAZLJrTB#Gun`;q+;KFMungd3gHB>&KUKo9d{13VYl>@{!b zs~=nP|4AF|;>O)IOB0cK0;QO!{jrT95}xsq1_}R{{+Hk?^3Syi0vQ7I!~YB0`v0Nb zwAriQFqFh*yT>oNUwXi?M#m+MvN|Fup!5s59`ypoFsu`suA$2Sc&Krfm3|b586+3NlflTInGo+PpJCzMR5ihQ4sj9| zg%htyz zdNeN!^UD&8yqRs(1==66Xk@-m>?Nt|uEYOP{%FybruGyxxP*`3D+=32WC{@CtahgQ zIDVWx{4d*|h-b&(OZ!XYe2KQ=D(lUW={z zV4rVwtG2pp;vr-6g~3ttvISZy&+67@!OwURIa%r!{y|Fe4?tc?PW(^*W2DHNVtyd+ zfp;KnTTm}-SD`CsI(mj7d}chGAuY5=+hBfZ2X%(|8MV&>c(fl2?3-J!^zj3!%^U2u z>dEs7>VmHG#tcl7|H13p4=~gY5r0{5kj{7;$su~_$8MfPZ^N^*mz*R9M#izef|09`p2ES z>!BF}Af-wHHTgdZhyIVk3CNLu!CRq+;SpC^6Y4pXAy`t zEdCb(xw#1roGFWrBl;&! zk>PE8p)^mtz&2jc4SPtVrx^!-F32tL zndSEF>Arsd{Mv>yyca8Z&gh)x5+EX{a`kwrlz$<-KweZ|2oK16aGo?~(SAe@zz6L` z{vFZZweUZlj}`d+={Mp@-#I6LL@_5_+;aZ+oHrhVPiTIoAj6PUvmpS5Y4By_h5xm7 zlm9as;{N=aw37u&2oMDnLngYiEC5_@5 zQT|c;Vg4w5UQmuuoL?#Z(0&R29u>q7cmbQ~1GCaT&GD}?*O>EB@ZA#+onM_b_}($M zz}0Wqy$8Bu`18p%kNwCNhwQ)Ou4W-?DRQNN;JgO8^nWxzoHuE|5MIOB@O-g-68nYo z9?q{3ZZSX1ceQ`#*Rm9B*H~12+HigWcgOV|9kRFO#i2YHhay4 zVf($WolmZLTwKHS4c9PtpDa*PKnVf#KffscNB+_LCH3PJI#fzOS$#drGVJ9GLutR5 zU)LA;_Y|n03*WHaoU0&8vLHPuyy#lslqELp3(})hK5z4VGSrm_%2K!ByqN+j{SW7r z!vytcn;!m0`>dc2=d4No&4f=U#n~P02K=^Vef?<5C^wjo?wok=8n1f&oOg7~S(pp< zo`(6Yyu$ohg*BA_HMpiW+k#9mN!nBN)O4Civm_2S2SIjTx_ql&u{`;ul>cvbt6siy zg+peGhc*>%-C!!$$Q+qbKo&?O3uM$*A%HrHG~ycJe;zT^DdG_s{2KYz;CHAHiukcD z3lexlzb`f!_BIFP-vrEywEVz|{r!mBut>;GznXf>dWZXAcRI&Xhl~Ph7C7wG69V9z z;s1mHti>XAivIz=$X_9V&$dE9fxi<57F%qsB__P%4O@*D&g+)nwbZub)ypmO|L@wj z^9wQ-nR^P)(?+6zSRe}||0DTkIgrVB!ctS?lMDe8TeYW_3`?Yy(lXiF)ACo07uNTG zw(Q61`Z+9gTx0!?Yc$nSMr{=W!vD$t#4j>5P6&uo@P%0E|E43Pb15GMTWsI7eU@DP zHbKmOIZd&8u6W84K@HIA1I*4DCYt&q@ zF&&SHHJySq5)sy2W};sU73gnU>7emt`{un|GhvT$@4lRW+)q0=VZSZ#x#jn3_VrvX zKR$WJeUqo&JNfX>PCoGRRrkDn)jg=zV^-eGFP?P&ZTh~IccEZAB6g+Su32fXdnQfs z@3B?J@)nCR-^l$Fr?mMomwwwh^~ZhKHRjtK>A+;VKUwoo25QY+1k{aoV^r-g)PrfBw7P^{)57|NS5Q;0G_h_~J`1z4VGJuDJ5bE3dlhs*io_W7k}B z%_q8c?X}nX-=9}sef7sb{&5vP@{x~x_`@H*7M9lzy0lRfA4$Wd-mC9 zfB3^6{^&<4h3B4o&LQ9Y z<~JXD=%KHE^{e;Zd+%+x-G+iN&mE_KXaR4i8?U$;NutfW?z-##`|sBd8sR(N`HmKF zYwq#qKmYkJe({U{{_p?(&2N75yWjop4}bW>AOHBrKmYm9|M!3Y_x~0!b4f1jPk;K8 zEBxR8{a=^*+u#27*T4StuYUEb|M{Q)(R(`XXFvOyK6Pu~|Ni%%dFB~+`mJw$>*0qV z{`%Ly4h9U(%LM%NeX!G z(2vGz&+RK5Sl_Y`7cl=LCtr%|!u*Ju@O$EkC!TuhsnYy7A(|h()4ixZ>_Ue|gMf^TR4yh#j}-#YNJ0P#jg0hNci(;Y*UtUu0^68!9opn8U-^o( zPX<9+Lpw-(CG(T;=YHKj7DcDXUyfw~r2U>tYKDO1f^=W{PVCMoKx)ef&dnO^0D$hJ z1VBPl5C%06xciG=USNB0%om3I=N_4ibX9Zj8yDg;_cZ3*yVLFdeg!{qweY2bzPr~u zA3pm^EJv0jI^@j3OZLkHV>|-BC+AbLKg=(U5Nk#O^jIOl87c&XKzx^2BN6o25%2Bk z#0BBchfldQa~}^r_#nE=#0MqpMt<^>pWsF#pfo}<|0VuWYY*8jn15CX5VI--=uP1) z!#8~=&^L2ILVyUED3D2kM1lKn{=&D8zsUG;LFl+Syao>b*}3^jhfywX?s3;beCA=> zb8g@7yO-YXf$@84c-UK*t2@89_d6fDYDJn92;^_JV8{F|qJB+(GXMfPix zGzc+V!ynmlH|RFqtagawB7Qp=!+N0-w?EX9N z3G<_Uo+}~Gg^4=(dCW}XmEe!i#rzl`6P*~3CEbDaFb3vFeP#aap^=D5C&8Rfh(d8` z%_{nKsdZ7n=;MX8-#XtL?V*p|MAK&Oqi4NSa{h^f$Y1VDJB0k*vKCk{m;Dsgk5mu> zSb8wsm+;+82Q|k6dQcP)0x}#B95s|sE@zm`UMHa6dCw!~Tshx+v&{wEy!`%0ysqzG zuj`jy+FVJ$=N#160;4zVJZPfjUb%=zBgg!kL8D*)xP1EDmZv>_;>8bL`f0Kjxkd0} zezZ@7%WOpR^Jpm=$zDW<-q(|;S=VM;%zcN-YY8_5gpWwl0=nGf;pW~J^_2w ztYQSDDByY?`uvxl+4q7M)|x*v!r$NOjPK02D9n$|G>(8MP9+0Y?U~pO zxnX{^ZxoN>vZe(sGp-%x=j>#Em|rRY`G@&K4#gFCBLXtpq7h26$sr+-WD=U};R0Mn zsDpqs7U(eWb+>)@(DyvQ(c9-ii#@yZ+28!oryu&#Jz;(cpU8e!rCIP>o^+^ahzvvi z83a`vbo<4nFUCT%`-TqUk_!_1^kAX@g+M0&jL~9qEC5$Rr+{87g^cVV@LQ*TWI+f7 z-<@>KeG?Dc;k7HyGiGRy;dypR3)2_1Y~J~G%U+yv>JpoMZjOI$X7t;9X8SXrn)beL zeDt$ly}vV!n+ygnt@IAB#Ia4n53})K!};R;Ea}g(7DUJEv0Tx9aUJpxT?|WfK1viw z2uKtVKEfecUYL9`4hey{7-XARU_6p=AWnrw+z3gDpBZ5(kj30)8%t#e<_xw;d?6_3 z&t$i6Oa_da#b&Mttu@C%H3-UVZ_++)FmcfR4%-wj#+}uDZmrpaU{IYXn%BHQW_A(_ zvQkJ_5*2zgg97nXM9|uuAzrs16A>P~@%C?Ca1H2=j_omj`t6mcd&l55uUX!krsj+L z_j~2@ybXOcwN)me{pX}tr%E%t- z5URo+i3RS(9WjjEG^{eZH7g4!&frum0a2{+Eto2eGtxLn%arVIM#Cb1_xoa``jM{L z?J&KZ5TNTb=&Mc?$}^y=C;&yZQb<>do{%P(YS@Hz{g^r z5z2KXaf z;Lo^*3J+iL*@v#XHBP~^xR^JH&RiUdPs4G- z_Iwq3%;6;nBtfGF75tH(!gu)i_IqdEbZt#fPgm7;&)YTCPakG_XRG?H zdaB;1YG(hmn!g6eUlSFJnBOrG8gj$~S-FzZJAV+}8v$7bWGUA-boyRERsoqvGMc&y z2u7J{I+Vgz-`wd!ApB*dbRm{SS=Iu5U!co@Y-1!fmE7P3VO$w-}buCmn4-`m2I0G;$b;-x9@+>l_a;a}>uM-X z(gjgkDB71p)-Pv%RbtuZuPs%zag@<)Tnl762|8Ic-EttaPVmYhv~?V1EOew~eV0S# zty|wUFBYXnl?}VkE-(C~kQ9?Q+BO+Y6bQr7ny zS%j*e4*TlRw+2oi;t=VS9I`0rp*?MXtjbQFrS4QklLsXuA>k55YTLpAA*M*=qn)?1uMpM}eWH*j73iE4$OykJZaA~4hqmM2rbQdId0*%i~N;7ELuW8<|mvjm6R{`G}(irirt^!USZY>apNMGcb+IsgvloC94OIK%l zDfN;%Uw8YuEJ`zKe59Q<`S(j^<*rs+1!T_XQ0iQoyjl9|%HMqmQXNeptiW{;Ws2%T zEQ_*R3uGqO@b$(8Il= zp4aWYv%T(YHIHh}ms}HhF4Dei)ihN_Yrfe;&dV^r^V_^PB)Yal9^D2~3@nGP1-?L} zJJKmV#M`NdR#YbPC|;7oy%my(PWz6}y!oXIt~(n9o#6b|P*7~9Y>W`)XRH_PDXzLKj!0Gxf)74+!H8nIZbb-(}3vDTJaq>^d z<#4q$DddAzn~Ww31mX$Yci(+F=lyA~0@vNCTOHiy*1fL9eA~-Ki>Gs29{mX)bM7iiM&SQ=DN)jl!2S2%@6Ugi2zt`8 ztF4uEU(VZX<>WwvM@zsh!EPG6iCsHclp3{S`lVd$o_p>wOKJi^Ad-OiUy|KT``W(9 zWkLH%t@>!~LC4D@;Lq*$EU10guPEpEKhj7Pvm~JYFE4HB5d^X#fd4JEMyyBz_&+O4 z(3nyQ;QvS?QOuG6{x4NH^eZa@_}@}%#EK+<|Fg0LjVYA?{*N>g#ViTn|5AlhYQMI( zw;z4<(E|q#Sf&ZFAW$R${9mdk%j=i?M}-q7P86AjbP5845WxTCd9uhJefZ&rgCqz+ zAy6;@{9mLk3+t5JRO{-iuP!(Z=^6wEA%Oo2Q)Q9eIePTyAlX4s2$W3#{}(CDk~$^- zc&h9iq<0V)iU9sENtH$R<)e>28Y(-83xTo;Wb1#qr|Ns}z4yWkFZ^p9$BQq%`1*f<0|K~qfY2k_z$jbjK%5X`|USD57bLLDw9v-SCk^aB-+H1f6{`)~1dGO%DCCvdY z3xTB)$jATkH$F@4=_+3QvBw@8L}*zBjq(5S@P}^Ys72 z4?mpw%%OP@2olJ||6hIeRj{e!ERc_fhiXZS|K-zNue-{q^C)hx-~|t^ym_Wt%KCER{ee{+BF%88K_`pe$8xkb#!|3{7-DPwBU3kWEIto;A$ufN`Y`|S*k5RiRlZXO=0J(>9bi6@?5 zWzZFJ!58aVpFOlCc9Ll%3#m&M0+uPfF7)Po=Aaeq_`CsJo)?07g zaKjCmb6Z~XuDa@~AAkHYHwh2eo*euy|A=pSBhHl}Fcg6d{qL(r{=ee?kK-8dpP2c; zGdv&G|CcXcK6dQbQ0Ylr2rQLA=Kjx6M>NTpz_9**>Zzxent)s!0s|7j|Hd{WTq=QK z{U6Lt`MfSzh5``ClK}ot)e+^52@K_bxv|&Lqet^hTUrHyUI^fSW1A5!mB3K`fAGNv zdr2`a0)cD^;Qv$|QQnxqQ2r0*tlV)vScU=+$e95CH?|q!QVH1m|LobbhYlUeIc;eh z1bQKW|5J5Dd1C_h{^!PCy|{&o+_7+l+9j1eWdp zV4m{PP!JIXR+Iq#Pt_6SjR`E(|DS#K*@|W^&4xgC0{Gw9W`s*6usr{tKYxDAcN*<( z53aE41n_^Vjwo+TV0r#u_2i}f5a>t%{~OzkaH#~AW{DbLPy!g9i)9U%CK+E(GwuvCRmVN}zZD|NZyhT~da+5GaNK{!i5r<&6pS z?*HeXf4)6`xv^I}AHG`*0sL=lGs2}3=-2-reDFar$xBBd(2)TCPt_6SjS2MV|4WxH zb<7v4L!cxA_}|!Ogi9sRm;dF)UXMKTNJ+^{Um(zt0RB(a5#@~u^yUATUV5oxvQQlY zg%QC2#x^5dDuG`7|IIhw6qdPk2Ljy*;Qv$|QQnw9cmMzS=byLTc3byk;R+Bai~#;O zwi)4433T`Wbwqh%0+Iedd-m+8GMCOmpgRHlZ)`Kdr4oqo|MBC; zyXOK|fWX)Y;Qv$|QQnw9jQ@ja%P$255m8`-1n|GH%?OuDAn5;3KmBxsnM;=;(47GO zPt_6SjR~~-pBsC1mjG87Edl&*Y%{{85@`4TXfv0NL!dhW{GX~L${Q1C@qh52yzwtU zyDNe#6ifjB8{3RE9s*_r+WpUsy=*K& zl!6JEH4{{?3yU4wuPf$i<>)U2dD1Z)W4e;Wx9Wy}N)95`T;&P0Jg5&`@_ zW-rlqGXf`0oJdMc3PZqv0RA^K0D(qM;N_QJ{`u#h4U(D=5Qrpz|3@w*x(@*eKmY;| zfB*y_009U<00Izzz}N`v+qZ9QU8cVffB*y_009U<00Izz00bZa0SG`~Bm}Oz?z)lm zlx{)*0uX=z1Rwwb2tWV=5P$##x)b1iba#ea0Rj+!00bZa0SG_<0uX=z1Rwwb2tWV= z5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHaf zKmY;|fB*y_009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_ z009U<00Izz00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz z00bZa0SG_<0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SG_< z0uX=z1Rwwb2*eQB;eY@HAOHafKwww`|A|XiW&q%~XaX{Sm_K0x7*$&JOuQ{)7pT zKjcp{0sIlp9@by{(VRW3zxV@xgbCme{LxGRe}uD#{RjSN&Ytf3kCQReZ}HEM{vB0) zZSn)WwY81;PQ-;@TRX6GS|V-MFA!&sNgJmPo%=D~k#7KG*A? zjI37`tJP0!e*MRwr>pD6KG))(jICFdo2?)BqtmMH=Pf|YpK4k}KQn)rKaK$T z6P-6zLGs7Vo}C@$Pc8sks);rA72J`9uCV0_0D0-c$w2A2)kgf5@LIEux>vAM(c$Ab+CsrYcDOxY@({ zL;h505&ca5kUx$9`4gQtRYCH{%^ubt@~29R=x6eW{BZ=xpXj`)3X(r=_Kb4<+3>o* zm*0Qe9Jv0LAAb+O|L<4dN&owU*}o!7|NDp8|H9Yx&rjqJq7sjdnf_1zA}Z+$TeI~q zBvqEev(=LaV7+7g+2NCmCANRYo|CqmOuNA5q>g`_?pQxD1N_j_^RyW}JA0=WNNWEa zzC?hVbM48glCwZCiq#nV5o{~J|n=bxk6-?jeceBA1lsQ6n|>G@}q`txDkD>3m~rhnA!sZsCTr<=1Zr>b(?<-#&fAj$QPF#d)=SPkc) z@!M>uUUxE}FUF!#@Zy^5A^@Gzt(Eg3t!D{%bef+xC z-)x9D`%-)St=Ym2Uv;hD{zKq~fc2N!e*O}kzU@DP4BA^7)Aj?nnEGu1!_&V`gRda~ M0SG_<0@)JyAN$kcNB{r; literal 0 HcmV?d00001 diff --git a/Slang/olcPixelGameEngine.h b/Slang/olcPixelGameEngine.h deleted file mode 100644 index ebcf545..0000000 --- a/Slang/olcPixelGameEngine.h +++ /dev/null @@ -1,5603 +0,0 @@ -#pragma region license_and_help -/* - olcPixelGameEngine.h - - +-------------------------------------------------------------+ - | OneLoneCoder Pixel Game Engine v2.16 | - | "What do you need? Pixels... Lots of Pixels..." - javidx9 | - +-------------------------------------------------------------+ - - What is this? - ~~~~~~~~~~~~~ - olc::PixelGameEngine is a single file, cross platform graphics and userinput - framework used for games, visualisations, algorithm exploration and learning. - It was developed by YouTuber "javidx9" as an assistive tool for many of his - videos. The goal of this project is to provide high speed graphics with - minimal project setup complexity, to encourage new programmers, younger people, - and anyone else that wants to make fun things. - - However, olc::PixelGameEngine is not a toy! It is a powerful and fast utility - capable of delivering high resolution, high speed, high quality applications - which behave the same way regardless of the operating system or platform. - - This file provides the core utility set of the olc::PixelGameEngine, including - window creation, keyboard/mouse input, main game thread, timing, pixel drawing - routines, image/sprite loading and drawing routines, and a bunch of utility - types to make rapid development of games/visualisations possible. - - - License (OLC-3) - ~~~~~~~~~~~~~~~ - - Copyright 2018 - 2021 OneLoneCoder.com - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - 1. Redistributions or derivations of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions or derivative works in binary form must reproduce the above - copyright notice. This list of conditions and the following disclaimer must be - reproduced in the documentation and/or other materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its contributors may - be used to endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT - SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - SUCH DAMAGE. - - Links - ~~~~~ - YouTube: https://www.youtube.com/javidx9 - https://www.youtube.com/javidx9extra - Discord: https://discord.gg/WhwHUMV - Twitter: https://www.twitter.com/javidx9 - Twitch: https://www.twitch.tv/javidx9 - GitHub: https://www.github.com/onelonecoder - Homepage: https://www.onelonecoder.com - Patreon: https://www.patreon.com/javidx9 - Community: https://community.onelonecoder.com - - - - Compiling in Linux - ~~~~~~~~~~~~~~~~~~ - You will need a modern C++ compiler, so update yours! - To compile use the command: - - g++ -o YourProgName YourSource.cpp -lX11 -lGL -lpthread -lpng -lstdc++fs -std=c++17 - - On some Linux configurations, the frame rate is locked to the refresh - rate of the monitor. This engine tries to unlock it but may not be - able to, in which case try launching your program like this: - - vblank_mode=0 ./YourProgName - - - - Compiling in Code::Blocks on Windows - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Well I wont judge you, but make sure your Code::Blocks installation - is really up to date - you may even consider updating your C++ toolchain - to use MinGW32-W64. - - Guide for installing recent GCC for Windows: - https://www.msys2.org/ - Guide for configuring code::blocks: - https://solarianprogrammer.com/2019/11/05/install-gcc-windows/ - https://solarianprogrammer.com/2019/11/16/install-codeblocks-gcc-windows-build-c-cpp-fortran-programs/ - - Add these libraries to "Linker Options": - user32 gdi32 opengl32 gdiplus Shlwapi dwmapi stdc++fs - - Set these compiler options: -std=c++17 - - - - Compiling on Mac - EXPERIMENTAL! PROBABLY HAS BUGS - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Yes yes, people use Macs for C++ programming! Who knew? Anyway, enough - arguing, thanks to Mumflr the PGE is now supported on Mac. Now I know nothing - about Mac, so if you need support, I suggest checking out the instructions - here: https://github.com/MumflrFumperdink/olcPGEMac - - clang++ -arch x86_64 -std=c++17 -mmacosx-version-min=10.15 -Wall -framework OpenGL - -framework GLUT -framework Carbon -lpng YourSource.cpp -o YourProgName - - - - Compiling with Emscripten (New & Experimental) - ~~~~~~~~~~~~~~~~~~~~~~~~~ - Emscripten compiler will turn your awesome C++ PixelGameEngine project into WASM! - This means you can run your application in teh browser, great for distributing - and submission in to jams and things! It's a bit new at the moment. - - em++ -std=c++17 -O2 -s ALLOW_MEMORY_GROWTH=1 -s MAX_WEBGL_VERSION=2 -s MIN_WEBGL_VERSION=2 -s USE_LIBPNG=1 ./YourSource.cpp -o pge.html - - - - Using stb_image.h - ~~~~~~~~~~~~~~~~~ - The PGE will load png images by default (with help from libpng on non-windows systems). - However, the excellent "stb_image.h" can be used instead, supporting a variety of - image formats, and has no library dependence - something we like at OLC studios ;) - To use stb_image.h, make sure it's in your code base, and simply: - - #define OLC_IMAGE_STB - - Before including the olcPixelGameEngine.h header file. stb_image.h works on many systems - and can be downloaded here: https://github.com/nothings/stb/blob/master/stb_image.h - - - - Multiple cpp file projects? - ~~~~~~~~~~~~~~~~~~~~~~~~~~~ - As a single header solution, the OLC_PGE_APPLICATION definition is used to - insert the engine implementation at a project location of your choosing. - The simplest way to setup multifile projects is to create a file called - "olcPixelGameEngine.cpp" which includes the following: - - #define OLC_PGE_APPLICATION - #include "olcPixelGameEngine.h" - - That's all it should include. You can also include PGEX includes and - defines in here too. With this in place, you dont need to - #define OLC_PGE_APPLICATION anywhere, and can simply include this - header file as an when you need to. - - - - Ports - ~~~~~ - olc::PixelGameEngine has been ported and tested with varying degrees of - success to: WinXP, Win7, Win8, Win10, Various Linux, Raspberry Pi, - Chromebook, Playstation Portable (PSP) and Nintendo Switch. If you are - interested in the details of these ports, come and visit the Discord! - - - - Thanks - ~~~~~~ - I'd like to extend thanks to Ian McKay, Bispoo, Eremiell, slavka, gurkanctn, Phantim, - IProgramInCPP, JackOJC, KrossX, Huhlig, Dragoneye, Appa, JustinRichardsMusic, SliceNDice, - dandistine, Ralakus, Gorbit99, raoul, joshinils, benedani, Moros1138, Alexio, SaladinAkara - & MagetzUb for advice, ideas and testing, and I'd like to extend my appreciation to the - 230K YouTube followers, 80+ Patreons and 10K Discord server members who give me - the motivation to keep going with all this :D - - Significant Contributors: @Moros1138, @SaladinAkara, @MaGetzUb, @slavka, - @Dragoneye, @Gorbit99, @dandistine & @Mumflr - - Special thanks to those who bring gifts! - GnarGnarHead.......Domina - Gorbit99...........Bastion, Ori & The Blind Forest, Terraria, Spelunky 2, Skully - Marti Morta........Gris - Danicron...........Terraria - SaladinAkara.......Aseprite, Inside, Quern: Undying Thoughts, Outer Wilds - AlterEgo...........Final Fantasy XII - The Zodiac Age - SlicEnDicE.........Noita, Inside - - Special thanks to my Patreons too - I wont name you on here, but I've - certainly enjoyed my tea and flapjacks :D - - - - Author - ~~~~~~ - David Barr, aka javidx9, ©OneLoneCoder 2018, 2019, 2020, 2021 -*/ -#pragma endregion - -#pragma region version_history -/* - 2.01: Made renderer and platform static for multifile projects - 2.02: Added Decal destructor, optimised Pixel constructor - 2.03: Added FreeBSD flags, Added DrawStringDecal() - 2.04: Windows Full-Screen bug fixed - 2.05: +DrawPartialWarpedDecal() - draws a warped decal from a subset image - +DrawPartialRotatedDecal() - draws a rotated decal from a subset image - 2.06: +GetTextSize() - returns area occupied by multiline string - +GetWindowSize() - returns actual window size - +GetElapsedTime() - returns last calculated fElapsedTime - +GetWindowMouse() - returns actual mouse location in window - +DrawExplicitDecal() - bow-chikka-bow-bow - +DrawPartialDecal(pos, size) - draws a partial decal to specified area - +FillRectDecal() - draws a flat shaded rectangle as a decal - +GradientFillRectDecal() - draws a rectangle, with unique colour corners - +Modified DrawCircle() & FillCircle() - Thanks IanM-Matrix1 (#PR121) - +Gone someway to appeasing pedants - 2.07: +GetPixelSize() - returns user specified pixel size - +GetScreenPixelSize() - returns actual size in monitor pixels - +Pixel Cohesion Mode (flag in Construct()) - disallows arbitrary window scaling - +Working VSYNC in Windows windowed application - now much smoother - +Added string conversion for olc::vectors - +Added comparator operators for olc::vectors - +Added DestroyWindow() on windows platforms for serial PGE launches - +Added GetMousePos() to stop TarriestPython whinging - 2.08: Fix SetScreenSize() aspect ratio pre-calculation - Fix DrawExplicitDecal() - stupid oversight with multiple decals - Disabled olc::Sprite copy constructor - +olc::Sprite Duplicate() - produces a new clone of the sprite - +olc::Sprite Duplicate(pos, size) - produces a new sprite from the region defined - +Unary operators for vectors - +More pedant mollification - Thanks TheLandfill - +ImageLoader modules - user selectable image handling core, gdi+, libpng, stb_image - +Mac Support via GLUT - thanks Mumflr! - 2.09: Fix olc::Renderable Image load error - Thanks MaGetzUb & Zij-IT for finding and moaning about it - Fix file rejection in image loaders when using resource packs - Tidied Compiler defines per platform - Thanks slavka - +Pedant fixes, const correctness in parts - +DecalModes - Normal, Additive, Multiplicative blend modes - +Pixel Operators & Lerping - +Filtered Decals - If you hate pixels, then erase this file - +DrawStringProp(), GetTextSizeProp(), DrawStringPropDecal() - Draws non-monospaced font - 2.10: Fix PixelLerp() - oops my bad, lerped the wrong way :P - Fix "Shader" support for strings - thanks Megarev for crying about it - Fix GetTextSizeProp() - Height was just plain wrong... - +vec2d operator overloads (element wise *=, /=) - +vec2d comparison operators... :| yup... hmmmm... - +vec2d ceil(), floor(), min(), max() functions - surprising how often I do it manually - +DrawExplicitDecal(... uint32_t elements) - complete control over convex polygons and lines - +DrawPolygonDecal() - to keep Bispoo happy, required significant rewrite of EVERYTHING, but hey ho - +Complete rewrite of decal renderer - +OpenGL 3.3 Renderer (also supports Raspberry Pi) - +PGEX Break-In Hooks - with a push from Dandistine - +Wireframe Decal Mode - For debug overlays - 2.11: Made PGEX hooks optional - (provide true to super constructor) - 2.12: Fix for MinGW compiler non-compliance :( - why is its sdk structure different?? why??? - 2.13: +GetFontSprite() - allows access to font data - 2.14: Fix WIN32 Definition reshuffle - Fix DrawPartialDecal() - messed up dimension during renderer experiment, didnt remove junk code, thanks Alexio - Fix? Strange error regarding GDI+ Image Loader not knowing about COM, SDK change? - 2.15: Big Reformat - +WASM Platform (via Emscripten) - Big Thanks to OLC Community - See Platform for details - +Sample Mode for Decals - +Made olc_ConfigureSystem() accessible - +Added OLC_----_CUSTOM_EX for externalised platforms, renderers and image loaders - =Refactored olc::Sprite pixel data store - -Deprecating LoadFromPGESprFile() - -Deprecating SaveToPGESprFile() - Fix Pixel -= operator (thanks Au Lit) - 2.16: FIX Emscripten JS formatting in VS IDE (thanks Moros) - +"Headless" Mode - +DrawLineDecal() - +Mouse Button Constants - +Move Constructor for olc::Renderable - +Polar/Cartesian conversion for v2d_generic - +DrawRotatedStringDecal()/DrawRotatedStringPropDecal() (thanks Oso-Grande/Sopadeoso (PR #209)) - =Using olc::Renderable for layer surface - +Major Mac and GLUT Update (thanks Mumflr) - - - - !! Apple Platforms will not see these updates immediately - Sorry, I dont have a mac to test... !! - !! Volunteers willing to help appreciated, though PRs are manually integrated with credit !! -*/ -#pragma endregion - -#pragma region hello_world_example -// O------------------------------------------------------------------------------O -// | Example "Hello World" Program (main.cpp) | -// O------------------------------------------------------------------------------O -/* - -#define OLC_PGE_APPLICATION -#include "olcPixelGameEngine.h" - -// Override base class with your custom functionality -class Example : public olc::PixelGameEngine -{ -public: - Example() - { - // Name your application - sAppName = "Example"; - } - -public: - bool OnUserCreate() override - { - // Called once at the start, so create things here - return true; - } - - bool OnUserUpdate(float fElapsedTime) override - { - // Called once per frame, draws random coloured pixels - for (int x = 0; x < ScreenWidth(); x++) - for (int y = 0; y < ScreenHeight(); y++) - Draw(x, y, olc::Pixel(rand() % 256, rand() % 256, rand() % 256)); - return true; - } -}; - -int main() -{ - Example demo; - if (demo.Construct(256, 240, 4, 4)) - demo.Start(); - return 0; -} - -*/ -#pragma endregion - -#ifndef OLC_PGE_DEF -#define OLC_PGE_DEF - -#pragma region std_includes -// O------------------------------------------------------------------------------O -// | STANDARD INCLUDES | -// O------------------------------------------------------------------------------O -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#pragma endregion - -#define PGE_VER 216 - -// O------------------------------------------------------------------------------O -// | COMPILER CONFIGURATION ODDITIES | -// O------------------------------------------------------------------------------O -#pragma region compiler_config -#define USE_EXPERIMENTAL_FS -#if defined(_WIN32) - #if _MSC_VER >= 1920 && _MSVC_LANG >= 201703L - #undef USE_EXPERIMENTAL_FS - #endif -#endif -#if defined(__linux__) || defined(__MINGW32__) || defined(__EMSCRIPTEN__) || defined(__FreeBSD__) || defined(__APPLE__) - #if __cplusplus >= 201703L - #undef USE_EXPERIMENTAL_FS - #endif -#endif - - -#if defined(USE_EXPERIMENTAL_FS) || defined(FORCE_EXPERIMENTAL_FS) - // C++14 - #define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING - #include - namespace _gfs = std::experimental::filesystem::v1; -#else - // C++17 - #include - namespace _gfs = std::filesystem; -#endif - -#if defined(UNICODE) || defined(_UNICODE) - #define olcT(s) L##s -#else - #define olcT(s) s -#endif - -#define UNUSED(x) (void)(x) - -// O------------------------------------------------------------------------------O -// | PLATFORM SELECTION CODE, Thanks slavka! | -// O------------------------------------------------------------------------------O - -// Platform -#if !defined(OLC_PLATFORM_WINAPI) && !defined(OLC_PLATFORM_X11) && !defined(OLC_PLATFORM_GLUT) && !defined(OLC_PLATFORM_EMSCRIPTEN) - #if !defined(OLC_PLATFORM_CUSTOM_EX) - #if defined(_WIN32) - #define OLC_PLATFORM_WINAPI - #endif - #if defined(__linux__) || defined(__FreeBSD__) - #define OLC_PLATFORM_X11 - #endif - #if defined(__APPLE__) - #define GL_SILENCE_DEPRECATION - #define OLC_PLATFORM_GLUT - #endif - #if defined(__EMSCRIPTEN__) - #define OLC_PLATFORM_EMSCRIPTEN - #endif - #endif -#endif - -// Start Situation -#if defined(OLC_PLATFORM_GLUT) || defined(OLC_PLATFORM_EMSCRIPTEN) - #define PGE_USE_CUSTOM_START -#endif - -// Renderer -#if !defined(OLC_GFX_OPENGL10) && !defined(OLC_GFX_OPENGL33) && !defined(OLC_GFX_DIRECTX10) - #if !defined(OLC_GFX_CUSTOM_EX) - #if defined(OLC_PLATFORM_EMSCRIPTEN) - #define OLC_GFX_OPENGL33 - #else - #define OLC_GFX_OPENGL10 - #endif - #endif -#endif - -// Image loader -#if !defined(OLC_IMAGE_STB) && !defined(OLC_IMAGE_GDI) && !defined(OLC_IMAGE_LIBPNG) - #if !defined(OLC_IMAGE_CUSTOM_EX) - #if defined(_WIN32) - #define OLC_IMAGE_GDI - #endif - #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__EMSCRIPTEN__) - #define OLC_IMAGE_LIBPNG - #endif - #endif -#endif - - -// O------------------------------------------------------------------------------O -// | PLATFORM-SPECIFIC DEPENDENCIES | -// O------------------------------------------------------------------------------O -#if !defined(OLC_PGE_HEADLESS) -#if defined(OLC_PLATFORM_WINAPI) - #define _WINSOCKAPI_ // Thanks Cornchipss - #if !defined(VC_EXTRALEAN) - #define VC_EXTRALEAN - #endif - #if !defined(NOMINMAX) - #define NOMINMAX - #endif - - // In Code::Blocks - #if !defined(_WIN32_WINNT) - #ifdef HAVE_MSMF - #define _WIN32_WINNT 0x0600 // Windows Vista - #else - #define _WIN32_WINNT 0x0500 // Windows 2000 - #endif - #endif - - #include - #undef _WINSOCKAPI_ -#endif - -#if defined(OLC_PLATFORM_X11) - namespace X11 - { - #include - #include - } -#endif - -#if defined(OLC_PLATFORM_GLUT) - #if defined(__linux__) - #include - #include - #endif - #if defined(__APPLE__) - #include - #include - #include - #endif -#endif -#endif -#pragma endregion - -// O------------------------------------------------------------------------------O -// | olcPixelGameEngine INTERFACE DECLARATION | -// O------------------------------------------------------------------------------O -#pragma region pge_declaration -namespace olc -{ - class PixelGameEngine; - class Sprite; - - // Pixel Game Engine Advanced Configuration - constexpr uint8_t nMouseButtons = 5; - constexpr uint8_t nDefaultAlpha = 0xFF; - constexpr uint32_t nDefaultPixel = (nDefaultAlpha << 24); - enum rcode { FAIL = 0, OK = 1, NO_FILE = -1 }; - - // O------------------------------------------------------------------------------O - // | olc::Pixel - Represents a 32-Bit RGBA colour | - // O------------------------------------------------------------------------------O - struct Pixel - { - union - { - uint32_t n = nDefaultPixel; - struct { uint8_t r; uint8_t g; uint8_t b; uint8_t a; }; - }; - - enum Mode { NORMAL, MASK, ALPHA, CUSTOM }; - - Pixel(); - Pixel(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = nDefaultAlpha); - Pixel(uint32_t p); - Pixel& operator = (const Pixel& v) = default; - bool operator ==(const Pixel& p) const; - bool operator !=(const Pixel& p) const; - Pixel operator * (const float i) const; - Pixel operator / (const float i) const; - Pixel& operator *=(const float i); - Pixel& operator /=(const float i); - Pixel operator + (const Pixel& p) const; - Pixel operator - (const Pixel& p) const; - Pixel& operator +=(const Pixel& p); - Pixel& operator -=(const Pixel& p); - Pixel inv() const; - }; - - Pixel PixelF(float red, float green, float blue, float alpha = 1.0f); - Pixel PixelLerp(const olc::Pixel& p1, const olc::Pixel& p2, float t); - - - // O------------------------------------------------------------------------------O - // | USEFUL CONSTANTS | - // O------------------------------------------------------------------------------O - static const Pixel - GREY(192, 192, 192), DARK_GREY(128, 128, 128), VERY_DARK_GREY(64, 64, 64), - RED(255, 0, 0), DARK_RED(128, 0, 0), VERY_DARK_RED(64, 0, 0), - YELLOW(255, 255, 0), DARK_YELLOW(128, 128, 0), VERY_DARK_YELLOW(64, 64, 0), - GREEN(0, 255, 0), DARK_GREEN(0, 128, 0), VERY_DARK_GREEN(0, 64, 0), - CYAN(0, 255, 255), DARK_CYAN(0, 128, 128), VERY_DARK_CYAN(0, 64, 64), - BLUE(0, 0, 255), DARK_BLUE(0, 0, 128), VERY_DARK_BLUE(0, 0, 64), - MAGENTA(255, 0, 255), DARK_MAGENTA(128, 0, 128), VERY_DARK_MAGENTA(64, 0, 64), - WHITE(255, 255, 255), BLACK(0, 0, 0), BLANK(0, 0, 0, 0); - - // Thanks to scripticuk and others for updating the key maps - // NOTE: The GLUT platform will need updating, open to contributions ;) - enum Key - { - NONE, - A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, - K0, K1, K2, K3, K4, K5, K6, K7, K8, K9, - F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, - UP, DOWN, LEFT, RIGHT, - SPACE, TAB, SHIFT, CTRL, INS, DEL, HOME, END, PGUP, PGDN, - BACK, ESCAPE, RETURN, ENTER, PAUSE, SCROLL, - NP0, NP1, NP2, NP3, NP4, NP5, NP6, NP7, NP8, NP9, - NP_MUL, NP_DIV, NP_ADD, NP_SUB, NP_DECIMAL, PERIOD, - EQUALS, COMMA, MINUS, - OEM_1, OEM_2, OEM_3, OEM_4, OEM_5, OEM_6, OEM_7, OEM_8, - CAPS_LOCK, ENUM_END - }; - - namespace Mouse - { - static constexpr int32_t LEFT = 0; - static constexpr int32_t RIGHT = 1; - static constexpr int32_t MIDDLE = 2; - }; - - // O------------------------------------------------------------------------------O - // | olc::HWButton - Represents the state of a hardware button (mouse/key/joy) | - // O------------------------------------------------------------------------------O - struct HWButton - { - bool bPressed = false; // Set once during the frame the event occurs - bool bReleased = false; // Set once during the frame the event occurs - bool bHeld = false; // Set true for all frames between pressed and released events - }; - - - - - // O------------------------------------------------------------------------------O - // | olc::vX2d - A generic 2D vector type | - // O------------------------------------------------------------------------------O -#if !defined(OLC_IGNORE_VEC2D) - template - struct v2d_generic - { - T x = 0; - T y = 0; - v2d_generic() : x(0), y(0) {} - v2d_generic(T _x, T _y) : x(_x), y(_y) {} - v2d_generic(const v2d_generic& v) : x(v.x), y(v.y) {} - v2d_generic& operator=(const v2d_generic& v) = default; - T mag() const { return T(std::sqrt(x * x + y * y)); } - T mag2() const { return x * x + y * y; } - v2d_generic norm() const { T r = 1 / mag(); return v2d_generic(x * r, y * r); } - v2d_generic perp() const { return v2d_generic(-y, x); } - v2d_generic floor() const { return v2d_generic(std::floor(x), std::floor(y)); } - v2d_generic ceil() const { return v2d_generic(std::ceil(x), std::ceil(y)); } - v2d_generic max(const v2d_generic& v) const { return v2d_generic(std::max(x, v.x), std::max(y, v.y)); } - v2d_generic min(const v2d_generic& v) const { return v2d_generic(std::min(x, v.x), std::min(y, v.y)); } - v2d_generic cart() { return { std::cos(y) * x, std::sin(y) * x }; } - v2d_generic polar() { return { mag(), std::atan2(y, x) }; } - T dot(const v2d_generic& rhs) const { return this->x * rhs.x + this->y * rhs.y; } - T cross(const v2d_generic& rhs) const { return this->x * rhs.y - this->y * rhs.x; } - v2d_generic operator + (const v2d_generic& rhs) const { return v2d_generic(this->x + rhs.x, this->y + rhs.y); } - v2d_generic operator - (const v2d_generic& rhs) const { return v2d_generic(this->x - rhs.x, this->y - rhs.y); } - v2d_generic operator * (const T& rhs) const { return v2d_generic(this->x * rhs, this->y * rhs); } - v2d_generic operator * (const v2d_generic& rhs) const { return v2d_generic(this->x * rhs.x, this->y * rhs.y); } - v2d_generic operator / (const T& rhs) const { return v2d_generic(this->x / rhs, this->y / rhs); } - v2d_generic operator / (const v2d_generic& rhs) const { return v2d_generic(this->x / rhs.x, this->y / rhs.y); } - v2d_generic& operator += (const v2d_generic& rhs) { this->x += rhs.x; this->y += rhs.y; return *this; } - v2d_generic& operator -= (const v2d_generic& rhs) { this->x -= rhs.x; this->y -= rhs.y; return *this; } - v2d_generic& operator *= (const T& rhs) { this->x *= rhs; this->y *= rhs; return *this; } - v2d_generic& operator /= (const T& rhs) { this->x /= rhs; this->y /= rhs; return *this; } - v2d_generic& operator *= (const v2d_generic& rhs) { this->x *= rhs.x; this->y *= rhs.y; return *this; } - v2d_generic& operator /= (const v2d_generic& rhs) { this->x /= rhs.x; this->y /= rhs.y; return *this; } - v2d_generic operator + () const { return { +x, +y }; } - v2d_generic operator - () const { return { -x, -y }; } - bool operator == (const v2d_generic& rhs) const { return (this->x == rhs.x && this->y == rhs.y); } - bool operator != (const v2d_generic& rhs) const { return (this->x != rhs.x || this->y != rhs.y); } - const std::string str() const { return std::string("(") + std::to_string(this->x) + "," + std::to_string(this->y) + ")"; } - friend std::ostream& operator << (std::ostream& os, const v2d_generic& rhs) { os << rhs.str(); return os; } - operator v2d_generic() const { return { static_cast(this->x), static_cast(this->y) }; } - operator v2d_generic() const { return { static_cast(this->x), static_cast(this->y) }; } - operator v2d_generic() const { return { static_cast(this->x), static_cast(this->y) }; } - }; - - // Note: joshinils has some good suggestions here, but they are complicated to implement at this moment, - // however they will appear in a future version of PGE - template inline v2d_generic operator * (const float& lhs, const v2d_generic& rhs) - { return v2d_generic((T)(lhs * (float)rhs.x), (T)(lhs * (float)rhs.y)); } - template inline v2d_generic operator * (const double& lhs, const v2d_generic& rhs) - { return v2d_generic((T)(lhs * (double)rhs.x), (T)(lhs * (double)rhs.y)); } - template inline v2d_generic operator * (const int& lhs, const v2d_generic& rhs) - { return v2d_generic((T)(lhs * (int)rhs.x), (T)(lhs * (int)rhs.y)); } - template inline v2d_generic operator / (const float& lhs, const v2d_generic& rhs) - { return v2d_generic((T)(lhs / (float)rhs.x), (T)(lhs / (float)rhs.y)); } - template inline v2d_generic operator / (const double& lhs, const v2d_generic& rhs) - { return v2d_generic((T)(lhs / (double)rhs.x), (T)(lhs / (double)rhs.y)); } - template inline v2d_generic operator / (const int& lhs, const v2d_generic& rhs) - { return v2d_generic((T)(lhs / (int)rhs.x), (T)(lhs / (int)rhs.y)); } - - // To stop dandistine crying... - template inline bool operator < (const v2d_generic& lhs, const v2d_generic& rhs) - { return lhs.y < rhs.y || (lhs.y == rhs.y && lhs.x < rhs.x); } - template inline bool operator > (const v2d_generic& lhs, const v2d_generic& rhs) - { return lhs.y > rhs.y || (lhs.y == rhs.y && lhs.x > rhs.x); } - - typedef v2d_generic vi2d; - typedef v2d_generic vu2d; - typedef v2d_generic vf2d; - typedef v2d_generic vd2d; -#endif - - - - - - - // O------------------------------------------------------------------------------O - // | olc::ResourcePack - A virtual scrambled filesystem to pack your assets into | - // O------------------------------------------------------------------------------O - struct ResourceBuffer : public std::streambuf - { - ResourceBuffer(std::ifstream& ifs, uint32_t offset, uint32_t size); - std::vector vMemory; - }; - - class ResourcePack : public std::streambuf - { - public: - ResourcePack(); - ~ResourcePack(); - bool AddFile(const std::string& sFile); - bool LoadPack(const std::string& sFile, const std::string& sKey); - bool SavePack(const std::string& sFile, const std::string& sKey); - ResourceBuffer GetFileBuffer(const std::string& sFile); - bool Loaded(); - private: - struct sResourceFile { uint32_t nSize; uint32_t nOffset; }; - std::map mapFiles; - std::ifstream baseFile; - std::vector scramble(const std::vector& data, const std::string& key); - std::string makeposix(const std::string& path); - }; - - - class ImageLoader - { - public: - ImageLoader() = default; - virtual ~ImageLoader() = default; - virtual olc::rcode LoadImageResource(olc::Sprite* spr, const std::string& sImageFile, olc::ResourcePack* pack) = 0; - virtual olc::rcode SaveImageResource(olc::Sprite* spr, const std::string& sImageFile) = 0; - }; - - - // O------------------------------------------------------------------------------O - // | olc::Sprite - An image represented by a 2D array of olc::Pixel | - // O------------------------------------------------------------------------------O - class Sprite - { - public: - Sprite(); - Sprite(const std::string& sImageFile, olc::ResourcePack* pack = nullptr); - Sprite(int32_t w, int32_t h); - Sprite(const olc::Sprite&) = delete; - ~Sprite(); - - public: - olc::rcode LoadFromFile(const std::string& sImageFile, olc::ResourcePack* pack = nullptr); - - public: - int32_t width = 0; - int32_t height = 0; - enum Mode { NORMAL, PERIODIC }; - enum Flip { NONE = 0, HORIZ = 1, VERT = 2 }; - - public: - void SetSampleMode(olc::Sprite::Mode mode = olc::Sprite::Mode::NORMAL); - Pixel GetPixel(int32_t x, int32_t y) const; - bool SetPixel(int32_t x, int32_t y, Pixel p); - Pixel GetPixel(const olc::vi2d& a) const; - bool SetPixel(const olc::vi2d& a, Pixel p); - Pixel Sample(float x, float y) const; - Pixel SampleBL(float u, float v) const; - Pixel* GetData(); - olc::Sprite* Duplicate(); - olc::Sprite* Duplicate(const olc::vi2d& vPos, const olc::vi2d& vSize); - std::vector pColData; - Mode modeSample = Mode::NORMAL; - - static std::unique_ptr loader; - }; - - // O------------------------------------------------------------------------------O - // | olc::Decal - A GPU resident storage of an olc::Sprite | - // O------------------------------------------------------------------------------O - class Decal - { - public: - Decal(olc::Sprite* spr, bool filter = false, bool clamp = true); - Decal(const uint32_t nExistingTextureResource, olc::Sprite* spr); - virtual ~Decal(); - void Update(); - void UpdateSprite(); - - public: // But dont touch - int32_t id = -1; - olc::Sprite* sprite = nullptr; - olc::vf2d vUVScale = { 1.0f, 1.0f }; - }; - - enum class DecalMode - { - NORMAL, - ADDITIVE, - MULTIPLICATIVE, - STENCIL, - ILLUMINATE, - WIREFRAME, - }; - - // O------------------------------------------------------------------------------O - // | olc::Renderable - Convenience class to keep a sprite and decal together | - // O------------------------------------------------------------------------------O - class Renderable - { - public: - Renderable() = default; - Renderable(Renderable&& r) : pSprite(std::move(r.pSprite)), pDecal(std::move(r.pDecal)) {} - Renderable(const Renderable&) = delete; - olc::rcode Load(const std::string& sFile, ResourcePack* pack = nullptr, bool filter = false, bool clamp = true); - void Create(uint32_t width, uint32_t height, bool filter = false, bool clamp = true); - olc::Decal* Decal() const; - olc::Sprite* Sprite() const; - - private: - std::unique_ptr pSprite = nullptr; - std::unique_ptr pDecal = nullptr; - }; - - - // O------------------------------------------------------------------------------O - // | Auxilliary components internal to engine | - // O------------------------------------------------------------------------------O - - struct DecalInstance - { - olc::Decal* decal = nullptr; - std::vector pos; - std::vector uv; - std::vector w; - std::vector tint; - olc::DecalMode mode = olc::DecalMode::NORMAL; - uint32_t points = 0; - }; - - struct LayerDesc - { - olc::vf2d vOffset = { 0, 0 }; - olc::vf2d vScale = { 1, 1 }; - bool bShow = false; - bool bUpdate = false; - olc::Renderable pDrawTarget; - uint32_t nResID = 0; - std::vector vecDecalInstance; - olc::Pixel tint = olc::WHITE; - std::function funcHook = nullptr; - }; - - class Renderer - { - public: - virtual ~Renderer() = default; - virtual void PrepareDevice() = 0; - virtual olc::rcode CreateDevice(std::vector params, bool bFullScreen, bool bVSYNC) = 0; - virtual olc::rcode DestroyDevice() = 0; - virtual void DisplayFrame() = 0; - virtual void PrepareDrawing() = 0; - virtual void SetDecalMode(const olc::DecalMode& mode) = 0; - virtual void DrawLayerQuad(const olc::vf2d& offset, const olc::vf2d& scale, const olc::Pixel tint) = 0; - virtual void DrawDecal(const olc::DecalInstance& decal) = 0; - virtual uint32_t CreateTexture(const uint32_t width, const uint32_t height, const bool filtered = false, const bool clamp = true) = 0; - virtual void UpdateTexture(uint32_t id, olc::Sprite* spr) = 0; - virtual void ReadTexture(uint32_t id, olc::Sprite* spr) = 0; - virtual uint32_t DeleteTexture(const uint32_t id) = 0; - virtual void ApplyTexture(uint32_t id) = 0; - virtual void UpdateViewport(const olc::vi2d& pos, const olc::vi2d& size) = 0; - virtual void ClearBuffer(olc::Pixel p, bool bDepth) = 0; - static olc::PixelGameEngine* ptrPGE; - }; - - class Platform - { - public: - virtual ~Platform() = default; - virtual olc::rcode ApplicationStartUp() = 0; - virtual olc::rcode ApplicationCleanUp() = 0; - virtual olc::rcode ThreadStartUp() = 0; - virtual olc::rcode ThreadCleanUp() = 0; - virtual olc::rcode CreateGraphics(bool bFullScreen, bool bEnableVSYNC, const olc::vi2d& vViewPos, const olc::vi2d& vViewSize) = 0; - virtual olc::rcode CreateWindowPane(const olc::vi2d& vWindowPos, olc::vi2d& vWindowSize, bool bFullScreen) = 0; - virtual olc::rcode SetWindowTitle(const std::string& s) = 0; - virtual olc::rcode StartSystemEventLoop() = 0; - virtual olc::rcode HandleSystemEvent() = 0; - static olc::PixelGameEngine* ptrPGE; - }; - - class PGEX; - - // The Static Twins (plus one) - static std::unique_ptr renderer; - static std::unique_ptr platform; - static std::map mapKeys; - - // O------------------------------------------------------------------------------O - // | olc::PixelGameEngine - The main BASE class for your application | - // O------------------------------------------------------------------------------O - class PixelGameEngine - { - public: - PixelGameEngine(); - virtual ~PixelGameEngine(); - public: - olc::rcode Construct(int32_t screen_w, int32_t screen_h, int32_t pixel_w, int32_t pixel_h, - bool full_screen = false, bool vsync = false, bool cohesion = false); - olc::rcode Start(); - - public: // User Override Interfaces - // Called once on application startup, use to load your resources - virtual bool OnUserCreate(); - // Called every frame, and provides you with a time per frame value - virtual bool OnUserUpdate(float fElapsedTime); - // Called once on application termination, so you can be one clean coder - virtual bool OnUserDestroy(); - - public: // Hardware Interfaces - // Returns true if window is currently in focus - bool IsFocused() const; - // Get the state of a specific keyboard button - HWButton GetKey(Key k) const; - // Get the state of a specific mouse button - HWButton GetMouse(uint32_t b) const; - // Get Mouse X coordinate in "pixel" space - int32_t GetMouseX() const; - // Get Mouse Y coordinate in "pixel" space - int32_t GetMouseY() const; - // Get Mouse Wheel Delta - int32_t GetMouseWheel() const; - // Get the mouse in window space - const olc::vi2d& GetWindowMouse() const; - // Gets the mouse as a vector to keep Tarriest happy - const olc::vi2d& GetMousePos() const; - - public: // Utility - // Returns the width of the screen in "pixels" - int32_t ScreenWidth() const; - // Returns the height of the screen in "pixels" - int32_t ScreenHeight() const; - // Returns the width of the currently selected drawing target in "pixels" - int32_t GetDrawTargetWidth() const; - // Returns the height of the currently selected drawing target in "pixels" - int32_t GetDrawTargetHeight() const; - // Returns the currently active draw target - olc::Sprite* GetDrawTarget() const; - // Resize the primary screen sprite - void SetScreenSize(int w, int h); - // Specify which Sprite should be the target of drawing functions, use nullptr - // to specify the primary screen - void SetDrawTarget(Sprite* target); - // Gets the current Frames Per Second - uint32_t GetFPS() const; - // Gets last update of elapsed time - float GetElapsedTime() const; - // Gets Actual Window size - const olc::vi2d& GetWindowSize() const; - // Gets pixel scale - const olc::vi2d& GetPixelSize() const; - // Gets actual pixel scale - const olc::vi2d& GetScreenPixelSize() const; - - public: // CONFIGURATION ROUTINES - // Layer targeting functions - void SetDrawTarget(uint8_t layer); - void EnableLayer(uint8_t layer, bool b); - void SetLayerOffset(uint8_t layer, const olc::vf2d& offset); - void SetLayerOffset(uint8_t layer, float x, float y); - void SetLayerScale(uint8_t layer, const olc::vf2d& scale); - void SetLayerScale(uint8_t layer, float x, float y); - void SetLayerTint(uint8_t layer, const olc::Pixel& tint); - void SetLayerCustomRenderFunction(uint8_t layer, std::function f); - - std::vector& GetLayers(); - uint32_t CreateLayer(); - - // Change the pixel mode for different optimisations - // olc::Pixel::NORMAL = No transparency - // olc::Pixel::MASK = Transparent if alpha is < 255 - // olc::Pixel::ALPHA = Full transparency - void SetPixelMode(Pixel::Mode m); - Pixel::Mode GetPixelMode(); - // Use a custom blend function - void SetPixelMode(std::function pixelMode); - // Change the blend factor from between 0.0f to 1.0f; - void SetPixelBlend(float fBlend); - - - - public: // DRAWING ROUTINES - // Draws a single Pixel - virtual bool Draw(int32_t x, int32_t y, Pixel p = olc::WHITE); - bool Draw(const olc::vi2d& pos, Pixel p = olc::WHITE); - // Draws a line from (x1,y1) to (x2,y2) - void DrawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, Pixel p = olc::WHITE, uint32_t pattern = 0xFFFFFFFF); - void DrawLine(const olc::vi2d& pos1, const olc::vi2d& pos2, Pixel p = olc::WHITE, uint32_t pattern = 0xFFFFFFFF); - // Draws a circle located at (x,y) with radius - void DrawCircle(int32_t x, int32_t y, int32_t radius, Pixel p = olc::WHITE, uint8_t mask = 0xFF); - void DrawCircle(const olc::vi2d& pos, int32_t radius, Pixel p = olc::WHITE, uint8_t mask = 0xFF); - // Fills a circle located at (x,y) with radius - void FillCircle(int32_t x, int32_t y, int32_t radius, Pixel p = olc::WHITE); - void FillCircle(const olc::vi2d& pos, int32_t radius, Pixel p = olc::WHITE); - // Draws a rectangle at (x,y) to (x+w,y+h) - void DrawRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p = olc::WHITE); - void DrawRect(const olc::vi2d& pos, const olc::vi2d& size, Pixel p = olc::WHITE); - // Fills a rectangle at (x,y) to (x+w,y+h) - void FillRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p = olc::WHITE); - void FillRect(const olc::vi2d& pos, const olc::vi2d& size, Pixel p = olc::WHITE); - // Draws a triangle between points (x1,y1), (x2,y2) and (x3,y3) - void DrawTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, Pixel p = olc::WHITE); - void DrawTriangle(const olc::vi2d& pos1, const olc::vi2d& pos2, const olc::vi2d& pos3, Pixel p = olc::WHITE); - // Flat fills a triangle between points (x1,y1), (x2,y2) and (x3,y3) - void FillTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, Pixel p = olc::WHITE); - void FillTriangle(const olc::vi2d& pos1, const olc::vi2d& pos2, const olc::vi2d& pos3, Pixel p = olc::WHITE); - // Draws an entire sprite at location (x,y) - void DrawSprite(int32_t x, int32_t y, Sprite* sprite, uint32_t scale = 1, uint8_t flip = olc::Sprite::NONE); - void DrawSprite(const olc::vi2d& pos, Sprite* sprite, uint32_t scale = 1, uint8_t flip = olc::Sprite::NONE); - // Draws an area of a sprite at location (x,y), where the - // selected area is (ox,oy) to (ox+w,oy+h) - void DrawPartialSprite(int32_t x, int32_t y, Sprite* sprite, int32_t ox, int32_t oy, int32_t w, int32_t h, uint32_t scale = 1, uint8_t flip = olc::Sprite::NONE); - void DrawPartialSprite(const olc::vi2d& pos, Sprite* sprite, const olc::vi2d& sourcepos, const olc::vi2d& size, uint32_t scale = 1, uint8_t flip = olc::Sprite::NONE); - // Draws a single line of text - traditional monospaced - void DrawString(int32_t x, int32_t y, const std::string& sText, Pixel col = olc::WHITE, uint32_t scale = 1); - void DrawString(const olc::vi2d& pos, const std::string& sText, Pixel col = olc::WHITE, uint32_t scale = 1); - olc::vi2d GetTextSize(const std::string& s); - // Draws a single line of text - non-monospaced - void DrawStringProp(int32_t x, int32_t y, const std::string& sText, Pixel col = olc::WHITE, uint32_t scale = 1); - void DrawStringProp(const olc::vi2d& pos, const std::string& sText, Pixel col = olc::WHITE, uint32_t scale = 1); - olc::vi2d GetTextSizeProp(const std::string& s); - - // Decal Quad functions - void SetDecalMode(const olc::DecalMode& mode); - // Draws a whole decal, with optional scale and tinting - void DrawDecal(const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE); - // Draws a region of a decal, with optional scale and tinting - void DrawPartialDecal(const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE); - void DrawPartialDecal(const olc::vf2d& pos, const olc::vf2d& size, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE); - // Draws fully user controlled 4 vertices, pos(pixels), uv(pixels), colours - void DrawExplicitDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d* uv, const olc::Pixel* col, uint32_t elements = 4); - // Draws a decal with 4 arbitrary points, warping the texture to look "correct" - void DrawWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::Pixel& tint = olc::WHITE); - void DrawWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::Pixel& tint = olc::WHITE); - void DrawWarpedDecal(olc::Decal* decal, const std::array& pos, const olc::Pixel& tint = olc::WHITE); - // As above, but you can specify a region of a decal source sprite - void DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE); - void DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE); - void DrawPartialWarpedDecal(olc::Decal* decal, const std::array& pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE); - // Draws a decal rotated to specified angle, wit point of rotation offset - void DrawRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center = { 0.0f, 0.0f }, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE); - void DrawPartialRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale = { 1.0f, 1.0f }, const olc::Pixel& tint = olc::WHITE); - // Draws a multiline string as a decal, with tiniting and scaling - void DrawStringDecal(const olc::vf2d& pos, const std::string& sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); - void DrawStringPropDecal(const olc::vf2d& pos, const std::string& sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); - // Draws a single shaded filled rectangle as a decal - void FillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::WHITE); - // Draws a corner shaded rectangle as a decal - void GradientFillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel colTL, const olc::Pixel colBL, const olc::Pixel colBR, const olc::Pixel colTR); - // Draws an arbitrary convex textured polygon using GPU - void DrawPolygonDecal(olc::Decal* decal, const std::vector& pos, const std::vector& uv, const olc::Pixel tint = olc::WHITE); - // Draws a line in Decal Space - void DrawLineDecal(const olc::vf2d& pos1, const olc::vf2d& pos2, Pixel p = olc::WHITE); - void DrawRotatedStringDecal(const olc::vf2d& pos, const std::string& sText, const float fAngle, const olc::vf2d& center = { 0.0f, 0.0f }, const olc::Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); - void DrawRotatedStringPropDecal(const olc::vf2d& pos, const std::string& sText, const float fAngle, const olc::vf2d& center = { 0.0f, 0.0f }, const olc::Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); - // Clears entire draw target to Pixel - void Clear(Pixel p); - // Clears the rendering back buffer - void ClearBuffer(Pixel p, bool bDepth = true); - // Returns the font image - olc::Sprite* GetFontSprite(); - - public: // Branding - std::string sAppName; - - private: // Inner mysterious workings - olc::Sprite* pDrawTarget = nullptr; - Pixel::Mode nPixelMode = Pixel::NORMAL; - float fBlendFactor = 1.0f; - olc::vi2d vScreenSize = { 256, 240 }; - olc::vf2d vInvScreenSize = { 1.0f / 256.0f, 1.0f / 240.0f }; - olc::vi2d vPixelSize = { 4, 4 }; - olc::vi2d vScreenPixelSize = { 4, 4 }; - olc::vi2d vMousePos = { 0, 0 }; - int32_t nMouseWheelDelta = 0; - olc::vi2d vMousePosCache = { 0, 0 }; - olc::vi2d vMouseWindowPos = { 0, 0 }; - int32_t nMouseWheelDeltaCache = 0; - olc::vi2d vWindowSize = { 0, 0 }; - olc::vi2d vViewPos = { 0, 0 }; - olc::vi2d vViewSize = { 0,0 }; - bool bFullScreen = false; - olc::vf2d vPixel = { 1.0f, 1.0f }; - bool bHasInputFocus = false; - bool bHasMouseFocus = false; - bool bEnableVSYNC = false; - float fFrameTimer = 1.0f; - float fLastElapsed = 0.0f; - int nFrameCount = 0; - Sprite* fontSprite = nullptr; - Decal* fontDecal = nullptr; - std::vector vLayers; - uint8_t nTargetLayer = 0; - uint32_t nLastFPS = 0; - bool bPixelCohesion = false; - DecalMode nDecalMode = DecalMode::NORMAL; - std::function funcPixelMode; - std::chrono::time_point m_tp1, m_tp2; - std::vector vFontSpacing; - - // State of keyboard - bool pKeyNewState[256] = { 0 }; - bool pKeyOldState[256] = { 0 }; - HWButton pKeyboardState[256] = { 0 }; - - // State of mouse - bool pMouseNewState[nMouseButtons] = { 0 }; - bool pMouseOldState[nMouseButtons] = { 0 }; - HWButton pMouseState[nMouseButtons] = { 0 }; - - // The main engine thread - void EngineThread(); - - - // If anything sets this flag to false, the engine - // "should" shut down gracefully - static std::atomic bAtomActive; - - public: - // "Break In" Functions - void olc_UpdateMouse(int32_t x, int32_t y); - void olc_UpdateMouseWheel(int32_t delta); - void olc_UpdateWindowSize(int32_t x, int32_t y); - void olc_UpdateViewport(); - void olc_ConstructFontSheet(); - void olc_CoreUpdate(); - void olc_PrepareEngine(); - void olc_UpdateMouseState(int32_t button, bool state); - void olc_UpdateKeyState(int32_t key, bool state); - void olc_UpdateMouseFocus(bool state); - void olc_UpdateKeyFocus(bool state); - void olc_Terminate(); - void olc_Reanimate(); - bool olc_IsRunning(); - - // At the very end of this file, chooses which - // components to compile - virtual void olc_ConfigureSystem(); - - // NOTE: Items Here are to be deprecated, I have left them in for now - // in case you are using them, but they will be removed. - // olc::vf2d vSubPixelOffset = { 0.0f, 0.0f }; - - public: // PGEX Stuff - friend class PGEX; - void pgex_Register(olc::PGEX* pgex); - - private: - std::vector vExtensions; - }; - - - - // O------------------------------------------------------------------------------O - // | PGE EXTENSION BASE CLASS - Permits access to PGE functions from extension | - // O------------------------------------------------------------------------------O - class PGEX - { - friend class olc::PixelGameEngine; - public: - PGEX(bool bHook = false); - - protected: - virtual void OnBeforeUserCreate(); - virtual void OnAfterUserCreate(); - virtual void OnBeforeUserUpdate(float &fElapsedTime); - virtual void OnAfterUserUpdate(float fElapsedTime); - - protected: - static PixelGameEngine* pge; - }; -} - -#pragma endregion - -#endif // OLC_PGE_DEF - - -// O------------------------------------------------------------------------------O -// | START OF OLC_PGE_APPLICATION | -// O------------------------------------------------------------------------------O -#ifdef OLC_PGE_APPLICATION -#undef OLC_PGE_APPLICATION - -// O------------------------------------------------------------------------------O -// | olcPixelGameEngine INTERFACE IMPLEMENTATION (CORE) | -// | Note: The core implementation is platform independent | -// O------------------------------------------------------------------------------O -#pragma region pge_implementation -namespace olc -{ - // O------------------------------------------------------------------------------O - // | olc::Pixel IMPLEMENTATION | - // O------------------------------------------------------------------------------O - Pixel::Pixel() - { r = 0; g = 0; b = 0; a = nDefaultAlpha; } - - Pixel::Pixel(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) - { n = red | (green << 8) | (blue << 16) | (alpha << 24); } // Thanks jarekpelczar - - Pixel::Pixel(uint32_t p) - { n = p; } - - bool Pixel::operator==(const Pixel& p) const - { return n == p.n; } - - bool Pixel::operator!=(const Pixel& p) const - { return n != p.n; } - - Pixel Pixel::operator * (const float i) const - { - float fR = std::min(255.0f, std::max(0.0f, float(r) * i)); - float fG = std::min(255.0f, std::max(0.0f, float(g) * i)); - float fB = std::min(255.0f, std::max(0.0f, float(b) * i)); - return Pixel(uint8_t(fR), uint8_t(fG), uint8_t(fB), a); - } - - Pixel Pixel::operator / (const float i) const - { - float fR = std::min(255.0f, std::max(0.0f, float(r) / i)); - float fG = std::min(255.0f, std::max(0.0f, float(g) / i)); - float fB = std::min(255.0f, std::max(0.0f, float(b) / i)); - return Pixel(uint8_t(fR), uint8_t(fG), uint8_t(fB), a); - } - - Pixel& Pixel::operator *=(const float i) - { - this->r = uint8_t(std::min(255.0f, std::max(0.0f, float(r) * i))); - this->g = uint8_t(std::min(255.0f, std::max(0.0f, float(g) * i))); - this->b = uint8_t(std::min(255.0f, std::max(0.0f, float(b) * i))); - return *this; - } - - Pixel& Pixel::operator /=(const float i) - { - this->r = uint8_t(std::min(255.0f, std::max(0.0f, float(r) / i))); - this->g = uint8_t(std::min(255.0f, std::max(0.0f, float(g) / i))); - this->b = uint8_t(std::min(255.0f, std::max(0.0f, float(b) / i))); - return *this; - } - - Pixel Pixel::operator + (const Pixel& p) const - { - uint8_t nR = uint8_t(std::min(255, std::max(0, int(r) + int(p.r)))); - uint8_t nG = uint8_t(std::min(255, std::max(0, int(g) + int(p.g)))); - uint8_t nB = uint8_t(std::min(255, std::max(0, int(b) + int(p.b)))); - return Pixel(nR, nG, nB, a); - } - - Pixel Pixel::operator - (const Pixel& p) const - { - uint8_t nR = uint8_t(std::min(255, std::max(0, int(r) - int(p.r)))); - uint8_t nG = uint8_t(std::min(255, std::max(0, int(g) - int(p.g)))); - uint8_t nB = uint8_t(std::min(255, std::max(0, int(b) - int(p.b)))); - return Pixel(nR, nG, nB, a); - } - - Pixel& Pixel::operator += (const Pixel& p) - { - this->r = uint8_t(std::min(255, std::max(0, int(r) + int(p.r)))); - this->g = uint8_t(std::min(255, std::max(0, int(g) + int(p.g)))); - this->b = uint8_t(std::min(255, std::max(0, int(b) + int(p.b)))); - return *this; - } - - Pixel& Pixel::operator -= (const Pixel& p) // Thanks Au Lit - { - this->r = uint8_t(std::min(255, std::max(0, int(r) - int(p.r)))); - this->g = uint8_t(std::min(255, std::max(0, int(g) - int(p.g)))); - this->b = uint8_t(std::min(255, std::max(0, int(b) - int(p.b)))); - return *this; - } - - Pixel Pixel::inv() const - { - uint8_t nR = uint8_t(std::min(255, std::max(0, 255 - int(r)))); - uint8_t nG = uint8_t(std::min(255, std::max(0, 255 - int(g)))); - uint8_t nB = uint8_t(std::min(255, std::max(0, 255 - int(b)))); - return Pixel(nR, nG, nB, a); - } - - Pixel PixelF(float red, float green, float blue, float alpha) - { return Pixel(uint8_t(red * 255.0f), uint8_t(green * 255.0f), uint8_t(blue * 255.0f), uint8_t(alpha * 255.0f)); } - - Pixel PixelLerp(const olc::Pixel& p1, const olc::Pixel& p2, float t) - { return (p2 * t) + p1 * (1.0f - t); } - - // O------------------------------------------------------------------------------O - // | olc::Sprite IMPLEMENTATION | - // O------------------------------------------------------------------------------O - Sprite::Sprite() - { width = 0; height = 0; } - - Sprite::Sprite(const std::string& sImageFile, olc::ResourcePack* pack) - { LoadFromFile(sImageFile, pack); } - - Sprite::Sprite(int32_t w, int32_t h) - { - width = w; height = h; - pColData.resize(width * height); - pColData.resize(width * height, nDefaultPixel); - } - - Sprite::~Sprite() - { pColData.clear(); } - - void Sprite::SetSampleMode(olc::Sprite::Mode mode) - { modeSample = mode; } - - Pixel Sprite::GetPixel(const olc::vi2d& a) const - { return GetPixel(a.x, a.y); } - - bool Sprite::SetPixel(const olc::vi2d& a, Pixel p) - { return SetPixel(a.x, a.y, p); } - - Pixel Sprite::GetPixel(int32_t x, int32_t y) const - { - if (modeSample == olc::Sprite::Mode::NORMAL) - { - if (x >= 0 && x < width && y >= 0 && y < height) - return pColData[y * width + x]; - else - return Pixel(0, 0, 0, 0); - } - else - { - return pColData[abs(y % height) * width + abs(x % width)]; - } - } - - bool Sprite::SetPixel(int32_t x, int32_t y, Pixel p) - { - if (x >= 0 && x < width && y >= 0 && y < height) - { - pColData[y * width + x] = p; - return true; - } - else - return false; - } - - Pixel Sprite::Sample(float x, float y) const - { - int32_t sx = std::min((int32_t)((x * (float)width)), width - 1); - int32_t sy = std::min((int32_t)((y * (float)height)), height - 1); - return GetPixel(sx, sy); - } - - Pixel Sprite::SampleBL(float u, float v) const - { - u = u * width - 0.5f; - v = v * height - 0.5f; - int x = (int)floor(u); // cast to int rounds toward zero, not downward - int y = (int)floor(v); // Thanks @joshinils - float u_ratio = u - x; - float v_ratio = v - y; - float u_opposite = 1 - u_ratio; - float v_opposite = 1 - v_ratio; - - olc::Pixel p1 = GetPixel(std::max(x, 0), std::max(y, 0)); - olc::Pixel p2 = GetPixel(std::min(x + 1, (int)width - 1), std::max(y, 0)); - olc::Pixel p3 = GetPixel(std::max(x, 0), std::min(y + 1, (int)height - 1)); - olc::Pixel p4 = GetPixel(std::min(x + 1, (int)width - 1), std::min(y + 1, (int)height - 1)); - - return olc::Pixel( - (uint8_t)((p1.r * u_opposite + p2.r * u_ratio) * v_opposite + (p3.r * u_opposite + p4.r * u_ratio) * v_ratio), - (uint8_t)((p1.g * u_opposite + p2.g * u_ratio) * v_opposite + (p3.g * u_opposite + p4.g * u_ratio) * v_ratio), - (uint8_t)((p1.b * u_opposite + p2.b * u_ratio) * v_opposite + (p3.b * u_opposite + p4.b * u_ratio) * v_ratio)); - } - - Pixel* Sprite::GetData() - { return pColData.data(); } - - - olc::rcode Sprite::LoadFromFile(const std::string& sImageFile, olc::ResourcePack* pack) - { - UNUSED(pack); - return loader->LoadImageResource(this, sImageFile, pack); - } - - olc::Sprite* Sprite::Duplicate() - { - olc::Sprite* spr = new olc::Sprite(width, height); - std::memcpy(spr->GetData(), GetData(), width * height * sizeof(olc::Pixel)); - spr->modeSample = modeSample; - return spr; - } - - olc::Sprite* Sprite::Duplicate(const olc::vi2d& vPos, const olc::vi2d& vSize) - { - olc::Sprite* spr = new olc::Sprite(vSize.x, vSize.y); - for (int y = 0; y < vSize.y; y++) - for (int x = 0; x < vSize.x; x++) - spr->SetPixel(x, y, GetPixel(vPos.x + x, vPos.y + y)); - return spr; - } - - // O------------------------------------------------------------------------------O - // | olc::Decal IMPLEMENTATION | - // O------------------------------------------------------------------------------O - Decal::Decal(olc::Sprite* spr, bool filter, bool clamp) - { - id = -1; - if (spr == nullptr) return; - sprite = spr; - id = renderer->CreateTexture(sprite->width, sprite->height, filter, clamp); - Update(); - } - - Decal::Decal(const uint32_t nExistingTextureResource, olc::Sprite* spr) - { - if (spr == nullptr) return; - id = nExistingTextureResource; - } - - void Decal::Update() - { - if (sprite == nullptr) return; - vUVScale = { 1.0f / float(sprite->width), 1.0f / float(sprite->height) }; - renderer->ApplyTexture(id); - renderer->UpdateTexture(id, sprite); - } - - void Decal::UpdateSprite() - { - if (sprite == nullptr) return; - renderer->ApplyTexture(id); - renderer->ReadTexture(id, sprite); - } - - Decal::~Decal() - { - if (id != -1) - { - renderer->DeleteTexture(id); - id = -1; - } - } - - void Renderable::Create(uint32_t width, uint32_t height, bool filter, bool clamp) - { - pSprite = std::make_unique(width, height); - pDecal = std::make_unique(pSprite.get(), filter, clamp); - } - - olc::rcode Renderable::Load(const std::string& sFile, ResourcePack* pack, bool filter, bool clamp) - { - pSprite = std::make_unique(); - if (pSprite->LoadFromFile(sFile, pack) == olc::rcode::OK) - { - pDecal = std::make_unique(pSprite.get(), filter, clamp); - return olc::rcode::OK; - } - else - { - pSprite.release(); - pSprite = nullptr; - return olc::rcode::NO_FILE; - } - } - - olc::Decal* Renderable::Decal() const - { return pDecal.get(); } - - olc::Sprite* Renderable::Sprite() const - { return pSprite.get(); } - - // O------------------------------------------------------------------------------O - // | olc::ResourcePack IMPLEMENTATION | - // O------------------------------------------------------------------------------O - - - //============================================================= - // Resource Packs - Allows you to store files in one large - // scrambled file - Thanks MaGetzUb for debugging a null char in std::stringstream bug - ResourceBuffer::ResourceBuffer(std::ifstream& ifs, uint32_t offset, uint32_t size) - { - vMemory.resize(size); - ifs.seekg(offset); ifs.read(vMemory.data(), vMemory.size()); - setg(vMemory.data(), vMemory.data(), vMemory.data() + size); - } - - ResourcePack::ResourcePack() { } - ResourcePack::~ResourcePack() { baseFile.close(); } - - bool ResourcePack::AddFile(const std::string& sFile) - { - const std::string file = makeposix(sFile); - - if (_gfs::exists(file)) - { - sResourceFile e; - e.nSize = (uint32_t)_gfs::file_size(file); - e.nOffset = 0; // Unknown at this stage - mapFiles[file] = e; - return true; - } - return false; - } - - bool ResourcePack::LoadPack(const std::string& sFile, const std::string& sKey) - { - // Open the resource file - baseFile.open(sFile, std::ifstream::binary); - if (!baseFile.is_open()) return false; - - // 1) Read Scrambled index - uint32_t nIndexSize = 0; - baseFile.read((char*)&nIndexSize, sizeof(uint32_t)); - - std::vector buffer(nIndexSize); - for (uint32_t j = 0; j < nIndexSize; j++) - buffer[j] = baseFile.get(); - - std::vector decoded = scramble(buffer, sKey); - size_t pos = 0; - auto read = [&decoded, &pos](char* dst, size_t size) { - memcpy((void*)dst, (const void*)(decoded.data() + pos), size); - pos += size; - }; - - auto get = [&read]() -> int { char c; read(&c, 1); return c; }; - - // 2) Read Map - uint32_t nMapEntries = 0; - read((char*)&nMapEntries, sizeof(uint32_t)); - for (uint32_t i = 0; i < nMapEntries; i++) - { - uint32_t nFilePathSize = 0; - read((char*)&nFilePathSize, sizeof(uint32_t)); - - std::string sFileName(nFilePathSize, ' '); - for (uint32_t j = 0; j < nFilePathSize; j++) - sFileName[j] = get(); - - sResourceFile e; - read((char*)&e.nSize, sizeof(uint32_t)); - read((char*)&e.nOffset, sizeof(uint32_t)); - mapFiles[sFileName] = e; - } - - // Don't close base file! we will provide a stream - // pointer when the file is requested - return true; - } - - bool ResourcePack::SavePack(const std::string& sFile, const std::string& sKey) - { - // Create/Overwrite the resource file - std::ofstream ofs(sFile, std::ofstream::binary); - if (!ofs.is_open()) return false; - - // Iterate through map - uint32_t nIndexSize = 0; // Unknown for now - ofs.write((char*)&nIndexSize, sizeof(uint32_t)); - uint32_t nMapSize = uint32_t(mapFiles.size()); - ofs.write((char*)&nMapSize, sizeof(uint32_t)); - for (auto& e : mapFiles) - { - // Write the path of the file - size_t nPathSize = e.first.size(); - ofs.write((char*)&nPathSize, sizeof(uint32_t)); - ofs.write(e.first.c_str(), nPathSize); - - // Write the file entry properties - ofs.write((char*)&e.second.nSize, sizeof(uint32_t)); - ofs.write((char*)&e.second.nOffset, sizeof(uint32_t)); - } - - // 2) Write the individual Data - std::streampos offset = ofs.tellp(); - nIndexSize = (uint32_t)offset; - for (auto& e : mapFiles) - { - // Store beginning of file offset within resource pack file - e.second.nOffset = (uint32_t)offset; - - // Load the file to be added - std::vector vBuffer(e.second.nSize); - std::ifstream i(e.first, std::ifstream::binary); - i.read((char*)vBuffer.data(), e.second.nSize); - i.close(); - - // Write the loaded file into resource pack file - ofs.write((char*)vBuffer.data(), e.second.nSize); - offset += e.second.nSize; - } - - // 3) Scramble Index - std::vector stream; - auto write = [&stream](const char* data, size_t size) { - size_t sizeNow = stream.size(); - stream.resize(sizeNow + size); - memcpy(stream.data() + sizeNow, data, size); - }; - - // Iterate through map - write((char*)&nMapSize, sizeof(uint32_t)); - for (auto& e : mapFiles) - { - // Write the path of the file - size_t nPathSize = e.first.size(); - write((char*)&nPathSize, sizeof(uint32_t)); - write(e.first.c_str(), nPathSize); - - // Write the file entry properties - write((char*)&e.second.nSize, sizeof(uint32_t)); - write((char*)&e.second.nOffset, sizeof(uint32_t)); - } - std::vector sIndexString = scramble(stream, sKey); - uint32_t nIndexStringLen = uint32_t(sIndexString.size()); - // 4) Rewrite Map (it has been updated with offsets now) - // at start of file - ofs.seekp(0, std::ios::beg); - ofs.write((char*)&nIndexStringLen, sizeof(uint32_t)); - ofs.write(sIndexString.data(), nIndexStringLen); - ofs.close(); - return true; - } - - ResourceBuffer ResourcePack::GetFileBuffer(const std::string& sFile) - { return ResourceBuffer(baseFile, mapFiles[sFile].nOffset, mapFiles[sFile].nSize); } - - bool ResourcePack::Loaded() - { return baseFile.is_open(); } - - std::vector ResourcePack::scramble(const std::vector& data, const std::string& key) - { - if (key.empty()) return data; - std::vector o; - size_t c = 0; - for (auto s : data) o.push_back(s ^ key[(c++) % key.size()]); - return o; - }; - - std::string ResourcePack::makeposix(const std::string& path) - { - std::string o; - for (auto s : path) o += std::string(1, s == '\\' ? '/' : s); - return o; - }; - - // O------------------------------------------------------------------------------O - // | olc::PixelGameEngine IMPLEMENTATION | - // O------------------------------------------------------------------------------O - PixelGameEngine::PixelGameEngine() - { - sAppName = "Undefined"; - olc::PGEX::pge = this; - - // Bring in relevant Platform & Rendering systems depending - // on compiler parameters - olc_ConfigureSystem(); - } - - PixelGameEngine::~PixelGameEngine() - {} - - - olc::rcode PixelGameEngine::Construct(int32_t screen_w, int32_t screen_h, int32_t pixel_w, int32_t pixel_h, bool full_screen, bool vsync, bool cohesion) - { - bPixelCohesion = cohesion; - vScreenSize = { screen_w, screen_h }; - vInvScreenSize = { 1.0f / float(screen_w), 1.0f / float(screen_h) }; - vPixelSize = { pixel_w, pixel_h }; - vWindowSize = vScreenSize * vPixelSize; - bFullScreen = full_screen; - bEnableVSYNC = vsync; - vPixel = 2.0f / vScreenSize; - - if (vPixelSize.x <= 0 || vPixelSize.y <= 0 || vScreenSize.x <= 0 || vScreenSize.y <= 0) - return olc::FAIL; - return olc::OK; - } - - - void PixelGameEngine::SetScreenSize(int w, int h) - { - vScreenSize = { w, h }; - vInvScreenSize = { 1.0f / float(w), 1.0f / float(h) }; - for (auto& layer : vLayers) - { - layer.pDrawTarget.Create(vScreenSize.x, vScreenSize.y); - layer.bUpdate = true; - } - SetDrawTarget(nullptr); - renderer->ClearBuffer(olc::BLACK, true); - renderer->DisplayFrame(); - renderer->ClearBuffer(olc::BLACK, true); - renderer->UpdateViewport(vViewPos, vViewSize); - } - -#if !defined(PGE_USE_CUSTOM_START) - olc::rcode PixelGameEngine::Start() - { - if (platform->ApplicationStartUp() != olc::OK) return olc::FAIL; - - // Construct the window - if (platform->CreateWindowPane({ 30,30 }, vWindowSize, bFullScreen) != olc::OK) return olc::FAIL; - olc_UpdateWindowSize(vWindowSize.x, vWindowSize.y); - - // Start the thread - bAtomActive = true; - std::thread t = std::thread(&PixelGameEngine::EngineThread, this); - - // Some implementations may form an event loop here - platform->StartSystemEventLoop(); - - // Wait for thread to be exited - t.join(); - - if (platform->ApplicationCleanUp() != olc::OK) return olc::FAIL; - - return olc::OK; - } -#endif - - void PixelGameEngine::SetDrawTarget(Sprite* target) - { - if (target) - { - pDrawTarget = target; - } - else - { - nTargetLayer = 0; - pDrawTarget = vLayers[0].pDrawTarget.Sprite(); - } - } - - void PixelGameEngine::SetDrawTarget(uint8_t layer) - { - if (layer < vLayers.size()) - { - pDrawTarget = vLayers[layer].pDrawTarget.Sprite(); - vLayers[layer].bUpdate = true; - nTargetLayer = layer; - } - } - - void PixelGameEngine::EnableLayer(uint8_t layer, bool b) - { if (layer < vLayers.size()) vLayers[layer].bShow = b; } - - void PixelGameEngine::SetLayerOffset(uint8_t layer, const olc::vf2d& offset) - { SetLayerOffset(layer, offset.x, offset.y); } - - void PixelGameEngine::SetLayerOffset(uint8_t layer, float x, float y) - { if (layer < vLayers.size()) vLayers[layer].vOffset = { x, y }; } - - void PixelGameEngine::SetLayerScale(uint8_t layer, const olc::vf2d& scale) - { SetLayerScale(layer, scale.x, scale.y); } - - void PixelGameEngine::SetLayerScale(uint8_t layer, float x, float y) - { if (layer < vLayers.size()) vLayers[layer].vScale = { x, y }; } - - void PixelGameEngine::SetLayerTint(uint8_t layer, const olc::Pixel& tint) - { if (layer < vLayers.size()) vLayers[layer].tint = tint; } - - void PixelGameEngine::SetLayerCustomRenderFunction(uint8_t layer, std::function f) - { if (layer < vLayers.size()) vLayers[layer].funcHook = f; } - - std::vector& PixelGameEngine::GetLayers() - { return vLayers; } - - uint32_t PixelGameEngine::CreateLayer() - { - LayerDesc ld; - ld.pDrawTarget.Create(vScreenSize.x, vScreenSize.y); - vLayers.push_back(std::move(ld)); - return uint32_t(vLayers.size()) - 1; - } - - Sprite* PixelGameEngine::GetDrawTarget() const - { return pDrawTarget; } - - int32_t PixelGameEngine::GetDrawTargetWidth() const - { - if (pDrawTarget) - return pDrawTarget->width; - else - return 0; - } - - int32_t PixelGameEngine::GetDrawTargetHeight() const - { - if (pDrawTarget) - return pDrawTarget->height; - else - return 0; - } - - uint32_t PixelGameEngine::GetFPS() const - { return nLastFPS; } - - bool PixelGameEngine::IsFocused() const - { return bHasInputFocus; } - - HWButton PixelGameEngine::GetKey(Key k) const - { return pKeyboardState[k]; } - - HWButton PixelGameEngine::GetMouse(uint32_t b) const - { return pMouseState[b]; } - - int32_t PixelGameEngine::GetMouseX() const - { return vMousePos.x; } - - int32_t PixelGameEngine::GetMouseY() const - { return vMousePos.y; } - - const olc::vi2d& PixelGameEngine::GetMousePos() const - { return vMousePos; } - - int32_t PixelGameEngine::GetMouseWheel() const - { return nMouseWheelDelta; } - - int32_t PixelGameEngine::ScreenWidth() const - { return vScreenSize.x; } - - int32_t PixelGameEngine::ScreenHeight() const - { return vScreenSize.y; } - - float PixelGameEngine::GetElapsedTime() const - { return fLastElapsed; } - - const olc::vi2d& PixelGameEngine::GetWindowSize() const - { return vWindowSize; } - - const olc::vi2d& PixelGameEngine::GetPixelSize() const - { return vPixelSize; } - - const olc::vi2d& PixelGameEngine::GetScreenPixelSize() const - { return vScreenPixelSize; } - - const olc::vi2d& PixelGameEngine::GetWindowMouse() const - { return vMouseWindowPos; } - - bool PixelGameEngine::Draw(const olc::vi2d& pos, Pixel p) - { return Draw(pos.x, pos.y, p); } - - // This is it, the critical function that plots a pixel - bool PixelGameEngine::Draw(int32_t x, int32_t y, Pixel p) - { - if (!pDrawTarget) return false; - - if (nPixelMode == Pixel::NORMAL) - { - return pDrawTarget->SetPixel(x, y, p); - } - - if (nPixelMode == Pixel::MASK) - { - if (p.a == 255) - return pDrawTarget->SetPixel(x, y, p); - } - - if (nPixelMode == Pixel::ALPHA) - { - Pixel d = pDrawTarget->GetPixel(x, y); - float a = (float)(p.a / 255.0f) * fBlendFactor; - float c = 1.0f - a; - float r = a * (float)p.r + c * (float)d.r; - float g = a * (float)p.g + c * (float)d.g; - float b = a * (float)p.b + c * (float)d.b; - return pDrawTarget->SetPixel(x, y, Pixel((uint8_t)r, (uint8_t)g, (uint8_t)b/*, (uint8_t)(p.a * fBlendFactor)*/)); - } - - if (nPixelMode == Pixel::CUSTOM) - { - return pDrawTarget->SetPixel(x, y, funcPixelMode(x, y, p, pDrawTarget->GetPixel(x, y))); - } - - return false; - } - - - void PixelGameEngine::DrawLine(const olc::vi2d& pos1, const olc::vi2d& pos2, Pixel p, uint32_t pattern) - { DrawLine(pos1.x, pos1.y, pos2.x, pos2.y, p, pattern); } - - void PixelGameEngine::DrawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, Pixel p, uint32_t pattern) - { - int x, y, dx, dy, dx1, dy1, px, py, xe, ye, i; - dx = x2 - x1; dy = y2 - y1; - - auto rol = [&](void) { pattern = (pattern << 1) | (pattern >> 31); return pattern & 1; }; - - // straight lines idea by gurkanctn - if (dx == 0) // Line is vertical - { - if (y2 < y1) std::swap(y1, y2); - for (y = y1; y <= y2; y++) if (rol()) Draw(x1, y, p); - return; - } - - if (dy == 0) // Line is horizontal - { - if (x2 < x1) std::swap(x1, x2); - for (x = x1; x <= x2; x++) if (rol()) Draw(x, y1, p); - return; - } - - // Line is Funk-aye - dx1 = abs(dx); dy1 = abs(dy); - px = 2 * dy1 - dx1; py = 2 * dx1 - dy1; - if (dy1 <= dx1) - { - if (dx >= 0) - { - x = x1; y = y1; xe = x2; - } - else - { - x = x2; y = y2; xe = x1; - } - - if (rol()) Draw(x, y, p); - - for (i = 0; x < xe; i++) - { - x = x + 1; - if (px < 0) - px = px + 2 * dy1; - else - { - if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) y = y + 1; else y = y - 1; - px = px + 2 * (dy1 - dx1); - } - if (rol()) Draw(x, y, p); - } - } - else - { - if (dy >= 0) - { - x = x1; y = y1; ye = y2; - } - else - { - x = x2; y = y2; ye = y1; - } - - if (rol()) Draw(x, y, p); - - for (i = 0; y < ye; i++) - { - y = y + 1; - if (py <= 0) - py = py + 2 * dx1; - else - { - if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) x = x + 1; else x = x - 1; - py = py + 2 * (dx1 - dy1); - } - if (rol()) Draw(x, y, p); - } - } - } - - void PixelGameEngine::DrawCircle(const olc::vi2d& pos, int32_t radius, Pixel p, uint8_t mask) - { DrawCircle(pos.x, pos.y, radius, p, mask); } - - void PixelGameEngine::DrawCircle(int32_t x, int32_t y, int32_t radius, Pixel p, uint8_t mask) - { // Thanks to IanM-Matrix1 #PR121 - if (radius < 0 || x < -radius || y < -radius || x - GetDrawTargetWidth() > radius || y - GetDrawTargetHeight() > radius) - return; - - if (radius > 0) - { - int x0 = 0; - int y0 = radius; - int d = 3 - 2 * radius; - - while (y0 >= x0) // only formulate 1/8 of circle - { - // Draw even octants - if (mask & 0x01) Draw(x + x0, y - y0, p);// Q6 - upper right right - if (mask & 0x04) Draw(x + y0, y + x0, p);// Q4 - lower lower right - if (mask & 0x10) Draw(x - x0, y + y0, p);// Q2 - lower left left - if (mask & 0x40) Draw(x - y0, y - x0, p);// Q0 - upper upper left - if (x0 != 0 && x0 != y0) - { - if (mask & 0x02) Draw(x + y0, y - x0, p);// Q7 - upper upper right - if (mask & 0x08) Draw(x + x0, y + y0, p);// Q5 - lower right right - if (mask & 0x20) Draw(x - y0, y + x0, p);// Q3 - lower lower left - if (mask & 0x80) Draw(x - x0, y - y0, p);// Q1 - upper left left - } - - if (d < 0) - d += 4 * x0++ + 6; - else - d += 4 * (x0++ - y0--) + 10; - } - } - else - Draw(x, y, p); - } - - void PixelGameEngine::FillCircle(const olc::vi2d& pos, int32_t radius, Pixel p) - { FillCircle(pos.x, pos.y, radius, p); } - - void PixelGameEngine::FillCircle(int32_t x, int32_t y, int32_t radius, Pixel p) - { // Thanks to IanM-Matrix1 #PR121 - if (radius < 0 || x < -radius || y < -radius || x - GetDrawTargetWidth() > radius || y - GetDrawTargetHeight() > radius) - return; - - if (radius > 0) - { - int x0 = 0; - int y0 = radius; - int d = 3 - 2 * radius; - - auto drawline = [&](int sx, int ex, int y) - { - for (int x = sx; x <= ex; x++) - Draw(x, y, p); - }; - - while (y0 >= x0) - { - drawline(x - y0, x + y0, y - x0); - if (x0 > 0) drawline(x - y0, x + y0, y + x0); - - if (d < 0) - d += 4 * x0++ + 6; - else - { - if (x0 != y0) - { - drawline(x - x0, x + x0, y - y0); - drawline(x - x0, x + x0, y + y0); - } - d += 4 * (x0++ - y0--) + 10; - } - } - } - else - Draw(x, y, p); - } - - void PixelGameEngine::DrawRect(const olc::vi2d& pos, const olc::vi2d& size, Pixel p) - { DrawRect(pos.x, pos.y, size.x, size.y, p); } - - void PixelGameEngine::DrawRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p) - { - DrawLine(x, y, x + w, y, p); - DrawLine(x + w, y, x + w, y + h, p); - DrawLine(x + w, y + h, x, y + h, p); - DrawLine(x, y + h, x, y, p); - } - - void PixelGameEngine::Clear(Pixel p) - { - int pixels = GetDrawTargetWidth() * GetDrawTargetHeight(); - Pixel* m = GetDrawTarget()->GetData(); - for (int i = 0; i < pixels; i++) m[i] = p; - } - - void PixelGameEngine::ClearBuffer(Pixel p, bool bDepth) - { renderer->ClearBuffer(p, bDepth); } - - olc::Sprite* PixelGameEngine::GetFontSprite() - { return fontSprite; } - - void PixelGameEngine::FillRect(const olc::vi2d& pos, const olc::vi2d& size, Pixel p) - { FillRect(pos.x, pos.y, size.x, size.y, p); } - - void PixelGameEngine::FillRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p) - { - int32_t x2 = x + w; - int32_t y2 = y + h; - - if (x < 0) x = 0; - if (x >= (int32_t)GetDrawTargetWidth()) x = (int32_t)GetDrawTargetWidth(); - if (y < 0) y = 0; - if (y >= (int32_t)GetDrawTargetHeight()) y = (int32_t)GetDrawTargetHeight(); - - if (x2 < 0) x2 = 0; - if (x2 >= (int32_t)GetDrawTargetWidth()) x2 = (int32_t)GetDrawTargetWidth(); - if (y2 < 0) y2 = 0; - if (y2 >= (int32_t)GetDrawTargetHeight()) y2 = (int32_t)GetDrawTargetHeight(); - - for (int i = x; i < x2; i++) - for (int j = y; j < y2; j++) - Draw(i, j, p); - } - - void PixelGameEngine::DrawTriangle(const olc::vi2d& pos1, const olc::vi2d& pos2, const olc::vi2d& pos3, Pixel p) - { DrawTriangle(pos1.x, pos1.y, pos2.x, pos2.y, pos3.x, pos3.y, p); } - - void PixelGameEngine::DrawTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, Pixel p) - { - DrawLine(x1, y1, x2, y2, p); - DrawLine(x2, y2, x3, y3, p); - DrawLine(x3, y3, x1, y1, p); - } - - void PixelGameEngine::FillTriangle(const olc::vi2d& pos1, const olc::vi2d& pos2, const olc::vi2d& pos3, Pixel p) - { FillTriangle(pos1.x, pos1.y, pos2.x, pos2.y, pos3.x, pos3.y, p); } - - // https://www.avrfreaks.net/sites/default/files/triangles.c - void PixelGameEngine::FillTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, Pixel p) - { - auto drawline = [&](int sx, int ex, int ny) { for (int i = sx; i <= ex; i++) Draw(i, ny, p); }; - - int t1x, t2x, y, minx, maxx, t1xp, t2xp; - bool changed1 = false; - bool changed2 = false; - int signx1, signx2, dx1, dy1, dx2, dy2; - int e1, e2; - // Sort vertices - if (y1 > y2) { std::swap(y1, y2); std::swap(x1, x2); } - if (y1 > y3) { std::swap(y1, y3); std::swap(x1, x3); } - if (y2 > y3) { std::swap(y2, y3); std::swap(x2, x3); } - - t1x = t2x = x1; y = y1; // Starting points - dx1 = (int)(x2 - x1); - if (dx1 < 0) { dx1 = -dx1; signx1 = -1; } - else signx1 = 1; - dy1 = (int)(y2 - y1); - - dx2 = (int)(x3 - x1); - if (dx2 < 0) { dx2 = -dx2; signx2 = -1; } - else signx2 = 1; - dy2 = (int)(y3 - y1); - - if (dy1 > dx1) { std::swap(dx1, dy1); changed1 = true; } - if (dy2 > dx2) { std::swap(dy2, dx2); changed2 = true; } - - e2 = (int)(dx2 >> 1); - // Flat top, just process the second half - if (y1 == y2) goto next; - e1 = (int)(dx1 >> 1); - - for (int i = 0; i < dx1;) { - t1xp = 0; t2xp = 0; - if (t1x < t2x) { minx = t1x; maxx = t2x; } - else { minx = t2x; maxx = t1x; } - // process first line until y value is about to change - while (i < dx1) { - i++; - e1 += dy1; - while (e1 >= dx1) { - e1 -= dx1; - if (changed1) t1xp = signx1;//t1x += signx1; - else goto next1; - } - if (changed1) break; - else t1x += signx1; - } - // Move line - next1: - // process second line until y value is about to change - while (1) { - e2 += dy2; - while (e2 >= dx2) { - e2 -= dx2; - if (changed2) t2xp = signx2;//t2x += signx2; - else goto next2; - } - if (changed2) break; - else t2x += signx2; - } - next2: - if (minx > t1x) minx = t1x; - if (minx > t2x) minx = t2x; - if (maxx < t1x) maxx = t1x; - if (maxx < t2x) maxx = t2x; - drawline(minx, maxx, y); // Draw line from min to max points found on the y - // Now increase y - if (!changed1) t1x += signx1; - t1x += t1xp; - if (!changed2) t2x += signx2; - t2x += t2xp; - y += 1; - if (y == y2) break; - } - next: - // Second half - dx1 = (int)(x3 - x2); if (dx1 < 0) { dx1 = -dx1; signx1 = -1; } - else signx1 = 1; - dy1 = (int)(y3 - y2); - t1x = x2; - - if (dy1 > dx1) { // swap values - std::swap(dy1, dx1); - changed1 = true; - } - else changed1 = false; - - e1 = (int)(dx1 >> 1); - - for (int i = 0; i <= dx1; i++) { - t1xp = 0; t2xp = 0; - if (t1x < t2x) { minx = t1x; maxx = t2x; } - else { minx = t2x; maxx = t1x; } - // process first line until y value is about to change - while (i < dx1) { - e1 += dy1; - while (e1 >= dx1) { - e1 -= dx1; - if (changed1) { t1xp = signx1; break; }//t1x += signx1; - else goto next3; - } - if (changed1) break; - else t1x += signx1; - if (i < dx1) i++; - } - next3: - // process second line until y value is about to change - while (t2x != x3) { - e2 += dy2; - while (e2 >= dx2) { - e2 -= dx2; - if (changed2) t2xp = signx2; - else goto next4; - } - if (changed2) break; - else t2x += signx2; - } - next4: - - if (minx > t1x) minx = t1x; - if (minx > t2x) minx = t2x; - if (maxx < t1x) maxx = t1x; - if (maxx < t2x) maxx = t2x; - drawline(minx, maxx, y); - if (!changed1) t1x += signx1; - t1x += t1xp; - if (!changed2) t2x += signx2; - t2x += t2xp; - y += 1; - if (y > y3) return; - } - } - - void PixelGameEngine::DrawSprite(const olc::vi2d& pos, Sprite* sprite, uint32_t scale, uint8_t flip) - { DrawSprite(pos.x, pos.y, sprite, scale, flip); } - - void PixelGameEngine::DrawSprite(int32_t x, int32_t y, Sprite* sprite, uint32_t scale, uint8_t flip) - { - if (sprite == nullptr) - return; - - int32_t fxs = 0, fxm = 1, fx = 0; - int32_t fys = 0, fym = 1, fy = 0; - if (flip & olc::Sprite::Flip::HORIZ) { fxs = sprite->width - 1; fxm = -1; } - if (flip & olc::Sprite::Flip::VERT) { fys = sprite->height - 1; fym = -1; } - - if (scale > 1) - { - fx = fxs; - for (int32_t i = 0; i < sprite->width; i++, fx += fxm) - { - fy = fys; - for (int32_t j = 0; j < sprite->height; j++, fy += fym) - for (uint32_t is = 0; is < scale; is++) - for (uint32_t js = 0; js < scale; js++) - Draw(x + (i * scale) + is, y + (j * scale) + js, sprite->GetPixel(fx, fy)); - } - } - else - { - fx = fxs; - for (int32_t i = 0; i < sprite->width; i++, fx += fxm) - { - fy = fys; - for (int32_t j = 0; j < sprite->height; j++, fy += fym) - Draw(x + i, y + j, sprite->GetPixel(fx, fy)); - } - } - } - - void PixelGameEngine::DrawPartialSprite(const olc::vi2d& pos, Sprite* sprite, const olc::vi2d& sourcepos, const olc::vi2d& size, uint32_t scale, uint8_t flip) - { DrawPartialSprite(pos.x, pos.y, sprite, sourcepos.x, sourcepos.y, size.x, size.y, scale, flip); } - - void PixelGameEngine::DrawPartialSprite(int32_t x, int32_t y, Sprite* sprite, int32_t ox, int32_t oy, int32_t w, int32_t h, uint32_t scale, uint8_t flip) - { - if (sprite == nullptr) - return; - - int32_t fxs = 0, fxm = 1, fx = 0; - int32_t fys = 0, fym = 1, fy = 0; - if (flip & olc::Sprite::Flip::HORIZ) { fxs = w - 1; fxm = -1; } - if (flip & olc::Sprite::Flip::VERT) { fys = h - 1; fym = -1; } - - if (scale > 1) - { - fx = fxs; - for (int32_t i = 0; i < w; i++, fx += fxm) - { - fy = fys; - for (int32_t j = 0; j < h; j++, fy += fym) - for (uint32_t is = 0; is < scale; is++) - for (uint32_t js = 0; js < scale; js++) - Draw(x + (i * scale) + is, y + (j * scale) + js, sprite->GetPixel(fx + ox, fy + oy)); - } - } - else - { - fx = fxs; - for (int32_t i = 0; i < w; i++, fx += fxm) - { - fy = fys; - for (int32_t j = 0; j < h; j++, fy += fym) - Draw(x + i, y + j, sprite->GetPixel(fx + ox, fy + oy)); - } - } - } - - void PixelGameEngine::SetDecalMode(const olc::DecalMode& mode) - { nDecalMode = mode; } - - void PixelGameEngine::DrawPartialDecal(const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale, const olc::Pixel& tint) - { - olc::vf2d vScreenSpacePos = - { - (std::floor(pos.x) * vInvScreenSize.x) * 2.0f - 1.0f, - ((std::floor(pos.y) * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f - }; - - olc::vf2d vScreenSpaceDim = - { - vScreenSpacePos.x + (2.0f * source_size.x * vInvScreenSize.x) * scale.x, - vScreenSpacePos.y - (2.0f * source_size.y * vInvScreenSize.y) * scale.y - }; - - DecalInstance di; - di.points = 4; - di.decal = decal; - di.tint = { tint, tint, tint, tint }; - di.pos = { { vScreenSpacePos.x, vScreenSpacePos.y }, { vScreenSpacePos.x, vScreenSpaceDim.y }, { vScreenSpaceDim.x, vScreenSpaceDim.y }, { vScreenSpaceDim.x, vScreenSpacePos.y } }; - olc::vf2d uvtl = source_pos * decal->vUVScale; - olc::vf2d uvbr = uvtl + (source_size * decal->vUVScale); - di.uv = { { uvtl.x, uvtl.y }, { uvtl.x, uvbr.y }, { uvbr.x, uvbr.y }, { uvbr.x, uvtl.y } }; - di.w = { 1,1,1,1 }; - di.mode = nDecalMode; - vLayers[nTargetLayer].vecDecalInstance.push_back(di); - } - - void PixelGameEngine::DrawPartialDecal(const olc::vf2d& pos, const olc::vf2d& size, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint) - { - olc::vf2d vScreenSpacePos = - { - (std::floor(pos.x) * vInvScreenSize.x) * 2.0f - 1.0f, - ((std::floor(pos.y) * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f - }; - - olc::vf2d vScreenSpaceDim = - { - vScreenSpacePos.x + (2.0f * size.x * vInvScreenSize.x), - vScreenSpacePos.y - (2.0f * size.y * vInvScreenSize.y) - }; - - DecalInstance di; - di.points = 4; - di.decal = decal; - di.tint = { tint, tint, tint, tint }; - di.pos = { { vScreenSpacePos.x, vScreenSpacePos.y }, { vScreenSpacePos.x, vScreenSpaceDim.y }, { vScreenSpaceDim.x, vScreenSpaceDim.y }, { vScreenSpaceDim.x, vScreenSpacePos.y } }; - olc::vf2d uvtl = (source_pos) * decal->vUVScale; - olc::vf2d uvbr = uvtl + ((source_size) * decal->vUVScale); - di.uv = { { uvtl.x, uvtl.y }, { uvtl.x, uvbr.y }, { uvbr.x, uvbr.y }, { uvbr.x, uvtl.y } }; - di.w = { 1,1,1,1 }; - di.mode = nDecalMode; - vLayers[nTargetLayer].vecDecalInstance.push_back(di); - } - - - void PixelGameEngine::DrawDecal(const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& scale, const olc::Pixel& tint) - { - olc::vf2d vScreenSpacePos = - { - (std::floor(pos.x) * vInvScreenSize.x) * 2.0f - 1.0f, - ((std::floor(pos.y) * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f - }; - - olc::vf2d vScreenSpaceDim = - { - vScreenSpacePos.x + (2.0f * (float(decal->sprite->width) * vInvScreenSize.x)) * scale.x, - vScreenSpacePos.y - (2.0f * (float(decal->sprite->height) * vInvScreenSize.y)) * scale.y - }; - - DecalInstance di; - di.decal = decal; - di.points = 4; - di.tint = { tint, tint, tint, tint }; - di.pos = { { vScreenSpacePos.x, vScreenSpacePos.y }, { vScreenSpacePos.x, vScreenSpaceDim.y }, { vScreenSpaceDim.x, vScreenSpaceDim.y }, { vScreenSpaceDim.x, vScreenSpacePos.y } }; - di.uv = { { 0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 0.0f} }; - di.w = { 1, 1, 1, 1 }; - di.mode = nDecalMode; - vLayers[nTargetLayer].vecDecalInstance.push_back(di); - } - - void PixelGameEngine::DrawExplicitDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d* uv, const olc::Pixel* col, uint32_t elements) - { - DecalInstance di; - di.decal = decal; - di.pos.resize(elements); - di.uv.resize(elements); - di.w.resize(elements); - di.tint.resize(elements); - di.points = elements; - for (uint32_t i = 0; i < elements; i++) - { - di.pos[i] = { (pos[i].x * vInvScreenSize.x) * 2.0f - 1.0f, ((pos[i].y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f }; - di.uv[i] = uv[i]; - di.tint[i] = col[i]; - di.w[i] = 1.0f; - } - di.mode = nDecalMode; - vLayers[nTargetLayer].vecDecalInstance.push_back(di); - } - - void PixelGameEngine::DrawPolygonDecal(olc::Decal* decal, const std::vector& pos, const std::vector& uv, const olc::Pixel tint) - { - DecalInstance di; - di.decal = decal; - di.points = uint32_t(pos.size()); - di.pos.resize(di.points); - di.uv.resize(di.points); - di.w.resize(di.points); - di.tint.resize(di.points); - for (uint32_t i = 0; i < di.points; i++) - { - di.pos[i] = { (pos[i].x * vInvScreenSize.x) * 2.0f - 1.0f, ((pos[i].y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f }; - di.uv[i] = uv[i]; - di.tint[i] = tint; - di.w[i] = 1.0f; - } - di.mode = nDecalMode; - vLayers[nTargetLayer].vecDecalInstance.push_back(di); - } - - void PixelGameEngine::DrawLineDecal(const olc::vf2d& pos1, const olc::vf2d& pos2, Pixel p) - { - DecalInstance di; - di.decal = nullptr; - di.points = uint32_t(2); - di.pos.resize(di.points); - di.uv.resize(di.points); - di.w.resize(di.points); - di.tint.resize(di.points); - di.pos[0] = { (pos1.x * vInvScreenSize.x) * 2.0f - 1.0f, ((pos1.y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f }; - di.uv[0] = { 0.0f, 0.0f }; - di.tint[0] = p; - di.w[0] = 1.0f; - di.pos[1] = { (pos2.x * vInvScreenSize.x) * 2.0f - 1.0f, ((pos2.y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f }; - di.uv[1] = { 0.0f, 0.0f }; - di.tint[1] = p; - di.w[1] = 1.0f; - di.mode = olc::DecalMode::WIREFRAME; - vLayers[nTargetLayer].vecDecalInstance.push_back(di); - } - - void PixelGameEngine::FillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) - { - std::array points = { { {pos}, {pos.x, pos.y + size.y}, {pos + size}, {pos.x + size.x, pos.y} } }; - std::array uvs = { {{0,0},{0,0},{0,0},{0,0}} }; - std::array cols = { {col, col, col, col} }; - DrawExplicitDecal(nullptr, points.data(), uvs.data(), cols.data(), 4); - } - - void PixelGameEngine::GradientFillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel colTL, const olc::Pixel colBL, const olc::Pixel colBR, const olc::Pixel colTR) - { - std::array points = { { {pos}, {pos.x, pos.y + size.y}, {pos + size}, {pos.x + size.x, pos.y} } }; - std::array uvs = { {{0,0},{0,0},{0,0},{0,0}} }; - std::array cols = { {colTL, colBL, colBR, colTR} }; - DrawExplicitDecal(nullptr, points.data(), uvs.data(), cols.data(), 4); - } - - void PixelGameEngine::DrawRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center, const olc::vf2d& scale, const olc::Pixel& tint) - { - DecalInstance di; - di.decal = decal; - di.pos.resize(4); - di.uv = { { 0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 0.0f} }; - di.w = { 1, 1, 1, 1 }; - di.tint = { tint, tint, tint, tint }; - di.points = 4; - di.pos[0] = (olc::vf2d(0.0f, 0.0f) - center) * scale; - di.pos[1] = (olc::vf2d(0.0f, float(decal->sprite->height)) - center) * scale; - di.pos[2] = (olc::vf2d(float(decal->sprite->width), float(decal->sprite->height)) - center) * scale; - di.pos[3] = (olc::vf2d(float(decal->sprite->width), 0.0f) - center) * scale; - float c = cos(fAngle), s = sin(fAngle); - for (int i = 0; i < 4; i++) - { - di.pos[i] = pos + olc::vf2d(di.pos[i].x * c - di.pos[i].y * s, di.pos[i].x * s + di.pos[i].y * c); - di.pos[i] = di.pos[i] * vInvScreenSize * 2.0f - olc::vf2d(1.0f, 1.0f); - di.pos[i].y *= -1.0f; - di.w[i] = 1; - } - di.mode = nDecalMode; - vLayers[nTargetLayer].vecDecalInstance.push_back(di); - } - - - void PixelGameEngine::DrawPartialRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale, const olc::Pixel& tint) - { - DecalInstance di; - di.decal = decal; - di.points = 4; - di.tint = { tint, tint, tint, tint }; - di.w = { 1, 1, 1, 1 }; - di.pos.resize(4); - di.pos[0] = (olc::vf2d(0.0f, 0.0f) - center) * scale; - di.pos[1] = (olc::vf2d(0.0f, source_size.y) - center) * scale; - di.pos[2] = (olc::vf2d(source_size.x, source_size.y) - center) * scale; - di.pos[3] = (olc::vf2d(source_size.x, 0.0f) - center) * scale; - float c = cos(fAngle), s = sin(fAngle); - for (int i = 0; i < 4; i++) - { - di.pos[i] = pos + olc::vf2d(di.pos[i].x * c - di.pos[i].y * s, di.pos[i].x * s + di.pos[i].y * c); - di.pos[i] = di.pos[i] * vInvScreenSize * 2.0f - olc::vf2d(1.0f, 1.0f); - di.pos[i].y *= -1.0f; - } - - olc::vf2d uvtl = source_pos * decal->vUVScale; - olc::vf2d uvbr = uvtl + (source_size * decal->vUVScale); - di.uv = { { uvtl.x, uvtl.y }, { uvtl.x, uvbr.y }, { uvbr.x, uvbr.y }, { uvbr.x, uvtl.y } }; - di.mode = nDecalMode; - vLayers[nTargetLayer].vecDecalInstance.push_back(di); - } - - void PixelGameEngine::DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint) - { - DecalInstance di; - di.points = 4; - di.decal = decal; - di.tint = { tint, tint, tint, tint }; - di.w = { 1, 1, 1, 1 }; - di.pos.resize(4); - di.uv = { { 0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 0.0f} }; - olc::vf2d center; - float rd = ((pos[2].x - pos[0].x) * (pos[3].y - pos[1].y) - (pos[3].x - pos[1].x) * (pos[2].y - pos[0].y)); - if (rd != 0) - { - olc::vf2d uvtl = source_pos * decal->vUVScale; - olc::vf2d uvbr = uvtl + (source_size * decal->vUVScale); - di.uv = { { uvtl.x, uvtl.y }, { uvtl.x, uvbr.y }, { uvbr.x, uvbr.y }, { uvbr.x, uvtl.y } }; - - rd = 1.0f / rd; - float rn = ((pos[3].x - pos[1].x) * (pos[0].y - pos[1].y) - (pos[3].y - pos[1].y) * (pos[0].x - pos[1].x)) * rd; - float sn = ((pos[2].x - pos[0].x) * (pos[0].y - pos[1].y) - (pos[2].y - pos[0].y) * (pos[0].x - pos[1].x)) * rd; - if (!(rn < 0.f || rn > 1.f || sn < 0.f || sn > 1.f)) center = pos[0] + rn * (pos[2] - pos[0]); - float d[4]; for (int i = 0; i < 4; i++) d[i] = (pos[i] - center).mag(); - for (int i = 0; i < 4; i++) - { - float q = d[i] == 0.0f ? 1.0f : (d[i] + d[(i + 2) & 3]) / d[(i + 2) & 3]; - di.uv[i] *= q; di.w[i] *= q; - di.pos[i] = { (pos[i].x * vInvScreenSize.x) * 2.0f - 1.0f, ((pos[i].y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f }; - } - di.mode = nDecalMode; - vLayers[nTargetLayer].vecDecalInstance.push_back(di); - } - } - - void PixelGameEngine::DrawWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::Pixel& tint) - { - // Thanks Nathan Reed, a brilliant article explaining whats going on here - // http://www.reedbeta.com/blog/quadrilateral-interpolation-part-1/ - DecalInstance di; - di.points = 4; - di.decal = decal; - di.tint = { tint, tint, tint, tint }; - di.w = { 1, 1, 1, 1 }; - di.pos.resize(4); - di.uv = { { 0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 0.0f} }; - olc::vf2d center; - float rd = ((pos[2].x - pos[0].x) * (pos[3].y - pos[1].y) - (pos[3].x - pos[1].x) * (pos[2].y - pos[0].y)); - if (rd != 0) - { - rd = 1.0f / rd; - float rn = ((pos[3].x - pos[1].x) * (pos[0].y - pos[1].y) - (pos[3].y - pos[1].y) * (pos[0].x - pos[1].x)) * rd; - float sn = ((pos[2].x - pos[0].x) * (pos[0].y - pos[1].y) - (pos[2].y - pos[0].y) * (pos[0].x - pos[1].x)) * rd; - if (!(rn < 0.f || rn > 1.f || sn < 0.f || sn > 1.f)) center = pos[0] + rn * (pos[2] - pos[0]); - float d[4]; for (int i = 0; i < 4; i++) d[i] = (pos[i] - center).mag(); - for (int i = 0; i < 4; i++) - { - float q = d[i] == 0.0f ? 1.0f : (d[i] + d[(i + 2) & 3]) / d[(i + 2) & 3]; - di.uv[i] *= q; di.w[i] *= q; - di.pos[i] = { (pos[i].x * vInvScreenSize.x) * 2.0f - 1.0f, ((pos[i].y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f }; - } - di.mode = nDecalMode; - vLayers[nTargetLayer].vecDecalInstance.push_back(di); - } - } - - void PixelGameEngine::DrawWarpedDecal(olc::Decal* decal, const std::array& pos, const olc::Pixel& tint) - { DrawWarpedDecal(decal, pos.data(), tint); } - - void PixelGameEngine::DrawWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::Pixel& tint) - { DrawWarpedDecal(decal, &pos[0], tint); } - - void PixelGameEngine::DrawPartialWarpedDecal(olc::Decal* decal, const std::array& pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint) - { DrawPartialWarpedDecal(decal, pos.data(), source_pos, source_size, tint); } - - void PixelGameEngine::DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint) - { DrawPartialWarpedDecal(decal, &pos[0], source_pos, source_size, tint); } - - void PixelGameEngine::DrawStringDecal(const olc::vf2d& pos, const std::string& sText, const Pixel col, const olc::vf2d& scale) - { - olc::vf2d spos = { 0.0f, 0.0f }; - for (auto c : sText) - { - if (c == '\n') - { - spos.x = 0; spos.y += 8.0f * scale.y; - } - else - { - int32_t ox = (c - 32) % 16; - int32_t oy = (c - 32) / 16; - DrawPartialDecal(pos + spos, fontDecal, { float(ox) * 8.0f, float(oy) * 8.0f }, { 8.0f, 8.0f }, scale, col); - spos.x += 8.0f * scale.x; - } - } - } - - void PixelGameEngine::DrawStringPropDecal(const olc::vf2d& pos, const std::string& sText, const Pixel col, const olc::vf2d& scale) - { - olc::vf2d spos = { 0.0f, 0.0f }; - for (auto c : sText) - { - if (c == '\n') - { - spos.x = 0; spos.y += 8.0f * scale.y; - } - else - { - int32_t ox = (c - 32) % 16; - int32_t oy = (c - 32) / 16; - DrawPartialDecal(pos + spos, fontDecal, { float(ox) * 8.0f + float(vFontSpacing[c - 32].x), float(oy) * 8.0f }, { float(vFontSpacing[c - 32].y), 8.0f }, scale, col); - spos.x += float(vFontSpacing[c - 32].y) * scale.x; - } - } - } - // Thanks Oso-Grande/Sopadeoso For these awesom and stupidly clever Text Rotation routines... duh XD - void PixelGameEngine::DrawRotatedStringDecal(const olc::vf2d& pos, const std::string& sText, const float fAngle, const olc::vf2d& center, const Pixel col, const olc::vf2d& scale) - { - olc::vf2d spos = center; - for (auto c : sText) - { - if (c == '\n') - { - spos.x = center.x; spos.y -= 8.0f; - } - else - { - int32_t ox = (c - 32) % 16; - int32_t oy = (c - 32) / 16; - DrawPartialRotatedDecal(pos, fontDecal, fAngle, spos, { float(ox) * 8.0f, float(oy) * 8.0f }, { 8.0f, 8.0f }, scale, col); - spos.x -= 8.0f; - } - } - } - - void PixelGameEngine::DrawRotatedStringPropDecal(const olc::vf2d& pos, const std::string& sText, const float fAngle, const olc::vf2d& center, const Pixel col, const olc::vf2d& scale) - { - olc::vf2d spos = center; - for (auto c : sText) - { - if (c == '\n') - { - spos.x = center.x; spos.y -= 8.0f; - } - else - { - int32_t ox = (c - 32) % 16; - int32_t oy = (c - 32) / 16; - DrawPartialRotatedDecal(pos, fontDecal, fAngle, spos, { float(ox) * 8.0f + float(vFontSpacing[c - 32].x), float(oy) * 8.0f }, { float(vFontSpacing[c - 32].y), 8.0f }, scale, col); - spos.x -= float(vFontSpacing[c - 32].y); - } - } - } - - olc::vi2d PixelGameEngine::GetTextSize(const std::string& s) - { - olc::vi2d size = { 0,1 }; - olc::vi2d pos = { 0,1 }; - for (auto c : s) - { - if (c == '\n') { pos.y++; pos.x = 0; } - else pos.x++; - size.x = std::max(size.x, pos.x); - size.y = std::max(size.y, pos.y); - } - return size * 8; - } - - void PixelGameEngine::DrawString(const olc::vi2d& pos, const std::string& sText, Pixel col, uint32_t scale) - { DrawString(pos.x, pos.y, sText, col, scale); } - - void PixelGameEngine::DrawString(int32_t x, int32_t y, const std::string& sText, Pixel col, uint32_t scale) - { - int32_t sx = 0; - int32_t sy = 0; - Pixel::Mode m = nPixelMode; - // Thanks @tucna, spotted bug with col.ALPHA :P - if (m != Pixel::CUSTOM) // Thanks @Megarev, required for "shaders" - { - if (col.a != 255) SetPixelMode(Pixel::ALPHA); - else SetPixelMode(Pixel::MASK); - } - for (auto c : sText) - { - if (c == '\n') - { - sx = 0; sy += 8 * scale; - } - else - { - int32_t ox = (c - 32) % 16; - int32_t oy = (c - 32) / 16; - - if (scale > 1) - { - for (uint32_t i = 0; i < 8; i++) - for (uint32_t j = 0; j < 8; j++) - if (fontSprite->GetPixel(i + ox * 8, j + oy * 8).r > 0) - for (uint32_t is = 0; is < scale; is++) - for (uint32_t js = 0; js < scale; js++) - Draw(x + sx + (i * scale) + is, y + sy + (j * scale) + js, col); - } - else - { - for (uint32_t i = 0; i < 8; i++) - for (uint32_t j = 0; j < 8; j++) - if (fontSprite->GetPixel(i + ox * 8, j + oy * 8).r > 0) - Draw(x + sx + i, y + sy + j, col); - } - sx += 8 * scale; - } - } - SetPixelMode(m); - } - - olc::vi2d PixelGameEngine::GetTextSizeProp(const std::string& s) - { - olc::vi2d size = { 0,1 }; - olc::vi2d pos = { 0,1 }; - for (auto c : s) - { - if (c == '\n') { pos.y += 1; pos.x = 0; } - else pos.x += vFontSpacing[c - 32].y; - size.x = std::max(size.x, pos.x); - size.y = std::max(size.y, pos.y); - } - - size.y *= 8; - return size; - } - - void PixelGameEngine::DrawStringProp(const olc::vi2d& pos, const std::string& sText, Pixel col, uint32_t scale) - { DrawStringProp(pos.x, pos.y, sText, col, scale); } - - void PixelGameEngine::DrawStringProp(int32_t x, int32_t y, const std::string& sText, Pixel col, uint32_t scale) - { - int32_t sx = 0; - int32_t sy = 0; - Pixel::Mode m = nPixelMode; - - if (m != Pixel::CUSTOM) - { - if (col.a != 255) SetPixelMode(Pixel::ALPHA); - else SetPixelMode(Pixel::MASK); - } - for (auto c : sText) - { - if (c == '\n') - { - sx = 0; sy += 8 * scale; - } - else - { - int32_t ox = (c - 32) % 16; - int32_t oy = (c - 32) / 16; - - if (scale > 1) - { - for (int32_t i = 0; i < vFontSpacing[c - 32].y; i++) - for (int32_t j = 0; j < 8; j++) - if (fontSprite->GetPixel(i + ox * 8 + vFontSpacing[c - 32].x, j + oy * 8).r > 0) - for (int32_t is = 0; is < int(scale); is++) - for (int32_t js = 0; js < int(scale); js++) - Draw(x + sx + (i * scale) + is, y + sy + (j * scale) + js, col); - } - else - { - for (int32_t i = 0; i < vFontSpacing[c - 32].y; i++) - for (int32_t j = 0; j < 8; j++) - if (fontSprite->GetPixel(i + ox * 8 + vFontSpacing[c - 32].x, j + oy * 8).r > 0) - Draw(x + sx + i, y + sy + j, col); - } - sx += vFontSpacing[c - 32].y * scale; - } - } - SetPixelMode(m); - } - - void PixelGameEngine::SetPixelMode(Pixel::Mode m) - { nPixelMode = m; } - - Pixel::Mode PixelGameEngine::GetPixelMode() - { return nPixelMode; } - - void PixelGameEngine::SetPixelMode(std::function pixelMode) - { - funcPixelMode = pixelMode; - nPixelMode = Pixel::Mode::CUSTOM; - } - - void PixelGameEngine::SetPixelBlend(float fBlend) - { - fBlendFactor = fBlend; - if (fBlendFactor < 0.0f) fBlendFactor = 0.0f; - if (fBlendFactor > 1.0f) fBlendFactor = 1.0f; - } - - // User must override these functions as required. I have not made - // them abstract because I do need a default behaviour to occur if - // they are not overwritten - - bool PixelGameEngine::OnUserCreate() - { return false; } - - bool PixelGameEngine::OnUserUpdate(float fElapsedTime) - { UNUSED(fElapsedTime); return false; } - - bool PixelGameEngine::OnUserDestroy() - { return true; } - - void PixelGameEngine::olc_UpdateViewport() - { - int32_t ww = vScreenSize.x * vPixelSize.x; - int32_t wh = vScreenSize.y * vPixelSize.y; - float wasp = (float)ww / (float)wh; - - if (bPixelCohesion) - { - vScreenPixelSize = (vWindowSize / vScreenSize); - vViewSize = (vWindowSize / vScreenSize) * vScreenSize; - } - else - { - vViewSize.x = (int32_t)vWindowSize.x; - vViewSize.y = (int32_t)((float)vViewSize.x / wasp); - - if (vViewSize.y > vWindowSize.y) - { - vViewSize.y = vWindowSize.y; - vViewSize.x = (int32_t)((float)vViewSize.y * wasp); - } - } - - vViewPos = (vWindowSize - vViewSize) / 2; - } - - void PixelGameEngine::olc_UpdateWindowSize(int32_t x, int32_t y) - { - vWindowSize = { x, y }; - olc_UpdateViewport(); - } - - void PixelGameEngine::olc_UpdateMouseWheel(int32_t delta) - { nMouseWheelDeltaCache += delta; } - - void PixelGameEngine::olc_UpdateMouse(int32_t x, int32_t y) - { - // Mouse coords come in screen space - // But leave in pixel space - bHasMouseFocus = true; - vMouseWindowPos = { x, y }; - // Full Screen mode may have a weird viewport we must clamp to - x -= vViewPos.x; - y -= vViewPos.y; - vMousePosCache.x = (int32_t)(((float)x / (float)(vWindowSize.x - (vViewPos.x * 2)) * (float)vScreenSize.x)); - vMousePosCache.y = (int32_t)(((float)y / (float)(vWindowSize.y - (vViewPos.y * 2)) * (float)vScreenSize.y)); - if (vMousePosCache.x >= (int32_t)vScreenSize.x) vMousePosCache.x = vScreenSize.x - 1; - if (vMousePosCache.y >= (int32_t)vScreenSize.y) vMousePosCache.y = vScreenSize.y - 1; - if (vMousePosCache.x < 0) vMousePosCache.x = 0; - if (vMousePosCache.y < 0) vMousePosCache.y = 0; - } - - void PixelGameEngine::olc_UpdateMouseState(int32_t button, bool state) - { pMouseNewState[button] = state; } - - void PixelGameEngine::olc_UpdateKeyState(int32_t key, bool state) - { pKeyNewState[key] = state; } - - void PixelGameEngine::olc_UpdateMouseFocus(bool state) - { bHasMouseFocus = state; } - - void PixelGameEngine::olc_UpdateKeyFocus(bool state) - { bHasInputFocus = state; } - - void PixelGameEngine::olc_Reanimate() - { bAtomActive = true; } - - bool PixelGameEngine::olc_IsRunning() - { return bAtomActive; } - - void PixelGameEngine::olc_Terminate() - { bAtomActive = false; } - - void PixelGameEngine::EngineThread() - { - // Allow platform to do stuff here if needed, since its now in the - // context of this thread - if (platform->ThreadStartUp() == olc::FAIL) return; - - // Do engine context specific initialisation - olc_PrepareEngine(); - - // Create user resources as part of this thread - for (auto& ext : vExtensions) ext->OnBeforeUserCreate(); - if (!OnUserCreate()) bAtomActive = false; - for (auto& ext : vExtensions) ext->OnAfterUserCreate(); - - while (bAtomActive) - { - // Run as fast as possible - while (bAtomActive) { olc_CoreUpdate(); } - - // Allow the user to free resources if they have overrided the destroy function - if (!OnUserDestroy()) - { - // User denied destroy for some reason, so continue running - bAtomActive = true; - } - } - - platform->ThreadCleanUp(); - } - - void PixelGameEngine::olc_PrepareEngine() - { - // Start OpenGL, the context is owned by the game thread - if (platform->CreateGraphics(bFullScreen, bEnableVSYNC, vViewPos, vViewSize) == olc::FAIL) return; - - // Construct default font sheet - olc_ConstructFontSheet(); - - // Create Primary Layer "0" - CreateLayer(); - vLayers[0].bUpdate = true; - vLayers[0].bShow = true; - SetDrawTarget(nullptr); - - m_tp1 = std::chrono::system_clock::now(); - m_tp2 = std::chrono::system_clock::now(); - } - - - void PixelGameEngine::olc_CoreUpdate() - { - // Handle Timing - m_tp2 = std::chrono::system_clock::now(); - std::chrono::duration elapsedTime = m_tp2 - m_tp1; - m_tp1 = m_tp2; - - // Our time per frame coefficient - float fElapsedTime = elapsedTime.count(); - fLastElapsed = fElapsedTime; - - // Some platforms will need to check for events - platform->HandleSystemEvent(); - - // Compare hardware input states from previous frame - auto ScanHardware = [&](HWButton* pKeys, bool* pStateOld, bool* pStateNew, uint32_t nKeyCount) - { - for (uint32_t i = 0; i < nKeyCount; i++) - { - pKeys[i].bPressed = false; - pKeys[i].bReleased = false; - if (pStateNew[i] != pStateOld[i]) - { - if (pStateNew[i]) - { - pKeys[i].bPressed = !pKeys[i].bHeld; - pKeys[i].bHeld = true; - } - else - { - pKeys[i].bReleased = true; - pKeys[i].bHeld = false; - } - } - pStateOld[i] = pStateNew[i]; - } - }; - - ScanHardware(pKeyboardState, pKeyOldState, pKeyNewState, 256); - ScanHardware(pMouseState, pMouseOldState, pMouseNewState, nMouseButtons); - - // Cache mouse coordinates so they remain consistent during frame - vMousePos = vMousePosCache; - nMouseWheelDelta = nMouseWheelDeltaCache; - nMouseWheelDeltaCache = 0; - - // renderer->ClearBuffer(olc::BLACK, true); - - // Handle Frame Update - for (auto& ext : vExtensions) ext->OnBeforeUserUpdate(fElapsedTime); - if (!OnUserUpdate(fElapsedTime)) bAtomActive = false; - for (auto& ext : vExtensions) ext->OnAfterUserUpdate(fElapsedTime); - - // Display Frame - renderer->UpdateViewport(vViewPos, vViewSize); - renderer->ClearBuffer(olc::BLACK, true); - - // Layer 0 must always exist - vLayers[0].bUpdate = true; - vLayers[0].bShow = true; - SetDecalMode(DecalMode::NORMAL); - renderer->PrepareDrawing(); - - for (auto layer = vLayers.rbegin(); layer != vLayers.rend(); ++layer) - { - if (layer->bShow) - { - if (layer->funcHook == nullptr) - { - renderer->ApplyTexture(layer->pDrawTarget.Decal()->id); - if (layer->bUpdate) - { - layer->pDrawTarget.Decal()->Update(); - layer->bUpdate = false; - } - - renderer->DrawLayerQuad(layer->vOffset, layer->vScale, layer->tint); - - // Display Decals in order for this layer - for (auto& decal : layer->vecDecalInstance) - renderer->DrawDecal(decal); - layer->vecDecalInstance.clear(); - } - else - { - // Mwa ha ha.... Have Fun!!! - layer->funcHook(); - } - } - } - - // Present Graphics to screen - renderer->DisplayFrame(); - - // Update Title Bar - fFrameTimer += fElapsedTime; - nFrameCount++; - if (fFrameTimer >= 1.0f) - { - nLastFPS = nFrameCount; - fFrameTimer -= 1.0f; - std::string sTitle = sAppName + " - FPS: " + std::to_string(nFrameCount); - platform->SetWindowTitle(sTitle); - nFrameCount = 0; - } - } - - void PixelGameEngine::olc_ConstructFontSheet() - { - std::string data; - data += "?Q`0001oOch0o01o@F40o000000000"; - data += "O000000nOT0063Qo4d8>?7a14Gno94AA4gno94AaOT0>o3`oO400o7QN00000400"; - data += "Of80001oOg<7O7moBGT7O7lABET024@aBEd714AiOdl717a_=TH013Q>00000000"; - data += "720D000V?V5oB3Q_HdUoE7a9@DdDE4A9@DmoE4A;Hg]oM4Aj8S4D84@`00000000"; - data += "OaPT1000Oa`^13P1@AI[?g`1@A=[OdAoHgljA4Ao?WlBA7l1710007l100000000"; - data += "ObM6000oOfMV?3QoBDD`O7a0BDDH@5A0BDD<@5A0BGeVO5ao@CQR?5Po00000000"; - data += "Oc``000?Ogij70PO2D]??0Ph2DUM@7i`2DTg@7lh2GUj?0TO0C1870T?00000000"; - data += "70<4001o?P<7?1QoHg43O;`h@GT0@:@LB@d0>:@hN@L0@?aoN@<0O7ao0000?000"; - data += "OcH0001SOglLA7mg24TnK7ln24US>0PL24U140PnOgl0>7QgOcH0K71S0000A000"; - data += "00H00000@Dm1S007@DUSg00?OdTnH7YhOfTL<7Yh@Cl0700?@Ah0300700000000"; - data += "<008001QL00ZA41a@6HnI<1i@FHLM81M@@0LG81?O`0nC?Y7?`0ZA7Y300080000"; - data += "O`082000Oh0827mo6>Hn?Wmo?6HnMb11MP08@C11H`08@FP0@@0004@000000000"; - data += "00P00001Oab00003OcKP0006@6=PMgl<@440MglH@000000`@000001P00000000"; - data += "Ob@8@@00Ob@8@Ga13R@8Mga172@8?PAo3R@827QoOb@820@0O`0007`0000007P0"; - data += "O`000P08Od400g`<3V=P0G`673IP0`@3>1`00P@6O`P00g`SetPixel(px, py, olc::Pixel(k, k, k, k)); - if (++py == 48) { px++; py = 0; } - } - } - - fontDecal = new olc::Decal(fontSprite); - - constexpr std::array vSpacing = { { - 0x03,0x25,0x16,0x08,0x07,0x08,0x08,0x04,0x15,0x15,0x08,0x07,0x15,0x07,0x24,0x08, - 0x08,0x17,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x24,0x15,0x06,0x07,0x16,0x17, - 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x17,0x08,0x08,0x17,0x08,0x08,0x08, - 0x08,0x08,0x08,0x08,0x17,0x08,0x08,0x08,0x08,0x17,0x08,0x15,0x08,0x15,0x08,0x08, - 0x24,0x18,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x33,0x17,0x17,0x33,0x18,0x17,0x17, - 0x17,0x17,0x17,0x17,0x07,0x17,0x17,0x18,0x18,0x17,0x17,0x07,0x33,0x07,0x08,0x00, } }; - - for (auto c : vSpacing) vFontSpacing.push_back({ c >> 4, c & 15 }); - - } - - void PixelGameEngine::pgex_Register(olc::PGEX* pgex) - { - if (std::find(vExtensions.begin(), vExtensions.end(), pgex) == vExtensions.end()) - vExtensions.push_back(pgex); - } - - - PGEX::PGEX(bool bHook) { if(bHook) pge->pgex_Register(this); } - void PGEX::OnBeforeUserCreate() {} - void PGEX::OnAfterUserCreate() {} - void PGEX::OnBeforeUserUpdate(float& fElapsedTime) {} - void PGEX::OnAfterUserUpdate(float fElapsedTime) {} - - // Need a couple of statics as these are singleton instances - // read from multiple locations - std::atomic PixelGameEngine::bAtomActive{ false }; - olc::PixelGameEngine* olc::PGEX::pge = nullptr; - olc::PixelGameEngine* olc::Platform::ptrPGE = nullptr; - olc::PixelGameEngine* olc::Renderer::ptrPGE = nullptr; - std::unique_ptr olc::Sprite::loader = nullptr; -}; -#pragma endregion - -// O------------------------------------------------------------------------------O -// | olcPixelGameEngine Renderers - the draw-y bits | -// O------------------------------------------------------------------------------O - -#if !defined(OLC_PGE_HEADLESS) - -#pragma region renderer_ogl10 -// O------------------------------------------------------------------------------O -// | START RENDERER: OpenGL 1.0 (the original, the best...) | -// O------------------------------------------------------------------------------O -#if defined(OLC_GFX_OPENGL10) - -#if defined(OLC_PLATFORM_WINAPI) - #include - #include - #if !defined(__MINGW32__) - #pragma comment(lib, "Dwmapi.lib") - #endif - typedef BOOL(WINAPI wglSwapInterval_t) (int interval); - static wglSwapInterval_t* wglSwapInterval = nullptr; - typedef HDC glDeviceContext_t; - typedef HGLRC glRenderContext_t; -#endif - -#if defined(__linux__) || defined(__FreeBSD__) - #include -#endif - -#if defined(OLC_PLATFORM_X11) - namespace X11 - { - #include - } - typedef int(glSwapInterval_t)(X11::Display* dpy, X11::GLXDrawable drawable, int interval); - static glSwapInterval_t* glSwapIntervalEXT; - typedef X11::GLXContext glDeviceContext_t; - typedef X11::GLXContext glRenderContext_t; -#endif - -#if defined(__APPLE__) - #define GL_SILENCE_DEPRECATION - #include - #include - #include -#endif - -namespace olc -{ - class Renderer_OGL10 : public olc::Renderer - { - private: -#if defined(OLC_PLATFORM_GLUT) - bool mFullScreen = false; -#else - glDeviceContext_t glDeviceContext = 0; - glRenderContext_t glRenderContext = 0; -#endif - - bool bSync = false; - olc::DecalMode nDecalMode = olc::DecalMode(-1); // Thanks Gusgo & Bispoo - -#if defined(OLC_PLATFORM_X11) - X11::Display* olc_Display = nullptr; - X11::Window* olc_Window = nullptr; - X11::XVisualInfo* olc_VisualInfo = nullptr; -#endif - - public: - void PrepareDevice() override - { -#if defined(OLC_PLATFORM_GLUT) - //glutInit has to be called with main() arguments, make fake ones - int argc = 0; - char* argv[1] = { (char*)"" }; - glutInit(&argc, argv); - glutInitWindowPosition(0, 0); - glutInitWindowSize(512, 512); - glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA); - // Creates the window and the OpenGL context for it - glutCreateWindow("OneLoneCoder.com - Pixel Game Engine"); - glEnable(GL_TEXTURE_2D); // Turn on texturing - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); -#endif - } - - olc::rcode CreateDevice(std::vector params, bool bFullScreen, bool bVSYNC) override - { -#if defined(OLC_PLATFORM_WINAPI) - // Create Device Context - glDeviceContext = GetDC((HWND)(params[0])); - PIXELFORMATDESCRIPTOR pfd = - { - sizeof(PIXELFORMATDESCRIPTOR), 1, - PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, - PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - PFD_MAIN_PLANE, 0, 0, 0, 0 - }; - - int pf = 0; - if (!(pf = ChoosePixelFormat(glDeviceContext, &pfd))) return olc::FAIL; - SetPixelFormat(glDeviceContext, pf, &pfd); - - if (!(glRenderContext = wglCreateContext(glDeviceContext))) return olc::FAIL; - wglMakeCurrent(glDeviceContext, glRenderContext); - - // Remove Frame cap - wglSwapInterval = (wglSwapInterval_t*)wglGetProcAddress("wglSwapIntervalEXT"); - if (wglSwapInterval && !bVSYNC) wglSwapInterval(0); - bSync = bVSYNC; -#endif - -#if defined(OLC_PLATFORM_X11) - using namespace X11; - // Linux has tighter coupling between OpenGL and X11, so we store - // various "platform" handles in the renderer - olc_Display = (X11::Display*)(params[0]); - olc_Window = (X11::Window*)(params[1]); - olc_VisualInfo = (X11::XVisualInfo*)(params[2]); - - glDeviceContext = glXCreateContext(olc_Display, olc_VisualInfo, nullptr, GL_TRUE); - glXMakeCurrent(olc_Display, *olc_Window, glDeviceContext); - - XWindowAttributes gwa; - XGetWindowAttributes(olc_Display, *olc_Window, &gwa); - glViewport(0, 0, gwa.width, gwa.height); - - glSwapIntervalEXT = nullptr; - glSwapIntervalEXT = (glSwapInterval_t*)glXGetProcAddress((unsigned char*)"glXSwapIntervalEXT"); - - if (glSwapIntervalEXT == nullptr && !bVSYNC) - { - printf("NOTE: Could not disable VSYNC, glXSwapIntervalEXT() was not found!\n"); - printf(" Don't worry though, things will still work, it's just the\n"); - printf(" frame rate will be capped to your monitors refresh rate - javidx9\n"); - } - - if (glSwapIntervalEXT != nullptr && !bVSYNC) - glSwapIntervalEXT(olc_Display, *olc_Window, 0); -#endif - -#if defined(OLC_PLATFORM_GLUT) - mFullScreen = bFullScreen; - if (!bVSYNC) - { -#if defined(__APPLE__) - GLint sync = 0; - CGLContextObj ctx = CGLGetCurrentContext(); - if (ctx) CGLSetParameter(ctx, kCGLCPSwapInterval, &sync); -#endif - } -#else - glEnable(GL_TEXTURE_2D); // Turn on texturing - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); -#endif - return olc::rcode::OK; - } - - olc::rcode DestroyDevice() override - { -#if defined(OLC_PLATFORM_WINAPI) - wglDeleteContext(glRenderContext); -#endif - -#if defined(OLC_PLATFORM_X11) - glXMakeCurrent(olc_Display, None, NULL); - glXDestroyContext(olc_Display, glDeviceContext); -#endif - -#if defined(OLC_PLATFORM_GLUT) - glutDestroyWindow(glutGetWindow()); -#endif - return olc::rcode::OK; - } - - void DisplayFrame() override - { -#if defined(OLC_PLATFORM_WINAPI) - SwapBuffers(glDeviceContext); - if (bSync) DwmFlush(); // Woooohooooooo!!!! SMOOOOOOOTH! -#endif - -#if defined(OLC_PLATFORM_X11) - X11::glXSwapBuffers(olc_Display, *olc_Window); -#endif - -#if defined(OLC_PLATFORM_GLUT) - glutSwapBuffers(); -#endif - } - - void PrepareDrawing() override - { - glEnable(GL_BLEND); - nDecalMode = DecalMode::NORMAL; - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - - void SetDecalMode(const olc::DecalMode& mode) - { - if (mode != nDecalMode) - { - switch (mode) - { - case olc::DecalMode::NORMAL: - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - break; - case olc::DecalMode::ADDITIVE: - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - break; - case olc::DecalMode::MULTIPLICATIVE: - glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); - break; - case olc::DecalMode::STENCIL: - glBlendFunc(GL_ZERO, GL_SRC_ALPHA); - break; - case olc::DecalMode::ILLUMINATE: - glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); - break; - case olc::DecalMode::WIREFRAME: - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - break; - } - - nDecalMode = mode; - } - } - - void DrawLayerQuad(const olc::vf2d& offset, const olc::vf2d& scale, const olc::Pixel tint) override - { - glBegin(GL_QUADS); - glColor4ub(tint.r, tint.g, tint.b, tint.a); - glTexCoord2f(0.0f * scale.x + offset.x, 1.0f * scale.y + offset.y); - glVertex3f(-1.0f /*+ vSubPixelOffset.x*/, -1.0f /*+ vSubPixelOffset.y*/, 0.0f); - glTexCoord2f(0.0f * scale.x + offset.x, 0.0f * scale.y + offset.y); - glVertex3f(-1.0f /*+ vSubPixelOffset.x*/, 1.0f /*+ vSubPixelOffset.y*/, 0.0f); - glTexCoord2f(1.0f * scale.x + offset.x, 0.0f * scale.y + offset.y); - glVertex3f(1.0f /*+ vSubPixelOffset.x*/, 1.0f /*+ vSubPixelOffset.y*/, 0.0f); - glTexCoord2f(1.0f * scale.x + offset.x, 1.0f * scale.y + offset.y); - glVertex3f(1.0f /*+ vSubPixelOffset.x*/, -1.0f /*+ vSubPixelOffset.y*/, 0.0f); - glEnd(); - } - - void DrawDecal(const olc::DecalInstance& decal) override - { - SetDecalMode(decal.mode); - - if (decal.decal == nullptr) - glBindTexture(GL_TEXTURE_2D, 0); - else - glBindTexture(GL_TEXTURE_2D, decal.decal->id); - - if (nDecalMode == DecalMode::WIREFRAME) - glBegin(GL_LINE_LOOP); - else - glBegin(GL_TRIANGLE_FAN); - - for (uint32_t n = 0; n < decal.points; n++) - { - glColor4ub(decal.tint[n].r, decal.tint[n].g, decal.tint[n].b, decal.tint[n].a); - glTexCoord4f(decal.uv[n].x, decal.uv[n].y, 0.0f, decal.w[n]); - glVertex2f(decal.pos[n].x, decal.pos[n].y); - } - glEnd(); - } - - uint32_t CreateTexture(const uint32_t width, const uint32_t height, const bool filtered, const bool clamp) override - { - UNUSED(width); - UNUSED(height); - uint32_t id = 0; - glGenTextures(1, &id); - glBindTexture(GL_TEXTURE_2D, id); - if (filtered) - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } - else - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } - - if (clamp) - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - } - else - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - } - - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - return id; - } - - uint32_t DeleteTexture(const uint32_t id) override - { - glDeleteTextures(1, &id); - return id; - } - - void UpdateTexture(uint32_t id, olc::Sprite* spr) override - { - UNUSED(id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, spr->width, spr->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spr->GetData()); - } - - void ReadTexture(uint32_t id, olc::Sprite* spr) override - { - glReadPixels(0, 0, spr->width, spr->height, GL_RGBA, GL_UNSIGNED_BYTE, spr->GetData()); - } - - void ApplyTexture(uint32_t id) override - { - glBindTexture(GL_TEXTURE_2D, id); - } - - void ClearBuffer(olc::Pixel p, bool bDepth) override - { - glClearColor(float(p.r) / 255.0f, float(p.g) / 255.0f, float(p.b) / 255.0f, float(p.a) / 255.0f); - glClear(GL_COLOR_BUFFER_BIT); - if (bDepth) glClear(GL_DEPTH_BUFFER_BIT); - } - - void UpdateViewport(const olc::vi2d& pos, const olc::vi2d& size) override - { - glViewport(pos.x, pos.y, size.x, size.y); - } - }; -} -#endif -// O------------------------------------------------------------------------------O -// | END RENDERER: OpenGL 1.0 (the original, the best...) | -// O------------------------------------------------------------------------------O -#pragma endregion - -#pragma region renderer_ogl33 -// O------------------------------------------------------------------------------O -// | START RENDERER: OpenGL 3.3 (3.0 es) (sh-sh-sh-shaders....) | -// O------------------------------------------------------------------------------O -#if defined(OLC_GFX_OPENGL33) - -#if defined(OLC_PLATFORM_WINAPI) - #include - #include - #if !defined(__MINGW32__) - #pragma comment(lib, "Dwmapi.lib") - #endif - typedef void __stdcall locSwapInterval_t(GLsizei n); - typedef HDC glDeviceContext_t; - typedef HGLRC glRenderContext_t; - #define CALLSTYLE __stdcall - #define OGL_LOAD(t, n) (t*)wglGetProcAddress(#n) -#endif - -#if defined(__linux__) || defined(__FreeBSD__) - #include -#endif - -#if defined(OLC_PLATFORM_X11) - namespace X11 - { - #include - } - typedef int(locSwapInterval_t)(X11::Display* dpy, X11::GLXDrawable drawable, int interval); - typedef X11::GLXContext glDeviceContext_t; - typedef X11::GLXContext glRenderContext_t; - #define CALLSTYLE - #define OGL_LOAD(t, n) (t*)glXGetProcAddress((unsigned char*)#n); -#endif - -#if defined(__APPLE__) - #define GL_SILENCE_DEPRECATION - #include - #include - #include -#endif - -#if defined(OLC_PLATFORM_EMSCRIPTEN) - #include - #include - #define GL_GLEXT_PROTOTYPES - #include - #include - #define CALLSTYLE - typedef EGLBoolean(locSwapInterval_t)(EGLDisplay display, EGLint interval); - #define GL_CLAMP GL_CLAMP_TO_EDGE - #define OGL_LOAD(t, n) n; -#endif - -namespace olc -{ - typedef char GLchar; - typedef ptrdiff_t GLsizeiptr; - typedef GLuint CALLSTYLE locCreateShader_t(GLenum type); - typedef GLuint CALLSTYLE locCreateProgram_t(void); - typedef void CALLSTYLE locDeleteShader_t(GLuint shader); -#if defined(OLC_PLATFORM_EMSCRIPTEN) - typedef void CALLSTYLE locShaderSource_t(GLuint shader, GLsizei count, const GLchar* const* string, const GLint* length); -#else - typedef void CALLSTYLE locShaderSource_t(GLuint shader, GLsizei count, const GLchar** string, const GLint* length); -#endif - typedef void CALLSTYLE locCompileShader_t(GLuint shader); - typedef void CALLSTYLE locLinkProgram_t(GLuint program); - typedef void CALLSTYLE locDeleteProgram_t(GLuint program); - typedef void CALLSTYLE locAttachShader_t(GLuint program, GLuint shader); - typedef void CALLSTYLE locBindBuffer_t(GLenum target, GLuint buffer); - typedef void CALLSTYLE locBufferData_t(GLenum target, GLsizeiptr size, const void* data, GLenum usage); - typedef void CALLSTYLE locGenBuffers_t(GLsizei n, GLuint* buffers); - typedef void CALLSTYLE locVertexAttribPointer_t(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer); - typedef void CALLSTYLE locEnableVertexAttribArray_t(GLuint index); - typedef void CALLSTYLE locUseProgram_t(GLuint program); - typedef void CALLSTYLE locBindVertexArray_t(GLuint array); - typedef void CALLSTYLE locGenVertexArrays_t(GLsizei n, GLuint* arrays); - typedef void CALLSTYLE locGetShaderInfoLog_t(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog); - - constexpr size_t OLC_MAX_VERTS = 128; - - class Renderer_OGL33 : public olc::Renderer - { - private: -#if defined(OLC_PLATFORM_EMSCRIPTEN) - EGLDisplay olc_Display; - EGLConfig olc_Config; - EGLContext olc_Context; - EGLSurface olc_Surface; -#endif - -#if defined(OLC_PLATFORM_GLUT) - bool mFullScreen = false; -#else - #if !defined(OLC_PLATFORM_EMSCRIPTEN) - glDeviceContext_t glDeviceContext = 0; - glRenderContext_t glRenderContext = 0; - #endif -#endif - bool bSync = false; - olc::DecalMode nDecalMode = olc::DecalMode(-1); // Thanks Gusgo & Bispoo -#if defined(OLC_PLATFORM_X11) - X11::Display* olc_Display = nullptr; - X11::Window* olc_Window = nullptr; - X11::XVisualInfo* olc_VisualInfo = nullptr; -#endif - - private: - locCreateShader_t* locCreateShader = nullptr; - locShaderSource_t* locShaderSource = nullptr; - locCompileShader_t* locCompileShader = nullptr; - locDeleteShader_t* locDeleteShader = nullptr; - locCreateProgram_t* locCreateProgram = nullptr; - locDeleteProgram_t* locDeleteProgram = nullptr; - locLinkProgram_t* locLinkProgram = nullptr; - locAttachShader_t* locAttachShader = nullptr; - locBindBuffer_t* locBindBuffer = nullptr; - locBufferData_t* locBufferData = nullptr; - locGenBuffers_t* locGenBuffers = nullptr; - locVertexAttribPointer_t* locVertexAttribPointer = nullptr; - locEnableVertexAttribArray_t* locEnableVertexAttribArray = nullptr; - locUseProgram_t* locUseProgram = nullptr; - locBindVertexArray_t* locBindVertexArray = nullptr; - locGenVertexArrays_t* locGenVertexArrays = nullptr; - locSwapInterval_t* locSwapInterval = nullptr; - locGetShaderInfoLog_t* locGetShaderInfoLog = nullptr; - - uint32_t m_nFS = 0; - uint32_t m_nVS = 0; - uint32_t m_nQuadShader = 0; - uint32_t m_vbQuad = 0; - uint32_t m_vaQuad = 0; - - struct locVertex - { - float pos[3]; - olc::vf2d tex; - olc::Pixel col; - }; - - locVertex pVertexMem[OLC_MAX_VERTS]; - - olc::Renderable rendBlankQuad; - - public: - void PrepareDevice() override - { -#if defined(OLC_PLATFORM_GLUT) - //glutInit has to be called with main() arguments, make fake ones - int argc = 0; - char* argv[1] = { (char*)"" }; - glutInit(&argc, argv); - glutInitWindowPosition(0, 0); - glutInitWindowSize(512, 512); - glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA); - // Creates the window and the OpenGL context for it - glutCreateWindow("OneLoneCoder.com - Pixel Game Engine"); - glEnable(GL_TEXTURE_2D); // Turn on texturing - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); -#endif - } - - olc::rcode CreateDevice(std::vector params, bool bFullScreen, bool bVSYNC) override - { - // Create OpenGL Context -#if defined(OLC_PLATFORM_WINAPI) - // Create Device Context - glDeviceContext = GetDC((HWND)(params[0])); - PIXELFORMATDESCRIPTOR pfd = - { - sizeof(PIXELFORMATDESCRIPTOR), 1, - PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, - PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - PFD_MAIN_PLANE, 0, 0, 0, 0 - }; - - int pf = 0; - if (!(pf = ChoosePixelFormat(glDeviceContext, &pfd))) return olc::FAIL; - SetPixelFormat(glDeviceContext, pf, &pfd); - - if (!(glRenderContext = wglCreateContext(glDeviceContext))) return olc::FAIL; - wglMakeCurrent(glDeviceContext, glRenderContext); - - // Set Vertical Sync - locSwapInterval = OGL_LOAD(locSwapInterval_t, "wglSwapIntervalEXT"); - if (locSwapInterval && !bVSYNC) locSwapInterval(0); - bSync = bVSYNC; -#endif - -#if defined(OLC_PLATFORM_X11) - using namespace X11; - // Linux has tighter coupling between OpenGL and X11, so we store - // various "platform" handles in the renderer - olc_Display = (X11::Display*)(params[0]); - olc_Window = (X11::Window*)(params[1]); - olc_VisualInfo = (X11::XVisualInfo*)(params[2]); - - glDeviceContext = glXCreateContext(olc_Display, olc_VisualInfo, nullptr, GL_TRUE); - glXMakeCurrent(olc_Display, *olc_Window, glDeviceContext); - - XWindowAttributes gwa; - XGetWindowAttributes(olc_Display, *olc_Window, &gwa); - glViewport(0, 0, gwa.width, gwa.height); - - locSwapInterval = OGL_LOAD(locSwapInterval_t, "glXSwapIntervalEXT"); - - if (locSwapInterval == nullptr && !bVSYNC) - { - printf("NOTE: Could not disable VSYNC, glXSwapIntervalEXT() was not found!\n"); - printf(" Don't worry though, things will still work, it's just the\n"); - printf(" frame rate will be capped to your monitors refresh rate - javidx9\n"); - } - - if (locSwapInterval != nullptr && !bVSYNC) - locSwapInterval(olc_Display, *olc_Window, 0); -#endif - -#if defined(OLC_PLATFORM_EMSCRIPTEN) - EGLint const attribute_list[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_NONE }; - EGLint const context_config[] = { EGL_CONTEXT_CLIENT_VERSION , 2, EGL_NONE }; - EGLint num_config; - - olc_Display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - eglInitialize(olc_Display, nullptr, nullptr); - eglChooseConfig(olc_Display, attribute_list, &olc_Config, 1, &num_config); - - /* create an EGL rendering context */ - olc_Context = eglCreateContext(olc_Display, olc_Config, EGL_NO_CONTEXT, context_config); - olc_Surface = eglCreateWindowSurface(olc_Display, olc_Config, NULL, nullptr); - eglMakeCurrent(olc_Display, olc_Surface, olc_Surface, olc_Context); - //eglSwapInterval is currently a NOP, plement anyways in case it becomes supported - locSwapInterval = &eglSwapInterval; - locSwapInterval(olc_Display, bVSYNC ? 1 : 0); -#endif - -#if defined(OLC_PLATFORM_GLUT) - mFullScreen = bFullScreen; - if (!bVSYNC) - { -#if defined(__APPLE__) - GLint sync = 0; - CGLContextObj ctx = CGLGetCurrentContext(); - if (ctx) CGLSetParameter(ctx, kCGLCPSwapInterval, &sync); -#endif - } -#else - #if !defined(OLC_PLATFORM_EMSCRIPTEN) - glEnable(GL_TEXTURE_2D); // Turn on texturing - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); - #endif -#endif - // Load External OpenGL Functions - locCreateShader = OGL_LOAD(locCreateShader_t, glCreateShader); - locCompileShader = OGL_LOAD(locCompileShader_t, glCompileShader); - locShaderSource = OGL_LOAD(locShaderSource_t, glShaderSource); - locDeleteShader = OGL_LOAD(locDeleteShader_t, glDeleteShader); - locCreateProgram = OGL_LOAD(locCreateProgram_t, glCreateProgram); - locDeleteProgram = OGL_LOAD(locDeleteProgram_t, glDeleteProgram); - locLinkProgram = OGL_LOAD(locLinkProgram_t, glLinkProgram); - locAttachShader = OGL_LOAD(locAttachShader_t, glAttachShader); - locBindBuffer = OGL_LOAD(locBindBuffer_t, glBindBuffer); - locBufferData = OGL_LOAD(locBufferData_t, glBufferData); - locGenBuffers = OGL_LOAD(locGenBuffers_t, glGenBuffers); - locVertexAttribPointer = OGL_LOAD(locVertexAttribPointer_t, glVertexAttribPointer); - locEnableVertexAttribArray = OGL_LOAD(locEnableVertexAttribArray_t, glEnableVertexAttribArray); - locUseProgram = OGL_LOAD(locUseProgram_t, glUseProgram); - locGetShaderInfoLog = OGL_LOAD(locGetShaderInfoLog_t, glGetShaderInfoLog); -#if !defined(OLC_PLATFORM_EMSCRIPTEN) - locBindVertexArray = OGL_LOAD(locBindVertexArray_t, glBindVertexArray); - locGenVertexArrays = OGL_LOAD(locGenVertexArrays_t, glGenVertexArrays); -#else - locBindVertexArray = glBindVertexArrayOES; - locGenVertexArrays = glGenVertexArraysOES; -#endif - - // Load & Compile Quad Shader - assumes no errors - m_nFS = locCreateShader(0x8B30); - const GLchar* strFS = -#if defined(__arm__) || defined(OLC_PLATFORM_EMSCRIPTEN) - "#version 300 es\n" - "precision mediump float;" -#else - "#version 330 core\n" -#endif - "out vec4 pixel;\n""in vec2 oTex;\n" - "in vec4 oCol;\n""uniform sampler2D sprTex;\n""void main(){pixel = texture(sprTex, oTex) * oCol;}"; - locShaderSource(m_nFS, 1, &strFS, NULL); - locCompileShader(m_nFS); - - m_nVS = locCreateShader(0x8B31); - const GLchar* strVS = -#if defined(__arm__) || defined(OLC_PLATFORM_EMSCRIPTEN) - "#version 300 es\n" - "precision mediump float;" -#else - "#version 330 core\n" -#endif - "layout(location = 0) in vec3 aPos;\n""layout(location = 1) in vec2 aTex;\n" - "layout(location = 2) in vec4 aCol;\n""out vec2 oTex;\n""out vec4 oCol;\n" - "void main(){ float p = 1.0 / aPos.z; gl_Position = p * vec4(aPos.x, aPos.y, 0.0, 1.0); oTex = p * aTex; oCol = aCol;}"; - locShaderSource(m_nVS, 1, &strVS, NULL); - locCompileShader(m_nVS); - - m_nQuadShader = locCreateProgram(); - locAttachShader(m_nQuadShader, m_nFS); - locAttachShader(m_nQuadShader, m_nVS); - locLinkProgram(m_nQuadShader); - - // Create Quad - locGenBuffers(1, &m_vbQuad); - locGenVertexArrays(1, &m_vaQuad); - locBindVertexArray(m_vaQuad); - locBindBuffer(0x8892, m_vbQuad); - - locVertex verts[OLC_MAX_VERTS]; - locBufferData(0x8892, sizeof(locVertex) * OLC_MAX_VERTS, verts, 0x88E0); - locVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(locVertex), 0); locEnableVertexAttribArray(0); - locVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(locVertex), (void*)(3 * sizeof(float))); locEnableVertexAttribArray(1); - locVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(locVertex), (void*)(5 * sizeof(float))); locEnableVertexAttribArray(2); - locBindBuffer(0x8892, 0); - locBindVertexArray(0); - - // Create blank texture for spriteless decals - rendBlankQuad.Create(1, 1); - rendBlankQuad.Sprite()->GetData()[0] = olc::WHITE; - rendBlankQuad.Decal()->Update(); - return olc::rcode::OK; - } - - olc::rcode DestroyDevice() override - { -#if defined(OLC_PLATFORM_WINAPI) - wglDeleteContext(glRenderContext); -#endif - -#if defined(OLC_PLATFORM_X11) - glXMakeCurrent(olc_Display, None, NULL); - glXDestroyContext(olc_Display, glDeviceContext); -#endif - -#if defined(OLC_PLATFORM_GLUT) - glutDestroyWindow(glutGetWindow()); -#endif - -#if defined(OLC_PLATFORM_EMSCRIPTEN) - eglMakeCurrent(olc_Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglDestroyContext(olc_Display, olc_Context); - eglDestroySurface(olc_Display, olc_Surface); - eglTerminate(olc_Display); - olc_Display = EGL_NO_DISPLAY; - olc_Surface = EGL_NO_SURFACE; - olc_Context = EGL_NO_CONTEXT; -#endif - return olc::rcode::OK; - } - - void DisplayFrame() override - { -#if defined(OLC_PLATFORM_WINAPI) - SwapBuffers(glDeviceContext); - if (bSync) DwmFlush(); // Woooohooooooo!!!! SMOOOOOOOTH! -#endif - -#if defined(OLC_PLATFORM_X11) - X11::glXSwapBuffers(olc_Display, *olc_Window); -#endif - -#if defined(OLC_PLATFORM_GLUT) - glutSwapBuffers(); -#endif - -#if defined(OLC_PLATFORM_EMSCRIPTEN) - eglSwapBuffers(olc_Display, olc_Surface); -#endif - } - - void PrepareDrawing() override - { - glEnable(GL_BLEND); - nDecalMode = DecalMode::NORMAL; - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - locUseProgram(m_nQuadShader); - locBindVertexArray(m_vaQuad); - -#if defined(OLC_PLATFORM_EMSCRIPTEN) - locVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(locVertex), 0); locEnableVertexAttribArray(0); - locVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(locVertex), (void*)(3 * sizeof(float))); locEnableVertexAttribArray(1); - locVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(locVertex), (void*)(5 * sizeof(float))); locEnableVertexAttribArray(2); -#endif - } - - void SetDecalMode(const olc::DecalMode& mode) override - { - if (mode != nDecalMode) - { - switch (mode) - { - case olc::DecalMode::NORMAL: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; - case olc::DecalMode::ADDITIVE: glBlendFunc(GL_SRC_ALPHA, GL_ONE); break; - case olc::DecalMode::MULTIPLICATIVE: glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); break; - case olc::DecalMode::STENCIL: glBlendFunc(GL_ZERO, GL_SRC_ALPHA); break; - case olc::DecalMode::ILLUMINATE: glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); break; - case olc::DecalMode::WIREFRAME: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; - } - - nDecalMode = mode; - } - } - - void DrawLayerQuad(const olc::vf2d& offset, const olc::vf2d& scale, const olc::Pixel tint) override - { - locBindBuffer(0x8892, m_vbQuad); - locVertex verts[4] = { - {{-1.0f, -1.0f, 1.0}, {0.0f * scale.x + offset.x, 1.0f * scale.y + offset.y}, tint}, - {{+1.0f, -1.0f, 1.0}, {1.0f * scale.x + offset.x, 1.0f * scale.y + offset.y}, tint}, - {{-1.0f, +1.0f, 1.0}, {0.0f * scale.x + offset.x, 0.0f * scale.y + offset.y}, tint}, - {{+1.0f, +1.0f, 1.0}, {1.0f * scale.x + offset.x, 0.0f * scale.y + offset.y}, tint}, - }; - - locBufferData(0x8892, sizeof(locVertex) * 4, verts, 0x88E0); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - } - - void DrawDecal(const olc::DecalInstance& decal) override - { - SetDecalMode(decal.mode); - if (decal.decal == nullptr) - glBindTexture(GL_TEXTURE_2D, rendBlankQuad.Decal()->id); - else - glBindTexture(GL_TEXTURE_2D, decal.decal->id); - - locBindBuffer(0x8892, m_vbQuad); - - for (uint32_t i = 0; i < decal.points; i++) - pVertexMem[i] = { { decal.pos[i].x, decal.pos[i].y, decal.w[i] }, { decal.uv[i].x, decal.uv[i].y }, decal.tint[i] }; - - locBufferData(0x8892, sizeof(locVertex) * decal.points, pVertexMem, 0x88E0); - - if (nDecalMode == DecalMode::WIREFRAME) - glDrawArrays(GL_LINE_LOOP, 0, decal.points); - else - glDrawArrays(GL_TRIANGLE_FAN, 0, decal.points); - } - - uint32_t CreateTexture(const uint32_t width, const uint32_t height, const bool filtered, const bool clamp) override - { - UNUSED(width); - UNUSED(height); - uint32_t id = 0; - glGenTextures(1, &id); - glBindTexture(GL_TEXTURE_2D, id); - - if (filtered) - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } - else - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } - - if (clamp) - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - } - else - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - } -#if !defined(OLC_PLATFORM_EMSCRIPTEN) - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); -#endif - return id; - } - - uint32_t DeleteTexture(const uint32_t id) override - { - glDeleteTextures(1, &id); - return id; - } - - void UpdateTexture(uint32_t id, olc::Sprite* spr) override - { - UNUSED(id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, spr->width, spr->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spr->GetData()); - } - - void ReadTexture(uint32_t id, olc::Sprite* spr) override - { - glReadPixels(0, 0, spr->width, spr->height, GL_RGBA, GL_UNSIGNED_BYTE, spr->GetData()); - } - - void ApplyTexture(uint32_t id) override - { - glBindTexture(GL_TEXTURE_2D, id); - } - - void ClearBuffer(olc::Pixel p, bool bDepth) override - { - glClearColor(float(p.r) / 255.0f, float(p.g) / 255.0f, float(p.b) / 255.0f, float(p.a) / 255.0f); - glClear(GL_COLOR_BUFFER_BIT); - if (bDepth) glClear(GL_DEPTH_BUFFER_BIT); - } - - void UpdateViewport(const olc::vi2d& pos, const olc::vi2d& size) override - { - glViewport(pos.x, pos.y, size.x, size.y); - } - }; -} -#endif -// O------------------------------------------------------------------------------O -// | END RENDERER: OpenGL 3.3 (3.0 es) (sh-sh-sh-shaders....) | -// O------------------------------------------------------------------------------O -#pragma endregion - -// O------------------------------------------------------------------------------O -// | olcPixelGameEngine Image loaders | -// O------------------------------------------------------------------------------O - -#pragma region image_gdi -// O------------------------------------------------------------------------------O -// | START IMAGE LOADER: GDI+, Windows Only, always exists, a little slow | -// O------------------------------------------------------------------------------O -#if defined(OLC_IMAGE_GDI) - -#define min(a, b) ((a < b) ? a : b) -#define max(a, b) ((a > b) ? a : b) -#include -#include -#if defined(__MINGW32__) // Thanks Gusgo & Dandistine, but c'mon mingw!! wtf?! - #include -#else - #include -#endif -#include -#undef min -#undef max - -#if !defined(__MINGW32__) - #pragma comment(lib, "gdiplus.lib") - #pragma comment(lib, "Shlwapi.lib") -#endif - -namespace olc -{ - // Thanks @MaGetzUb for this, which allows sprites to be defined - // at construction, by initialising the GDI subsystem - static class GDIPlusStartup - { - public: - GDIPlusStartup() - { - Gdiplus::GdiplusStartupInput startupInput; - GdiplusStartup(&token, &startupInput, NULL); - } - - ULONG_PTR token; - - ~GDIPlusStartup() - { - // Well, MarcusTU thought this was important :D - Gdiplus::GdiplusShutdown(token); - } - } gdistartup; - - class ImageLoader_GDIPlus : public olc::ImageLoader - { - private: - std::wstring ConvertS2W(std::string s) - { -#ifdef __MINGW32__ - wchar_t* buffer = new wchar_t[s.length() + 1]; - mbstowcs(buffer, s.c_str(), s.length()); - buffer[s.length()] = L'\0'; -#else - int count = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0); - wchar_t* buffer = new wchar_t[count]; - MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, buffer, count); -#endif - std::wstring w(buffer); - delete[] buffer; - return w; - } - - public: - ImageLoader_GDIPlus() : ImageLoader() - {} - - olc::rcode LoadImageResource(olc::Sprite* spr, const std::string& sImageFile, olc::ResourcePack* pack) override - { - // clear out existing sprite - spr->pColData.clear(); - - // Open file - UNUSED(pack); - Gdiplus::Bitmap* bmp = nullptr; - if (pack != nullptr) - { - // Load sprite from input stream - ResourceBuffer rb = pack->GetFileBuffer(sImageFile); - bmp = Gdiplus::Bitmap::FromStream(SHCreateMemStream((BYTE*)rb.vMemory.data(), UINT(rb.vMemory.size()))); - } - else - { - // Check file exists - if (!_gfs::exists(sImageFile)) return olc::rcode::NO_FILE; - - // Load sprite from file - bmp = Gdiplus::Bitmap::FromFile(ConvertS2W(sImageFile).c_str()); - } - - if (bmp->GetLastStatus() != Gdiplus::Ok) return olc::rcode::FAIL; - spr->width = bmp->GetWidth(); - spr->height = bmp->GetHeight(); - - spr->pColData.resize(spr->width * spr->height); - - for (int y = 0; y < spr->height; y++) - for (int x = 0; x < spr->width; x++) - { - Gdiplus::Color c; - bmp->GetPixel(x, y, &c); - spr->SetPixel(x, y, olc::Pixel(c.GetRed(), c.GetGreen(), c.GetBlue(), c.GetAlpha())); - } - delete bmp; - return olc::rcode::OK; - } - - olc::rcode SaveImageResource(olc::Sprite* spr, const std::string& sImageFile) override - { - return olc::rcode::OK; - } - }; -} -#endif -// O------------------------------------------------------------------------------O -// | END IMAGE LOADER: GDI+ | -// O------------------------------------------------------------------------------O -#pragma endregion - -#pragma region image_libpng -// O------------------------------------------------------------------------------O -// | START IMAGE LOADER: libpng, default on linux, requires -lpng (libpng-dev) | -// O------------------------------------------------------------------------------O -#if defined(OLC_IMAGE_LIBPNG) -#include -namespace olc -{ - void pngReadStream(png_structp pngPtr, png_bytep data, png_size_t length) - { - png_voidp a = png_get_io_ptr(pngPtr); - ((std::istream*)a)->read((char*)data, length); - } - - class ImageLoader_LibPNG : public olc::ImageLoader - { - public: - ImageLoader_LibPNG() : ImageLoader() - {} - - olc::rcode LoadImageResource(olc::Sprite* spr, const std::string& sImageFile, olc::ResourcePack* pack) override - { - UNUSED(pack); - - // clear out existing sprite - spr->pColData.clear(); - - //////////////////////////////////////////////////////////////////////////// - // Use libpng, Thanks to Guillaume Cottenceau - // https://gist.github.com/niw/5963798 - // Also reading png from streams - // http://www.piko3d.net/tutorials/libpng-tutorial-loading-png-files-from-streams/ - png_structp png; - png_infop info; - - auto loadPNG = [&]() - { - png_read_info(png, info); - png_byte color_type; - png_byte bit_depth; - png_bytep* row_pointers; - spr->width = png_get_image_width(png, info); - spr->height = png_get_image_height(png, info); - color_type = png_get_color_type(png, info); - bit_depth = png_get_bit_depth(png, info); - if (bit_depth == 16) png_set_strip_16(png); - if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png); - if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png); - if (png_get_valid(png, info, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png); - if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE) - png_set_filler(png, 0xFF, PNG_FILLER_AFTER); - if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - png_set_gray_to_rgb(png); - png_read_update_info(png, info); - row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * spr->height); - for (int y = 0; y < spr->height; y++) { - row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png, info)); - } - png_read_image(png, row_pointers); - //////////////////////////////////////////////////////////////////////////// - // Create sprite array - spr->pColData.resize(spr->width * spr->height); - // Iterate through image rows, converting into sprite format - for (int y = 0; y < spr->height; y++) - { - png_bytep row = row_pointers[y]; - for (int x = 0; x < spr->width; x++) - { - png_bytep px = &(row[x * 4]); - spr->SetPixel(x, y, Pixel(px[0], px[1], px[2], px[3])); - } - } - - for (int y = 0; y < spr->height; y++) // Thanks maksym33 - free(row_pointers[y]); - free(row_pointers); - png_destroy_read_struct(&png, &info, nullptr); - }; - - png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png) goto fail_load; - - info = png_create_info_struct(png); - if (!info) goto fail_load; - - if (setjmp(png_jmpbuf(png))) goto fail_load; - - if (pack == nullptr) - { - FILE* f = fopen(sImageFile.c_str(), "rb"); - if (!f) return olc::rcode::NO_FILE; - png_init_io(png, f); - loadPNG(); - fclose(f); - } - else - { - ResourceBuffer rb = pack->GetFileBuffer(sImageFile); - std::istream is(&rb); - png_set_read_fn(png, (png_voidp)&is, pngReadStream); - loadPNG(); - } - - return olc::rcode::OK; - - fail_load: - spr->width = 0; - spr->height = 0; - spr->pColData.clear(); - return olc::rcode::FAIL; - } - - olc::rcode SaveImageResource(olc::Sprite* spr, const std::string& sImageFile) override - { - return olc::rcode::OK; - } - }; -} -#endif -// O------------------------------------------------------------------------------O -// | END IMAGE LOADER: | -// O------------------------------------------------------------------------------O -#pragma endregion - -#pragma region image_stb -// O------------------------------------------------------------------------------O -// | START IMAGE LOADER: stb_image.h, all systems, very fast | -// O------------------------------------------------------------------------------O -// Thanks to Sean Barrett - https://github.com/nothings/stb/blob/master/stb_image.h -// MIT License - Copyright(c) 2017 Sean Barrett - -// Note you need to download the above file into your project folder, and -// #define OLC_IMAGE_STB -// #define OLC_PGE_APPLICATION -// #include "olcPixelGameEngine.h" - -#if defined(OLC_IMAGE_STB) -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" -namespace olc -{ - class ImageLoader_STB : public olc::ImageLoader - { - public: - ImageLoader_STB() : ImageLoader() - {} - - olc::rcode LoadImageResource(olc::Sprite* spr, const std::string& sImageFile, olc::ResourcePack* pack) override - { - UNUSED(pack); - // clear out existing sprite - spr->pColData.clear(); - // Open file - stbi_uc* bytes = nullptr; - int w = 0, h = 0, cmp = 0; - if (pack != nullptr) - { - ResourceBuffer rb = pack->GetFileBuffer(sImageFile); - bytes = stbi_load_from_memory((unsigned char*)rb.vMemory.data(), rb.vMemory.size(), &w, &h, &cmp, 4); - } - else - { - // Check file exists - if (!_gfs::exists(sImageFile)) return olc::rcode::NO_FILE; - bytes = stbi_load(sImageFile.c_str(), &w, &h, &cmp, 4); - } - - if (!bytes) return olc::rcode::FAIL; - spr->width = w; spr->height = h; - spr->pColData.resize(spr->width * spr->height); - std::memcpy(spr->pColData.data(), bytes, spr->width * spr->height * 4); - delete[] bytes; - return olc::rcode::OK; - } - - olc::rcode SaveImageResource(olc::Sprite* spr, const std::string& sImageFile) override - { - return olc::rcode::OK; - } - }; -} -#endif -// O------------------------------------------------------------------------------O -// | START IMAGE LOADER: stb_image.h | -// O------------------------------------------------------------------------------O -#pragma endregion - -// O------------------------------------------------------------------------------O -// | olcPixelGameEngine Platforms | -// O------------------------------------------------------------------------------O - -#pragma region platform_windows -// O------------------------------------------------------------------------------O -// | START PLATFORM: MICROSOFT WINDOWS XP, VISTA, 7, 8, 10 | -// O------------------------------------------------------------------------------O -#if defined(OLC_PLATFORM_WINAPI) - -#if defined(_WIN32) && !defined(__MINGW32__) - #pragma comment(lib, "user32.lib") // Visual Studio Only - #pragma comment(lib, "gdi32.lib") // For other Windows Compilers please add - #pragma comment(lib, "opengl32.lib") // these libs to your linker input -#endif - -namespace olc -{ - class Platform_Windows : public olc::Platform - { - private: - HWND olc_hWnd = nullptr; - std::wstring wsAppName; - - std::wstring ConvertS2W(std::string s) - { -#ifdef __MINGW32__ - wchar_t* buffer = new wchar_t[s.length() + 1]; - mbstowcs(buffer, s.c_str(), s.length()); - buffer[s.length()] = L'\0'; -#else - int count = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0); - wchar_t* buffer = new wchar_t[count]; - MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, buffer, count); -#endif - std::wstring w(buffer); - delete[] buffer; - return w; - } - - public: - virtual olc::rcode ApplicationStartUp() override { return olc::rcode::OK; } - virtual olc::rcode ApplicationCleanUp() override { return olc::rcode::OK; } - virtual olc::rcode ThreadStartUp() override { return olc::rcode::OK; } - - virtual olc::rcode ThreadCleanUp() override - { - renderer->DestroyDevice(); - PostMessage(olc_hWnd, WM_DESTROY, 0, 0); - return olc::OK; - } - - virtual olc::rcode CreateGraphics(bool bFullScreen, bool bEnableVSYNC, const olc::vi2d& vViewPos, const olc::vi2d& vViewSize) override - { - if (renderer->CreateDevice({ olc_hWnd }, bFullScreen, bEnableVSYNC) == olc::rcode::OK) - { - renderer->UpdateViewport(vViewPos, vViewSize); - return olc::rcode::OK; - } - else - return olc::rcode::FAIL; - } - - virtual olc::rcode CreateWindowPane(const olc::vi2d& vWindowPos, olc::vi2d& vWindowSize, bool bFullScreen) override - { - WNDCLASS wc; - wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; - wc.hInstance = GetModuleHandle(nullptr); - wc.lpfnWndProc = olc_WindowEvent; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.lpszMenuName = nullptr; - wc.hbrBackground = nullptr; - wc.lpszClassName = olcT("OLC_PIXEL_GAME_ENGINE"); - RegisterClass(&wc); - - // Define window furniture - DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; - DWORD dwStyle = WS_CAPTION | WS_SYSMENU | WS_VISIBLE | WS_THICKFRAME; - - olc::vi2d vTopLeft = vWindowPos; - - // Handle Fullscreen - if (bFullScreen) - { - dwExStyle = 0; - dwStyle = WS_VISIBLE | WS_POPUP; - HMONITOR hmon = MonitorFromWindow(olc_hWnd, MONITOR_DEFAULTTONEAREST); - MONITORINFO mi = { sizeof(mi) }; - if (!GetMonitorInfo(hmon, &mi)) return olc::rcode::FAIL; - vWindowSize = { mi.rcMonitor.right, mi.rcMonitor.bottom }; - vTopLeft.x = 0; - vTopLeft.y = 0; - } - - // Keep client size as requested - RECT rWndRect = { 0, 0, vWindowSize.x, vWindowSize.y }; - AdjustWindowRectEx(&rWndRect, dwStyle, FALSE, dwExStyle); - int width = rWndRect.right - rWndRect.left; - int height = rWndRect.bottom - rWndRect.top; - - olc_hWnd = CreateWindowEx(dwExStyle, olcT("OLC_PIXEL_GAME_ENGINE"), olcT(""), dwStyle, - vTopLeft.x, vTopLeft.y, width, height, NULL, NULL, GetModuleHandle(nullptr), this); - - // Create Keyboard Mapping - mapKeys[0x00] = Key::NONE; - mapKeys[0x41] = Key::A; mapKeys[0x42] = Key::B; mapKeys[0x43] = Key::C; mapKeys[0x44] = Key::D; mapKeys[0x45] = Key::E; - mapKeys[0x46] = Key::F; mapKeys[0x47] = Key::G; mapKeys[0x48] = Key::H; mapKeys[0x49] = Key::I; mapKeys[0x4A] = Key::J; - mapKeys[0x4B] = Key::K; mapKeys[0x4C] = Key::L; mapKeys[0x4D] = Key::M; mapKeys[0x4E] = Key::N; mapKeys[0x4F] = Key::O; - mapKeys[0x50] = Key::P; mapKeys[0x51] = Key::Q; mapKeys[0x52] = Key::R; mapKeys[0x53] = Key::S; mapKeys[0x54] = Key::T; - mapKeys[0x55] = Key::U; mapKeys[0x56] = Key::V; mapKeys[0x57] = Key::W; mapKeys[0x58] = Key::X; mapKeys[0x59] = Key::Y; - mapKeys[0x5A] = Key::Z; - - mapKeys[VK_F1] = Key::F1; mapKeys[VK_F2] = Key::F2; mapKeys[VK_F3] = Key::F3; mapKeys[VK_F4] = Key::F4; - mapKeys[VK_F5] = Key::F5; mapKeys[VK_F6] = Key::F6; mapKeys[VK_F7] = Key::F7; mapKeys[VK_F8] = Key::F8; - mapKeys[VK_F9] = Key::F9; mapKeys[VK_F10] = Key::F10; mapKeys[VK_F11] = Key::F11; mapKeys[VK_F12] = Key::F12; - - mapKeys[VK_DOWN] = Key::DOWN; mapKeys[VK_LEFT] = Key::LEFT; mapKeys[VK_RIGHT] = Key::RIGHT; mapKeys[VK_UP] = Key::UP; - mapKeys[VK_RETURN] = Key::ENTER; //mapKeys[VK_RETURN] = Key::RETURN; - - mapKeys[VK_BACK] = Key::BACK; mapKeys[VK_ESCAPE] = Key::ESCAPE; mapKeys[VK_RETURN] = Key::ENTER; mapKeys[VK_PAUSE] = Key::PAUSE; - mapKeys[VK_SCROLL] = Key::SCROLL; mapKeys[VK_TAB] = Key::TAB; mapKeys[VK_DELETE] = Key::DEL; mapKeys[VK_HOME] = Key::HOME; - mapKeys[VK_END] = Key::END; mapKeys[VK_PRIOR] = Key::PGUP; mapKeys[VK_NEXT] = Key::PGDN; mapKeys[VK_INSERT] = Key::INS; - mapKeys[VK_SHIFT] = Key::SHIFT; mapKeys[VK_CONTROL] = Key::CTRL; - mapKeys[VK_SPACE] = Key::SPACE; - - mapKeys[0x30] = Key::K0; mapKeys[0x31] = Key::K1; mapKeys[0x32] = Key::K2; mapKeys[0x33] = Key::K3; mapKeys[0x34] = Key::K4; - mapKeys[0x35] = Key::K5; mapKeys[0x36] = Key::K6; mapKeys[0x37] = Key::K7; mapKeys[0x38] = Key::K8; mapKeys[0x39] = Key::K9; - - mapKeys[VK_NUMPAD0] = Key::NP0; mapKeys[VK_NUMPAD1] = Key::NP1; mapKeys[VK_NUMPAD2] = Key::NP2; mapKeys[VK_NUMPAD3] = Key::NP3; mapKeys[VK_NUMPAD4] = Key::NP4; - mapKeys[VK_NUMPAD5] = Key::NP5; mapKeys[VK_NUMPAD6] = Key::NP6; mapKeys[VK_NUMPAD7] = Key::NP7; mapKeys[VK_NUMPAD8] = Key::NP8; mapKeys[VK_NUMPAD9] = Key::NP9; - mapKeys[VK_MULTIPLY] = Key::NP_MUL; mapKeys[VK_ADD] = Key::NP_ADD; mapKeys[VK_DIVIDE] = Key::NP_DIV; mapKeys[VK_SUBTRACT] = Key::NP_SUB; mapKeys[VK_DECIMAL] = Key::NP_DECIMAL; - - // Thanks scripticuk - mapKeys[VK_OEM_1] = Key::OEM_1; // On US and UK keyboards this is the ';:' key - mapKeys[VK_OEM_2] = Key::OEM_2; // On US and UK keyboards this is the '/?' key - mapKeys[VK_OEM_3] = Key::OEM_3; // On US keyboard this is the '~' key - mapKeys[VK_OEM_4] = Key::OEM_4; // On US and UK keyboards this is the '[{' key - mapKeys[VK_OEM_5] = Key::OEM_5; // On US keyboard this is '\|' key. - mapKeys[VK_OEM_6] = Key::OEM_6; // On US and UK keyboards this is the ']}' key - mapKeys[VK_OEM_7] = Key::OEM_7; // On US keyboard this is the single/double quote key. On UK, this is the single quote/@ symbol key - mapKeys[VK_OEM_8] = Key::OEM_8; // miscellaneous characters. Varies by keyboard - mapKeys[VK_OEM_PLUS] = Key::EQUALS; // the '+' key on any keyboard - mapKeys[VK_OEM_COMMA] = Key::COMMA; // the comma key on any keyboard - mapKeys[VK_OEM_MINUS] = Key::MINUS; // the minus key on any keyboard - mapKeys[VK_OEM_PERIOD] = Key::PERIOD; // the period key on any keyboard - mapKeys[VK_CAPITAL] = Key::CAPS_LOCK; - return olc::OK; - } - - virtual olc::rcode SetWindowTitle(const std::string& s) override - { -#ifdef UNICODE - SetWindowText(olc_hWnd, ConvertS2W(s).c_str()); -#else - SetWindowText(olc_hWnd, s.c_str()); -#endif - return olc::OK; - } - - virtual olc::rcode StartSystemEventLoop() override - { - MSG msg; - while (GetMessage(&msg, NULL, 0, 0) > 0) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - return olc::OK; - } - - virtual olc::rcode HandleSystemEvent() override { return olc::rcode::FAIL; } - - // Windows Event Handler - this is statically connected to the windows event system - static LRESULT CALLBACK olc_WindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) - { - switch (uMsg) - { - case WM_MOUSEMOVE: - { - // Thanks @ForAbby (Discord) - uint16_t x = lParam & 0xFFFF; uint16_t y = (lParam >> 16) & 0xFFFF; - int16_t ix = *(int16_t*)&x; int16_t iy = *(int16_t*)&y; - ptrPGE->olc_UpdateMouse(ix, iy); - return 0; - } - case WM_SIZE: ptrPGE->olc_UpdateWindowSize(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; - case WM_MOUSEWHEEL: ptrPGE->olc_UpdateMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); return 0; - case WM_MOUSELEAVE: ptrPGE->olc_UpdateMouseFocus(false); return 0; - case WM_SETFOCUS: ptrPGE->olc_UpdateKeyFocus(true); return 0; - case WM_KILLFOCUS: ptrPGE->olc_UpdateKeyFocus(false); return 0; - case WM_KEYDOWN: ptrPGE->olc_UpdateKeyState(mapKeys[wParam], true); return 0; - case WM_KEYUP: ptrPGE->olc_UpdateKeyState(mapKeys[wParam], false); return 0; - case WM_SYSKEYDOWN: ptrPGE->olc_UpdateKeyState(mapKeys[wParam], true); return 0; - case WM_SYSKEYUP: ptrPGE->olc_UpdateKeyState(mapKeys[wParam], false); return 0; - case WM_LBUTTONDOWN:ptrPGE->olc_UpdateMouseState(0, true); return 0; - case WM_LBUTTONUP: ptrPGE->olc_UpdateMouseState(0, false); return 0; - case WM_RBUTTONDOWN:ptrPGE->olc_UpdateMouseState(1, true); return 0; - case WM_RBUTTONUP: ptrPGE->olc_UpdateMouseState(1, false); return 0; - case WM_MBUTTONDOWN:ptrPGE->olc_UpdateMouseState(2, true); return 0; - case WM_MBUTTONUP: ptrPGE->olc_UpdateMouseState(2, false); return 0; - case WM_CLOSE: ptrPGE->olc_Terminate(); return 0; - case WM_DESTROY: PostQuitMessage(0); DestroyWindow(hWnd); return 0; - } - return DefWindowProc(hWnd, uMsg, wParam, lParam); - } - }; -} -#endif -// O------------------------------------------------------------------------------O -// | END PLATFORM: MICROSOFT WINDOWS XP, VISTA, 7, 8, 10 | -// O------------------------------------------------------------------------------O -#pragma endregion - -#pragma region platform_linux -// O------------------------------------------------------------------------------O -// | START PLATFORM: LINUX | -// O------------------------------------------------------------------------------O -#if defined(OLC_PLATFORM_X11) -namespace olc -{ - class Platform_Linux : public olc::Platform - { - private: - X11::Display* olc_Display = nullptr; - X11::Window olc_WindowRoot; - X11::Window olc_Window; - X11::XVisualInfo* olc_VisualInfo; - X11::Colormap olc_ColourMap; - X11::XSetWindowAttributes olc_SetWindowAttribs; - - public: - virtual olc::rcode ApplicationStartUp() override - { - return olc::rcode::OK; - } - - virtual olc::rcode ApplicationCleanUp() override - { - XDestroyWindow(olc_Display, olc_Window); - return olc::rcode::OK; - } - - virtual olc::rcode ThreadStartUp() override - { - return olc::rcode::OK; - } - - virtual olc::rcode ThreadCleanUp() override - { - renderer->DestroyDevice(); - return olc::OK; - } - - virtual olc::rcode CreateGraphics(bool bFullScreen, bool bEnableVSYNC, const olc::vi2d& vViewPos, const olc::vi2d& vViewSize) override - { - if (renderer->CreateDevice({ olc_Display, &olc_Window, olc_VisualInfo }, bFullScreen, bEnableVSYNC) == olc::rcode::OK) - { - renderer->UpdateViewport(vViewPos, vViewSize); - return olc::rcode::OK; - } - else - return olc::rcode::FAIL; - } - - virtual olc::rcode CreateWindowPane(const olc::vi2d& vWindowPos, olc::vi2d& vWindowSize, bool bFullScreen) override - { - using namespace X11; - XInitThreads(); - - // Grab the deafult display and window - olc_Display = XOpenDisplay(NULL); - olc_WindowRoot = DefaultRootWindow(olc_Display); - - // Based on the display capabilities, configure the appearance of the window - GLint olc_GLAttribs[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None }; - olc_VisualInfo = glXChooseVisual(olc_Display, 0, olc_GLAttribs); - olc_ColourMap = XCreateColormap(olc_Display, olc_WindowRoot, olc_VisualInfo->visual, AllocNone); - olc_SetWindowAttribs.colormap = olc_ColourMap; - - // Register which events we are interested in receiving - olc_SetWindowAttribs.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | - ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask | StructureNotifyMask; - - // Create the window - olc_Window = XCreateWindow(olc_Display, olc_WindowRoot, vWindowPos.x, vWindowPos.y, - vWindowSize.x, vWindowSize.y, - 0, olc_VisualInfo->depth, InputOutput, olc_VisualInfo->visual, - CWColormap | CWEventMask, &olc_SetWindowAttribs); - - Atom wmDelete = XInternAtom(olc_Display, "WM_DELETE_WINDOW", true); - XSetWMProtocols(olc_Display, olc_Window, &wmDelete, 1); - - XMapWindow(olc_Display, olc_Window); - XStoreName(olc_Display, olc_Window, "OneLoneCoder.com - Pixel Game Engine"); - - if (bFullScreen) // Thanks DragonEye, again :D - { - Atom wm_state; - Atom fullscreen; - wm_state = XInternAtom(olc_Display, "_NET_WM_STATE", False); - fullscreen = XInternAtom(olc_Display, "_NET_WM_STATE_FULLSCREEN", False); - XEvent xev{ 0 }; - xev.type = ClientMessage; - xev.xclient.window = olc_Window; - xev.xclient.message_type = wm_state; - xev.xclient.format = 32; - xev.xclient.data.l[0] = (bFullScreen ? 1 : 0); // the action (0: off, 1: on, 2: toggle) - xev.xclient.data.l[1] = fullscreen; // first property to alter - xev.xclient.data.l[2] = 0; // second property to alter - xev.xclient.data.l[3] = 0; // source indication - XMapWindow(olc_Display, olc_Window); - XSendEvent(olc_Display, DefaultRootWindow(olc_Display), False, - SubstructureRedirectMask | SubstructureNotifyMask, &xev); - XFlush(olc_Display); - XWindowAttributes gwa; - XGetWindowAttributes(olc_Display, olc_Window, &gwa); - vWindowSize.x = gwa.width; - vWindowSize.y = gwa.height; - } - - // Create Keyboard Mapping - mapKeys[0x00] = Key::NONE; - mapKeys[0x61] = Key::A; mapKeys[0x62] = Key::B; mapKeys[0x63] = Key::C; mapKeys[0x64] = Key::D; mapKeys[0x65] = Key::E; - mapKeys[0x66] = Key::F; mapKeys[0x67] = Key::G; mapKeys[0x68] = Key::H; mapKeys[0x69] = Key::I; mapKeys[0x6A] = Key::J; - mapKeys[0x6B] = Key::K; mapKeys[0x6C] = Key::L; mapKeys[0x6D] = Key::M; mapKeys[0x6E] = Key::N; mapKeys[0x6F] = Key::O; - mapKeys[0x70] = Key::P; mapKeys[0x71] = Key::Q; mapKeys[0x72] = Key::R; mapKeys[0x73] = Key::S; mapKeys[0x74] = Key::T; - mapKeys[0x75] = Key::U; mapKeys[0x76] = Key::V; mapKeys[0x77] = Key::W; mapKeys[0x78] = Key::X; mapKeys[0x79] = Key::Y; - mapKeys[0x7A] = Key::Z; - - mapKeys[XK_F1] = Key::F1; mapKeys[XK_F2] = Key::F2; mapKeys[XK_F3] = Key::F3; mapKeys[XK_F4] = Key::F4; - mapKeys[XK_F5] = Key::F5; mapKeys[XK_F6] = Key::F6; mapKeys[XK_F7] = Key::F7; mapKeys[XK_F8] = Key::F8; - mapKeys[XK_F9] = Key::F9; mapKeys[XK_F10] = Key::F10; mapKeys[XK_F11] = Key::F11; mapKeys[XK_F12] = Key::F12; - - mapKeys[XK_Down] = Key::DOWN; mapKeys[XK_Left] = Key::LEFT; mapKeys[XK_Right] = Key::RIGHT; mapKeys[XK_Up] = Key::UP; - mapKeys[XK_KP_Enter] = Key::ENTER; mapKeys[XK_Return] = Key::ENTER; - - mapKeys[XK_BackSpace] = Key::BACK; mapKeys[XK_Escape] = Key::ESCAPE; mapKeys[XK_Linefeed] = Key::ENTER; mapKeys[XK_Pause] = Key::PAUSE; - mapKeys[XK_Scroll_Lock] = Key::SCROLL; mapKeys[XK_Tab] = Key::TAB; mapKeys[XK_Delete] = Key::DEL; mapKeys[XK_Home] = Key::HOME; - mapKeys[XK_End] = Key::END; mapKeys[XK_Page_Up] = Key::PGUP; mapKeys[XK_Page_Down] = Key::PGDN; mapKeys[XK_Insert] = Key::INS; - mapKeys[XK_Shift_L] = Key::SHIFT; mapKeys[XK_Shift_R] = Key::SHIFT; mapKeys[XK_Control_L] = Key::CTRL; mapKeys[XK_Control_R] = Key::CTRL; - mapKeys[XK_space] = Key::SPACE; mapKeys[XK_period] = Key::PERIOD; - - mapKeys[XK_0] = Key::K0; mapKeys[XK_1] = Key::K1; mapKeys[XK_2] = Key::K2; mapKeys[XK_3] = Key::K3; mapKeys[XK_4] = Key::K4; - mapKeys[XK_5] = Key::K5; mapKeys[XK_6] = Key::K6; mapKeys[XK_7] = Key::K7; mapKeys[XK_8] = Key::K8; mapKeys[XK_9] = Key::K9; - - mapKeys[XK_KP_0] = Key::NP0; mapKeys[XK_KP_1] = Key::NP1; mapKeys[XK_KP_2] = Key::NP2; mapKeys[XK_KP_3] = Key::NP3; mapKeys[XK_KP_4] = Key::NP4; - mapKeys[XK_KP_5] = Key::NP5; mapKeys[XK_KP_6] = Key::NP6; mapKeys[XK_KP_7] = Key::NP7; mapKeys[XK_KP_8] = Key::NP8; mapKeys[XK_KP_9] = Key::NP9; - mapKeys[XK_KP_Multiply] = Key::NP_MUL; mapKeys[XK_KP_Add] = Key::NP_ADD; mapKeys[XK_KP_Divide] = Key::NP_DIV; mapKeys[XK_KP_Subtract] = Key::NP_SUB; mapKeys[XK_KP_Decimal] = Key::NP_DECIMAL; - - // These keys vary depending on the keyboard. I've included comments for US and UK keyboard layouts - mapKeys[XK_semicolon] = Key::OEM_1; // On US and UK keyboards this is the ';:' key - mapKeys[XK_slash] = Key::OEM_2; // On US and UK keyboards this is the '/?' key - mapKeys[XK_asciitilde] = Key::OEM_3; // On US keyboard this is the '~' key - mapKeys[XK_bracketleft] = Key::OEM_4; // On US and UK keyboards this is the '[{' key - mapKeys[XK_backslash] = Key::OEM_5; // On US keyboard this is '\|' key. - mapKeys[XK_bracketright] = Key::OEM_6; // On US and UK keyboards this is the ']}' key - mapKeys[XK_apostrophe] = Key::OEM_7; // On US keyboard this is the single/double quote key. On UK, this is the single quote/@ symbol key - mapKeys[XK_numbersign] = Key::OEM_8; // miscellaneous characters. Varies by keyboard. I believe this to be the '#~' key on UK keyboards - mapKeys[XK_equal] = Key::EQUALS; // the '+' key on any keyboard - mapKeys[XK_comma] = Key::COMMA; // the comma key on any keyboard - mapKeys[XK_minus] = Key::MINUS; // the minus key on any keyboard - - mapKeys[XK_Caps_Lock] = Key::CAPS_LOCK; - - return olc::OK; - } - - virtual olc::rcode SetWindowTitle(const std::string& s) override - { - X11::XStoreName(olc_Display, olc_Window, s.c_str()); - return olc::OK; - } - - virtual olc::rcode StartSystemEventLoop() override - { - return olc::OK; - } - - virtual olc::rcode HandleSystemEvent() override - { - using namespace X11; - // Handle Xlib Message Loop - we do this in the - // same thread that OpenGL was created so we dont - // need to worry too much about multithreading with X11 - XEvent xev; - while (XPending(olc_Display)) - { - XNextEvent(olc_Display, &xev); - if (xev.type == Expose) - { - XWindowAttributes gwa; - XGetWindowAttributes(olc_Display, olc_Window, &gwa); - ptrPGE->olc_UpdateWindowSize(gwa.width, gwa.height); - } - else if (xev.type == ConfigureNotify) - { - XConfigureEvent xce = xev.xconfigure; - ptrPGE->olc_UpdateWindowSize(xce.width, xce.height); - } - else if (xev.type == KeyPress) - { - KeySym sym = XLookupKeysym(&xev.xkey, 0); - ptrPGE->olc_UpdateKeyState(mapKeys[sym], true); - XKeyEvent* e = (XKeyEvent*)&xev; // Because DragonEye loves numpads - XLookupString(e, NULL, 0, &sym, NULL); - ptrPGE->olc_UpdateKeyState(mapKeys[sym], true); - } - else if (xev.type == KeyRelease) - { - KeySym sym = XLookupKeysym(&xev.xkey, 0); - ptrPGE->olc_UpdateKeyState(mapKeys[sym], false); - XKeyEvent* e = (XKeyEvent*)&xev; - XLookupString(e, NULL, 0, &sym, NULL); - ptrPGE->olc_UpdateKeyState(mapKeys[sym], false); - } - else if (xev.type == ButtonPress) - { - switch (xev.xbutton.button) - { - case 1: ptrPGE->olc_UpdateMouseState(0, true); break; - case 2: ptrPGE->olc_UpdateMouseState(2, true); break; - case 3: ptrPGE->olc_UpdateMouseState(1, true); break; - case 4: ptrPGE->olc_UpdateMouseWheel(120); break; - case 5: ptrPGE->olc_UpdateMouseWheel(-120); break; - default: break; - } - } - else if (xev.type == ButtonRelease) - { - switch (xev.xbutton.button) - { - case 1: ptrPGE->olc_UpdateMouseState(0, false); break; - case 2: ptrPGE->olc_UpdateMouseState(2, false); break; - case 3: ptrPGE->olc_UpdateMouseState(1, false); break; - default: break; - } - } - else if (xev.type == MotionNotify) - { - ptrPGE->olc_UpdateMouse(xev.xmotion.x, xev.xmotion.y); - } - else if (xev.type == FocusIn) - { - ptrPGE->olc_UpdateKeyFocus(true); - } - else if (xev.type == FocusOut) - { - ptrPGE->olc_UpdateKeyFocus(false); - } - else if (xev.type == ClientMessage) - { - ptrPGE->olc_Terminate(); - } - } - return olc::OK; - } - }; -} -#endif -// O------------------------------------------------------------------------------O -// | END PLATFORM: LINUX | -// O------------------------------------------------------------------------------O -#pragma endregion - -#pragma region platform_glut -// O------------------------------------------------------------------------------O -// | START PLATFORM: GLUT (used to make it simple for Apple) | -// O------------------------------------------------------------------------------O -// -// VERY IMPORTANT!!! The Apple port was originally created by @Mumflr (discord) -// and the repo for the development of this project can be found here: -// https://github.com/MumflrFumperdink/olcPGEMac which contains maccy goodness -// and support on how to setup your build environment. -// -// "MASSIVE MASSIVE THANKS TO MUMFLR" - Javidx9 -#if defined(OLC_PLATFORM_GLUT) -namespace olc { - - class Platform_GLUT : public olc::Platform - { - public: - static std::atomic* bActiveRef; - - virtual olc::rcode ApplicationStartUp() override { - return olc::rcode::OK; - } - - virtual olc::rcode ApplicationCleanUp() override - { - return olc::rcode::OK; - } - - virtual olc::rcode ThreadStartUp() override - { - return olc::rcode::OK; - } - - virtual olc::rcode ThreadCleanUp() override - { - renderer->DestroyDevice(); - return olc::OK; - } - - virtual olc::rcode CreateGraphics(bool bFullScreen, bool bEnableVSYNC, const olc::vi2d& vViewPos, const olc::vi2d& vViewSize) override - { - if (renderer->CreateDevice({}, bFullScreen, bEnableVSYNC) == olc::rcode::OK) - { - renderer->UpdateViewport(vViewPos, vViewSize); - return olc::rcode::OK; - } - else - return olc::rcode::FAIL; - } - - static void ExitMainLoop() { - if (!ptrPGE->OnUserDestroy()) { - *bActiveRef = true; - return; - } - platform->ThreadCleanUp(); - platform->ApplicationCleanUp(); - exit(0); - } - -#if defined(__APPLE__) - static void scrollWheelUpdate(id selff, SEL _sel, id theEvent) { - static const SEL deltaYSel = sel_registerName("deltaY"); - - double deltaY = ((double (*)(id, SEL))objc_msgSend_fpret)(theEvent, deltaYSel); - - for (int i = 0; i < abs(deltaY); i++) { - if (deltaY > 0) { - ptrPGE->olc_UpdateMouseWheel(-1); - } - else if (deltaY < 0) { - ptrPGE->olc_UpdateMouseWheel(1); - } - } - } -#endif - static void ThreadFunct() { -#if defined(__APPLE__) - static bool hasEnabledCocoa = false; - if (!hasEnabledCocoa) { - // Objective-C Wizardry - Class NSApplicationClass = objc_getClass("NSApplication"); - - // NSApp = [NSApplication sharedApplication] - SEL sharedApplicationSel = sel_registerName("sharedApplication"); - id NSApp = ((id(*)(Class, SEL))objc_msgSend)(NSApplicationClass, sharedApplicationSel); - // window = [NSApp mainWindow] - SEL mainWindowSel = sel_registerName("mainWindow"); - id window = ((id(*)(id, SEL))objc_msgSend)(NSApp, mainWindowSel); - - // [window setStyleMask: NSWindowStyleMaskClosable | ~NSWindowStyleMaskResizable] - SEL setStyleMaskSel = sel_registerName("setStyleMask:"); - ((void (*)(id, SEL, NSUInteger))objc_msgSend)(window, setStyleMaskSel, 7); - - hasEnabledCocoa = true; - } -#endif - if (!*bActiveRef) { - ExitMainLoop(); - return; - } - glutPostRedisplay(); - } - - static void DrawFunct() { - ptrPGE->olc_CoreUpdate(); - } - - virtual olc::rcode CreateWindowPane(const olc::vi2d& vWindowPos, olc::vi2d& vWindowSize, bool bFullScreen) override - { -#if defined(__APPLE__) - Class GLUTViewClass = objc_getClass("GLUTView"); - - SEL scrollWheelSel = sel_registerName("scrollWheel:"); - bool resultAddMethod = class_addMethod(GLUTViewClass, scrollWheelSel, (IMP)scrollWheelUpdate, "v@:@"); - assert(resultAddMethod); -#endif - - renderer->PrepareDevice(); - - if (bFullScreen) - { - vWindowSize.x = glutGet(GLUT_SCREEN_WIDTH); - vWindowSize.y = glutGet(GLUT_SCREEN_HEIGHT); - glutFullScreen(); - } - else - { - if (vWindowSize.x > glutGet(GLUT_SCREEN_WIDTH) || vWindowSize.y > glutGet(GLUT_SCREEN_HEIGHT)) - { - perror("ERROR: The specified window dimensions do not fit on your screen\n"); - return olc::FAIL; - } - glutReshapeWindow(vWindowSize.x, vWindowSize.y - 1); - } - - // Create Keyboard Mapping - mapKeys[0x00] = Key::NONE; - mapKeys['A'] = Key::A; mapKeys['B'] = Key::B; mapKeys['C'] = Key::C; mapKeys['D'] = Key::D; mapKeys['E'] = Key::E; - mapKeys['F'] = Key::F; mapKeys['G'] = Key::G; mapKeys['H'] = Key::H; mapKeys['I'] = Key::I; mapKeys['J'] = Key::J; - mapKeys['K'] = Key::K; mapKeys['L'] = Key::L; mapKeys['M'] = Key::M; mapKeys['N'] = Key::N; mapKeys['O'] = Key::O; - mapKeys['P'] = Key::P; mapKeys['Q'] = Key::Q; mapKeys['R'] = Key::R; mapKeys['S'] = Key::S; mapKeys['T'] = Key::T; - mapKeys['U'] = Key::U; mapKeys['V'] = Key::V; mapKeys['W'] = Key::W; mapKeys['X'] = Key::X; mapKeys['Y'] = Key::Y; - mapKeys['Z'] = Key::Z; - - mapKeys[GLUT_KEY_F1] = Key::F1; mapKeys[GLUT_KEY_F2] = Key::F2; mapKeys[GLUT_KEY_F3] = Key::F3; mapKeys[GLUT_KEY_F4] = Key::F4; - mapKeys[GLUT_KEY_F5] = Key::F5; mapKeys[GLUT_KEY_F6] = Key::F6; mapKeys[GLUT_KEY_F7] = Key::F7; mapKeys[GLUT_KEY_F8] = Key::F8; - mapKeys[GLUT_KEY_F9] = Key::F9; mapKeys[GLUT_KEY_F10] = Key::F10; mapKeys[GLUT_KEY_F11] = Key::F11; mapKeys[GLUT_KEY_F12] = Key::F12; - - mapKeys[GLUT_KEY_DOWN] = Key::DOWN; mapKeys[GLUT_KEY_LEFT] = Key::LEFT; mapKeys[GLUT_KEY_RIGHT] = Key::RIGHT; mapKeys[GLUT_KEY_UP] = Key::UP; - mapKeys[13] = Key::ENTER; - - mapKeys[127] = Key::BACK; mapKeys[27] = Key::ESCAPE; - mapKeys[9] = Key::TAB; mapKeys[GLUT_KEY_HOME] = Key::HOME; - mapKeys[GLUT_KEY_END] = Key::END; mapKeys[GLUT_KEY_PAGE_UP] = Key::PGUP; mapKeys[GLUT_KEY_PAGE_DOWN] = Key::PGDN; mapKeys[GLUT_KEY_INSERT] = Key::INS; - mapKeys[32] = Key::SPACE; mapKeys[46] = Key::PERIOD; - - mapKeys[48] = Key::K0; mapKeys[49] = Key::K1; mapKeys[50] = Key::K2; mapKeys[51] = Key::K3; mapKeys[52] = Key::K4; - mapKeys[53] = Key::K5; mapKeys[54] = Key::K6; mapKeys[55] = Key::K7; mapKeys[56] = Key::K8; mapKeys[57] = Key::K9; - - // NOTE: MISSING KEYS :O - - glutKeyboardFunc([](unsigned char key, int x, int y) -> void { - switch (glutGetModifiers()) { - case 0: //This is when there are no modifiers - if ('a' <= key && key <= 'z') key -= 32; - break; - case GLUT_ACTIVE_SHIFT: - ptrPGE->olc_UpdateKeyState(Key::SHIFT, true); - break; - case GLUT_ACTIVE_CTRL: - if ('a' <= key && key <= 'z') key -= 32; - ptrPGE->olc_UpdateKeyState(Key::CTRL, true); - break; - case GLUT_ACTIVE_ALT: - if ('a' <= key && key <= 'z') key -= 32; - break; - } - - if (mapKeys[key]) - ptrPGE->olc_UpdateKeyState(mapKeys[key], true); - }); - - glutKeyboardUpFunc([](unsigned char key, int x, int y) -> void { - switch (glutGetModifiers()) { - case 0: //This is when there are no modifiers - if ('a' <= key && key <= 'z') key -= 32; - break; - case GLUT_ACTIVE_SHIFT: - ptrPGE->olc_UpdateKeyState(Key::SHIFT, false); - break; - case GLUT_ACTIVE_CTRL: - if ('a' <= key && key <= 'z') key -= 32; - ptrPGE->olc_UpdateKeyState(Key::CTRL, false); - break; - case GLUT_ACTIVE_ALT: - if ('a' <= key && key <= 'z') key -= 32; - //No ALT in PGE - break; - } - - if (mapKeys[key]) - ptrPGE->olc_UpdateKeyState(mapKeys[key], false); - }); - - //Special keys - glutSpecialFunc([](int key, int x, int y) -> void { - if (mapKeys[key]) - ptrPGE->olc_UpdateKeyState(mapKeys[key], true); - }); - - glutSpecialUpFunc([](int key, int x, int y) -> void { - if (mapKeys[key]) - ptrPGE->olc_UpdateKeyState(mapKeys[key], false); - }); - - glutMouseFunc([](int button, int state, int x, int y) -> void { - switch (button) { - case GLUT_LEFT_BUTTON: - if (state == GLUT_UP) ptrPGE->olc_UpdateMouseState(0, false); - else if (state == GLUT_DOWN) ptrPGE->olc_UpdateMouseState(0, true); - break; - case GLUT_MIDDLE_BUTTON: - if (state == GLUT_UP) ptrPGE->olc_UpdateMouseState(2, false); - else if (state == GLUT_DOWN) ptrPGE->olc_UpdateMouseState(2, true); - break; - case GLUT_RIGHT_BUTTON: - if (state == GLUT_UP) ptrPGE->olc_UpdateMouseState(1, false); - else if (state == GLUT_DOWN) ptrPGE->olc_UpdateMouseState(1, true); - break; - } - }); - - auto mouseMoveCall = [](int x, int y) -> void { - ptrPGE->olc_UpdateMouse(x, y); - }; - - glutMotionFunc(mouseMoveCall); - glutPassiveMotionFunc(mouseMoveCall); - - glutEntryFunc([](int state) -> void { - if (state == GLUT_ENTERED) ptrPGE->olc_UpdateKeyFocus(true); - else if (state == GLUT_LEFT) ptrPGE->olc_UpdateKeyFocus(false); - }); - - glutDisplayFunc(DrawFunct); - glutIdleFunc(ThreadFunct); - - return olc::OK; - } - - virtual olc::rcode SetWindowTitle(const std::string& s) override - { - glutSetWindowTitle(s.c_str()); - return olc::OK; - } - - virtual olc::rcode StartSystemEventLoop() override { - glutMainLoop(); - return olc::OK; - } - - virtual olc::rcode HandleSystemEvent() override - { - return olc::OK; - } - }; - - std::atomic* Platform_GLUT::bActiveRef{ nullptr }; - - //Custom Start - olc::rcode PixelGameEngine::Start() - { - if (platform->ApplicationStartUp() != olc::OK) return olc::FAIL; - - // Construct the window - if (platform->CreateWindowPane({ 30,30 }, vWindowSize, bFullScreen) != olc::OK) return olc::FAIL; - olc_UpdateWindowSize(vWindowSize.x, vWindowSize.y); - - if (platform->ThreadStartUp() == olc::FAIL) return olc::FAIL; - olc_PrepareEngine(); - if (!OnUserCreate()) return olc::FAIL; - Platform_GLUT::bActiveRef = &bAtomActive; - glutWMCloseFunc(Platform_GLUT::ExitMainLoop); - bAtomActive = true; - platform->StartSystemEventLoop(); - - //This code will not even be run but why not - if (platform->ApplicationCleanUp() != olc::OK) return olc::FAIL; - - return olc::OK; - } -} - -#endif -// O------------------------------------------------------------------------------O -// | END PLATFORM: GLUT | -// O------------------------------------------------------------------------------O -#pragma endregion - - -#pragma region platform_emscripten -// O------------------------------------------------------------------------------O -// | START PLATFORM: Emscripten - Totally Game Changing... | -// O------------------------------------------------------------------------------O - -// -// Firstly a big mega thank you to members of the OLC Community for sorting this -// out. Making a browser compatible version has been a priority for quite some -// time, but I lacked the expertise to do it. This awesome feature is possible -// because a group of former strangers got together and formed friendships over -// their shared passion for code. If anything demonstrates how powerful helping -// each other can be, it's this. - Javidx9 - -// Emscripten Platform: MaGetzUb, Moros1138, Slavka, Dandistine, Gorbit99, Bispoo -// also: Ishidex, Gusgo99, SlicEnDicE, Alexio - - -#if defined(OLC_PLATFORM_EMSCRIPTEN) - -#include -#include - -extern "C" -{ - EMSCRIPTEN_KEEPALIVE inline int olc_OnPageUnload() - { olc::platform->ApplicationCleanUp(); return 0; } -} - -namespace olc -{ - class Platform_Emscripten : public olc::Platform - { - public: - - virtual olc::rcode ApplicationStartUp() override - { return olc::rcode::OK; } - - virtual olc::rcode ApplicationCleanUp() override - { ThreadCleanUp(); return olc::rcode::OK; } - - virtual olc::rcode ThreadStartUp() override - { return olc::rcode::OK; } - - virtual olc::rcode ThreadCleanUp() override - { renderer->DestroyDevice(); return olc::OK; } - - virtual olc::rcode CreateGraphics(bool bFullScreen, bool bEnableVSYNC, const olc::vi2d& vViewPos, const olc::vi2d& vViewSize) override - { - if (renderer->CreateDevice({}, bFullScreen, bEnableVSYNC) == olc::rcode::OK) - { - renderer->UpdateViewport(vViewPos, vViewSize); - return olc::rcode::OK; - } - else - return olc::rcode::FAIL; - } - - virtual olc::rcode CreateWindowPane(const olc::vi2d& vWindowPos, olc::vi2d& vWindowSize, bool bFullScreen) override - { - emscripten_set_canvas_element_size("#canvas", vWindowSize.x, vWindowSize.y); - - mapKeys[DOM_PK_UNKNOWN] = Key::NONE; - mapKeys[DOM_PK_A] = Key::A; mapKeys[DOM_PK_B] = Key::B; mapKeys[DOM_PK_C] = Key::C; mapKeys[DOM_PK_D] = Key::D; - mapKeys[DOM_PK_E] = Key::E; mapKeys[DOM_PK_F] = Key::F; mapKeys[DOM_PK_G] = Key::G; mapKeys[DOM_PK_H] = Key::H; - mapKeys[DOM_PK_I] = Key::I; mapKeys[DOM_PK_J] = Key::J; mapKeys[DOM_PK_K] = Key::K; mapKeys[DOM_PK_L] = Key::L; - mapKeys[DOM_PK_M] = Key::M; mapKeys[DOM_PK_N] = Key::N; mapKeys[DOM_PK_O] = Key::O; mapKeys[DOM_PK_P] = Key::P; - mapKeys[DOM_PK_Q] = Key::Q; mapKeys[DOM_PK_R] = Key::R; mapKeys[DOM_PK_S] = Key::S; mapKeys[DOM_PK_T] = Key::T; - mapKeys[DOM_PK_U] = Key::U; mapKeys[DOM_PK_V] = Key::V; mapKeys[DOM_PK_W] = Key::W; mapKeys[DOM_PK_X] = Key::X; - mapKeys[DOM_PK_Y] = Key::Y; mapKeys[DOM_PK_Z] = Key::Z; - mapKeys[DOM_PK_0] = Key::K0; mapKeys[DOM_PK_1] = Key::K1; mapKeys[DOM_PK_2] = Key::K2; - mapKeys[DOM_PK_3] = Key::K3; mapKeys[DOM_PK_4] = Key::K4; mapKeys[DOM_PK_5] = Key::K5; - mapKeys[DOM_PK_6] = Key::K6; mapKeys[DOM_PK_7] = Key::K7; mapKeys[DOM_PK_8] = Key::K8; - mapKeys[DOM_PK_9] = Key::K9; - mapKeys[DOM_PK_F1] = Key::F1; mapKeys[DOM_PK_F2] = Key::F2; mapKeys[DOM_PK_F3] = Key::F3; mapKeys[DOM_PK_F4] = Key::F4; - mapKeys[DOM_PK_F5] = Key::F5; mapKeys[DOM_PK_F6] = Key::F6; mapKeys[DOM_PK_F7] = Key::F7; mapKeys[DOM_PK_F8] = Key::F8; - mapKeys[DOM_PK_F9] = Key::F9; mapKeys[DOM_PK_F10] = Key::F10; mapKeys[DOM_PK_F11] = Key::F11; mapKeys[DOM_PK_F12] = Key::F12; - mapKeys[DOM_PK_ARROW_UP] = Key::UP; mapKeys[DOM_PK_ARROW_DOWN] = Key::DOWN; - mapKeys[DOM_PK_ARROW_LEFT] = Key::LEFT; mapKeys[DOM_PK_ARROW_RIGHT] = Key::RIGHT; - mapKeys[DOM_PK_SPACE] = Key::SPACE; mapKeys[DOM_PK_TAB] = Key::TAB; - mapKeys[DOM_PK_SHIFT_LEFT] = Key::SHIFT; mapKeys[DOM_PK_SHIFT_RIGHT] = Key::SHIFT; - mapKeys[DOM_PK_CONTROL_LEFT] = Key::CTRL; mapKeys[DOM_PK_CONTROL_RIGHT] = Key::CTRL; - mapKeys[DOM_PK_INSERT] = Key::INS; mapKeys[DOM_PK_DELETE] = Key::DEL; mapKeys[DOM_PK_HOME] = Key::HOME; - mapKeys[DOM_PK_END] = Key::END; mapKeys[DOM_PK_PAGE_UP] = Key::PGUP; mapKeys[DOM_PK_PAGE_DOWN] = Key::PGDN; - mapKeys[DOM_PK_BACKSPACE] = Key::BACK; mapKeys[DOM_PK_ESCAPE] = Key::ESCAPE; - mapKeys[DOM_PK_ENTER] = Key::ENTER; mapKeys[DOM_PK_NUMPAD_EQUAL] = Key::EQUALS; - mapKeys[DOM_PK_NUMPAD_ENTER] = Key::ENTER; mapKeys[DOM_PK_PAUSE] = Key::PAUSE; - mapKeys[DOM_PK_SCROLL_LOCK] = Key::SCROLL; - mapKeys[DOM_PK_NUMPAD_0] = Key::NP0; mapKeys[DOM_PK_NUMPAD_1] = Key::NP1; mapKeys[DOM_PK_NUMPAD_2] = Key::NP2; - mapKeys[DOM_PK_NUMPAD_3] = Key::NP3; mapKeys[DOM_PK_NUMPAD_4] = Key::NP4; mapKeys[DOM_PK_NUMPAD_5] = Key::NP5; - mapKeys[DOM_PK_NUMPAD_6] = Key::NP6; mapKeys[DOM_PK_NUMPAD_7] = Key::NP7; mapKeys[DOM_PK_NUMPAD_8] = Key::NP8; - mapKeys[DOM_PK_NUMPAD_9] = Key::NP9; - mapKeys[DOM_PK_NUMPAD_MULTIPLY] = Key::NP_MUL; mapKeys[DOM_PK_NUMPAD_DIVIDE] = Key::NP_DIV; - mapKeys[DOM_PK_NUMPAD_ADD] = Key::NP_ADD; mapKeys[DOM_PK_NUMPAD_SUBTRACT] = Key::NP_SUB; - mapKeys[DOM_PK_NUMPAD_DECIMAL] = Key::NP_DECIMAL; - mapKeys[DOM_PK_PERIOD] = Key::PERIOD; mapKeys[DOM_PK_EQUAL] = Key::EQUALS; - mapKeys[DOM_PK_COMMA] = Key::COMMA; mapKeys[DOM_PK_MINUS] = Key::MINUS; - mapKeys[DOM_PK_CAPS_LOCK] = Key::CAPS_LOCK; - mapKeys[DOM_PK_SEMICOLON] = Key::OEM_1; mapKeys[DOM_PK_SLASH] = Key::OEM_2; mapKeys[DOM_PK_BACKQUOTE] = Key::OEM_3; - mapKeys[DOM_PK_BRACKET_LEFT] = Key::OEM_4; mapKeys[DOM_PK_BACKSLASH] = Key::OEM_5; mapKeys[DOM_PK_BRACKET_RIGHT] = Key::OEM_6; - mapKeys[DOM_PK_QUOTE] = Key::OEM_7; mapKeys[DOM_PK_BACKSLASH] = Key::OEM_8; - - // Keyboard Callbacks - emscripten_set_keydown_callback("#canvas", 0, 1, keyboard_callback); - emscripten_set_keyup_callback("#canvas", 0, 1, keyboard_callback); - - // Mouse Callbacks - emscripten_set_wheel_callback("#canvas", 0, 1, wheel_callback); - emscripten_set_mousedown_callback("#canvas", 0, 1, mouse_callback); - emscripten_set_mouseup_callback("#canvas", 0, 1, mouse_callback); - emscripten_set_mousemove_callback("#canvas", 0, 1, mouse_callback); - - // Touch Callbacks - emscripten_set_touchstart_callback("#canvas", 0, 1, touch_callback); - emscripten_set_touchmove_callback("#canvas", 0, 1, touch_callback); - emscripten_set_touchend_callback("#canvas", 0, 1, touch_callback); - - // Canvas Focus Callbacks - emscripten_set_blur_callback("#canvas", 0, 1, focus_callback); - emscripten_set_focus_callback("#canvas", 0, 1, focus_callback); - -#pragma warning disable format - EM_ASM( window.onunload = Module._olc_OnPageUnload; ); - - // IMPORTANT! - Sorry About This... - // - // In order to handle certain browser based events, such as resizing and - // going to full screen, we have to effectively inject code into the container - // running the PGE. Yes, I vomited about 11 times too when the others were - // convincing me this is the future. Well, this isnt the future, and if it - // were to be, I want no part of what must be a miserable distopian free - // for all of anarchic code injection to get rudimentary events like "Resize()". - // - // Wake up people! Of course theres a spoon. There has to be to keep feeding - // the giant web baby. - - - // Fullscreen and Resize Observers - EM_ASM({ - - // cache for reuse - Module._olc_EmscriptenShellCss = "width: 100%; height: 70vh; margin-left: auto; margin-right: auto;"; - - // width / height = aspect ratio - Module._olc_WindowAspectRatio = $0 / $1; - Module.canvas.parentNode.addEventListener("resize", function(e) { - - if (e.defaultPrevented) { e.stopPropagation(); return; } - var viewWidth = e.detail.width; - var viewHeight = e.detail.width / Module._olc_WindowAspectRatio; - if (viewHeight > e.detail.height) - { - viewHeight = e.detail.height; - viewWidth = e.detail.height * Module._olc_WindowAspectRatio; - } - - if (Module.canvas.parentNode.className == 'emscripten_border') - Module.canvas.parentNode.style.cssText = Module._olc_EmscriptenShellCss + " width: " + viewWidth.toString() + "px; height: " + viewHeight.toString() + "px;"; - - Module.canvas.setAttribute("width", viewWidth); - Module.canvas.setAttribute("height", viewHeight); - - if (document.fullscreenElement != null) - { - var top = (e.detail.height - viewHeight) / 2; - var left = (e.detail.width - viewWidth) / 2; - Module.canvas.style.position = "fixed"; - Module.canvas.style.top = top.toString() + "px"; - Module.canvas.style.left = left.toString() + "px"; - Module.canvas.style.width = ""; - Module.canvas.style.height = ""; - } - - // trigger PGE update - Module._olc_PGE_UpdateWindowSize(viewWidth, viewHeight); - // this is really only needed when enter/exiting fullscreen - Module.canvas.focus(); - // prevent this event from ever affecting the document beyond this element - e.stopPropagation(); - }); - - // helper function to prevent repeating the same code everywhere - Module._olc_ResizeCanvas = function() - { - // yes, we still have to wait, sigh.. - setTimeout(function() - { - // if default template, stretch width as well - if (Module.canvas.parentNode.className == 'emscripten_border') - Module.canvas.parentNode.style.cssText = Module._olc_EmscriptenShellCss; - - // override it's styling so we can get it's stretched size - Module.canvas.style.cssText = "width: 100%; height: 100%; outline: none;"; - - // setup custom resize event - var resizeEvent = new CustomEvent('resize', - { - detail: { - width: Module.canvas.clientWidth, - height : Module.canvas.clientHeight - }, - bubbles : true, - cancelable : true - }); - - // trigger custom resize event on canvas element - Module.canvas.dispatchEvent(resizeEvent); - }, 50); - }; - - - // Disable Refresh Gesture on mobile - document.body.style.cssText += " overscroll-behavior-y: contain;"; - - if (Module.canvas.parentNode.className == 'emscripten_border') - { - // force body to have no margin in emscripten's minimal shell - document.body.style.margin = "0"; - Module.canvas.parentNode.style.cssText = Module._olc_EmscriptenShellCss; - } - - Module._olc_ResizeCanvas(); - - // observe and react to resizing of the container element - var resizeObserver = new ResizeObserver(function(entries) {Module._olc_ResizeCanvas();}).observe(Module.canvas.parentNode); - - // observe and react to changes that occur when entering/exiting fullscreen - var mutationObserver = new MutationObserver(function(mutationsList, observer) - { - // a change has occurred, let's check them out! - for (var i = 0; i < mutationsList.length; i++) - { - // cycle through all of the newly added elements - for (var j = 0; j < mutationsList[i].addedNodes.length; j++) - { - // if this element is a our canvas, trigger resize - if (mutationsList[i].addedNodes[j].id == 'canvas') - Module._olc_ResizeCanvas(); - } - } - }).observe(Module.canvas.parentNode, - { - attributes: false, - childList : true, - subtree : false - }); - - // add resize listener on window - window.addEventListener("resize", function(e) { Module._olc_ResizeCanvas(); }); - - }, vWindowSize.x, vWindowSize.y); // Fullscreen and Resize Observers -#pragma warning restore format - return olc::rcode::OK; - } - - // Interface PGE's UpdateWindowSize, for use in Javascript - void UpdateWindowSize(int width, int height) - { - ptrPGE->olc_UpdateWindowSize(width, height); - } - - //TY Gorbit - static EM_BOOL focus_callback(int eventType, const EmscriptenFocusEvent* focusEvent, void* userData) - { - if (eventType == EMSCRIPTEN_EVENT_BLUR) - { - ptrPGE->olc_UpdateKeyFocus(false); - ptrPGE->olc_UpdateMouseFocus(false); - } - else if (eventType == EMSCRIPTEN_EVENT_FOCUS) - { - ptrPGE->olc_UpdateKeyFocus(true); - ptrPGE->olc_UpdateMouseFocus(true); - } - - return 0; - } - - //TY Moros - static EM_BOOL keyboard_callback(int eventType, const EmscriptenKeyboardEvent* e, void* userData) - { - if (eventType == EMSCRIPTEN_EVENT_KEYDOWN) - ptrPGE->olc_UpdateKeyState(mapKeys[emscripten_compute_dom_pk_code(e->code)], true); - - // THANK GOD!! for this compute function. And thanks Dandistine for pointing it out! - if (eventType == EMSCRIPTEN_EVENT_KEYUP) - ptrPGE->olc_UpdateKeyState(mapKeys[emscripten_compute_dom_pk_code(e->code)], false); - - //Consume keyboard events so that keys like F1 and F5 don't do weird things - return EM_TRUE; - } - - //TY Moros - static EM_BOOL wheel_callback(int eventType, const EmscriptenWheelEvent* e, void* userData) - { - if (eventType == EMSCRIPTEN_EVENT_WHEEL) - ptrPGE->olc_UpdateMouseWheel(-1 * e->deltaY); - - return EM_TRUE; - } - - //TY Bispoo - static EM_BOOL touch_callback(int eventType, const EmscriptenTouchEvent* e, void* userData) - { - // Move - if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) - { - ptrPGE->olc_UpdateMouse(e->touches->targetX, e->touches->targetY); - } - - // Start - if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) - { - ptrPGE->olc_UpdateMouse(e->touches->targetX, e->touches->targetY); - ptrPGE->olc_UpdateMouseState(0, true); - } - - // End - if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) - { - ptrPGE->olc_UpdateMouseState(0, false); - } - - return EM_TRUE; - } - - //TY Moros - static EM_BOOL mouse_callback(int eventType, const EmscriptenMouseEvent* e, void* userData) - { - //Mouse Movement - if (eventType == EMSCRIPTEN_EVENT_MOUSEMOVE) - ptrPGE->olc_UpdateMouse(e->targetX, e->targetY); - - - //Mouse button press - if (e->button == 0) // left click - { - if (eventType == EMSCRIPTEN_EVENT_MOUSEDOWN) - ptrPGE->olc_UpdateMouseState(0, true); - else if (eventType == EMSCRIPTEN_EVENT_MOUSEUP) - ptrPGE->olc_UpdateMouseState(0, false); - } - - if (e->button == 2) // right click - { - if (eventType == EMSCRIPTEN_EVENT_MOUSEDOWN) - ptrPGE->olc_UpdateMouseState(1, true); - else if (eventType == EMSCRIPTEN_EVENT_MOUSEUP) - ptrPGE->olc_UpdateMouseState(1, false); - - } - - if (e->button == 1) // middle click - { - if (eventType == EMSCRIPTEN_EVENT_MOUSEDOWN) - ptrPGE->olc_UpdateMouseState(2, true); - else if (eventType == EMSCRIPTEN_EVENT_MOUSEUP) - ptrPGE->olc_UpdateMouseState(2, false); - - //at the moment only middle mouse needs to consume events. - return EM_TRUE; - } - - return EM_FALSE; - } - - - virtual olc::rcode SetWindowTitle(const std::string& s) override - { emscripten_set_window_title(s.c_str()); return olc::OK; } - - virtual olc::rcode StartSystemEventLoop() override - { return olc::OK; } - - virtual olc::rcode HandleSystemEvent() override - { return olc::OK; } - - static void MainLoop() - { - olc::Platform::ptrPGE->olc_CoreUpdate(); - if (!ptrPGE->olc_IsRunning()) - { - if (ptrPGE->OnUserDestroy()) - { - emscripten_cancel_main_loop(); - platform->ApplicationCleanUp(); - } - else - { - ptrPGE->olc_Reanimate(); - } - } - } - }; - - //Emscripten needs a special Start function - //Much of this is usually done in EngineThread, but that isn't used here - olc::rcode PixelGameEngine::Start() - { - if (platform->ApplicationStartUp() != olc::OK) return olc::FAIL; - - // Construct the window - if (platform->CreateWindowPane({ 30,30 }, vWindowSize, bFullScreen) != olc::OK) return olc::FAIL; - olc_UpdateWindowSize(vWindowSize.x, vWindowSize.y); - - // Some implementations may form an event loop here - if (platform->ThreadStartUp() == olc::FAIL) return olc::FAIL; - - // Do engine context specific initialisation - olc_PrepareEngine(); - - // Consider the "thread" started - bAtomActive = true; - - // Create user resources as part of this thread - for (auto& ext : vExtensions) ext->OnBeforeUserCreate(); - if (!OnUserCreate()) bAtomActive = false; - for (auto& ext : vExtensions) ext->OnAfterUserCreate(); - - platform->StartSystemEventLoop(); - - //This causes a heap memory corruption in Emscripten for some reason - //Platform_Emscripten::bActiveRef = &bAtomActive; - emscripten_set_main_loop(&Platform_Emscripten::MainLoop, 0, 1); - - // Wait for thread to be exited - if (platform->ApplicationCleanUp() != olc::OK) return olc::FAIL; - return olc::OK; - } -} - -extern "C" -{ - EMSCRIPTEN_KEEPALIVE inline void olc_PGE_UpdateWindowSize(int width, int height) - { - emscripten_set_canvas_element_size("#canvas", width, height); - // Thanks slavka - ((olc::Platform_Emscripten*)olc::platform.get())->UpdateWindowSize(width, height); - } -} - -#endif -// O------------------------------------------------------------------------------O -// | END PLATFORM: Emscripten | -// O------------------------------------------------------------------------------O -#pragma endregion - - -#endif // Headless - -// O------------------------------------------------------------------------------O -// | olcPixelGameEngine Auto-Configuration | -// O------------------------------------------------------------------------------O -#pragma region pge_config -namespace olc -{ - void PixelGameEngine::olc_ConfigureSystem() - { - -#if !defined(OLC_PGE_HEADLESS) - -#if defined(OLC_IMAGE_GDI) - olc::Sprite::loader = std::make_unique(); -#endif - -#if defined(OLC_IMAGE_LIBPNG) - olc::Sprite::loader = std::make_unique(); -#endif - -#if defined(OLC_IMAGE_STB) - olc::Sprite::loader = std::make_unique(); -#endif - -#if defined(OLC_IMAGE_CUSTOM_EX) - olc::Sprite::loader = std::make_unique(); -#endif - - - - -#if defined(OLC_PLATFORM_WINAPI) - platform = std::make_unique(); -#endif - -#if defined(OLC_PLATFORM_X11) - platform = std::make_unique(); -#endif - -#if defined(OLC_PLATFORM_GLUT) - platform = std::make_unique(); -#endif - -#if defined(OLC_PLATFORM_EMSCRIPTEN) - platform = std::make_unique(); -#endif - -#if defined(OLC_PLATFORM_CUSTOM_EX) - platform = std::make_unique(); -#endif - - - -#if defined(OLC_GFX_OPENGL10) - renderer = std::make_unique(); -#endif - -#if defined(OLC_GFX_OPENGL33) - renderer = std::make_unique(); -#endif - -#if defined(OLC_GFX_OPENGLES2) - renderer = std::make_unique(); -#endif - -#if defined(OLC_GFX_DIRECTX10) - renderer = std::make_unique(); -#endif - -#if defined(OLC_GFX_DIRECTX11) - renderer = std::make_unique(); -#endif - -#if defined(OLC_GFX_CUSTOM_EX) - renderer = std::make_unique(); -#endif - - // Associate components with PGE instance - platform->ptrPGE = this; - renderer->ptrPGE = this; -#else - olc::Sprite::loader = nullptr; - platform = nullptr; - renderer = nullptr; -#endif - } -} - -#pragma endregion - -#endif // End OLC_PGE_APPLICATION - -// O------------------------------------------------------------------------------O -// | END OF OLC_PGE_APPLICATION | -// O------------------------------------------------------------------------------O - diff --git a/Slang/resource.h b/Slang/resource.h new file mode 100644 index 0000000..b53c88e --- /dev/null +++ b/Slang/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Slang.rc +// +#define IDI_ICON1 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif