#include #include #include #pragma newdecls required; #define MODEL_SKELETON "models/bots/skeleton_sniper_boss/skeleton_sniper_boss.mdl" #define PLUGIN_VERSION "1.2" bool g_bSpecial[MAXPLAYERS + 1]; bool g_bSkeleton[MAXPLAYERS + 1]; TFTeam g_iOldTeam[MAXPLAYERS + 1]; Handle g_hCvarStompDamage; public Plugin myinfo = { name = "[TF2] Be The Skeleton King!", author = "Pelipoika", description = "Spooky scary skeleton", version = PLUGIN_VERSION, url = "" } public void OnPluginStart() { AddTempEntHook("TFBlood", TempHook); RegAdminCmd("sm_beskeleton", Command_Skeleton, ADMFLAG_ROOT); HookEvent("post_inventory_application", Event_SkeletonDeath); HookEvent("player_death", Event_SkeletonDeath, EventHookMode_Pre); CreateConVar("tf2_beskeleton_version", PLUGIN_VERSION, "Be the Skeleton King version", FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY); g_hCvarStompDamage = CreateConVar("tf2_beskeleton_stompdamage", "120", "Movement speed penalty when carrying a bomb", FCVAR_NOTIFY|FCVAR_DONTRECORD, true, 0.0, true, 1.0); for (int client = 1; client <= MaxClients; client++) { if(client > 0 && client <= MaxClients && IsClientInGame(client)) { g_bSkeleton[client] = false; g_bSpecial[client] = false; g_iOldTeam[client] = TF2_GetClientTeam(client); SDKHook(client, SDKHook_OnTakeDamageAlive, TakeDamage); } } AddNormalSoundHook(SkeletonSH); } public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { CreateNative("BeSkeletonKing_MakeSkeleton", Native_SetSkeleton); CreateNative("BeSkeletonKing_IsSkeleton", Native_IsSkeleton); RegPluginLibrary("beskeleton"); return APLRes_Success; } public Action TempHook(const char[] te_name, const Players[], int numClients, float delay) { int client = TE_ReadNum("entindex"); if (client > 0 && client <= MaxClients && IsClientInGame(client) && g_bSkeleton[client]) { float m_vecOrigin[3]; m_vecOrigin[0] = TE_ReadFloat("m_vecOrigin[0]"); m_vecOrigin[1] = TE_ReadFloat("m_vecOrigin[1]"); m_vecOrigin[2] = TE_ReadFloat("m_vecOrigin[2]"); if(GetEntProp(client, Prop_Send, "m_iTeamNum") == 0) { CreateParticle("spell_skeleton_goop_green", m_vecOrigin); } else { switch(TF2_GetClientTeam(client)) { case TFTeam_Red: CreateParticle("spell_pumpkin_mirv_goop_red", m_vecOrigin); case TFTeam_Blue: CreateParticle("spell_pumpkin_mirv_goop_blue", m_vecOrigin); } } return Plugin_Stop; } return Plugin_Continue; } public void OnMapStart() { PrecacheModel(MODEL_SKELETON); PrecacheSound("misc/halloween/skeleton_break.wav"); } public void OnClientPutInServer(int client) { g_bSkeleton[client] = false; g_bSpecial[client] = false; g_iOldTeam[client] = TFTeam_Spectator; SDKHook(client, SDKHook_OnTakeDamageAlive, TakeDamage); } public Action Command_Skeleton(int client, int args) { char arg1[32], arg2[6]; GetCmdArg(1, arg1, sizeof(arg1)); GetCmdArg(2, arg2, sizeof(arg2)); char target_name[MAX_TARGET_LENGTH]; int target_list[MAXPLAYERS]; int target_count; bool tn_is_ml; if ((target_count = ProcessTargetString( arg1, client, target_list, MAXPLAYERS, 0, target_name, sizeof(target_name), tn_is_ml)) <= 0) { ReplyToTargetError(client, target_count); return Plugin_Handled; } for (int i = 0; i < target_count; i++) { int player = target_list[i]; if(player > 0 && player <= MaxClients && IsClientInGame(player) && IsPlayerAlive(player)) { if(StringToInt(arg2) == 1) MakeSkeleton(player, true); else MakeSkeleton(player); } } return Plugin_Handled; } public Action GetMaxHealth(int client, int &MaxHealth) { if (client > 0 && client <= MaxClients && IsClientInGame(client)) { MaxHealth = 1000; return Plugin_Changed; } return Plugin_Continue; } public Action SetModel(int client, const char[] model) { SetVariantString(model); AcceptEntityInput(client, "SetCustomModel"); SetEntProp(client, Prop_Send, "m_bUseClassAnimations", 1); } stock void MakeSkeleton(int client, bool spectator = false) { if(spectator) { g_iOldTeam[client] = TF2_GetClientTeam(client); SetEntProp(client, Prop_Send, "m_bForcedSkin", 1); SetEntProp(client, Prop_Send, "m_nForcedSkin", 2); SetEntProp(client, Prop_Send, "m_iTeamNum", 0); } SetVariantString("2.0"); AcceptEntityInput(client, "SetModelScale"); SetModel(client, MODEL_SKELETON); TF2_SetPlayerClass(client, TFClass_Sniper, _, false); TF2_RemoveAllWearables(client); TF2_RemoveAllWeapons(client); Handle hWeaponFists = TF2Items_CreateItem(OVERRIDE_ALL); TF2Items_SetClassname(hWeaponFists, "tf_weapon_club"); TF2Items_SetItemIndex(hWeaponFists, 3); TF2Items_SetQuality(hWeaponFists, 6); TF2Items_SetAttribute(hWeaponFists, 0, 15, 0.0); TF2Items_SetAttribute(hWeaponFists, 1, 5, 1.65); TF2Items_SetAttribute(hWeaponFists, 2, 402, 1.0); TF2Items_SetNumAttributes(hWeaponFists, 3); int iEntity = TF2Items_GiveNamedItem(client, hWeaponFists); EquipPlayerWeapon(client, iEntity); CloseHandle(hWeaponFists); SetEntityRenderMode(iEntity, RENDER_TRANSCOLOR); SetEntityRenderColor(iEntity, 255, 255, 255, 0); SetEntProp(iEntity, Prop_Send, "m_fEffects", 16); char anim[16]; Format(anim, 32, "spawn0%i", GetRandomInt(1, 7)); PlayAnimation(client, anim); g_bSpecial[client] = true; SetNextAttack(iEntity, 2.0); SDKHook(client, SDKHook_GetMaxHealth, GetMaxHealth); SetEntProp(client, Prop_Send, "m_iHealth", 1000); g_bSkeleton[client] = true; } stock void PlayAnimation(int client, char[] anim) { TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, view_as({0.0, 0.0, 0.0})); SetEntityRenderMode(client, RENDER_TRANSCOLOR); SetEntityRenderColor(client, 255, 255, 255, 0); SetEntProp(client, Prop_Send, "m_bUseClassAnimations", 0); SetEntityMoveType(client, MOVETYPE_NONE); float vecOrigin[3], vecAngles[3]; GetClientAbsOrigin(client, vecOrigin); GetClientAbsAngles(client, vecAngles); vecAngles[0] = 0.0; int animationentity = CreateEntityByName("prop_dynamic_override"); if(IsValidEntity(animationentity)) { DispatchKeyValueVector(animationentity, "origin", vecOrigin); DispatchKeyValueVector(animationentity, "angles", vecAngles); DispatchKeyValue(animationentity, "model", MODEL_SKELETON); DispatchKeyValue(animationentity, "defaultanim", anim); DispatchSpawn(animationentity); SetEntPropEnt(animationentity, Prop_Send, "m_hOwnerEntity", client); if(GetEntProp(client, Prop_Send, "m_iTeamNum") == 0) SetEntProp(animationentity, Prop_Send, "m_nSkin", GetEntProp(client, Prop_Send, "m_nForcedSkin")); else SetEntProp(animationentity, Prop_Send, "m_nSkin", GetClientTeam(client) - 2); SetEntPropFloat(animationentity, Prop_Send, "m_flModelScale", 2.0); SetVariantString("OnAnimationDone !self:KillHierarchy::0.0:1"); AcceptEntityInput(animationentity, "AddOutput"); HookSingleEntityOutput(animationentity, "OnAnimationDone", OnAnimationDone, true); } } public void OnAnimationDone(const char[] output, int caller, int activator, float delay) { if(IsValidEntity(caller)) { int client = GetEntPropEnt(caller, Prop_Send, "m_hOwnerEntity"); if(client > 0 && client <= MaxClients && IsClientInGame(client) && IsPlayerAlive(client)) { SetEntityMoveType(client, MOVETYPE_WALK); SetEntProp(client, Prop_Send, "m_bUseClassAnimations", 1); SetEntityRenderMode(client, RENDER_TRANSCOLOR); SetEntityRenderColor(client, 255, 255, 255, 255); g_bSpecial[client] = false; } } } public Action OnPlayerRunCmd(int client, int &iButtons, int &iImpulse, float fVel[3], float fAng[3], int &iWeapon) { if (IsPlayerAlive(client)) { if(iButtons & IN_ATTACK2 && !g_bSpecial[client] && g_bSkeleton[client] && GetEntPropEnt(client, Prop_Send, "m_hGroundEntity") != -1) { SetNextAttack(GetPlayerWeaponSlot(client, TFWeaponSlot_Melee), 2.0); PlayAnimation(client, "MELEE_swing3"); g_bSpecial[client] = true; float vecAngles[3], vecOrigin[3]; GetClientAbsAngles(client, vecAngles); GetClientAbsOrigin(client, vecOrigin); vecAngles[0] = 0.0; Handle pack; CreateDataTimer(0.75, Timer_PerformStomp, pack, TIMER_FLAG_NO_MAPCHANGE); WritePackCell(pack, client); WritePackFloat(pack, vecOrigin[0]); WritePackFloat(pack, vecOrigin[1]); WritePackFloat(pack, vecOrigin[2]); WritePackFloat(pack, vecAngles[0]); WritePackFloat(pack, vecAngles[1]); WritePackFloat(pack, vecAngles[2]); } } return Plugin_Continue; } public Action Timer_PerformStomp(Handle timer, Handle pack) { ResetPack(pack); int client = ReadPackCell(pack); float vecAngles[3], vecOrigin[3]; vecOrigin[0] = ReadPackFloat(pack); vecOrigin[1] = ReadPackFloat(pack); vecOrigin[2] = ReadPackFloat(pack); vecAngles[0] = ReadPackFloat(pack); vecAngles[1] = ReadPackFloat(pack); vecAngles[2] = ReadPackFloat(pack); float vForward[3], vLeft[3]; GetAngleVectors(vecAngles, vForward, NULL_VECTOR, NULL_VECTOR); GetAngleVectors(vecAngles, NULL_VECTOR, vLeft, NULL_VECTOR); vecOrigin[0] += (vForward[0] * 55); vecOrigin[1] += (vForward[1] * 55); vecOrigin[2] += (vForward[2] * 55); vecOrigin[0] += (vLeft[0] * -35); vecOrigin[1] += (vLeft[1] * -35); vecOrigin[2] += (vLeft[2] * -35); CreateParticle("bomibomicon_ring", vecOrigin); //The effect actually comes out of his leg VALVE float pos2[3], Vec[3], AngBuff[3]; for(int i = 1; i <= MaxClients; i++) { if(IsClientInGame(i) && IsPlayerAlive(i) && i != client && GetClientTeam(i) != GetClientTeam(client)) { GetClientAbsOrigin(i, pos2); if(GetVectorDistance(vecOrigin, pos2) <= 200.0) { MakeVectorFromPoints(vecOrigin, pos2, Vec); GetVectorAngles(Vec, AngBuff); AngBuff[0] -= 30.0; GetAngleVectors(AngBuff, Vec, NULL_VECTOR, NULL_VECTOR); NormalizeVector(Vec, Vec); ScaleVector(Vec, 500.0); Vec[2] += 250.0; SDKHooks_TakeDamage(i, client, client, GetConVarFloat(g_hCvarStompDamage)); TeleportEntity(i, NULL_VECTOR, NULL_VECTOR, Vec); } } } } public Action TakeDamage(int victim, int &attacker, int &inflictor, float &damage, int &damagetype) { if(victim > 0 && victim <= MaxClients && IsClientInGame(victim) && attacker > 0 && attacker <= MaxClients && IsClientInGame(attacker) && attacker != victim) { if(g_bSkeleton[attacker]) { damage = GetRandomFloat(95.0, 120.0); return Plugin_Changed; } } return Plugin_Continue; } public Action Event_SkeletonDeath(Handle hEvent, char[] name, bool dontBroadcast) { int client = GetClientOfUserId(GetEventInt(hEvent, "userid")); int attacker = GetEventInt(hEvent, "inflictor_entindex"); if(g_bSkeleton[client]) { EmitSoundToAll("misc/halloween/skeleton_break.wav", client); g_bSkeleton[client] = false; g_bSpecial[client] = false; SDKUnhook(client, SDKHook_GetMaxHealth, GetMaxHealth); SetEntityMoveType(client, MOVETYPE_WALK); SetEntProp(client, Prop_Send, "m_bUseClassAnimations", 1); SetEntProp(client, Prop_Send, "m_bForcedSkin", 0); if(GetEntProp(client, Prop_Send, "m_iTeamNum") == 0) { SetEntProp(client, Prop_Send, "m_iTeamNum", g_iOldTeam[client]); g_iOldTeam[client] = TFTeam_Spectator; } SetEntityRenderMode(client, RENDER_TRANSCOLOR); SetEntityRenderColor(client, 255, 255, 255, 255); SetVariantString(""); AcceptEntityInput(client, "SetCustomModel"); SetVariantString("1.0"); AcceptEntityInput(client, "SetModelScale"); float vecOrigin[3]; GetClientAbsOrigin(client, vecOrigin); //Drop a Rare spellbook int spell = CreateEntityByName("tf_spell_pickup"); if(IsValidEntity(spell)) { DispatchKeyValueVector(spell, "origin", vecOrigin); DispatchKeyValueVector(spell, "basevelocity", view_as({0.0, 0.0, 0.0})); DispatchKeyValueVector(spell, "velocity", view_as({0.0, 0.0, 0.0})); DispatchKeyValue(spell, "powerup_model", "models/props_halloween/hwn_spellbook_upright_major.mdl"); DispatchKeyValue(spell, "OnPlayerTouch", "!self,Kill,,0,-1"); DispatchSpawn(spell); SetVariantString("OnUser1 !self:kill::60:1"); AcceptEntityInput(spell, "AddOutput"); AcceptEntityInput(spell, "FireUser1"); SetEntPropEnt(spell, Prop_Send, "m_hOwnerEntity", client); SetEntProp(spell, Prop_Data, "m_nTier", 1); } } if(attacker > 0 && attacker <= MaxClients && IsClientInGame(attacker) && g_bSkeleton[attacker]) { SetEventInt(hEvent, "attacker", 0); SetEventString(hEvent, "weapon", "spellbook_skeleton"); SetEventInt(hEvent, "customkill", 66); SetEventString(hEvent, "weapon_logclassname", "spellbook_skeleton"); } return Plugin_Continue; } public Action SkeletonSH(clients[64], int &numClients, char sample[PLATFORM_MAX_PATH], int &entity, int &channel, float &volume, int &level, int &pitch, int &flags, char soundEntry[PLATFORM_MAX_PATH], int &seed) { if (entity > 0 && entity <= MaxClients && IsClientInGame(entity)) { if (!g_bSkeleton[entity]) return Plugin_Continue; if (StrContains(sample, "vo/sniper", false) != -1) { Format(sample, sizeof(sample), "misc/halloween/skeletons/skelly_giant_0%i.wav", GetRandomInt(1, 3)); PrecacheSound(sample); EmitSoundToAll(sample, entity, channel, level, flags, volume); return Plugin_Changed; } } return Plugin_Continue; } stock void TF2_RemoveAllWearables(int client) { int wearable = -1; while ((wearable = FindEntityByClassname(wearable, "tf_wearable*")) != -1) { if (IsValidEntity(wearable)) { int player = GetEntPropEnt(wearable, Prop_Send, "m_hOwnerEntity"); if (client == player) { TF2_RemoveWearable(client, wearable); } } } while ((wearable = FindEntityByClassname(wearable, "vgui_screen")) != -1) { if (IsValidEntity(wearable)) { int player = GetEntPropEnt(wearable, Prop_Data, "m_hOwnerEntity"); if (client == player) { AcceptEntityInput(wearable, "Kill"); } } } while ((wearable = FindEntityByClassname(wearable, "tf_powerup_bottle")) != -1) { if (IsValidEntity(wearable)) { int player = GetEntPropEnt(wearable, Prop_Send, "m_hOwnerEntity"); if (client == player) { TF2_RemoveWearable(client, wearable); } } } while ((wearable = FindEntityByClassname(wearable, "tf_weapon_spellbook")) != -1) { if (IsValidEntity(wearable)) { int player = GetEntPropEnt(wearable, Prop_Send, "m_hOwnerEntity"); if (client == player) { TF2_RemoveWearable(client, wearable); } } } } stock void SetNextAttack(int weapon, float duration = 0.0) { if (!IsValidEntity(weapon)) return; float next = GetGameTime() + duration; SetEntPropFloat(weapon, Prop_Send, "m_flNextPrimaryAttack", next); SetEntPropFloat(weapon, Prop_Send, "m_flNextSecondaryAttack", next); } stock void CreateParticle(char[] particle, float pos[3]) { int tblidx = FindStringTable("ParticleEffectNames"); char tmp[256]; int count = GetStringTableNumStrings(tblidx); int stridx = INVALID_STRING_INDEX; for(int i = 0; i < count; i++) { ReadStringTable(tblidx, i, tmp, sizeof(tmp)); if(StrEqual(tmp, particle, false)) { stridx = i; break; } } for(int i = 1; i <= GetMaxClients(); i++) { if(!IsValidEntity(i)) continue; if(!IsClientInGame(i)) continue; TE_Start("TFParticleEffect"); TE_WriteFloat("m_vecOrigin[0]", pos[0]); TE_WriteFloat("m_vecOrigin[1]", pos[1]); TE_WriteFloat("m_vecOrigin[2]", pos[2]); TE_WriteNum("m_iParticleSystemIndex", stridx); TE_WriteNum("entindex", -1); TE_WriteNum("m_iAttachType", 5); //Dont associate with any entity TE_SendToClient(i, 0.0); } } public int Native_SetSkeleton(Handle plugin, int args) { MakeSkeleton(GetNativeCell(1)); } public int Native_IsSkeleton(Handle plugin, int args) { return g_bSkeleton[GetNativeCell(1)]; }