Compare commits

...

10 Commits

Author SHA1 Message Date
EmsiaetKadosh ce5ada7d38 适应推 2025-03-23 16:59:23 +08:00
EmsiaetKadosh a335259edc world初步 2025-03-23 13:23:10 +08:00
EmsiaetKadosh 037e532748 文件结构重构 2025-03-21 21:39:49 +08:00
EmsiaetKadosh 20f4ef1cee Translator初步 2025-03-21 11:06:51 +08:00
EmsiaetKadosh 28cc82bcd7 格式整理 2025-03-19 17:08:28 +08:00
EmsiaetKadosh c344588924 格式整理 2025-03-16 14:18:05 +08:00
EmsiaetKadosh a1d0f11032 remake UTF-8 and I forgot what I have done in this commit 2025-03-11 18:37:34 +08:00
EmsiaetKadosh 7e63aa1217 gc测试 2025-03-06 22:25:43 +08:00
EmsiaetKadosh 28a836ff5f 更改字符集 2025-03-06 16:49:23 +08:00
EmsiaetKadosh ae4372827f 更改字符集 2025-02-07 14:38:48 +08:00
67 changed files with 5049 additions and 1713 deletions
+1 -2
View File
@@ -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
View File
@@ -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
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+32 -25
View File
@@ -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)
-14
View File
@@ -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();
-51
View File
@@ -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;
-26
View File
@@ -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();
}
}
-395
View File
@@ -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();
-123
View File
@@ -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(); }
};
-26
View File
@@ -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
View File
@@ -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;
-83
View File
@@ -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
View File
@@ -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 {}
-129
View File
@@ -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 {}
};
-27
View File
@@ -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");
}
-55
View File
@@ -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");
}
-153
View File
@@ -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);
};
+5
View File
@@ -0,0 +1,5 @@
Module:
File(30%), including Log, Save, Config, Language
Translator(30%), with File
KeyBinding(20%)
World, Entity(10%), including gc problem
-71
View File
@@ -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
View File
@@ -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;
-31
View File
@@ -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
View File
@@ -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"
-223
View File
@@ -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
View File
@@ -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";
}
}
+325
View File
@@ -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;
}
};
+156
View File
@@ -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;
}
};
+64
View File
@@ -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();
+79
View File
@@ -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;
+43
View File
@@ -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;
};
+5
View File
@@ -0,0 +1,5 @@
//
// Created by EmsiaetKadosh on 25-3-22.
//
#include "Entity.h"
+90
View File
@@ -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();
}
};
+5
View File
@@ -0,0 +1,5 @@
//
// Created by EmsiaetKadosh on 25-3-21.
//
#include "Player.h"
+12
View File
@@ -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) {}
};
+43
View File
@@ -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)); }
};
+12
View File
@@ -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);
+143
View File
@@ -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); }
+181
View File
@@ -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;
}
};
+19
View File
@@ -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();
+40
View File
@@ -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); }
+244
View File
@@ -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
View File
@@ -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);
}
+86
View File
@@ -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();
+267
View File
@@ -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;
+19
View File
@@ -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();
+1 -1
View File
@@ -4,5 +4,5 @@
#include "Hud.h"
void Hud::render() const noexcept {}
void Hud::render(double tickDelta) const noexcept {}
void Hud::tick() noexcept {}
+4 -2
View File
@@ -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 {}
};
+338
View File
@@ -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
View File
@@ -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))); }
};
+41
View File
@@ -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
View File
@@ -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;
}
+17
View File
@@ -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);
}
+166
View File
@@ -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();
+152
View File
@@ -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();
+605
View File
@@ -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;
+55
View File
@@ -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); }
};
+1 -2
View File
@@ -4,5 +4,4 @@
#pragma once
inline void test() {
}
inline void test() {}
+13
View File
@@ -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";
}
+198
View File
@@ -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");
+5
View File
@@ -0,0 +1,5 @@
//
// Created by EmsiaetKadosh on 25-3-6.
//
#include "gc.h"
+150
View File
@@ -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();
+131
View File
@@ -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); }
};
+219
View File
@@ -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();
}
+10
View File
@@ -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
View File
@@ -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);
}
};