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
|
||||
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
# https://www.cnblogs.com/PaulpauL/p/5929753.html
|
||||
@@ -167,7 +166,7 @@ SortUsingDeclarations: Lexicographic
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterTemplateKeyword: false # WSO
|
||||
SpaceAroundPointerQualifiers: Before # WSO
|
||||
SpacesBeforeTrailingComments: 0 # Better 1; 0 ok, 2+ better not.
|
||||
SpacesBeforeTrailingComments: 1 # Better 1; 0 ok, 2+ better not.
|
||||
Standard: Latest
|
||||
TabWidth: 2 # 4 or 2, WSO
|
||||
# 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(-DCINTERFACE)
|
||||
add_definitions(-D__CARLBEKS_CMAKE_VSCODE__)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD 26)
|
||||
set(CMAKE_WIN32_EXECUTABLE true)
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
add_compile_options(${PROJECT_NAME} -Wno-microsoft-string-literal-from-predefined)
|
||||
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)
|
||||
enable_testing()
|
||||
|
||||
add_executable(${PROJECT_NAME} main.cpp
|
||||
def.h
|
||||
InteractManager.cpp
|
||||
InteractManager.h
|
||||
Renderer.cpp
|
||||
Renderer.h
|
||||
Game.cpp
|
||||
Game.h
|
||||
Window.cpp
|
||||
Window.h
|
||||
Hud.cpp
|
||||
Hud.h
|
||||
includes.h
|
||||
IText.h
|
||||
Chars.h
|
||||
IText.cpp
|
||||
xWindows.h
|
||||
exception.h
|
||||
def.cpp
|
||||
TestCode.h
|
||||
TextureManager.cpp
|
||||
TextureManager.h
|
||||
exception.cpp)
|
||||
add_executable(${PROJECT_NAME}
|
||||
src/main.cpp
|
||||
|
||||
src/def.cpp
|
||||
src/utils/File.cpp
|
||||
src/utils/exception.cpp
|
||||
|
||||
src/interact/InteractManager.cpp
|
||||
src/utils/IText.cpp
|
||||
src/game/world/Location.cpp
|
||||
src/render/Renderer.cpp
|
||||
src/render/TextureManager.cpp
|
||||
src/ui/Hud.cpp
|
||||
src/ui/Window.cpp
|
||||
|
||||
src/game/Game.cpp
|
||||
src/game/entity/Entity.cpp
|
||||
src/game/entity/Player.cpp
|
||||
)
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
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)
|
||||
|
||||
|
||||
@@ -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 "..\ui\Window.h"
|
||||
|
||||
InteractManager::InteractManager() {
|
||||
keyStatus[0x00].name = L"NONE";
|
||||
keyStatus[0x01].name = L"LeftButton";
|
||||
@@ -263,5 +265,19 @@ InteractManager::InteractManager() {
|
||||
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"
|
||||
|
||||
void Hud::render() const noexcept {}
|
||||
void Hud::render(double tickDelta) const noexcept {}
|
||||
void Hud::tick() noexcept {}
|
||||
@@ -3,11 +3,13 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include "Renderer.h"
|
||||
|
||||
#include "..\render\Renderer.h"
|
||||
|
||||
class Hud final : public IRenderable, public ITickable {
|
||||
public:
|
||||
void render() const noexcept override;
|
||||
void render(double tickDelta) const 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);
|
||||
}
|
||||
};
|
||||
+66
-17
@@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "def.h"
|
||||
#include "..\def.h"
|
||||
|
||||
[[nodiscard]] inline unsigned int wtouib16(const wchar* string) noexcept {
|
||||
unsigned int ret = 0;
|
||||
@@ -15,10 +15,10 @@
|
||||
ret += *string - L'0';
|
||||
} else if (*string >= L'A' && *string <= L'F') {
|
||||
ret <<= 4;
|
||||
ret += *string - 0x41;// 'A' - 10
|
||||
ret += *string - 0x41; // 'A' - 10
|
||||
} else if (*string >= L'a' && *string <= L'f') {
|
||||
ret <<= 4;
|
||||
ret += *string - 0x61;// 'a' - 10
|
||||
ret += *string - 0x61; // 'a' - 10
|
||||
} else return 0xffffffff;
|
||||
++string;
|
||||
}
|
||||
@@ -35,10 +35,10 @@
|
||||
ret += string[i] - L'0';
|
||||
} else if (string[i] >= L'A' && string[i] <= L'F') {
|
||||
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') {
|
||||
ret <<= 4;
|
||||
ret += string[i] - 0x61;// L'a' - 10
|
||||
ret += string[i] - 87; // L'a' - 10
|
||||
} else return 0xffffffff;
|
||||
++i;
|
||||
}
|
||||
@@ -46,6 +46,7 @@
|
||||
}
|
||||
|
||||
static constexpr wchar Table16[17] = L"0123456789ABCDEF";
|
||||
|
||||
/**
|
||||
* 将数字转换为字符串
|
||||
* @param value 要转换的数字
|
||||
@@ -54,20 +55,49 @@ static constexpr wchar Table16[17] = L"0123456789ABCDEF";
|
||||
*/
|
||||
[[nodiscard]] inline String uitowb16(unsigned int value, const unsigned int fills = 1) noexcept {
|
||||
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');
|
||||
for (unsigned int i = fills - 1; i != 0 && value; --i) {
|
||||
for (unsigned int i = fills - 1; i && value; --i) {
|
||||
ret[i] = Table16[value & 0xf];
|
||||
value >>= 4;
|
||||
}
|
||||
} else {
|
||||
unsigned int i = 0;
|
||||
while (i < 8) {
|
||||
if ((value >> i) & 0xf) break;
|
||||
if (value >> i & 0xf) break;
|
||||
++i;
|
||||
}
|
||||
while (i < 8) {
|
||||
ret.push_back(Table16[(value >> i) & 0xf]);
|
||||
ret.push_back(Table16[value >> i & 0xf]);
|
||||
++i;
|
||||
}
|
||||
if (ret.empty()) ret = L"0";
|
||||
}
|
||||
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";
|
||||
@@ -75,15 +105,15 @@ static constexpr wchar Table16[17] = L"0123456789ABCDEF";
|
||||
return ret;
|
||||
}
|
||||
|
||||
static constexpr wchar Table10[11] = L"0123456789";
|
||||
static constexpr QWORD Compare10[20] = {
|
||||
0, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
|
||||
100000000, 1000000000, 10000000000, 100000000000,
|
||||
1000000000000, 10000000000000, 100000000000000,
|
||||
1000000000000000, 10000000000000000, 100000000000000000,
|
||||
1000000000000000000, 10000000000000000000
|
||||
};
|
||||
[[nodiscard]] inline String qwtowb10(QWORD value, const unsigned int fills = 1) noexcept {
|
||||
static constexpr wchar Table10[11] = L"0123456789";
|
||||
static constexpr QWORD Compare10[20] = {
|
||||
0ull, 10ull, 100ull, 1000ull, 10000ull, 100000ull, 1000000ull, 10000000ull,
|
||||
100000000ull, 1000000000ull, 10000000000ull, 100000000000ull,
|
||||
1000000000000ull, 10000000000000ull, 100000000000000ull,
|
||||
1000000000000000ull, 10000000000000000ull, 100000000000000000ull,
|
||||
1000000000000000000ull, 10000000000000000000ull
|
||||
};
|
||||
String ret;
|
||||
if (value < Compare10[fills]) {
|
||||
ret.assign(fills, L'0');
|
||||
@@ -106,3 +136,22 @@ static constexpr QWORD Compare10[20] = {
|
||||
}
|
||||
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
|
||||
|
||||
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