mirror of
https://github.com/Jackzmc/sourcemod-plugins.git
synced 2025-05-06 16:33:21 +00:00
Bug fixes
This commit is contained in:
parent
1932d6f02b
commit
34dd2d3bd3
4 changed files with 1483 additions and 1214 deletions
Binary file not shown.
727
scripting/include/hats/hats.sp
Normal file
727
scripting/include/hats/hats.sp
Normal file
|
@ -0,0 +1,727 @@
|
|||
enum hatFlags {
|
||||
HAT_NONE = 0,
|
||||
HAT_POCKET = 1,
|
||||
HAT_REVERSED = 2,
|
||||
HAT_COMMANDABLE = 4,
|
||||
HAT_RAINBOW = 8
|
||||
}
|
||||
enum struct HatData {
|
||||
int entity; // The entity REFERENCE
|
||||
int visibleEntity; // Thee visible entity REF
|
||||
Handle yeetGroundTimer;
|
||||
|
||||
// Original data for entity
|
||||
float orgPos[3];
|
||||
float orgAng[3];
|
||||
float offset[3];
|
||||
float angles[3];
|
||||
int collisionGroup;
|
||||
int solidType;
|
||||
int moveType;
|
||||
|
||||
float scale;
|
||||
int flags;
|
||||
float rainbowColor[3];
|
||||
int rainbowTicks;
|
||||
bool rainbowReverse;
|
||||
}
|
||||
enum hatFeatures {
|
||||
HatConfig_None = 0,
|
||||
HatConfig_PlayerHats = 1,
|
||||
HatConfig_RespectAdminImmunity = 2,
|
||||
HatConfig_FakeHat = 4,
|
||||
HatConfig_NoSaferoomHats = 8,
|
||||
HatConfig_PlayerHatConsent = 16,
|
||||
HatConfig_InfectedHats = 32,
|
||||
HatConfig_ReversedHats = 64,
|
||||
HatConfig_DeleteThrownHats = 128
|
||||
}
|
||||
|
||||
HatData hatData[MAXPLAYERS+1];
|
||||
int lastHatRequestTime[MAXPLAYERS+1];
|
||||
|
||||
#define MAX_FORBIDDEN_CLASSNAMES 13
|
||||
char FORBIDDEN_CLASSNAMES[MAX_FORBIDDEN_CLASSNAMES][] = {
|
||||
"prop_door_rotating_checkpoint",
|
||||
"env_physics_blocker",
|
||||
"env_player_blocker",
|
||||
"func_brush",
|
||||
"func_simpleladder",
|
||||
"prop_door_rotating",
|
||||
"func_button",
|
||||
"func_elevator",
|
||||
"func_button_timed",
|
||||
"func_tracktrain",
|
||||
// "func_movelinear",
|
||||
// "infected",
|
||||
"func_lod",
|
||||
"func_door",
|
||||
"prop_ragdoll"
|
||||
};
|
||||
|
||||
#define MAX_REVERSE_CLASSNAMES 2
|
||||
static char REVERSE_CLASSNAMES[MAX_REVERSE_CLASSNAMES][] = {
|
||||
"infected",
|
||||
"func_movelinear"
|
||||
};
|
||||
|
||||
public Action Command_DoAHat(int client, int args) {
|
||||
int hatter = GetHatter(client);
|
||||
if(hatter > 0) {
|
||||
ClearHat(hatter, HasFlag(hatter, HAT_REVERSED));
|
||||
PrintToChat(hatter, "[Hats] %N has unhatted themselves", client);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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 oldVisible = EntRefToEntIndex(hatData[client].visibleEntity);
|
||||
if(oldVisible > 0) {
|
||||
RemoveEntity(oldVisible);
|
||||
hatData[client].visibleEntity = INVALID_ENT_REFERENCE;
|
||||
}
|
||||
|
||||
int entity = GetHat(client);
|
||||
if(entity > 0) {
|
||||
char arg[4];
|
||||
GetCmdArg(1, arg, sizeof(arg));
|
||||
// int orgEntity = entity;
|
||||
if(HasFlag(client, HAT_REVERSED)) {
|
||||
entity = client;
|
||||
}
|
||||
ClearParent(entity);
|
||||
|
||||
if(arg[0] == 's') {
|
||||
char sizeStr[4];
|
||||
GetCmdArg(2, sizeStr, sizeof(sizeStr));
|
||||
float size = StringToFloat(sizeStr);
|
||||
if(size == 0.0) {
|
||||
ReplyToCommand(client, "[Hats] Invalid size");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
if(HasEntProp(entity, Prop_Send, "m_flModelScale"))
|
||||
SetEntPropFloat(entity, Prop_Send, "m_flModelScale", size);
|
||||
else
|
||||
PrintHintText(client, "Hat does not support scaling");
|
||||
// Change the size of it's parent instead
|
||||
int child = -1;
|
||||
while((child = FindEntityByClassname(child, "*")) != INVALID_ENT_REFERENCE )
|
||||
{
|
||||
int parent = GetEntPropEnt(child, Prop_Data, "m_pParent");
|
||||
if(parent == entity) {
|
||||
if(HasEntProp(child, Prop_Send, "m_flModelScale")) {
|
||||
PrintToConsole(client, "found child %d for %d", child, entity);
|
||||
SetEntPropFloat(child, Prop_Send, "m_flModelScale", size);
|
||||
} else {
|
||||
PrintToChat(client, "Child %d for %d cannot be scaled", child, entity);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Reattach entity:
|
||||
EquipHat(client, entity);
|
||||
return Plugin_Handled;
|
||||
} else if(arg[0] == 'r' && arg[1] == 'a') {
|
||||
SetFlag(client, HAT_RAINBOW);
|
||||
hatData[client].rainbowTicks = 0;
|
||||
hatData[client].rainbowReverse = false;
|
||||
hatData[client].rainbowColor[0] = 0.0;
|
||||
hatData[client].rainbowColor[1] = 255.0;
|
||||
hatData[client].rainbowColor[2] = 255.0;
|
||||
EquipHat(client, entity);
|
||||
ReplyToCommand(client, "Rainbow hats enabled");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
// Re-enable physics and restore collision/solidity
|
||||
AcceptEntityInput(entity, "EnableMotion");
|
||||
SetEntProp(entity, Prop_Send, "m_CollisionGroup", hatData[client].collisionGroup);
|
||||
SetEntProp(entity, Prop_Send, "m_nSolidType", hatData[client].solidType);
|
||||
|
||||
// Remove frozen flag (only "infected" and "witch" are frozen, but just incase:)
|
||||
int flags = GetEntityFlags(entity) & ~FL_FROZEN;
|
||||
SetEntityFlags(entity, flags);
|
||||
|
||||
// Clear visible hats (HatConfig_FakeHat is enabled)
|
||||
int visibleEntity = EntRefToEntIndex(hatData[client].visibleEntity);
|
||||
SDKUnhook(entity, SDKHook_SetTransmit, OnRealTransmit);
|
||||
if(visibleEntity > 0) {
|
||||
SDKUnhook(visibleEntity, SDKHook_SetTransmit, OnVisibleTransmit);
|
||||
RemoveEntity(visibleEntity);
|
||||
hatData[client].visibleEntity = INVALID_ENT_REFERENCE;
|
||||
}
|
||||
// Grant temp god & remove after time
|
||||
tempGod[client] = true;
|
||||
if(client <= MaxClients) {
|
||||
SDKHook(client, SDKHook_OnTakeDamageAlive, OnTakeDamageAlive);
|
||||
CreateTimer(2.0, Timer_RemoveGod, GetClientUserId(client));
|
||||
}
|
||||
if(entity <= MaxClients) {
|
||||
SDKHook(entity, SDKHook_OnTakeDamageAlive, OnTakeDamageAlive);
|
||||
CreateTimer(2.0, Timer_RemoveGod, GetClientUserId(entity));
|
||||
}
|
||||
|
||||
// Restore movement:
|
||||
if(entity <= MaxClients) {
|
||||
// If player, remove roll & and just default to WALK movetype
|
||||
hatData[client].orgAng[2] = 0.0;
|
||||
SetEntityMoveType(entity, MOVETYPE_WALK);
|
||||
} else {
|
||||
// If not player, then just use whatever they were pre-hat
|
||||
SetEntProp(entity, Prop_Send, "movetype", hatData[client].moveType);
|
||||
}
|
||||
|
||||
if(arg[0] == 'y') { // Hat yeeting:
|
||||
GetClientEyeAngles(client, hatData[client].orgAng);
|
||||
GetClientAbsOrigin(client, hatData[client].orgPos);
|
||||
hatData[client].orgPos[2] += 45.0;
|
||||
float ang[3], vel[3];
|
||||
|
||||
// Calculate the angle to throw at
|
||||
GetClientEyeAngles(client, ang);
|
||||
ang[2] = 0.0;
|
||||
if(ang[0] > 0.0) ang[0] = -ang[0];
|
||||
// ang[0] = -45.0;
|
||||
|
||||
// Calculate velocity to throw based on direction
|
||||
vel[0] = Cosine(DegToRad(ang[1])) * GetRandomFloat(1300.0, 1700.0);
|
||||
vel[1] = Sine(DegToRad(ang[1])) * GetRandomFloat(1300.0, 1700.0);
|
||||
vel[2] = GetRandomFloat(700.0, 900.0);
|
||||
if(entity <= MaxClients) {
|
||||
// For players, use the built in fling function
|
||||
TeleportEntity(entity, hatData[client].orgPos, hatData[client].orgAng, NULL_VECTOR);
|
||||
L4D2_CTerrorPlayer_Fling(entity, client, vel);
|
||||
} /*else if(visibleEntity > 0) {
|
||||
PrintToChat(client, "Yeeting fake car...");
|
||||
ClearParent(visibleEntity);
|
||||
|
||||
SetEntProp(visibleEntity, Prop_Send, "movetype", 6);
|
||||
|
||||
AcceptEntityInput(visibleEntity, "EnableMotion");
|
||||
|
||||
TeleportEntity(entity, OUT_OF_BOUNDS, hatData[client].orgAng, NULL_VECTOR);
|
||||
TeleportEntity(visibleEntity, hatData[client].orgPos, hatData[client].orgAng, vel);
|
||||
DataPack pack;
|
||||
CreateDataTimer(4.0, Timer_PropYeetEnd, pack);
|
||||
pack.WriteCell(hatData[client].entity);
|
||||
pack.WriteCell(hatData[client].visibleEntity);
|
||||
pack.WriteCell(hatData[client].collisionGroup);
|
||||
pack.WriteCell(hatData[client].solidType);
|
||||
pack.WriteCell(hatData[client].moveType);
|
||||
hatData[client].visibleEntity = INVALID_ENT_REFERENCE;
|
||||
hatData[client].entity = INVALID_ENT_REFERENCE;
|
||||
} */ else {
|
||||
// For actual props, offset it 35 units above and 80 units infront to reduce collision-incaps and then throw
|
||||
GetHorizontalPositionFromClient(client, 80.0, hatData[client].orgPos);
|
||||
hatData[client].orgPos[2] += 35.0;
|
||||
TeleportEntity(entity, hatData[client].orgPos, hatData[client].orgAng, vel);
|
||||
// Sleep the physics after enoug time for it to most likely have landed
|
||||
if(hatData[client].yeetGroundTimer != null) {
|
||||
delete hatData[client].yeetGroundTimer;
|
||||
}
|
||||
DataPack pack1;
|
||||
hatData[client].yeetGroundTimer = CreateDataTimer(0.5, Timer_GroundKill, pack1, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
|
||||
pack1.WriteCell(hatData[client].entity);
|
||||
pack1.WriteCell(GetClientUserId(client));
|
||||
DataPack pack2;
|
||||
CreateDataTimer(7.7, Timer_PropSleep, pack2);
|
||||
pack2.WriteCell(hatData[client].entity);
|
||||
pack2.WriteCell(GetClientUserId(client));
|
||||
}
|
||||
PrintToChat(client, "[Hats] Yeeted hat");
|
||||
hatData[client].entity = INVALID_ENT_REFERENCE;
|
||||
return Plugin_Handled;
|
||||
} else if(arg[0] == 'c') {
|
||||
float pos[3];
|
||||
// Grabs a cursor position with some checks to prevent placing into (in)visible walls
|
||||
if(GetSmartCursorLocation(client, pos)) {
|
||||
if(CanHatBePlaced(client, pos)) {
|
||||
if(entity <= MaxClients)
|
||||
L4D_WarpToValidPositionIfStuck(entity);
|
||||
hatData[client].orgPos = pos;
|
||||
TeleportEntity(entity, hatData[client].orgPos, hatData[client].orgAng, NULL_VECTOR);
|
||||
PrintToChat(client, "[Hats] Placed hat on cursor.");
|
||||
}
|
||||
} else {
|
||||
PrintToChat(client, "[Hats] Could not find cursor position.");
|
||||
}
|
||||
} else if(arg[0] == 'p' || (entity <= MaxClients && arg[0] != 'r')) {
|
||||
// Place the hat down on the cursor if specified OR if entity is hat
|
||||
float pos[3], ang[3];
|
||||
if(HasFlag(client, HAT_REVERSED)) {
|
||||
// If we are reversed, then place ourselves where our "hatter" is
|
||||
GetClientEyePosition(entity, hatData[client].orgPos);
|
||||
GetClientEyeAngles(entity, hatData[client].orgAng);
|
||||
TeleportEntity(client, hatData[client].orgPos, hatData[client].orgAng, NULL_VECTOR);
|
||||
PrintToChat(entity, "[Hats] Placed hat in front of you.");
|
||||
} else {
|
||||
// If we are normal, then get position infront of us, offset by model size
|
||||
GetClientEyePosition(client, pos);
|
||||
GetClientEyeAngles(client, ang);
|
||||
GetHorizontalPositionFromOrigin(pos, ang, 80.0, pos);
|
||||
ang[0] = 0.0;
|
||||
float mins[3];
|
||||
GetEntPropVector(entity, Prop_Data, "m_vecMins", mins);
|
||||
pos[2] += mins[2];
|
||||
// Find the nearest ground (raytrace bottom->up)
|
||||
FindGround(pos, pos);
|
||||
// Check if the destination is acceptable (not saferooms if enabled)
|
||||
if(CanHatBePlaced(client, pos)) {
|
||||
hatData[client].orgPos = pos;
|
||||
hatData[client].orgAng = ang;
|
||||
TeleportEntity(entity, hatData[client].orgPos, hatData[client].orgAng, NULL_VECTOR);
|
||||
PrintToChat(client, "[Hats] Placed hat in front of you.");
|
||||
}
|
||||
}
|
||||
} else if(arg[0] == 'd') {
|
||||
// Use the new wall editor
|
||||
WallBuilder[client].Reset();
|
||||
WallBuilder[client].entity = EntIndexToEntRef(entity);
|
||||
WallBuilder[client].canScale = false;
|
||||
WallBuilder[client].SetMode(MOVE_ORIGIN);
|
||||
PrintToChat(client, "\x04[Hats] \x01Beta Prop Mover active for \x04%d", entity);
|
||||
} else {
|
||||
PrintToChat(client, "[Hats] Restored hat to its original position.");
|
||||
}
|
||||
|
||||
// Restore the scale pre-hat
|
||||
if(hatData[client].scale > 0 && HasEntProp(entity, Prop_Send, "m_flModelScale"))
|
||||
SetEntPropFloat(entity, Prop_Send, "m_flModelScale", hatData[client].scale);
|
||||
|
||||
// If no other options performed, then restore to original position and remove our reference
|
||||
AcceptEntityInput(entity, "Sleep");
|
||||
TeleportEntity(entity, hatData[client].orgPos, hatData[client].orgAng, NULL_VECTOR);
|
||||
hatData[client].entity = INVALID_ENT_REFERENCE;
|
||||
} else {
|
||||
// Find a new hatable entity
|
||||
int flags = 0;
|
||||
entity = GetLookingEntity(client, Filter_ValidHats);
|
||||
if(entity <= 0) {
|
||||
PrintCenterText(client, "[Hats] No entity found");
|
||||
return Plugin_Handled;
|
||||
} else if(entity == EntRefToEntIndex(WallBuilder[client].entity)) {
|
||||
// Prevent making an entity you editing a hat
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
// Make hat reversed if 'r' passed in
|
||||
if(args > 0) {
|
||||
char arg[4];
|
||||
GetCmdArg(1, arg, sizeof(arg));
|
||||
if(arg[0] == 'r') {
|
||||
flags |= view_as<int>(HAT_REVERSED);
|
||||
}
|
||||
}
|
||||
|
||||
int parent = GetEntPropEnt(entity, Prop_Data, "m_hParent");
|
||||
if(parent > 0 && entity > MaxClients) {
|
||||
PrintToConsole(client, "[Hats] Selected a child entity, selecting parent (child %d -> parent %d)", entity, parent);
|
||||
entity = parent;
|
||||
} else if(entity <= MaxClients) { // Checks for hatting a player entity
|
||||
if(GetClientTeam(entity) != 2 && ~cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_InfectedHats)) {
|
||||
PrintToChat(client, "[Hats] Cannot make enemy a hat... it's dangerous");
|
||||
return Plugin_Handled;
|
||||
} else if(entity == EntRefToEntIndex(WallBuilder[client].entity)) {
|
||||
// Old check left in in case you hatting child entity
|
||||
PrintToChat(client, "[Hats] You are currently editing this entity");
|
||||
return Plugin_Handled;
|
||||
} else if(inSaferoom[client] && cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_NoSaferoomHats)) {
|
||||
PrintToChat(client, "[Hats] Hats are not allowed in the saferoom");
|
||||
return Plugin_Handled;
|
||||
} else if(!IsPlayerAlive(entity) || GetEntProp(entity, Prop_Send, "m_isHangingFromLedge") || L4D_IsPlayerCapped(entity)) {
|
||||
PrintToChat(client, "[Hats] Player is either dead, hanging, or in the process of dying.");
|
||||
return Plugin_Handled;
|
||||
} else if(EntRefToEntIndex(hatData[entity].entity) == client) {
|
||||
PrintToChat(client, "[Hats] Woah you can't be making a black hole, jesus be careful.");
|
||||
return Plugin_Handled;
|
||||
} else if(~cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_PlayerHats)) {
|
||||
PrintToChat(client, "[Hats] Player hats are disabled");
|
||||
return Plugin_Handled;
|
||||
} else if(!CanTarget(entity)) {
|
||||
PrintToChat(client, "[Hats] Player has disabled player hats for themselves.");
|
||||
return Plugin_Handled;
|
||||
} else if(!CanTarget(client)) {
|
||||
PrintToChat(client, "[Hats] Cannot hat a player when you have player hats turned off");
|
||||
return Plugin_Handled;
|
||||
} else if(cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_RespectAdminImmunity)) {
|
||||
AdminId targetAdmin = GetUserAdmin(entity);
|
||||
AdminId clientAdmin = GetUserAdmin(client);
|
||||
if(targetAdmin != INVALID_ADMIN_ID && clientAdmin == INVALID_ADMIN_ID) {
|
||||
PrintToChat(client, "[Hats] Cannot target an admin");
|
||||
return Plugin_Handled;
|
||||
} else if(targetAdmin != INVALID_ADMIN_ID && targetAdmin.ImmunityLevel > clientAdmin.ImmunityLevel) {
|
||||
PrintToChat(client, "[Hats] Cannot target %N, they are immune to you", entity);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
}
|
||||
if(!IsFakeClient(entity) && cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_PlayerHatConsent) && ~flags & view_as<int>(HAT_REVERSED)) {
|
||||
int lastRequestDiff = GetTime() - lastHatRequestTime[client];
|
||||
if(lastRequestDiff < PLAYER_HAT_REQUEST_COOLDOWN) {
|
||||
PrintToChat(client, "[Hats] Player hat under %d seconds cooldown", lastRequestDiff);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
Menu menu = new Menu(HatConsentHandler);
|
||||
menu.SetTitle("%N: Requests to hat you", client);
|
||||
char id[8];
|
||||
Format(id, sizeof(id), "%d|1", GetClientUserId(client));
|
||||
menu.AddItem(id, "Accept");
|
||||
Format(id, sizeof(id), "%d|0", GetClientUserId(client));
|
||||
menu.AddItem(id, "Reject");
|
||||
menu.Display(entity, 12);
|
||||
PrintHintText(client, "Sent hat request to %N", entity);
|
||||
PrintToChat(entity, "[Hats] %N requests to hat you, 1 to Accept, 2 to Reject. Expires in 12 seconds.", client);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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)) {
|
||||
for(int i = 0; i < MAX_REVERSE_CLASSNAMES; i++) {
|
||||
if(StrEqual(REVERSE_CLASSNAMES[i], classname)) {
|
||||
flags |= view_as<int>(HAT_REVERSED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EquipHat(client, entity, classname, flags);
|
||||
}
|
||||
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) {
|
||||
static char info[8];
|
||||
menu.GetItem(param2, info, sizeof(info));
|
||||
static char str[2][8];
|
||||
ExplodeString(info, "|", str, 2, 8, false);
|
||||
int activator = GetClientOfUserId(StringToInt(str[0]));
|
||||
int hatAction = StringToInt(str[1]);
|
||||
if(activator == 0) {
|
||||
ReplyToCommand(target, "Player has disconnected");
|
||||
return 0;
|
||||
} else if(hatAction == 1) {
|
||||
EquipHat(activator, target, "player", 0);
|
||||
} else {
|
||||
ClientCommand(activator, "play player/orch_hit_csharp_short.wav");
|
||||
PrintHintText(activator, "%N refused your request", target);
|
||||
lastHatRequestTime[activator] = GetTime();
|
||||
}
|
||||
} else if (action == MenuAction_End)
|
||||
delete menu;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool IsHatsEnabled(int client) {
|
||||
return (cvar_sm_hats_enabled.IntValue == 1 && GetUserAdmin(client) != INVALID_ADMIN_ID) || cvar_sm_hats_enabled.IntValue == 2
|
||||
}
|
||||
|
||||
void ClearHats() {
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(HasHat(i)) {
|
||||
ClearHat(i, false);
|
||||
}
|
||||
if(IsClientConnected(i) && IsClientInGame(i)) SetEntityMoveType(i, MOVETYPE_WALK);
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
if(visibleEntity > 0) {
|
||||
SDKUnhook(visibleEntity, SDKHook_SetTransmit, OnVisibleTransmit);
|
||||
RemoveEntity(visibleEntity);
|
||||
}
|
||||
if(modifyEntity > 0) {
|
||||
SDKUnhook(modifyEntity, SDKHook_SetTransmit, OnRealTransmit);
|
||||
ClearParent(modifyEntity);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
int flags = GetEntityFlags(entity) & ~FL_FROZEN;
|
||||
SetEntityFlags(entity, flags);
|
||||
// if(HasEntProp(entity, Prop_Send, "m_flModelScale"))
|
||||
// SetEntPropFloat(entity, Prop_Send, "m_flModelScale", 1.0);
|
||||
SetEntProp(modifyEntity, Prop_Send, "m_CollisionGroup", hatData[i].collisionGroup);
|
||||
SetEntProp(modifyEntity, Prop_Send, "m_nSolidType", hatData[i].solidType);
|
||||
SetEntProp(modifyEntity, Prop_Send, "movetype", hatData[i].moveType);
|
||||
|
||||
hatData[i].entity = INVALID_ENT_REFERENCE;
|
||||
hatData[i].visibleEntity = INVALID_ENT_REFERENCE;
|
||||
|
||||
if(HasFlag(i, HAT_REVERSED)) {
|
||||
entity = i;
|
||||
i = modifyEntity;
|
||||
}
|
||||
|
||||
if(entity <= MAXPLAYERS) {
|
||||
AcceptEntityInput(entity, "EnableLedgeHang");
|
||||
}
|
||||
if(restore) {
|
||||
// If hat is a player, override original position to hat wearer's
|
||||
if(entity <= MAXPLAYERS && HasEntProp(i, Prop_Send, "m_vecOrigin")) {
|
||||
GetEntPropVector(i, Prop_Send, "m_vecOrigin", hatData[i].orgPos);
|
||||
}
|
||||
// Restore to original position
|
||||
if(HasFlag(i, HAT_REVERSED)) {
|
||||
TeleportEntity(i, hatData[i].orgPos, hatData[i].orgAng, NULL_VECTOR);
|
||||
} else {
|
||||
TeleportEntity(entity, hatData[i].orgPos, hatData[i].orgAng, NULL_VECTOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool HasHat(int client) {
|
||||
return GetHat(client) > 0;
|
||||
}
|
||||
|
||||
int GetHat(int client) {
|
||||
if(hatData[client].entity == INVALID_ENT_REFERENCE) return -1;
|
||||
int index = EntRefToEntIndex(hatData[client].entity);
|
||||
if(index <= 0) return -1;
|
||||
if(!IsValidEntity(index)) return -1;
|
||||
return index;
|
||||
}
|
||||
|
||||
int GetHatter(int client) {
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(EntRefToEntIndex(hatData[client].entity) == client) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool CanTarget(int victim) {
|
||||
static char buf[2];
|
||||
noHatVictimCookie.Get(victim, buf, sizeof(buf));
|
||||
return StringToInt(buf) == 0;
|
||||
}
|
||||
|
||||
bool IsHatAllowed(int client) {
|
||||
char name[32];
|
||||
GetEntityClassname(hatData[client].entity, name, sizeof(name));
|
||||
// Don't allow non-weapons in saferoom
|
||||
if(StrEqual(name, "prop_physics")) {
|
||||
GetEntPropString(hatData[client].entity, Prop_Data, "m_ModelName", name, sizeof(name));
|
||||
if(StrContains(name, "gnome") != -1) {
|
||||
return true;
|
||||
}
|
||||
PrintToConsole(client, "Dropping hat: prop_physics (%s)", name);
|
||||
return false;
|
||||
}
|
||||
else if(StrEqual(name, "player") || StrContains(name, "weapon_") > -1 || StrContains(name, "upgrade_") > -1) {
|
||||
return true;
|
||||
}
|
||||
PrintToConsole(client, "Dropping hat: %s", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CanHatBePlaced(int client, const float pos[3]) {
|
||||
if(cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_NoSaferoomHats)) {
|
||||
Address nav = L4D_GetNearestNavArea(pos, 200.0);
|
||||
if(nav != Address_Null) {
|
||||
int spawnFlags = L4D_GetNavArea_SpawnAttributes(nav) ;
|
||||
if(spawnFlags & NAV_SPAWN_CHECKPOINT) {
|
||||
PrintToServer("\"%L\" tried to place hat in saferoom, denied.", client);
|
||||
PrintToChat(client, "[Hats] Hats are not allowed in saferoom and has been returned.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetFlag(int client, hatFlags flag) {
|
||||
hatData[client].flags |= view_as<int>(flag);
|
||||
}
|
||||
|
||||
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) {
|
||||
if(HasHat(client))
|
||||
ClearHat(client, true);
|
||||
|
||||
// Player specific tweaks
|
||||
int visibleEntity;
|
||||
if(entity == 0) {
|
||||
ThrowError("Attempted to equip world (client = %d)", client);
|
||||
return;
|
||||
}
|
||||
|
||||
hatData[client].entity = EntIndexToEntRef(entity);
|
||||
int modifyEntity = HasFlag(client, HAT_REVERSED) ? client : entity;
|
||||
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");
|
||||
|
||||
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;
|
||||
// char model[64];
|
||||
// GetEntPropString(entity, Prop_Data, "m_ModelName", model, sizeof(model));
|
||||
// visibleEntity = CreateEntityByName("prop_dynamic");
|
||||
// DispatchKeyValue(visibleEntity, "model", model);
|
||||
// DispatchKeyValue(visibleEntity, "disableshadows", "1");
|
||||
// DispatchSpawn(visibleEntity);
|
||||
// SetEntProp(visibleEntity, Prop_Send, "m_CollisionGroup", 1);
|
||||
// hatData[client].visibleEntity = EntIndexToEntRef(visibleEntity);
|
||||
// SDKHook(visibleEntity, SDKHook_SetTransmit, OnVisibleTransmit);
|
||||
// SDKHook(entity, SDKHook_SetTransmit, OnRealTransmit);
|
||||
}
|
||||
SDKHook(client, SDKHook_OnTakeDamageAlive, OnTakeDamageAlive);
|
||||
// Temp remove the hat to be yoinked by another player
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(i != client && EntRefToEntIndex(hatData[i].entity) == entity) {
|
||||
ClearHat(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Called on initial hat
|
||||
if(classname[0] != '\0') {
|
||||
if(entity <= MaxClients && !IsFakeClient(entity)) {
|
||||
PrintToChat(entity, "[Hats] %N has hatted you, type /hat to dismount at any time", client);
|
||||
}
|
||||
|
||||
// Reset things:
|
||||
hatData[client].flags = 0;
|
||||
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(modifyEntity <= MaxClients) {
|
||||
if(HasFlag(client, HAT_REVERSED)) {
|
||||
hatData[client].offset[2] += 7.2;
|
||||
} else {
|
||||
hatData[client].offset[2] += 4.2;
|
||||
}
|
||||
} else {
|
||||
float mins[3];
|
||||
GetEntPropVector(modifyEntity, Prop_Send, "m_vecMins", mins);
|
||||
hatData[client].offset[2] += mins[2];
|
||||
}
|
||||
|
||||
if(cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_ReversedHats) && flags & view_as<int>(HAT_REVERSED)) {
|
||||
SetFlag(client, HAT_REVERSED);
|
||||
if(StrEqual(classname, "infected") || (entity <= MaxClients && IsFakeClient(entity))) {
|
||||
SetFlag(client, HAT_COMMANDABLE);
|
||||
}
|
||||
PrintToChat(client, "[Hats] Set yourself as %s (%d)'s hat", classname, entity);
|
||||
if(entity <= MaxClients) {
|
||||
LogAction(client, entity, "\"%L\" made themselves \"%L\" (%s)'s hat (%d, %d)", client, entity, classname, entity, visibleEntity);
|
||||
PrintToChat(entity, "[Hats] %N has set themselves as your hat", client);
|
||||
}
|
||||
} else {
|
||||
if(StrEqual(classname, "infected") || StrEqual(classname, "witch")) {
|
||||
int eflags = GetEntityFlags(entity) | FL_FROZEN;
|
||||
SetEntityFlags(entity, eflags);
|
||||
hatData[client].offset[2] = 36.0;
|
||||
}
|
||||
if(entity <= MaxClients)
|
||||
PrintToChat(client, "[Hats] Set %N (%d) as a hat", entity, entity);
|
||||
else
|
||||
PrintToChat(client, "[Hats] Set %s (%d) as a hat", classname, entity);
|
||||
if(entity <= MaxClients)
|
||||
LogAction(client, entity, "\"%L\" picked up \"%L\" (%s) as a hat (%d, %d)", client, entity, classname, entity, visibleEntity);
|
||||
else
|
||||
LogAction(client, -1, "\"%L\" picked up %s as a hat (%d, %d)", client, classname, entity, visibleEntity);
|
||||
}
|
||||
hatData[client].scale = -1.0;
|
||||
|
||||
}
|
||||
AcceptEntityInput(modifyEntity, "DisableMotion");
|
||||
|
||||
// Get the data (position, angle, movement shit)
|
||||
|
||||
GetEntPropVector(modifyEntity, Prop_Send, "m_vecOrigin", hatData[client].orgPos);
|
||||
GetEntPropVector(modifyEntity, Prop_Send, "m_angRotation", hatData[client].orgAng);
|
||||
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");
|
||||
|
||||
|
||||
if(!HasFlag(client, HAT_POCKET)) {
|
||||
// TeleportEntity(entity, EMPTY_ANG, EMPTY_ANG, NULL_VECTOR);
|
||||
if(HasFlag(client, HAT_REVERSED)) {
|
||||
SetParent(client, entity);
|
||||
if(StrEqual(classname, "infected")) {
|
||||
SetParentAttachment(modifyEntity, "head", true);
|
||||
TeleportEntity(modifyEntity, hatData[client].offset, hatData[client].angles, NULL_VECTOR);
|
||||
SetParentAttachment(modifyEntity, "head", true);
|
||||
} else {
|
||||
SetParentAttachment(modifyEntity, "eyes", true);
|
||||
TeleportEntity(modifyEntity, hatData[client].offset, hatData[client].angles, NULL_VECTOR);
|
||||
SetParentAttachment(modifyEntity, "eyes", true);
|
||||
}
|
||||
|
||||
if(HasFlag(client, HAT_COMMANDABLE)) {
|
||||
ChooseRandomPosition(hatData[client].offset);
|
||||
L4D2_CommandABot(entity, client, BOT_CMD_MOVE, hatData[client].offset);
|
||||
}
|
||||
} else {
|
||||
SetParent(entity, client);
|
||||
SetParentAttachment(modifyEntity, "eyes", true);
|
||||
TeleportEntity(modifyEntity, hatData[client].offset, hatData[client].angles, NULL_VECTOR);
|
||||
SetParentAttachment(modifyEntity, "eyes", true);
|
||||
}
|
||||
|
||||
if(visibleEntity > 0) {
|
||||
SetParent(visibleEntity, client);
|
||||
SetParentAttachment(visibleEntity, "eyes", true);
|
||||
hatData[client].offset[2] += 10.0;
|
||||
TeleportEntity(visibleEntity, hatData[client].offset, hatData[client].angles, NULL_VECTOR);
|
||||
SetParentAttachment(visibleEntity, "eyes", true);
|
||||
#if defined DEBUG_HAT_SHOW_FAKE
|
||||
L4D2_SetEntityGlow(visibleEntity, L4D2Glow_Constant, 0, 0, color2, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined DEBUG_HAT_SHOW_FAKE
|
||||
L4D2_SetEntityGlow(modifyEntity, L4D2Glow_Constant, 0, 0, color, false);
|
||||
#endif
|
||||
|
||||
SetEntProp(modifyEntity, Prop_Send, "m_nSolidType", 0);
|
||||
SetEntProp(modifyEntity, Prop_Send, "m_CollisionGroup", 1);
|
||||
SetEntProp(modifyEntity, Prop_Send, "movetype", MOVETYPE_NONE);
|
||||
}
|
||||
}
|
609
scripting/include/hats/walls.sp
Normal file
609
scripting/include/hats/walls.sp
Normal file
|
@ -0,0 +1,609 @@
|
|||
int BUILDER_COLOR[4] = { 0, 255, 0, 235 };
|
||||
int WALL_COLOR[4] = { 255, 0, 0, 235 };
|
||||
float ORIGIN_SIZE[3] = { 2.0, 2.0, 2.0 };
|
||||
|
||||
enum wallMode {
|
||||
INACTIVE = 0,
|
||||
MOVE_ORIGIN,
|
||||
SCALE,
|
||||
FREELOOK
|
||||
}
|
||||
|
||||
ArrayList createdWalls;
|
||||
|
||||
enum struct WallBuilderData {
|
||||
float origin[3];
|
||||
float mins[3];
|
||||
float angles[3];
|
||||
float size[3];
|
||||
wallMode mode;
|
||||
int axis;
|
||||
int snapAngle;
|
||||
int moveSpeed;
|
||||
float moveDistance;
|
||||
int entity;
|
||||
bool canScale;
|
||||
bool hasCollision;
|
||||
|
||||
void Reset(bool initial = false) {
|
||||
this.size[0] = this.size[1] = this.size[2] = 5.0;
|
||||
this.angles[0] = this.angles[1] = this.angles[2] = 0.0;
|
||||
this.axis = 1;
|
||||
this.canScale = true;
|
||||
this.moveDistance = 200.0;
|
||||
this.entity = INVALID_ENT_REFERENCE;
|
||||
this.hasCollision = true;
|
||||
this.CalculateMins();
|
||||
this.SetMode(INACTIVE);
|
||||
if(initial) {
|
||||
this.moveSpeed = 1;
|
||||
this.snapAngle = 30;
|
||||
}
|
||||
}
|
||||
|
||||
void CalculateMins() {
|
||||
this.mins[0] = -this.size[0];
|
||||
this.mins[1] = -this.size[1];
|
||||
this.mins[2] = -this.size[2];
|
||||
}
|
||||
|
||||
void Draw(int color[4], float lifetime, float amplitude = 0.1) {
|
||||
if(!this.canScale && this.entity != INVALID_ENT_REFERENCE) {
|
||||
TeleportEntity(this.entity, this.origin, this.angles, NULL_VECTOR);
|
||||
} else {
|
||||
Effect_DrawBeamBoxRotatableToAll(this.origin, this.mins, this.size, this.angles, g_iLaserIndex, 0, 0, 30, lifetime, 0.4, 0.4, 0, amplitude, color, 0);
|
||||
}
|
||||
Effect_DrawAxisOfRotationToAll(this.origin, this.angles, ORIGIN_SIZE, g_iLaserIndex, 0, 0, 30, 0.2, 0.1, 0.1, 0, 0.0, 0);
|
||||
}
|
||||
|
||||
bool CheckEntity(int client) {
|
||||
if(this.entity != INVALID_ENT_REFERENCE) {
|
||||
if(!IsValidEntity(this.entity)) {
|
||||
PrintToChat(client, "\x04[Hats]\x01 Entity has vanished, editing cancelled.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsActive() {
|
||||
return this.mode != INACTIVE;
|
||||
}
|
||||
|
||||
void SetMode(wallMode mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
void CycleMode(int client, float tick) {
|
||||
if(tick - cmdThrottle[client] <= 0.25) return;
|
||||
int flags = GetEntityFlags(client) & ~FL_FROZEN;
|
||||
SetEntityFlags(client, flags);
|
||||
switch(this.mode) {
|
||||
// MODES:
|
||||
// - MOVE (cursor point)
|
||||
// - ROTATE
|
||||
// - SCALE
|
||||
// - FREECAM
|
||||
case MOVE_ORIGIN: {
|
||||
if(this.canScale) {
|
||||
this.mode = SCALE;
|
||||
PrintToChat(client, "\x04[Hats]\x01 Mode: \x05Scale\x01 (Press \x04RELOAD\x01 to change mode)");
|
||||
} else {
|
||||
this.mode = FREELOOK;
|
||||
PrintToChat(client, "\x04[Hats]\x01 Mode: \x05Freelook\x01 (Press \x04RELOAD\x01 to change mode)");
|
||||
}
|
||||
}
|
||||
case SCALE: {
|
||||
this.mode = FREELOOK;
|
||||
PrintToChat(client, "\x04[Hats]\x01 Mode: \x05Freelook\x01 (Press \x04RELOAD\x01 to change mode)");
|
||||
}
|
||||
case FREELOOK: {
|
||||
this.mode = MOVE_ORIGIN;
|
||||
PrintToChat(client, "\x04[Hats]\x01 Mode: \x05Move & Rotate\x01 (Press \x04RELOAD\x01 to change mode)");
|
||||
// PrintToChat(client, "Hold \x04USE (E)\x01 to rotate, \x04WALK (SHIFT)\x01 to change speed");
|
||||
}
|
||||
}
|
||||
cmdThrottle[client] = tick;
|
||||
}
|
||||
|
||||
void ToggleCollision(int client, float tick) {
|
||||
if(tick - cmdThrottle[client] <= 0.15) return;
|
||||
this.hasCollision = !this.hasCollision
|
||||
if(this.hasCollision)
|
||||
PrintToChat(client, "\x04[Hats]\x01 Collision: \x05ON\x01");
|
||||
else
|
||||
PrintToChat(client, "\x04[Hats]\x01 Collision: \x04OFF\x01");
|
||||
cmdThrottle[client] = tick;
|
||||
}
|
||||
|
||||
void CycleAxis(int client, float tick) {
|
||||
if(tick - cmdThrottle[client] <= 0.15) return;
|
||||
if(this.axis == 0) {
|
||||
this.axis = 1;
|
||||
PrintToChat(client, "\x04[Hats]\x01 Rotate Axis: \x05HEADING (Y)\x01");
|
||||
} else if(this.axis == 1) {
|
||||
this.axis = 2;
|
||||
PrintToChat(client, "\x04[Hats]\x01 Rotate Axis: \x05PITCH (X)\x01");
|
||||
} else {
|
||||
this.axis = 0;
|
||||
PrintToChat(client, "\x04[Hats]\x01 Rotate Axis: \x05ROLL (Z)\x01");
|
||||
}
|
||||
cmdThrottle[client] = tick;
|
||||
}
|
||||
|
||||
void CycleSnapAngle(int client, float tick) {
|
||||
if(tick - cmdThrottle[client] <= 0.15) return;
|
||||
switch(this.snapAngle) {
|
||||
case 1: this.snapAngle = 15;
|
||||
case 15: this.snapAngle = 30;
|
||||
case 30: this.snapAngle = 45;
|
||||
case 45: this.snapAngle = 90;
|
||||
case 90: this.snapAngle = 1;
|
||||
}
|
||||
|
||||
this.angles[0] = SnapTo(this.angles[0], float(this.snapAngle));
|
||||
this.angles[1] = SnapTo(this.angles[1], float(this.snapAngle));
|
||||
this.angles[2] = SnapTo(this.angles[2], float(this.snapAngle));
|
||||
|
||||
if(this.snapAngle == 1)
|
||||
PrintToChat(client, "\x04[Hats]\x01 Rotate Snap Degrees: \x04(OFF)\x01", this.snapAngle);
|
||||
else
|
||||
PrintToChat(client, "\x04[Hats]\x01 Rotate Snap Degrees: \x05%d\x01", this.snapAngle);
|
||||
cmdThrottle[client] = tick;
|
||||
}
|
||||
|
||||
void CycleSpeed(int client, float tick) {
|
||||
if(tick - cmdThrottle[client] <= 0.25) return;
|
||||
this.moveSpeed++;
|
||||
if(this.moveSpeed > 10) this.moveSpeed = 1;
|
||||
PrintToChat(client, "\x04[Hats]\x01 Scale Speed: \x05%d\x01", this.moveSpeed);
|
||||
// if(this.movetype == 0) {
|
||||
// this.movetype = 1;
|
||||
// PrintToChat(client, "\x04[SM]\x01 Move Type: \x05HEADING (Y)\x01");
|
||||
// } else {
|
||||
// this.movetype = 0;
|
||||
// PrintToChat(client, "\x04[SM]\x01 Rotate Axis: \x05PITCH (X)\x01");
|
||||
// }
|
||||
cmdThrottle[client] = tick;
|
||||
}
|
||||
|
||||
int Build() {
|
||||
if(!this.canScale) {
|
||||
this.Reset();
|
||||
return -3;
|
||||
}
|
||||
// Don't need to build a new one if we editing:
|
||||
int blocker = this.entity;
|
||||
bool isEdit = false;
|
||||
if(blocker != INVALID_ENT_REFERENCE) {
|
||||
RemoveEntity(this.entity);
|
||||
isEdit = true;
|
||||
}
|
||||
blocker = CreateEntityByName("func_brush");
|
||||
if(blocker == -1) return -1;
|
||||
DispatchKeyValueVector(blocker, "mins", this.mins);
|
||||
DispatchKeyValueVector(blocker, "maxs", this.size);
|
||||
DispatchKeyValueVector(blocker, "boxmins", this.mins);
|
||||
DispatchKeyValueVector(blocker, "boxmaxs", this.size);
|
||||
|
||||
DispatchKeyValueVector(blocker, "angles", this.angles);
|
||||
DispatchKeyValue(blocker, "model", DUMMY_MODEL);
|
||||
DispatchKeyValue(blocker, "intialstate", "1");
|
||||
// DispatchKeyValueVector(blocker, "angles", this.angles);
|
||||
DispatchKeyValue(blocker, "BlockType", "4");
|
||||
char name[32];
|
||||
Format(name, sizeof(name), "l4d2_hats_%d", createdWalls.Length);
|
||||
DispatchKeyValue(blocker, "targetname", name);
|
||||
// DispatchKeyValue(blocker, "excludednpc", "player");
|
||||
TeleportEntity(blocker, this.origin, this.angles, NULL_VECTOR);
|
||||
if(!DispatchSpawn(blocker)) return -1;
|
||||
SetEntPropVector(blocker, Prop_Send, "m_vecMins", this.mins);
|
||||
SetEntPropVector(blocker, Prop_Send, "m_vecMaxs", this.size);
|
||||
SetEntProp(blocker, Prop_Send, "m_nSolidType", 2);
|
||||
int enteffects = GetEntProp(blocker, Prop_Send, "m_fEffects");
|
||||
enteffects |= 32; //EF_NODRAW
|
||||
SetEntProp(blocker, Prop_Send, "m_fEffects", enteffects);
|
||||
AcceptEntityInput(blocker, "Enable");
|
||||
|
||||
this.Draw(WALL_COLOR, 5.0, 1.0);
|
||||
this.Reset();
|
||||
return isEdit ? -2 : createdWalls.Push(EntIndexToEntRef(blocker));
|
||||
}
|
||||
|
||||
int Copy() {
|
||||
if(this.entity == INVALID_ENT_REFERENCE) return -1;
|
||||
char classname[64];
|
||||
GetEntityClassname(this.entity, classname, sizeof(classname));
|
||||
|
||||
int entity = CreateEntityByName(classname);
|
||||
PrintToServer("Created %s: %d", classname, entity);
|
||||
if(entity == -1) return -1;
|
||||
GetEntPropString(this.entity, Prop_Data, "m_ModelName", classname, sizeof(classname));
|
||||
DispatchKeyValue(entity, "model", classname);
|
||||
PrintToServer("Set model %s: %d", classname, entity);
|
||||
DispatchSpawn(entity);
|
||||
TeleportEntity(entity, this.origin, this.angles, NULL_VECTOR);
|
||||
this.entity = entity;
|
||||
return entity;
|
||||
}
|
||||
|
||||
void Import(int entity, bool makeCopy = false, wallMode mode = SCALE) {
|
||||
this.Reset();
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", this.origin);
|
||||
GetEntPropVector(entity, Prop_Send, "m_angRotation", this.angles);
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecMins", this.mins);
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecMaxs", this.size);
|
||||
if(!makeCopy) {
|
||||
this.entity = entity;
|
||||
}
|
||||
this.SetMode(mode);
|
||||
}
|
||||
}
|
||||
|
||||
WallBuilderData WallBuilder[MAXPLAYERS+1];
|
||||
|
||||
|
||||
// TODO: Stacker, copy tool, new command?
|
||||
public Action Command_MakeWall(int client, int args) {
|
||||
if(WallBuilder[client].IsActive()) {
|
||||
ReplyToCommand(client, "\x04[Hats]\x01 You are already editing/building, either finish with \x05/wall build\x01 or cancel with \x04/wall cancel\x01");
|
||||
} else {
|
||||
WallBuilder[client].Reset();
|
||||
if(args > 0) {
|
||||
// Get values for X, Y and Z axis (defaulting to 1.0):
|
||||
char arg2[8];
|
||||
for(int i = 0; i < 3; i++) {
|
||||
GetCmdArg(i + 1, arg2, sizeof(arg2));
|
||||
float value;
|
||||
if(StringToFloatEx(arg2, value) == 0) {
|
||||
value = 1.0;
|
||||
}
|
||||
WallBuilder[client].size[i] = value;
|
||||
}
|
||||
|
||||
float rot[3];
|
||||
GetClientEyeAngles(client, rot);
|
||||
// Flip X and Y depending on rotation
|
||||
// TODO: Validate
|
||||
if(rot[2] > 45 && rot[2] < 135 || rot[2] > -135 && rot[2] < -45) {
|
||||
float temp = WallBuilder[client].size[0];
|
||||
WallBuilder[client].size[0] = WallBuilder[client].size[1];
|
||||
WallBuilder[client].size[1] = temp;
|
||||
}
|
||||
|
||||
WallBuilder[client].CalculateMins();
|
||||
}
|
||||
|
||||
WallBuilder[client].SetMode(SCALE);
|
||||
GetCursorLimited(client, 100.0, WallBuilder[client].origin, Filter_IgnorePlayer);
|
||||
PrintToChat(client, "\x04[Hats]\x01 New Wall Started. End with \x05/wall build\x01 or \x04/wall cancel\x01");
|
||||
PrintToChat(client, "\x04[Hats]\x01 Mode: \x05Scale\x01");
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Command_ManageWalls(int client, int args) {
|
||||
if(args == 0) {
|
||||
PrintToChat(client, "\x04[Hats]\x01 Created Walls: \x05%d\x01", createdWalls.Length);
|
||||
for(int i = 1; i <= createdWalls.Length; i++) {
|
||||
GlowWall(i, 20.0);
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
char arg1[16], arg2[16];
|
||||
GetCmdArg(1, arg1, sizeof(arg1));
|
||||
GetCmdArg(2, arg2, sizeof(arg2));
|
||||
if(StrEqual(arg1, "build") || StrEqual(arg1, "done")) {
|
||||
// Remove frozen flag from user, as some modes use this
|
||||
int flags = GetEntityFlags(client) & ~FL_FROZEN;
|
||||
SetEntityFlags(client, flags);
|
||||
|
||||
int id = WallBuilder[client].Build();
|
||||
if(id == -1) {
|
||||
PrintToChat(client, "\x04[Hats]\x01 Wall Creation: \x04Error\x01");
|
||||
} else if(id == -2) {
|
||||
PrintToChat(client, "\x04[Hats]\x01 Wall Edit: \x04Complete\x01");
|
||||
} else if(id == -3) {
|
||||
PrintToChat(client, "\x04[Hats]\x01 Entity Edit: \x04Complete\x01");
|
||||
} else {
|
||||
PrintToChat(client, "\x04[Hats]\x01 Wall Creation: \x05Wall #%d Created\x01", id + 1);
|
||||
}
|
||||
} else if(StrEqual(arg1, "cancel")) {
|
||||
int flags = GetEntityFlags(client) & ~FL_FROZEN;
|
||||
SetEntityFlags(client, flags);
|
||||
WallBuilder[client].SetMode(INACTIVE);
|
||||
PrintToChat(client, "\x04[Hats]\x01 Wall Creation: \x04Cancelled\x01");
|
||||
} else if(StrEqual(arg1, "export")) {
|
||||
// TODO: support exp #id
|
||||
float origin[3], angles[3], size[3];
|
||||
if(WallBuilder[client].IsActive()) {
|
||||
origin = WallBuilder[client].origin;
|
||||
angles = WallBuilder[client].angles;
|
||||
size = WallBuilder[client].size;
|
||||
Export(client, arg2, WallBuilder[client].entity, origin, angles, size);
|
||||
} else {
|
||||
int id = GetWallId(client, arg2);
|
||||
if(id == -1) return Plugin_Handled;
|
||||
int entity = GetWallEntity(id);
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", origin);
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecAngles", angles);
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecMaxs", size);
|
||||
Export(client, arg2, entity, origin, angles, size);
|
||||
}
|
||||
|
||||
|
||||
} else if(StrEqual(arg1, "delete")) {
|
||||
if(WallBuilder[client].IsActive() && args == 1) {
|
||||
int entity = WallBuilder[client].entity;
|
||||
if(IsValidEntity(entity)) {
|
||||
PrintToChat(client, "\x04[Hats]\x01 You are not editing any existing entity, use \x05/wall cancel\x01 to stop or \x05/wall delete <id/all>");
|
||||
} else if(entity > MaxClients) {
|
||||
RemoveEntity(entity);
|
||||
WallBuilder[client].Reset();
|
||||
PrintToChat(client, "\x04[Hats]\x01 Deleted current entity");
|
||||
} else {
|
||||
PrintToChat(client, "\x04[Hats]\x01 Cannot delete player entities.");
|
||||
}
|
||||
} else if(StrEqual(arg2, "all")) {
|
||||
int walls = createdWalls.Length;
|
||||
for(int i = 1; i <= createdWalls.Length; i++) {
|
||||
DeleteWall(i);
|
||||
}
|
||||
PrintToChat(client, "\x04[Hats]\x01 Deleted \x05%d\x01 Walls", walls);
|
||||
} else {
|
||||
int id = GetWallId(client, arg2);
|
||||
if(id > -1) {
|
||||
DeleteWall(id);
|
||||
PrintToChat(client, "\x04[Hats]\x01 Deleted Wall: \x05#%d\x01", id);
|
||||
}
|
||||
}
|
||||
} else if(StrEqual(arg1, "create")) {
|
||||
ReplyToCommand(client, "\x04[Hats]\x01 Syntax: /mkwall [size x] [size y] [size z]");
|
||||
} else if(StrEqual(arg1, "toggle")) {
|
||||
if(StrEqual(arg2, "all")) {
|
||||
int walls = createdWalls.Length;
|
||||
for(int i = 1; i <= createdWalls.Length; i++) {
|
||||
int entity = GetWallEntity(i);
|
||||
AcceptEntityInput(entity, "Toggle");
|
||||
GlowWall(i);
|
||||
}
|
||||
PrintToChat(client, "\x04[Hats]\x01 Toggled \x05%d\x01 walls", walls);
|
||||
} else {
|
||||
int id = GetWallId(client, arg2);
|
||||
if(id > -1) {
|
||||
int entity = GetWallEntity(id);
|
||||
AcceptEntityInput(entity, "Toggle");
|
||||
GlowWall(id);
|
||||
PrintToChat(client, "\x04[Hats]\x01 Toggled Wall: \x05#%d\x01", id);
|
||||
}
|
||||
}
|
||||
} else if(StrEqual(arg1, "filter")) {
|
||||
if(args < 3) {
|
||||
ReplyToCommand(client, "\x04[Hats]\x01 Syntax: \x05/walls filter <id/all> <filter type>\x04");
|
||||
ReplyToCommand(client, "\x04[Hats]\x01 Valid filters: \x05player");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
char arg3[32];
|
||||
GetCmdArg(3, arg3, sizeof(arg3));
|
||||
|
||||
SetVariantString(arg3);
|
||||
if(StrEqual(arg2, "all")) {
|
||||
int walls = createdWalls.Length;
|
||||
for(int i = 1; i <= createdWalls.Length; i++) {
|
||||
int entity = GetWallEntity(i);
|
||||
AcceptEntityInput(entity, "SetExcluded");
|
||||
}
|
||||
PrintToChat(client, "\x04[Hats]\x01 Set %d walls' filter to \x05%s\x01", walls, arg3);
|
||||
} else {
|
||||
int id = GetWallId(client, arg2);
|
||||
if(id > -1) {
|
||||
int entity = GetWallEntity(id);
|
||||
AcceptEntityInput(entity, "SetExcluded");
|
||||
PrintToChat(client, "\x04[Hats]\x01 Set wall #%d filter to \x05%s\x01", id, arg3);
|
||||
}
|
||||
}
|
||||
} else if(StrEqual(arg1, "edit")) {
|
||||
int id = GetWallId(client, arg2);
|
||||
if(id > -1) {
|
||||
int entity = GetWallEntity(id);
|
||||
WallBuilder[client].Import(entity);
|
||||
PrintToChat(client, "\x04[Hats]\x01 Editing wall \x05%d\x01. End with \x05/wall done\x01 or \x04/wall cancel\x01", id);
|
||||
PrintToChat(client, "\x04[Hats]\x01 Mode: \x05Scale\x01");
|
||||
}
|
||||
} else if(StrEqual(arg1, "edite") || arg1[0] == 'c') {
|
||||
int index = GetLookingEntity(client, Filter_ValidHats); //GetClientAimTarget(client, false);
|
||||
if(index > 0) {
|
||||
WallBuilder[client].Import(index, false, MOVE_ORIGIN);
|
||||
WallBuilder[client].canScale = false;
|
||||
char classname[32];
|
||||
char targetname[32];
|
||||
GetEntityClassname(index, classname, sizeof(classname));
|
||||
GetEntPropString(index, Prop_Data, "m_target", targetname, sizeof(targetname));
|
||||
PrintToChat(client, "\x04[Hats]\x01 Editing entity \x05%d (%s) [%s]\x01. End with \x05/wall done\x01", index, classname, targetname);
|
||||
PrintToChat(client, "\x04[Hats]\x01 Mode: \x05Move & Rotate\x01");
|
||||
} else {
|
||||
ReplyToCommand(client, "\x04[Hats]\x01 Invalid or non existent entity");
|
||||
}
|
||||
} else if(StrEqual(arg1, "copy")) {
|
||||
if(WallBuilder[client].IsActive()) {
|
||||
int oldEntity = WallBuilder[client].entity;
|
||||
if(oldEntity == INVALID_ENT_REFERENCE) {
|
||||
PrintToChat(client, "\x04[Hats]\x01 Finish editing your wall first: \x05/wall done\x01 or \x04/wall cancel\x01");
|
||||
} else {
|
||||
int entity = WallBuilder[client].Copy();
|
||||
PrintToChat(client, "\x04[Hats]\x01 Editing copy \x05%d\x01 of entity \x05%d\x01. End with \x05/wall done\x01 or \x04/wall cancel\x01", entity, oldEntity);
|
||||
}
|
||||
} else {
|
||||
int id = GetWallId(client, arg2);
|
||||
if(id > -1) {
|
||||
int entity = GetWallEntity(id);
|
||||
WallBuilder[client].Import(entity, true);
|
||||
GetCursorLimited(client, 100.0, WallBuilder[client].origin, Filter_IgnorePlayer);
|
||||
PrintToChat(client, "\x04[Hats]\x01 Editing copy of wall \x05%d\x01. End with \x05/wall build\x01 or \x04/wall cancel\x01", id);
|
||||
PrintToChat(client, "\x04[Hats]\x01 Mode: \x05Scale\x01");
|
||||
}
|
||||
}
|
||||
} else if(StrEqual(arg1, "list")) {
|
||||
for(int i = 1; i <= createdWalls.Length; i++) {
|
||||
int entity = GetWallEntity(i);
|
||||
ReplyToCommand(client, "Wall #%d - EntIndex: %d", i, EntRefToEntIndex(entity));
|
||||
}
|
||||
} else {
|
||||
ReplyToCommand(client, "\x04[Hats]\x01 See console for list of commands");
|
||||
GetCmdArg(0, arg1, sizeof(arg1));
|
||||
PrintToConsole(client, "%s done / build - Finishes editing, creates wall if making wall", arg1);
|
||||
PrintToConsole(client, "%s cancel - Cancels editing (for entity edits is same as done)", arg1);
|
||||
PrintToConsole(client, "%s list - Lists all walls", arg1);
|
||||
PrintToConsole(client, "%s filter <id/all> <filter type> - Sets classname filter for walls, doesnt really work", arg1);
|
||||
PrintToConsole(client, "%s toggle <id/all> - Toggles if wall is active (collides)", arg1);
|
||||
PrintToConsole(client, "%s delete <id/all> - Deletes the wall(s)", arg1);
|
||||
PrintToConsole(client, "%s edit <id> - Edits wall id", arg1);
|
||||
PrintToConsole(client, "%s copy [id] - If editing creates a new copy of wall/entity, else copies wall id", arg1);
|
||||
PrintToConsole(client, "%s cursor - Starts editing the entity you looking at", arg1);
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
int GetWallId(int client, const char[] arg) {
|
||||
int id;
|
||||
if(StringToIntEx(arg, id) > 0 && id > 0 && id <= createdWalls.Length) {
|
||||
int entity = GetWallEntity(id);
|
||||
if(!IsValidEntity(entity)) {
|
||||
ReplyToCommand(client, "\x04[Hats]\x01 The wall with specified id no longer exists.");
|
||||
createdWalls.Erase(id);
|
||||
return -2;
|
||||
}
|
||||
return id;
|
||||
} else {
|
||||
ReplyToCommand(client, "\x04[Hats]\x01 Invalid wall id, must be between 0 - %d", createdWalls.Length - 1 );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int GetWallEntity(int id) {
|
||||
if(id <= 0 || id > createdWalls.Length) {
|
||||
ThrowError("Invalid wall id (%d)", id);
|
||||
}
|
||||
return createdWalls.Get(id - 1);
|
||||
}
|
||||
|
||||
void GlowWall(int id, float lifetime = 5.0) {
|
||||
int ref = GetWallEntity(id);
|
||||
if(IsValidEntity(ref)) {
|
||||
float pos[3], mins[3], maxs[3], angles[3];
|
||||
GetEntPropVector(ref, Prop_Send, "m_angRotation", angles);
|
||||
GetEntPropVector(ref, Prop_Send, "m_vecOrigin", pos);
|
||||
GetEntPropVector(ref, Prop_Send, "m_vecMins", mins);
|
||||
GetEntPropVector(ref, Prop_Send, "m_vecMaxs", maxs);
|
||||
Effect_DrawBeamBoxRotatableToAll(pos, mins, maxs, angles, g_iLaserIndex, 0, 0, 30, lifetime, 0.4, 0.4, 0, 1.0, WALL_COLOR, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteWall(int id) {
|
||||
GlowWall(id);
|
||||
int ref = GetWallEntity(id);
|
||||
if(IsValidEntity(ref)) {
|
||||
RemoveEntity(ref);
|
||||
}
|
||||
createdWalls.Erase(id - 1);
|
||||
}
|
||||
|
||||
void Export(int client, const char[] expType, int entity, const float origin[3], const float angles[3], const float size[3]) {
|
||||
char sPath[PLATFORM_MAX_PATH];
|
||||
char currentMap[64];
|
||||
GetCurrentMap(currentMap, sizeof(currentMap));
|
||||
|
||||
BuildPath(Path_SM, sPath, sizeof(sPath), "data/exports");
|
||||
CreateDirectory(sPath, 1406);
|
||||
BuildPath(Path_SM, sPath, sizeof(sPath), "data/exports/%s.cfg", currentMap);
|
||||
File file = OpenFile(sPath, "w");
|
||||
if(file == null) {
|
||||
PrintToServer("[Hats] Export: Cannot open \"%s\", cant write", sPath);
|
||||
}
|
||||
|
||||
PrintWriteLine(client, file, "{");
|
||||
if(entity != INVALID_ENT_REFERENCE) {
|
||||
char model[64];
|
||||
GetEntPropString(entity, Prop_Data, "m_ModelName", model, sizeof(model));
|
||||
if(StrEqual(expType, "json")) {
|
||||
PrintWriteLine(client, file, "\t\"model\": \"%s\",", model);
|
||||
} else{
|
||||
PrintWriteLine(client, file, "\t\"model\" \"%s\"", model);
|
||||
}
|
||||
}
|
||||
|
||||
if(StrEqual(expType, "json")) {
|
||||
PrintWriteLine(client, file, "\t\"origin\": [%.2f, %.2f, %.2f],", origin[0], origin[1], origin[2]);
|
||||
PrintWriteLine(client, file, "\t\"angles\": [%.2f, %.2f, %.2f],", angles[0], angles[1], angles[2]);
|
||||
PrintWriteLine(client, file, "\t\"size\": [%.2f, %.2f, %.2f]", size[0], size[1], size[2]);
|
||||
} else {
|
||||
PrintWriteLine(client, file, "\t\"origin\" \"%.2f %.2f %.2f\"", origin[0], origin[1], origin[2]);
|
||||
PrintWriteLine(client, file, "\t\"angles\" \"%.2f %.2f %.2f\"", angles[0], angles[1], angles[2]);
|
||||
PrintWriteLine(client, file, "\t\"size\" \"%.2f %.2f %.2f\"", size[0], size[1], size[2]);
|
||||
}
|
||||
PrintWriteLine(client, file, "}");
|
||||
delete file;
|
||||
}
|
||||
|
||||
void PrintWriteLine(int client, File file, const char[] format, any ...) {
|
||||
char line[100];
|
||||
VFormat(line, sizeof(line), format, 4);
|
||||
if(file != null)
|
||||
file.WriteLine(line);
|
||||
PrintToChat(client, line);
|
||||
}
|
||||
|
||||
enum struct WallModelSizeEntry {
|
||||
char name[32];
|
||||
char model[64];
|
||||
}
|
||||
enum struct WallModelEntry {
|
||||
char name[32];
|
||||
|
||||
WallModelSizeEntry size1;
|
||||
WallModelSizeEntry size2;
|
||||
WallModelSizeEntry size3;
|
||||
}
|
||||
ArrayList wallModels;
|
||||
|
||||
void LoadModels() {
|
||||
if(wallModels != null) delete wallModels;
|
||||
wallModels = new ArrayList(sizeof(WallModelEntry));
|
||||
KeyValues kv = new KeyValues("WallData");
|
||||
|
||||
char sPath[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, sPath, sizeof(sPath), "data/walls_data.cfg");
|
||||
|
||||
if(!FileExists(sPath) || !kv.ImportFromFile(sPath)) {
|
||||
delete kv;
|
||||
PrintToServer("[FTT] Could not load phrase list from data/walls_data.cfg");
|
||||
return;
|
||||
}
|
||||
char name[32];
|
||||
// Go through all the words:
|
||||
// kv.GotoFirstSubKey();
|
||||
// int i = 0;
|
||||
// char buffer[4];
|
||||
// do {
|
||||
// kv.GetSectionName(name, sizeof(name));
|
||||
// for(;;) {
|
||||
// IntToString(++i, buffer, sizeof(buffer));
|
||||
// kv.GetString(buffer, phrase, MAX_PHRASE_LENGTH, "_null");
|
||||
// if(strcmp(phrase, "_null") == 0) break;
|
||||
// phrases.PushString(phrase);
|
||||
// }
|
||||
// i = 0;
|
||||
// if(StrEqual(word, "_full message phrases")) {
|
||||
// fullMessagePhraseList = phrases.Clone();
|
||||
// continue;
|
||||
// }
|
||||
// #if defined DEBUG_PHRASE_LOAD
|
||||
// PrintToServer("Loaded %d phrases for word \"%s\"", phrases.Length, word);
|
||||
// #endif
|
||||
// REPLACEMENT_PHRASES.SetValue(word, phrases.Clone(), true);
|
||||
// } while (kv.GotoNextKey(false));
|
||||
|
||||
delete kv;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue