Files
pke/src/game/game.cpp
2026-03-05 15:20:18 +03:00

513 lines
12 KiB
C++

#include "ifilesystem.h"
#include "core.h"
#include "log.h"
#include "game.h"
#include "game_lua_help.h"
#include "ientity.h"
#include "entitymanager.h"
#include "world.h"
#include "game_object.h"
#include "soundsystem.h"
#include <pugixml.hpp>
#include <map>
#include <string>
static Game s_game;
Game* g_game = &s_game;
class GameSoundSystem
{
public:
static GameSoundSystem& GetInstance();
public:
void PlaySound2D(const std::string& filename);
void CacheSound(const std::string& filename);
private:
std::map<std::string, SoundHandle> m_sounds;
};
GameSoundSystem& GameSoundSystem::GetInstance()
{
static GameSoundSystem inst;
return inst;
}
void GameSoundSystem::PlaySound2D(const std::string& filename)
{
auto it = m_sounds.find(filename);
if (it == m_sounds.end())
{
CacheSound(filename);
it = m_sounds.find(filename);
}
SDL_assert(it != m_sounds.end());
// .hack due unimplemented sound sources^
if (g_soundSystem.IsPlaying(it->second))
g_soundSystem.Stop(it->second);
g_soundSystem.Play(it->second, false);
}
void GameSoundSystem::CacheSound(const std::string& filename)
{
m_sounds.emplace(filename, g_soundSystem.LoadSound(filename.c_str()));
}
void consoleMsg(const char* msg)
{
Logger::Msg("%s", msg);
}
void engineError(const char* msg)
{
Core::Error("%s", msg);
}
void engineWarning(const char* msg)
{
Core::Warning("%s", msg);
}
void enginePlaySound(const char* filename)
{
GameSoundSystem::GetInstance().PlaySound2D(filename);
}
LuaPlus::LuaObject engineCreateEntity(const char* classname)
{
Entity* entity = static_cast<Entity*>(g_game->Lua_CreateEntity(classname));
SDL_assert_always(entity);
entity->Init();
return entity->GetLuaObject();
}
LuaPlus::LuaObject engineGetEntityFromID(lua_Integer id)
{
LuaPlus::LuaObject lookup = GetLuaState().GetGlobal("g_entities");
LuaPlus::LuaObject entityTable = lookup.GetByIndex(id);
return entityTable;
}
void engineAddEntityToWorld(LuaPlus::LuaObject& object)
{
LuaPlus::LuaObject cppclass = object["__object"];
SDL_assert_always(cppclass.IsLightUserdata());
Entity* entity = static_cast<Entity*>(cppclass.GetLightUserdata());
g_world->AddEntity(entity);
}
int cameraGetPos(LuaPlus::LuaState* state)
{
glm::vec3 v = glm::vec3(0.0f);
Camera* camera = g_cameraManager.GetActiveCamera();
if (camera)
v = camera->GetPosition();
state->PushNumber(v.x);
state->PushNumber(v.y);
state->PushNumber(v.z);
return 3;
}
int cameraGetFront(LuaPlus::LuaState* state)
{
glm::vec3 v = glm::vec3(0.0f);
Camera* camera = g_cameraManager.GetActiveCamera();
if (camera)
v = camera->GetFront();
state->PushNumber(v.x);
state->PushNumber(v.y);
state->PushNumber(v.z);
return 3;
}
int cameraGetRight(LuaPlus::LuaState* state)
{
glm::vec3 v = glm::vec3(0.0f);
Camera* camera = g_cameraManager.GetActiveCamera();
if (camera)
v = camera->GetRight();
state->PushNumber(v.x);
state->PushNumber(v.y);
state->PushNumber(v.z);
return 3;
}
int cameraGetUp(LuaPlus::LuaState* state)
{
glm::vec3 v = glm::vec3(0.0f);
Camera* camera = g_cameraManager.GetActiveCamera();
if (camera)
v = camera->GetUp();
state->PushNumber(v.x);
state->PushNumber(v.y);
state->PushNumber(v.z);
return 3;
}
int cameraGetYaw(LuaPlus::LuaState* state)
{
float v = 0.0f;
Camera* camera = g_cameraManager.GetActiveCamera();
if (camera)
v = camera->GetYaw();
state->PushNumber(v);
return 1;
}
int cameraGetPitch(LuaPlus::LuaState* state)
{
float v = 0.0f;
Camera* camera = g_cameraManager.GetActiveCamera();
if (camera)
v = camera->GetPitch();
state->PushNumber(v);
return 1;
}
void registerEngine()
{
using namespace LuaPlus;
// register engine functions
LuaObject engineTable = GetLuaState().GetGlobals().CreateTable("engine");
engineTable.RegisterDirect("error", &engineError);
engineTable.RegisterDirect("warning", &engineWarning);
engineTable.RegisterDirect("create_entity", &engineCreateEntity);
engineTable.RegisterDirect("add_entity_to_world", &engineAddEntityToWorld);
engineTable.RegisterDirect("get_entity_from_id", &engineGetEntityFromID);
engineTable.RegisterDirect("play_sound", &enginePlaySound);
LuaObject consoleTable = GetLuaState().GetGlobals().CreateTable("console");
consoleTable.RegisterDirect("print", &consoleMsg);
LuaObject cameraTable = GetLuaState().GetGlobals().CreateTable("camera");
cameraTable.Register("get_position", &cameraGetPos);
cameraTable.Register("get_front", &cameraGetFront);
cameraTable.Register("get_right", &cameraGetRight);
cameraTable.Register("get_up", &cameraGetUp);
cameraTable.Register("get_yaw", &cameraGetYaw);
cameraTable.Register("get_pitch", &cameraGetPitch);
// action globals
GetLuaState().DoString("ACTION_FIRE = 0");
GetLuaState().DoString("ACTION_ALT_FIRE = 1");
GetLuaState().DoString("ACTION_RELOAD = 2");
// animations globals
GetLuaState().DoString("ANIM_PLAYBACK_NONE = 0");
GetLuaState().DoString("ANIM_PLAYBACK_REPEAT = 1");
}
void registerClasses()
{
using namespace LuaPlus;
// base thing
GetLuaState().GetGlobals().RegisterDirect("load_script", &luaLoadScript);
GetLuaState().DoString("g_factory = {}");
GetLuaState().DoString("g_entities = {}");
registerEngine();
}
struct LuaPrototype
{
std::string m_luaname;
std::string m_enginename;
std::string m_description;
};
std::vector<LuaPrototype> g_prototypes;
void initializeEntityPrototypesFromLua()
{
using namespace LuaPlus;
LuaObject entitiesTable = GetLuaState().GetGlobal("g_entity_table");
for (LuaTableIterator it(entitiesTable); it; it.Next()) {
LuaObject entityTable = it.GetValue();
assert(entityTable.IsTable());
if (entityTable.GetTableCount() < 3)
{
Core::Error("Entry in entity prototype table is bad, missing argument");
}
LuaObject luaname = entityTable[1];
LuaObject classname = entityTable[2];
LuaObject description = entityTable[3];
Logger::Msg("lua ent: name: %s classname: %s description: %s", luaname.ToString(), classname.ToString(), description.ToString());
LuaPrototype prototype;
prototype.m_luaname = luaname.ToString();
prototype.m_enginename = classname.ToString();
prototype.m_description = description.ToString();
g_prototypes.push_back(prototype);
// get a prototype
LuaObject prototypeTable = GetLuaState().GetGlobal(luaname.ToString());
SDL_assert_always(!prototypeTable.IsNil());
prototypeTable.SetObject("__index", prototypeTable);
/*for (LuaTableIterator it2(entityTable); it2; it2.Next()) {
Logger::Msg("%s", it2.GetValue().ToString());
}*/
}
}
void initializeEntitiesTable()
{
using namespace LuaPlus;
LuaObject entitiesTable = GetLuaState().GetGlobal("g_entity_table");
if (entitiesTable.IsNil()) {
Core::Error("initializeEntitiesTable: failed initialize the entity table! g_entity_table is nil");
}
if (!entitiesTable.IsTable()) {
Core::Error("initializeEntitiesTable: failed initialize the entity table! g_entity_table is not a table");
}
initializeEntityPrototypesFromLua();
}
void initializeLua()
{
using namespace LuaPlus;
// register base classes
registerClasses();
// load game base script
luaLoadScript("game_init.lua");
}
void Game::Init()
{
initializeLua();
initializeEntitiesTable();
}
void Game::InitForNewMap(const char* mapname)
{
LoadLevelXML(mapname);
}
void Game::LoadLevelXML(const char* mapname)
{
// Load XML file of the level
char buffer[kMaxPathLength];
snprintf(buffer, kMaxPathLength, "data/levels/%s/%s.xml", mapname, mapname);
FileHandle_t file = GetFileSystem()->OpenFile(buffer, "rb");
SDL_assert_always(file != kInvalidFileHandleValue);
size_t length = GetFileSystem()->GetFileLength(file);
char* filedata = new char[length + 1];
GetFileSystem()->ReadFile(file, filedata, length);
filedata[length] = '\0';
GetFileSystem()->CloseFile(file);
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_buffer(filedata, length);
delete[] filedata;
if (!result) {
Core::Error("Game::LoadLevelXML: Error while reading level description file '%s'\nError: %s:%i",
buffer, result.description(), result.offset);
}
// std::map<std::string, std::string> properties;
for (pugi::xml_node entitynode : doc.child("Level").child("Entities").children("Entity")) {
pugi::xml_attribute entityname = entitynode.attribute("name");
pugi::xml_attribute classname = entitynode.attribute("classname");
if (classname.empty()) {
if (!entityname.empty()) {
Core::Error(" Game::LoadLevelXML: Classname is not specified for entity '%s'", entityname.as_string());
}
else {
Core::Error(" Game::LoadLevelXML: Classname is not specified for entity");
}
}
// Create entity and expose it to the engine
Entity* entity = static_cast<Entity*>(Lua_CreateEntity(classname.as_string()));
pugi::xml_node position = entitynode.child("Position");
if (position)
{
float x = position.attribute("x").as_float();
float y = position.attribute("y").as_float();
float z = position.attribute("z").as_float();
entity->SetPosition(glm::vec3(x, y, z));
}
pugi::xml_node model = entitynode.child("Model");
if (model)
{
const char* filename = model.attribute("filename").as_string();
if (filename)
entity->LoadModel(filename);
}
pugi::xml_node physics = entitynode.child("Physics");
if (physics)
{
bool value = physics.attribute("value").as_bool();
if (value)
entity->CreateTestBody();
}
entity->Init();
//IEntityBase* entity = g_entityManager->CreateEntity(classname.as_string());
g_world->AddEntity(entity);
}
}
IEntityBase* Game::Lua_CreateEntity(const char* classname)
{
using namespace LuaPlus;
SDL_assert_always(classname);
// find prototype
LuaPrototype* luaprototype = Lua_FindPrototype(classname);
// create an entity
Entity* entity = nullptr;
if (luaprototype)
{
entity = static_cast<Entity*>(g_entityManager->CreateEntity(luaprototype->m_enginename.c_str()));
// override classname because of entity system is so dumb
entity->SetClassname(luaprototype->m_luaname.c_str());
}
else
{
entity = static_cast<Entity*>(g_entityManager->CreateEntity(classname));
}
SDL_assert_always(entity);
// get the factrory
LuaObject factory = GetLuaState().GetGlobal("g_factory");
// and the lookup table
LuaObject lookup = GetLuaState().GetGlobal("g_entities");
// generate name
char entityname[256];
snprintf(entityname, sizeof(entityname), "%s_%d", classname, factory.GetTableCount());
// create an table
LuaObject entityTable = GetLuaState().CreateTable();
entityTable.SetString("m_name", entityname);
entityTable.SetInteger("m_id", entity->GetID());
// assign prototype
if (luaprototype)
{
LuaObject prototype = GetLuaState().GetGlobal(luaprototype->m_luaname.c_str());
SDL_assert_always(!prototype.IsNil());
entityTable.SetMetatable(prototype);
}
// link to the entity
entity->InitFromTable(entityTable);
// push in to the factory
factory.SetObject(entityname, entityTable);
lookup.SetObject(entity->GetID(), entityTable);
return entity;
}
LuaPrototype* Game::Lua_FindPrototype(const char* classname)
{
// find a prototype
for (std::vector<LuaPrototype>::iterator it = g_prototypes.begin();
it != g_prototypes.end();
++it)
{
if (strcmp((*it).m_luaname.c_str(), classname) == 0)
{
return &(*it);
}
}
return nullptr;
}
void Game::Shutdown()
{
}
//LuaPrototype* pluaprototype = Lua_FindPrototype(classname);
//
//if (pluaprototype)
//{
// Entity* entity = static_cast<Entity*>(g_entityManager->CreateEntity(pluaprototype->m_enginename.c_str()));
// SDL_assert_always(entity);
// // get a prototype
// LuaObject prototype = GetLuaState().GetGlobal(pluaprototype->m_luaname.c_str());
// SDL_assert_always(!prototype.IsNil());
// //prototype.SetObject("__index", prototype);
// // generate table
// LuaObject factory = GetLuaState().GetGlobal("g_factory");
// // generate name
// std::string entityname = pluaprototype->m_luaname + "_" + std::to_string(factory.GetTableCount());
//
// // create an table
// LuaObject entityTable = GetLuaState().GetGlobals().CreateTable(entityname.c_str());
// entityTable.SetMetatable(prototype);
// entityTable.SetString("m_name", entityname.c_str());
//
// // push in to the factory
// factory.SetObject(entityname.c_str(), entityTable);
// // link to the entity
// entity->InitFromTable(entityTable);
// return entity;
//}
//IEntityBase* entity = g_entityManager->CreateEntity(classname);