int main(int argc, char* argv[]) { Threading::SetMainThread(); try { spring_clock::PushTickRate(); // initialize start time (can safely be done before SDL_Init // since we are not using SDL_GetTicks as our clock anymore) spring_time::setstarttime(spring_time::gettime(true)); CLogOutput::LogSystemInfo(); std::string scriptName; std::string scriptText; std::string binaryName = argv[0]; gflags::SetUsageMessage("Usage: " + binaryName + " [options] path_to_script.txt"); gflags::SetVersionString(SpringVersion::GetFull()); gflags::ParseCommandLineFlags(&argc, &argv, true); ParseCmdLine(argc, argv, scriptName); GlobalConfig::Instantiate(); FileSystemInitializer::InitializeLogOutput(); FileSystemInitializer::Initialize(); // Initialize crash reporting CrashHandler::Install(); LOG("report any errors to Mantis or the forums."); LOG("loading script from file: %s", scriptName.c_str()); // server will take ownership of these std::shared_ptr<ClientSetup> dsClientSetup(new ClientSetup()); std::shared_ptr<GameData> dsGameData(new GameData()); std::shared_ptr<CGameSetup> dsGameSetup(new CGameSetup()); CFileHandler fh(scriptName); if (!fh.FileExists()) throw content_error("script does not exist in given location: " + scriptName); if (!fh.LoadStringData(scriptText)) throw content_error("script cannot be read: " + scriptName); dsClientSetup->LoadFromStartScript(scriptText); if (!dsGameSetup->Init(scriptText)) { // read the script provided by cmdline LOG_L(L_ERROR, "failed to load script %s", scriptName.c_str()); return 1; } // Create the server, it will run in a separate thread CGlobalUnsyncedRNG rng; const unsigned sleepTime = FLAGS_sleeptime; const unsigned randSeed = time(nullptr) % ((spring_gettime().toNanoSecsi() + 1) * 9007); rng.Seed(randSeed); dsGameData->SetRandomSeed(rng.NextInt()); // Use script provided hashes if they exist if (dsGameSetup->mapHash != 0) { dsGameData->SetMapChecksum(dsGameSetup->mapHash); dsGameSetup->LoadStartPositions(false); // reduced mode } else { dsGameData->SetMapChecksum(archiveScanner->GetArchiveCompleteChecksum(dsGameSetup->mapName)); CFileHandler f("maps/" + dsGameSetup->mapName); if (!f.FileExists()) vfsHandler->AddArchiveWithDeps(dsGameSetup->mapName, false); dsGameSetup->LoadStartPositions(); // full mode } if (dsGameSetup->modHash != 0) { dsGameData->SetModChecksum(dsGameSetup->modHash); } else { const std::string& modArchive = archiveScanner->ArchiveFromName(dsGameSetup->modName); const unsigned int modCheckSum = archiveScanner->GetArchiveCompleteChecksum(modArchive); dsGameData->SetModChecksum(modCheckSum); } LOG("starting server..."); { dsGameData->SetSetupText(dsGameSetup->setupText); CGameServer server(dsClientSetup, dsGameData, dsGameSetup); while (!server.HasGameID()) { // wait until gameID has been generated or // a timeout occurs (if no clients connect) if (server.HasFinished()) break; spring_sleep(spring_secs(sleepTime)); } while (!server.HasFinished()) { static bool printData = (server.GetDemoRecorder() != nullptr); if (printData) { printData = false; const std::unique_ptr<CDemoRecorder>& demoRec = server.GetDemoRecorder(); const std::uint8_t* gameID = (demoRec->GetFileHeader()).gameID; LOG("recording demo: %s", (demoRec->GetName()).c_str()); LOG("using mod: %s", (dsGameSetup->modName).c_str()); LOG("using map: %s", (dsGameSetup->mapName).c_str()); LOG("GameID: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", gameID[0], gameID[1], gameID[2], gameID[3], gameID[4], gameID[5], gameID[6], gameID[7], gameID[8], gameID[9], gameID[10], gameID[11], gameID[12], gameID[13], gameID[14], gameID[15]); } spring_secs(sleepTime).sleep(true); } } LOG("exiting"); FileSystemInitializer::Cleanup(); GlobalConfig::Deallocate(); DataDirLocater::FreeInstance(); spring_clock::PopTickRate(); LOG("exited"); } CATCH_SPRING_ERRORS return 0; }
void DataDirLocater::LocateDataDirs() { // Prepare the data-dirs defined in different places // environment variable std::string dd_env = ""; { char* env = getenv("SPRING_DATADIR"); if (env && *env) { dd_env = SubstEnvVars(env); } } // If this is true, ie the var is present in env, we will only add the dir // where both binary and unitysnc lib reside in Portable mode, // or the parent dir, if it is a versioned data-dir. const bool isolationMode = (getenv("SPRING_ISOLATED") != NULL); #if defined(UNITSYNC) const std::string dd_curWorkDir = Platform::GetModulePath(); #else // defined(UNITSYNC) const std::string dd_curWorkDir = Platform::GetProcessExecutablePath(); #endif // defined(UNITSYNC) #if defined(WIN32) // fetch my documents path TCHAR pathMyDocs[MAX_PATH]; SHGetFolderPath( NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, pathMyDocs); // fetch app-data path TCHAR pathAppData[MAX_PATH]; SHGetFolderPath( NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, pathAppData); std::string dd_myDocs = pathMyDocs; // e.g. F:\Dokumente und Einstellungen\Karl-Robert\Eigene Dateien\Spring dd_myDocs += "\\Spring"; std::string dd_myDocsMyGames = pathMyDocs; // My Documents\My Games seems to be the MS standard even if no official guidelines exist // most if not all new Games For Windows(TM) games use this dir dd_myDocsMyGames += "\\My Games\\Spring"; std::string dd_appData = pathAppData; // e.g. F:\Dokumente und Einstellungen\All Users\Anwendungsdaten\Spring dd_appData += "\\Spring"; #else // *nix (-OSX) // settings in /etc std::string dd_etc = ""; { FILE* fileH = ::fopen("/etc/spring/datadir", "r"); if (fileH) { const char whiteSpaces[3] = {'\t', ' ', '\0'}; char lineBuf[1024]; while (fgets(lineBuf, sizeof(lineBuf), fileH)) { char* newLineCharPos = strchr(lineBuf, '\n'); if (newLineCharPos) { // remove the new line char *newLineCharPos = '\0'; } // ignore lines consisting of only whitespaces if ((strlen(lineBuf) > 0) && strspn(lineBuf, whiteSpaces) != strlen(lineBuf)) { // append, separated by sPD (depending on OS): ';' or ':' dd_etc = dd_etc + (dd_etc.empty() ? "" : sPD) + SubstEnvVars(lineBuf); } } fclose(fileH); } } #endif // defined(WIN32), defined(MACOSX_BUNDLE), else // Construct the list of dataDirs from various sources. dataDirs.clear(); // The first dir added will be the writeable data dir. if (isolationMode) { AddCwdOrParentDir(dd_curWorkDir, true); // "./" or "../" } else { // same on all platforms AddDirs(dd_env); // ENV{SPRING_DATADIR} // user defined in spring config handler // (Linux: ~/.springrc, Windows: .\springsettings.cfg) AddDirs(SubstEnvVars(configHandler->GetString("SpringData"))); #ifdef WIN32 // All MS Windows variants AddCwdOrParentDir(dd_curWorkDir); // "./" or "../" AddDirs(dd_myDocsMyGames); // "C:/.../My Documents/My Games/Spring/" AddDirs(dd_myDocs); // "C:/.../My Documents/Spring/" AddDirs(dd_appData); // "C:/.../All Users/Applications/Spring/" #elif defined(MACOSX_BUNDLE) // Mac OS X Application Bundle (*.app) - single file install // directory structure (Apple standard): // Spring.app/Contents/MacOS/springlobby // Spring.app/Contents/Resources/bin/spring // Spring.app/Contents/Resources/lib/unitsync.dylib // Spring.app/Contents/Resources/share/games/spring/base/ // This corresponds to Spring.app/Contents/Resources/ const std::string bundleResourceDir = FileSystem::GetParent(dd_curWorkDir); // This has to correspond with the value in the build-script const std::string dd_curWorkDirData = bundleResourceDir + "/share/games/spring"; // we need this as default writeable dir, because the Bundle.pp dir // might not be writeable by the user starting the game AddDirs(SubstEnvVars("$HOME/.spring")); // "~/.spring/" AddDirs(dd_curWorkDirData); // "Spring.app/Contents/Resources/share/games/spring" AddDirs(dd_etc); // from /etc/spring/datadir #else // Linux, FreeBSD, Solaris, Apple non-bundle AddCwdOrParentDir(dd_curWorkDir); // "./" or "../" AddDirs(SubstEnvVars("$HOME/.spring")); // "~/.spring/" AddDirs(dd_etc); // from /etc/spring/datadir #endif #ifdef SPRING_DATADIR AddDirs(SubstEnvVars(SPRING_DATADIR)); // from -DSPRING_DATADIR #endif } // Figure out permissions of all dataDirs DeterminePermissions(); if (!writeDir) { // bail out const std::string errstr = "Not a single writable data directory found!\n\n" "Configure a writable data directory using either:\n" "- the SPRING_DATADIR environment variable,\n" #ifdef WIN32 "- a SpringData=C:/path/to/data declaration in spring's config file ./springsettings.cfg\n" "- by giving you write access to the installation directory"; #else "- a SpringData=/path/to/data declaration in ~/.springrc or\n" "- the configuration file /etc/spring/datadir"; #endif throw content_error(errstr); } // for now, chdir to the data directory as a safety measure: // Not only safety anymore, it's just easier if other code can safely assume that // writeDir == current working directory FileSystem::ChDir(GetWriteDir()->path.c_str()); // Initialize the log. Only after this moment log will be written to file. logOutput.Initialize(); // Logging MAY NOT start before the chdir, otherwise the logfile ends up // in the wrong directory. // Update: now it actually may start before, log has preInitLog. for (std::vector<DataDir>::const_iterator d = dataDirs.begin(); d != dataDirs.end(); ++d) { if (d->writable) { LOG("Using read-write data directory: %s", d->path.c_str()); // tag the cache dir const std::string cacheDir = d->path + "cache"; if (FileSystem::CreateDirectory(cacheDir)) { CacheDir::SetCacheDir(cacheDir, true); } } else { LOG("Using read-only data directory: %s", d->path.c_str()); } } }
static void option_parseOption(const LuaTable& root, int index, Option& opt, std::set<string>& optionsSet) { const LuaTable& optTbl = root.SubTable(index); if (!optTbl.IsValid()) { throw content_error("parseOption: subtable " + IntToString(index) + " is invalid"); } // common options properties opt.key = optTbl.GetString("key", ""); if (opt.key.empty() || (opt.key.find_first_of(Option_badKeyChars) != string::npos)) { throw content_error("parseOption: (key=\"" + opt.key + "\") empty key or key contains bad characters (\"" + Option_badKeyChars + "\")"); } opt.key = StringToLower(opt.key); opt.scope = optTbl.GetString("scope", "scope"); if (opt.scope.find_first_of(Option_badKeyChars) != string::npos) { throw content_error("parseOption: (key=" + opt.key + ") scope contains bad characters (\"" + Option_badKeyChars + "\"): \"" + opt.scope + "\""); } opt.scope = StringToLower(opt.scope); if (optionsSet.find(opt.key) != optionsSet.end()) { throw content_error("parseOption: key \"" + opt.key + "\" exists already"); } opt.name = optTbl.GetString("name", opt.key); if (opt.name.empty()) { throw content_error("parseOption: (key=" + opt.key + ") empty name"); } opt.desc = optTbl.GetString("desc", opt.name); opt.section = optTbl.GetString("section", ""); opt.style = optTbl.GetString("style", ""); opt.type = optTbl.GetString("type", ""); opt.type = StringToLower(opt.type); // option type specific properties if (opt.type == "bool") { opt.typeCode = opt_bool; opt.boolDef = optTbl.GetBool("def", false); } else if (opt.type == "number") { opt.typeCode = opt_number; opt.numberDef = optTbl.GetFloat("def", 0.0f); opt.numberMin = optTbl.GetFloat("min", -1.0e30f); opt.numberMax = optTbl.GetFloat("max", +1.0e30f); opt.numberStep = optTbl.GetFloat("step", 0.0f); } else if (opt.type == "string") { opt.typeCode = opt_string; opt.stringDef = optTbl.GetString("def", ""); opt.stringMaxLen = optTbl.GetInt("maxlen", 0); } else if (opt.type == "list") { opt.typeCode = opt_list; const LuaTable& listTbl = optTbl.SubTable("items"); if (!listTbl.IsValid()) { throw content_error("parseOption: (key=" + opt.key + ") subtables: items invalid"); } for (int i = 1; listTbl.KeyExists(i); i++) { OptionListItem item; // string format item.key = listTbl.GetString(i, ""); if (!item.key.empty() && (item.key.find_first_of(Option_badKeyChars) == string::npos)) { item.name = item.key; item.desc = item.name; opt.list.push_back(item); continue; } // table format (name & desc) const LuaTable& itemTbl = listTbl.SubTable(i); if (!itemTbl.IsValid()) { throw content_error("parseOption: (key=" + opt.key + ") subtables: subtable " + IntToString(i) + " contains invalid items"); } item.key = itemTbl.GetString("key", ""); if (item.key.empty() || (item.key.find_first_of(Option_badKeyChars) != string::npos)) { throw content_error("parseOption: (key=" + opt.key + ") subtables: (key=\"" + item.key + "\") empty key or key contains bad characters (\"" + Option_badKeyChars + "\")"); } item.key = StringToLower(item.key); item.name = itemTbl.GetString("name", item.key); if (item.name.empty()) { throw content_error("parseOption: (key=" + opt.key + ") subtables: (key=" + item.key + ") empty name"); } item.desc = itemTbl.GetString("desc", item.name); opt.list.push_back(item); } if (opt.list.size() <= 0) { throw content_error("parseOption: (key=" + opt.key + ") subtables: empty list"); } opt.listDef = optTbl.GetString("def", opt.list[0].name); } else if (opt.type == "section") { opt.typeCode = opt_section; } else { throw content_error("parseOption: (key=" + opt.key + ") unknown type \"" + opt.type + "\""); } optionsSet.insert(opt.key); }
CAdvWater::CAdvWater(bool loadShader) { if (!FBO::IsSupported()) { throw content_error("Water Error: missing FBO support"); } glGenTextures(1, &reflectTexture); unsigned char* scrap = new unsigned char[512 * 512 * 4]; glBindTexture(GL_TEXTURE_2D, reflectTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, scrap); glGenTextures(1, &bumpTexture); glBindTexture(GL_TEXTURE_2D, bumpTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, scrap); glGenTextures(4, rawBumpTexture); for (int y = 0; y < 64; ++y) { for (int x = 0; x < 64; ++x) { scrap[(y*64 + x)*4 + 0] = 128; scrap[(y*64 + x)*4 + 1] = (unsigned char)(sin(y*PI*2.0f/64.0f)*128 + 128); scrap[(y*64 + x)*4 + 2] = 0; scrap[(y*64 + x)*4 + 3] = 255; } } glBindTexture(GL_TEXTURE_2D, rawBumpTexture[0]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, scrap); for (int y = 0; y < 64; ++y) { for (int x = 0; x < 64; ++x) { const float ang = 26.5f*PI/180.0f; const float pos = y*2+x; scrap[(y*64 + x)*4 + 0] = (unsigned char)((sin(pos*PI*2.0f/64.0f))*128*sin(ang)) + 128; scrap[(y*64 + x)*4 + 1] = (unsigned char)((sin(pos*PI*2.0f/64.0f))*128*cos(ang)) + 128; } } glBindTexture(GL_TEXTURE_2D, rawBumpTexture[1]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, scrap); for (int y = 0; y < 64; ++y) { for (int x = 0; x < 64; ++x) { const float ang = -19*PI/180.0f; const float pos = 3*y - x; scrap[(y*64 + x)*4 + 0] = (unsigned char)((sin(pos*PI*2.0f/64.0f))*128*sin(ang)) + 128; scrap[(y*64 + x)*4 + 1] = (unsigned char)((sin(pos*PI*2.0f/64.0f))*128*cos(ang)) + 128; } } glBindTexture(GL_TEXTURE_2D, rawBumpTexture[2]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, scrap); delete[] scrap; if (loadShader) { waterFP = LoadFragmentProgram("ARB/water.fp"); } waterSurfaceColor = mapInfo->water.surfaceColor; reflectFBO.Bind(); reflectFBO.AttachTexture(reflectTexture, GL_TEXTURE_2D, GL_COLOR_ATTACHMENT0_EXT); reflectFBO.CreateRenderBuffer(GL_DEPTH_ATTACHMENT_EXT, GL_DEPTH_COMPONENT32, 512, 512); bumpFBO.Bind(); bumpFBO.AttachTexture(bumpTexture, GL_TEXTURE_2D, GL_COLOR_ATTACHMENT0_EXT); FBO::Unbind(); if (!bumpFBO.IsValid()) { throw content_error("Water Error: Invalid FBO"); } }
void CCustomExplosionGenerator::ParseExplosionCode( CCustomExplosionGenerator::ProjectileSpawnInfo* psi, int offset, boost::shared_ptr<creg::IType> type, const string& script, string& code) { string::size_type end = script.find(';', 0); string vastr = script.substr(0, end); if (vastr == "dir") { // first see if we can match any keywords // if the user uses a keyword assume he knows that it is put on the right datatype for now code += OP_DIR; boost::uint16_t ofs = offset; code.append((char*) &ofs, (char*) &ofs + 2); } else if (dynamic_cast<creg::BasicType*>(type.get())) { creg::BasicType *bt = (creg::BasicType*)type.get(); if (bt->id != creg::crInt && bt->id != creg::crFloat && bt->id != creg::crUChar && bt->id != creg::crBool) { throw content_error("Projectile properties other than int, float and uchar, are not supported (" + script + ")"); return; } int p = 0; while (p < script.length()) { char opcode; char c; do { c = script[p++]; } while (c == ' '); bool useInt = false; if (c == 'i') opcode = OP_INDEX; else if (c == 'r') opcode = OP_RAND; else if (c == 'd') opcode = OP_DAMAGE; else if (c == 'm') opcode = OP_SAWTOOTH; else if (c == 'k') opcode = OP_DISCRETE; else if (c == 's') opcode = OP_SINE; else if (c == 'y') {opcode = OP_YANK; useInt = true;} else if (c == 'x') {opcode = OP_MULTIPLY; useInt = true;} else if (c == 'a') {opcode = OP_ADDBUFF; useInt = true;} else if (c == 'p') opcode = OP_POW; else if (c == 'q') {opcode = OP_POWBUFF; useInt = true;} else if (isdigit(c) || c == '.' || c == '-') { opcode = OP_ADD; p--; } else { logOutput.Print("[CCEG::ParseExplosionCode] WARNING: unknown op-code \"" + string(1, c) + "\" in \"" + script + "\""); continue; } char* endp; if (!useInt) { const float v = (float)strtod(&script[p], &endp); p += endp - &script[p]; code += opcode; code.append((char*) &v, ((char*) &v) + 4); } else { const int v = std::max(0, std::min(16, (int)strtol(&script[p], &endp, 10))); p += endp - &script[p]; code += opcode; code.append((char*) &v, ((char*) &v) + 4); } } switch (bt->id) { case creg::crInt: code.push_back(OP_STOREI); break; case creg::crBool: code.push_back(OP_STOREI); break; case creg::crFloat: code.push_back(OP_STOREF); break; case creg::crUChar: code.push_back(OP_STOREC); break; default: throw content_error("Explosion script variable is of unsupported type. " "Contact the Spring team to fix this."); break; } boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } else if (dynamic_cast<creg::ObjectInstanceType*>(type.get())) { creg::ObjectInstanceType *oit = (creg::ObjectInstanceType *)type.get(); string::size_type start = 0; for (creg::Class* c = oit->objectClass; c; c=c->base) { for (int a = 0; a < c->members.size(); a++) { string::size_type end = script.find(',', start+1); ParseExplosionCode(psi, offset + c->members [a]->offset, c->members[a]->type, script.substr(start,end-start), code); start = end+1; if (start >= script.length()) { break; } } if (start >= script.length()) { break; } } } else if (dynamic_cast<creg::StaticArrayBaseType*>(type.get())) { creg::StaticArrayBaseType *sat = (creg::StaticArrayBaseType*)type.get(); string::size_type start = 0; for (unsigned int i=0; i < sat->size; i++) { string::size_type end = script.find(',', start+1); ParseExplosionCode(psi, offset + sat->elemSize * i, sat->elemType, script.substr(start, end-start), code); start = end+1; if (start >= script.length()) { break; } } } else { if (type->GetName() == "AtlasedTexture*") { string::size_type end = script.find(';', 0); string texname = script.substr(0, end); void* tex = projectileDrawer->textureAtlas->GetTexturePtr(texname); code += OP_LOADP; code.append((char*)(&tex), ((char*)(&tex)) + sizeof(void*)); code += OP_STOREP; boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } else if (type->GetName() == "GroundFXTexture*") { string::size_type end = script.find(';', 0); string texname = script.substr(0, end); void* tex = projectileDrawer->groundFXAtlas->GetTexturePtr(texname); code += OP_LOADP; code.append((char*)(&tex), ((char*)(&tex)) + sizeof(void*)); code += OP_STOREP; boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } else if (type->GetName() == "CColorMap*") { string::size_type end = script.find(';', 0); string colorstring = script.substr(0, end); void* colormap = CColorMap::LoadFromDefString(colorstring); code += OP_LOADP; code.append((char*)(&colormap), ((char*)(&colormap)) + sizeof(void*)); code += OP_STOREP; boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } else if (type->GetName() == "IExplosionGenerator*") { string::size_type end = script.find(';', 0); string name = script.substr(0, end); void* explgen = explGenHandler->LoadGenerator(name); code += OP_LOADP; code.append((char*)(&explgen), ((char*)(&explgen)) + sizeof(void*)); code += OP_STOREP; boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } } }
void CCustomExplosionGenerator::ParseExplosionCode( CCustomExplosionGenerator::ProjectileSpawnInfo* psi, const int offset, const boost::shared_ptr<creg::IType> type, const string& script, string& code) { // strtod&co expect C-style strings with NULLs, // c_str() is guaranteed to be NULL-terminated // (whether .data() == .c_str() depends on the // implementation of std::string) const char* scriptStr = script.c_str(); string::size_type end = script.find(';', 0); string vastr = script.substr(0, end); if (vastr == "dir") { // first see if we can match any keywords // if the user uses a keyword assume he knows that it is put on the right datatype for now code += OP_DIR; boost::uint16_t ofs = offset; code.append((char*) &ofs, (char*) &ofs + 2); } else if (dynamic_cast<creg::BasicType*>(type.get())) { const creg::BasicType* basicType = (creg::BasicType*) type.get(); const bool legalType = (basicType->id == creg::crInt ) || (basicType->id == creg::crFloat) || (basicType->id == creg::crUChar) || (basicType->id == creg::crBool ); if (!legalType) { throw content_error("[CCEG::ParseExplosionCode] projectile type-properties other than int, float, uchar, or bool are not supported (" + script + ")"); return; } int p = 0; while (p < script.length()) { char opcode = OP_END; char c = script[p++]; // consume whitespace if (c == ' ') continue; bool useInt = false; if (c == 'i') opcode = OP_INDEX; else if (c == 'r') opcode = OP_RAND; else if (c == 'd') opcode = OP_DAMAGE; else if (c == 'm') opcode = OP_SAWTOOTH; else if (c == 'k') opcode = OP_DISCRETE; else if (c == 's') opcode = OP_SINE; else if (c == 'p') opcode = OP_POW; else if (c == 'y') { opcode = OP_YANK; useInt = true; } else if (c == 'x') { opcode = OP_MULTIPLY; useInt = true; } else if (c == 'a') { opcode = OP_ADDBUFF; useInt = true; } else if (c == 'q') { opcode = OP_POWBUFF; useInt = true; } else if (isdigit(c) || c == '.' || c == '-') { opcode = OP_ADD; p--; } else { const char* fmt = "[CCEG::ParseExplosionCode] unknown op-code \"%c\" in \"%s\" at index %d"; LOG_L(L_WARNING, fmt, c, scriptStr, p); continue; } // be sure to exit cleanly if there are no more operators or operands if (p >= script.size()) continue; char* endp = NULL; if (!useInt) { const float v = (float)strtod(&scriptStr[p], &endp); p += (endp - &scriptStr[p]); code += opcode; code.append((char*) &v, ((char*) &v) + 4); } else { const int v = std::max(0, std::min(16, (int)strtol(&scriptStr[p], &endp, 10))); p += (endp - &scriptStr[p]); code += opcode; code.append((char*) &v, ((char*) &v) + 4); } } switch (basicType->id) { case creg::crInt: code.push_back(OP_STOREI); break; case creg::crBool: code.push_back(OP_STOREI); break; case creg::crFloat: code.push_back(OP_STOREF); break; case creg::crUChar: code.push_back(OP_STOREC); break; default: break; } boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } else if (dynamic_cast<creg::ObjectInstanceType*>(type.get())) { creg::ObjectInstanceType *oit = (creg::ObjectInstanceType *)type.get(); string::size_type start = 0; for (creg::Class* c = oit->objectClass; c; c=c->base) { for (int a = 0; a < c->members.size(); a++) { string::size_type end = script.find(',', start+1); ParseExplosionCode(psi, offset + c->members [a]->offset, c->members[a]->type, script.substr(start,end-start), code); start = end+1; if (start >= script.length()) { break; } } if (start >= script.length()) { break; } } } else if (dynamic_cast<creg::StaticArrayBaseType*>(type.get())) { creg::StaticArrayBaseType *sat = (creg::StaticArrayBaseType*)type.get(); string::size_type start = 0; for (unsigned int i=0; i < sat->size; i++) { string::size_type end = script.find(',', start+1); ParseExplosionCode(psi, offset + sat->elemSize * i, sat->elemType, script.substr(start, end-start), code); start = end+1; if (start >= script.length()) { break; } } } else { if (type->GetName() == "AtlasedTexture*") { string::size_type end = script.find(';', 0); string texname = script.substr(0, end); // this memory is managed by textureAtlas (CTextureAtlas) void* tex = projectileDrawer->textureAtlas->GetTexturePtr(texname); code += OP_LOADP; code.append((char*)(&tex), ((char*)(&tex)) + sizeof(void*)); code += OP_STOREP; boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } else if (type->GetName() == "GroundFXTexture*") { string::size_type end = script.find(';', 0); string texname = script.substr(0, end); // this memory is managed by groundFXAtlas (CTextureAtlas) void* tex = projectileDrawer->groundFXAtlas->GetTexturePtr(texname); code += OP_LOADP; code.append((char*)(&tex), ((char*)(&tex)) + sizeof(void*)); code += OP_STOREP; boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } else if (type->GetName() == "CColorMap*") { string::size_type end = script.find(';', 0); string colorstring = script.substr(0, end); // gets stored and deleted at game end from inside CColorMap void* colormap = CColorMap::LoadFromDefString(colorstring); code += OP_LOADP; code.append((char*)(&colormap), ((char*)(&colormap)) + sizeof(void*)); code += OP_STOREP; boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } else if (type->GetName() == "IExplosionGenerator*") { string::size_type end = script.find(';', 0); string name = script.substr(0, end); IExplosionGenerator* explGen = explGenHandler->LoadGenerator(name); spawnExplGens.push_back(explGen); // these will be unloaded in ~CCustomExplosionGenerator() void* explGenRaw = (void*) explGen; code += OP_LOADP; code.append((char*)(&explGenRaw), ((char*)(&explGenRaw)) + sizeof(void*)); code += OP_STOREP; boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } } }
CProjectileDrawer::CProjectileDrawer(): CEventClient("[CProjectileDrawer]", 123456, false) { eventHandler.AddClient(this); loadscreen->SetLoadMessage("Creating Projectile Textures"); textureAtlas = new CTextureAtlas(2048, 2048); groundFXAtlas = new CTextureAtlas(2048, 2048); LuaParser resourcesParser("gamedata/resources.lua", SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP); LuaParser mapResParser("gamedata/resources_map.lua", SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP); resourcesParser.Execute(); const LuaTable& resTable = resourcesParser.GetRoot(); const LuaTable& resGraphicsTable = resTable.SubTable("graphics"); const LuaTable& resProjTexturesTable = resGraphicsTable.SubTable("projectileTextures"); const LuaTable& resSmokeTexturesTable = resGraphicsTable.SubTable("smoke"); const LuaTable& resGroundFXTexturesTable = resGraphicsTable.SubTable("groundfx"); // used to block resources_map.* from overriding any of // resources.lua:{projectile, smoke, groundfx}textures, // as well as various defaults (repulsegfxtexture, etc) std::set<std::string> blockedTexNames; ParseAtlasTextures(true, resProjTexturesTable, blockedTexNames, textureAtlas); ParseAtlasTextures(true, resGroundFXTexturesTable, blockedTexNames, groundFXAtlas); int smokeTexCount = -1; { // get the smoke textures, hold the count in 'smokeTexCount' if (resSmokeTexturesTable.IsValid()) { for (smokeTexCount = 0; true; smokeTexCount++) { const std::string& tex = resSmokeTexturesTable.GetString(smokeTexCount + 1, ""); if (tex.empty()) { break; } const std::string texName = "bitmaps/" + tex; const std::string smokeName = "ismoke" + IntToString(smokeTexCount, "%02i"); textureAtlas->AddTexFromFile(smokeName, texName); blockedTexNames.insert(StringToLower(smokeName)); } } else { // setup the defaults for (smokeTexCount = 0; smokeTexCount < 12; smokeTexCount++) { const std::string smokeNum = IntToString(smokeTexCount, "%02i"); const std::string smokeName = "ismoke" + smokeNum; const std::string texName = "bitmaps/smoke/smoke" + smokeNum + ".tga"; textureAtlas->AddTexFromFile(smokeName, texName); blockedTexNames.insert(StringToLower(smokeName)); } } if (smokeTexCount <= 0) { // this needs to be an exception, other code // assumes at least one smoke-texture exists throw content_error("missing smoke textures"); } } { // shield-texture memory char perlinTexMem[128][128][4]; for (int y = 0; y < 128; y++) { for (int x = 0; x < 128; x++) { perlinTexMem[y][x][0] = 70; perlinTexMem[y][x][1] = 70; perlinTexMem[y][x][2] = 70; perlinTexMem[y][x][3] = 70; } } textureAtlas->AddTexFromMem("perlintex", 128, 128, CTextureAtlas::RGBA32, perlinTexMem); } blockedTexNames.insert("perlintex"); blockedTexNames.insert("flare"); blockedTexNames.insert("explo"); blockedTexNames.insert("explofade"); blockedTexNames.insert("heatcloud"); blockedTexNames.insert("laserend"); blockedTexNames.insert("laserfalloff"); blockedTexNames.insert("randdots"); blockedTexNames.insert("smoketrail"); blockedTexNames.insert("wake"); blockedTexNames.insert("perlintex"); blockedTexNames.insert("flame"); blockedTexNames.insert("sbtrailtexture"); blockedTexNames.insert("missiletrailtexture"); blockedTexNames.insert("muzzleflametexture"); blockedTexNames.insert("repulsetexture"); blockedTexNames.insert("dguntexture"); blockedTexNames.insert("flareprojectiletexture"); blockedTexNames.insert("sbflaretexture"); blockedTexNames.insert("missileflaretexture"); blockedTexNames.insert("beamlaserflaretexture"); blockedTexNames.insert("bubbletexture"); blockedTexNames.insert("geosquaretexture"); blockedTexNames.insert("gfxtexture"); blockedTexNames.insert("projectiletexture"); blockedTexNames.insert("repulsegfxtexture"); blockedTexNames.insert("sphereparttexture"); blockedTexNames.insert("torpedotexture"); blockedTexNames.insert("wrecktexture"); blockedTexNames.insert("plasmatexture"); if (mapResParser.Execute()) { // allow map-specified atlas textures (for gaia-projectiles and ground-flashes) const LuaTable& mapResTable = mapResParser.GetRoot(); const LuaTable& mapResGraphicsTable = mapResTable.SubTable("graphics"); const LuaTable& mapResProjTexturesTable = mapResGraphicsTable.SubTable("projectileTextures"); const LuaTable& mapResGroundFXTexturesTable = mapResGraphicsTable.SubTable("groundfx"); ParseAtlasTextures(false, mapResProjTexturesTable, blockedTexNames, textureAtlas); ParseAtlasTextures(false, mapResGroundFXTexturesTable, blockedTexNames, groundFXAtlas); } if (!textureAtlas->Finalize()) { LOG_L(L_ERROR, "Could not finalize projectile-texture atlas. Use less/smaller textures."); } flaretex = textureAtlas->GetTexturePtr("flare"); explotex = textureAtlas->GetTexturePtr("explo"); explofadetex = textureAtlas->GetTexturePtr("explofade"); heatcloudtex = textureAtlas->GetTexturePtr("heatcloud"); laserendtex = textureAtlas->GetTexturePtr("laserend"); laserfallofftex = textureAtlas->GetTexturePtr("laserfalloff"); randdotstex = textureAtlas->GetTexturePtr("randdots"); smoketrailtex = textureAtlas->GetTexturePtr("smoketrail"); waketex = textureAtlas->GetTexturePtr("wake"); perlintex = textureAtlas->GetTexturePtr("perlintex"); flametex = textureAtlas->GetTexturePtr("flame"); for (int i = 0; i < smokeTexCount; i++) { const std::string smokeName = "ismoke" + IntToString(i, "%02i"); const AtlasedTexture* smokeTex = textureAtlas->GetTexturePtr(smokeName); smoketex.push_back(smokeTex); } #define GETTEX(t, b) (textureAtlas->GetTexturePtrWithBackup((t), (b))) sbtrailtex = GETTEX("sbtrailtexture", "smoketrail" ); missiletrailtex = GETTEX("missiletrailtexture", "smoketrail" ); muzzleflametex = GETTEX("muzzleflametexture", "explo" ); repulsetex = GETTEX("repulsetexture", "explo" ); dguntex = GETTEX("dguntexture", "flare" ); flareprojectiletex = GETTEX("flareprojectiletexture", "flare" ); sbflaretex = GETTEX("sbflaretexture", "flare" ); missileflaretex = GETTEX("missileflaretexture", "flare" ); beamlaserflaretex = GETTEX("beamlaserflaretexture", "flare" ); bubbletex = GETTEX("bubbletexture", "circularthingy"); geosquaretex = GETTEX("geosquaretexture", "circularthingy"); gfxtex = GETTEX("gfxtexture", "circularthingy"); projectiletex = GETTEX("projectiletexture", "circularthingy"); repulsegfxtex = GETTEX("repulsegfxtexture", "circularthingy"); sphereparttex = GETTEX("sphereparttexture", "circularthingy"); torpedotex = GETTEX("torpedotexture", "circularthingy"); wrecktex = GETTEX("wrecktexture", "circularthingy"); plasmatex = GETTEX("plasmatexture", "circularthingy"); #undef GETTEX if (!groundFXAtlas->Finalize()) { LOG_L(L_ERROR, "Could not finalize groundFX texture atlas. Use less/smaller textures."); } groundflashtex = groundFXAtlas->GetTexturePtr("groundflash"); groundringtex = groundFXAtlas->GetTexturePtr("groundring"); seismictex = groundFXAtlas->GetTexturePtr("seismic"); for (int a = 0; a < 4; ++a) { perlinBlend[a] = 0.0f; } { unsigned char tempmem[4 * 16 * 16] = {0}; for (int a = 0; a < 8; ++a) { glGenTextures(1, &perlinTex[a]); glBindTexture(GL_TEXTURE_2D, perlinTex[a]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 16,16, 0, GL_RGBA, GL_UNSIGNED_BYTE, tempmem); } } drawPerlinTex = false; if (perlinFB.IsValid()) { // we never refresh the full texture (just the perlin part). So we need to reload it then. perlinFB.reloadOnAltTab = true; perlinFB.Bind(); perlinFB.AttachTexture(textureAtlas->gltex); drawPerlinTex = perlinFB.CheckStatus("PERLIN"); perlinFB.Unbind(); } modelRenderers.resize(MODELTYPE_OTHER, NULL); for (int modelType = MODELTYPE_3DO; modelType < MODELTYPE_OTHER; modelType++) { modelRenderers[modelType] = IWorldObjectModelRenderer::GetInstance(modelType); } }
void CUnitDefHandler::ParseUnitDefTable(const LuaTable& udTable, const string& unitName, int id) { UnitDef& ud = unitDefs[id]; // allocate and fill ud->unitImage ud.buildPicName = udTable.GetString("buildPic", ""); ud.humanName = udTable.GetString("name", ""); if (ud.humanName.empty()) { const string errmsg = "missing 'name' parameter for the " + unitName + " unitdef"; throw content_error(errmsg); } ud.filename = udTable.GetString("filename", ""); if (ud.filename.empty()) { const string errmsg = "missing 'filename' parameter for the" + unitName + " unitdef"; throw content_error(errmsg); } ud.tooltip = udTable.GetString("description", ud.name); const string decoy = udTable.GetString("decoyFor", ""); if (!decoy.empty()) { decoyNameMap[ud.name] = StringToLower(decoy); } ud.gaia = udTable.GetString("gaia", ""); ud.isCommander = udTable.GetBool("commander", false); if (ud.isCommander && gameSetup) { ud.metalStorage = udTable.GetFloat("metalStorage", gameSetup->startMetal); ud.energyStorage = udTable.GetFloat("energyStorage", gameSetup->startEnergy); } else { ud.metalStorage = udTable.GetFloat("metalStorage", 0.0f); ud.energyStorage = udTable.GetFloat("energyStorage", 0.0f); } ud.extractsMetal = udTable.GetFloat("extractsMetal", 0.0f); ud.windGenerator = udTable.GetFloat("windGenerator", 0.0f); ud.tidalGenerator = udTable.GetFloat("tidalGenerator", 0.0f); ud.metalUpkeep = udTable.GetFloat("metalUse", 0.0f); ud.energyUpkeep = udTable.GetFloat("energyUse", 0.0f); ud.metalMake = udTable.GetFloat("metalMake", 0.0f); ud.makesMetal = udTable.GetFloat("makesMetal", 0.0f); ud.energyMake = udTable.GetFloat("energyMake", 0.0f); ud.health = udTable.GetFloat("maxDamage", 0.0f); ud.autoHeal = udTable.GetFloat("autoHeal", 0.0f) * (16.0f / GAME_SPEED); ud.idleAutoHeal = udTable.GetFloat("idleAutoHeal", 10.0f) * (16.0f / GAME_SPEED); ud.idleTime = udTable.GetInt("idleTime", 600); ud.buildangle = udTable.GetInt("buildAngle", 0); ud.isMetalMaker = (ud.makesMetal >= 1 && ud.energyUpkeep > ud.makesMetal * 40); ud.controlRadius = 32; ud.losHeight = 20; ud.metalCost = udTable.GetFloat("buildCostMetal", 0.0f); if (ud.metalCost < 1.0f) { ud.metalCost = 1.0f; //avoid some nasty divide by 0 etc } ud.mass = udTable.GetFloat("mass", 0.0f); if (ud.mass <= 0.0f) { ud.mass=ud.metalCost; } ud.energyCost = udTable.GetFloat("buildCostEnergy", 0.0f); ud.buildTime = udTable.GetFloat("buildTime", 0.0f); if (ud.buildTime < 1.0f) { ud.buildTime = 1.0f; //avoid some nasty divide by 0 etc } ud.aihint = id; // FIXME? (as noted in SelectedUnits.cpp, aihint is ignored) ud.cobID = udTable.GetInt("cobID", -1); ud.losRadius = udTable.GetFloat("sightDistance", 0.0f) * modInfo.losMul / (SQUARE_SIZE * (1 << modInfo.losMipLevel)); ud.airLosRadius = udTable.GetFloat("airSightDistance", -1.0f); if (ud.airLosRadius == -1.0f) { ud.airLosRadius=udTable.GetFloat("sightDistance", 0.0f) * modInfo.airLosMul * 1.5f / (SQUARE_SIZE * (1 << modInfo.airMipLevel)); } else { ud.airLosRadius = ud.airLosRadius * modInfo.airLosMul / (SQUARE_SIZE * (1 << modInfo.airMipLevel)); } ud.canSubmerge = udTable.GetBool("canSubmerge", false); ud.canfly = udTable.GetBool("canFly", false); ud.canmove = udTable.GetBool("canMove", false); ud.reclaimable = udTable.GetBool("reclaimable", true); ud.capturable = udTable.GetBool("capturable", true); ud.repairable = udTable.GetBool("repairable", true); ud.canAttack = udTable.GetBool("canAttack", true); ud.canFight = udTable.GetBool("canFight", true); ud.canPatrol = udTable.GetBool("canPatrol", true); ud.canGuard = udTable.GetBool("canGuard", true); ud.canRepeat = udTable.GetBool("canRepeat", true); ud.builder = udTable.GetBool("builder", true); ud.canRestore = udTable.GetBool("canRestore", ud.builder); ud.canRepair = udTable.GetBool("canRepair", ud.builder); ud.canReclaim = udTable.GetBool("canReclaim", ud.builder); ud.canAssist = udTable.GetBool("canAssist", ud.builder); ud.canBeAssisted = udTable.GetBool("canBeAssisted", true); ud.canSelfRepair = udTable.GetBool("canSelfRepair", false); ud.fullHealthFactory = udTable.GetBool("fullHealthFactory", false); ud.factoryHeadingTakeoff = udTable.GetBool("factoryHeadingTakeoff", true); ud.upright = udTable.GetBool("upright", false); ud.collide = udTable.GetBool("collide", true); ud.onoffable = udTable.GetBool("onoffable", false); ud.maxSlope = udTable.GetFloat("maxSlope", 0.0f); ud.maxHeightDif = 40 * tan(ud.maxSlope * (PI / 180)); ud.maxSlope = cos(ud.maxSlope * (PI / 180)); ud.minWaterDepth = udTable.GetFloat("minWaterDepth", -10e6f); ud.maxWaterDepth = udTable.GetFloat("maxWaterDepth", +10e6f); ud.minCollisionSpeed = udTable.GetFloat("minCollisionSpeed", 1.0f); ud.slideTolerance = udTable.GetFloat("slideTolerance", 0.0f); // disabled ud.pushResistant = udTable.GetBool("pushResistant", false); ud.waterline = udTable.GetFloat("waterline", 0.0f); if ((ud.waterline >= 5.0f) && ud.canmove) { // make subs travel at somewhat larger depths // to reduce vulnerability to surface weapons ud.waterline += 10.0f; } ud.canSelfD = udTable.GetBool("canSelfDestruct", true); ud.selfDCountdown = udTable.GetInt("selfDestructCountdown", 5); ud.speed = udTable.GetFloat("maxVelocity", 0.0f) * GAME_SPEED; ud.rSpeed = udTable.GetFloat("maxReverseVelocity", 0.0f) * GAME_SPEED; ud.speed = fabs(ud.speed); ud.rSpeed = fabs(ud.rSpeed); ud.maxAcc = fabs(udTable.GetFloat("acceleration", 0.5f)); // no negative values ud.maxDec = fabs(udTable.GetFloat("brakeRate", 3.0f * ud.maxAcc)) * (ud.canfly? 0.1f: 1.0f); // no negative values ud.turnRate = udTable.GetFloat("turnRate", 0.0f); ud.turnInPlace = udTable.GetBool( "turnInPlace", true); ud.turnInPlaceDistance = udTable.GetFloat("turnInPlaceDistance", 350.f); ud.turnInPlaceSpeedLimit = udTable.GetFloat("turnInPlaceSpeedLimit", 15.f); const bool noAutoFire = udTable.GetBool("noAutoFire", false); ud.canFireControl = udTable.GetBool("canFireControl", !noAutoFire); ud.fireState = udTable.GetInt("fireState", ud.canFireControl ? -1 : 2); ud.fireState = std::min(ud.fireState,2); ud.moveState = udTable.GetInt("moveState", (ud.canmove && ud.speed>0.0f) ? -1 : 1); ud.moveState = std::min(ud.moveState,2); ud.buildRange3D = udTable.GetBool("buildRange3D", false); ud.buildDistance = udTable.GetFloat("buildDistance", 128.0f); ud.buildDistance = std::max(128.0f, ud.buildDistance); ud.buildSpeed = udTable.GetFloat("workerTime", 0.0f); ud.repairSpeed = udTable.GetFloat("repairSpeed", ud.buildSpeed); ud.maxRepairSpeed = udTable.GetFloat("maxRepairSpeed", 1e20f); ud.reclaimSpeed = udTable.GetFloat("reclaimSpeed", ud.buildSpeed); ud.resurrectSpeed = udTable.GetFloat("resurrectSpeed", ud.buildSpeed); ud.captureSpeed = udTable.GetFloat("captureSpeed", ud.buildSpeed); ud.terraformSpeed = udTable.GetFloat("terraformSpeed", ud.buildSpeed); ud.flankingBonusMode = udTable.GetInt("flankingBonusMode", modInfo.flankingBonusModeDefault); ud.flankingBonusMax = udTable.GetFloat("flankingBonusMax", 1.9f); ud.flankingBonusMin = udTable.GetFloat("flankingBonusMin", 0.9); ud.flankingBonusDir = udTable.GetFloat3("flankingBonusDir", float3(0.0f, 0.0f, 1.0f)); ud.flankingBonusMobilityAdd = udTable.GetFloat("flankingBonusMobilityAdd", 0.01f); ud.armoredMultiple = udTable.GetFloat("damageModifier", 1.0f); ud.armorType = damageArrayHandler->GetTypeFromName(ud.name); ud.radarRadius = udTable.GetInt("radarDistance", 0); ud.sonarRadius = udTable.GetInt("sonarDistance", 0); ud.jammerRadius = udTable.GetInt("radarDistanceJam", 0); ud.sonarJamRadius = udTable.GetInt("sonarDistanceJam", 0); ud.stealth = udTable.GetBool("stealth", false); ud.sonarStealth = udTable.GetBool("sonarStealth", false); ud.targfac = udTable.GetBool("isTargetingUpgrade", false); ud.isFeature = udTable.GetBool("isFeature", false); ud.canResurrect = udTable.GetBool("canResurrect", false); ud.canCapture = udTable.GetBool("canCapture", false); ud.hideDamage = udTable.GetBool("hideDamage", false); ud.showPlayerName = udTable.GetBool("showPlayerName", false); ud.cloakCost = udTable.GetFloat("cloakCost", -1.0f); ud.cloakCostMoving = udTable.GetFloat("cloakCostMoving", -1.0f); if (ud.cloakCostMoving < 0) { ud.cloakCostMoving = ud.cloakCost; } ud.canCloak = (ud.cloakCost >= 0); ud.startCloaked = udTable.GetBool("initCloaked", false); ud.decloakDistance = udTable.GetFloat("minCloakDistance", 0.0f); ud.decloakSpherical = udTable.GetBool("decloakSpherical", true); ud.decloakOnFire = udTable.GetBool("decloakOnFire", true); ud.highTrajectoryType = udTable.GetInt("highTrajectory", 0); ud.canKamikaze = udTable.GetBool("kamikaze", false); ud.kamikazeDist = udTable.GetFloat("kamikazeDistance", -25.0f) + 25.0f; //we count 3d distance while ta count 2d distance so increase slightly ud.showNanoFrame = udTable.GetBool("showNanoFrame", true); ud.showNanoSpray = udTable.GetBool("showNanoSpray", true); ud.nanoColor = udTable.GetFloat3("nanoColor", float3(0.2f,0.7f,0.2f)); ud.canhover = udTable.GetBool("canHover", false); ud.floater = udTable.GetBool("floater", udTable.KeyExists("WaterLine")); ud.builder = udTable.GetBool("builder", false); if (ud.builder && !ud.buildSpeed) { // core anti is flagged as builder for some reason ud.builder = false; } ud.airStrafe = udTable.GetBool("airStrafe", true); ud.hoverAttack = udTable.GetBool("hoverAttack", false); ud.wantedHeight = udTable.GetFloat("cruiseAlt", 0.0f); ud.dlHoverFactor = udTable.GetFloat("airHoverFactor", -1.0f); ud.bankingAllowed = udTable.GetBool("bankingAllowed", true); ud.transportSize = udTable.GetInt("transportSize", 0); ud.minTransportSize = udTable.GetInt("minTransportSize", 0); ud.transportCapacity = udTable.GetInt("transportCapacity", 0); ud.isFirePlatform = udTable.GetBool("isFirePlatform", false); ud.isAirBase = udTable.GetBool("isAirBase", false); ud.loadingRadius = udTable.GetFloat("loadingRadius", 220.0f); ud.unloadSpread = udTable.GetFloat("unloadSpread", 1.0f); ud.transportMass = udTable.GetFloat("transportMass", 100000.0f); ud.minTransportMass = udTable.GetFloat("minTransportMass", 0.0f); ud.holdSteady = udTable.GetBool("holdSteady", true); ud.releaseHeld = udTable.GetBool("releaseHeld", false); ud.cantBeTransported = udTable.GetBool("cantBeTransported", false); ud.transportByEnemy = udTable.GetBool("transportByEnemy", true); ud.fallSpeed = udTable.GetFloat("fallSpeed", 0.2); ud.unitFallSpeed = udTable.GetFloat("unitFallSpeed", 0); ud.transportUnloadMethod = udTable.GetInt("transportUnloadMethod" , 0); // modrules transport settings if ((!modInfo.transportAir && ud.canfly) || (!modInfo.transportShip && ud.floater) || (!modInfo.transportHover && ud.canhover) || (!modInfo.transportGround && !ud.canhover && !ud.floater && !ud.canfly)) { ud.cantBeTransported = true; } ud.wingDrag = udTable.GetFloat("wingDrag", 0.07f); // drag caused by wings ud.wingDrag = std::min(1.0f, std::max(0.0f, ud.wingDrag)); ud.wingAngle = udTable.GetFloat("wingAngle", 0.08f); // angle between front and the wing plane ud.frontToSpeed = udTable.GetFloat("frontToSpeed", 0.1f); // fudge factor for lining up speed and front of plane ud.speedToFront = udTable.GetFloat("speedToFront", 0.07f); // fudge factor for lining up speed and front of plane ud.myGravity = udTable.GetFloat("myGravity", 0.4f); // planes are slower than real airplanes so lower gravity to compensate ud.crashDrag = udTable.GetFloat("crashDrag",0.005f); // drag used when crashing ud.crashDrag = std::min(1.0f, std::max(0.0f, ud.crashDrag)); ud.maxBank = udTable.GetFloat("maxBank", 0.8f); // max roll ud.maxPitch = udTable.GetFloat("maxPitch", 0.45f); // max pitch this plane tries to keep ud.turnRadius = udTable.GetFloat("turnRadius", 500.0f); // hint to the ai about how large turn radius this plane needs ud.verticalSpeed = udTable.GetFloat("verticalSpeed", 3.0f); // speed of takeoff and landing, at least for gunships ud.maxAileron = udTable.GetFloat("maxAileron", 0.015f); // turn speed around roll axis ud.maxElevator = udTable.GetFloat("maxElevator", 0.01f); // turn speed around pitch axis ud.maxRudder = udTable.GetFloat("maxRudder", 0.004f); // turn speed around yaw axis ud.maxFuel = udTable.GetFloat("maxFuel", 0.0f); //max flight time in seconds before aircraft must return to base ud.refuelTime = udTable.GetFloat("refuelTime", 5.0f); ud.minAirBasePower = udTable.GetFloat("minAirBasePower", 0.0f); ud.maxThisUnit = udTable.GetInt("unitRestricted", MAX_UNITS); string lname = StringToLower(ud.name); if (gameSetup->restrictedUnits.find(lname) != gameSetup->restrictedUnits.end()) { ud.maxThisUnit = std::min(ud.maxThisUnit, gameSetup->restrictedUnits.find(lname)->second); } ud.categoryString = udTable.GetString("category", ""); ud.category = CCategoryHandler::Instance()->GetCategories(udTable.GetString("category", "")); ud.noChaseCategory = CCategoryHandler::Instance()->GetCategories(udTable.GetString("noChaseCategory", "")); // logOutput.Print("Unit %s has cat %i",ud.humanName.c_str(),ud.category); const string iconName = udTable.GetString("iconType", "default"); ud.iconType = iconHandler->GetIcon(iconName); ud.shieldWeaponDef = NULL; ud.stockpileWeaponDef = NULL; ud.maxWeaponRange = 0.0f; ud.maxCoverage = 0.0f; const WeaponDef* noWeaponDef = weaponDefHandler->GetWeapon("NOWEAPON"); LuaTable weaponsTable = udTable.SubTable("weapons"); for (int w = 0; w < COB_MaxWeapons; w++) { LuaTable wTable; string name = weaponsTable.GetString(w + 1, ""); if (name.empty()) { wTable = weaponsTable.SubTable(w + 1); name = wTable.GetString("name", ""); } const WeaponDef* wd = NULL; if (!name.empty()) { wd = weaponDefHandler->GetWeapon(name); } if (wd == NULL) { if (w <= 3) { continue; // allow empty weapons among the first 3 } else { break; } } while (ud.weapons.size() < w) { if (!noWeaponDef) { logOutput.Print("Error: Spring requires a NOWEAPON weapon type " "to be present as a placeholder for missing weapons"); break; } else { ud.weapons.push_back(UnitDef::UnitDefWeapon()); ud.weapons.back().def = noWeaponDef; } } const string badTarget = wTable.GetString("badTargetCategory", ""); unsigned int btc = CCategoryHandler::Instance()->GetCategories(badTarget); const string onlyTarget = wTable.GetString("onlyTargetCategory", ""); unsigned int otc; if (onlyTarget.empty()) { otc = 0xffffffff; } else { otc = CCategoryHandler::Instance()->GetCategories(onlyTarget); } const unsigned int slaveTo = wTable.GetInt("slaveTo", 0); float3 mainDir = wTable.GetFloat3("mainDir", float3(1.0f, 0.0f, 0.0f)); mainDir.SafeNormalize(); const float angleDif = cos(wTable.GetFloat("maxAngleDif", 360.0f) * (PI / 360.0f)); const float fuelUse = wTable.GetFloat("fuelUsage", 0.0f); ud.weapons.push_back(UnitDef::UnitDefWeapon(name, wd, slaveTo, mainDir, angleDif, btc, otc, fuelUse)); if (wd->range > ud.maxWeaponRange) { ud.maxWeaponRange = wd->range; } if (wd->interceptor && wd->coverageRange > ud.maxCoverage) { ud.maxCoverage = wd->coverageRange; } if (wd->isShield) { if (!ud.shieldWeaponDef || // use the biggest shield (ud.shieldWeaponDef->shieldRadius < wd->shieldRadius)) { ud.shieldWeaponDef = wd; } } if (wd->stockpile) { // interceptors have priority if (wd->interceptor || !ud.stockpileWeaponDef || !ud.stockpileWeaponDef->interceptor) { ud.stockpileWeaponDef = wd; } } } ud.canDGun = udTable.GetBool("canDGun", false); string TEDClass = udTable.GetString("TEDClass", "0"); ud.TEDClassString = TEDClass; ud.extractRange = 0.0f; ud.extractSquare = udTable.GetBool("extractSquare", false); if (ud.extractsMetal) { ud.extractRange = mapInfo->map.extractorRadius; ud.type = "MetalExtractor"; } else if (ud.transportCapacity) { ud.type = "Transport"; } else if (ud.builder) { if (TEDClass != "PLANT") { ud.type = "Builder"; } else { ud.type = "Factory"; } } else if (ud.canfly && !ud.hoverAttack) { if (!ud.weapons.empty() && (ud.weapons[0].def != 0) && (ud.weapons[0].def->type=="AircraftBomb" || ud.weapons[0].def->type=="TorpedoLauncher")) { ud.type = "Bomber"; if (ud.turnRadius == 500) { // only reset it if user hasnt set it explicitly ud.turnRadius = 1000; // hint to the ai about how large turn radius this plane needs } } else { ud.type = "Fighter"; } ud.maxAcc = udTable.GetFloat("maxAcc", 0.065f); // engine power } else if (ud.canmove) { ud.type = "GroundUnit"; } else { ud.type = "Building"; } ud.movedata = 0; if (ud.canmove && !ud.canfly && (ud.type != "Factory")) { string moveclass = StringToLower(udTable.GetString("movementClass", "")); ud.movedata = moveinfo->GetMoveDataFromName(moveclass); if (!ud.movedata) { const string errmsg = "WARNING: Couldn't find a MoveClass named " + moveclass + " (used in UnitDef: " + unitName + ")"; logOutput.Print(errmsg); // remove the UnitDef throw content_error(errmsg); } if ((ud.movedata->moveType == MoveData::Hover_Move) || (ud.movedata->moveType == MoveData::Ship_Move)) { ud.upright = true; } if (ud.canhover) { if (ud.movedata->moveType != MoveData::Hover_Move) { logOutput.Print("Inconsistent movedata %i for %s (moveclass %s): canhover, but not a hovercraft movetype", ud.movedata->pathType, ud.name.c_str(), moveclass.c_str()); } } else if (ud.floater) { if (ud.movedata->moveType != MoveData::Ship_Move) { logOutput.Print("Inconsistent movedata %i for %s (moveclass %s): floater, but not a ship movetype", ud.movedata->pathType, ud.name.c_str(), moveclass.c_str()); } } else { if (ud.movedata->moveType != MoveData::Ground_Move) { logOutput.Print("Inconsistent movedata %i for %s (moveclass %s): neither canhover nor floater, but not a ground movetype", ud.movedata->pathType, ud.name.c_str(), moveclass.c_str()); } } } if ((ud.maxAcc != 0) && (ud.speed != 0)) { //meant to set the drag such that the maxspeed becomes what it should be ud.drag = 1.0f / (ud.speed/GAME_SPEED * 1.1f / ud.maxAcc) - (ud.wingAngle * ud.wingAngle * ud.wingDrag); ud.drag = std::min(1.0f, std::max(0.0f, ud.drag)); } else { //shouldn't be needed since drag is only used in CAirMoveType anyway, //and aircraft without acceleration or speed aren't common :) //initializing it anyway just for safety ud.drag = 0.005f; } std::string objectname = udTable.GetString("objectName", ""); if (objectname.find(".") == std::string::npos) { objectname += ".3do"; } ud.modelDef.modelpath = "objects3d/" + objectname; ud.modelDef.modelname = objectname; ud.scriptName = udTable.GetString("script", unitName + ".cob"); ud.scriptPath = "scripts/" + ud.scriptName; ud.wreckName = udTable.GetString("corpse", ""); ud.deathExplosion = udTable.GetString("explodeAs", ""); ud.selfDExplosion = udTable.GetString("selfDestructAs", ""); ud.power = udTable.GetFloat("power", (ud.metalCost + (ud.energyCost / 60.0f))); // Prevent a division by zero in experience calculations. if (ud.power < 1.0e-3f) { logOutput.Print("Unit %s is really cheap? %f", ud.humanName.c_str(), ud.power); logOutput.Print("This can cause a division by zero in experience calculations."); ud.power = 1.0e-3f; } ud.activateWhenBuilt = udTable.GetBool("activateWhenBuilt", false); // TA has only half our res so multiply size with 2 ud.xsize = udTable.GetInt("footprintX", 1) * 2; ud.zsize = udTable.GetInt("footprintZ", 1) * 2; ud.needGeo = false; if ((ud.type == "Building") || (ud.type == "Factory")) { CreateYardMap(&ud, udTable.GetString("yardMap", "c")); } else { for (int u = 0; u < 4; u++) { ud.yardmaps[u] = 0; } } ud.leaveTracks = udTable.GetBool("leaveTracks", false); ud.trackWidth = udTable.GetFloat("trackWidth", 32.0f); ud.trackOffset = udTable.GetFloat("trackOffset", 0.0f); ud.trackStrength = udTable.GetFloat("trackStrength", 0.0f); ud.trackStretch = udTable.GetFloat("trackStretch", 1.0f); if (ud.leaveTracks && groundDecals) { ud.trackType = groundDecals->GetTrackType(udTable.GetString("trackType", "StdTank")); } ud.useBuildingGroundDecal = udTable.GetBool("useBuildingGroundDecal", false); ud.buildingDecalSizeX = udTable.GetInt("buildingGroundDecalSizeX", 4); ud.buildingDecalSizeY = udTable.GetInt("buildingGroundDecalSizeY", 4); ud.buildingDecalDecaySpeed = udTable.GetFloat("buildingGroundDecalDecaySpeed", 0.1f); if (ud.useBuildingGroundDecal && groundDecals) { ud.buildingDecalType = groundDecals->GetBuildingDecalType(udTable.GetString("buildingGroundDecalType", "")); } ud.canDropFlare = udTable.GetBool("canDropFlare", false); ud.flareReloadTime = udTable.GetFloat("flareReload", 5.0f); ud.flareDelay = udTable.GetFloat("flareDelay", 0.3f); ud.flareEfficiency = udTable.GetFloat("flareEfficiency", 0.5f); ud.flareDropVector = udTable.GetFloat3("flareDropVector", ZeroVector); ud.flareTime = udTable.GetInt("flareTime", 3) * GAME_SPEED; ud.flareSalvoSize = udTable.GetInt("flareSalvoSize", 4); ud.flareSalvoDelay = udTable.GetInt("flareSalvoDelay", 0) * GAME_SPEED; ud.smoothAnim = udTable.GetBool("smoothAnim", false); ud.canLoopbackAttack = udTable.GetBool("canLoopbackAttack", false); ud.canCrash = udTable.GetBool("canCrash", true); ud.levelGround = udTable.GetBool("levelGround", true); ud.strafeToAttack = udTable.GetBool("strafeToAttack", false); ud.modelCenterOffset = udTable.GetFloat3("modelCenterOffset", ZeroVector); ud.collisionVolumeTypeStr = udTable.GetString("collisionVolumeType", ""); ud.collisionVolumeScales = udTable.GetFloat3("collisionVolumeScales", ZeroVector); ud.collisionVolumeOffsets = udTable.GetFloat3("collisionVolumeOffsets", ZeroVector); ud.collisionVolumeTest = udTable.GetInt("collisionVolumeTest", COLVOL_TEST_DISC); ud.usePieceCollisionVolumes = udTable.GetBool("usePieceCollisionVolumes", false); // initialize the (per-unitdef) collision-volume // all CUnit instances hold a copy of this object ud.collisionVolume = new CollisionVolume( ud.collisionVolumeTypeStr, ud.collisionVolumeScales, ud.collisionVolumeOffsets, ud.collisionVolumeTest ); if (ud.usePieceCollisionVolumes) { ud.collisionVolume->Disable(); } ud.seismicRadius = udTable.GetInt("seismicDistance", 0); ud.seismicSignature = udTable.GetFloat("seismicSignature", -1.0f); if (ud.seismicSignature == -1.0f) { if (!ud.floater && !ud.canhover && !ud.canfly) { ud.seismicSignature = sqrt(ud.mass / 100.0f); } else { ud.seismicSignature = 0.0f; } } LuaTable buildsTable = udTable.SubTable("buildOptions"); if (buildsTable.IsValid()) { for (int bo = 1; true; bo++) { const string order = buildsTable.GetString(bo, ""); if (order.empty()) { break; } ud.buildOptions[bo] = order; } } LuaTable sfxTable = udTable.SubTable("SFXTypes"); LuaTable expTable = sfxTable.SubTable("explosionGenerators"); for (int expNum = 1; expNum <= 1024; expNum++) { string expsfx = expTable.GetString(expNum, ""); if (expsfx == "") { break; } else { ud.sfxExplGens.push_back(explGenHandler->LoadGenerator(expsfx)); } } // we use range in a modulo operation, so it needs to be >= 1 ud.pieceTrailCEGTag = udTable.GetString("pieceTrailCEGTag", ""); ud.pieceTrailCEGRange = udTable.GetInt("pieceTrailCEGRange", 1); ud.pieceTrailCEGRange = std::max(ud.pieceTrailCEGRange, 1); LuaTable soundsTable = udTable.SubTable("sounds"); LoadSounds(soundsTable, ud.sounds.ok, "ok"); // eg. "ok1", "ok2", ... LoadSounds(soundsTable, ud.sounds.select, "select"); // eg. "select1", "select2", ... LoadSounds(soundsTable, ud.sounds.arrived, "arrived"); // eg. "arrived1", "arrived2", ... LoadSounds(soundsTable, ud.sounds.build, "build"); LoadSounds(soundsTable, ud.sounds.activate, "activate"); LoadSounds(soundsTable, ud.sounds.deactivate, "deactivate"); LoadSounds(soundsTable, ud.sounds.cant, "cant"); LoadSounds(soundsTable, ud.sounds.underattack, "underattack"); // custom parameters table udTable.SubTable("customParams").GetMap(ud.customParams); }
CUnitDefHandler::CUnitDefHandler(void) : noCost(false) { weaponDefHandler = new CWeaponDefHandler(); PrintLoadMsg("Loading unit definitions"); const LuaTable rootTable = game->defsParser->GetRoot().SubTable("UnitDefs"); if (!rootTable.IsValid()) { throw content_error("Error loading UnitDefs"); } vector<string> unitDefNames; rootTable.GetKeys(unitDefNames); numUnitDefs = unitDefNames.size(); /* // ?? "restricted" does not automatically mean "cannot be built // at all, so we don't need the unitdef for this unit" -- Kloot numUnitDefs -= gameSetup->restrictedUnits.size(); */ // This could be wasteful if there is a lot of restricted units, but that is not that likely unitDefs = new UnitDef[numUnitDefs + 1]; // start at unitdef id 1 unsigned int id = 1; for (unsigned int a = 0; a < unitDefNames.size(); ++a) { const string unitName = unitDefNames[a]; /* // Restrictions may tell us not to use this unit at all // FIXME: causes mod errors when a unit is restricted to // 0, since GetUnitByName() will return NULL if its UnitDef // has not been loaded -- Kloot const std::map<std::string, int>& resUnits = gameSetup->restrictedUnits; if ((resUnits.find(unitName) != resUnits.end()) && (resUnits.find(unitName)->second == 0)) { continue; } */ // Seems ok, load it unitDefs[id].valid = false; unitDefs[id].name = unitName; unitDefs[id].id = id; unitDefs[id].buildangle = 0; unitDefs[id].buildPic = NULL; unitDefs[id].decoyDef = NULL; unitDefs[id].techLevel = -1; unitDefs[id].collisionVolume = NULL; unitID[unitName] = id; for (int ym = 0; ym < 4; ym++) { unitDefs[id].yardmaps[ym] = 0; } // parse the unitdef data (but don't load buildpics, etc...) LuaTable udTable = rootTable.SubTable(unitName); ParseUnitDef(udTable, unitName, id); // Increase index for next unit id++; } // set the real number of unitdefs numUnitDefs = (id - 1); CleanBuildOptions(); FindStartUnits(); ProcessDecoys(); AssignTechLevels(); }
UnitDef::UnitDef(const LuaTable& udTable, const std::string& unitName, int id) : name(unitName) , id(id) , collisionVolume(NULL) , decoyDef(NULL) , techLevel(-1) , buildPic(NULL) , buildangle(0) { humanName = udTable.GetString("name", ""); if (humanName.empty()) { const string errmsg = "missing 'name' parameter for the " + unitName + " unitdef"; throw content_error(errmsg); } filename = udTable.GetString("filename", ""); if (filename.empty()) { const string errmsg = "missing 'filename' parameter for the" + unitName + " unitdef"; throw content_error(errmsg); } tooltip = udTable.GetString("description", name); buildPicName = udTable.GetString("buildPic", ""); decoyName = udTable.GetString("decoyFor", ""); gaia = udTable.GetString("gaia", ""); isCommander = udTable.GetBool("commander", false); metalStorage = udTable.GetFloat("metalStorage", 0.0f); energyStorage = udTable.GetFloat("energyStorage", 0.0f); extractsMetal = udTable.GetFloat("extractsMetal", 0.0f); windGenerator = udTable.GetFloat("windGenerator", 0.0f); tidalGenerator = udTable.GetFloat("tidalGenerator", 0.0f); metalUpkeep = udTable.GetFloat("metalUse", 0.0f); energyUpkeep = udTable.GetFloat("energyUse", 0.0f); metalMake = udTable.GetFloat("metalMake", 0.0f); makesMetal = udTable.GetFloat("makesMetal", 0.0f); energyMake = udTable.GetFloat("energyMake", 0.0f); health = udTable.GetFloat("maxDamage", 0.0f); autoHeal = udTable.GetFloat("autoHeal", 0.0f) * (16.0f / GAME_SPEED); idleAutoHeal = udTable.GetFloat("idleAutoHeal", 10.0f) * (16.0f / GAME_SPEED); idleTime = udTable.GetInt("idleTime", 600); buildangle = udTable.GetInt("buildAngle", 0); losHeight = 20; metalCost = udTable.GetFloat("buildCostMetal", 0.0f); if (metalCost < 1.0f) { metalCost = 1.0f; //avoid some nasty divide by 0 etc } mass = udTable.GetFloat("mass", 0.0f); if (mass <= 0.0f) { mass = metalCost; } energyCost = udTable.GetFloat("buildCostEnergy", 0.0f); buildTime = udTable.GetFloat("buildTime", 0.0f); if (buildTime < 1.0f) { buildTime = 1.0f; //avoid some nasty divide by 0 etc } aihint = id; // FIXME? (as noted in SelectedUnits.cpp, aihint is ignored) cobID = udTable.GetInt("cobID", -1); losRadius = udTable.GetFloat("sightDistance", 0.0f) * modInfo.losMul / (SQUARE_SIZE * (1 << modInfo.losMipLevel)); airLosRadius = udTable.GetFloat("airSightDistance", -1.0f); if (airLosRadius == -1.0f) { airLosRadius = udTable.GetFloat("sightDistance", 0.0f) * modInfo.airLosMul * 1.5f / (SQUARE_SIZE * (1 << modInfo.airMipLevel)); } else { airLosRadius = airLosRadius * modInfo.airLosMul / (SQUARE_SIZE * (1 << modInfo.airMipLevel)); } canSubmerge = udTable.GetBool("canSubmerge", false); canfly = udTable.GetBool("canFly", false); canmove = udTable.GetBool("canMove", false); reclaimable = udTable.GetBool("reclaimable", true); capturable = udTable.GetBool("capturable", true); repairable = udTable.GetBool("repairable", true); canAttack = udTable.GetBool("canAttack", true); canFight = udTable.GetBool("canFight", true); canPatrol = udTable.GetBool("canPatrol", true); canGuard = udTable.GetBool("canGuard", true); canRepeat = udTable.GetBool("canRepeat", true); builder = udTable.GetBool("builder", false); canRestore = udTable.GetBool("canRestore", builder); canRepair = udTable.GetBool("canRepair", builder); canReclaim = udTable.GetBool("canReclaim", builder); canAssist = udTable.GetBool("canAssist", builder); canBeAssisted = udTable.GetBool("canBeAssisted", true); canSelfRepair = udTable.GetBool("canSelfRepair", false); fullHealthFactory = udTable.GetBool("fullHealthFactory", false); factoryHeadingTakeoff = udTable.GetBool("factoryHeadingTakeoff", true); upright = udTable.GetBool("upright", false); collide = udTable.GetBool("collide", true); onoffable = udTable.GetBool("onoffable", false); maxSlope = Clamp(udTable.GetFloat("maxSlope", 0.0f), 0.0f, 89.0f); maxHeightDif = 40 * tan(maxSlope * (PI / 180)); maxSlope = cos(maxSlope * (PI / 180)); minWaterDepth = udTable.GetFloat("minWaterDepth", -10e6f); maxWaterDepth = udTable.GetFloat("maxWaterDepth", +10e6f); minCollisionSpeed = udTable.GetFloat("minCollisionSpeed", 1.0f); slideTolerance = udTable.GetFloat("slideTolerance", 0.0f); // disabled pushResistant = udTable.GetBool("pushResistant", false); waterline = udTable.GetFloat("waterline", 0.0f); if ((waterline >= 5.0f) && canmove) { // make subs travel at somewhat larger depths // to reduce vulnerability to surface weapons waterline += 10.0f; } canSelfD = udTable.GetBool("canSelfDestruct", true); selfDCountdown = udTable.GetInt("selfDestructCountdown", 5); speed = udTable.GetFloat("maxVelocity", 0.0f) * GAME_SPEED; rSpeed = udTable.GetFloat("maxReverseVelocity", 0.0f) * GAME_SPEED; speed = fabs(speed); rSpeed = fabs(rSpeed); maxAcc = fabs(udTable.GetFloat("acceleration", 0.5f)); // no negative values maxDec = fabs(udTable.GetFloat("brakeRate", 3.0f * maxAcc)) * (canfly? 0.1f: 1.0f); // no negative values turnRate = udTable.GetFloat("turnRate", 0.0f); turnInPlace = udTable.GetBool( "turnInPlace", true); turnInPlaceDistance = udTable.GetFloat("turnInPlaceDistance", 350.f); turnInPlaceSpeedLimit = udTable.GetFloat("turnInPlaceSpeedLimit", (speed / GAME_SPEED) * 0.2f); const bool noAutoFire = udTable.GetBool("noAutoFire", false); canFireControl = udTable.GetBool("canFireControl", !noAutoFire); fireState = udTable.GetInt("fireState", canFireControl ? -1 : 2); fireState = std::min(fireState,2); moveState = udTable.GetInt("moveState", (canmove && speed>0.0f) ? -1 : 1); moveState = std::min(moveState,2); buildRange3D = udTable.GetBool("buildRange3D", false); buildDistance = udTable.GetFloat("buildDistance", 128.0f); buildDistance = std::max(128.0f, buildDistance); buildSpeed = udTable.GetFloat("workerTime", 0.0f); repairSpeed = udTable.GetFloat("repairSpeed", buildSpeed); maxRepairSpeed = udTable.GetFloat("maxRepairSpeed", 1e20f); reclaimSpeed = udTable.GetFloat("reclaimSpeed", buildSpeed); resurrectSpeed = udTable.GetFloat("resurrectSpeed", buildSpeed); captureSpeed = udTable.GetFloat("captureSpeed", buildSpeed); terraformSpeed = udTable.GetFloat("terraformSpeed", buildSpeed); flankingBonusMode = udTable.GetInt("flankingBonusMode", modInfo.flankingBonusModeDefault); flankingBonusMax = udTable.GetFloat("flankingBonusMax", 1.9f); flankingBonusMin = udTable.GetFloat("flankingBonusMin", 0.9); flankingBonusDir = udTable.GetFloat3("flankingBonusDir", float3(0.0f, 0.0f, 1.0f)); flankingBonusMobilityAdd = udTable.GetFloat("flankingBonusMobilityAdd", 0.01f); armoredMultiple = udTable.GetFloat("damageModifier", 1.0f); armorType = damageArrayHandler->GetTypeFromName(name); radarRadius = udTable.GetInt("radarDistance", 0); sonarRadius = udTable.GetInt("sonarDistance", 0); jammerRadius = udTable.GetInt("radarDistanceJam", 0); sonarJamRadius = udTable.GetInt("sonarDistanceJam", 0); stealth = udTable.GetBool("stealth", false); sonarStealth = udTable.GetBool("sonarStealth", false); targfac = udTable.GetBool("isTargetingUpgrade", false); isFeature = udTable.GetBool("isFeature", false); canResurrect = udTable.GetBool("canResurrect", false); canCapture = udTable.GetBool("canCapture", false); hideDamage = udTable.GetBool("hideDamage", false); showPlayerName = udTable.GetBool("showPlayerName", false); cloakCost = udTable.GetFloat("cloakCost", -1.0f); cloakCostMoving = udTable.GetFloat("cloakCostMoving", -1.0f); if (cloakCostMoving < 0) { cloakCostMoving = cloakCost; } canCloak = (cloakCost >= 0); startCloaked = udTable.GetBool("initCloaked", false); decloakDistance = udTable.GetFloat("minCloakDistance", 0.0f); decloakSpherical = udTable.GetBool("decloakSpherical", true); decloakOnFire = udTable.GetBool("decloakOnFire", true); cloakTimeout = udTable.GetInt("cloakTimeout", 128); highTrajectoryType = udTable.GetInt("highTrajectory", 0); canKamikaze = udTable.GetBool("kamikaze", false); kamikazeDist = udTable.GetFloat("kamikazeDistance", -25.0f) + 25.0f; //we count 3d distance while ta count 2d distance so increase slightly kamikazeUseLOS = udTable.GetBool("kamikazeUseLOS", false); showNanoFrame = udTable.GetBool("showNanoFrame", true); showNanoSpray = udTable.GetBool("showNanoSpray", true); nanoColor = udTable.GetFloat3("nanoColor", float3(0.2f,0.7f,0.2f)); canhover = udTable.GetBool("canHover", false); floater = udTable.GetBool("floater", udTable.KeyExists("WaterLine")); if (builder && !buildSpeed) { // core anti is flagged as builder for some reason builder = false; } airStrafe = udTable.GetBool("airStrafe", true); hoverAttack = udTable.GetBool("hoverAttack", false); wantedHeight = udTable.GetFloat("cruiseAlt", 0.0f); dlHoverFactor = udTable.GetFloat("airHoverFactor", -1.0f); bankingAllowed = udTable.GetBool("bankingAllowed", true); useSmoothMesh = udTable.GetBool("useSmoothMesh", true); transportSize = udTable.GetInt("transportSize", 0); minTransportSize = udTable.GetInt("minTransportSize", 0); transportCapacity = udTable.GetInt("transportCapacity", 0); isFirePlatform = udTable.GetBool("isFirePlatform", false); isAirBase = udTable.GetBool("isAirBase", false); loadingRadius = udTable.GetFloat("loadingRadius", 220.0f); unloadSpread = udTable.GetFloat("unloadSpread", 1.0f); transportMass = udTable.GetFloat("transportMass", 100000.0f); minTransportMass = udTable.GetFloat("minTransportMass", 0.0f); holdSteady = udTable.GetBool("holdSteady", false); releaseHeld = udTable.GetBool("releaseHeld", false); cantBeTransported = udTable.GetBool("cantBeTransported", false); transportByEnemy = udTable.GetBool("transportByEnemy", true); fallSpeed = udTable.GetFloat("fallSpeed", 0.2); unitFallSpeed = udTable.GetFloat("unitFallSpeed", 0); transportUnloadMethod = udTable.GetInt("transportUnloadMethod" , 0); // modrules transport settings if ((!modInfo.transportAir && canfly) || (!modInfo.transportShip && floater) || (!modInfo.transportHover && canhover) || (!modInfo.transportGround && !canhover && !floater && !canfly)) { cantBeTransported = true; } wingDrag = udTable.GetFloat("wingDrag", 0.07f); // drag caused by wings wingDrag = Clamp(wingDrag, 0.0f, 1.0f); wingAngle = udTable.GetFloat("wingAngle", 0.08f); // angle between front and the wing plane frontToSpeed = udTable.GetFloat("frontToSpeed", 0.1f); // fudge factor for lining up speed and front of plane speedToFront = udTable.GetFloat("speedToFront", 0.07f); // fudge factor for lining up speed and front of plane myGravity = udTable.GetFloat("myGravity", 0.4f); // planes are slower than real airplanes so lower gravity to compensate crashDrag = udTable.GetFloat("crashDrag", 0.005f); // drag used when crashing crashDrag = Clamp(crashDrag, 0.0f, 1.0f); maxBank = udTable.GetFloat("maxBank", 0.8f); // max roll maxPitch = udTable.GetFloat("maxPitch", 0.45f); // max pitch this plane tries to keep turnRadius = udTable.GetFloat("turnRadius", 500.0f); // hint to the ai about how large turn radius this plane needs verticalSpeed = udTable.GetFloat("verticalSpeed", 3.0f); // speed of takeoff and landing, at least for gunships maxAileron = udTable.GetFloat("maxAileron", 0.015f); // turn speed around roll axis maxElevator = udTable.GetFloat("maxElevator", 0.01f); // turn speed around pitch axis maxRudder = udTable.GetFloat("maxRudder", 0.004f); // turn speed around yaw axis maxFuel = udTable.GetFloat("maxFuel", 0.0f); //max flight time in seconds before aircraft must return to base refuelTime = udTable.GetFloat("refuelTime", 5.0f); minAirBasePower = udTable.GetFloat("minAirBasePower", 0.0f); maxThisUnit = udTable.GetInt("unitRestricted", MAX_UNITS); transportableBuilding = udTable.GetBool("transportableBuilding", false); const string lname = StringToLower(name); if (gameSetup->restrictedUnits.find(lname) != gameSetup->restrictedUnits.end()) { maxThisUnit = std::min(maxThisUnit, gameSetup->restrictedUnits.find(lname)->second); } categoryString = udTable.GetString("category", ""); category = CCategoryHandler::Instance()->GetCategories(udTable.GetString("category", "")); noChaseCategory = CCategoryHandler::Instance()->GetCategories(udTable.GetString("noChaseCategory", "")); const string iconName = udTable.GetString("iconType", "default"); iconType = iconHandler->GetIcon(iconName); shieldWeaponDef = NULL; stockpileWeaponDef = NULL; maxWeaponRange = 0.0f; maxCoverage = 0.0f; LuaTable weaponsTable = udTable.SubTable("weapons"); ParseWeaponsTable(weaponsTable); canDGun = udTable.GetBool("canDGun", false); extractRange = 0.0f; extractSquare = udTable.GetBool("extractSquare", false); if (extractsMetal) { extractRange = mapInfo->map.extractorRadius; type = "MetalExtractor"; } else if (transportCapacity) { type = "Transport"; } else if (builder) { if ((speed > 0.0f) || canfly || udTable.GetString("yardMap", "").empty()) { // hubs and nano-towers need to be builders (for now) type = "Builder"; } else { type = "Factory"; } } else if (canfly && !hoverAttack) { if (!weapons.empty() && (weapons[0].def != 0) && (weapons[0].def->type == "AircraftBomb" || weapons[0].def->type == "TorpedoLauncher")) { type = "Bomber"; if (turnRadius == 500) { // only reset it if user hasnt set it explicitly turnRadius *= 2; // hint to the ai about how large turn radius this plane needs } } else { type = "Fighter"; } maxAcc = udTable.GetFloat("maxAcc", 0.065f); // engine power } else if (canmove) { type = "GroundUnit"; } else { type = "Building"; } movedata = NULL; if (canmove && !canfly && (type != "Factory")) { string moveclass = StringToLower(udTable.GetString("movementClass", "")); movedata = moveinfo->GetMoveDataFromName(moveclass); if (!movedata) { const string errmsg = "WARNING: Couldn't find a MoveClass named " + moveclass + " (used in UnitDef: " + unitName + ")"; throw content_error(errmsg); //! invalidate unitDef (this gets catched in ParseUnitDef!) } if ((movedata->moveType == MoveData::Hover_Move) || (movedata->moveType == MoveData::Ship_Move)) { upright = true; } if (canhover) { if (movedata->moveType != MoveData::Hover_Move) { logOutput.Print("Inconsistent movedata %i for %s (moveclass %s): canhover, but not a hovercraft movetype", movedata->pathType, name.c_str(), moveclass.c_str()); } } else if (floater) { if (movedata->moveType != MoveData::Ship_Move) { logOutput.Print("Inconsistent movedata %i for %s (moveclass %s): floater, but not a ship movetype", movedata->pathType, name.c_str(), moveclass.c_str()); } } else { if (movedata->moveType != MoveData::Ground_Move) { logOutput.Print("Inconsistent movedata %i for %s (moveclass %s): neither canhover nor floater, but not a ground movetype", movedata->pathType, name.c_str(), moveclass.c_str()); } } } if ((maxAcc != 0) && (speed != 0)) { //meant to set the drag such that the maxspeed becomes what it should be drag = 1.0f / (speed/GAME_SPEED * 1.1f / maxAcc) - (wingAngle * wingAngle * wingDrag); drag = Clamp(drag, 0.0f, 1.0f); } else { //shouldn't be needed since drag is only used in CAirMoveType anyway, //and aircraft without acceleration or speed aren't common :) //initializing it anyway just for safety drag = 0.005f; } objectName = udTable.GetString("objectName", ""); if (objectName.find(".") == std::string::npos) { objectName += ".3do"; // NOTE: get rid of this? } modelDef.modelPath = "objects3d/" + objectName; modelDef.modelName = objectName; scriptName = udTable.GetString("script", unitName + ".cob"); scriptPath = "scripts/" + scriptName; wreckName = udTable.GetString("corpse", ""); deathExplosion = udTable.GetString("explodeAs", ""); selfDExplosion = udTable.GetString("selfDestructAs", ""); power = udTable.GetFloat("power", (metalCost + (energyCost / 60.0f))); // Prevent a division by zero in experience calculations. if (power < 1.0e-3f) { logOutput.Print("Unit %s is really cheap? %f", humanName.c_str(), power); logOutput.Print("This can cause a division by zero in experience calculations."); power = 1.0e-3f; } activateWhenBuilt = udTable.GetBool("activateWhenBuilt", false); // TA has only half our res so multiply size with 2 xsize = udTable.GetInt("footprintX", 1) * 2; zsize = udTable.GetInt("footprintZ", 1) * 2; needGeo = false; if (speed <= 0.0f) { CreateYardMap(udTable.GetString("yardMap", "")); } leaveTracks = udTable.GetBool("leaveTracks", false); trackTypeName = udTable.GetString("trackType", "StdTank"); trackWidth = udTable.GetFloat("trackWidth", 32.0f); trackOffset = udTable.GetFloat("trackOffset", 0.0f); trackStrength = udTable.GetFloat("trackStrength", 0.0f); trackStretch = udTable.GetFloat("trackStretch", 1.0f); useBuildingGroundDecal = udTable.GetBool("useBuildingGroundDecal", false); buildingDecalTypeName = udTable.GetString("buildingGroundDecalType", ""); buildingDecalSizeX = udTable.GetInt("buildingGroundDecalSizeX", 4); buildingDecalSizeY = udTable.GetInt("buildingGroundDecalSizeY", 4); buildingDecalDecaySpeed = udTable.GetFloat("buildingGroundDecalDecaySpeed", 0.1f); canDropFlare = udTable.GetBool("canDropFlare", false); flareReloadTime = udTable.GetFloat("flareReload", 5.0f); flareDelay = udTable.GetFloat("flareDelay", 0.3f); flareEfficiency = udTable.GetFloat("flareEfficiency", 0.5f); flareDropVector = udTable.GetFloat3("flareDropVector", ZeroVector); flareTime = udTable.GetInt("flareTime", 3) * GAME_SPEED; flareSalvoSize = udTable.GetInt("flareSalvoSize", 4); flareSalvoDelay = udTable.GetInt("flareSalvoDelay", 0) * GAME_SPEED; smoothAnim = udTable.GetBool("smoothAnim", false); canLoopbackAttack = udTable.GetBool("canLoopbackAttack", false); canCrash = udTable.GetBool("canCrash", true); levelGround = udTable.GetBool("levelGround", true); strafeToAttack = udTable.GetBool("strafeToAttack", false); modelCenterOffset = udTable.GetFloat3("modelCenterOffset", ZeroVector); usePieceCollisionVolumes = udTable.GetBool("usePieceCollisionVolumes", false); // initialize the (per-unitdef) collision-volume // all CUnit instances hold a copy of this object collisionVolume = new CollisionVolume( udTable.GetString("collisionVolumeType", ""), udTable.GetFloat3("collisionVolumeScales", ZeroVector), udTable.GetFloat3("collisionVolumeOffsets", ZeroVector), udTable.GetInt("collisionVolumeTest", CollisionVolume::COLVOL_HITTEST_DISC) ); if (usePieceCollisionVolumes) { collisionVolume->Disable(); } seismicRadius = udTable.GetInt("seismicDistance", 0); seismicSignature = udTable.GetFloat("seismicSignature", -1.0f); if (seismicSignature == -1.0f) { if (!floater && !canhover && !canfly) { seismicSignature = sqrt(mass / 100.0f); } else { seismicSignature = 0.0f; } } LuaTable buildsTable = udTable.SubTable("buildOptions"); if (buildsTable.IsValid()) { for (int bo = 1; true; bo++) { const string order = buildsTable.GetString(bo, ""); if (order.empty()) { break; } buildOptions[bo] = order; } } LuaTable sfxTable = udTable.SubTable("SFXTypes"); LuaTable expTable = sfxTable.SubTable("explosionGenerators"); for (int expNum = 1; expNum <= 1024; expNum++) { std::string expsfx = expTable.GetString(expNum, ""); if (expsfx == "") { break; } else { sfxExplGenNames.push_back(expsfx); } } // we use range in a modulo operation, so it needs to be >= 1 pieceTrailCEGTag = udTable.GetString("pieceTrailCEGTag", ""); pieceTrailCEGRange = udTable.GetInt("pieceTrailCEGRange", 1); pieceTrailCEGRange = std::max(pieceTrailCEGRange, 1); // custom parameters table udTable.SubTable("customParams").GetMap(customParams); }
int main(int argc, char *argv[]) { #ifdef _WIN32 try { #endif std::cout << "If you find any errors, report them to mantis or the forums." << std::endl << std::endl; ConfigHandler::Instantiate(""); FileSystemHandler::Cleanup(); FileSystemHandler::Initialize(false); CGameServer* server = 0; CGameSetup* gameSetup = 0; if (argc > 1) { const std::string script(argv[1]); std::cout << "Loading script from file: " << script << std::endl; ClientSetup settings; CFileHandler fh(argv[1]); if (!fh.FileExists()) throw content_error("Setupscript doesn't exists in given location: "+script); std::string buf; if (!fh.LoadStringData(buf)) throw content_error("Setupscript cannot be read: "+script); settings.Init(buf); gameSetup = new CGameSetup(); // to store the gamedata inside if (!gameSetup->Init(buf)) // read the script provided by cmdline { std::cout << "Failed to load script" << std::endl; return 1; } std::cout << "Starting server..." << std::endl; // Create the server, it will run in a separate thread GameData* data = new GameData(); UnsyncedRNG rng; rng.Seed(gameSetup->gameSetupText.length()); rng.Seed(script.length()); data->SetRandomSeed(rng.RandInt()); // Use script provided hashes if they exist if (gameSetup->mapHash != 0) { data->SetMapChecksum(gameSetup->mapHash); gameSetup->LoadStartPositions(false); // reduced mode } else { data->SetMapChecksum(archiveScanner->GetMapChecksum(gameSetup->mapName)); CFileHandler* f = new CFileHandler("maps/" + gameSetup->mapName); if (!f->FileExists()) { std::vector<std::string> ars = archiveScanner->GetArchivesForMap(gameSetup->mapName); if (ars.empty()) { throw content_error("Couldn't find any archives for map '" + gameSetup->mapName + "'."); } for (std::vector<std::string>::iterator i = ars.begin(); i != ars.end(); ++i) { if (!vfsHandler->AddArchive(*i, false)) { throw content_error("Couldn't load archive '" + *i + "' for map '" + gameSetup->mapName + "'."); } } } delete f; gameSetup->LoadStartPositions(); // full mode } if (gameSetup->modHash != 0) { data->SetModChecksum(gameSetup->modHash); } else { const std::string modArchive = archiveScanner->ModNameToModArchive(gameSetup->modName); data->SetModChecksum(archiveScanner->GetModChecksum(modArchive)); } data->SetSetup(gameSetup->gameSetupText); server = new CGameServer(&settings, false, data, gameSetup); while (!server->HasFinished()) // check if still running #ifdef _WIN32 Sleep(1000); #else sleep(1); // if so, wait 1 second #endif delete server; // delete the server after usage } else { std::cout << "usage: spring-dedicated <full_path_to_script>" << std::endl; } FileSystemHandler::Cleanup(); #ifdef _WIN32 } catch (const std::exception& err) { std::cout << "Exception raised: " << err.what() << std::endl; return 1; } #endif return 0; }
C3DOTextureHandler::C3DOTextureHandler() { CFileHandler teamTexFile("unittextures/tatex/teamtex.txt"); CFileHandler paletteFile("unittextures/tatex/palette.pal"); CSimpleParser parser(teamTexFile); std::set<std::string> teamTexes; while (!parser.Eof()) { teamTexes.insert(StringToLower(parser.GetCleanLine())); } TexFile* texfiles[10000]; int numfiles = 0; int totalSize = 0; const std::vector<std::string>& filesBMP = CFileHandler::FindFiles("unittextures/tatex/", "*.bmp"); std::vector<std::string> files = CFileHandler::FindFiles("unittextures/tatex/", "*.tga"); files.insert(files.end(), filesBMP.begin(), filesBMP.end()); std::set<string> usedNames; for (std::vector<std::string>::iterator fi = files.begin(); fi != files.end(); ++fi) { const std::string& s = *fi; const std::string s2 = StringToLower(FileSystem::GetBasename(s)); // avoid duplicate names and give tga images priority if (usedNames.find(s2) != usedNames.end()) { continue; } usedNames.insert(s2); if(teamTexes.find(s2) == teamTexes.end()){ TexFile* tex = CreateTex(s, s2, false); texfiles[numfiles++] = tex; totalSize += tex->tex.xsize * tex->tex.ysize; } else { TexFile* tex = CreateTex(s, s2, true); texfiles[numfiles++] = tex; totalSize += tex->tex.xsize * tex->tex.ysize; } } if (paletteFile.FileExists()) { palette.Init(paletteFile); } for (unsigned a = 0; a < CTAPalette::NUM_PALETTE_ENTRIES; ++a) { const std::string name = "ta_color" + IntToString(a, "%i"); TexFile* tex = new TexFile; tex->name = name; tex->tex.Alloc(1, 1); tex->tex.mem[0] = palette[a][0]; tex->tex.mem[1] = palette[a][1]; tex->tex.mem[2] = palette[a][2]; tex->tex.mem[3] = 0; // teamcolor tex->tex2.Alloc(1, 1); tex->tex2.mem[0] = 0; // self illum tex->tex2.mem[1] = 30; // reflectivity tex->tex2.mem[2] = 0; tex->tex2.mem[3] = 255; texfiles[numfiles++] = tex; totalSize += tex->tex.xsize * tex->tex.ysize; } // pessimistic guess about how much space will be wasted totalSize = (int)(totalSize * 1.2f); if (totalSize < 1024*1024) { bigTexX = 1024; bigTexY = 1024; } else if (totalSize < 1024*2048) { bigTexX = 1024; bigTexY = 2048; } else if (totalSize < 2048*2048) { bigTexX = 2048; bigTexY = 2048; } else { bigTexX = 2048; bigTexY = 2048; throw content_error("Too many/large texture in 3do texture-atlas to fit in 2048*2048"); } qsort(texfiles,numfiles,sizeof(TexFile*), CompareTatex2); unsigned char* bigtex1 = new unsigned char[bigTexX * bigTexY * 4]; unsigned char* bigtex2 = new unsigned char[bigTexX * bigTexY * 4]; for (int a = 0; a < (bigTexX * bigTexY); ++a) { bigtex1[a*4 + 0] = 128; bigtex1[a*4 + 1] = 128; bigtex1[a*4 + 2] = 128; bigtex1[a*4 + 3] = 0; bigtex2[a*4 + 0] = 0; bigtex2[a*4 + 1] = 128; bigtex2[a*4 + 2] = 0; bigtex2[a*4 + 3] = 255; } int cury = 0; int maxy = 0; int foundx = 0; int foundy = 0; std::list<int2> nextSub; std::list<int2> thisSub; for (int a = 0; a < numfiles; ++a) { CBitmap* curtex1 = &texfiles[a]->tex; CBitmap* curtex2 = &texfiles[a]->tex2; bool done = false; while (!done) { // Find space for us if (thisSub.empty()) { if (nextSub.empty()) { cury = maxy; maxy += curtex1->ysize; if (maxy > bigTexY) { delete[] bigtex1; delete[] bigtex2; throw content_error("Too many/large texture in 3do texture-atlas to fit in 2048*2048"); } thisSub.push_back(int2(0, cury)); } else { thisSub = nextSub; nextSub.clear(); } } if (thisSub.front().x + curtex1->xsize>bigTexX) { thisSub.clear(); continue; } if(thisSub.front().y+curtex1->ysize>maxy){ thisSub.pop_front(); continue; } // ok found space for us foundx=thisSub.front().x; foundy=thisSub.front().y; done=true; if (thisSub.front().y + curtex1->ysize<maxy){ nextSub.push_back(int2(thisSub.front().x, thisSub.front().y + curtex1->ysize)); } thisSub.front().x += curtex1->xsize; while ((thisSub.size() > 1) && (thisSub.front().x >= (++thisSub.begin())->x)) { (++thisSub.begin())->x = thisSub.front().x; thisSub.erase(thisSub.begin()); } } for (int y = 0; y < curtex1->ysize; ++y) { for (int x = 0; x < curtex1->xsize; ++x) { for (int col = 0; col < 4; ++col) { bigtex1[(((foundy + y) * bigTexX + (foundx + x)) * 4) + col] = curtex1->mem[(((y * curtex1->xsize) + x) * 4) + col]; bigtex2[(((foundy + y) * bigTexX + (foundx + x)) * 4) + col] = curtex2->mem[(((y * curtex1->xsize) + x) * 4) + col]; } } } UnitTexture* unittex = new UnitTexture; unittex->xstart = (foundx + 0.5f) / (float)bigTexX; unittex->ystart = (foundy + 0.5f) / (float)bigTexY; unittex->xend = (foundx + curtex1->xsize - 0.5f) / (float)bigTexX; unittex->yend = (foundy + curtex1->ysize - 0.5f) / (float)bigTexY; textures[texfiles[a]->name] = unittex; delete texfiles[a]; } glGenTextures(1, &atlas3do1); glBindTexture(GL_TEXTURE_2D, atlas3do1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR/*_MIPMAP_NEAREST*/); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8 ,bigTexX, bigTexY, 0, GL_RGBA, GL_UNSIGNED_BYTE, bigtex1); //glBuildMipmaps(GL_TEXTURE_2D,GL_RGBA8 ,bigTexX, bigTexY, GL_RGBA, GL_UNSIGNED_BYTE, bigtex1); glGenTextures(1, &atlas3do2); glBindTexture(GL_TEXTURE_2D, atlas3do2); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR/*_MIPMAP_NEAREST*/); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, bigTexX, bigTexY, 0, GL_RGBA, GL_UNSIGNED_BYTE, bigtex2); //glBuildMipmaps(GL_TEXTURE_2D,GL_RGBA8, bigTexX, bigTexY, GL_RGBA, GL_UNSIGNED_BYTE, bigtex2); // CBitmap save(tex, bigTexX, bigTexY); // save.Save("unittex-1x.jpg"); UnitTexture* t = new UnitTexture(); t->xstart = 0.0f; t->ystart = 0.0f; t->xend = 1.0f; t->yend = 1.0f; textures["___dummy___"] = t; delete[] bigtex1; delete[] bigtex2; }
CMoveInfo::CMoveInfo() { const LuaTable rootTable = game->defsParser->GetRoot().SubTable("MoveDefs"); if (!rootTable.IsValid()) { throw content_error("Error loading movement definitions"); } groundMoveMath = new CGroundMoveMath(); hoverMoveMath = new CHoverMoveMath(); seaMoveMath = new CShipMoveMath(); CRC crc; for (int tt = 0; tt < CMapInfo::NUM_TERRAIN_TYPES; ++tt) { const CMapInfo::TerrainType& terrType = mapInfo->terrainTypes[tt]; crc << terrType.tankSpeed << terrType.kbotSpeed; crc << terrType.hoverSpeed << terrType.shipSpeed; } for (size_t num = 1; /* no test */; num++) { const LuaTable moveTable = rootTable.SubTable(num); if (!moveTable.IsValid()) { break; } MoveData* md = new MoveData(NULL); md->name = StringToLower(moveTable.GetString("name", "")); md->pathType = (num - 1); md->crushStrength = moveTable.GetFloat("crushStrength", 10.0f); const float minWaterDepth = moveTable.GetFloat("minWaterDepth", 10.0f); const float maxWaterDepth = moveTable.GetFloat("maxWaterDepth", 0.0f); if ((md->name.find("boat") != string::npos) || (md->name.find("ship") != string::npos)) { md->moveType = MoveData::Ship_Move; md->depth = minWaterDepth; md->moveFamily = MoveData::Ship; md->moveMath = seaMoveMath; md->subMarine = moveTable.GetBool("subMarine", 0); } else if (md->name.find("hover") != string::npos) { md->moveType = MoveData::Hover_Move; md->maxSlope = DegreesToMaxSlope(moveTable.GetFloat("maxSlope", 15.0f)); md->moveFamily = MoveData::Hover; md->moveMath = hoverMoveMath; } else { md->moveType = MoveData::Ground_Move; md->depthMod = moveTable.GetFloat("depthMod", 0.1f); md->depth = maxWaterDepth; md->maxSlope = DegreesToMaxSlope(moveTable.GetFloat("maxSlope", 60.0f)); md->moveMath = groundMoveMath; if (md->name.find("tank") != string::npos) { md->moveFamily = MoveData::Tank; } else { md->moveFamily = MoveData::KBot; } } md->heatMapping = moveTable.GetBool("heatMapping", false); md->heatMod = moveTable.GetFloat("heatMod", 50.0f); md->heatProduced = moveTable.GetInt("heatProduced", 60); // ground units hug the ocean floor when in water, // ships stay at a "fixed" level (their waterline) md->followGround = (md->moveFamily == MoveData::Tank || md->moveFamily == MoveData::KBot); // tank or bot that cannot get its threads / feet // wet, or hovercraft (which doesn't touch ground // or water) const bool b0 = ((md->followGround && maxWaterDepth <= 0.0) || md->moveFamily == MoveData::Hover); // ship (or sub) that cannot crawl onto shore, OR tank or // kbot restricted to snorkling (strange but possible) const bool b1 = ((md->moveFamily == MoveData::Ship && minWaterDepth > 0.0) || ((md->followGround) && minWaterDepth > 0.0)); // tank or kbot that CAN go skinny-dipping (amph.), // or ship that CAN sprout legs when at the beach const bool b2 = ((md->followGround) && maxWaterDepth > 0.0) || (md->moveFamily == MoveData::Ship && minWaterDepth < 0.0); if (b0) { md->terrainClass = MoveData::Land; } if (b1) { md->terrainClass = MoveData::Water; } if (b2) { md->terrainClass = MoveData::Mixed; } const int xsize = std::max(1, moveTable.GetInt("footprintX", 1)); const int zsize = std::max(1, moveTable.GetInt("footprintZ", xsize)); const int scale = 2; // make all mobile footprints point-symmetric in heightmap space // (meaning that only non-even dimensions are possible and each // footprint always has a unique center square) md->xsize = xsize * scale; md->zsize = zsize * scale; md->xsize -= ((md->xsize & 1)? 0: 1); md->zsize -= ((md->zsize & 1)? 0: 1); md->slopeMod = moveTable.GetFloat("slopeMod", 4.0f / (md->maxSlope + 0.001f)); const unsigned int checksum = (md->xsize << 16) + (md->zsize << 8) + (md->followGround << 4) + (md->subMarine << 3) + (b2 << 2) + (b1 << 1) + (b0 << 0); crc << checksum << md->maxSlope << md->slopeMod << md->depth << md->depthMod << md->crushStrength; moveData.push_back(md); name2moveData[md->name] = md->pathType; } const float waterDamage = mapInfo->water.damage; if (waterDamage >= 1000.0f) { CGroundMoveMath::waterDamageCost = 0.0f; //! block water } else { CGroundMoveMath::waterDamageCost = 1.0f / (1.0f + waterDamage * 0.1f); } CHoverMoveMath::noWaterMove = (waterDamage >= 10000.0f); crc << CGroundMoveMath::waterDamageCost; crc << CHoverMoveMath::noWaterMove; moveInfoChecksum = crc.GetDigest(); }
CDxSound::CDxSound() { maxSounds=ConfigHandler::GetInstance().GetInt("MaxSounds",16); if (maxSounds <= 0) { throw content_error("Internal error, (maxSounds <= 0) in CDxSound"); } curThreshhold=0.1f; wantedSounds=maxSounds*0.75f; globalVolume=1.0f; m_pDS = NULL; m_hWnd = NULL; HRESULT hr; LPDIRECTSOUNDBUFFER pDSBPrimary = NULL; // Get window from SDL SDL_SysWMinfo wmInfo; SDL_VERSION(&wmInfo.version); if (SDL_GetWMInfo (&wmInfo) != 1) { throw "DxSound: Could not get window from SDL"; } m_hWnd = wmInfo.window; // Initialize COM hr = CoInitialize( NULL ); if( hr!=S_OK && hr!=S_FALSE){ throw "DxSound: Could not initialize com"; } // Create IDirectSound using the primary sound device if( FAILED( hr = DirectSoundCreate( NULL, &m_pDS, NULL ) ) ){ throw "DxSound: Could not create direct sound object"; } // Set coop level to DSSCL_PRIORITY if( FAILED( hr = m_pDS->SetCooperativeLevel( m_hWnd, DSSCL_PRIORITY ) ) ){ throw "DxSound: Could not set cooperative level"; } // Get the primary buffer DSBUFFERDESC dsbd; ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) ); dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; dsbd.dwBufferBytes = 0; dsbd.lpwfxFormat = NULL; if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL ) ) ){ throw "DxSound: Could not create primary sound buffer"; } // Set primary buffer format to 22kHz and 16-bit output. WAVEFORMATEX wfx; ZeroMemory( &wfx, sizeof(WAVEFORMATEX) ); wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nChannels = 2; wfx.nSamplesPerSec = 22050; wfx.wBitsPerSample = 16; wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels; wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; if( FAILED( hr = pDSBPrimary->SetFormat(&wfx) ) ){ throw "DxSound: Could not initialize primary sound format"; } SAFE_RELEASE( pDSBPrimary ); waveid[""]=0; SoundInfo* si=SAFE_NEW SoundInfo; loadedSounds.push_back(si); }
CBuilderCAI::CBuilderCAI(CUnit* owner): CMobileCAI(owner), building(false), cachedRadiusId(0), cachedRadius(0), buildRetries(0), lastPC1(-1), lastPC2(-1), lastPC3(-1), range3D(owner->unitDef->buildRange3D) { CommandDescription c; if (owner->unitDef->canRepair) { c.id = CMD_REPAIR; c.action = "repair"; c.type = CMDTYPE_ICON_UNIT_OR_AREA; c.name = "Repair"; c.mouseicon = c.name; c.tooltip = "Repair: Repairs another unit"; possibleCommands.push_back(c); } else if (owner->unitDef->canAssist) { c.id = CMD_REPAIR; c.action = "assist"; c.type = CMDTYPE_ICON_UNIT_OR_AREA; c.name = "Assist"; c.mouseicon = c.name; c.tooltip = "Assist: Help build something"; possibleCommands.push_back(c); } if (owner->unitDef->canReclaim) { c.id = CMD_RECLAIM; c.action = "reclaim"; c.type = CMDTYPE_ICON_UNIT_FEATURE_OR_AREA; c.name = "Reclaim"; c.mouseicon = c.name; c.tooltip = "Reclaim: Sucks in the metal/energy content of a unit/feature and add it to your storage"; possibleCommands.push_back(c); } if (owner->unitDef->canRestore && !mapDamage->disabled) { c.id = CMD_RESTORE; c.action = "restore"; c.type = CMDTYPE_ICON_AREA; c.name = "Restore"; c.mouseicon = c.name; c.tooltip = "Restore: Restores an area of the map to its original height"; c.params.push_back("200"); possibleCommands.push_back(c); } c.params.clear(); if (owner->unitDef->canResurrect) { c.id = CMD_RESURRECT; c.action = "resurrect"; c.type = CMDTYPE_ICON_UNIT_FEATURE_OR_AREA; c.name = "Resurrect"; c.mouseicon = c.name; c.tooltip = "Resurrect: Resurrects a unit from a feature"; possibleCommands.push_back(c); } if (owner->unitDef->canCapture) { c.id = CMD_CAPTURE; c.action = "capture"; c.type = CMDTYPE_ICON_UNIT_OR_AREA; c.name = "Capture"; c.mouseicon = c.name; c.tooltip = "Capture: Captures a unit from the enemy"; possibleCommands.push_back(c); } CBuilder* builder = (CBuilder*) owner; map<int, string>::const_iterator bi; for (bi = builder->unitDef->buildOptions.begin(); bi != builder->unitDef->buildOptions.end(); ++bi) { const string name = bi->second; const UnitDef* ud = unitDefHandler->GetUnitDefByName(name); if (ud == NULL) { string errmsg = "MOD ERROR: loading "; errmsg += name.c_str(); errmsg += " for "; errmsg += owner->unitDef->name; throw content_error(errmsg); } CommandDescription c; c.id = -ud->id; //build options are always negative c.action = "buildunit_" + StringToLower(ud->name); c.type = CMDTYPE_ICON_BUILDING; c.name = name; c.mouseicon = c.name; c.disabled = (ud->maxThisUnit <= 0); char tmp[1024]; sprintf(tmp, "\nHealth %.0f\nMetal cost %.0f\nEnergy cost %.0f Build time %.0f", ud->health, ud->metalCost, ud->energyCost, ud->buildTime); if (c.disabled) { c.tooltip = "\xff\xff\x22\x22" "DISABLED: " "\xff\xff\xff\xff"; } else { c.tooltip = "Build: "; } c.tooltip += ud->humanName + " - " + ud->tooltip + tmp; possibleCommands.push_back(c); buildOptions[c.id] = name; } uh->AddBuilderCAI(this); }
/** * @brief Adds the directories in the colon separated string to the datadir handler. */ void DataDirLocater::AddDirs(const std::string& in) { size_t prev_colon = 0, colon; #ifndef _WIN32 while ((colon = in.find(':', prev_colon)) != std::string::npos) { #else while ((colon = in.find(';', prev_colon)) != std::string::npos) { #endif datadirs.push_back(in.substr(prev_colon, colon - prev_colon)); #ifdef DEBUG logOutput.Print("Adding %s to directories" , in.substr(prev_colon, colon - prev_colon).c_str()); #endif prev_colon = colon + 1; } #ifdef DEBUG logOutput.Print("Adding %s to directories" , in.substr(prev_colon).c_str()); #endif datadirs.push_back(in.substr(prev_colon)); } /** * @brief Figure out permissions we have for a single data directory d. * @returns whether we have permissions to read the data directory. */ bool DataDirLocater::DeterminePermissions(DataDir* d) { #ifndef _WIN32 if (d->path.c_str()[0] != '/' || d->path.find("..") != std::string::npos) #else if (d->path.find("..") != std::string::npos) #endif throw content_error("specify data directories using absolute paths please"); // Figure out whether we have read/write permissions // First check read access, if we got that check write access too // (no support for write-only directories) // Note: we check for executable bit otherwise we can't browse the directory // Note: we fail to test whether the path actually is a directory // Note: modifying permissions while or after this function runs has undefined behaviour #ifndef _WIN32 if (access(d->path.c_str(), R_OK | X_OK | F_OK) == 0) { // Note: disallow multiple write directories. // There isn't really a use for it as every thing is written to the first one anyway, // and it may give funny effects on errors, e.g. it probably only gives funny things // like network mounted datadir lost connection and suddenly files end up in some // other random writedir you didn't even remember you had added it. if (!writedir && access(d->path.c_str(), W_OK) == 0) { #else if (_access(d->path.c_str(), 4) == 0) { if (!writedir && _access(d->path.c_str(), 2) == 0) { #endif d->writable = true; writedir = &*d; } return true; } else { if (filesystem.CreateDirectory(d->path)) { // it didn't exist before, now it does and we just created it with rw access, // so we just assume we still have read-write acces... if (!writedir) { d->writable = true; writedir = d; } return true; } } return false; } /** * @brief Figure out permissions we have for the data directories. */ void DataDirLocater::DeterminePermissions() { std::vector<DataDir> newDatadirs; std::string previous; // used to filter out consecutive duplicates // (I didn't bother filtering out non-consecutive duplicates because then // there is the question which of the multiple instances to purge.) writedir = NULL; for (std::vector<DataDir>::iterator d = datadirs.begin(); d != datadirs.end(); ++d) { if (d->path != previous && DeterminePermissions(&*d)) { newDatadirs.push_back(*d); previous = d->path; } } datadirs = newDatadirs; } /** * @brief locate spring data directory * * On *nix platforms, attempts to locate * and change to the spring data directory * * In Unixes, the data directory to chdir to is determined by the following, in this * order (first items override lower items): * * - 'SPRING_DATADIR' environment variable. (colon separated list, like PATH) * - 'SpringData=/path/to/data' declaration in '~/.springrc'. (colon separated list) * - "$HOME/.spring" * - In the same order any line in '/etc/spring/datadir', if that file exists. * - 'datadir=/path/to/data' option passed to 'scons configure'. * - 'prefix=/install/path' option passed to scons configure. The datadir is * assumed to be at '$prefix/games/spring' in this case. * - the default datadirs in the default prefix, ie. '/usr/local/games/spring' * (This is set by the build system, ie. SPRING_DATADIR and SPRING_DATADIR_2 * preprocessor definitions.) * * In Windows, its: * - SPRING_DATADIR env-variable * - user configurable (SpringData in registry) * - location of the binary dir (like it has been until 0.76b1) * - the Users 'Documents'-directory (in subdirectory Spring), unless spring is configured to use another * - all users app-data (in subdirectory Spring) * - compiler flags SPRING_DATADIR and SPRING_DATADIR_2 * * All of the above methods support environment variable substitution, eg. * '$HOME/myspringdatadir' will be converted by spring to something like * '/home/username/myspringdatadir'. * * If it fails to chdir to the above specified directory spring will asume the * current working directory is the data directory. */ void DataDirLocater::LocateDataDirs() { // Construct the list of datadirs from various sources. datadirs.clear(); // environment variable char* env = getenv("SPRING_DATADIR"); if (env && *env) AddDirs(SubstEnvVars(env)); // user defined (in spring config handler (Linux: ~/.spring, Windows: registry)) std::string userDef = configHandler.GetString("SpringData", ""); if (!userDef.empty()) { AddDirs(SubstEnvVars(userDef)); } #ifdef WIN32 TCHAR currentDir[MAX_PATH]; ::GetCurrentDirectory(sizeof(currentDir) - 1, currentDir); std::string curPath = currentDir; AddDirs(std::string(currentDir)); // my documents TCHAR strPath[MAX_PATH]; SHGetFolderPath( NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, strPath); std::string cfg = strPath; cfg += "\\Spring"; // e.g. F:\Dokumente und Einstellungen\Karl-Robert\Eigene Dateien\Spring AddDirs(cfg); cfg.clear(); // appdata SHGetFolderPath( NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, strPath); cfg = strPath; cfg += "\\Spring"; // e.g. F:\Dokumente und Einstellungen\All Users\Anwendungsdaten\Spring AddDirs(cfg); #else // home AddDirs(SubstEnvVars("$HOME/.spring")); // settings in /etc FILE* f = ::fopen("/etc/spring/datadir", "r"); if (f) { char buf[1024]; while (fgets(buf, sizeof(buf), f)) { char* newl = strchr(buf, '\n'); if (newl) *newl = 0; char white[3] = {'\t', ' ', 0}; if (strlen(buf) > 0 && strspn(buf, white) != strlen(buf)) // don't count lines of whitespaces / tabulators AddDirs(SubstEnvVars(buf)); } fclose(f); } #endif // compiler flags #ifdef SPRING_DATADIR datadirs.push_back(SubstEnvVars(SPRING_DATADIR)); #endif // should not be needed because you can seperate directories with a ':' in SPRING_DATADIR(1) #ifdef SPRING_DATADIR_2 datadirs.push_back(SubstEnvVars(SPRING_DATADIR_2)); #endif // Figure out permissions of all datadirs DeterminePermissions(); if (!writedir) { // bail out throw content_error("Not a single writable data directory found!\n\n" "Configure a writable data directory using either:\n" "- the SPRING_DATADIR environment variable,\n" "- a SpringData=/path/to/data declaration in ~/.springrc or\n" "- the configuration file /etc/spring/datadir"); } // for now, chdir to the datadirectory as a safety measure: // all AIs still just assume it's ok to put their stuff in the current directory after all // Not only safety anymore, it's just easier if other code can safely assume that // writedir == current working directory #ifndef _WIN32 chdir(GetWriteDir()->path.c_str()); #else _chdir(GetWriteDir()->path.c_str()); #endif // Logging MAY NOT start before the chdir, otherwise the logfile ends up // in the wrong directory. for (std::vector<DataDir>::const_iterator d = datadirs.begin(); d != datadirs.end(); ++d) { if (d->writable) logOutput.Print("Using read-write data directory: %s", d->path.c_str()); else logOutput.Print("Using read-only data directory: %s", d->path.c_str()); } }
void CModInfo::Init(const char* modname) { filename = modname; humanName = archiveScanner->ModArchiveToModName(modname); const CArchiveScanner::ModData md = archiveScanner->ModArchiveToModData(modname); shortName = md.shortName; version = md.version; mutator = md.mutator; description = md.description; // initialize the parser LuaParser parser("gamedata/modrules.lua", SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP); // customize the defs environment parser.GetTable("Spring"); parser.AddFunc("GetModOptions", LuaSyncedRead::GetModOptions); parser.EndTable(); parser.Execute(); if (!parser.IsValid()) { logOutput.Print("Error loading modrules, using defaults"); logOutput.Print(parser.GetErrorLog()); } const LuaTable root = parser.GetRoot(); // determine if bombers are allowed to leave map boundaries const LuaTable movementTbl = root.SubTable("movement"); allowAirPlanesToLeaveMap = movementTbl.GetBool("allowAirPlanesToLeaveMap", true); // determine whether the modder allows the user to use team coloured nanospray const LuaTable nanosprayTbl = root.SubTable("nanospray"); allowTeamColors = nanosprayTbl.GetBool("allow_team_colors", true); if (allowTeamColors) { // Load the users preference for team coloured nanospray gu->teamNanospray = !!configHandler->Get("TeamNanoSpray", 1); } // constructions const LuaTable constructionTbl = root.SubTable("construction"); constructionDecay = constructionTbl.GetBool("constructionDecay", true); constructionDecayTime = (int)(constructionTbl.GetFloat("constructionDecayTime", 6.66) * 30); constructionDecaySpeed = constructionTbl.GetFloat("constructionDecaySpeed", 0.03); // reclaim const LuaTable reclaimTbl = root.SubTable("reclaim"); multiReclaim = reclaimTbl.GetInt("multiReclaim", 0); reclaimMethod = reclaimTbl.GetInt("reclaimMethod", 1); reclaimUnitMethod = reclaimTbl.GetInt("unitMethod", 1); reclaimUnitEnergyCostFactor = reclaimTbl.GetFloat("unitEnergyCostFactor", 0.0); reclaimUnitEfficiency = reclaimTbl.GetFloat("unitEfficiency", 1.0); reclaimFeatureEnergyCostFactor = reclaimTbl.GetFloat("featureEnergyCostFactor", 0.0); reclaimAllowEnemies = reclaimTbl.GetBool("allowEnemies", true); reclaimAllowAllies = reclaimTbl.GetBool("allowAllies", true); // repair const LuaTable repairTbl = root.SubTable("repair"); repairEnergyCostFactor = repairTbl.GetFloat("energyCostFactor", 0.0); // resurrect const LuaTable resurrectTbl = root.SubTable("resurrect"); resurrectEnergyCostFactor = resurrectTbl.GetFloat("energyCostFactor", 0.5); // capture const LuaTable captureTbl = root.SubTable("capture"); captureEnergyCostFactor = captureTbl.GetFloat("energyCostFactor", 0.0); // fire-at-dead-units const LuaTable fireAtDeadTbl = root.SubTable("fireAtDead"); fireAtKilled = fireAtDeadTbl.GetBool("fireAtKilled", false); fireAtCrashing = fireAtDeadTbl.GetBool("fireAtCrashing", false); // transportability const LuaTable transportTbl = root.SubTable("transportability"); transportAir = transportTbl.GetInt("transportAir", false); transportShip = transportTbl.GetInt("transportShip", false); transportHover = transportTbl.GetInt("transportHover", false); transportGround = transportTbl.GetInt("transportGround", true); // experience const LuaTable experienceTbl = root.SubTable("experience"); CUnit::SetExpMultiplier (experienceTbl.GetFloat("experienceMult", 1.0f)); CUnit::SetExpPowerScale (experienceTbl.GetFloat("powerScale", 1.0f)); CUnit::SetExpHealthScale(experienceTbl.GetFloat("healthScale", 0.7f)); CUnit::SetExpReloadScale(experienceTbl.GetFloat("reloadScale", 0.4f)); // flanking bonus const LuaTable flankingBonusTbl = root.SubTable("flankingBonus"); flankingBonusModeDefault = flankingBonusTbl.GetInt("defaultMode", 1); // feature visibility const LuaTable featureLOS = root.SubTable("featureLOS"); featureVisibility = featureLOS.GetInt("featureVisibility", 3); if (featureVisibility < 0 || featureVisibility > 3) throw content_error("invalid modinfo: featureVisibility, valid range is 0..3"); // sensors const LuaTable sensors = root.SubTable("sensors"); requireSonarUnderWater = sensors.GetBool("requireSonarUnderWater", true); /// LoS const LuaTable los = sensors.SubTable("los"); // losMipLevel is used as index to readmap->mipHeightmap, // so the max value is CReadMap::numHeightMipMaps - 1 losMipLevel = los.GetInt("losMipLevel", 1); losMul = los.GetFloat("losMul", 1.0f); if ((losMipLevel < 0) || (losMipLevel > 6)) { throw content_error("Sensors\\Los\\LosMipLevel out of bounds. " "The minimum value is 0. The maximum value is 6."); } // airLosMipLevel doesn't have such restrictions, it's just used in various // bitshifts with signed integers airMipLevel = los.GetInt("airMipLevel", 2); if ((airMipLevel < 0) || (airMipLevel > 30)) { throw content_error("Sensors\\Los\\AirLosMipLevel out of bounds. " "The minimum value is 0. The maximum value is 30."); } airLosMul = los.GetFloat("airLosMul", 1.0f); }
CWeapon* CUnitLoader::LoadWeapon(const WeaponDef *weapondef, CUnit* owner, const UnitDef::UnitDefWeapon* udw) { CWeapon* weapon; if (!weapondef) { logOutput.Print("Error: No weapon def?"); } if (udw->name == "NOWEAPON") { weapon = new CNoWeapon(owner); } else if (weapondef->type == "Cannon") { weapon = new CCannon(owner); ((CCannon*)weapon)->selfExplode = weapondef->selfExplode; } else if (weapondef->type == "Rifle") { weapon = new CRifle(owner); } else if (weapondef->type == "Melee") { weapon = new CMeleeWeapon(owner); } else if (weapondef->type == "AircraftBomb") { weapon = new CBombDropper(owner, false); } else if (weapondef->type == "Shield") { weapon = new CPlasmaRepulser(owner); } else if (weapondef->type == "Flame") { weapon = new CFlameThrower(owner); } else if (weapondef->type == "MissileLauncher") { weapon = new CMissileLauncher(owner); } else if (weapondef->type == "TorpedoLauncher") { if (owner->unitDef->canfly && !weapondef->submissile) { weapon = new CBombDropper(owner, true); if (weapondef->tracks) ((CBombDropper*) weapon)->tracking = weapondef->turnrate; ((CBombDropper*) weapon)->bombMoveRange = weapondef->range; } else { weapon = new CTorpedoLauncher(owner); if (weapondef->tracks) ((CTorpedoLauncher*) weapon)->tracking = weapondef->turnrate; } } else if (weapondef->type == "LaserCannon") { weapon = new CLaserCannon(owner); ((CLaserCannon*) weapon)->color = weapondef->visuals.color; } else if (weapondef->type == "BeamLaser") { weapon = new CBeamLaser(owner); ((CBeamLaser*) weapon)->color = weapondef->visuals.color; } else if (weapondef->type == "LightningCannon") { weapon = new CLightningCannon(owner); ((CLightningCannon*) weapon)->color = weapondef->visuals.color; } else if (weapondef->type == "EmgCannon") { weapon = new CEmgCannon(owner); } else if (weapondef->type == "DGun") { weapon = new CDGunWeapon(owner); } else if (weapondef->type == "StarburstLauncher"){ weapon = new CStarburstLauncher(owner); if (weapondef->tracks) ((CStarburstLauncher*) weapon)->tracking = weapondef->turnrate; else ((CStarburstLauncher*) weapon)->tracking = 0; ((CStarburstLauncher*) weapon)->uptime = weapondef->uptime * GAME_SPEED; } else { LogObject() << "Unknown weapon type " << weapondef->type.c_str() << "\n"; return 0; } weapon->weaponDef = weapondef; weapon->reloadTime = (int) (weapondef->reload * GAME_SPEED); if (weapon->reloadTime == 0) weapon->reloadTime = 1; weapon->range = weapondef->range; // weapon->baseRange = weapondef->range; weapon->heightMod = weapondef->heightmod; weapon->projectileSpeed = weapondef->projectilespeed; // weapon->baseProjectileSpeed = weapondef->projectilespeed / GAME_SPEED; weapon->areaOfEffect = weapondef->areaOfEffect; weapon->accuracy = weapondef->accuracy; weapon->sprayAngle = weapondef->sprayAngle; weapon->stockpileTime = (int) (weapondef->stockpileTime * GAME_SPEED); weapon->salvoSize = weapondef->salvosize; weapon->salvoDelay = (int) (weapondef->salvodelay * GAME_SPEED); weapon->projectilesPerShot = weapondef->projectilespershot; weapon->metalFireCost = weapondef->metalcost; weapon->energyFireCost = weapondef->energycost; weapon->fireSoundId = weapondef->firesound.getID(0); weapon->fireSoundVolume = weapondef->firesound.getVolume(0); weapon->onlyForward = weapondef->onlyForward; if (owner->unitDef->type == "Fighter" && !owner->unitDef->hoverAttack) { // fighter aircraft have too big tolerance in TA weapon->maxAngleDif = cos(weapondef->maxAngle * 0.4f / 180 * PI); } else { weapon->maxAngleDif = cos(weapondef->maxAngle / 180 * PI); } weapon->SetWeaponNum(owner->weapons.size()); weapon->badTargetCategory = udw->badTargetCat; weapon->onlyTargetCategory = weapondef->onlyTargetCategory & udw->onlyTargetCat; if (udw->slavedTo) { const int index = (udw->slavedTo - 1); if ((index < 0) || (static_cast<size_t>(index) >= owner->weapons.size())) { throw content_error("Bad weapon slave in " + owner->unitDef->name); } weapon->slavedTo = owner->weapons[index]; } weapon->mainDir = udw->mainDir; weapon->maxMainDirAngleDif = udw->maxAngleDif; weapon->fuelUsage = udw->fuelUsage; weapon->avoidFriendly = weapondef->avoidFriendly; weapon->avoidFeature = weapondef->avoidFeature; weapon->avoidNeutral = weapondef->avoidNeutral; weapon->targetBorder = weapondef->targetBorder; weapon->cylinderTargetting = weapondef->cylinderTargetting; weapon->minIntensity = weapondef->minIntensity; weapon->heightBoostFactor = weapondef->heightBoostFactor; weapon->collisionFlags = weapondef->collisionFlags; weapon->Init(); return weapon; }
S3DModel* CAssParser::Load(const std::string& modelFilePath) { LOG_SL(LOG_SECTION_MODEL, L_INFO, "Loading model: %s", modelFilePath.c_str()); const std::string& modelPath = FileSystem::GetDirectory(modelFilePath); const std::string& modelName = FileSystem::GetBasename(modelFilePath); // Load the lua metafile. This contains properties unique to Spring models and must return a table std::string metaFileName = modelFilePath + ".lua"; if (!CFileHandler::FileExists(metaFileName, SPRING_VFS_ZIP)) { // Try again without the model file extension metaFileName = modelPath + '/' + modelName + ".lua"; } if (!CFileHandler::FileExists(metaFileName, SPRING_VFS_ZIP)) { LOG_SL(LOG_SECTION_MODEL, L_INFO, "No meta-file '%s'. Using defaults.", metaFileName.c_str()); } LuaParser metaFileParser(metaFileName, SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP); if (!metaFileParser.Execute()) { LOG_SL(LOG_SECTION_MODEL, L_ERROR, "'%s': %s. Using defaults.", metaFileName.c_str(), metaFileParser.GetErrorLog().c_str()); } // Get the (root-level) model table const LuaTable& modelTable = metaFileParser.GetRoot(); if (!modelTable.IsValid()) { LOG_SL(LOG_SECTION_MODEL, L_INFO, "No valid model metadata in '%s' or no meta-file", metaFileName.c_str()); } // Create a model importer instance Assimp::Importer importer; // Create a logger for debugging model loading issues Assimp::DefaultLogger::create("", Assimp::Logger::VERBOSE); Assimp::DefaultLogger::get()->attachStream(new AssLogStream(), ASS_LOGGING_OPTIONS); // Give the importer an IO class that handles Spring's VFS importer.SetIOHandler(new AssVFSSystem()); // Speed-up processing by skipping things we don't need importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, ASS_IMPORTER_OPTIONS); #ifndef BITMAP_NO_OPENGL { // Optimize VBO-Mesh sizes/ranges GLint maxIndices = 1024; GLint maxVertices = 1024; // FIXME returns non-optimal data, at best compute it ourselves (pre-TL cache size!) glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &maxIndices); glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &maxVertices); importer.SetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT, maxVertices); importer.SetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT, maxIndices / 3); } #endif // Read the model file to build a scene object LOG_SL(LOG_SECTION_MODEL, L_INFO, "Importing model file: %s", modelFilePath.c_str()); const aiScene* scene; { // ASSIMP spams many SIGFPEs atm in normal & tangent generation ScopedDisableFpuExceptions fe; scene = importer.ReadFile(modelFilePath, ASS_POSTPROCESS_OPTIONS); } if (scene != NULL) { LOG_SL(LOG_SECTION_MODEL, L_INFO, "Processing scene for model: %s (%d meshes / %d materials / %d textures)", modelFilePath.c_str(), scene->mNumMeshes, scene->mNumMaterials, scene->mNumTextures); } else { throw content_error("[AssimpParser] Model Import: " + std::string(importer.GetErrorString())); } S3DModel* model = new S3DModel(); model->name = modelFilePath; model->type = MODELTYPE_ASS; // Load textures FindTextures(model, scene, modelTable, modelPath, modelName); LOG_SL(LOG_SECTION_MODEL, L_INFO, "Loading textures. Tex1: '%s' Tex2: '%s'", model->tex1.c_str(), model->tex2.c_str()); texturehandlerS3O->LoadS3OTexture(model); // Load all pieces in the model LOG_SL(LOG_SECTION_MODEL, L_INFO, "Loading pieces from root node '%s'", scene->mRootNode->mName.data); LoadPiece(model, scene->mRootNode, scene, modelTable); // Update piece hierarchy based on metadata BuildPieceHierarchy(model); CalculateModelProperties(model, modelTable); // Verbose logging of model properties LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->name: %s", model->name.c_str()); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->numobjects: %d", model->numPieces); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->radius: %f", model->radius); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->height: %f", model->height); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->drawRadius: %f", model->drawRadius); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->mins: (%f,%f,%f)", model->mins[0], model->mins[1], model->mins[2]); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->maxs: (%f,%f,%f)", model->maxs[0], model->maxs[1], model->maxs[2]); LOG_SL(LOG_SECTION_MODEL, L_INFO, "Model %s Imported.", model->name.c_str()); return model; }
CUnit* CUnitLoader::LoadUnit(const UnitDef* ud, float3 pos, int team, bool build, int facing, const CUnit* builder) { GML_RECMUTEX_LOCK(sel); // LoadUnit - for anti deadlock purposes. GML_RECMUTEX_LOCK(quad); // LoadUnit - make sure other threads cannot access an incomplete unit SCOPED_TIMER("Unit loader"); CUnit* unit; std::string type = ud->type; // clamp to map if (pos.x < 0) pos.x = 0; if (pos.x >= gs->mapx * SQUARE_SIZE) pos.x = gs->mapx-1; if (pos.z < 0) pos.z = 0; if (pos.z >= gs->mapy * SQUARE_SIZE) pos.z = gs->mapy-1; if (!build) { pos.y = ground->GetHeight2(pos.x, pos.z); if (ud->floater && pos.y < 0.0f) { // adjust to waterline iif we are submerged pos.y = -ud->waterline; } } if (team < 0) { team = teamHandler->GaiaTeamID(); // FIXME use gs->gaiaTeamID ? (once it is always enabled) if (team < 0) throw content_error("Invalid team and no gaia team to put unit in"); } if (type == "GroundUnit") { unit = new CUnit; } else if (type == "Transport") { unit = new CTransportUnit; } else if (type == "Building") { unit = new CBuilding; } else if (type == "Factory") { unit = new CFactory; } else if (type == "Builder") { unit = new CBuilder; } else if (type == "Bomber" || type == "Fighter") { unit = new CUnit; } else if (type == "MetalExtractor") { unit = new CExtractorBuilding; } else { LogObject() << "Unknown unit type " << type.c_str() << "\n"; return NULL; } unit->UnitInit(ud, team, pos); unit->beingBuilt = build; unit->buildFacing = abs(facing) % 4; unit->xsize = ((unit->buildFacing & 1) == 0) ? ud->xsize : ud->zsize; unit->zsize = ((unit->buildFacing & 1) == 1) ? ud->xsize : ud->zsize; unit->power = ud->power; unit->maxHealth = ud->health; unit->health = ud->health; //unit->metalUpkeep = ud->metalUpkeep*16.0f/GAME_SPEED; //unit->energyUpkeep = ud->energyUpkeep*16.0f/GAME_SPEED; unit->controlRadius = (int)(ud->controlRadius / SQUARE_SIZE); unit->losHeight = ud->losHeight; unit->metalCost = ud->metalCost; unit->energyCost = ud->energyCost; unit->buildTime = ud->buildTime; unit->aihint = ud->aihint; unit->tooltip = ud->humanName + " - " + ud->tooltip; unit->armoredMultiple = std::max(0.0001f, ud->armoredMultiple); //armored multiple of 0 will crash spring unit->wreckName = ud->wreckName; unit->realLosRadius = (int) (ud->losRadius); unit->realAirLosRadius = (int) (ud->airLosRadius); unit->upright = ud->upright; unit->radarRadius = ud->radarRadius / (SQUARE_SIZE * 8); unit->sonarRadius = ud->sonarRadius / (SQUARE_SIZE * 8); unit->jammerRadius = ud->jammerRadius / (SQUARE_SIZE * 8); unit->sonarJamRadius = ud->sonarJamRadius / (SQUARE_SIZE * 8); unit->seismicRadius = ud->seismicRadius / (SQUARE_SIZE * 8); unit->seismicSignature = ud->seismicSignature; unit->hasRadarCapacity = unit->radarRadius || unit->sonarRadius || unit->jammerRadius || unit->sonarJamRadius || unit->seismicRadius; unit->stealth = ud->stealth; unit->sonarStealth = ud->sonarStealth; unit->category = ud->category; unit->armorType = ud->armorType; unit->floatOnWater = ud->floater || (ud->movedata && ((ud->movedata->moveType == MoveData::Hover_Move) || (ud->movedata->moveType == MoveData::Ship_Move))); unit->maxSpeed = ud->speed / GAME_SPEED; unit->decloakDistance = ud->decloakDistance; unit->flankingBonusMode = ud->flankingBonusMode; unit->flankingBonusDir = ud->flankingBonusDir; unit->flankingBonusMobility = ud->flankingBonusMobilityAdd * 1000; unit->flankingBonusMobilityAdd = ud->flankingBonusMobilityAdd; unit->flankingBonusAvgDamage = (ud->flankingBonusMax + ud->flankingBonusMin) * 0.5f; unit->flankingBonusDifDamage = (ud->flankingBonusMax - ud->flankingBonusMin) * 0.5f; if(ud->highTrajectoryType==1) unit->useHighTrajectory=true; if(ud->fireState >= 0) unit->fireState = ud->fireState; if(build){ unit->ChangeLos(1,1); unit->health=0.1f; } else { unit->ChangeLos((int)(ud->losRadius),(int)(ud->airLosRadius)); } if (type == "GroundUnit") { new CMobileCAI(unit); } else if (type == "Transport") { new CTransportCAI(unit); } else if (type == "Factory") { new CFactoryCAI(unit); } else if (type == "Builder") { new CBuilderCAI(unit); } else if (type == "Bomber") { if (ud->hoverAttack) { new CMobileCAI(unit); } else { new CAirCAI(unit); } } else if(type == "Fighter"){ if (ud->hoverAttack) { new CMobileCAI(unit); } else { new CAirCAI(unit); } } else { new CCommandAI(unit); } if (ud->canmove && !ud->canfly && (type != "Factory")) { CGroundMoveType* mt = new CGroundMoveType(unit); mt->maxSpeed = ud->speed / GAME_SPEED; mt->maxWantedSpeed = ud->speed / GAME_SPEED; mt->turnRate = ud->turnRate; mt->baseTurnRate = ud->turnRate; if (mt->accRate <= 0.0f) { LogObject() << "acceleration of unit-type " << ud->name.c_str() << " is zero or negative!\n"; mt->accRate = 0.01f; } mt->accRate = ud->maxAcc; mt->decRate = ud->maxDec; mt->floatOnWater = (ud->movedata->moveType == MoveData::Hover_Move || ud->movedata->moveType == MoveData::Ship_Move); if (!unit->beingBuilt) { // otherwise set this when finished building instead unit->mass = ud->mass; } unit->moveType = mt; // Ground-mobility unit->mobility = new MoveData(ud->movedata); } else if (ud->canfly) { // Air-mobility unit->mobility = new MoveData(ud->movedata); if (!unit->beingBuilt) { // otherwise set this when finished building instead unit->mass = ud->mass; } if ((type == "Builder") || ud->hoverAttack || ud->transportCapacity) { CTAAirMoveType* mt = new CTAAirMoveType(unit); mt->turnRate = ud->turnRate; mt->maxSpeed = ud->speed / GAME_SPEED; mt->accRate = ud->maxAcc; mt->decRate = ud->maxDec; mt->wantedHeight = ud->wantedHeight + gs->randFloat() * 5; mt->orgWantedHeight = mt->wantedHeight; mt->dontLand = ud->DontLand(); mt->collide = ud->collide; mt->altitudeRate = ud->verticalSpeed; mt->bankingAllowed = ud->bankingAllowed; unit->moveType = mt; } else { CAirMoveType *mt = new CAirMoveType(unit); if(type=="Fighter") mt->isFighter=true; mt->collide = ud->collide; mt->wingAngle = ud->wingAngle; mt->crashDrag = 1 - ud->crashDrag; mt->invDrag = 1 - ud->drag; mt->frontToSpeed = ud->frontToSpeed; mt->speedToFront = ud->speedToFront; mt->myGravity = ud->myGravity; mt->maxBank = ud->maxBank; mt->maxPitch = ud->maxPitch; mt->turnRadius = ud->turnRadius; mt->wantedHeight = (ud->wantedHeight * 1.5f) + ((gs->randFloat() - 0.3f) * 15 * (mt->isFighter ? 2 : 1)); mt->maxAcc = ud->maxAcc; mt->maxAileron = ud->maxAileron; mt->maxElevator = ud->maxElevator; mt->maxRudder = ud->maxRudder; unit->moveType = mt; } } else { unit->moveType = new CMoveType(unit); unit->upright = true; } unit->energyTickMake = ud->energyMake; if (ud->tidalGenerator > 0) unit->energyTickMake += ud->tidalGenerator * mapInfo->map.tidalStrength; unit->model = ud->LoadModel(); unit->SetRadius(unit->model->radius); modelParser->CreateLocalModel(unit); // copy the UnitDef volume instance // // aircraft still get half-size spheres for coldet purposes // iif no custom volume is defined (unit->model->radius and // unit->radius themselves are no longer altered) unit->collisionVolume = new CollisionVolume(ud->collisionVolume, unit->model->radius * ((ud->canfly)? 0.5f: 1.0f)); if (unit->model->radius <= 60.0f) { // the interval-based method fails too easily for units // with small default volumes, force use of raytracing unit->collisionVolume->SetTestType(COLVOL_TEST_CONT); } if (ud->floater) { // restrict our depth to our waterline unit->pos.y = std::max(-ud->waterline, ground->GetHeight2(unit->pos.x, unit->pos.z)); } else { unit->pos.y = ground->GetHeight2(unit->pos.x, unit->pos.z); } unit->script = CUnitScriptFactory::CreateScript(ud->scriptPath, unit); unit->weapons.reserve(ud->weapons.size()); for (unsigned int i = 0; i < ud->weapons.size(); i++) { unit->weapons.push_back(LoadWeapon(ud->weapons[i].def, unit, &ud->weapons[i])); } // Call initializing script functions unit->script->Create(); unit->heading = GetHeadingFromFacing(facing); unit->frontdir = GetVectorFromHeading(unit->heading); unit->updir = UpVector; unit->rightdir = unit->frontdir.cross(unit->updir); unit->yardMap = ud->yardmaps[facing]; unit->Init(builder); if (!build) { unit->FinishedBuilding(); } return unit; }
void CPreGame::UpdateClientNet() { int a; if((a=net->GetData(&inbuf[inbuflength],15000-inbuflength,0))==-1){ globalQuit=true; return; } inbuflength+=a; while(inbufpos<inbuflength){ switch (inbuf[inbufpos]){ case NETMSG_HELLO: inbufpos += 1; break; case NETMSG_SCRIPT: CScriptHandler::SelectScript((char*)(&inbuf[inbufpos+2])); if (mapName.empty()) state = WAIT_ON_MAP; else if (modName.empty()) state = WAIT_ON_MOD; else state = ALL_READY; inbufpos += inbuf[inbufpos+1]; break; case NETMSG_MAPNAME: SelectMap((char*)(&inbuf[inbufpos+6])); if (GetMapChecksum() != *(unsigned*)(&inbuf[inbufpos+2])) { char buf[256]; sprintf(buf, "Local map archive(s) are not binary equal to host map archive(s).\n" "Make sure you installed all map dependencies & consider redownloading the map." "\n\nLocal checksum = %u\nRemote checksum = %u", GetMapChecksum(), *(unsigned*)(&inbuf[inbufpos+2])); throw content_error(buf); } if (!CScriptHandler::Instance().chosenScript) state = WAIT_ON_SCRIPT; else if (modName.empty()) state = WAIT_ON_MOD; else state = ALL_READY; inbufpos += inbuf[inbufpos+1]; break; case NETMSG_MODNAME: SelectMod((char*)(&inbuf[inbufpos+6])); if (GetModChecksum() != *(unsigned*)(&inbuf[inbufpos+2])) { char buf[256]; sprintf(buf, "Local mod archive(s) are not binary equal to host mod archive(s).\n" "Make sure you installed all mod dependencies & consider redownloading the mod." "\n\nLocal checksum = %u\nRemote checksum = %u", GetModChecksum(), *(unsigned*)(&inbuf[inbufpos+2])); throw content_error(buf); } if (!CScriptHandler::Instance().chosenScript) state = WAIT_ON_SCRIPT; else if (mapName.empty()) state = WAIT_ON_MAP; else state = ALL_READY; inbufpos += inbuf[inbufpos+1]; case NETMSG_MAPDRAW: inbufpos += inbuf[inbufpos+1]; break; case NETMSG_SYSTEMMSG: case NETMSG_CHAT:{ int player=inbuf[inbufpos+2]; string s=(char*)(&inbuf[inbufpos+3]); logOutput.Print(s); inbufpos += inbuf[inbufpos+1]; break;} case NETMSG_STARTPOS:{ inbufpos += 15; break;} case NETMSG_SETPLAYERNUM: gu->myPlayerNum=inbuf[inbufpos+1]; logOutput.Print("Became player %i",gu->myPlayerNum); inbufpos += 2; break; case NETMSG_PLAYERNAME: gs->players[inbuf[inbufpos+2]]->playerName=(char*)(&inbuf[inbufpos+3]); gs->players[inbuf[inbufpos+2]]->readyToStart=true; gs->players[inbuf[inbufpos+2]]->active=true; inbufpos += inbuf[inbufpos+1]; break; case NETMSG_QUIT: net->connected=false; globalQuit=true; return; case NETMSG_USER_SPEED: case NETMSG_INTERNAL_SPEED: inbufpos += 5; break; default: char txt[200]; sprintf(txt,"Unknown net msg in client %d",(int)inbuf[inbufpos]); handleerror(0,txt,"Network error in CPreGame",0); inbufpos++; break; } } }
S3DOModel* CS3OParser::LoadS3O(std::string name,float scale,int side) { GML_STDMUTEX_LOCK(model); // LoadS3O if(name.find(".")==std::string::npos) name+=".s3o"; StringToLowerInPlace(name); std::map<std::string,S3DOModel*>::iterator ui; if((ui=units.find(name))!=units.end()){ return ui->second; } PUSH_CODE_MODE; ENTER_SYNCED; CFileHandler file(name); if(!file.FileExists()){ POP_CODE_MODE; throw content_error("File not found: "+name); } unsigned char* fileBuf=SAFE_NEW unsigned char[file.FileSize()]; file.Read(fileBuf, file.FileSize()); S3OHeader header; memcpy(&header,fileBuf,sizeof(header)); header.swap(); S3DOModel *model = SAFE_NEW S3DOModel; model->numobjects=0; SS3O* object=LoadPiece(fileBuf,header.rootPiece,model); model->rootobjects3o=object; model->rootobject3do=0; object->isEmpty=true; model->name=name; model->tex1=(char*)&fileBuf[header.texture1]; model->tex2=(char*)&fileBuf[header.texture2]; texturehandler->LoadS3OTexture(model); FindMinMax(object); units[name]=model; CreateLists(object); // this is a hack to make aircrafts less likely to collide and get hit by nontracking weapons // note: does not apply anymore, unit <--> projectile coldet no longer depends on model->radius model->radius = header.radius * scale; model->height = header.height; model->relMidPos.x=header.midx; model->relMidPos.y=header.midy; model->relMidPos.z=header.midz; if(model->relMidPos.y<1) model->relMidPos.y=1; // logOutput.Print("%s has height %f",name,model->height); fartextureHandler->CreateFarTexture(model); model->maxx=model->rootobjects3o->maxx; model->maxy=model->rootobjects3o->maxy; model->maxz=model->rootobjects3o->maxz; model->minx=model->rootobjects3o->minx; model->miny=model->rootobjects3o->miny; model->minz=model->rootobjects3o->minz; delete[] fileBuf; POP_CODE_MODE; return model; }
void CMouseHandler::ReloadCursors() { const CMouseCursor::HotSpot mCenter = CMouseCursor::Center; const CMouseCursor::HotSpot mTopLeft = CMouseCursor::TopLeft; activeCursorIdx = -1; loadedCursors.clear(); loadedCursors.reserve(32); // null-cursor; always lives at index 0 loadedCursors.emplace_back(); cursorCommandMap.clear(); cursorCommandMap.reserve(32); cursorFileMap.clear(); cursorFileMap.reserve(32); cursorCommandMap["none"] = loadedCursors.size() - 1; cursorFileMap["null"] = loadedCursors.size() - 1; AssignMouseCursor("", "cursornormal", mTopLeft, false); AssignMouseCursor("Area attack", "cursorareaattack", mCenter, false); AssignMouseCursor("Area attack", "cursorattack", mCenter, false); // backup AssignMouseCursor("Attack", "cursorattack", mCenter, false); AssignMouseCursor("AttackBad", "cursorattackbad", mCenter, false); AssignMouseCursor("AttackBad", "cursorattack", mCenter, false); // backup AssignMouseCursor("BuildBad", "cursorbuildbad", mCenter, false); AssignMouseCursor("BuildGood", "cursorbuildgood", mCenter, false); AssignMouseCursor("Capture", "cursorcapture", mCenter, false); AssignMouseCursor("Centroid", "cursorcentroid", mCenter, false); AssignMouseCursor("DeathWait", "cursordwatch", mCenter, false); AssignMouseCursor("DeathWait", "cursorwait", mCenter, false); // backup AssignMouseCursor("ManualFire", "cursormanfire", mCenter, false); AssignMouseCursor("ManualFire", "cursordgun", mCenter, false); // backup (backward compability) AssignMouseCursor("ManualFire", "cursorattack", mCenter, false); // backup AssignMouseCursor("Fight", "cursorfight", mCenter, false); AssignMouseCursor("Fight", "cursorattack", mCenter, false); // backup AssignMouseCursor("GatherWait", "cursorgather", mCenter, false); AssignMouseCursor("GatherWait", "cursorwait", mCenter, false); // backup AssignMouseCursor("Guard", "cursordefend", mCenter, false); AssignMouseCursor("Load units", "cursorpickup", mCenter, false); AssignMouseCursor("Move", "cursormove", mCenter, false); AssignMouseCursor("Patrol", "cursorpatrol", mCenter, false); AssignMouseCursor("Reclaim", "cursorreclamate", mCenter, false); AssignMouseCursor("Repair", "cursorrepair", mCenter, false); AssignMouseCursor("Resurrect", "cursorrevive", mCenter, false); AssignMouseCursor("Resurrect", "cursorrepair", mCenter, false); // backup AssignMouseCursor("Restore", "cursorrestore", mCenter, false); AssignMouseCursor("Restore", "cursorrepair", mCenter, false); // backup AssignMouseCursor("SelfD", "cursorselfd", mCenter, false); AssignMouseCursor("SquadWait", "cursornumber", mCenter, false); AssignMouseCursor("SquadWait", "cursorwait", mCenter, false); // backup AssignMouseCursor("TimeWait", "cursortime", mCenter, false); AssignMouseCursor("TimeWait", "cursorwait", mCenter, false); // backup AssignMouseCursor("Unload units", "cursorunload", mCenter, false); AssignMouseCursor("Wait", "cursorwait", mCenter, false); // the default cursor must exist const auto defCursorIt = cursorCommandMap.find(""); if (defCursorIt == cursorCommandMap.end()) { throw content_error( "Unable to load default cursor. Check that you have the required\n" "content packages installed in your Spring \"base/\" directory.\n"); } activeCursorIdx = defCursorIt->second; }
void CSpawnScript::Update() { switch(gs->frameNum){ case 0: LoadSpawns(); const std::string startUnit0 = sideParser.GetStartUnit(0); if (startUnit0.length() == 0) { throw content_error ("Unable to load a startUnit for the first side"); } MapParser mapParser(stupidGlobalMapname); if (!mapParser.IsValid()) { throw content_error("MapParser: " + mapParser.GetErrorLog()); } float3 startPos0(1000.0f, 80.0f, 1000.0f); mapParser.GetStartPos(0, startPos0); // Set the TEAM0 startpos as spawnpos if we're supposed to be // autonomous, load the commander for the player if not. if (autonomous) { spawnPos.push_back(startPos0); } else { unitLoader.LoadUnit(startUnit0, startPos0, 0, false, 0, NULL); } // load the start positions for teams 1 - 3 for (int teamID = 1; teamID <= 3; teamID++) { float3 sp(1000.0f, 80.0f, 1000.0f); mapParser.GetStartPos(teamID, sp); spawnPos.push_back(sp); } } if(!spawns.empty()){ while(curSpawn->frame+frameOffset<gs->frameNum){ int num = gs->randInt() % spawnPos.size(); int team = autonomous ? (num & 1) : 1; float3 pos; float dist=200; CFeature* feature; do { pos=spawnPos[num]+gs->randVector()*dist; dist*=1.05f; } while (dist < 500 && uh->TestUnitBuildSquare(BuildInfo(curSpawn->name,pos,0),feature,team) != 2); // Ignore unit if it really doesn't fit. // (within 18 tries, 200*1.05f^18 < 500 < 200*1.05f^19) if (dist < 500) { CUnit* u = unitLoader.LoadUnit(curSpawn->name, pos, team, false, 0, NULL); Unit unit; unit.id=u->id; unit.target=-1; unit.team=team; myUnits.push_back(unit); if(myUnits.size()==1) curUnit=myUnits.begin(); } ++curSpawn; if(curSpawn==spawns.end()){ curSpawn=spawns.begin(); frameOffset+=spawns.back().frame; } } } if(!myUnits.empty() && !gs->Team(1 - curUnit->team)->units.empty()) { if(uh->units[curUnit->id]){ if(curUnit->target<0 || uh->units[curUnit->target]==0){ // We can't rely on the ordering of units in a std::set<CUnit*>, // because they're sorted on memory address. Hence we must first // build a set of IDs and then pick an unit from that. // This guarantees the script doesn't desync in multiplayer games. int num = gs->randInt() % gs->Team(1 - curUnit->team)->units.size(); std::set<int> unitids; CUnitSet* tu = &gs->Team(1 - curUnit->team)->units; for (CUnitSet::iterator u = tu->begin(); u != tu->end(); ++u) unitids.insert((*u)->id); std::set<int>::iterator ui = unitids.begin(); for(int a=0;a<num;++a) ++ui; curUnit->target=(*ui); curUnit->lastTargetPos.x=-500; } float3 pos=uh->units[curUnit->target]->pos; if(pos.distance2D(curUnit->lastTargetPos)>100){ curUnit->lastTargetPos=pos; Command c; c.id=CMD_PATROL; c.options=0; c.params.push_back(pos.x); c.params.push_back(pos.y); c.params.push_back(pos.z); uh->units[curUnit->id]->commandAI->GiveCommand(c); } curUnit++; } else { curUnit=myUnits.erase(curUnit); } if(curUnit==myUnits.end()) curUnit=myUnits.begin(); } }
int main(int argc, char *argv[]) { #ifdef _WIN32 try { #endif SDL_Init(SDL_INIT_TIMER); std::cout << "If you find any errors, report them to mantis or the forums." << std::endl << std::endl; ConfigHandler::Instantiate(); // use the default config file FileSystemHandler::Initialize(false); CGameServer* server = 0; CGameSetup* gameSetup = 0; if (argc == 2) { const std::string script(argv[1]); std::string buf; std::cout << "Loading script from file: " << script << std::endl; ClientSetup settings; CFileHandler fh(argv[1]); if (!fh.FileExists()) throw content_error("script does not exist in given location: " + script); if (!fh.LoadStringData(buf)) throw content_error("script cannot be read: " + script); settings.Init(buf); gameSetup = new CGameSetup(); // to store the gamedata inside if (!gameSetup->Init(buf)) { // read the script provided by cmdline std::cout << "Failed to load script" << std::endl; return 1; } std::cout << "Starting server..." << std::endl; // Create the server, it will run in a separate thread GameData data; UnsyncedRNG rng; rng.Seed(gameSetup->gameSetupText.length()); rng.Seed(script.length()); data.SetRandomSeed(rng.RandInt()); // Use script provided hashes if they exist if (gameSetup->mapHash != 0) { data.SetMapChecksum(gameSetup->mapHash); gameSetup->LoadStartPositions(false); // reduced mode } else { data.SetMapChecksum(archiveScanner->GetArchiveCompleteChecksum(gameSetup->mapName)); CFileHandler f("maps/" + gameSetup->mapName); if (!f.FileExists()) { vfsHandler->AddArchiveWithDeps(gameSetup->mapName, false); } gameSetup->LoadStartPositions(); // full mode } if (gameSetup->modHash != 0) { data.SetModChecksum(gameSetup->modHash); } else { const std::string& modArchive = archiveScanner->ArchiveFromName(gameSetup->modName); const unsigned int modCheckSum = archiveScanner->GetArchiveCompleteChecksum(modArchive); data.SetModChecksum(modCheckSum); } data.SetSetup(gameSetup->gameSetupText); server = new CGameServer(settings.hostIP, settings.hostPort, &data, gameSetup); while (!server->HasFinished()) // check if still running #ifdef _WIN32 Sleep(1000); #else sleep(1); // if so, wait 1 second #endif delete server; // delete the server after usage } else { std::cout << "usage: " << argv[0] << " <full_path_to_script>" << std::endl; } FileSystemHandler::Cleanup(); ConfigHandler::Deallocate(); #ifdef _WIN32 } catch (const std::exception& err) { std::cout << "Exception raised: " << err.what() << std::endl; return 1; } #endif return 0; }
void CMapGenerator::GenerateSMT(CVirtualArchive* archive) { CVirtualFile* fileSMT = archive->AddFile("maps/generated.smt"); const int tileSize = 32; //--- Make TileFileHeader --- TileFileHeader smtHeader; strcpy(smtHeader.magic, "spring tilefile"); smtHeader.version = 1; smtHeader.numTiles = 1; //32 * 32 * (generator->GetMapSize().x * 32) * (generator->GetMapSize().y * 32); smtHeader.tileSize = tileSize; smtHeader.compressionType = 1; const int bpp = 3; int tilePos = 0; unsigned char tileData[tileSize * tileSize * bpp]; for(int x = 0; x < tileSize; x++) { for(int y = 0; y < tileSize; y++) { tileData[tilePos] = 0; tileData[tilePos + 1] = 0xFF; tileData[tilePos + 2] = 0; tilePos += bpp; } } glClearErrors(); GLuint tileTex; glGenTextures(1, &tileTex); glBindTexture(GL_TEXTURE_2D, tileTex); glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, tileSize, tileSize, 0, GL_RGB, GL_UNSIGNED_BYTE, tileData); glGenerateMipmapEXT(GL_TEXTURE_2D); char tileDataDXT[SMALL_TILE_SIZE]; int dxtImageOffset = 0; int dxtImageSize = 512; for(int x = 0; x < 4; x++) { glGetCompressedTexImage(GL_TEXTURE_2D, x, tileDataDXT + dxtImageOffset); dxtImageOffset += dxtImageSize; dxtImageSize /= 4; } glDeleteTextures(1, &tileTex); GLenum errorcode = glGetError(); if(errorcode != GL_NO_ERROR) { throw content_error("Error generating map - texture generation not supported"); } size_t totalSize = sizeof(TileFileHeader); fileSMT->buffer.resize(totalSize); int writePosition = 0; memcpy(&(fileSMT->buffer[writePosition]), &smtHeader, sizeof(smtHeader)); writePosition += sizeof(smtHeader); fileSMT->buffer.resize(fileSMT->buffer.size() + smtHeader.numTiles * SMALL_TILE_SIZE); for(int x = 0; x < smtHeader.numTiles; x++) { memcpy(&(fileSMT->buffer[writePosition]), tileDataDXT, SMALL_TILE_SIZE); writePosition += SMALL_TILE_SIZE; } }
void CMouseHandler::LoadCursors() { const CMouseCursor::HotSpot mCenter = CMouseCursor::Center; const CMouseCursor::HotSpot mTopLeft = CMouseCursor::TopLeft; CMouseCursor* nullCursor = CMouseCursor::GetNullCursor(); cursorCommandMap["none"] = nullCursor; // Note: we intentionally don't add it there cause GetNullCursor() returns // a pointer to a static var, so it gets automatically deleted //cursorFileMap["null"] = nullCursor; AssignMouseCursor("", "cursornormal", mTopLeft, false); AssignMouseCursor("Area attack", "cursorareaattack", mCenter, false); AssignMouseCursor("Area attack", "cursorattack", mCenter, false); // backup AssignMouseCursor("Attack", "cursorattack", mCenter, false); AssignMouseCursor("AttackBad", "cursorattackbad", mCenter, false); AssignMouseCursor("AttackBad", "cursorattack", mCenter, false); // backup AssignMouseCursor("BuildBad", "cursorbuildbad", mCenter, false); AssignMouseCursor("BuildGood", "cursorbuildgood", mCenter, false); AssignMouseCursor("Capture", "cursorcapture", mCenter, false); AssignMouseCursor("Centroid", "cursorcentroid", mCenter, false); AssignMouseCursor("DeathWait", "cursordwatch", mCenter, false); AssignMouseCursor("DeathWait", "cursorwait", mCenter, false); // backup AssignMouseCursor("ManualFire", "cursormanfire", mCenter, false); AssignMouseCursor("ManualFire", "cursordgun", mCenter, false); // backup (backward compability) AssignMouseCursor("ManualFire", "cursorattack", mCenter, false); // backup AssignMouseCursor("Fight", "cursorfight", mCenter, false); AssignMouseCursor("Fight", "cursorattack", mCenter, false); // backup AssignMouseCursor("GatherWait", "cursorgather", mCenter, false); AssignMouseCursor("GatherWait", "cursorwait", mCenter, false); // backup AssignMouseCursor("Guard", "cursordefend", mCenter, false); AssignMouseCursor("Load units", "cursorpickup", mCenter, false); AssignMouseCursor("Move", "cursormove", mCenter, false); AssignMouseCursor("Patrol", "cursorpatrol", mCenter, false); AssignMouseCursor("Reclaim", "cursorreclamate", mCenter, false); AssignMouseCursor("Repair", "cursorrepair", mCenter, false); AssignMouseCursor("Resurrect", "cursorrevive", mCenter, false); AssignMouseCursor("Resurrect", "cursorrepair", mCenter, false); // backup AssignMouseCursor("Restore", "cursorrestore", mCenter, false); AssignMouseCursor("Restore", "cursorrepair", mCenter, false); // backup AssignMouseCursor("SelfD", "cursorselfd", mCenter, false); AssignMouseCursor("SquadWait", "cursornumber", mCenter, false); AssignMouseCursor("SquadWait", "cursorwait", mCenter, false); // backup AssignMouseCursor("TimeWait", "cursortime", mCenter, false); AssignMouseCursor("TimeWait", "cursorwait", mCenter, false); // backup AssignMouseCursor("Unload units", "cursorunload", mCenter, false); AssignMouseCursor("Wait", "cursorwait", mCenter, false); // the default cursor must exist if (cursorCommandMap.find("") == cursorCommandMap.end()) { throw content_error( "Unable to load default cursor. Check that you have the required\n" "content packages installed in your Spring \"base/\" directory.\n"); } }
CUnit* CUnitLoader::LoadUnit(const UnitLoadParams& cparams) { CUnit* unit = NULL; UnitLoadParams& params = const_cast<UnitLoadParams&>(cparams); { GML_RECMUTEX_LOCK(sel); // LoadUnit - for anti deadlock purposes. GML_RECMUTEX_LOCK(quad); // LoadUnit - make sure other threads cannot access an incomplete unit const UnitDef* ud = params.unitDef; if (ud == NULL) return unit; // need to check this BEFORE creating the instance if (!unitHandler->CanAddUnit(cparams.unitID)) return unit; if (params.teamID < 0) { // FIXME use gs->gaiaTeamID ? (once it is always enabled) if ((params.teamID = teamHandler->GaiaTeamID()) < 0) throw content_error("Invalid team and no gaia team to put unit in"); } if (ud->IsTransportUnit()) { unit = new CTransportUnit(); } else if (ud->IsFactoryUnit()) { // special static builder structures that can always be given // move orders (which are passed on to all mobile buildees) unit = new CFactory(); } else if (ud->IsMobileBuilderUnit() || ud->IsStaticBuilderUnit()) { // all other types of non-structure "builders", including hubs and // nano-towers (the latter should not have any build-options at all, // whereas the former should be unable to build any mobile units) unit = new CBuilder(); } else if (ud->IsBuildingUnit()) { // static non-builder structures if (ud->IsExtractorUnit()) { unit = new CExtractorBuilding(); } else { unit = new CBuilding(); } } else { // regular mobile unit unit = new CUnit(); } unit->PreInit(params); if (ud->IsTransportUnit()) { new CTransportCAI(unit); } else if (ud->IsFactoryUnit()) { new CFactoryCAI(unit); } else if (ud->IsMobileBuilderUnit() || ud->IsStaticBuilderUnit()) { new CBuilderCAI(unit); } else if (ud->IsStrafingAirUnit()) { // non-hovering fighter or bomber aircraft; coupled to StrafeAirMoveType new CAirCAI(unit); } else if (ud->IsAirUnit()) { // all other aircraft; coupled to HoverAirMoveType new CMobileCAI(unit); } else if (ud->IsGroundUnit()) { new CMobileCAI(unit); } else { new CCommandAI(unit); } } unit->PostInit(params.builder); (eventBatchHandler->GetUnitCreatedDestroyedBatch()).enqueue(EventBatchHandler::UD(unit, unit->isCloaked)); if (params.flattenGround) { FlattenGround(unit); } return unit; }
CFactoryCAI::CFactoryCAI(CUnit* owner) : CCommandAI(owner), building(false), lastRestrictedWarning(0) { commandQue.SetQueueType(CCommandQueue::BuildQueueType); newUnitCommands.SetQueueType(CCommandQueue::NewUnitQueueType); CommandDescription c; // can't check for canmove here because it should be possible to assign rallypoint // with a non-moving factory. c.id=CMD_MOVE; c.action="move"; c.type=CMDTYPE_ICON_MAP; c.name="Move"; c.mouseicon=c.name; c.tooltip="Move: Order ready built units to move to a position"; possibleCommands.push_back(c); if (owner->unitDef->canPatrol) { c.id=CMD_PATROL; c.action="patrol"; c.type=CMDTYPE_ICON_MAP; c.name="Patrol"; c.mouseicon=c.name; c.tooltip="Patrol: Order ready built units to patrol to one or more waypoints"; possibleCommands.push_back(c); } if (owner->unitDef->canFight) { c.id = CMD_FIGHT; c.action="fight"; c.type = CMDTYPE_ICON_MAP; c.name = "Fight"; c.mouseicon=c.name; c.tooltip = "Fight: Order ready built units to take action while moving to a position"; possibleCommands.push_back(c); } if (owner->unitDef->canGuard) { c.id=CMD_GUARD; c.action="guard"; c.type=CMDTYPE_ICON_UNIT; c.name="Guard"; c.mouseicon=c.name; c.tooltip="Guard: Order ready built units to guard another unit and attack units attacking it"; possibleCommands.push_back(c); } CFactory* fac=(CFactory*)owner; map<int,string>::const_iterator bi; for (bi = fac->unitDef->buildOptions.begin(); bi != fac->unitDef->buildOptions.end(); ++bi) { const string name = bi->second; const UnitDef* ud = unitDefHandler->GetUnitByName(name); if (ud == NULL) { string errmsg = "MOD ERROR: loading "; errmsg += name.c_str(); errmsg += " for "; errmsg += owner->unitDef->name; throw content_error(errmsg); } CommandDescription c; c.id = -ud->id; //build options are always negative c.action = "buildunit_" + StringToLower(ud->name); c.type = CMDTYPE_ICON; c.name = name; c.mouseicon = c.name; c.disabled = (ud->maxThisUnit <= 0); char tmp[1024]; sprintf(tmp, "\nHealth %.0f\nMetal cost %.0f\nEnergy cost %.0f Build time %.0f", ud->health, ud->metalCost, ud->energyCost, ud->buildTime); if (c.disabled) { c.tooltip = "\xff\xff\x22\x22" "DISABLED: " "\xff\xff\xff\xff"; } else { c.tooltip = "Build: "; } c.tooltip += ud->humanName + " - " + ud->tooltip + tmp; possibleCommands.push_back(c); BuildOption bo; bo.name = name; bo.fullName = name; bo.numQued = 0; buildOptions[c.id] = bo; } }
CUnit* CUnitLoader::LoadUnit(const string& name, float3 pos, int team, bool build, int facing, const CUnit* builder) { // GML_RECMUTEX_LOCK(unit); // LoadUnit. Unitinit puts unit in the quadfield and activeUnits - GML_RECMUTEX_LOCK(sel); // LoadUnit. For anti deadlock purposes. GML_RECMUTEX_LOCK(quad); // LoadUnit. - make sure other threads cannot access an incomplete unit CUnit* unit; SCOPED_TIMER("Unit loader"); const UnitDef* ud = unitDefHandler->GetUnitByName(name); if (!ud) { throw content_error("Couldn't find unittype " + name); } string type = ud->type; if (!build) { pos.y = ground->GetHeight2(pos.x, pos.z); if (ud->floater && pos.y < 0.0f) { // adjust to waterline iif we are submerged pos.y = -ud->waterline; } } if (team < 0) { team = MAX_TEAMS - 1; // FIXME use gs->gaiaTeamID ? (once it is always enabled) } if (type == "GroundUnit") { unit = SAFE_NEW CUnit; } else if (type == "Transport") { unit = SAFE_NEW CTransportUnit; } else if (type == "Building") { unit = SAFE_NEW CBuilding; } else if (type == "Factory") { unit = SAFE_NEW CFactory; } else if (type == "Builder") { unit = SAFE_NEW CBuilder; } else if (type == "Bomber" || type == "Fighter") { unit = SAFE_NEW CUnit; } else if (type == "MetalExtractor") { unit = SAFE_NEW CExtractorBuilding; } else { logOutput << "Unknown unit type " << type.c_str() << "\n"; return NULL; } unit->UnitInit(ud, team, pos); unit->beingBuilt = build; unit->xsize = ((facing & 1) == 0) ? ud->xsize : ud->zsize; unit->zsize = ((facing & 1) == 1) ? ud->xsize : ud->zsize; unit->buildFacing = facing; unit->power = ud->power; unit->maxHealth = ud->health; unit->health = ud->health; //unit->metalUpkeep = ud->metalUpkeep*16.0f/GAME_SPEED; //unit->energyUpkeep = ud->energyUpkeep*16.0f/GAME_SPEED; unit->controlRadius = (int)(ud->controlRadius / SQUARE_SIZE); unit->losHeight = ud->losHeight; unit->metalCost = ud->metalCost; unit->energyCost = ud->energyCost; unit->buildTime = ud->buildTime; unit->aihint = ud->aihint; unit->tooltip = ud->humanName + " - " + ud->tooltip; unit->armoredMultiple = std::max(0.0001f, ud->armoredMultiple); //armored multiple of 0 will crash spring unit->wreckName = ud->wreckName; unit->realLosRadius = (int) (ud->losRadius); unit->realAirLosRadius = (int) (ud->airLosRadius); unit->upright = ud->upright; unit->radarRadius = ud->radarRadius / (SQUARE_SIZE * 8); unit->sonarRadius = ud->sonarRadius / (SQUARE_SIZE * 8); unit->jammerRadius = ud->jammerRadius / (SQUARE_SIZE * 8); unit->sonarJamRadius = ud->sonarJamRadius / (SQUARE_SIZE * 8); unit->seismicRadius = ud->seismicRadius / (SQUARE_SIZE * 8); unit->seismicSignature = ud->seismicSignature; unit->hasRadarCapacity = unit->radarRadius || unit->sonarRadius || unit->jammerRadius || unit->sonarJamRadius || unit->seismicRadius; unit->stealth = ud->stealth; unit->sonarStealth = ud->sonarStealth; unit->category = ud->category; unit->armorType = ud->armorType; unit->floatOnWater = ud->floater || (ud->movedata && ((ud->movedata->moveType == MoveData::Hover_Move) || (ud->movedata->moveType == MoveData::Ship_Move))); unit->maxSpeed = ud->speed / 30.0f; unit->decloakDistance = ud->decloakDistance; unit->flankingBonusMode = ud->flankingBonusMode; unit->flankingBonusDir = ud->flankingBonusDir; unit->flankingBonusMobility = ud->flankingBonusMobilityAdd * 1000; unit->flankingBonusMobilityAdd = ud->flankingBonusMobilityAdd; unit->flankingBonusAvgDamage = (ud->flankingBonusMax + ud->flankingBonusMin) * 0.5f; unit->flankingBonusDifDamage = (ud->flankingBonusMax - ud->flankingBonusMin) * 0.5f; if(ud->highTrajectoryType==1) unit->useHighTrajectory=true; if(ud->fireState >= 0) unit->fireState = ud->fireState; if(build){ unit->ChangeLos(1,1); unit->health=0.1f; } else { unit->ChangeLos((int)(ud->losRadius),(int)(ud->airLosRadius)); } if (type == "GroundUnit") { SAFE_NEW CMobileCAI(unit); } else if (type == "Transport") { SAFE_NEW CTransportCAI(unit); } else if (type == "Factory") { SAFE_NEW CFactoryCAI(unit); } else if (type == "Builder") { SAFE_NEW CBuilderCAI(unit); } else if (type == "Bomber") { if (ud->hoverAttack) { SAFE_NEW CMobileCAI(unit); } else { SAFE_NEW CAirCAI(unit); } } else if(type == "Fighter"){ if (ud->hoverAttack) { SAFE_NEW CMobileCAI(unit); } else { SAFE_NEW CAirCAI(unit); } } else { SAFE_NEW CCommandAI(unit); } if (ud->canmove && !ud->canfly && (type != "Factory")) { CGroundMoveType* mt = SAFE_NEW CGroundMoveType(unit); mt->maxSpeed = ud->speed / GAME_SPEED; mt->maxWantedSpeed = ud->speed / GAME_SPEED; mt->turnRate = ud->turnRate; mt->baseTurnRate = ud->turnRate; if (!mt->accRate) { logOutput << "acceleration of " << ud->name.c_str() << " is zero!!\n"; } mt->moveType = ud->moveType; mt->accRate = ud->maxAcc; mt->decRate = ud->maxDec; mt->floatOnWater = (ud->movedata->moveType == MoveData::Hover_Move || ud->movedata->moveType == MoveData::Ship_Move); if (!unit->beingBuilt) { // otherwise set this when finished building instead unit->mass = ud->mass; } unit->moveType = mt; // Ground-mobility unit->mobility = SAFE_NEW MoveData(ud->movedata, GAME_SPEED); } else if (ud->canfly) { // Air-mobility unit->mobility = SAFE_NEW MoveData(ud->movedata, GAME_SPEED); if (!unit->beingBuilt) { // otherwise set this when finished building instead unit->mass = ud->mass; } if ((type == "Builder") || ud->hoverAttack || ud->transportCapacity) { CTAAirMoveType* mt = SAFE_NEW CTAAirMoveType(unit); mt->turnRate = ud->turnRate; mt->maxSpeed = ud->speed / GAME_SPEED; mt->accRate = ud->maxAcc; mt->decRate = ud->maxDec; mt->wantedHeight = ud->wantedHeight + gs->randFloat() * 5; mt->orgWantedHeight = mt->wantedHeight; mt->dontLand = ud->DontLand(); mt->collide = ud->collide; mt->altitudeRate = ud->verticalSpeed; mt->bankingAllowed = ud->bankingAllowed; unit->moveType = mt; } else { CAirMoveType *mt = SAFE_NEW CAirMoveType(unit); if(type=="Fighter") mt->isFighter=true; mt->collide = ud->collide; mt->wingAngle = ud->wingAngle; mt->crashDrag = 1 - ud->crashDrag; mt->invDrag = 1 - ud->drag; mt->frontToSpeed = ud->frontToSpeed; mt->speedToFront = ud->speedToFront; mt->myGravity = ud->myGravity; mt->maxBank = ud->maxBank; mt->maxPitch = ud->maxPitch; mt->turnRadius = ud->turnRadius; mt->wantedHeight = (ud->wantedHeight * 1.5f) + ((gs->randFloat() - 0.3f) * 15 * (mt->isFighter ? 2 : 1)); mt->maxAcc = ud->maxAcc; mt->maxAileron = ud->maxAileron; mt->maxElevator = ud->maxElevator; mt->maxRudder = ud->maxRudder; unit->moveType = mt; } } else { unit->moveType = SAFE_NEW CMoveType(unit); unit->upright = true; } unit->energyTickMake = ud->energyMake; if (ud->tidalGenerator > 0) unit->energyTickMake += ud->tidalGenerator * mapInfo->map.tidalStrength; unit->model = LoadModel(ud); unit->SetRadius(unit->model->radius); // copy the UnitDef volume archetype data unit->collisionVolume = SAFE_NEW CollisionVolume(ud->collisionVolume); // if no "collisionVolumeScales" tag was defined in UnitDef, // the default scale for this volume will be a ZeroVector if (unit->collisionVolume->GetScale(COLVOL_AXIS_X) <= 1.0f && unit->collisionVolume->GetScale(COLVOL_AXIS_Y) <= 1.0f && unit->collisionVolume->GetScale(COLVOL_AXIS_Z) <= 1.0f) { // aircraft still get half-size spheres for coldet purposes // if no custom volume is defined (unit->model->radius and // unit->radius themselves are no longer altered) const float scaleFactor = (ud->canfly)? 0.5f: 1.0f; unit->collisionVolume->SetDefaultScale(unit->model->radius * scaleFactor); if (unit->collisionVolume->GetBoundingRadius() <= 30.0f) { // the interval-based method fails too easily for units // with small default volumes, force use of raytracing unit->collisionVolume->SetTestType(COLVOL_TEST_CONT); } } if (ud->floater) { // restrict our depth to our waterline unit->pos.y = std::max(-ud->waterline, ground->GetHeight2(unit->pos.x, unit->pos.z)); } else { unit->pos.y = ground->GetHeight2(unit->pos.x, unit->pos.z); } modelParser->CreateLocalModel(unit); unit->cob = SAFE_NEW CCobInstance(GCobEngine.GetCobFile("scripts/" + ud->cobFilename), unit); unit->weapons.reserve(ud->weapons.size()); for (unsigned int i = 0; i < ud->weapons.size(); i++) { unit->weapons.push_back(LoadWeapon(ud->weapons[i].def, unit, &ud->weapons[i])); } // Calculate the max() of the available weapon reloadtimes int relMax = 0; for (vector<CWeapon*>::iterator i = unit->weapons.begin(); i != unit->weapons.end(); ++i) { if ((*i)->reloadTime > relMax) relMax = (*i)->reloadTime; if (dynamic_cast<CBeamLaser*>(*i)) relMax = 150; } // convert ticks to milliseconds relMax *= 30; // TA does some special handling depending on weapon count if (unit->weapons.size() > 1) { relMax = std::max(relMax, 3000); } // Call initializing script functions unit->cob->Call(COBFN_Create); unit->cob->Call("SetMaxReloadTime", relMax); unit->heading = GetHeadingFromFacing(facing); unit->frontdir = GetVectorFromHeading(unit->heading); unit->updir = UpVector; unit->rightdir = unit->frontdir.cross(unit->updir); unit->yardMap = ud->yardmaps[facing]; unit->Init(builder); if (!build) { unit->FinishedBuilding(); } return unit; }