It's little strange to see two issues about ff2 in this subforum. The important thing is this subplugin:
Spoiler
HTML Code:
/*
This is the source code of Freak Fortress 2: Kill Icon Changer
Version: 0.5
Description: Changes the kill icon of the bosses. Created especially for Freak Fortress 2,
because VS Saxton Hale changes the icon of the Saxton Hale from shovel to fists, but FF2 doesn't.
Developer(s):
Naydef
*/
#pragma semicolon 1
#include <sourcemod>
#include <tf2>
#include <sdkhooks>
#include <freak_fortress_2>
#include <freak_fortress_2_subplugin>
//Defines
#define PLUGIN_VERSION "0.5"
#define WSLOTS 6
#define Ability "ff2_changekillicon"
//Variables, handles, etc
new Handle:dataPack=INVALID_HANDLE; //This is really stupid way to do sentry support!
public Plugin:myinfo =
{
name = "Freak Fortress 2: Kill Icon Changer",
author = "Naydef",
description = "Subplugin, which change the kill icon of the boss",
version = PLUGIN_VERSION,
url = "http://www.sourcemod.net/"
};
public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
{
if(!IsTF2())
{
strcopy(error, err_max, "This plugin is only for Team Fortress 2. Remove the plugin!");
return APLRes_Failure;
}
return APLRes_Success;
}
public OnPluginStart2()
{
HookEvent("player_death", Event_PlayerDeath, EventHookMode_Pre);
HookEvent("object_destroyed", Event_ObjectDestroy, EventHookMode_Pre);
}
public Action:FF2_OnAbility2(boss, const String:plugin_name[], const String:ability_name[], action)
{
return Plugin_Continue; // Not used
}
public Action:Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
{
new killer=GetClientOfUserId(GetEventInt(event, "attacker"));
if(!FF2_IsFF2Enabled() || !IsValidClient(killer)) return Plugin_Continue;
if(FF2_GetBossIndex(killer)==-1) return Plugin_Continue;
new boss=FF2_GetBossIndex(killer);
if(!FF2_HasAbility(boss, this_plugin_name, Ability)) return Plugin_Continue;
new String:mode[3];
FF2_GetAbilityArgumentString(boss, this_plugin_name, Ability, 1, mode, sizeof(mode));
switch(StringToInt(mode))
{
case 0: //0. Set kill icon by item index of the weapon, used for the kill.
{
new String:buffer[8];
FF2_GetAbilityArgumentString(boss, this_plugin_name, Ability, 2, buffer, sizeof(buffer));
new itemindex1=StringToInt(buffer);
new itemidex2=GetEventInt(event, "weapon_def_index");
if(itemindex1==itemidex2)
{
new String:killicon[32];
FF2_GetAbilityArgumentString(boss, this_plugin_name, Ability, 3, killicon, sizeof(killicon));
SetEventString(event, "weapon", killicon);
}
}
case 1: //1. Set kill icon by specified weapon slot
{
new String:buffer[10];
FF2_GetAbilityArgumentString(boss, this_plugin_name, Ability, 2, buffer, sizeof(buffer));
new slot=StringToInt(buffer);
new killitemindex=GetEventInt(event, "weapon_def_index");
for(new i=0; i<=WSLOTS; i++)
{
new weapon=GetPlayerWeaponSlot(killer, i);
if(IsValidEntity(weapon))
{
if(slot==i)
{
new itemindex=GetEntProp(weapon, Prop_Send, "m_iItemDefinitionIndex");
if(itemindex==killitemindex)
{
new String:killicon[32];
FF2_GetAbilityArgumentString(boss, this_plugin_name, Ability, 3, killicon, sizeof(killicon));
SetEventString(event, "weapon", killicon);
break; // Only the first weapon, matching all criteria
}
}
}
}
}
case 2: //2. Set kill icon by another weapon icon (name)
{
new String:wepname1[32];
new String:wepname2[32];
FF2_GetAbilityArgumentString(boss, this_plugin_name, Ability, 2, wepname1, sizeof(wepname1));
GetEventString(event, "weapon", wepname2, sizeof(wepname2));
if(StrEqual(wepname1, wepname2, false))
{
new String:killicon[32];
FF2_GetAbilityArgumentString(boss, this_plugin_name, Ability, 3, killicon, sizeof(killicon));
SetEventString(event, "weapon", killicon);
}
}
case 3: //3. Always use this kill icon
{
new String:killicon[32];
FF2_GetAbilityArgumentString(boss, this_plugin_name, Ability, 2, killicon, sizeof(killicon));
SetEventString(event, "weapon", killicon);
}
case 4: //4. No kill icon at all
{
SetEventBroadcast(event, false);
//return Plugin_Handled;
}
default:
{
new String:buffer[32];
if(FF2_GetBossSpecial(boss, buffer, sizeof(buffer)))
{
Debug("Error: The first ability argument is invalid. Mode: %i | Boss name: %s", StringToInt(mode), buffer);
LogError("[FF2 Subplugin] Error: The first ability argument is invalid. Mode: %i | Boss name: %s", StringToInt(mode), buffer);
}
else
{
Debug("Error: The first ability argument is invalid. Mode: %i", StringToInt(mode));
LogError("[FF2 Subplugin] Error: The first ability argument is invalid. Mode: %i", StringToInt(mode));
}
}
}
return Plugin_Continue;
}
public OnEntityCreated(entity, const String:classname[])
{
if(!FF2_IsFF2Enabled())
{
return;
}
if(StrContains(classname, "obj_", false)>-1) // obj_dispenser, obj_sentrygun, obj_teleporters and obj_attachment_sapper (You can apply attribute for destroying enemy sappers)
{
PrintToChatAll("Test -1");
SDKHook(entity, SDKHook_SpawnPost, Hook_SpawnPost);
}
}
public Action:Hook_SpawnPost(entity)
{
if(IsValidEntity(entity))
{
PrintToChatAll("Test -1232");
PrintToChatAll("Success: %i", SDKHookEx(entity, SDKHook_OnTakeDamage, Hook_OnTakeDamagePost));
}
return Plugin_Continue;
}
public Hook_OnTakeDamagePost(victim, attacker, inflictor, Float:damage, damagetype, weapon, const Float:damageForce[3], const Float:damagePosition[3])
{
PrintToChatAll("Test 54");
//Predict building destruction!
if(!IsValidClient(attacker))
{
PrintToChatAll("Invalid client? Entity: %i", attacker);
return;
}
PrintToChatAll("Test 1");
if(GetEntProp(victim, Prop_Data, "m_takedamage")<2 || damage<Float:GetEntProp(victim, Prop_Send, "m_iHealth") || FF2_GetBossIndex(GetEntPropEnt(victim, Prop_Send, "m_hBuilder"))!=-1)
{
PrintToChatAll("Test 2");
return;
}
PrintToChatAll("Test 3");
dataPack=CreateDataPack();
WritePackCell(dataPack, EntIndexToEntRef(attacker));
WritePackCell(dataPack, EntIndexToEntRef(victim));
WritePackCell(dataPack, EntIndexToEntRef(inflictor));
}
public Action:Event_ObjectDestroy(Handle:event, const String:name[], bool:dontBroadcast)
{
if(!FF2_IsFF2Enabled())
{
return Plugin_Continue;
}
new killer=GetClientOfUserId(GetEventInt(event, "attacker"));
if(!IsValidClient(killer))
{
return Plugin_Continue;
}
if(FF2_GetBossIndex(killer)==-1) return Plugin_Continue;
new boss=FF2_GetBossIndex(killer);
if(!FF2_HasAbility(boss, this_plugin_name, Ability))
{
return Plugin_Continue;
}
new String:mode[3];
FF2_GetAbilityArgumentString(boss, this_plugin_name, Ability, 1, mode, sizeof(mode));
switch(StringToInt(mode))
{
case 2: //2. Set kill icon by another weapon icon (name)
{
new String:wepname1[32];
new String:wepname2[32];
FF2_GetAbilityArgumentString(boss, this_plugin_name, Ability, 2, wepname1, sizeof(wepname1));
GetEventString(event, "weapon", wepname2, sizeof(wepname2));
if(StrEqual(wepname1, wepname2, false))
{
new String:killicon[32];
FF2_GetAbilityArgumentString(boss, this_plugin_name, Ability, 3, killicon, sizeof(killicon));
SetEventString(event, "weapon", killicon);
}
}
case 3: //3. Always use this kill icon
{
new String:killicon[32];
FF2_GetAbilityArgumentString(boss, this_plugin_name, Ability, 2, killicon, sizeof(killicon));
SetEventString(event, "weapon", killicon);
}
case 4: //4. No kill icon at all
{
SetEventBroadcast(event, false);
//return Plugin_Handled;
}
case 0:
{
if(dataPack==INVALID_HANDLE)
{
LogMessage("Invalid handle? How could this happen?");
PrintToChatAll("Invalid handle? How could this happen?");
return Plugin_Continue;
}
ResetPack(dataPack);
new datakiller=EntRefToEntIndex(ReadPackCell(dataPack));
new datavictim=EntRefToEntIndex(ReadPackCell(dataPack));
new datawepindex=EntRefToEntIndex(ReadPackCell(dataPack));
CloseHandle(dataPack);
dataPack=INVALID_HANDLE;
if(!IsValidEntity(datawepindex))
{
return Plugin_Continue;
}
new String:buffer[8];
FF2_GetAbilityArgumentString(boss, this_plugin_name, Ability, 2, buffer, sizeof(buffer));
new itemindex1=StringToInt(buffer);
new itemidex2=datawepindex;
if(itemindex1==itemidex2)
{
new String:killicon[32];
FF2_GetAbilityArgumentString(boss, this_plugin_name, Ability, 3, killicon, sizeof(killicon));
SetEventString(event, "weapon", killicon);
}
}
case 1:
{
if(dataPack==INVALID_HANDLE)
{
LogMessage("Invalid handle? How could this happen?");
PrintToChatAll("Invalid handle? How could this happen?");
return Plugin_Continue;
}
ResetPack(dataPack);
new datakiller=EntRefToEntIndex(ReadPackCell(dataPack));
new datavictim=EntRefToEntIndex(ReadPackCell(dataPack));
new datawepindex=EntRefToEntIndex(ReadPackCell(dataPack));
CloseHandle(dataPack);
dataPack=INVALID_HANDLE;
new String:buffer[10];
FF2_GetAbilityArgumentString(boss, this_plugin_name, Ability, 2, buffer, sizeof(buffer));
new slot=StringToInt(buffer);
for(new i=0; i<=WSLOTS; i++)
{
new weapon=GetPlayerWeaponSlot(killer, i);
if(IsValidEntity(weapon))
{
if(slot==i)
{
if(weapon==datawepindex)
{
new String:killicon[32];
FF2_GetAbilityArgumentString(boss, this_plugin_name, Ability, 3, killicon, sizeof(killicon));
SetEventString(event, "weapon", killicon);
break; // Only the first weapon, matching all criteria
}
}
}
}
}
default:
{
new String:buffer[32];
if(FF2_GetBossSpecial(boss, buffer, sizeof(buffer)))
{
Debug("Error: The first ability argument is invalid. Mode: %i | Boss name: %s", StringToInt(mode), buffer);
LogError("[FF2 Subplugin] Error: The first ability argument is invalid. Mode: %i | Boss name: %s", StringToInt(mode), buffer);
}
else
{
Debug("Error: The first ability argument is invalid. Mode: %i", StringToInt(mode));
LogError("[FF2 Subplugin] Error: The first ability argument is invalid. Mode: %i", StringToInt(mode));
}
}
}
return Plugin_Continue;
}
/* Stocks */
bool:IsValidClient(client, bool:replaycheck=true)//From Freak Fortress 2
{
if(client<=0 || client>MaxClients)
{
return false;
}
if(!IsClientInGame(client))
{
return false;
}
if(GetEntProp(client, Prop_Send, "m_bIsCoaching"))
{
return false;
}
if(replaycheck)
{
if(IsClientSourceTV(client) || IsClientReplay(client))
{
return false;
}
}
return true;
}
bool:IsTF2()
{
return (GetEngineVersion()==Engine_TF2) ? true : false;
}
I'm hooking every sentry. SDKHook_OnTakeDamagePost is not called for sentries(on my server is not called, why?) and for this reason I use the pre hook. The strage thing is that attacker argument is 7012!!! HOW IS IT POSSIBLE?
The callbacks for OnTakeDamage and OnTakeDamagePost are different.
Callbacks for OnTakeDamage:
Code:
function Action (int victim, int &attacker, int &inflictor, float &damage, int &damagetype);
function Action (int victim, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon, float damageForce[3], float damagePosition[3]);
// DON'T attempt to access 'damagecustom' var if feature status != available
function Action (int victim, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon,
float damageForce[3], float damagePosition[3], int damagecustom);
Callbacks for OnTakeDamagePost:
Code:
function void (int victim, int attacker, int inflictor, float damage, int damagetype);
function void (int victim, int attacker, int inflictor, float damage, int damagetype, int weapon, const float damageForce[3], const float damagePosition[3]);
function void (int victim, int attacker, int inflictor, float damage, int damagetype, int weapon,
const float damageForce[3], const float damagePosition[3], int damagecustom);
The main differences are that, other than victim, OnTakeDamage takes references / non-const arrays while OnTakeDamagePost takes values and const arrays.
So, what you're actually seeing is the reference rather than the value of the reference.
Edit: These are in 1.7 syntax.
__________________
Not currently working on SourceMod plugin development.
Ok, maybe I have little confused while trying different hook types and callbacks. But there is one little problem. OnTakeDamagePost is not called for sentries. Is this normal?
Edit: Yes, I was really confused. But the question stays. OnTakeDamagePost is not called for sentries. Is this normal?