void CArchiveScanner::ReadCacheData(const std::string& filename) { if (!FileSystem::FileExists(filename)) { LOG_L(L_INFO, "Archive cache doesn't exist: %s", filename.c_str()); return; } LuaParser p(filename, SPRING_VFS_RAW, SPRING_VFS_BASE); if (!p.Execute()) { LOG_L(L_ERROR, "Failed to parse archive cache: %s", p.GetErrorLog().c_str()); return; } const LuaTable archiveCache = p.GetRoot(); // Do not load old version caches const int ver = archiveCache.GetInt("internalVer", (INTERNAL_VER + 1)); if (ver != INTERNAL_VER) { return; } const LuaTable archives = archiveCache.SubTable("archives"); for (int i = 1; archives.KeyExists(i); ++i) { const LuaTable curArchive = archives.SubTable(i); const LuaTable archived = curArchive.SubTable("archivedata"); std::string name = curArchive.GetString("name", ""); ArchiveInfo& ai = archiveInfos[StringToLower(name)]; ai.origName = name; ai.path = curArchive.GetString("path", ""); // do not use LuaTable.GetInt() for 32-bit integers, the Spring lua // library uses 32-bit floats to represent numbers, which can only // represent 2^24 consecutive integers ai.modified = strtoul(curArchive.GetString("modified", "0").c_str(), 0, 10); ai.checksum = strtoul(curArchive.GetString("checksum", "0").c_str(), 0, 10); ai.updated = false; ai.archiveData = CArchiveScanner::ArchiveData(archived, true); if (ai.archiveData.GetModType() == modtype::map) { AddDependency(ai.archiveData.GetDependencies(), "Map Helper v1"); } else if (ai.archiveData.GetModType() == modtype::primary) { AddDependency(ai.archiveData.GetDependencies(), "Spring content v1"); } } const LuaTable brokenArchives = archiveCache.SubTable("brokenArchives"); for (int i = 1; brokenArchives.KeyExists(i); ++i) { const LuaTable curArchive = brokenArchives.SubTable(i); std::string name = curArchive.GetString("name", ""); StringToLowerInPlace(name); BrokenArchive& ba = this->brokenArchives[name]; ba.path = curArchive.GetString("path", ""); ba.modified = strtoul(curArchive.GetString("modified", "0").c_str(), 0, 10); ba.updated = false; ba.problem = curArchive.GetString("problem", "unknown"); } isDirty = false; }
void SolidObjectDecalDef::Parse(const LuaTable& table) { groundDecalTypeName = table.GetString("groundDecalType", table.GetString("buildingGroundDecalType", "")); trackDecalTypeName = table.GetString("trackType", "StdTank"); useGroundDecal = table.GetBool("useGroundDecal", table.GetBool("useBuildingGroundDecal", false)); groundDecalType = -1; groundDecalSizeX = table.GetInt("groundDecalSizeX", table.GetInt("buildingGroundDecalSizeX", 4)); groundDecalSizeY = table.GetInt("groundDecalSizeY", table.GetInt("buildingGroundDecalSizeY", 4)); groundDecalDecaySpeed = table.GetFloat("groundDecalDecaySpeed", table.GetFloat("buildingGroundDecalDecaySpeed", 0.1f)); leaveTrackDecals = table.GetBool("leaveTracks", false); trackDecalType = -1; trackDecalWidth = table.GetFloat("trackWidth", 32.0f); trackDecalOffset = table.GetFloat("trackOffset", 0.0f); trackDecalStrength = table.GetFloat("trackStrength", 0.0f); trackDecalStretch = table.GetFloat("trackStretch", 1.0f); }
void CArchiveScanner::ReadCacheData(const string& filename) { LuaParser p(filename, SPRING_VFS_RAW, SPRING_VFS_BASE); if (!p.Execute()) { logOutput.Print("ERROR in " + filename + ": " + p.GetErrorLog()); } const LuaTable archiveCache = p.GetRoot(); const LuaTable archives = archiveCache.SubTable("archives"); // Do not load old version caches const int ver = archiveCache.GetInt("internalVer", (INTERNAL_VER + 1)); if (ver != INTERNAL_VER) { return; } for (int i = 1; archives.KeyExists(i); ++i) { const LuaTable curArchive = archives.SubTable(i); const LuaTable archived = curArchive.SubTable("archivedata"); ArchiveInfo ai; ai.origName = curArchive.GetString("name", ""); ai.path = curArchive.GetString("path", ""); // do not use LuaTable.GetInt() for 32-bit integers, the Spring lua // library uses 32-bit floats to represent numbers, which can only // represent 2^24 consecutive integers ai.modified = strtoul(curArchive.GetString("modified", "0").c_str(), 0, 10); ai.checksum = strtoul(curArchive.GetString("checksum", "0").c_str(), 0, 10); ai.updated = false; ai.archiveData = GetArchiveData(archived); if (ai.archiveData.modType == modtype::map) AddDependency(ai.archiveData.dependencies, "maphelper.sdz"); else if (ai.archiveData.modType == modtype::primary) AddDependency(ai.archiveData.dependencies, "Spring content v1"); string lcname = StringToLower(ai.origName); archiveInfo[lcname] = ai; } isDirty = false; }
static MoveDef::SpeedModClass ParseSpeedModClass(const std::string& moveDefName, const LuaTable& moveDefTable) { const MoveDef::SpeedModClass speedModClass = MoveDef::SpeedModClass(moveDefTable.GetInt("speedModClass", -1)); if (speedModClass != -1) return Clamp(speedModClass, MoveDef::Tank, MoveDef::Ship); // name-based fallbacks if (moveDefName.find( "boat") != string::npos) return MoveDef::Ship; if (moveDefName.find( "ship") != string::npos) return MoveDef::Ship; if (moveDefName.find("hover") != string::npos) return MoveDef::Hover; if (moveDefName.find( "tank") != string::npos) return MoveDef::Tank; return MoveDef::KBot; }
CArchiveScanner::ArchiveData CArchiveScanner::GetArchiveData(const LuaTable& archiveTable) { ArchiveData md; if (!archiveTable.IsValid()) { return md; } md.name = archiveTable.GetString("name", ""); md.shortName = archiveTable.GetString("shortName", ""); md.version = archiveTable.GetString("version", ""); md.mutator = archiveTable.GetString("mutator", ""); md.game = archiveTable.GetString("game", ""); md.shortGame = archiveTable.GetString("shortGame", ""); md.description = archiveTable.GetString("description", ""); md.modType = archiveTable.GetInt("modType", 0); md.mapfile = archiveTable.GetString("mapfile", ""); const LuaTable dependencies = archiveTable.SubTable("depend"); for (int dep = 1; dependencies.KeyExists(dep); ++dep) { const std::string depend = dependencies.GetString(dep, ""); md.dependencies.push_back(depend); } const LuaTable replaces = archiveTable.SubTable("replace"); for (int rep = 1; replaces.KeyExists(rep); ++rep) { md.replaces.push_back(replaces.GetString(rep, "")); } // HACK needed until lobbies, lobbyserver and unitsync are sorted out // so they can uniquely identify different versions of the same mod. // (at time of this writing they use name only) // NOTE when changing this, this function is used both by the code that // reads ArchiveCache.lua and the code that reads modinfo.lua from the mod. // so make sure it doesn't keep adding stuff to the name everytime // Spring/unitsync is loaded. if (md.name.find(md.version) == std::string::npos && !md.version.empty()) { md.name += " " + md.version; } return md; }
MoveDef::MoveDef(const LuaTable& moveDefTable, int moveDefID) { *this = MoveDef(); name = StringToLower(moveDefTable.GetString("name", "")); pathType = moveDefID - 1; crushStrength = moveDefTable.GetFloat("crushStrength", 10.0f); const LuaTable& depthModTable = moveDefTable.SubTable("depthModParams"); const LuaTable& speedModMultsTable = moveDefTable.SubTable("speedModMults"); const float minWaterDepth = moveDefTable.GetFloat("minWaterDepth", GetDefaultMinWaterDepth()); const float maxWaterDepth = moveDefTable.GetFloat("maxWaterDepth", GetDefaultMaxWaterDepth()); switch ((speedModClass = ParseSpeedModClass(name, moveDefTable))) { case MoveDef::Tank: { // fall-through } case MoveDef::KBot: { depthModParams[DEPTHMOD_MIN_HEIGHT] = std::max(0.00f, depthModTable.GetFloat("minHeight", 0.0f )); depthModParams[DEPTHMOD_MAX_HEIGHT] = ( depthModTable.GetFloat("maxHeight", std::numeric_limits<float>::max() )); depthModParams[DEPTHMOD_MAX_SCALE ] = std::max(0.01f, depthModTable.GetFloat("maxScale", std::numeric_limits<float>::max() )); depthModParams[DEPTHMOD_QUA_COEFF ] = std::max(0.00f, depthModTable.GetFloat("quadraticCoeff", 0.0f )); depthModParams[DEPTHMOD_LIN_COEFF ] = std::max(0.00f, depthModTable.GetFloat("linearCoeff", moveDefTable.GetFloat("depthMod", 0.1f))); depthModParams[DEPTHMOD_CON_COEFF ] = std::max(0.00f, depthModTable.GetFloat("constantCoeff", 1.0f )); // ensure [depthModMinHeight, depthModMaxHeight] is a valid range depthModParams[DEPTHMOD_MAX_HEIGHT] = std::max(depthModParams[DEPTHMOD_MIN_HEIGHT], depthModParams[DEPTHMOD_MAX_HEIGHT]); depth = maxWaterDepth; maxSlope = DegreesToMaxSlope(moveDefTable.GetFloat("maxSlope", 60.0f)); } break; case MoveDef::Hover: { depth = maxWaterDepth; maxSlope = DegreesToMaxSlope(moveDefTable.GetFloat("maxSlope", 15.0f)); } break; case MoveDef::Ship: { depth = minWaterDepth; subMarine = moveDefTable.GetBool("subMarine", false); } break; } speedModMults[SPEEDMOD_MOBILE_BUSY_MULT] = std::max(0.01f, speedModMultsTable.GetFloat("mobileBusyMult", 1.0f /*0.10f*/)); speedModMults[SPEEDMOD_MOBILE_IDLE_MULT] = std::max(0.01f, speedModMultsTable.GetFloat("mobileIdleMult", 1.0f /*0.35f*/)); speedModMults[SPEEDMOD_MOBILE_MOVE_MULT] = std::max(0.01f, speedModMultsTable.GetFloat("mobileMoveMult", 1.0f /*0.65f*/)); avoidMobilesOnPath = moveDefTable.GetBool("avoidMobilesOnPath", true); allowTerrainCollisions = moveDefTable.GetBool("allowTerrainCollisions", true); heatMapping = moveDefTable.GetBool("heatMapping", false); flowMapping = moveDefTable.GetBool("flowMapping", true); heatMod = moveDefTable.GetFloat("heatMod", (1.0f / (GAME_SPEED * 2)) * 0.25f); flowMod = moveDefTable.GetFloat("flowMod", 1.0f); // by default heat decays to zero after N=2 seconds // // the cost contribution to a square from heat must // be on the same order as its normal movement cost // PER FRAME, i.e. such that heatMod * heatProduced // ~= O(1 / (GAME_SPEED * N)) because unit behavior // in groups quickly becomes FUBAR if heatMod >>> 1 // heatProduced = moveDefTable.GetInt("heatProduced", GAME_SPEED * 2); // <maxSlope> ranges from 0.0 to 60 * 1.5 degrees, ie. from 0.0 to // 0.5 * PI radians, ie. from 1.0 - cos(0.0) to 1.0 - cos(0.5 * PI) // = [0, 1] --> DEFAULT <slopeMod> values range from (4 / 0.001) to // (4 / 1.001) = [4000.0, 3.996] // // speedMod values for a terrain-square slope in [0, 1] are given by // (1.0 / (1.0 + slope * slopeMod)) and therefore have a MAXIMUM at // <slope=0, slopeMod=...> and a MINIMUM at <slope=1, slopeMod=4000> // (of 1.0 / (1.0 + 0.0 * ...) = 1.0 and 1.0 / (1.0 + 1.0 * 4000.0) // = 0.00025 respectively) // slopeMod = moveDefTable.GetFloat("slopeMod", 4.0f / (maxSlope + 0.001f)); // ground units hug the ocean floor when in water, // ships stay at a "fixed" level (their waterline) followGround = (speedModClass == MoveDef::Tank || speedModClass == MoveDef::KBot); // TODO: // remove terrainClass, not used anywhere // and only AI's MIGHT have benefit from it // // tank or bot that cannot get its threads / feet // wet, or hovercraft (which doesn't touch ground // or water) if ((followGround && maxWaterDepth <= 0.0f) || speedModClass == MoveDef::Hover) terrainClass = MoveDef::Land; // ship (or sub) that cannot crawl onto shore, OR tank // or bot restricted to snorkling (strange but possible) if ((speedModClass == MoveDef::Ship && minWaterDepth > 0.0f) || (followGround && minWaterDepth > 0.0f)) terrainClass = MoveDef::Water; // tank or kbot that CAN go skinny-dipping (amph.), // or ship that CAN sprout legs when at the beach if ((followGround && maxWaterDepth > 0.0f) || (speedModClass == MoveDef::Ship && minWaterDepth < 0.0f)) terrainClass = MoveDef::Mixed; const int xsizeDef = std::max(1, moveDefTable.GetInt("footprintX", 1)); const int zsizeDef = std::max(1, moveDefTable.GetInt("footprintZ", xsizeDef)); // 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) xsize = xsizeDef * SPRING_FOOTPRINT_SCALE; zsize = zsizeDef * SPRING_FOOTPRINT_SCALE; xsize -= ((xsize & 1)? 0: 1); zsize -= ((zsize & 1)? 0: 1); // precalculated data for MoveMath xsizeh = xsize >> 1; zsizeh = zsize >> 1; assert((xsize & 1) == 1); assert((zsize & 1) == 1); }
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); }
void UnitDef::ParseWeaponsTable(const LuaTable& weaponsTable) { const WeaponDef* noWeaponDef = weaponDefHandler->GetWeapon("NOWEAPON"); for (int w = 0; w < MAX_WEAPONS_PER_UNIT; 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 (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 { weapons.push_back(UnitDefWeapon()); 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)).SafeNormalize(); const float angleDif = cos(wTable.GetFloat("maxAngleDif", 360.0f) * (PI / 360.0f)); const float fuelUse = wTable.GetFloat("fuelUsage", 0.0f); UnitDefWeapon weapon(name, wd, slaveTo, mainDir, angleDif, btc, otc, fuelUse); weapons.push_back(weapon); maxWeaponRange = std::max(maxWeaponRange, wd->range); if (wd->interceptor && wd->coverageRange > maxCoverage) maxCoverage = wd->coverageRange; if (wd->isShield) { if (!shieldWeaponDef || // use the biggest shield (shieldWeaponDef->shieldRadius < wd->shieldRadius)) { shieldWeaponDef = wd; } } if (wd->stockpile) { // interceptors have priority if (wd->interceptor || !stockpileWeaponDef || !stockpileWeaponDef->interceptor) { stockpileWeaponDef = wd; } } } }
WeaponDef::WeaponDef(const LuaTable& wdTable, const std::string& name_, int id_) : name(name_) , id(id_) , isShield(false) , collisionFlags(0) , explosionGenerator(NULL) , bounceExplosionGenerator(NULL) { WeaponDefs.Load(this, wdTable); if (wdTable.KeyExists("cylinderTargetting")) LOG_L(L_WARNING, "WeaponDef (%s) cylinderTargetting is deprecated and will be removed in the next release (use cylinderTargeting).", name.c_str()); if (wdTable.KeyExists("color1") || wdTable.KeyExists("color2")) LOG_L(L_WARNING, "WeaponDef (%s) color1 & color2 (= hue & sat) are removed. Use rgbColor instead!", name.c_str()); if (wdTable.KeyExists("isShield")) LOG_L(L_WARNING, "WeaponDef (%s) isShield is removed. Use weaponType=\"Shield\" instead!", name.c_str()); shieldRechargeDelay = int(wdTable.GetFloat("rechargeDelay", 0) * GAME_SPEED); flighttime = int(wdTable.GetFloat("flighttime", 0.0f) * 32); //FIXME may be smarter to merge the collideXYZ tags with avoidXYZ and removing the collisionFlags tag (and move the code into CWeapon)? collisionFlags = 0; if (!wdTable.GetBool("collideEnemy", true)) { collisionFlags |= Collision::NOENEMIES; } if (!wdTable.GetBool("collideFriendly", true)) { collisionFlags |= Collision::NOFRIENDLIES; } if (!wdTable.GetBool("collideFeature", true)) { collisionFlags |= Collision::NOFEATURES; } if (!wdTable.GetBool("collideNeutral", true)) { collisionFlags |= Collision::NONEUTRALS; } if (!wdTable.GetBool("collideGround", true)) { collisionFlags |= Collision::NOGROUND; } //FIXME defaults depend on other tags { if (paralyzer) cameraShake = wdTable.GetFloat("cameraShake", 0.0f); if (selfExplode) predictBoost = wdTable.GetFloat("predictBoost", 0.5f); if (type == "Melee") { targetBorder = Clamp(wdTable.GetFloat("targetBorder", 1.0f), -1.0f, 1.0f); cylinderTargeting = Clamp(wdTable.GetFloat("cylinderTargeting", wdTable.GetFloat("cylinderTargetting", 1.0f)), 0.0f, 128.0f); } if (type == "Flame") { //FIXME move to lua (for all other weapons this tag is named `duration` and has a different default) duration = wdTable.GetFloat("flameGfxTime", 1.2f); } if (type == "Cannon") { heightmod = wdTable.GetFloat("heightMod", 0.8f); } else if (type == "BeamLaser" || type == "LightningCannon") { heightmod = wdTable.GetFloat("heightMod", 1.0f); } } // setup the default damages { const LuaTable dmgTable = wdTable.SubTable("damage"); float defDamage = dmgTable.GetFloat("default", 1.0f); if (defDamage == 0.0f) { defDamage = 1.0f; //avoid division by zeros } damages.SetDefaultDamage(defDamage); if (!paralyzer) damages.paralyzeDamageTime = 0; std::map<string, float> dmgs; dmgTable.GetMap(dmgs); std::map<string, float>::const_iterator di; for (di = dmgs.begin(); di != dmgs.end(); ++di) { const int type = damageArrayHandler->GetTypeFromName(di->first); if (type != 0) { float dmg = di->second; if (dmg != 0.0f) { damages[type] = dmg; } else { damages[type] = 1.0f; } } } const float tempsize = 2.0f + std::min(defDamage * 0.0025f, damageAreaOfEffect * 0.1f); size = wdTable.GetFloat("size", tempsize); const float gd = std::max(30.0f, defDamage / 20.0f); const float defExpSpeed = (8.0f + (gd * 2.5f)) / (9.0f + (math::sqrt(gd) * 0.7f)) * 0.5f; explosionSpeed = wdTable.GetFloat("explosionSpeed", defExpSpeed); } { // 0.78.2.1 backwards compatibility: non-burst beamlasers play one // sample per shot, not for each individual beam making up the shot const bool singleSampleShot = (type == "BeamLaser" && !beamburst); const bool singleShotWeapon = (type == "Melee" || type == "Rifle"); soundTrigger = wdTable.GetBool("soundTrigger", singleSampleShot || singleShotWeapon); } // get some weapon specific defaults int defInterceptType = 0; if ((type == "Cannon") || (type == "EmgCannon")) { defInterceptType = 1; } else if ((type == "LaserCannon") || (type == "BeamLaser")) { defInterceptType = 2; } else if ((type == "StarburstLauncher") || (type == "MissileLauncher")) { defInterceptType = 4; } else if (type == "AircraftBomb") { defInterceptType = 8; } else if (type == "Flame") { defInterceptType = 16; } else if (type == "TorpedoLauncher") { defInterceptType = 32; } else if (type == "LightningCannon") { defInterceptType = 64; } else if (type == "Rifle") { defInterceptType = 128; } else if (type == "Melee") { defInterceptType = 256; } interceptedByShieldType = wdTable.GetInt("interceptedByShieldType", defInterceptType); if (type == "Cannon") { // CExplosiveProjectile ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.9f); intensity = wdTable.GetFloat("intensity", 0.2f); } else if (type == "Rifle") { ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.9f); } else if (type == "Melee") { // ... } else if (type == "Flame") { // CFlameProjectile ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.2f); collisionSize = wdTable.GetFloat("collisionSize", 0.5f); } else if (type == "MissileLauncher") { // CMissileProjectile ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.5f); } else if (type == "LaserCannon") { // CLaserProjectile ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.7f); collisionSize = wdTable.GetFloat("collisionSize", 0.5f); } else if (type == "BeamLaser") { ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.7f); } else if (type == "LightningCannon") { ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.5f); } else if (type == "EmgCannon") { // CEmgProjectile ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.5f); size = wdTable.GetFloat("size", 3.0f); } else if (type == "TorpedoLauncher") { assert(waterweapon); waterweapon = true; } else if (type == "DGun") { // CFireBallProjectile ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.5f); collisionSize = wdTable.GetFloat("collisionSize", 10.0f); } else if (type == "StarburstLauncher") { // CStarburstProjectile ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.7f); } else { ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.0f); } const std::string& colormap = wdTable.GetString("colormap", ""); visuals.colorMap = NULL; if (!colormap.empty()) { visuals.colorMap = CColorMap::LoadFromDefString(colormap); } ParseWeaponSounds(wdTable); // custom parameters table wdTable.SubTable("customParams").GetMap(customParams); // internal only isShield = (type == "Shield"); noAutoTarget = (manualfire || interceptor || isShield); onlyForward = !turret && (type != "StarburstLauncher"); }
unsigned int CCustomExplosionGenerator::Load(CExplosionGeneratorHandler* h, const string& tag) { unsigned int explosionID = -1U; if (tag.empty()) { return explosionID; } const std::map<std::string, unsigned int>::const_iterator it = explosionIDs.find(tag); if (it == explosionIDs.end()) { CEGData cegData; const LuaTable* root = h->GetExplosionTableRoot(); const LuaTable& expTable = (root != NULL)? root->SubTable(tag): LuaTable(); if (!expTable.IsValid()) { // not a fatal error: any calls to ::Explosion will just return early logOutput.Print("[CCEG::Load] WARNING: table for CEG \"" + tag + "\" invalid (parse errors?)"); return explosionID; } vector<string> spawns; expTable.GetKeys(spawns); for (vector<string>::iterator si = spawns.begin(); si != spawns.end(); ++si) { ProjectileSpawnInfo psi; const string& spawnName = *si; const LuaTable spawnTable = expTable.SubTable(spawnName); if (!spawnTable.IsValid() || spawnName == "groundflash") { continue; } const string className = spawnTable.GetString("class", spawnName); psi.projectileClass = h->projectileClasses.GetClass(className); psi.flags = GetFlagsFromTable(spawnTable); psi.count = spawnTable.GetInt("count", 1); if (psi.projectileClass->binder->flags & creg::CF_Synced) { psi.flags |= SPW_SYNCED; } string code; map<string, string> props; map<string, string>::const_iterator propIt; spawnTable.SubTable("properties").GetMap(props); for (propIt = props.begin(); propIt != props.end(); ++propIt) { creg::Class::Member* m = psi.projectileClass->FindMember(propIt->first.c_str()); if (m && (m->flags & creg::CM_Config)) { ParseExplosionCode(&psi, m->offset, m->type, propIt->second, code); } } code += (char)OP_END; psi.code.resize(code.size()); copy(code.begin(), code.end(), psi.code.begin()); cegData.projectileSpawn.push_back(psi); } const LuaTable gndTable = expTable.SubTable("groundflash"); const int ttl = gndTable.GetInt("ttl", 0); if (ttl > 0) { cegData.groundFlash.circleAlpha = gndTable.GetFloat("circleAlpha", 0.0f); cegData.groundFlash.flashSize = gndTable.GetFloat("flashSize", 0.0f); cegData.groundFlash.flashAlpha = gndTable.GetFloat("flashAlpha", 0.0f); cegData.groundFlash.circleGrowth = gndTable.GetFloat("circleGrowth", 0.0f); cegData.groundFlash.color = gndTable.GetFloat3("color", float3(1.0f, 1.0f, 0.8f)); cegData.groundFlash.flags = SPW_GROUND | GetFlagsFromTable(gndTable); cegData.groundFlash.ttl = ttl; } cegData.useDefaultExplosions = expTable.GetBool("useDefaultExplosions", false); explosionID = explosionData.size(); explosionData.push_back(cegData); explosionIDs[tag] = explosionID; } else { explosionID = it->second; } return explosionID; }
void CWeaponDefHandler::ParseTAWeapon(const LuaTable& wdTable, WeaponDef& wd) { bool lineofsight; bool ballistic; //bool twophase; bool beamweapon; bool manualBombSettings; //Allow the user to manually specify the burst and burstrate for his AircraftBomb //bool guided; //bool vlaunch; int rendertype; int color; int beamlaser; //bool tracking; //bool selfprop; //bool turret; //bool smokeTrail; //string modelName; wd.tdfId = wdTable.GetInt("id", 0); wd.filename = wdTable.GetString("filename", "unknown"); wd.description = wdTable.GetString("name", "Weapon"); wd.cegTag = wdTable.GetString("cegTag", ""); wd.avoidFriendly = wdTable.GetBool("avoidFriendly", true); wd.avoidFeature = wdTable.GetBool("avoidFeature", true); wd.collisionFlags = 0; const bool collideFriendly = wdTable.GetBool("collideFriendly", true); const bool collideFeature = wdTable.GetBool("collideFeature", true); if (!collideFriendly) { wd.collisionFlags += COLLISION_NOFRIENDLY; } if (!collideFeature) { wd.collisionFlags += COLLISION_NOFEATURE; } wd.minIntensity = wdTable.GetFloat("minIntensity", 0.0f); wd.dropped = wdTable.GetBool("dropped", false); manualBombSettings = wdTable.GetBool("manualBombSettings", false); lineofsight = wdTable.GetBool("lineOfSight", false); ballistic = wdTable.GetBool("ballistic", false); wd.twophase = wdTable.GetBool("twoPhase", false); beamweapon = wdTable.GetBool("beamWeapon", false); wd.guided = wdTable.GetBool("guidance", false); rendertype = wdTable.GetInt("renderType", 0); color = wdTable.GetInt("color", 0); beamlaser = wdTable.GetInt("beamlaser", 0); wd.vlaunch = wdTable.GetBool("vlaunch", false); wd.selfprop = wdTable.GetBool("selfprop", false); wd.turret = wdTable.GetBool("turret", false); wd.noSelfDamage = wdTable.GetBool("noSelfDamage", false); wd.visuals.modelName = wdTable.GetString("model", ""); wd.visuals.smokeTrail = wdTable.GetBool("smokeTrail", false); wd.visuals.alwaysVisible = wdTable.GetBool("alwaysVisible", false); wd.waterweapon = wdTable.GetBool("waterWeapon", false); wd.fireSubmersed = wdTable.GetBool("fireSubmersed", wd.waterweapon); wd.submissile = wdTable.GetBool("submissile", false); wd.tracks = wdTable.GetBool("tracks", false); wd.fixedLauncher = wdTable.GetBool("fixedLauncher", false); wd.noExplode = wdTable.GetBool("noExplode", false); wd.isShield = wdTable.GetBool("isShield", false); wd.maxvelocity = wdTable.GetFloat("weaponVelocity", 0.0f); wd.beamtime = wdTable.GetFloat("beamTime", 1.0f); wd.beamburst = wdTable.GetBool("beamburst", false); wd.waterBounce = wdTable.GetBool("waterbounce", false); wd.groundBounce = wdTable.GetBool("groundbounce", false); wd.bounceSlip = wdTable.GetFloat("bounceslip", 1); wd.bounceRebound = wdTable.GetFloat("bouncerebound", 1); wd.numBounce = wdTable.GetInt("numbounce", -1); wd.thickness = wdTable.GetFloat("thickness", 2.0f); wd.corethickness = wdTable.GetFloat("coreThickness", 0.25f); wd.laserflaresize = wdTable.GetFloat("laserFlareSize", 15.0f); wd.intensity = wdTable.GetFloat("intensity", 0.9f); wd.duration = wdTable.GetFloat("duration", 0.05f); wd.falloffRate = wdTable.GetFloat("fallOffRate", 0.5f); wd.lodDistance = wdTable.GetInt("lodDistance", 1000); wd.visuals.sizeDecay = wdTable.GetFloat("sizeDecay", 0.0f); wd.visuals.alphaDecay = wdTable.GetFloat("alphaDecay", 1.0f); wd.visuals.separation = wdTable.GetFloat("separation", 1.0f); wd.visuals.noGap = wdTable.GetBool("noGap", true); wd.visuals.stages = wdTable.GetInt("stages", 5); if (wd.name.find("disintegrator") != string::npos) { //fulhack wd.visuals.renderType = WEAPON_RENDERTYPE_FIREBALL; } else if (wd.visuals.modelName.compare("") != 0) { wd.visuals.renderType = WEAPON_RENDERTYPE_MODEL; } else if (beamweapon) { wd.visuals.renderType = WEAPON_RENDERTYPE_LASER; } else { wd.visuals.renderType = WEAPON_RENDERTYPE_PLASMA; } wd.gravityAffected = false; if (wd.dropped || ballistic) { wd.gravityAffected = true; } if (wd.dropped) { wd.type = "AircraftBomb"; } else if (wd.vlaunch) { wd.type = "StarburstLauncher"; } else if (beamlaser) { wd.type = "BeamLaser"; } else if (wd.isShield) { wd.type = "Shield"; } else if (wd.waterweapon) { wd.type = "TorpedoLauncher"; } else if (wd.name.find("disintegrator") != string::npos) { wd.type = "DGun"; } else if (lineofsight) { if (rendertype==7) { wd.type = "LightingCannon"; } else if (beamweapon) { wd.type = "LaserCannon"; } else if (wd.visuals.modelName.find("laser") != string::npos) { wd.type = "LaserCannon"; //swta fix } else if (/*selfprop && */wd.visuals.smokeTrail) { wd.type = "MissileLauncher"; } else if (rendertype == 4 && color == 2) { wd.type = "EmgCannon"; } else if (rendertype == 5) { wd.type = "Flame"; // } else if(rendertype == 1) { // wd.type = "MissileLauncher"; } else { wd.type = "Cannon"; } } else { wd.type = "Cannon"; } string ttype = wdTable.GetString("weaponType", ""); if (ttype != "") { wd.type = ttype; } // logOutput.Print("%s as %s",weaponname.c_str(),wd.type.c_str()); const bool melee = (wd.type == "Melee"); wd.targetBorder = wdTable.GetFloat("targetBorder", melee ? 1.0f : 0.0f); if (wd.targetBorder > 1.0f) { logOutput.Print("warning: targetBorder truncated to 1 (was %f)", wd.targetBorder); wd.targetBorder = 1.0f; } else if (wd.targetBorder < -1.0f) { logOutput.Print("warning: targetBorder truncated to -1 (was %f)", wd.targetBorder); wd.targetBorder = -1.0f; } wd.cylinderTargetting = wdTable.GetFloat("cylinderTargetting", melee ? 1.0f : 0.0f); wd.range = wdTable.GetFloat("range", 10.0f); const float accuracy = wdTable.GetFloat("accuracy", 0.0f); const float sprayangle = wdTable.GetFloat("sprayAngle", 0.0f); const float movingAccuracy = wdTable.GetFloat("movingAccuracy", accuracy); // should really be tan but TA seem to cap it somehow // should also be 7fff or ffff theoretically but neither seems good wd.accuracy = sin((accuracy) * PI / 0xafff); wd.sprayangle = sin((sprayangle) * PI / 0xafff); wd.movingAccuracy = sin((movingAccuracy) * PI / 0xafff); wd.targetMoveError = wdTable.GetFloat("targetMoveError", 0.0f); wd.leadLimit = wdTable.GetFloat("leadLimit", -1.0f); wd.leadBonus = wdTable.GetFloat("leadBonus", 0.0f); // setup the default damages const LuaTable dmgTable = wdTable.SubTable("damage"); float defDamage = dmgTable.GetFloat("default", 0.0f); if (defDamage == 0.0f) { defDamage = 1.0f; //avoid division by zeroes } for (int a = 0; a < damageArrayHandler->GetNumTypes(); ++a) { wd.damages[a] = defDamage; } map<string, float> damages; dmgTable.GetMap(damages); map<string, float>::const_iterator di; for (di = damages.begin(); di != damages.end(); ++di) { const int type = damageArrayHandler->GetTypeFromName(di->first); if (type != 0) { float dmg = di->second; if (dmg != 0.0f) { wd.damages[type] = dmg; } else { wd.damages[type] = 1.0f; } } } wd.damages.impulseFactor = wdTable.GetFloat("impulseFactor", 1.0f); wd.damages.impulseBoost = wdTable.GetFloat("impulseBoost", 0.0f); wd.damages.craterMult = wdTable.GetFloat("craterMult", wd.damages.impulseFactor); wd.damages.craterBoost = wdTable.GetFloat("craterBoost", 0.0f); wd.areaOfEffect = wdTable.GetFloat("areaOfEffect", 8.0f) * 0.5f; wd.edgeEffectiveness = wdTable.GetFloat("edgeEffectiveness", 0.0f); // prevent 0/0 division in CGameHelper::Explosion if (wd.edgeEffectiveness > 0.999f) { wd.edgeEffectiveness = 0.999f; } wd.projectilespeed = wdTable.GetFloat("weaponVelocity", 0.0f) / GAME_SPEED; wd.startvelocity = max(0.01f, wdTable.GetFloat("startVelocity", 0.0f) / GAME_SPEED); wd.weaponacceleration = wdTable.GetFloat("weaponAcceleration", 0.0f) / GAME_SPEED / GAME_SPEED; wd.reload = wdTable.GetFloat("reloadtime", 1.0f); wd.salvodelay = wdTable.GetFloat("burstrate", 0.1f); wd.salvosize = wdTable.GetInt("burst", 1); wd.projectilespershot = wdTable.GetInt("projectiles", 1); wd.maxAngle = wdTable.GetFloat("tolerance", 3000.0f) * 180.0f / 0x7fff; wd.restTime = 0.0f; wd.metalcost = wdTable.GetFloat("metalpershot", 0.0f); wd.energycost = wdTable.GetFloat("energypershot", 0.0f); wd.selfExplode = wdTable.GetBool("burnblow", false); wd.predictBoost = wdTable.GetFloat("predictBoost", wd.selfExplode ? 0.5f : 0.0f); wd.sweepFire = wdTable.GetBool("sweepfire", false); wd.canAttackGround = wdTable.GetBool("canattackground", true); wd.myGravity = wdTable.GetFloat("myGravity", 0.0f); wd.fireStarter = wdTable.GetFloat("fireStarter", 0.0f) * 0.01f; wd.paralyzer = wdTable.GetBool("paralyzer", false); if (wd.paralyzer) { wd.damages.paralyzeDamageTime = max(0, wdTable.GetInt("paralyzeTime", 10)); } else { wd.damages.paralyzeDamageTime = 0; } const float defShake = wd.paralyzer ? 0.0f : wd.damages.GetDefaultDamage(); wd.cameraShake = wdTable.GetFloat("cameraShake", defShake); wd.cameraShake = max(0.0f, wd.cameraShake); wd.soundTrigger = wdTable.GetBool("soundTrigger", false); //sunparser->GetDef(wd.highTrajectory, "0", weaponname + "minbarrelangle"); wd.stockpile = wdTable.GetBool("stockpile", false); wd.interceptor = wdTable.GetInt("interceptor", 0); wd.targetable = wdTable.GetInt("targetable", 0); wd.manualfire = wdTable.GetBool("commandfire", false); wd.coverageRange = wdTable.GetFloat("coverage", 0.0f); // FIXME -- remove the old style ? LuaTable shTable = wdTable.SubTable("shield"); const float3 shieldBadColor (1.0f, 0.5f, 0.5f); const float3 shieldGoodColor(0.5f, 0.5f, 1.0f); if (shTable.IsValid()) { wd.shieldRepulser = shTable.GetBool("repulser", false); wd.smartShield = shTable.GetBool("smart", false); wd.exteriorShield = shTable.GetBool("exterior", false); wd.visibleShield = shTable.GetBool("visible", false); wd.visibleShieldRepulse = shTable.GetBool("visibleRepulse", false); wd.visibleShieldHitFrames = shTable.GetInt("visibleHitFrames", 0); wd.shieldEnergyUse = shTable.GetFloat("energyUse", 0.0f); wd.shieldForce = shTable.GetFloat("force", 0.0f); wd.shieldRadius = shTable.GetFloat("radius", 0.0f); wd.shieldMaxSpeed = shTable.GetFloat("maxSpeed", 0.0f); wd.shieldPower = shTable.GetFloat("power", 0.0f); wd.shieldPowerRegen = shTable.GetFloat("powerRegen", 0.0f); wd.shieldPowerRegenEnergy = shTable.GetFloat("powerRegenEnergy", 0.0f); wd.shieldStartingPower = shTable.GetFloat("startingPower", 0.0f); wd.shieldInterceptType = shTable.GetInt("interceptType", 0); wd.shieldBadColor = shTable.GetFloat3("badColor", shieldBadColor); wd.shieldGoodColor = shTable.GetFloat3("goodColor", shieldGoodColor); wd.shieldAlpha = shTable.GetFloat("alpha", 0.2f); } else { wd.shieldRepulser = wdTable.GetBool("shieldRepulser", false); wd.smartShield = wdTable.GetBool("smartShield", false); wd.exteriorShield = wdTable.GetBool("exteriorShield", false); wd.visibleShield = wdTable.GetBool("visibleShield", false); wd.visibleShieldRepulse = wdTable.GetBool("visibleShieldRepulse", false); wd.visibleShieldHitFrames = wdTable.GetInt("visibleShieldHitFrames", 0); wd.shieldEnergyUse = wdTable.GetFloat("shieldEnergyUse", 0.0f); wd.shieldForce = wdTable.GetFloat("shieldForce", 0.0f); wd.shieldRadius = wdTable.GetFloat("shieldRadius", 0.0f); wd.shieldMaxSpeed = wdTable.GetFloat("shieldMaxSpeed", 0.0f); wd.shieldPower = wdTable.GetFloat("shieldPower", 0.0f); wd.shieldPowerRegen = wdTable.GetFloat("shieldPowerRegen", 0.0f); wd.shieldPowerRegenEnergy = wdTable.GetFloat("shieldPowerRegenEnergy", 0.0f); wd.shieldStartingPower = wdTable.GetFloat("shieldStartingPower", 0.0f); wd.shieldInterceptType = wdTable.GetInt("shieldInterceptType", 0); wd.shieldBadColor = wdTable.GetFloat3("shieldBadColor", shieldBadColor); wd.shieldGoodColor = wdTable.GetFloat3("shieldGoodColor", shieldGoodColor); wd.shieldAlpha = wdTable.GetFloat("shieldAlpha", 0.2f); } int defInterceptType = 0; if (wd.type == "Cannon") { defInterceptType = 1; } else if ((wd.type == "LaserCannon") || (wd.type == "BeamLaser")) { defInterceptType = 2; } else if ((wd.type == "StarburstLauncher") || (wd.type == "MissileLauncher")) { defInterceptType = 4; } wd.interceptedByShieldType = wdTable.GetInt("interceptedByShieldType", defInterceptType); wd.wobble = wdTable.GetFloat("wobble", 0.0f) * PI / 0x7fff / 30.0f; wd.dance = wdTable.GetFloat("dance", 0.0f) / GAME_SPEED; wd.trajectoryHeight = wdTable.GetFloat("trajectoryHeight", 0.0f); wd.noAutoTarget = (wd.manualfire || wd.interceptor || wd.isShield); wd.onlyTargetCategory = 0xffffffff; if (wdTable.GetBool("toAirWeapon", false)) { // fix if we sometime call aircrafts otherwise wd.onlyTargetCategory = CCategoryHandler::Instance()->GetCategories("VTOL"); //logOutput.Print("air only weapon %s %i",weaponname.c_str(),wd.onlyTargetCategory); } wd.largeBeamLaser = wdTable.GetBool("largeBeamLaser", false); wd.visuals.tilelength = wdTable.GetFloat("tileLength", 200.0f); wd.visuals.scrollspeed = wdTable.GetFloat("scrollSpeed", 5.0f); wd.visuals.pulseSpeed = wdTable.GetFloat("pulseSpeed", 1.0f); wd.visuals.beamdecay = wdTable.GetFloat("beamDecay", 1.0f); wd.visuals.beamttl = wdTable.GetInt("beamTTL", 0); if (wd.type == "Cannon") { wd.heightmod = wdTable.GetFloat("heightMod", 0.8f); } else if (wd.type == "BeamLaser") { wd.heightmod = wdTable.GetFloat("heightMod", 1.0f); } else { wd.heightmod = wdTable.GetFloat("heightMod", 0.2f); } wd.supplycost = 0.0f; wd.onlyForward = !wd.turret && (wd.type != "StarburstLauncher"); const int color2 = wdTable.GetInt("color2", 0); const float3 rgbcol = hs2rgb(color / float(255), color2 / float(255)); wd.visuals.color = wdTable.GetFloat3("rgbColor", rgbcol); wd.visuals.color2 = wdTable.GetFloat3("rgbColor2", float3(1.0f, 1.0f, 1.0f)); wd.uptime = wdTable.GetFloat("weaponTimer", 0.0f); wd.flighttime = wdTable.GetInt("flightTime", 0) * 32; wd.turnrate = wdTable.GetFloat("turnRate", 0.0f) * PI / 0x7fff / 30.0f; if ((wd.type == "AircraftBomb") && !manualBombSettings) { if (wd.reload < 0.5f) { wd.salvodelay = min(0.2f, wd.reload); wd.salvosize = (int)(1 / wd.salvodelay) + 1; wd.reload = 5; } else { wd.salvodelay = min(0.4f, wd.reload); wd.salvosize = 2; } } //if(!wd.turret && (wd.type != "TorpedoLauncher")) { // wd.maxAngle*=0.4f; //} //2+min(damages[0]*0.0025f,weaponDef->areaOfEffect*0.1f) const float tempsize = 2.0f + min(wd.damages[0] * 0.0025f, wd.areaOfEffect * 0.1f); wd.size = wdTable.GetFloat("size", tempsize); wd.sizeGrowth = wdTable.GetFloat("sizeGrowth", 0.2f); wd.collisionSize = wdTable.GetFloat("collisionSize", 0.05f); wd.visuals.colorMap = 0; const string colormap = wdTable.GetString("colormap", ""); if (colormap != "") { wd.visuals.colorMap = CColorMap::LoadFromDefString(colormap); } wd.heightBoostFactor = wdTable.GetFloat("heightBoostFactor", -1.0f); wd.proximityPriority = wdTable.GetFloat("proximityPriority", 1.0f); // get some weapon specific defaults if (wd.type == "Cannon") { // CExplosiveProjectile wd.visuals.texture1 = &ph->plasmatex; wd.visuals.color = wdTable.GetFloat3("rgbColor", float3(1.0f,0.5f,0.0f)); wd.intensity = wdTable.GetFloat("intensity", 0.2f); } else if (wd.type == "Rifle") { // ... } else if (wd.type == "Melee") { // ... } else if (wd.type == "AircraftBomb") { // CExplosiveProjectile or CTorpedoProjectile wd.visuals.texture1 = &ph->plasmatex; } else if (wd.type == "Shield") { wd.visuals.texture1 = &ph->perlintex; } else if (wd.type == "Flame") { // CFlameProjectile wd.visuals.texture1 = &ph->flametex; wd.size = wdTable.GetFloat("size", tempsize); wd.sizeGrowth = wdTable.GetFloat("sizeGrowth", 0.5f); wd.collisionSize = wdTable.GetFloat("collisionSize", 0.5f); wd.duration = wdTable.GetFloat("flameGfxTime", 1.2f); if (wd.visuals.colorMap == 0) { wd.visuals.colorMap = CColorMap::Load12f(1.000f, 1.000f, 1.000f, 0.100f, 0.025f, 0.025f, 0.025f, 0.100f, 0.000f, 0.000f, 0.000f, 0.000f); } } else if (wd.type == "MissileLauncher") { // CMissileProjectile wd.visuals.texture1 = &ph->missileflaretex; wd.visuals.texture2 = &ph->missiletrailtex; } else if (wd.type == "TorpedoLauncher") { // CExplosiveProjectile or CTorpedoProjectile wd.visuals.texture1 = &ph->plasmatex; } else if (wd.type == "LaserCannon") { // CLaserProjectile wd.visuals.texture1 = &ph->laserfallofftex; wd.visuals.texture2 = &ph->laserendtex; wd.visuals.hardStop = wdTable.GetBool("hardstop", false); wd.collisionSize = wdTable.GetFloat("collisionSize", 0.5f); } else if (wd.type == "BeamLaser") { if (wd.largeBeamLaser) { wd.visuals.texture1 = ph->textureAtlas->GetTexturePtr("largebeam"); wd.visuals.texture2 = &ph->laserendtex; wd.visuals.texture3 = ph->textureAtlas->GetTexturePtr("muzzleside"); wd.visuals.texture4 = &ph->beamlaserflaretex; } else { wd.visuals.texture1 = &ph->laserfallofftex; wd.visuals.texture2 = &ph->laserendtex; wd.visuals.texture3 = &ph->beamlaserflaretex; } } else if (wd.type == "LightingCannon") { wd.visuals.texture1 = &ph->laserfallofftex; wd.thickness = wdTable.GetFloat("thickness", 0.8f); } else if (wd.type == "EmgCannon") { // CEmgProjectile wd.visuals.texture1 = &ph->plasmatex; wd.visuals.color = wdTable.GetFloat3("rgbColor", float3(0.9f,0.9f,0.2f)); wd.size = wdTable.GetFloat("size", 3.0f); } else if (wd.type == "DGun") { // CFireBallProjectile wd.collisionSize = wdTable.GetFloat("collisionSize", 10.0f); } else if (wd.type == "StarburstLauncher") { // CStarburstProjectile wd.visuals.texture1 = &ph->sbflaretex; wd.visuals.texture2 = &ph->sbtrailtex; wd.visuals.texture3 = &ph->explotex; } else { wd.visuals.texture1 = &ph->plasmatex; wd.visuals.texture2 = &ph->plasmatex; } // FIXME -- remove the 'textureN' format? LuaTable texTable = wdTable.SubTable("textures"); string texName; texName = wdTable.GetString("texture1", ""); texName = texTable.GetString(1, texName); if (texName != "") { wd.visuals.texture1 = ph->textureAtlas->GetTexturePtr(texName); } texName = wdTable.GetString("texture2", ""); texName = texTable.GetString(2, texName); if (texName != "") { wd.visuals.texture2 = ph->textureAtlas->GetTexturePtr(texName); } texName = wdTable.GetString("texture3", ""); texName = texTable.GetString(3, texName); if (texName != "") { wd.visuals.texture3 = ph->textureAtlas->GetTexturePtr(texName); } texName = wdTable.GetString("texture4", ""); texName = texTable.GetString(4, texName); if (texName != "") { wd.visuals.texture4 = ph->textureAtlas->GetTexturePtr(texName); } const string expGenTag = wdTable.GetString("explosionGenerator", ""); if (expGenTag.empty()) { wd.explosionGenerator = NULL; } else { wd.explosionGenerator = explGenHandler->LoadGenerator(expGenTag); } const string bounceExpGenTag = wdTable.GetString("bounceExplosionGenerator", ""); if (bounceExpGenTag.empty()) { wd.bounceExplosionGenerator = NULL; } else { wd.bounceExplosionGenerator = explGenHandler->LoadGenerator(bounceExpGenTag); } const float gd = max(30.0f, wd.damages[0] / 20.0f); const float defExpSpeed = (8.0f + (gd * 2.5f)) / (9.0f + (sqrtf(gd) * 0.7f)) * 0.5f; wd.explosionSpeed = wdTable.GetFloat("explosionSpeed", defExpSpeed); // Dynamic Damage wd.dynDamageInverted = wdTable.GetBool("dynDamageInverted", false); wd.dynDamageExp = wdTable.GetFloat("dynDamageExp", 0.0f); wd.dynDamageMin = wdTable.GetFloat("dynDamageMin", 0.0f); wd.dynDamageRange = wdTable.GetFloat("dynDamageRange", 0.0f); LoadSound(wdTable, wd.firesound, "firesound"); LoadSound(wdTable, wd.soundhit, "soundhit"); if ((wd.firesound.getVolume(0) == -1.0f) || (wd.soundhit.getVolume(0) == -1.0f)) { // no volume (-1.0f) read from weapon definition, set it dynamically here if (wd.damages[0] <= 50.0f) { wd.soundhit.setVolume(0, 5.0f); wd.firesound.setVolume(0, 5.0f); } else { float soundVolume = sqrt(wd.damages[0] * 0.5f); if (wd.type == "LaserCannon") { soundVolume *= 0.5f; } float hitSoundVolume = soundVolume; if ((soundVolume > 100.0f) && ((wd.type == "MissileLauncher") || (wd.type == "StarburstLauncher"))) { soundVolume = 10.0f * sqrt(soundVolume); } if (wd.firesound.getVolume(0) == -1.0f) { wd.firesound.setVolume(0, soundVolume); } soundVolume = hitSoundVolume; if (wd.areaOfEffect > 8.0f) { soundVolume *= 2.0f; } if (wd.type == "DGun") { soundVolume *= 0.15f; } if (wd.soundhit.getVolume(0) == -1.0f) { wd.soundhit.setVolume(0, soundVolume); } } } }
void CAssParser::LoadPieceTransformations( SAssPiece* piece, const S3DModel* model, const aiNode* pieceNode, const LuaTable& pieceTable ) { aiVector3D aiScaleVec, aiTransVec; aiQuaternion aiRotateQuat; // process transforms pieceNode->mTransformation.Decompose(aiScaleVec, aiRotateQuat, aiTransVec); // metadata-scaling piece->scales = pieceTable.GetFloat3("scale", aiVectorToFloat3(aiScaleVec)); piece->scales.x = pieceTable.GetFloat("scalex", piece->scales.x); piece->scales.y = pieceTable.GetFloat("scaley", piece->scales.y); piece->scales.z = pieceTable.GetFloat("scalez", piece->scales.z); if (piece->scales.x != piece->scales.y || piece->scales.y != piece->scales.z) { // LOG_SL(LOG_SECTION_MODEL, L_WARNING, "Spring doesn't support non-uniform scaling"); piece->scales.y = piece->scales.x; piece->scales.z = piece->scales.x; } // metadata-translation piece->offset = pieceTable.GetFloat3("offset", aiVectorToFloat3(aiTransVec)); piece->offset.x = pieceTable.GetFloat("offsetx", piece->offset.x); piece->offset.y = pieceTable.GetFloat("offsety", piece->offset.y); piece->offset.z = pieceTable.GetFloat("offsetz", piece->offset.z); // metadata-rotation // NOTE: // these rotations are "pre-scripting" but "post-modelling" // together with the (baked) aiRotateQuad they determine the // model's pose *before* any animations execute // // float3 rotAngles = pieceTable.GetFloat3("rotate", aiQuaternionToRadianAngles(aiRotateQuat) * RADTODEG); float3 pieceRotAngles = pieceTable.GetFloat3("rotate", ZeroVector); pieceRotAngles.x = pieceTable.GetFloat("rotatex", pieceRotAngles.x); pieceRotAngles.y = pieceTable.GetFloat("rotatey", pieceRotAngles.y); pieceRotAngles.z = pieceTable.GetFloat("rotatez", pieceRotAngles.z); pieceRotAngles *= DEGTORAD; LOG_SL(LOG_SECTION_PIECE, L_INFO, "(%d:%s) Assimp offset (%f,%f,%f), rotate (%f,%f,%f,%f), scale (%f,%f,%f)", model->numPieces, piece->name.c_str(), aiTransVec.x, aiTransVec.y, aiTransVec.z, aiRotateQuat.w, aiRotateQuat.x, aiRotateQuat.y, aiRotateQuat.z, aiScaleVec.x, aiScaleVec.y, aiScaleVec.z ); LOG_SL(LOG_SECTION_PIECE, L_INFO, "(%d:%s) Relative offset (%f,%f,%f), rotate (%f,%f,%f), scale (%f,%f,%f)", model->numPieces, piece->name.c_str(), piece->offset.x, piece->offset.y, piece->offset.z, pieceRotAngles.x, pieceRotAngles.y, pieceRotAngles.z, piece->scales.x, piece->scales.y, piece->scales.z ); // NOTE: // at least collada (.dae) files generated by Blender represent // a coordinate-system that differs from the "standard" formats // (3DO, S3O, ...) for which existing tools at least have prior // knowledge of Spring's expectations --> let the user override // the ROOT rotational transform and the rotation-axis mapping // used by animation scripts (but re-modelling/re-exporting is // always preferred!) even though AssImp should convert models // to its own system which matches that of Spring // // .dae : x=Rgt, y=-Fwd, z= Up, as=(-1, -1, 1), am=AXIS_XZY (if Z_UP) // .dae : x=Rgt, y=-Fwd, z= Up, as=(-1, -1, 1), am=AXIS_XZY (if Y_UP) [!?] // .blend: ???? piece->bakedRotMatrix = aiMatrixToMatrix(aiMatrix4x4t<float>(aiRotateQuat.GetMatrix())); if (piece == model->GetRootPiece()) { const float3 xaxis = pieceTable.GetFloat3("xaxis", piece->bakedRotMatrix.GetX()); const float3 yaxis = pieceTable.GetFloat3("yaxis", piece->bakedRotMatrix.GetY()); const float3 zaxis = pieceTable.GetFloat3("zaxis", piece->bakedRotMatrix.GetZ()); if (math::fabs(xaxis.SqLength() - yaxis.SqLength()) < 0.01f && math::fabs(yaxis.SqLength() - zaxis.SqLength()) < 0.01f) { piece->bakedRotMatrix = CMatrix44f(ZeroVector, xaxis, yaxis, zaxis); } } piece->rotAxisSigns = pieceTable.GetFloat3("rotAxisSigns", float3(-OnesVector)); piece->axisMapType = AxisMappingType(pieceTable.GetInt("rotAxisMap", AXIS_MAPPING_XYZ)); // construct 'baked' part of the piece-space matrix // AssImp order is translate * rotate * scale * v; // we leave the translation and scale parts out and // put those in <offset> and <scales> --> transform // is just R instead of T * R * S // // note: for all non-AssImp models this is identity! // piece->ComposeRotation(piece->bakedRotMatrix, pieceRotAngles); piece->SetHasIdentityRotation(piece->bakedRotMatrix.IsIdentity() == 0); assert(piece->bakedRotMatrix.IsOrthoNormal() == 0); }
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(); moveInfoChecksum = 0; for (size_t num = 1; /* no test */; num++) { const LuaTable moveTable = rootTable.SubTable(num); if (!moveTable.IsValid()) { break; } MoveData* md = new MoveData(0x0, 0); md->name = moveTable.GetString("name", ""); md->pathType = (num - 1); md->maxSlope = 1.0f; md->depth = 0.0f; md->depthMod = 0.0f; 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; } } // 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; } md->slopeMod = moveTable.GetFloat("slopeMod", 4.0f / (md->maxSlope + 0.001f)); // TA has only half our resolution, multiply size by 2 md->size = max(2, min(8, moveTable.GetInt("footprintX", 1) * 2)); moveInfoChecksum += (md->size << 5) + (md->followGround << 4) + (md->subMarine << 3) + (b2 << 2) + (b1 << 1) + (b0 << 0); moveInfoChecksum = moveInfoChecksum * 3 + *(unsigned int*) &md->maxSlope; moveInfoChecksum = moveInfoChecksum * 3 + *(unsigned int*) &md->slopeMod; moveInfoChecksum = moveInfoChecksum * 3 + *(unsigned int*) &md->depth; moveInfoChecksum = moveInfoChecksum * 3 + *(unsigned int*) &md->depthMod; moveInfoChecksum = moveInfoChecksum * 5 + *(unsigned int*) &md->crushStrength; moveData.push_back(md); name2moveData[md->name] = md->pathType; } for (int a = 0; a < 256; ++a) { terrainType2MoveFamilySpeed[a][0] = mapInfo->terrainTypes[a].tankSpeed; terrainType2MoveFamilySpeed[a][1] = mapInfo->terrainTypes[a].kbotSpeed; terrainType2MoveFamilySpeed[a][2] = mapInfo->terrainTypes[a].hoverSpeed; terrainType2MoveFamilySpeed[a][3] = mapInfo->terrainTypes[a].shipSpeed; } const float waterDamage = mapInfo->water.damage; if (waterDamage >= 1000.0f) { CGroundMoveMath::waterCost = 0.0f; } else { CGroundMoveMath::waterCost = 1.0f / (1.0f + waterDamage * 0.1f); } CHoverMoveMath::noWaterMove = (waterDamage >= 10000.0f); }
/* * CArchiveScanner::ArchiveData */ CArchiveScanner::ArchiveData::ArchiveData(const LuaTable& archiveTable, bool fromCache) { if (!archiveTable.IsValid()) return; std::vector<std::string> keys; if (!archiveTable.GetKeys(keys)) return; for (std::string& key: keys) { const std::string& keyLower = StringToLower(key); if (ArchiveData::IsReservedKey(keyLower)) continue; if (keyLower == "modtype") { SetInfoItemValueInteger(key, archiveTable.GetInt(key, 0)); continue; } switch (archiveTable.GetType(key)) { case LuaTable::STRING: { SetInfoItemValueString(key, archiveTable.GetString(key, "")); } break; case LuaTable::NUMBER: { SetInfoItemValueFloat(key, archiveTable.GetFloat(key, 0.0f)); } break; case LuaTable::BOOLEAN: { SetInfoItemValueBool(key, archiveTable.GetBool(key, false)); } break; default: { // just ignore unsupported types (most likely to be lua-tables) //throw content_error("Lua-type " + IntToString(luaType) + " not supported in archive-info, but it is used on key \"" + *key + "\""); } break; } } const LuaTable& _dependencies = archiveTable.SubTable("depend"); const LuaTable& _replaces = archiveTable.SubTable("replace"); for (int dep = 1; _dependencies.KeyExists(dep); ++dep) { dependencies.push_back(_dependencies.GetString(dep, "")); } for (int rep = 1; _replaces.KeyExists(rep); ++rep) { replaces.push_back(_replaces.GetString(rep, "")); } // FIXME // XXX HACK needed until lobbies, lobbyserver and unitsync are sorted out // so they can uniquely identify different versions of the same mod. // (at time of this writing they use name only) // NOTE when changing this, this function is used both by the code that // reads ArchiveCache.lua and the code that reads modinfo.lua from the mod. // so make sure it doesn't keep adding stuff to the name everytime // Spring/unitsync is loaded. const std::string& name = GetNameVersioned(); const std::string& version = GetVersion(); if (!version.empty()) { if (name.find(version) == std::string::npos) { SetInfoItemValueString("name", name + " " + version); } else if (!fromCache) { LOG_L(L_WARNING, "[%s] version \"%s\" included in name \"%s\"", __func__, version.c_str(), name.c_str()); } } if (GetName().empty()) SetInfoItemValueString("name_pure", name); }
void CWeaponDefHandler::ParseWeapon(const LuaTable& wdTable, WeaponDef& wd) { bool manualBombSettings; //Allow the user to manually specify the burst and burstrate for his AircraftBomb int color; int color2; wd.tdfId = wdTable.GetInt("id", 0); wd.description = wdTable.GetString("name", "Weapon"); wd.cegTag = wdTable.GetString("cegTag", ""); wd.avoidFriendly = wdTable.GetBool("avoidFriendly", true); wd.avoidFeature = wdTable.GetBool("avoidFeature", true); wd.avoidNeutral = wdTable.GetBool("avoidNeutral", false); //FIXME may be smarter to merge the collideXYZ tags with avoidXYZ and removing the collisionFlags tag (and move the code into CWeapon)? wd.collisionFlags = 0; if (!wdTable.GetBool("collideEnemy", true)) { wd.collisionFlags |= Collision::NOENEMIES; } if (!wdTable.GetBool("collideFriendly", true)) { wd.collisionFlags |= Collision::NOFRIENDLIES; } if (!wdTable.GetBool("collideFeature", true)) { wd.collisionFlags |= Collision::NOFEATURES; } if (!wdTable.GetBool("collideNeutral", true)) { wd.collisionFlags |= Collision::NONEUTRALS; } if (!wdTable.GetBool("collideGround", true)) { wd.collisionFlags |= Collision::NOGROUND; } wd.minIntensity = wdTable.GetFloat("minIntensity", 0.0f); manualBombSettings = wdTable.GetBool("manualBombSettings", false); wd.turret = wdTable.GetBool("turret", false); wd.highTrajectory = wdTable.GetInt("highTrajectory", 2); wd.noSelfDamage = wdTable.GetBool("noSelfDamage", false); wd.impactOnly = wdTable.GetBool("impactOnly", false); wd.waterweapon = wdTable.GetBool("waterWeapon", false); wd.fireSubmersed = wdTable.GetBool("fireSubmersed", wd.waterweapon); wd.submissile = wdTable.GetBool("submissile", false); wd.tracks = wdTable.GetBool("tracks", false); wd.fixedLauncher = wdTable.GetBool("fixedLauncher", false); wd.noExplode = wdTable.GetBool("noExplode", false); wd.isShield = wdTable.GetBool("isShield", false); wd.beamtime = wdTable.GetFloat("beamTime", 1.0f); wd.beamburst = wdTable.GetBool("beamburst", false); wd.waterBounce = wdTable.GetBool("waterBounce", false); wd.groundBounce = wdTable.GetBool("groundBounce", false); wd.bounceSlip = wdTable.GetFloat("bounceSlip", 1.0f); wd.bounceRebound = wdTable.GetFloat("bounceRebound", 1.0f); wd.numBounce = wdTable.GetInt("numBounce", -1); wd.thickness = wdTable.GetFloat("thickness", 2.0f); wd.corethickness = wdTable.GetFloat("coreThickness", 0.25f); wd.laserflaresize = wdTable.GetFloat("laserFlareSize", 15.0f); wd.intensity = wdTable.GetFloat("intensity", 0.9f); wd.duration = wdTable.GetFloat("duration", 0.05f); wd.falloffRate = wdTable.GetFloat("fallOffRate", 0.5f); wd.lodDistance = wdTable.GetInt("lodDistance", 1000); wd.visuals.modelName = wdTable.GetString("model", ""); wd.visuals.explosionScar = wdTable.GetBool("explosionScar", true); wd.visuals.smokeTrail = wdTable.GetBool("smokeTrail", false); wd.visuals.alwaysVisible = wdTable.GetBool("alwaysVisible", false); wd.visuals.sizeDecay = wdTable.GetFloat("sizeDecay", 0.0f); wd.visuals.alphaDecay = wdTable.GetFloat("alphaDecay", 1.0f); wd.visuals.separation = wdTable.GetFloat("separation", 1.0f); wd.visuals.noGap = wdTable.GetBool("noGap", true); wd.visuals.stages = wdTable.GetInt("stages", 5); wd.gravityAffected = wdTable.GetBool("gravityAffected", false); wd.type = wdTable.GetString("weaponType", "Cannon"); wd.targetBorder = Clamp(wdTable.GetFloat("targetBorder", (wd.type == "Melee")? 1.0f : 0.0f), -1.0f, 1.0f); wd.cylinderTargetting = Clamp(wdTable.GetFloat("cylinderTargetting", (wd.type == "Melee")? 1.0f : 0.0f), 0.0f, 128.0f); wd.range = wdTable.GetFloat("range", 10.0f); const float accuracy = wdTable.GetFloat("accuracy", 0.0f); const float sprayAngle = wdTable.GetFloat("sprayAngle", 0.0f); const float movingAccuracy = wdTable.GetFloat("movingAccuracy", accuracy); // should really be tan but TA seem to cap it somehow // should also be 7fff or ffff theoretically but neither seems good wd.accuracy = sin((accuracy) * PI / 0xafff); wd.sprayAngle = sin((sprayAngle) * PI / 0xafff); wd.movingAccuracy = sin((movingAccuracy) * PI / 0xafff); wd.targetMoveError = wdTable.GetFloat("targetMoveError", 0.0f); wd.leadLimit = wdTable.GetFloat("leadLimit", -1.0f); wd.leadBonus = wdTable.GetFloat("leadBonus", 0.0f); // setup the default damages const LuaTable dmgTable = wdTable.SubTable("damage"); float defDamage = dmgTable.GetFloat("default", 0.0f); if (defDamage == 0.0f) { defDamage = 1.0f; //avoid division by zeroes } for (int a = 0; a < damageArrayHandler->GetNumTypes(); ++a) { wd.damages[a] = defDamage; } map<string, float> damages; dmgTable.GetMap(damages); map<string, float>::const_iterator di; for (di = damages.begin(); di != damages.end(); ++di) { const int type = damageArrayHandler->GetTypeFromName(di->first); if (type != 0) { float dmg = di->second; if (dmg != 0.0f) { wd.damages[type] = dmg; } else { wd.damages[type] = 1.0f; } } } wd.damages.impulseFactor = wdTable.GetFloat("impulseFactor", 1.0f); wd.damages.impulseBoost = wdTable.GetFloat("impulseBoost", 0.0f); wd.damages.craterMult = wdTable.GetFloat("craterMult", wd.damages.impulseFactor); wd.damages.craterBoost = wdTable.GetFloat("craterBoost", 0.0f); wd.areaOfEffect = wdTable.GetFloat("areaOfEffect", 8.0f) * 0.5f; wd.edgeEffectiveness = wdTable.GetFloat("edgeEffectiveness", 0.0f); // prevent 0/0 division in CGameHelper::Explosion if (wd.edgeEffectiveness > 0.999f) { wd.edgeEffectiveness = 0.999f; } wd.projectilespeed = std::max(0.01f, wdTable.GetFloat("weaponVelocity", 0.0f) / GAME_SPEED); wd.startvelocity = max(0.01f, wdTable.GetFloat("startVelocity", 0.0f) / GAME_SPEED); wd.weaponacceleration = wdTable.GetFloat("weaponAcceleration", 0.0f) / GAME_SPEED / GAME_SPEED; wd.reload = wdTable.GetFloat("reloadTime", 1.0f); wd.salvodelay = wdTable.GetFloat("burstRate", 0.1f); wd.salvosize = wdTable.GetInt("burst", 1); wd.projectilespershot = wdTable.GetInt("projectiles", 1); wd.maxAngle = wdTable.GetFloat("tolerance", 3000.0f) * 180.0f / COBSCALEHALF; wd.restTime = 0.0f; wd.metalcost = wdTable.GetFloat("metalPerShot", 0.0f); wd.energycost = wdTable.GetFloat("energyPerShot", 0.0f); wd.selfExplode = wdTable.GetBool("burnblow", false); wd.predictBoost = wdTable.GetFloat("predictBoost", wd.selfExplode ? 0.5f : 0.0f); wd.sweepFire = wdTable.GetBool("sweepfire", false); wd.canAttackGround = wdTable.GetBool("canAttackGround", true); wd.myGravity = wdTable.GetFloat("myGravity", 0.0f); wd.fireStarter = wdTable.GetFloat("fireStarter", 0.0f) * 0.01f; wd.paralyzer = wdTable.GetBool("paralyzer", false); if (wd.paralyzer) { wd.damages.paralyzeDamageTime = max(0, wdTable.GetInt("paralyzeTime", 10)); } else { wd.damages.paralyzeDamageTime = 0; } const float defShake = wd.paralyzer ? 0.0f : wd.damages.GetDefaultDamage(); wd.cameraShake = wdTable.GetFloat("cameraShake", defShake); wd.cameraShake = max(0.0f, wd.cameraShake); // 0.78.2.1 backwards compatibility bool defaultSoundTrigger = (wd.type == "BeamLaser" && !wd.beamburst) || wd.type == "Melee" || wd.type == "Rifle"; wd.soundTrigger = wdTable.GetBool("soundTrigger", defaultSoundTrigger); //sunparser->GetDef(wd.highTrajectory, "0", weaponname + "minbarrelangle"); wd.stockpile = wdTable.GetBool("stockpile", false); wd.stockpileTime = wdTable.GetFloat("stockpileTime", wd.reload); wd.interceptor = wdTable.GetInt("interceptor", 0); wd.targetable = wdTable.GetInt("targetable", 0); wd.manualfire = wdTable.GetBool("commandfire", false); wd.coverageRange = wdTable.GetFloat("coverage", 0.0f); // FIXME -- remove the old style ? LuaTable shTable = wdTable.SubTable("shield"); const float3 shieldBadColor (1.0f, 0.5f, 0.5f); const float3 shieldGoodColor(0.5f, 0.5f, 1.0f); if (shTable.IsValid()) { wd.shieldRepulser = shTable.GetBool("repulser", false); wd.smartShield = shTable.GetBool("smart", false); wd.exteriorShield = shTable.GetBool("exterior", false); wd.visibleShield = shTable.GetBool("visible", false); wd.visibleShieldRepulse = shTable.GetBool("visibleRepulse", false); wd.visibleShieldHitFrames = shTable.GetInt("visibleHitFrames", 0); wd.shieldEnergyUse = shTable.GetFloat("energyUse", 0.0f); wd.shieldForce = shTable.GetFloat("force", 0.0f); wd.shieldRadius = shTable.GetFloat("radius", 0.0f); wd.shieldMaxSpeed = shTable.GetFloat("maxSpeed", 0.0f); wd.shieldPower = shTable.GetFloat("power", 0.0f); wd.shieldPowerRegen = shTable.GetFloat("powerRegen", 0.0f); wd.shieldPowerRegenEnergy = shTable.GetFloat("powerRegenEnergy", 0.0f); wd.shieldRechargeDelay = (int)(shTable.GetFloat("rechargeDelay", 0) * GAME_SPEED); wd.shieldStartingPower = shTable.GetFloat("startingPower", 0.0f); wd.shieldInterceptType = shTable.GetInt("interceptType", 0); wd.shieldBadColor = shTable.GetFloat3("badColor", shieldBadColor); wd.shieldGoodColor = shTable.GetFloat3("goodColor", shieldGoodColor); wd.shieldAlpha = shTable.GetFloat("alpha", 0.2f); } else { wd.shieldRepulser = wdTable.GetBool("shieldRepulser", false); wd.smartShield = wdTable.GetBool("smartShield", false); wd.exteriorShield = wdTable.GetBool("exteriorShield", false); wd.visibleShield = wdTable.GetBool("visibleShield", false); wd.visibleShieldRepulse = wdTable.GetBool("visibleShieldRepulse", false); wd.visibleShieldHitFrames = wdTable.GetInt("visibleShieldHitFrames", 0); wd.shieldEnergyUse = wdTable.GetFloat("shieldEnergyUse", 0.0f); wd.shieldForce = wdTable.GetFloat("shieldForce", 0.0f); wd.shieldRadius = wdTable.GetFloat("shieldRadius", 0.0f); wd.shieldMaxSpeed = wdTable.GetFloat("shieldMaxSpeed", 0.0f); wd.shieldPower = wdTable.GetFloat("shieldPower", 0.0f); wd.shieldPowerRegen = wdTable.GetFloat("shieldPowerRegen", 0.0f); wd.shieldPowerRegenEnergy = wdTable.GetFloat("shieldPowerRegenEnergy", 0.0f); wd.shieldRechargeDelay = (int)(wdTable.GetFloat("shieldRechargeDelay", 0) * GAME_SPEED); wd.shieldStartingPower = wdTable.GetFloat("shieldStartingPower", 0.0f); wd.shieldInterceptType = wdTable.GetInt("shieldInterceptType", 0); wd.shieldBadColor = wdTable.GetFloat3("shieldBadColor", shieldBadColor); wd.shieldGoodColor = wdTable.GetFloat3("shieldGoodColor", shieldGoodColor); wd.shieldAlpha = wdTable.GetFloat("shieldAlpha", 0.2f); } int defInterceptType = 0; if ((wd.type == "Cannon") || (wd.type == "EmgCannon")) { defInterceptType = 1; } else if ((wd.type == "LaserCannon") || (wd.type == "BeamLaser")) { defInterceptType = 2; } else if ((wd.type == "StarburstLauncher") || (wd.type == "MissileLauncher")) { defInterceptType = 4; } else if (wd.type == "AircraftBomb") { defInterceptType = 8; } else if (wd.type == "Flame") { defInterceptType = 16; } else if (wd.type == "TorpedoLauncher") { defInterceptType = 32; } else if (wd.type == "LightningCannon") { defInterceptType = 64; } else if (wd.type == "Rifle") { defInterceptType = 128; } else if (wd.type == "Melee") { defInterceptType = 256; } wd.interceptedByShieldType = wdTable.GetInt("interceptedByShieldType", defInterceptType); wd.wobble = wdTable.GetFloat("wobble", 0.0f) * TAANG2RAD / 30.0f; wd.dance = wdTable.GetFloat("dance", 0.0f) / GAME_SPEED; wd.trajectoryHeight = wdTable.GetFloat("trajectoryHeight", 0.0f); wd.noAutoTarget = (wd.manualfire || wd.interceptor || wd.isShield); wd.onlyTargetCategory = 0xffffffff; if (wdTable.GetBool("toAirWeapon", false)) { // fix if we sometime call aircrafts otherwise wd.onlyTargetCategory = CCategoryHandler::Instance()->GetCategories("VTOL"); //LOG("air only weapon %s %i", weaponname.c_str(), wd.onlyTargetCategory); } wd.largeBeamLaser = wdTable.GetBool("largeBeamLaser", false); wd.visuals.tilelength = wdTable.GetFloat("tileLength", 200.0f); wd.visuals.scrollspeed = wdTable.GetFloat("scrollSpeed", 5.0f); wd.visuals.pulseSpeed = wdTable.GetFloat("pulseSpeed", 1.0f); wd.visuals.beamdecay = wdTable.GetFloat("beamDecay", 1.0f); wd.visuals.beamttl = wdTable.GetInt("beamTTL", 0); if (wd.type == "Cannon") { wd.heightmod = wdTable.GetFloat("heightMod", 0.8f); } else if (wd.type == "BeamLaser" || wd.type == "LightningCannon") { wd.heightmod = wdTable.GetFloat("heightMod", 1.0f); } else { wd.heightmod = wdTable.GetFloat("heightMod", 0.2f); } wd.onlyForward = !wd.turret && (wd.type != "StarburstLauncher"); color = wdTable.GetInt("color", 0); color2 = wdTable.GetInt("color2", 0); const float3 rgbcol = hs2rgb(color / float(255), color2 / float(255)); wd.visuals.color = wdTable.GetFloat3("rgbColor", rgbcol); wd.visuals.color2 = wdTable.GetFloat3("rgbColor2", float3(1.0f, 1.0f, 1.0f)); wd.uptime = wdTable.GetFloat("weaponTimer", 0.0f); wd.flighttime = wdTable.GetFloat("flightTime", 0) * 32; wd.turnrate = wdTable.GetFloat("turnRate", 0.0f) * TAANG2RAD / 30.0f; if ((wd.type == "AircraftBomb") && !manualBombSettings) { if (wd.reload < 0.5f) { wd.salvodelay = min(0.2f, wd.reload); wd.salvosize = (int)(1 / wd.salvodelay) + 1; wd.reload = 5; } else { wd.salvodelay = min(0.4f, wd.reload); wd.salvosize = 2; } } //if(!wd.turret && (wd.type != "TorpedoLauncher")) { // wd.maxAngle*=0.4f; //} //2+min(damages[0]*0.0025f,weaponDef->areaOfEffect*0.1f) const float tempsize = 2.0f + min(wd.damages[0] * 0.0025f, wd.areaOfEffect * 0.1f); wd.size = wdTable.GetFloat("size", tempsize); wd.sizeGrowth = wdTable.GetFloat("sizeGrowth", 0.2f); wd.collisionSize = wdTable.GetFloat("collisionSize", 0.05f); wd.visuals.colorMap = 0; const string colormap = wdTable.GetString("colormap", ""); if (colormap != "") { wd.visuals.colorMap = CColorMap::LoadFromDefString(colormap); } wd.heightBoostFactor = wdTable.GetFloat("heightBoostFactor", -1.0f); wd.proximityPriority = wdTable.GetFloat("proximityPriority", 1.0f); // get some weapon specific defaults if (wd.type == "Cannon") { // CExplosiveProjectile wd.ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.9f); wd.visuals.color = wdTable.GetFloat3("rgbColor", float3(1.0f, 0.5f, 0.0f)); wd.intensity = wdTable.GetFloat("intensity", 0.2f); } else if (wd.type == "Rifle") { wd.ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.9f); } else if (wd.type == "Melee") { // ... } else if (wd.type == "Flame") { // CFlameProjectile wd.ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.2f); wd.size = wdTable.GetFloat("size", tempsize); wd.sizeGrowth = wdTable.GetFloat("sizeGrowth", 0.5f); wd.collisionSize = wdTable.GetFloat("collisionSize", 0.5f); wd.duration = wdTable.GetFloat("flameGfxTime", 1.2f); } else if (wd.type == "MissileLauncher") { // CMissileProjectile wd.ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.5f); } else if (wd.type == "LaserCannon") { // CLaserProjectile wd.ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.7f); wd.collisionSize = wdTable.GetFloat("collisionSize", 0.5f); wd.visuals.hardStop = wdTable.GetBool("hardstop", false); } else if (wd.type == "BeamLaser") { wd.ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.7f); } else if (wd.type == "LightningCannon") { wd.ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.5f); wd.thickness = wdTable.GetFloat("thickness", 0.8f); } else if (wd.type == "EmgCannon") { // CEmgProjectile wd.ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.5f); wd.size = wdTable.GetFloat("size", 3.0f); wd.visuals.color = wdTable.GetFloat3("rgbColor", float3(0.9f, 0.9f, 0.2f)); } else if (wd.type == "DGun") { // CFireBallProjectile wd.ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.5f); wd.collisionSize = wdTable.GetFloat("collisionSize", 10.0f); } else if (wd.type == "StarburstLauncher") { // CStarburstProjectile wd.ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.7f); } else { wd.ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.0f); } const LuaTable texTable = wdTable.SubTable("textures"); wd.visuals.texNames[0] = texTable.GetString(1, wdTable.GetString("texture1", "")); wd.visuals.texNames[1] = texTable.GetString(2, wdTable.GetString("texture2", "")); wd.visuals.texNames[2] = texTable.GetString(3, wdTable.GetString("texture3", "")); wd.visuals.texNames[3] = texTable.GetString(4, wdTable.GetString("texture4", "")); wd.visuals.expGenTag = wdTable.GetString("explosionGenerator", ""); wd.visuals.bounceExpGenTag = wdTable.GetString("bounceExplosionGenerator", ""); const float gd = max(30.0f, wd.damages[0] / 20.0f); const float defExpSpeed = (8.0f + (gd * 2.5f)) / (9.0f + (sqrt(gd) * 0.7f)) * 0.5f; wd.explosionSpeed = wdTable.GetFloat("explosionSpeed", defExpSpeed); // Dynamic Damage wd.dynDamageInverted = wdTable.GetBool("dynDamageInverted", false); wd.dynDamageExp = wdTable.GetFloat("dynDamageExp", 0.0f); wd.dynDamageMin = wdTable.GetFloat("dynDamageMin", 0.0f); wd.dynDamageRange = wdTable.GetFloat("dynDamageRange", 0.0f); LoadSound(wdTable, wd.firesound, "start"); LoadSound(wdTable, wd.soundhit, "hit"); if ((wd.firesound.getVolume(0) == -1.0f) || (wd.soundhit.getVolume(0) == -1.0f)) { // no volume (-1.0f) read from weapon definition, set it dynamically here if (wd.damages[0] <= 50.0f) { wd.soundhit.setVolume(0, 5.0f); wd.firesound.setVolume(0, 5.0f); } else { float soundVolume = sqrt(wd.damages[0] * 0.5f); if (wd.type == "LaserCannon") { soundVolume *= 0.5f; } float hitSoundVolume = soundVolume; if ((soundVolume > 100.0f) && ((wd.type == "MissileLauncher") || (wd.type == "StarburstLauncher"))) { soundVolume = 10.0f * sqrt(soundVolume); } if (wd.firesound.getVolume(0) == -1.0f) { wd.firesound.setVolume(0, soundVolume); } soundVolume = hitSoundVolume; if (wd.areaOfEffect > 8.0f) { soundVolume *= 2.0f; } if (wd.type == "DGun") { soundVolume *= 0.15f; } if (wd.soundhit.getVolume(0) == -1.0f) { wd.soundhit.setVolume(0, soundVolume); } } } // custom parameters table wdTable.SubTable("customParams").GetMap(wd.customParams); }
void CFeatureHandler::CreateFeatureDef(const LuaTable& fdTable, const string& mixedCase) { const string name = StringToLower(mixedCase); if (featureDefs.find(name) != featureDefs.end()) { return; } FeatureDef* fd = new FeatureDef; fd->myName = name; fd->filename = fdTable.GetString("filename", "unknown"); fd->description = fdTable.GetString("description", ""); fd->blocking = fdTable.GetBool("blocking", true); fd->burnable = fdTable.GetBool("flammable", false); fd->destructable = !fdTable.GetBool("indestructible", false); fd->reclaimable = fdTable.GetBool("reclaimable", fd->destructable); fd->autoreclaim = fdTable.GetBool("autoreclaimable", fd->autoreclaim); fd->resurrectable = fdTable.GetInt("resurrectable", -1); //this seem to be the closest thing to floating that ta wreckage contains fd->floating = fdTable.GetBool("nodrawundergray", false); if (fd->floating && !fd->blocking) { fd->floating = false; } fd->noSelect = fdTable.GetBool("noselect", false); fd->deathFeature = fdTable.GetString("featureDead", ""); fd->metal = fdTable.GetFloat("metal", 0.0f); fd->energy = fdTable.GetFloat("energy", 0.0f); fd->maxHealth = fdTable.GetFloat("damage", 0.0f); fd->reclaimTime = fdTable.GetFloat("reclaimTime", (fd->metal + fd->energy)*6.f); fd->smokeTime = fdTable.GetInt("smokeTime", (fd->blocking) ? 300 : 0); fd->drawType = fdTable.GetInt("drawType", DRAWTYPE_MODEL); fd->modelname = fdTable.GetString("object", ""); if (!fd->modelname.empty()) { if (fd->modelname.find(".") == std::string::npos) { fd->modelname += ".3do"; } fd->modelname = string("objects3d/") + fd->modelname; } // these take precedence over the old sphere tags as well as // feature->radius (for feature <--> projectile interactions) fd->collisionVolumeTypeStr = fdTable.GetString("collisionVolumeType", ""); fd->collisionVolumeScales = fdTable.GetFloat3("collisionVolumeScales", ZeroVector); fd->collisionVolumeOffsets = fdTable.GetFloat3("collisionVolumeOffsets", ZeroVector); fd->collisionVolumeTest = fdTable.GetInt("collisionVolumeTest", COLVOL_TEST_CONT); // initialize the (per-featuredef) collision-volume, // all CFeature instances hold a copy of this object fd->collisionVolume = new CollisionVolume( fd->collisionVolumeTypeStr, fd->collisionVolumeScales, fd->collisionVolumeOffsets, fd->collisionVolumeTest ); fd->upright = fdTable.GetBool("upright", false); // our resolution is double TA's fd->xsize = fdTable.GetInt("footprintX", 1) * 2; fd->zsize = fdTable.GetInt("footprintZ", 1) * 2; const float defMass = (fd->metal * 0.4f) + (fd->maxHealth * 0.1f); fd->mass = fdTable.GetFloat("mass", defMass); fd->mass = max(0.001f, fd->mass); // custom parameters table fdTable.SubTable("customParams").GetMap(fd->customParams); AddFeatureDef(name, fd); }
void CMapInfo::ReadWater() { const LuaTable wt = mapRoot->SubTable("water"); water.repeatX = wt.GetFloat("repeatX", 0.0f); water.repeatY = wt.GetFloat("repeatY", 0.0f); water.damage = wt.GetFloat("damage", 0.0f) * (16.0f / 30.0f); water.absorb = wt.GetFloat3("absorb", float3(0.0f, 0.0f, 0.0f)); water.baseColor = wt.GetFloat3("baseColor", float3(0.0f, 0.0f, 0.0f)); water.minColor = wt.GetFloat3("minColor", float3(0.0f, 0.0f, 0.0f)); water.ambientFactor = wt.GetFloat("ambientFactor", 1.0f); water.diffuseFactor = wt.GetFloat("diffuseFactor", 1.0f); water.specularFactor= wt.GetFloat("specularFactor",1.0f); water.specularPower = wt.GetFloat("specularPower", 20.0f); water.planeColor = wt.GetFloat3("planeColor", float3(0.0f, 0.4f, 0.0f)); water.hasWaterPlane = wt.KeyExists("planeColor"); water.surfaceColor = wt.GetFloat3("surfaceColor", float3(0.75f, 0.8f, 0.85f)); water.surfaceAlpha = wt.GetFloat("surfaceAlpha", 0.55f); water.diffuseColor = wt.GetFloat3("diffuseColor", float3(1.0f, 1.0f, 1.0f)); water.specularColor = wt.GetFloat3("specularColor", light.groundSunColor); water.fresnelMin = wt.GetFloat("fresnelMin", 0.2f); water.fresnelMax = wt.GetFloat("fresnelMax", 0.8f); water.fresnelPower = wt.GetFloat("fresnelPower", 4.0f); water.reflDistortion = wt.GetFloat("reflectionDistortion", 1.0f); water.blurBase = wt.GetFloat("blurBase", 2.0f); water.blurExponent = wt.GetFloat("blurExponent", 1.5f); water.perlinStartFreq = wt.GetFloat("perlinStartFreq", 8.0f); water.perlinLacunarity = wt.GetFloat("perlinLacunarity", 3.0f); water.perlinAmplitude = wt.GetFloat("perlinAmplitude", 0.9f); water.windSpeed = wt.GetFloat("windSpeed", 1.0f); water.texture = wt.GetString("texture", ""); water.foamTexture = wt.GetString("foamTexture", ""); water.normalTexture = wt.GetString("normalTexture", ""); water.shoreWaves = wt.GetBool("shoreWaves", true); water.forceRendering = wt.GetBool("forceRendering", false); // use 'resources.lua' for missing fields (our the engine defaults) const LuaTable resGfxMaps = resRoot->SubTable("graphics").SubTable("maps"); if (!water.texture.empty()) { water.texture = "maps/" + water.texture; } else { water.texture = "bitmaps/" + resGfxMaps.GetString("watertex", "ocean.jpg"); } if (!water.foamTexture.empty()) { water.foamTexture = "maps/" + water.foamTexture; } else { water.foamTexture = "bitmaps/" + resGfxMaps.GetString("waterfoamtex", "foam.jpg"); } if (!water.normalTexture.empty()) { water.normalTexture = "maps/" + water.normalTexture; water.numTiles = std::min(16,std::max(1,wt.GetInt("numTiles",1))); } else { water.normalTexture = "bitmaps/" + resGfxMaps.GetString("waternormaltex", "waterbump.png"); if (resGfxMaps.KeyExists("waternormaltex")) { water.numTiles = std::min(16,std::max(1,resGfxMaps.GetInt("numTiles",1))); }else{ // default texture is a TileSet of 3x3 // user-defined textures are expected to be 1x1 (no DynWaves possible) water.numTiles = 3; } } // water caustic textures LuaTable caustics = wt.SubTable("caustics"); string causticPrefix = "maps/"; if (!caustics.IsValid()) { caustics = resRoot->SubTable("graphics").SubTable("caustics"); causticPrefix = "bitmaps/"; } if (caustics.IsValid()) { for (int i = 1; true; i++) { const string texName = caustics.GetString(i, ""); if (texName.empty()) { break; } water.causticTextures.push_back(causticPrefix + texName); } } else { // load the default 32 textures for (int i = 0; i < 32; i++) { char defTex[256]; sprintf(defTex, "bitmaps/caustics/caustic%02i.jpg", i); water.causticTextures.push_back(defTex); } } }
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); }
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(); }
/* * CArchiveScanner::ArchiveData */ CArchiveScanner::ArchiveData::ArchiveData(const LuaTable& archiveTable) { if (!archiveTable.IsValid()) { return; } std::vector<std::string> keys; if (!archiveTable.GetKeys(keys)) { return; } std::vector<std::string>::const_iterator key; for (key = keys.begin(); key != keys.end(); ++key) { const std::string& keyLower = StringToLower(*key); if (!ArchiveData::IsReservedKey(keyLower)) { if (keyLower == "modtype") { SetInfoItemValueInteger(*key, archiveTable.GetInt(*key, 0)); continue; } const int luaType = archiveTable.GetType(*key); switch (luaType) { case LuaTable::STRING: { SetInfoItemValueString(*key, archiveTable.GetString(*key, "")); } break; case LuaTable::NUMBER: { SetInfoItemValueFloat(*key, archiveTable.GetFloat(*key, 0.0f)); } break; case LuaTable::BOOLEAN: { SetInfoItemValueBool(*key, archiveTable.GetBool(*key, false)); } break; default: { // just ignore unsupported types (most likely to be lua-tables) //throw content_error("Lua-type " + IntToString(luaType) + " not supported in archive-info, but it is used on key \"" + *key + "\""); } break; } } } const LuaTable _dependencies = archiveTable.SubTable("depend"); for (int dep = 1; _dependencies.KeyExists(dep); ++dep) { dependencies.push_back(_dependencies.GetString(dep, "")); } const LuaTable _replaces = archiveTable.SubTable("replace"); for (int rep = 1; _replaces.KeyExists(rep); ++rep) { replaces.push_back(_replaces.GetString(rep, "")); } //! FIXME //! XXX HACK needed until lobbies, lobbyserver and unitsync are sorted out //! so they can uniquely identify different versions of the same mod. //! (at time of this writing they use name only) //! NOTE when changing this, this function is used both by the code that //! reads ArchiveCache.lua and the code that reads modinfo.lua from the mod. //! so make sure it doesn't keep adding stuff to the name everytime //! Spring/unitsync is loaded. const std::string& name = GetName(); const std::string& version = GetVersion(); if ((name.find(version) == std::string::npos) && !version.empty()) { SetInfoItemValueString("name", name + " " + version); } }
MoveDef::MoveDef(const LuaTable& moveTable, int moveDefID) { *this = MoveDef(); name = StringToLower(moveTable.GetString("name", "")); pathType = moveDefID - 1; crushStrength = moveTable.GetFloat("crushStrength", 10.0f); const LuaTable& depthModTable = moveTable.SubTable("depthModParams"); const LuaTable& speedModMultsTable = moveTable.SubTable("speedModMults"); const float minWaterDepth = moveTable.GetFloat("minWaterDepth", 10.0f); const float maxWaterDepth = moveTable.GetFloat("maxWaterDepth", 0.0f); if ((name.find("boat") != string::npos) || (name.find("ship") != string::npos)) { depth = minWaterDepth; moveFamily = MoveDef::Ship; subMarine = moveTable.GetBool("subMarine", false); } else if (name.find("hover") != string::npos) { maxSlope = DegreesToMaxSlope(moveTable.GetFloat("maxSlope", 15.0f)); moveFamily = MoveDef::Hover; } else { depthModParams[DEPTHMOD_MIN_HEIGHT] = std::max(0.00f, depthModTable.GetFloat("minHeight", 0.0f )); depthModParams[DEPTHMOD_MAX_HEIGHT] = ( depthModTable.GetFloat("maxHeight", std::numeric_limits<float>::max() )); depthModParams[DEPTHMOD_MAX_SCALE ] = std::max(0.01f, depthModTable.GetFloat("maxScale", std::numeric_limits<float>::max() )); depthModParams[DEPTHMOD_QUA_COEFF ] = std::max(0.00f, depthModTable.GetFloat("quadraticCoeff", 0.0f )); depthModParams[DEPTHMOD_LIN_COEFF ] = std::max(0.00f, depthModTable.GetFloat("linearCoeff", moveTable.GetFloat("depthMod", 0.1f))); depthModParams[DEPTHMOD_CON_COEFF ] = std::max(0.00f, depthModTable.GetFloat("constantCoeff", 1.0f )); // ensure [depthModMinHeight, depthModMaxHeight] is a valid range depthModParams[DEPTHMOD_MAX_HEIGHT] = std::max(depthModParams[DEPTHMOD_MIN_HEIGHT], depthModParams[DEPTHMOD_MAX_HEIGHT]); depth = maxWaterDepth; maxSlope = DegreesToMaxSlope(moveTable.GetFloat("maxSlope", 60.0f)); if (name.find("tank") != string::npos) { moveFamily = MoveDef::Tank; } else { moveFamily = MoveDef::KBot; } } speedModMults[SPEEDMOD_MOBILE_BUSY_MULT] = std::max(0.01f, speedModMultsTable.GetFloat("mobileBusyMult", 1.0f /*0.10f*/)); speedModMults[SPEEDMOD_MOBILE_IDLE_MULT] = std::max(0.01f, speedModMultsTable.GetFloat("mobileIdleMult", 1.0f /*0.35f*/)); speedModMults[SPEEDMOD_MOBILE_MOVE_MULT] = std::max(0.01f, speedModMultsTable.GetFloat("mobileMoveMult", 1.0f /*0.65f*/)); avoidMobilesOnPath = moveTable.GetBool("avoidMobilesOnPath", true); heatMapping = moveTable.GetBool("heatMapping", false); flowMapping = moveTable.GetBool("flowMapping", true); heatMod = moveTable.GetFloat("heatMod", 50.0f); flowMod = moveTable.GetFloat("flowMod", 1.0f); heatProduced = moveTable.GetInt("heatProduced", GAME_SPEED * 2); // <maxSlope> ranges from 0.0 to 60 * 1.5 degrees, ie. from 0.0 to // 0.5 * PI radians, ie. from 1.0 - cos(0.0) to 1.0 - cos(0.5 * PI) // = [0, 1] --> DEFAULT <slopeMod> values range from (4 / 0.001) to // (4 / 1.001) = [4000.0, 3.996] // // speedMod values for a terrain-square slope in [0, 1] are given by // (1.0 / (1.0 + slope * slopeMod)) and therefore have a MAXIMUM at // <slope=0, slopeMod=...> and a MINIMUM at <slope=1, slopeMod=4000> // (of 1.0 / (1.0 + 0.0 * ...) = 1.0 and 1.0 / (1.0 + 1.0 * 4000.0) // = 0.00025 respectively) // slopeMod = moveTable.GetFloat("slopeMod", 4.0f / (maxSlope + 0.001f)); // ground units hug the ocean floor when in water, // ships stay at a "fixed" level (their waterline) followGround = (moveFamily == MoveDef::Tank || moveFamily == MoveDef::KBot); // tank or bot that cannot get its threads / feet // wet, or hovercraft (which doesn't touch ground // or water) const bool b0 = ((followGround && maxWaterDepth <= 0.0) || moveFamily == MoveDef::Hover); // ship (or sub) that cannot crawl onto shore, OR tank or // kbot restricted to snorkling (strange but possible) const bool b1 = ((moveFamily == MoveDef::Ship && minWaterDepth > 0.0) || ((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 = ((followGround) && maxWaterDepth > 0.0) || (moveFamily == MoveDef::Ship && minWaterDepth < 0.0); if (b0) { terrainClass = MoveDef::Land; } if (b1) { terrainClass = MoveDef::Water; } if (b2) { terrainClass = MoveDef::Mixed; } const int xsizeDef = std::max(1, moveTable.GetInt("footprintX", 1)); const int zsizeDef = std::max(1, moveTable.GetInt("footprintZ", xsizeDef)); // 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) xsize = xsizeDef * SPRING_FOOTPRINT_SCALE; zsize = zsizeDef * SPRING_FOOTPRINT_SCALE; xsize -= ((xsize & 1)? 0: 1); zsize -= ((zsize & 1)? 0: 1); // precalculated data for MoveMath xsizeh = xsize >> 1; zsizeh = zsize >> 1; assert((xsize & 1) == 1); assert((zsize & 1) == 1); }
void CCustomExplosionGenerator::Load(CExplosionGeneratorHandler* h, const string& tag) { typedef std::map<string, CEGData> CEGMap; typedef std::map<string, CEGData>::iterator CEGMapIt; CEGMapIt it = cachedCEGs.find(tag); if (it == cachedCEGs.end()) { CEGData cegData; const LuaTable& root = h->GetTable(); const LuaTable expTable = root.SubTable(tag); if (!expTable.IsValid()) { throw content_error("Explosion info for " + tag + " not found."); } vector<string> spawns; expTable.GetKeys(spawns); for (vector<string>::iterator si = spawns.begin(); si != spawns.end(); ++si) { ProjectileSpawnInfo psi; const string& spawnName = *si; const LuaTable spawnTable = expTable.SubTable(spawnName); if (!spawnTable.IsValid() || spawnName == "groundflash") { continue; } const string className = spawnTable.GetString("class", spawnName); psi.projectileClass = h->projectileClasses.GetClass(className); psi.flags = GetFlagsFromTable(spawnTable); psi.count = spawnTable.GetInt("count", 1); if (psi.projectileClass->binder->flags & creg::CF_Synced) { psi.flags |= SPW_SYNCED; } string code; map<string, string> props; map<string, string>::const_iterator propIt; spawnTable.SubTable("properties").GetMap(props); for (propIt = props.begin(); propIt != props.end(); ++propIt) { creg::Class::Member* m = psi.projectileClass->FindMember(propIt->first.c_str()); if (m && (m->flags & creg::CM_Config)) { ParseExplosionCode(&psi, m->offset, m->type, propIt->second, code); } } code += (char)OP_END; psi.code.resize(code.size()); copy(code.begin(), code.end(), psi.code.begin()); cegData.projectileSpawn.push_back(psi); } const LuaTable gndTable = expTable.SubTable("groundflash"); const int ttl = gndTable.GetInt("ttl", 0); if (ttl > 0) { cegData.groundFlash.circleAlpha = gndTable.GetFloat("circleAlpha", 0.0f); cegData.groundFlash.flashSize = gndTable.GetFloat("flashSize", 0.0f); cegData.groundFlash.flashAlpha = gndTable.GetFloat("flashAlpha", 0.0f); cegData.groundFlash.circleGrowth = gndTable.GetFloat("circleGrowth", 0.0f); cegData.groundFlash.color = gndTable.GetFloat3("color", float3(1.0f, 1.0f, 0.8f)); cegData.groundFlash.flags = SPW_GROUND | GetFlagsFromTable(gndTable); cegData.groundFlash.ttl = ttl; } cegData.useDefaultExplosions = expTable.GetBool("useDefaultExplosions", false); it = cachedCEGs.insert(std::make_pair(tag, cegData)).first; } currentCEG = it; }
WeaponDef::WeaponDef(const LuaTable& wdTable, const std::string& name_, int id_) : name(name_) , ptrailExplosionGeneratorID(CExplosionGeneratorHandler::EXPGEN_ID_INVALID) , impactExplosionGeneratorID(CExplosionGeneratorHandler::EXPGEN_ID_STANDARD) , bounceExplosionGeneratorID(CExplosionGeneratorHandler::EXPGEN_ID_INVALID) , id(id_) , projectileType(WEAPON_BASE_PROJECTILE) , collisionFlags(0) { WeaponDefs.Load(this, wdTable); if (wdTable.KeyExists("cylinderTargetting")) LOG_L(L_WARNING, "WeaponDef (%s) cylinderTargetting is deprecated and will be removed in the next release (use cylinderTargeting).", name.c_str()); if (wdTable.KeyExists("color1") || wdTable.KeyExists("color2")) LOG_L(L_WARNING, "WeaponDef (%s) color1 & color2 (= hue & sat) are removed. Use rgbColor instead!", name.c_str()); if (wdTable.KeyExists("isShield")) LOG_L(L_WARNING, "WeaponDef (%s) The \"isShield\" tag has been removed. Use the weaponType=\"Shield\" tag instead!", name.c_str()); shieldRechargeDelay = int(wdTable.GetFloat("rechargeDelay", 0) * GAME_SPEED); shieldArmorType = damageArrayHandler->GetTypeFromName(shieldArmorTypeName); flighttime = int(wdTable.GetFloat("flighttime", 0.0f) * 32); maxFireAngle = math::cos(wdTable.GetFloat("firetolerance", 3640.0f) * TAANG2RAD); //FIXME may be smarter to merge the collideXYZ tags with avoidXYZ and removing the collisionFlags tag (and move the code into CWeapon)? collisionFlags = 0; if (!wdTable.GetBool("collideEnemy", true)) { collisionFlags |= Collision::NOENEMIES; } if (!wdTable.GetBool("collideFriendly", true)) { collisionFlags |= Collision::NOFRIENDLIES; } if (!wdTable.GetBool("collideFeature", true)) { collisionFlags |= Collision::NOFEATURES; } if (!wdTable.GetBool("collideNeutral", true)) { collisionFlags |= Collision::NONEUTRALS; } if (!wdTable.GetBool("collideGround", true)) { collisionFlags |= Collision::NOGROUND; } //FIXME defaults depend on other tags { if (paralyzer) cameraShake = wdTable.GetFloat("cameraShake", 0.0f); if (selfExplode) predictBoost = wdTable.GetFloat("predictBoost", 0.5f); if (type == "Melee") { targetBorder = Clamp(wdTable.GetFloat("targetBorder", 1.0f), -1.0f, 1.0f); cylinderTargeting = Clamp(wdTable.GetFloat("cylinderTargeting", wdTable.GetFloat("cylinderTargetting", 1.0f)), 0.0f, 128.0f); } if (type == "Flame") { //FIXME move to lua (for all other weapons this tag is named `duration` and has a different default) duration = wdTable.GetFloat("flameGfxTime", 1.2f); } if (type == "Cannon") { heightmod = wdTable.GetFloat("heightMod", 0.8f); } else if (type == "BeamLaser" || type == "LightningCannon") { heightmod = wdTable.GetFloat("heightMod", 1.0f); } if (type == "LaserCannon") { // for lasers we want this to be true by default: it sets // projectile ttl values to the minimum required to hit a // target which prevents them overshooting (lasers travel // many elmos per frame and ttl's are rounded) at maximum // range selfExplode = wdTable.GetBool("burnblow", true); } } // setup the default damages { const LuaTable dmgTable = wdTable.SubTable("damage"); float defDamage = dmgTable.GetFloat("default", 1.0f); // avoid division by zeros if (defDamage == 0.0f) defDamage = 1.0f; damages.SetDefaultDamage(defDamage); if (!paralyzer) damages.paralyzeDamageTime = 0; std::map<string, float> dmgs; std::map<string, float>::const_iterator di; dmgTable.GetMap(dmgs); for (di = dmgs.begin(); di != dmgs.end(); ++di) { const int type = damageArrayHandler->GetTypeFromName(di->first); if (type != 0) { float dmg = di->second; if (dmg != 0.0f) { damages[type] = dmg; } else { damages[type] = 1.0f; } } } const float tempsize = 2.0f + std::min(defDamage * 0.0025f, damageAreaOfEffect * 0.1f); const float gd = std::max(30.0f, defDamage / 20.0f); const float defExpSpeed = (8.0f + (gd * 2.5f)) / (9.0f + (math::sqrt(gd) * 0.7f)) * 0.5f; size = wdTable.GetFloat("size", tempsize); explosionSpeed = wdTable.GetFloat("explosionSpeed", defExpSpeed); } { // 0.78.2.1 backwards compatibility: non-burst beamlasers play one // sample per shot, not for each individual beam making up the shot const bool singleSampleShot = (type == "BeamLaser" && !beamburst); const bool singleShotWeapon = (type == "Melee" || type == "Rifle"); soundTrigger = wdTable.GetBool("soundTrigger", singleSampleShot || singleShotWeapon); } // get some weapon specific defaults int defInterceptType = 0; if (type == "Cannon") { // CExplosiveProjectile defInterceptType = 1; projectileType = WEAPON_EXPLOSIVE_PROJECTILE; ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.9f); intensity = wdTable.GetFloat("intensity", 0.2f); } else if (type == "Rifle") { // no projectile or intercept type defInterceptType = 128; ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.9f); } else if (type == "Melee") { // no projectile or intercept type defInterceptType = 256; } else if (type == "Flame") { // CFlameProjectile projectileType = WEAPON_FLAME_PROJECTILE; defInterceptType = 16; ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.2f); collisionSize = wdTable.GetFloat("collisionSize", 0.5f); } else if (type == "MissileLauncher") { // CMissileProjectile projectileType = WEAPON_MISSILE_PROJECTILE; defInterceptType = 4; ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.5f); } else if (type == "LaserCannon") { // CLaserProjectile projectileType = WEAPON_LASER_PROJECTILE; defInterceptType = 2; ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.7f); collisionSize = wdTable.GetFloat("collisionSize", 0.5f); } else if (type == "BeamLaser") { projectileType = largeBeamLaser? WEAPON_LARGEBEAMLASER_PROJECTILE: WEAPON_BEAMLASER_PROJECTILE; defInterceptType = 2; ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.7f); } else if (type == "LightningCannon") { projectileType = WEAPON_LIGHTNING_PROJECTILE; defInterceptType = 64; ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.5f); } else if (type == "EmgCannon") { // CEmgProjectile projectileType = WEAPON_EMG_PROJECTILE; defInterceptType = 1; ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.5f); size = wdTable.GetFloat("size", 3.0f); } else if (type == "TorpedoLauncher") { // WeaponLoader will create either BombDropper with dropTorpedoes = true // (owner->unitDef->canfly && !weaponDef->submissile) or TorpedoLauncher // (both types of weapons will spawn TorpedoProjectile's) // projectileType = WEAPON_TORPEDO_PROJECTILE; defInterceptType = 32; waterweapon = true; } else if (type == "DGun") { // CFireBallProjectile projectileType = WEAPON_FIREBALL_PROJECTILE; ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.5f); collisionSize = wdTable.GetFloat("collisionSize", 10.0f); } else if (type == "StarburstLauncher") { // CStarburstProjectile projectileType = WEAPON_STARBURST_PROJECTILE; defInterceptType = 4; ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.7f); } else if (type == "AircraftBomb") { // WeaponLoader will create BombDropper with dropTorpedoes = false // BombDropper with dropTorpedoes=false spawns ExplosiveProjectile's // projectileType = WEAPON_EXPLOSIVE_PROJECTILE; defInterceptType = 8; ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.9f); } else { ownerExpAccWeight = wdTable.GetFloat("ownerExpAccWeight", 0.0f); } interceptedByShieldType = wdTable.GetInt("interceptedByShieldType", defInterceptType); const std::string& colormap = wdTable.GetString("colormap", ""); if (!colormap.empty()) { visuals.colorMap = CColorMap::LoadFromDefString(colormap); } else { visuals.colorMap = NULL; } ParseWeaponSounds(wdTable); // custom parameters table wdTable.SubTable("customParams").GetMap(customParams); // internal only isShield = (type == "Shield"); noAutoTarget = (manualfire || interceptor || isShield); onlyForward = !turret && (type != "StarburstLauncher"); }
void CCustomExplosionGenerator::Load(CExplosionGeneratorHandler* h, const string& tag) { static std::map<string, CEGData> cachedCEGs; std::map<string, CEGData>::iterator it = cachedCEGs.find(tag); if (it == cachedCEGs.end()) { CEGData cegData; const LuaTable& root = h->GetTable(); const LuaTable expTable = root.SubTable(tag); if (!expTable.IsValid()) { throw content_error("Explosion info for " + tag + " not found."); } vector<string> spawns; expTable.GetKeys(spawns); for (vector<string>::iterator si = spawns.begin(); si != spawns.end(); ++si) { ProjectileSpawnInfo psi; const string& spawnName = *si; const LuaTable spawnTable = expTable.SubTable(spawnName); if (!spawnTable.IsValid() || spawnName == "groundflash") { continue; } const string className = spawnTable.GetString("class", spawnName); unsigned int flags = 0; if (spawnTable.GetBool("ground", false)) { flags |= SPW_GROUND; } if (spawnTable.GetBool("water", false)) { flags |= SPW_WATER; } if (spawnTable.GetBool("air", false)) { flags |= SPW_AIR; } if (spawnTable.GetBool("underwater", false)) { flags |= SPW_UNDERWATER; } if (spawnTable.GetBool("unit", false)) { flags |= SPW_UNIT; } if (spawnTable.GetBool("nounit", false)) { flags |= SPW_NO_UNIT; } psi.projectileClass = h->projectileClasses.GetClass(className); psi.flags = flags; psi.count = spawnTable.GetInt("count", 1); string code; map<string, string> props; map<string, string>::const_iterator propIt; spawnTable.SubTable("properties").GetMap(props); for (propIt = props.begin(); propIt != props.end(); ++propIt) { creg::Class::Member* m = psi.projectileClass->FindMember(propIt->first.c_str()); if (m && (m->flags & creg::CM_Config)) { ParseExplosionCode(&psi, m->offset, m->type, propIt->second, code); } } code += (char)OP_END; psi.code.resize(code.size()); copy(code.begin(), code.end(), psi.code.begin()); cegData.projectileSpawn.push_back(psi); } const LuaTable gndTable = expTable.SubTable("groundflash"); const int ttl = gndTable.GetInt("ttl", 0); if (ttl > 0) { cegData.groundFlash.circleAlpha = gndTable.GetFloat("circleAlpha", 0.0f); cegData.groundFlash.flashSize = gndTable.GetFloat("flashSize", 0.0f); cegData.groundFlash.flashAlpha = gndTable.GetFloat("flashAlpha", 0.0f); cegData.groundFlash.circleGrowth = gndTable.GetFloat("circleGrowth", 0.0f); cegData.groundFlash.color = gndTable.GetFloat3("color", float3(1.0f, 1.0f, 0.8f)); unsigned int flags = SPW_GROUND; if (gndTable.GetBool("ground", false)) { flags |= SPW_GROUND; } if (gndTable.GetBool("water", false)) { flags |= SPW_WATER; } if (gndTable.GetBool("air", false)) { flags |= SPW_AIR; } if (gndTable.GetBool("underwater", false)) { flags |= SPW_UNDERWATER; } if (gndTable.GetBool("unit", false)) { flags |= SPW_UNIT; } if (gndTable.GetBool("nounit", false)) { flags |= SPW_NO_UNIT; } cegData.groundFlash.flags = flags; cegData.groundFlash.ttl = ttl; } cegData.useDefaultExplosions = expTable.GetBool("useDefaultExplosions", false); cachedCEGs[tag] = cegData; currentCEG = &cachedCEGs[tag]; } else { currentCEG = &(it->second); } }
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); }