Example #1
0
void CMapInfo::ReadGlobal()
{
	const LuaTable topTable = *mapRoot;
	
	map.humanName    = topTable.GetString("description", map.name);
	map.wantedScript = topTable.GetString("script", map.wantedScript);

	map.hardness      = topTable.GetFloat("maphardness", 100.0f);
	map.notDeformable = topTable.GetBool("notDeformable", false);

	map.gravity = topTable.GetFloat("gravity", 130.0f);
	map.gravity = max(0.001f, map.gravity);
	map.gravity = -map.gravity / (GAME_SPEED * GAME_SPEED);

	map.tidalStrength   = topTable.GetFloat("tidalStrength", 0.0f);
	map.maxMetal        = topTable.GetFloat("maxMetal", 0.02f);
	map.extractorRadius = topTable.GetFloat("extractorRadius", 500.0f);

	map.voidWater = topTable.GetBool("voidWater", false);

	// clamps
	map.hardness        = max(0.0f, map.hardness);
	map.tidalStrength   = max(0.0f, map.tidalStrength);
	map.maxMetal        = max(0.0f, map.maxMetal);
	map.extractorRadius = max(0.0f, map.extractorRadius);
}
Example #2
0
void CUnitDefHandler::LoadSounds(const LuaTable& soundsTable, GuiSoundSet& gsound, const string& soundName)
{
	string fileName = soundsTable.GetString(soundName, "");
	if (!fileName.empty()) {
		LoadSound(gsound, fileName, 1.0f);
		return;
	}

	LuaTable sndTable = soundsTable.SubTable(soundName);
	for (int i = 1; true; i++) {
		LuaTable sndFileTable = sndTable.SubTable(i);
		if (sndFileTable.IsValid()) {
			fileName = sndFileTable.GetString("file", "");
			if (!fileName.empty()) {
				const float volume = sndFileTable.GetFloat("volume", 1.0f);
				if (volume > 0.0f) {
					LoadSound(gsound, fileName, volume);
				}
			}
		} else {
			fileName = sndTable.GetString(i, "");
			if (fileName.empty()) {
				break;
			}
			LoadSound(gsound, fileName, 1.0f);
		}
	}
}
Example #3
0
void CWeaponDefHandler::LoadSound(const LuaTable& wdTable,
                                  GuiSoundSet& gsound, const string& soundCat)
{
	string name = "";
	float volume = -1.0f;

	if (soundCat == "start") {
		name   = wdTable.GetString("soundStart", "");
		volume = wdTable.GetFloat("soundStartVolume", -1.0f);
	}
	else if (soundCat == "hit") {
		name   = wdTable.GetString("soundHit", "");
		volume = wdTable.GetFloat("soundHitVolume", -1.0f);
	}

	if (name != "") {
		const int id = LoadSoundFile(name);
		if (id > 0)
		{
			GuiSoundSet::Data soundData(name, 0, volume);
			gsound.sounds.push_back(soundData);
			gsound.setID(0, id);
		}
	}
}
Example #4
0
void WeaponDef::LoadSound(
	const LuaTable& wdTable,
	const std::string& soundKey,
	const unsigned int soundIdx,
	std::vector<GuiSoundSet::Data>& soundData)
{
	string name = "";
	int id = -1;
	float volume = -1.0f;

	soundData.push_back(GuiSoundSet::Data(name, id, volume));
	assert(soundIdx < soundData.size());
	assert(soundData[soundIdx].id == -1);

	if (soundKey == "soundStart") {
		name   = wdTable.GetString(soundKey, "");
		volume = wdTable.GetFloat(soundKey + "Volume", -1.0f);
	}
	else if (soundKey == "soundHitDry") {
		name   = wdTable.GetString(soundKey, wdTable.GetString("soundHit", ""));
		volume = wdTable.GetFloat(soundKey + "Volume", wdTable.GetFloat("soundHitVolume", -1.0f));
	}
	else if (soundKey == "soundHitWet") {
		name   = wdTable.GetString(soundKey, wdTable.GetString("soundHit", ""));
		volume = wdTable.GetFloat(soundKey + "Volume", wdTable.GetFloat("soundHitVolume", -1.0f));
	}

	if (name.empty())
		return;

	if ((id = CommonDefHandler::LoadSoundFile(name)) <= 0)
		return;

	soundData[soundIdx] = GuiSoundSet::Data(name, id, volume);
}
Example #5
0
void CMapInfo::ReadGlobal()
{
	const LuaTable topTable = parser->GetRoot();

	map.description  = topTable.GetString("description", map.name);
	map.author       = topTable.GetString("author", "");

	map.hardness      = topTable.GetFloat("maphardness", 100.0f);
	map.notDeformable = topTable.GetBool("notDeformable", false);

	map.gravity = topTable.GetFloat("gravity", 130.0f);
	map.gravity = max(0.001f, map.gravity);
	map.gravity = -map.gravity / (GAME_SPEED * GAME_SPEED);

	map.tidalStrength   = topTable.GetFloat("tidalStrength", 0.0f);
	map.maxMetal        = topTable.GetFloat("maxMetal", 0.02f);
	map.extractorRadius = topTable.GetFloat("extractorRadius", 500.0f);

	map.voidWater = topTable.GetBool("voidWater", false);

	// clamps
	if (-0.001f < map.hardness && map.hardness <= 0.0f)
		map.hardness = -0.001f;
	else if (0.0f <= map.hardness && map.hardness < 0.001f)
		map.hardness = 0.001f;
	map.tidalStrength   = max(0.000f, map.tidalStrength);
	map.maxMetal        = max(0.000f, map.maxMetal);
	map.extractorRadius = max(0.000f, map.extractorRadius);
}
Example #6
0
void CMapInfo::ReadSmf()
{
	// SMF specific settings
	const LuaTable mapResTable = parser->GetRoot().SubTable("resources");

	smf.detailTexName      = mapResTable.GetString("detailTex", "");
	smf.specularTexName    = mapResTable.GetString("specularTex", "");
	smf.splatDetailTexName = mapResTable.GetString("splatDetailTex", "");
	smf.splatDistrTexName  = mapResTable.GetString("splatDistrTex", "");

	smf.grassBladeTexName = mapResTable.GetString("grassBladeTex", "");
	smf.grassShadingTexName = mapResTable.GetString("grassShadingTex", "");

	smf.skyReflectModTexName = mapResTable.GetString("skyReflectModTex", "");
	smf.detailNormalTexName = mapResTable.GetString("detailNormalTex", "");
	smf.lightEmissionTexName = mapResTable.GetString("lightEmissionTex", "");

	if (!smf.detailTexName.empty()) {
		smf.detailTexName = "maps/" + smf.detailTexName;
	} else {
		const LuaTable& resGfxMaps = resRoot->SubTable("graphics").SubTable("maps");
		smf.detailTexName = resGfxMaps.GetString("detailtex", "detailtex2.bmp");
		smf.detailTexName = "bitmaps/" + smf.detailTexName;
	}

	if (!smf.specularTexName.empty()) { smf.specularTexName = "maps/" + smf.specularTexName; }
	if (!smf.splatDetailTexName.empty()) { smf.splatDetailTexName = "maps/" + smf.splatDetailTexName; }
	if (!smf.splatDistrTexName.empty()) { smf.splatDistrTexName = "maps/" + smf.splatDistrTexName; }
	if (!smf.grassBladeTexName.empty()) { smf.grassBladeTexName = "maps/" + smf.grassBladeTexName; }
	if (!smf.grassShadingTexName.empty()) { smf.grassShadingTexName = "maps/" + smf.grassShadingTexName; }
	if (!smf.skyReflectModTexName.empty()) { smf.skyReflectModTexName = "maps/" + smf.skyReflectModTexName; }
	if (!smf.detailNormalTexName.empty()) { smf.detailNormalTexName = "maps/" + smf.detailNormalTexName; }
	if (!smf.lightEmissionTexName.empty()) { smf.lightEmissionTexName = "maps/" + smf.lightEmissionTexName; }

	// height overrides
	const LuaTable smfTable = parser->GetRoot().SubTable("smf");

	smf.minHeightOverride = smfTable.KeyExists("minHeight");
	smf.maxHeightOverride = smfTable.KeyExists("maxHeight");
	smf.minHeight = smfTable.GetFloat("minHeight", 0.0f);
	smf.maxHeight = smfTable.GetFloat("maxHeight", 0.0f);


	std::stringstream ss;

	for (int i = 0; /* no test */; i++) {
		ss.str("");
		ss << "smtFileName" << i;

		if (smfTable.KeyExists(ss.str())) {
			smf.smtFileNames.push_back(smfTable.GetString(ss.str(), ".smt"));
		} else {
			break;
		}
	}
}
Example #7
0
void CSyncer::LoadUnits(bool checksum)
{
	unitsLeft = 0;

	LuaParser luaParser("gamedata/defs.lua",
	                    SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP);
	if (!luaParser.Execute()) {
		logOutput.Print("luaParser.Execute() failed");
		return;
	}

	LuaTable rootTable = luaParser.GetRoot().SubTable("UnitDefs");
	if (!rootTable.IsValid()) {
		logOutput.Print("root unitdef table invalid");
		return;
	}

	vector<string> unitDefNames;
	rootTable.GetKeys(unitDefNames);

	const int count = (int)unitDefNames.size();

	for (int i = 0; i < count; ++i) {
		const string& udName =  unitDefNames[i];
		LuaTable udTable = rootTable.SubTable(udName);

		Unit u;

		u.fullName = udTable.GetString("name", udName);

		if (checksum) {
			const string fileName  = udTable.GetString("filename", "");
			const string deadName  = udTable.GetString("corpse", udName + "_dead");
			const string modelName = udTable.GetString("objectname", udName);

			u.fbi    = CalculateCRC(fileName);
			u.cob    = CalculateCRC("scripts/" + udName + ".cob");
			u.model  = CalculateCRC("objects3d/" + modelName); // s3o ?
			u.model += CalculateCRC("objects3d/" + modelName + ".3do");
			u.model += CalculateCRC("objects3d/" +  deadName + ".3do");
		}

		units[udName] = u;
	}

	// map the unitIds
	map<string, Unit>::iterator mit;
	for (mit = units.begin(); mit != units.end(); ++mit) {
		unitIds.push_back(mit->first);
	}

	unitsLeft = count;

	return;
}
Example #8
0
bool SideParser::Load()
{
	dataVec.clear();
	errorLog.clear();

	LuaParser parser("gamedata/sidedata.lua",
	                 SPRING_VFS_MOD_BASE, SPRING_VFS_MOD_BASE);
#if !defined UNITSYNC && !defined DEDICATED
	// this should not be included with unitsync:
	// 1. avoids linkage with LuaSyncedRead
	// 2. ModOptions are not valid during unitsync mod parsing
	parser.GetTable("Spring");
	parser.AddFunc("GetModOptions", LuaSyncedRead::GetModOptions);
	parser.EndTable();
#endif
	if (!parser.Execute()) {
		errorLog = parser.GetErrorLog();
		return false;
	}

	set<string> sideSet;

	const LuaTable root = parser.GetRoot();
	for (int i = 1; /* no-op */; i++) {
		const LuaTable sideTable = root.SubTable(i);
		if (!sideTable.IsValid()) {
			break;
		}

		Data data;
		data.caseName  = sideTable.GetString("name", "");
		data.sideName  = StringToLower(data.caseName);
		data.startUnit = sideTable.GetString("startUnit", "");
		data.startUnit = StringToLower(data.startUnit);

		if (data.sideName.empty()) {
			logOutput.Print("Missing side name: %i", i);
		}
		else if (data.startUnit.empty()) {
			logOutput.Print("Missing side start unit: " + data.sideName);
		}
		else {
			if (sideSet.find(data.sideName) != sideSet.end()) {
				logOutput.Print("Duplicate side name: " + data.sideName);
			}
			else {
				sideSet.insert(data.sideName);
				dataVec.push_back(data);
			}
		}
	}
	return true;
}
CGroundDecalHandler::CGroundDecalHandler(void)
{
	drawDecals = false;
	decalLevel = std::max(0, configHandler->Get("GroundDecals", 1));
	groundScarAlphaFade = configHandler->Get("GroundScarAlphaFade", 0);

	if (decalLevel == 0) {
		return;
	}

	drawDecals = true;

	unsigned char* buf=new unsigned char[512*512*4];
	memset(buf,0,512*512*4);

	LuaParser resourcesParser("gamedata/resources.lua",
	                          SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP);
	if (!resourcesParser.Execute()) {
		logOutput.Print(resourcesParser.GetErrorLog());
	}

	const LuaTable scarsTable = resourcesParser.GetRoot().SubTable("graphics").SubTable("scars");
	LoadScar("bitmaps/" + scarsTable.GetString(2, "scars/scar2.bmp"), buf, 0,   0);
	LoadScar("bitmaps/" + scarsTable.GetString(3, "scars/scar3.bmp"), buf, 256, 0);
	LoadScar("bitmaps/" + scarsTable.GetString(1, "scars/scar1.bmp"), buf, 0,   256);
	LoadScar("bitmaps/" + scarsTable.GetString(4, "scars/scar4.bmp"), buf, 256, 256);

	glGenTextures(1, &scarTex);
	glBindTexture(GL_TEXTURE_2D, scarTex);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
//	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
//	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glBuildMipmaps(GL_TEXTURE_2D,GL_RGBA8 ,512, 512, GL_RGBA, GL_UNSIGNED_BYTE, buf);

	scarFieldX=gs->mapx/32;
	scarFieldY=gs->mapy/32;
	scarField=new std::set<Scar*>[scarFieldX*scarFieldY];

	lastTest=0;
	maxOverlap=decalLevel+1;

	delete[] buf;

	if (shadowHandler->canUseShadows) {
		decalVP    = LoadVertexProgram("ARB/GroundDecals.vp");
		decalFPsmf = LoadFragmentProgram("ARB/GroundDecalsSMF.fp");
		decalFPsm3 = LoadFragmentProgram("ARB/GroundDecalsSM3.fp");
	}
}
Example #10
0
void CMapInfo::ReadSmf()
{
	// SMF specific settings
	const LuaTable mapResTable = mapRoot->SubTable("resources");
	smf.detailTexName = mapResTable.GetString("detailTex", "");
	if (!smf.detailTexName.empty()) {
		smf.detailTexName = "maps/" + smf.detailTexName;
	}
	else {
		const LuaTable resGfxMaps = resRoot->SubTable("graphics").SubTable("maps");
		smf.detailTexName = resGfxMaps.GetString("detailtex", "detailtex2.bmp");
		smf.detailTexName = "bitmaps/" + smf.detailTexName;
	}
}
Example #11
0
void CMapInfo::ReadTerrainTypes()
{
	const LuaTable terrTypeTable =
		mapRoot->SubTable("terrainTypes");

	for (int tt = 0; tt < 256; tt++) {
		TerrainType& terrType = terrainTypes[tt];
		const LuaTable terrain = terrTypeTable.SubTable(tt);
		terrType.name          = terrain.GetString("name", "Default");
		terrType.hardness      = terrain.GetFloat("hardness",   1.0f);
		terrType.receiveTracks = terrain.GetBool("receiveTracks", true);
		const LuaTable moveTable = terrain.SubTable("moveSpeeds");
		terrType.tankSpeed  = moveTable.GetFloat("tank",  1.0f);
		terrType.kbotSpeed  = moveTable.GetFloat("kbot",  1.0f);
		terrType.hoverSpeed = moveTable.GetFloat("hover", 1.0f);
		terrType.shipSpeed  = moveTable.GetFloat("ship",  1.0f);

		// clamps
		terrType.hardness   = max(0.0f, terrType.hardness);
		terrType.tankSpeed  = max(0.0f, terrType.tankSpeed);
		terrType.kbotSpeed  = max(0.0f, terrType.kbotSpeed);
		terrType.hoverSpeed = max(0.0f, terrType.hoverSpeed);
		terrType.shipSpeed  = max(0.0f, terrType.shipSpeed);
	}
}
Example #12
0
void CAssParser::SetPieceParentName(
	SAssPiece* piece,
	const S3DModel* model,
	const aiNode* pieceNode,
	const LuaTable& pieceTable,
	ParentNameMap& parentMap
) {
	// Get parent name from metadata or model
	if (pieceTable.KeyExists("parent")) {
		parentMap[piece] = pieceTable.GetString("parent", "");
		return;
	}

	if (pieceNode->mParent == nullptr)
		return;

	if (pieceNode->mParent->mParent != nullptr) {
		// parent is not the root
		parentMap[piece] = std::string(pieceNode->mParent->mName.data);
	} else {
		// parent is the root (which must already exist)
		assert(model->GetRootPiece() != nullptr);
		parentMap[piece] = (model->GetRootPiece())->name;
	}
}
Example #13
0
CDamageArrayHandler::CDamageArrayHandler(LuaParser* defsParser)
{
	#define DEFAULT_ARMORDEF_NAME "default"

	try {
		const LuaTable rootTable = defsParser->GetRoot().SubTable("ArmorDefs");

		if (!rootTable.IsValid())
			throw content_error("Error loading ArmorDefs");

		// GetKeys() sorts the keys, so can not simply push_back before call
		rootTable.GetKeys(armorDefKeys);
		armorDefKeys.insert(armorDefKeys.begin(), DEFAULT_ARMORDEF_NAME);

		armorDefNameIdxMap[DEFAULT_ARMORDEF_NAME] = 0;

		LOG("[%s] number of ArmorDefs: " _STPF_, __FUNCTION__, armorDefKeys.size());

		// expects the following structure, subtables must be in array-format:
		//
		// {"tanks" = {[1] = "supertank", [2] = "megatank"}, "infantry" = {[1] = "dude"}, ...}
		//
		// the old (pre-95.0) <key, value> subtable definitions are no longer supported!
		//
		for (unsigned int armorDefIdx = 1; armorDefIdx < armorDefKeys.size(); armorDefIdx++) {
			const std::string armorDefName = StringToLower(armorDefKeys[armorDefIdx]);

			if (armorDefName == DEFAULT_ARMORDEF_NAME) {
				// ignore, no need to clear entire table
				LOG_L(L_WARNING, "[%s] ArmorDefs: tried to define the \"%s\" armor type!", __FUNCTION__, DEFAULT_ARMORDEF_NAME);
				continue;
			}

			armorDefNameIdxMap[armorDefName] = armorDefIdx;

			const LuaTable armorDefTable = rootTable.SubTable(armorDefKeys[armorDefIdx]);
			const unsigned int numArmorDefEntries = armorDefTable.GetLength();

			for (unsigned int armorDefEntryIdx = 0; armorDefEntryIdx < numArmorDefEntries; armorDefEntryIdx++) {
				const std::string& unitDefName = StringToLower(armorDefTable.GetString(armorDefEntryIdx + 1, ""));
				const auto armorDefTableIt = armorDefNameIdxMap.find(unitDefName);

				if (armorDefTableIt == armorDefNameIdxMap.end()) {
					armorDefNameIdxMap[unitDefName] = armorDefIdx;
					continue;
				}

				LOG_L(L_WARNING,
					"[%s] UnitDef \"%s\" in ArmorDef \"%s\" already belongs to ArmorDef category %d!",
					__FUNCTION__, unitDefName.c_str(), armorDefName.c_str(), armorDefTableIt->second);
			}
		}
	} catch (const content_error&) {
		armorDefNameIdxMap.clear();
		armorDefNameIdxMap[DEFAULT_ARMORDEF_NAME] = 0;

		armorDefKeys.clear();
		armorDefKeys.push_back(DEFAULT_ARMORDEF_NAME);
	}
}
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);
}
Example #15
0
void CPreGame::StartServer(const std::string& setupscript)
{
	assert(!gameServer);
	GameData* startupData = new GameData();
	CGameSetup* setup = new CGameSetup();
	setup->Init(setupscript);

	startupData->SetRandomSeed(static_cast<unsigned>(gu->usRandInt()));
	if (! setup->mapName.empty())
	{
		// would be better to use MapInfo here, but this doesn't work
		LoadMap(setup->mapName); // map into VFS
		std::string mapDefFile;
		const std::string extension = setup->mapName.substr(setup->mapName.length()-3);
		if (extension == "smf")
			mapDefFile = std::string("maps/")+setup->mapName.substr(0,setup->mapName.find_last_of('.'))+".smd";
		else if(extension == "sm3")
			mapDefFile = string("maps/")+setup->mapName;
		else
			throw std::runtime_error("CPreGame::StartServer(): Unknown extension: " + extension);

		MapParser mp(setup->mapName);
		LuaTable mapRoot = mp.GetRoot();
		const std::string mapWantedScript = mapRoot.GetString("script",     "");
		const std::string scriptFile      = mapRoot.GetString("scriptFile", "");

		if (!mapWantedScript.empty()) {
			setup->scriptName = mapWantedScript;
		}
	}
	// here we now the name of the script to use

	CScriptHandler::SelectScript(setup->scriptName);
	std::string scriptWantedMod;
	scriptWantedMod = CScriptHandler::Instance().chosenScript->GetModName();
	if (!scriptWantedMod.empty()) {
		setup->modName = archiveScanner->ModArchiveToModName(scriptWantedMod);
	}
	LoadMod(setup->modName);

	std::string modArchive = archiveScanner->ModNameToModArchive(setup->modName);
	startupData->SetModChecksum(archiveScanner->GetModChecksum(modArchive));

	std::string mapFromScript = CScriptHandler::Instance().chosenScript->GetMapName();
	if (!mapFromScript.empty() &&  setup->mapName != mapFromScript) {
		//TODO unload old map
		LoadMap(mapFromScript, true);
	}

	startupData->SetMapChecksum(archiveScanner->GetMapChecksum(setup->mapName));
	setup->LoadStartPositions();

	good_fpu_control_registers("before CGameServer creation");
	startupData->SetSetup(setup->gameSetupText);
	gameServer = new CGameServer(settings.get(), false, startupData, setup);
	gameServer->AddLocalClient(settings->myPlayerName, SpringVersion::GetFull());
	good_fpu_control_registers("after CGameServer creation");
}
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;
}
Example #17
0
void CAssParser::FindTextures(
	S3DModel* model,
	const aiScene* scene,
	const LuaTable& modelTable,
	const std::string& modelPath,
	const std::string& modelName
) {
	// Assign textures
	// The S3O texture handler uses two textures.
	// The first contains diffuse color (RGB) and teamcolor (A)
	// The second contains glow (R), reflectivity (G) and 1-bit Alpha (A).


	// 1. try to find by name (lowest prioriy)
	if (model->tex1.empty()) model->tex1 = FindTextureByRegex("unittextures/", modelName); // high priority
	if (model->tex1.empty()) model->tex1 = FindTextureByRegex("unittextures/", modelName + "1");
	if (model->tex2.empty()) model->tex2 = FindTextureByRegex("unittextures/", modelName + "2");
	if (model->tex1.empty()) model->tex1 = FindTextureByRegex(modelPath, "tex1");
	if (model->tex2.empty()) model->tex2 = FindTextureByRegex(modelPath, "tex2");
	if (model->tex1.empty()) model->tex1 = FindTextureByRegex(modelPath, "diffuse");
	if (model->tex2.empty()) model->tex2 = FindTextureByRegex(modelPath, "glow");          // low priority


	// 2. gather model-defined textures of first material (medium priority)
	if (scene->mNumMaterials > 0) {
		const unsigned int texTypes[] = {
			aiTextureType_SPECULAR,
			aiTextureType_UNKNOWN,
			aiTextureType_DIFFUSE,
			/*
			// TODO: support these too (we need to allow constructing tex1 & tex2 from several sources)
			aiTextureType_EMISSIVE,
			aiTextureType_HEIGHT,
			aiTextureType_NORMALS,
			aiTextureType_SHININESS,
			aiTextureType_OPACITY,
			*/
		};
		for (unsigned int n = 0; n < (sizeof(texTypes) / sizeof(texTypes[0])); n++) {
			aiString textureFile;

			if (scene->mMaterials[0]->Get(AI_MATKEY_TEXTURE(texTypes[n], 0), textureFile) != aiReturn_SUCCESS)
				continue;

			assert(textureFile.length > 0);
			model->tex1 = FindTexture(textureFile.data, modelPath, model->tex1);
		}
	}


	// 3. try to load from metafile (highest priority)
	model->tex1 = FindTexture(modelTable.GetString("tex1", ""), modelPath, model->tex1);
	model->tex2 = FindTexture(modelTable.GetString("tex2", ""), modelPath, model->tex2);

	model->invertTexYAxis = modelTable.GetBool("fliptextures", true); // Flip texture upside down
	model->invertTexAlpha = modelTable.GetBool("invertteamcolor", true); // Reverse teamcolor levels
}
Example #18
0
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;
}
Example #19
0
void CWeaponDefHandler::LoadSound(const LuaTable& wdTable,
                                  GuiSoundSet& gsound, const string& soundCat)
{
	string name = "";
	float volume = -1.0f;

	if (soundCat == "start") {
		name   = wdTable.GetString("soundStart", "");
		volume = wdTable.GetFloat("soundStartVolume", -1.0f);
	}
	else if (soundCat == "hit") {
		name   = wdTable.GetString("soundHit", "");
		volume = wdTable.GetFloat("soundHitVolume", -1.0f);
	}

	if (name != "") {
		if (!sound->HasSoundItem(name))
		{
			if (name.find(".wav") == string::npos) {
				// .wav extension missing, add it
				name += ".wav";
			}
			const string soundPath = "sounds/" + name;
			CFileHandler sfile(soundPath);
			if (sfile.FileExists()) {
				// only push data if we extracted a valid name
				GuiSoundSet::Data soundData(name, 0, volume);
				gsound.sounds.push_back(soundData);
				int id = sound->GetSoundId(soundPath);
				gsound.setID(0, id);
			}
		}
		else
		{
			GuiSoundSet::Data soundData(name, 0, volume);
			gsound.sounds.push_back(soundData);
			int id = sound->GetSoundId(name);
			gsound.setID(0, id);
		}
	}
}
Example #20
0
void CMapInfo::ReadSmf()
{
	// SMF specific settings
	const LuaTable mapResTable = mapRoot->SubTable("resources");
	smf.detailTexName = mapResTable.GetString("detailTex", "");
	if (!smf.detailTexName.empty()) {
		smf.detailTexName = "maps/" + smf.detailTexName;
	}
	else {
		const LuaTable resGfxMaps = resRoot->SubTable("graphics").SubTable("maps");
		smf.detailTexName = resGfxMaps.GetString("detailtex", "detailtex2.bmp");
		smf.detailTexName = "bitmaps/" + smf.detailTexName;
	}

	// height overrides
	const LuaTable smfTable = mapRoot->SubTable("smf");
	smf.minHeightOverride = smfTable.KeyExists("minHeight");
	smf.maxHeightOverride = smfTable.KeyExists("maxHeight");
	smf.minHeight = smfTable.GetFloat("minHeight", 0.0f);
	smf.maxHeight = smfTable.GetFloat("maxHeight", 0.0f);
}
void SolidObjectDef::ParseCollisionVolume(const LuaTable& table)
{
	collisionVolume = new CollisionVolume(
		table.GetString("collisionVolumeType", ""),
		table.GetFloat3("collisionVolumeScales", ZeroVector),
		table.GetFloat3("collisionVolumeOffsets", ZeroVector)
	);

	// if this unit wants per-piece volumes, make
	// its main collision volume deferent and let
	// it ignore hits
	collisionVolume->SetDefaultToPieceTree(table.GetBool("usePieceCollisionVolumes", false));
	collisionVolume->SetDefaultToFootPrint(table.GetBool("useFootPrintCollisionVolume", false));
	collisionVolume->SetIgnoreHits(collisionVolume->DefaultToPieceTree());
}
Example #22
0
void CAssParser::FindTextures(
	S3DModel* model,
	const aiScene* scene,
	const LuaTable& modelTable,
	const std::string& modelPath,
	const std::string& modelName
) {
	// 1. try to find by name (lowest priority)
	model->texs[0] = FindTextureByRegex("unittextures/", modelName);

	if (model->texs[0].empty()) model->texs[0] = FindTextureByRegex("unittextures/", modelName + "1");
	if (model->texs[1].empty()) model->texs[1] = FindTextureByRegex("unittextures/", modelName + "2");
	if (model->texs[0].empty()) model->texs[0] = FindTextureByRegex(modelPath, "tex1");
	if (model->texs[1].empty()) model->texs[1] = FindTextureByRegex(modelPath, "tex2");
	if (model->texs[0].empty()) model->texs[0] = FindTextureByRegex(modelPath, "diffuse");
	if (model->texs[1].empty()) model->texs[1] = FindTextureByRegex(modelPath, "glow"); // lowest-priority name


	// 2. gather model-defined textures of first material (medium priority)
	if (scene->mNumMaterials > 0) {
		constexpr unsigned int texTypes[] = {
			aiTextureType_SPECULAR,
			aiTextureType_UNKNOWN,
			aiTextureType_DIFFUSE,
			/*
			// TODO: support these too (we need to allow constructing tex1 & tex2 from several sources)
			aiTextureType_EMISSIVE,
			aiTextureType_HEIGHT,
			aiTextureType_NORMALS,
			aiTextureType_SHININESS,
			aiTextureType_OPACITY,
			*/
		};
		for (unsigned int texType: texTypes) {
			aiString textureFile;
			if (scene->mMaterials[0]->Get(AI_MATKEY_TEXTURE(texType, 0), textureFile) != aiReturn_SUCCESS)
				continue;

			assert(textureFile.length > 0);
			model->texs[0] = FindTexture(textureFile.data, modelPath, model->texs[0]);
		}
	}

	// 3. try to load from metafile (highest priority)
	model->texs[0] = FindTexture(modelTable.GetString("tex1", ""), modelPath, model->texs[0]);
	model->texs[1] = FindTexture(modelTable.GetString("tex2", ""), modelPath, model->texs[1]);
}
Example #23
0
void CAssParser::SetPieceParentName(
	SAssPiece* piece,
	const S3DModel* model,
	const aiNode* pieceNode,
	const LuaTable& pieceTable
) {
	// Get parent name from metadata or model
	if (pieceTable.KeyExists("parent")) {
		piece->parentName = pieceTable.GetString("parent", "");
	} else if (pieceNode->mParent != NULL) {
		if (pieceNode->mParent->mParent != NULL) {
			piece->parentName = std::string(pieceNode->mParent->mName.data);
		} else {
			// my parent is the root (which must already exist)
			assert(model->GetRootPiece() != NULL);
			piece->parentName = model->GetRootPiece()->name;
		}
	}
}
Example #24
0
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;
}
Example #25
0
void CMapInfo::ReadAtmosphere()
{
	// MAP\ATMOSPHERE
	const LuaTable atmoTable = mapRoot->SubTable("atmosphere");
	atmosphere_t& atmo = atmosphere;
	atmo.cloudDensity = atmoTable.GetFloat("cloudDensity", 0.5f);
	atmo.minWind      = atmoTable.GetFloat("minWind", 5.0f);
	atmo.maxWind      = atmoTable.GetFloat("maxWind", 25.0f);
	atmo.fogStart     = atmoTable.GetFloat("fogStart", 0.1f);
	atmo.fogColor   = atmoTable.GetFloat3("fogColor", float3(0.7f, 0.7f, 0.8f));
	atmo.skyColor   = atmoTable.GetFloat3("skyColor", float3(0.1f, 0.15f, 0.7f));
	atmo.sunColor   = atmoTable.GetFloat3("sunColor", float3(1.0f, 1.0f, 1.0f));
	atmo.cloudColor = atmoTable.GetFloat3("cloudColor", float3(1.0f, 1.0f, 1.0f));
	atmo.skyBox = atmoTable.GetString("skyBox", "");

	// clamps
	atmo.cloudDensity = max(0.0f, atmo.cloudDensity);
	atmo.maxWind      = max(0.0f, atmo.maxWind);
	atmo.minWind      = max(0.0f, atmo.minWind);
	atmo.minWind      = min(atmo.maxWind, atmo.minWind);
}
Example #26
0
static inline void GetGroundScars(std::unordered_map<std::string, STex>& textures)
{
	LuaParser resourcesParser("gamedata/resources.lua", SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP);
	if (!resourcesParser.Execute()) {
		LOG_L(L_ERROR, "Failed to load resources: %s", resourcesParser.GetErrorLog().c_str());
	}

	const LuaTable scarsTable = resourcesParser.GetRoot().SubTable("graphics").SubTable("scars");

	for (int i = 1; i <= scarsTable.GetLength(); ++i) {
		std::string texName  = scarsTable.GetString(i, IntToString(i, "scars/scar%i.bmp"));
		std::string texName2 = texName + ".dds"; //FIXME
		auto name = IntToString(i);

		try {
			textures[                    name ] = LoadTexture(texName);
			textures[GetExtraTextureName(name)] = LoadTexture(texName2);
		} catch(const content_error& err) {
			LOG_L(L_ERROR, "%s", err.what());
		}
	}
}
Example #27
0
/** Load the messages from gamedata/messages.lua into memory. */
void CMessages::Load()
{
    LuaParser luaParser("gamedata/messages.lua",
                        SPRING_VFS_MOD_BASE, SPRING_VFS_MOD_BASE);
    if (!luaParser.Execute()) {
        // Show parse errors in the infolog.
        logOutput.Print(string("ERROR: messages.lua: ") + luaParser.GetErrorLog());
        return;
    }

    const LuaTable root = luaParser.GetRoot();

    vector<string> labels;
    root.GetKeys(labels);

    for (size_t l = 0; l < labels.size(); l++) {
        const string label = StringToLower(labels[l]);
        const LuaTable msgTable = root.SubTable(label);
        if (!msgTable.IsValid()) {
            continue;
        }
        vector<string> msgs;
        for (int s = 1; true; s++) {
            const string msg = msgTable.GetString(s, "");
            if (msg.empty()) {
                break;
            }
            msgs.push_back(msg);
        }
        if (!msgs.empty()) {
            tr[label] = msgs;
        }
    }

    loaded = true;
}
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);
    }
}
Example #29
0
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;
}
Example #30
0
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;
}