world初步

This commit is contained in:
EmsiaetKadosh
2025-03-23 13:23:10 +08:00
parent 037e532748
commit a335259edc
27 changed files with 982 additions and 313 deletions
+14 -31
View File
@@ -21,39 +21,22 @@ enable_testing()
add_executable(${PROJECT_NAME} add_executable(${PROJECT_NAME}
src/main.cpp src/main.cpp
src/def.h
src/def.cpp
src/interact/InteractManager.h
src/interact/InteractManager.cpp
src/render/Renderer.h
src/render/Renderer.cpp
src/game/Game.h
src/game/Game.cpp
src/ui/Window.h
src/ui/Window.cpp
src/ui/Hud.h
src/ui/Hud.cpp
src/utils/IText.h
src/utils/IText.cpp
src/utils/exception.h
src/utils/exception.cpp
src/utils/File.h
src/utils/File.cpp
src/render/TextureManager.h
src/render/TextureManager.cpp
src/includes.h src/def.cpp
src/utils/Chars.h src/utils/File.cpp
src/ui/xWindows.h src/utils/exception.cpp
src/utils/TestCode.h
src/game/entity/Entity.h src/interact/InteractManager.cpp
src/game/entity/Player.h src/utils/IText.cpp
src/game/world/Location.cpp
src/render/Renderer.cpp
src/render/TextureManager.cpp
src/ui/Hud.cpp
src/ui/Window.cpp
src/game/Game.cpp
src/game/entity/Entity.cpp
src/game/entity/Player.cpp src/game/entity/Player.cpp
src/game/entity/Damage.h
src/utils/math.h
src/game/world/World.h
src/game/world/Location.h
src/game/world/Block.h
) )
set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
+1
View File
@@ -2,3 +2,4 @@ Module:
File(30%), including Log, Save, Config, Language File(30%), including Log, Save, Config, Language
Translator(30%), with File Translator(30%), with File
KeyBinding(20%) KeyBinding(20%)
World, Entity(10%), including gc problem
+22 -9
View File
@@ -4,8 +4,10 @@
#include "def.h" #include "def.h"
#include "utils\Chars.h" #include "utils\File.h"
#include "utils\exception.h" #include "utils\exception.h"
#include "utils\Chars.h"
#include "utils\gc.h"
template<TypeName Base> template<NewCopyable T> requires std::is_base_of_v<Base, T> && TypeName<T> template<TypeName Base> template<NewCopyable T> requires std::is_base_of_v<Base, T> && TypeName<T>
void ObjectHolder<Base>::set(const T& value) { void ObjectHolder<Base>::set(const T& value) {
@@ -28,20 +30,31 @@ void ObjectHolder<Base>::set(T&& value) {
void requireNonnull(const void* value) noexcept(false) { if (!value) throw NullPointerException(L"value is null"); } 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 checkAllocation(const void* value) noexcept(false) { if (!value) throw BadAllocationException(L"bad allocation"); }
void printAllocate(void* value, const size_t size, const String& msg) { void $LimitedUse::printAllocate(void* value, const size_t size, const String& msg) {
const String str = L"alloc " + ptrtow(reinterpret_cast<QWORD>(value)) + L" " + std::to_wstring(size) + String(L"B ") + msg; const String str = L"alloc " + ptrtow(reinterpret_cast<QWORD>(value)) + L" " + std::to_wstring(size) + String(L"B ") + msg;
Logger.log(str); Logger.log(str);
#if __CARLBEKS_MEMORY__ > 2
MainLogFile << str << std::endl;
#endif
} }
void printDeallocate(void* value, const size_t size, const String& msg) { void $LimitedUse::printDeallocate(void* value, const size_t size, const String& msg) {
const String str = L"dealloc " + ptrtow(reinterpret_cast<QWORD>(value)) + L" " + std::to_wstring(size) + String(L"B ") + msg; const String str = L"dealloc " + ptrtow(reinterpret_cast<QWORD>(value)) + L" " + std::to_wstring(size) + String(L"B ") + msg;
Logger.log(str); Logger.log(str);
#if __CARLBEKS_MEMORY__ > 2
MainLogFile << str << std::endl;
#endif
} }
void $LimitedUse::printDeallocateWarning(void* value, const String& msg) {
const String str = L"dealloc " + ptrtow(reinterpret_cast<QWORD>(value)) + L": " + msg;
Logger.error(str);
}
String ptrtow(const QWORD value) { return qwtowb16(value, 16); } String ptrtow(const QWORD value) { return qwtowb16(value, 16); }
namespace $LimitedUse {
Release::~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;
delete &memoryManager;
std::wcout << L"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
}
}
+60 -34
View File
@@ -5,7 +5,7 @@
#pragma once #pragma once
#define __CARLBEKS_DEBUG__ #define __CARLBEKS_DEBUG__
#define __CARLBEKS_MEMORY__ 1 #define __CARLBEKS_MEMORY__ 3
#pragma warning(disable: 4819) #pragma warning(disable: 4819)
@@ -29,10 +29,14 @@ using wchar = wchar_t;
using QWORD = unsigned long long int; using QWORD = unsigned long long int;
using String = std::wstring; using String = std::wstring;
using Thread = std::thread; 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 K, typename V, typename Cmp = std::less<K>, typename Alloc = std::allocator<std::pair<const K, V>>>
template<typename T, typename Comparator, typename Allocator = std::allocator<T>> using Set = std::set<T, Comparator, Allocator>; using Map = std::map<K, V, Cmp, Alloc>;
template<typename T, typename Allocator = std::allocator<T>> using List = std::list<T, Allocator>; template <typename T, typename Comparator = std::less<T>, typename Allocator = std::allocator<T>>
template<typename F> using Function = std::function<F>; 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 Success() { return 0; }
#define Failed() { return 1; } #define Failed() { return 1; }
@@ -65,61 +69,75 @@ template<typename F> using Function = std::function<F>;
#pragma comment(lib, "Uxtheme.lib") #pragma comment(lib, "Uxtheme.lib")
#pragma comment(lib, "winmm.lib") #pragma comment(lib, "winmm.lib")
template<typename T> template <typename T>
concept Copyable = requires(const T& t) { T(t); }; concept Copyable = requires(const T& t) { T(t); };
template<typename T> template <typename T>
concept NewCopyable = requires(const T& t) { delete new T(t); }; concept NewCopyable = requires(const T& t) { delete new T(t); };
template<typename T> template <typename T>
concept Moveable = requires(T&& t) { T(std::move(t)); }; concept Moveable = requires(T&& t) { T(std::move(t)); };
template<typename T> template <typename T>
concept NewMoveable = requires(T&& t) { delete new T(std::move(t)); }; concept NewMoveable = requires(T&& t) { delete new T(std::move(t)); };
template<typename T> template <typename T>
concept NonreferenceType = !std::is_reference_v<T>; concept NonreferenceType = !std::is_reference_v<T>;
template<typename T> template <typename T>
concept NonpointerType = !std::is_pointer_v<T>; concept NonpointerType = !std::is_pointer_v<T>;
template<typename T> template <typename T>
concept ReferenceType = std::is_reference_v<T>; concept ReferenceType = std::is_reference_v<T>;
template<typename T> template <typename T>
concept PointerType = std::is_pointer_v<T>; concept PointerType = std::is_pointer_v<T>;
template<typename T> template <typename T>
concept TypeName = NonreferenceType<T> && NonpointerType<T>; concept TypeName = NonreferenceType<T> && NonpointerType<T>;
namespace $LimitedUse {
struct Release {
Release() = default;
~Release();
} inline gcRelease_LoggerRelease_memoryManagerRelease;
} // namespace $LimitedUse
struct MemoryManager { struct MemoryManager {
struct MemoryInfo { struct MemoryInfo {
const String msg; const String msg;
std::size_t size; std::size_t size;
}; };
Map<void*, MemoryInfo> allocated {}; Map<void*, MemoryInfo> allocated{};
constexpr MemoryManager() noexcept = default; constexpr MemoryManager() noexcept = default;
} inline memoryManager; } inline& [[carlbeks::releasedat("def.cpp")]] memoryManager = *new MemoryManager;
void requireNonnull(const void* value) noexcept(false); void requireNonnull(const void* value) noexcept(false);
void checkAllocation(const void* value) noexcept(false); void checkAllocation(const void* value) noexcept(false);
inline String ptrtow(QWORD value); inline String ptrtow(QWORD value);
#if defined __CARLBEKS_DEBUG__ || defined __CARLBEKS_MEMORY__ #if defined __CARLBEKS_DEBUG__ || defined __CARLBEKS_MEMORY__
void printAllocate(void* value, std::size_t size, const String&); namespace $LimitedUse {
void printDeallocate(void* value, std::size_t size, const String&); void printAllocate(void* value, std::size_t size, const String&);
void printDeallocate(void* value, std::size_t size, const String&);
void printDeallocateWarning(void* value, const String& msg);
}
extern String atow(const char* chars); extern String atow(const char* chars);
template<typename T> T* allocatedFor$(T* value, const String& msg = L"", std::size_t size = sizeof(T)) { template <typename T>
T* allocatedFor$(T* value, const String& msg = L"", std::size_t size = sizeof(T)) {
requireNonnull(value); requireNonnull(value);
const auto& k = memoryManager.allocated.emplace(value, MemoryManager::MemoryInfo{ L"[" + atow(typeid(T).name()) + L"] " + msg , size}).first; const auto& k = memoryManager.allocated.emplace(value, MemoryManager::MemoryInfo{L"[" + atow(typeid(T).name()) + L"] " + msg, size}).first;
#if __CARLBEKS_MEMORY__ > 1 #if __CARLBEKS_MEMORY__ > 1
printAllocate(value, k->second.size, k->second.msg); $LimitedUse::printAllocate(value, k->second.size, k->second.msg);
#endif #endif
return value; return value;
} }
template<typename T> T* deallocating$(T* value) { template <typename T>
T* deallocating$(T* value, const String& stack) {
#if __CARLBEKS_MEMORY__ > 1 #if __CARLBEKS_MEMORY__ > 1
const MemoryManager::MemoryInfo* info = nullptr; const MemoryManager::MemoryInfo* info = nullptr;
if (memoryManager.allocated.contains(value)) info = &memoryManager.allocated.at(value); if (memoryManager.allocated.contains(value)) info = &memoryManager.allocated.at(value);
printDeallocate(value, info ? info->size : 0, info ? info->msg : L"???"); $LimitedUse::printDeallocate(value, info ? info->size : 0, info ? info->msg : L"???");
#endif #endif
if (value) memoryManager.allocated.erase(value); if (value) { if (!memoryManager.allocated.erase(value)) $LimitedUse::printDeallocateWarning(value, L"value not recorded" + stack); }
return value; return value;
} }
@@ -128,13 +146,18 @@ template<typename T> T* deallocating$(T* value) {
#else #else
#define allocatedFor(val, ...) allocatedFor$(val, L"" __VA_OPT__(,) __VA_ARGS__) #define allocatedFor(val, ...) allocatedFor$(val, L"" __VA_OPT__(,) __VA_ARGS__)
#endif #endif
#if __CARLBEKS_MEMORY__ > 2
#define deallocating(val) deallocating$(val, L"\n From " __FUNCSIG__ "\n At " __FILE__ ":" _STL_STRINGIZE(__LINE__))
#else
#define deallocating(val) deallocating$(val) #define deallocating(val) deallocating$(val)
#endif
#else #else
#define allocatedFor(val, ...) val #define allocatedFor(val, ...) val
#define deallocating(val) val #define deallocating(val) val
#endif #endif
template<TypeName Base> class ObjectHolder { template <TypeName Base>
class ObjectHolder {
Base* value; Base* value;
bool hasValue; bool hasValue;
char padding[7]{}; char padding[7]{};
@@ -147,10 +170,10 @@ public:
ObjectHolder(Base* value) : value(value), 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> 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) {} 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> 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(T&& value) : value(allocatedFor(new T(std::forward<T>(value)))), hasValue(true) {}
ObjectHolder(const ObjectHolder& other) noexcept: value(other.value), hasValue(false) {} ObjectHolder(const ObjectHolder& other) noexcept: value(other.value), hasValue(false) {}
@@ -160,10 +183,10 @@ public:
other.hasValue = false; other.hasValue = false;
} }
template<NewCopyable T> requires std::is_base_of_v<Base, T> && TypeName<T> template <NewCopyable T> requires std::is_base_of_v<Base, T> && TypeName<T>
void set(const T& value); void set(const T& value);
template<NewMoveable T> requires std::is_base_of_v<Base, T> && TypeName<T> template <NewMoveable T> requires std::is_base_of_v<Base, T> && TypeName<T>
void set(T&& value); void set(T&& value);
~ObjectHolder() { ~ObjectHolder() {
@@ -207,14 +230,16 @@ public:
bool operator!() const noexcept { return value == nullptr; } bool operator!() const noexcept { return value == nullptr; }
[[nodiscard]] bool isManager() const noexcept { return hasValue; } [[nodiscard]] bool isManager() const noexcept { return hasValue; }
template<typename T> ObjectHolder<T> referenceof(const T& other) { template <typename T>
ObjectHolder<T> referenceof(const T& other) {
ObjectHolder ret{}; ObjectHolder ret{};
ret.value = &other; ret.value = &other;
return ret; return ret;
} }
}; };
template<TypeName Base> class SynchronizedHolder { template <TypeName Base>
class SynchronizedHolder {
mutable Base* newValue = nullptr; mutable Base* newValue = nullptr;
mutable Base* value = nullptr; mutable Base* value = nullptr;
mutable bool isOk = false; mutable bool isOk = false;
@@ -223,7 +248,8 @@ public:
SynchronizedHolder() = default; SynchronizedHolder() = default;
~SynchronizedHolder() { ~SynchronizedHolder() {
if (newValue == value) { if (value) delete deallocating(value); } else { if (newValue == value) { if (value) delete deallocating(value); }
else {
if (newValue) delete deallocating(newValue); if (newValue) delete deallocating(newValue);
if (value) delete deallocating(value); if (value) delete deallocating(value);
} }
@@ -232,14 +258,14 @@ public:
} }
template<NewCopyable T> requires std::is_base_of_v<Base, T> && TypeName<T> template <NewCopyable T> requires std::is_base_of_v<Base, T> && TypeName<T>
void setNew(const Base& other) noexcept { void setNew(const Base& other) noexcept {
isOk = false; isOk = false;
if (newValue && newValue != value) deleteNew(); if (newValue && newValue != value) deleteNew();
newValue = allocatedFor(new T(other)); newValue = allocatedFor(new T(other));
} }
template<NewMoveable T> requires std::is_base_of_v<Base, T> && TypeName<T> template <NewMoveable T> requires std::is_base_of_v<Base, T> && TypeName<T>
void setNew(T&& val) noexcept { void setNew(T&& val) noexcept {
isOk = false; isOk = false;
if (newValue && newValue != value) deleteNew(); if (newValue && newValue != value) deleteNew();
+38 -1
View File
@@ -4,12 +4,15 @@
#include "Game.h" #include "Game.h"
#include "entity\Entity.h"
#include "world\World.h" #include "world\World.h"
#include "..\ui\xWindows.h" #include "..\ui\xWindows.h"
void Game::initialize() { void Game::initialize() {
worldManager = allocatedFor(new WorldManager); worldManager = allocatedFor(new WorldManager);
game.setWindow(StartWindow::create()); entityManager = allocatedFor(new EntityManager);
setWindow(StartWindow::create());
windows.onResize();
} }
Game::Game() : caption{ allocatedFor(new CaptionWindow()) }, floatWindow{ allocatedFor(new FloatWindow()) } { Game::Game() : caption{ allocatedFor(new CaptionWindow()) }, floatWindow{ allocatedFor(new FloatWindow()) } {
@@ -20,8 +23,42 @@ Game::Game() : caption{ allocatedFor(new CaptionWindow()) }, floatWindow{ alloca
Game::~Game() { Game::~Game() {
setWindow(nullptr); setWindow(nullptr);
delete deallocating(floatWindow); delete deallocating(floatWindow);
gc.pack();
gc.collect();
delete deallocating(caption); delete deallocating(caption);
gc.pack();
gc.collect();
delete deallocating(worldManager); delete deallocating(worldManager);
gc.pack();
gc.collect();
delete deallocating(entityManager);
gc.pack();
gc.collect();
}
void Game::render(const double tickDelta) const noexcept {
if (renderer.checkResizing()) return;
renderer.gameStartRender();
if (worldManager->current) worldManager->current->render(tickDelta);
caption->render(tickDelta);
hud.render(tickDelta);
windows.render(tickDelta);
floatWindow->render(tickDelta);
renderer.gameEndRender();
gc.pack();
}
void Game::tick() noexcept {
++currentTick;
if (worldManager->current) worldManager->current->tick();
floatWindow->clear();
floatWindow->tick();
caption->tick();
hud.tick();
windows.tick();
floatWindow->update();
tasks.runAll();
gc.collect();
} }
inline Game game = Game(); inline Game game = Game();
+7 -26
View File
@@ -10,6 +10,7 @@
#include "../ui/Window.h" #include "../ui/Window.h"
class [[carlbeks::predecl, carlbeks::defineat("World.h")]] WorldManager; class [[carlbeks::predecl, carlbeks::defineat("World.h")]] WorldManager;
class [[carlbeks::predecl, carlbeks::defineat("Entity.h")]] EntityManager;
class Game final /* : public IRenderable, public ITickable */ { class Game final /* : public IRenderable, public ITickable */ {
friend void gameThread(); friend void gameThread();
@@ -23,24 +24,17 @@ public:
TaskScheduler tasks; // 8 TaskScheduler tasks; // 8
std::minstd_rand random; std::minstd_rand random;
WorldManager* worldManager = nullptr; WorldManager* worldManager = nullptr;
EntityManager* entityManager = nullptr;
void initialize(); void initialize();
Game(); Game();
~Game(); ~Game();
[[nodiscard]] QWORD getTick() const noexcept { return currentTick; }
int closeWindow(Window* const window) noexcept { return windows.pop(window); } int closeWindow(Window* const window) noexcept { return windows.pop(window); }
[[nodiscard]] FloatWindow& getFloatWindow() const noexcept { return *floatWindow; }
void render() const noexcept { [[nodiscard]] QWORD getTick() const noexcept { return currentTick; }
if (renderer.checkResizing()) return; void tick() noexcept;
renderer.gameStartRender(); void render(double tickDelta) const noexcept;
caption->render();
hud.render();
windows.render();
floatWindow->render();
renderer.gameEndRender();
gc.pack();
}
/** /**
* 所有窗口都提交给Game保管,在适当时刻自动删除。 * 所有窗口都提交给Game保管,在适当时刻自动删除。
@@ -59,25 +53,12 @@ public:
Success(); Success();
} }
[[nodiscard]] FloatWindow& getFloatWindow() const noexcept { return *floatWindow; }
[[nodiscard]] Window* getWindow() const noexcept { [[nodiscard]] Window* getWindow() const noexcept {
if (auto *const back = windows.back()) return dynamic_cast<Window*>(back); if (auto* const back = windows.back()) return dynamic_cast<Window*>(back);
Logger.error(L"Game::getWindow returns nullptr"); Logger.error(L"Game::getWindow returns nullptr");
return 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() { void handleResize() {
caption->onResize(); caption->onResize();
+5
View File
@@ -0,0 +1,5 @@
//
// Created by EmsiaetKadosh on 25-3-22.
//
#include "Entity.h"
+52 -23
View File
@@ -5,11 +5,15 @@
#pragma once #pragma once
#include "..\..\utils\math.h" #include "..\..\utils\math.h"
#include "..\..\render\Renderer.h"
#include "..\world\Location.h" #include "..\world\Location.h"
#include "Damage.h" #include "Damage.h"
class Entity; class Entity;
class World; class [[carlbeks::predecl, carlbeks::defineat("World.h")]] World;
class EntityManager;
using EntityID = QWORD;
interface IDamageable { interface IDamageable {
protected: protected:
@@ -22,40 +26,65 @@ public:
virtual void onDeath() = 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 { interface IArtificialIntelligent {
protected: protected:
virtual ~IArtificialIntelligent() = default; virtual ~IArtificialIntelligent() = default;
virtual void aiProcess() {} virtual void aiProcess() {}
}; };
class Entity { class Entity : public IRenderable, public ITickable {
friend class World; friend class World;
Location location; friend class EntityManager;
QWORD id = 0; EntityID idEntity = 0;
World* world = nullptr;
protected: protected:
Entity(const Location& location) : location(location) {} Location location;
Entity(Location&& location) : location(std::move(location)) {} Vector2D velocity;
virtual ~Entity() = default; double maxSpeed = 1.0;
Entity(const Vector2D& location) : location(location) {}
~Entity() override = default;
public: public:
virtual void tick() {} virtual void onRemove() = 0;
virtual void onRemove() {} virtual void onEnterWorld(World* world, WorldTransportReason reason) {}
virtual void onExitWorld(World* world, WorldTransportReason reason) {}
void setVelocity(const Vector2D& velocity) noexcept { this->velocity = velocity; }
[[nodiscard]] const Location& getLocation() const noexcept { return this->location; }
[[nodiscard]] const Location& getLocation(const double tickDelta) const noexcept { return Location(this->location.getPosition() + this->velocity * tickDelta, this->location.getWorld()); }
[[nodiscard]] Vector2D getVelocity() const noexcept { return this->velocity; }
[[nodiscard]] double getMaxSpeed() const noexcept { return this->maxSpeed; }
}; };
class Enemy : public Entity, public IDamageable, public IMoveable, public IArtificialIntelligent { class Enemy : public Entity, public IDamageable, public IArtificialIntelligent {
protected: protected:
Enemy(const Location& location) : Entity(location) {} Enemy(const Vector2D& location) : Entity(location) {}
Enemy(Location&& location) : Entity(std::move(location)) {} };
class EntityManager {
friend class Game;
EntityID nextID = 0;
Map<EntityID, Entity*> entities;
EntityManager() = default;
~EntityManager() { for (auto& [id, entity] : entities) { entity->onRemove(); } }
public:
int addEntity(Entity* entity) {
if (!entity) Failed();
if (entity->idEntity) Failed();
entity->idEntity = ++nextID;
entities.emplace(entity->idEntity, entity);
Success();
}
int removeEntity(Entity* entity) {
if (!entity) Failed();
if (entity->idEntity) Failed();
if (entity->getLocation().getWorld()) Failed(); // 确保必须已经从其他世界移除
entities.erase(entity->idEntity);
entity->onRemove();
Success();
}
}; };
+2 -3
View File
@@ -6,8 +6,7 @@
#include "Entity.h" #include "Entity.h"
class Player : public Entity, public IMoveable, public IDamageable { class Player : public Entity, public IDamageable {
public: public:
Player(const Location& location) : Entity(location) {} Player(const Vector2D& location) : Entity(location) {}
Player(Location&& location) : Entity(std::move(location)) {}
}; };
+33 -2
View File
@@ -4,9 +4,40 @@
#pragma once #pragma once
#include "..\..\def.h"
#include "..\..\utils\gc.h"
#include "Location.h" #include "Location.h"
class Block { class [[carlbeks::predecl, carlbeks::defineat("World.h")]] World;
public:
class Block : public IRenderable, public ITickable {
friend class Garbage<Block>;
friend class World;
World* world = nullptr;
BlockLocation location;
public:
Block(const BlockLocation& location) : location(location) { this->location.setWorld(0); }
[[nodiscard]] const BlockLocation& getLocation() const { return location; }
virtual void onEnterWorld(World* world, WorldTransportReason reason) {}
virtual void onExitWorld(World* world, WorldTransportReason reason) {}
virtual void onRemove() { gc.submit<Block>(this); }
/**
* 渲染方块影。方块影会覆盖所有的方块。
*/
virtual void renderShadow() const noexcept {}
/**
* 渲染方块本体。渲染范围不应当超过方块占据的范围。
*/
void render(double tickDelta) const noexcept override = 0;
};
class PureBarrierBlock final : public Block {
PureBarrierBlock(const BlockLocation& location) : Block(location) {}
~PureBarrierBlock() override {}
public:
void render(double tickDelta) const noexcept override { renderer.fillWorldBlock(getLocation(), 0xffeeeeee); }
void tick() noexcept override {}
static PureBarrierBlock* create(const BlockLocation& location) { return allocatedFor(new PureBarrierBlock(location)); }
}; };
+12
View File
@@ -0,0 +1,12 @@
//
// Created by EmsiaetKadosh on 25-3-23.
//
#include "Location.h"
inline const WorldTransportReason WorldTransportReason::Shutdown = *manager.create(L"game shutdown", true, true);
inline const WorldTransportReason WorldTransportReason::InitialGeneration = *manager.create(L"initial generation", true, true);
inline const WorldTransportReason WorldTransportReason::WorldCollapse = *manager.create(L"world collapse", true, true);
inline const WorldTransportReason WorldTransportReason::BlockBreak = *manager.create(L"block break", true, false);
inline const WorldTransportReason WorldTransportReason::BlockReplace = *manager.create(L"block replace", true, false);
inline const WorldTransportReason WorldTransportReason::EntityTeleport = *manager.create(L"entity teleport", false, true);
+118 -17
View File
@@ -9,34 +9,135 @@
using WorldID = QWORD; using WorldID = QWORD;
class Location; class Location;
class BlockLocation; class BlockLocation;
class [[carlbeks::predecl, carlbeks::defineat("World.h")]] World;
class Location { /**
* 当一个方块/实体被移入/移出世界时,需要有一个理由。
* 如果方块移出时附带内置的Shutdown理由,那么方块在移出时会被立刻自动submit至gc。
* Shutdown理由的含义是,世界管理器下的所有世界内的所有方块,都会被依次附带Shutdown理由移出世界。
* 此过程中不可以进行其他的方块WorldTransport行为(因为正在遍历方块),也不需要手动清除其他的关联方块。
* 禁止在处理附带Shutdown理由的移出世界事件时提交自身给gc。但是方块自身的其他垃圾可能需要手动清理。
*
* 该类视同enum类。元素只允许引用访问,不允许复制、移动。
*/
class WorldTransportReason final {
friend class World;
struct LessReason;
class Reason final {
friend struct LessReason;
friend class WorldTransportReason;
const String description;
const bool isBlockReason, isEntityReason;
~Reason() = default;
public:
Reason(const String& description, const bool isBlockReason, const bool isEntityReason) : description(description), isBlockReason(isBlockReason), isEntityReason(isEntityReason) {}
Reason(String&& description, const bool isBlockReason, const bool isEntityReason) : description(std::move(description)), isBlockReason(isBlockReason), isEntityReason(isEntityReason) {}
Reason(const Reason&) = delete;
Reason(Reason&&) = delete;
Reason& operator=(const Reason&) = delete;
Reason& operator=(Reason&&) = delete;
};
struct LessReason {
using is_transparent = String;
[[nodiscard]] bool operator()(const Reason* const lhs, const Reason* const rhs) const { return lhs->description < rhs->description; }
[[nodiscard]] bool operator()(const String& str, const Reason* const rhs) const { return str < rhs->description; }
[[nodiscard]] bool operator()(const Reason* const lhs, const String& str) const { return lhs->description < str; }
};
struct Manager {
Set<Reason*, LessReason> reasons;
~Manager() { for (Reason* r : reasons) delete deallocating(r); }
Reason* create(const String& description, const bool isBlockReason, const bool isEntityReason) {
if (const Set<Reason*, LessReason>::const_iterator iter = reasons.find(description); iter != reasons.end()) return iter.operator*();
Reason* r = allocatedFor(new Reason(description, isBlockReason, isEntityReason));
reasons.emplace(r);
return r;
}
Reason* create(String&& description, const bool isBlockReason, const bool isEntityReason) {
if (const Set<Reason*, LessReason>::const_iterator iter = reasons.find(description); iter != reasons.end()) return iter.operator*();
Reason* r = allocatedFor(new Reason(std::move(description), isBlockReason, isEntityReason));
reasons.emplace(r);
return r;
}
} inline static manager;
static const WorldTransportReason Shutdown;
const Reason* reason;
WorldTransportReason(const Reason& reason) : reason(&reason) {}
public:
static const WorldTransportReason InitialGeneration;
static const WorldTransportReason WorldCollapse;
static const WorldTransportReason BlockBreak;
static const WorldTransportReason BlockReplace;
static const WorldTransportReason EntityTeleport;
WorldTransportReason() = delete;
WorldTransportReason(const WorldTransportReason&) = default;
WorldTransportReason(WorldTransportReason&&) = default;
WorldTransportReason& operator=(const WorldTransportReason&) = default;
WorldTransportReason& operator=(WorldTransportReason&&) = default;
~WorldTransportReason() = default;
[[nodiscard]] bool operator==(const WorldTransportReason& other) const noexcept { return reason == other.reason; }
[[nodiscard]] bool operator!=(const WorldTransportReason& other) const noexcept { return reason != other.reason; }
[[nodiscard]] bool isBlockReason() const noexcept { return reason->isBlockReason; }
[[nodiscard]] bool isEntityReason() const noexcept { return reason->isEntityReason; }
static WorldTransportReason registerReason(const String& description, const bool isBlockReason, const bool isEntityReason) { return *manager.create(description, isBlockReason, isEntityReason); }
static WorldTransportReason registerReason(String&& description, const bool isBlockReason, const bool isEntityReason) { return *manager.create(std::move(description), isBlockReason, isEntityReason); }
};
class [[carlbeks::TriviallyCopyable]] Location {
Vector2D position; Vector2D position;
WorldID idWorld; WorldID idWorld;
public: public:
Location(const Vector2D& position, const WorldID idWorld) : position(position), idWorld(idWorld) {} Location(const Vector2D& position) noexcept : position(position), idWorld(0) {}
Location(Vector2D&& position, const WorldID idWorld) : position(position), idWorld(idWorld) {} Location(const Vector2D& position, const WorldID idWorld) noexcept : position(position), idWorld(idWorld) {}
[[nodiscard]] Vector2D getPosition() const { return position; } Location(const Location& other) noexcept = default;
[[nodiscard]] WorldID getWorld() const { return idWorld; } Location(Location&& other) noexcept = default;
[[nodiscard]] double getX() const { return position.getX(); } [[nodiscard]] Vector2D getPosition() const noexcept { return position; }
[[nodiscard]] double getY() const { return position.getY(); } [[nodiscard]] WorldID getWorld() const noexcept { return idWorld; }
[[nodiscard]] BlockLocation getBlockLocation() const; [[nodiscard]] double getX() const noexcept { return position.getX(); }
[[nodiscard]] double getY() const noexcept { return position.getY(); }
[[nodiscard]] BlockLocation getBlockLocation() const noexcept;
void setPosition(const Vector2D& vector) noexcept { position = vector; }
void setWorld(const WorldID idWorld) noexcept { this->idWorld = idWorld; }
}; };
class BlockLocation { class [[carlbeks::TriviallyCopyable]] BlockLocation {
long long x, y; long long x, y;
WorldID idWorld; WorldID idWorld;
public: public:
BlockLocation(const long long x, const long long y, const WorldID idWorld) : x(x), y(y), idWorld(idWorld) {} BlockLocation(const long long x, const long long y) noexcept : x(x), y(y), idWorld(0) {}
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) {} BlockLocation(const Vector2D& position) noexcept : x(static_cast<long long>(position.getX())), y(static_cast<long long>(position.getY())), idWorld(0) {}
[[nodiscard]] Vector2D getPosition() const { return Vector2D(static_cast<double>(x), static_cast<double>(y)); } BlockLocation(const long long x, const long long y, const WorldID idWorld) noexcept : x(x), y(y), idWorld(idWorld) {}
[[nodiscard]] WorldID getWorld() const { return idWorld; } BlockLocation(const Vector2D& position, const WorldID idWorld) noexcept : x(static_cast<long long>(std::floor(position.getX()))), y(static_cast<long long>(std::floor(position.getY()))), idWorld(idWorld) {}
[[nodiscard]] long long getX() const { return x; } BlockLocation(const BlockLocation& other) noexcept = default;
[[nodiscard]] long long getY() const { return y; } BlockLocation(BlockLocation&& other) noexcept = default;
[[nodiscard]] Location toLocation() const { return Location({ static_cast<double>(x), static_cast<double>(y) }, idWorld); } [[nodiscard]] Vector2D getPosition() const noexcept { return Vector2D(static_cast<double>(x), static_cast<double>(y)); }
[[nodiscard]] WorldID getWorld() const noexcept { return idWorld; }
[[nodiscard]] long long getX() const noexcept { return x; }
[[nodiscard]] long long getY() const noexcept { return y; }
[[nodiscard]] Location toLocation() const noexcept { return Location({ static_cast<double>(x), static_cast<double>(y) }, idWorld); }
void setPosition(const long long x, const long long y) noexcept { this->x = x, this->y = y; }
void setWorld(const WorldID idWorld) noexcept { this->idWorld = idWorld; }
struct Less {
bool operator()(const BlockLocation& lhs, const BlockLocation& rhs) const noexcept;
};
}; };
inline BlockLocation Location::getBlockLocation() const { return BlockLocation(position, idWorld); } inline bool BlockLocation::Less::operator()(const BlockLocation& lhs, const BlockLocation& rhs) const noexcept { return lhs.y < rhs.y || (lhs.y == rhs.y && lhs.x < rhs.x); }
inline BlockLocation Location::getBlockLocation() const noexcept { return BlockLocation(position, idWorld); }
+154 -13
View File
@@ -6,34 +6,175 @@
#include "..\..\def.h" #include "..\..\def.h"
#include "..\entity\Entity.h" #include "..\entity\Entity.h"
#include "Block.h"
class World { class World;
QWORD idEntity = 0; class WorldManager;
class World : public IRenderable, public ITickable {
friend class WorldManager;
friend class Garbage<World>;
WorldID idWorld;
Map<QWORD, Entity*> entities; Map<QWORD, Entity*> entities;
Map<BlockLocation, Block*, BlockLocation::Less> blocks;
using IterEntity = Map<QWORD, Entity*>::const_iterator; using IterEntity = Map<QWORD, Entity*>::const_iterator;
using IterBlock = Map<BlockLocation, Block*, BlockLocation::Less>::const_iterator;
public: protected:
World() = default; World() = default;
int addEntity(Entity* entity) { ~World() override {
Logger.debug(L"~World() called");
for (auto& [location, block] : blocks) {
block->onExitWorld(this, WorldTransportReason::Shutdown);
block->location.setWorld(0);
block->world = nullptr;
block->onRemove();
}
for (auto& [id, entity] : entities) {
entity->onExitWorld(this, WorldTransportReason::Shutdown);
entity->location.setWorld(0);
entity->world = nullptr;
// entity->onRemove() 交给EntityManager调用
}
}
public:
void render(const double tickDelta) const noexcept override {
for (const auto& [location, block] : blocks) block->render(tickDelta);
for (const auto& [id, entity] : entities) entity->render(tickDelta);
for (const auto& [location, block] : blocks) block->renderShadow();
}
virtual int addEntity(Entity* entity, const WorldTransportReason reason) {
if (!entity) Failed(); if (!entity) Failed();
if (entity->id) Failed(); if (!entity->idEntity) Failed();
entities.emplace(++idEntity, entity); if (entity->location.getWorld()) Failed();
if (entity->world) Failed();
if (!reason.isEntityReason()) Failed();
entity->onEnterWorld(this, reason);
entity->location.setWorld(idWorld);
entity->world = this;
entities.emplace(entity->idEntity, entity);
Success(); Success();
} }
int removeEntity(Entity* entity) { virtual int removeEntity(Entity* entity, const WorldTransportReason reason) {
if (!entity) Failed(); if (!entity) Failed();
if (!entity->id) Failed(); if (!entity->idEntity) Failed();
const IterEntity iter = entities.find(entity->id); if (!entities.erase(entity->idEntity)) Failed();
if (iter != entities.end()) Failed(); if (!reason.isEntityReason()) Failed();
entities.erase(iter); entity->onExitWorld(this, reason);
entity->onRemove(); entity->location.setWorld(0);
entity->id = 0; entity->world = nullptr;
Success(); Success();
} }
virtual int addBlock(Block* block, const WorldTransportReason reason) {
if (!block) Failed();
if (block->getLocation().getWorld()) Failed();
if (block->world) Failed();
if (blocks.contains(block->getLocation())) Failed();
if (!reason.isBlockReason()) Failed();
blocks.emplace(block->getLocation(), block);
block->onEnterWorld(this, reason);
block->location.setWorld(idWorld);
block->world = this;
Success();
}
virtual int removeBlock(Block* block, const WorldTransportReason reason) {
if (!block) Failed();
if (block->getLocation().getWorld() != idWorld) Failed();
if (!blocks.erase(block->getLocation())) Failed();
if (!reason.isBlockReason()) Failed();
block->onExitWorld(this, reason);
block->location.setWorld(0);
block->world = nullptr;
Success();
}
virtual int removeBlockAt(const BlockLocation& location, const WorldTransportReason reason) {
if (!reason.isBlockReason()) Failed();
if (location.getWorld() != idWorld) Failed();
const IterBlock it = blocks.find(location);
if (it == blocks.cend()) Failed();
Block* block = it->second;
blocks.erase(it);
block->onExitWorld(this, reason);
block->location.setWorld(0);
block->world = nullptr;
Success();
}
virtual void onRemove() {
// Entity不需要再此处删除,交给EntityManager管理
for (auto& [id, entity] : entities) {
entity->onExitWorld(this, WorldTransportReason::WorldCollapse);
entity->location.setWorld(0);
entity->world = nullptr;
}
for (auto& [location, block] : blocks) {
block->onExitWorld(this, WorldTransportReason::WorldCollapse);
block->location.setWorld(0);
block->world = nullptr;
block->onRemove();
}
gc.submit<World>(this);
}
}; };
class WorldManager { class WorldManager {
friend class Game;
WorldID nextID = 0;
Map<WorldID, World*> worlds;
World* current = nullptr;
WorldManager() = default;
~WorldManager() {
Logger.debug(L"~WorldManager() called");
for (auto& [id, world] : worlds) world->onRemove();
}
public: public:
int addWorld(World* world) {
if (!world) Failed();
if (world->idWorld) Failed();
world->idWorld = ++nextID;
worlds.emplace(world->idWorld, world);
Success();
}
int removeWorld(World* world) {
if (!world) Failed();
if (!world->idWorld) Failed();
world->idWorld = 0;
worlds.erase(world->idWorld);
world->onRemove();
Success();
}
int setWorld(World* world) {
if (!world) Failed();
if (world->idWorld) Failed();
current = world;
Success();
}
};
class StartWorld final : public World {
StartWorld() = default;
~StartWorld() override { Logger.debug(L"~StartWorld() called"); }
public:
void tick() noexcept override {}
static StartWorld* create() {
StartWorld* world = allocatedFor(new StartWorld);
world->addBlock(PureBarrierBlock::create(BlockLocation(0, 0)), WorldTransportReason::InitialGeneration);
world->addBlock(PureBarrierBlock::create(BlockLocation(1, 0)), WorldTransportReason::InitialGeneration);
world->addBlock(PureBarrierBlock::create(BlockLocation(2, 0)), WorldTransportReason::InitialGeneration);
world->addBlock(PureBarrierBlock::create(BlockLocation(3, 0)), WorldTransportReason::InitialGeneration);
world->addBlock(PureBarrierBlock::create(BlockLocation(4, 0)), WorldTransportReason::InitialGeneration);
return world;
}
}; };
+2 -4
View File
@@ -16,10 +16,8 @@ inline HRESULT RemoveDefaultCaption(const HWND hWnd, const MARGINS* p) noexcept
inline bool ShowConsoleIO() noexcept { inline bool ShowConsoleIO() noexcept {
AllocConsole(); AllocConsole();
FILE* pCout; freopen("CONOUT$", "w", stdout);
freopen_s(&pCout, "CONOUT$", "w", stdout); freopen("CONOUT$", "r+", stdin);
FILE* pCin;
freopen_s(&pCin, "CONOUT$", "r+", stdin);
return true; return true;
} }
+7 -6
View File
@@ -8,20 +8,21 @@
// Any include // Any include
#include "def.h" #include "def.h"
#include "utils\math.h" #include "utils\math.h" // required by Location.h
#include "utils\File.h" #include "utils\File.h"
#include "utils\exception.h" #include "utils\exception.h"
#include "utils\Chars.h"
// utils serialized // utils serialized
#include "utils\gc.h" #include "utils\gc.h" // required by utils.h
#include "utils\utils.h" #include "utils\utils.h" // required by Task.h
#include "hbp.h"
#include "utils\Chars.h"
#include "utils\Task.h" #include "utils\Task.h"
#include "hbp.h"
// game basic // game basic
#include "interact\InteractManager.h" #include "interact\InteractManager.h"
#include "utils\IText.h" #include "utils\IText.h"
#include "game\world\Location.h" // required by Renderer.h
#include "render\Renderer.h" #include "render\Renderer.h"
#include "game\Animation.h" #include "game\Animation.h"
#include "render\TextureManager.h" #include "render\TextureManager.h"
@@ -33,7 +34,7 @@
// game extension // game extension
#include "ui\xWindows.h" #include "ui\xWindows.h"
#include "game\world\Location.h"
#include "game\entity\Entity.h" #include "game\entity\Entity.h"
#include "game\world\Block.h"
#include "game\world\World.h" #include "game\world\World.h"
#include "game\entity\Player.h" #include "game\entity\Player.h"
+6 -2
View File
@@ -206,6 +206,7 @@ class InteractSettings {
int marginWidth = 40; int marginWidth = 40;
int fontHeight = 96; int fontHeight = 96;
int floatWindowMargin = 16; int floatWindowMargin = 16;
double mapScale = 32.0; // 1格表现为32像素
}; };
struct Constants { struct Constants {
@@ -215,12 +216,15 @@ class InteractSettings {
*/ */
double uiScale = 1; double uiScale = 1;
double screenScale = 1; double screenScale = 1;
double smoothCamera = 0.5; // 相机平滑度。为0,始终瞬时设置相机的位置;为1,相机不动。
long long msPerTick = 50;
long long msPerRender = 16;
unsigned int floatWindowBackground = 0xdd000000; unsigned int floatWindowBackground = 0xdd000000;
}; };
public: public:
Options options; Options options; // 用户设置值
Options actual; Options actual; // 适应Scale后的实际值
Constants constants; Constants constants;
InteractSettings& setUiScale(const double scale) noexcept { InteractSettings& setUiScale(const double scale) noexcept {
+279 -16
View File
@@ -1,6 +1,271 @@
#include "includes.h" #include "includes.h"
#include "utils/TestCode.h" #include "utils\TestCode.h"
long __stdcall UnhandledExceptionHandler(PEXCEPTION_POINTERS exception) {
isRunning = false;
Logger.error(L"Unhandled exception handler called");
const auto ContextRecord = exception->ContextRecord;
const auto ExceptionRecord = exception->ExceptionRecord;
Logger.print(
std::hex,
L"Register parameter home addresses:"
L"\n P1Home", ContextRecord->P1Home,
L"\n P2Home", ContextRecord->P2Home,
L"\n P3Home", ContextRecord->P3Home,
L"\n P4Home", ContextRecord->P4Home,
L"\n P5Home", ContextRecord->P5Home,
L"\n P6Home", ContextRecord->P6Home);
Logger.print(
L"Control Flags:"
L"\n ContextFlags", ContextRecord->ContextFlags,
L"\n MxCsr", ContextRecord->MxCsr);
Logger.print(
L"Segment Registers and processor flags:"
L"\n SegCs", ContextRecord->SegCs,
L"\n SegDs", ContextRecord->SegDs,
L"\n SegEs", ContextRecord->SegEs,
L"\n SegFs", ContextRecord->SegFs,
L"\n SegGs", ContextRecord->SegGs,
L"\n SegSs", ContextRecord->SegSs,
L"\n EFlags", ContextRecord->EFlags);
Logger.print(
L"Debug registers:"
L"\n Dr0", ContextRecord->Dr0,
L"\n Dr1", ContextRecord->Dr1,
L"\n Dr2", ContextRecord->Dr2,
L"\n Dr3", ContextRecord->Dr3,
L"\n Dr6", ContextRecord->Dr6,
L"\n Dr7", ContextRecord->Dr7);
Logger.print(
L"Integer registers:"
L"\n Rax", ContextRecord->Rax,
L"\n Rcx", ContextRecord->Rcx,
L"\n Rdx", ContextRecord->Rdx,
L"\n Rbx", ContextRecord->Rbx,
L"\n Rsp", ContextRecord->Rsp,
L"\n Rbp", ContextRecord->Rbp,
L"\n Rsi", ContextRecord->Rsi,
L"\n Rdi", ContextRecord->Rdi,
L"\n R8", ContextRecord->R8,
L"\n R9", ContextRecord->R9,
L"\n R10", ContextRecord->R10,
L"\n R11", ContextRecord->R11,
L"\n R12", ContextRecord->R12,
L"\n R13", ContextRecord->R13,
L"\n R14", ContextRecord->R14,
L"\n R15", ContextRecord->R15);
Logger.print(
L"Program Counter:"
L"\n Rip", ContextRecord->Rip);
Logger.print(
L"Floating point state:"
L"\n XMM_SAVE_AREA32:"
L"\n ControlWord", ContextRecord->FltSave.ControlWord,
L"\n StatusWord", ContextRecord->FltSave.StatusWord,
L"\n TagWord", ContextRecord->FltSave.TagWord,
L"\n Reserved1", ContextRecord->FltSave.Reserved1,
L"\n ErrorOpcode", ContextRecord->FltSave.ErrorOpcode,
L"\n ErrorOffset", ContextRecord->FltSave.ErrorOffset,
L"\n ErrorSelector", ContextRecord->FltSave.ErrorSelector,
L"\n Reserved2", ContextRecord->FltSave.Reserved2,
L"\n DataOffset", ContextRecord->FltSave.DataOffset,
L"\n DataSelector", ContextRecord->FltSave.DataSelector,
L"\n Reserved3", ContextRecord->FltSave.Reserved3,
L"\n MxCsr", ContextRecord->FltSave.MxCsr,
L"\n MxCsr_Mask", ContextRecord->FltSave.MxCsr_Mask);
Logger.print(
L"\n FloatRegisters[0]",
ContextRecord->FltSave.FloatRegisters[0].High,
ContextRecord->FltSave.FloatRegisters[0].Low,
L"\n FloatRegisters[1]",
ContextRecord->FltSave.FloatRegisters[1].High,
ContextRecord->FltSave.FloatRegisters[1].Low,
L"\n FloatRegisters[2]",
ContextRecord->FltSave.FloatRegisters[2].High,
ContextRecord->FltSave.FloatRegisters[2].Low,
L"\n FloatRegisters[3]",
ContextRecord->FltSave.FloatRegisters[3].High,
ContextRecord->FltSave.FloatRegisters[3].Low,
L"\n FloatRegisters[4]",
ContextRecord->FltSave.FloatRegisters[4].High,
ContextRecord->FltSave.FloatRegisters[4].Low,
L"\n FloatRegisters[5]",
ContextRecord->FltSave.FloatRegisters[5].High,
ContextRecord->FltSave.FloatRegisters[5].Low,
L"\n FloatRegisters[6]",
ContextRecord->FltSave.FloatRegisters[6].High,
ContextRecord->FltSave.FloatRegisters[6].Low,
L"\n FloatRegisters[7]",
ContextRecord->FltSave.FloatRegisters[7].High,
ContextRecord->FltSave.FloatRegisters[7].Low);
Logger.print(
L"\n XmmRegisters[]"
L"\n Xmm0",
ContextRecord->Xmm0.High,
ContextRecord->Xmm0.Low,
L"\n Xmm1",
ContextRecord->Xmm1.High,
ContextRecord->Xmm1.Low,
L"\n Xmm2",
ContextRecord->Xmm2.High,
ContextRecord->Xmm2.Low,
L"\n Xmm3",
ContextRecord->Xmm3.High,
ContextRecord->Xmm3.Low,
L"\n Xmm4",
ContextRecord->Xmm4.High,
ContextRecord->Xmm4.Low,
L"\n Xmm5",
ContextRecord->Xmm5.High,
ContextRecord->Xmm5.Low,
L"\n Xmm6",
ContextRecord->Xmm6.High,
ContextRecord->Xmm6.Low,
L"\n Xmm7",
ContextRecord->Xmm7.High,
ContextRecord->Xmm7.Low,
L"\n Xmm8",
ContextRecord->Xmm8.High,
ContextRecord->Xmm8.Low,
L"\n Xmm9",
ContextRecord->Xmm9.High,
ContextRecord->Xmm9.Low,
L"\n Xmm10",
ContextRecord->Xmm10.High,
ContextRecord->Xmm10.Low,
L"\n Xmm11",
ContextRecord->Xmm11.High,
ContextRecord->Xmm11.Low,
L"\n Xmm12",
ContextRecord->Xmm12.High,
ContextRecord->Xmm12.Low,
L"\n Xmm13",
ContextRecord->Xmm13.High,
ContextRecord->Xmm13.Low,
L"\n Xmm14",
ContextRecord->Xmm14.High,
ContextRecord->Xmm14.Low,
L"\n Xmm15",
ContextRecord->Xmm15.High,
ContextRecord->Xmm15.Low);
Logger.print(
"\nVectorRegister"
L"\n 0",
ContextRecord->VectorRegister[0].High,
ContextRecord->VectorRegister[0].Low,
"\n 1",
ContextRecord->VectorRegister[1].High,
ContextRecord->VectorRegister[1].Low,
L"\n 2",
ContextRecord->VectorRegister[2].High,
ContextRecord->VectorRegister[2].Low,
L"\n 3",
ContextRecord->VectorRegister[3].High,
ContextRecord->VectorRegister[3].Low,
L"\n 4",
ContextRecord->VectorRegister[4].High,
ContextRecord->VectorRegister[4].Low,
L"\n 5",
ContextRecord->VectorRegister[5].High,
ContextRecord->VectorRegister[5].Low,
L"\n 6",
ContextRecord->VectorRegister[6].High,
ContextRecord->VectorRegister[6].Low,
L"\n 7",
ContextRecord->VectorRegister[7].High,
ContextRecord->VectorRegister[7].Low,
L"\n 8",
ContextRecord->VectorRegister[8].High,
ContextRecord->VectorRegister[8].Low,
L"\n 9",
ContextRecord->VectorRegister[9].High,
ContextRecord->VectorRegister[9].Low,
L"\n 10",
ContextRecord->VectorRegister[10].High,
ContextRecord->VectorRegister[10].Low,
L"\n 11",
ContextRecord->VectorRegister[11].High,
ContextRecord->VectorRegister[11].Low,
L"\n 12",
ContextRecord->VectorRegister[12].High,
ContextRecord->VectorRegister[12].Low,
L"\n 13",
ContextRecord->VectorRegister[13].High,
ContextRecord->VectorRegister[13].Low,
L"\n 14",
ContextRecord->VectorRegister[14].High,
ContextRecord->VectorRegister[14].Low,
L"\n 15",
ContextRecord->VectorRegister[15].High,
ContextRecord->VectorRegister[15].Low,
L"\n 16",
ContextRecord->VectorRegister[16].High,
ContextRecord->VectorRegister[16].Low,
L"\n 17",
ContextRecord->VectorRegister[17].High,
ContextRecord->VectorRegister[17].Low,
L"\n 18",
ContextRecord->VectorRegister[18].High,
ContextRecord->VectorRegister[18].Low,
L"\n 19",
ContextRecord->VectorRegister[19].High,
ContextRecord->VectorRegister[19].Low,
L"\n 20",
ContextRecord->VectorRegister[20].High,
ContextRecord->VectorRegister[20].Low,
L"\n 21",
ContextRecord->VectorRegister[21].High,
ContextRecord->VectorRegister[21].Low,
L"\n 22",
ContextRecord->VectorRegister[22].High,
ContextRecord->VectorRegister[22].Low,
L"\n 23",
ContextRecord->VectorRegister[23].High,
ContextRecord->VectorRegister[23].Low,
L"\n 24",
ContextRecord->VectorRegister[24].High,
ContextRecord->VectorRegister[24].Low,
L"\n 25",
ContextRecord->VectorRegister[25].High,
ContextRecord->VectorRegister[25].Low,
L"\n VectorControl", ContextRecord->VectorControl);
Logger.print(
L"\nSpecial debug control registers:"
L"\n DebugControl", ContextRecord->DebugControl,
L"\n LastBranchToRip", ContextRecord->LastBranchToRip,
L"\n LastBranchFromRip", ContextRecord->LastBranchFromRip,
L"\n LastExceptionToRip", ContextRecord->LastExceptionToRip,
L"\n LastExceptionFromRip", ContextRecord->LastExceptionFromRip
);
Logger.print(
L"\nExceptionRecord:"
L"\n ExceptionCode", ExceptionRecord->ExceptionCode,
L"\n ExceptionFlags", ExceptionRecord->ExceptionFlags,
L"\n ExceptionAddress", ExceptionRecord->ExceptionAddress,
L"\n NumberParameters", ExceptionRecord->NumberParameters,
L"\n ExceptionInformation",
L"\n ", ExceptionRecord->ExceptionInformation[0],
L"\n ", ExceptionRecord->ExceptionInformation[1],
L"\n ", ExceptionRecord->ExceptionInformation[2],
L"\n ", ExceptionRecord->ExceptionInformation[3],
L"\n ", ExceptionRecord->ExceptionInformation[4],
L"\n ", ExceptionRecord->ExceptionInformation[5],
L"\n ", ExceptionRecord->ExceptionInformation[6],
L"\n ", ExceptionRecord->ExceptionInformation[7],
L"\n ", ExceptionRecord->ExceptionInformation[8],
L"\n ", ExceptionRecord->ExceptionInformation[9],
L"\n ", ExceptionRecord->ExceptionInformation[10],
L"\n ", ExceptionRecord->ExceptionInformation[11],
L"\n ", ExceptionRecord->ExceptionInformation[12],
L"\n ", ExceptionRecord->ExceptionInformation[13],
L"\n ", ExceptionRecord->ExceptionInformation[14]
);
return EXCEPTION_EXECUTE_HANDLER;
}
LRESULT __stdcall WndProc(const HWND hwnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam) { LRESULT __stdcall WndProc(const HWND hwnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam) {
switch (uMsg) { switch (uMsg) {
@@ -199,14 +464,16 @@ LRESULT __stdcall HookProc(const int code, const WPARAM wParam, const LPARAM lPa
return CallNextHookEx(nullptr, code, wParam, lParam); return CallNextHookEx(nullptr, code, wParam, lParam);
} }
using Time = std::chrono::time_point<std::chrono::system_clock>;
Time lastTick = std::chrono::system_clock::now();
void gameThread() { void gameThread() {
try { try {
using namespace std::chrono; using namespace std::chrono;
using Time = time_point<system_clock>; using Time = time_point<system_clock>;
Time lastTick = system_clock::now();
while (isRunning) { while (isRunning) {
const Time thisTime = system_clock::now(); const Time thisTime = system_clock::now();
if (thisTime - lastTick < milliseconds(45)) { if (thisTime - lastTick < milliseconds(interactSettings.constants.msPerTick)) {
Sleep(1); Sleep(1);
continue; continue;
} }
@@ -229,11 +496,11 @@ void renderThread() {
Time lastRender = system_clock::now(); Time lastRender = system_clock::now();
while (isRunning) { while (isRunning) {
const Time thisTime = system_clock::now(); const Time thisTime = system_clock::now();
if (thisTime - lastRender < milliseconds(12)) { if (thisTime - lastRender < milliseconds(interactSettings.constants.msPerRender)) {
Sleep(1); Sleep(1);
continue; continue;
} }
game.render(); game.render(nRange(static_cast<double>((thisTime - lastTick).count()) / interactSettings.constants.msPerRender, 0.0, 1.0));
lastRender = thisTime; lastRender = thisTime;
} }
} catch (const Exception& e) { Logger.log(L"Render thread exception: " + e.getMessage()); } } catch (const Exception& e) { Logger.log(L"Render thread exception: " + e.getMessage()); }
@@ -246,6 +513,7 @@ void renderThread() {
int __stdcall wWinMain(const HINSTANCE hInstance, const HINSTANCE, [[maybe_unused]] const LPWSTR lpCmdLine, [[maybe_unused]] const int nShowCmd) { int __stdcall wWinMain(const HINSTANCE hInstance, const HINSTANCE, [[maybe_unused]] const LPWSTR lpCmdLine, [[maybe_unused]] const int nShowCmd) {
Logger.info(L"wWinMain started"); Logger.info(L"wWinMain started");
SetConsoleOutputCP(65001); SetConsoleOutputCP(65001);
SetUnhandledExceptionFilter(UnhandledExceptionFilter);
translator.initialize(); translator.initialize();
Logger.info(L"--------Program Start--------"); Logger.info(L"--------Program Start--------");
for (const auto& [addr, info] : memoryManager.allocated) { Logger.print(L" using", addr, info.size, L"B", info.msg); } for (const auto& [addr, info] : memoryManager.allocated) { Logger.print(L" using", addr, info.size, L"B", info.msg); }
@@ -295,6 +563,9 @@ int __stdcall wWinMain(const HINSTANCE hInstance, const HINSTANCE, [[maybe_unuse
{ {
game.initialize(); game.initialize();
interactManager.initialize(); interactManager.initialize();
World* w = StartWorld::create();
game.worldManager->addWorld(w);
game.worldManager->setWorld(w);
GameThread = Thread(gameThread); GameThread = Thread(gameThread);
RenderThread = Thread(renderThread); RenderThread = Thread(renderThread);
} }
@@ -315,16 +586,8 @@ int __stdcall wWinMain(const HINSTANCE hInstance, const HINSTANCE, [[maybe_unuse
Logger.info(L"------- Program End --------"); Logger.info(L"------- Program End --------");
for (const auto& [addr, info] : memoryManager.allocated) { Logger.print(L" using", addr, info.size, L"B", info.msg); } for (const auto& [addr, info] : memoryManager.allocated) { Logger.print(L" using", addr, info.size, L"B", info.msg); }
_wsystem(L"pause"); _wsystem(L"pause");
{
fontManager.finalize(); // 似乎GDI有终止自动回收,所以此代码需要提前
}
return static_cast<int>(msg.wParam); 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;
+88 -28
View File
@@ -5,8 +5,11 @@
#pragma once #pragma once
#include "..\def.h" #include "..\def.h"
#include "..\utils\math.h"
#include "..\utils\exception.h" #include "..\utils\exception.h"
#include "..\utils\Task.h" #include "..\utils\Task.h"
#include "..\interact\InteractManager.h"
#include "..\game\world\Location.h"
class Game; class Game;
@@ -17,7 +20,7 @@ enum class UILocation : char { LEFT_TOP, LEFT, LEFT_BOTTOM, TOP, CENTER, BOTTOM,
interface IRenderable { interface IRenderable {
virtual ~IRenderable() = default; virtual ~IRenderable() = default;
virtual void render() const noexcept = 0; virtual void render(double tickDelta) const noexcept = 0;
}; };
interface ITickable { interface ITickable {
@@ -39,16 +42,36 @@ inline static constexpr Color TextColor = {
.clicked = 0xff000000 .clicked = 0xff000000
}; };
class [[carlbeks::predecl, carlbeks::defineat("Entity.h")]] Entity;
class Camera {
Vector2D position; // 当前位置
Vector2D velocity; // 移动速度,如果设置了平滑Camera
Vector2D targetPosition; // Camera需要移动到的位置
Entity* targeting = nullptr;
public:
void setTargetEntity(Entity* target) noexcept { targeting = target; }
[[nodiscard]] double getCurrentX() const noexcept { return position.getX(); }
[[nodiscard]] double getCurrentY() const noexcept { return position.getY(); }
[[nodiscard]] double getTargetX() const noexcept { return targetPosition.getX(); }
[[nodiscard]] double getTargetY() const noexcept { return targetPosition.getY(); }
[[nodiscard]] Vector2D getCurrentPosition() const noexcept { return position; }
[[nodiscard]] Vector2D getVelocity() const noexcept { return velocity; }
[[nodiscard]] Vector2D getTargetPosition() const noexcept { return targetPosition; }
};
class Renderer final : public ITickable { class Renderer final : public ITickable {
friend class Game; friend class Game;
friend class Font; friend class Font;
mutable List<HGDIOBJ> failed;
inline static BLENDFUNCTION blendFunction = { inline static BLENDFUNCTION blendFunction = {
.BlendOp = AC_SRC_OVER, // Only .BlendOp = AC_SRC_OVER, // Only
.BlendFlags = 0, // Must 0 .BlendFlags = 0, // Must 0
.SourceConstantAlpha = 255, // 预乘 .SourceConstantAlpha = 255, // 预乘
.AlphaFormat = 0, // Not AC_SRC_ALPHA .AlphaFormat = 0, // Not AC_SRC_ALPHA
}; };
mutable List<HGDIOBJ> failed;
mutable Camera camera;
HDC MainDC = nullptr; // 8 HDC MainDC = nullptr; // 8
HDC resizeCopyDC = nullptr; // 8 HDC resizeCopyDC = nullptr; // 8
HBITMAP resizeCopyBitmap = nullptr; // 8 HBITMAP resizeCopyBitmap = nullptr; // 8
@@ -72,22 +95,24 @@ class Renderer final : public ITickable {
public: public:
byte windowSize = 0; // 1, Windows: SIZE_*** byte windowSize = 0; // 1, Windows: SIZE_***
byte reserved[5]{}; byte reserved[5]{};
Task resizeReloadBitmap{ [this](Task& task) { Task resizeReloadBitmap{
Logger.info(L"Scheduled task: resize reload bitmap " + std::to_wstring(windowWidth) + L" * " + std::to_wstring(windowHeight)); [this](Task& task) {
if (!canvasBitmap) { Logger.info(L"Scheduled task: resize reload bitmap " + std::to_wstring(windowWidth) + L" * " + std::to_wstring(windowHeight));
canvasBitmap = CreateCompatibleBitmap(MainDC, windowWidth, windowHeight); if (!canvasBitmap) {
if (canvasBitmap) SelectObject(canvasDC, 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();
}
} }
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: private:
void gameStartRender() noexcept; void gameStartRender() noexcept;
@@ -118,20 +143,16 @@ public:
[[nodiscard]] int getSyncHeight() const noexcept { return syncHeight; } [[nodiscard]] int getSyncHeight() const noexcept { return syncHeight; }
[[nodiscard]] bool checkResizing() const noexcept { return isResizing; } [[nodiscard]] bool checkResizing() const noexcept { return isResizing; }
void tick() noexcept override {} void tick() noexcept override {}
/** /**
* @attention 会忽略A透明度值 * @attention 会忽略A透明度值
* @param argb ARGB式颜色 * @param argb ARGB式颜色
* @return int BGR式颜色 * @return int BGR式颜色
*/ */
[[nodiscard]] static unsigned int changeColorFormat(const unsigned int argb) { return argb << 16 & 0xff0000 | argb & 0xff00 | argb >> 16 & 0xff; } [[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 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 assertRenderThread() const noexcept(false) { if (std::this_thread::get_id() != renderThread) throw InvalidOperationException(L"Operation should be done in render thread"); }
Camera& getCamera() const noexcept { return camera; }
void resizeStart() noexcept { isResizing = true; } void resizeStart() noexcept { isResizing = true; }
void resizeShow() const noexcept { StretchBlt(MainDC, 0, 0, syncWidth, syncHeight, resizeCopyDC, 0, 0, resizeCopyWidth, resizeCopyHeight, SRCCOPY); } void resizeShow() const noexcept { StretchBlt(MainDC, 0, 0, syncWidth, syncHeight, resizeCopyDC, 0, 0, resizeCopyWidth, resizeCopyHeight, SRCCOPY); }
void resizeEnd() noexcept { void resizeEnd() noexcept {
@@ -143,9 +164,13 @@ public:
} }
void deleteObject(HGDIOBJ obj) const noexcept { void deleteObject(HGDIOBJ obj) const noexcept {
for (List<HGDIOBJ>::const_iterator iter = failed.cbegin(); iter != failed.cend(); ++iter) List<HGDIOBJ> tempList;
if (!DeleteObject(*iter)) Logger.error(L"DeleteObject failed again. Deleting: " + std::to_wstring(reinterpret_cast<QWORD>(*iter))); tempList.swap(failed);
else failed.erase(iter); for (List<HGDIOBJ>::const_iterator iter = tempList.cbegin(); iter != tempList.cend(); ++iter)
if (!DeleteObject(*iter)) {
Logger.error(L"DeleteObject failed again. Deleting: " + std::to_wstring(reinterpret_cast<QWORD>(*iter)));
failed.push_back(*iter);
}
if (obj && !DeleteObject(obj)) { if (obj && !DeleteObject(obj)) {
failed.push_back(obj); failed.push_back(obj);
Logger.error(L"DeleteObject failed. Deleting: " + std::to_wstring(reinterpret_cast<QWORD>(obj))); Logger.error(L"DeleteObject failed. Deleting: " + std::to_wstring(reinterpret_cast<QWORD>(obj)));
@@ -166,7 +191,8 @@ public:
const HBRUSH clr = CreateSolidBrush(changeColorFormat(color)); const HBRUSH clr = CreateSolidBrush(changeColorFormat(color));
FillRect(canvasDC, &rect, clr); FillRect(canvasDC, &rect, clr);
deleteObject(clr); deleteObject(clr);
} else { }
else {
const RECT rect{ const RECT rect{
.left = 0, .left = 0,
.top = 0, .top = 0,
@@ -185,10 +211,12 @@ public:
assertRendering(); assertRendering();
//assertRenderThread(); //assertRenderThread();
if ((color & 0xff000000) == 0) return; if ((color & 0xff000000) == 0) return;
if ((color & 0xff000000) == 0xff000000) { const HBRUSH clr = CreateSolidBrush(changeColorFormat(color)); if ((color & 0xff000000) == 0xff000000) {
const HBRUSH clr = CreateSolidBrush(changeColorFormat(color));
FillRect(canvasDC, rect, clr); FillRect(canvasDC, rect, clr);
deleteObject(clr); deleteObject(clr);
} else { }
else {
const RECT r{ const RECT r{
.left = 0, .left = 0,
.top = 0, .top = 0,
@@ -202,6 +230,38 @@ public:
if (!AlphaBlend(canvasDC, rect->left, rect->top, r.right, r.bottom, assistDC, 0, 0, r.right, r.bottom, blendFunction)) Logger.error(L"AlphaBlend failed"); if (!AlphaBlend(canvasDC, rect->left, rect->top, r.right, r.bottom, assistDC, 0, 0, r.right, r.bottom, blendFunction)) Logger.error(L"AlphaBlend failed");
} }
} }
void fillWorld(const Vector2D& from, const Vector2D& to, const unsigned int color) const {
RECT rect{};
Vector2D vector = (from - camera.getCurrentPosition()) * interactSettings.actual.mapScale;
rect.left = static_cast<long>(vector.getX());
if (rect.left >= windowWidth) return;
rect.top = static_cast<long>(vector.getY());
if (rect.top >= windowHeight) return;
vector = (to - camera.getCurrentPosition()) * interactSettings.actual.mapScale;
rect.right = static_cast<long>(vector.getX());
if (rect.right < 0) return;
rect.bottom = static_cast<long>(vector.getY());
if (rect.bottom < 0) return;
fill(&rect, color);
}
void fillWorld(const Vector2D& from, const double blockWidth, const double blockHeight, const unsigned int color) const {
RECT rect{};
Vector2D vector = (from - camera.getCurrentPosition()) * interactSettings.actual.mapScale;
rect.left = static_cast<long>(vector.getX());
if (rect.left >= windowWidth) return;
rect.top = static_cast<long>(vector.getY());
if (rect.top >= windowHeight) return;
vector += Vector2D(blockWidth, blockHeight).multiply(interactSettings.actual.mapScale);
rect.right = static_cast<long>(vector.getX());
if (rect.right < 0) return;
rect.bottom = static_cast<long>(vector.getY());
if (rect.bottom < 0) return;
fill(&rect, color);
}
void fillWorldBlock(const BlockLocation& from, const unsigned int color) const { fillWorld(from.getPosition(), 1, 1, color); }
}; };
extern Renderer renderer; extern Renderer renderer;
+1 -1
View File
@@ -4,5 +4,5 @@
#include "Hud.h" #include "Hud.h"
void Hud::render() const noexcept {} void Hud::render(double tickDelta) const noexcept {}
void Hud::tick() noexcept {} void Hud::tick() noexcept {}
+1 -1
View File
@@ -8,7 +8,7 @@
class Hud final : public IRenderable, public ITickable { class Hud final : public IRenderable, public ITickable {
public: public:
void render() const noexcept override; void render(double tickDelta) const noexcept override;
void tick() noexcept override; void tick() noexcept override;
void onResize() noexcept {} void onResize() noexcept {}
}; };
+9 -9
View File
@@ -13,7 +13,7 @@ int Window::pop() noexcept {
Success(); Success();
} }
void Window::render() const noexcept { for (const Widget* widget : widgets) widget->render(); } void Window::render(const double tickDelta) const noexcept { for (const Widget* widget : widgets) widget->render(tickDelta); }
void Window::tick() noexcept { for (Widget* widget : widgets) widget->tick(); } void Window::tick() noexcept { for (Widget* widget : widgets) widget->tick(); }
void Window::onResize() { for (Widget* widget : widgets) widget->onResize(); } void Window::onResize() { for (Widget* widget : widgets) widget->onResize(); }
@@ -58,13 +58,13 @@ CaptionWindow::CaptionWindow() {
close->foregroundColor.inactive = 0xff000000; close->foregroundColor.inactive = 0xff000000;
close->foregroundColor.clicked = 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 )); 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&, MouseButtonCode) {};
maxRestore->mouseClick = [](Widget& self, MouseButtonCode) { maxRestore->mouseClick = [](Widget& self, MouseButtonCode) {
if ((self.unused[1] = static_cast<char>(IsZoomed(MainWindowHandle)))) ShowWindow(MainWindowHandle, SW_RESTORE); if ((self.unused[1] = static_cast<char>(IsZoomed(MainWindowHandle)))) ShowWindow(MainWindowHandle, SW_RESTORE);
else ShowWindow(MainWindowHandle, SW_MAXIMIZE); 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->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->absolute();
maxRestore->unused[1] = static_cast<char>(IsZoomed(MainWindowHandle)); maxRestore->unused[1] = static_cast<char>(IsZoomed(MainWindowHandle));
maxRestore->backgroundColor.hover = 0xffcccccc; maxRestore->backgroundColor.hover = 0xffcccccc;
@@ -111,9 +111,9 @@ CaptionWindow::CaptionWindow() {
bool CaptionWindow::onOpen() { throw InvalidOperationException(L"Should not open CaptionWindow"); } bool CaptionWindow::onOpen() { throw InvalidOperationException(L"Should not open CaptionWindow"); }
void CaptionWindow::onClose() { throw InvalidOperationException(L"Should not close CaptionWindow"); } void CaptionWindow::onClose() { throw InvalidOperationException(L"Should not close CaptionWindow"); }
void CaptionWindow::render() const noexcept { void CaptionWindow::render(const double tickDelta) const noexcept {
renderer.fill(0, 0, renderer.getWidth(), interactSettings.actual.captionHeight, 0xff666666); renderer.fill(0, 0, renderer.getWidth(), interactSettings.actual.captionHeight, 0xff666666);
for (const Widget* widget : widgets) widget->render(); for (const Widget* widget : widgets) widget->render(tickDelta);
} }
void CaptionWindow::onResize() { void CaptionWindow::onResize() {
@@ -137,7 +137,7 @@ void CaptionWindow::onResize() {
Window::onResize(); Window::onResize();
} }
void FloatWindow::render() const noexcept { void FloatWindow::render(double tickDelta) const noexcept {
if (not interactManager.isInWindow()) { if (not interactManager.isInWindow()) {
strings.async(); strings.async();
return; return;
@@ -180,7 +180,7 @@ unsigned int Widget::colorSelector(const Color& clr) const {
return clr.hover; return clr.hover;
} }
void Widget::render() const noexcept { renderer.fill(left, top, width, height, colorSelector(backgroundColor)); } void Widget::render(double tickDelta) const noexcept { renderer.fill(left, top, width, height, colorSelector(backgroundColor)); }
void Widget::onResize() { void Widget::onResize() {
if (isAbsoluteLocation) { if (isAbsoluteLocation) {
@@ -268,8 +268,8 @@ void Widget::onResize() {
} }
} }
void Button::render() const noexcept { void Button::render(const double tickDelta) const noexcept {
Widget::render(); Widget::render(tickDelta);
if (name) fontManager.getDefault().drawCenter(name->getRenderableString(), left, top, width, height, colorSelector(foregroundColor)); if (name) fontManager.getDefault().drawCenter(name->getRenderableString(), left, top, width, height, colorSelector(foregroundColor));
} }
+8 -8
View File
@@ -65,7 +65,7 @@ public:
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) {} 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; unsigned int colorSelector(const Color& clr) const;
void render() const noexcept override; void render(double tickDelta) const noexcept override;
Widget& alignLocation(const UILocation loc) noexcept { Widget& alignLocation(const UILocation loc) noexcept {
location = loc; location = loc;
@@ -155,7 +155,7 @@ protected:
public: public:
int pop() noexcept override; int pop() noexcept override;
void render() const noexcept override; void render(double tickDelta) const noexcept override;
void tick() noexcept override; void tick() noexcept override;
/** /**
* 在Game.setWindow()时,本窗口开启时调用。 * 在Game.setWindow()时,本窗口开启时调用。
@@ -176,7 +176,7 @@ public:
class WindowManager final : public AnywhereEditableList<Window, WindowManager>, public IRenderable, public ITickable { class WindowManager final : public AnywhereEditableList<Window, WindowManager>, public IRenderable, public ITickable {
public: public:
void render() const noexcept override { for (const Window& i : *this) i.render(); } void render(const double tickDelta) const noexcept override { for (const Window& i : *this) i.render(tickDelta); }
void tick() noexcept override { for (Window& i : *this) i.tick(); } void tick() noexcept override { for (Window& i : *this) i.tick(); }
int pop(Window* value) noexcept override; int pop(Window* value) noexcept override;
void clear() noexcept; void clear() noexcept;
@@ -188,7 +188,7 @@ public:
CaptionWindow(); CaptionWindow();
bool onOpen() override; bool onOpen() override;
void onClose() override; void onClose() override;
void render() const noexcept override; void render(double tickDelta) const noexcept override;
void onResize() override; void onResize() override;
}; };
@@ -209,7 +209,7 @@ public:
void push(const ObjectHolder<RenderableString>& string) const { if (strings.ptrNew()) strings.getNew().push_back(string); } 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 push(ObjectHolder<RenderableString>&& string) const { if (strings.ptrNew()) strings.getNew().push_back(std::move(string)); }
void render() const noexcept override; void render(double tickDelta) const noexcept override;
void tick() noexcept override {} void tick() noexcept override {}
bool onOpen() override { return true; } bool onOpen() override { return true; }
void onClose() override {} void onClose() override {}
@@ -222,7 +222,7 @@ public:
Animation animation = Animation().features(Animation::AS_CUBIC).setDuration(20); 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, 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)) {} 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; void render(double tickDelta) const noexcept override;
}; };
@@ -240,13 +240,13 @@ private:
public: public:
~ConfirmWindow() override = default; // 不需要delete,析构时Window会自动delete ~ConfirmWindow() override = default; // 不需要delete,析构时Window会自动delete
void render() const noexcept override { void render(const double tickDelta) const noexcept override {
int w, h; int w, h;
w = renderer.getWidth(), h = renderer.getHeight(); w = renderer.getWidth(), h = renderer.getHeight();
w >>= 2, h >>= 2; w >>= 2, h >>= 2;
renderer.fill(w, h, w + w, h + h, 0xcc222222); renderer.fill(w, h, w + w, h + h, 0xcc222222);
fontManager.getDefault().drawCenter(text->getRenderableString(), w, h, w + w, h + (h >> 1), 0xffeeeeee); fontManager.getDefault().drawCenter(text->getRenderableString(), w, h, w + w, h + (h >> 1), 0xffeeeeee);
for (const ObjectHolder<Widget>& widget : widgets) widget->render(); for (const ObjectHolder<Widget>& widget : widgets) widget->render(tickDelta);
} }
ConfirmWindow& requireConfirm(const Function<void(Button&)>& func = {}); ConfirmWindow& requireConfirm(const Function<void(Button&)>& func = {});
+2 -2
View File
@@ -34,8 +34,8 @@ public:
static StartWindow* create() noexcept { return allocatedFor(new StartWindow()); } static StartWindow* create() noexcept { return allocatedFor(new StartWindow()); }
void onClose() override { pop(); } void onClose() override { pop(); }
void render() const noexcept override { void render(const double tickDelta) const noexcept override {
fontManager.getDefault().drawCenter(title.getRenderableString(), 0, 0, renderer.getWidth(), renderer.getHeight()); fontManager.getDefault().drawCenter(title.getRenderableString(), 0, 0, renderer.getWidth(), renderer.getHeight());
Window::render(); Window::render(tickDelta);
} }
}; };
+15 -8
View File
@@ -146,19 +146,22 @@ public:
if (config.idFont == idFont || config.idFont != 0) { if (config.idFont == idFont || config.idFont != 0) {
font = false; font = false;
++flags; ++flags;
} else config.idFont = idFont; }
else config.idFont = idFont;
} }
if (clr) { if (clr) {
if (config.color == color || config.color != 0xffffffff) { if (config.color == color || config.color != 0xffffffff) {
clr = false; clr = false;
++flags; ++flags;
} else config.color = color; }
else config.color = color;
} }
if (bg) { if (bg) {
if (config.background == background || config.background != 0xffffffff) { if (config.background == background || config.background != 0xffffffff) {
bg = false; bg = false;
++flags; ++flags;
} else config.background = background; }
else config.background = background;
} }
configs.push_back(std::move(config)); configs.push_back(std::move(config));
++iterator; ++iterator;
@@ -374,14 +377,17 @@ private:
const FontID id; const FontID id;
bool adaptAllSize = false; 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, 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) {} 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: public:
Font(const Font&) = default; Font(const Font&) = default;
Font(Font&&) = default; Font(Font&&) = default;
~Font() { clear(); } ~Font() {
// 此处Font的回收已经到结束阶段,GDI应该已经收回了资源,不能在手动释放了
if (fonts.size()) Logger.warn(L"Font is not successfully cleared when ~Font() called: " + name);
}
private: private:
HFONT tryCreate(const RenderableString::StringConfig& config) const { HFONT tryCreate(const RenderableString::StringConfig& config) const {
@@ -447,6 +453,8 @@ public:
newFont(L"", 1.0, -0.05); newFont(L"", 1.0, -0.05);
} }
void finalize() { for (auto& [id, font] : fonts) font.clear(); }
[[nodiscard]] const Font& get(const FontID id) const noexcept { [[nodiscard]] const Font& get(const FontID id) const noexcept {
const IterFonts iter = fonts.find(id); const IterFonts iter = fonts.find(id);
if (iter == fonts.cend()) return *defaultFont; if (iter == fonts.cend()) return *defaultFont;
@@ -543,7 +551,7 @@ void languageMakeChinese(Language&);
class Translator { class Translator {
Map<String, Language> langMap{}; Map<String, Language> langMap{};
List<Language*> langList{}; List<Language*> langList{};
TranslatedText nullText{ L"\\#FF""EE0000<translator-null>" }; TranslatedText nullText{L"\\#FF""EE0000<translator-null>"};
String lang = L"zh-cn"; String lang = L"zh-cn";
LangID idLangMax = 0; LangID idLangMax = 0;
int langConfig = 1; int langConfig = 1;
@@ -583,7 +591,6 @@ public:
void loadLang(); void loadLang();
[[nodiscard]] const TranslatedText* getText(const String& id) const noexcept { [[nodiscard]] const TranslatedText* getText(const String& id) const noexcept {
Logger.debug(L"getText called");
for (const Language* language : langList) { for (const Language* language : langList) {
const Language::IterText iterator = language->translateTable.find(id); const Language::IterText iterator = language->translateTable.find(id);
if (iterator != language->translateTable.cend()) return &iterator->second; if (iterator != language->translateTable.cend()) return &iterator->second;
+2 -2
View File
@@ -162,7 +162,7 @@ public:
template<typename T, typename... Ts> requires requires(T t, Ts... ts) { template<typename T, typename... Ts> requires requires(T t, Ts... ts) {
std::wcout << t; std::wcout << t;
std::wcout << (ts, ...); (std::wcout << ... << std::forward<Ts>(ts));
} }
PublicLogger& print(T&& msg, Ts&&... other) noexcept { PublicLogger& print(T&& msg, Ts&&... other) noexcept {
std::wstringstream stream = {}; std::wstringstream stream = {};
@@ -194,5 +194,5 @@ public:
} }
}; };
[[carlbeks::releasedat("main.cpp")]] inline PublicLogger& Logger = *new PublicLogger(L"Main"); [[carlbeks::releasedat("def.cpp")]] inline PublicLogger& Logger = *new PublicLogger(L"Main");
+2 -2
View File
@@ -76,7 +76,7 @@ public:
} }
/** 只能在gameThread调用 */ /** 只能在gameThread调用 */
template <TypeName T> template<TypeName T>
void submit(T* ptr) noexcept(false) { void submit(T* ptr) noexcept(false) {
IGarbage* newedGarbage = allocatedFor(new Garbage<T>(ptr)); IGarbage* newedGarbage = allocatedFor(new Garbage<T>(ptr));
builtinSubmit(newedGarbage); builtinSubmit(newedGarbage);
@@ -147,4 +147,4 @@ public:
} }
}; };
inline GarbageCollector& [[carlbeks::releasedat("main.cpp")]] gc = *new GarbageCollector(); inline GarbageCollector& [[carlbeks::releasedat("def.cpp")]] gc = *new GarbageCollector();
+42 -65
View File
@@ -5,11 +5,27 @@
#pragma once #pragma once
#include "..\warnings.h" #include "..\warnings.h"
#include "..\def.h"
class Vector3D; template <typename T>
class Vector2D; const T& nRange(const T& val, const T& min, const T& max) { return val < min ? min : val > max ? max : val; }
class Vector3D { template <typename T>
const T& nMin(const T& a, const T& b) { return a < b ? a : b; }
template <typename T>
const T& nMin(const T& val0, const T& val1, const T& vals...) { return nMin(nMin(val0, val1), nMin(vals...)); }
template <typename T>
const T& nMax(const T& a, const T& b) { return a > b ? a : b; }
template <typename T>
const T& nMax(const T& val0, const T& val1, const T& vals...) { return nMax(nMin(val0, val1), nMax(vals...)); }
class [[carlbeks::TriviallyCopyable]] Vector3D;
class [[carlbeks::TriviallyCopyable]] Vector2D;
class [[carlbeks::TriviallyCopyable]] Vector3D {
double x, y, z; double x, y, z;
public: public:
@@ -30,39 +46,13 @@ public:
[[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]] 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; } [[nodiscard]] double operator*(const Vector3D& other) const noexcept { return x * other.x + y * other.y + z * other.z; }
Vector3D& operator+=(const Vector3D& other) noexcept { return x += other.x, y += other.y, z += other.z, *this; }
Vector3D& operator+=(const Vector3D& other) noexcept { Vector3D& operator-=(const Vector3D& other) noexcept { return x -= other.x, y -= other.y, z -= other.z, *this; }
x += other.x; Vector3D& operator*=(const double scalar) noexcept { return x *= scalar, y *= scalar, z *= scalar, *this; }
y += other.y; Vector3D& operator/=(const double scalar) noexcept { return x /= scalar, y /= scalar, z /= scalar, *this; }
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); } [[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; }
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]] double length() const noexcept { return std::sqrt(x * x + y * y + z * z); }
[[nodiscard]] Vector3D getNormalized() const noexcept { [[nodiscard]] Vector3D getNormalized() const noexcept {
@@ -74,17 +64,21 @@ public:
Vector3D& normalize() noexcept { Vector3D& normalize() noexcept {
if (x == 0 && y == 0 && z == 0) return *this; if (x == 0 && y == 0 && z == 0) return *this;
const double length = this->length(); const double length = this->length();
x /= length; x /= length, y /= length, z /= length;
y /= length;
z /= length;
return *this; return *this;
} }
Vector3D& add(const Vector3D& other) noexcept { return x += other.x, y += other.y, z += other.z, *this; }
Vector3D& add(const double x, const double y, const double z) noexcept { return this->x += x, this->y += y, this->z += z, *this; }
Vector3D& subtract(const Vector3D& other) noexcept { return x -= other.x, y -= other.y, z -= other.z, *this; }
Vector3D& subtract(const double x, const double y, const double z) noexcept { return this->x -= x, this->y -= y, this->z -= z, *this; }
Vector3D& multiply(const double value) noexcept { return this->x *= value, this->y *= value, this->z *= value, *this; }
Vector3D& divide(const double value) noexcept { return this->x /= value, this->y /= value, this->z /= value, *this; }
[[nodiscard]] double dot(const Vector3D& other) const noexcept { return x * other.x + y * other.y + z * other.z; } [[nodiscard]] 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); } [[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 { class [[carlbeks::TriviallyCopyable]] Vector2D {
double x, y; double x, y;
public: public:
@@ -104,35 +98,13 @@ public:
[[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]] 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; } [[nodiscard]] double operator*(const Vector2D& other) const noexcept { return x * other.x + y * other.y; }
Vector2D& operator+=(const Vector2D& other) noexcept { return x += other.x, y += other.y, *this; }
Vector2D& operator+=(const Vector2D& other) noexcept { Vector2D& operator-=(const Vector2D& other) noexcept { return x -= other.x, y -= other.y, *this; }
x += other.x; Vector2D& operator*=(const double scalar) noexcept { return x *= scalar, y *= scalar, *this; }
y += other.y; Vector2D& operator/=(const double scalar) noexcept { return x /= scalar, y /= scalar, *this; }
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); } [[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; }
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]] double length() const noexcept { return std::sqrt(x * x + y * y); }
[[nodiscard]] Vector2D getNormalized() const noexcept { [[nodiscard]] Vector2D getNormalized() const noexcept {
@@ -144,11 +116,16 @@ public:
Vector2D& normalize() noexcept { Vector2D& normalize() noexcept {
if (x == 0 && y == 0) return *this; if (x == 0 && y == 0) return *this;
const double length = this->length(); const double length = this->length();
x /= length; x /= length, y /= length;
y /= length;
return *this; return *this;
} }
Vector2D& add(const Vector2D& other) noexcept { return x += other.x, y += other.y, *this; }
Vector2D& add(const double x, const double y) noexcept { return this->x += x, this->y += y, *this; }
Vector2D& subtract(const Vector2D& other) noexcept { return x -= other.x, y -= other.y, *this; }
Vector2D& subtract(const double x, const double y) noexcept { return this->x -= x, this->y -= y, *this; }
Vector2D& multiply(const double value) noexcept { return this->x *= value, this->y *= value, *this; }
Vector2D& divide(const double value) noexcept { return this->x /= value, this->y /= value, *this; }
[[nodiscard]] double dot(const Vector2D& other) const noexcept { return x * other.x + y * other.y; } [[nodiscard]] 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); } [[nodiscard]] Vector3D cross(const Vector2D& other) const noexcept { return Vector3D(0, 0, x * other.y - y * other.x); }
}; };