#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 #include #include 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 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(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(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 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 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(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(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(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::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(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);