| 1 | /* |
|---|
| 2 | ================================================================================= |
|---|
| 3 | This file is part of Cafu, the open-source game engine and graphics engine |
|---|
| 4 | for multiplayer, cross-platform, real-time 3D action. |
|---|
| 5 | Copyright (C) 2002-2012 Carsten Fuchs Software. |
|---|
| 6 | |
|---|
| 7 | Cafu is free software: you can redistribute it and/or modify it under the terms |
|---|
| 8 | of the GNU General Public License as published by the Free Software Foundation, |
|---|
| 9 | either version 3 of the License, or (at your option) any later version. |
|---|
| 10 | |
|---|
| 11 | Cafu is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
|---|
| 12 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
|---|
| 13 | PURPOSE. See the GNU General Public License for more details. |
|---|
| 14 | |
|---|
| 15 | You should have received a copy of the GNU General Public License |
|---|
| 16 | along with Cafu. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 17 | |
|---|
| 18 | For support and more information about Cafu, visit us at <http://www.cafu.de>. |
|---|
| 19 | ================================================================================= |
|---|
| 20 | */ |
|---|
| 21 | |
|---|
| 22 | #include "GameConfig.hpp" |
|---|
| 23 | #include "EntityClass.hpp" |
|---|
| 24 | #include "FileSys/FileMan.hpp" |
|---|
| 25 | #include "wx/fileconf.h" |
|---|
| 26 | |
|---|
| 27 | extern "C" |
|---|
| 28 | { |
|---|
| 29 | #include <lua.h> |
|---|
| 30 | #include <lualib.h> |
|---|
| 31 | #include <lauxlib.h> |
|---|
| 32 | } |
|---|
| 33 | |
|---|
| 34 | |
|---|
| 35 | GameConfigT::GameConfigT(wxFileConfig& CfgFile, const wxString& Name_, const wxString& ModDir_) |
|---|
| 36 | : Name(Name_), |
|---|
| 37 | ModDir(ModDir_), |
|---|
| 38 | m_MatMan(*this), |
|---|
| 39 | m_ModelMan(), |
|---|
| 40 | m_GuiResources(m_ModelMan), |
|---|
| 41 | m_MaxMapCoord(8192) |
|---|
| 42 | { |
|---|
| 43 | // The next line doesn't work with wx 2.6.4, it always returns the default value 1.0. |
|---|
| 44 | // DefaultTextureScale =CfgFile.Read("DefaultTextureScale", 1.0); |
|---|
| 45 | double d; |
|---|
| 46 | CfgFile.Read("DefaultTextureScale", &d, 0.25); DefaultTextureScale =d; // Work-around... |
|---|
| 47 | CfgFile.Read("DefaultLightmapScale", &d, 16.0); DefaultLightmapScale=d; |
|---|
| 48 | |
|---|
| 49 | DefaultSolidEntity =CfgFile.Read("DefaultSolidEntity", "func_wall"); |
|---|
| 50 | DefaultPointEntity =CfgFile.Read("DefaultPointEntity", "info_player_start"); |
|---|
| 51 | CordonTexture =CfgFile.Read("CordonTexture", "Textures/generic/_orange"); |
|---|
| 52 | |
|---|
| 53 | |
|---|
| 54 | // Mount the file systems relevant for this MOD. |
|---|
| 55 | const std::string ModDirSlash(ModDir+"/"); |
|---|
| 56 | |
|---|
| 57 | m_MountedFileSystems.PushBack(cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_LOCAL_PATH, ModDirSlash, ModDirSlash)); |
|---|
| 58 | m_MountedFileSystems.PushBack(cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_ZIP_ARCHIVE, ModDirSlash+"Textures/TechDemo.zip", ModDirSlash+"Textures/TechDemo/", "Ca3DE")); |
|---|
| 59 | m_MountedFileSystems.PushBack(cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_ZIP_ARCHIVE, ModDirSlash+"Textures/SkyDomes.zip", ModDirSlash+"Textures/SkyDomes/", "Ca3DE")); |
|---|
| 60 | |
|---|
| 61 | |
|---|
| 62 | // Create a new Lua state. |
|---|
| 63 | lua_State* LuaState=lua_open(); |
|---|
| 64 | |
|---|
| 65 | try |
|---|
| 66 | { |
|---|
| 67 | if (LuaState==NULL) throw InitErrorT(); |
|---|
| 68 | |
|---|
| 69 | lua_pushcfunction(LuaState, luaopen_base); lua_pushstring(LuaState, ""); lua_call(LuaState, 1, 0); // Opens the basic library. |
|---|
| 70 | lua_pushcfunction(LuaState, luaopen_package); lua_pushstring(LuaState, LUA_LOADLIBNAME); lua_call(LuaState, 1, 0); // Opens the package library. |
|---|
| 71 | lua_pushcfunction(LuaState, luaopen_table); lua_pushstring(LuaState, LUA_TABLIBNAME); lua_call(LuaState, 1, 0); // Opens the table library. |
|---|
| 72 | lua_pushcfunction(LuaState, luaopen_io); lua_pushstring(LuaState, LUA_IOLIBNAME); lua_call(LuaState, 1, 0); // Opens the I/O library. |
|---|
| 73 | lua_pushcfunction(LuaState, luaopen_os); lua_pushstring(LuaState, LUA_OSLIBNAME); lua_call(LuaState, 1, 0); // Opens the OS library. |
|---|
| 74 | lua_pushcfunction(LuaState, luaopen_string); lua_pushstring(LuaState, LUA_STRLIBNAME); lua_call(LuaState, 1, 0); // Opens the string lib. |
|---|
| 75 | lua_pushcfunction(LuaState, luaopen_math); lua_pushstring(LuaState, LUA_MATHLIBNAME); lua_call(LuaState, 1, 0); // Opens the math lib. |
|---|
| 76 | |
|---|
| 77 | |
|---|
| 78 | // Load and process the Lua script file with the entity class definitions. |
|---|
| 79 | if (luaL_loadfile(LuaState, (ModDir+"/EntityClassDefs.lua").c_str())!=0 || lua_pcall(LuaState, 0, 0, 0)!=0) |
|---|
| 80 | { |
|---|
| 81 | wxMessageBox("Entity class definitions script could not be processed,\n"+ |
|---|
| 82 | wxString(lua_tostring(LuaState, -1))+".\n", |
|---|
| 83 | "Error while initializing game configuration \""+Name+"\"", wxOK | wxICON_ERROR); |
|---|
| 84 | |
|---|
| 85 | lua_pop(LuaState, 1); |
|---|
| 86 | throw InitErrorT(); |
|---|
| 87 | } |
|---|
| 88 | |
|---|
| 89 | assert(lua_gettop(LuaState)==0); |
|---|
| 90 | |
|---|
| 91 | |
|---|
| 92 | // Obtain the "Mapsize" information. |
|---|
| 93 | lua_getglobal(LuaState, "Mapsize"); |
|---|
| 94 | |
|---|
| 95 | if (lua_istable(LuaState, -1)) |
|---|
| 96 | { |
|---|
| 97 | lua_rawgeti(LuaState, -1, 1); |
|---|
| 98 | int Min=lua_tointeger(LuaState, -1); |
|---|
| 99 | lua_pop(LuaState, 1); |
|---|
| 100 | |
|---|
| 101 | lua_rawgeti(LuaState, -1, 2); |
|---|
| 102 | int Max=lua_tointeger(LuaState, -1); |
|---|
| 103 | lua_pop(LuaState, 1); |
|---|
| 104 | |
|---|
| 105 | if (Min!=0 && Max!=0) |
|---|
| 106 | { |
|---|
| 107 | m_MaxMapCoord=std::max(abs(Min), abs(Max)); |
|---|
| 108 | } |
|---|
| 109 | |
|---|
| 110 | // wxMessageBox(wxString::Format("Found the Mapsize table: %i, %i", m_MinMapCoord, m_MaxMapCoord)); |
|---|
| 111 | } |
|---|
| 112 | |
|---|
| 113 | assert(lua_gettop(LuaState)==1); |
|---|
| 114 | lua_pop(LuaState, 1); |
|---|
| 115 | |
|---|
| 116 | |
|---|
| 117 | // Create an EntityClassT instance for each entity class definition in the "EntityClassDefs" table. |
|---|
| 118 | lua_getglobal(LuaState, "EntityClassDefs"); |
|---|
| 119 | lua_pushnil(LuaState); // The initial key for the traversal. |
|---|
| 120 | |
|---|
| 121 | while (lua_next(LuaState, -2)!=0) |
|---|
| 122 | { |
|---|
| 123 | // The key is now at stack index -2, the value is at index -1. |
|---|
| 124 | // Note that in general, the warning from the Lua reference documentation applies: |
|---|
| 125 | // "While traversing a table, do not call lua_tolstring() directly on a key, unless you know that the key is actually a string." |
|---|
| 126 | // Fortunately, we know that the key is of type string, and so the called function is free to inspect (but not to modify!) |
|---|
| 127 | // the two topmost stack values. |
|---|
| 128 | if (lua_type(LuaState, -2)!=LUA_TSTRING) |
|---|
| 129 | { |
|---|
| 130 | wxMessageBox("Non-string entity class name in EntityClassDefs table.\n", |
|---|
| 131 | "Error while initializing game configuration \""+Name+"\"", wxOK | wxICON_ERROR); |
|---|
| 132 | throw InitErrorT(); |
|---|
| 133 | } |
|---|
| 134 | |
|---|
| 135 | try |
|---|
| 136 | { |
|---|
| 137 | m_EntityClasses.PushBack(new EntityClassT(*this, LuaState)); |
|---|
| 138 | } |
|---|
| 139 | catch (const EntityClassT::InitErrorT&) |
|---|
| 140 | { |
|---|
| 141 | // For some reason, the entity class could not be created, and our stack is now in an unknown state |
|---|
| 142 | // (our lower three elements should still be intact, but any number of elements may be on top). |
|---|
| 143 | // We could of course try to recover (pop all elements above ours) and continue, |
|---|
| 144 | // but there is still some risk and there was an error with the script that should be looked into anyway, |
|---|
| 145 | // so we're just bailing out. |
|---|
| 146 | throw InitErrorT(); |
|---|
| 147 | } |
|---|
| 148 | |
|---|
| 149 | // Make sure that the EntityClassT ctor left the stack behind properly. |
|---|
| 150 | assert(lua_gettop(LuaState)==3); |
|---|
| 151 | |
|---|
| 152 | // Remove the value, keep the key for the next iteration. |
|---|
| 153 | lua_pop(LuaState, 1); |
|---|
| 154 | } |
|---|
| 155 | |
|---|
| 156 | assert(lua_gettop(LuaState)==1); |
|---|
| 157 | lua_pop(LuaState, 1); |
|---|
| 158 | } |
|---|
| 159 | catch (const InitErrorT&) |
|---|
| 160 | { |
|---|
| 161 | // What is done here is of course utterly against all (RAII-related) advice, see |
|---|
| 162 | // a) Bjarne Stroustrup, "Die C++ Programmiersprache", chapter 14.4. |
|---|
| 163 | // b) Scott Meyers, "Effective C++, 3rd Edition", item 13 (and 14). |
|---|
| 164 | // c) http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization |
|---|
| 165 | // Unfortunately, auto_ptr<> is not well suitable here, and I currently see no |
|---|
| 166 | // easy solution rather than to do it like this. |
|---|
| 167 | |
|---|
| 168 | // Close the LuaState. |
|---|
| 169 | if (LuaState!=NULL) lua_close(LuaState); |
|---|
| 170 | |
|---|
| 171 | // Delete all entity classes. |
|---|
| 172 | for (unsigned long ECNr=0; ECNr<m_EntityClasses.Size(); ECNr++) |
|---|
| 173 | delete m_EntityClasses[ECNr]; |
|---|
| 174 | |
|---|
| 175 | // Unmount any mounted file systems. |
|---|
| 176 | for (unsigned long MFSNr=0; MFSNr<m_MountedFileSystems.Size(); MFSNr++) |
|---|
| 177 | cf::FileSys::FileMan->Unmount(m_MountedFileSystems[MFSNr]); |
|---|
| 178 | |
|---|
| 179 | throw; // Re-throw the caught exception. |
|---|
| 180 | } |
|---|
| 181 | |
|---|
| 182 | // Close the LuaState. |
|---|
| 183 | lua_close(LuaState); |
|---|
| 184 | |
|---|
| 185 | |
|---|
| 186 | // Output a brief overview of the loaded classes and their variables. |
|---|
| 187 | // This is for debugging purposes. |
|---|
| 188 | #if 0 |
|---|
| 189 | wxString out; |
|---|
| 190 | |
|---|
| 191 | for (unsigned long ClassNr=0; ClassNr<m_EntityClasses.Size(); ClassNr++) |
|---|
| 192 | { |
|---|
| 193 | const EntityClassT* EC=m_EntityClasses[ClassNr]; |
|---|
| 194 | |
|---|
| 195 | out+=EC->GetName()+": "+EC->GetDescription()+"\n"; |
|---|
| 196 | out+=EC->GetColor().GetAsString(wxC2S_CSS_SYNTAX)+", ["+convertToString(EC->GetBoundingBox().Min).c_str()+", "+convertToString(EC->GetBoundingBox().Max).c_str()+"]\n"; |
|---|
| 197 | |
|---|
| 198 | if (EC->IsSolidClass()) out+="isSolid, "; |
|---|
| 199 | |
|---|
| 200 | out+="Helpers: "; |
|---|
| 201 | for (unsigned long HNr=0; HNr<EC->GetHelpers().Size(); HNr++) |
|---|
| 202 | { |
|---|
| 203 | const HelperInfoT* HI=EC->GetHelpers()[HNr]; |
|---|
| 204 | |
|---|
| 205 | out+=HI->Name+"("; |
|---|
| 206 | for (unsigned long PNr=0; PNr<HI->Parameters.Size(); PNr++) out+=HI->Parameters[PNr]+" "; |
|---|
| 207 | out+=") "; |
|---|
| 208 | } |
|---|
| 209 | out+="\n"; |
|---|
| 210 | |
|---|
| 211 | // out+="Vars:\n"; |
|---|
| 212 | for (unsigned long VarNr=0; VarNr<EC->GetVariables().Size(); VarNr++) |
|---|
| 213 | { |
|---|
| 214 | const EntClassVarT* Var=EC->GetVariables()[VarNr]; |
|---|
| 215 | |
|---|
| 216 | out+=" "+Var->GetName()+", "+Var->GetLongName()+", "+Var->GetDescription()+wxString::Format(" Type: %i", Var->GetType())+"\n"; |
|---|
| 217 | } |
|---|
| 218 | |
|---|
| 219 | out+="\n"; |
|---|
| 220 | } |
|---|
| 221 | |
|---|
| 222 | // wxMessageBox(out, "Entity Classes Overview", wxOK | wxICON_INFORMATION); |
|---|
| 223 | wxLogDebug(out); |
|---|
| 224 | #endif |
|---|
| 225 | } |
|---|
| 226 | |
|---|
| 227 | |
|---|
| 228 | GameConfigT::~GameConfigT() |
|---|
| 229 | { |
|---|
| 230 | // Delete all entity classes. |
|---|
| 231 | for (unsigned long ECNr=0; ECNr<m_EntityClasses.Size(); ECNr++) |
|---|
| 232 | delete m_EntityClasses[ECNr]; |
|---|
| 233 | |
|---|
| 234 | if (cf::FileSys::FileMan!=NULL) |
|---|
| 235 | { |
|---|
| 236 | for (unsigned long MFSNr=0; MFSNr<m_MountedFileSystems.Size(); MFSNr++) |
|---|
| 237 | cf::FileSys::FileMan->Unmount(m_MountedFileSystems[MFSNr]); |
|---|
| 238 | } |
|---|
| 239 | |
|---|
| 240 | m_MountedFileSystems.Clear(); |
|---|
| 241 | } |
|---|
| 242 | |
|---|
| 243 | |
|---|
| 244 | const EntityClassT* GameConfigT::FindClass(const wxString& Name) const |
|---|
| 245 | { |
|---|
| 246 | for (unsigned long ClassNr=0; ClassNr<m_EntityClasses.Size(); ClassNr++) |
|---|
| 247 | if (m_EntityClasses[ClassNr]->GetName()==Name) |
|---|
| 248 | return m_EntityClasses[ClassNr]; |
|---|
| 249 | |
|---|
| 250 | return NULL; |
|---|
| 251 | } |
|---|
| 252 | |
|---|
| 253 | |
|---|
| 254 | const CafuModelT* GameConfigT::GetModel(const wxString& FileName, wxString* ErrorMsg) const |
|---|
| 255 | { |
|---|
| 256 | std::string Msg; |
|---|
| 257 | const CafuModelT* Model=m_ModelMan.GetModel(std::string(ModDir + "/" + FileName), &Msg); |
|---|
| 258 | |
|---|
| 259 | if (ErrorMsg) |
|---|
| 260 | *ErrorMsg=Msg; |
|---|
| 261 | |
|---|
| 262 | return Model; |
|---|
| 263 | } |
|---|
| 264 | |
|---|
| 265 | |
|---|
| 266 | BoundingBox3fT GameConfigT::GetMaxMapBB() const |
|---|
| 267 | { |
|---|
| 268 | const float f=m_MaxMapCoord; |
|---|
| 269 | |
|---|
| 270 | return BoundingBox3fT(Vector3fT(-f, -f, -f), Vector3fT(f, f, f)); |
|---|
| 271 | } |
|---|
| 272 | |
|---|
| 273 | |
|---|
| 274 | void GameConfigT::Save(wxFileConfig& CfgFile) const |
|---|
| 275 | { |
|---|
| 276 | CfgFile.DeleteGroup("."); |
|---|
| 277 | |
|---|
| 278 | CfgFile.Write("DefaultTextureScale", DefaultTextureScale); |
|---|
| 279 | CfgFile.Write("DefaultLightmapScale", DefaultLightmapScale); |
|---|
| 280 | |
|---|
| 281 | CfgFile.Write("DefaultSolidEntity", DefaultSolidEntity); |
|---|
| 282 | CfgFile.Write("DefaultPointEntity", DefaultPointEntity); |
|---|
| 283 | |
|---|
| 284 | CfgFile.Write("CordonTexture", CordonTexture); |
|---|
| 285 | } |
|---|