mirror of
https://github.com/Jackzmc/sourcemod-plugins.git
synced 2025-05-06 16:43:21 +00:00
Add ability to override attachment point, add hat presets
This commit is contained in:
parent
1c31a1ef46
commit
0f251e7d4f
3 changed files with 325 additions and 39 deletions
245
scripting/include/hats/hat_presets.sp
Normal file
245
scripting/include/hats/hat_presets.sp
Normal file
|
@ -0,0 +1,245 @@
|
|||
#define MODEL_LENGTH 64
|
||||
char SURVIVOR_NAMES[8][] = { "nick", "rochelle", "coach", "ellis", "bill", "zoey", "francis", "louis"};
|
||||
|
||||
enum struct PresetLocation {
|
||||
int survivorSet;
|
||||
float offset[3];
|
||||
float angles[3];
|
||||
}
|
||||
|
||||
enum struct HatPreset {
|
||||
float offset[3];
|
||||
float angles[3];
|
||||
char model[MODEL_LENGTH];
|
||||
char type[32];
|
||||
float size;
|
||||
|
||||
ArrayList locations;
|
||||
|
||||
int Spawn() {
|
||||
PrecacheModel(this.model);
|
||||
int entity = CreateEntityByName(this.type);
|
||||
DispatchKeyValue(entity, "model", this.model);
|
||||
if(HasEntProp(entity, Prop_Send, "m_flModelScale"))
|
||||
SetEntPropFloat(entity, Prop_Send, "m_flModelScale", this.size);
|
||||
if(!DispatchSpawn(entity)) {
|
||||
LogError("Could not spawn entity of type %s model \"%s\"", this.type, this.model);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
int Apply(int client) {
|
||||
int entity = this.Spawn();
|
||||
float offset[3], angles[3];
|
||||
EquipHat(client, entity, this.type, HAT_PRESET);
|
||||
this.GetLocation(client, offset, angles);
|
||||
hatData[client].offset = offset;
|
||||
hatData[client].angles = angles;
|
||||
CreateTimer(0.1, Timer_RemountSimple, GetClientUserId(client));
|
||||
return entity;
|
||||
}
|
||||
|
||||
void GetLocation(int client, float offset[3], float angles[3]) {
|
||||
if(this.locations != null) {
|
||||
int survivorSet = GetEntProp(client, Prop_Send, "m_survivorCharacter");
|
||||
if(survivorSet < 0 || survivorSet > 7) survivorSet = 0;
|
||||
PresetLocation location;
|
||||
for(int i = 0; i < this.locations.Length; i++) {
|
||||
this.locations.GetArray(i, location, sizeof(location));
|
||||
if(location.survivorSet == survivorSet) {
|
||||
offset = location.offset;
|
||||
angles = location.angles;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
offset = this.offset;
|
||||
angles = this.angles;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Action Timer_RemountSimple(Handle h, int userid) {
|
||||
int client = GetClientOfUserId(userid);
|
||||
if(client > 0) {
|
||||
int entity = GetHat(client);
|
||||
if(entity > 0)
|
||||
TeleportEntity(entity, hatData[client].offset, hatData[client].angles, NULL_VECTOR);
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
void LoadPresets() {
|
||||
KeyValues kv = new KeyValues("Presets");
|
||||
|
||||
char sPath[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, sPath, sizeof(sPath), "data/hat_presets.cfg");
|
||||
|
||||
if(!FileExists(sPath) || !kv.ImportFromFile(sPath)) {
|
||||
PrintToServer("[Hats] Missing presets list");
|
||||
delete kv;
|
||||
}
|
||||
StringMap presets = new StringMap();
|
||||
|
||||
kv.GotoFirstSubKey();
|
||||
// char type[32];
|
||||
int count = 0;
|
||||
char name[32];
|
||||
do {
|
||||
kv.GetSectionName(name, sizeof(name));
|
||||
HatPreset preset;
|
||||
// kv.GetString("type", entCfg.type, sizeof(entCfg.type), "prop_physics");
|
||||
kv.GetString("model", preset.model, sizeof(preset.model), "");
|
||||
kv.GetString("type", preset.type, sizeof(preset.type), "prop_dynamic");
|
||||
preset.size = kv.GetFloat("size", 1.0);
|
||||
if(preset.model[0] == '\0') {
|
||||
PrintToServer("[Hats] Warn: Skipping %s, no model", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(kv.JumpToKey("default")) {
|
||||
kv.GetVector("offset", preset.offset, NULL_VECTOR);
|
||||
kv.GetVector("angles", preset.angles, NULL_VECTOR);
|
||||
kv.GoBack();
|
||||
} else {
|
||||
PrintToServer("[Hats] Warn: Missing default for %s", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 7; i++) {
|
||||
if(kv.JumpToKey(SURVIVOR_NAMES[i])) {
|
||||
if(preset.locations == null) {
|
||||
preset.locations = new ArrayList(sizeof(PresetLocation));
|
||||
}
|
||||
PresetLocation location;
|
||||
location.survivorSet = i; // TODO: confirm with l4d/l4d2 modes?
|
||||
kv.GetVector("offset", location.offset, NULL_VECTOR);
|
||||
kv.GetVector("angles", location.angles, NULL_VECTOR);
|
||||
preset.locations.PushArray(location);
|
||||
kv.GoBack();
|
||||
}
|
||||
}
|
||||
|
||||
count++;
|
||||
presets.SetArray(name, preset, sizeof(preset));
|
||||
} while(kv.GotoNextKey(true));
|
||||
kv.GoBack();
|
||||
|
||||
PrintToServer("[Hats] Loaded %d presets", count);
|
||||
|
||||
|
||||
if(g_HatPresets != null) {
|
||||
HatPreset preset;
|
||||
StringMapSnapshot snapshot = g_HatPresets.Snapshot();
|
||||
for(int i = 0; i <= snapshot.Length; i++) {
|
||||
snapshot.GetKey(i, name, sizeof(name));
|
||||
g_HatPresets.GetArray(name, preset, sizeof(preset));
|
||||
if(preset.locations != null) {
|
||||
delete preset.locations;
|
||||
}
|
||||
}
|
||||
delete g_HatPresets;
|
||||
}
|
||||
g_HatPresets = presets;
|
||||
}
|
||||
|
||||
|
||||
Action Command_DoAHatPreset(int client, int args) {
|
||||
AdminId adminId = GetUserAdmin(client);
|
||||
if(cvar_sm_hats_enabled.IntValue == 1) {
|
||||
if(adminId == INVALID_ADMIN_ID) {
|
||||
PrintToChat(client, "[Hats] Hats are for admins only");
|
||||
return Plugin_Handled;
|
||||
} else if(!adminId.HasFlag(Admin_Cheats)) {
|
||||
PrintToChat(client, "[Hats] You do not have permission");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
} else if(cvar_sm_hats_enabled.IntValue == 0) {
|
||||
ReplyToCommand(client, "[Hats] Hats are disabled");
|
||||
return Plugin_Handled;
|
||||
} else if(GetClientTeam(client) != 2 && ~cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_InfectedHats)) {
|
||||
PrintToChat(client, "[Hats] Hats are only available for survivors.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
|
||||
int entity = GetHat(client);
|
||||
if(entity > 0) {
|
||||
if(args > 0) {
|
||||
char arg[64];
|
||||
GetCmdArg(1, arg, sizeof(arg));
|
||||
if(arg[0] == 'v') {
|
||||
ReplyToCommand(client, "\t{");
|
||||
GetEntPropString(entity, Prop_Data, "m_ModelName", arg, sizeof(arg));
|
||||
ReplyToCommand(client, "\t\t\"model\"\t\"%s\"", arg);
|
||||
ReplyToCommand(client, "\t\t\"default\"\n\t\t{");
|
||||
ReplyToCommand(client, "\t\t\t\"origin\"\t\"%f %f %f\"", hatData[client].offset[0], hatData[client].offset[1], hatData[client].offset[2]);
|
||||
ReplyToCommand(client, "\t\t\t\"angles\"\t\"%f %f %f\"", hatData[client].angles[0], hatData[client].angles[1], hatData[client].angles[2]);
|
||||
ReplyToCommand(client, "\t\t}");
|
||||
ReplyToCommand(client, "\t\t\"size\"\t\"%.1f\"", GetEntPropFloat(entity, Prop_Send, "m_flModelScale"));
|
||||
GetEntityClassname(entity, arg, sizeof(arg));
|
||||
ReplyToCommand(client, "\t\t\"type\"\t\"%.1f\"", arg);
|
||||
ReplyToCommand(client, "\t}");
|
||||
ReplyToCommand(client, "Flags: %d", hatData[client].flags);
|
||||
} else {
|
||||
ReplyToCommand(client, "Unknown option");
|
||||
}
|
||||
// ReplyToCommand(client, "CurOffset: %f %f %f", );
|
||||
return Plugin_Handled;
|
||||
}
|
||||
if(HasFlag(client, HAT_PRESET)) {
|
||||
ClearHat(client);
|
||||
RemoveEntity(entity);
|
||||
PrintToChat(client, "[Hats] Cleared your hat preset");
|
||||
hatPresetCookie.Set(client, "");
|
||||
ActivePreset[client][0] = '\0';
|
||||
|
||||
} else {
|
||||
PrintToChat(client, "[Hats] You already have a hat. Clear your hat to apply a preset.");
|
||||
}
|
||||
} else {
|
||||
Menu menu = new Menu(HatPresetHandler);
|
||||
menu.SetTitle("Choose a Hat", client);
|
||||
char id[32];
|
||||
StringMapSnapshot snapshot = g_HatPresets.Snapshot();
|
||||
if(snapshot.Length == 0) {
|
||||
PrintToChat(client, "[Hats] Seems there is no presets...");
|
||||
delete snapshot;
|
||||
return Plugin_Handled;
|
||||
}
|
||||
for(int i = 0; i < snapshot.Length; i++) {
|
||||
snapshot.GetKey(i, id, sizeof(id));
|
||||
menu.AddItem(id, id);
|
||||
}
|
||||
menu.Display(client, 0);
|
||||
delete snapshot;
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
|
||||
int HatPresetHandler(Menu menu, MenuAction action, int param1, int param2) {
|
||||
if (action == MenuAction_Select) {
|
||||
static char info[32];
|
||||
menu.GetItem(param2, info, sizeof(info));
|
||||
HatPreset preset;
|
||||
if(g_HatPresets.GetArray(info, preset, sizeof(preset))) {
|
||||
strcopy(ActivePreset[param1], 32, info);
|
||||
hatPresetCookie.Set(param1, info);
|
||||
preset.Apply(param1);
|
||||
ReplyToCommand(param1, "[Hats] Hat preset \"%s\" applied, enjoy!", info);
|
||||
} else {
|
||||
ReplyToCommand(param1, "Unknown hat preset \"%s\"", info);
|
||||
}
|
||||
} else if (action == MenuAction_End)
|
||||
delete menu;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RestoreActivePreset(int client) {
|
||||
if(ActivePreset[client][0] != '\0') {
|
||||
HatPreset preset;
|
||||
g_HatPresets.GetArray(ActivePreset[client], preset, sizeof(preset));
|
||||
preset.Apply(client);
|
||||
}
|
||||
}
|
|
@ -3,7 +3,8 @@ enum hatFlags {
|
|||
HAT_POCKET = 1,
|
||||
HAT_REVERSED = 2,
|
||||
HAT_COMMANDABLE = 4,
|
||||
HAT_RAINBOW = 8
|
||||
HAT_RAINBOW = 8,
|
||||
HAT_PRESET = 16
|
||||
}
|
||||
enum struct HatData {
|
||||
int entity; // The entity REFERENCE
|
||||
|
@ -24,6 +25,7 @@ enum struct HatData {
|
|||
float rainbowColor[3];
|
||||
int rainbowTicks;
|
||||
bool rainbowReverse;
|
||||
char attachPoint[32];
|
||||
}
|
||||
enum hatFeatures {
|
||||
HatConfig_None = 0,
|
||||
|
@ -38,6 +40,8 @@ enum hatFeatures {
|
|||
}
|
||||
|
||||
HatData hatData[MAXPLAYERS+1];
|
||||
char ActivePreset[MAXPLAYERS+1][32];
|
||||
StringMap g_HatPresets;
|
||||
int lastHatRequestTime[MAXPLAYERS+1];
|
||||
|
||||
#define MAX_FORBIDDEN_CLASSNAMES 13
|
||||
|
@ -61,7 +65,7 @@ char FORBIDDEN_CLASSNAMES[MAX_FORBIDDEN_CLASSNAMES][] = {
|
|||
|
||||
#define MAX_FORBIDDEN_MODELS 1
|
||||
char FORBIDDEN_MODELS[MAX_FORBIDDEN_MODELS][] = {
|
||||
"models/props_vehicles/c130.mdl"
|
||||
"models/props_vehicles/c130.mdl",
|
||||
};
|
||||
|
||||
#define MAX_REVERSE_CLASSNAMES 2
|
||||
|
@ -70,7 +74,7 @@ static char REVERSE_CLASSNAMES[MAX_REVERSE_CLASSNAMES][] = {
|
|||
"func_movelinear"
|
||||
};
|
||||
|
||||
public Action Command_DoAHat(int client, int args) {
|
||||
Action Command_DoAHat(int client, int args) {
|
||||
static char cmdName[8];
|
||||
GetCmdArg(0, cmdName, sizeof(cmdName));
|
||||
|
||||
|
@ -107,8 +111,21 @@ public Action Command_DoAHat(int client, int args) {
|
|||
|
||||
int entity = GetHat(client);
|
||||
if(entity > 0) {
|
||||
if(HasFlag(client, HAT_PRESET)) {
|
||||
PrintToChat(client, "[Hats] Your hat is a preset, use /hatp to remove it.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
char arg[4];
|
||||
GetCmdArg(1, arg, sizeof(arg));
|
||||
if(arg[0] == 'e') {
|
||||
ReplyToCommand(client, "\t\t\"origin\"\t\"%f %f %f\"", hatData[client].offset[0], hatData[client].offset[1], hatData[client].offset[2]);
|
||||
ReplyToCommand(client, "\t\t\"angles\"\t\"%f %f %f\"", hatData[client].angles[0], hatData[client].angles[1], hatData[client].angles[2]);
|
||||
return Plugin_Handled;
|
||||
} else if(arg[0] == 'v') {
|
||||
ReplyToCommand(client, "Flags: %d", hatData[client].flags);
|
||||
// ReplyToCommand(client, "CurOffset: %f %f %f", );
|
||||
return Plugin_Handled;
|
||||
}
|
||||
// int orgEntity = entity;
|
||||
if(HasFlag(client, HAT_REVERSED)) {
|
||||
entity = client;
|
||||
|
@ -421,15 +438,6 @@ public Action Command_DoAHat(int client, int args) {
|
|||
|
||||
char classname[64];
|
||||
GetEntityClassname(entity, classname, sizeof(classname));
|
||||
// Check is pretty redudant as the traceray itself shouldn't grab it, but just in case:
|
||||
if(cvar_sm_hats_blacklist_enabled.BoolValue) {
|
||||
for(int i = 0; i < MAX_FORBIDDEN_CLASSNAMES; i++) {
|
||||
if(StrEqual(FORBIDDEN_CLASSNAMES[i], classname)) {
|
||||
PrintToChat(client, "[Hats] Entity (%s) is a blacklisted entity. Naughty.", classname);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for any class that should always be reversed
|
||||
if(~flags & view_as<int>(HAT_REVERSED)) {
|
||||
|
@ -446,6 +454,8 @@ public Action Command_DoAHat(int client, int args) {
|
|||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Handles consent that a person to be hatted by another player
|
||||
public int HatConsentHandler(Menu menu, MenuAction action, int target, int param2) {
|
||||
if (action == MenuAction_Select) {
|
||||
|
@ -483,7 +493,7 @@ void ClearHats() {
|
|||
}
|
||||
}
|
||||
void ClearHat(int i, bool restore = false) {
|
||||
|
||||
|
||||
int entity = EntRefToEntIndex(hatData[i].entity);
|
||||
int visibleEntity = EntRefToEntIndex(hatData[i].visibleEntity);
|
||||
int modifyEntity = HasFlag(i, HAT_REVERSED) ? i : entity;
|
||||
|
@ -561,6 +571,7 @@ bool CanTarget(int victim) {
|
|||
|
||||
bool IsHatAllowedInSaferoom(int client) {
|
||||
if(L4D_IsMissionFinalMap()) return true;
|
||||
if(HasFlag(client, HAT_PRESET)) return true;
|
||||
char name[32];
|
||||
GetEntityClassname(hatData[client].entity, name, sizeof(name));
|
||||
// Don't allow non-weapons in saferoom
|
||||
|
@ -617,7 +628,7 @@ bool HasFlag(int client, hatFlags flag) {
|
|||
return hatData[client].flags & view_as<int>(flag) != 0;
|
||||
}
|
||||
|
||||
void EquipHat(int client, int entity, const char[] classname = "", int flags = HAT_NONE) {
|
||||
void EquipHat(int client, int entity, const char[] classname = "", int flags = HAT_NONE, const char[] attachPoint = "eyes") {
|
||||
if(HasHat(client))
|
||||
ClearHat(client, true);
|
||||
|
||||
|
@ -633,12 +644,12 @@ void EquipHat(int client, int entity, const char[] classname = "", int flags = H
|
|||
hatData[client].collisionGroup = GetEntProp(modifyEntity, Prop_Send, "m_CollisionGroup");
|
||||
hatData[client].solidType = GetEntProp(modifyEntity, Prop_Send, "m_nSolidType");
|
||||
hatData[client].moveType = GetEntProp(modifyEntity, Prop_Send, "movetype");
|
||||
strcopy(hatData[client].attachPoint, 32, attachPoint);
|
||||
|
||||
if(client <= MaxClients) SDKHook(client, SDKHook_OnTakeDamageAlive, OnTakeDamageAlive);
|
||||
if(entity <= MaxClients) SDKHook(entity, SDKHook_OnTakeDamageAlive, OnTakeDamageAlive);
|
||||
|
||||
if(modifyEntity <= MaxClients) {
|
||||
|
||||
AcceptEntityInput(modifyEntity, "DisableLedgeHang");
|
||||
} else if(cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_FakeHat)) {
|
||||
return;
|
||||
|
@ -672,6 +683,10 @@ void EquipHat(int client, int entity, const char[] classname = "", int flags = H
|
|||
hatData[client].offset[0] = hatData[client].offset[1] = hatData[client].offset[2] = 0.0;
|
||||
hatData[client].angles[0] = hatData[client].angles[1] = hatData[client].angles[2] = 0.0;
|
||||
|
||||
if(flags & view_as<int>(HAT_PRESET)) {
|
||||
hatData[client].flags |= view_as<int>(HAT_PRESET);
|
||||
}
|
||||
|
||||
if(modifyEntity <= MaxClients) {
|
||||
if(HasFlag(client, HAT_REVERSED)) {
|
||||
hatData[client].offset[2] += 7.2;
|
||||
|
@ -732,9 +747,9 @@ void EquipHat(int client, int entity, const char[] classname = "", int flags = H
|
|||
TeleportEntity(modifyEntity, hatData[client].offset, hatData[client].angles, NULL_VECTOR);
|
||||
SetParentAttachment(modifyEntity, "head", true);
|
||||
} else {
|
||||
SetParentAttachment(modifyEntity, "eyes", true);
|
||||
SetParentAttachment(modifyEntity, attachPoint, true);
|
||||
TeleportEntity(modifyEntity, hatData[client].offset, hatData[client].angles, NULL_VECTOR);
|
||||
SetParentAttachment(modifyEntity, "eyes", true);
|
||||
SetParentAttachment(modifyEntity, attachPoint, true);
|
||||
}
|
||||
|
||||
if(HasFlag(client, HAT_COMMANDABLE)) {
|
||||
|
@ -743,17 +758,17 @@ void EquipHat(int client, int entity, const char[] classname = "", int flags = H
|
|||
}
|
||||
} else {
|
||||
SetParent(entity, client);
|
||||
SetParentAttachment(modifyEntity, "eyes", true);
|
||||
SetParentAttachment(modifyEntity, attachPoint, true);
|
||||
TeleportEntity(modifyEntity, hatData[client].offset, hatData[client].angles, NULL_VECTOR);
|
||||
SetParentAttachment(modifyEntity, "eyes", true);
|
||||
SetParentAttachment(modifyEntity, attachPoint, true);
|
||||
}
|
||||
|
||||
if(visibleEntity > 0) {
|
||||
SetParent(visibleEntity, client);
|
||||
SetParentAttachment(visibleEntity, "eyes", true);
|
||||
SetParentAttachment(visibleEntity, attachPoint, true);
|
||||
hatData[client].offset[2] += 10.0;
|
||||
TeleportEntity(visibleEntity, hatData[client].offset, hatData[client].angles, NULL_VECTOR);
|
||||
SetParentAttachment(visibleEntity, "eyes", true);
|
||||
SetParentAttachment(visibleEntity, attachPoint, true);
|
||||
#if defined DEBUG_HAT_SHOW_FAKE
|
||||
L4D2_SetEntityGlow(visibleEntity, L4D2Glow_Constant, 0, 0, color2, false);
|
||||
#endif
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue