Working prop spawner

This commit is contained in:
Jackzie 2024-01-06 22:01:22 -06:00
parent a15efc9cce
commit 5796ddd536
5 changed files with 1021 additions and 244 deletions

View file

@ -328,7 +328,6 @@ Action Command_DoAHat(int client, int args) {
// 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 {
@ -353,7 +352,7 @@ Action Command_DoAHat(int client, int args) {
} else if(entity == EntRefToEntIndex(WallBuilder[client].entity)) {
// Prevent making an entity you editing a hat
return Plugin_Handled;
} else if(cvar_sm_hats_max_distance.FloatValue > 0.0 && entity >= MaxClients) {
} else if(!isForced && cvar_sm_hats_max_distance.FloatValue > 0.0 && entity >= MaxClients) {
float posP[3], posE[3];
GetClientEyePosition(client, posP);
GetEntPropVector(entity, Prop_Data, "m_vecOrigin", posE);
@ -377,8 +376,8 @@ Action Command_DoAHat(int client, int args) {
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(IsFakeClient(entity)) {
PrintToChat(client, "[Hats] Cannot hat bots");
if(IsFakeClient(entity) && L4D_GetIdlePlayerOfBot(entity) != -1) {
PrintToChat(client, "[Hats] Cannot hat idle bots");
return Plugin_Handled;
} else 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");
@ -537,7 +536,7 @@ int HatConsentHandler(Menu menu, MenuAction action, int target, int param2) {
}
bool IsHatsEnabled(int client) {
return (cvar_sm_hats_enabled.IntValue == 1 && GetUserAdmin(client) != INVALID_ADMIN_ID) || cvar_sm_hats_enabled.IntValue == 2
return (cvar_sm_hats_enabled.IntValue == 1 && GetUserAdmin(client) != INVALID_ADMIN_ID) || cvar_sm_hats_enabled.IntValue == 2
}
void ClearHats() {

View file

@ -1,69 +1,644 @@
TopMenuObject g_propSpawnerCategory;
public void OnAdminMenuReady(Handle topMenuHandle) {
TopMenu topMenu = TopMenu.FromHandle(topMenuHandle);
if(topMenu != g_topMenu) {
TopMenuObject propSpawner = topMenu.AddCategory("Prop Spawner (Alpha)", Category_Handler);
if(propSpawner != INVALID_TOPMENUOBJECT) {
topMenu.AddItem("Spawn Prop", AdminMenu_Spawn, propSpawner, "sm_prop");
topMenu.AddItem("Edit Props", AdminMenu_Edit, propSpawner, "sm_prop");
topMenu.AddItem("Delete Props", AdminMenu_Delete, propSpawner, "sm_prop");
topMenu.AddItem("Save / Load", AdminMenu_SaveLoad, propSpawner, "sm_prop");
g_propSpawnerCategory = topMenu.AddCategory("hats_editor", Category_Handler);
if(g_propSpawnerCategory != INVALID_TOPMENUOBJECT) {
topMenu.AddItem("editor_spawn", AdminMenu_Spawn, g_propSpawnerCategory, "sm_prop");
topMenu.AddItem("editor_edit", AdminMenu_Edit, g_propSpawnerCategory, "sm_prop");
topMenu.AddItem("editor_delete", AdminMenu_Delete, g_propSpawnerCategory, "sm_prop");
topMenu.AddItem("editor_saveload", AdminMenu_SaveLoad, g_propSpawnerCategory, "sm_prop");
}
}
g_topMenu = topMenu;
}
enum struct CategoryData {
char name[64];
bool hasItems;
ArrayList items;
}
enum struct ItemData {
char model[128];
char name[64];
}
enum struct SaveData {
char model[128];
buildType type;
float origin[3];
float angles[3];
int color[4];
void FromEntity(int entity) {
// Use this.model as a buffer:
GetEntityClassname(entity, this.model, sizeof(this.model));
if(StrEqual(this.model, "prop_physics")) this.type = Build_Physics;
else if(StrEqual(this.model, "prop_dynamic")) {
if(GetEntProp(entity, Prop_Send, "m_nSolidType") == 0) {
this.type = Build_NonSolid;
} else {
this.type = Build_Solid;
}
}
GetEntPropString(entity, Prop_Data, "m_ModelName", this.model, sizeof(this.model));
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", this.origin);
GetEntPropVector(entity, Prop_Send, "m_angRotation", this.angles);
GetEntityRenderColor(entity, this.color[0],this.color[1],this.color[2],this.color[3]);
}
void Serialize(char[] output, int maxlen) {
Format(
output, maxlen, "%s,%d,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%d,%d,%d,%d",
this.model, this.type, this.origin[0], this.origin[1], this.origin[2],
this.angles[0], this.angles[1], this.angles[2],
this.color[0], this.color[1], this.color[2], this.color[3]
);
}
void Deserialize(const char[] output) {
char buffer[32];
int index = SplitString(output, ",", this.model, sizeof(this.model));
index = SplitString(output[index], ",", buffer, sizeof(buffer));
this.type = view_as<buildType>(StringToInt(buffer));
for(int i = 0; i < 3; i++) {
index = SplitString(output[index], ",", buffer, sizeof(buffer));
this.origin[i] = StringToFloat(buffer);
}
for(int i = 0; i < 3; i++) {
index = SplitString(output[index], ",", buffer, sizeof(buffer));
this.angles[i] = StringToFloat(buffer);
}
for(int i = 0; i < 4; i++) {
index = SplitString(output[index], ",", buffer, sizeof(buffer));
this.color[i] = StringToInt(buffer);
}
}
}
ArrayList g_categories;
ArrayList g_spawnedItems;
ArrayList g_savedItems;
bool LoadSaves(ArrayList saves) {
saves = new ArrayList(ByteCountToCells(64));
char path[PLATFORM_MAX_PATH];
BuildPath(Path_SM, path, sizeof(path), "data/prop_spawner/saves/%s", g_currentMap);
FileType fileType;
DirectoryListing listing = OpenDirectory(path);
if(listing == null) return false;
char buffer[64];
while(listing.GetNext(buffer, sizeof(buffer), fileType)) {
saves.PushString(buffer);
}
delete listing;
return true;
}
public bool LoadSave(const char[] save) {
char path[PLATFORM_MAX_PATH];
BuildPath(Path_SM, path, sizeof(path), "data/prop_spawner/saves/%s/%s", g_currentMap, save);
// ArrayList savedItems = new ArrayList(sizeof(SaveData));
File file = OpenFile(path, "r");
if(file == null) return false;
char buffer[256];
SaveData data;
while(file.ReadLine(buffer, sizeof(buffer))) {
data.Deserialize(buffer);
int entity = -1;
if(data.type == Build_Physics)
entity = CreateEntityByName("prop_physics");
else
entity = CreateEntityByName("prop_dynamic");
if(entity == -1) continue;
PrecacheModel(data.model);
DispatchKeyValue(entity, "model", data.model);
DispatchKeyValue(entity, "targetname", "saved_prop");
DispatchKeyValue(entity, "solid", data.type == Build_NonSolid ? "0" : "6");
TeleportEntity(entity, data.origin, data.angles, NULL_VECTOR);
if(!DispatchSpawn(entity)) continue;
// TODO: Setrendertype?
SetEntityRenderColor(entity, data.color[0], data.color[1], data.color[2], data.color[3]);
// TODO: previews?
// g_savedItems.PushArray(data);
}
delete file;
// delete savedItems;
return true;
}
bool CreateSave(const char[] name) {
char path[PLATFORM_MAX_PATH];
BuildPath(Path_SM, path, sizeof(path), "data/prop_spawner/saves/%s/%s.txt", g_currentMap, name);
File file = OpenFile(name, "w");
if(file == null) return false;
char buffer[132];
SaveData data;
for(int i = 0; i < g_spawnedItems.Length; i++) {
int ref = g_spawnedItems.Get(i);
data.FromEntity(ref);
data.Serialize(buffer, sizeof(buffer));
file.WriteLine("%s", buffer);
}
file.Flush();
delete file;
return true;
}
void UnloadSave() {
if(g_savedItems != null) {
delete g_savedItems;
}
}
public void LoadCategories() {
if(g_categories != null) return;
g_categories = new ArrayList(sizeof(CategoryData));
char path[PLATFORM_MAX_PATH];
BuildPath(Path_SM, path, sizeof(path), "data/prop_spawner/models");
LoadFolder(g_categories, path);
}
public void UnloadCategories() {
if(g_categories == null) return;
_UnloadCategories(g_categories);
delete g_categories;
}
void _UnloadCategories(ArrayList list) {
CategoryData cat;
for(int i = 0; i < list.Length; i++) {
list.GetArray(i, cat);
_UnloadCategory(cat);
}
}
void _UnloadCategory(CategoryData cat) {
// Is a sub-category:
if(!cat.hasItems) {
_UnloadCategories(cat.items);
}
delete cat.items;
}
void LoadFolder(ArrayList parent, const char[] rootPath) {
char buffer[PLATFORM_MAX_PATH];
FileType fileType;
DirectoryListing listing = OpenDirectory(rootPath);
if(listing == null) {
LogError("Cannot open \"%s\"", rootPath);
}
while(listing.GetNext(buffer, sizeof(buffer), fileType)) {
if(fileType == FileType_Directory) {
// TODO: support subcategory
if(buffer[0] == '.') continue;
CategoryData data;
Format(data.name, sizeof(data.name), "%s>>", buffer);
data.items = new ArrayList();
Format(buffer, sizeof(buffer), "%s/%s", rootPath, buffer);
LoadFolder(data.items, buffer);
parent.PushArray(data);
} else if(fileType == FileType_File) {
Format(buffer, sizeof(buffer), "%s/%s", rootPath, buffer);
LoadProps(parent, buffer);
}
}
delete listing;
}
void LoadProps(ArrayList parent, const char[] filePath) {
File file = OpenFile(filePath, "r");
if(file == null) {
PrintToServer("[Props] Cannot open file \"%s\"", filePath);
return;
}
CategoryData category;
category.items = new ArrayList(sizeof(ItemData));
category.hasItems = true;
char buffer[128];
if(!file.ReadLine(buffer, sizeof(buffer))) {
delete file;
return;
}
ReplaceString(buffer, sizeof(buffer), "\n", "");
ReplaceString(buffer, sizeof(buffer), "\r", "");
Format(category.name, sizeof(category.name), "%s>", buffer);
while(file.ReadLine(buffer, sizeof(buffer))) {
ItemData item;
int index = SplitString(buffer, " ", item.model, sizeof(item.model));
if(index == -1) {
strcopy(item.name, sizeof(item.name), buffer);
} else {
strcopy(item.name, sizeof(item.name), buffer[index]);
}
category.items.PushArray(item);
}
parent.PushArray(category);
delete file;
}
public Action OnClientSayCommand(int client, const char[] command, const char[] sArgs) {
if(!g_isSearchActive[client]) {
return Plugin_Continue;
}
ArrayList results = SearchItems(sArgs);
if(results.Length == 0) {
CPrintToChat(client, "\x04[Editor]\x01 No results found. :(");
} else {
ShowItemMenuAny(client, results);
}
delete results;
return Plugin_Handled;
}
#define MAX_SEARCH_RESULTS 30
ArrayList SearchItems(const char[] query) {
// TODO: search
ArrayList results = new ArrayList(sizeof(ItemData));
_searchCategory(results, g_categories, query);
return results;
}
void _searchCategory(ArrayList results, ArrayList categories, const char[] query) {
CategoryData cat;
for(int i = 0; i < categories.Length; i++) {
categories.GetArray(i, cat);
if(cat.hasItems) {
_searchItems(results, cat.items, query);
} else {
_searchCategory(results, cat.items, query);
}
if(results.Length > MAX_SEARCH_RESULTS) return;
}
}
void _searchItems(ArrayList results, ArrayList items, const char[] query) {
ItemData item;
for(int i = 0; i < items.Length; i++) {
items.GetArray(i, item);
if(StrContains(item.name, query, false)) {
results.PushArray(item);
}
if(results.Length > MAX_SEARCH_RESULTS) return;
}
}
void Category_Handler(TopMenu topmenu, TopMenuAction action, TopMenuObject topobj_id, int param, char[] buffer, int maxlength) {
if(action == TopMenuAction_DisplayTitle) {
Format(buffer, maxlength, "Select a task:");
} else if(action == TopMenuAction_DisplayOption) {
Format(buffer, maxlength, "Spawn Props");
Format(buffer, maxlength, "Spawn Props (Beta)");
}
}
void AdminMenu_Spawn(TopMenu topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) {
if(action == TopMenuAction_SelectOption) {
if(!FindConVar("sv_cheats").BoolValue) {
ReplyToCommand(param, "[Props] Enable cheats to use the prop spawner");
if(action == TopMenuAction_DisplayOption) {
Format(buffer, maxlength, "Spawn Props");
} else if(action == TopMenuAction_SelectOption) {
if(!FindConVar("sm_cheats").BoolValue) {
CReplyToCommand(param, "\x04[Editor] \x01Set \x05sm_cheats\x01 to \x051\x01 to use the prop spawner");
return;
}
// TODO:
/*
Flow:
1. /admin -> Prop Spawner -> Spawn -> [category] -> [prop]
2. ghost spawner active (press '?somekey?' to switch spawn mode)
3. continue on place. press button to press?
*/
// Menu menu = new Menu(Handler_Spawn);
// menu.SetTitle("Spawn Method:");
// menu.AddItem("p", "Physics");
// menu.AddItem("s", "Solid");
// menu.AddItem("n", "Non Solid");
// menu.ExitBackButton = true;
// menu.ExitButton = true;
// menu.Display(param, MENU_TIME_FOREVER);
Menu menu = new Menu(Spawn_RootHandler);
menu.SetTitle("Choose list:");
menu.AddItem("f", "Favorites");
menu.AddItem("r", "Recents");
menu.AddItem("s", "Search");
menu.AddItem("n", "Prop List");
menu.ExitBackButton = true;
menu.ExitButton = true;
menu.Display(param, MENU_TIME_FOREVER);
}
}
void AdminMenu_Edit(TopMenu topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) {
}
void AdminMenu_Delete(TopMenu topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) {
}
void AdminMenu_SaveLoad(TopMenu topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) {
}
int Handler_Spawn(Menu menu, MenuAction action, int client, int param2) {
int Spawn_RootHandler(Menu menu, MenuAction action, int client, int param2) {
if (action == MenuAction_Select) {
static char info[2];
if(info[0] == 'p') {
}
char info[2];
menu.GetItem(param2, info, sizeof(info));
switch(info[0]) {
case 'f': Spawn_ShowFavorites(client);
case 'r': Spawn_ShowRecents(client);
case 's': Spawn_ShowSearch(client);
case 'n': ShowCategoryList(client);
}
// TODO: handle back (to top menu)
} else if (action == MenuAction_End)
delete menu;
return 0;
}
Spawn_ShowFavorites(int client) {
PrintToChat(client, "In development");
return;
// Menu menu = new Menu(SpawnItemHandler);
// char model[128];
// for(int i = 0; i <= g_spawnedItems.Length; i++) {
// int ref = g_spawnedItems.Get(i);
// if(IsValidEntity(ref)) {
// GetEntPropString(ref, Prop_Data, "m_ModelName", model, sizeof(model));
// menu.AddItem(model, model);
// }
// }
// menu.ExitBackButton = true;
// menu.ExitButton = true;
// menu.Display(client, MENU_TIME_FOREVER);
}
Spawn_ShowRecents(int client) {
Menu menu = new Menu(SpawnItemHandler);
menu.SetTitle("Recent Props:");
char model[128];
for(int i = 0; i <= g_spawnedItems.Length; i++) {
int ref = g_spawnedItems.Get(i);
if(IsValidEntity(ref)) {
GetEntPropString(ref, Prop_Data, "m_ModelName", model, sizeof(model));
menu.AddItem(model, model);
}
}
menu.ExitBackButton = true;
menu.ExitButton = true;
menu.Display(client, MENU_TIME_FOREVER);
}
Spawn_ShowSearch(int client) {
g_isSearchActive[client] = true;
CReplyToCommand(client, "\x04[Editor] \x01Please enter search query in chat:");
}
void AdminMenu_Edit(TopMenu topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) {
if(action == TopMenuAction_DisplayOption) {
Format(buffer, maxlength, "Edit Props");
} else if(action == TopMenuAction_SelectOption) {
ShowEditList(param);
}
}
void ShowDeleteList(int client, int index) {
Menu menu = new Menu(DeleteHandler);
menu.SetTitle("Delete Props");
menu.AddItem("-1", "Delete All");
menu.AddItem("-2", "Delete All (Mine Only)");
char info[8];
char buffer[128];
for(int i = 0; i < g_spawnedItems.Length; i++) {
int ref = g_spawnedItems.Get(i);
IntToString(i, info, sizeof(info));
GetEntPropString(ref, Prop_Data, "m_ModelName", buffer, sizeof(buffer));
index = FindCharInString(buffer, '/', true);
if(index != -1)
menu.AddItem(info, buffer[index + 1]);
}
menu.ExitBackButton = true;
menu.ExitButton = true;
// Add +2 to the index for the two "Delete ..." buttons
menu.DisplayAt(client, index + 2, MENU_TIME_FOREVER);
}
void ShowEditList(int client, int index = 0) {
Menu menu = new Menu(EditHandler);
menu.SetTitle("Edit Prop");
char info[8];
char buffer[32];
for(int i = 0; i < g_spawnedItems.Length; i++) {
int ref = g_spawnedItems.Get(i);
IntToString(i, info, sizeof(info));
GetEntPropString(ref, Prop_Data, "m_ModelName", buffer, sizeof(buffer));
index = FindCharInString(buffer, '/', true);
if(index != -1)
menu.AddItem(info, buffer[index + 1]);
}
menu.ExitBackButton = true;
menu.ExitButton = true;
// Add +2 to the index for the two "Delete ..." buttons
menu.DisplayAt(client, index, MENU_TIME_FOREVER);
}
void AdminMenu_Delete(TopMenu topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) {
if(action == TopMenuAction_DisplayOption) {
Format(buffer, maxlength, "Delete Props");
} else if(action == TopMenuAction_SelectOption) {
ShowDeleteList(param, -2);
}
}
void AdminMenu_SaveLoad(TopMenu topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) {
if(action == TopMenuAction_DisplayOption) {
Format(buffer, maxlength, "Save / Load");
} else if(action == TopMenuAction_SelectOption) {
ArrayList saves;
LoadSaves(saves);
Menu menu = new Menu(SaveLoadHandler);
menu.SetTitle("Save / Load");
char name[64];
menu.AddItem("", "[New Save]");
for(int i = 0; i < saves.Length; i++) {
saves.GetString(i, name, sizeof(name));
menu.AddItem(name, name);
}
menu.ExitBackButton = true;
menu.ExitButton = true;
menu.Display(param, MENU_TIME_FOREVER);
delete saves;
}
}
void ShowCategoryList(int client) {
LoadCategories();
Menu menu = new Menu(SpawnCategoryHandler);
menu.SetTitle("Choose a category");
CategoryData cat;
char info[4];
for(int i = 0; i < g_categories.Length; i++) {
g_categories.GetArray(i, cat);
Format(info, sizeof(info), "%d", i);
// TODO: add support for nested
if(cat.hasItems)
menu.AddItem(info, cat.name);
}
menu.ExitBackButton = true;
menu.ExitButton = true;
menu.DisplayAt(client, g_lastCategoryIndex[client], MENU_TIME_FOREVER);
}
int SpawnCategoryHandler(Menu menu, MenuAction action, int client, int param2) {
if (action == MenuAction_Select) {
char info[8];
menu.GetItem(param2, info, sizeof(info));
int index = StringToInt(info);
if(index > 0) {
ShowItemMenu(client, index);
}
} else if (action == MenuAction_End)
delete menu;
return 0;
}
int SaveLoadHandler(Menu menu, MenuAction action, int client, int param2) {
if (action == MenuAction_Select) {
char saveName[64];
menu.GetItem(param2, saveName, sizeof(saveName));
if(saveName[0] == '\0') {
// Save new
FormatTime(saveName, sizeof(saveName), "%Y-%m-%d %X");
if(CreateSave(saveName)) {
PrintToChat(client, "\x04[Editor]\x01 Created save \x05%s.txt", saveName);
} else {
PrintToChat(client, "\x04[Editor]\x01 Error creating save file");
}
} else if(LoadSave(saveName)) {
PrintToChat(client, "\x04[Editor]\x01 Loaded save \x05%s", saveName);
} else {
PrintToChat(client, "\x04[Editor]\x01 Error loading save file");
}
} else if (action == MenuAction_Cancel) {
if(param2 == MenuCancel_ExitBack) {
DisplayTopMenuCategory(g_topMenu, g_propSpawnerCategory, client);
}
} else if (action == MenuAction_End)
delete menu;
return 0;
}
int DeleteHandler(Menu menu, MenuAction action, int client, int param2) {
if (action == MenuAction_Select) {
char info[8];
menu.GetItem(param2, info, sizeof(info));
int index = StringToInt(info);
if(index == -1) {
// Delete all (everyone)
int count = DeleteAll();
PrintToChat(client, "\x04[Editor]\x01 Deleted \x05%d\x01 items", count);
ShowDeleteList(client, index);
} else if(index == -2) {
// Delete all (mine only)
int count = DeleteAll(client);
PrintToChat(client, "\x04[Editor]\x01 Deleted \x05%d\x01 items", count);
ShowDeleteList(client, index);
} else {
int ref = g_spawnedItems.Get(index);
// TODO: add delete confirm
if(IsValidEntity(ref)) {
RemoveEntity(ref);
}
g_spawnedItems.Erase(index);
if(index > 0) {
index--;
}
ShowDeleteList(client, index);
}
} else if (action == MenuAction_Cancel) {
if(param2 == MenuCancel_ExitBack) {
DisplayTopMenuCategory(g_topMenu, g_propSpawnerCategory, client);
}
} else if (action == MenuAction_End)
delete menu;
return 0;
}
void
int DeleteAll(int onlyPlayer = 0) {
int userid = onlyPlayer > 0 ? GetClientUserId(onlyPlayer) : 0;
int count;
for(int i = 0; i < g_spawnedItems.Length; i++) {
int ref = g_spawnedItems.Get(i);
int spawnedBy = g_spawnedItems.Get(i, 1);
// Skip if wishing to only delete certain items:
if(onlyPlayer != 0 && spawnedBy != userid) continue;
if(IsValidEntity(ref)) {
RemoveEntity(ref);
}
g_spawnedItems.Erase(i);
count++;
}
return count;
}
int EditHandler(Menu menu, MenuAction action, int client, int param2) {
if (action == MenuAction_Select) {
char info[8];
menu.GetItem(param2, info, sizeof(info));
int index = StringToInt(info);
int ref = g_spawnedItems.Get(index);
int entity = EntRefToEntIndex(ref);
if(entity > 0) {
WallBuilder[client].Import(entity, false);
PrintToChat(client, "\x04[Editor]\x01 Editing entity \x05%d", entity);
} else {
PrintToChat(client, "\x04[Editor]\x01 Entity disappeared.");
g_spawnedItems.Erase(index);
index--;
}
ShowEditList(client, index);
} else if (action == MenuAction_Cancel) {
if(param2 == MenuCancel_ExitBack) {
DisplayTopMenuCategory(g_topMenu, g_propSpawnerCategory, client);
}
} else if (action == MenuAction_End)
delete menu;
return 0;
}
void ShowItemMenuAny(int client, ArrayList items, const char[] title = "") {
Menu itemMenu = new Menu(SpawnItemHandler);
if(title[0] != '\0')
itemMenu.SetTitle(title);
ItemData item;
char info[132];
for(int i = 0; i < items.Length; i++) {
items.GetArray(i, item);
Format(info, sizeof(info), "%d|%s", i, item.model);
itemMenu.AddItem(info, item.name);
}
itemMenu.ExitBackButton = true;
itemMenu.ExitButton = true;
itemMenu.DisplayAt(client, g_lastItemIndex[client], MENU_TIME_FOREVER);
}
void ShowItemMenu(int client, int index) {
if(g_lastCategoryIndex[client] != index) {
g_lastCategoryIndex[client] = index;
g_lastItemIndex[client] = 0; //Reset
}
CategoryData category;
g_categories.GetArray(index, category);
ShowItemMenuAny(client, category.items, category.name);
}
int SpawnItemHandler(Menu menu, MenuAction action, int client, int param2) {
if (action == MenuAction_Select) {
char info[132];
menu.GetItem(param2, info, sizeof(info));
char index[4];
int modelIndex = SplitString(info, "|", index, sizeof(index));
g_lastItemIndex[client] = StringToInt(index);
if(WallBuilder[client].PreviewModel(info[modelIndex])) {
PrintToChat(client, "\x04[Editor]\x01 Spawning: \x04%s\x01", info[modelIndex+7]);
ShowHint(client);
} else {
PrintToChat(client, "\x04[Editor]\x01 Error spawning model \x01(%s)");
}
ShowItemMenu(client, g_lastCategoryIndex[client]);
} else if(action == MenuAction_Cancel) {
if(param2 == MenuCancel_ExitBack) {
ShowCategoryList(client);
}
} else if (action == MenuAction_End) {
delete menu;
}
return 0;
}
#define SHOW_HINT_MIN_DURATION 600 // 600 s (10min)
void ShowHint(int client) {
int time = GetTime();
if(time - g_lastShowedHint[client] < SHOW_HINT_MIN_DURATION) return;
PrintToChat(client, "\x05R: \x01Change Mode");
PrintToChat(client, "\x05Middle Click: \x01Cancel Placement \x05Shift + Middle Click: \x01Place \x05Ctrl + Middle Click: \x01Change Type");
PrintToChat(client, "\x05E: \x01Rotate (hold, use mouse) \x05Left Click: \x01Rotate Axis \x05Right Click: \x01Snap Angle");
g_lastShowedHint[client] = time;
}
Action Command_Props(int client, int args) {
PrintToChat(client, "\x05Not implemented");
return Plugin_Handled;
}

View file

@ -5,12 +5,43 @@ int GLOW_WHITE[4] = { 255, 255, 255, 255 };
int GLOW_GREEN[4] = { 3, 252, 53 };
float ORIGIN_SIZE[3] = { 2.0, 2.0, 2.0 };
enum wallMode {
enum editMode {
INACTIVE = 0,
MOVE_ORIGIN,
SCALE,
FREELOOK
COLOR,
FREELOOK,
}
char MODE_NAME[5][] = {
"Error",
"Move & Rotate",
"Scale",
"Color",
"Freelook"
}
enum editFlag {
Edit_None,
Edit_Copy = 1,
Edit_Preview = 2,
Edit_WallCreator = 4
}
enum buildType {
Build_Solid,
Build_Physics,
Build_NonSolid,
}
enum CompleteType {
Complete_WallSuccess,
Complete_WallError,
Complete_PropSpawned,
Complete_PropError,
Complete_EditSuccess
}
char COLOR_INDEX[4] = "RGBA";
ArrayList createdWalls;
@ -19,25 +50,35 @@ enum struct WallBuilderData {
float mins[3];
float angles[3];
float size[3];
wallMode mode;
int color[4];
int colorIndex;
int axis;
int snapAngle;
int moveSpeed;
float moveDistance;
int entity;
bool canScale;
bool hasCollision;
bool isCopy;
editMode mode;
buildType buildType;
editFlag flags;
void Reset(bool initial = false) {
// Clear previews
if(this.entity != INVALID_ENT_REFERENCE && this.flags & Edit_Preview) {
if(IsValidEntity(this.entity))
RemoveEntity(this.entity);
}
this.entity = INVALID_ENT_REFERENCE;
this.isCopy = false;
this.size[0] = this.size[1] = this.size[2] = 5.0;
this.angles[0] = this.angles[1] = this.angles[2] = 0.0;
this.color[0] = this.color[1] = this.color[2] = this.color[3] = 255;
this.colorIndex = 0;
this.axis = 1;
this.canScale = true;
this.moveDistance = 200.0;
this.hasCollision = true;
this.flags = Edit_None;
this.buildType = Build_Solid;
this.CalculateMins();
this.SetMode(INACTIVE);
if(initial) {
@ -53,18 +94,27 @@ enum struct WallBuilderData {
}
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 {
if(this.flags & Edit_WallCreator || this.entity == INVALID_ENT_REFERENCE) {
Effect_DrawBeamBoxRotatableToAll(this.origin, this.mins, this.size, this.angles, g_iLaserIndex, 0, 0, 30, lifetime, 0.4, 0.4, 0, amplitude, color, 0);
} else {
TeleportEntity(this.entity, this.origin, this.angles, NULL_VECTOR);
}
Effect_DrawAxisOfRotationToAll(this.origin, this.angles, ORIGIN_SIZE, g_iLaserIndex, 0, 0, 30, 0.2, 0.1, 0.1, 0, 0.0, 0);
}
void UpdateEntity() {
int alpha = this.color[3];
// Keep previews transparent
if(this.flags & Edit_Preview) {
alpha = 200;
}
SetEntityRenderColor(this.entity, this.color[0], this.color[1], this.color[2], alpha);
}
bool CheckEntity(int client) {
if(this.entity != INVALID_ENT_REFERENCE) {
if(!IsValidEntity(this.entity)) {
PrintToChat(client, "\x04[Hats]\x01 Entity has vanished, editing cancelled.");
PrintToChat(client, "\x04[Editor]\x01 Entity has vanished, editing cancelled.");
this.Reset();
return false;
}
@ -76,7 +126,7 @@ enum struct WallBuilderData {
return this.mode != INACTIVE;
}
void SetMode(wallMode mode) {
void SetMode(editMode mode) {
this.mode = mode;
}
@ -91,34 +141,36 @@ enum struct WallBuilderData {
// - SCALE
// - FREECAM
case MOVE_ORIGIN: {
if(this.canScale) {
if(this.flags & Edit_WallCreator) {
this.mode = SCALE;
PrintToChat(client, "\x04[Hats]\x01 Mode: \x05Scale\x01 (Press \x04RELOAD\x01 to change mode)");
} else if(this.flags & Edit_Preview) {
this.mode = COLOR;
} 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 COLOR: {
this.mode = FREELOOK;
}
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");
}
}
PrintToChat(client, "\x04[Editor]\x01 Mode: \x05%s\x01 (Press \x04RELOAD\x01 to change mode)", MODE_NAME[this.mode]);
cmdThrottle[client] = tick;
}
void ToggleCollision(int client, float tick) {
if(tick - cmdThrottle[client] <= 0.15) return;
if(tick - cmdThrottle[client] <= 0.25) return;
this.hasCollision = !this.hasCollision
if(this.hasCollision)
PrintToChat(client, "\x04[Hats]\x01 Collision: \x05ON\x01");
PrintToChat(client, "\x04[Editor]\x01 Collision: \x05ON\x01");
else
PrintToChat(client, "\x04[Hats]\x01 Collision: \x04OFF\x01");
PrintToChat(client, "\x04[Editor]\x01 Collision: \x04OFF\x01");
cmdThrottle[client] = tick;
}
@ -126,13 +178,13 @@ enum struct WallBuilderData {
if(tick - cmdThrottle[client] <= 0.15) return;
if(this.axis == 0) {
this.axis = 1;
PrintToChat(client, "\x04[Hats]\x01 Rotate Axis: \x05HEADING (Y)\x01");
PrintToChat(client, "\x04[Editor]\x01 Rotate Axis: \x05HEADING (Y)\x01");
} else if(this.axis == 1) {
this.axis = 2;
PrintToChat(client, "\x04[Hats]\x01 Rotate Axis: \x05PITCH (X)\x01");
PrintToChat(client, "\x04[Editor]\x01 Rotate Axis: \x05PITCH (X)\x01");
} else {
this.axis = 0;
PrintToChat(client, "\x04[Hats]\x01 Rotate Axis: \x05ROLL (Z)\x01");
PrintToChat(client, "\x04[Editor]\x01 Rotate Axis: \x05ROLL (Z)\x01");
}
cmdThrottle[client] = tick;
}
@ -152,9 +204,9 @@ enum struct WallBuilderData {
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);
PrintToChat(client, "\x04[Editor]\x01 Rotate Snap Degrees: \x04(OFF)\x01", this.snapAngle);
else
PrintToChat(client, "\x04[Hats]\x01 Rotate Snap Degrees: \x05%d\x01", this.snapAngle);
PrintToChat(client, "\x04[Editor]\x01 Rotate Snap Degrees: \x05%d\x01", this.snapAngle);
cmdThrottle[client] = tick;
}
@ -162,21 +214,50 @@ enum struct WallBuilderData {
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");
// }
PrintToChat(client, "\x04[Editor]\x01 Scale Speed: \x05%d\x01", this.moveSpeed);
cmdThrottle[client] = tick;
}
int Build() {
if(!this.canScale) {
void CycleBuildType(int client) {
// No tick needed, is handled externally
if(this.buildType == Build_Physics) {
this.buildType = Build_Solid;
PrintToChat(client, "\x04[Editor]\x01 Spawn as: \x05Solid\x01");
} else if(this.buildType == Build_Solid) {
this.buildType = Build_Physics;
PrintToChat(client, "\x04[Editor]\x01 Spawn as: \x05Physics\x01");
} else {
this.buildType = Build_NonSolid;
PrintToChat(client, "\x04[Editor]\x01 Spawn as: \x05Non Solid\x01");
}
}
void CycleColorComponent(int client, float tick) {
if(tick - cmdThrottle[client] <= 0.25) return;
this.colorIndex++;
if(this.colorIndex > 3) this.colorIndex = 0;
PrintToChat(client, "\x04[Editor]\x01 Color Component: \x05%c\x01", COLOR_INDEX[this.colorIndex]);
cmdThrottle[client] = tick;
}
CompleteType Create(int& entity) {
if(this.flags & Edit_WallCreator) {
return this._FinishWall(entity) ? Complete_WallSuccess : Complete_WallError;
} else if(this.flags & Edit_Preview) {
return this._FinishPreview(entity) ? Complete_PropSpawned : Complete_PropError;
} else {
// Is edit, do nothing, just reset
this.Reset();
return -3;
entity = 0;
return Complete_EditSuccess;
}
}
bool _FinishWall(int& id) {
if(~this.flags & Edit_WallCreator) {
this.Reset();
return false;
}
// Don't need to build a new one if we editing:
int blocker = this.entity;
@ -186,7 +267,7 @@ enum struct WallBuilderData {
isEdit = true;
}
blocker = CreateEntityByName("func_brush");
if(blocker == -1) return -1;
if(blocker == -1) return false;
DispatchKeyValueVector(blocker, "mins", this.mins);
DispatchKeyValueVector(blocker, "maxs", this.size);
DispatchKeyValueVector(blocker, "boxmins", this.mins);
@ -203,7 +284,7 @@ enum struct WallBuilderData {
DispatchKeyValue(blocker, "targetname", name);
// DispatchKeyValue(blocker, "excludednpc", "player");
TeleportEntity(blocker, this.origin, this.angles, NULL_VECTOR);
if(!DispatchSpawn(blocker)) return -1;
if(!DispatchSpawn(blocker)) return false;
SetEntPropVector(blocker, Prop_Send, "m_vecMins", this.mins);
SetEntPropVector(blocker, Prop_Send, "m_vecMaxs", this.size);
SetEntProp(blocker, Prop_Send, "m_nSolidType", 2);
@ -215,7 +296,42 @@ enum struct WallBuilderData {
this.Draw(GLOW_GREEN, 5.0, 1.0);
this.Reset();
return isEdit ? -2 : createdWalls.Push(EntIndexToEntRef(blocker));
if(!isEdit) {
id = createdWalls.Push(EntIndexToEntRef(blocker));
}
return true;
}
bool _FinishPreview(int& entity) {
entity = -1;
if(this.buildType == Build_Physics)
entity = CreateEntityByName("prop_physics");
else
entity = CreateEntityByName("prop_dynamic");
if(entity == -1) return false;
char model[128];
GetEntPropString(this.entity, Prop_Data, "m_ModelName", model, sizeof(model));
DispatchKeyValue(entity, "model", model);
DispatchKeyValue(entity, "targetname", "prop_preview");
if(this.buildType == Build_NonSolid)
DispatchKeyValue(entity, "solid", "0");
else
DispatchKeyValue(entity, "solid", "6");
TeleportEntity(entity, this.origin, this.angles, NULL_VECTOR);
if(!DispatchSpawn(entity)) {
return false;
}
SetEntityRenderColor(entity, this.color[0], this.color[1], this.color[2], this.color[3]);
SetEntityRenderColor(this.entity, 255, 128, 255, 200); // reset ghost
GlowEntity(entity, 1.4)
if(this.mode == FREELOOK)
this.SetMode(MOVE_ORIGIN);
// Don't kill preview until cancel
return true;
}
int Copy() {
@ -239,11 +355,38 @@ enum struct WallBuilderData {
// SetEntProp(entity, Prop_Send, "m_nSolidType", 6);
TeleportEntity(entity, this.origin, this.angles, NULL_VECTOR);
this.entity = entity;
this.isCopy = true;
this.flags |= Edit_Copy;
return entity;
}
void Import(int entity, bool makeCopy = false, wallMode mode = SCALE) {
void StartWall() {
this.Reset();
this.flags |= Edit_WallCreator;
}
bool PreviewModel(const char[] model) {
// If last entity was preview, kill it
PrecacheModel(model);
this.Reset();
int entity = CreateEntityByName("prop_dynamic");
if(entity == -1) return false;
DispatchKeyValue(entity, "model", model);
DispatchKeyValue(entity, "targetname", "prop_preview");
DispatchKeyValue(entity, "solid", "0");
DispatchKeyValue(entity, "rendercolor", "255 128 255");
DispatchKeyValue(entity, "renderamt", "200");
DispatchKeyValue(entity, "rendermode", "1");
if(!DispatchSpawn(entity)) {
return false;
}
this.entity = entity;
this.flags |= (Edit_Copy | Edit_Preview);
this.SetMode(MOVE_ORIGIN);
// Seems some entities fail here:
return IsValidEntity(entity);
}
void Import(int entity, bool makeCopy = false, editMode mode = SCALE) {
this.Reset();
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", this.origin);
GetEntPropVector(entity, Prop_Send, "m_angRotation", this.angles);
@ -257,13 +400,65 @@ enum struct WallBuilderData {
void Cancel() {
// Delete any copies:
if(this.isCopy) {
if(this.flags & Edit_Copy || this.flags & Edit_Preview) {
RemoveEntity(this.entity);
}
this.SetMode(INACTIVE);
}
// void OpenSettings(int client) {
// Menu menu = new Menu(SettingsHandler);
// menu.AddItem("color", "Change Color");
// menu.AddItem("type", "Change Spawn Type");
// // if(this.flags & Edit_Scale)
// menu.ExitButton = true;
// // Only show back if we are for a preview
// menu.ExitBackButton = (this.flags & Edit_Preview) != 0;
// menu.Display(client, MENU_TIME_FOREVER);
// }
}
WallBuilderData WallBuilder[MAXPLAYERS+1];
// int SettingsHandler(Menu menu, MenuAction action, int client, int param2) {
// if (action == MenuAction_Select) {
// char info[8];
// menu.GetItem(param2, info, sizeof(info));
// if(StrEqual(info, "color")) {
// Menu newMenu = new Menu(ColorHandler);
// newMenu.AddItem()
// } else if(StrEqual(info, "type")) {
// } else {
// CPrintToChat(client, "\x04[Editor]\x01 Error: Unknown setting \x05%s", info);
// }
// // Re-open category list when done editing setting
// if(WallBuilder[client].flags & Edit_Preview) {
// ShowItemMenu(client, g_lastCategoryIndex[client]);
// }
// } else if(action == MenuAction_Cancel) {
// if(WallBuilder[client].flags & Edit_Preview) {
// ShowItemMenu(client, g_lastCategoryIndex[client]);
// }
// } else if (action == MenuAction_End)
// delete menu;
// return 0;
// }
// int ColorHandler(Menu menu, MenuAction action, int client, int param2) {
// if (action == MenuAction_Select) {
// char info[8];
// } else if(action == MenuAction_Cancel) {
// if(WallBuilder[client].flags & Edit_Preview) {
// ShowItemMenu(client, g_lastCategoryIndex[client]);
// }
// } else if (action == MenuAction_End)
// delete menu;
// return 0;
// }
Action OnWallClicked(int entity, int activator, int caller, UseType type, float value) {
int wallId = FindWallId(entity);
if(wallId > 0) {
@ -275,15 +470,14 @@ Action OnWallClicked(int entity, int activator, int caller, UseType type, float
return Plugin_Continue;
}
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 currently editing an entity. Finish editing your current entity with with \x05/edit done\x01 or cancel with \x04/edit cancel\x01");
ReplyToCommand(client, "\x04[Editor]\x01 You are currently editing an entity. Finish editing your current entity with with \x05/edit done\x01 or cancel with \x04/edit cancel\x01");
} else {
WallBuilder[client].Reset();
WallBuilder[client].StartWall();
if(args > 0) {
// Get values for X, Y and Z axis (defaulting to 1.0):
char arg2[8];
@ -311,15 +505,15 @@ public Action Command_MakeWall(int client, int args) {
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");
PrintToChat(client, "\x04[Editor]\x01 New Wall Started. End with \x05/wall build\x01 or \x04/wall cancel\x01");
PrintToChat(client, "\x04[Editor]\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);
PrintToChat(client, "\x04[Editor]\x01 Created Walls: \x05%d\x01", createdWalls.Length);
for(int i = 1; i <= createdWalls.Length; i++) {
GlowWall(i, GLOW_WHITE, 20.0);
}
@ -332,22 +526,36 @@ public Action Command_ManageWalls(int client, int args) {
// 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);
int entity;
CompleteType result = WallBuilder[client].Create(entity);
switch(result) {
case Complete_WallSuccess: {
if(entity > 0)
PrintToChat(client, "\x04[Editor]\x01 Wall Creation: \x05Wall #%d Created\x01", entity + 1);
else
PrintToChat(client, "\x04[Editor]\x01 Wall Edit: \x04Complete\x01");
}
case Complete_PropSpawned:
PrintToChat(client, "\x04[Editor]\x01 Prop Spawned: \x04%d\x01", entity);
case Complete_EditSuccess:
PrintToChat(client, "\x04[Editor]\x01 Entity Edited: \x04%d\x01", entity);
default:
PrintToChat(client, "\x04[Editor]\x01 Unknown result");
}
} else if(StrEqual(arg1, "cancel")) {
int flags = GetEntityFlags(client) & ~FL_FROZEN;
SetEntityFlags(client, flags);
WallBuilder[client].Cancel();
PrintToChat(client, "\x04[Hats]\x01 Wall Creation: \x04Cancelled\x01");
if(WallBuilder[client].flags & Edit_Preview)
PrintToChat(client, "\x04[Editor]\x01 Prop Spawer: \x04Done\x01");
else if(WallBuilder[client].flags & Edit_WallCreator) {
PrintToChat(client, "\x04[Editor]\x01 Wall Creation: \x04Cancelled\x01");
} else {
PrintToChat(client, "\x04[Editor]\x01 Entity Edit: \x04Cancelled\x01");
}
} else if(StrEqual(arg1, "export")) {
// TODO: support exp #id
float origin[3], angles[3], size[3];
@ -372,29 +580,29 @@ public Action Command_ManageWalls(int client, int args) {
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>");
PrintToChat(client, "\x04[Editor]\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");
PrintToChat(client, "\x04[Editor]\x01 Deleted current entity");
} else {
PrintToChat(client, "\x04[Hats]\x01 Cannot delete player entities.");
PrintToChat(client, "\x04[Editor]\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);
PrintToChat(client, "\x04[Editor]\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);
PrintToChat(client, "\x04[Editor]\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]");
ReplyToCommand(client, "\x04[Editor]\x01 Syntax: /mkwall [size x] [size y] [size z]");
} else if(StrEqual(arg1, "toggle")) {
if(StrEqual(arg2, "all")) {
int walls = createdWalls.Length;
@ -403,20 +611,20 @@ public Action Command_ManageWalls(int client, int args) {
AcceptEntityInput(entity, "Toggle");
GlowWall(i, GLOW_BLUE);
}
PrintToChat(client, "\x04[Hats]\x01 Toggled \x05%d\x01 walls", walls);
PrintToChat(client, "\x04[Editor]\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, GLOW_BLUE);
PrintToChat(client, "\x04[Hats]\x01 Toggled Wall: \x05#%d\x01", id);
PrintToChat(client, "\x04[Editor]\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");
ReplyToCommand(client, "\x04[Editor]\x01 Syntax: \x05/walls filter <id/all> <filter type>\x04");
ReplyToCommand(client, "\x04[Editor]\x01 Valid filters: \x05player");
return Plugin_Handled;
}
@ -430,13 +638,13 @@ public Action Command_ManageWalls(int client, int args) {
int entity = GetWallEntity(i);
AcceptEntityInput(entity, "SetExcluded");
}
PrintToChat(client, "\x04[Hats]\x01 Set %d walls' filter to \x05%s\x01", walls, arg3);
PrintToChat(client, "\x04[Editor]\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);
PrintToChat(client, "\x04[Editor]\x01 Set wall #%d filter to \x05%s\x01", id, arg3);
}
}
} else if(StrEqual(arg1, "edit")) {
@ -444,31 +652,30 @@ public Action Command_ManageWalls(int client, int args) {
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");
PrintToChat(client, "\x04[Editor]\x01 Editing wall \x05%d\x01. End with \x05/wall done\x01 or \x04/wall cancel\x01", id);
PrintToChat(client, "\x04[Editor]\x01 Mode: \x05Scale\x01");
}
} else if(StrEqual(arg1, "edite") || (arg1[0] == 'c' && arg1[1] == 'u')) {
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");
PrintToChat(client, "\x04[Editor]\x01 Editing entity \x05%d (%s) [%s]\x01. End with \x05/wall done\x01", index, classname, targetname);
PrintToChat(client, "\x04[Editor]\x01 Mode: \x05Move & Rotate\x01");
} else {
ReplyToCommand(client, "\x04[Hats]\x01 Invalid or non existent entity");
ReplyToCommand(client, "\x04[Editor]\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");
PrintToChat(client, "\x04[Editor]\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/edit done\x01 or \x04/edit cancel\x01", entity, oldEntity);
PrintToChat(client, "\x04[Editor]\x01 Editing copy \x05%d\x01 of entity \x05%d\x01. End with \x05/edit done\x01 or \x04/edit cancel\x01", entity, oldEntity);
}
} else {
int id = GetWallId(client, arg2);
@ -476,8 +683,8 @@ public Action Command_ManageWalls(int client, int args) {
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");
PrintToChat(client, "\x04[Editor]\x01 Editing copy of wall \x05%d\x01. End with \x05/wall build\x01 or \x04/wall cancel\x01", id);
PrintToChat(client, "\x04[Editor]\x01 Mode: \x05Scale\x01");
}
}
} else if(StrEqual(arg1, "list")) {
@ -486,7 +693,7 @@ public Action Command_ManageWalls(int client, int args) {
ReplyToCommand(client, "Wall #%d - EntIndex: %d", i, EntRefToEntIndex(entity));
}
} else {
ReplyToCommand(client, "\x04[Hats]\x01 See console for list of commands");
ReplyToCommand(client, "\x04[Editor]\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);
@ -506,13 +713,13 @@ int GetWallId(int client, const char[] arg) {
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.");
ReplyToCommand(client, "\x04[Editor]\x01 The wall with specified id no longer exists.");
createdWalls.Erase(id - 1);
return -2;
}
return id;
} else {
ReplyToCommand(client, "\x04[Hats]\x01 Invalid wall id, must be between 0 - %d", createdWalls.Length - 1 );
ReplyToCommand(client, "\x04[Editor]\x01 Invalid wall id, must be between 0 - %d", createdWalls.Length - 1 );
return -1;
}
}
@ -567,7 +774,7 @@ void DeleteWall(int id) {
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);
PrintToServer("[Editor] Export: Cannot open \"%s\", cant write", sPath);
}
PrintWriteLine(client, file, "{");
@ -600,59 +807,4 @@ void PrintWriteLine(int client, File file, const char[] format, any ...) {
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;
}
// TODO: implement models to spawn
// 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;
}
}

View file

@ -913,8 +913,8 @@ stock void GlowPoint(const float pos[3], float lifetime = 5.0) {
CreateTimer(lifetime, Timer_KillEntity, entity);
}
stock void GlowEntity(int entity, float lifetime = 10.0) {
L4D2_SetEntityGlow(entity, L4D2Glow_Constant, 10000, 0, _glowColor, false);
stock void GlowEntity(int entity, float lifetime = 10.0, int glowColor[3] = _glowColor) {
L4D2_SetEntityGlow(entity, L4D2Glow_Constant, 10000, 0, glowColor, false);
CreateTimer(lifetime, Timer_ClearGlow, EntIndexToEntRef(entity));
}

View file

@ -38,6 +38,13 @@ ConVar cvar_sm_hats_max_distance;
TopMenu g_topMenu;
int g_lastCategoryIndex[MAXPLAYERS+1];
int g_lastItemIndex[MAXPLAYERS+1];
int g_lastShowedHint[MAXPLAYERS+1];
bool g_isSearchActive[MAXPLAYERS+1];
char g_currentMap[64];
#include <hats/walls.sp>
#include <hats/hats.sp>
#include <hats/hat_presets.sp>
@ -62,6 +69,7 @@ public void OnPluginStart() {
}
createdWalls = new ArrayList();
g_spawnedItems = new ArrayList(2);
LoadTranslations("common.phrases");
HookEvent("player_entered_checkpoint", OnEnterSaferoom);
@ -75,6 +83,7 @@ public void OnPluginStart() {
RegAdminCmd("sm_mkwall", Command_MakeWall, ADMFLAG_CHEATS);
RegAdminCmd("sm_walls", Command_ManageWalls, ADMFLAG_CHEATS);
RegAdminCmd("sm_wall", Command_ManageWalls, ADMFLAG_CHEATS);
RegAdminCmd("sm_prop", Command_Props, ADMFLAG_CHEATS);
RegConsoleCmd("sm_hatp", Command_DoAHatPreset);
cvar_sm_hats_blacklist_enabled = CreateConVar("sm_hats_blacklist_enabled", "1", "Is the prop blacklist enabled", FCVAR_NONE, true, 0.0, true, 1.0);
@ -106,6 +115,11 @@ public void OnPluginStart() {
}
LoadPresets();
TopMenu topMenu;
if (LibraryExists("adminmenu") && ((topMenu = GetAdminTopMenu()) != null)) {
OnAdminMenuReady(topMenu);
}
}
public void OnLibraryRemoved(const char[] name) {
@ -116,23 +130,6 @@ public void OnLibraryRemoved(const char[] name) {
///////////////////////////////////////////////////////////////////////////////////////////////
public void Event_ItemPickup(Event event, const char[] name, bool dontBroadcast) {
int client = GetClientOfUserId(event.GetInt("userid"));
// Check if an item picked up a user's hat and do nothing...
// for(int slot = 0; slot <= 5; slot++) {
// int wpn = GetPlayerWeaponSlot(client, slot);
// for(int i = 1; i <= MaxClients; i++) {
// if(i != client && IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i)) {
// int hat = GetHat(i);
// if(hat == wpn) {
// break;
// }
// }
// }
// }
}
public void OnEnterSaferoom(Event event, const char[] name, bool dontBroadcast) {
int userid = event.GetInt("userid");
int client = GetClientOfUserId(userid);
@ -172,12 +169,6 @@ Action Timer_PlaceHat(Handle h, int userid) {
return Plugin_Handled;
}
Action Timer_Kill(Handle h, int entity) {
if(IsValidEntity(entity))
RemoveEntity(entity);
return Plugin_Handled;
}
// Tries to find a valid location at user's cursor, avoiding placing into solid walls (such as invisible walls) or objects
stock bool GetSmartCursorLocation(int client, float outPos[3]) {
float start[3], angle[3], ceilPos[3], wallPos[3], normal[3];
@ -199,7 +190,7 @@ stock bool GetSmartCursorLocation(int client, float outPos[3]) {
bool ceilCollided = TR_DidHit();
bool ceilOK = !TR_AllSolid();
TR_GetEndPosition(ceilPos);
float distCeil = GetVectorDistance(outPos, ceilPos, true);
// float distCeil = GetVectorDistance(outPos, ceilPos, true);
// Find a suitable position backwards
angle[0] = 70.0;
@ -334,7 +325,7 @@ void CheckKill(int ref, int client) {
Action Timer_PropYeetEnd(Handle h, DataPack pack) {
pack.Reset();
int realEnt = EntRefToEntIndex(pack.ReadCell());
int visibleEnt = EntRefToEntIndex(pack.ReadCell());
// int visibleEnt = EntRefToEntIndex(pack.ReadCell());
// if(IsValidEntity(visibleEnt)) {
// float pos[3], ang[3];
// GetEntPropVector(visibleEnt, Prop_Send, "m_vecOrigin", pos);
@ -488,7 +479,7 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
ClearParent(entity);
// If hat is not a player, we teleport them to the void (0, 0, 0)
// Otherwise, we just simply dismount the player while hatter is on ladder
// Ostherwise, we just simply dismount the player while hatter is on ladder
if(entity >= MaxClients)
TeleportEntity(entity, EMPTY_ANG, NULL_VECTOR, NULL_VECTOR);
if(visibleEntity > 0) {
@ -584,30 +575,35 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
bool allowMove = true;
switch(WallBuilder[client].mode) {
case MOVE_ORIGIN: {
SetWeaponDelay(client, 0.5);
bool isRotate;
int flags = GetEntityFlags(client);
if(buttons & IN_USE) {
PrintCenterText(client, "%.1f %.1f %.1f", WallBuilder[client].angles[0], WallBuilder[client].angles[1], WallBuilder[client].angles[2]);
isRotate = true;
SetEntityFlags(client, flags |= FL_FROZEN);
if(buttons & IN_ATTACK) WallBuilder[client].CycleAxis(client, tick);
else if(buttons & IN_ATTACK2) WallBuilder[client].CycleSnapAngle(client, tick);
// Rotation control:
if(tick - cmdThrottle[client] > 0.20) {
if(WallBuilder[client].axis == 0) {
if(mouse[1] > 10) WallBuilder[client].angles[0] += WallBuilder[client].snapAngle;
else if(mouse[1] < -10) WallBuilder[client].angles[0] -= WallBuilder[client].snapAngle;
} else if(WallBuilder[client].axis == 1) {
if(mouse[0] > 10) WallBuilder[client].angles[1] += WallBuilder[client].snapAngle;
else if(mouse[0] < -10) WallBuilder[client].angles[1] -= WallBuilder[client].snapAngle;
} else {
if(mouse[1] > 10) WallBuilder[client].angles[2] += WallBuilder[client].snapAngle;
else if(mouse[1] < -10) WallBuilder[client].angles[2] -= WallBuilder[client].snapAngle;
if(buttons & IN_USE && ~buttons & IN_ZOOM) {
if(buttons & IN_SPEED) {
WallBuilder[client].ToggleCollision(client, tick);
} else {
PrintCenterText(client, "%.1f %.1f %.1f", WallBuilder[client].angles[0], WallBuilder[client].angles[1], WallBuilder[client].angles[2]);
isRotate = true;
SetEntityFlags(client, flags |= FL_FROZEN);
if(buttons & IN_ATTACK) WallBuilder[client].CycleAxis(client, tick);
else if(buttons & IN_ATTACK2) WallBuilder[client].CycleSnapAngle(client, tick);
// Rotation control:
if(tick - cmdThrottle[client] > 0.20) {
if(WallBuilder[client].axis == 0) {
if(mouse[1] > 10) WallBuilder[client].angles[0] += WallBuilder[client].snapAngle;
else if(mouse[1] < -10) WallBuilder[client].angles[0] -= WallBuilder[client].snapAngle;
} else if(WallBuilder[client].axis == 1) {
if(mouse[0] > 10) WallBuilder[client].angles[1] += WallBuilder[client].snapAngle;
else if(mouse[0] < -10) WallBuilder[client].angles[1] -= WallBuilder[client].snapAngle;
} else {
if(mouse[1] > 10) WallBuilder[client].angles[2] += WallBuilder[client].snapAngle;
else if(mouse[1] < -10) WallBuilder[client].angles[2] -= WallBuilder[client].snapAngle;
}
cmdThrottle[client] = tick;
}
cmdThrottle[client] = tick;
}
} else {
// Move position
@ -620,9 +616,6 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
flags = flags & ~FL_FROZEN;
SetEntityFlags(client, flags);
}
if(buttons & IN_SPEED) {
WallBuilder[client].ToggleCollision(client, tick);
}
GetCursorLimited2(client, WallBuilder[client].moveDistance, WallBuilder[client].origin, Filter_IgnorePlayerAndWall, WallBuilder[client].hasCollision);
}
@ -659,10 +652,44 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
WallBuilder[client].CalculateMins();
}
}
case COLOR: {
SetWeaponDelay(client, 0.5);
PrintCenterText(client, "%d %d %d %d", WallBuilder[client].color[0], WallBuilder[client].color[1], WallBuilder[client].color[2], WallBuilder[client].color[3]);
if(buttons & IN_USE) {
WallBuilder[client].CycleColorComponent(client, tick);
} else if(buttons & IN_ATTACK) {
WallBuilder[client].color[WallBuilder[client].colorIndex]--;
if(WallBuilder[client].color[WallBuilder[client].colorIndex] < 0) {
WallBuilder[client].color[WallBuilder[client].colorIndex] = 0;
}
WallBuilder[client].UpdateEntity();
allowMove = false;
} else if(buttons & IN_ATTACK2) {
WallBuilder[client].color[WallBuilder[client].colorIndex]++;
if(WallBuilder[client].color[WallBuilder[client].colorIndex] > 255) {
WallBuilder[client].color[WallBuilder[client].colorIndex] = 255;
}
WallBuilder[client].UpdateEntity();
allowMove = false;
}
}
}
switch(buttons) {
case IN_RELOAD: WallBuilder[client].CycleMode(client, tick); // R: Cycle forward
if(buttons & IN_RELOAD)
WallBuilder[client].CycleMode(client, tick); // R: Cycle forward
else if(WallBuilder[client].flags & Edit_Preview && tick - cmdThrottle[client] >= 0.25 && buttons & IN_ZOOM) {
if(buttons & IN_SPEED) {
int entity;
WallBuilder[client].Create(entity);
int index = g_spawnedItems.Push(EntIndexToEntRef(entity));
g_spawnedItems.Set(index, GetClientUserId(client), 1);
} else if(buttons & IN_DUCK) {
WallBuilder[client].CycleBuildType(client);
} else {
WallBuilder[client].Cancel();
CPrintToChat(client, "\x04[Editor]\x01 Cancelled.");
}
cmdThrottle[client] = tick;
}
WallBuilder[client].Draw(BUILDER_COLOR, 0.1, 0.1);
@ -723,6 +750,10 @@ public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast
public void OnClientDisconnect(int client) {
tempGod[client] = false;
WallBuilder[client].Reset();
g_isSearchActive[client] = false;
g_lastCategoryIndex[client] = 0;
g_lastItemIndex[client] = 0;
g_lastShowedHint[client] = 0;
hatData[client].yeetGroundTimer = null;
ClearHat(client, true);
}
@ -747,12 +778,14 @@ public void OnMapStart() {
cmdThrottle[i] = 0.0;
tempGod[i] = false;
}
GetCurrentMap(g_currentMap, sizeof(g_currentMap));
NavAreas = GetSpawnLocations();
}
public void OnMapEnd() {
delete NavAreas;
g_spawnedItems.Clear();
for(int i = 1; i <= createdWalls.Length; i++) {
if(hatData[i].yeetGroundTimer != null) {
delete hatData[i].yeetGroundTimer;
@ -762,6 +795,8 @@ public void OnMapEnd() {
}
createdWalls.Clear();
ClearHats();
UnloadCategories();
UnloadSave();
}
public void OnPluginEnd() {
ClearHats();
@ -771,6 +806,14 @@ public void OnPluginEnd() {
SetEntityFlags(i, flags);
}
}
if(g_spawnedItems != null) {
for(int i = 0; i < g_spawnedItems.Length; i++) {
int ref = g_spawnedItems.Get(i);
RemoveEntity(ref);
}
delete g_spawnedItems;
}
TriggerInput("prop_preview", "Kill");
}
public bool TraceEntityFilterPlayer(int entity, int contentsMask, any data) {
@ -780,7 +823,6 @@ public bool TraceEntityFilterPlayer(int entity, int contentsMask, any data) {
return entity != data;
}
int GetLookingEntity(int client, TraceEntityFilter filter) {
static float pos[3], ang[3];
GetClientEyePosition(client, pos);
@ -844,7 +886,16 @@ bool CheckBlacklist(int entity) {
////////////////////////////////
stock void TriggerInput(const char[] targetName, const char[] input) {
int entity = -1;
char _targetName[32];
while((entity = FindEntityByClassname(entity, "*")) != INVALID_ENT_REFERENCE) {
GetEntPropString(entity, Prop_Data, "m_iName", _targetName, sizeof(_targetName));
if(StrEqual(_targetName, targetName)) {
AcceptEntityInput(entity, input);
}
}
}
stock bool FindGround(const float start[3], float end[3]) {