文件结构重构

This commit is contained in:
EmsiaetKadosh
2025-03-21 21:39:49 +08:00
parent 20f4ef1cee
commit 037e532748
43 changed files with 539 additions and 134 deletions
+47
View File
@@ -0,0 +1,47 @@
//
// Created by EmsiaetKadosh on 25-1-18.
//
#include "def.h"
#include "utils\Chars.h"
#include "utils\exception.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 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);
#if __CARLBEKS_MEMORY__ > 2
MainLogFile << str << std::endl;
#endif
}
void 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);
#if __CARLBEKS_MEMORY__ > 2
MainLogFile << str << std::endl;
#endif
}
String ptrtow(const QWORD value) { return qwtowb16(value, 16); }
+286
View File
@@ -0,0 +1,286 @@
//
// Created by EmsiaetKadosh on 25-1-14.
//
#pragma once
#define __CARLBEKS_DEBUG__
#define __CARLBEKS_MEMORY__ 1
#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, 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")
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>;
struct MemoryManager {
struct MemoryInfo {
const String msg;
std::size_t size;
};
Map<void*, MemoryInfo> allocated {};
constexpr MemoryManager() noexcept = default;
} inline 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__
void printAllocate(void* value, std::size_t size, const String&);
void printDeallocate(void* value, std::size_t size, const String&);
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);
const auto& k = memoryManager.allocated.emplace(value, MemoryManager::MemoryInfo{ L"[" + atow(typeid(T).name()) + L"] " + msg , size}).first;
#if __CARLBEKS_MEMORY__ > 1
printAllocate(value, k->second.size, k->second.msg);
#endif
return value;
}
template<typename T> T* deallocating$(T* value) {
#if __CARLBEKS_MEMORY__ > 1
const MemoryManager::MemoryInfo* info = nullptr;
if (memoryManager.allocated.contains(value)) info = &memoryManager.allocated.at(value);
printDeallocate(value, info ? info->size : 0, info ? info->msg : L"???");
#endif
if (value) memoryManager.allocated.erase(value);
return value;
}
#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
#define deallocating(val) deallocating$(val)
#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;
}
};
+27
View File
@@ -0,0 +1,27 @@
//
// Created by EmsiaetKadosh on 25-1-14.
//
#include "Game.h"
#include "world\World.h"
#include "..\ui\xWindows.h"
void Game::initialize() {
worldManager = allocatedFor(new WorldManager);
game.setWindow(StartWindow::create());
}
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);
delete deallocating(caption);
delete deallocating(worldManager);
}
inline Game game = Game();
+98
View File
@@ -0,0 +1,98 @@
//
// 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 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;
void initialize();
Game();
~Game();
[[nodiscard]] QWORD getTick() const noexcept { return currentTick; }
int closeWindow(Window* const window) noexcept { return windows.pop(window); }
void render() const noexcept {
if (renderer.checkResizing()) return;
renderer.gameStartRender();
caption->render();
hud.render();
windows.render();
floatWindow->render();
renderer.gameEndRender();
gc.pack();
}
/**
* 所有窗口都提交给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]] FloatWindow& getFloatWindow() const noexcept { return *floatWindow; }
[[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 tick() noexcept {
++currentTick;
floatWindow->clear();
floatWindow->tick();
caption->tick();
hud.tick();
windows.tick();
floatWindow->update();
tasks.runAll();
gc.collect();
}
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;
};
+61
View File
@@ -0,0 +1,61 @@
//
// Created by EmsiaetKadosh on 25-3-21.
//
#pragma once
#include "..\..\utils\math.h"
#include "..\world\Location.h"
#include "Damage.h"
class Entity;
class World;
interface IDamageable {
protected:
double maxHealth = 0;
double health = 0;
public:
virtual ~IDamageable() = default;
virtual void Damage(Damage&) = 0;
virtual void onDeath() = 0;
};
interface IMoveable {
protected:
double maxSpeed = 1.0;
Vector2D velocity;
virtual ~IMoveable() = default;
public:
void setVelocity(const Vector2D& velocity) { this->velocity = velocity; }
[[nodiscard]] Vector2D getVelocity() const { return this->velocity; }
};
interface IArtificialIntelligent {
protected:
virtual ~IArtificialIntelligent() = default;
virtual void aiProcess() {}
};
class Entity {
friend class World;
Location location;
QWORD id = 0;
protected:
Entity(const Location& location) : location(location) {}
Entity(Location&& location) : location(std::move(location)) {}
virtual ~Entity() = default;
public:
virtual void tick() {}
virtual void onRemove() {}
};
class Enemy : public Entity, public IDamageable, public IMoveable, public IArtificialIntelligent {
protected:
Enemy(const Location& location) : Entity(location) {}
Enemy(Location&& location) : Entity(std::move(location)) {}
};
+5
View File
@@ -0,0 +1,5 @@
//
// Created by EmsiaetKadosh on 25-3-21.
//
#include "Player.h"
+13
View File
@@ -0,0 +1,13 @@
//
// Created by EmsiaetKadosh on 25-3-21.
//
#pragma once
#include "Entity.h"
class Player : public Entity, public IMoveable, public IDamageable {
public:
Player(const Location& location) : Entity(location) {}
Player(Location&& location) : Entity(std::move(location)) {}
};
+12
View File
@@ -0,0 +1,12 @@
//
// Created by EmsiaetKadosh on 25-3-21.
//
#pragma once
#include "Location.h"
class Block {
public:
};
+42
View File
@@ -0,0 +1,42 @@
//
// Created by EmsiaetKadosh on 25-3-21.
//
#pragma once
#include "..\..\utils\math.h"
using WorldID = QWORD;
class Location;
class BlockLocation;
class Location {
Vector2D position;
WorldID idWorld;
public:
Location(const Vector2D& position, const WorldID idWorld) : position(position), idWorld(idWorld) {}
Location(Vector2D&& position, const WorldID idWorld) : position(position), idWorld(idWorld) {}
[[nodiscard]] Vector2D getPosition() const { return position; }
[[nodiscard]] WorldID getWorld() const { return idWorld; }
[[nodiscard]] double getX() const { return position.getX(); }
[[nodiscard]] double getY() const { return position.getY(); }
[[nodiscard]] BlockLocation getBlockLocation() const;
};
class BlockLocation {
long long x, y;
WorldID idWorld;
public:
BlockLocation(const long long x, const long long y, const WorldID idWorld) : x(x), y(y), idWorld(idWorld) {}
BlockLocation(const Vector2D& position, const WorldID idWorld) : x(static_cast<long long>(std::floor(position.getX()))), y(static_cast<long long>(std::floor(position.getY()))), idWorld(idWorld) {}
[[nodiscard]] Vector2D getPosition() const { return Vector2D(static_cast<double>(x), static_cast<double>(y)); }
[[nodiscard]] WorldID getWorld() const { return idWorld; }
[[nodiscard]] long long getX() const { return x; }
[[nodiscard]] long long getY() const { return y; }
[[nodiscard]] Location toLocation() const { return Location({ static_cast<double>(x), static_cast<double>(y) }, idWorld); }
};
inline BlockLocation Location::getBlockLocation() const { return BlockLocation(position, idWorld); }
+39
View File
@@ -0,0 +1,39 @@
//
// Created by EmsiaetKadosh on 25-3-21.
//
#pragma once
#include "..\..\def.h"
#include "..\entity\Entity.h"
class World {
QWORD idEntity = 0;
Map<QWORD, Entity*> entities;
using IterEntity = Map<QWORD, Entity*>::const_iterator;
public:
World() = default;
int addEntity(Entity* entity) {
if (!entity) Failed();
if (entity->id) Failed();
entities.emplace(++idEntity, entity);
Success();
}
int removeEntity(Entity* entity) {
if (!entity) Failed();
if (!entity->id) Failed();
const IterEntity iter = entities.find(entity->id);
if (iter != entities.end()) Failed();
entities.erase(iter);
entity->onRemove();
entity->id = 0;
Success();
}
};
class WorldManager {
public:
};
+31
View File
@@ -0,0 +1,31 @@
#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(const 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, "CONOUT$", "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();
+39
View File
@@ -0,0 +1,39 @@
//
// Created by EmsiaetKadosh on 25-1-14.
//
#pragma once
#include "warnings.h"
// Any include
#include "def.h"
#include "utils\math.h"
#include "utils\File.h"
#include "utils\exception.h"
// utils serialized
#include "utils\gc.h"
#include "utils\utils.h"
#include "hbp.h"
#include "utils\Chars.h"
#include "utils\Task.h"
// game basic
#include "interact\InteractManager.h"
#include "utils\IText.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\world\Location.h"
#include "game\entity\Entity.h"
#include "game\world\World.h"
#include "game\entity\Player.h"
+283
View File
@@ -0,0 +1,283 @@
//
// Created by EmsiaetKadosh on 25-1-14.
//
#include "InteractManager.h"
#include "..\ui\Window.h"
InteractManager::InteractManager() {
keyStatus[0x00].name = L"NONE";
keyStatus[0x01].name = L"LeftButton";
keyStatus[0x02].name = L"RightButton";
keyStatus[0x03].name = L"Cancel";
keyStatus[0x04].name = L"MiddleButton";
keyStatus[0x05].name = L"XButton1";
keyStatus[0x06].name = L"XButton2";
keyStatus[0x07].name = L"None";
keyStatus[0x08].name = L"Backspace";
keyStatus[0x09].name = L"Tab";
keyStatus[0x0D].name = L"Enter";
keyStatus[0x0A].name = L"None";
keyStatus[0x0B].name = L"None";
keyStatus[0x0C].name = L"Clear";
keyStatus[0x0D].name = L"Enter";
keyStatus[0x0E].name = L"None";
keyStatus[0x0F].name = L"None";
keyStatus[0x10].name = L"Shift";
keyStatus[0x11].name = L"Ctrl";
keyStatus[0x12].name = L"Alt";
keyStatus[0x13].name = L"Pause";
keyStatus[0x14].name = L"CapsLock";
keyStatus[0x15].name = L"IME-KanaHangul";
keyStatus[0x16].name = L"IME-ON";
keyStatus[0x17].name = L"IME-Junja";
keyStatus[0x18].name = L"IME-Final";
keyStatus[0x19].name = L"IME-HanjaKanji";
keyStatus[0x1A].name = L"IME-OFF";
keyStatus[0x1B].name = L"Escape";
keyStatus[0x1C].name = L"IME-Convert";
keyStatus[0x1D].name = L"IME-NonConvert";
keyStatus[0x1E].name = L"IME-Accept";
keyStatus[0x1F].name = L"IME-ModeChange";
keyStatus[0x20].name = L"Space";
keyStatus[0x21].name = L"PageUp";
keyStatus[0x22].name = L"PageDown";
keyStatus[0x23].name = L"End";
keyStatus[0x24].name = L"Home";
keyStatus[0x25].name = L"Left";
keyStatus[0x26].name = L"Up";
keyStatus[0x27].name = L"Right";
keyStatus[0x28].name = L"Down";
keyStatus[0x29].name = L"Select";
keyStatus[0x2A].name = L"Print";
keyStatus[0x2B].name = L"Execute";
keyStatus[0x2C].name = L"PrintScreen";
keyStatus[0x2D].name = L"Insert";
keyStatus[0x2E].name = L"Delete";
keyStatus[0x2F].name = L"Help";
keyStatus[0x30].name = L"0";
keyStatus[0x31].name = L"1";
keyStatus[0x32].name = L"2";
keyStatus[0x33].name = L"3";
keyStatus[0x34].name = L"4";
keyStatus[0x35].name = L"5";
keyStatus[0x36].name = L"6";
keyStatus[0x37].name = L"7";
keyStatus[0x38].name = L"8";
keyStatus[0x39].name = L"9";
keyStatus[0x3A].name = L"None";
keyStatus[0x3B].name = L"None";
keyStatus[0x3C].name = L"None";
keyStatus[0x3D].name = L"None";
keyStatus[0x3E].name = L"None";
keyStatus[0x3F].name = L"None";
keyStatus[0x40].name = L"None";
keyStatus[0x41].name = L"A";
keyStatus[0x42].name = L"B";
keyStatus[0x43].name = L"C";
keyStatus[0x44].name = L"D";
keyStatus[0x45].name = L"E";
keyStatus[0x46].name = L"F";
keyStatus[0x47].name = L"G";
keyStatus[0x48].name = L"H";
keyStatus[0x49].name = L"I";
keyStatus[0x4A].name = L"J";
keyStatus[0x4B].name = L"K";
keyStatus[0x4C].name = L"L";
keyStatus[0x4D].name = L"M";
keyStatus[0x4E].name = L"N";
keyStatus[0x4F].name = L"O";
keyStatus[0x50].name = L"P";
keyStatus[0x51].name = L"Q";
keyStatus[0x52].name = L"R";
keyStatus[0x53].name = L"S";
keyStatus[0x54].name = L"T";
keyStatus[0x55].name = L"U";
keyStatus[0x56].name = L"V";
keyStatus[0x57].name = L"W";
keyStatus[0x58].name = L"X";
keyStatus[0x59].name = L"Y";
keyStatus[0x5A].name = L"Z";
keyStatus[0x5B].name = L"LeftWindows";
keyStatus[0x5C].name = L"RightWindows";
keyStatus[0x5D].name = L"Applications";
keyStatus[0x5E].name = L"None";
keyStatus[0x5F].name = L"Sleep";
keyStatus[0x60].name = L"NUMPAD-0";
keyStatus[0x61].name = L"NUMPAD-1";
keyStatus[0x62].name = L"NUMPAD-2";
keyStatus[0x63].name = L"NUMPAD-3";
keyStatus[0x64].name = L"NUMPAD-4";
keyStatus[0x65].name = L"NUMPAD-5";
keyStatus[0x66].name = L"NUMPAD-6";
keyStatus[0x67].name = L"NUMPAD-7";
keyStatus[0x68].name = L"NUMPAD-8";
keyStatus[0x69].name = L"NUMPAD-9";
keyStatus[0x6A].name = L"*";
keyStatus[0x6B].name = L"+";
keyStatus[0x6C].name = L"·";
keyStatus[0x6D].name = L"-";
keyStatus[0x6E].name = L".";
keyStatus[0x6F].name = L"/";
keyStatus[0x70].name = L"F1";
keyStatus[0x71].name = L"F2";
keyStatus[0x72].name = L"F3";
keyStatus[0x73].name = L"F4";
keyStatus[0x74].name = L"F5";
keyStatus[0x75].name = L"F6";
keyStatus[0x76].name = L"F7";
keyStatus[0x77].name = L"F8";
keyStatus[0x78].name = L"F9";
keyStatus[0x79].name = L"F10";
keyStatus[0x7A].name = L"F11";
keyStatus[0x7B].name = L"F12";
keyStatus[0x7C].name = L"F13";
keyStatus[0x7D].name = L"F14";
keyStatus[0x7E].name = L"F15";
keyStatus[0x7F].name = L"F16";
keyStatus[0x80].name = L"F17";
keyStatus[0x81].name = L"F18";
keyStatus[0x82].name = L"F19";
keyStatus[0x83].name = L"F20";
keyStatus[0x84].name = L"F21";
keyStatus[0x85].name = L"F22";
keyStatus[0x86].name = L"F23";
keyStatus[0x87].name = L"F24";
keyStatus[0x88].name = L"None";
keyStatus[0x89].name = L"None";
keyStatus[0x8A].name = L"None";
keyStatus[0x8B].name = L"None";
keyStatus[0x8C].name = L"None";
keyStatus[0x8D].name = L"None";
keyStatus[0x8E].name = L"None";
keyStatus[0x8F].name = L"None";
keyStatus[0x90].name = L"NumLock";
keyStatus[0x91].name = L"ScrollLock";
keyStatus[0x92].name = L"None";
keyStatus[0x93].name = L"None";
keyStatus[0x94].name = L"None";
keyStatus[0x95].name = L"None";
keyStatus[0x96].name = L"None";
keyStatus[0x97].name = L"None";
keyStatus[0x98].name = L"None";
keyStatus[0x99].name = L"None";
keyStatus[0x9A].name = L"None";
keyStatus[0x9B].name = L"None";
keyStatus[0x9C].name = L"None";
keyStatus[0x9D].name = L"None";
keyStatus[0x9E].name = L"None";
keyStatus[0x9F].name = L"None";
keyStatus[0xA0].name = L"LeftShift";
keyStatus[0xA1].name = L"RightShift";
keyStatus[0xA2].name = L"LeftCtrl";
keyStatus[0xA3].name = L"RightCtrl";
keyStatus[0xA4].name = L"LeftAlt";
keyStatus[0xA5].name = L"RightAlt";
keyStatus[0xA6].name = L"BrowserBack";
keyStatus[0xA7].name = L"BrowserForward";
keyStatus[0xA8].name = L"BrowserRefresh";
keyStatus[0xA9].name = L"BrowserStop";
keyStatus[0xAA].name = L"BrowserSearch";
keyStatus[0xAB].name = L"BrowserFavorites";
keyStatus[0xAC].name = L"BrowserHome";
keyStatus[0xAD].name = L"VolumeMute";
keyStatus[0xAE].name = L"VolumeDown";
keyStatus[0xAF].name = L"VolumeUp";
keyStatus[0xB0].name = L"MediaNextTrack";
keyStatus[0xB1].name = L"MediaPrevTrack";
keyStatus[0xB2].name = L"MediaStop";
keyStatus[0xB3].name = L"MediaPlayPause";
keyStatus[0xB4].name = L"LaunchMail";
keyStatus[0xB5].name = L"LaunchMediaSelect";
keyStatus[0xB6].name = L"LaunchApp1";
keyStatus[0xB7].name = L"LaunchApp2";
keyStatus[0xB8].name = L"None";
keyStatus[0xB9].name = L"None";
keyStatus[0xBA].name = L"OEM-1;:";
keyStatus[0xBB].name = L"OEM+";
keyStatus[0xBC].name = L"OEM,";
keyStatus[0xBD].name = L"OEM-";
keyStatus[0xBE].name = L"OEM.";
keyStatus[0xBF].name = L"OEM-2/?";
keyStatus[0xC0].name = L"OEM-3`~";
keyStatus[0xC1].name = L"None";
keyStatus[0xC2].name = L"None";
keyStatus[0xC3].name = L"None";
keyStatus[0xC4].name = L"None";
keyStatus[0xC5].name = L"None";
keyStatus[0xC6].name = L"None";
keyStatus[0xC7].name = L"None";
keyStatus[0xC8].name = L"None";
keyStatus[0xC9].name = L"None";
keyStatus[0xCA].name = L"None";
keyStatus[0xCB].name = L"None";
keyStatus[0xCC].name = L"None";
keyStatus[0xCD].name = L"None";
keyStatus[0xCE].name = L"None";
keyStatus[0xCF].name = L"None";
keyStatus[0xD0].name = L"None";
keyStatus[0xD1].name = L"None";
keyStatus[0xD2].name = L"None";
keyStatus[0xD3].name = L"None";
keyStatus[0xD4].name = L"None";
keyStatus[0xD5].name = L"None";
keyStatus[0xD6].name = L"None";
keyStatus[0xD7].name = L"None";
keyStatus[0xD8].name = L"None";
keyStatus[0xD9].name = L"None";
keyStatus[0xDA].name = L"None";
keyStatus[0xDB].name = L"OEM-4{[";
keyStatus[0xDC].name = L"OEM-5|\\";
keyStatus[0xDD].name = L"OEM-6}]";
keyStatus[0xDE].name = L"OEM-7'\"";
keyStatus[0xDF].name = L"OEM-8";
keyStatus[0xE0].name = L"None";
keyStatus[0xE1].name = L"None";
keyStatus[0xE2].name = L"OEM-102<>";
keyStatus[0xE3].name = L"None";
keyStatus[0xE4].name = L"None";
keyStatus[0xE5].name = L"IME-ProcessKey";
keyStatus[0xE6].name = L"None";
keyStatus[0xE7].name = L"IME-Packet";
keyStatus[0xE8].name = L"None";
keyStatus[0xE9].name = L"None";
keyStatus[0xEA].name = L"None";
keyStatus[0xEB].name = L"None";
keyStatus[0xEC].name = L"None";
keyStatus[0xED].name = L"None";
keyStatus[0xEE].name = L"None";
keyStatus[0xEF].name = L"None";
keyStatus[0xF0].name = L"None";
keyStatus[0xF1].name = L"None";
keyStatus[0xF2].name = L"None";
keyStatus[0xF3].name = L"None";
keyStatus[0xF4].name = L"None";
keyStatus[0xF5].name = L"None";
keyStatus[0xF6].name = L"Attn";
keyStatus[0xF7].name = L"CrSel";
keyStatus[0xF8].name = L"ExSel";
keyStatus[0xF9].name = L"ErEof";
keyStatus[0xFA].name = L"Play";
keyStatus[0xFB].name = L"Zoom";
keyStatus[0xFC].name = L"None";
keyStatus[0xFD].name = L"PA1";
keyStatus[0xFE].name = L"OEM-Clear";
}
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); }
+240
View File
@@ -0,0 +1,240 @@
//
// 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;
};
struct Constants {
/**
* 非常重要的设置项。<br>
* 管理ui的缩放比例。
*/
double uiScale = 1;
double screenScale = 1;
unsigned int floatWindowBackground = 0xdd000000;
};
public:
Options options;
Options actual;
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();
+330
View File
@@ -0,0 +1,330 @@
#include "includes.h"
#include "utils/TestCode.h"
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);
}
void gameThread() {
try {
using namespace std::chrono;
using Time = time_point<system_clock>;
Time lastTick = system_clock::now();
while (isRunning) {
const Time thisTime = system_clock::now();
if (thisTime - lastTick < milliseconds(45)) {
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();
while (isRunning) {
const Time thisTime = system_clock::now();
if (thisTime - lastRender < milliseconds(12)) {
Sleep(1);
continue;
}
game.render();
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);
translator.initialize();
Logger.info(L"--------Program Start--------");
for (const auto& [addr, info] : 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);
int a = -40;
game.tasks.pushNewed(new Task([&a](Task& self) {
if (a) {
++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;
}));
SetWindowLongW(MainWindowHandle, GWL_EXSTYLE, GetWindowLongW(MainWindowHandle, GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(MainWindowHandle, 0xffffff, 0xe0, LWA_COLORKEY | LWA_ALPHA);
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();
{
game.initialize();
interactManager.initialize();
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] : memoryManager.allocated) { Logger.print(L" using", addr, info.size, L"B", info.msg); }
_wsystem(L"pause");
return static_cast<int>(msg.wParam);
}
struct Release {
Release() = default;
~Release() {
delete &gc;
Logger.put(L"--------- Last Check ---------\n");
for (const auto& [addr, info] : memoryManager.allocated) { Logger.print(L" using", addr, info.size, L"B", info.msg); }
delete &Logger;
}
} NEVER_REFERENCED_release;
+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();
+207
View File
@@ -0,0 +1,207 @@
//
// Created by EmsiaetKadosh on 25-1-14.
//
#pragma once
#include "..\def.h"
#include "..\utils\exception.h"
#include "..\utils\Task.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() 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 {
friend class Game;
friend class Font;
mutable List<HGDIOBJ> failed;
inline static BLENDFUNCTION blendFunction = {
.BlendOp = AC_SRC_OVER, // Only
.BlendFlags = 0, // Must 0
.SourceConstantAlpha = 255, // 预乘
.AlphaFormat = 0, // Not AC_SRC_ALPHA
};
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"); }
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 {
for (List<HGDIOBJ>::const_iterator iter = failed.cbegin(); iter != failed.cend(); ++iter)
if (!DeleteObject(*iter)) Logger.error(L"DeleteObject failed again. Deleting: " + std::to_wstring(reinterpret_cast<QWORD>(*iter)));
else failed.erase(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");
}
}
};
extern Renderer renderer;
+5
View File
@@ -0,0 +1,5 @@
//
// Created by EmsiaetKadosh on 25-1-21.
//
#include "TextureManager.h"
+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();
+8
View File
@@ -0,0 +1,8 @@
//
// Created by EmsiaetKadosh on 25-1-14.
//
#include "Hud.h"
void Hud::render() const noexcept {}
void Hud::tick() noexcept {}
+15
View File
@@ -0,0 +1,15 @@
//
// Created by EmsiaetKadosh on 25-1-14.
//
#pragma once
#include "..\render\Renderer.h"
class Hud final : public IRenderable, public ITickable {
public:
void render() 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 noexcept { for (const Widget* widget : widgets) widget->render(); }
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 noexcept {
renderer.fill(0, 0, renderer.getWidth(), interactSettings.actual.captionHeight, 0xff666666);
for (const Widget* widget : widgets) widget->render();
}
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() 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() 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 noexcept {
Widget::render();
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(); }
+262
View File
@@ -0,0 +1,262 @@
//
// 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() 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);
}
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() 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 noexcept override { for (const Window& i : *this) i.render(); }
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() 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() 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() 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 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();
}
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 noexcept override {
fontManager.getDefault().drawCenter(title.getRenderableString(), 0, 0, renderer.getWidth(), renderer.getHeight());
Window::render();
}
};
+157
View File
@@ -0,0 +1,157 @@
//
// Created by EmsiaetKadosh on 25-1-17.
//
#pragma once
#include "..\def.h"
[[nodiscard]] inline unsigned int wtouib16(const wchar* string) noexcept {
unsigned int ret = 0;
while (*string != L'\0') {
if (ret & 0xf0000000) return 0xffffffff;
if (*string >= L'0' && *string <= L'9') {
ret <<= 4;
ret += *string - L'0';
} else if (*string >= L'A' && *string <= L'F') {
ret <<= 4;
ret += *string - 0x41; // 'A' - 10
} else if (*string >= L'a' && *string <= L'f') {
ret <<= 4;
ret += *string - 0x61; // 'a' - 10
} else return 0xffffffff;
++string;
}
return ret;
}
[[nodiscard]] inline unsigned int wtouib16(const wchar* const string, const unsigned long long length) noexcept {
unsigned int ret = 0;
unsigned long long i = 0;
while (i < length) {
if (ret & 0xf0000000) return 0xffffffff;
if (string[i] >= L'0' && string[i] <= L'9') {
ret <<= 4;
ret += string[i] - L'0';
} else if (string[i] >= L'A' && string[i] <= L'F') {
ret <<= 4;
ret += string[i] - 55; // L'A' - 10
} else if (string[i] >= L'a' && string[i] <= L'f') {
ret <<= 4;
ret += string[i] - 87; // L'a' - 10
} else return 0xffffffff;
++i;
}
return ret;
}
static constexpr wchar Table16[17] = L"0123456789ABCDEF";
/**
* 将数字转换为字符串
* @param value 要转换的数字
* @param fills 填充位数。返回的字符串长度一定不小于该值
* @return String类型
*/
[[nodiscard]] inline String uitowb16(unsigned int value, const unsigned int fills = 1) noexcept {
String ret;
if (fills >= 8 || value < static_cast<unsigned int>(1) << fills * 4) {
ret.assign(fills, L'0');
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;
++i;
}
while (i < 8) {
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";
}
return ret;
}
[[nodiscard]] inline String qwtowb10(QWORD value, const unsigned int fills = 1) noexcept {
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
};
String ret;
if (value < Compare10[fills]) {
ret.assign(fills, L'0');
for (unsigned int i = fills - 1; i != 0 && value; --i) {
ret[i] = Table10[value % 10];
value /= 10;
}
} else {
unsigned int i = 0;
while (i < 19) {
if (value >= Compare10[i]) break;
++i;
}
while (i < 19) {
ret.push_back(Table10[value / Compare10[i]]);
value %= Compare10[i];
++i;
}
if (ret.empty()) ret = L"0";
}
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();
+598
View File
@@ -0,0 +1,598 @@
//
// 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() { clear(); }
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);
}
[[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 {
Logger.debug(L"getText called");
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); }
};
+7
View File
@@ -0,0 +1,7 @@
//
// Created by EmsiaetKadosh on 25-1-20.
//
#pragma once
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 << (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("main.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("main.cpp")]] gc = *new GarbageCollector();
+154
View File
@@ -0,0 +1,154 @@
//
// Created by EmsiaetKadosh on 25-3-21.
//
#pragma once
#include "..\warnings.h"
class Vector3D;
class Vector2D;
class 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 {
x += other.x;
y += other.y;
z += other.z;
return *this;
}
Vector3D& operator-=(const Vector3D& other) noexcept {
x -= other.x;
y -= other.y;
z -= other.z;
return *this;
}
Vector3D& operator*=(const double scalar) noexcept {
x *= scalar;
y *= scalar;
z *= scalar;
return *this;
}
Vector3D& operator/=(const double scalar) noexcept {
x /= scalar;
y /= scalar;
z /= scalar;
return *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;
}
[[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 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 {
x += other.x;
y += other.y;
return *this;
}
Vector2D& operator-=(const Vector2D& other) noexcept {
x -= other.x;
y -= other.y;
return *this;
}
Vector2D& operator*=(const double scalar) noexcept {
x *= scalar;
y *= scalar;
return *this;
}
Vector2D& operator/=(const double scalar) noexcept {
x /= scalar;
y /= scalar;
return *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;
}
[[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();
}
+9
View File
@@ -0,0 +1,9 @@
//
// Created by EmsiaetKadosh on 25-3-13.
//
#pragma once
#pragma warning(disable: 4554)
#pragma warning(default: 4555)
#pragma warning(disable: 5030)