You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
399 lines
13 KiB
C++
399 lines
13 KiB
C++
/******************************************************************************
|
|
* File - world.cpp
|
|
* Author - Joey Pollack
|
|
* Date - 2022/01/12 (y/m/d)
|
|
* Mod Date - 2022/01/20 (y/m/d)
|
|
* Description - Manages a game "world". A world is made up of regions which
|
|
* are subdivisions of the world. Each region contains: a set
|
|
* of images for the maps layers, a list of objects that spawn
|
|
* in this region and static collision data.
|
|
******************************************************************************/
|
|
|
|
#include "world.h"
|
|
#include <LunariumConfig.h>
|
|
#include <utils/logger.h>
|
|
#include <utils/helpers.h>
|
|
#include <assets/types/script.h>
|
|
#include <assets/types/image.h>
|
|
#include <renderer/renderer2D.h>
|
|
#include <renderer/frame_buffer.h>
|
|
#include <renderer/orthographic_camera.h>
|
|
#include "entity.h"
|
|
|
|
//#define LOAD_ASSETS_FROM_EDITOR true
|
|
#if !BUILD_NO_EDITOR
|
|
#include <editor/contents/content_manager.h>
|
|
#include <editor/contents/script.h>
|
|
#endif
|
|
|
|
namespace lunarium
|
|
{
|
|
// World::World(Camera* pCam, Sizei region_size, Sizei world_size)
|
|
// : mpCamera(pCam), mRegionSize(region_size), mpWorldScript(nullptr), mWorldSize(world_size)
|
|
// {
|
|
// mActiveRegion = { 0, 0 };
|
|
// }
|
|
|
|
World::World(std::string name)
|
|
: mUUID(UUID::GetNewID()), mName(name), mpActiveCamera(nullptr), mFrameBuffer(nullptr)
|
|
//mGetAssetsFromEditor(EDITOR_MODE)
|
|
{
|
|
}
|
|
|
|
|
|
void World::InitScriptState()
|
|
{
|
|
mScriptState.Initialize().LogIfFailed(LogCategory::GAME_SYSTEM, "Failed to initialize the world script state");
|
|
|
|
// Load the world interface script
|
|
mWorldInterface.SetModuleName("WorldInterface");
|
|
std::string code = File::ReadTextFile("world_interface.wren"); // This will eventually move to internal data
|
|
mWorldInterface.SetScriptCode(code);
|
|
|
|
mScriptState.RunScript(&mWorldInterface);
|
|
|
|
// Get class and method handles
|
|
mWIHandle = mScriptState.GetWrenClassHandle("WorldInterface", "WorldInterface");
|
|
mWIInitMethod = mScriptState.GetWrenMethodHandle("Init()");
|
|
mWIDoOnLoadMethod = mScriptState.GetWrenMethodHandle("DoOnLoad()");
|
|
mWIDoOnUnloadMethod = mScriptState.GetWrenMethodHandle("DoOnUnload()");
|
|
mWIUpdateMethod = mScriptState.GetWrenMethodHandle("Update(_)");
|
|
|
|
// Init the interface
|
|
mScriptState.CallWrenMethod(mWIInitMethod, mWIHandle, {}, "WorldInterface.Init()");
|
|
}
|
|
|
|
void World::OnLoad()
|
|
{
|
|
InitScriptState();
|
|
|
|
// Load scripts from all ScriptComponents in the world
|
|
auto group_scripts = mECSRegistry.view<ScriptComponent>();
|
|
for(auto entity: group_scripts)
|
|
{
|
|
auto& script_comp = group_scripts.get<ScriptComponent>(entity);
|
|
|
|
// If were attached to the editor we need to get the script from the editor's content manager
|
|
#if !BUILD_NO_EDITOR
|
|
|
|
editor::Script* pScript = (editor::Script*) editor::ContentManager::GetInstance().GetAsset(script_comp.ScriptID);
|
|
|
|
WrenScript script(pScript->GetScriptFile().filename().string().c_str(), pScript->GetScript());
|
|
|
|
mScriptState.RunScript(&script);
|
|
#endif
|
|
}
|
|
|
|
// Call OnLoad for each registered EntityBehavior class object
|
|
//mScriptState.RunSnippet("WorldInterface", "WorldInterface.DoOnLoad()");
|
|
mScriptState.CallWrenMethod(mWIDoOnLoadMethod, mWIHandle, {}, "WorldInterface.DoOnLoad()");
|
|
}
|
|
|
|
void World::OnUnload()
|
|
{
|
|
// TODO: Call OnUnLoad for each registered EntityBehavior class object
|
|
//mScriptState.RunSnippet("WorldInterface", "WorldInterface.DoOnUnload()");
|
|
mScriptState.CallWrenMethod(mWIDoOnUnloadMethod, mWIHandle, {}, "WorldInterface.DoOnUnload()");
|
|
mScriptState.Shutdown();
|
|
}
|
|
|
|
void World::SetRunMode(RunMode mode)
|
|
{
|
|
mMode = mode;
|
|
}
|
|
|
|
World::RunMode World::GetRunMode()
|
|
{
|
|
return mMode;
|
|
}
|
|
|
|
void World::Update(float dt)
|
|
{
|
|
// Update all scripts
|
|
// Call OnUpdate for each registered EntityBehavior class object
|
|
// std::string code = "WorldInterface.Update(";
|
|
// code.append(std::to_string(dt));
|
|
// code.append(")");
|
|
// mScriptState.RunSnippet("WorldInterface", code);
|
|
mScriptState.CallWrenMethod(mWIUpdateMethod, mWIHandle, { {WrenParamType::WPT_DOUBLE, (double)dt} }, "WorldInterface.Update(dt)");
|
|
|
|
|
|
|
|
// Update Transforms for any enity with a velocity
|
|
auto group = mECSRegistry.group<>(entt::get<VelocityComponent, TransformComponent>);
|
|
for(auto entity: group)
|
|
{
|
|
auto &transform = group.get<TransformComponent>(entity);
|
|
auto &velocity = group.get<VelocityComponent>(entity);
|
|
|
|
transform.Position += velocity.Velocity * dt;
|
|
}
|
|
|
|
}
|
|
|
|
void World::Render(lunarium::Renderer2D* pGraphics)
|
|
{
|
|
|
|
// First get the group we want
|
|
auto group = mECSRegistry.group<>(entt::get<BlockOutComponent, TransformComponent>);
|
|
|
|
// Next sort the group
|
|
group.sort<BlockOutComponent>([](const BlockOutComponent &lhs, const BlockOutComponent &rhs)
|
|
{
|
|
return lhs.RenderLayer < rhs.RenderLayer;
|
|
});
|
|
|
|
// Render the group
|
|
for(auto entity: group)
|
|
{
|
|
auto &transform = group.get<TransformComponent>(entity);
|
|
auto &blockout = group.get<BlockOutComponent>(entity);
|
|
|
|
|
|
Rectangle rect(transform.Position.x, transform.Position.y, blockout.Size.x, blockout.Size.y);
|
|
Color color(blockout.Color.x, blockout.Color.y, blockout.Color.z, blockout.Color.w);
|
|
|
|
// Get and apply parent transforms
|
|
LUUID uuid = mECSRegistry.get<UUIDComponent>(entity).UUID;
|
|
Entity* pEnt = GetEntity(uuid);
|
|
glm::mat4 parent_transform = glm::mat4(1.0f);
|
|
if (pEnt->HasComponent<ParentEntityComponent>())
|
|
{
|
|
|
|
parent_transform = GetParentTransform(pEnt->GetComponent<ParentEntityComponent>().Parent);
|
|
}
|
|
|
|
pGraphics->DrawQuad(rect, color, nullptr, transform.Rotation.z, Rectangle(-1, -1, -1, -1), parent_transform);
|
|
}
|
|
}
|
|
|
|
|
|
void World::SetActiveCamera(OrthographicCamera* pCam)
|
|
{
|
|
mpActiveCamera = pCam;
|
|
}
|
|
|
|
entt::registry* World::GetEntityRegistry()
|
|
{
|
|
return &mECSRegistry;
|
|
}
|
|
|
|
LUUID World::CreateEntity()
|
|
{
|
|
//Logger::Error(LogCategory::GAME_SYSTEM, "World::CreateEntity not implemented!");
|
|
Entity* new_ent = new Entity(*this);
|
|
|
|
if (mEntitiesByUUID.find(new_ent->GetUUID()) != mEntitiesByUUID.end())
|
|
{
|
|
Logger::Warn(LogCategory::GAME_SYSTEM, "UUID collision when creating new entity! UUID: %d", new_ent->GetUUID());
|
|
}
|
|
|
|
mEntitiesByUUID[new_ent->GetUUID()] = new_ent;
|
|
mEntities.push_back(new_ent);
|
|
return new_ent->GetUUID();
|
|
}
|
|
|
|
void World::RemoveEntity(LUUID id)
|
|
{
|
|
Entity* temp = mEntitiesByUUID[id];
|
|
mEntitiesByUUID.erase(id);
|
|
|
|
for (auto iter = mEntities.begin(); iter != mEntities.end(); iter++)
|
|
{
|
|
if ((*iter)->GetUUID() == id)
|
|
{
|
|
mEntities.erase(iter);
|
|
break;
|
|
}
|
|
}
|
|
|
|
mECSRegistry.destroy(temp->GetEnttHandle());
|
|
delete temp;
|
|
}
|
|
|
|
Entity* World::GetEntity(LUUID id)
|
|
{
|
|
if (mEntitiesByUUID.find(id) == mEntitiesByUUID.end())
|
|
{
|
|
Logger::Warn(LogCategory::GAME_SYSTEM, "Entity with id: %d not found.", id);
|
|
}
|
|
return mEntitiesByUUID[id];
|
|
}
|
|
|
|
unsigned int World::GetNumEntities() const
|
|
{
|
|
return mEntities.size();
|
|
}
|
|
|
|
std::vector<Entity*>::iterator World::EntitiesBegin()
|
|
{
|
|
return mEntities.begin();
|
|
}
|
|
|
|
bool World::EntitiesIsEnd(std::vector<Entity*>::iterator& iter)
|
|
{
|
|
return iter == mEntities.end();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// RUN MODE HELPERS
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
void World::DrawHeirarchy(lunarium::Renderer2D* g, entt::entity& entity, TransformComponent& my_trans, BlockOutComponent& bo_comp, glm::mat4 current_transform)
|
|
{
|
|
// Draw current entity
|
|
Rectangle rect(my_trans.Position.x, my_trans.Position.y, bo_comp.Size.x, bo_comp.Size.y);
|
|
Color color(bo_comp.Color.x, bo_comp.Color.y, bo_comp.Color.z, bo_comp.Color.w);
|
|
g->DrawQuad(rect, color, nullptr, -my_trans.Rotation.z, Rectangle(-1, -1, -1, -1), current_transform);
|
|
|
|
// Apply transform to children's transforms
|
|
current_transform *= my_trans.GetTransform();
|
|
|
|
// Iterate and draw children
|
|
if (mECSRegistry.all_of<ChildrenComponent>(entity))
|
|
{
|
|
ChildrenComponent& children_comp = mECSRegistry.get<ChildrenComponent>(entity);
|
|
for (int i = 0; i < children_comp.Children.size(); i++)
|
|
{
|
|
Entity* pEnt = GetEntity(children_comp.Children[i]);
|
|
entt::entity handle = pEnt->GetEnttHandle();
|
|
|
|
if (pEnt->HasComponent<TransformComponent>() && pEnt->HasComponent<BlockOutComponent>())
|
|
{
|
|
TransformComponent& trans_comp = pEnt->GetComponent<TransformComponent>();
|
|
BlockOutComponent& bo_comp = pEnt->GetComponent<BlockOutComponent>();
|
|
|
|
DrawHeirarchy(g, handle, trans_comp, bo_comp, current_transform);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
glm::mat4 World::GetParentTransform(LUUID parent)
|
|
{
|
|
glm::mat4 transform(1.0f);
|
|
Entity* p = GetEntity(parent);
|
|
|
|
if (p->HasComponent<TransformComponent>())
|
|
{
|
|
|
|
|
|
if (p->HasComponent<ParentEntityComponent>())
|
|
{
|
|
transform = GetParentTransform(p->GetComponent<ParentEntityComponent>().Parent);
|
|
}
|
|
|
|
TransformComponent t = p->GetComponent<TransformComponent>();
|
|
transform = glm::translate(transform, t.Position);
|
|
transform = glm::rotate(transform, t.Rotation.z * -1.0f, glm::vec3(0.0f, 0.0f, 1.0f));
|
|
|
|
}
|
|
|
|
return transform;
|
|
}
|
|
|
|
void World::RenderEditor(lunarium::Renderer2D* pGraphics) const
|
|
{
|
|
// NOTE: MAY BE REMOVED
|
|
}
|
|
|
|
|
|
WorldState World::GetState()
|
|
{
|
|
return { AsJSON() };
|
|
}
|
|
|
|
void World::ResetState(WorldState& state)
|
|
{
|
|
if (!IsValidNode(state.State))
|
|
{
|
|
Logger::Warn(LogCategory::GAME_SYSTEM, "World state could not be set - state is invalid");
|
|
return;
|
|
}
|
|
mECSRegistry.clear();
|
|
mEntities.clear();
|
|
mEntitiesByUUID.clear();
|
|
|
|
Deserialize(state.State);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// SERIALIZING
|
|
/////////////////////////////////////////////////////////////////////
|
|
OpRes World::Serialize(nlohmann::ordered_json& node)
|
|
{
|
|
#if !BUILD_NO_EDITOR // Only does this when this is an editor build
|
|
|
|
node["UUID"] = mUUID;
|
|
|
|
node["NumberOfEntities"] = mEntities.size();
|
|
auto& ents = node["Entities"];
|
|
for (int i = 0; i < mEntities.size(); i++)
|
|
{
|
|
nlohmann::ordered_json e;
|
|
mEntities[i]->Serialize(e).LogIfFailed(LogCategory::GAME_SYSTEM);
|
|
ents.emplace_back(e);
|
|
}
|
|
|
|
#endif
|
|
return OpRes::OK();
|
|
}
|
|
|
|
OpRes World::Deserialize(nlohmann::ordered_json& node)
|
|
{
|
|
#if !BUILD_NO_EDITOR // Only does this when this is an editor build
|
|
|
|
mUUID = node["UUID"].get<u64>();
|
|
int num_entities = node["NumberOfEntities"].get<int>();
|
|
auto& ents = node["Entities"];
|
|
for (auto it = ents.begin(); it != ents.end(); ++it)
|
|
{
|
|
auto& ent = (*it);
|
|
Entity* new_ent = new Entity(*this);
|
|
|
|
if (!new_ent->IsValidNode(ent))
|
|
{
|
|
delete new_ent;
|
|
return OpRes::Fail("Invalid entity node");
|
|
}
|
|
|
|
new_ent->Deserialize(ent).LogIfFailed(LogCategory::GAME_SYSTEM);
|
|
mEntities.push_back(new_ent);
|
|
mEntitiesByUUID[new_ent->GetUUID()] = new_ent;
|
|
}
|
|
|
|
#endif
|
|
return OpRes::OK();
|
|
}
|
|
|
|
bool World::IsValidNode(nlohmann::ordered_json& node)
|
|
{
|
|
if (node["UUID"].is_null()) { return false; }
|
|
if (!node["UUID"].is_number()) { return false; }
|
|
|
|
if (node["NumberOfEntities"].is_null()) { return false; }
|
|
if (!node["NumberOfEntities"].is_number()) { return false; }
|
|
|
|
if (node["Entities"].is_null()) { return false; }
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
nlohmann::ordered_json World::AsJSON()
|
|
{
|
|
#if !BUILD_NO_EDITOR // Only does this when this is an editor build
|
|
nlohmann::ordered_json node;
|
|
|
|
Serialize(node).LogIfFailed(LogCategory::GAME_SYSTEM);
|
|
|
|
return node;
|
|
#endif
|
|
|
|
return nlohmann::ordered_json();
|
|
}
|
|
|
|
} |