#include "ifilesystem.h" #include "core.h" #include "log.h" #include "engine.h" #include "game.h" #include "game_lua_help.h" #include "game_ui.h" #include "inputmanager.h" #include "ientity.h" #include "entitymanager.h" #include "world.h" #include "game_object.h" #include "soundsystem.h" #include "animation.h" #include "scenemanager.h" #include "game_world_objects.h" #include "actor_base.h" #include "physics/physicsworld.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 PlaySound3D(const std::string& filename, const glm::vec3& pos); 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::PlaySound3D(const std::string& filename, const glm::vec3& pos) { 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.Play3D(it->second, pos.x, pos.y, pos.z, true); } 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); } void enginePlaySound3D(const char* filename, float x, float y, float z) { GameSoundSystem::GetInstance().PlaySound3D(filename, glm::vec3(x, y, z)); } 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; } LuaPlus::LuaObject engineFindEntityByName(const char* name) { Entity* entity = (Entity*)g_game->FindEntityByName(name); if (!entity) { LuaPlus::LuaObject table; table.AssignNil(GetLuaState()); return table; } return entity->GetLuaObject(); } 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); } LuaPlus::LuaObject engineTraceRay(float rayBeginX, float rayBeginY, float rayBeginZ, float rayEndX, float rayEndY, float rayEndZ, const LuaPlus::LuaObject& ignoreTable) { IEntityBase* pIgnoreEntity = nullptr; if (ignoreTable.IsTable()) { LuaPlus::LuaObject ignoreEntityTable = ignoreTable.GetByName("__object"); if (ignoreEntityTable.IsLightUserdata()) pIgnoreEntity = (IEntityBase*)ignoreEntityTable.GetLightUserdata(); } TraceRayResult result = {}; glm::vec3 rayBegin = glm::vec3(rayBeginX, rayBeginY, rayBeginZ); glm::vec3 rayEnd = glm::vec3(rayEndX, rayEndY, rayEndZ); if (g_PhysicsWorld) g_PhysicsWorld->TraceRay(result, rayBegin, rayEnd, pIgnoreEntity); else Logger::Msg("engine.trace_ray(): no server started or game loaded!"); LuaPlus::LuaObject resultTable = GetLuaState().CreateTable(); resultTable.SetNumber("pos_x", result.position.x); resultTable.SetNumber("pos_y", result.position.y); resultTable.SetNumber("pos_z", result.position.z); resultTable.SetNumber("normal_x", result.normal.x); resultTable.SetNumber("normal_y", result.normal.y); resultTable.SetNumber("normal_z", result.normal.z); resultTable.SetInteger("entity_id", result.pEntity ? result.pEntity->GetID() : -1); resultTable.SetInteger("hit", result.hit); return resultTable; } float engineGetDelta() { return GetEngine()->GetDelta(); } 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("find_entity_by_name", &engineFindEntityByName); engineTable.RegisterDirect("play_sound", &enginePlaySound); engineTable.RegisterDirect("play_sound_3d", &enginePlaySound3D); engineTable.RegisterDirect("get_delta", &engineGetDelta); engineTable.RegisterDirect("trace_ray", &engineTraceRay); LuaObject consoleTable = GetLuaState().GetGlobals().CreateTable("console"); consoleTable.RegisterDirect("print", &consoleMsg); registerCamera(); registerInput(); registerEngineUI(); // action globals GetLuaState().DoString("ACTION_FIRE = 0"); GetLuaState().DoString("ACTION_ALT_FIRE = 1"); GetLuaState().DoString("ACTION_RELOAD = 2"); GetLuaState().DoString("ACTION_USE = 3"); // animations globals GetLuaState().DoString("ANIM_PLAYBACK_NONE = 0"); GetLuaState().DoString("ANIM_PLAYBACK_REPEAT = 1"); char buffer[64]; #define REGISTER_CONSTANT(constant) \ snprintf(buffer, sizeof(buffer), #constant" = %d", constant); \ GetLuaState().DoString(buffer) REGISTER_CONSTANT(EMovementDir_None); REGISTER_CONSTANT(EMovementDir_Forward); REGISTER_CONSTANT(EMovementDir_Backward); REGISTER_CONSTANT(EMovementDir_Left); REGISTER_CONSTANT(EMovementDir_Right); REGISTER_CONSTANT(EMovementDir_Jump); // Physics REGISTER_CONSTANT(PhysicsFilter_None); REGISTER_CONSTANT(PhysicsFilter_Player); REGISTER_CONSTANT(PhysicsFilter_Triggers); REGISTER_CONSTANT(PhysicsFilter_Usable); REGISTER_CONSTANT(PhysicsFilter_NPC); REGISTER_CONSTANT(PhysicsFilter_Obstacle); REGISTER_CONSTANT(kPhysicsFilter_AllAux); #undef REGISTER_CONSTANT } int inputGetMousePos(LuaPlus::LuaState* state) { const glm::vec2& pos = g_inputManager.GetMousePos(); state->PushNumber(pos.x); state->PushNumber(pos.y); return 2; } int inputGetDeltaMousePos(LuaPlus::LuaState* state) { const glm::vec2& pos = g_inputManager.GetMouseDeltaPos(); state->PushNumber(pos.x); state->PushNumber(pos.y); return 2; } void inputLock(bool value) { g_inputManager.SetRelativeMouseMode(value); } bool inputGetLock() { return g_inputManager.GetRelativeMouseMode(); } void registerInput() { LuaPlus::LuaObject inputTable = GetLuaState().GetGlobals().CreateTable("input"); inputTable.Register("get_mouse_pos", &inputGetMousePos); inputTable.Register("get_delta_mouse_pos", &inputGetDeltaMousePos); inputTable.RegisterDirect("lock_mouse", &inputLock); inputTable.RegisterDirect("get_lock_mouse", &inputGetLock); } void toggleNoclip() { static bool s_noclip = false; s_noclip = !s_noclip; LuaPlus::LuaObject player = GetLuaState().GetGlobal("g_player"); if (player.IsTable()) { ActorBase* actor = (ActorBase*)player["__object"].GetLightUserdata(); actor->SetNoclip(s_noclip); } } 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 cameraSetYawPitch(float yaw, float pitch) { Camera* camera = g_cameraManager.GetActiveCamera(); if (camera) camera->SetYawPitch(yaw, pitch); } void registerCamera() { LuaPlus::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); cameraTable.RegisterDirect("set_yaw_pitch", &cameraSetYawPitch); } 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(); luaCallFunction("sv_game_init"); } void Game::InitForNewMap(const char* mapname) { LoadLevelXML(mapname); luaCallFunction("sv_on_game_start"); } 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())); if (!entityname.empty()) entity->SetName(entityname.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 animation = entitynode.child("Animation"); if (animation) { const char* name = animation.attribute("name").as_string(); int mode = animation.attribute("mode").as_int(); SkeletonInstance* skeleton = entity->GetSkeletonInstance(); if (skeleton) skeleton->PlayAnimation(skeleton->FindAnimation(name), mode); } pugi::xml_node physics = entitynode.child("Physics"); if (physics) { bool value = physics.attribute("value").as_bool(); if (value) entity->CreateTestBody(); } Light* light = dynamic_cast(entity); // BAD!!! if (light) light->InitFromXML(entitynode); 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("__private_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; } IEntityBase* Game::FindEntityByName(const char* name) { if (!g_world) return nullptr; int numEntities = g_world->GetNumEntities(); for (int i = 0; i < numEntities; i++) { Entity* entity = dynamic_cast(g_world->GetEntity(i)); if (entity && entity->GetName() == name) return entity; } return nullptr; } void Game::Shutdown() { } void Game::Render2D() { gameRenderUI(); } //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);