Compare commits
10 Commits
a086558f2d
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| ce5ada7d38 | |||
| a335259edc | |||
| 037e532748 | |||
| 20f4ef1cee | |||
| 28cc82bcd7 | |||
| c344588924 | |||
| a1d0f11032 | |||
| 7e63aa1217 | |||
| 28a836ff5f | |||
| ae4372827f |
+1
-2
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
BasedOnStyle: llvm
|
BasedOnStyle: llvm
|
||||||
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||||
# https://www.cnblogs.com/PaulpauL/p/5929753.html
|
# https://www.cnblogs.com/PaulpauL/p/5929753.html
|
||||||
@@ -167,7 +166,7 @@ SortUsingDeclarations: Lexicographic
|
|||||||
SpaceAfterCStyleCast: true
|
SpaceAfterCStyleCast: true
|
||||||
SpaceAfterTemplateKeyword: false # WSO
|
SpaceAfterTemplateKeyword: false # WSO
|
||||||
SpaceAroundPointerQualifiers: Before # WSO
|
SpaceAroundPointerQualifiers: Before # WSO
|
||||||
SpacesBeforeTrailingComments: 0 # Better 1; 0 ok, 2+ better not.
|
SpacesBeforeTrailingComments: 1 # Better 1; 0 ok, 2+ better not.
|
||||||
Standard: Latest
|
Standard: Latest
|
||||||
TabWidth: 2 # 4 or 2, WSO
|
TabWidth: 2 # 4 or 2, WSO
|
||||||
# UseTab possible values:
|
# UseTab possible values:
|
||||||
|
|||||||
+70
@@ -0,0 +1,70 @@
|
|||||||
|
Checks: >
|
||||||
|
*,
|
||||||
|
-altera-id-dependent-backward-branch,
|
||||||
|
-altera-struct-pack-align,
|
||||||
|
-altera-unroll-loops,
|
||||||
|
-boost-*,
|
||||||
|
-bugprone-assignment-in-if-condition,
|
||||||
|
-bugprone-easily-swappable-parameters,
|
||||||
|
-bugprone-forward-declaration-namespace,
|
||||||
|
-bugprone-reserved-identifier,
|
||||||
|
-bugprone-unhandled-exception-at-new,
|
||||||
|
-cert-err58-cpp,
|
||||||
|
-cppcoreguidelines-avoid-*,
|
||||||
|
-cppcoreguidelines-init-variables,
|
||||||
|
-cppcoreguidelines-macro-usage,
|
||||||
|
-cppcoreguidelines-missing-std-forward,
|
||||||
|
-cppcoreguidelines-owning-memory,
|
||||||
|
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||||
|
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||||
|
-cppcoreguidelines-pro-type-member-init,
|
||||||
|
-cppcoreguidelines-pro-type-reinterpret-cast,
|
||||||
|
-cppcoreguidelines-rvalue-reference-param-not-moved,
|
||||||
|
-cppcoreguidelines-virtual-class-destructor,
|
||||||
|
-fuchsia-default-arguments-*,
|
||||||
|
-fuchsia-overloaded-operator,
|
||||||
|
-fuchsia-statically-constructed-objects,
|
||||||
|
-fuchsia-trailing-return,
|
||||||
|
-google-build-using-namespace,
|
||||||
|
-google-default-arguments,
|
||||||
|
-google-explicit-constructor,
|
||||||
|
-google-runtime-int,
|
||||||
|
-hicpp-avoid-c-arrays,
|
||||||
|
-hicpp-explicit-conversions,
|
||||||
|
-hicpp-member-init,
|
||||||
|
-hicpp-signed-bitwise,
|
||||||
|
-hicpp-use-auto,
|
||||||
|
-llvm-header-guard,
|
||||||
|
-llvm-include-order,
|
||||||
|
-llvmlibc-*,
|
||||||
|
-misc-include-cleaner,
|
||||||
|
-misc-misplaced-const,
|
||||||
|
-misc-unused-parameters,
|
||||||
|
-misc-use-internal-linkage,
|
||||||
|
-modernize-avoid-c-arrays,
|
||||||
|
-modernize-return-braced-init-list,
|
||||||
|
-modernize-use-auto,
|
||||||
|
-modernize-use-designated-initializers,
|
||||||
|
-modernize-use-ranges,
|
||||||
|
-modernize-use-trailing-return-type,
|
||||||
|
-modernize-use-using,
|
||||||
|
-performance-avoid-endl,
|
||||||
|
-performance-enum-size,
|
||||||
|
-performance-no-int-to-ptr,
|
||||||
|
-readability-avoid-unconditional-preprocessor-if,
|
||||||
|
-readability-braces-around-statements,
|
||||||
|
-readability-convert-member-functions-to-static,
|
||||||
|
-readability-function-cognitive-complexity,
|
||||||
|
-readability-identifier-length,
|
||||||
|
-readability-implicit-bool-conversion,
|
||||||
|
-readability-isolate-declaration,
|
||||||
|
-readability-magic-numbers,
|
||||||
|
-readability-math-missing-parentheses,
|
||||||
|
-readability-redundant-member-init,
|
||||||
|
-readability-use-std-min-max,
|
||||||
|
-*-braces-around-statements,
|
||||||
|
-*-exception-escape,
|
||||||
|
-*-named-parameter,
|
||||||
|
-*-non-private-member-variables-in-classes,
|
||||||
|
-*-special-member-functions,
|
||||||
|
-*-uppercase-literal-suffix
|
||||||
BIN
Binary file not shown.
+32
-25
@@ -5,39 +5,46 @@ add_definitions(-DUNICODE)
|
|||||||
add_definitions(-D_UNICODE)
|
add_definitions(-D_UNICODE)
|
||||||
add_definitions(-DCINTERFACE)
|
add_definitions(-DCINTERFACE)
|
||||||
add_definitions(-D__CARLBEKS_CMAKE_VSCODE__)
|
add_definitions(-D__CARLBEKS_CMAKE_VSCODE__)
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 26)
|
||||||
set(CMAKE_WIN32_EXECUTABLE true)
|
set(CMAKE_WIN32_EXECUTABLE true)
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||||
add_compile_options(${PROJECT_NAME} -Wno-microsoft-string-literal-from-predefined)
|
add_compile_options(${PROJECT_NAME} -Wno-microsoft-string-literal-from-predefined)
|
||||||
endif ()
|
endif ()
|
||||||
#add_compile_options(${PROJECT_NAME} /utf-8)
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||||
|
add_compile_options(/source-charset:utf-8)
|
||||||
|
add_compile_options(/execution-charset:utf-8)
|
||||||
|
endif ()
|
||||||
|
add_compile_options(/Zc:preprocessor)
|
||||||
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME} main.cpp
|
add_executable(${PROJECT_NAME}
|
||||||
def.h
|
src/main.cpp
|
||||||
InteractManager.cpp
|
|
||||||
InteractManager.h
|
src/def.cpp
|
||||||
Renderer.cpp
|
src/utils/File.cpp
|
||||||
Renderer.h
|
src/utils/exception.cpp
|
||||||
Game.cpp
|
|
||||||
Game.h
|
src/interact/InteractManager.cpp
|
||||||
Window.cpp
|
src/utils/IText.cpp
|
||||||
Window.h
|
src/game/world/Location.cpp
|
||||||
Hud.cpp
|
src/render/Renderer.cpp
|
||||||
Hud.h
|
src/render/TextureManager.cpp
|
||||||
includes.h
|
src/ui/Hud.cpp
|
||||||
IText.h
|
src/ui/Window.cpp
|
||||||
Chars.h
|
|
||||||
IText.cpp
|
src/game/Game.cpp
|
||||||
xWindows.h
|
src/game/entity/Entity.cpp
|
||||||
exception.h
|
src/game/entity/Player.cpp
|
||||||
def.cpp
|
)
|
||||||
TestCode.h
|
|
||||||
TextureManager.cpp
|
|
||||||
TextureManager.h
|
|
||||||
exception.cpp)
|
|
||||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||||
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
|
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
|
||||||
|
|
||||||
|
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/../exenv/")
|
||||||
|
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/../exenv/")
|
||||||
|
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/../exenv/")
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/../exenv/")
|
||||||
|
|
||||||
include(CPack)
|
include(CPack)
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by EmsiaetKadosh on 25-1-14.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "Game.h"
|
|
||||||
|
|
||||||
|
|
||||||
void Game::initialize() noexcept { renderer.setGame(game); }
|
|
||||||
|
|
||||||
Game::Game() : floatWindow{ new FloatWindow() } { Logger.put(L"Game created"); }
|
|
||||||
|
|
||||||
Game::~Game() { delete floatWindow; }
|
|
||||||
|
|
||||||
inline Game game = Game();
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by EmsiaetKadosh on 25-1-14.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Hud.h"
|
|
||||||
#include "Window.h"
|
|
||||||
|
|
||||||
class Game : public IRenderable {
|
|
||||||
friend void gameThread();
|
|
||||||
Hud hud = Hud();
|
|
||||||
Window* window = nullptr;
|
|
||||||
FloatWindow* floatWindow;
|
|
||||||
QWORD currentTick = 0;
|
|
||||||
public:
|
|
||||||
explicit Game();
|
|
||||||
~Game() override;
|
|
||||||
static void initialize() noexcept;
|
|
||||||
|
|
||||||
void render() const noexcept override {
|
|
||||||
renderer.gameStartRender();
|
|
||||||
hud.render();
|
|
||||||
if (window) window->render();
|
|
||||||
if (floatWindow) floatWindow->render();
|
|
||||||
renderer.gameEndRender();
|
|
||||||
}
|
|
||||||
|
|
||||||
int setWindow(Window* window) noexcept {
|
|
||||||
if (this->window == window) Success();
|
|
||||||
if (this->window) this->window->onClose();
|
|
||||||
this->window = nullptr;
|
|
||||||
if (window && window->onOpen()) {
|
|
||||||
this->window = window;
|
|
||||||
Success();
|
|
||||||
}
|
|
||||||
Failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] Window* getWindow() const noexcept { return window; }
|
|
||||||
[[nodiscard]] QWORD getTick() const noexcept { return currentTick; }
|
|
||||||
|
|
||||||
void tick() noexcept {
|
|
||||||
++currentTick;
|
|
||||||
hud.tick();
|
|
||||||
if (window) window->tick();
|
|
||||||
if (floatWindow) floatWindow->tick();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
extern Game game;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by EmsiaetKadosh on 25-1-18.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "IText.h"
|
|
||||||
|
|
||||||
#include "def.h"
|
|
||||||
|
|
||||||
const String& TranslatableText::getText() const noexcept {
|
|
||||||
refreshText();
|
|
||||||
return target->getText();
|
|
||||||
}
|
|
||||||
|
|
||||||
const RenderableString& TranslatableText::getRenderableString() const noexcept {
|
|
||||||
refreshText();
|
|
||||||
return target->getRenderableString();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TranslatableText::refreshText() const noexcept {
|
|
||||||
if (target == nullptr || langConfig == translator.getConfigVersion()) {
|
|
||||||
target = translator.getText(idSrc);
|
|
||||||
langConfig = translator.getConfigVersion();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,395 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by EmsiaetKadosh on 25-1-16.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "Chars.h"
|
|
||||||
#include "def.h"
|
|
||||||
|
|
||||||
class LiteralText;
|
|
||||||
class Translator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注意一些默认值。
|
|
||||||
* FontID=0,表示继承使用默认字体;
|
|
||||||
* color和background=0xffffffff,表示继承使用默认颜色;
|
|
||||||
*/
|
|
||||||
class RenderableString {
|
|
||||||
friend class LiteralText;
|
|
||||||
|
|
||||||
struct StringConfig {
|
|
||||||
String text;
|
|
||||||
/**
|
|
||||||
* 注意。-1也即0xffffffff被用于标记使用传入的默认颜色。
|
|
||||||
* 其余,以0xAARRGGBB的形式标记颜色。
|
|
||||||
*/
|
|
||||||
unsigned int color = -1;
|
|
||||||
/**
|
|
||||||
* 注意。-1也即0xffffffff被用于标记使用传入的默认颜色。
|
|
||||||
* 其余,以0xAARRGGBB的形式标记颜色。
|
|
||||||
*/
|
|
||||||
unsigned int background = -1;
|
|
||||||
wchar idFont = 0;
|
|
||||||
bool bold = false;
|
|
||||||
bool italic = false;
|
|
||||||
bool underline = false;
|
|
||||||
bool strikeThrough = false;
|
|
||||||
|
|
||||||
explicit StringConfig() = default;
|
|
||||||
|
|
||||||
void reset() noexcept {
|
|
||||||
idFont = 0;
|
|
||||||
color = -1;
|
|
||||||
background = -1;
|
|
||||||
bold = false;
|
|
||||||
italic = false;
|
|
||||||
underline = false;
|
|
||||||
strikeThrough = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] StringConfig copyConfig() const noexcept {
|
|
||||||
StringConfig ret;
|
|
||||||
ret.idFont = idFont;
|
|
||||||
ret.color = color;
|
|
||||||
ret.background = background;
|
|
||||||
ret.bold = bold;
|
|
||||||
ret.italic = italic;
|
|
||||||
ret.underline = underline;
|
|
||||||
ret.strikeThrough = strikeThrough;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] StringConfig copy() const noexcept {
|
|
||||||
StringConfig ret;
|
|
||||||
ret.idFont = idFont;
|
|
||||||
ret.text = text;
|
|
||||||
ret.idFont = idFont;
|
|
||||||
ret.color = color;
|
|
||||||
ret.background = background;
|
|
||||||
ret.bold = bold;
|
|
||||||
ret.italic = italic;
|
|
||||||
ret.underline = underline;
|
|
||||||
ret.strikeThrough = strikeThrough;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] String toString() const noexcept {
|
|
||||||
String ret;
|
|
||||||
ret.append(L"#");
|
|
||||||
ret.append(uitowb16(color));
|
|
||||||
ret.append(L".");
|
|
||||||
ret.append(uitowb16(background));
|
|
||||||
ret.append(L",F");
|
|
||||||
ret.append(std::to_wstring(idFont));
|
|
||||||
ret.append(L",");
|
|
||||||
if (bold) ret.append(L"=");
|
|
||||||
if (italic) ret.append(L"/");
|
|
||||||
if (underline) ret.append(L"_");
|
|
||||||
if (strikeThrough) ret.append(L"-");
|
|
||||||
ret.append(L":");
|
|
||||||
ret.append(text);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
List<StringConfig> configs;
|
|
||||||
using Iterator = List<StringConfig>::iterator;
|
|
||||||
using ConstIterator = List<StringConfig>::const_iterator;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit RenderableString(const String& string): RenderableString(string.c_str(), string.length()) {}
|
|
||||||
explicit RenderableString(String&& string) : RenderableString(string.c_str(), string.length()) {}
|
|
||||||
|
|
||||||
explicit RenderableString(const wchar* const string, const QWORD length = static_cast<QWORD>(-1)) {
|
|
||||||
if (length == -1) parseAppend(string);
|
|
||||||
else parseAppend(string, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] String toString() const noexcept {
|
|
||||||
String ret;
|
|
||||||
for (const auto& config : configs) {
|
|
||||||
ret.append(config.toString());
|
|
||||||
ret.append(L";\n");
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderableString& append(const RenderableString& other) {
|
|
||||||
auto iterator = other.configs.cbegin();
|
|
||||||
int flags = 0;
|
|
||||||
bool clr = true, bg = true, font = true;
|
|
||||||
unsigned int color = configs.back().color;
|
|
||||||
unsigned int background = configs.back().background;
|
|
||||||
wchar idFont = configs.back().idFont;
|
|
||||||
while (iterator != other.configs.cend()) {
|
|
||||||
if (flags >= 3) break;
|
|
||||||
StringConfig config = iterator->copy();
|
|
||||||
if (font) {
|
|
||||||
if (config.idFont == idFont || config.idFont != 0) {
|
|
||||||
font = false;
|
|
||||||
++flags;
|
|
||||||
} else config.idFont = idFont;
|
|
||||||
}
|
|
||||||
if (clr) {
|
|
||||||
if (config.color == color || config.color != 0xffffffff) {
|
|
||||||
clr = false;
|
|
||||||
++flags;
|
|
||||||
} else config.color = color;
|
|
||||||
}
|
|
||||||
if (bg) {
|
|
||||||
if (config.background == background || config.background != 0xffffffff) {
|
|
||||||
bg = false;
|
|
||||||
++flags;
|
|
||||||
} else config.background = background;
|
|
||||||
}
|
|
||||||
configs.push_back(std::move(config));
|
|
||||||
++iterator;
|
|
||||||
}
|
|
||||||
while (iterator != other.configs.cend()) configs.push_back(iterator->copy());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderableString& append(const String& other) {
|
|
||||||
parseAppend(other.c_str(), other.length());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void parseAppend(const wchar* string) noexcept {
|
|
||||||
StringConfig config;
|
|
||||||
if (!configs.empty()) {
|
|
||||||
config = configs.back();
|
|
||||||
configs.pop_back();
|
|
||||||
}
|
|
||||||
while (*string != L'\0') {
|
|
||||||
const wchar* start = string;
|
|
||||||
if (!config.text.empty()) {
|
|
||||||
configs.push_back(std::move(config));
|
|
||||||
config = config.copyConfig();
|
|
||||||
}
|
|
||||||
while (*string != L'\0' && *string != '\\') ++string;
|
|
||||||
if (*string == L'\0') {
|
|
||||||
if (*start != L'\0') config.text.append(start, string);
|
|
||||||
configs.push_back(std::move(config));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (start != string) config.text.append(start, string);
|
|
||||||
if (*++string == L'\0') {
|
|
||||||
config.text.append(start, string);
|
|
||||||
configs.push_back(std::move(config));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!config.text.empty()) {
|
|
||||||
configs.push_back(std::move(config));
|
|
||||||
config = config.copyConfig();
|
|
||||||
}
|
|
||||||
switch (*string) {
|
|
||||||
case L'\\':
|
|
||||||
config.text.append(1, L'\\');
|
|
||||||
++string;
|
|
||||||
continue;
|
|
||||||
case L'#': {
|
|
||||||
unsigned long long i = 0;
|
|
||||||
while (i < 9) if (string[i++] == L'\0') goto end;
|
|
||||||
config.color = wtouib16(++string, 8);
|
|
||||||
string += 8;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
case L'.': {
|
|
||||||
unsigned long long i = 0;
|
|
||||||
while (i < 9) if (string[i++] == L'\0') goto end;
|
|
||||||
config.background = wtouib16(++string, 8);
|
|
||||||
string += 8;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
case L'F':
|
|
||||||
case L'f':
|
|
||||||
if (*++string == L'\0') goto end;
|
|
||||||
config.idFont = *string;
|
|
||||||
break;
|
|
||||||
case L'-':
|
|
||||||
case L's':
|
|
||||||
case L'S':
|
|
||||||
config.strikeThrough = true;
|
|
||||||
break;
|
|
||||||
case L'_':
|
|
||||||
case L'u':
|
|
||||||
case L'U':
|
|
||||||
config.underline = true;
|
|
||||||
break;
|
|
||||||
case L'/':
|
|
||||||
case L'i':
|
|
||||||
case L'I':
|
|
||||||
config.italic = true;
|
|
||||||
break;
|
|
||||||
case L'=':
|
|
||||||
case L'b':
|
|
||||||
case L'B':
|
|
||||||
config.bold = true;
|
|
||||||
break;
|
|
||||||
case L'r':
|
|
||||||
case L'R':
|
|
||||||
if (!config.text.empty()) configs.push_back(std::move(config));
|
|
||||||
config = StringConfig();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
config.text.append(string - 1, 2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++string;
|
|
||||||
}
|
|
||||||
end:
|
|
||||||
configs.push_back(std::move(config));
|
|
||||||
}
|
|
||||||
|
|
||||||
void parseAppend(const wchar* string, const QWORD length) noexcept {
|
|
||||||
StringConfig config = StringConfig();
|
|
||||||
if (!configs.empty()) {
|
|
||||||
config = configs.back();
|
|
||||||
configs.pop_back();
|
|
||||||
}
|
|
||||||
const wchar* const end = string + length;
|
|
||||||
while (string < end) {
|
|
||||||
const wchar* start = string;
|
|
||||||
if (!config.text.empty()) {
|
|
||||||
configs.push_back(std::move(config));
|
|
||||||
config = config.copyConfig();
|
|
||||||
}
|
|
||||||
while (string < end && *string != '\\') ++string;
|
|
||||||
if (string >= end) {
|
|
||||||
if (start != end) config.text.append(start, end);
|
|
||||||
configs.push_back(std::move(config));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (start != string) config.text.append(start, string);
|
|
||||||
if (++string == end) {
|
|
||||||
config.text.append(start, end);
|
|
||||||
configs.push_back(std::move(config));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!config.text.empty()) {
|
|
||||||
configs.push_back(std::move(config));
|
|
||||||
config = config.copyConfig();
|
|
||||||
}
|
|
||||||
switch (*string) {
|
|
||||||
case L'\\':
|
|
||||||
config.text.append(1, L'\\');
|
|
||||||
++string;
|
|
||||||
continue;
|
|
||||||
case L'#': {
|
|
||||||
if (end - string < 9) break;
|
|
||||||
config.color = wtouib16(++string, 8);
|
|
||||||
string += 8;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
case L'.': {
|
|
||||||
if (end - string < 9) break;
|
|
||||||
config.background = wtouib16(++string, 8);
|
|
||||||
string += 8;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
case L'F':
|
|
||||||
case L'f':
|
|
||||||
if (++string == end) break;
|
|
||||||
config.idFont = *string;
|
|
||||||
break;
|
|
||||||
case L'-':
|
|
||||||
case L's':
|
|
||||||
case L'S':
|
|
||||||
config.strikeThrough = true;
|
|
||||||
break;
|
|
||||||
case L'_':
|
|
||||||
case L'u':
|
|
||||||
case L'U':
|
|
||||||
config.underline = true;
|
|
||||||
break;
|
|
||||||
case L'/':
|
|
||||||
case L'i':
|
|
||||||
case L'I':
|
|
||||||
config.italic = true;
|
|
||||||
break;
|
|
||||||
case L'=':
|
|
||||||
case L'b':
|
|
||||||
case L'B':
|
|
||||||
config.bold = true;
|
|
||||||
break;
|
|
||||||
case L'r':
|
|
||||||
case L'R':
|
|
||||||
if (!config.text.empty()) configs.push_back(std::move(config));
|
|
||||||
config = StringConfig();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
config.text.append(string - 1, 2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++string;
|
|
||||||
}
|
|
||||||
configs.push_back(std::move(config));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
interface IText {
|
|
||||||
virtual ~IText() = default;
|
|
||||||
virtual const String& getText() const noexcept = 0;
|
|
||||||
virtual const RenderableString& getRenderableString() const noexcept = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef class LiteralText : public IText {
|
|
||||||
const String string;
|
|
||||||
mutable RenderableString renderableString;
|
|
||||||
|
|
||||||
public:
|
|
||||||
LiteralText(const String& string): string(string), renderableString(string) {}
|
|
||||||
LiteralText(String&& string): string(std::move(string)), renderableString(string) {}
|
|
||||||
|
|
||||||
const String& getText() const noexcept override { return string; }
|
|
||||||
|
|
||||||
const RenderableString& getRenderableString() const noexcept override { return renderableString; }
|
|
||||||
} TranslatedText;
|
|
||||||
|
|
||||||
class TranslatableText final : public IText {
|
|
||||||
const String idSrc;
|
|
||||||
mutable const LiteralText* target = nullptr;
|
|
||||||
mutable QWORD langConfig = 0;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit TranslatableText(const String& id) : idSrc(id) { std::wcout << L"Lvalue init\n"; }
|
|
||||||
explicit TranslatableText(String&& id) : idSrc(std::move(id)) { std::wcout << L"Rvalue init\n"; }
|
|
||||||
const String& getText() const noexcept override;
|
|
||||||
const RenderableString& getRenderableString() const noexcept override;
|
|
||||||
void refreshText() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Language {
|
|
||||||
Map<String, TranslatedText> translateTable;
|
|
||||||
int id = 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Translator {
|
|
||||||
Map<String, int> langMap{};
|
|
||||||
List<Language> langList{};
|
|
||||||
TranslatedText nullText{ L"\\#FF""EE0000<translator-null>" };
|
|
||||||
String lang = L"zh-cn";
|
|
||||||
int langConfig = 1;
|
|
||||||
int idLangMax = 1;
|
|
||||||
using IterID = Map<String, TranslatedText>::const_iterator;
|
|
||||||
using IterLang = List<Language>::const_iterator;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit Translator() { langMap.insert(std::make_pair(String(L"zh-cn"), 1)); }
|
|
||||||
void addLang(const String& lang) noexcept { langMap.insert(std::make_pair(lang, ++idLangMax)); }
|
|
||||||
void addLang(String&& lang) noexcept { langMap.insert(std::make_pair(lang, ++idLangMax)); }
|
|
||||||
void loadLang();
|
|
||||||
|
|
||||||
[[nodiscard]] const TranslatedText* getText(const String& id) const noexcept {
|
|
||||||
for (const auto& [translateTable, _] : langList) {
|
|
||||||
IterID iterator = translateTable.find(id);
|
|
||||||
if (iterator != translateTable.cend()) return &iterator->second;
|
|
||||||
}
|
|
||||||
return &nullText;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] int getConfigVersion() const noexcept { return langConfig; }
|
|
||||||
};
|
|
||||||
|
|
||||||
inline static Translator translator = Translator();
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by EmsiaetKadosh on 25-1-14.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "def.h"
|
|
||||||
#include "hbp.h"
|
|
||||||
|
|
||||||
struct KeyStatus {
|
|
||||||
String name;
|
|
||||||
unsigned int pressTimes = 0;
|
|
||||||
bool pressed = false;
|
|
||||||
bool notDealt = false;
|
|
||||||
|
|
||||||
[[nodiscard]] bool isPressed() const noexcept { return pressed; }
|
|
||||||
[[nodiscard]] unsigned int wasPressed() const noexcept { return pressTimes; }
|
|
||||||
|
|
||||||
void deals() noexcept {
|
|
||||||
notDealt = false;
|
|
||||||
pressTimes = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] String toString() const noexcept { return L"KeyStatus: { name = \"" + name + L"\"; pressTimes = " + std::to_wstring(pressTimes) + L"; pressed = " + (pressed ? L"true; }" : L"false; }"); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MouseStatus {
|
|
||||||
String name;
|
|
||||||
unsigned int pressTimes = 0;
|
|
||||||
bool pressed = false;
|
|
||||||
bool notDealt = false;
|
|
||||||
bool longHold = false;
|
|
||||||
|
|
||||||
[[nodiscard]] bool isPressed() const noexcept { return pressed; }
|
|
||||||
[[nodiscard]] unsigned int wasPressed() const noexcept { return pressTimes; }
|
|
||||||
|
|
||||||
void deals() noexcept {
|
|
||||||
notDealt = false;
|
|
||||||
pressTimes = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] String toString() const noexcept { return L"MouseStatus: { name = \""+ name + L"\"; pressTimes = " + std::to_wstring(pressTimes) + L"; pressed = " + (pressed ? L"true, longHold = " : L"false, longHold = ") + (longHold ? L"true; }" : L"false; }") ; }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct KeyBinding;
|
|
||||||
|
|
||||||
class InteractManager {
|
|
||||||
inline static TRACKMOUSEEVENT trackMouseEvent{
|
|
||||||
.cbSize = sizeof(TRACKMOUSEEVENT),
|
|
||||||
.dwFlags = TME_HOVER | TME_LEAVE | TME_NONCLIENT,
|
|
||||||
.hwndTrack = nullptr,
|
|
||||||
.dwHoverTime = 0
|
|
||||||
};
|
|
||||||
KeyStatus keyStatus[256];
|
|
||||||
int mouseX = 0, mouseY = 0;
|
|
||||||
int mouseWheel = 0;
|
|
||||||
int rebindResult = 0;
|
|
||||||
bool rebinding = false;
|
|
||||||
bool inWindow = false;// 鼠标是否在窗口内部
|
|
||||||
bool hovering = false;
|
|
||||||
|
|
||||||
public:
|
|
||||||
static void initialize() noexcept { trackMouseEvent.hwndTrack = MainWindowHandle; }
|
|
||||||
explicit InteractManager();
|
|
||||||
|
|
||||||
void update(const int keyCode, const bool isPressed) noexcept {
|
|
||||||
if (keyCode >= 256) return;
|
|
||||||
if (rebinding) {
|
|
||||||
rebindResult = keyCode;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
keyStatus[keyCode].pressed = isPressed;
|
|
||||||
if (isPressed) {
|
|
||||||
keyStatus[keyCode].pressTimes++;
|
|
||||||
keyStatus[keyCode].notDealt = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateMouse(const int x, const int y) noexcept {
|
|
||||||
mouseX = x;
|
|
||||||
mouseY = y;
|
|
||||||
inWindow = true;
|
|
||||||
hovering = false;
|
|
||||||
if (!TrackMouseEvent(&trackMouseEvent)) std::wcout << L"TrackMouseEvent failed. LastError: " << GetLastError() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void mouseHover() noexcept {
|
|
||||||
hovering = true;
|
|
||||||
inWindow = true;
|
|
||||||
std::wcout << L"hover" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void mouseLeave() noexcept {
|
|
||||||
inWindow = false;
|
|
||||||
hovering = false;
|
|
||||||
std::wcout << L"leave" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateWheel(const int wheel) noexcept { mouseWheel += wheel; }
|
|
||||||
[[nodiscard]] int getMouseX() const noexcept { return mouseX; }
|
|
||||||
[[nodiscard]] int getMouseY() const noexcept { return mouseY; }
|
|
||||||
[[nodiscard]] int getMouseWheel() const noexcept { return mouseWheel; }
|
|
||||||
[[nodiscard]] bool isHovering() const noexcept { return hovering; }
|
|
||||||
[[nodiscard]] bool isInWindow() const noexcept { return inWindow; }
|
|
||||||
KeyStatus& getKey(const int keyCode) noexcept { return keyStatus[keyCode]; }
|
|
||||||
KeyStatus& getKey(const KeyBinding& binding) noexcept;
|
|
||||||
|
|
||||||
int dealMouseWheel() noexcept {
|
|
||||||
const int ret = mouseWheel;
|
|
||||||
mouseWheel = 0;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline static InteractManager interactManager = InteractManager();
|
|
||||||
|
|
||||||
struct KeyBinding {
|
|
||||||
String id;
|
|
||||||
int keyCode;
|
|
||||||
[[nodiscard]] bool isPressed() const noexcept { return interactManager.getKey(keyCode).isPressed(); }
|
|
||||||
[[nodiscard]] unsigned int wasPressed() const noexcept { return interactManager.getKey(keyCode).wasPressed(); }
|
|
||||||
void deals() const noexcept { interactManager.getKey(keyCode).deals(); }
|
|
||||||
};
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by EmsiaetKadosh on 25-1-14.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "Renderer.h"
|
|
||||||
|
|
||||||
#include "Game.h"
|
|
||||||
#include "hbp.h"
|
|
||||||
|
|
||||||
void Renderer::initialize() noexcept {
|
|
||||||
MainDC = GetDC(MainWindowHandle);
|
|
||||||
assistDC = CreateCompatibleDC(MainDC);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::resize(const int width, const int height) noexcept(false) {
|
|
||||||
windowWidth = width;
|
|
||||||
windowHeight = height;
|
|
||||||
if (resizeTime > 0) return; // 防抖、防止频繁触发
|
|
||||||
if (resizeTime < 0) {
|
|
||||||
resizeTime = 20;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (game.getWindow()) game.getWindow()->onResize();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Renderer renderer = Renderer();
|
|
||||||
-117
@@ -1,117 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by EmsiaetKadosh on 25-1-14.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include "def.h"
|
|
||||||
#include "exception.h"
|
|
||||||
|
|
||||||
class Game;
|
|
||||||
/**
|
|
||||||
* 用于标记相对位置。
|
|
||||||
*/
|
|
||||||
enum class Location : char { LEFT_TOP, LEFT, LEFT_BOTTOM, TOP, CENTER, BOTTOM, RIGHT_TOP, RIGHT, RIGHT_BOTTOM };
|
|
||||||
|
|
||||||
interface IRenderable {
|
|
||||||
virtual ~IRenderable() = default;
|
|
||||||
virtual void render() const noexcept = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
interface ITickable {
|
|
||||||
virtual ~ITickable() = default;
|
|
||||||
virtual void tick() noexcept = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Color {
|
|
||||||
unsigned int inactive = 0xff777777;
|
|
||||||
unsigned int active = 0xff000000;
|
|
||||||
unsigned int hover = 0xff444444;
|
|
||||||
unsigned int clicked = 0xffeeeeee;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline static constexpr Color TextColor = {
|
|
||||||
.inactive = 0xff333333,
|
|
||||||
.active = 0xffeeeeee,
|
|
||||||
.hover = 0xffeeeeee,
|
|
||||||
.clicked = 0xff000000
|
|
||||||
};
|
|
||||||
|
|
||||||
class Renderer final : public ITickable {
|
|
||||||
/**
|
|
||||||
* 后续可能用不到,可能可删
|
|
||||||
*/
|
|
||||||
Game* pGame = nullptr;
|
|
||||||
|
|
||||||
friend class Game;
|
|
||||||
inline static HDC MainDC;
|
|
||||||
inline static HDC assistDC;
|
|
||||||
inline static BLENDFUNCTION blendFunction = {
|
|
||||||
.BlendOp = AC_SRC_OVER,// Only
|
|
||||||
.BlendFlags = 0,// Must 0
|
|
||||||
.SourceConstantAlpha = 255,// 预乘
|
|
||||||
.AlphaFormat = AC_SRC_ALPHA,// Only
|
|
||||||
};
|
|
||||||
std::thread::id renderThread = std::this_thread::get_id();
|
|
||||||
int windowWidth = 0, windowHeight = 0;
|
|
||||||
int resizeTime = 0;
|
|
||||||
bool isRendering = false;
|
|
||||||
|
|
||||||
void gameStartRender() noexcept {
|
|
||||||
isRendering = true;
|
|
||||||
renderThread = std::this_thread::get_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
void gameEndRender() noexcept { isRendering = false; }
|
|
||||||
|
|
||||||
public:
|
|
||||||
static void initialize() noexcept;
|
|
||||||
explicit Renderer() { Logger.put(L"Renderer created"); }
|
|
||||||
void resize(int width, int height) noexcept(false);
|
|
||||||
void setGame(Game& game) noexcept { pGame = &game; }
|
|
||||||
[[nodiscard]] int getWidth() const noexcept { return windowWidth; }
|
|
||||||
[[nodiscard]] int getHeight() const noexcept { return windowHeight; }
|
|
||||||
|
|
||||||
void tick() noexcept override {
|
|
||||||
// 联合处理resize防抖
|
|
||||||
if (resizeTime > 0) --resizeTime;
|
|
||||||
else if (resizeTime == 0) {
|
|
||||||
resize(windowWidth, windowHeight);
|
|
||||||
resizeTime = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @attention 会忽略A透明度值
|
|
||||||
* @param argb ARGB式颜色
|
|
||||||
* @return int BGR式颜色
|
|
||||||
*/
|
|
||||||
[[nodiscard]] static unsigned int changeColorFormat(const unsigned int argb) { return (argb & 0xff) << 16 | (argb & 0xff00) | (argb & 0xff0000) >> 16; }
|
|
||||||
|
|
||||||
void assertRendering() const noexcept(false) { if (!isRendering) throw InvalidOperationException(L"Operation should be done while rendering"); }
|
|
||||||
|
|
||||||
void assertRenderThread() const noexcept(false) { if (std::this_thread::get_id() != renderThread) throw InvalidOperationException(L"Operation should be done in render thread"); }
|
|
||||||
|
|
||||||
void fill(const int x, const int y, const int w, const int h, const unsigned int color) const {
|
|
||||||
assertRendering();
|
|
||||||
//assertRenderThread();
|
|
||||||
if (color & 0xff000000 == 0) return;
|
|
||||||
const RECT rect{
|
|
||||||
.left = x,
|
|
||||||
.top = y,
|
|
||||||
.right = x + w,
|
|
||||||
.bottom = y + h
|
|
||||||
};
|
|
||||||
HBRUSH clr = CreateSolidBrush(changeColorFormat(color));
|
|
||||||
if ((color & 0xff000000) == 0xff000000) FillRect(MainDC, &rect, clr);
|
|
||||||
else {
|
|
||||||
FillRect(assistDC, &rect, clr);
|
|
||||||
AlphaBlend(MainDC, x, y, w, h, assistDC, x, y, w, h, blendFunction);
|
|
||||||
}
|
|
||||||
DeleteObject(clr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
extern Renderer renderer;
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by EmsiaetKadosh on 25-1-21.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "def.h"
|
|
||||||
#include "exception.h"
|
|
||||||
|
|
||||||
class ITexture {};
|
|
||||||
|
|
||||||
class Font {
|
|
||||||
String name;
|
|
||||||
HFONT fonts[16]{};
|
|
||||||
|
|
||||||
static int idOf(const bool italic, const bool bold, const bool underline, const bool strike) {
|
|
||||||
int ret = 0;
|
|
||||||
if (italic) ret |= 8;
|
|
||||||
if (bold) ret |= 4;
|
|
||||||
if (underline) ret |= 2;
|
|
||||||
if (strike) ret |= 1;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit Font(const String& name) : name(name) {}
|
|
||||||
|
|
||||||
void load(const int size) noexcept(false) {
|
|
||||||
LOGFONTW font{
|
|
||||||
.lfHeight = size,
|
|
||||||
.lfWidth = 0,
|
|
||||||
.lfEscapement = 0,
|
|
||||||
.lfOrientation = 0,
|
|
||||||
.lfCharSet = ANSI_CHARSET,
|
|
||||||
.lfOutPrecision = OUT_DEFAULT_PRECIS,
|
|
||||||
.lfClipPrecision = CLIP_DEFAULT_PRECIS,
|
|
||||||
.lfQuality = DEFAULT_QUALITY,
|
|
||||||
.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE,
|
|
||||||
};
|
|
||||||
if (name.size() < LF_FACESIZE) memcpy(font.lfFaceName, name.c_str(), name.size());
|
|
||||||
else throw ArrayIndexOutOfBoundException(L"Font name is too long: " + name);
|
|
||||||
for (int i = 0; i < 16; i++) {
|
|
||||||
font.lfWeight = i & 4 ? 700 : 400;
|
|
||||||
font.lfItalic = i & 8;
|
|
||||||
font.lfUnderline = i & 2;
|
|
||||||
font.lfStrikeOut = i & 1;
|
|
||||||
fonts[i] = CreateFontIndirectW(&font);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void unload() noexcept(false) {
|
|
||||||
for (int i = 0; i < 16; ++i) {
|
|
||||||
DeleteObject(fonts[i]);
|
|
||||||
fonts[i] = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class TextureManager {
|
|
||||||
Map<int, Font> fonts;
|
|
||||||
Map<String, ObjectHolder<ITexture>> textures;
|
|
||||||
Font* defaultFont = nullptr;
|
|
||||||
using IterFont = Map<int, Font>::const_iterator;
|
|
||||||
using IterTexture = Map<String, ObjectHolder<ITexture>>::const_iterator;
|
|
||||||
|
|
||||||
public:
|
|
||||||
TextureManager() {
|
|
||||||
try {
|
|
||||||
fonts.insert(std::make_pair(0, Font(L"Arial")));
|
|
||||||
defaultFont = &fonts.at(0);
|
|
||||||
} catch (const std::out_of_range& e) {
|
|
||||||
|
|
||||||
} catch (const ArrayIndexOutOfBoundException& e){}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Font& getFont(const int id) const noexcept {
|
|
||||||
IterFont iterator = fonts.find(id);
|
|
||||||
if (iterator == fonts.cend()) return *defaultFont;
|
|
||||||
return iterator->second;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline static TextureManager textureManager = TextureManager();
|
|
||||||
-13
@@ -1,13 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by EmsiaetKadosh on 25-1-14.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "Window.h"
|
|
||||||
void Window::render() const noexcept {}
|
|
||||||
void Window::tick() noexcept {}
|
|
||||||
void Window::onResize() {}
|
|
||||||
void FloatWindow::render() const noexcept {
|
|
||||||
renderer.fill(20, 20, 40, 40, 0xffeeeeee);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Button::render() const noexcept {}
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by EmsiaetKadosh on 25-1-14.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "def.h"
|
|
||||||
#include "Renderer.h"
|
|
||||||
#include "IText.h"
|
|
||||||
|
|
||||||
enum class MouseActionCode : char {
|
|
||||||
MAC_MOVE, MAC_HOVER, MAC_DOWN, MAC_UP, MAC_DOUBLE
|
|
||||||
};
|
|
||||||
|
|
||||||
class Widget : public IRenderable, public ITickable {
|
|
||||||
protected:
|
|
||||||
int left = 0, top = 0, width = 0, height = 0;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using Action = Function<void(int)>;
|
|
||||||
double x, y, w, h;
|
|
||||||
Action hover;// 传入int忽略
|
|
||||||
Action longHover;// 传入int表示时间
|
|
||||||
Action mouseDown;// 传入int表示变更按键。0左, 1中, 2右
|
|
||||||
Action mouseUp;// 传入int表示变更按键。0左, 1中, 2右
|
|
||||||
Action mouseLeave;// 传入int忽略
|
|
||||||
Action mouseClick;// 传入int表示变更按键。0x0左, 0x1中, 0x2右;0xf表示是否双击
|
|
||||||
Color backgroundColor;
|
|
||||||
Color foregroundColor;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
mutable bool hasMouse = false;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Location location;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
char unused[6] {};
|
|
||||||
|
|
||||||
public:
|
|
||||||
unsigned int colorSelector(const Color& clr, const Widget& widget) const {
|
|
||||||
return hasMouse ? clr.hover : clr.inactive;
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit Widget(const double x, const double y, const double w, const double h, Location location) : x(x), y(y), w(w), h(h), location(location) {}
|
|
||||||
virtual void onResize() {}
|
|
||||||
|
|
||||||
virtual bool isMouseIn(int x, int y) noexcept {
|
|
||||||
x -= left;
|
|
||||||
y -= top;
|
|
||||||
return 0 <= x and x <= width and 0 <= y and y <= height;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void onHover(const int value) noexcept { if (hover) hover(value); }
|
|
||||||
virtual void onLongHover(const int value) noexcept { if (longHover) longHover(value); }
|
|
||||||
virtual void onMouseDown(const int value) noexcept { if (mouseDown) mouseDown(value); }
|
|
||||||
virtual void onMouseUp(const int value) noexcept { if (mouseUp) mouseUp(value); }
|
|
||||||
virtual void onMouseLeave(const int value) noexcept { if (mouseLeave) mouseLeave(value); }
|
|
||||||
virtual void onMouseClick(const int value) noexcept { if (mouseClick) mouseClick(value); }
|
|
||||||
|
|
||||||
virtual void passEvent(const int action, const int value, const int x, const int y) noexcept {
|
|
||||||
if (!isMouseIn(x, y)) {
|
|
||||||
if (hasMouse) onMouseLeave(0);
|
|
||||||
hasMouse = false;
|
|
||||||
}
|
|
||||||
hasMouse = true;
|
|
||||||
switch (static_cast<char>(action & 0xf)) {
|
|
||||||
case static_cast<char>(MouseActionCode::MAC_HOVER):
|
|
||||||
onLongHover(0);
|
|
||||||
break;
|
|
||||||
case static_cast<char>(MouseActionCode::MAC_MOVE):
|
|
||||||
onHover(0);
|
|
||||||
break;
|
|
||||||
case static_cast<char>(MouseActionCode::MAC_DOWN):
|
|
||||||
onMouseDown(value);
|
|
||||||
break;
|
|
||||||
case static_cast<char>(MouseActionCode::MAC_UP):
|
|
||||||
onMouseUp(value);
|
|
||||||
break;
|
|
||||||
case static_cast<char>(MouseActionCode::MAC_DOUBLE):
|
|
||||||
onMouseClick(1);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Window : public IRenderable, public ITickable {
|
|
||||||
protected:
|
|
||||||
List<Widget*> widgets;
|
|
||||||
|
|
||||||
Window() = default;
|
|
||||||
|
|
||||||
~Window() override { for (Widget*& widget : widgets) { delete widget; } }
|
|
||||||
|
|
||||||
public:
|
|
||||||
void render() const noexcept override;
|
|
||||||
void tick() noexcept override;
|
|
||||||
/**
|
|
||||||
* 在Game.setWindow()时,本窗口开启时调用。
|
|
||||||
* 不应当外部调用。
|
|
||||||
* 如果返回false,则拒绝设置窗口。
|
|
||||||
* @return 是否允许将显示窗口设为自身
|
|
||||||
*/
|
|
||||||
virtual bool onOpen() = 0;
|
|
||||||
/**
|
|
||||||
* 在Game.setWindow()时,本窗口关闭时调用。
|
|
||||||
* 不应当外部调用。
|
|
||||||
* 注意,关闭未必就是删除。
|
|
||||||
*/
|
|
||||||
virtual void onClose() = 0;
|
|
||||||
virtual void onResize();
|
|
||||||
};
|
|
||||||
|
|
||||||
class FloatWindow final : public Window {
|
|
||||||
public:
|
|
||||||
void render() const noexcept override;
|
|
||||||
bool onOpen() override { return true; }
|
|
||||||
void onClose() override {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Button : public Widget {
|
|
||||||
public:
|
|
||||||
ObjectHolder<IText> name;
|
|
||||||
explicit Button(const double x, const double y, const double w, const double h, const Location location, const ObjectHolder<IText>& text) : Widget(x, y, w, h, location), name(text) {}
|
|
||||||
void render() const noexcept override;
|
|
||||||
void tick() noexcept override {}
|
|
||||||
};
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <winuser.h>
|
|
||||||
|
|
||||||
#include "../CarlbeksLib/gxdef.h"
|
|
||||||
#include "../CarlbeksLib/rgui.h"
|
|
||||||
#include "../CarlbeksLib/iwindows.h"
|
|
||||||
|
|
||||||
static HINSTANCE MainInstance;
|
|
||||||
static HWND MainWindowHandle;
|
|
||||||
|
|
||||||
static inline String ApplicationName = L"High Blood Pressure";
|
|
||||||
|
|
||||||
inline Carlbeks::UI::Main MainWindow;
|
|
||||||
inline Carlbeks::UI::TickThread MainTick{ &MainWindow };
|
|
||||||
|
|
||||||
inline void Initialize() noexcept {
|
|
||||||
Carlbeks::WindowsInterface::ShowConsoleIO();
|
|
||||||
MainWindow.setWindow(MainWindowHandle);
|
|
||||||
MainWindow.setDC(GetDC(MainWindowHandle));
|
|
||||||
MainTick.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Finalize() noexcept {
|
|
||||||
MainTick.stopAndWait();
|
|
||||||
_wsystem(L"pause");
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by EmsiaetKadosh on 25-1-18.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "def.h"
|
|
||||||
|
|
||||||
#include "exception.h"
|
|
||||||
|
|
||||||
template<TypeName Base> Base& ObjectHolder<Base>::operator->() noexcept(false) {
|
|
||||||
if (value) return *value;
|
|
||||||
throw NullPointerException(L"value is null");
|
|
||||||
}
|
|
||||||
|
|
||||||
template<TypeName Base> const Base& ObjectHolder<Base>::operator->() const noexcept(false) {
|
|
||||||
if (value) return *value;
|
|
||||||
throw NullPointerException(L"value is null");
|
|
||||||
}
|
|
||||||
|
|
||||||
template<TypeName Base> Base& ObjectHolder<Base>::operator*() noexcept(false) {
|
|
||||||
if (value) return *value;
|
|
||||||
throw NullPointerException(L"value is null");
|
|
||||||
}
|
|
||||||
|
|
||||||
template<TypeName Base> const Base& ObjectHolder<Base>::operator*() const noexcept(false) {
|
|
||||||
if (value) return *value;
|
|
||||||
throw NullPointerException(L"value is null");
|
|
||||||
}
|
|
||||||
|
|
||||||
template<TypeName Base> template<NewCopyable T> requires std::is_base_of_v<Base, T> && TypeName<T>
|
|
||||||
void ObjectHolder<Base>::set(const T& value) {
|
|
||||||
if (!value) this->value = new T(value);
|
|
||||||
else {
|
|
||||||
if (hasValue) delete this->value;
|
|
||||||
this->value = new T(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<TypeName Base> template<NewMoveable T> requires std::is_base_of_v<Base, T> && TypeName<T>
|
|
||||||
void ObjectHolder<Base>::set(T&& value) {
|
|
||||||
if (!value) this->value = new T(value);
|
|
||||||
else {
|
|
||||||
if (hasValue) delete this->value;
|
|
||||||
this->value = new T(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<TypeName T> const T& Reference<T>::getLvalue() const noexcept(false) {
|
|
||||||
if (type == ReferenceValueType::LVALUE) return *lvalue;
|
|
||||||
throw NullPointerException(L"reference is rvalue");
|
|
||||||
}
|
|
||||||
|
|
||||||
template<TypeName T> T&& Reference<T>::getRvalue() noexcept(false) {
|
|
||||||
if (type == ReferenceValueType::RVALUE) return std::move(*rvalue);
|
|
||||||
throw NullPointerException(L"reference is lvalue");
|
|
||||||
}
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by EmsiaetKadosh on 25-1-14.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <iostream>
|
|
||||||
#include <list>
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
#include <atomic>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
using wchar = wchar_t;
|
|
||||||
using QWORD = unsigned long long int;
|
|
||||||
using String = std::wstring;
|
|
||||||
using Thread = std::thread;
|
|
||||||
template<typename K, typename V, typename Cmp = std::less<K>, typename Alloc = std::allocator<std::pair<const K, V>>> using Map = std::map<K, V, Cmp, Alloc>;
|
|
||||||
template<typename T, typename Alloc = std::allocator<T>> using List = std::list<T, Alloc>;
|
|
||||||
template<typename F> using Function = std::function<F>;
|
|
||||||
|
|
||||||
#define Success() { return 0; }
|
|
||||||
#define Failed() { return 1; }
|
|
||||||
#define Error() { return -1; }
|
|
||||||
|
|
||||||
#if false
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#define __FUNCSIGW__ L"" __FUNCSIG__
|
|
||||||
#elif defined(__GNUC__) || defined(__clang__)
|
|
||||||
#define __FUNCSIGW__ L"" __PRETTY_FUNCTION__
|
|
||||||
#else
|
|
||||||
#define __FUNCSIGW__ L"" __func__
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define _WINSOCKAPI_ /* 防止winsock.h被引入。winsock.h和winsock2.h冲突。 */
|
|
||||||
#if false
|
|
||||||
#include <WinSock2.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define NOMINMAX
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <Windowsx.h>
|
|
||||||
#include <minwindef.h>
|
|
||||||
#include <windef.h>
|
|
||||||
#include <wingdi.h>
|
|
||||||
#include <WinUser.h>
|
|
||||||
#include <Uxtheme.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <dwmapi.h>
|
|
||||||
|
|
||||||
#pragma comment(lib, "Msimg32.lib")
|
|
||||||
#pragma comment(lib, "ws2_32.lib")
|
|
||||||
#pragma comment(lib, "dwmapi.lib")
|
|
||||||
#pragma comment(lib, "Uxtheme.lib")
|
|
||||||
#pragma comment(lib, "winmm.lib")
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
concept Copyable = requires(const T& t) { T(t); };
|
|
||||||
template<typename T>
|
|
||||||
concept NewCopyable = requires(const T& t) { delete new T(t); };
|
|
||||||
template<typename T>
|
|
||||||
concept Moveable = requires(T&& t) { T(std::move(t)); };
|
|
||||||
template<typename T>
|
|
||||||
concept NewMoveable = requires(T&& t) { delete new T(std::move(t)); };
|
|
||||||
template<typename T>
|
|
||||||
concept NonreferenceType = !std::is_reference_v<T>;
|
|
||||||
template<typename T>
|
|
||||||
concept NonpointerType = !std::is_pointer_v<T>;
|
|
||||||
template<typename T>
|
|
||||||
concept ReferenceType = std::is_reference_v<T>;
|
|
||||||
template<typename T>
|
|
||||||
concept PointerType = std::is_pointer_v<T>;
|
|
||||||
template<typename T>
|
|
||||||
concept TypeName = NonreferenceType<T> && NonpointerType<T>;
|
|
||||||
|
|
||||||
template<TypeName Base> class ObjectHolder {
|
|
||||||
Base* value;
|
|
||||||
bool hasValue;
|
|
||||||
char padding[7]{};
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* 用于延迟初始化。
|
|
||||||
*/
|
|
||||||
ObjectHolder() : value(nullptr), hasValue(false) {}
|
|
||||||
|
|
||||||
ObjectHolder(Base* const value) : value(value), hasValue(false) {}
|
|
||||||
|
|
||||||
template<NewCopyable T> requires std::is_base_of_v<Base, T> && TypeName<T>
|
|
||||||
ObjectHolder(const T& value) : value(new T(value)), hasValue(true) {}
|
|
||||||
|
|
||||||
template<NewMoveable T> requires std::is_base_of_v<Base, T> && TypeName<T>
|
|
||||||
ObjectHolder(T&& value) : value(new T(std::forward<T>(value))), hasValue(true) {}
|
|
||||||
|
|
||||||
ObjectHolder(const ObjectHolder& other) noexcept: value(other.value), hasValue(false) {}
|
|
||||||
|
|
||||||
ObjectHolder(ObjectHolder&& other) noexcept: value(other.value), hasValue(other.hasValue) {
|
|
||||||
other.value = nullptr;
|
|
||||||
other.hasValue = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<NewCopyable T> requires std::is_base_of_v<Base, T> && TypeName<T>
|
|
||||||
void set(const T& value);
|
|
||||||
|
|
||||||
template<NewMoveable T> requires std::is_base_of_v<Base, T> && TypeName<T>
|
|
||||||
void set(T&& value);
|
|
||||||
|
|
||||||
|
|
||||||
~ObjectHolder() {
|
|
||||||
if (hasValue) delete value;
|
|
||||||
value = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Base& operator->() noexcept(false);
|
|
||||||
const Base& operator->() const noexcept(false);
|
|
||||||
Base& operator*() noexcept(false);
|
|
||||||
const Base& operator*() const noexcept(false);
|
|
||||||
operator bool() const noexcept { return value; }
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ReferenceValueType {
|
|
||||||
LVALUE,
|
|
||||||
RVALUE
|
|
||||||
};
|
|
||||||
|
|
||||||
template<TypeName T> class Reference {
|
|
||||||
const T* lvalue = nullptr;
|
|
||||||
T* rvalue = nullptr;
|
|
||||||
|
|
||||||
public:
|
|
||||||
const ReferenceValueType type;
|
|
||||||
Reference(const T& lvalue) : lvalue(&lvalue), type(ReferenceValueType::LVALUE) {}
|
|
||||||
Reference(T&& rvalue) : rvalue(&rvalue), type(ReferenceValueType::RVALUE) {}
|
|
||||||
Reference& operator=(const Reference&) = delete;
|
|
||||||
Reference& operator=(Reference&&) = delete;
|
|
||||||
|
|
||||||
Reference& operator=(const T& lvalue) {
|
|
||||||
this->lvalue = &lvalue;
|
|
||||||
this->type = ReferenceValueType::LVALUE;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference& operator=(T&& rvalue) {
|
|
||||||
this->rvalue = &rvalue;
|
|
||||||
this->type = ReferenceValueType::RVALUE;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
const T& getLvalue() const noexcept(false);
|
|
||||||
T&& getRvalue() noexcept(false);
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Module:
|
||||||
|
File(30%), including Log, Save, Config, Language
|
||||||
|
Translator(30%), with File
|
||||||
|
KeyBinding(20%)
|
||||||
|
World, Entity(10%), including gc problem
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by EmsiaetKadosh on 25-1-22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "def.h"
|
|
||||||
#include "exception.h"
|
|
||||||
#include "Game.h"
|
|
||||||
|
|
||||||
ILogger& PublicLogger::trace(const String& msg) noexcept {
|
|
||||||
std::wcout << L"T-" + qwtowb10(game.getTick(), 8) + L" [Trace] " + msg + L"\n";
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ILogger& PublicLogger::trace(String&& msg) noexcept {
|
|
||||||
std::wcout << L"T-" + qwtowb10(game.getTick(), 8) + L" [Trace] " + msg + L"\n";
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ILogger& PublicLogger::debug(const String& msg) noexcept {
|
|
||||||
std::wcout << L"T-" + qwtowb10(game.getTick(), 8) + L" [Debug] " + msg + L"\n";
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ILogger& PublicLogger::debug(String&& msg) noexcept {
|
|
||||||
std::wcout << L"T-" + qwtowb10(game.getTick(), 8) + L" [Debug] " + msg + L"\n";
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ILogger& PublicLogger::log(const String& msg) noexcept {
|
|
||||||
std::wcout << L"T-" + qwtowb10(game.getTick(), 8) + L" [Log] " + msg + L"\n";
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ILogger& PublicLogger::log(String&& msg) noexcept {
|
|
||||||
std::wcout << L"T-" + qwtowb10(game.getTick(), 8) + L" [Log] " + msg + L"\n";
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ILogger& PublicLogger::info(const String& msg) noexcept {
|
|
||||||
std::wcout << L"T-" + qwtowb10(game.getTick(), 8) + L" [Info] " + msg + L"\n";
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ILogger& PublicLogger::info(String&& msg) noexcept {
|
|
||||||
std::wcout << L"T-" + qwtowb10(game.getTick(), 8) + L" [Info] " + msg + L"\n";
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ILogger& PublicLogger::warn(const String& msg) noexcept {
|
|
||||||
std::wcout << L"T-" + qwtowb10(game.getTick(), 8) + L" [Warn] " + msg + L"\n";
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ILogger& PublicLogger::warn(String&& msg) noexcept {
|
|
||||||
std::wcout << L"T-" + qwtowb10(game.getTick(), 8) + L" [Warn] " + msg + L"\n";
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ILogger& PublicLogger::error(const String& msg) noexcept {
|
|
||||||
std::wcout << L"T-" + qwtowb10(game.getTick(), 8) + L" [Error] " + msg + L"\n";
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ILogger& PublicLogger::error(String&& msg) noexcept {
|
|
||||||
std::wcout << L"T-" + qwtowb10(game.getTick(), 8) + L" [Error] " + msg + L"\n";
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline PublicLogger Logger{};
|
|
||||||
-90
@@ -1,90 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by EmsiaetKadosh on 25-1-18.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
class Exception : public std::exception {
|
|
||||||
const String* type;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
String msg;
|
|
||||||
|
|
||||||
Exception(String&& msg, const String* type) : type(type), msg(std::move(msg)) {}
|
|
||||||
Exception(const String& msg, const String* type) : type(type), msg(msg) {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
[[nodiscard]] String getMessage() const noexcept { return msg; }
|
|
||||||
[[nodiscard]] const String* getType() const noexcept { return type; }
|
|
||||||
[[nodiscard]] const char* what() const override { return "Use Exception::getMessage() instead."; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class NullPointerException final : public Exception {
|
|
||||||
inline static const String type = L"NullPointerException";
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit NullPointerException(String&& msg) : Exception(std::move(msg), &type) {}
|
|
||||||
explicit NullPointerException(const String& msg) : Exception(msg, &type) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ArrayIndexOutOfBoundException final : public Exception {
|
|
||||||
inline static const String type = L"ArrayIndexOutOfBoundException";
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit ArrayIndexOutOfBoundException(String&& msg) : Exception(std::move(msg), &type) {}
|
|
||||||
explicit ArrayIndexOutOfBoundException(const String& msg) : Exception(msg, &type) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class InvalidOperationException final : public Exception {
|
|
||||||
inline static const String type = L"InvalidOperationException";
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit InvalidOperationException(String&& msg) : Exception(std::move(msg), &type) {}
|
|
||||||
explicit InvalidOperationException(const String& msg) : Exception(msg, &type) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
interface ILogger {
|
|
||||||
virtual ~ILogger() = default;
|
|
||||||
virtual ILogger& trace(const String&) noexcept = 0;
|
|
||||||
virtual ILogger& trace(String&&) noexcept = 0;
|
|
||||||
virtual ILogger& debug(const String&) noexcept = 0;
|
|
||||||
virtual ILogger& debug(String&&) noexcept = 0;
|
|
||||||
virtual ILogger& log(const String&) noexcept = 0;
|
|
||||||
virtual ILogger& log(String&&) noexcept = 0;
|
|
||||||
virtual ILogger& info(const String&) noexcept = 0;
|
|
||||||
virtual ILogger& info(String&&) noexcept = 0;
|
|
||||||
virtual ILogger& warn(const String&) noexcept = 0;
|
|
||||||
virtual ILogger& warn(String&&) noexcept = 0;
|
|
||||||
virtual ILogger& error(const String&) noexcept = 0;
|
|
||||||
virtual ILogger& error(String&&) noexcept = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PublicLogger final : ILogger {
|
|
||||||
PublicLogger() { std::wcout << L"PublicLogger created\n"; }
|
|
||||||
|
|
||||||
PublicLogger& put(const String& msg) noexcept {
|
|
||||||
std::wcout << L"[] [Root] " + msg + L"\n";
|
|
||||||
return * this;
|
|
||||||
}
|
|
||||||
|
|
||||||
PublicLogger& put(String&& msg) noexcept {
|
|
||||||
std::wcout << L"[] [Root] " + msg + L"\n";
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ILogger& trace(const String&) noexcept override;
|
|
||||||
ILogger& trace(String&&) noexcept override;
|
|
||||||
ILogger& debug(const String&) noexcept override;
|
|
||||||
ILogger& debug(String&&) noexcept override;
|
|
||||||
ILogger& log(const String&) noexcept override;
|
|
||||||
ILogger& log(String&&) noexcept override;
|
|
||||||
ILogger& info(const String&) noexcept override;
|
|
||||||
ILogger& info(String&&) noexcept override;
|
|
||||||
ILogger& warn(const String&) noexcept override;
|
|
||||||
ILogger& warn(String&&) noexcept override;
|
|
||||||
ILogger& error(const String&) noexcept override;
|
|
||||||
ILogger& error(String&&) noexcept override;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern PublicLogger Logger;
|
|
||||||
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "def.h"
|
|
||||||
|
|
||||||
inline BOOL NewProcess(const String& cmdline) noexcept {
|
|
||||||
STARTUPINFOW si = {
|
|
||||||
sizeof(si), nullptr, nullptr, nullptr, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr, nullptr
|
|
||||||
};
|
|
||||||
PROCESS_INFORMATION pi{
|
|
||||||
nullptr, nullptr, 0, 0
|
|
||||||
};
|
|
||||||
return CreateProcessW(nullptr, const_cast<wchar*>(cmdline.c_str()), nullptr, nullptr, 0, 0, nullptr, nullptr, &si, &pi);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline HRESULT RemoveDefaultCaption(HWND hWnd, const MARGINS* p) noexcept { return DwmExtendFrameIntoClientArea(hWnd, p); }
|
|
||||||
|
|
||||||
inline bool ShowConsoleIO() noexcept {
|
|
||||||
AllocConsole();
|
|
||||||
FILE* pCout;
|
|
||||||
freopen_s(&pCout, "CONOUT$", "w", stdout);
|
|
||||||
FILE* pCin;
|
|
||||||
freopen_s(&pCin, "CONIN$", "r", stdin);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const String ApplicationName = L"Hyblud Presher";
|
|
||||||
inline HINSTANCE MainInstance;
|
|
||||||
inline HWND MainWindowHandle;
|
|
||||||
inline Thread GameThread;
|
|
||||||
inline Thread RenderThread;
|
|
||||||
inline static bool isRunning = ShowConsoleIO();
|
|
||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by EmsiaetKadosh on 25-1-14.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "def.h"
|
|
||||||
#include "exception.h"
|
|
||||||
#include "hbp.h"
|
|
||||||
#include "Chars.h"
|
|
||||||
#include "Renderer.h"
|
|
||||||
#include "InteractManager.h"
|
|
||||||
#include "TextureManager.h"
|
|
||||||
#include "IText.h"
|
|
||||||
|
|
||||||
#include "Hud.h"
|
|
||||||
#include "Window.h"
|
|
||||||
|
|
||||||
#include "Game.h"
|
|
||||||
|
|
||||||
#include "xWindows.h"
|
|
||||||
@@ -1,223 +0,0 @@
|
|||||||
|
|
||||||
#include "includes.h"
|
|
||||||
#include "IText.h"
|
|
||||||
#include "TestCode.h"
|
|
||||||
|
|
||||||
LRESULT __stdcall WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
||||||
switch (uMsg) {
|
|
||||||
_LIKELY
|
|
||||||
case WM_PAINT: {
|
|
||||||
PAINTSTRUCT ps;
|
|
||||||
BeginPaint(hwnd, &ps);
|
|
||||||
EndPaint(hwnd, &ps);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_LIKELY
|
|
||||||
case WM_NCHITTEST: {
|
|
||||||
POINT point = { GET_X_LPARAM(lParam), (GET_Y_LPARAM(lParam)) };
|
|
||||||
ScreenToClient(hwnd, &point);
|
|
||||||
const int xPos = point.x;
|
|
||||||
const int yPos = point.y;
|
|
||||||
if (xPos < 30) {
|
|
||||||
if (yPos < 30) return HTTOPLEFT;
|
|
||||||
if (renderer.getHeight() - yPos < 30) return HTBOTTOMLEFT;
|
|
||||||
return HTLEFT;
|
|
||||||
}
|
|
||||||
if (renderer.getWidth() - xPos < 30) {
|
|
||||||
if (yPos < 30) return HTTOPRIGHT;
|
|
||||||
if (renderer.getHeight() - yPos < 30) return HTBOTTOMRIGHT;
|
|
||||||
return HTRIGHT;
|
|
||||||
}
|
|
||||||
if (yPos < 30) return HTTOP;
|
|
||||||
if (renderer.getHeight() - yPos < 30) return HTBOTTOM;
|
|
||||||
if (yPos < 240) return HTCAPTION;
|
|
||||||
LRESULT lr = 0;
|
|
||||||
DwmDefWindowProc(hwnd, uMsg, wParam, lParam, &lr);
|
|
||||||
return HTCLIENT;
|
|
||||||
}
|
|
||||||
case WM_KEYDOWN:
|
|
||||||
interactManager.update(static_cast<int>(wParam), true);
|
|
||||||
break;
|
|
||||||
case WM_KEYUP:
|
|
||||||
interactManager.update(static_cast<int>(wParam), false);
|
|
||||||
break;
|
|
||||||
case WM_SYSKEYDOWN:
|
|
||||||
interactManager.update(static_cast<int>(wParam), true);
|
|
||||||
break;
|
|
||||||
case WM_SYSKEYUP:
|
|
||||||
interactManager.update(static_cast<int>(wParam), false);
|
|
||||||
break;
|
|
||||||
case WM_LBUTTONDOWN:
|
|
||||||
interactManager.update(VK_LBUTTON, true);
|
|
||||||
break;
|
|
||||||
case WM_RBUTTONDOWN:
|
|
||||||
interactManager.update(VK_RBUTTON, true);
|
|
||||||
break;
|
|
||||||
case WM_LBUTTONUP:
|
|
||||||
interactManager.update(VK_LBUTTON, false);
|
|
||||||
break;
|
|
||||||
case WM_RBUTTONUP:
|
|
||||||
interactManager.update(VK_RBUTTON, false);
|
|
||||||
break;
|
|
||||||
case WM_MOUSEMOVE:
|
|
||||||
interactManager.updateMouse(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
|
||||||
break;
|
|
||||||
case WM_MBUTTONDOWN:
|
|
||||||
if (wParam & 0x10) interactManager.update(VK_MBUTTON, true);
|
|
||||||
break;
|
|
||||||
case WM_MBUTTONUP:
|
|
||||||
interactManager.update(VK_MBUTTON, false);
|
|
||||||
break;
|
|
||||||
case WM_MOUSEWHEEL:
|
|
||||||
interactManager.update(VK_MBUTTON, true);
|
|
||||||
interactManager.update(VK_MBUTTON, true);
|
|
||||||
break;
|
|
||||||
case WM_MOUSEHOVER:
|
|
||||||
// case WM_NCMOUSEHOVER:
|
|
||||||
interactManager.mouseHover();
|
|
||||||
break;
|
|
||||||
case WM_MOUSELEAVE:
|
|
||||||
// case WM_NCMOUSELEAVE:
|
|
||||||
interactManager.mouseLeave();
|
|
||||||
break;
|
|
||||||
case WM_COMMAND:
|
|
||||||
std::wcout << L"WM_COMMAND" << std::endl;
|
|
||||||
break;
|
|
||||||
case WM_DWMCOMPOSITIONCHANGED: {
|
|
||||||
MARGINS margins{
|
|
||||||
.cxLeftWidth = 0,
|
|
||||||
.cxRightWidth = 0,
|
|
||||||
.cyTopHeight = 0,
|
|
||||||
.cyBottomHeight = 0
|
|
||||||
};
|
|
||||||
RemoveDefaultCaption(hwnd, &margins);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WM_NCCALCSIZE:
|
|
||||||
if (wParam == 1) {
|
|
||||||
NCCALCSIZE_PARAMS* pncsp = reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam);
|
|
||||||
pncsp->rgrc[0].left = pncsp->rgrc[0].left + 0;
|
|
||||||
pncsp->rgrc[0].top = pncsp->rgrc[0].top + 0;
|
|
||||||
pncsp->rgrc[0].right = pncsp->rgrc[0].right - 0;
|
|
||||||
pncsp->rgrc[0].bottom = pncsp->rgrc[0].bottom - 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case WM_SIZE: {
|
|
||||||
renderer.resize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
|
||||||
switch (wParam) {
|
|
||||||
case SIZE_RESTORED:
|
|
||||||
case SIZE_MINIMIZED:
|
|
||||||
case SIZE_MAXIMIZED:
|
|
||||||
case SIZE_MAXSHOW:
|
|
||||||
case SIZE_MAXHIDE:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case WM_ACTIVATE: {
|
|
||||||
constexpr MARGINS margins{
|
|
||||||
.cxLeftWidth = 0,
|
|
||||||
.cxRightWidth = 0,
|
|
||||||
.cyTopHeight = 0,
|
|
||||||
.cyBottomHeight = 0
|
|
||||||
};
|
|
||||||
HRESULT hr = RemoveDefaultCaption(hwnd, &margins);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
_UNLIKELY
|
|
||||||
case WM_DESTROY:
|
|
||||||
PostQuitMessage(0);
|
|
||||||
isRunning = false;
|
|
||||||
return 0;
|
|
||||||
_UNLIKELY
|
|
||||||
case WM_CREATE:
|
|
||||||
SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);// Force post NCCALCSIZE
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
||||||
}
|
|
||||||
|
|
||||||
void gameThread() {
|
|
||||||
using namespace std::chrono;
|
|
||||||
using Time = time_point<system_clock>;
|
|
||||||
Time lastTick = system_clock::now();
|
|
||||||
Time thisTime;
|
|
||||||
while (isRunning) {
|
|
||||||
thisTime = system_clock::now();
|
|
||||||
if (thisTime - lastTick < milliseconds(45)) {
|
|
||||||
Sleep(1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
game.tick();
|
|
||||||
lastTick = thisTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderThread() {
|
|
||||||
using namespace std::chrono;
|
|
||||||
using Time = time_point<system_clock>;
|
|
||||||
Time lastRender = system_clock::now();
|
|
||||||
Time thisTime;
|
|
||||||
while (isRunning) {
|
|
||||||
thisTime = system_clock::now();
|
|
||||||
if (thisTime - lastRender < milliseconds(12)) {
|
|
||||||
Sleep(1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
game.render();
|
|
||||||
lastRender = thisTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) {
|
|
||||||
WNDCLASSEX wc = {};
|
|
||||||
wc.cbSize = sizeof(WNDCLASSEX);
|
|
||||||
wc.style = CS_HREDRAW | CS_VREDRAW;
|
|
||||||
wc.lpfnWndProc = WndProc;
|
|
||||||
wc.cbClsExtra = 0;
|
|
||||||
wc.cbWndExtra = 0;
|
|
||||||
wc.hInstance = hInstance;
|
|
||||||
wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
|
|
||||||
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
|
||||||
wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
|
|
||||||
wc.lpszMenuName = L"None";
|
|
||||||
wc.lpszClassName = ApplicationName.c_str();
|
|
||||||
if (!RegisterClassExW(&wc)) return FALSE;
|
|
||||||
if (!SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) std::wcout << L"SetProcessDpiAwarenessContext failed. LastError: " << GetLastError() << std::endl;
|
|
||||||
MainInstance = hInstance;
|
|
||||||
MainWindowHandle = CreateWindowExW(0, wc.lpszClassName, wc.lpszClassName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr);
|
|
||||||
ShowWindow(MainWindowHandle, nCmdShow);
|
|
||||||
InteractManager::initialize();
|
|
||||||
Renderer::initialize();
|
|
||||||
Game::initialize();
|
|
||||||
SetConsoleOutputCP(65001);
|
|
||||||
{
|
|
||||||
const String string = String(L"\\#12345678Hello\\/\\f\1Well\\.87654321你好\\r\\r\\u\\-");
|
|
||||||
std::cout << "你好" << std::endl;
|
|
||||||
std::wcout.imbue(std::locale("zh_CN"));
|
|
||||||
std::wcout
|
|
||||||
<< sizeof(L"你好") << " " << sizeof("Hu") << std::endl
|
|
||||||
<< L"GetConsoleOutputCP => " << GetConsoleOutputCP() << std::endl
|
|
||||||
<< string << std::endl
|
|
||||||
<< RenderableString(string).toString() << std::endl;
|
|
||||||
test();
|
|
||||||
}
|
|
||||||
HACCEL hAccelTable = LoadAcceleratorsW(hInstance, MAKEINTRESOURCE(109));
|
|
||||||
MSG msg = { nullptr };
|
|
||||||
GameThread = Thread(gameThread);
|
|
||||||
RenderThread = Thread(renderThread);
|
|
||||||
while (GetMessageW(&msg, nullptr, 0, 0)) {
|
|
||||||
if (!TranslateAcceleratorW(msg.hwnd, hAccelTable, &msg)) {
|
|
||||||
TranslateMessage(&msg);
|
|
||||||
DispatchMessageW(&msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DestroyAcceleratorTable(hAccelTable);
|
|
||||||
if (GameThread.joinable()) GameThread.join();
|
|
||||||
if (RenderThread.joinable()) RenderThread.join();
|
|
||||||
_wsystem(L"pause");
|
|
||||||
return (int) msg.wParam;
|
|
||||||
}
|
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-1-18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "def.h"
|
||||||
|
|
||||||
|
#include "utils\File.h"
|
||||||
|
#include "utils\exception.h"
|
||||||
|
#include "utils\Chars.h"
|
||||||
|
#include "utils\gc.h"
|
||||||
|
|
||||||
|
template<TypeName Base> template<NewCopyable T> requires std::is_base_of_v<Base, T> && TypeName<T>
|
||||||
|
void ObjectHolder<Base>::set(const T& value) {
|
||||||
|
if (!value) this->value = allocatedFor(new T(value));
|
||||||
|
else {
|
||||||
|
if (hasValue) delete deallocating(this->value);
|
||||||
|
this->value = allocatedFor(new T(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<TypeName Base> template<NewMoveable T> requires std::is_base_of_v<Base, T> && TypeName<T>
|
||||||
|
void ObjectHolder<Base>::set(T&& value) {
|
||||||
|
if (!value) this->value = allocatedFor(new T(value));
|
||||||
|
else {
|
||||||
|
if (hasValue) delete deallocating(this->value);
|
||||||
|
this->value = allocatedFor(new T(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void requireNonnull(const void* value) noexcept(false) { if (!value) throw NullPointerException(L"value is null"); }
|
||||||
|
void checkAllocation(const void* value) noexcept(false) { if (!value) throw BadAllocationException(L"bad allocation"); }
|
||||||
|
|
||||||
|
void $LimitedUse::printAllocate(void* value, const size_t size, const String& msg) {
|
||||||
|
const String str = L"alloc " + ptrtow(reinterpret_cast<QWORD>(value)) + L" " + std::to_wstring(size) + String(L"B ") + msg;
|
||||||
|
Logger.log(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void $LimitedUse::printDeallocate(void* value, const size_t size, const String& msg) {
|
||||||
|
const String str = L"dealloc " + ptrtow(reinterpret_cast<QWORD>(value)) + L" " + std::to_wstring(size) + String(L"B ") + msg;
|
||||||
|
Logger.log(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void $LimitedUse::printDeallocateWarning(void* value, const String& msg) {
|
||||||
|
const String str = L"dealloc " + ptrtow(reinterpret_cast<QWORD>(value)) + L": " + msg;
|
||||||
|
Logger.error(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String ptrtow(const QWORD value) { return qwtowb16(value, 16); }
|
||||||
|
|
||||||
|
namespace $LimitedUse {
|
||||||
|
Release::~Release() {
|
||||||
|
delete &gc;
|
||||||
|
Logger.put(L"--------- Last Check ---------");
|
||||||
|
for (const auto& [addr, info] : memoryManager.allocated) { Logger.print(L" using", addr, info.size, L"B", info.msg); }
|
||||||
|
delete &Logger;
|
||||||
|
delete &memoryManager;
|
||||||
|
std::wcout << L"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,325 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-1-14.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define __CARLBEKS_DEBUG__
|
||||||
|
#define __CARLBEKS_MEMORY__ 2
|
||||||
|
|
||||||
|
#pragma warning(disable: 4819)
|
||||||
|
|
||||||
|
#include <typeinfo>
|
||||||
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <cmath>
|
||||||
|
#include <set>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
using wchar = wchar_t;
|
||||||
|
using QWORD = unsigned long long int;
|
||||||
|
using String = std::wstring;
|
||||||
|
using Thread = std::thread;
|
||||||
|
template <typename K, typename V, typename Cmp = std::less<K>, typename Alloc = std::allocator<std::pair<const K, V>>>
|
||||||
|
using Map = std::map<K, V, Cmp, Alloc>;
|
||||||
|
template <typename T, typename Comparator = std::less<T>, typename Allocator = std::allocator<T>>
|
||||||
|
using Set = std::set<T, Comparator, Allocator>;
|
||||||
|
template <typename T, typename Allocator = std::allocator<T>>
|
||||||
|
using List = std::list<T, Allocator>;
|
||||||
|
template <typename F>
|
||||||
|
using Function = std::function<F>;
|
||||||
|
|
||||||
|
#define Success() { return 0; }
|
||||||
|
#define Failed() { return 1; }
|
||||||
|
#define Error() { return -1; }
|
||||||
|
#define Comment(PARAMS) /##/ PARAMS
|
||||||
|
#define SameAs(PARAMS) Comment(PARAMS)
|
||||||
|
|
||||||
|
//NOLINTNEXTLINE(*-reserved-identifier)
|
||||||
|
#define _WINSOCKAPI_ /* 防止winsock.h被引入。winsock.h和winsock2.h冲突。 */
|
||||||
|
#if false
|
||||||
|
#include <WinSock2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NOMINMAX
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <Windowsx.h>
|
||||||
|
#include <minwindef.h>
|
||||||
|
#include <windef.h>
|
||||||
|
#include <wingdi.h>
|
||||||
|
#include <WinUser.h>
|
||||||
|
#include <Uxtheme.h>
|
||||||
|
#include <dwmapi.h>
|
||||||
|
|
||||||
|
#define WM_APP_LBUTTONUP (WM_APP + 1)
|
||||||
|
#define WM_APP_MBUTTONDOWN (WM_APP + 2)
|
||||||
|
|
||||||
|
#pragma comment(lib, "Msimg32.lib")
|
||||||
|
#pragma comment(lib, "ws2_32.lib")
|
||||||
|
#pragma comment(lib, "dwmapi.lib")
|
||||||
|
#pragma comment(lib, "Uxtheme.lib")
|
||||||
|
#pragma comment(lib, "winmm.lib")
|
||||||
|
|
||||||
|
#include "warnings.h"
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept Copyable = requires(const T& t) { T(t); };
|
||||||
|
template <typename T>
|
||||||
|
concept NewCopyable = requires(const T& t) { delete new T(t); };
|
||||||
|
template <typename T>
|
||||||
|
concept Moveable = requires(T&& t) { T(std::move(t)); };
|
||||||
|
template <typename T>
|
||||||
|
concept NewMoveable = requires(T&& t) { delete new T(std::move(t)); };
|
||||||
|
template <typename T>
|
||||||
|
concept NonreferenceType = !std::is_reference_v<T>;
|
||||||
|
template <typename T>
|
||||||
|
concept NonpointerType = !std::is_pointer_v<T>;
|
||||||
|
template <typename T>
|
||||||
|
concept ReferenceType = std::is_reference_v<T>;
|
||||||
|
template <typename T>
|
||||||
|
concept PointerType = std::is_pointer_v<T>;
|
||||||
|
template <typename T>
|
||||||
|
concept TypeName = NonreferenceType<T> && NonpointerType<T>;
|
||||||
|
|
||||||
|
namespace $LimitedUse {
|
||||||
|
struct Release {
|
||||||
|
Release() = default;
|
||||||
|
|
||||||
|
~Release();
|
||||||
|
} inline gcRelease_LoggerRelease_memoryManagerRelease;
|
||||||
|
|
||||||
|
struct MemoryManager {
|
||||||
|
struct MemoryInfo {
|
||||||
|
const String msg;
|
||||||
|
std::size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
Map<void*, MemoryInfo> allocated{};
|
||||||
|
std::atomic_bool acquiring = false;
|
||||||
|
|
||||||
|
constexpr MemoryManager() noexcept = default;
|
||||||
|
} inline& [[carlbeks::releasedat("def.cpp")]] memoryManager = *new MemoryManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
void requireNonnull(const void* value) noexcept(false);
|
||||||
|
void checkAllocation(const void* value) noexcept(false);
|
||||||
|
inline String ptrtow(QWORD value);
|
||||||
|
|
||||||
|
#if defined __CARLBEKS_DEBUG__ || defined __CARLBEKS_MEMORY__
|
||||||
|
namespace $LimitedUse {
|
||||||
|
void printAllocate(void* value, std::size_t size, const String&);
|
||||||
|
void printDeallocate(void* value, std::size_t size, const String&);
|
||||||
|
void printDeallocateWarning(void* value, const String& msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern String atow(const char* chars);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* allocatedFor$(T* value, const String& msg = L"", std::size_t size = sizeof(T)) {
|
||||||
|
requireNonnull(value);
|
||||||
|
bool expect = false;
|
||||||
|
while (!$LimitedUse::memoryManager.acquiring.compare_exchange_strong(expect, true)) expect = false;
|
||||||
|
const auto& k = $LimitedUse::memoryManager.allocated.emplace(value, $LimitedUse::MemoryManager::MemoryInfo{L"[" + atow(typeid(T).name()) + L"] " + msg, size}).first;
|
||||||
|
#if __CARLBEKS_MEMORY__ > 2
|
||||||
|
$LimitedUse::printAllocate(value, k->second.size, k->second.msg);
|
||||||
|
#endif
|
||||||
|
$LimitedUse::memoryManager.acquiring.store(false);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* deallocating$(T* value, const String& stack) {
|
||||||
|
bool expect = false;
|
||||||
|
while (!$LimitedUse::memoryManager.acquiring.compare_exchange_strong(expect, true)) expect = false;
|
||||||
|
#if __CARLBEKS_MEMORY__ > 2
|
||||||
|
const $LimitedUse::MemoryManager::MemoryInfo* info = nullptr;
|
||||||
|
if ($LimitedUse::memoryManager.allocated.contains(value)) info = &$LimitedUse::memoryManager.allocated.at(value);
|
||||||
|
$LimitedUse::printDeallocate(value, info ? info->size : 0, info ? info->msg : L"???");
|
||||||
|
#endif
|
||||||
|
if (value) { if (!$LimitedUse::memoryManager.allocated.erase(value)) $LimitedUse::printDeallocateWarning(value, L"value not recorded" + stack); }
|
||||||
|
$LimitedUse::memoryManager.acquiring.store(false);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef __FUNCSIG__
|
||||||
|
#define __FUNCSIG__ __FUNCTION__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __CARLBEKS_MEMORY__ > 3
|
||||||
|
#define allocatedFor(val, ...) allocatedFor$(val, L"\n From " __FUNCSIG__ "\n At " __FILE__ ":" _STL_STRINGIZE(__LINE__) __VA_OPT__(,) __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define allocatedFor(val, ...) allocatedFor$(val, L"" __VA_OPT__(,) __VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
#if __CARLBEKS_MEMORY__ > 1
|
||||||
|
#define deallocating(val) deallocating$(val, L"\n From " __FUNCSIG__ "\n At " __FILE__ ":" _STL_STRINGIZE(__LINE__))
|
||||||
|
#else
|
||||||
|
#define deallocating(val) deallocating$(val)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define allocatedFor(val, ...) val
|
||||||
|
#define deallocating(val) val
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <TypeName Base>
|
||||||
|
class ObjectHolder {
|
||||||
|
Base* value;
|
||||||
|
bool hasValue;
|
||||||
|
char padding[7]{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* 用于延迟初始化。
|
||||||
|
*/
|
||||||
|
ObjectHolder() : value(nullptr), hasValue(false) {}
|
||||||
|
|
||||||
|
ObjectHolder(Base* value) : value(value), hasValue(false) {}
|
||||||
|
|
||||||
|
template <NewCopyable T> requires (std::is_base_of_v<Base, T> || std::is_same_v<Base, T>) && TypeName<T>
|
||||||
|
ObjectHolder(const T& value) : value(allocatedFor(new T(value))), hasValue(true) {}
|
||||||
|
|
||||||
|
template <NewMoveable T> requires (std::is_base_of_v<Base, T> || std::is_same_v<Base, T>) && TypeName<T>
|
||||||
|
ObjectHolder(T&& value) : value(allocatedFor(new T(std::forward<T>(value)))), hasValue(true) {}
|
||||||
|
|
||||||
|
ObjectHolder(const ObjectHolder& other) noexcept: value(other.value), hasValue(false) {}
|
||||||
|
|
||||||
|
ObjectHolder(ObjectHolder&& other) noexcept: value(other.value), hasValue(other.hasValue) {
|
||||||
|
other.value = nullptr;
|
||||||
|
other.hasValue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <NewCopyable T> requires std::is_base_of_v<Base, T> && TypeName<T>
|
||||||
|
void set(const T& value);
|
||||||
|
|
||||||
|
template <NewMoveable T> requires std::is_base_of_v<Base, T> && TypeName<T>
|
||||||
|
void set(T&& value);
|
||||||
|
|
||||||
|
~ObjectHolder() {
|
||||||
|
if (hasValue) delete deallocating(value);
|
||||||
|
value = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Base* operator->() noexcept(false) {
|
||||||
|
requireNonnull(value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const Base* operator->() const noexcept(false) {
|
||||||
|
requireNonnull(value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Base& operator*() noexcept(false) {
|
||||||
|
requireNonnull(value);
|
||||||
|
return *value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const Base& operator*() const noexcept(false) {
|
||||||
|
requireNonnull(value);
|
||||||
|
return *value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Base& get() noexcept(false) {
|
||||||
|
requireNonnull(value);
|
||||||
|
return *value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const Base& get() const noexcept(false) {
|
||||||
|
requireNonnull(value);
|
||||||
|
return *value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] operator Base*() const noexcept(false) { return value; }
|
||||||
|
Base* ptr() const noexcept { return value; }
|
||||||
|
operator bool() const noexcept { return value; }
|
||||||
|
bool operator!() const noexcept { return value == nullptr; }
|
||||||
|
[[nodiscard]] bool isManager() const noexcept { return hasValue; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
ObjectHolder<T> referenceof(const T& other) {
|
||||||
|
ObjectHolder ret{};
|
||||||
|
ret.value = &other;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <TypeName Base>
|
||||||
|
class SynchronizedHolder {
|
||||||
|
mutable Base* newValue = nullptr;
|
||||||
|
mutable Base* value = nullptr;
|
||||||
|
mutable bool isOk = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SynchronizedHolder() = default;
|
||||||
|
|
||||||
|
~SynchronizedHolder() {
|
||||||
|
if (newValue == value) { if (value) delete deallocating(value); }
|
||||||
|
else {
|
||||||
|
if (newValue) delete deallocating(newValue);
|
||||||
|
if (value) delete deallocating(value);
|
||||||
|
}
|
||||||
|
newValue = nullptr;
|
||||||
|
value = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <NewCopyable T> requires std::is_base_of_v<Base, T> && TypeName<T>
|
||||||
|
void setNew(const Base& other) noexcept {
|
||||||
|
isOk = false;
|
||||||
|
if (newValue && newValue != value) deleteNew();
|
||||||
|
newValue = allocatedFor(new T(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <NewMoveable T> requires std::is_base_of_v<Base, T> && TypeName<T>
|
||||||
|
void setNew(T&& val) noexcept {
|
||||||
|
isOk = false;
|
||||||
|
if (newValue && newValue != value) deleteNew();
|
||||||
|
newValue = allocatedFor(new T(std::forward<T>(val)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ok() const noexcept { isOk = true; }
|
||||||
|
|
||||||
|
Base& get() const noexcept(false) {
|
||||||
|
requireNonnull(value);
|
||||||
|
return *value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Base& getNew() const noexcept(false) {
|
||||||
|
requireNonnull(newValue); // 用于抛错
|
||||||
|
return *newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Base* ptr() const noexcept { return value; }
|
||||||
|
Base* ptrNew() const noexcept { return newValue; }
|
||||||
|
Base* ptrs() const noexcept { return newValue ? newValue : value; }
|
||||||
|
|
||||||
|
void async() const noexcept {
|
||||||
|
if (newValue == value) return;
|
||||||
|
if (!isOk) return;
|
||||||
|
Base* nuv = newValue;
|
||||||
|
newValue = nullptr;
|
||||||
|
if (nuv) {
|
||||||
|
if (value && value != nuv) deleteOld();
|
||||||
|
value = nuv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void deleteOld() const noexcept {
|
||||||
|
delete deallocating(value);
|
||||||
|
value = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteNew() const noexcept {
|
||||||
|
delete deallocating(newValue);
|
||||||
|
newValue = nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-3-7.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "..\def.h"
|
||||||
|
|
||||||
|
class Animation {
|
||||||
|
public:
|
||||||
|
enum Depend : unsigned char {
|
||||||
|
AD_TIME, AD_GET
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Style : unsigned short {
|
||||||
|
AS_LINEAR = 0,
|
||||||
|
AS_SIN_IN = 0x1, AS_SIN_OUT = 0x100, AS_SIN = 0x101,
|
||||||
|
AS_QUADRATIC_IN = 0x2, AS_QUADRATIC_OUT = 0x200, AS_QUADRATIC = 0x202,
|
||||||
|
AS_CUBIC_IN = 0x3, AS_CUBIC_OUT = 0x300, AS_CUBIC = 0x303,
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable int progress = 0;
|
||||||
|
int duration = 20;
|
||||||
|
Style style = AS_LINEAR;
|
||||||
|
Depend depend = AD_GET;
|
||||||
|
bool positiveSuperAllowed = false;
|
||||||
|
bool negativeSuperAllowed = false;
|
||||||
|
bool doLoop = false;
|
||||||
|
bool doReverse = true;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Animation() noexcept = default;
|
||||||
|
|
||||||
|
Animation& allowPositiveSuper(const bool val = true) noexcept {
|
||||||
|
positiveSuperAllowed = val;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation& allowNegativeSuper(const bool val = true) noexcept {
|
||||||
|
negativeSuperAllowed = val;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation& depends(const Depend depend) noexcept {
|
||||||
|
this->depend = depend;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation& features(const Style style) noexcept {
|
||||||
|
this->style = style;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation& loop(const bool val = true) noexcept {
|
||||||
|
doLoop = val;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation& includeReverse(const bool val = true) noexcept {
|
||||||
|
doReverse = val;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation& setDuration(const int val) noexcept {
|
||||||
|
if (duration == 0) duration = 1;
|
||||||
|
duration = val;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() const noexcept { progress = 0; }
|
||||||
|
|
||||||
|
static double weight(double x) noexcept {
|
||||||
|
if (x <= 0.0) return 0.0;
|
||||||
|
if (x >= 1.0) return 1.0;
|
||||||
|
x *= 2;
|
||||||
|
return x < 1.0 ? pow(x, 5.0) * 0.5 : pow(x - 2.0, 5.0) * 0.5 + 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double calculateNext() const noexcept {
|
||||||
|
if (++progress > duration) progress = doReverse ? 1 - duration : 0;
|
||||||
|
double p = static_cast<double>(progress < 0 ? -progress : progress) / duration;
|
||||||
|
const double w = weight(p);
|
||||||
|
double val1 = 0, val2 = 0;
|
||||||
|
switch (style & 0xff) {
|
||||||
|
case AS_SIN_IN:
|
||||||
|
val1 = 1 - cos(p * 1.5707963267948966192313216916398);
|
||||||
|
break;
|
||||||
|
case AS_QUADRATIC_IN:
|
||||||
|
val1 = p * p;
|
||||||
|
break;
|
||||||
|
case AS_CUBIC_IN:
|
||||||
|
val1 = p * p * p;
|
||||||
|
break;
|
||||||
|
case AS_LINEAR:
|
||||||
|
default:
|
||||||
|
val1 = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (style & 0xff00) {
|
||||||
|
case AS_SIN_OUT:
|
||||||
|
val2 = sin(p * 1.5707963267948966192313216916398);
|
||||||
|
break;
|
||||||
|
case AS_QUADRATIC_OUT:
|
||||||
|
p = 1 - p;
|
||||||
|
val2 = 1 - p * p;
|
||||||
|
break;
|
||||||
|
case AS_CUBIC_OUT:
|
||||||
|
p = 1 - p;
|
||||||
|
val2 = 1 + p * p * p;
|
||||||
|
break;
|
||||||
|
case AS_LINEAR:
|
||||||
|
default:
|
||||||
|
val2 = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return val1 * (1 - w) + val2 * w;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getValue() const noexcept { return static_cast<double>(progress) / duration; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
double adapts(T from, T to) const noexcept {
|
||||||
|
const double val = calculateNext();
|
||||||
|
return (to - from) * val + from;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int adaptsColor(const unsigned int from, const unsigned int to) const noexcept {
|
||||||
|
const double val = calculateNext();
|
||||||
|
const long long fr = from;
|
||||||
|
const long long t = to;
|
||||||
|
unsigned int ret = 0;
|
||||||
|
long long temp = 0;
|
||||||
|
temp = (t & 0xff000000) - (fr & 0xff000000);
|
||||||
|
temp = static_cast<long long>(temp * val);
|
||||||
|
temp += fr & 0xff000000;
|
||||||
|
if (temp > 0xffffffffLL) ret = 0xff000000;
|
||||||
|
else ret = temp & 0xff000000;
|
||||||
|
temp = (t & 0x00ff0000) - (fr & 0x00ff0000);
|
||||||
|
temp = static_cast<long long>(temp * val);
|
||||||
|
temp += fr & 0x00ff0000;
|
||||||
|
if (temp > 0xffffffLL) ret |= 0x00ff0000;
|
||||||
|
else ret |= temp & 0x00ff0000;
|
||||||
|
temp = (t & 0x0000ff00) - (fr & 0x0000ff00);
|
||||||
|
temp = static_cast<long long>(temp * val);
|
||||||
|
temp += fr & 0x0000ff00;
|
||||||
|
if (temp > 0xffffLL) ret |= 0x0000ff00;
|
||||||
|
else ret |= temp & 0x0000ff00;
|
||||||
|
temp = (t & 0x000000ff) - (fr & 0x000000ff);
|
||||||
|
temp = static_cast<long long>(temp * val);
|
||||||
|
temp += fr & 0x000000ff;
|
||||||
|
if (temp > 0xffLL) ret |= 0x000000ff;
|
||||||
|
else ret |= temp & 0x000000ff;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-1-14.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Game.h"
|
||||||
|
|
||||||
|
#include "entity\Entity.h"
|
||||||
|
#include "world\World.h"
|
||||||
|
#include "..\ui\xWindows.h"
|
||||||
|
|
||||||
|
void Game::initialize() {
|
||||||
|
worldManager = allocatedFor(new WorldManager);
|
||||||
|
entityManager = allocatedFor(new EntityManager);
|
||||||
|
setWindow(StartWindow::create());
|
||||||
|
windows.onResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::Game() : caption{ allocatedFor(new CaptionWindow()) }, floatWindow{ allocatedFor(new FloatWindow()) } {
|
||||||
|
Logger.put(L"Game created");
|
||||||
|
random.seed(timeGetTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::~Game() {
|
||||||
|
setWindow(nullptr);
|
||||||
|
delete deallocating(floatWindow);
|
||||||
|
gc.pack();
|
||||||
|
gc.collect();
|
||||||
|
delete deallocating(caption);
|
||||||
|
gc.pack();
|
||||||
|
gc.collect();
|
||||||
|
delete deallocating(worldManager);
|
||||||
|
gc.pack();
|
||||||
|
gc.collect();
|
||||||
|
delete deallocating(entityManager);
|
||||||
|
gc.pack();
|
||||||
|
gc.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::render(const double tickDelta) const noexcept {
|
||||||
|
if (renderer.checkResizing()) return;
|
||||||
|
renderer.gameStartRender();
|
||||||
|
if (worldManager->current) worldManager->current->render(tickDelta);
|
||||||
|
caption->render(tickDelta);
|
||||||
|
hud.render(tickDelta);
|
||||||
|
windows.render(tickDelta);
|
||||||
|
floatWindow->render(tickDelta);
|
||||||
|
renderer.gameEndRender();
|
||||||
|
gc.pack();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::tick() noexcept {
|
||||||
|
++currentTick;
|
||||||
|
if (worldManager->current) worldManager->current->tick();
|
||||||
|
floatWindow->clear();
|
||||||
|
floatWindow->tick();
|
||||||
|
caption->tick();
|
||||||
|
hud.tick();
|
||||||
|
windows.tick();
|
||||||
|
floatWindow->update();
|
||||||
|
tasks.runAll();
|
||||||
|
gc.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Game game = Game();
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-1-14.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// #include "..\utils\gc.h"
|
||||||
|
#include "..\ui\Hud.h"
|
||||||
|
#include "..\utils\Task.h"
|
||||||
|
#include "..\ui\Window.h"
|
||||||
|
|
||||||
|
class [[carlbeks::predecl, carlbeks::defineat("World.h")]] WorldManager;
|
||||||
|
class [[carlbeks::predecl, carlbeks::defineat("Entity.h")]] EntityManager;
|
||||||
|
|
||||||
|
class Game final /* : public IRenderable, public ITickable */ {
|
||||||
|
friend void gameThread();
|
||||||
|
WindowManager windows;
|
||||||
|
Hud hud = Hud(); // 8
|
||||||
|
CaptionWindow* caption; // 8
|
||||||
|
FloatWindow* floatWindow; // 8
|
||||||
|
QWORD currentTick = 0; // 8
|
||||||
|
|
||||||
|
public:
|
||||||
|
TaskScheduler tasks; // 8
|
||||||
|
std::minstd_rand random;
|
||||||
|
WorldManager* worldManager = nullptr;
|
||||||
|
EntityManager* entityManager = nullptr;
|
||||||
|
|
||||||
|
void initialize();
|
||||||
|
Game();
|
||||||
|
~Game();
|
||||||
|
|
||||||
|
int closeWindow(Window* const window) noexcept { return windows.pop(window); }
|
||||||
|
[[nodiscard]] FloatWindow& getFloatWindow() const noexcept { return *floatWindow; }
|
||||||
|
[[nodiscard]] QWORD getTick() const noexcept { return currentTick; }
|
||||||
|
void tick() noexcept;
|
||||||
|
void render(double tickDelta) const noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所有窗口都提交给Game保管,在适当时刻自动删除。
|
||||||
|
* 建议WindowType::create
|
||||||
|
*/
|
||||||
|
int setWindow(Window* window) noexcept {
|
||||||
|
if (window) {
|
||||||
|
if (window->onOpen()) {
|
||||||
|
for (Window& i : windows) i.passEvent(MouseActionCode::MAC_LEAVE, 0, 0, 0);
|
||||||
|
windows.pushNewed(window);
|
||||||
|
Success();
|
||||||
|
}
|
||||||
|
Failed();
|
||||||
|
}
|
||||||
|
windows.clear();
|
||||||
|
Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Window* getWindow() const noexcept {
|
||||||
|
if (auto* const back = windows.back()) return dynamic_cast<Window*>(back);
|
||||||
|
Logger.error(L"Game::getWindow returns nullptr");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void handleResize() {
|
||||||
|
caption->onResize();
|
||||||
|
hud.onResize();
|
||||||
|
windows.onResize();
|
||||||
|
floatWindow->onResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
int passEvent(const MouseActionCode action, const MouseButtonCode value, const int x, const int y) const noexcept {
|
||||||
|
int ret = 0;
|
||||||
|
ret = caption->passEvent(action, value, x, y);
|
||||||
|
if (Window* const window = getWindow()) window->passEvent(action, value, x, y);
|
||||||
|
floatWindow->passEvent(action, value, x, y);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Game game;
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-3-21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "..\..\def.h"
|
||||||
|
|
||||||
|
class [[carlbeks::predecl, carlbeks::defineat("Entity.h")]] Entity;
|
||||||
|
|
||||||
|
struct DamageTypeEnum {
|
||||||
|
enum : unsigned char {
|
||||||
|
NONE, // 无伤害
|
||||||
|
SYSTEMATIC_DAMAGE, // 系统伤害
|
||||||
|
PHYSICAL_DAMAGE, // 物理伤害
|
||||||
|
MAGICAL_DAMAGE, // 魔法伤害
|
||||||
|
TRUE_DAMAGE, // 真实伤害
|
||||||
|
DENY_DAMAGE, // 否定伤害
|
||||||
|
RETURN_DAMAGE, // 回敬伤害
|
||||||
|
BLOOD_PRESSURE_DAMAGE, // 降压伤害
|
||||||
|
} value;
|
||||||
|
|
||||||
|
static constexpr QWORD elementCount() { return BLOOD_PRESSURE_DAMAGE + 1; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DamageFormEnum {
|
||||||
|
enum : unsigned char {
|
||||||
|
NONE,
|
||||||
|
MELEE_DAMAGE, // 近战伤害
|
||||||
|
REMOTE_DAMAGE, // 远程伤害
|
||||||
|
PROJECTILE_DAMAGE, // 弹射物伤害
|
||||||
|
EXECUTE_DAMAGE, // 处决伤害
|
||||||
|
FINAL_DAMAGE,
|
||||||
|
} value;
|
||||||
|
|
||||||
|
static constexpr QWORD elementCount() { return FINAL_DAMAGE + 1; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Damage {
|
||||||
|
double damages[DamageTypeEnum::elementCount()];
|
||||||
|
DamageFormEnum form;
|
||||||
|
|
||||||
|
};
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-3-22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Entity.h"
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-3-21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "..\..\utils\math.h"
|
||||||
|
#include "..\..\render\Renderer.h"
|
||||||
|
#include "..\world\Location.h"
|
||||||
|
#include "Damage.h"
|
||||||
|
|
||||||
|
class Entity;
|
||||||
|
class [[carlbeks::predecl, carlbeks::defineat("World.h")]] World;
|
||||||
|
class EntityManager;
|
||||||
|
|
||||||
|
using EntityID = QWORD;
|
||||||
|
|
||||||
|
interface IDamageable {
|
||||||
|
protected:
|
||||||
|
double maxHealth = 0;
|
||||||
|
double health = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~IDamageable() = default;
|
||||||
|
virtual void Damage(Damage&) = 0;
|
||||||
|
virtual void onDeath() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IArtificialIntelligent {
|
||||||
|
protected:
|
||||||
|
virtual ~IArtificialIntelligent() = default;
|
||||||
|
virtual void aiProcess() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Entity : public IRenderable, public ITickable {
|
||||||
|
friend class World;
|
||||||
|
friend class EntityManager;
|
||||||
|
EntityID idEntity = 0;
|
||||||
|
World* world = nullptr;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Location location;
|
||||||
|
Vector2D velocity;
|
||||||
|
double maxSpeed = 1.0;
|
||||||
|
|
||||||
|
Entity(const Vector2D& location) : location(location) {}
|
||||||
|
~Entity() override = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual void onRemove() = 0;
|
||||||
|
virtual void onEnterWorld(World* world, WorldTransportReason reason) {}
|
||||||
|
virtual void onExitWorld(World* world, WorldTransportReason reason) {}
|
||||||
|
|
||||||
|
void setVelocity(const Vector2D& velocity) noexcept { this->velocity = velocity; }
|
||||||
|
[[nodiscard]] const Location& getLocation() const noexcept { return this->location; }
|
||||||
|
[[nodiscard]] const Location& getLocation(const double tickDelta) const noexcept { return Location(this->location.getPosition() + this->velocity * tickDelta, this->location.getWorld()); }
|
||||||
|
[[nodiscard]] Vector2D getVelocity() const noexcept { return this->velocity; }
|
||||||
|
[[nodiscard]] double getMaxSpeed() const noexcept { return this->maxSpeed; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Enemy : public Entity, public IDamageable, public IArtificialIntelligent {
|
||||||
|
protected:
|
||||||
|
Enemy(const Vector2D& location) : Entity(location) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class EntityManager {
|
||||||
|
friend class Game;
|
||||||
|
EntityID nextID = 0;
|
||||||
|
Map<EntityID, Entity*> entities;
|
||||||
|
EntityManager() = default;
|
||||||
|
~EntityManager() { for (auto& [id, entity] : entities) { entity->onRemove(); } }
|
||||||
|
|
||||||
|
public:
|
||||||
|
int addEntity(Entity* entity) {
|
||||||
|
if (!entity) Failed();
|
||||||
|
if (entity->idEntity) Failed();
|
||||||
|
entity->idEntity = ++nextID;
|
||||||
|
entities.emplace(entity->idEntity, entity);
|
||||||
|
Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
int removeEntity(Entity* entity) {
|
||||||
|
if (!entity) Failed();
|
||||||
|
if (entity->idEntity) Failed();
|
||||||
|
if (entity->getLocation().getWorld()) Failed(); // 确保必须已经从其他世界移除
|
||||||
|
entities.erase(entity->idEntity);
|
||||||
|
entity->onRemove();
|
||||||
|
Success();
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-3-21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Player.h"
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-3-21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Entity.h"
|
||||||
|
|
||||||
|
class Player : public Entity, public IDamageable {
|
||||||
|
public:
|
||||||
|
Player(const Vector2D& location) : Entity(location) {}
|
||||||
|
};
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-3-21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "..\..\def.h"
|
||||||
|
#include "..\..\utils\gc.h"
|
||||||
|
#include "Location.h"
|
||||||
|
|
||||||
|
class [[carlbeks::predecl, carlbeks::defineat("World.h")]] World;
|
||||||
|
|
||||||
|
class Block : public IRenderable, public ITickable {
|
||||||
|
friend class Garbage<Block>;
|
||||||
|
friend class World;
|
||||||
|
World* world = nullptr;
|
||||||
|
BlockLocation location;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Block(const BlockLocation& location) : location(location) { this->location.setWorld(0); }
|
||||||
|
[[nodiscard]] const BlockLocation& getLocation() const { return location; }
|
||||||
|
virtual void onEnterWorld(World* world, WorldTransportReason reason) {}
|
||||||
|
virtual void onExitWorld(World* world, WorldTransportReason reason) {}
|
||||||
|
virtual void onRemove() { gc.submit<Block>(this); }
|
||||||
|
/**
|
||||||
|
* 渲染方块影。方块影会覆盖所有的方块。
|
||||||
|
*/
|
||||||
|
virtual void renderShadow() const noexcept {}
|
||||||
|
/**
|
||||||
|
* 渲染方块本体。渲染范围不应当超过方块占据的范围。
|
||||||
|
*/
|
||||||
|
void render(double tickDelta) const noexcept override = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PureBarrierBlock final : public Block {
|
||||||
|
PureBarrierBlock(const BlockLocation& location) : Block(location) {}
|
||||||
|
~PureBarrierBlock() override {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void render(double tickDelta) const noexcept override { renderer.fillWorldBlock(getLocation(), 0xffeeeeee); }
|
||||||
|
void tick() noexcept override {}
|
||||||
|
static PureBarrierBlock* create(const BlockLocation& location) { return allocatedFor(new PureBarrierBlock(location)); }
|
||||||
|
};
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-3-23.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Location.h"
|
||||||
|
|
||||||
|
inline const WorldTransportReason WorldTransportReason::Shutdown = *manager.create(L"game shutdown", true, true);
|
||||||
|
inline const WorldTransportReason WorldTransportReason::InitialGeneration = *manager.create(L"initial generation", true, true);
|
||||||
|
inline const WorldTransportReason WorldTransportReason::WorldCollapse = *manager.create(L"world collapse", true, true);
|
||||||
|
inline const WorldTransportReason WorldTransportReason::BlockBreak = *manager.create(L"block break", true, false);
|
||||||
|
inline const WorldTransportReason WorldTransportReason::BlockReplace = *manager.create(L"block replace", true, false);
|
||||||
|
inline const WorldTransportReason WorldTransportReason::EntityTeleport = *manager.create(L"entity teleport", false, true);
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-3-21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "..\..\utils\math.h"
|
||||||
|
|
||||||
|
using WorldID = QWORD;
|
||||||
|
class Location;
|
||||||
|
class BlockLocation;
|
||||||
|
class [[carlbeks::predecl, carlbeks::defineat("World.h")]] World;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当一个方块/实体被移入/移出世界时,需要有一个理由。
|
||||||
|
* 如果方块移出时附带内置的Shutdown理由,那么方块在移出时会被立刻自动submit至gc。
|
||||||
|
* Shutdown理由的含义是,世界管理器下的所有世界内的所有方块,都会被依次附带Shutdown理由移出世界。
|
||||||
|
* 此过程中不可以进行其他的方块WorldTransport行为(因为正在遍历方块),也不需要手动清除其他的关联方块。
|
||||||
|
* 禁止在处理附带Shutdown理由的移出世界事件时提交自身给gc。但是方块自身的其他垃圾可能需要手动清理。
|
||||||
|
*
|
||||||
|
* 该类视同enum类。元素只允许引用访问,不允许复制、移动。
|
||||||
|
*/
|
||||||
|
class WorldTransportReason final {
|
||||||
|
friend class World;
|
||||||
|
struct LessReason;
|
||||||
|
|
||||||
|
class Reason final {
|
||||||
|
friend struct LessReason;
|
||||||
|
friend class WorldTransportReason;
|
||||||
|
const String description;
|
||||||
|
const bool isBlockReason, isEntityReason;
|
||||||
|
~Reason() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Reason(const String& description, const bool isBlockReason, const bool isEntityReason) : description(description), isBlockReason(isBlockReason), isEntityReason(isEntityReason) {}
|
||||||
|
Reason(String&& description, const bool isBlockReason, const bool isEntityReason) : description(std::move(description)), isBlockReason(isBlockReason), isEntityReason(isEntityReason) {}
|
||||||
|
Reason(const Reason&) = delete;
|
||||||
|
Reason(Reason&&) = delete;
|
||||||
|
Reason& operator=(const Reason&) = delete;
|
||||||
|
Reason& operator=(Reason&&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LessReason {
|
||||||
|
using is_transparent = String;
|
||||||
|
[[nodiscard]] bool operator()(const Reason* const lhs, const Reason* const rhs) const { return lhs->description < rhs->description; }
|
||||||
|
[[nodiscard]] bool operator()(const String& str, const Reason* const rhs) const { return str < rhs->description; }
|
||||||
|
[[nodiscard]] bool operator()(const Reason* const lhs, const String& str) const { return lhs->description < str; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Manager {
|
||||||
|
Set<Reason*, LessReason> reasons;
|
||||||
|
~Manager() { for (Reason* r : reasons) delete deallocating(r); }
|
||||||
|
|
||||||
|
Reason* create(const String& description, const bool isBlockReason, const bool isEntityReason) {
|
||||||
|
if (const Set<Reason*, LessReason>::const_iterator iter = reasons.find(description); iter != reasons.end()) return iter.operator*();
|
||||||
|
Reason* r = allocatedFor(new Reason(description, isBlockReason, isEntityReason));
|
||||||
|
reasons.emplace(r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reason* create(String&& description, const bool isBlockReason, const bool isEntityReason) {
|
||||||
|
if (const Set<Reason*, LessReason>::const_iterator iter = reasons.find(description); iter != reasons.end()) return iter.operator*();
|
||||||
|
Reason* r = allocatedFor(new Reason(std::move(description), isBlockReason, isEntityReason));
|
||||||
|
reasons.emplace(r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} inline static manager;
|
||||||
|
|
||||||
|
static const WorldTransportReason Shutdown;
|
||||||
|
const Reason* reason;
|
||||||
|
WorldTransportReason(const Reason& reason) : reason(&reason) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const WorldTransportReason InitialGeneration;
|
||||||
|
static const WorldTransportReason WorldCollapse;
|
||||||
|
static const WorldTransportReason BlockBreak;
|
||||||
|
static const WorldTransportReason BlockReplace;
|
||||||
|
static const WorldTransportReason EntityTeleport;
|
||||||
|
|
||||||
|
WorldTransportReason() = delete;
|
||||||
|
WorldTransportReason(const WorldTransportReason&) = default;
|
||||||
|
WorldTransportReason(WorldTransportReason&&) = default;
|
||||||
|
WorldTransportReason& operator=(const WorldTransportReason&) = default;
|
||||||
|
WorldTransportReason& operator=(WorldTransportReason&&) = default;
|
||||||
|
~WorldTransportReason() = default;
|
||||||
|
[[nodiscard]] bool operator==(const WorldTransportReason& other) const noexcept { return reason == other.reason; }
|
||||||
|
[[nodiscard]] bool operator!=(const WorldTransportReason& other) const noexcept { return reason != other.reason; }
|
||||||
|
[[nodiscard]] bool isBlockReason() const noexcept { return reason->isBlockReason; }
|
||||||
|
[[nodiscard]] bool isEntityReason() const noexcept { return reason->isEntityReason; }
|
||||||
|
|
||||||
|
static WorldTransportReason registerReason(const String& description, const bool isBlockReason, const bool isEntityReason) { return *manager.create(description, isBlockReason, isEntityReason); }
|
||||||
|
static WorldTransportReason registerReason(String&& description, const bool isBlockReason, const bool isEntityReason) { return *manager.create(std::move(description), isBlockReason, isEntityReason); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class [[carlbeks::TriviallyCopyable]] Location {
|
||||||
|
Vector2D position;
|
||||||
|
WorldID idWorld;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Location(const Vector2D& position) noexcept : position(position), idWorld(0) {}
|
||||||
|
Location(const Vector2D& position, const WorldID idWorld) noexcept : position(position), idWorld(idWorld) {}
|
||||||
|
Location(const Location& other) noexcept = default;
|
||||||
|
Location(Location&& other) noexcept = default;
|
||||||
|
[[nodiscard]] Vector2D getPosition() const noexcept { return position; }
|
||||||
|
[[nodiscard]] WorldID getWorld() const noexcept { return idWorld; }
|
||||||
|
[[nodiscard]] double getX() const noexcept { return position.getX(); }
|
||||||
|
[[nodiscard]] double getY() const noexcept { return position.getY(); }
|
||||||
|
[[nodiscard]] BlockLocation getBlockLocation() const noexcept;
|
||||||
|
|
||||||
|
void setPosition(const Vector2D& vector) noexcept { position = vector; }
|
||||||
|
void setWorld(const WorldID idWorld) noexcept { this->idWorld = idWorld; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class [[carlbeks::TriviallyCopyable]] BlockLocation {
|
||||||
|
long long x, y;
|
||||||
|
WorldID idWorld;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BlockLocation(const long long x, const long long y) noexcept : x(x), y(y), idWorld(0) {}
|
||||||
|
BlockLocation(const Vector2D& position) noexcept : x(static_cast<long long>(position.getX())), y(static_cast<long long>(position.getY())), idWorld(0) {}
|
||||||
|
BlockLocation(const long long x, const long long y, const WorldID idWorld) noexcept : x(x), y(y), idWorld(idWorld) {}
|
||||||
|
BlockLocation(const Vector2D& position, const WorldID idWorld) noexcept : x(static_cast<long long>(std::floor(position.getX()))), y(static_cast<long long>(std::floor(position.getY()))), idWorld(idWorld) {}
|
||||||
|
BlockLocation(const BlockLocation& other) noexcept = default;
|
||||||
|
BlockLocation(BlockLocation&& other) noexcept = default;
|
||||||
|
[[nodiscard]] Vector2D getPosition() const noexcept { return Vector2D(static_cast<double>(x), static_cast<double>(y)); }
|
||||||
|
[[nodiscard]] WorldID getWorld() const noexcept { return idWorld; }
|
||||||
|
[[nodiscard]] long long getX() const noexcept { return x; }
|
||||||
|
[[nodiscard]] long long getY() const noexcept { return y; }
|
||||||
|
[[nodiscard]] Location toLocation() const noexcept { return Location({ static_cast<double>(x), static_cast<double>(y) }, idWorld); }
|
||||||
|
|
||||||
|
void setPosition(const long long x, const long long y) noexcept { this->x = x, this->y = y; }
|
||||||
|
void setWorld(const WorldID idWorld) noexcept { this->idWorld = idWorld; }
|
||||||
|
|
||||||
|
struct Less {
|
||||||
|
bool operator()(const BlockLocation& lhs, const BlockLocation& rhs) const noexcept;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool BlockLocation::Less::operator()(const BlockLocation& lhs, const BlockLocation& rhs) const noexcept { return lhs.y < rhs.y || (lhs.y == rhs.y && lhs.x < rhs.x); }
|
||||||
|
|
||||||
|
|
||||||
|
inline BlockLocation Location::getBlockLocation() const noexcept { return BlockLocation(position, idWorld); }
|
||||||
|
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-3-21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "..\..\def.h"
|
||||||
|
#include "..\entity\Entity.h"
|
||||||
|
#include "Block.h"
|
||||||
|
|
||||||
|
class World;
|
||||||
|
class WorldManager;
|
||||||
|
|
||||||
|
class World : public IRenderable, public ITickable {
|
||||||
|
friend class WorldManager;
|
||||||
|
friend class Garbage<World>;
|
||||||
|
WorldID idWorld;
|
||||||
|
Map<QWORD, Entity*> entities;
|
||||||
|
Map<BlockLocation, Block*, BlockLocation::Less> blocks;
|
||||||
|
using IterEntity = Map<QWORD, Entity*>::const_iterator;
|
||||||
|
using IterBlock = Map<BlockLocation, Block*, BlockLocation::Less>::const_iterator;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
World() = default;
|
||||||
|
|
||||||
|
~World() override {
|
||||||
|
Logger.debug(L"~World() called");
|
||||||
|
for (auto& [location, block] : blocks) {
|
||||||
|
block->onExitWorld(this, WorldTransportReason::Shutdown);
|
||||||
|
block->location.setWorld(0);
|
||||||
|
block->world = nullptr;
|
||||||
|
block->onRemove();
|
||||||
|
}
|
||||||
|
for (auto& [id, entity] : entities) {
|
||||||
|
entity->onExitWorld(this, WorldTransportReason::Shutdown);
|
||||||
|
entity->location.setWorld(0);
|
||||||
|
entity->world = nullptr;
|
||||||
|
// entity->onRemove() 交给EntityManager调用
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void render(const double tickDelta) const noexcept override {
|
||||||
|
for (const auto& [location, block] : blocks) block->render(tickDelta);
|
||||||
|
for (const auto& [id, entity] : entities) entity->render(tickDelta);
|
||||||
|
for (const auto& [location, block] : blocks) block->renderShadow();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int addEntity(Entity* entity, const WorldTransportReason reason) {
|
||||||
|
if (!entity) Failed();
|
||||||
|
if (!entity->idEntity) Failed();
|
||||||
|
if (entity->location.getWorld()) Failed();
|
||||||
|
if (entity->world) Failed();
|
||||||
|
if (!reason.isEntityReason()) Failed();
|
||||||
|
entity->onEnterWorld(this, reason);
|
||||||
|
entity->location.setWorld(idWorld);
|
||||||
|
entity->world = this;
|
||||||
|
entities.emplace(entity->idEntity, entity);
|
||||||
|
Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int removeEntity(Entity* entity, const WorldTransportReason reason) {
|
||||||
|
if (!entity) Failed();
|
||||||
|
if (!entity->idEntity) Failed();
|
||||||
|
if (!entities.erase(entity->idEntity)) Failed();
|
||||||
|
if (!reason.isEntityReason()) Failed();
|
||||||
|
entity->onExitWorld(this, reason);
|
||||||
|
entity->location.setWorld(0);
|
||||||
|
entity->world = nullptr;
|
||||||
|
Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int addBlock(Block* block, const WorldTransportReason reason) {
|
||||||
|
if (!block) Failed();
|
||||||
|
if (block->getLocation().getWorld()) Failed();
|
||||||
|
if (block->world) Failed();
|
||||||
|
if (blocks.contains(block->getLocation())) Failed();
|
||||||
|
if (!reason.isBlockReason()) Failed();
|
||||||
|
blocks.emplace(block->getLocation(), block);
|
||||||
|
block->onEnterWorld(this, reason);
|
||||||
|
block->location.setWorld(idWorld);
|
||||||
|
block->world = this;
|
||||||
|
Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int removeBlock(Block* block, const WorldTransportReason reason) {
|
||||||
|
if (!block) Failed();
|
||||||
|
if (block->getLocation().getWorld() != idWorld) Failed();
|
||||||
|
if (!blocks.erase(block->getLocation())) Failed();
|
||||||
|
if (!reason.isBlockReason()) Failed();
|
||||||
|
block->onExitWorld(this, reason);
|
||||||
|
block->location.setWorld(0);
|
||||||
|
block->world = nullptr;
|
||||||
|
Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int removeBlockAt(const BlockLocation& location, const WorldTransportReason reason) {
|
||||||
|
if (!reason.isBlockReason()) Failed();
|
||||||
|
if (location.getWorld() != idWorld) Failed();
|
||||||
|
const IterBlock it = blocks.find(location);
|
||||||
|
if (it == blocks.cend()) Failed();
|
||||||
|
Block* block = it->second;
|
||||||
|
blocks.erase(it);
|
||||||
|
block->onExitWorld(this, reason);
|
||||||
|
block->location.setWorld(0);
|
||||||
|
block->world = nullptr;
|
||||||
|
Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void onRemove() {
|
||||||
|
// Entity不需要再此处删除,交给EntityManager管理
|
||||||
|
for (auto& [id, entity] : entities) {
|
||||||
|
entity->onExitWorld(this, WorldTransportReason::WorldCollapse);
|
||||||
|
entity->location.setWorld(0);
|
||||||
|
entity->world = nullptr;
|
||||||
|
}
|
||||||
|
for (auto& [location, block] : blocks) {
|
||||||
|
block->onExitWorld(this, WorldTransportReason::WorldCollapse);
|
||||||
|
block->location.setWorld(0);
|
||||||
|
block->world = nullptr;
|
||||||
|
block->onRemove();
|
||||||
|
}
|
||||||
|
gc.submit<World>(this);
|
||||||
|
Logger.debug(L"World::onRemove() called");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class WorldManager {
|
||||||
|
friend class Game;
|
||||||
|
WorldID nextID = 0;
|
||||||
|
Map<WorldID, World*> worlds;
|
||||||
|
World* current = nullptr;
|
||||||
|
WorldManager() = default;
|
||||||
|
~WorldManager() {
|
||||||
|
Logger.debug(L"~WorldManager() called");
|
||||||
|
for (auto& [id, world] : worlds) world->onRemove();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
int addWorld(World* world) {
|
||||||
|
if (!world) Failed();
|
||||||
|
if (world->idWorld) Failed();
|
||||||
|
world->idWorld = ++nextID;
|
||||||
|
worlds.emplace(world->idWorld, world);
|
||||||
|
Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
int removeWorld(World* world) {
|
||||||
|
if (!world) Failed();
|
||||||
|
if (!world->idWorld) Failed();
|
||||||
|
world->idWorld = 0;
|
||||||
|
worlds.erase(world->idWorld);
|
||||||
|
world->onRemove();
|
||||||
|
Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
int setWorld(World* world) {
|
||||||
|
if (!world) Failed();
|
||||||
|
if (world->idWorld) Failed();
|
||||||
|
current = world;
|
||||||
|
Success();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class StartWorld final : public World {
|
||||||
|
StartWorld() = default;
|
||||||
|
~StartWorld() override { Logger.debug(L"~StartWorld() called"); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
void tick() noexcept override {}
|
||||||
|
|
||||||
|
static StartWorld* create() {
|
||||||
|
StartWorld* world = allocatedFor(new StartWorld);
|
||||||
|
world->addBlock(PureBarrierBlock::create(BlockLocation(0, 0)), WorldTransportReason::InitialGeneration);
|
||||||
|
world->addBlock(PureBarrierBlock::create(BlockLocation(1, 0)), WorldTransportReason::InitialGeneration);
|
||||||
|
world->addBlock(PureBarrierBlock::create(BlockLocation(2, 0)), WorldTransportReason::InitialGeneration);
|
||||||
|
world->addBlock(PureBarrierBlock::create(BlockLocation(3, 0)), WorldTransportReason::InitialGeneration);
|
||||||
|
world->addBlock(PureBarrierBlock::create(BlockLocation(4, 0)), WorldTransportReason::InitialGeneration);
|
||||||
|
return world;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "def.h"
|
||||||
|
|
||||||
|
inline HRESULT RemoveDefaultCaption(const HWND hWnd, const MARGINS* p) noexcept { return DwmExtendFrameIntoClientArea(hWnd, p); }
|
||||||
|
|
||||||
|
inline bool ShowConsoleIO() noexcept {
|
||||||
|
AllocConsole();
|
||||||
|
freopen("CONOUT$", "w", stdout);
|
||||||
|
freopen("CONIN$", "r+", stdin);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const String ApplicationName = L"High Blood Pressure";
|
||||||
|
inline HINSTANCE MainInstance;
|
||||||
|
inline HWND MainWindowHandle;
|
||||||
|
inline Thread GameThread;
|
||||||
|
inline Thread RenderThread;
|
||||||
|
inline bool isRunning = ShowConsoleIO();
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-1-14.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "warnings.h"
|
||||||
|
|
||||||
|
// Any include
|
||||||
|
#include "def.h"
|
||||||
|
#include "utils\math.h" // required by Location.h
|
||||||
|
#include "utils\File.h"
|
||||||
|
#include "utils\exception.h"
|
||||||
|
#include "utils\Chars.h"
|
||||||
|
|
||||||
|
// utils serialized
|
||||||
|
#include "utils\gc.h" // required by utils.h
|
||||||
|
#include "utils\utils.h" // required by Task.h
|
||||||
|
#include "utils\Task.h"
|
||||||
|
#include "hbp.h"
|
||||||
|
|
||||||
|
// game basic
|
||||||
|
#include "interact\InteractManager.h"
|
||||||
|
#include "utils\IText.h"
|
||||||
|
#include "game\world\Location.h" // required by Renderer.h
|
||||||
|
#include "render\Renderer.h"
|
||||||
|
#include "game\Animation.h"
|
||||||
|
#include "render\TextureManager.h"
|
||||||
|
#include "ui\Hud.h"
|
||||||
|
#include "ui\Window.h"
|
||||||
|
|
||||||
|
// game
|
||||||
|
#include "game\Game.h"
|
||||||
|
|
||||||
|
// game extension
|
||||||
|
#include "ui\xWindows.h"
|
||||||
|
#include "game\entity\Entity.h"
|
||||||
|
#include "game\world\Block.h"
|
||||||
|
#include "game\world\World.h"
|
||||||
|
#include "game\entity\Player.h"
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include "InteractManager.h"
|
#include "InteractManager.h"
|
||||||
|
|
||||||
|
#include "..\ui\Window.h"
|
||||||
|
|
||||||
InteractManager::InteractManager() {
|
InteractManager::InteractManager() {
|
||||||
keyStatus[0x00].name = L"NONE";
|
keyStatus[0x00].name = L"NONE";
|
||||||
keyStatus[0x01].name = L"LeftButton";
|
keyStatus[0x01].name = L"LeftButton";
|
||||||
@@ -263,5 +265,19 @@ InteractManager::InteractManager() {
|
|||||||
keyStatus[0xFE].name = L"OEM-Clear";
|
keyStatus[0xFE].name = L"OEM-Clear";
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyStatus& InteractManager::getKey(const KeyBinding& binding) noexcept { return keyStatus[binding.keyCode]; }
|
void InteractManager::updateMouse(const int x, const int y) noexcept {
|
||||||
|
mouseX = x;
|
||||||
|
mouseY = y;
|
||||||
|
if (y < interactSettings.actual.captionHeight) outsideWindow = 2;
|
||||||
|
else outsideWindow = 1;
|
||||||
|
hovering = false;
|
||||||
|
if (!TrackMouseEvent(&trackMouseEvent)) Logger.error(L"TrackMouseEvent failed. LastError: " + std::to_wstring(GetLastError()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InteractManager::isInSizeBox() const noexcept { return isInWindow() && !isInClientCaption(); }
|
||||||
|
|
||||||
|
bool InteractManager::isInClientCaption() const noexcept { return mouseX > interactSettings.actual.marginWidth && mouseX < renderer.getSyncWidth() - interactSettings.actual.marginWidth && mouseY > interactSettings.actual.marginWidth && mouseY < renderer.getSyncHeight() - interactSettings.actual.marginWidth; }
|
||||||
|
|
||||||
|
|
||||||
|
KeyStatus& InteractManager::getKey(const KeyBindingLegacy& binding) noexcept { return keyStatus[binding.keyCode]; }
|
||||||
|
MouseButtonCode InteractManager::getMouseButtonCode() const noexcept { return (keyStatus[VK_LBUTTON].isPressed() ? static_cast<unsigned int>(MouseButtonCodeEnum::MBC_L_DOWN) : 0) | (keyStatus[VK_RBUTTON].isPressed() ? static_cast<int>(MouseButtonCodeEnum::MBC_R_DOWN) : 0) | (keyStatus[VK_MBUTTON].isPressed() ? static_cast<int>(MouseButtonCodeEnum::MBC_M_DOWN) : 0); }
|
||||||
@@ -0,0 +1,244 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-1-14.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "..\hbp.h"
|
||||||
|
#include "..\def.h"
|
||||||
|
|
||||||
|
struct KeyStatus {
|
||||||
|
String name;
|
||||||
|
unsigned int pressTimes = 0;
|
||||||
|
bool pressed = false;
|
||||||
|
bool notDealt = false;
|
||||||
|
|
||||||
|
[[nodiscard]] bool isPressed() const noexcept { return pressed; }
|
||||||
|
[[nodiscard]] unsigned int wasPressed() const noexcept { return pressTimes; }
|
||||||
|
|
||||||
|
void deals() noexcept {
|
||||||
|
notDealt = false;
|
||||||
|
pressTimes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] String toString() const noexcept { return L"KeyStatus: { name = \"" + name + L"\"; pressTimes = " + std::to_wstring(pressTimes) + L"; pressed = " + (pressed ? L"true; }" : L"false; }"); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MouseStatus {
|
||||||
|
String name;
|
||||||
|
unsigned int pressTimes = 0;
|
||||||
|
bool pressed = false;
|
||||||
|
bool notDealt = false;
|
||||||
|
bool longHold = false;
|
||||||
|
|
||||||
|
[[nodiscard]] bool isPressed() const noexcept { return pressed; }
|
||||||
|
[[nodiscard]] unsigned int wasPressed() const noexcept { return pressTimes; }
|
||||||
|
|
||||||
|
void deals() noexcept {
|
||||||
|
notDealt = false;
|
||||||
|
pressTimes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] String toString() const noexcept { return L"MouseStatus: { name = \"" + name + L"\"; pressTimes = " + std::to_wstring(pressTimes) + L"; pressed = " + (pressed ? L"true, longHold = " : L"false, longHold = ") + (longHold ? L"true; }" : L"false; }"); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct KeyBindingLegacy;
|
||||||
|
|
||||||
|
class InteractManager {
|
||||||
|
TRACKMOUSEEVENT trackMouseEvent{
|
||||||
|
.cbSize = sizeof(TRACKMOUSEEVENT),
|
||||||
|
.dwFlags = TME_HOVER | TME_LEAVE,
|
||||||
|
.hwndTrack = nullptr,
|
||||||
|
.dwHoverTime = HOVER_DEFAULT
|
||||||
|
};
|
||||||
|
KeyStatus keyStatus[256] {};
|
||||||
|
int mouseX = 0, mouseY = 0;
|
||||||
|
int mouseWheel = 0;
|
||||||
|
int rebindResult = 0;
|
||||||
|
char outsideWindow = 0; // 鼠标是否在窗口外部。1位:在客户区;2位:在标题栏
|
||||||
|
bool rebinding = false;
|
||||||
|
bool hovering = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void initialize() noexcept { trackMouseEvent.hwndTrack = MainWindowHandle; }
|
||||||
|
InteractManager();
|
||||||
|
|
||||||
|
void update(const int keyCode, const bool isPressed) noexcept {
|
||||||
|
if (keyCode >= 256) return;
|
||||||
|
if (rebinding) {
|
||||||
|
rebindResult = keyCode;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
keyStatus[keyCode].pressed = isPressed;
|
||||||
|
if (isPressed) {
|
||||||
|
keyStatus[keyCode].pressTimes++;
|
||||||
|
keyStatus[keyCode].notDealt = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void mouseLeaveCaption() noexcept {
|
||||||
|
outsideWindow &= ~2;
|
||||||
|
hovering = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mouseLeaveClient() noexcept {
|
||||||
|
outsideWindow &= ~1;
|
||||||
|
hovering = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateMouse(int x, int y) noexcept;
|
||||||
|
void updateWheel(const int wheel) noexcept { mouseWheel += wheel; }
|
||||||
|
void mouseHover() noexcept { hovering = true; }
|
||||||
|
[[nodiscard]] int getMouseX() const noexcept { return mouseX; }
|
||||||
|
[[nodiscard]] int getMouseY() const noexcept { return mouseY; }
|
||||||
|
[[nodiscard]] int getMouseWheel() const noexcept { return mouseWheel; }
|
||||||
|
[[nodiscard]] bool isHovering() const noexcept { return hovering; }
|
||||||
|
[[nodiscard]] bool isInWindow() const noexcept { return outsideWindow; }
|
||||||
|
[[nodiscard]] bool isInSizeBox() const noexcept;
|
||||||
|
[[nodiscard]] bool isInClientCaption() const noexcept;
|
||||||
|
[[nodiscard]] KeyStatus& getKey(const int keyCode) noexcept { return keyStatus[keyCode]; }
|
||||||
|
[[nodiscard]] KeyStatus& getKey(const KeyBindingLegacy& binding) noexcept;
|
||||||
|
[[nodiscard]] unsigned int /*MouseButtonCode*/ getMouseButtonCode() const noexcept;
|
||||||
|
|
||||||
|
int dealMouseWheel() noexcept {
|
||||||
|
const int ret = mouseWheel;
|
||||||
|
mouseWheel = 0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline InteractManager interactManager = InteractManager();
|
||||||
|
|
||||||
|
struct KeyBindingLegacy {
|
||||||
|
String id;
|
||||||
|
int keyCode;
|
||||||
|
[[nodiscard]] bool isPressed() const noexcept { return interactManager.getKey(keyCode).isPressed(); }
|
||||||
|
[[nodiscard]] unsigned int wasPressed() const noexcept { return interactManager.getKey(keyCode).wasPressed(); }
|
||||||
|
void deals() const noexcept { interactManager.getKey(keyCode).deals(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class KeyBinding;
|
||||||
|
struct LessKeyBinding;
|
||||||
|
class KeyRegion;
|
||||||
|
struct LessKeyRegion;
|
||||||
|
class KeyBindingManager;
|
||||||
|
|
||||||
|
class KeyBinding {
|
||||||
|
friend struct LessKeyBinding;
|
||||||
|
friend class KeyBindingManager;
|
||||||
|
String id;
|
||||||
|
unsigned int pressTimes = 0;
|
||||||
|
unsigned char keyCode[8]{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
[[nodiscard]] bool isPressed() const noexcept {
|
||||||
|
for (const int i : keyCode) if (!interactManager.getKey(i).isPressed()) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] unsigned int wasPressed() const noexcept { return pressTimes; }
|
||||||
|
|
||||||
|
[[nodiscard]] unsigned int boundKeyCount() const noexcept {
|
||||||
|
unsigned int ret = 0;
|
||||||
|
while (keyCode[ret] && ret < 8) ++ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LessKeyBinding { // std::less
|
||||||
|
[[nodiscard]] bool operator()(const KeyBinding& lhs, const KeyBinding& rhs) const noexcept {
|
||||||
|
const unsigned int lc = lhs.boundKeyCount(), rc = rhs.boundKeyCount();
|
||||||
|
if (lc < rc) return false;
|
||||||
|
if (lc > rc) return true;
|
||||||
|
if (lhs.keyCode[0] < rhs.keyCode[0]) return true;
|
||||||
|
if (lhs.keyCode[0] > rhs.keyCode[0]) return false;
|
||||||
|
if (lhs.keyCode[1] == 0) return false;
|
||||||
|
if (lhs.keyCode[1] < rhs.keyCode[1]) return true;
|
||||||
|
if (lhs.keyCode[1] > rhs.keyCode[1]) return false;
|
||||||
|
if (lhs.keyCode[2] == 0) return false;
|
||||||
|
if (lhs.keyCode[2] < rhs.keyCode[2]) return true;
|
||||||
|
if (lhs.keyCode[2] > rhs.keyCode[2]) return false;
|
||||||
|
if (lhs.keyCode[3] == 0) return false;
|
||||||
|
if (lhs.keyCode[3] < rhs.keyCode[3]) return true;
|
||||||
|
if (lhs.keyCode[3] > rhs.keyCode[3]) return false;
|
||||||
|
if (lhs.keyCode[4] == 0) return false;
|
||||||
|
if (lhs.keyCode[4] < rhs.keyCode[4]) return true;
|
||||||
|
if (lhs.keyCode[4] > rhs.keyCode[4]) return false;
|
||||||
|
if (lhs.keyCode[5] == 0) return false;
|
||||||
|
if (lhs.keyCode[5] < rhs.keyCode[5]) return true;
|
||||||
|
if (lhs.keyCode[5] > rhs.keyCode[5]) return false;
|
||||||
|
if (lhs.keyCode[6] == 0) return false;
|
||||||
|
if (lhs.keyCode[6] < rhs.keyCode[6]) return true;
|
||||||
|
if (lhs.keyCode[6] > rhs.keyCode[6]) return false;
|
||||||
|
if (lhs.keyCode[7] == 0) return false;
|
||||||
|
if (lhs.keyCode[7] < rhs.keyCode[7]) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class KeyRegion {
|
||||||
|
friend struct LessKeyRegion;
|
||||||
|
friend class KeyBindingManager;
|
||||||
|
const unsigned int idRegion;
|
||||||
|
mutable Set<KeyBinding, LessKeyBinding> keyBindings;
|
||||||
|
|
||||||
|
KeyRegion(const unsigned int id) : idRegion(id) {}
|
||||||
|
void addKeyBinding(const KeyBinding& binding) const noexcept { keyBindings.insert(binding); }
|
||||||
|
void addKeyBinding(KeyBinding&& binding) const noexcept { keyBindings.insert(std::move(binding)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LessKeyRegion {
|
||||||
|
[[nodiscard]] bool operator()(const KeyRegion& lhs, const KeyRegion& rhs) const noexcept { return lhs.idRegion < rhs.idRegion; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class KeyBindingManager {
|
||||||
|
Set<KeyRegion, LessKeyRegion> keyRegions;
|
||||||
|
unsigned int keyRegionCount = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
const KeyRegion& registerRegion() noexcept { return keyRegions.emplace(KeyRegion(++keyRegionCount)).first.operator*(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class InteractSettings {
|
||||||
|
struct Options {
|
||||||
|
int captionHeight = 120;
|
||||||
|
int marginWidth = 40;
|
||||||
|
int fontHeight = 96;
|
||||||
|
int floatWindowMargin = 16;
|
||||||
|
double mapScale = 32.0; // 1格表现为32像素
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Constants {
|
||||||
|
/**
|
||||||
|
* 非常重要的设置项。<br>
|
||||||
|
* 管理ui的缩放比例。
|
||||||
|
*/
|
||||||
|
double uiScale = 1;
|
||||||
|
double screenScale = 1;
|
||||||
|
double smoothCamera = 0.5; // 相机平滑度。为0,始终瞬时设置相机的位置;为1,相机不动。
|
||||||
|
long long msPerTick = 50;
|
||||||
|
long long msPerRender = 16;
|
||||||
|
unsigned int floatWindowBackground = 0xdd000000;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
Options options; // 用户设置值
|
||||||
|
Options actual; // 适应Scale后的实际值
|
||||||
|
Constants constants;
|
||||||
|
|
||||||
|
InteractSettings& setUiScale(const double scale) noexcept {
|
||||||
|
constants.uiScale = scale;
|
||||||
|
actual.fontHeight = static_cast<int>(options.fontHeight * scale);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
InteractSettings& setScreenScale(const double scale) noexcept {
|
||||||
|
constants.screenScale = scale;
|
||||||
|
actual.captionHeight = static_cast<int>(options.captionHeight * scale);
|
||||||
|
actual.marginWidth = static_cast<int>(options.marginWidth * scale);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline InteractSettings interactSettings = InteractSettings();
|
||||||
+596
@@ -0,0 +1,596 @@
|
|||||||
|
|
||||||
|
#include "includes.h"
|
||||||
|
#include "utils\TestCode.h"
|
||||||
|
|
||||||
|
long __stdcall UnhandledExceptionHandler(PEXCEPTION_POINTERS exception) {
|
||||||
|
isRunning = false;
|
||||||
|
Logger.error(L"Unhandled exception handler called");
|
||||||
|
const auto ContextRecord = exception->ContextRecord;
|
||||||
|
const auto ExceptionRecord = exception->ExceptionRecord;
|
||||||
|
Logger.print(
|
||||||
|
std::hex,
|
||||||
|
L"Register parameter home addresses:"
|
||||||
|
L"\n P1Home", ContextRecord->P1Home,
|
||||||
|
L"\n P2Home", ContextRecord->P2Home,
|
||||||
|
L"\n P3Home", ContextRecord->P3Home,
|
||||||
|
L"\n P4Home", ContextRecord->P4Home,
|
||||||
|
L"\n P5Home", ContextRecord->P5Home,
|
||||||
|
L"\n P6Home", ContextRecord->P6Home);
|
||||||
|
Logger.print(
|
||||||
|
L"Control Flags:"
|
||||||
|
L"\n ContextFlags", ContextRecord->ContextFlags,
|
||||||
|
L"\n MxCsr", ContextRecord->MxCsr);
|
||||||
|
Logger.print(
|
||||||
|
L"Segment Registers and processor flags:"
|
||||||
|
L"\n SegCs", ContextRecord->SegCs,
|
||||||
|
L"\n SegDs", ContextRecord->SegDs,
|
||||||
|
L"\n SegEs", ContextRecord->SegEs,
|
||||||
|
L"\n SegFs", ContextRecord->SegFs,
|
||||||
|
L"\n SegGs", ContextRecord->SegGs,
|
||||||
|
L"\n SegSs", ContextRecord->SegSs,
|
||||||
|
L"\n EFlags", ContextRecord->EFlags);
|
||||||
|
Logger.print(
|
||||||
|
L"Debug registers:"
|
||||||
|
L"\n Dr0", ContextRecord->Dr0,
|
||||||
|
L"\n Dr1", ContextRecord->Dr1,
|
||||||
|
L"\n Dr2", ContextRecord->Dr2,
|
||||||
|
L"\n Dr3", ContextRecord->Dr3,
|
||||||
|
L"\n Dr6", ContextRecord->Dr6,
|
||||||
|
L"\n Dr7", ContextRecord->Dr7);
|
||||||
|
Logger.print(
|
||||||
|
L"Integer registers:"
|
||||||
|
L"\n Rax", ContextRecord->Rax,
|
||||||
|
L"\n Rcx", ContextRecord->Rcx,
|
||||||
|
L"\n Rdx", ContextRecord->Rdx,
|
||||||
|
L"\n Rbx", ContextRecord->Rbx,
|
||||||
|
L"\n Rsp", ContextRecord->Rsp,
|
||||||
|
L"\n Rbp", ContextRecord->Rbp,
|
||||||
|
L"\n Rsi", ContextRecord->Rsi,
|
||||||
|
L"\n Rdi", ContextRecord->Rdi,
|
||||||
|
L"\n R8", ContextRecord->R8,
|
||||||
|
L"\n R9", ContextRecord->R9,
|
||||||
|
L"\n R10", ContextRecord->R10,
|
||||||
|
L"\n R11", ContextRecord->R11,
|
||||||
|
L"\n R12", ContextRecord->R12,
|
||||||
|
L"\n R13", ContextRecord->R13,
|
||||||
|
L"\n R14", ContextRecord->R14,
|
||||||
|
L"\n R15", ContextRecord->R15);
|
||||||
|
Logger.print(
|
||||||
|
L"Program Counter:"
|
||||||
|
L"\n Rip", ContextRecord->Rip);
|
||||||
|
Logger.print(
|
||||||
|
L"Floating point state:"
|
||||||
|
L"\n XMM_SAVE_AREA32:"
|
||||||
|
L"\n ControlWord", ContextRecord->FltSave.ControlWord,
|
||||||
|
L"\n StatusWord", ContextRecord->FltSave.StatusWord,
|
||||||
|
L"\n TagWord", ContextRecord->FltSave.TagWord,
|
||||||
|
L"\n Reserved1", ContextRecord->FltSave.Reserved1,
|
||||||
|
L"\n ErrorOpcode", ContextRecord->FltSave.ErrorOpcode,
|
||||||
|
L"\n ErrorOffset", ContextRecord->FltSave.ErrorOffset,
|
||||||
|
L"\n ErrorSelector", ContextRecord->FltSave.ErrorSelector,
|
||||||
|
L"\n Reserved2", ContextRecord->FltSave.Reserved2,
|
||||||
|
L"\n DataOffset", ContextRecord->FltSave.DataOffset,
|
||||||
|
L"\n DataSelector", ContextRecord->FltSave.DataSelector,
|
||||||
|
L"\n Reserved3", ContextRecord->FltSave.Reserved3,
|
||||||
|
L"\n MxCsr", ContextRecord->FltSave.MxCsr,
|
||||||
|
L"\n MxCsr_Mask", ContextRecord->FltSave.MxCsr_Mask);
|
||||||
|
Logger.print(
|
||||||
|
L"\n FloatRegisters[0]",
|
||||||
|
ContextRecord->FltSave.FloatRegisters[0].High,
|
||||||
|
ContextRecord->FltSave.FloatRegisters[0].Low,
|
||||||
|
L"\n FloatRegisters[1]",
|
||||||
|
ContextRecord->FltSave.FloatRegisters[1].High,
|
||||||
|
ContextRecord->FltSave.FloatRegisters[1].Low,
|
||||||
|
L"\n FloatRegisters[2]",
|
||||||
|
ContextRecord->FltSave.FloatRegisters[2].High,
|
||||||
|
ContextRecord->FltSave.FloatRegisters[2].Low,
|
||||||
|
L"\n FloatRegisters[3]",
|
||||||
|
ContextRecord->FltSave.FloatRegisters[3].High,
|
||||||
|
ContextRecord->FltSave.FloatRegisters[3].Low,
|
||||||
|
L"\n FloatRegisters[4]",
|
||||||
|
ContextRecord->FltSave.FloatRegisters[4].High,
|
||||||
|
ContextRecord->FltSave.FloatRegisters[4].Low,
|
||||||
|
L"\n FloatRegisters[5]",
|
||||||
|
ContextRecord->FltSave.FloatRegisters[5].High,
|
||||||
|
ContextRecord->FltSave.FloatRegisters[5].Low,
|
||||||
|
L"\n FloatRegisters[6]",
|
||||||
|
ContextRecord->FltSave.FloatRegisters[6].High,
|
||||||
|
ContextRecord->FltSave.FloatRegisters[6].Low,
|
||||||
|
L"\n FloatRegisters[7]",
|
||||||
|
ContextRecord->FltSave.FloatRegisters[7].High,
|
||||||
|
ContextRecord->FltSave.FloatRegisters[7].Low);
|
||||||
|
Logger.print(
|
||||||
|
L"\n XmmRegisters[]"
|
||||||
|
L"\n Xmm0",
|
||||||
|
ContextRecord->Xmm0.High,
|
||||||
|
ContextRecord->Xmm0.Low,
|
||||||
|
L"\n Xmm1",
|
||||||
|
ContextRecord->Xmm1.High,
|
||||||
|
ContextRecord->Xmm1.Low,
|
||||||
|
L"\n Xmm2",
|
||||||
|
ContextRecord->Xmm2.High,
|
||||||
|
ContextRecord->Xmm2.Low,
|
||||||
|
L"\n Xmm3",
|
||||||
|
ContextRecord->Xmm3.High,
|
||||||
|
ContextRecord->Xmm3.Low,
|
||||||
|
L"\n Xmm4",
|
||||||
|
ContextRecord->Xmm4.High,
|
||||||
|
ContextRecord->Xmm4.Low,
|
||||||
|
L"\n Xmm5",
|
||||||
|
ContextRecord->Xmm5.High,
|
||||||
|
ContextRecord->Xmm5.Low,
|
||||||
|
L"\n Xmm6",
|
||||||
|
ContextRecord->Xmm6.High,
|
||||||
|
ContextRecord->Xmm6.Low,
|
||||||
|
L"\n Xmm7",
|
||||||
|
ContextRecord->Xmm7.High,
|
||||||
|
ContextRecord->Xmm7.Low,
|
||||||
|
L"\n Xmm8",
|
||||||
|
ContextRecord->Xmm8.High,
|
||||||
|
ContextRecord->Xmm8.Low,
|
||||||
|
L"\n Xmm9",
|
||||||
|
ContextRecord->Xmm9.High,
|
||||||
|
ContextRecord->Xmm9.Low,
|
||||||
|
L"\n Xmm10",
|
||||||
|
ContextRecord->Xmm10.High,
|
||||||
|
ContextRecord->Xmm10.Low,
|
||||||
|
L"\n Xmm11",
|
||||||
|
ContextRecord->Xmm11.High,
|
||||||
|
ContextRecord->Xmm11.Low,
|
||||||
|
L"\n Xmm12",
|
||||||
|
ContextRecord->Xmm12.High,
|
||||||
|
ContextRecord->Xmm12.Low,
|
||||||
|
L"\n Xmm13",
|
||||||
|
ContextRecord->Xmm13.High,
|
||||||
|
ContextRecord->Xmm13.Low,
|
||||||
|
L"\n Xmm14",
|
||||||
|
ContextRecord->Xmm14.High,
|
||||||
|
ContextRecord->Xmm14.Low,
|
||||||
|
L"\n Xmm15",
|
||||||
|
ContextRecord->Xmm15.High,
|
||||||
|
ContextRecord->Xmm15.Low);
|
||||||
|
|
||||||
|
Logger.print(
|
||||||
|
"\nVectorRegister"
|
||||||
|
L"\n 0",
|
||||||
|
ContextRecord->VectorRegister[0].High,
|
||||||
|
ContextRecord->VectorRegister[0].Low,
|
||||||
|
"\n 1",
|
||||||
|
ContextRecord->VectorRegister[1].High,
|
||||||
|
ContextRecord->VectorRegister[1].Low,
|
||||||
|
L"\n 2",
|
||||||
|
ContextRecord->VectorRegister[2].High,
|
||||||
|
ContextRecord->VectorRegister[2].Low,
|
||||||
|
L"\n 3",
|
||||||
|
ContextRecord->VectorRegister[3].High,
|
||||||
|
ContextRecord->VectorRegister[3].Low,
|
||||||
|
L"\n 4",
|
||||||
|
ContextRecord->VectorRegister[4].High,
|
||||||
|
ContextRecord->VectorRegister[4].Low,
|
||||||
|
L"\n 5",
|
||||||
|
ContextRecord->VectorRegister[5].High,
|
||||||
|
ContextRecord->VectorRegister[5].Low,
|
||||||
|
L"\n 6",
|
||||||
|
ContextRecord->VectorRegister[6].High,
|
||||||
|
ContextRecord->VectorRegister[6].Low,
|
||||||
|
L"\n 7",
|
||||||
|
ContextRecord->VectorRegister[7].High,
|
||||||
|
ContextRecord->VectorRegister[7].Low,
|
||||||
|
L"\n 8",
|
||||||
|
ContextRecord->VectorRegister[8].High,
|
||||||
|
ContextRecord->VectorRegister[8].Low,
|
||||||
|
L"\n 9",
|
||||||
|
ContextRecord->VectorRegister[9].High,
|
||||||
|
ContextRecord->VectorRegister[9].Low,
|
||||||
|
L"\n 10",
|
||||||
|
ContextRecord->VectorRegister[10].High,
|
||||||
|
ContextRecord->VectorRegister[10].Low,
|
||||||
|
L"\n 11",
|
||||||
|
ContextRecord->VectorRegister[11].High,
|
||||||
|
ContextRecord->VectorRegister[11].Low,
|
||||||
|
L"\n 12",
|
||||||
|
ContextRecord->VectorRegister[12].High,
|
||||||
|
ContextRecord->VectorRegister[12].Low,
|
||||||
|
L"\n 13",
|
||||||
|
ContextRecord->VectorRegister[13].High,
|
||||||
|
ContextRecord->VectorRegister[13].Low,
|
||||||
|
L"\n 14",
|
||||||
|
ContextRecord->VectorRegister[14].High,
|
||||||
|
ContextRecord->VectorRegister[14].Low,
|
||||||
|
L"\n 15",
|
||||||
|
ContextRecord->VectorRegister[15].High,
|
||||||
|
ContextRecord->VectorRegister[15].Low,
|
||||||
|
L"\n 16",
|
||||||
|
ContextRecord->VectorRegister[16].High,
|
||||||
|
ContextRecord->VectorRegister[16].Low,
|
||||||
|
L"\n 17",
|
||||||
|
ContextRecord->VectorRegister[17].High,
|
||||||
|
ContextRecord->VectorRegister[17].Low,
|
||||||
|
L"\n 18",
|
||||||
|
ContextRecord->VectorRegister[18].High,
|
||||||
|
ContextRecord->VectorRegister[18].Low,
|
||||||
|
L"\n 19",
|
||||||
|
ContextRecord->VectorRegister[19].High,
|
||||||
|
ContextRecord->VectorRegister[19].Low,
|
||||||
|
L"\n 20",
|
||||||
|
ContextRecord->VectorRegister[20].High,
|
||||||
|
ContextRecord->VectorRegister[20].Low,
|
||||||
|
L"\n 21",
|
||||||
|
ContextRecord->VectorRegister[21].High,
|
||||||
|
ContextRecord->VectorRegister[21].Low,
|
||||||
|
L"\n 22",
|
||||||
|
ContextRecord->VectorRegister[22].High,
|
||||||
|
ContextRecord->VectorRegister[22].Low,
|
||||||
|
L"\n 23",
|
||||||
|
ContextRecord->VectorRegister[23].High,
|
||||||
|
ContextRecord->VectorRegister[23].Low,
|
||||||
|
L"\n 24",
|
||||||
|
ContextRecord->VectorRegister[24].High,
|
||||||
|
ContextRecord->VectorRegister[24].Low,
|
||||||
|
L"\n 25",
|
||||||
|
ContextRecord->VectorRegister[25].High,
|
||||||
|
ContextRecord->VectorRegister[25].Low,
|
||||||
|
L"\n VectorControl", ContextRecord->VectorControl);
|
||||||
|
Logger.print(
|
||||||
|
L"\nSpecial debug control registers:"
|
||||||
|
L"\n DebugControl", ContextRecord->DebugControl,
|
||||||
|
L"\n LastBranchToRip", ContextRecord->LastBranchToRip,
|
||||||
|
L"\n LastBranchFromRip", ContextRecord->LastBranchFromRip,
|
||||||
|
L"\n LastExceptionToRip", ContextRecord->LastExceptionToRip,
|
||||||
|
L"\n LastExceptionFromRip", ContextRecord->LastExceptionFromRip
|
||||||
|
);
|
||||||
|
|
||||||
|
Logger.print(
|
||||||
|
L"\nExceptionRecord:"
|
||||||
|
L"\n ExceptionCode", ExceptionRecord->ExceptionCode,
|
||||||
|
L"\n ExceptionFlags", ExceptionRecord->ExceptionFlags,
|
||||||
|
L"\n ExceptionAddress", ExceptionRecord->ExceptionAddress,
|
||||||
|
L"\n NumberParameters", ExceptionRecord->NumberParameters,
|
||||||
|
L"\n ExceptionInformation",
|
||||||
|
L"\n ", ExceptionRecord->ExceptionInformation[0],
|
||||||
|
L"\n ", ExceptionRecord->ExceptionInformation[1],
|
||||||
|
L"\n ", ExceptionRecord->ExceptionInformation[2],
|
||||||
|
L"\n ", ExceptionRecord->ExceptionInformation[3],
|
||||||
|
L"\n ", ExceptionRecord->ExceptionInformation[4],
|
||||||
|
L"\n ", ExceptionRecord->ExceptionInformation[5],
|
||||||
|
L"\n ", ExceptionRecord->ExceptionInformation[6],
|
||||||
|
L"\n ", ExceptionRecord->ExceptionInformation[7],
|
||||||
|
L"\n ", ExceptionRecord->ExceptionInformation[8],
|
||||||
|
L"\n ", ExceptionRecord->ExceptionInformation[9],
|
||||||
|
L"\n ", ExceptionRecord->ExceptionInformation[10],
|
||||||
|
L"\n ", ExceptionRecord->ExceptionInformation[11],
|
||||||
|
L"\n ", ExceptionRecord->ExceptionInformation[12],
|
||||||
|
L"\n ", ExceptionRecord->ExceptionInformation[13],
|
||||||
|
L"\n ", ExceptionRecord->ExceptionInformation[14]
|
||||||
|
);
|
||||||
|
|
||||||
|
return EXCEPTION_EXECUTE_HANDLER;
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT __stdcall WndProc(const HWND hwnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam) {
|
||||||
|
switch (uMsg) {
|
||||||
|
[[likely]]
|
||||||
|
case WM_PAINT: {
|
||||||
|
PAINTSTRUCT ps;
|
||||||
|
BeginPaint(hwnd, &ps);
|
||||||
|
if (renderer.checkResizing()) renderer.resizeShow();
|
||||||
|
EndPaint(hwnd, &ps);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
[[likely]]
|
||||||
|
case WM_NCHITTEST: {
|
||||||
|
POINT point = { GET_X_LPARAM(lParam), (GET_Y_LPARAM(lParam)) };
|
||||||
|
ScreenToClient(hwnd, &point);
|
||||||
|
const int xPos = point.x;
|
||||||
|
const int yPos = point.y;
|
||||||
|
if (renderer.windowSize != SIZE_MAXIMIZED) {
|
||||||
|
if (xPos < interactSettings.actual.marginWidth) {
|
||||||
|
if (yPos < interactSettings.actual.marginWidth) return HTTOPLEFT;
|
||||||
|
if (renderer.getSyncHeight() - yPos < interactSettings.actual.marginWidth) return HTBOTTOMLEFT;
|
||||||
|
return HTLEFT;
|
||||||
|
}
|
||||||
|
if (renderer.getSyncWidth() - xPos < interactSettings.actual.marginWidth) {
|
||||||
|
if (yPos < interactSettings.actual.marginWidth) return HTTOPRIGHT;
|
||||||
|
if (renderer.getSyncHeight() - yPos < interactSettings.actual.marginWidth) return HTBOTTOMRIGHT;
|
||||||
|
return HTRIGHT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (yPos < interactSettings.actual.marginWidth) return HTTOP;
|
||||||
|
if (renderer.windowSize != SIZE_MAXIMIZED && renderer.getSyncHeight() - yPos < interactSettings.actual.marginWidth) return HTBOTTOM;
|
||||||
|
if (yPos < interactSettings.actual.captionHeight) return HTCAPTION;
|
||||||
|
LRESULT lr = 0;
|
||||||
|
DwmDefWindowProc(hwnd, uMsg, wParam, lParam, &lr);
|
||||||
|
return HTCLIENT;
|
||||||
|
}
|
||||||
|
[[likely]]
|
||||||
|
case WM_MOUSEMOVE:
|
||||||
|
interactManager.updateMouse(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
||||||
|
game.passEvent(MouseActionCode::MAC_MOVE, 0, interactManager.getMouseX(), interactManager.getMouseY());
|
||||||
|
break;
|
||||||
|
[[likely]]
|
||||||
|
case WM_NCMOUSEMOVE: {
|
||||||
|
POINT pt{ .x = GET_X_LPARAM(lParam), .y = GET_Y_LPARAM(lParam) };
|
||||||
|
ScreenToClient(MainWindowHandle, &pt);
|
||||||
|
interactManager.updateMouse(pt.x, pt.y);
|
||||||
|
game.passEvent(MouseActionCode::MAC_MOVE, 0, interactManager.getMouseX(), interactManager.getMouseY());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_SIZE:
|
||||||
|
renderer.syncSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
||||||
|
switch (wParam) {
|
||||||
|
case SIZE_RESTORED:
|
||||||
|
case SIZE_MAXIMIZED:
|
||||||
|
renderer.resize(
|
||||||
|
renderer.getSyncWidth(),
|
||||||
|
renderer.getSyncHeight()
|
||||||
|
);
|
||||||
|
PostMessageW(hwnd, WM_EXITSIZEMOVE, 0, 0);
|
||||||
|
break;
|
||||||
|
case SIZE_MINIMIZED:
|
||||||
|
case SIZE_MAXSHOW:
|
||||||
|
case SIZE_MAXHIDE:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
renderer.windowSize = static_cast<byte>(wParam);
|
||||||
|
break;
|
||||||
|
case WM_KEYDOWN:
|
||||||
|
interactManager.update(static_cast<int>(wParam), true);
|
||||||
|
break;
|
||||||
|
case WM_KEYUP:
|
||||||
|
interactManager.update(static_cast<int>(wParam), false);
|
||||||
|
break;
|
||||||
|
case WM_SYSKEYDOWN:
|
||||||
|
interactManager.update(static_cast<int>(wParam), true);
|
||||||
|
break;
|
||||||
|
case WM_SYSKEYUP:
|
||||||
|
interactManager.update(static_cast<int>(wParam), false);
|
||||||
|
break;
|
||||||
|
case WM_LBUTTONDOWN:
|
||||||
|
case WM_NCLBUTTONDOWN:
|
||||||
|
if (interactManager.isInClientCaption()) {
|
||||||
|
interactManager.update(VK_LBUTTON, true);
|
||||||
|
if (game.passEvent(MouseActionCode::MAC_DOWN, interactManager.getMouseButtonCode() | static_cast<unsigned int>(MouseButtonCodeEnum::MBC_L_CHANGE), interactManager.getMouseX(), interactManager.getMouseY())) return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WM_RBUTTONDOWN:
|
||||||
|
case WM_NCRBUTTONDOWN:
|
||||||
|
if (interactManager.isInClientCaption()) {
|
||||||
|
interactManager.update(VK_RBUTTON, true);
|
||||||
|
game.passEvent(MouseActionCode::MAC_DOWN, interactManager.getMouseButtonCode() | static_cast<unsigned int>(MouseButtonCodeEnum::MBC_R_CHANGE), interactManager.getMouseX(), interactManager.getMouseY());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WM_APP_LBUTTONUP:
|
||||||
|
if (interactManager.isInClientCaption()) {
|
||||||
|
interactManager.update(VK_LBUTTON, false);
|
||||||
|
game.passEvent(MouseActionCode::MAC_UP, interactManager.getMouseButtonCode() | static_cast<unsigned int>(MouseButtonCodeEnum::MBC_L_CHANGE), interactManager.getMouseX(), interactManager.getMouseY());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WM_RBUTTONUP:
|
||||||
|
case WM_NCRBUTTONUP:
|
||||||
|
if (interactManager.isInClientCaption()) {
|
||||||
|
interactManager.update(VK_RBUTTON, false);
|
||||||
|
game.passEvent(MouseActionCode::MAC_UP, interactManager.getMouseButtonCode() | static_cast<unsigned int>(MouseButtonCodeEnum::MBC_R_CHANGE), interactManager.getMouseX(), interactManager.getMouseY());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WM_APP_MBUTTONDOWN:
|
||||||
|
if (interactManager.isInClientCaption()) {
|
||||||
|
if (static_cast<QWORD>(wParam) & 0x10u) interactManager.update(VK_MBUTTON, true);
|
||||||
|
game.passEvent(MouseActionCode::MAC_DOWN, interactManager.getMouseButtonCode() | static_cast<unsigned int>(MouseButtonCodeEnum::MBC_M_CHANGE), interactManager.getMouseX(), interactManager.getMouseY());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WM_MBUTTONUP:
|
||||||
|
case WM_NCMBUTTONUP:
|
||||||
|
if (interactManager.isInClientCaption()) {
|
||||||
|
interactManager.update(VK_MBUTTON, false);
|
||||||
|
game.passEvent(MouseActionCode::MAC_UP, interactManager.getMouseButtonCode() | static_cast<unsigned int>(MouseButtonCodeEnum::MBC_M_CHANGE), interactManager.getMouseX(), interactManager.getMouseY());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WM_MOUSEWHEEL:
|
||||||
|
interactManager.update(VK_MBUTTON, true);
|
||||||
|
interactManager.update(VK_MBUTTON, true);
|
||||||
|
break;
|
||||||
|
case WM_MOUSEHOVER:
|
||||||
|
interactManager.mouseHover();
|
||||||
|
break;
|
||||||
|
case WM_NCMOUSELEAVE:
|
||||||
|
interactManager.mouseLeaveCaption();
|
||||||
|
if (!interactManager.isInWindow()) game.passEvent(MouseActionCode::MAC_LEAVE, 0, interactManager.getMouseX(), interactManager.getMouseY());
|
||||||
|
break;
|
||||||
|
case WM_MOUSELEAVE:
|
||||||
|
interactManager.mouseLeaveClient();
|
||||||
|
if (!interactManager.isInWindow()) game.passEvent(MouseActionCode::MAC_LEAVE, 0, interactManager.getMouseX(), interactManager.getMouseY());
|
||||||
|
break;
|
||||||
|
case WM_DWMCOMPOSITIONCHANGED: {
|
||||||
|
constexpr MARGINS margins{
|
||||||
|
.cxLeftWidth = 0,
|
||||||
|
.cxRightWidth = 0,
|
||||||
|
.cyTopHeight = 0,
|
||||||
|
.cyBottomHeight = 0
|
||||||
|
};
|
||||||
|
RemoveDefaultCaption(hwnd, &margins);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_NCCALCSIZE:
|
||||||
|
if (wParam == 1) {
|
||||||
|
NCCALCSIZE_PARAMS* params = reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam);
|
||||||
|
params->rgrc[0].left = params->rgrc[0].left + 0;
|
||||||
|
params->rgrc[0].top = params->rgrc[0].top + 0;
|
||||||
|
params->rgrc[0].right = params->rgrc[0].right - 0;
|
||||||
|
params->rgrc[0].bottom = params->rgrc[0].bottom - 0;
|
||||||
|
renderer.resizeStart();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case WM_EXITSIZEMOVE:
|
||||||
|
renderer.resizeEnd();
|
||||||
|
renderer.resize(renderer.getSyncWidth(), renderer.getSyncHeight());
|
||||||
|
interactSettings.setScreenScale(static_cast<double>(GetSystemMetrics(SM_CYSCREEN)) / 2160.);
|
||||||
|
break;
|
||||||
|
[[unlikely]]
|
||||||
|
case WM_DESTROY:
|
||||||
|
PostQuitMessage(0);
|
||||||
|
isRunning = false;
|
||||||
|
return 0;
|
||||||
|
[[unlikely]]
|
||||||
|
case WM_CREATE:
|
||||||
|
SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
|
||||||
|
renderer.resizeEnd();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT __stdcall HookProc(const int code, const WPARAM wParam, const LPARAM lParam) {
|
||||||
|
if (code < 0) {
|
||||||
|
Logger.log(L"HookProc nCode < 0");
|
||||||
|
return CallNextHookEx(nullptr, code, wParam, lParam);
|
||||||
|
}
|
||||||
|
switch (const MSG* p = reinterpret_cast<MSG*>(lParam); p->message) {
|
||||||
|
case WM_LBUTTONUP:
|
||||||
|
case WM_NCLBUTTONUP:
|
||||||
|
PostMessageW(MainWindowHandle, WM_APP_LBUTTONUP, p->wParam, p->lParam);
|
||||||
|
Logger.info(wParam ? L"[Hook] LButtonUp (Removed)" : L"[Hook] LButtonUp");
|
||||||
|
return 0;
|
||||||
|
case WM_MBUTTONDOWN:
|
||||||
|
case WM_NCMBUTTONDOWN:
|
||||||
|
PostMessageW(MainWindowHandle, WM_APP_MBUTTONDOWN, p->wParam, p->lParam);
|
||||||
|
Logger.info(wParam ? L"[Hook] MButtonDown (Removed)" : L"[Hook] MButtonDown");
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return CallNextHookEx(nullptr, code, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
using Time = std::chrono::time_point<std::chrono::system_clock>;
|
||||||
|
Time lastTick = std::chrono::system_clock::now();
|
||||||
|
|
||||||
|
void gameThread() {
|
||||||
|
try {
|
||||||
|
using namespace std::chrono;
|
||||||
|
using Time = time_point<system_clock>;
|
||||||
|
while (isRunning) {
|
||||||
|
const Time thisTime = system_clock::now();
|
||||||
|
if (thisTime - lastTick < milliseconds(interactSettings.constants.msPerTick)) {
|
||||||
|
Sleep(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
game.tick();
|
||||||
|
renderer.tick();
|
||||||
|
lastTick = thisTime;
|
||||||
|
}
|
||||||
|
} catch (const Exception& e) { Logger.error(L"Game thread exception: " + e.getMessage()); }
|
||||||
|
catch (const std::exception& e) { Logger.error(L"Game thread exception: " + atow(e.what())); }
|
||||||
|
Logger.error(L"Game thread ended.");
|
||||||
|
isRunning = false;
|
||||||
|
DestroyWindow(MainWindowHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderThread() {
|
||||||
|
try {
|
||||||
|
using namespace std::chrono;
|
||||||
|
using Time = time_point<system_clock>;
|
||||||
|
renderer.initialize();
|
||||||
|
Time lastRender = system_clock::now();
|
||||||
|
// bool _TestFlag = false;
|
||||||
|
while (isRunning) {
|
||||||
|
const Time thisTime = system_clock::now();
|
||||||
|
if (thisTime - lastRender < milliseconds(interactSettings.constants.msPerRender)) {
|
||||||
|
Sleep(1);
|
||||||
|
// if (_TestFlag) _TestFlag = false, Logger.debug(L"Render Test: " + std::to_wstring((thisTime - lastRender) / milliseconds(1)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// _TestFlag = true;
|
||||||
|
game.render(nRange(static_cast<double>((thisTime - lastTick).count()) / static_cast<double>(interactSettings.constants.msPerRender), 0.0, 1.0));
|
||||||
|
lastRender = thisTime;
|
||||||
|
}
|
||||||
|
} catch (const Exception& e) { Logger.log(L"Render thread exception: " + e.getMessage()); }
|
||||||
|
catch (const std::exception& e) { Logger.log(L"Render thread exception: " + atow(e.what())); }
|
||||||
|
Logger.error(L"Render thread ended.");
|
||||||
|
isRunning = false;
|
||||||
|
DestroyWindow(MainWindowHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
int __stdcall wWinMain(const HINSTANCE hInstance, const HINSTANCE, [[maybe_unused]] const LPWSTR lpCmdLine, [[maybe_unused]] const int nShowCmd) {
|
||||||
|
Logger.info(L"wWinMain started");
|
||||||
|
SetConsoleOutputCP(65001);
|
||||||
|
SetUnhandledExceptionFilter(UnhandledExceptionFilter);
|
||||||
|
translator.initialize();
|
||||||
|
Logger.info(L"--------Program Start--------");
|
||||||
|
for (const auto& [addr, info] : $LimitedUse::memoryManager.allocated) { Logger.print(L" using", addr, info.size, L"B", info.msg); }
|
||||||
|
WNDCLASSEX wc = {};
|
||||||
|
wc.cbSize = sizeof(WNDCLASSEX);
|
||||||
|
wc.style = CS_HREDRAW | CS_VREDRAW;
|
||||||
|
wc.lpfnWndProc = WndProc;
|
||||||
|
wc.cbClsExtra = 0;
|
||||||
|
wc.cbWndExtra = 0;
|
||||||
|
wc.hInstance = hInstance;
|
||||||
|
wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
|
||||||
|
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||||
|
wc.hbrBackground = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
|
||||||
|
wc.lpszMenuName = L"None";
|
||||||
|
wc.lpszClassName = ApplicationName.c_str();
|
||||||
|
if (!RegisterClassExW(&wc)) return FALSE;
|
||||||
|
if (!SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) Logger.print(L"SetProcessDpiAwarenessContext failed. LastError:", GetLastError());
|
||||||
|
MainInstance = hInstance;
|
||||||
|
MainWindowHandle = CreateWindowExW(0, wc.lpszClassName, wc.lpszClassName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr);
|
||||||
|
SetWindowLongW(MainWindowHandle, GWL_STYLE, WS_VISIBLE | WS_MAXIMIZEBOX);
|
||||||
|
constexpr MARGINS margins{
|
||||||
|
.cxLeftWidth = 0,
|
||||||
|
.cxRightWidth = 0,
|
||||||
|
.cyTopHeight = 0,
|
||||||
|
.cyBottomHeight = 0
|
||||||
|
};
|
||||||
|
RemoveDefaultCaption(MainWindowHandle, &margins);
|
||||||
|
SetWindowLongW(MainWindowHandle, GWL_EXSTYLE, GetWindowLongW(MainWindowHandle, GWL_EXSTYLE) | WS_EX_LAYERED);
|
||||||
|
// SetLayeredWindowAttributes(MainWindowHandle, 0xffffff, 0xe0, LWA_COLORKEY | LWA_ALPHA);
|
||||||
|
SetLayeredWindowAttributes(MainWindowHandle, 0xffffff, 0, LWA_COLORKEY);
|
||||||
|
ShowWindow(MainWindowHandle, nShowCmd);
|
||||||
|
const HHOOK hook = SetWindowsHookW(WH_GETMESSAGE, HookProc);
|
||||||
|
const HACCEL hAccelTable = LoadAcceleratorsW(hInstance, MAKEINTRESOURCE(109));
|
||||||
|
if (!hook) Logger.error(Logger.of(L"SetWindowsHookW failed. LastError:", GetLastError()));
|
||||||
|
test();
|
||||||
|
{
|
||||||
|
// int a = -40;
|
||||||
|
// game.tasks.pushNewed(allocatedFor(new Task([&a](Task& self) {
|
||||||
|
// if (a) SetLayeredWindowAttributes(MainWindowHandle, 0xffffff, static_cast<BYTE>(0xff * (40 + ++a) / 40), LWA_COLORKEY | LWA_ALPHA);
|
||||||
|
// else {
|
||||||
|
// SetLayeredWindowAttributes(MainWindowHandle, 0xffffff, 0xff, LWA_COLORKEY | LWA_ALPHA);
|
||||||
|
// self.schedulePop(true);
|
||||||
|
// SetWindowLongW(MainWindowHandle, GWL_EXSTYLE, GetWindowLongW(MainWindowHandle, GWL_EXSTYLE) & ~WS_EX_LAYERED);
|
||||||
|
// }
|
||||||
|
// return 0;
|
||||||
|
// })
|
||||||
|
// ));
|
||||||
|
game.initialize();
|
||||||
|
interactManager.initialize();
|
||||||
|
World* w = StartWorld::create();
|
||||||
|
game.worldManager->addWorld(w);
|
||||||
|
game.worldManager->setWorld(w);
|
||||||
|
GameThread = Thread(gameThread);
|
||||||
|
RenderThread = Thread(renderThread);
|
||||||
|
}
|
||||||
|
MSG msg = { nullptr };
|
||||||
|
while (GetMessageW(&msg, nullptr, 0, 0)) {
|
||||||
|
if (!TranslateAcceleratorW(msg.hwnd, hAccelTable, &msg)) {
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessageW(&msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
isRunning = false;
|
||||||
|
if (GameThread.joinable()) GameThread.join();
|
||||||
|
if (RenderThread.joinable()) RenderThread.join();
|
||||||
|
}
|
||||||
|
DestroyAcceleratorTable(hAccelTable);
|
||||||
|
UnhookWindowsHookEx(hook);
|
||||||
|
Logger.info(L"------- Program End --------");
|
||||||
|
for (const auto& [addr, info] : $LimitedUse::memoryManager.allocated) { Logger.print(L" using", addr, info.size, L"B", info.msg); }
|
||||||
|
{
|
||||||
|
fontManager.finalize(); // 似乎GDI有终止自动回收,所以此代码需要提前
|
||||||
|
}
|
||||||
|
_wsystem(L"pause");
|
||||||
|
return static_cast<int>(msg.wParam);
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-1-14.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Renderer.h"
|
||||||
|
|
||||||
|
#include "..\game\Game.h"
|
||||||
|
#include "..\hbp.h"
|
||||||
|
#include "..\interact\InteractManager.h"
|
||||||
|
|
||||||
|
void Renderer::gameStartRender() noexcept {
|
||||||
|
isRendering = true;
|
||||||
|
renderThread = std::this_thread::get_id();
|
||||||
|
renderer.fill(0, interactSettings.actual.captionHeight, renderer.getWidth(), renderer.getHeight(), 0xff000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::gameEndRender() noexcept {
|
||||||
|
isRendering = false;
|
||||||
|
BitBlt(MainDC, 0, 0, windowWidth, windowHeight, canvasDC, 0, 0, SRCCOPY);
|
||||||
|
if (isResizing) {
|
||||||
|
if (!resizeCopyBitmap) {
|
||||||
|
resizeCopyWidth = windowWidth;
|
||||||
|
resizeCopyHeight = windowHeight;
|
||||||
|
resizeCopyBitmap = CreateCompatibleBitmap(MainDC, windowWidth, windowHeight);
|
||||||
|
} else if (resizeCopyWidth != windowWidth || resizeCopyHeight != windowHeight) {
|
||||||
|
renderer.deleteObject(resizeCopyBitmap);
|
||||||
|
resizeCopyBitmap = CreateCompatibleBitmap(MainDC, windowWidth, windowHeight);
|
||||||
|
}
|
||||||
|
SelectObject(resizeCopyDC, resizeCopyBitmap);
|
||||||
|
BitBlt(resizeCopyDC, 0, 0, windowWidth, windowHeight, canvasDC, 0, 0, SRCCOPY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::initialize() noexcept {
|
||||||
|
if (MainDC && assistDC && canvasDC) return;
|
||||||
|
MainDC = GetDC(MainWindowHandle);
|
||||||
|
canvasDC = CreateCompatibleDC(MainDC);
|
||||||
|
assistDC = CreateCompatibleDC(canvasDC);
|
||||||
|
resizeCopyDC = CreateCompatibleDC(MainDC);
|
||||||
|
canvasBitmap = CreateCompatibleBitmap(MainDC, windowWidth, windowHeight);
|
||||||
|
SelectObject(canvasDC, canvasBitmap);
|
||||||
|
assistBitmap = CreateCompatibleBitmap(canvasDC, windowWidth, windowHeight);
|
||||||
|
SelectObject(assistDC, assistBitmap);
|
||||||
|
if (!canvasDC) Logger.error(L"canvasDC is nullptr");
|
||||||
|
if (!assistDC) Logger.error(L"assistDC is nullptr");
|
||||||
|
if (!resizeCopyDC) Logger.error(L"resizeCopyDC is nullptr");
|
||||||
|
if (!canvasBitmap) Logger.error(L"canvasBitmap is nullptr");
|
||||||
|
if (!assistBitmap) Logger.error(L"assistBitmap is nullptr");
|
||||||
|
SetBkMode(MainDC, TRANSPARENT);
|
||||||
|
SetBkMode(canvasDC, TRANSPARENT);
|
||||||
|
SetBkMode(assistDC, TRANSPARENT);
|
||||||
|
RECT rect;
|
||||||
|
GetWindowRect(MainWindowHandle, &rect);
|
||||||
|
resize(rect.right - rect.left, rect.bottom - rect.top);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::resize(const int width, const int height) noexcept(false) {
|
||||||
|
if (windowWidth == width && windowHeight == height) return;
|
||||||
|
windowWidth = width;
|
||||||
|
windowHeight = height;
|
||||||
|
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||||
|
deleteObject(canvasBitmap);
|
||||||
|
canvasBitmap = nullptr;
|
||||||
|
canvasBitmap = CreateCompatibleBitmap(MainDC, width, height);
|
||||||
|
SelectObject(canvasDC, canvasBitmap);
|
||||||
|
deleteObject(assistBitmap);
|
||||||
|
assistBitmap = nullptr;
|
||||||
|
assistBitmap = CreateCompatibleBitmap(assistDC, width, height);
|
||||||
|
SelectObject(assistDC, assistBitmap);
|
||||||
|
if (!canvasBitmap || !assistBitmap) {
|
||||||
|
if (!resizeReloadBitmap.getContainer()) {
|
||||||
|
game.tasks.pushThis(resizeReloadBitmap);
|
||||||
|
Logger.debug(L"Failed to create bitmap. Pushed task.");
|
||||||
|
}
|
||||||
|
} else Logger.debug(L"Successfully resized bitmap");
|
||||||
|
interactSettings.setUiScale(static_cast<double>(height) / 2160.);
|
||||||
|
game.handleResize();
|
||||||
|
fontManager.resize(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::syncSize(const int width, const int height) noexcept(false) {
|
||||||
|
syncWidth = width;
|
||||||
|
syncHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Renderer renderer = Renderer();
|
||||||
@@ -0,0 +1,267 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-1-14.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "..\def.h"
|
||||||
|
#include "..\utils\math.h"
|
||||||
|
#include "..\utils\exception.h"
|
||||||
|
#include "..\utils\Task.h"
|
||||||
|
#include "..\interact\InteractManager.h"
|
||||||
|
#include "..\game\world\Location.h"
|
||||||
|
|
||||||
|
class Game;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于标记相对位置。
|
||||||
|
*/
|
||||||
|
enum class UILocation : char { LEFT_TOP, LEFT, LEFT_BOTTOM, TOP, CENTER, BOTTOM, RIGHT_TOP, RIGHT, RIGHT_BOTTOM };
|
||||||
|
|
||||||
|
interface IRenderable {
|
||||||
|
virtual ~IRenderable() = default;
|
||||||
|
virtual void render(double tickDelta) const noexcept = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ITickable {
|
||||||
|
virtual ~ITickable() = default;
|
||||||
|
virtual void tick() noexcept = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Color {
|
||||||
|
unsigned int inactive = 0xff777777;
|
||||||
|
unsigned int active = 0xff000000;
|
||||||
|
unsigned int hover = 0xff444444;
|
||||||
|
unsigned int clicked = 0xffeeeeee;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline static constexpr Color TextColor = {
|
||||||
|
.inactive = 0xff333333,
|
||||||
|
.active = 0xffeeeeee,
|
||||||
|
.hover = 0xffeeeeee,
|
||||||
|
.clicked = 0xff000000
|
||||||
|
};
|
||||||
|
|
||||||
|
class [[carlbeks::predecl, carlbeks::defineat("Entity.h")]] Entity;
|
||||||
|
|
||||||
|
class Camera {
|
||||||
|
Vector2D position; // 当前位置
|
||||||
|
Vector2D velocity; // 移动速度,如果设置了平滑Camera
|
||||||
|
Vector2D targetPosition; // Camera需要移动到的位置
|
||||||
|
Entity* targeting = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void setTargetEntity(Entity* target) noexcept { targeting = target; }
|
||||||
|
[[nodiscard]] double getCurrentX() const noexcept { return position.getX(); }
|
||||||
|
[[nodiscard]] double getCurrentY() const noexcept { return position.getY(); }
|
||||||
|
[[nodiscard]] double getTargetX() const noexcept { return targetPosition.getX(); }
|
||||||
|
[[nodiscard]] double getTargetY() const noexcept { return targetPosition.getY(); }
|
||||||
|
[[nodiscard]] Vector2D getCurrentPosition() const noexcept { return position; }
|
||||||
|
[[nodiscard]] Vector2D getVelocity() const noexcept { return velocity; }
|
||||||
|
[[nodiscard]] Vector2D getTargetPosition() const noexcept { return targetPosition; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Renderer final : public ITickable {
|
||||||
|
friend class Game;
|
||||||
|
friend class Font;
|
||||||
|
inline static BLENDFUNCTION blendFunction = {
|
||||||
|
.BlendOp = AC_SRC_OVER, // Only
|
||||||
|
.BlendFlags = 0, // Must 0
|
||||||
|
.SourceConstantAlpha = 255, // 预乘
|
||||||
|
.AlphaFormat = 0, // Not AC_SRC_ALPHA
|
||||||
|
};
|
||||||
|
mutable List<HGDIOBJ> failed;
|
||||||
|
mutable Camera camera;
|
||||||
|
HDC MainDC = nullptr; // 8
|
||||||
|
HDC resizeCopyDC = nullptr; // 8
|
||||||
|
HBITMAP resizeCopyBitmap = nullptr; // 8
|
||||||
|
HDC canvasDC = nullptr; // 8
|
||||||
|
HBITMAP canvasBitmap = nullptr; // 8
|
||||||
|
HDC assistDC = nullptr; // 8
|
||||||
|
HBITMAP assistBitmap = nullptr; // 8
|
||||||
|
std::thread::id renderThread = std::this_thread::get_id();
|
||||||
|
int windowWidth = 0, windowHeight = 0; // 4 + 4
|
||||||
|
/**
|
||||||
|
* 指示实时大小。为了防抖,只会在改变窗口大小结束时resize并重写windowWidth和windowHeight
|
||||||
|
*/
|
||||||
|
int syncWidth = 0, syncHeight = 0; // 4 + 4
|
||||||
|
/**
|
||||||
|
* 缓存resizeCopyBitmap的宽高
|
||||||
|
*/
|
||||||
|
int resizeCopyWidth = 0, resizeCopyHeight = 0; // 4 + 4
|
||||||
|
bool isRendering = false; // 1
|
||||||
|
bool isResizing = false; // 1
|
||||||
|
|
||||||
|
public:
|
||||||
|
byte windowSize = 0; // 1, Windows: SIZE_***
|
||||||
|
byte reserved[5]{};
|
||||||
|
Task resizeReloadBitmap{
|
||||||
|
[this](Task& task) {
|
||||||
|
Logger.info(L"Scheduled task: resize reload bitmap " + std::to_wstring(windowWidth) + L" * " + std::to_wstring(windowHeight));
|
||||||
|
if (!canvasBitmap) {
|
||||||
|
canvasBitmap = CreateCompatibleBitmap(MainDC, windowWidth, windowHeight);
|
||||||
|
if (canvasBitmap) SelectObject(canvasDC, canvasBitmap);
|
||||||
|
}
|
||||||
|
if (!assistBitmap) {
|
||||||
|
assistBitmap = CreateCompatibleBitmap(canvasDC, windowWidth, windowHeight);
|
||||||
|
if (assistBitmap) SelectObject(assistDC, assistBitmap);
|
||||||
|
}
|
||||||
|
if (canvasBitmap && assistBitmap) {
|
||||||
|
task.pop();
|
||||||
|
Logger.info(L"Successfully reload bitmap " + ptrtow(reinterpret_cast<QWORD>(canvasBitmap)) + L" " + ptrtow(reinterpret_cast<QWORD>(assistBitmap)));
|
||||||
|
this->resizeEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
void gameStartRender() noexcept;
|
||||||
|
void gameEndRender() noexcept;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Renderer() { Logger.put(L"Renderer created"); }
|
||||||
|
|
||||||
|
~Renderer() override {
|
||||||
|
Logger.put(L"Renderer destroyed");
|
||||||
|
if (assistDC) DeleteDC(assistDC);
|
||||||
|
if (canvasDC) DeleteDC(canvasDC);
|
||||||
|
if (resizeCopyDC) DeleteDC(resizeCopyDC);
|
||||||
|
if (canvasBitmap) DeleteObject(canvasBitmap);
|
||||||
|
if (assistBitmap) DeleteObject(assistBitmap);
|
||||||
|
if (resizeCopyBitmap) DeleteObject(resizeCopyBitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initialize() noexcept;
|
||||||
|
/**
|
||||||
|
* 负责转发所有resize信息
|
||||||
|
*/
|
||||||
|
void resize(int width, int height) noexcept(false);
|
||||||
|
void syncSize(int width, int height) noexcept(false);
|
||||||
|
[[nodiscard]] int getWidth() const noexcept { return windowWidth; }
|
||||||
|
[[nodiscard]] int getHeight() const noexcept { return windowHeight; }
|
||||||
|
[[nodiscard]] int getSyncWidth() const noexcept { return syncWidth; }
|
||||||
|
[[nodiscard]] int getSyncHeight() const noexcept { return syncHeight; }
|
||||||
|
[[nodiscard]] bool checkResizing() const noexcept { return isResizing; }
|
||||||
|
void tick() noexcept override {}
|
||||||
|
/**
|
||||||
|
* @attention 会忽略A透明度值
|
||||||
|
* @param argb ARGB式颜色
|
||||||
|
* @return int BGR式颜色
|
||||||
|
*/
|
||||||
|
[[nodiscard]] static unsigned int changeColorFormat(const unsigned int argb) { return argb << 16 & 0xff0000 | argb & 0xff00 | argb >> 16 & 0xff; }
|
||||||
|
void assertRendering() const noexcept(false) { if (!isRendering) throw InvalidOperationException(L"Operation should be done while rendering"); }
|
||||||
|
void assertRenderThread() const noexcept(false) { if (std::this_thread::get_id() != renderThread) throw InvalidOperationException(L"Operation should be done in render thread"); }
|
||||||
|
Camera& getCamera() const noexcept { return camera; }
|
||||||
|
void resizeStart() noexcept { isResizing = true; }
|
||||||
|
void resizeShow() const noexcept { StretchBlt(MainDC, 0, 0, syncWidth, syncHeight, resizeCopyDC, 0, 0, resizeCopyWidth, resizeCopyHeight, SRCCOPY); }
|
||||||
|
|
||||||
|
void resizeEnd() noexcept {
|
||||||
|
isResizing = false;
|
||||||
|
deleteObject(resizeCopyBitmap);
|
||||||
|
resizeCopyBitmap = nullptr;
|
||||||
|
resizeCopyWidth = 0;
|
||||||
|
resizeCopyHeight = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteObject(HGDIOBJ obj) const noexcept {
|
||||||
|
List<HGDIOBJ> tempList;
|
||||||
|
tempList.swap(failed);
|
||||||
|
for (List<HGDIOBJ>::const_iterator iter = tempList.cbegin(); iter != tempList.cend(); ++iter)
|
||||||
|
if (!DeleteObject(*iter)) {
|
||||||
|
Logger.error(L"DeleteObject failed again. Deleting: " + std::to_wstring(reinterpret_cast<QWORD>(*iter)));
|
||||||
|
failed.push_back(*iter);
|
||||||
|
}
|
||||||
|
if (obj && !DeleteObject(obj)) {
|
||||||
|
failed.push_back(obj);
|
||||||
|
Logger.error(L"DeleteObject failed. Deleting: " + std::to_wstring(reinterpret_cast<QWORD>(obj)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill(const int x, const int y, const int w, const int h, const unsigned int color) const {
|
||||||
|
assertRendering();
|
||||||
|
//assertRenderThread();
|
||||||
|
if ((color & 0xff000000) == 0) return;
|
||||||
|
if ((color & 0xff000000) == 0xff000000) {
|
||||||
|
const RECT rect{
|
||||||
|
.left = x,
|
||||||
|
.top = y,
|
||||||
|
.right = x + w,
|
||||||
|
.bottom = y + h
|
||||||
|
};
|
||||||
|
const HBRUSH clr = CreateSolidBrush(changeColorFormat(color));
|
||||||
|
FillRect(canvasDC, &rect, clr);
|
||||||
|
deleteObject(clr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const RECT rect{
|
||||||
|
.left = 0,
|
||||||
|
.top = 0,
|
||||||
|
.right = x + w > windowWidth ? windowWidth - x : w,
|
||||||
|
.bottom = y + h > windowHeight ? windowHeight - y : h
|
||||||
|
};
|
||||||
|
blendFunction.SourceConstantAlpha = color >> 24;
|
||||||
|
const HBRUSH clr = CreateSolidBrush(changeColorFormat(color));
|
||||||
|
FillRect(assistDC, &rect, clr);
|
||||||
|
deleteObject(clr);
|
||||||
|
if (!AlphaBlend(canvasDC, x, y, rect.right, rect.bottom, assistDC, 0, 0, rect.right, rect.bottom, blendFunction)) Logger.error(L"AlphaBlend failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill(const RECT* const rect, const unsigned int color) const {
|
||||||
|
assertRendering();
|
||||||
|
//assertRenderThread();
|
||||||
|
if ((color & 0xff000000) == 0) return;
|
||||||
|
if ((color & 0xff000000) == 0xff000000) {
|
||||||
|
const HBRUSH clr = CreateSolidBrush(changeColorFormat(color));
|
||||||
|
FillRect(canvasDC, rect, clr);
|
||||||
|
deleteObject(clr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const RECT r{
|
||||||
|
.left = 0,
|
||||||
|
.top = 0,
|
||||||
|
.right = rect->right > windowWidth ? windowWidth : rect->right - rect->left,
|
||||||
|
.bottom = rect->bottom > windowHeight ? windowHeight : rect->bottom - rect->top
|
||||||
|
};
|
||||||
|
blendFunction.SourceConstantAlpha = color >> 24;
|
||||||
|
const HBRUSH clr = CreateSolidBrush(changeColorFormat(color));
|
||||||
|
FillRect(assistDC, &r, clr);
|
||||||
|
deleteObject(clr);
|
||||||
|
if (!AlphaBlend(canvasDC, rect->left, rect->top, r.right, r.bottom, assistDC, 0, 0, r.right, r.bottom, blendFunction)) Logger.error(L"AlphaBlend failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fillWorld(const Vector2D& from, const Vector2D& to, const unsigned int color) const {
|
||||||
|
RECT rect{};
|
||||||
|
Vector2D vector = (from - camera.getCurrentPosition()) * interactSettings.actual.mapScale;
|
||||||
|
rect.left = static_cast<long>(vector.getX());
|
||||||
|
if (rect.left >= windowWidth) return;
|
||||||
|
rect.top = static_cast<long>(vector.getY());
|
||||||
|
if (rect.top >= windowHeight) return;
|
||||||
|
vector = (to - camera.getCurrentPosition()) * interactSettings.actual.mapScale;
|
||||||
|
rect.right = static_cast<long>(vector.getX());
|
||||||
|
if (rect.right < 0) return;
|
||||||
|
rect.bottom = static_cast<long>(vector.getY());
|
||||||
|
if (rect.bottom < 0) return;
|
||||||
|
fill(&rect, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fillWorld(const Vector2D& from, const double blockWidth, const double blockHeight, const unsigned int color) const {
|
||||||
|
RECT rect{};
|
||||||
|
Vector2D vector = (from - camera.getCurrentPosition()) * interactSettings.actual.mapScale;
|
||||||
|
rect.left = static_cast<long>(vector.getX());
|
||||||
|
if (rect.left >= windowWidth) return;
|
||||||
|
rect.top = static_cast<long>(vector.getY());
|
||||||
|
if (rect.top >= windowHeight) return;
|
||||||
|
vector += Vector2D(blockWidth, blockHeight).multiply(interactSettings.actual.mapScale);
|
||||||
|
rect.right = static_cast<long>(vector.getX());
|
||||||
|
if (rect.right < 0) return;
|
||||||
|
rect.bottom = static_cast<long>(vector.getY());
|
||||||
|
if (rect.bottom < 0) return;
|
||||||
|
fill(&rect, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fillWorldBlock(const BlockLocation& from, const unsigned int color) const { fillWorld(from.getPosition(), 1, 1, color); }
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Renderer renderer;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-1-21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "..\def.h"
|
||||||
|
|
||||||
|
class ITexture {};
|
||||||
|
|
||||||
|
class TextureManager {
|
||||||
|
Map<String, ObjectHolder<ITexture>> textures;
|
||||||
|
using IterTexture = Map<String, ObjectHolder<ITexture>>::const_iterator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TextureManager() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline static TextureManager textureManager = TextureManager();
|
||||||
@@ -4,5 +4,5 @@
|
|||||||
|
|
||||||
#include "Hud.h"
|
#include "Hud.h"
|
||||||
|
|
||||||
void Hud::render() const noexcept {}
|
void Hud::render(double tickDelta) const noexcept {}
|
||||||
void Hud::tick() noexcept {}
|
void Hud::tick() noexcept {}
|
||||||
@@ -3,11 +3,13 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Renderer.h"
|
|
||||||
|
#include "..\render\Renderer.h"
|
||||||
|
|
||||||
class Hud final : public IRenderable, public ITickable {
|
class Hud final : public IRenderable, public ITickable {
|
||||||
public:
|
public:
|
||||||
void render() const noexcept override;
|
void render(double tickDelta) const noexcept override;
|
||||||
void tick() noexcept override;
|
void tick() noexcept override;
|
||||||
|
void onResize() noexcept {}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,338 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-1-14.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Window.h"
|
||||||
|
|
||||||
|
#include "..\game\Animation.h"
|
||||||
|
#include "..\game\Game.h"
|
||||||
|
#include "..\interact\InteractManager.h"
|
||||||
|
|
||||||
|
int Window::pop() noexcept {
|
||||||
|
gc.submit(this);
|
||||||
|
Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::render(const double tickDelta) const noexcept { for (const Widget* widget : widgets) widget->render(tickDelta); }
|
||||||
|
void Window::tick() noexcept { for (Widget* widget : widgets) widget->tick(); }
|
||||||
|
void Window::onResize() { for (Widget* widget : widgets) widget->onResize(); }
|
||||||
|
|
||||||
|
int Window::passEvent(const MouseActionCode action, const MouseButtonCode value, const int x, const int y) noexcept {
|
||||||
|
int ret = 0;
|
||||||
|
for (Widget* widget : widgets) ret |= widget->passEvent(action, value, x, y);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WindowManager::pop(Window* value) noexcept {
|
||||||
|
if (value->list != static_cast<AnywhereEditableList*>(this)) {
|
||||||
|
Logger.error(L"AnywhereEditableList::pop() : value is not in this list");
|
||||||
|
Failed();
|
||||||
|
}
|
||||||
|
value->list = nullptr;
|
||||||
|
value->next->prev = value->prev;
|
||||||
|
value->prev->next = value->next;
|
||||||
|
value->onClose();
|
||||||
|
Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowManager::clear() noexcept {
|
||||||
|
for (Window& window : *this) {
|
||||||
|
window.list = nullptr;
|
||||||
|
window.onClose();
|
||||||
|
}
|
||||||
|
head.next = nullptr;
|
||||||
|
tail.prev = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CaptionWindow::CaptionWindow() {
|
||||||
|
Widget* close = widgets.emplace_back(Button(0, 0, interactSettings.actual.captionHeight, interactSettings.actual.captionHeight, UILocation::RIGHT_TOP, L"\\f\1\u2716"_literal));
|
||||||
|
close->mouseClick = [](Widget&, MouseButtonCode) { DestroyWindow(MainWindowHandle); };
|
||||||
|
close->onTick = [](const Widget& self, MouseButtonCode) { if (self.containsMouse()) game.getFloatWindow().push(TranslatableText(L"hbp.caption.close").getRenderableString()); };
|
||||||
|
close->absolute();
|
||||||
|
close->backgroundColor.hover = 0xffee0000;
|
||||||
|
close->backgroundColor.active = 0;
|
||||||
|
close->backgroundColor.inactive = 0xffaaaaaa;
|
||||||
|
close->backgroundColor.clicked = 0xffee8888;
|
||||||
|
close->foregroundColor.hover = 0xff000000;
|
||||||
|
close->foregroundColor.active = 0xff000000;
|
||||||
|
close->foregroundColor.inactive = 0xff000000;
|
||||||
|
close->foregroundColor.clicked = 0xff000000;
|
||||||
|
|
||||||
|
Widget* maxRestore = widgets.emplace_back(Button(-interactSettings.actual.captionHeight, 0, interactSettings.actual.captionHeight, interactSettings.actual.captionHeight, UILocation::RIGHT_TOP, IsZoomed(MainWindowHandle) ? L"\\f\1🗗"_literal : L"\\f\1🗖"_literal));
|
||||||
|
maxRestore->mouseClick = [](Widget&, MouseButtonCode) {};
|
||||||
|
maxRestore->mouseClick = [](Widget& self, MouseButtonCode) {
|
||||||
|
if ((self.unused[1] = static_cast<char>(IsZoomed(MainWindowHandle)))) ShowWindow(MainWindowHandle, SW_RESTORE);
|
||||||
|
else ShowWindow(MainWindowHandle, SW_MAXIMIZE);
|
||||||
|
};
|
||||||
|
maxRestore->onTick = [](const Widget& self, MouseButtonCode) { if (self.containsMouse()) game.getFloatWindow().push(self.unused[1] ? TranslatableText(L"hbp.caption.maximize").getRenderableString() : TranslatableText(L"hbp.caption.restore").getRenderableString()); };
|
||||||
|
maxRestore->absolute();
|
||||||
|
maxRestore->unused[1] = static_cast<char>(IsZoomed(MainWindowHandle));
|
||||||
|
maxRestore->backgroundColor.hover = 0xffcccccc;
|
||||||
|
maxRestore->backgroundColor.active = 0;
|
||||||
|
maxRestore->backgroundColor.inactive = 0xff555555;
|
||||||
|
maxRestore->backgroundColor.clicked = 0xffaaaaaa;
|
||||||
|
maxRestore->foregroundColor.hover = 0xff000000;
|
||||||
|
maxRestore->foregroundColor.active = 0xff000000;
|
||||||
|
maxRestore->foregroundColor.inactive = 0xff000000;
|
||||||
|
maxRestore->foregroundColor.clicked = 0xff000000;
|
||||||
|
|
||||||
|
Widget* hide = widgets.emplace_back(Button(-2 * interactSettings.actual.captionHeight, 0, interactSettings.actual.captionHeight, interactSettings.actual.captionHeight, UILocation::RIGHT_TOP, L"\\f\1🗕"_literal));
|
||||||
|
hide->onTick = [](const Widget& self, MouseButtonCode) { if (self.containsMouse()) game.getFloatWindow().push(TranslatableText(L"hbp.caption.minimize").getRenderableString()); };
|
||||||
|
hide->mouseClick = [](Widget&, MouseButtonCode) { ShowWindow(MainWindowHandle, SW_MINIMIZE); };
|
||||||
|
hide->absolute();
|
||||||
|
hide->backgroundColor.hover = 0xffcccccc;
|
||||||
|
hide->backgroundColor.active = 0;
|
||||||
|
hide->backgroundColor.inactive = 0xff555555;
|
||||||
|
hide->backgroundColor.clicked = 0xffaaaaaa;
|
||||||
|
hide->foregroundColor.hover = 0xff000000;
|
||||||
|
hide->foregroundColor.active = 0xff000000;
|
||||||
|
hide->foregroundColor.inactive = 0xff000000;
|
||||||
|
hide->foregroundColor.clicked = 0xff000000;
|
||||||
|
|
||||||
|
Widget* options = widgets.emplace_back(Button(0, 0, interactSettings.actual.captionHeight, interactSettings.actual.captionHeight, UILocation::LEFT_TOP, L"\\f\1⛭"_literal));
|
||||||
|
options->onTick = [](const Widget& self, MouseButtonCode) {
|
||||||
|
if (self.containsMouse()) {
|
||||||
|
game.getFloatWindow().push(TranslatableText(L"hbp.float.settings").getRenderableString());
|
||||||
|
game.getFloatWindow().push(TranslatableText(L"hbp.float.freshCanvas").getRenderableString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
options->mouseClick = [](Widget&, const MouseButtonCode code) { if (static_cast<int>(MouseButtonCodeEnum::MBC_R_DOWN) & code) { game.tasks.pushThis(renderer.resizeReloadBitmap); } };
|
||||||
|
options->absolute();
|
||||||
|
options->backgroundColor.hover = 0xffcccccc;
|
||||||
|
options->backgroundColor.active = 0;
|
||||||
|
options->backgroundColor.inactive = 0xff555555;
|
||||||
|
options->backgroundColor.clicked = 0xffaaaaaa;
|
||||||
|
options->foregroundColor.hover = 0xff000000;
|
||||||
|
options->foregroundColor.active = 0xff000000;
|
||||||
|
options->foregroundColor.inactive = 0xff000000;
|
||||||
|
options->foregroundColor.clicked = 0xff000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CaptionWindow::onOpen() { throw InvalidOperationException(L"Should not open CaptionWindow"); }
|
||||||
|
void CaptionWindow::onClose() { throw InvalidOperationException(L"Should not close CaptionWindow"); }
|
||||||
|
|
||||||
|
void CaptionWindow::render(const double tickDelta) const noexcept {
|
||||||
|
renderer.fill(0, 0, renderer.getWidth(), interactSettings.actual.captionHeight, 0xff666666);
|
||||||
|
for (const Widget* widget : widgets) widget->render(tickDelta);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CaptionWindow::onResize() {
|
||||||
|
int left = 0, right = 0;
|
||||||
|
for (Widget* widget : widgets) {
|
||||||
|
widget->w = interactSettings.actual.captionHeight;
|
||||||
|
widget->h = interactSettings.actual.captionHeight;
|
||||||
|
switch (widget->location) {
|
||||||
|
case UILocation::LEFT_TOP:
|
||||||
|
widget->x = left;
|
||||||
|
left += interactSettings.actual.captionHeight;
|
||||||
|
break;
|
||||||
|
case UILocation::RIGHT_TOP:
|
||||||
|
widget->x = right;
|
||||||
|
right -= interactSettings.actual.captionHeight;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Window::onResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatWindow::render(double tickDelta) const noexcept {
|
||||||
|
if (not interactManager.isInWindow()) {
|
||||||
|
strings.async();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (strings.get().empty()) {
|
||||||
|
strings.async();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
x = interactManager.getMouseX();
|
||||||
|
y = interactManager.getMouseY();
|
||||||
|
int height = 0, width = 0;
|
||||||
|
for (const RenderableString* str : strings.get()) {
|
||||||
|
height += str->getHeight();
|
||||||
|
if (str->getWidth() > width) width = str->getWidth();
|
||||||
|
}
|
||||||
|
const int fwm2 = interactSettings.actual.floatWindowMargin * 2;
|
||||||
|
x += fwm2; // 做一个偏移。不让小窗左下角直接对准鼠标
|
||||||
|
width += fwm2;
|
||||||
|
height += fwm2;
|
||||||
|
if (x + width > renderer.getWidth()) x = renderer.getWidth() - width;
|
||||||
|
y = y < height + interactSettings.actual.captionHeight ? interactSettings.actual.captionHeight : y - height;
|
||||||
|
renderer.fill(x, y, width, height, interactSettings.constants.floatWindowBackground);
|
||||||
|
|
||||||
|
const int xf = x + interactSettings.actual.floatWindowMargin;
|
||||||
|
int yf = y + interactSettings.actual.floatWindowMargin;
|
||||||
|
|
||||||
|
for (const RenderableString* str : strings.get()) {
|
||||||
|
fontManager.getDefault().draw(*str, xf, yf);
|
||||||
|
yf += str->getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
strings.async();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned int Widget::colorSelector(const Color& clr) const {
|
||||||
|
if (!isActive) return clr.inactive;
|
||||||
|
if (!hasMouse) return clr.active;
|
||||||
|
if (hasMouseTrigger && (interactManager.getKey(VK_LBUTTON).isPressed() || interactManager.getKey(VK_RBUTTON).isPressed() || interactManager.getKey(VK_MBUTTON).isPressed())) return clr.clicked;
|
||||||
|
return clr.hover;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::render(double tickDelta) const noexcept { renderer.fill(left, top, width, height, colorSelector(backgroundColor)); }
|
||||||
|
|
||||||
|
void Widget::onResize() {
|
||||||
|
if (isAbsoluteLocation) {
|
||||||
|
width = static_cast<int>(w);
|
||||||
|
height = static_cast<int>(h);
|
||||||
|
switch (location) {
|
||||||
|
case UILocation::LEFT_TOP:
|
||||||
|
left = static_cast<int>(x);
|
||||||
|
top = static_cast<int>(y);
|
||||||
|
break;
|
||||||
|
case UILocation::LEFT:
|
||||||
|
left = static_cast<int>(x);
|
||||||
|
top = static_cast<int>(y) + (renderer.getHeight() - height >> 1);
|
||||||
|
break;
|
||||||
|
case UILocation::LEFT_BOTTOM:
|
||||||
|
left = static_cast<int>(x);
|
||||||
|
top = static_cast<int>(y) + renderer.getHeight() - height;
|
||||||
|
break;
|
||||||
|
case UILocation::TOP:
|
||||||
|
left = static_cast<int>(x) + (renderer.getWidth() - width >> 1);
|
||||||
|
top = static_cast<int>(y);
|
||||||
|
break;
|
||||||
|
case UILocation::CENTER:
|
||||||
|
left = static_cast<int>(x) + (renderer.getWidth() - width >> 1);
|
||||||
|
top = static_cast<int>(y) + (renderer.getHeight() - height >> 1);
|
||||||
|
break;
|
||||||
|
case UILocation::BOTTOM:
|
||||||
|
left = static_cast<int>(x) + (renderer.getWidth() - width >> 1);
|
||||||
|
top = static_cast<int>(y) + renderer.getHeight() - height;
|
||||||
|
break;
|
||||||
|
case UILocation::RIGHT_TOP:
|
||||||
|
left = static_cast<int>(x) + renderer.getWidth() - width;
|
||||||
|
top = static_cast<int>(y);
|
||||||
|
break;
|
||||||
|
case UILocation::RIGHT:
|
||||||
|
left = static_cast<int>(x) + renderer.getWidth() - width;
|
||||||
|
top = static_cast<int>(y) + (renderer.getHeight() - height >> 1);
|
||||||
|
break;
|
||||||
|
case UILocation::RIGHT_BOTTOM:
|
||||||
|
left = static_cast<int>(x) + renderer.getWidth() - width;
|
||||||
|
top = static_cast<int>(y) + renderer.getHeight() - height;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
width = static_cast<int>(renderer.getWidth() * w);
|
||||||
|
height = static_cast<int>(renderer.getHeight() * h);
|
||||||
|
switch (location) {
|
||||||
|
case UILocation::LEFT_TOP:
|
||||||
|
left = static_cast<int>(renderer.getWidth() * x);
|
||||||
|
top = static_cast<int>(renderer.getHeight() * y);
|
||||||
|
break;
|
||||||
|
case UILocation::LEFT:
|
||||||
|
left = static_cast<int>(renderer.getWidth() * x);
|
||||||
|
top = static_cast<int>(renderer.getHeight() * y) + (renderer.getHeight() - height >> 1);
|
||||||
|
break;
|
||||||
|
case UILocation::LEFT_BOTTOM:
|
||||||
|
left = static_cast<int>(renderer.getWidth() * x);
|
||||||
|
top = static_cast<int>(renderer.getHeight() * y) + renderer.getHeight() - height;
|
||||||
|
break;
|
||||||
|
case UILocation::TOP:
|
||||||
|
left = static_cast<int>(renderer.getWidth() * x) + (renderer.getWidth() - width >> 1);
|
||||||
|
top = static_cast<int>(renderer.getHeight() * y);
|
||||||
|
break;
|
||||||
|
case UILocation::CENTER:
|
||||||
|
left = static_cast<int>(renderer.getWidth() * x) + (renderer.getWidth() - width >> 1);
|
||||||
|
top = static_cast<int>(renderer.getHeight() * y) + (renderer.getHeight() - height >> 1);
|
||||||
|
break;
|
||||||
|
case UILocation::BOTTOM:
|
||||||
|
left = static_cast<int>(renderer.getWidth() * x) + (renderer.getWidth() - width >> 1);
|
||||||
|
top = static_cast<int>(renderer.getHeight() * y) + renderer.getHeight() - height;
|
||||||
|
break;
|
||||||
|
case UILocation::RIGHT_TOP:
|
||||||
|
left = static_cast<int>(renderer.getWidth() * x) + renderer.getWidth() - width;
|
||||||
|
top = static_cast<int>(renderer.getHeight() * y);
|
||||||
|
break;
|
||||||
|
case UILocation::RIGHT:
|
||||||
|
left = static_cast<int>(renderer.getWidth() * x) + renderer.getWidth() - width;
|
||||||
|
top = static_cast<int>(renderer.getHeight() * y) + (renderer.getHeight() - height >> 1);
|
||||||
|
break;
|
||||||
|
case UILocation::RIGHT_BOTTOM:
|
||||||
|
left = static_cast<int>(renderer.getWidth() * x) + renderer.getWidth() - width;
|
||||||
|
top = static_cast<int>(renderer.getHeight() * y) + renderer.getHeight() - height;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::render(const double tickDelta) const noexcept {
|
||||||
|
Widget::render(tickDelta);
|
||||||
|
if (name) fontManager.getDefault().drawCenter(name->getRenderableString(), left, top, width, height, colorSelector(foregroundColor));
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfirmWindow& ConfirmWindow::requireConfirm(const Function<void(Button&)>& func) {
|
||||||
|
confirm = dynamic_cast<Button*>(widgets.emplace_back(std::move(Button(0, 0, 0.4, 0.08, UILocation::CENTER, TranslatableText(L"hbp.confirm.confirm")))).ptr());
|
||||||
|
confirm->location = UILocation::CENTER;
|
||||||
|
confirm->backgroundColor.active = 0x99000000;
|
||||||
|
confirm->backgroundColor.hover = 0x9900ff00;
|
||||||
|
confirm->backgroundColor.clicked = 0xff00ee00;
|
||||||
|
confirm->foregroundColor.active = 0xff00ee00;
|
||||||
|
confirm->foregroundColor.hover = 0xff000000;
|
||||||
|
confirm->foregroundColor.clicked = 0xff000000;
|
||||||
|
confirm->y = 0.21;
|
||||||
|
confirm->h = 0.08;
|
||||||
|
if (cancel) {
|
||||||
|
confirm->w = 0.25;
|
||||||
|
confirm->x = -0.125;
|
||||||
|
cancel->x = 0.125;
|
||||||
|
cancel->w = 0.25;
|
||||||
|
cancel->onResize();
|
||||||
|
} else {
|
||||||
|
confirm->w = 0.5;
|
||||||
|
confirm->x = 0;
|
||||||
|
}
|
||||||
|
confirm->onTick = [](Widget& confirm, MouseButtonCode) { if (confirm.containsMouse()) confirm.backgroundColor.hover = dynamic_cast<Button&>(confirm).animation.adaptsColor(0x99008800, 0x9900ff00); };
|
||||||
|
confirm->mouseLeave = [](Widget& confirm, MouseButtonCode) { dynamic_cast<Button&>(confirm).animation.reset(); };
|
||||||
|
if (func) func(*confirm);
|
||||||
|
confirm->onResize();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfirmWindow& ConfirmWindow::requireCancel(const Function<void(Button&)>& func) {
|
||||||
|
cancel = dynamic_cast<Button*>(widgets.emplace_back(std::move(Button(0, 0.1, 0.4, 0.08, UILocation::CENTER, TranslatableText(L"hbp.confirm.cancel")))).ptr());
|
||||||
|
cancel->mouseClick = [this](Widget&, MouseButtonCode) {
|
||||||
|
game.tasks.pushNewed(allocatedFor(new Task([this](Task& self) {
|
||||||
|
if (game.closeWindow(this)) this->onClose();
|
||||||
|
self.pop();
|
||||||
|
})));
|
||||||
|
};
|
||||||
|
cancel->location = UILocation::CENTER;
|
||||||
|
cancel->backgroundColor.active = 0x99000000;
|
||||||
|
cancel->backgroundColor.hover = 0x99ff0000;
|
||||||
|
cancel->backgroundColor.clicked = 0xffee0000;
|
||||||
|
cancel->foregroundColor.active = 0xffee0000;
|
||||||
|
cancel->foregroundColor.hover = 0xff000000;
|
||||||
|
cancel->foregroundColor.clicked = 0xff000000;
|
||||||
|
cancel->y = 0.21;
|
||||||
|
cancel->h = 0.08;
|
||||||
|
if (confirm) {
|
||||||
|
cancel->x = 0;
|
||||||
|
cancel->w = 0.25;
|
||||||
|
confirm->x = -0.125;
|
||||||
|
confirm->w = 0.25;
|
||||||
|
confirm->onResize();
|
||||||
|
} else {
|
||||||
|
cancel->x = 0.125;
|
||||||
|
cancel->w = 0.5;
|
||||||
|
}
|
||||||
|
cancel->onTick = [](Widget& cancel, MouseButtonCode) { if (cancel.containsMouse()) cancel.backgroundColor.hover = dynamic_cast<Button&>(cancel).animation.adaptsColor(0x99880000, 0x99ff0000); };
|
||||||
|
cancel->mouseLeave = [](Widget& cancel, int) { dynamic_cast<Button&>(cancel).animation.reset(); };
|
||||||
|
if (func) func(*cancel);
|
||||||
|
cancel->onResize();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfirmWindow::onClose() { pop(); }
|
||||||
+263
@@ -0,0 +1,263 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-1-14.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "..\def.h"
|
||||||
|
#include "..\utils\gc.h"
|
||||||
|
#include "..\render\Renderer.h"
|
||||||
|
#include "..\game\Animation.h"
|
||||||
|
#include "..\utils\IText.h"
|
||||||
|
|
||||||
|
class WindowManager;
|
||||||
|
|
||||||
|
enum class MouseActionCode : unsigned int {
|
||||||
|
MAC_MOVE = 0,
|
||||||
|
MAC_HOVER = 1,
|
||||||
|
MAC_DOWN = 2,
|
||||||
|
MAC_UP = 3,
|
||||||
|
MAC_DOUBLE = 4,
|
||||||
|
MAC_LEAVE = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MouseButtonCodeEnum : unsigned int {
|
||||||
|
MBC_L_DOWN = 0x1,
|
||||||
|
MBC_R_DOWN = 0x2,
|
||||||
|
MBC_M_DOWN = 0x4,
|
||||||
|
MBC_L_CHANGE = 0x10,
|
||||||
|
MBC_R_CHANGE = 0x20,
|
||||||
|
MBC_M_CHANGE = 0x40,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see MouseButtonCodeEnum
|
||||||
|
*/
|
||||||
|
using MouseButtonCode = unsigned int;
|
||||||
|
|
||||||
|
class Widget : public IRenderable, public ITickable {
|
||||||
|
protected:
|
||||||
|
int left = 0, top = 0, width = 0, height = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Action = Function<void(Widget&, MouseButtonCode)>;
|
||||||
|
double x, y, w, h;
|
||||||
|
Action mouseHover; // 传入int为:0,鼠标移动;其余,鼠标在其上的长时间悬浮
|
||||||
|
Action mouseDown; // 传入int表示变更按键。0左, 1中, 2右
|
||||||
|
Action mouseUp; // 传入int表示变更按键。0左, 1中, 2右
|
||||||
|
Action mouseLeave; // 传入int忽略
|
||||||
|
Action mouseClick; // 传入int表示变更按键。0x0左, 0x1中, 0x2右;0x8表示是否双击
|
||||||
|
Action onTick; // 传入int忽略
|
||||||
|
Color backgroundColor;
|
||||||
|
Color foregroundColor{ TextColor };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
mutable bool hasMouse = false;
|
||||||
|
mutable bool isActive = true;
|
||||||
|
mutable bool hasMouseTrigger = false;
|
||||||
|
bool isAbsoluteLocation = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UILocation location;
|
||||||
|
UILocation textLocation = UILocation::CENTER; // 多余字节预声明备用
|
||||||
|
char unused[2]{}; // [0]: [1]: maxRestore_flag_IsZoomed
|
||||||
|
|
||||||
|
Widget(const double x, const double y, const double w, const double h, const UILocation location) : x(x), y(y), w(w), h(h), location(location) {}
|
||||||
|
|
||||||
|
unsigned int colorSelector(const Color& clr) const;
|
||||||
|
void render(double tickDelta) const noexcept override;
|
||||||
|
|
||||||
|
Widget& alignLocation(const UILocation loc) noexcept {
|
||||||
|
location = loc;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget& alignTextLocation(const UILocation loc) noexcept {
|
||||||
|
textLocation = loc;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget& absolute(const bool value = true) {
|
||||||
|
if (value == isAbsoluteLocation) return *this;
|
||||||
|
isAbsoluteLocation = value;
|
||||||
|
if (renderer.getWidth() != 0 && renderer.getHeight() != 0) onResize();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool containsMouse() const noexcept { return hasMouse; }
|
||||||
|
|
||||||
|
virtual bool isMouseIn(int x, int y) noexcept {
|
||||||
|
x -= left;
|
||||||
|
y -= top;
|
||||||
|
return 0 <= x and x <= width and 0 <= y and y <= height;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void onResize();
|
||||||
|
virtual void onHover(const int value) noexcept { if (mouseHover) mouseHover(*this, value); }
|
||||||
|
|
||||||
|
virtual void onMouseDown(const MouseButtonCode code) noexcept {
|
||||||
|
hasMouseTrigger = true;
|
||||||
|
if (mouseDown) mouseDown(*this, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void onMouseUp(const MouseButtonCode code) noexcept {
|
||||||
|
if (mouseUp) mouseUp(*this, code);
|
||||||
|
if (hasMouseTrigger) onMouseClick(code);
|
||||||
|
hasMouseTrigger = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void onMouseLeave(const MouseButtonCode value) noexcept {
|
||||||
|
hasMouseTrigger = false;
|
||||||
|
if (mouseLeave) mouseLeave(*this, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void onMouseClick(const MouseButtonCode value) noexcept { if (mouseClick) mouseClick(*this, value); }
|
||||||
|
void tick() noexcept override { if (onTick) onTick(*this, 0); }
|
||||||
|
|
||||||
|
virtual int passEvent(const MouseActionCode action, const MouseButtonCode value, const int x, const int y) noexcept {
|
||||||
|
if (action == MouseActionCode::MAC_LEAVE || !isMouseIn(x, y)) {
|
||||||
|
if (hasMouse) onMouseLeave(0);
|
||||||
|
hasMouse = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
hasMouse = true;
|
||||||
|
switch (action) {
|
||||||
|
case MouseActionCode::MAC_HOVER:
|
||||||
|
onHover(1);
|
||||||
|
break;
|
||||||
|
case MouseActionCode::MAC_MOVE:
|
||||||
|
onHover(0);
|
||||||
|
break;
|
||||||
|
case MouseActionCode::MAC_DOWN:
|
||||||
|
onMouseDown(value);
|
||||||
|
break;
|
||||||
|
case MouseActionCode::MAC_UP:
|
||||||
|
onMouseUp(value);
|
||||||
|
break;
|
||||||
|
case MouseActionCode::MAC_DOUBLE:
|
||||||
|
onMouseClick(0x80);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Window : public AnywhereEditable<Window, WindowManager>, public IRenderable, public ITickable {
|
||||||
|
protected:
|
||||||
|
friend class Garbage<Window>;
|
||||||
|
friend class AnywhereEditableList<Window, WindowManager>;
|
||||||
|
List<ObjectHolder<Widget>> widgets;
|
||||||
|
|
||||||
|
Window() = default;
|
||||||
|
|
||||||
|
~Window() override = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
int pop() noexcept override;
|
||||||
|
void render(double tickDelta) const noexcept override;
|
||||||
|
void tick() noexcept override;
|
||||||
|
/**
|
||||||
|
* 在Game.setWindow()时,本窗口开启时调用。
|
||||||
|
* 不应当外部调用。
|
||||||
|
* 如果返回false,则拒绝设置窗口。
|
||||||
|
* @return 是否允许将显示窗口设为自身
|
||||||
|
*/
|
||||||
|
virtual bool onOpen() { return true; }
|
||||||
|
/**
|
||||||
|
* 在Game.setWindow()时,本窗口关闭时调用。
|
||||||
|
* 不应当外部调用。
|
||||||
|
* 注意,关闭未必就是删除。
|
||||||
|
*/
|
||||||
|
virtual void onClose() = 0;
|
||||||
|
virtual void onResize();
|
||||||
|
virtual int passEvent(MouseActionCode action, MouseButtonCode value, int x, int y) noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WindowManager final : public AnywhereEditableList<Window, WindowManager>, public IRenderable, public ITickable {
|
||||||
|
public:
|
||||||
|
void render(const double tickDelta) const noexcept override { for (const Window& i : *this) i.render(tickDelta); }
|
||||||
|
void tick() noexcept override { for (Window& i : *this) i.tick(); }
|
||||||
|
int pop(Window* value) noexcept override;
|
||||||
|
void clear() noexcept;
|
||||||
|
void onResize() noexcept { for (Window& i : *this) i.onResize(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class CaptionWindow final : public Window {
|
||||||
|
public:
|
||||||
|
CaptionWindow();
|
||||||
|
bool onOpen() override;
|
||||||
|
void onClose() override;
|
||||||
|
void render(double tickDelta) const noexcept override;
|
||||||
|
void onResize() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FloatWindow final : public Window {
|
||||||
|
mutable int x = 0; // 标记渲染起始点
|
||||||
|
mutable int y = 0; // 标记渲染起始点
|
||||||
|
typedef List<ObjectHolder<RenderableString>> lt;
|
||||||
|
SynchronizedHolder<lt> strings;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FloatWindow() : Window() {
|
||||||
|
strings.setNew<lt>(std::move(lt()));
|
||||||
|
strings.ok();
|
||||||
|
strings.async();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() { strings.setNew<lt>(std::move(lt())); }
|
||||||
|
|
||||||
|
void push(const ObjectHolder<RenderableString>& string) const { if (strings.ptrNew()) strings.getNew().push_back(string); }
|
||||||
|
void push(ObjectHolder<RenderableString>&& string) const { if (strings.ptrNew()) strings.getNew().push_back(std::move(string)); }
|
||||||
|
void render(double tickDelta) const noexcept override;
|
||||||
|
void tick() noexcept override {}
|
||||||
|
bool onOpen() override { return true; }
|
||||||
|
void onClose() override {}
|
||||||
|
void update() const noexcept { strings.ok(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Button : public Widget {
|
||||||
|
public:
|
||||||
|
ObjectHolder<IText> name;
|
||||||
|
Animation animation = Animation().features(Animation::AS_CUBIC).setDuration(20);
|
||||||
|
Button(const double x, const double y, const double w, const double h, const UILocation location, const ObjectHolder<IText>& text) : Widget(x, y, w, h, location), name(text) {}
|
||||||
|
Button(const double x, const double y, const double w, const double h, const UILocation location, ObjectHolder<IText>&& text) : Widget(x, y, w, h, location), name(std::move(text)) {}
|
||||||
|
void render(double tickDelta) const noexcept override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class ConfirmWindow : public Window {
|
||||||
|
public:
|
||||||
|
ObjectHolder<IText> text;
|
||||||
|
Button
|
||||||
|
*confirm = nullptr,
|
||||||
|
*cancel = nullptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConfirmWindow(const ObjectHolder<IText>& text) : Window(), text(text) {}
|
||||||
|
ConfirmWindow(ObjectHolder<IText>&& text) : Window(), text(std::move(text)) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
~ConfirmWindow() override = default; // 不需要delete,析构时Window会自动delete
|
||||||
|
|
||||||
|
void render(const double tickDelta) const noexcept override {
|
||||||
|
int w, h;
|
||||||
|
w = renderer.getWidth(), h = renderer.getHeight();
|
||||||
|
w >>= 2, h >>= 2;
|
||||||
|
renderer.fill(w, h, w + w, h + h, 0xcc222222);
|
||||||
|
fontManager.getDefault().drawCenter(text->getRenderableString(), w, h, w + w, h + (h >> 1), 0xffeeeeee);
|
||||||
|
for (const ObjectHolder<Widget>& widget : widgets) widget->render(tickDelta);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfirmWindow& requireConfirm(const Function<void(Button&)>& func = {});
|
||||||
|
|
||||||
|
ConfirmWindow& requireCancel(const Function<void(Button&)>& func = {});
|
||||||
|
|
||||||
|
ConfirmWindow&& move() noexcept { return std::move(*this); }
|
||||||
|
|
||||||
|
void onClose() override;
|
||||||
|
|
||||||
|
static ConfirmWindow* of(const ObjectHolder<IText>& text) { return allocatedFor(new ConfirmWindow(text)); }
|
||||||
|
static ConfirmWindow* of(ObjectHolder<IText>&& text) { return allocatedFor(new ConfirmWindow(std::move(text))); }
|
||||||
|
};
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-1-18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Window.h"
|
||||||
|
|
||||||
|
class StartWindow final : public Window {
|
||||||
|
TranslatableText title = TranslatableText(L"hbp.title");
|
||||||
|
|
||||||
|
StartWindow() {
|
||||||
|
Button* start = dynamic_cast<Button*>(widgets.emplace_back(std::move(Button(0, 0.1, 0.4, 0.08, UILocation::CENTER, L"hbp.button.exit"_translates))).ptr());
|
||||||
|
start->onTick = [](const Widget& self, MouseButtonCode) { if (self.containsMouse()) game.getFloatWindow().push(L"hbp.float.exit"_translates.getRenderableString()); };
|
||||||
|
start->mouseClick = [](Widget&, MouseButtonCode) {
|
||||||
|
game.setWindow(&ConfirmWindow::of(L"hbp.confirming.exit"_translates)->requireCancel().requireConfirm([](Button& confirm) {
|
||||||
|
confirm.mouseClick = [](Widget&, MouseButtonCode) { DestroyWindow(MainWindowHandle); };
|
||||||
|
confirm.onTick = [](Widget& self, MouseButtonCode) { if (self.containsMouse()) self.backgroundColor.hover = static_cast<Button&>(self).animation.adaptsColor(0x99008800, 0x9900ff00); };
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
Button* optn = dynamic_cast<Button*>(widgets.emplace_back(std::move(Button(0, 0.2, 0.4, 0.08, UILocation::CENTER, L"hbp.button.settings"_translates))).ptr());
|
||||||
|
optn->onTick = [](const Widget& self, MouseButtonCode) { if (self.containsMouse()) game.getFloatWindow().push(L"hbp.float.settings"_translates.getRenderableString()); };
|
||||||
|
Button* exit = dynamic_cast<Button*>(widgets.emplace_back(std::move(Button(0, 0.3, 0.4, 0.08, UILocation::CENTER, L"hbp.button.start"_translates))).ptr());
|
||||||
|
exit->onTick = [](const Widget& self, MouseButtonCode) { if (self.containsMouse()) game.getFloatWindow().push(L"hbp.float.start"_translates.getRenderableString()); };
|
||||||
|
exit->mouseClick = [](Widget&, MouseButtonCode) {
|
||||||
|
game.setWindow(&ConfirmWindow::of(L"hbp.confirming.start"_translates)->requireCancel().requireConfirm([](Button& confirm) {
|
||||||
|
confirm.mouseClick = [](Widget&, MouseButtonCode) { DestroyWindow(MainWindowHandle); };
|
||||||
|
confirm.onTick = [](Widget& self, MouseButtonCode) { if (self.containsMouse()) self.backgroundColor.hover = static_cast<Button&>(self).animation.adaptsColor(0x99008800, 0x9900ff00); };
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static StartWindow* create() noexcept { return allocatedFor(new StartWindow()); }
|
||||||
|
void onClose() override { pop(); }
|
||||||
|
|
||||||
|
void render(const double tickDelta) const noexcept override {
|
||||||
|
fontManager.getDefault().drawCenter(title.getRenderableString(), 0, 0, renderer.getWidth(), renderer.getHeight());
|
||||||
|
Window::render(tickDelta);
|
||||||
|
}
|
||||||
|
};
|
||||||
+62
-13
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "def.h"
|
#include "..\def.h"
|
||||||
|
|
||||||
[[nodiscard]] inline unsigned int wtouib16(const wchar* string) noexcept {
|
[[nodiscard]] inline unsigned int wtouib16(const wchar* string) noexcept {
|
||||||
unsigned int ret = 0;
|
unsigned int ret = 0;
|
||||||
@@ -35,10 +35,10 @@
|
|||||||
ret += string[i] - L'0';
|
ret += string[i] - L'0';
|
||||||
} else if (string[i] >= L'A' && string[i] <= L'F') {
|
} else if (string[i] >= L'A' && string[i] <= L'F') {
|
||||||
ret <<= 4;
|
ret <<= 4;
|
||||||
ret += string[i] - 0x41;// L'A' - 10
|
ret += string[i] - 55; // L'A' - 10
|
||||||
} else if (string[i] >= L'a' && string[i] <= L'f') {
|
} else if (string[i] >= L'a' && string[i] <= L'f') {
|
||||||
ret <<= 4;
|
ret <<= 4;
|
||||||
ret += string[i] - 0x61;// L'a' - 10
|
ret += string[i] - 87; // L'a' - 10
|
||||||
} else return 0xffffffff;
|
} else return 0xffffffff;
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
@@ -46,6 +46,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
static constexpr wchar Table16[17] = L"0123456789ABCDEF";
|
static constexpr wchar Table16[17] = L"0123456789ABCDEF";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将数字转换为字符串
|
* 将数字转换为字符串
|
||||||
* @param value 要转换的数字
|
* @param value 要转换的数字
|
||||||
@@ -54,20 +55,20 @@ static constexpr wchar Table16[17] = L"0123456789ABCDEF";
|
|||||||
*/
|
*/
|
||||||
[[nodiscard]] inline String uitowb16(unsigned int value, const unsigned int fills = 1) noexcept {
|
[[nodiscard]] inline String uitowb16(unsigned int value, const unsigned int fills = 1) noexcept {
|
||||||
String ret;
|
String ret;
|
||||||
if (value < static_cast<unsigned int>(1) << fills - 1) {
|
if (fills >= 8 || value < static_cast<unsigned int>(1) << fills * 4) {
|
||||||
ret.assign(fills, L'0');
|
ret.assign(fills, L'0');
|
||||||
for (unsigned int i = fills - 1; i != 0 && value; --i) {
|
for (unsigned int i = fills - 1; i && value; --i) {
|
||||||
ret[i] = Table16[value & 0xf];
|
ret[i] = Table16[value & 0xf];
|
||||||
value >>= 4;
|
value >>= 4;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unsigned int i = 0;
|
unsigned int i = 0;
|
||||||
while (i < 8) {
|
while (i < 8) {
|
||||||
if ((value >> i) & 0xf) break;
|
if (value >> i & 0xf) break;
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
while (i < 8) {
|
while (i < 8) {
|
||||||
ret.push_back(Table16[(value >> i) & 0xf]);
|
ret.push_back(Table16[value >> i & 0xf]);
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
if (ret.empty()) ret = L"0";
|
if (ret.empty()) ret = L"0";
|
||||||
@@ -75,15 +76,44 @@ static constexpr wchar Table16[17] = L"0123456789ABCDEF";
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将数字转换为字符串
|
||||||
|
* @param value 要转换的数字
|
||||||
|
* @param fills 填充位数。返回的字符串长度一定不小于该值
|
||||||
|
* @return String类型
|
||||||
|
*/
|
||||||
|
[[nodiscard]] inline String qwtowb16(QWORD value, const unsigned int fills = 1) noexcept {
|
||||||
|
String ret;
|
||||||
|
if (fills >= 16 || value < static_cast<QWORD>(1) << fills * 4) {
|
||||||
|
ret.assign(fills, L'0');
|
||||||
|
for (QWORD i = fills - 1; i && value; --i) {
|
||||||
|
ret[i] = Table16[value & 0xf];
|
||||||
|
value >>= 4;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QWORD i = 0;
|
||||||
|
while (i < 16) {
|
||||||
|
if (value >> i & 0xf) break;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
while (i < 16) {
|
||||||
|
ret.push_back(Table16[value >> i & 0xf]);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
if (ret.empty()) ret = L"0";
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline String qwtowb10(QWORD value, const unsigned int fills = 1) noexcept {
|
||||||
static constexpr wchar Table10[11] = L"0123456789";
|
static constexpr wchar Table10[11] = L"0123456789";
|
||||||
static constexpr QWORD Compare10[20] = {
|
static constexpr QWORD Compare10[20] = {
|
||||||
0, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
|
0ull, 10ull, 100ull, 1000ull, 10000ull, 100000ull, 1000000ull, 10000000ull,
|
||||||
100000000, 1000000000, 10000000000, 100000000000,
|
100000000ull, 1000000000ull, 10000000000ull, 100000000000ull,
|
||||||
1000000000000, 10000000000000, 100000000000000,
|
1000000000000ull, 10000000000000ull, 100000000000000ull,
|
||||||
1000000000000000, 10000000000000000, 100000000000000000,
|
1000000000000000ull, 10000000000000000ull, 100000000000000000ull,
|
||||||
1000000000000000000, 10000000000000000000
|
1000000000000000000ull, 10000000000000000000ull
|
||||||
};
|
};
|
||||||
[[nodiscard]] inline String qwtowb10(QWORD value, const unsigned int fills = 1) noexcept {
|
|
||||||
String ret;
|
String ret;
|
||||||
if (value < Compare10[fills]) {
|
if (value < Compare10[fills]) {
|
||||||
ret.assign(fills, L'0');
|
ret.assign(fills, L'0');
|
||||||
@@ -106,3 +136,22 @@ static constexpr QWORD Compare10[20] = {
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[[nodiscard]] inline String atow(const char* chars) {
|
||||||
|
const int c = MultiByteToWideChar(CP_ACP, 0, chars, -1, nullptr, 0);
|
||||||
|
wchar* e = new wchar[c];
|
||||||
|
MultiByteToWideChar(CP_ACP, 0, chars, -1, e, c);
|
||||||
|
String ret(e);
|
||||||
|
delete[] e;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline std::string wtoa(const wchar* wchars) {
|
||||||
|
const int c = WideCharToMultiByte(CP_ACP, 0, wchars, -1, nullptr, 0, nullptr, nullptr);
|
||||||
|
char* e = new char[c];
|
||||||
|
WideCharToMultiByte(CP_ACP, 0, wchars, -1, e, c, nullptr, nullptr);
|
||||||
|
std::string ret = e;
|
||||||
|
delete[] e;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
#include "..\def.h"
|
||||||
|
|
||||||
|
#include "exception.h"
|
||||||
|
#include "File.h"
|
||||||
|
|
||||||
|
File FileAccessor::getAccess(const String& path) {
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
using Path = fs::path;
|
||||||
|
Path original = path;
|
||||||
|
Path p = original.parent_path();
|
||||||
|
if (!fs::exists(p)) {
|
||||||
|
create_directories(p);
|
||||||
|
Logger.info(L"Created directory " + p.wstring());
|
||||||
|
}
|
||||||
|
return File(p);
|
||||||
|
}
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-3-7.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "..\def.h"
|
||||||
|
|
||||||
|
class File final {
|
||||||
|
public:
|
||||||
|
String path{};
|
||||||
|
std::wfstream file{};
|
||||||
|
int flags = std::ios::in | std::ios::out | std::ios::binary;
|
||||||
|
|
||||||
|
File(const String& path) : path(path) {}
|
||||||
|
File(const File&) = delete;
|
||||||
|
File(File&&) = delete;
|
||||||
|
~File() = default;
|
||||||
|
File& operator=(const File&) = delete;
|
||||||
|
File& operator=(File&&) = delete;
|
||||||
|
|
||||||
|
File& open() {
|
||||||
|
if (file.is_open()) file.close();
|
||||||
|
file.open(path, flags);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
File& close() {
|
||||||
|
if (file.is_open()) file.close();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
File& clearContent() {
|
||||||
|
file.clear();
|
||||||
|
if (file.is_open()) file.close();
|
||||||
|
file.open(path, flags | std::ios::trunc);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
File& inputs(const bool value = true) {
|
||||||
|
if (value) flags |= std::ios::in;
|
||||||
|
else flags &= ~std::ios::in;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
File& outputs(const bool value = true) {
|
||||||
|
if (value) flags |= std::ios::out;
|
||||||
|
else flags &= ~std::ios::out;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
File& binary(const bool value = true) {
|
||||||
|
if (value) flags |= std::ios::binary;
|
||||||
|
else flags &= ~std::ios::binary;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
File& append(const bool value = true) {
|
||||||
|
if (value) flags |= std::ios::app;
|
||||||
|
else flags &= ~std::ios::app;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
File& truncate(const bool value = true) {
|
||||||
|
if (value) flags |= std::ios::trunc;
|
||||||
|
else flags &= ~std::ios::trunc;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
File& operator<<(T&& value) {
|
||||||
|
if (file.is_open()) file << std::forward<T>(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
File& operator<<(decltype(std::endl<wchar, std::char_traits<wchar>>)& value) {
|
||||||
|
if (file.is_open()) file << value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
File& operator>>(T&& value) {
|
||||||
|
if (file.is_open()) file >> std::forward<T>(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Data {
|
||||||
|
public:
|
||||||
|
enum class DataType : unsigned char {
|
||||||
|
Integer, Double, String, Boolean, Null, List, Object
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
String name;
|
||||||
|
String value;
|
||||||
|
DataType type;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Data(String&& name, String&& value, const DataType type): name(std::move(name)), value(std::move(value)), type(type) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DataLoader {
|
||||||
|
File file;
|
||||||
|
|
||||||
|
int parseString(int& line, String& string) {
|
||||||
|
while (!file.file.eof()) {
|
||||||
|
wchar c = file.file.get();
|
||||||
|
if (c == L'\"') Success();
|
||||||
|
if (c == L'\\') { c = file.file.get(); }
|
||||||
|
}
|
||||||
|
errorInfo = L"Error: String never ends. EOF comes before a '\"'";
|
||||||
|
Failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
int loadUntil(int& line, wchar at = 0) {
|
||||||
|
String name = {};
|
||||||
|
String val = {};
|
||||||
|
enum : unsigned char { identifier, eq, value, end } status = identifier;
|
||||||
|
while (!file.file.eof()) {
|
||||||
|
const wchar c = file.file.get();
|
||||||
|
if (c == L'\n') ++line;
|
||||||
|
switch (status) {
|
||||||
|
case identifier:
|
||||||
|
if (!isspace(c)) name.append(1, c);
|
||||||
|
else if (!name.empty()) status = eq;
|
||||||
|
break;
|
||||||
|
case eq:
|
||||||
|
if (c == L'=') status = value;
|
||||||
|
else if (!isspace(c)) {
|
||||||
|
errorInfo = L"Error: expected '=' after identifier at line " + std::to_wstring(line);
|
||||||
|
Failed();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case value:
|
||||||
|
if (!isspace(c)) { if (c == L'\"') {} }
|
||||||
|
break;
|
||||||
|
case end:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
String errorInfo = {};
|
||||||
|
DataLoader(const String& path) : file(path) {}
|
||||||
|
DataLoader(const DataLoader&) = delete;
|
||||||
|
DataLoader(DataLoader&&) = delete;
|
||||||
|
DataLoader& operator=(const DataLoader&) = delete;
|
||||||
|
DataLoader& operator=(DataLoader&&) = delete;
|
||||||
|
~DataLoader() = default;
|
||||||
|
|
||||||
|
int load() {
|
||||||
|
int line = 1;
|
||||||
|
return loadUntil(line);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileAccessor {
|
||||||
|
public:
|
||||||
|
[[nodiscard]] bool exists(const String& path) const { return std::filesystem::exists(path); }
|
||||||
|
|
||||||
|
File getAccess(const String& path);
|
||||||
|
};
|
||||||
|
|
||||||
|
inline FileAccessor fileAccessor = FileAccessor();
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-1-18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "IText.h"
|
||||||
|
|
||||||
|
#include "..\def.h"
|
||||||
|
#include "..\render\Renderer.h"
|
||||||
|
|
||||||
|
int RenderableString::getHeight() const noexcept {
|
||||||
|
int height = 0;
|
||||||
|
for (const StringConfig& config : configs) if (const Font& font = fontManager.get(config.idFont); font.getHeight() > height) height = font.getHeight();
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RenderableString::getWidth(const FontID defaultID) const noexcept {
|
||||||
|
int width = 0;
|
||||||
|
for (const StringConfig& config : configs) width += fontManager.get(config.idFont ? config.idFont : defaultID).getWidth(config);
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RenderableString::getWidth(RenderConfig* renderConfigs, const FontID defaultID) const noexcept {
|
||||||
|
int width = 0;
|
||||||
|
int i = 0;
|
||||||
|
for (const StringConfig& config : configs) {
|
||||||
|
renderConfigs[i].config = &config;
|
||||||
|
const Font& font = fontManager.get(config.idFont ? config.idFont : defaultID);
|
||||||
|
renderConfigs[i].font = &font;
|
||||||
|
width += renderConfigs[i].width = font.getWidth(config);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Font::drawSingle(const RenderableString::StringConfig& config, const int x, const int y, const unsigned int defaultColor) const {
|
||||||
|
SelectObject(renderer.canvasDC, tryCreate(config));
|
||||||
|
RECT rect{
|
||||||
|
.left = x,
|
||||||
|
.top = y + yOffsetPx,
|
||||||
|
.right = 0,
|
||||||
|
.bottom = 0
|
||||||
|
};
|
||||||
|
SetTextColor(renderer.canvasDC, config.isDefaultColor() ? defaultColor : Renderer::changeColorFormat(config.color));
|
||||||
|
DrawTextW(renderer.canvasDC, config.text.c_str(), static_cast<int>(config.text.length()), &rect, DT_SINGLELINE | DT_NOCLIP | DT_CALCRECT);
|
||||||
|
if (!config.isDefaultBackground()) renderer.fill(&rect, config.background);
|
||||||
|
DrawTextW(renderer.canvasDC, config.text.c_str(), static_cast<int>(config.text.length()), &rect, DT_SINGLELINE | DT_NOCLIP);
|
||||||
|
return rect.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Font::drawDirect(const RenderableString::StringConfig& config, const int x, const int y, const unsigned int defaultColor) const {
|
||||||
|
SelectObject(renderer.canvasDC, tryCreate(config));
|
||||||
|
RECT rect{
|
||||||
|
.left = x,
|
||||||
|
.top = y + yOffsetPx,
|
||||||
|
.right = 0,
|
||||||
|
.bottom = 0
|
||||||
|
};
|
||||||
|
SetTextColor(renderer.canvasDC, config.isDefaultColor() ? defaultColor : Renderer::changeColorFormat(config.color));
|
||||||
|
if (!config.isDefaultBackground()) renderer.fill(&rect, config.background);
|
||||||
|
DrawTextW(renderer.canvasDC, config.text.c_str(), static_cast<int>(config.text.length()), &rect, DT_SINGLELINE | DT_NOCLIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Font::clear() const {
|
||||||
|
for (const auto& [_, fnt] : fonts) renderer.deleteObject(fnt);
|
||||||
|
fonts.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Font::draw(const RenderableString& text, int x, const int y, const unsigned int color) const {
|
||||||
|
const COLORREF defaultColor = Renderer::changeColorFormat(color);
|
||||||
|
for (const RenderableString::StringConfig& config : text.configs) {
|
||||||
|
if (config.idFont) {
|
||||||
|
x = fontManager.get(config.idFont).drawSingle(config, x, y, defaultColor);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
SelectObject(renderer.canvasDC, tryCreate(config));
|
||||||
|
RECT rect{
|
||||||
|
.left = x,
|
||||||
|
.top = y + yOffsetPx,
|
||||||
|
.right = 0,
|
||||||
|
.bottom = 0
|
||||||
|
};
|
||||||
|
SetTextColor(renderer.canvasDC, config.isDefaultColor() ? defaultColor : Renderer::changeColorFormat(config.color));
|
||||||
|
DrawTextW(renderer.canvasDC, config.text.c_str(), static_cast<int>(config.text.length()), &rect, DT_SINGLELINE | DT_NOCLIP | DT_CALCRECT);
|
||||||
|
if (!config.isDefaultBackground()) renderer.fill(&rect, config.background);
|
||||||
|
x = rect.right;
|
||||||
|
DrawTextW(renderer.canvasDC, config.text.c_str(), static_cast<int>(config.text.length()), &rect, DT_SINGLELINE | DT_NOCLIP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Font::drawCenter(const RenderableString& text, int x, int y, const int w, const int h, const unsigned int color) const {
|
||||||
|
const COLORREF defaultColor = Renderer::changeColorFormat(color);
|
||||||
|
using RenderConfig = RenderableString::RenderConfig;
|
||||||
|
const QWORD size = text.configs.size();
|
||||||
|
RenderConfig* configs = allocatedFor(new RenderConfig[size], sizeof(RenderConfig) * size);
|
||||||
|
const int stringWidth = text.getWidth(configs, id);
|
||||||
|
x += w - stringWidth >> 1;
|
||||||
|
y += h - text.getHeight() >> 1;
|
||||||
|
for (QWORD i = 0; i < size; ++i) {
|
||||||
|
configs[i].font->drawDirect(*configs[i].config, x, y, defaultColor);
|
||||||
|
x += configs[i].width;
|
||||||
|
}
|
||||||
|
delete[] deallocating(configs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Font::getWidth(const RenderableString::StringConfig& config) const {
|
||||||
|
HFONT const font = tryCreate(config);
|
||||||
|
RECT rect{};
|
||||||
|
SelectObject(renderer.assistDC, font);
|
||||||
|
DrawTextW(renderer.assistDC, config.text.c_str(), static_cast<int>(config.text.length()), &rect, DT_CALCRECT | DT_NOCLIP | DT_SINGLELINE);
|
||||||
|
return rect.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
const String& TranslatableText::getText() const noexcept {
|
||||||
|
refreshText();
|
||||||
|
return target->getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
const RenderableString& TranslatableText::getRenderableString() const noexcept {
|
||||||
|
refreshText();
|
||||||
|
return target->getRenderableString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TranslatableText::refreshText() const noexcept {
|
||||||
|
if (target == nullptr || langConfig == translator.getConfigVersion()) {
|
||||||
|
target = translator.getText(idSrc);
|
||||||
|
langConfig = translator.getConfigVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void languageMakeChinese(Language& lang) {
|
||||||
|
Logger.debug(L"languageMakeChinese called");
|
||||||
|
Map<std::wstring, TranslatedText>& map = lang.translateTable;
|
||||||
|
map.emplace(L"hbp.title", TranslatedText(L"高血压"));
|
||||||
|
map.emplace(L"hbp.confirm.confirm", TranslatedText(L"确认"));
|
||||||
|
map.emplace(L"hbp.confirm.cancel", TranslatedText(L"取消"));
|
||||||
|
map.emplace(L"hbp.caption.close", TranslatedText(L"\\#ffee0000关闭窗口"));
|
||||||
|
map.emplace(L"hbp.caption.maximize", TranslatedText(L"\\#ff4488ee最大化窗口"));
|
||||||
|
map.emplace(L"hbp.caption.restore", TranslatedText(L"\\#ff4488ee复原窗口"));
|
||||||
|
map.emplace(L"hbp.caption.minimize", TranslatedText(L"\\#ffeeaaaa最小化窗口"));
|
||||||
|
map.emplace(L"hbp.float.freshCanvas", TranslatedText(L"\\.ff4488aa\\#ff000000右键以刷新窗口绘制"));
|
||||||
|
map.emplace(L"hbp.button.exit", TranslatedText(L"\\#ff44ee66退出"));
|
||||||
|
map.emplace(L"hbp.float.exit", TranslatedText(L"\\#ff44ee66退出游戏"));
|
||||||
|
map.emplace(L"hbp.confirming.exit", TranslatedText(L"是否\\#ff44ee66退出游戏\\r?"));
|
||||||
|
map.emplace(L"hbp.button.settings", TranslatedText(L"\\#ff4488aa设置"));
|
||||||
|
map.emplace(L"hbp.float.settings", TranslatedText(L"\\#ff4488aa设置"));
|
||||||
|
map.emplace(L"hbp.button.start", TranslatedText(L"\\#ffee0000开始"));
|
||||||
|
map.emplace(L"hbp.float.start", TranslatedText(L"\\#ffee0000开始游戏"));
|
||||||
|
map.emplace(L"hbp.confirming.start", TranslatedText(L"是否\\#ffee0000退出游戏\\r?"));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Translator translator = Translator();
|
||||||
|
inline FontManager fontManager = FontManager();
|
||||||
@@ -0,0 +1,605 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-1-16.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include "..\def.h"
|
||||||
|
#include "Chars.h"
|
||||||
|
#include "exception.h"
|
||||||
|
#include "..\interact\InteractManager.h"
|
||||||
|
|
||||||
|
class LiteralText;
|
||||||
|
class Translator;
|
||||||
|
// class Font;
|
||||||
|
class FontManager;
|
||||||
|
|
||||||
|
using FontStyle = int;
|
||||||
|
using FontID = unsigned short;
|
||||||
|
|
||||||
|
class RenderableString {
|
||||||
|
friend class LiteralText;
|
||||||
|
friend class Font;
|
||||||
|
|
||||||
|
struct StringConfig {
|
||||||
|
String text;
|
||||||
|
unsigned int color = -1;
|
||||||
|
unsigned int background = -1;
|
||||||
|
/**
|
||||||
|
* 位意义
|
||||||
|
* 1 - bold
|
||||||
|
* 2 - italic
|
||||||
|
* 4 - underline
|
||||||
|
* 8 - strikeThrough
|
||||||
|
* 16 - default bg
|
||||||
|
* 32 - default clr
|
||||||
|
*/
|
||||||
|
FontStyle style = 0;
|
||||||
|
FontID idFont = 0;
|
||||||
|
|
||||||
|
StringConfig() = default;
|
||||||
|
|
||||||
|
void reset() noexcept {
|
||||||
|
idFont = 0;
|
||||||
|
color = -1;
|
||||||
|
background = -1;
|
||||||
|
style = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool isBold() const noexcept { return style & 1; }
|
||||||
|
[[nodiscard]] bool isItalic() const noexcept { return style & 2; }
|
||||||
|
[[nodiscard]] bool isUnderline() const noexcept { return style & 4; }
|
||||||
|
[[nodiscard]] bool isStrikeThrough() const noexcept { return style & 8; }
|
||||||
|
[[nodiscard]] bool isDefaultBackground() const noexcept { return (style & 16) == 0; }
|
||||||
|
[[nodiscard]] bool isDefaultColor() const noexcept { return (style & 32) == 0; }
|
||||||
|
void setBold(const bool value) noexcept { style = value ? style | 1 : style & ~1; }
|
||||||
|
void setItalic(const bool value) noexcept { style = value ? style | 2 : style & ~2; }
|
||||||
|
void setUnderline(const bool value) noexcept { style = value ? style | 4 : style & ~4; }
|
||||||
|
void setStrikeThrough(const bool value) noexcept { style = value ? style | 8 : style & ~8; }
|
||||||
|
void useDefaultBackground(const bool value) noexcept { style = value ? style & ~16 : style | 16; }
|
||||||
|
void useDefaultColor(const bool value) noexcept { style = value ? style & ~32 : style | 32; }
|
||||||
|
|
||||||
|
[[nodiscard]] StringConfig copyConfig() const noexcept {
|
||||||
|
StringConfig ret;
|
||||||
|
ret.idFont = idFont;
|
||||||
|
ret.color = color;
|
||||||
|
ret.background = background;
|
||||||
|
ret.style = style;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] StringConfig copy() const noexcept {
|
||||||
|
StringConfig ret;
|
||||||
|
ret.idFont = idFont;
|
||||||
|
ret.text = text;
|
||||||
|
ret.idFont = idFont;
|
||||||
|
ret.color = color;
|
||||||
|
ret.background = background;
|
||||||
|
ret.style = style;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] String toString() const noexcept {
|
||||||
|
String ret;
|
||||||
|
ret.append(L"#");
|
||||||
|
ret.append(isDefaultColor() ? L"~~" : uitowb16(color));
|
||||||
|
ret.append(L".");
|
||||||
|
ret.append(isDefaultBackground() ? L"~~" : uitowb16(background));
|
||||||
|
ret.append(L",F");
|
||||||
|
ret.append(std::to_wstring(idFont));
|
||||||
|
ret.append(L",");
|
||||||
|
if (isBold()) ret.append(L"b");
|
||||||
|
if (isItalic()) ret.append(L"i");
|
||||||
|
if (isUnderline()) ret.append(L"u");
|
||||||
|
if (isStrikeThrough()) ret.append(L"s");
|
||||||
|
ret.append(L":");
|
||||||
|
ret.append(text);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RenderConfig {
|
||||||
|
const Font* font;
|
||||||
|
const StringConfig* config;
|
||||||
|
int width;
|
||||||
|
};
|
||||||
|
|
||||||
|
List<StringConfig> configs;
|
||||||
|
using Iterator = List<StringConfig>::iterator;
|
||||||
|
using ConstIterator = List<StringConfig>::const_iterator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RenderableString(const String& string): RenderableString(string.c_str(), string.length()) {}
|
||||||
|
RenderableString(String&& string) : RenderableString(string.c_str(), string.length()) {}
|
||||||
|
|
||||||
|
RenderableString(const wchar* const string, const QWORD length = static_cast<QWORD>(-1)) {
|
||||||
|
if (length == -1) parseAppend(string);
|
||||||
|
else parseAppend(string, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderableString(const RenderableString&) = default;
|
||||||
|
RenderableString(RenderableString&&) = default;
|
||||||
|
|
||||||
|
~RenderableString() = default;
|
||||||
|
|
||||||
|
[[nodiscard]] String toString() const noexcept {
|
||||||
|
String ret;
|
||||||
|
for (const auto& config : configs) {
|
||||||
|
ret.append(config.toString());
|
||||||
|
ret.append(L";\n");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderableString& append(const RenderableString& other) {
|
||||||
|
auto iterator = other.configs.cbegin();
|
||||||
|
int flags = 0;
|
||||||
|
bool clr = true, bg = true, font = true;
|
||||||
|
const unsigned int color = configs.back().color;
|
||||||
|
const unsigned int background = configs.back().background;
|
||||||
|
const FontID idFont = configs.back().idFont;
|
||||||
|
while (iterator != other.configs.cend()) {
|
||||||
|
if (flags >= 3) break;
|
||||||
|
StringConfig config = iterator->copy();
|
||||||
|
if (font) {
|
||||||
|
if (config.idFont == idFont || config.idFont != 0) {
|
||||||
|
font = false;
|
||||||
|
++flags;
|
||||||
|
}
|
||||||
|
else config.idFont = idFont;
|
||||||
|
}
|
||||||
|
if (clr) {
|
||||||
|
if (config.color == color || config.color != 0xffffffff) {
|
||||||
|
clr = false;
|
||||||
|
++flags;
|
||||||
|
}
|
||||||
|
else config.color = color;
|
||||||
|
}
|
||||||
|
if (bg) {
|
||||||
|
if (config.background == background || config.background != 0xffffffff) {
|
||||||
|
bg = false;
|
||||||
|
++flags;
|
||||||
|
}
|
||||||
|
else config.background = background;
|
||||||
|
}
|
||||||
|
configs.push_back(std::move(config));
|
||||||
|
++iterator;
|
||||||
|
}
|
||||||
|
while (iterator != other.configs.cend()) configs.emplace_back(*iterator);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderableString& append(const String& other) {
|
||||||
|
parseAppend(other.c_str(), other.length());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int getHeight() const noexcept;
|
||||||
|
[[nodiscard]] int getWidth(FontID defaultID = 0) const noexcept;
|
||||||
|
int getWidth(RenderConfig* renderConfigs, FontID defaultID) const noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void parseAppend(const wchar* string) noexcept {
|
||||||
|
StringConfig config;
|
||||||
|
if (!configs.empty()) {
|
||||||
|
config = configs.back();
|
||||||
|
configs.pop_back();
|
||||||
|
}
|
||||||
|
while (*string != L'\0') {
|
||||||
|
const wchar* start = string;
|
||||||
|
if (!config.text.empty()) {
|
||||||
|
configs.push_back(std::move(config));
|
||||||
|
config = config.copyConfig();
|
||||||
|
}
|
||||||
|
while (*string != L'\0' && *string != '\\') ++string;
|
||||||
|
if (*string == L'\0') {
|
||||||
|
if (*start != L'\0') config.text.append(start, string);
|
||||||
|
configs.push_back(std::move(config));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (start != string) config.text.append(start, string);
|
||||||
|
if (*++string == L'\0') {
|
||||||
|
config.text.append(start, string);
|
||||||
|
configs.push_back(std::move(config));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!config.text.empty()) {
|
||||||
|
configs.push_back(std::move(config));
|
||||||
|
config = config.copyConfig();
|
||||||
|
}
|
||||||
|
switch (*string) {
|
||||||
|
case L'\\':
|
||||||
|
config.text.append(1, L'\\');
|
||||||
|
++string;
|
||||||
|
continue;
|
||||||
|
case L'#': {
|
||||||
|
unsigned long long i = 0;
|
||||||
|
while (i < 9) if (string[i++] == L'\0') goto end;
|
||||||
|
config.color = wtouib16(++string, 8);
|
||||||
|
config.useDefaultColor(false);
|
||||||
|
string += 8;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
case L'.': {
|
||||||
|
unsigned long long i = 0;
|
||||||
|
while (i < 9) if (string[i++] == L'\0') goto end;
|
||||||
|
config.background = wtouib16(++string, 8);
|
||||||
|
config.useDefaultBackground(false);
|
||||||
|
string += 8;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
case L'F':
|
||||||
|
case L'f':
|
||||||
|
if (*++string == L'\0') goto end;
|
||||||
|
config.idFont = *string;
|
||||||
|
break;
|
||||||
|
case L'-':
|
||||||
|
case L's':
|
||||||
|
case L'S':
|
||||||
|
config.setStrikeThrough(true);
|
||||||
|
break;
|
||||||
|
case L'_':
|
||||||
|
case L'u':
|
||||||
|
case L'U':
|
||||||
|
config.setUnderline(true);
|
||||||
|
break;
|
||||||
|
case L'/':
|
||||||
|
case L'i':
|
||||||
|
case L'I':
|
||||||
|
config.setItalic(true);
|
||||||
|
break;
|
||||||
|
case L'=':
|
||||||
|
case L'b':
|
||||||
|
case L'B':
|
||||||
|
config.setBold(true);
|
||||||
|
break;
|
||||||
|
case L'r':
|
||||||
|
case L'R':
|
||||||
|
if (!config.text.empty()) configs.push_back(std::move(config));
|
||||||
|
config = StringConfig();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
config.text.append(string - 1, 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++string;
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
configs.push_back(std::move(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseAppend(const wchar* string, const QWORD length) noexcept {
|
||||||
|
StringConfig config = StringConfig();
|
||||||
|
if (!configs.empty()) {
|
||||||
|
config = configs.back();
|
||||||
|
configs.pop_back();
|
||||||
|
}
|
||||||
|
const wchar* const end = string + length;
|
||||||
|
while (string < end) {
|
||||||
|
const wchar* start = string;
|
||||||
|
if (!config.text.empty()) {
|
||||||
|
configs.push_back(std::move(config));
|
||||||
|
config = config.copyConfig();
|
||||||
|
}
|
||||||
|
while (string < end && *string != '\\') ++string;
|
||||||
|
if (string >= end) {
|
||||||
|
if (start != end) config.text.append(start, end);
|
||||||
|
configs.push_back(std::move(config));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (start != string) config.text.append(start, string);
|
||||||
|
if (++string == end) {
|
||||||
|
config.text.append(start, end);
|
||||||
|
configs.push_back(std::move(config));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!config.text.empty()) {
|
||||||
|
configs.push_back(std::move(config));
|
||||||
|
config = config.copyConfig();
|
||||||
|
}
|
||||||
|
switch (*string) {
|
||||||
|
case L'\\':
|
||||||
|
config.text.append(1, L'\\');
|
||||||
|
++string;
|
||||||
|
continue;
|
||||||
|
case L'#': {
|
||||||
|
if (end - string < 9) break;
|
||||||
|
config.color = wtouib16(++string, 8);
|
||||||
|
config.useDefaultColor(false);
|
||||||
|
string += 8;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
case L'.': {
|
||||||
|
if (end - string < 9) break;
|
||||||
|
config.background = wtouib16(++string, 8);
|
||||||
|
config.useDefaultBackground(false);
|
||||||
|
string += 8;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
case L'F':
|
||||||
|
case L'f':
|
||||||
|
if (++string == end) break;
|
||||||
|
config.idFont = *string;
|
||||||
|
break;
|
||||||
|
case L'-':
|
||||||
|
case L's':
|
||||||
|
case L'S':
|
||||||
|
config.setStrikeThrough(true);
|
||||||
|
break;
|
||||||
|
case L'_':
|
||||||
|
case L'u':
|
||||||
|
case L'U':
|
||||||
|
config.setUnderline(true);
|
||||||
|
break;
|
||||||
|
case L'/':
|
||||||
|
case L'i':
|
||||||
|
case L'I':
|
||||||
|
config.setItalic(true);
|
||||||
|
break;
|
||||||
|
case L'=':
|
||||||
|
case L'b':
|
||||||
|
case L'B':
|
||||||
|
config.setBold(true);
|
||||||
|
break;
|
||||||
|
case L'r':
|
||||||
|
case L'R':
|
||||||
|
if (!config.text.empty()) configs.push_back(std::move(config));
|
||||||
|
config = StringConfig();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
config.text.append(string - 1, 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++string;
|
||||||
|
}
|
||||||
|
configs.push_back(std::move(config));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline RenderableString operator""_renderable(const wchar* const text, const QWORD length) noexcept { return RenderableString(String(text, length)); }
|
||||||
|
|
||||||
|
class Font {
|
||||||
|
public:
|
||||||
|
Function<void(int width, int height)> resize;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class FontManager;
|
||||||
|
friend class RenderableString;
|
||||||
|
const String name;
|
||||||
|
mutable Map<FontStyle, HFONT> fonts{};
|
||||||
|
double yOffset;
|
||||||
|
double heightModifier;
|
||||||
|
long height;
|
||||||
|
long escapement;
|
||||||
|
long orientation;
|
||||||
|
long yOffsetPx;
|
||||||
|
const FontID id;
|
||||||
|
bool adaptAllSize = false;
|
||||||
|
|
||||||
|
Font(const FontID id, const String& name, const double heightModifier, const double yOffset, const long escapement, const long orientation, const bool adaptAllSize) : name{name}, yOffset(yOffset), heightModifier(heightModifier), height(static_cast<long>(interactSettings.actual.fontHeight * heightModifier)), escapement(escapement), orientation(orientation), yOffsetPx(static_cast<long>(yOffset * height)), id(id), adaptAllSize(adaptAllSize) {}
|
||||||
|
|
||||||
|
Font(const FontID id, String&& name, const double heightModifier, const double yOffset, const long escapement, const long orientation, const bool adaptAllSize) : name{std::move(name)}, yOffset(yOffset), heightModifier(heightModifier), height(static_cast<long>(interactSettings.actual.fontHeight * heightModifier)), escapement(escapement), orientation(orientation), yOffsetPx(static_cast<long>(yOffset * height)), id(id), adaptAllSize(adaptAllSize) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Font(const Font&) = default;
|
||||||
|
Font(Font&&) = default;
|
||||||
|
~Font() {
|
||||||
|
// 此处Font的回收已经到结束阶段,GDI应该已经收回了资源,不能在手动释放了
|
||||||
|
if (fonts.size()) Logger.warn(L"Font is not successfully cleared when ~Font() called: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
HFONT tryCreate(const RenderableString::StringConfig& config) const {
|
||||||
|
if (const auto iter = fonts.find(config.style); iter != fonts.end()) return iter->second;
|
||||||
|
LOGFONTW f{
|
||||||
|
.lfHeight = height,
|
||||||
|
.lfWidth = 0,
|
||||||
|
.lfEscapement = escapement,
|
||||||
|
.lfOrientation = orientation,
|
||||||
|
.lfWeight = config.isBold() ? FW_BOLD : FW_NORMAL,
|
||||||
|
.lfItalic = config.isItalic(),
|
||||||
|
.lfUnderline = config.isUnderline(),
|
||||||
|
.lfStrikeOut = config.isStrikeThrough(),
|
||||||
|
.lfCharSet = DEFAULT_CHARSET,
|
||||||
|
.lfOutPrecision = OUT_DEFAULT_PRECIS,
|
||||||
|
.lfClipPrecision = CLIP_DEFAULT_PRECIS,
|
||||||
|
.lfQuality = static_cast<unsigned char>(adaptAllSize ? DEFAULT_QUALITY : PROOF_QUALITY),
|
||||||
|
.lfPitchAndFamily = FF_DONTCARE,
|
||||||
|
.lfFaceName{}
|
||||||
|
};
|
||||||
|
memcpy(f.lfFaceName, name.c_str(), 64);
|
||||||
|
HFONT fnt = CreateFontIndirectW(&f);
|
||||||
|
fonts.emplace(std::make_pair(config.style, fnt));
|
||||||
|
return fnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getWidth(const RenderableString::StringConfig& config) const;
|
||||||
|
int drawSingle(const RenderableString::StringConfig& config, int x, int y, unsigned int defaultColor) const;
|
||||||
|
void drawDirect(const RenderableString::StringConfig& config, int x, int y, unsigned int defaultColor) const;
|
||||||
|
|
||||||
|
void clear() const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void draw(const RenderableString& text, int x, int y, unsigned int color = 0xffeeeeee) const;
|
||||||
|
void drawCenter(const RenderableString& text, int x, int y, int w, int h, unsigned int color = 0xffeeeeee) const;
|
||||||
|
|
||||||
|
[[nodiscard]] int getHeight() const noexcept { return height; }
|
||||||
|
[[nodiscard]] int getEscapement() const noexcept { return escapement; }
|
||||||
|
[[nodiscard]] int getOrientation() const noexcept { return orientation; }
|
||||||
|
[[nodiscard]] FontID getID() const noexcept { return id; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class FontManager {
|
||||||
|
Map<FontID, Font> fonts;
|
||||||
|
Font *defaultFont, *captionFont;
|
||||||
|
FontID assigned = 0;
|
||||||
|
using IterFonts = Map<FontID, Font>::const_iterator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FontManager() {
|
||||||
|
captionFont = &newFont(L"Microsoft YaHei UI Light");
|
||||||
|
defaultFont = &newFont(L"Microsoft YaHei UI Light");
|
||||||
|
captionFont->height = interactSettings.actual.captionHeight >> 1;
|
||||||
|
captionFont->resize = [this](int, int) {
|
||||||
|
if (interactSettings.actual.captionHeight != captionFont->height) {
|
||||||
|
captionFont->height = interactSettings.actual.captionHeight >> 1;
|
||||||
|
captionFont->clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
newFont(L"Jetbrains Mono", 1.0, -0.078);
|
||||||
|
newFont(L"STSong", 1.0, -0.12);
|
||||||
|
newFont(L"Arial", 1.0, -0.13);
|
||||||
|
newFont(L"", 1.0, -0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
void finalize() { for (auto& [id, font] : fonts) font.clear(); }
|
||||||
|
|
||||||
|
[[nodiscard]] const Font& get(const FontID id) const noexcept {
|
||||||
|
const IterFonts iter = fonts.find(id);
|
||||||
|
if (iter == fonts.cend()) return *defaultFont;
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Font& newFont(const String& name, const double heightModifier = 1.0, const double yOffset = 0.0, const bool adaptAllSize = true, const long escapement = 0, const long orientation = 0) noexcept {
|
||||||
|
++assigned;
|
||||||
|
return fonts.emplace(assigned, std::move(Font(assigned, name, heightModifier, yOffset, adaptAllSize, escapement, orientation))).first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
Font& newFont(String&& name, const double heightModifier = 1.0, const double yOffset = 0.0, const bool adaptAllSize = true, const long escapement = 0, const long orientation = 0) {
|
||||||
|
++assigned;
|
||||||
|
return fonts.emplace(assigned, std::move(Font(assigned, std::move(name), heightModifier, yOffset, adaptAllSize, escapement, orientation))).first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Font& getDefault() const noexcept { return *defaultFont; }
|
||||||
|
|
||||||
|
void resize(const int width, const int height) {
|
||||||
|
for (auto& [_, font] : fonts)
|
||||||
|
if (font.resize) font.resize(width, height);
|
||||||
|
else {
|
||||||
|
font.height = static_cast<long>(interactSettings.actual.fontHeight * font.heightModifier);
|
||||||
|
font.yOffsetPx = static_cast<long>(font.height * font.yOffset);
|
||||||
|
font.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IText {
|
||||||
|
virtual ~IText() = default;
|
||||||
|
[[nodiscard]] virtual const String& getText() const noexcept = 0;
|
||||||
|
[[nodiscard]] virtual const RenderableString& getRenderableString() const noexcept = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef class LiteralText final : public IText {
|
||||||
|
const String string;
|
||||||
|
mutable RenderableString renderableString;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LiteralText(const String& string): string(string), renderableString(string) {}
|
||||||
|
LiteralText(String&& string): string(std::move(string)), renderableString(this->string) {}
|
||||||
|
|
||||||
|
const String& getText() const noexcept override { return string; }
|
||||||
|
|
||||||
|
const RenderableString& getRenderableString() const noexcept override { return renderableString; }
|
||||||
|
} TranslatedText;
|
||||||
|
|
||||||
|
inline LiteralText operator""_literal(const wchar* const text, const QWORD length) noexcept { return LiteralText(String(text, length)); }
|
||||||
|
|
||||||
|
class TranslatableText final : public IText {
|
||||||
|
const String idSrc;
|
||||||
|
mutable const LiteralText* target = nullptr;
|
||||||
|
mutable QWORD langConfig = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TranslatableText(const String& id) : idSrc(id) {}
|
||||||
|
TranslatableText(String&& id) : idSrc(std::move(id)) {}
|
||||||
|
TranslatableText(const TranslatableText& other) : idSrc(other.idSrc) {}
|
||||||
|
TranslatableText(TranslatableText&& other) noexcept : idSrc(std::move(other.idSrc)) {}
|
||||||
|
const String& getID() const noexcept { return idSrc; }
|
||||||
|
const String& getText() const noexcept override;
|
||||||
|
const RenderableString& getRenderableString() const noexcept override;
|
||||||
|
void refreshText() const noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline TranslatableText operator""_translates(const wchar* const text, const QWORD length) noexcept { return TranslatableText(String(text, length)); }
|
||||||
|
inline TranslatedText operator""__translated(const wchar* const text, const QWORD length) noexcept { return TranslatedText(String(text, length)); }
|
||||||
|
|
||||||
|
using LangID = unsigned int;
|
||||||
|
|
||||||
|
struct Language {
|
||||||
|
Map<String, TranslatedText> translateTable;
|
||||||
|
LangID id = 1;
|
||||||
|
using IterText = Map<String, TranslatedText>::const_iterator;
|
||||||
|
|
||||||
|
Language(const LangID id) : id(id) {}
|
||||||
|
Language(const Language&) = delete;
|
||||||
|
Language(Language&&) = default;
|
||||||
|
Language& operator=(const Language&) = delete;
|
||||||
|
Language& operator=(Language&&) = default;
|
||||||
|
|
||||||
|
String toString() {
|
||||||
|
std::wstringstream stream{};
|
||||||
|
stream.imbue(std::locale("zh-CN.UTF-8"));
|
||||||
|
stream << L"Language ID: " << id << std::endl;
|
||||||
|
for (const auto& [key, value] : translateTable) stream << L" " << key << L": (" << value.getText().length() << L") " << value.getText() << std::endl;
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void languageMakeChinese(Language&);
|
||||||
|
|
||||||
|
class Translator {
|
||||||
|
Map<String, Language> langMap{};
|
||||||
|
List<Language*> langList{};
|
||||||
|
TranslatedText nullText{L"\\#FF""EE0000<translator-null>"};
|
||||||
|
String lang = L"zh-cn";
|
||||||
|
LangID idLangMax = 0;
|
||||||
|
int langConfig = 1;
|
||||||
|
using IterLangName = Map<String, Language>::const_iterator;
|
||||||
|
using IterLang = List<Language>::const_iterator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void initialize() {
|
||||||
|
Language& chinese = addLang(L"zh-cn");
|
||||||
|
languageMakeChinese(chinese);
|
||||||
|
useLanguage(L"zh-cn");
|
||||||
|
}
|
||||||
|
|
||||||
|
Translator() = default;
|
||||||
|
Language& addLang(const String& lang) noexcept { return langMap.insert(std::make_pair(lang, Language(++idLangMax))).first->second; }
|
||||||
|
Language& addLang(String&& lang) noexcept { return langMap.insert(std::make_pair(lang, Language(++idLangMax))).first->second; }
|
||||||
|
Language& getLang(const String& lang) noexcept { return langMap.at(lang); }
|
||||||
|
|
||||||
|
const Language* useLanguage(const String& lang) noexcept {
|
||||||
|
const Map<String, Language>::iterator language = langMap.find(lang);
|
||||||
|
if (language == langMap.cend()) return nullptr;
|
||||||
|
for (const Language* i : langList) if (i->id == language->second.id) return i;
|
||||||
|
langList.push_front(&language->second);
|
||||||
|
return &language->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unuseLanguage(const String& lang) noexcept {
|
||||||
|
const Map<String, Language>::iterator language = langMap.find(lang);
|
||||||
|
if (language == langMap.cend()) return;
|
||||||
|
for (const Language* i : langList)
|
||||||
|
if (i->id == language->second.id) {
|
||||||
|
langMap.erase(lang);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadLang();
|
||||||
|
|
||||||
|
[[nodiscard]] const TranslatedText* getText(const String& id) const noexcept {
|
||||||
|
for (const Language* language : langList) {
|
||||||
|
const Language::IterText iterator = language->translateTable.find(id);
|
||||||
|
if (iterator != language->translateTable.cend()) return &iterator->second;
|
||||||
|
}
|
||||||
|
return &nullText;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int getConfigVersion() const noexcept { return langConfig; }
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Translator translator;
|
||||||
|
extern FontManager fontManager;
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-3-4.
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
class Task;
|
||||||
|
class TaskScheduler;
|
||||||
|
|
||||||
|
class Task final : public AnywhereEditable<Task> {
|
||||||
|
friend class TaskScheduler;
|
||||||
|
#ifndef __CARLBEKS_DEBUG__
|
||||||
|
QWORD triggerInterval = 0, nextExecuteTime = 0, lastExecuteTime = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
Function<void(Task& self)> func;
|
||||||
|
|
||||||
|
Task(const Function<void(Task& self)>& func) : func(func) {}
|
||||||
|
Task(Function<void(Task& self)>&& func) : func(std::move(func)) {}
|
||||||
|
void schedulePop(const bool pop) noexcept { reserved[0] = pop; }
|
||||||
|
[[nodiscard]] bool scheduledPop() const noexcept { return reserved[0]; }
|
||||||
|
|
||||||
|
int pop() noexcept override {
|
||||||
|
schedulePop(true);
|
||||||
|
Success();
|
||||||
|
}
|
||||||
|
#ifndef __CARLBEKS_DEBUG__
|
||||||
|
Task& every(QWORD tick);
|
||||||
|
Task& after(QWORD tick);
|
||||||
|
Task& until(QWORD tick);
|
||||||
|
Task& forever();
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
class TaskScheduler {
|
||||||
|
public:
|
||||||
|
AnywhereEditableList<Task> tasks;
|
||||||
|
|
||||||
|
void runAll() {
|
||||||
|
for (Task& task : tasks) {
|
||||||
|
task.func(task);
|
||||||
|
if (task.scheduledPop()) {
|
||||||
|
task.schedulePop(false);
|
||||||
|
tasks.pop(&task); // pop后删除了内存,迭代器的current失效
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pushCopy(Task& task) { tasks.pushCopy(&task); }
|
||||||
|
/* 必须是new Task,交付托管 */
|
||||||
|
void pushNewed(Task* task) { tasks.pushNewed(task); }
|
||||||
|
void pushThis(Task& task) { tasks.pushThis(&task); }
|
||||||
|
};
|
||||||
@@ -4,5 +4,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
inline void test() {
|
inline void test() {}
|
||||||
}
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-1-22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "..\def.h"
|
||||||
|
#include "exception.h"
|
||||||
|
#include "..\game\Game.h"
|
||||||
|
|
||||||
|
String PublicLogger::build(const String& msg, const String& type) const {
|
||||||
|
return L"T-" + qwtowb10(game.getTick(), 8) + name + type + msg + L"\n";
|
||||||
|
}
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-1-18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "..\warnings.h"
|
||||||
|
#include "..\def.h"
|
||||||
|
#include "File.h"
|
||||||
|
|
||||||
|
class Exception : public std::exception {
|
||||||
|
const String* type;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
String msg;
|
||||||
|
|
||||||
|
Exception(String&& msg, const String* type) : type(type), msg(std::move(msg)) {}
|
||||||
|
Exception(const String& msg, const String* type) : type(type), msg(msg) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
[[nodiscard]] String getMessage() const noexcept { return msg; }
|
||||||
|
[[nodiscard]] const String* getType() const noexcept { return type; }
|
||||||
|
[[nodiscard]] const char* what() const override { return "Use Exception::getMessage() instead."; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class NullPointerException final : public Exception {
|
||||||
|
inline static const String type = L"NullPointerException";
|
||||||
|
|
||||||
|
public:
|
||||||
|
NullPointerException(String&& msg) : Exception(std::move(msg), &type) {}
|
||||||
|
NullPointerException(const String& msg) : Exception(msg, &type) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BadAllocationException final : public Exception {
|
||||||
|
inline static const String type = L"BadAllocationException";
|
||||||
|
|
||||||
|
public:
|
||||||
|
BadAllocationException(String&& msg) : Exception(std::move(msg), &type) {}
|
||||||
|
BadAllocationException(const String& msg) : Exception(msg, &type) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ArrayIndexOutOfBoundException final : public Exception {
|
||||||
|
inline static const String type = L"ArrayIndexOutOfBoundException";
|
||||||
|
|
||||||
|
public:
|
||||||
|
ArrayIndexOutOfBoundException(String&& msg) : Exception(std::move(msg), &type) {}
|
||||||
|
ArrayIndexOutOfBoundException(const String& msg) : Exception(msg, &type) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class InvalidOperationException final : public Exception {
|
||||||
|
inline static const String type = L"InvalidOperationException";
|
||||||
|
|
||||||
|
public:
|
||||||
|
InvalidOperationException(String&& msg) : Exception(std::move(msg), &type) {}
|
||||||
|
InvalidOperationException(const String& msg) : Exception(msg, &type) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class RuntimeException final : public Exception {
|
||||||
|
inline static const String type = L"RuntimeException";
|
||||||
|
|
||||||
|
public:
|
||||||
|
RuntimeException(String&& msg) : Exception(std::move(msg), &type) {}
|
||||||
|
RuntimeException(const String& msg) : Exception(msg, &type) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class PublicLogger final {
|
||||||
|
File mainLogger = File(L"log.txt");
|
||||||
|
[[nodiscard]] String build(const String& msg, const String& type) const;
|
||||||
|
|
||||||
|
template<typename T, typename... Ts> requires requires(std::wstringstream stream, T& t, Ts&&... ts) {
|
||||||
|
stream << std::forward<T>(t);
|
||||||
|
(stream << ... << std::forward<Ts>(ts));
|
||||||
|
}
|
||||||
|
static void prints(std::wstringstream& stream, T&& t, Ts&&... ts) {
|
||||||
|
stream << L" " << std::forward<T>(t);
|
||||||
|
prints(stream, std::forward<Ts>(ts)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> requires requires(std::wstringstream stream, T&& t) { stream << std::forward<T>(t); }
|
||||||
|
static void prints(std::wstringstream& stream, T&& t) { stream << L" " << std::forward<T>(t); }
|
||||||
|
|
||||||
|
template<typename T, typename... Ts> requires requires(std::wstringstream stream, T&& t, Ts&&... ts) {
|
||||||
|
stream << std::forward<T>(t);
|
||||||
|
(stream << ... << std::forward<Ts>(ts));
|
||||||
|
}
|
||||||
|
static void ofs(std::wstringstream& stream, T&& t, Ts&&... ts) {
|
||||||
|
stream << L" " << std::forward<T>(t);
|
||||||
|
ofs(stream, std::forward<Ts>(ts)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> requires requires(std::wstringstream stream, T&& t) { stream << std::forward<T>(t); }
|
||||||
|
static void ofs(std::wstringstream& stream, T&& t) { stream << L" " << std::forward<T>(t); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
const String name;
|
||||||
|
|
||||||
|
PublicLogger(const String& name): name(L" [" + name + L"] ") {
|
||||||
|
std::wcout << L"PublicLogger created\n";
|
||||||
|
std::wcout.imbue(std::locale("zh-CN.UTF-8"));
|
||||||
|
mainLogger.truncate().open();
|
||||||
|
}
|
||||||
|
|
||||||
|
PublicLogger& put(const String& msg) noexcept {
|
||||||
|
String str = L" " + name + L" " + msg + L"\n";
|
||||||
|
std::wcout << str;
|
||||||
|
mainLogger << str;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
PublicLogger& trace(const String& msg) noexcept {
|
||||||
|
String str = build(msg, L"[Trace] ");
|
||||||
|
std::wcout << str;
|
||||||
|
mainLogger << str;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
PublicLogger& debug(const String& msg) noexcept {
|
||||||
|
String str = build(msg, L"[Debug] ");
|
||||||
|
std::wcout << str;
|
||||||
|
mainLogger << str;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
PublicLogger& log(const String& msg) noexcept {
|
||||||
|
String str = build(msg, L"[Log] ");
|
||||||
|
std::wcout << str;
|
||||||
|
mainLogger << str;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
PublicLogger& info(const String& msg) noexcept {
|
||||||
|
String str = build(msg, L"[Info] ");
|
||||||
|
std::wcout << str;
|
||||||
|
mainLogger << str;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
PublicLogger& warn(const String& msg) noexcept {
|
||||||
|
String str = build(msg, L"[Warn] ");
|
||||||
|
std::wcout << str;
|
||||||
|
mainLogger << str;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
PublicLogger& error(const String& msg) noexcept {
|
||||||
|
String str = build(msg, L"[Error] ");
|
||||||
|
std::wcout << str;
|
||||||
|
mainLogger << str;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> requires requires(T t) { std::wcout << t; }
|
||||||
|
PublicLogger& print(T&& msg) noexcept {
|
||||||
|
std::wstringstream stream = {};
|
||||||
|
stream << L" " << name << L" " << msg << std::endl;
|
||||||
|
String str = stream.str();
|
||||||
|
mainLogger << str;
|
||||||
|
std::wcout << str;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Ts> requires requires(T t, Ts... ts) {
|
||||||
|
std::wcout << t;
|
||||||
|
(std::wcout << ... << std::forward<Ts>(ts));
|
||||||
|
}
|
||||||
|
PublicLogger& print(T&& msg, Ts&&... other) noexcept {
|
||||||
|
std::wstringstream stream = {};
|
||||||
|
stream << L" " << name << L" " << msg;
|
||||||
|
prints(stream, other...);
|
||||||
|
stream << std::endl;
|
||||||
|
String str = stream.str();
|
||||||
|
mainLogger << str;
|
||||||
|
std::wcout << str;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Ts> requires requires(std::wstringstream stream, T&& t, Ts&&... ts) {
|
||||||
|
stream << std::forward<T>(t);
|
||||||
|
(stream << ... << std::forward<Ts>(ts));
|
||||||
|
}
|
||||||
|
String of(T&& t, Ts&&... ts) const {
|
||||||
|
std::wstringstream stream = {};
|
||||||
|
stream << std::forward<T>(t);
|
||||||
|
ofs(stream, ts...);
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> requires requires(std::wstringstream stream, T&& t) { std::wstringstream(t); }
|
||||||
|
String of(T&& ts) const {
|
||||||
|
std::wstringstream stream = {};
|
||||||
|
ofs(stream, ts...);
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
[[carlbeks::releasedat("def.cpp")]] inline PublicLogger& Logger = *new PublicLogger(L"Main");
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-3-6.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "gc.h"
|
||||||
+150
@@ -0,0 +1,150 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-3-6.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "..\warnings.h"
|
||||||
|
#include "..\def.h"
|
||||||
|
#include "exception.h"
|
||||||
|
|
||||||
|
struct IGarbage;
|
||||||
|
template <typename T>
|
||||||
|
class Garbage;
|
||||||
|
class GarbageCollector;
|
||||||
|
|
||||||
|
struct IGarbage {
|
||||||
|
private:
|
||||||
|
friend class GarbageCollector;
|
||||||
|
IGarbage* next = nullptr;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void* ptr;
|
||||||
|
IGarbage(void* ptr) : ptr(ptr) {}
|
||||||
|
virtual ~IGarbage() = default;
|
||||||
|
virtual void collect() = 0;
|
||||||
|
virtual void deleteThis() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class Garbage final : public IGarbage {
|
||||||
|
friend class GarbageCollector;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Garbage(T* ptr) : IGarbage(ptr) {}
|
||||||
|
void collect() override { delete static_cast<T*>(deallocating(ptr)); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void deleteThis() override { delete deallocating(this); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class GarbageCollector {
|
||||||
|
IGarbage* submitted = nullptr;
|
||||||
|
IGarbage* submittedEnd = nullptr;
|
||||||
|
IGarbage* packed = nullptr;
|
||||||
|
IGarbage* packedEnd = nullptr;
|
||||||
|
IGarbage* processing = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
GarbageCollector() { Logger.info(L"initialize GarbageCollector"); }
|
||||||
|
GarbageCollector(const GarbageCollector&) = delete;
|
||||||
|
GarbageCollector(GarbageCollector&&) = delete;
|
||||||
|
GarbageCollector& operator=(const GarbageCollector&) = delete;
|
||||||
|
GarbageCollector& operator=(GarbageCollector&&) = delete;
|
||||||
|
|
||||||
|
~GarbageCollector() {
|
||||||
|
IGarbage* iter;
|
||||||
|
while (processing) {
|
||||||
|
iter = processing->next;
|
||||||
|
processing->collect();
|
||||||
|
processing->deleteThis();
|
||||||
|
processing = iter;
|
||||||
|
}
|
||||||
|
while (packed) {
|
||||||
|
iter = packed->next;
|
||||||
|
packed->collect();
|
||||||
|
packed->deleteThis();
|
||||||
|
packed = iter;
|
||||||
|
}
|
||||||
|
while (submitted) {
|
||||||
|
iter = submitted->next;
|
||||||
|
submitted->collect();
|
||||||
|
submitted->deleteThis();
|
||||||
|
submitted = iter;
|
||||||
|
}
|
||||||
|
submittedEnd = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 只能在gameThread调用 */
|
||||||
|
template<TypeName T>
|
||||||
|
void submit(T* ptr) noexcept(false) {
|
||||||
|
IGarbage* newedGarbage = allocatedFor(new Garbage<T>(ptr));
|
||||||
|
builtinSubmit(newedGarbage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void builtinSubmit(IGarbage* const newedGarbage) noexcept(false) {
|
||||||
|
if (IGarbage* end = submittedEnd) { // 后续添加,可能存在线程竞争
|
||||||
|
while (end->next) end = end->next; // 理论上不会进入循环,但防止万一
|
||||||
|
end->next = newedGarbage;
|
||||||
|
if (submitted) submittedEnd = newedGarbage; // 参考pack(),submitted会被先置空
|
||||||
|
std::atomic_thread_fence(std::memory_order_acquire);
|
||||||
|
if (!submitted) submittedEnd = nullptr; // 防止submittedEnd = garbage在pack()中置空后进行。此函数是同步的,所以可以这样操作。
|
||||||
|
}
|
||||||
|
else { // 添加首个,一定不会有线程竞争;或先前的已经pack
|
||||||
|
submitted = newedGarbage;
|
||||||
|
submittedEnd = newedGarbage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 只能在renderThread调用 */
|
||||||
|
void pack() {
|
||||||
|
if (!submitted) return; // 提交链为空,不进行后续操作,防止竞争
|
||||||
|
std::atomic_thread_fence(std::memory_order_acquire);
|
||||||
|
IGarbage* submit = submitted;
|
||||||
|
IGarbage* submitEnd = submittedEnd;
|
||||||
|
submitted = nullptr; // 先把这个置为nullptr
|
||||||
|
std::atomic_thread_fence(std::memory_order_acquire);
|
||||||
|
submittedEnd = nullptr;
|
||||||
|
if (IGarbage* end = packedEnd) {
|
||||||
|
while (end->next) end = end->next; // 以防万一
|
||||||
|
end->next = submit;
|
||||||
|
if (packed) packedEnd = submitEnd;
|
||||||
|
std::atomic_thread_fence(std::memory_order_acquire);
|
||||||
|
if (!packed) packedEnd = nullptr;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
packed = submit;
|
||||||
|
packedEnd = submitEnd;
|
||||||
|
}
|
||||||
|
while (packedEnd->next) packedEnd = packedEnd->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 只能在gameThread调用 */
|
||||||
|
void collect() {
|
||||||
|
IGarbage* next;
|
||||||
|
while (processing) {
|
||||||
|
next = processing->next;
|
||||||
|
processing->next = nullptr;
|
||||||
|
processing->collect();
|
||||||
|
processing->deleteThis();
|
||||||
|
processing = next;
|
||||||
|
}
|
||||||
|
if (!packed) return;
|
||||||
|
// 搬运packed且搬运期间的packed链必须必须完全固定
|
||||||
|
// 保证运行顺序
|
||||||
|
processing = packed;
|
||||||
|
packed = nullptr;
|
||||||
|
std::atomic_thread_fence(std::memory_order_acquire);
|
||||||
|
packedEnd = nullptr;
|
||||||
|
std::atomic_thread_fence(std::memory_order_acquire);
|
||||||
|
while (processing) {
|
||||||
|
next = processing->next;
|
||||||
|
processing->next = nullptr;
|
||||||
|
processing->collect();
|
||||||
|
processing->deleteThis();
|
||||||
|
processing = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline GarbageCollector& [[carlbeks::releasedat("def.cpp")]] gc = *new GarbageCollector();
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-3-21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "..\warnings.h"
|
||||||
|
#include "..\def.h"
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T& nRange(const T& val, const T& min, const T& max) { return val < min ? min : val > max ? max : val; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T& nMin(const T& a, const T& b) { return a < b ? a : b; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T& nMin(const T& val0, const T& val1, const T& vals...) { return nMin(nMin(val0, val1), nMin(vals...)); }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T& nMax(const T& a, const T& b) { return a > b ? a : b; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T& nMax(const T& val0, const T& val1, const T& vals...) { return nMax(nMin(val0, val1), nMax(vals...)); }
|
||||||
|
|
||||||
|
class [[carlbeks::TriviallyCopyable]] Vector3D;
|
||||||
|
class [[carlbeks::TriviallyCopyable]] Vector2D;
|
||||||
|
|
||||||
|
class [[carlbeks::TriviallyCopyable]] Vector3D {
|
||||||
|
double x, y, z;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Vector3D(const double x, const double y, const double z) noexcept : x(x), y(y), z(z) {}
|
||||||
|
Vector3D() noexcept : x(0), y(0), z(0) {}
|
||||||
|
Vector3D(const Vector3D& other) noexcept = default;
|
||||||
|
Vector3D(Vector3D&& other) noexcept = default;
|
||||||
|
Vector3D& operator=(const Vector3D& other) noexcept = default;
|
||||||
|
Vector3D& operator=(Vector3D&& other) noexcept = default;
|
||||||
|
~Vector3D() noexcept = default;
|
||||||
|
|
||||||
|
[[nodiscard]] double getX() const noexcept { return x; }
|
||||||
|
[[nodiscard]] double getY() const noexcept { return y; }
|
||||||
|
[[nodiscard]] double getZ() const noexcept { return z; }
|
||||||
|
|
||||||
|
[[nodiscard]] Vector3D operator+(const Vector3D& other) const noexcept { return Vector3D(x + other.x, y + other.y, z + other.z); }
|
||||||
|
[[nodiscard]] Vector3D operator-(const Vector3D& other) const noexcept { return Vector3D(x - other.x, y - other.y, z - other.z); }
|
||||||
|
[[nodiscard]] Vector3D operator*(const double scalar) const noexcept { return Vector3D(x * scalar, y * scalar, z * scalar); }
|
||||||
|
[[nodiscard]] Vector3D operator/(const double scalar) const noexcept { return Vector3D(x / scalar, y / scalar, z / scalar); }
|
||||||
|
[[nodiscard]] double operator*(const Vector3D& other) const noexcept { return x * other.x + y * other.y + z * other.z; }
|
||||||
|
Vector3D& operator+=(const Vector3D& other) noexcept { return x += other.x, y += other.y, z += other.z, *this; }
|
||||||
|
Vector3D& operator-=(const Vector3D& other) noexcept { return x -= other.x, y -= other.y, z -= other.z, *this; }
|
||||||
|
Vector3D& operator*=(const double scalar) noexcept { return x *= scalar, y *= scalar, z *= scalar, *this; }
|
||||||
|
Vector3D& operator/=(const double scalar) noexcept { return x /= scalar, y /= scalar, z /= scalar, *this; }
|
||||||
|
[[nodiscard]] Vector3D operator-() const noexcept { return Vector3D(-x, -y, -z); }
|
||||||
|
bool operator==(const Vector3D& other) const noexcept { return x == other.x && y == other.y && z == other.z; }
|
||||||
|
bool operator!=(const Vector3D& other) const noexcept { return x != other.x || y != other.y || z != other.z; }
|
||||||
|
[[nodiscard]] double length() const noexcept { return std::sqrt(x * x + y * y + z * z); }
|
||||||
|
|
||||||
|
[[nodiscard]] Vector3D getNormalized() const noexcept {
|
||||||
|
if (x == 0.0 && y == 0.0 && z == 0) return Vector3D(0.0, 0.0, 0.0);
|
||||||
|
const double length = this->length();
|
||||||
|
return Vector3D(x / length, y / length, z / length);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3D& normalize() noexcept {
|
||||||
|
if (x == 0 && y == 0 && z == 0) return *this;
|
||||||
|
const double length = this->length();
|
||||||
|
x /= length, y /= length, z /= length;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3D& add(const Vector3D& other) noexcept { return x += other.x, y += other.y, z += other.z, *this; }
|
||||||
|
Vector3D& add(const double x, const double y, const double z) noexcept { return this->x += x, this->y += y, this->z += z, *this; }
|
||||||
|
Vector3D& subtract(const Vector3D& other) noexcept { return x -= other.x, y -= other.y, z -= other.z, *this; }
|
||||||
|
Vector3D& subtract(const double x, const double y, const double z) noexcept { return this->x -= x, this->y -= y, this->z -= z, *this; }
|
||||||
|
Vector3D& multiply(const double value) noexcept { return this->x *= value, this->y *= value, this->z *= value, *this; }
|
||||||
|
Vector3D& divide(const double value) noexcept { return this->x /= value, this->y /= value, this->z /= value, *this; }
|
||||||
|
[[nodiscard]] double dot(const Vector3D& other) const noexcept { return x * other.x + y * other.y + z * other.z; }
|
||||||
|
[[nodiscard]] Vector3D cross(const Vector3D& other) const noexcept { return Vector3D(y * other.z - z * other.y, z * other.x - x * other.z, x * other.y - y * other.x); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class [[carlbeks::TriviallyCopyable]] Vector2D {
|
||||||
|
double x, y;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Vector2D(const double x, const double y) noexcept : x(x), y(y) {}
|
||||||
|
Vector2D() noexcept : x(0), y(0) {}
|
||||||
|
Vector2D(const Vector2D& other) noexcept = default;
|
||||||
|
Vector2D(Vector2D&& other) noexcept = default;
|
||||||
|
Vector2D& operator=(const Vector2D& other) noexcept = default;
|
||||||
|
Vector2D& operator=(Vector2D&& other) noexcept = default;
|
||||||
|
~Vector2D() noexcept = default;
|
||||||
|
|
||||||
|
[[nodiscard]] double getX() const noexcept { return x; }
|
||||||
|
[[nodiscard]] double getY() const noexcept { return y; }
|
||||||
|
|
||||||
|
[[nodiscard]] Vector2D operator+(const Vector2D& other) const noexcept { return Vector2D(x + other.x, y + other.y); }
|
||||||
|
[[nodiscard]] Vector2D operator-(const Vector2D& other) const noexcept { return Vector2D(x - other.x, y - other.y); }
|
||||||
|
[[nodiscard]] Vector2D operator*(const double scalar) const noexcept { return Vector2D(x * scalar, y * scalar); }
|
||||||
|
[[nodiscard]] Vector2D operator/(const double scalar) const noexcept { return Vector2D(x / scalar, y / scalar); }
|
||||||
|
[[nodiscard]] double operator*(const Vector2D& other) const noexcept { return x * other.x + y * other.y; }
|
||||||
|
Vector2D& operator+=(const Vector2D& other) noexcept { return x += other.x, y += other.y, *this; }
|
||||||
|
Vector2D& operator-=(const Vector2D& other) noexcept { return x -= other.x, y -= other.y, *this; }
|
||||||
|
Vector2D& operator*=(const double scalar) noexcept { return x *= scalar, y *= scalar, *this; }
|
||||||
|
Vector2D& operator/=(const double scalar) noexcept { return x /= scalar, y /= scalar, *this; }
|
||||||
|
[[nodiscard]] Vector2D operator-() const noexcept { return Vector2D(-x, -y); }
|
||||||
|
bool operator==(const Vector2D& other) const noexcept { return x == other.x && y == other.y; }
|
||||||
|
bool operator!=(const Vector2D& other) const noexcept { return x != other.x || y != other.y; }
|
||||||
|
[[nodiscard]] double length() const noexcept { return std::sqrt(x * x + y * y); }
|
||||||
|
|
||||||
|
[[nodiscard]] Vector2D getNormalized() const noexcept {
|
||||||
|
if (x == 0.0 && y == 0.0) return Vector2D(0.0, 0.0);
|
||||||
|
const double length = this->length();
|
||||||
|
return Vector2D(x / length, y / length);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2D& normalize() noexcept {
|
||||||
|
if (x == 0 && y == 0) return *this;
|
||||||
|
const double length = this->length();
|
||||||
|
x /= length, y /= length;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2D& add(const Vector2D& other) noexcept { return x += other.x, y += other.y, *this; }
|
||||||
|
Vector2D& add(const double x, const double y) noexcept { return this->x += x, this->y += y, *this; }
|
||||||
|
Vector2D& subtract(const Vector2D& other) noexcept { return x -= other.x, y -= other.y, *this; }
|
||||||
|
Vector2D& subtract(const double x, const double y) noexcept { return this->x -= x, this->y -= y, *this; }
|
||||||
|
Vector2D& multiply(const double value) noexcept { return this->x *= value, this->y *= value, *this; }
|
||||||
|
Vector2D& divide(const double value) noexcept { return this->x /= value, this->y /= value, *this; }
|
||||||
|
[[nodiscard]] double dot(const Vector2D& other) const noexcept { return x * other.x + y * other.y; }
|
||||||
|
[[nodiscard]] Vector3D cross(const Vector2D& other) const noexcept { return Vector3D(0, 0, x * other.y - y * other.x); }
|
||||||
|
};
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-3-4.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "gc.h"
|
||||||
|
|
||||||
|
template <typename T, typename L = void>
|
||||||
|
class AnywhereEditable;
|
||||||
|
template <typename T, typename L = void>
|
||||||
|
class AnywhereEditableList;
|
||||||
|
template <typename T, typename L = void>
|
||||||
|
class AnywhereIterator;
|
||||||
|
class AnywhereIteratorEnd;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 此处无法进行代码编译层面的直接约束
|
||||||
|
* @tparam T 满足T extends AnywhereEditable<T, L>
|
||||||
|
* @tparam L 满足L extends AnywhereEditableList<T, L>
|
||||||
|
*/
|
||||||
|
template <typename T, typename L>
|
||||||
|
class AnywhereEditable {
|
||||||
|
public:
|
||||||
|
using Lst = std::conditional_t<std::is_same_v<L, void>, AnywhereEditableList<T, L>, L>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class AnywhereIterator<T, L>;
|
||||||
|
friend class AnywhereEditableList<T, L>;
|
||||||
|
friend class AnywhereIteratorEnd;
|
||||||
|
friend L;
|
||||||
|
AnywhereEditable* prev = nullptr;
|
||||||
|
AnywhereEditable* next = nullptr;
|
||||||
|
AnywhereEditableList<T, L>* list = nullptr; // 指示自身属于某个列表
|
||||||
|
bool managedByList = false; // 指示是否在列表内管理内存,而非列表外管理内存
|
||||||
|
public:
|
||||||
|
byte reserved[7]{}; // reserved[0]: Task::schedulePop
|
||||||
|
|
||||||
|
AnywhereEditable() = default;
|
||||||
|
AnywhereEditable(const AnywhereEditable&) {}
|
||||||
|
|
||||||
|
AnywhereEditable(AnywhereEditable&& other) noexcept : prev(other->prev), next(other->next), list(other->list) {
|
||||||
|
other->prev = nullptr;
|
||||||
|
other->next = nullptr;
|
||||||
|
other->list = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~AnywhereEditable() {
|
||||||
|
if (list) {
|
||||||
|
managedByList = false;
|
||||||
|
auto lst = list;
|
||||||
|
list = nullptr;
|
||||||
|
lst->pop(static_cast<T*>(this));
|
||||||
|
Logger.warn(L"~AnywhereEditable() : 析构时没有清除list,导致了析构时pop");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int pushCopy(AnywhereEditableList<T, L>& list) noexcept;
|
||||||
|
int pushThis(AnywhereEditableList<T, L>& list) noexcept;
|
||||||
|
virtual int pop() noexcept;
|
||||||
|
AnywhereEditableList<T, L>* getContainer() const noexcept { return list; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename L>
|
||||||
|
class AnywhereIterator {
|
||||||
|
friend class AnywhereIteratorEnd;
|
||||||
|
AnywhereEditable<T, L>* current;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AnywhereIterator(AnywhereEditable<T, L>* current) : current(current) {}
|
||||||
|
AnywhereIterator(const AnywhereIterator& other) = default;
|
||||||
|
|
||||||
|
T& operator*() noexcept(false) {
|
||||||
|
if (current->next) return static_cast<T&>(*current);
|
||||||
|
throw RuntimeException(L"AnywhereIterator::operator*() : next is null, end of list");
|
||||||
|
}
|
||||||
|
|
||||||
|
T* operator->() noexcept(false) {
|
||||||
|
if (current->next) return &static_cast<T&>(*current);
|
||||||
|
throw RuntimeException(L"AnywhereIterator::operator*() : next is null, end of list");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const AnywhereIterator& other) const noexcept { return current != other.current; }
|
||||||
|
bool operator==(const AnywhereIterator& other) const noexcept { return current == other.current; }
|
||||||
|
bool operator!=(const AnywhereIteratorEnd& other) const noexcept;
|
||||||
|
bool operator==(const AnywhereIteratorEnd& other) const noexcept;
|
||||||
|
|
||||||
|
AnywhereIterator& operator++() noexcept(false) {
|
||||||
|
if (current->next) current = current->next;
|
||||||
|
else throw InvalidOperationException(L"AnywhereIterator::operator++() : end of list");
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnywhereIterator operator++(int) noexcept(false) {
|
||||||
|
if (current->next) {
|
||||||
|
AnywhereIterator ret = *this;
|
||||||
|
current = current->next;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
throw InvalidOperationException(L"AnywhereIterator::operator++() : end of list");
|
||||||
|
}
|
||||||
|
|
||||||
|
AnywhereIterator& operator--() noexcept(false) {
|
||||||
|
if (current->prev) current = current->prev;
|
||||||
|
else throw InvalidOperationException(L"AnywhereIterator::operator--() : begin of list");
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnywhereIterator operator--(int) noexcept(false) {
|
||||||
|
if (current->prev) {
|
||||||
|
AnywhereIterator ret = *this;
|
||||||
|
current = current->prev;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
throw InvalidOperationException(L"AnywhereIterator::operator--() : begin of list");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class AnywhereIteratorEnd {
|
||||||
|
public:
|
||||||
|
AnywhereIteratorEnd() = default;
|
||||||
|
AnywhereIteratorEnd(const AnywhereIteratorEnd& other) = delete;
|
||||||
|
AnywhereIteratorEnd(AnywhereIteratorEnd&& other) = delete;
|
||||||
|
|
||||||
|
template <typename T, typename L>
|
||||||
|
bool operator!=(const AnywhereIterator<T, L>& other) const noexcept { return other.current && other.current->next; }
|
||||||
|
|
||||||
|
template <typename T, typename L>
|
||||||
|
bool operator==(const AnywhereIterator<T, L>& other) const noexcept { return !operator!=(other); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename L>
|
||||||
|
class AnywhereEditableList {
|
||||||
|
protected:
|
||||||
|
AnywhereEditable<T, L> head;
|
||||||
|
AnywhereEditable<T, L> tail;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AnywhereEditableList() {
|
||||||
|
head.next = &tail;
|
||||||
|
tail.prev = &head;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~AnywhereEditableList() { for (AnywhereIterator<T, L> it = begin(); it != end(); ++it) {} }
|
||||||
|
|
||||||
|
int pushCopy(T* value) noexcept;
|
||||||
|
int pushThis(T* value) noexcept;
|
||||||
|
int pushNewed(T* value) noexcept;
|
||||||
|
virtual int pop(T* value) noexcept;
|
||||||
|
AnywhereIterator<T, L> begin() noexcept { return AnywhereIterator<T, L>(head.next); }
|
||||||
|
[[nodiscard]] AnywhereIteratorEnd end() noexcept { return {}; }
|
||||||
|
AnywhereIterator<T, L> begin() const noexcept { return AnywhereIterator<T, L>(head.next); }
|
||||||
|
[[nodiscard]] AnywhereIteratorEnd end() const noexcept { return {}; }
|
||||||
|
AnywhereEditable<T, L>* front() const noexcept { return head.next == &tail ? nullptr : head.next; }
|
||||||
|
AnywhereEditable<T, L>* back() const noexcept { return tail.prev == &head ? nullptr : tail.prev; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename L>
|
||||||
|
int AnywhereEditable<T, L>::pushCopy(AnywhereEditableList<T, L>& list) noexcept { return list.pushCopy(static_cast<T*>(this)); }
|
||||||
|
|
||||||
|
template <typename T, typename L>
|
||||||
|
int AnywhereEditable<T, L>::pushThis(AnywhereEditableList<T, L>& list) noexcept { return list.pushThis(static_cast<T*>(this)); }
|
||||||
|
|
||||||
|
template <typename T, typename L>
|
||||||
|
int AnywhereEditable<T, L>::pop() noexcept {
|
||||||
|
if (!list) {
|
||||||
|
Logger.error(L"AnywhereEditable::pop() : list is null");
|
||||||
|
Failed();
|
||||||
|
}
|
||||||
|
return list->pop(static_cast<T*>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename L>
|
||||||
|
bool AnywhereIterator<T, L>::operator!=(const AnywhereIteratorEnd& other) const noexcept { return other != *this; }
|
||||||
|
|
||||||
|
template <typename T, typename L>
|
||||||
|
bool AnywhereIterator<T, L>::operator==(const AnywhereIteratorEnd& other) const noexcept { return other == *this; }
|
||||||
|
|
||||||
|
template <typename T, typename L>
|
||||||
|
int AnywhereEditableList<T, L>::pushCopy(T* value) noexcept {
|
||||||
|
T* nv = allocatedFor(new T(*value));
|
||||||
|
return pushNewed(nv);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename L>
|
||||||
|
int AnywhereEditableList<T, L>::pushThis(T* value) noexcept {
|
||||||
|
if (value->list) {
|
||||||
|
Logger.error(L"AnywhereEditableList::pushThis() : value is already in a list");
|
||||||
|
Failed();
|
||||||
|
}
|
||||||
|
value->list = this;
|
||||||
|
tail.prev->next = value;
|
||||||
|
value->prev = tail.prev;
|
||||||
|
value->next = &tail;
|
||||||
|
tail.prev = value;
|
||||||
|
Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename L>
|
||||||
|
int AnywhereEditableList<T, L>::pushNewed(T* value) noexcept {
|
||||||
|
const int ret = pushThis(value);
|
||||||
|
if (ret) delete deallocating(value);
|
||||||
|
else value->managedByList = true;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename L>
|
||||||
|
int AnywhereEditableList<T, L>::pop(T* value) noexcept {
|
||||||
|
if (value->list != this) {
|
||||||
|
Logger.error(L"AnywhereEditableList::pop() : value is not in this list");
|
||||||
|
Failed();
|
||||||
|
}
|
||||||
|
value->list = nullptr;
|
||||||
|
value->next->prev = value->prev;
|
||||||
|
value->prev->next = value->next;
|
||||||
|
if (value->managedByList) gc.submit(value);
|
||||||
|
Success();
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
//
|
||||||
|
// Created by EmsiaetKadosh on 25-3-13.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#pragma warning(disable: 4554)
|
||||||
|
#pragma warning(default: 4555)
|
||||||
|
#pragma warning(disable: 4996)
|
||||||
|
#pragma warning(disable: 5030)
|
||||||
-15
@@ -1,15 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by EmsiaetKadosh on 25-1-18.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Window.h"
|
|
||||||
|
|
||||||
class StartWindow : public Window {
|
|
||||||
public:
|
|
||||||
StartWindow() {
|
|
||||||
Button* button = new Button(0, 0, 0.4, 0.08, Location::CENTER, TranslatableText(L"123"));
|
|
||||||
widgets.push_back(button);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user