void CCustomExplosionGenerator::ParseExplosionCode( CCustomExplosionGenerator::ProjectileSpawnInfo* psi, int offset, boost::shared_ptr<creg::IType> type, const string& script, string& code) { string::size_type end = script.find(';', 0); string vastr = script.substr(0, end); if (vastr == "dir") { // first see if we can match any keywords // if the user uses a keyword assume he knows that it is put on the right datatype for now code += OP_DIR; boost::uint16_t ofs = offset; code.append((char*) &ofs, (char*) &ofs + 2); } else if (dynamic_cast<creg::BasicType*>(type.get())) { creg::BasicType *bt = (creg::BasicType*)type.get(); if (bt->id != creg::crInt && bt->id != creg::crFloat && bt->id != creg::crUChar && bt->id != creg::crBool) { throw content_error("Projectile properties other than int, float and uchar, are not supported (" + script + ")"); return; } int p = 0; while (p < script.length()) { char opcode; char c; do { c = script[p++]; } while (c == ' '); bool useInt = false; if (c == 'i') opcode = OP_INDEX; else if (c == 'r') opcode = OP_RAND; else if (c == 'd') opcode = OP_DAMAGE; else if (c == 'm') opcode = OP_SAWTOOTH; else if (c == 'k') opcode = OP_DISCRETE; else if (c == 's') opcode = OP_SINE; else if (c == 'y') {opcode = OP_YANK; useInt = true;} else if (c == 'x') {opcode = OP_MULTIPLY; useInt = true;} else if (c == 'a') {opcode = OP_ADDBUFF; useInt = true;} else if (c == 'p') opcode = OP_POW; else if (c == 'q') {opcode = OP_POWBUFF; useInt = true;} else if (isdigit(c) || c == '.' || c == '-') { opcode = OP_ADD; p--; } else { logOutput.Print("[CCEG::ParseExplosionCode] WARNING: unknown op-code \"" + string(1, c) + "\" in \"" + script + "\""); continue; } char* endp; if (!useInt) { const float v = (float)strtod(&script[p], &endp); p += endp - &script[p]; code += opcode; code.append((char*) &v, ((char*) &v) + 4); } else { const int v = std::max(0, std::min(16, (int)strtol(&script[p], &endp, 10))); p += endp - &script[p]; code += opcode; code.append((char*) &v, ((char*) &v) + 4); } } switch (bt->id) { case creg::crInt: code.push_back(OP_STOREI); break; case creg::crBool: code.push_back(OP_STOREI); break; case creg::crFloat: code.push_back(OP_STOREF); break; case creg::crUChar: code.push_back(OP_STOREC); break; default: throw content_error("Explosion script variable is of unsupported type. " "Contact the Spring team to fix this."); break; } boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } else if (dynamic_cast<creg::ObjectInstanceType*>(type.get())) { creg::ObjectInstanceType *oit = (creg::ObjectInstanceType *)type.get(); string::size_type start = 0; for (creg::Class* c = oit->objectClass; c; c=c->base) { for (int a = 0; a < c->members.size(); a++) { string::size_type end = script.find(',', start+1); ParseExplosionCode(psi, offset + c->members [a]->offset, c->members[a]->type, script.substr(start,end-start), code); start = end+1; if (start >= script.length()) { break; } } if (start >= script.length()) { break; } } } else if (dynamic_cast<creg::StaticArrayBaseType*>(type.get())) { creg::StaticArrayBaseType *sat = (creg::StaticArrayBaseType*)type.get(); string::size_type start = 0; for (unsigned int i=0; i < sat->size; i++) { string::size_type end = script.find(',', start+1); ParseExplosionCode(psi, offset + sat->elemSize * i, sat->elemType, script.substr(start, end-start), code); start = end+1; if (start >= script.length()) { break; } } } else { if (type->GetName() == "AtlasedTexture*") { string::size_type end = script.find(';', 0); string texname = script.substr(0, end); void* tex = projectileDrawer->textureAtlas->GetTexturePtr(texname); code += OP_LOADP; code.append((char*)(&tex), ((char*)(&tex)) + sizeof(void*)); code += OP_STOREP; boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } else if (type->GetName() == "GroundFXTexture*") { string::size_type end = script.find(';', 0); string texname = script.substr(0, end); void* tex = projectileDrawer->groundFXAtlas->GetTexturePtr(texname); code += OP_LOADP; code.append((char*)(&tex), ((char*)(&tex)) + sizeof(void*)); code += OP_STOREP; boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } else if (type->GetName() == "CColorMap*") { string::size_type end = script.find(';', 0); string colorstring = script.substr(0, end); void* colormap = CColorMap::LoadFromDefString(colorstring); code += OP_LOADP; code.append((char*)(&colormap), ((char*)(&colormap)) + sizeof(void*)); code += OP_STOREP; boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } else if (type->GetName() == "IExplosionGenerator*") { string::size_type end = script.find(';', 0); string name = script.substr(0, end); void* explgen = explGenHandler->LoadGenerator(name); code += OP_LOADP; code.append((char*)(&explgen), ((char*)(&explgen)) + sizeof(void*)); code += OP_STOREP; boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } } }
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 CCustomExplosionGenerator::ParseExplosionCode( CCustomExplosionGenerator::ProjectileSpawnInfo* psi, const int offset, const boost::shared_ptr<creg::IType> type, const string& script, string& code) { // strtod&co expect C-style strings with NULLs, // c_str() is guaranteed to be NULL-terminated // (whether .data() == .c_str() depends on the // implementation of std::string) const char* scriptStr = script.c_str(); string::size_type end = script.find(';', 0); string vastr = script.substr(0, end); if (vastr == "dir") { // first see if we can match any keywords // if the user uses a keyword assume he knows that it is put on the right datatype for now code += OP_DIR; boost::uint16_t ofs = offset; code.append((char*) &ofs, (char*) &ofs + 2); } else if (dynamic_cast<creg::BasicType*>(type.get())) { const creg::BasicType* basicType = (creg::BasicType*) type.get(); const bool legalType = (basicType->id == creg::crInt ) || (basicType->id == creg::crFloat) || (basicType->id == creg::crUChar) || (basicType->id == creg::crBool ); if (!legalType) { throw content_error("[CCEG::ParseExplosionCode] projectile type-properties other than int, float, uchar, or bool are not supported (" + script + ")"); return; } int p = 0; while (p < script.length()) { char opcode = OP_END; char c = script[p++]; // consume whitespace if (c == ' ') continue; bool useInt = false; if (c == 'i') opcode = OP_INDEX; else if (c == 'r') opcode = OP_RAND; else if (c == 'd') opcode = OP_DAMAGE; else if (c == 'm') opcode = OP_SAWTOOTH; else if (c == 'k') opcode = OP_DISCRETE; else if (c == 's') opcode = OP_SINE; else if (c == 'p') opcode = OP_POW; else if (c == 'y') { opcode = OP_YANK; useInt = true; } else if (c == 'x') { opcode = OP_MULTIPLY; useInt = true; } else if (c == 'a') { opcode = OP_ADDBUFF; useInt = true; } else if (c == 'q') { opcode = OP_POWBUFF; useInt = true; } else if (isdigit(c) || c == '.' || c == '-') { opcode = OP_ADD; p--; } else { const char* fmt = "[CCEG::ParseExplosionCode] unknown op-code \"%c\" in \"%s\" at index %d"; LOG_L(L_WARNING, fmt, c, scriptStr, p); continue; } // be sure to exit cleanly if there are no more operators or operands if (p >= script.size()) continue; char* endp = NULL; if (!useInt) { const float v = (float)strtod(&scriptStr[p], &endp); p += (endp - &scriptStr[p]); code += opcode; code.append((char*) &v, ((char*) &v) + 4); } else { const int v = std::max(0, std::min(16, (int)strtol(&scriptStr[p], &endp, 10))); p += (endp - &scriptStr[p]); code += opcode; code.append((char*) &v, ((char*) &v) + 4); } } switch (basicType->id) { case creg::crInt: code.push_back(OP_STOREI); break; case creg::crBool: code.push_back(OP_STOREI); break; case creg::crFloat: code.push_back(OP_STOREF); break; case creg::crUChar: code.push_back(OP_STOREC); break; default: break; } boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } else if (dynamic_cast<creg::ObjectInstanceType*>(type.get())) { creg::ObjectInstanceType *oit = (creg::ObjectInstanceType *)type.get(); string::size_type start = 0; for (creg::Class* c = oit->objectClass; c; c=c->base) { for (int a = 0; a < c->members.size(); a++) { string::size_type end = script.find(',', start+1); ParseExplosionCode(psi, offset + c->members [a]->offset, c->members[a]->type, script.substr(start,end-start), code); start = end+1; if (start >= script.length()) { break; } } if (start >= script.length()) { break; } } } else if (dynamic_cast<creg::StaticArrayBaseType*>(type.get())) { creg::StaticArrayBaseType *sat = (creg::StaticArrayBaseType*)type.get(); string::size_type start = 0; for (unsigned int i=0; i < sat->size; i++) { string::size_type end = script.find(',', start+1); ParseExplosionCode(psi, offset + sat->elemSize * i, sat->elemType, script.substr(start, end-start), code); start = end+1; if (start >= script.length()) { break; } } } else { if (type->GetName() == "AtlasedTexture*") { string::size_type end = script.find(';', 0); string texname = script.substr(0, end); // this memory is managed by textureAtlas (CTextureAtlas) void* tex = projectileDrawer->textureAtlas->GetTexturePtr(texname); code += OP_LOADP; code.append((char*)(&tex), ((char*)(&tex)) + sizeof(void*)); code += OP_STOREP; boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } else if (type->GetName() == "GroundFXTexture*") { string::size_type end = script.find(';', 0); string texname = script.substr(0, end); // this memory is managed by groundFXAtlas (CTextureAtlas) void* tex = projectileDrawer->groundFXAtlas->GetTexturePtr(texname); code += OP_LOADP; code.append((char*)(&tex), ((char*)(&tex)) + sizeof(void*)); code += OP_STOREP; boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } else if (type->GetName() == "CColorMap*") { string::size_type end = script.find(';', 0); string colorstring = script.substr(0, end); // gets stored and deleted at game end from inside CColorMap void* colormap = CColorMap::LoadFromDefString(colorstring); code += OP_LOADP; code.append((char*)(&colormap), ((char*)(&colormap)) + sizeof(void*)); code += OP_STOREP; boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } else if (type->GetName() == "IExplosionGenerator*") { string::size_type end = script.find(';', 0); string name = script.substr(0, end); IExplosionGenerator* explGen = explGenHandler->LoadGenerator(name); spawnExplGens.push_back(explGen); // these will be unloaded in ~CCustomExplosionGenerator() void* explGenRaw = (void*) explGen; code += OP_LOADP; code.append((char*)(&explGenRaw), ((char*)(&explGenRaw)) + sizeof(void*)); code += OP_STOREP; boost::uint16_t ofs = offset; code.append((char*)&ofs, (char*)&ofs + 2); } } }