/* Plugin Template generated by Pawn Studio */ #include #include #include #include new String:clientParticle[MAXPLAYERS+1][256]; new Handle:hProjectileArray[56]; new curSecCount=-1; new Handle:hDb = INVALID_HANDLE; public Plugin:myinfo = { name = "ClientParticles", author = "Chefe", description = "Set your own particle effect", version = "1.0", url = "oktoberfest-servers.net" } public OnPluginStart() { RegConsoleCmd("sm_particles", Command_Particles, "Set your own custom projectile particle effects"); HookEvent("player_changeclass", Event_PlayerChangeClass); InitDB(); ProcessConfigFile("configs/clientparticles.cfg"); } InitDB() { SQL_TConnect(InitDBHnld, "clientparticles"); } public InitDBHnld(Handle:owner, Handle:hndl, const String:error[], any:data) { if (hndl == INVALID_HANDLE) { SetFailState("Database connection failed: %s", error); } else { hDb = hndl; } } public Event_PlayerChangeClass(Handle:event,const String:name[],bool:dontBroadcast) { new client = GetClientOfUserId(GetEventInt(event, "userid")); PrintToServer("Class changed, update requested"); CreateTimer(0.5, Timer_Update, client); } public Action:Timer_Update(Handle:timer, any:client) { updateClientParticle(client); } stock ProcessConfigFile(const String:file[]) { new String:sConfigFile[PLATFORM_MAX_PATH]; BuildPath(Path_SM, sConfigFile, sizeof(sConfigFile), file); if (!FileExists(sConfigFile)) { /** Config file doesn't exists, stop the plugin */ LogError("[CP] Plugin startup failed! Could not find file %s", sConfigFile); SetFailState("Could not find file %s", sConfigFile); } else if (!ParseConfigFile(sConfigFile)) { /** Couldn't parse the file, stop the plugin */ LogError("[SM] Plugin is not running! Failed to parse %s", sConfigFile); SetFailState("Parse error on file %s", sConfigFile); } } stock bool:ParseConfigFile(const String:file[]) { new Handle:hParser = SMC_CreateParser(); new String:error[128]; new line = 0; new col = 0; /** Define the parser functions */ SMC_SetReaders(hParser, Config_NewSection, Config_KeyValue, Config_EndSection); SMC_SetParseEnd(hParser, Config_End); /** Parse the file and get the result */ new SMCError:result = SMC_ParseFile(hParser, file, line, col); CloseHandle(hParser); if (result != SMCError_Okay) { SMC_GetErrorString(result, error, sizeof(error)); LogError("%s on line %d, col %d of %s", error, line, col, file); } return (result == SMCError_Okay); } public SMCResult:Config_NewSection(Handle:parser, const String:section[], bool:quotes) { if (StrEqual(section, "projectiles")) { return SMCParse_Continue; } curSecCount++; hProjectileArray[curSecCount] = CreateArray(32, 0); PushArrayString(hProjectileArray[curSecCount], section); return SMCParse_Continue; } public SMCResult:Config_KeyValue(Handle:parser, const String:key[], const String:value[], bool:key_quotes, bool:value_quotes) { if(StrEqual(key, "display", false)) { PushArrayString(hProjectileArray[curSecCount], value); PrintToServer("[PC_DEBUG] New value %s to key %s to array %i", value, key, curSecCount); } else if (StrEqual(key, "effect", false)) { new index = PushArrayString(hProjectileArray[curSecCount], value); PrintToServer("[PC_DEBUG] New value %s to key %s to array %i with index %i", value, key, curSecCount, index); } return SMCParse_Continue; } public SMCResult:Config_EndSection(Handle:parser) { return SMCParse_Continue; } public Config_End(Handle:parser, bool:halted, bool:failed) { if (failed) { SetFailState("Plugin configuration error"); } } public Action:Command_Particles(client, args) { new Handle:menu = CreateMenu(Menu_Particles); SetMenuTitle(menu, "Choose the projectile"); for (new i=0; i<32; i++) { if (hProjectileArray[i] != INVALID_HANDLE && hProjectileArray[i]) { new String:projectileName[256]; GetArrayString(hProjectileArray[i], 0, projectileName, sizeof(projectileName)); new String:displayName[256]; GetArrayString(hProjectileArray[i], 1, displayName, sizeof(displayName)); //new String:secNum[5]; //IntToString(i, secNum, sizeof(secNum)); AddMenuItem(menu, projectileName, displayName); } } SetMenuExitButton(menu, true); DisplayMenu(menu, client, MENU_TIME_FOREVER); return Plugin_Handled; } public Menu_Particles(Handle:menu, MenuAction:action, param1, param2) { /* If an option was selected, tell the client about the item. */ if (action == MenuAction_Select) { new String:projectile[256]; GetMenuItem(menu, param2, projectile, sizeof(projectile)); new Handle:effectmenu = CreateMenu(Menu_Effect); SetMenuTitle(effectmenu, "Choose your effect"); new client = param1; for (new a=0; a<32; a++) { if (hProjectileArray[a] != INVALID_HANDLE && hProjectileArray[a]) { new String:projectileName[256]; GetArrayString(hProjectileArray[a], 0, projectileName, sizeof(projectileName)); if (StrEqual(projectile, projectileName)) { for (new g=1; g<=(GetArraySize(hProjectileArray[a])-2); g++) { new String:myNewItem[256]; GetArrayString(hProjectileArray[a], g+1, myNewItem, sizeof(myNewItem)); if (!StrEqual(myNewItem, NULL_STRING)) { AddMenuItem(effectmenu, myNewItem, myNewItem); } } break; } } } // Pass projetile to menu as item for next menu handler AddMenuItem(effectmenu, projectile, "", ITEMDRAW_IGNORE); SetMenuExitButton(effectmenu, true); DisplayMenu(effectmenu, client, MENU_TIME_FOREVER); } /* If the menu was cancelled, print a message to the server about it. */ else if (action == MenuAction_Cancel) { } /* If the menu has ended, destroy it */ else if (action == MenuAction_End) { CloseHandle(menu); } } public Menu_Effect(Handle:menu, MenuAction:action, param1, param2) { /* If an option was selected, tell the client about the item. */ if (action == MenuAction_Select) { new String:effect[256]; GetMenuItem(menu, param2, effect, sizeof(effect)); new String:projectile[256]; GetMenuItem(menu, GetMenuItemCount(menu)-1, projectile, sizeof(projectile)); PrintToChat(param1, "[CP] Effect: %s", effect); new String:steamid[256]; GetClientAuthString(param1, steamid, sizeof(steamid)); new String:sqlstr[1024]; Format(sqlstr, sizeof(sqlstr), "SELECT particle FROM clientParticles WHERE steamid='%s' AND proj='%s'", steamid, projectile); new Handle:pck = CreateDataPack(); WritePackCell(pck, param1); WritePackString(pck, steamid); WritePackString(pck, projectile); WritePackString(pck, effect); SQL_TQuery(hDb, searchClassEffect, sqlstr, pck); } /* If the menu was cancelled, print a message to the server about it. */ else if (action == MenuAction_Cancel) { } /* If the menu has ended, destroy it */ else if (action == MenuAction_End) { CloseHandle(menu); } } public searchClassEffect(Handle:owner, Handle:hndl, const String:error[], any:data) { if (hndl == INVALID_HANDLE || strlen(error) > 0) { LogError("Query error: %s", error); return; } ResetPack(data); new client = ReadPackCell(data); new String:steamid[256]; ReadPackString(data, steamid, sizeof(steamid)); new String:projectile[256]; ReadPackString(data, projectile, sizeof(projectile)); new String:effect[256]; ReadPackString(data, effect, sizeof(effect)); if (SQL_GetRowCount(hndl)) { while (SQL_FetchRow(hndl)) { new String:name[150]; SQL_FetchString(hndl, 0, name, sizeof(name)) if (strcmp(name, projectile) != 0) { PrintToServer("%s was foudn in db. Update!", name); new String:sqlstr[1024]; Format(sqlstr, sizeof(sqlstr), "UPDATE clientParticles SET particle = '%s' WHERE steamid='%s' AND proj='%s'", effect, steamid, projectile); SQL_TQuery(hDb, noRtnCllbck, sqlstr); CreateTimer(0.5, Timer_Update, client); } } } else { PrintToServer("NOT foudn in db. Insert!"); new String:sqlstr[1024]; Format(sqlstr, sizeof(sqlstr), "INSERT INTO clientParticles (steamid,proj,particle) VALUES ('%s','%s','%s')", steamid, projectile, effect); SQL_TQuery(hDb, noRtnCllbck, sqlstr); CreateTimer(0.5, Timer_Update, client); } } public noRtnCllbck(Handle:owner, Handle:hndl, const String:error[], any:data) { if (hndl == INVALID_HANDLE || strlen(error) > 0) { LogError("Query error: %s", error); return; } } stock updateClientParticle(client) { if (IsClientInGame(client)) { new String:steamid[256]; GetClientAuthString(client, steamid, sizeof(steamid)); new TFClassType:class = TF2_GetPlayerClass(client); new String:proj[256]; if (class == TFClass_Soldier) { proj = "tf_projectile_rocket"; } else if (class == TFClass_DemoMan) { proj = "tf_projectile_pipe_remote"; } else { clientParticle[client] = NULL_STRING; return; } new String:sqlstr[1024]; Format(sqlstr, sizeof(sqlstr), "SELECT particle FROM clientParticles WHERE steamid='%s' AND proj='%s'", steamid, proj); SQL_TQuery(hDb, updateClientParticleDbCallback, sqlstr, client); } } public updateClientParticleDbCallback(Handle:owner, Handle:hndl, const String:error[], any:data) { if (hndl == INVALID_HANDLE || strlen(error) > 0) { LogError("Query error: %s", error); return; } if (SQL_GetRowCount(hndl)) { while (SQL_FetchRow(hndl)) { new String:effect[256]; SQL_FetchString(hndl, 0, effect, sizeof(effect)); clientParticle[data] = effect; PrintToServer("Effect for %N activated: %s", data, effect); } } } public OnEntityCreated(entity, const String:classname[]) { if (StrEqual(classname, "tf_projectile_rocket") || StrEqual(classname, "tf_projectile_pipe_remote")) { SDKHook(entity, SDKHook_Spawn, OnEntitySpawned); } } public OnEntitySpawned(entity) { new client = GetEntPropEnt(entity, Prop_Data, "m_hOwnerEntity"); if (client > 0 && IsClientInGame(client) && !StrEqual(clientParticle[client], NULL_STRING)) { AddParticle(client, entity); } SDKUnhook(entity, SDKHook_Spawn, OnEntitySpawned); } stock AddParticle(client, entity) { new iClusters = 5, iIndex = -1; new String:sParticle[256]; sParticle = clientParticle[client]; decl String:sExtras[64]; for (new i = 0; i < iClusters; i++) { while ((iIndex = SplitString(sParticle, ",", sExtras, sizeof(sExtras))) > -1) { CreateParticle(entity, sExtras, true); strcopy(sParticle, sizeof(sParticle), sParticle[iIndex]); } CreateParticle(entity, sParticle, true); } } stock CreateParticle(iEntity, String:sParticle[], bool:bAttach = false) { new iParticle = CreateEntityByName("info_particle_system"); if (IsValidEdict(iParticle)) { decl Float:fPosition[3]; GetEntPropVector(iEntity, Prop_Send, "m_vecOrigin", fPosition); TeleportEntity(iParticle, fPosition, NULL_VECTOR, NULL_VECTOR); DispatchKeyValue(iParticle, "effect_name", sParticle); if (bAttach) { SetVariantString("!activator"); AcceptEntityInput(iParticle, "SetParent", iEntity, iParticle, 0); } DispatchSpawn(iParticle); ActivateEntity(iParticle); AcceptEntityInput(iParticle, "Start"); } return iParticle; }