513 lines
12 KiB
C++
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);
|