mirror of
https://github.com/Jackzmc/sourcemod-plugins.git
synced 2025-05-06 05:23:20 +00:00
Split out editor from hats
This commit is contained in:
parent
c71924e9e1
commit
89d4cfbd17
16 changed files with 3391 additions and 6566 deletions
BIN
plugins/l4d2_editor.smx
Normal file
BIN
plugins/l4d2_editor.smx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load diff
|
@ -320,12 +320,6 @@ Action Command_DoAHat(int client, int args) {
|
||||||
PrintToChat(client, "[Hats] Placed hat in front of you.");
|
PrintToChat(client, "[Hats] Placed hat in front of you.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(arg[0] == 'd') {
|
|
||||||
// Use the new wall editor
|
|
||||||
Editor[client].Reset();
|
|
||||||
Editor[client].entity = EntIndexToEntRef(entity);
|
|
||||||
Editor[client].SetMode(MOVE_ORIGIN);
|
|
||||||
PrintToChat(client, "\x04[Hats] \x01Beta Prop Mover active for \x04%d", entity);
|
|
||||||
} else {
|
} else {
|
||||||
PrintToChat(client, "[Hats] Restored hat to its original position.");
|
PrintToChat(client, "[Hats] Restored hat to its original position.");
|
||||||
}
|
}
|
||||||
|
@ -345,7 +339,8 @@ Action Command_DoAHat(int client, int args) {
|
||||||
if(entity <= 0) {
|
if(entity <= 0) {
|
||||||
PrintCenterText(client, "[Hats] No entity found");
|
PrintCenterText(client, "[Hats] No entity found");
|
||||||
return Plugin_Handled;
|
return Plugin_Handled;
|
||||||
} else if(entity == EntRefToEntIndex(Editor[client].entity)) {
|
} else if(false) {
|
||||||
|
// TODO: editor API call to check
|
||||||
// Prevent making an entity you editing a hat
|
// Prevent making an entity you editing a hat
|
||||||
return Plugin_Handled;
|
return Plugin_Handled;
|
||||||
} else if(!isForced && cvar_sm_hats_max_distance.FloatValue > 0.0 && entity >= MaxClients) {
|
} else if(!isForced && cvar_sm_hats_max_distance.FloatValue > 0.0 && entity >= MaxClients) {
|
||||||
|
@ -378,7 +373,8 @@ Action Command_DoAHat(int client, int args) {
|
||||||
} else if(!isForced && GetClientTeam(entity) != 2 && ~cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_InfectedHats)) {
|
} else if(!isForced && GetClientTeam(entity) != 2 && ~cvar_sm_hats_flags.IntValue & view_as<int>(HatConfig_InfectedHats)) {
|
||||||
PrintToChat(client, "[Hats] Cannot make enemy a hat... it's dangerous");
|
PrintToChat(client, "[Hats] Cannot make enemy a hat... it's dangerous");
|
||||||
return Plugin_Handled;
|
return Plugin_Handled;
|
||||||
} else if(entity == EntRefToEntIndex(Editor[client].entity)) {
|
} else if(false) {
|
||||||
|
// TODO: use editor API to check
|
||||||
// Old check left in in case you hatting child entity
|
// Old check left in in case you hatting child entity
|
||||||
PrintToChat(client, "[Hats] You are currently editing this entity");
|
PrintToChat(client, "[Hats] You are currently editing this entity");
|
||||||
return Plugin_Handled;
|
return Plugin_Handled;
|
||||||
|
|
|
@ -1,122 +0,0 @@
|
||||||
|
|
||||||
int Native_StartEdit(Handle plugin, int numParams) {
|
|
||||||
int client = GetNativeCell(1);
|
|
||||||
int entity = GetNativeCell(2);
|
|
||||||
Editor[client].Import(entity, false);
|
|
||||||
PrivateForward fwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell);
|
|
||||||
fwd.AddFunction(INVALID_HANDLE, GetNativeFunction(3));
|
|
||||||
Editor[client].SetCallback(fwd, true);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int Native_StartSpawner(Handle plugin, int numParams) {
|
|
||||||
int client = GetNativeCell(1);
|
|
||||||
g_PropData[client].Selector.Cancel();
|
|
||||||
PrivateForward fwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell);
|
|
||||||
fwd.AddFunction(INVALID_HANDLE, GetNativeFunction(2));
|
|
||||||
Editor[client].SetCallback(fwd, false);
|
|
||||||
ShowCategoryList(client, ROOT_CATEGORY);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int Native_CancelEdit(Handle plugin, int numParams) {
|
|
||||||
int client = GetNativeCell(1);
|
|
||||||
Editor[client].Cancel();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int Native_IsEditorActive(Handle plugin, int numParams) {
|
|
||||||
int client = GetNativeCell(1);
|
|
||||||
Editor[client].IsActive();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Native_StartSelector(Handle plugin, int numParams) {
|
|
||||||
int client = GetNativeCell(1);
|
|
||||||
int color[3] = { 0, 255, 0 };
|
|
||||||
PrivateForward fwd = new PrivateForward(ET_Single, Param_Cell, Param_Cell);
|
|
||||||
fwd.AddFunction(plugin, GetNativeFunction(2));
|
|
||||||
GetNativeArray(3, color, 3);
|
|
||||||
int limit = GetNativeCell(4);
|
|
||||||
g_PropData[client].Selector.Start(color, 0, limit);
|
|
||||||
g_PropData[client].Selector.SetOnEnd(fwd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int Native_CancelSelector(Handle plugin, int numParams) {
|
|
||||||
int client = GetNativeCell(1);
|
|
||||||
g_PropData[client].Selector.Cancel();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int Native_IsSelectorActive(Handle plugin, int numParams) {
|
|
||||||
int client = GetNativeCell(1);
|
|
||||||
g_PropData[client].Selector.IsActive();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int Native_Selector_Start(Handle plugin, int numParams) {
|
|
||||||
int client = GetNativeCell(1);
|
|
||||||
int color[3] = { 0, 255, 0 };
|
|
||||||
GetNativeArray(2, color, 3);
|
|
||||||
int flags = GetNativeCell(3);
|
|
||||||
int limit = GetNativeCell(4);
|
|
||||||
g_PropData[client].Selector.Start(color, flags, limit);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int Native_Selector_GetCount(Handle plugin, int numParams) {
|
|
||||||
int client = GetNativeCell(1);
|
|
||||||
if(!g_PropData[client].Selector.IsActive()) {
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
return g_PropData[client].Selector.list.Length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int Native_Selector_GetActive(Handle plugin, int numParams) {
|
|
||||||
int client = GetNativeCell(1);
|
|
||||||
return g_PropData[client].Selector.IsActive();
|
|
||||||
}
|
|
||||||
int Native_Selector_SetOnEnd(Handle plugin, int numParams) {
|
|
||||||
int client = GetNativeCell(1);
|
|
||||||
PrivateForward fwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell);
|
|
||||||
fwd.AddFunction(plugin, GetNativeFunction(2));
|
|
||||||
g_PropData[client].Selector.SetOnEnd(fwd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int Native_Selector_SetOnPreSelect(Handle plugin, int numParams) {
|
|
||||||
int client = GetNativeCell(1);
|
|
||||||
PrivateForward fwd = new PrivateForward(ET_Single, Param_Cell, Param_Cell);
|
|
||||||
if(!fwd.AddFunction(plugin, GetNativeFunction(2))) return 0;
|
|
||||||
g_PropData[client].Selector.SetOnPreSelect(fwd);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
int Native_Selector_SetOnPostSelect(Handle plugin, int numParams) {
|
|
||||||
int client = GetNativeCell(1);
|
|
||||||
PrivateForward fwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell);
|
|
||||||
if(!fwd.AddFunction(plugin, GetNativeFunction(2))) return 0;
|
|
||||||
g_PropData[client].Selector.SetOnPostSelect(fwd);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
int Native_Selector_SetOnUnselect(Handle plugin, int numParams) {
|
|
||||||
int client = GetNativeCell(1);
|
|
||||||
PrivateForward fwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell);
|
|
||||||
if(!fwd.AddFunction(plugin, GetNativeFunction(2))) return 0;
|
|
||||||
g_PropData[client].Selector.SetOnUnselect(fwd);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
int Native_Selector_AddEntity(Handle plugin, int numParams) {
|
|
||||||
int client = GetNativeCell(1);
|
|
||||||
int entity = GetNativeCell(2);
|
|
||||||
g_PropData[client].Selector.AddEntity(entity, false);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int Native_Selector_RemoveEntity(Handle plugin, int numParams) {
|
|
||||||
int client = GetNativeCell(1);
|
|
||||||
int entity = GetNativeCell(2);
|
|
||||||
g_PropData[client].Selector.RemoveEntity(entity);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int Native_Selector_Cancel(Handle plugin, int numParams) {
|
|
||||||
int client = GetNativeCell(1);
|
|
||||||
g_PropData[client].Selector.Cancel();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int Native_Selector_End(Handle plugin, int numParams) {
|
|
||||||
int client = GetNativeCell(1);
|
|
||||||
g_PropData[client].Selector.End();
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,569 +0,0 @@
|
||||||
int g_pendingSaveClient;
|
|
||||||
ArrayList g_previewItems;
|
|
||||||
CategoryData ROOT_CATEGORY;
|
|
||||||
ArrayList g_spawnedItems; // ArrayList(block=2)<entRef, [creator]>
|
|
||||||
ArrayList g_savedItems; // ArrayList<entRef>
|
|
||||||
StringMap g_recentItems; // Key: model[128], value: RecentEntry
|
|
||||||
|
|
||||||
/* Wish to preface this file:
|
|
||||||
* It's kinda messy. The main structs are:
|
|
||||||
* - ItemData
|
|
||||||
* - CategoryData
|
|
||||||
|
|
||||||
The rest are kinda necessary, for sorting reasons (SearchData, RecentEntry).
|
|
||||||
|
|
||||||
*/
|
|
||||||
enum ChatPrompt {
|
|
||||||
Prompt_None,
|
|
||||||
Prompt_Search,
|
|
||||||
Prompt_SaveScene,
|
|
||||||
Prompt_SaveSchematic
|
|
||||||
}
|
|
||||||
enum SaveType {
|
|
||||||
Save_None,
|
|
||||||
Save_Scene,
|
|
||||||
Save_Schematic
|
|
||||||
}
|
|
||||||
|
|
||||||
int GLOW_MANAGER[3] = { 52, 174, 235 };
|
|
||||||
|
|
||||||
enum struct Schematic {
|
|
||||||
char name[64];
|
|
||||||
char creatorSteamid[32];
|
|
||||||
char creatorName[32];
|
|
||||||
ArrayList entities;
|
|
||||||
|
|
||||||
void Reset() {
|
|
||||||
this.name[0] = '\0';
|
|
||||||
this.creatorSteamid[0] = '\0';
|
|
||||||
this.creatorName[0] = '\0';
|
|
||||||
if(this.entities != null) delete this.entities;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddEntity(int entity, int client) {
|
|
||||||
SaveData save;
|
|
||||||
save.FromEntity(entity);
|
|
||||||
this.entities.PushArray(save);
|
|
||||||
}
|
|
||||||
|
|
||||||
void New(int client, const char[] name) {
|
|
||||||
if(client > 0) {
|
|
||||||
GetClientName(client, this.creatorName, sizeof(this.creatorName));
|
|
||||||
GetClientAuthId(client, AuthId_Steam2, this.creatorSteamid, sizeof(this.creatorSteamid));
|
|
||||||
}
|
|
||||||
strcopy(this.name, sizeof(this.name), name);
|
|
||||||
this.entities = new ArrayList(sizeof(SaveData));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Save() {
|
|
||||||
char path[PLATFORM_MAX_PATH];
|
|
||||||
BuildPath(Path_SM, path, sizeof(path), "data/prop_spawner/schematics/%s.schem", this.name);
|
|
||||||
CreateDirectory("data/prop_spawner/schematics", 0775);
|
|
||||||
KeyValues kv = new KeyValues(this.name);
|
|
||||||
kv.SetString("creator_steamid", this.creatorSteamid);
|
|
||||||
kv.SetString("creator_name", this.creatorName);
|
|
||||||
kv.JumpToKey("entities");
|
|
||||||
this.entities = new ArrayList(sizeof(SaveData));
|
|
||||||
SaveData ent;
|
|
||||||
while(kv.GotoNextKey()) {
|
|
||||||
kv.GetVector("offset", ent.origin);
|
|
||||||
kv.GetVector("angles", ent.angles);
|
|
||||||
kv.GetColor4("color", ent.color);
|
|
||||||
kv.GetString("model", ent.model, sizeof(ent.model));
|
|
||||||
this.entities.PushArray(ent);
|
|
||||||
}
|
|
||||||
kv.ExportToFile(path);
|
|
||||||
delete kv;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Import(const char[] name) {
|
|
||||||
char path[PLATFORM_MAX_PATH];
|
|
||||||
BuildPath(Path_SM, path, sizeof(path), "data/prop_spawner/schematics/%s.schem", name);
|
|
||||||
KeyValues kv = new KeyValues("root");
|
|
||||||
if(kv.ImportFromFile(path)) {
|
|
||||||
delete kv;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
strcopy(this.name, sizeof(this.name), name);
|
|
||||||
kv.GetString("creator_steamid", this.creatorSteamid, sizeof(this.creatorSteamid));
|
|
||||||
kv.GetString("creator_name", this.creatorName, sizeof(this.creatorName));
|
|
||||||
kv.JumpToKey("entities");
|
|
||||||
this.entities = new ArrayList(sizeof(SaveData));
|
|
||||||
SaveData ent;
|
|
||||||
while(kv.GotoNextKey()) {
|
|
||||||
kv.GetVector("offset", ent.origin);
|
|
||||||
kv.GetVector("angles", ent.angles);
|
|
||||||
kv.GetColor4("color", ent.color);
|
|
||||||
kv.GetString("model", ent.model, sizeof(ent.model));
|
|
||||||
this.entities.PushArray(ent);
|
|
||||||
}
|
|
||||||
delete kv;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Spawns all schematics entities, returns list of entities, first being parent.
|
|
||||||
ArrayList SpawnEntities(const float origin[3], bool asPreview = true) {
|
|
||||||
if(this.entities == null) return null;
|
|
||||||
SaveData ent;
|
|
||||||
int parent = -1;
|
|
||||||
ArrayList spawnedEntities = new ArrayList();
|
|
||||||
for(int i = 0; i < this.entities.Length; i++) {
|
|
||||||
this.entities.GetArray(i, ent, sizeof(ent));
|
|
||||||
int entity = ent.ToEntity(origin, asPreview);
|
|
||||||
spawnedEntities.Push(EntIndexToEntRef(entity));
|
|
||||||
if(i == 0) {
|
|
||||||
SetParent(entity, parent)
|
|
||||||
} else {
|
|
||||||
parent = entity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return spawnedEntities;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public any Native_SpawnSchematic(Handle plugin, int numParams) {
|
|
||||||
char name[32];
|
|
||||||
float pos[3];
|
|
||||||
float ang[3];
|
|
||||||
GetNativeString(0, name, sizeof(name));
|
|
||||||
GetNativeArray(1, pos, 3);
|
|
||||||
GetNativeArray(1, ang, 3);
|
|
||||||
Schematic schem;
|
|
||||||
if(!schem.Import(name)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ArrayList list = schem.SpawnEntities(pos, false);
|
|
||||||
delete list;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum struct PropSelectorIterator {
|
|
||||||
ArrayList _list;
|
|
||||||
int _index;
|
|
||||||
int Entity;
|
|
||||||
|
|
||||||
void _Init(ArrayList list) {
|
|
||||||
this._list = list;
|
|
||||||
this._index = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Next() {
|
|
||||||
this._index++;
|
|
||||||
return this._index + 1 < this._list.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum struct PropSelector {
|
|
||||||
int selectColor[3];
|
|
||||||
int limit;
|
|
||||||
ArrayList list;
|
|
||||||
PrivateForward endCallback;
|
|
||||||
PrivateForward selectPreCallback;
|
|
||||||
PrivateForward selectPostCallback;
|
|
||||||
PrivateForward unSelectCallback;
|
|
||||||
int _client;
|
|
||||||
|
|
||||||
PropSelectorIterator Iter() {
|
|
||||||
PropSelectorIterator iter;
|
|
||||||
iter._Init(this.list);
|
|
||||||
return iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reset() {
|
|
||||||
if(this.endCallback) delete this.endCallback;
|
|
||||||
if(this.selectPreCallback) delete this.selectPreCallback;
|
|
||||||
if(this.selectPostCallback) delete this.selectPostCallback;
|
|
||||||
if(this.unSelectCallback) delete this.unSelectCallback;
|
|
||||||
if(this.list) delete this.list;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Start(int color[3], int flags = 0, int limit = 0) {
|
|
||||||
this.selectColor = color;
|
|
||||||
this.limit = 0;
|
|
||||||
this.list = new ArrayList();
|
|
||||||
SendEditorMessage(this._client, "Left click to select, right click to unselect");
|
|
||||||
SendEditorMessage(this._client, "Press WALK+USE to confirm, DUCK+USE to cancel");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetOnEnd(PrivateForward callback) {
|
|
||||||
this.endCallback = callback;
|
|
||||||
}
|
|
||||||
void SetOnPreSelect(PrivateForward callback) {
|
|
||||||
this.selectPreCallback = callback;
|
|
||||||
}
|
|
||||||
void SetOnPostSelect(PrivateForward callback) {
|
|
||||||
this.selectPostCallback = callback;
|
|
||||||
}
|
|
||||||
void SetOnUnselect(PrivateForward callback) {
|
|
||||||
this.unSelectCallback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
void StartDirect(int color[3], SelectDoneCallback callback, int limit = 0) {
|
|
||||||
PrivateForward fwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell);
|
|
||||||
fwd.AddFunction(INVALID_HANDLE, callback);
|
|
||||||
this.Start(color, 0, limit);
|
|
||||||
this.SetOnEnd(fwd);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsActive() {
|
|
||||||
return this.list != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
void End() {
|
|
||||||
if(this.list == null) return;
|
|
||||||
SendEditorMessage(this._client, "Selection completed");
|
|
||||||
// Reset glows, remove selection from our spawned props
|
|
||||||
for(int i = 0; i < this.list.Length; i++) {
|
|
||||||
int ref = this.list.Get(i);
|
|
||||||
if(IsValidEntity(ref)) {
|
|
||||||
L4D2_RemoveEntityGlow(ref);
|
|
||||||
RemoveSpawnedProp(ref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(this.endCallback) {
|
|
||||||
if(GetForwardFunctionCount(this.endCallback) == 0) {
|
|
||||||
PrintToServer("[Editor] Warn: Selector.End(): callback has no functions assigned to it.");
|
|
||||||
}
|
|
||||||
Call_StartForward(this.endCallback);
|
|
||||||
Call_PushCell(this._client);
|
|
||||||
Call_PushCell(this.list.Clone());
|
|
||||||
int result = Call_Finish();
|
|
||||||
if(result != SP_ERROR_NONE) {
|
|
||||||
PrintToServer("[Editor] Warn: Selector.End() forward error: %d", result);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PrintToServer("[Editor] Warn: Selector.End() called but no callback assigned, voiding list");
|
|
||||||
}
|
|
||||||
this.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Cancel() {
|
|
||||||
if(this.endCallback) {
|
|
||||||
Call_StartForward(this.endCallback);
|
|
||||||
Call_PushCell(this._client);
|
|
||||||
Call_PushCell(INVALID_HANDLE);
|
|
||||||
Call_Finish();
|
|
||||||
}
|
|
||||||
if(this.list) {
|
|
||||||
for(int i = 0; i < this.list.Length; i++) {
|
|
||||||
int ref = this.list.Get(i);
|
|
||||||
L4D2_RemoveEntityGlow(ref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PrintToChat(this._client, "\x04[Editor]\x01 Selection cancelled.");
|
|
||||||
this.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetEntityRefIndex(int ref) {
|
|
||||||
int index = this.list.FindValue(ref);
|
|
||||||
if(index > -1) {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Removes entity from list
|
|
||||||
* @return returns entity ref of entity removed
|
|
||||||
*/
|
|
||||||
int RemoveEntity(int entity) {
|
|
||||||
if(this.list == null) return -2;
|
|
||||||
|
|
||||||
L4D2_RemoveEntityGlow(entity);
|
|
||||||
int ref = EntIndexToEntRef(entity);
|
|
||||||
int index = this.GetEntityRefIndex(ref);
|
|
||||||
if(index > -1) {
|
|
||||||
this.list.Erase(index);
|
|
||||||
if(this.unSelectCallback != null) {
|
|
||||||
Call_StartForward(this.unSelectCallback)
|
|
||||||
Call_PushCell(this._client);
|
|
||||||
Call_PushCell(EntRefToEntIndex(ref));
|
|
||||||
Call_Finish();
|
|
||||||
}
|
|
||||||
return ref;
|
|
||||||
}
|
|
||||||
return INVALID_ENT_REFERENCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds entity to list
|
|
||||||
* @return index into list of entity
|
|
||||||
* @return -1 if already added
|
|
||||||
* @return -2 if callback rejected
|
|
||||||
*/
|
|
||||||
int AddEntity(int entity, bool useCallback = true) {
|
|
||||||
if(this.list == null) return -2;
|
|
||||||
|
|
||||||
int ref = EntIndexToEntRef(entity);
|
|
||||||
if(this.GetEntityRefIndex(ref) == -1) {
|
|
||||||
if(this.selectPreCallback != null && useCallback) {
|
|
||||||
Call_StartForward(this.selectPreCallback)
|
|
||||||
Call_PushCell(this._client);
|
|
||||||
Call_PushCell(entity);
|
|
||||||
bool allowed = true;
|
|
||||||
PrintToServer("Selector.AddEntity: PRE CALLBACK pre finish");
|
|
||||||
Call_Finish(allowed);
|
|
||||||
PrintToServer("Selector.AddEntity: PRE CALLBACK pre result %b", allowed);
|
|
||||||
if(!allowed) return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
L4D2_SetEntityGlow(entity, L4D2Glow_Constant, 10000, 0, this.selectColor, false);
|
|
||||||
int index = this.list.Push(ref);
|
|
||||||
PrintToServer("Selector.AddEntity: post CALLBACK pre");
|
|
||||||
if(this.selectPostCallback != null && useCallback) {
|
|
||||||
Call_StartForward(this.selectPostCallback)
|
|
||||||
Call_PushCell(this._client);
|
|
||||||
Call_PushCell(entity);
|
|
||||||
//Call_PushCell(index);
|
|
||||||
Call_Finish();
|
|
||||||
}
|
|
||||||
PrintToServer("Selector.AddEntity: post CALLBACK post");
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
enum struct PlayerPropData {
|
|
||||||
ArrayList categoryStack;
|
|
||||||
ArrayList itemBuffer;
|
|
||||||
bool clearListBuffer;
|
|
||||||
int lastCategoryIndex;
|
|
||||||
int lastItemIndex;
|
|
||||||
// When did the user last interact with prop spawner? (Shows hints after long inactivity)
|
|
||||||
int lastActiveTime;
|
|
||||||
char classnameOverride[64];
|
|
||||||
ChatPrompt chatPrompt;
|
|
||||||
PropSelector Selector;
|
|
||||||
SaveType pendingSaveType;
|
|
||||||
|
|
||||||
Schematic schematic;
|
|
||||||
int highlightedEntityRef;
|
|
||||||
int managerEntityRef;
|
|
||||||
|
|
||||||
void Init(int client) {
|
|
||||||
this.Selector._client = client;
|
|
||||||
}
|
|
||||||
// Called on PlayerDisconnect
|
|
||||||
void Reset() {
|
|
||||||
if(this.Selector.IsActive()) this.Selector.Cancel();
|
|
||||||
this.chatPrompt = Prompt_None;
|
|
||||||
this.clearListBuffer = false;
|
|
||||||
this.lastCategoryIndex = 0;
|
|
||||||
this.lastItemIndex = 0;
|
|
||||||
this.lastActiveTime = 0;
|
|
||||||
this.classnameOverride[0] = '\0';
|
|
||||||
this.CleanupBuffers();
|
|
||||||
this.pendingSaveType = Save_None;
|
|
||||||
this.schematic.Reset();
|
|
||||||
this.managerEntityRef = INVALID_ENT_REFERENCE;
|
|
||||||
this.StopHighlight();
|
|
||||||
}
|
|
||||||
|
|
||||||
void StartHighlight(int entity) {
|
|
||||||
this.highlightedEntityRef = EntIndexToEntRef(entity);
|
|
||||||
L4D2_SetEntityGlow(entity, L4D2Glow_Constant, 10000, 0, GLOW_MANAGER, false);
|
|
||||||
}
|
|
||||||
void StopHighlight() {
|
|
||||||
if(IsValidEntity(this.highlightedEntityRef)) {
|
|
||||||
L4D2_RemoveEntityGlow(this.highlightedEntityRef);
|
|
||||||
}
|
|
||||||
this.highlightedEntityRef = INVALID_ENT_REFERENCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void StartSchematic(int client, const char[] name) {
|
|
||||||
this.schematic.New(client, name);
|
|
||||||
this.pendingSaveType = Save_Schematic;
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Started new schematic: \x05%s", name);
|
|
||||||
ShowCategoryList(client, ROOT_CATEGORY);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets the list buffer
|
|
||||||
void SetItemBuffer(ArrayList list, bool cleanupAfterUse = false) {
|
|
||||||
// Cleanup previous buffer if exist
|
|
||||||
this.itemBuffer = list;
|
|
||||||
this.clearListBuffer = cleanupAfterUse;
|
|
||||||
}
|
|
||||||
void ClearItemBuffer() {
|
|
||||||
if(this.itemBuffer != null && this.clearListBuffer) {
|
|
||||||
PrintToServer("ClearItemBuffer(): arraylist deleted.");
|
|
||||||
delete this.itemBuffer;
|
|
||||||
}
|
|
||||||
this.clearListBuffer = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PushCategory(CategoryData category) {
|
|
||||||
if(this.categoryStack == null) this.categoryStack = new ArrayList(sizeof(CategoryData));
|
|
||||||
this.categoryStack.PushArray(category);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PopCategory(CategoryData data) {
|
|
||||||
if(this.categoryStack == null || this.categoryStack.Length == 0) return false;
|
|
||||||
int index = this.categoryStack.Length - 1;
|
|
||||||
this.categoryStack.GetArray(index, data);
|
|
||||||
this.categoryStack.Erase(index);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool PeekCategory(CategoryData data) {
|
|
||||||
if(this.categoryStack == null || this.categoryStack.Length == 0) return false;
|
|
||||||
int index = this.categoryStack.Length - 1;
|
|
||||||
this.categoryStack.GetArray(index, data);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetCategoryTitle(char[] title, int maxlen) {
|
|
||||||
CategoryData cat;
|
|
||||||
for(int i = 0; i < this.categoryStack.Length; i++) {
|
|
||||||
this.categoryStack.GetArray(i, cat);
|
|
||||||
if(i == 0)
|
|
||||||
Format(title, maxlen, "%s", cat.name);
|
|
||||||
else
|
|
||||||
Format(title, maxlen, "%s>[%s]", title, cat.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasCategories() {
|
|
||||||
return this.categoryStack != null && this.categoryStack.Length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is currently only called on item/category handler cancel (to clear search/recents buffer)
|
|
||||||
void CleanupBuffers() {
|
|
||||||
this.ClearItemBuffer();
|
|
||||||
if(this.categoryStack != null) {
|
|
||||||
delete this.categoryStack;
|
|
||||||
}
|
|
||||||
this.clearListBuffer = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PlayerPropData g_PropData[MAXPLAYERS+1];
|
|
||||||
|
|
||||||
|
|
||||||
enum struct CategoryData {
|
|
||||||
// The display name of category
|
|
||||||
char name[64];
|
|
||||||
// If set, overwrites the classname it is spawned as
|
|
||||||
char classnameOverride[64];
|
|
||||||
bool hasItems; // true: items is ArrayList<ItemData>, false: items is ArrayList<CategoryData>
|
|
||||||
ArrayList items;
|
|
||||||
}
|
|
||||||
enum struct ItemData {
|
|
||||||
char model[128];
|
|
||||||
char name[64];
|
|
||||||
|
|
||||||
void FromSearchData(SearchData search) {
|
|
||||||
strcopy(this.model, sizeof(this.model), search.model);
|
|
||||||
strcopy(this.name, sizeof(this.name), search.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
enum struct SearchData {
|
|
||||||
char model[128];
|
|
||||||
char name[64];
|
|
||||||
int index;
|
|
||||||
|
|
||||||
void FromItemData(ItemData item) {
|
|
||||||
strcopy(this.model, sizeof(this.model), item.model);
|
|
||||||
strcopy(this.name, sizeof(this.name), item.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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));
|
|
||||||
this.type = Build_Solid;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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]);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ToEntity(const float offset[3], bool asPreview = true) {
|
|
||||||
int entity = -1;
|
|
||||||
if(this.type == Build_Physics)
|
|
||||||
entity = CreateEntityByName("prop_physics");
|
|
||||||
else
|
|
||||||
entity = CreateEntityByName("prop_dynamic_override");
|
|
||||||
if(entity == -1) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
PrecacheModel(this.model);
|
|
||||||
DispatchKeyValue(entity, "model", this.model);
|
|
||||||
DispatchKeyValue(entity, "targetname", "saved_prop");
|
|
||||||
if(asPreview) {
|
|
||||||
DispatchKeyValue(entity, "rendermode", "1");
|
|
||||||
DispatchKeyValue(entity, "solid", "0");
|
|
||||||
} else {
|
|
||||||
DispatchKeyValue(entity, "solid", this.type == Build_NonSolid ? "0" : "6");
|
|
||||||
}
|
|
||||||
float pos[3];
|
|
||||||
for(int i = 0; i < 3; i++)
|
|
||||||
pos[i] = this.origin[i] + offset[i];
|
|
||||||
|
|
||||||
TeleportEntity(entity, pos, this.angles, NULL_VECTOR);
|
|
||||||
if(!DispatchSpawn(entity)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int alpha = asPreview ? 200 : this.color[3];
|
|
||||||
SetEntityRenderColor(entity, this.color[0], this.color[1], this.color[2], alpha);
|
|
||||||
|
|
||||||
if(asPreview)
|
|
||||||
g_previewItems.Push(EntIndexToEntRef(entity));
|
|
||||||
else
|
|
||||||
AddSpawnedItem(entity);
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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[] input) {
|
|
||||||
char buffer[16];
|
|
||||||
int index = SplitString(input, ",", this.model, sizeof(this.model));
|
|
||||||
index += SplitString(input[index], ",", buffer, sizeof(buffer));
|
|
||||||
this.type = view_as<buildType>(StringToInt(buffer));
|
|
||||||
for(int i = 0; i < 3; i++) {
|
|
||||||
index += SplitString(input[index], ",", buffer, sizeof(buffer));
|
|
||||||
this.origin[i] = StringToFloat(buffer);
|
|
||||||
}
|
|
||||||
for(int i = 0; i < 3; i++) {
|
|
||||||
index += SplitString(input[index], ",", buffer, sizeof(buffer));
|
|
||||||
this.angles[i] = StringToFloat(buffer);
|
|
||||||
}
|
|
||||||
for(int i = 0; i < 4; i++) {
|
|
||||||
index += SplitString(input[index], ",", buffer, sizeof(buffer));
|
|
||||||
this.color[i] = StringToInt(buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum struct RecentEntry {
|
|
||||||
char name[64];
|
|
||||||
int count;
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <hats/props/db.sp>
|
|
||||||
#include <hats/props/methods.sp>
|
|
||||||
#include <hats/props/cmd.sp>
|
|
||||||
#include <hats/props/menu_handlers.sp>
|
|
||||||
#include <hats/props/menu_methods.sp>
|
|
|
@ -1,139 +0,0 @@
|
||||||
Action Command_Props(int client, int args) {
|
|
||||||
char arg[32];
|
|
||||||
GetCmdArg(1, arg, sizeof(arg));
|
|
||||||
if(args == 0 || StrEqual(arg, "help")) {
|
|
||||||
PrintToChat(client, "See console for available sub-commands");
|
|
||||||
PrintToConsole(client, "help - this");
|
|
||||||
PrintToConsole(client, "list <classname/index/owner> - lists all props and their distances");
|
|
||||||
PrintToConsole(client, "search <search query>");
|
|
||||||
PrintToConsole(client, "edit <last/#index>");
|
|
||||||
PrintToConsole(client, "del <last/#index/tool>");
|
|
||||||
PrintToConsole(client, "add <cursor/tool>");
|
|
||||||
PrintToConsole(client, "favorite - favorites active editor entity");
|
|
||||||
PrintToConsole(client, "controls - list all the controls");
|
|
||||||
PrintToConsole(client, "reload - reload prop list");
|
|
||||||
PrintToConsole(client, "schem[atic] <new/save/edit/delete/load> <name>");
|
|
||||||
} else if(StrEqual(arg, "schem") || StrEqual(arg, "schematic")) {
|
|
||||||
char arg2[16];
|
|
||||||
GetCmdArg(2, arg2, sizeof(arg2));
|
|
||||||
if(StrEqual(arg2, "new")) {
|
|
||||||
char name[32];
|
|
||||||
GetCmdArg(3, name, sizeof(name));
|
|
||||||
if(name[0] == '\0') {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Please enter a name");
|
|
||||||
} else {
|
|
||||||
g_PropData[client].StartSchematic(client, name);
|
|
||||||
}
|
|
||||||
} else if(StrEqual(arg2, "save")) {
|
|
||||||
if(g_PropData[client].pendingSaveType == Save_Schematic) {
|
|
||||||
g_PropData[client].schematic.Save();
|
|
||||||
} else {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 No schematic to save.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Unknown option: %s", arg2);
|
|
||||||
}
|
|
||||||
} else if(StrEqual(arg, "list")) {
|
|
||||||
char arg2[16];
|
|
||||||
GetCmdArg(2, arg2, sizeof(arg2));
|
|
||||||
bool isClassname = StrEqual(arg2, "classname");
|
|
||||||
bool isIndex = StrEqual(arg2, "index");
|
|
||||||
bool isOwner = StrEqual(arg2, "owner");
|
|
||||||
if(args == 1 || isClassname || isIndex || isOwner) {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Please specify: \x05classname, index, owner. ");
|
|
||||||
return Plugin_Handled;
|
|
||||||
}
|
|
||||||
float pos[3], propPos[3], dist;
|
|
||||||
GetAbsOrigin(client, pos);
|
|
||||||
for(int i = 0; i < g_spawnedItems.Length; i++) {
|
|
||||||
int ref = GetSpawnedItem(i);
|
|
||||||
if(ref > -1) {
|
|
||||||
GetEntPropVector(ref, Prop_Send, "m_vecOrigin", propPos);
|
|
||||||
dist = GetVectorDistance(pos, propPos, false);
|
|
||||||
if(isIndex) {
|
|
||||||
int entity = EntRefToEntIndex(ref);
|
|
||||||
PrintToConsole(client, "%d. ent #%d - %.0fu away", i, entity, dist);
|
|
||||||
} else if(isClassname) {
|
|
||||||
char classname[32];
|
|
||||||
GetEntityClassname(ref, classname, sizeof(classname));
|
|
||||||
PrintToConsole(client, "%d. %s - %.0fu away", i, classname, dist);
|
|
||||||
} else if(isOwner) {
|
|
||||||
int spawner = g_spawnedItems.Get(i, 1);
|
|
||||||
int player = GetClientOfUserId(spawner);
|
|
||||||
if(player > 0) {
|
|
||||||
PrintToConsole(client, "%d. %N - %.0fu away", i, player, dist);
|
|
||||||
} else {
|
|
||||||
PrintToConsole(client, "%d. (disconnected) - %.0fu away", i, dist);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Check console");
|
|
||||||
} else if(StrEqual(arg, "search")) {
|
|
||||||
if(args == 1) {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Enter your search query:");
|
|
||||||
g_PropData[client].chatPrompt = Prompt_Search;
|
|
||||||
} else {
|
|
||||||
char query[32];
|
|
||||||
GetCmdArg(2, query, sizeof(query));
|
|
||||||
DoSearch(client, query);
|
|
||||||
}
|
|
||||||
} else if(StrEqual(arg, "edit")) {
|
|
||||||
char arg2[32];
|
|
||||||
GetCmdArg(2, arg2, sizeof(arg2));
|
|
||||||
int index;
|
|
||||||
if(StrEqual(arg2, "last")) {
|
|
||||||
// Get last one
|
|
||||||
index = GetSpawnedIndex(client, -1);
|
|
||||||
} else {
|
|
||||||
index = StringToInt(arg2);
|
|
||||||
}
|
|
||||||
if(index >= 0 && index < g_spawnedItems.Length) {
|
|
||||||
int entity = EntRefToEntIndex(g_spawnedItems.Get(index));
|
|
||||||
Editor[client].Import(entity);
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Editing entity \x05%d", entity);
|
|
||||||
} else {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Invalid index, out of bounds. Enter a value between [0, %d]", g_spawnedItems.Length - 1);
|
|
||||||
}
|
|
||||||
} else if(StrEqual(arg, "del")) {
|
|
||||||
char arg2[32];
|
|
||||||
GetCmdArg(2, arg2, sizeof(arg2));
|
|
||||||
int index;
|
|
||||||
if(StrEqual(arg2, "last")) {
|
|
||||||
// Get last one
|
|
||||||
index = GetSpawnedIndex(client, -1);
|
|
||||||
} else {
|
|
||||||
index = StringToInt(arg2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(index >= 0 && index < g_spawnedItems.Length) {
|
|
||||||
int entity = EntRefToEntIndex(g_spawnedItems.Get(index));
|
|
||||||
if(entity > 0 && IsValidEntity(entity)) {
|
|
||||||
RemoveEntity(entity);
|
|
||||||
}
|
|
||||||
g_spawnedItems.Erase(index);
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Deleted entity \x05%d", entity);
|
|
||||||
} else {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Invalid index, out of bounds. Enter a value between [0, %d]", g_spawnedItems.Length - 1);
|
|
||||||
}
|
|
||||||
} else if(StrEqual(arg, "controls")) {
|
|
||||||
PrintToChat(client, "View controls at https://admin.jackz.me/docs/props");
|
|
||||||
} else if(StrEqual(arg, "favorite")) {
|
|
||||||
if(g_db == null) {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Cannot connect to database.");
|
|
||||||
} else if(Editor[client].IsActive()) {
|
|
||||||
char model[128];
|
|
||||||
GetEntPropString(Editor[client].entity, Prop_Data, "m_ModelName", model, sizeof(model));
|
|
||||||
ToggleFavorite(client, model, Editor[client].data);
|
|
||||||
} else {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Edit a prop to use this command.");
|
|
||||||
}
|
|
||||||
} else if(StrEqual(arg, "reload")) {
|
|
||||||
PrintHintText(client, "Reloading categories...");
|
|
||||||
UnloadCategories();
|
|
||||||
LoadCategories();
|
|
||||||
} else {
|
|
||||||
PrintToChat(client, "\x05Not implemented");
|
|
||||||
}
|
|
||||||
return Plugin_Handled;
|
|
||||||
}
|
|
|
@ -1,125 +0,0 @@
|
||||||
#define DATABASE_CONFIG_NAME "hats_editor"
|
|
||||||
Database g_db;
|
|
||||||
|
|
||||||
bool ConnectDB() {
|
|
||||||
char error[255];
|
|
||||||
Database db = SQL_Connect(DATABASE_CONFIG_NAME, true, error, sizeof(error));
|
|
||||||
if (db == null) {
|
|
||||||
LogError("Database error %s", error);
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
PrintToServer("l4d2_hats: Connected to database %s", DATABASE_CONFIG_NAME);
|
|
||||||
db.SetCharset("utf8mb4");
|
|
||||||
g_db = db;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DB_GetFavoritesCallback(Database db, DBResultSet results, const char[] error, int userid) {
|
|
||||||
if(results == null) {
|
|
||||||
PrintToServer("l4d2_hats: DB_GetFavoritesCallback returned error: \"%s\"", error);
|
|
||||||
}
|
|
||||||
int client = GetClientOfUserId(userid);
|
|
||||||
if(client > 0) {
|
|
||||||
if(results == null) {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Error occurred fetching favorites");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ArrayList list = new ArrayList(sizeof(ItemData));
|
|
||||||
ItemData item;
|
|
||||||
while(results.FetchRow()) {
|
|
||||||
results.FetchString(0, item.model, sizeof(item.model));
|
|
||||||
DBResult result;
|
|
||||||
results.FetchString(1, item.name, sizeof(item.name), result);
|
|
||||||
if(result == DBVal_Null) {
|
|
||||||
// No name set - use the end part of the model
|
|
||||||
int index = FindCharInString(item.model, '/', true);
|
|
||||||
strcopy(item.name, sizeof(item.name), item.model[index + 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ShowTempItemMenu(client, list, "Favorites");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DB_ToggleFavoriteCallback(Database db, DBResultSet results, const char[] error, DataPack pack) {
|
|
||||||
if(results == null) {
|
|
||||||
PrintToServer("l4d2_hats: DB_GetFavoriteCallback returned error: \"%s\"", error);
|
|
||||||
}
|
|
||||||
pack.Reset();
|
|
||||||
int userid = pack.ReadCell();
|
|
||||||
int client = GetClientOfUserId(userid);
|
|
||||||
if(client > 0) {
|
|
||||||
if(results == null) {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Error occurred fetching favorite data");
|
|
||||||
delete pack;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
char query[256];
|
|
||||||
char model[128];
|
|
||||||
char steamid[32];
|
|
||||||
GetClientAuthId(client, AuthId_Steam2, steamid, sizeof(steamid));
|
|
||||||
pack.ReadString(model, sizeof(model));
|
|
||||||
if(results.FetchRow()) {
|
|
||||||
// Model was favorited, erase it
|
|
||||||
g_db.Format(query, sizeof(query), "DELETE FROM editor_favorites WHERE steamid = '%s' AND model = '%s'", steamid, model);
|
|
||||||
g_db.Query(DB_DeleteFavoriteCallback, query, userid);
|
|
||||||
} else {
|
|
||||||
// Model is not favorited, save it.
|
|
||||||
char name[64];
|
|
||||||
pack.ReadString(name, sizeof(name));
|
|
||||||
// TODO: calculate next position automatically
|
|
||||||
int position = 0;
|
|
||||||
g_db.Format(query, sizeof(query),
|
|
||||||
"INSERT INTO editor_favorites (steamid, model, name, position) VALUES ('%s', '%s', '%s', %d)",
|
|
||||||
steamid, model, name, position
|
|
||||||
);
|
|
||||||
g_db.Query(DB_InsertFavoriteCallback, query, pack);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Only delete if we lost client - otherwise we will reuse it
|
|
||||||
delete pack;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DB_DeleteFavoriteCallback(Database db, DBResultSet results, const char[] error, DataPack pack) {
|
|
||||||
if(results == null) {
|
|
||||||
PrintToServer("l4d2_hats: DB_DeleteFavoriteCallback returned error: \"%s\"", error);
|
|
||||||
}
|
|
||||||
pack.Reset();
|
|
||||||
char model[128];
|
|
||||||
char name[64];
|
|
||||||
int client = GetClientOfUserId(pack.ReadCell());
|
|
||||||
if(client > 0) {
|
|
||||||
if(results == null) {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Could not delete favorite");
|
|
||||||
delete pack;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pack.ReadString(model, sizeof(model));
|
|
||||||
pack.ReadString(name, sizeof(name));
|
|
||||||
int index = FindCharInString(model, '/', true);
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Removed favorite: \"%s\" \x05(%s)", model[index], name);
|
|
||||||
}
|
|
||||||
delete pack;
|
|
||||||
}
|
|
||||||
void DB_InsertFavoriteCallback(Database db, DBResultSet results, const char[] error, DataPack pack) {
|
|
||||||
if(results == null) {
|
|
||||||
PrintToServer("l4d2_hats: DB_InsertFavoriteCallback returned error: \"%s\"", error);
|
|
||||||
}
|
|
||||||
pack.Reset();
|
|
||||||
char model[128];
|
|
||||||
char name[64];
|
|
||||||
int client = GetClientOfUserId(pack.ReadCell());
|
|
||||||
if(client > 0) {
|
|
||||||
if(results == null) {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Could not add favorite");
|
|
||||||
delete pack;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pack.ReadString(model, sizeof(model));
|
|
||||||
pack.ReadString(name, sizeof(name));
|
|
||||||
int index = FindCharInString(model, '/', true);
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Added favorite: \"%s\" \x05(%s)", model[index], name);
|
|
||||||
}
|
|
||||||
delete pack;
|
|
||||||
}
|
|
|
@ -1,481 +0,0 @@
|
||||||
TopMenuObject g_propSpawnerCategory;
|
|
||||||
public void OnAdminMenuReady(Handle topMenuHandle) {
|
|
||||||
TopMenu topMenu = TopMenu.FromHandle(topMenuHandle);
|
|
||||||
if(g_topMenu != topMenuHandle) {
|
|
||||||
g_propSpawnerCategory = topMenu.AddCategory("hats_editor", Category_Handler, "sm_prop");
|
|
||||||
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");
|
|
||||||
topMenu.AddItem("editor_manager", AdminMenu_Manager, g_propSpawnerCategory, "sm_prop");
|
|
||||||
topMenu.AddItem("editor_selector", AdminMenu_Selector, g_propSpawnerCategory, "sm_prop");
|
|
||||||
}
|
|
||||||
g_topMenu = topMenu;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////
|
|
||||||
// HANDLERS
|
|
||||||
/////////////
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdminMenu_Selector(TopMenu topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) {
|
|
||||||
if(action == TopMenuAction_DisplayOption) {
|
|
||||||
Format(buffer, maxlength, "Selector");
|
|
||||||
} else if(action == TopMenuAction_SelectOption) {
|
|
||||||
ShowManagerSelectorMenu(param);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void AdminMenu_Spawn(TopMenu topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) {
|
|
||||||
if(action == TopMenuAction_DisplayOption) {
|
|
||||||
Format(buffer, maxlength, "Spawn Props");
|
|
||||||
} else if(action == TopMenuAction_SelectOption) {
|
|
||||||
ConVar cheats = FindConVar("sm_cheats");
|
|
||||||
if(cheats != null && !cheats.BoolValue) {
|
|
||||||
CReplyToCommand(param, "\x04[Editor] \x01Set \x05sm_cheats\x01 to \x051\x01 to use the prop spawner");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ShowSpawnRoot(param);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int Spawn_RootHandler(Menu menu, MenuAction action, int client, int param2) {
|
|
||||||
if (action == MenuAction_Select) {
|
|
||||||
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, ROOT_CATEGORY);
|
|
||||||
}
|
|
||||||
// TODO: handle back (to top menu)
|
|
||||||
} 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 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 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
Spawn_ShowSaveLoadMainMenu(param);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AdminMenu_Manager(TopMenu topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) {
|
|
||||||
if(action == TopMenuAction_DisplayOption) {
|
|
||||||
Format(buffer, maxlength, "Manager (ALPHA)");
|
|
||||||
} else if(action == TopMenuAction_SelectOption) {
|
|
||||||
Spawn_ShowManagerMainMenu(param);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int SaveLoadMainMenuHandler(Menu menu, MenuAction action, int client, int param2) {
|
|
||||||
if (action == MenuAction_Select) {
|
|
||||||
char info[2];
|
|
||||||
menu.GetItem(param2, info, sizeof(info));
|
|
||||||
SaveType type = view_as<SaveType>(StringToInt(info));
|
|
||||||
ShowSaves(client, type);
|
|
||||||
} 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 SaveLoadSceneHandler(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_%H-%I-%M");
|
|
||||||
if(CreateSceneSave(saveName)) {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Saved as \x05%s/%s.txt", g_currentMap, saveName);
|
|
||||||
} else {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Unable to save. Sorry.");
|
|
||||||
}
|
|
||||||
} else if(g_pendingSaveClient != 0 && g_pendingSaveClient != client) {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Another user is currently loading a save.");
|
|
||||||
} else if(g_PropData[client].pendingSaveType == Save_Schematic) {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Please complete or cancel current schematic to continue.");
|
|
||||||
} else if(LoadScene(saveName, true)) {
|
|
||||||
ConfirmSave(client, saveName);
|
|
||||||
g_pendingSaveClient = client;
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Previewing save \x05%s", saveName);
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Press \x05Shift + Middle Mouse\x01 to spawn, \x05Middle Mouse\x01 to cancel");
|
|
||||||
} else {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Could not load save file.");
|
|
||||||
}
|
|
||||||
} else if (action == MenuAction_Cancel) {
|
|
||||||
if(param2 == MenuCancel_ExitBack) {
|
|
||||||
Spawn_ShowSaveLoadMainMenu(client);
|
|
||||||
}
|
|
||||||
} else if (action == MenuAction_End)
|
|
||||||
delete menu;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int SaveLoadSchematicHandler(Menu menu, MenuAction action, int client, int param2) {
|
|
||||||
if (action == MenuAction_Select) {
|
|
||||||
char saveName[64];
|
|
||||||
menu.GetItem(param2, saveName, sizeof(saveName));
|
|
||||||
Schematic schem;
|
|
||||||
if(saveName[0] == '\0') {
|
|
||||||
if(g_PropData[client].pendingSaveType == Save_Schematic) {
|
|
||||||
if(g_PropData[client].schematic.Save()) {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Saved schematic as \x05%s", g_PropData[client].schematic.name);
|
|
||||||
} else {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Failed to save schematic.");
|
|
||||||
}
|
|
||||||
g_PropData[client].schematic.Reset();
|
|
||||||
g_PropData[client].pendingSaveType = Save_None;
|
|
||||||
} else {
|
|
||||||
g_PropData[client].chatPrompt = Prompt_SaveSchematic;
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Enter in chat a name for schematic");
|
|
||||||
}
|
|
||||||
} else if(schem.Import(saveName)) {
|
|
||||||
float pos[3];
|
|
||||||
GetCursorLocation(client, pos);
|
|
||||||
ArrayList list = schem.SpawnEntities(pos, true);
|
|
||||||
SaveData save;
|
|
||||||
int parent = list.GetArray(0, save);
|
|
||||||
delete list;
|
|
||||||
Editor[client].Import(parent);
|
|
||||||
if(g_pendingSaveClient != 0 && g_pendingSaveClient != client) {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Another user is currently loading a scene.");
|
|
||||||
} else {
|
|
||||||
g_pendingSaveClient = client;
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Previewing schematic \x05%s", saveName);
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Press \x05Shift + Middle Mouse\x01 to spawn, \x05Middle Mouse\x01 to cancel");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Could not load save file.");
|
|
||||||
}
|
|
||||||
} else if (action == MenuAction_Cancel) {
|
|
||||||
if(param2 == MenuCancel_ExitBack) {
|
|
||||||
Spawn_ShowSaveLoadMainMenu(client);
|
|
||||||
}
|
|
||||||
} else if (action == MenuAction_End)
|
|
||||||
delete menu;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SaveLoadConfirmHandler(Menu menu, MenuAction action, int client, int param2) {
|
|
||||||
if (action == MenuAction_Select) {
|
|
||||||
ClearSavePreview();
|
|
||||||
char info[64];
|
|
||||||
menu.GetItem(param2, info, sizeof(info));
|
|
||||||
if(info[0] != '\0') {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Loaded scene \x05%s", info);
|
|
||||||
LoadScene(info, false);
|
|
||||||
}
|
|
||||||
} else if (action == MenuAction_Cancel) {
|
|
||||||
if(param2 == MenuCancel_ExitBack) {
|
|
||||||
Spawn_ShowSaveLoadMainMenu(client);
|
|
||||||
}
|
|
||||||
} else if (action == MenuAction_End)
|
|
||||||
delete menu;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int ManagerHandler(Menu menu, MenuAction action, int client, int param2) {
|
|
||||||
if (action == MenuAction_Select) {
|
|
||||||
char info[8];
|
|
||||||
menu.GetItem(param2, info, sizeof(info));
|
|
||||||
if(info[0] != '\0') {
|
|
||||||
int index = StringToInt(info);
|
|
||||||
int ref = g_spawnedItems.Get(index);
|
|
||||||
// TODO: add delete confirm
|
|
||||||
if(!IsValidEntity(ref)) {
|
|
||||||
SendEditorMessage(client, "Entity has disappeared");
|
|
||||||
} else {
|
|
||||||
int entity = EntRefToEntIndex(ref);
|
|
||||||
g_PropData[client].managerEntityRef = ref;
|
|
||||||
g_PropData[client].StartHighlight(entity);
|
|
||||||
ShowManagerEntityMenu(client, entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (action == MenuAction_Cancel) {
|
|
||||||
if(param2 == MenuCancel_ExitBack) {
|
|
||||||
Spawn_ShowSaveLoadMainMenu(client);
|
|
||||||
}
|
|
||||||
} else if (action == MenuAction_End)
|
|
||||||
delete menu;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int ManagerEntityHandler(Menu menu, MenuAction action, int client, int param2) {
|
|
||||||
if (action == MenuAction_Select) {
|
|
||||||
g_PropData[client].StopHighlight();
|
|
||||||
char info[32];
|
|
||||||
menu.GetItem(param2, info, sizeof(info));
|
|
||||||
int ref = g_PropData[client].managerEntityRef;
|
|
||||||
if(!IsValidEntity(ref)) {
|
|
||||||
SendEditorMessage(client, "Entity disappeared");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if(StrEqual(info, "edit")) {
|
|
||||||
Editor[client].ImportEntity(EntRefToEntIndex(ref), Edit_Manager);
|
|
||||||
Spawn_ShowManagerMainMenu(client);
|
|
||||||
} else if(StrEqual(info, "delete")) {
|
|
||||||
for(int i = 0; i < g_spawnedItems.Length; i++) {
|
|
||||||
int spawnedRef = g_spawnedItems.Get(i);
|
|
||||||
if(spawnedRef == ref) {
|
|
||||||
g_spawnedItems.Erase(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(IsValidEntity(ref)) {
|
|
||||||
RemoveEntity(ref);
|
|
||||||
}
|
|
||||||
Spawn_ShowManagerMainMenu(client);
|
|
||||||
} else if(StrEqual(info, "view")) {
|
|
||||||
ReplyToCommand(client, "Maybe soon.");
|
|
||||||
} else if(StrEqual(info, "select")) {
|
|
||||||
ShowManagerSelectorMenu(client);
|
|
||||||
int entity = EntRefToEntIndex(ref);
|
|
||||||
g_PropData[client].Selector.AddEntity(entity);
|
|
||||||
} else {
|
|
||||||
SendEditorMessage(client, "Unknown option / not implemented");
|
|
||||||
}
|
|
||||||
} else if (action == MenuAction_Cancel) {
|
|
||||||
g_PropData[client].StopHighlight();
|
|
||||||
if(param2 == MenuCancel_ExitBack) {
|
|
||||||
Spawn_ShowManagerMainMenu(client);
|
|
||||||
}
|
|
||||||
} else if (action == MenuAction_End)
|
|
||||||
delete menu;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int ManagerSelectorMainMenuHandler(Menu menu, MenuAction action, int client, int param2) {
|
|
||||||
if (action == MenuAction_Select) {
|
|
||||||
if(!EntitySelector.FromClient(client).Active) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
char info[32];
|
|
||||||
menu.GetItem(param2, info, sizeof(info));
|
|
||||||
if(StrEqual(info, "list")) {
|
|
||||||
SendEditorMessage(client, "Not implemented");
|
|
||||||
} else if(StrEqual(info, "actions")) {
|
|
||||||
ShowManagerSelectorActionsMenu(client);
|
|
||||||
} else if(StrEqual(info, "cancel")) {
|
|
||||||
g_PropData[client].Selector.Cancel();
|
|
||||||
}
|
|
||||||
} else if (action == MenuAction_Cancel) {
|
|
||||||
g_PropData[client].Selector.Cancel();
|
|
||||||
} else if (action == MenuAction_End)
|
|
||||||
delete menu;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int ManagerSelectorActionHandler(Menu menu, MenuAction action, int client, int param2) {
|
|
||||||
if (action == MenuAction_Select) {
|
|
||||||
if(!g_PropData[client].Selector.IsActive()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
char info[32];
|
|
||||||
menu.GetItem(param2, info, sizeof(info));
|
|
||||||
if(StrEqual(info, "delete")) {
|
|
||||||
for(int i = 0; i < g_PropData[client].Selector.list.Length; i++) {
|
|
||||||
int ref = g_PropData[client].Selector.list.Get(i);
|
|
||||||
if(IsValidEntity(ref)) {
|
|
||||||
RemoveEntity(ref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_PropData[client].Selector.End();
|
|
||||||
Spawn_ShowManagerMainMenu(client);
|
|
||||||
} else if(StrEqual(info, "save")) {
|
|
||||||
// TODO: implement
|
|
||||||
SendEditorMessage(client, "Not implemented");
|
|
||||||
} else {
|
|
||||||
SendEditorMessage(client, "Unknown option / not implemented");
|
|
||||||
}
|
|
||||||
} else if (action == MenuAction_Cancel) {
|
|
||||||
if(param2 == MenuCancel_ExitBack) {
|
|
||||||
Spawn_ShowSaveLoadMainMenu(client);
|
|
||||||
}
|
|
||||||
} else if (action == MenuAction_End)
|
|
||||||
delete menu;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int COLOR_DELETE[3] = { 255, 0, 0 }
|
|
||||||
|
|
||||||
int DeleteHandler(Menu menu, MenuAction action, int client, int param2) {
|
|
||||||
if (action == MenuAction_Select) {
|
|
||||||
char info[128];
|
|
||||||
menu.GetItem(param2, info, sizeof(info));
|
|
||||||
int ref = StringToInt(info[2]);
|
|
||||||
int option = StringToInt(info);
|
|
||||||
if(option == -1) {
|
|
||||||
// Delete all (everyone)
|
|
||||||
int count = DeleteAll();
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Deleted \x05%d\x01 items", count);
|
|
||||||
ShowDeleteList(client);
|
|
||||||
} else if(option == -2) {
|
|
||||||
// Delete all (mine only)
|
|
||||||
int count = DeleteAll(client);
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Deleted \x05%d\x01 items", count);
|
|
||||||
ShowDeleteList(client);
|
|
||||||
} else if(option == -3) {
|
|
||||||
if(g_PropData[client].Selector.IsActive()) {
|
|
||||||
g_PropData[client].Selector.End();
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Delete tool cancelled");
|
|
||||||
} else {
|
|
||||||
g_PropData[client].Selector.StartDirect(COLOR_DELETE, OnDeleteToolEnd);
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Delete tool active. Press \x05Left Mouse\x01 to mark props, \x05Right Mouse\x01 to undo. SHIFT+USE to spawn, CTRL+USE to cancel");
|
|
||||||
}
|
|
||||||
ShowDeleteList(client);
|
|
||||||
} else {
|
|
||||||
int index = g_spawnedItems.FindValue(ref);
|
|
||||||
if(IsValidEntity(ref)) {
|
|
||||||
RemoveEntity(ref);
|
|
||||||
}
|
|
||||||
if(index > -1) {
|
|
||||||
g_spawnedItems.Erase(index);
|
|
||||||
index--;
|
|
||||||
} else { index = 0; }
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
// Reset item index when selecting new category
|
|
||||||
if(g_PropData[client].lastCategoryIndex != index) {
|
|
||||||
g_PropData[client].lastCategoryIndex = index;
|
|
||||||
g_PropData[client].lastItemIndex = 0;
|
|
||||||
}
|
|
||||||
CategoryData category;
|
|
||||||
g_PropData[client].PeekCategory(category); // Just need to get the category.items[index], don't want to pop
|
|
||||||
category.items.GetArray(index, category);
|
|
||||||
if(category.items == null) {
|
|
||||||
LogError("Category %s has null items array (index=%d)", category.name, index);
|
|
||||||
} else if(category.hasItems) {
|
|
||||||
ShowCategoryItemMenu(client, category);
|
|
||||||
} else {
|
|
||||||
// Reset the category index for nested
|
|
||||||
g_PropData[client].lastCategoryIndex = 0;
|
|
||||||
// Make the list now be the selected category's list.
|
|
||||||
ShowCategoryList(client, category);
|
|
||||||
}
|
|
||||||
} else if (action == MenuAction_Cancel) {
|
|
||||||
if(param2 == MenuCancel_ExitBack) {
|
|
||||||
CategoryData category;
|
|
||||||
// Double pop
|
|
||||||
if(g_PropData[client].PopCategory(category) && g_PropData[client].PopCategory(category)) {
|
|
||||||
// Use the last category (go back one)
|
|
||||||
ShowCategoryList(client, category);
|
|
||||||
} else {
|
|
||||||
ShowSpawnRoot(client);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
g_PropData[client].CleanupBuffers();
|
|
||||||
}
|
|
||||||
} else if (action == MenuAction_End)
|
|
||||||
delete menu;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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];
|
|
||||||
char model[128];
|
|
||||||
int nameIndex = SplitString(info, "|", index, sizeof(index));
|
|
||||||
nameIndex += SplitString(info[nameIndex], "|", model, sizeof(model));
|
|
||||||
g_PropData[client].lastItemIndex = StringToInt(index);
|
|
||||||
if(Editor[client].PreviewModel(model, g_PropData[client].classnameOverride)) {
|
|
||||||
Editor[client].SetName(info[nameIndex]);
|
|
||||||
PrintHintText(client, "%s\n%s", info[nameIndex], model);
|
|
||||||
ShowHint(client);
|
|
||||||
} else {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Error spawning preview \x01(%s)", model);
|
|
||||||
}
|
|
||||||
// Use same item menu again:
|
|
||||||
ShowItemMenu(client);
|
|
||||||
} else if(action == MenuAction_Cancel) {
|
|
||||||
g_PropData[client].ClearItemBuffer();
|
|
||||||
if(param2 == MenuCancel_ExitBack) {
|
|
||||||
CategoryData category;
|
|
||||||
if(g_PropData[client].PopCategory(category)) {
|
|
||||||
// Use the last category (go back one)
|
|
||||||
ShowCategoryList(client, category);
|
|
||||||
} else {
|
|
||||||
// If there is no categories, it means we are in a temp menu (search / recents / favorites)
|
|
||||||
ShowSpawnRoot(client);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
g_PropData[client].CleanupBuffers();
|
|
||||||
}
|
|
||||||
} else if (action == MenuAction_End) {
|
|
||||||
delete menu;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int EditHandler(Menu menu, MenuAction action, int client, int param2) {
|
|
||||||
if (action == MenuAction_Select) {
|
|
||||||
char info[8];
|
|
||||||
menu.GetItem(param2, info, sizeof(info));
|
|
||||||
int ref = StringToInt(info);
|
|
||||||
int index = g_spawnedItems.FindValue(ref);
|
|
||||||
int entity = EntRefToEntIndex(ref);
|
|
||||||
if(entity > 0) {
|
|
||||||
Editor[client].Import(entity, false);
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Editing entity \x05%d", entity);
|
|
||||||
} else {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Entity disappeared.");
|
|
||||||
if(index > -1) {
|
|
||||||
g_spawnedItems.Erase(index);
|
|
||||||
index--;
|
|
||||||
} else { index = 0; }
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
|
@ -1,315 +0,0 @@
|
||||||
/////////////
|
|
||||||
// METHODS
|
|
||||||
/////////////
|
|
||||||
void ShowSpawnRoot(int client) {
|
|
||||||
Menu menu = new Menu(Spawn_RootHandler);
|
|
||||||
menu.SetTitle("Choose spawn list:");
|
|
||||||
menu.AddItem("f", "Favorites (Broken :D)");
|
|
||||||
menu.AddItem("r", "Recently Spawned Props");
|
|
||||||
menu.AddItem("s", "Search for Props");
|
|
||||||
menu.AddItem("n", "Browse Props");
|
|
||||||
menu.ExitBackButton = true;
|
|
||||||
menu.ExitButton = true;
|
|
||||||
menu.Display(client, MENU_TIME_FOREVER);
|
|
||||||
}
|
|
||||||
void Spawn_ShowRecents(int client) {
|
|
||||||
if(g_recentItems == null) LoadRecents();
|
|
||||||
ArrayList items = GetRecentsItemList();
|
|
||||||
if(items.Length == 0) {
|
|
||||||
CReplyToCommand(client, "\x04[Editor] \x01No recent props spawned.");
|
|
||||||
DisplayTopMenuCategory(g_topMenu, g_propSpawnerCategory, client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ShowTempItemMenu(client, items, "Recents");
|
|
||||||
}
|
|
||||||
void Spawn_ShowSearch(int client) {
|
|
||||||
g_PropData[client].chatPrompt = Prompt_Search;
|
|
||||||
CReplyToCommand(client, "\x04[Editor] \x01Please enter search query in chat:");
|
|
||||||
}
|
|
||||||
void ShowDeleteList(int client, int index = -3) {
|
|
||||||
if(g_spawnedItems.Length == 0) {
|
|
||||||
SendEditorMessage(client, "No spawned items to delete");
|
|
||||||
DisplayTopMenuCategory(g_topMenu, g_propSpawnerCategory, client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Menu menu = new Menu(DeleteHandler);
|
|
||||||
menu.SetTitle("Delete Props");
|
|
||||||
|
|
||||||
menu.AddItem("-1", "Delete All");
|
|
||||||
menu.AddItem("-2", "Delete All (Mine Only)");
|
|
||||||
menu.AddItem("-3", "Delete Tool");
|
|
||||||
// menu.AddItem("-4", "Delete Last Save");
|
|
||||||
char info[8];
|
|
||||||
char buffer[128];
|
|
||||||
for(int i = 0; i < g_spawnedItems.Length; i++) {
|
|
||||||
int ref = GetSpawnedItem(i);
|
|
||||||
if(ref == -1) continue;
|
|
||||||
Format(info, sizeof(info), "0|%d", ref);
|
|
||||||
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 +3 to the index for the 3 "Delete ..." buttons
|
|
||||||
// TODO: restore the delete index issue, use /7*7
|
|
||||||
menu.DisplayAt(client, 0, MENU_TIME_FOREVER);
|
|
||||||
}
|
|
||||||
void ShowEditList(int client, int index = 0) {
|
|
||||||
if(g_spawnedItems.Length == 0) {
|
|
||||||
SendEditorMessage(client, "No spawned items to edit");
|
|
||||||
DisplayTopMenuCategory(g_topMenu, g_propSpawnerCategory, client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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 = GetSpawnedItem(i);
|
|
||||||
if(ref == -1) continue;
|
|
||||||
Format(info, sizeof(info), "%d", ref);
|
|
||||||
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 ShowCategoryList(int client, CategoryData category) {
|
|
||||||
LoadCategories();
|
|
||||||
char info[4];
|
|
||||||
// No category list provided, use the global one.
|
|
||||||
g_PropData[client].PushCategory(category);
|
|
||||||
Menu menu = new Menu(SpawnCategoryHandler);
|
|
||||||
char title[32];
|
|
||||||
g_PropData[client].GetCategoryTitle(title, sizeof(title));
|
|
||||||
menu.SetTitle(title);
|
|
||||||
CategoryData cat;
|
|
||||||
for(int i = 0; i < category.items.Length; i++) {
|
|
||||||
category.items.GetArray(i, cat);
|
|
||||||
Format(info, sizeof(info), "%d", i);
|
|
||||||
if(cat.hasItems)
|
|
||||||
menu.AddItem(info, cat.name);
|
|
||||||
else {
|
|
||||||
Format(title, sizeof(title), "[%s]", cat.name);
|
|
||||||
menu.AddItem(info, title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
menu.ExitBackButton = true;
|
|
||||||
menu.ExitButton = true;
|
|
||||||
// Round to page instead of index (int division)
|
|
||||||
int index = g_PropData[client].lastCategoryIndex / 7 * 7;
|
|
||||||
menu.DisplayAt(client, index, MENU_TIME_FOREVER);
|
|
||||||
}
|
|
||||||
void _showItemMenu(int client, ArrayList items, const char[] title = "", bool clearArray = false, const char[] classnameOverride = "") {
|
|
||||||
if(items == null) {
|
|
||||||
// Use previous list buffer
|
|
||||||
items = g_PropData[client].itemBuffer;
|
|
||||||
if(items == null) {
|
|
||||||
LogError("Previous list does not exist and no new list was provided ShowItemMenu(%N)", client);
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 An error occurred (no list)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Populate the buffer with this list
|
|
||||||
g_PropData[client].SetItemBuffer(items, clearArray);
|
|
||||||
// Reset the index, so we start on the first item
|
|
||||||
g_PropData[client].lastItemIndex = 0;
|
|
||||||
strcopy(g_PropData[client].classnameOverride, 32, classnameOverride);
|
|
||||||
}
|
|
||||||
if(items.Length == 0) {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 No items to show.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Menu itemMenu = new Menu(SpawnItemHandler);
|
|
||||||
if(title[0] != '\0')
|
|
||||||
itemMenu.SetTitle(title);
|
|
||||||
ItemData item;
|
|
||||||
char info[8+128+64]; //i[8] + item.model[128] + item.name[64]
|
|
||||||
for(int i = 0; i < items.Length; i++) {
|
|
||||||
items.GetArray(i, item);
|
|
||||||
// Sadly need to duplicate item.name, for recents to work
|
|
||||||
Format(info, sizeof(info), "%d|%s|%s", i, item.model, item.name);
|
|
||||||
itemMenu.AddItem(info, item.name);
|
|
||||||
}
|
|
||||||
itemMenu.ExitBackButton = true;
|
|
||||||
itemMenu.ExitButton = true;
|
|
||||||
// We don't want to start at the index but the page of the index
|
|
||||||
int index = (g_PropData[client].lastItemIndex / 7) * 7;
|
|
||||||
itemMenu.DisplayAt(client, index, MENU_TIME_FOREVER);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Show a list of a category's items to spawn to the client
|
|
||||||
*
|
|
||||||
* @param client client to show menu to
|
|
||||||
* @param category the category to show items of
|
|
||||||
*/
|
|
||||||
void ShowCategoryItemMenu(int client, CategoryData category) {
|
|
||||||
char title[32];
|
|
||||||
g_PropData[client].GetCategoryTitle(title, sizeof(title));
|
|
||||||
Format(title, sizeof(title), "%s>%s", title, category.name);
|
|
||||||
_showItemMenu(client, category.items, title, false, category.classnameOverride);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Show a list of items to spawn to the client
|
|
||||||
*
|
|
||||||
* @param client client to show menu to
|
|
||||||
* @param items A list of ItemData. Optional, null to reuse last list
|
|
||||||
* @param title An optional title to show
|
|
||||||
* @param clearArray Should the items array be destroyed when menu is closed?
|
|
||||||
* @param classnameOverride Override the classname to spawn as
|
|
||||||
*/
|
|
||||||
void ShowItemMenu(int client, ArrayList items = null, const char[] title = "", const char[] classnameOverride = "") {
|
|
||||||
_showItemMenu(client, items, title, false, classnameOverride);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Show a list of items, deleting the arraylist on completion
|
|
||||||
* @param client client to show menu to
|
|
||||||
* @param items A list of ItemData
|
|
||||||
* @param title An optional title to show
|
|
||||||
* @param classnameOverride Override the classname to spawn as
|
|
||||||
*/
|
|
||||||
void ShowTempItemMenu(int client, ArrayList items, const char[] title = "", const char[] classnameOverride = "") {
|
|
||||||
if(items == null) {
|
|
||||||
LogError("ShowTempItemMenu: Given null item list");
|
|
||||||
}
|
|
||||||
_showItemMenu(client, items, title, true, classnameOverride);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Spawn_ShowFavorites(int client) {
|
|
||||||
if(g_db == null) {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Cannot connect to database.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
PrintCenterText(client, "Loading favorites...\nPlease wait");
|
|
||||||
char query[256];
|
|
||||||
GetClientAuthId(client, AuthId_Steam2, query, sizeof(query));
|
|
||||||
g_db.Format(query, sizeof(query), "SELECT model, name FROM editor_favorites WHERE steamid = '%s' ORDER BY position DESC", query);
|
|
||||||
g_db.Query(DB_GetFavoritesCallback, query, GetClientUserId(client));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Spawn_ShowSaveLoadMainMenu(int client) {
|
|
||||||
Menu menu = new Menu(SaveLoadMainMenuHandler);
|
|
||||||
menu.SetTitle("Save / Load");
|
|
||||||
// Id is SaveType
|
|
||||||
menu.AddItem("1", "Map Scenes");
|
|
||||||
menu.AddItem("2", "Schematics");
|
|
||||||
menu.ExitBackButton = true;
|
|
||||||
menu.ExitButton = true;
|
|
||||||
menu.Display(client, MENU_TIME_FOREVER);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Spawn_ShowManagerMainMenu(int client, int index = 0) {
|
|
||||||
if(g_spawnedItems.Length == 0) {
|
|
||||||
SendEditorMessage(client, "No spawned items to manage");
|
|
||||||
DisplayTopMenuCategory(g_topMenu, g_propSpawnerCategory, client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Menu menu = new Menu(ManagerHandler);
|
|
||||||
menu.SetTitle("Manager");
|
|
||||||
// Id is SaveType
|
|
||||||
char info[8];
|
|
||||||
char buffer[128];
|
|
||||||
for(int i = 0; i < g_spawnedItems.Length; i++) {
|
|
||||||
int ref = GetSpawnedItem(i);
|
|
||||||
if(ref == -1) continue;
|
|
||||||
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;
|
|
||||||
menu.DisplayAt(client, index, MENU_TIME_FOREVER);
|
|
||||||
}
|
|
||||||
void ShowManagerEntityMenu(int client, int entity) {
|
|
||||||
if(!IsValidEntity(entity)) {
|
|
||||||
SendEditorMessage(client, "Item has vanished");
|
|
||||||
Spawn_ShowManagerMainMenu(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Menu menu = new Menu(ManagerEntityHandler);
|
|
||||||
menu.SetTitle("Manage %d", entity);
|
|
||||||
menu.AddItem("edit", "Edit");
|
|
||||||
menu.AddItem("delete", "Delete");
|
|
||||||
menu.AddItem("select", "Select");
|
|
||||||
menu.AddItem("view", "View");
|
|
||||||
menu.ExitBackButton = true;
|
|
||||||
menu.ExitButton = true;
|
|
||||||
menu.Display(client, MENU_TIME_FOREVER);
|
|
||||||
}
|
|
||||||
void ShowManagerSelectorMenu(int client) {
|
|
||||||
EntitySelector sel = EntitySelector.FromClient(client);
|
|
||||||
if(!sel.Active) {
|
|
||||||
sel.Start(GLOW_MANAGER);
|
|
||||||
sel.SetOnEnd(OnManagerSelectorEnd);
|
|
||||||
sel.SetOnPostSelect(OnManagerSelectorSelect);
|
|
||||||
sel.SetOnUnselect(OnManagerSelectorSelect);
|
|
||||||
}
|
|
||||||
Menu menu = new Menu(ManagerSelectorMainMenuHandler);
|
|
||||||
menu.SetTitle("Selector");
|
|
||||||
menu.AddItem("list", "> List Entities");
|
|
||||||
menu.AddItem("actions", "> Actions");
|
|
||||||
menu.AddItem("add-self", "Add All Self-Spawned");
|
|
||||||
menu.AddItem("add-all", "Add All Spawned");
|
|
||||||
menu.ExitBackButton = false;
|
|
||||||
menu.ExitButton = true;
|
|
||||||
menu.Display(client, MENU_TIME_FOREVER);
|
|
||||||
}
|
|
||||||
void ShowManagerSelectorActionsMenu(int client) {
|
|
||||||
Menu menu = new Menu(ManagerSelectorActionHandler);
|
|
||||||
menu.SetTitle("Selector: Select action");
|
|
||||||
char display[32];
|
|
||||||
Format(display, sizeof(display), "Entities: %d", g_PropData[client].Selector.list.Length);
|
|
||||||
menu.AddItem("", display, ITEMDRAW_DISABLED);
|
|
||||||
|
|
||||||
// menu.AddItem("edit", "Edit");
|
|
||||||
menu.AddItem("delete", "Delete");
|
|
||||||
// menu.AddItem("select", "Select");
|
|
||||||
menu.AddItem("save", "Save");
|
|
||||||
menu.ExitBackButton = true;
|
|
||||||
menu.ExitButton = true;
|
|
||||||
menu.Display(client, MENU_TIME_FOREVER);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShowSaves(int client, SaveType type) {
|
|
||||||
ArrayList saves;
|
|
||||||
Menu newMenu;
|
|
||||||
if(type == Save_Scene) {
|
|
||||||
newMenu = new Menu(SaveLoadSceneHandler);
|
|
||||||
newMenu.SetTitle("Save & Load > Map Scenes");
|
|
||||||
newMenu.AddItem("", "[Save New Scene]");
|
|
||||||
saves = LoadScenes();
|
|
||||||
} else if(type == Save_Schematic) {
|
|
||||||
newMenu = new Menu(SaveLoadSchematicHandler);
|
|
||||||
newMenu.SetTitle("Save & Load > Schematics");
|
|
||||||
if(g_PropData[client].pendingSaveType == Save_Schematic) {
|
|
||||||
newMenu.AddItem("", "[Save Schematic]");
|
|
||||||
} else {
|
|
||||||
newMenu.AddItem("", "[Start New Schematic]");
|
|
||||||
// Don't load saves when in middle of creating schematic
|
|
||||||
saves = LoadSchematics();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(saves != null) {
|
|
||||||
char name[64];
|
|
||||||
for(int i = 0; i < saves.Length; i++) {
|
|
||||||
saves.GetString(i, name, sizeof(name));
|
|
||||||
newMenu.AddItem(name, name);
|
|
||||||
}
|
|
||||||
delete saves;
|
|
||||||
}
|
|
||||||
newMenu.ExitBackButton = true;
|
|
||||||
newMenu.ExitButton = true;
|
|
||||||
newMenu.Display(client, MENU_TIME_FOREVER);
|
|
||||||
}
|
|
|
@ -1,513 +0,0 @@
|
||||||
|
|
||||||
ArrayList LoadScenes() {
|
|
||||||
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 null;
|
|
||||||
char buffer[64];
|
|
||||||
ArrayList saves = new ArrayList(ByteCountToCells(64));
|
|
||||||
while(listing.GetNext(buffer, sizeof(buffer), fileType)) {
|
|
||||||
if(buffer[0] == '.') continue;
|
|
||||||
saves.PushString(buffer);
|
|
||||||
}
|
|
||||||
delete listing;
|
|
||||||
return saves;
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList LoadSchematics() {
|
|
||||||
char path[PLATFORM_MAX_PATH];
|
|
||||||
BuildPath(Path_SM, path, sizeof(path), "data/prop_spawner/schematics");
|
|
||||||
FileType fileType;
|
|
||||||
DirectoryListing listing = OpenDirectory(path);
|
|
||||||
if(listing == null) return null;
|
|
||||||
char buffer[64];
|
|
||||||
ArrayList saves = new ArrayList(ByteCountToCells(64));
|
|
||||||
while(listing.GetNext(buffer, sizeof(buffer), fileType) && fileType == FileType_File) {
|
|
||||||
if(buffer[0] == '.') continue;
|
|
||||||
saves.PushString(buffer);
|
|
||||||
}
|
|
||||||
delete listing;
|
|
||||||
return saves;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LoadScene(const char[] save, bool asPreview = false) {
|
|
||||||
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];
|
|
||||||
if(asPreview) {
|
|
||||||
// Kill any previous preview
|
|
||||||
if(g_previewItems != null) ClearSavePreview();
|
|
||||||
g_previewItems = new ArrayList();
|
|
||||||
}
|
|
||||||
SaveData data;
|
|
||||||
while(file.ReadLine(buffer, sizeof(buffer))) {
|
|
||||||
if(buffer[0] == '#') continue;
|
|
||||||
data.Deserialize(buffer);
|
|
||||||
int entity = data.ToEntity(NULL_VECTOR, asPreview);
|
|
||||||
if(entity == -1) {
|
|
||||||
PrintToServer("[Editor] LoadScene(\"%s\", %b): failed to create %s", save, asPreview, buffer);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete file;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConfirmSave(int client, const char[] name) {
|
|
||||||
Menu newMenu = new Menu(SaveLoadConfirmHandler);
|
|
||||||
newMenu.AddItem(name, "Spawn");
|
|
||||||
newMenu.AddItem("", "Cancel");
|
|
||||||
newMenu.ExitBackButton = false;
|
|
||||||
newMenu.ExitButton = false;
|
|
||||||
newMenu.Display(client, 0);
|
|
||||||
}
|
|
||||||
void ClearSavePreview() {
|
|
||||||
if(g_previewItems != null) {
|
|
||||||
for(int i = 0; i < g_previewItems.Length; i++) {
|
|
||||||
int ref = g_previewItems.Get(i);
|
|
||||||
if(IsValidEntity(ref)) {
|
|
||||||
RemoveEntity(ref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete g_previewItems;
|
|
||||||
}
|
|
||||||
g_pendingSaveClient = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddSpawnedItem(int entity, int client = 0) {
|
|
||||||
if(client > 0 && g_PropData[client].pendingSaveType == Save_Schematic) {
|
|
||||||
g_PropData[client].schematic.AddEntity(entity, client);
|
|
||||||
}
|
|
||||||
// TODO: confirm if we want it to be in list, otherwise we need to clean manually
|
|
||||||
int userid = client > 0 ? GetClientUserId(client) : 0;
|
|
||||||
int index = g_spawnedItems.Push(EntIndexToEntRef(entity));
|
|
||||||
g_spawnedItems.Set(index, userid, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CreateSceneSave(const char[] name) {
|
|
||||||
char path[PLATFORM_MAX_PATH];
|
|
||||||
BuildPath(Path_SM, path, sizeof(path), "data/prop_spawner/saves/%s", g_currentMap);
|
|
||||||
CreateDirectory(path, 509);
|
|
||||||
Format(path, sizeof(path), "%s/%s.txt", path, name);
|
|
||||||
File file = OpenFile(path, "w");
|
|
||||||
if(file == null) {
|
|
||||||
PrintToServer("[Editor] Could not save: %s", path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
char buffer[132];
|
|
||||||
SaveData data;
|
|
||||||
for(int i = 0; i < g_spawnedItems.Length; i++) {
|
|
||||||
int ref = g_spawnedItems.Get(i);
|
|
||||||
if(IsValidEntity(ref)) {
|
|
||||||
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(ROOT_CATEGORY.items != null) return;
|
|
||||||
ROOT_CATEGORY.items = new ArrayList(sizeof(CategoryData));
|
|
||||||
char path[PLATFORM_MAX_PATH];
|
|
||||||
BuildPath(Path_SM, path, sizeof(path), "data/prop_spawner/models");
|
|
||||||
LoadFolder(ROOT_CATEGORY.items, path);
|
|
||||||
ROOT_CATEGORY.items.SortCustom(SortCategories);
|
|
||||||
}
|
|
||||||
int SortCategories(int index1, int index2, ArrayList array, Handle hndl) {
|
|
||||||
CategoryData cat1;
|
|
||||||
array.GetArray(index1, cat1);
|
|
||||||
CategoryData cat2;
|
|
||||||
array.GetArray(index2, cat2);
|
|
||||||
return strcmp(cat1.name, cat2.name);
|
|
||||||
}
|
|
||||||
public void UnloadCategories() {
|
|
||||||
if(ROOT_CATEGORY.items == null) return;
|
|
||||||
_UnloadCategories(ROOT_CATEGORY.items);
|
|
||||||
delete ROOT_CATEGORY.items;
|
|
||||||
}
|
|
||||||
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(sizeof(CategoryData));
|
|
||||||
|
|
||||||
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))) {
|
|
||||||
if(buffer[0] == '#') continue;
|
|
||||||
ReplaceString(buffer, sizeof(buffer), "\n", "");
|
|
||||||
ReplaceString(buffer, sizeof(buffer), "\r", "");
|
|
||||||
ItemData item;
|
|
||||||
int index = SplitString(buffer, ":", item.model, sizeof(item.model));
|
|
||||||
if(index == -1) {
|
|
||||||
index = SplitString(buffer, " ", item.model, sizeof(item.model));
|
|
||||||
if(index == -1) {
|
|
||||||
// No name provided, use the model's filename
|
|
||||||
index = FindCharInString(buffer, '/', true);
|
|
||||||
strcopy(item.name, sizeof(item.name), item.model[index + 1]);
|
|
||||||
} else {
|
|
||||||
strcopy(item.name, sizeof(item.name), buffer[index]);
|
|
||||||
}
|
|
||||||
category.items.PushArray(item);
|
|
||||||
} else if(StrEqual(item.model, "Classname")) {
|
|
||||||
strcopy(category.classnameOverride, sizeof(category.classnameOverride), buffer[index]);
|
|
||||||
} else if(StrEqual(item.model, "Type")) {
|
|
||||||
Format(category.classnameOverride, sizeof(category.classnameOverride), "_%s", buffer[index]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parent.PushArray(category);
|
|
||||||
delete file;
|
|
||||||
}
|
|
||||||
bool recentsChanged = false;
|
|
||||||
bool SaveRecents() {
|
|
||||||
if(!recentsChanged) return true; // Nothing to do, nothing changed
|
|
||||||
if(g_recentItems == null) return false;
|
|
||||||
char path[PLATFORM_MAX_PATH];
|
|
||||||
BuildPath(Path_SM, path, sizeof(path), "data/prop_spawner/recents_cache.csv");
|
|
||||||
File file = OpenFile(path, "w");
|
|
||||||
if(file == null) {
|
|
||||||
PrintToServer("[Editor] Could not write to %s", path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
StringMapSnapshot snapshot = g_recentItems.Snapshot();
|
|
||||||
char model[128];
|
|
||||||
RecentEntry entry;
|
|
||||||
for(int i = 0; i < snapshot.Length; i++) {
|
|
||||||
snapshot.GetKey(i, model, sizeof(model));
|
|
||||||
g_recentItems.GetArray(model, entry, sizeof(entry));
|
|
||||||
file.WriteLine("%s,%s,%d", model, entry.name, entry.count);
|
|
||||||
}
|
|
||||||
file.Flush();
|
|
||||||
delete file;
|
|
||||||
delete snapshot;
|
|
||||||
recentsChanged = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool LoadRecents() {
|
|
||||||
if(g_recentItems != null) delete g_recentItems;
|
|
||||||
char path[PLATFORM_MAX_PATH];
|
|
||||||
BuildPath(Path_SM, path, sizeof(path), "data/prop_spawner/recents_cache.csv");
|
|
||||||
File file = OpenFile(path, "r");
|
|
||||||
if(file == null) return false;
|
|
||||||
g_recentItems = new StringMap();
|
|
||||||
char buffer[128+64+16];
|
|
||||||
char model[128];
|
|
||||||
RecentEntry entry;
|
|
||||||
while(file.ReadLine(buffer, sizeof(buffer))) {
|
|
||||||
int index = SplitString(buffer, ",", model, sizeof(model));
|
|
||||||
index += SplitString(buffer[index], ",", entry.name, sizeof(entry.name));
|
|
||||||
entry.count = StringToInt(buffer[index]);
|
|
||||||
g_recentItems.SetArray(model, entry, sizeof(entry));
|
|
||||||
}
|
|
||||||
delete file;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns an ArrayList<ItemData> of all the recents
|
|
||||||
ArrayList GetRecentsItemList() {
|
|
||||||
ArrayList items = new ArrayList(sizeof(ItemData));
|
|
||||||
StringMapSnapshot snapshot = g_recentItems.Snapshot();
|
|
||||||
char model[128];
|
|
||||||
RecentEntry entry;
|
|
||||||
ItemData item;
|
|
||||||
for(int i = 0; i < snapshot.Length; i++) {
|
|
||||||
snapshot.GetKey(i, model, sizeof(model));
|
|
||||||
g_recentItems.GetArray(model, entry, sizeof(entry));
|
|
||||||
strcopy(item.model, sizeof(item.model), model);
|
|
||||||
strcopy(item.name, sizeof(item.name), entry.name);
|
|
||||||
}
|
|
||||||
// This is pretty expensive in terms of allocations but shrug
|
|
||||||
items.SortCustom(SortRecents);
|
|
||||||
delete snapshot;
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SortRecents(int index1, int index2, ArrayList array, Handle handle) {
|
|
||||||
ItemData data1;
|
|
||||||
array.GetArray(index1, data1);
|
|
||||||
ItemData data2;
|
|
||||||
array.GetArray(index2, data2);
|
|
||||||
|
|
||||||
int count1, count2;
|
|
||||||
RecentEntry entry;
|
|
||||||
if(g_recentItems.GetArray(data1.model, entry, sizeof(entry))) return 0; //skip if somehow no entry
|
|
||||||
count1 = entry.count;
|
|
||||||
if(g_recentItems.GetArray(data2.model, entry, sizeof(entry))) return 0; //skip if somehow no entry
|
|
||||||
count2 = entry.count;
|
|
||||||
return count2 - count1; // desc
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddRecent(const char[] model, const char[] name) {
|
|
||||||
if(g_recentItems == null) {
|
|
||||||
if(!LoadRecents()) return;
|
|
||||||
}
|
|
||||||
RecentEntry entry;
|
|
||||||
if(!g_recentItems.GetArray(model, entry, sizeof(entry))) {
|
|
||||||
entry.count = 0;
|
|
||||||
strcopy(entry.name, sizeof(entry.name), name);
|
|
||||||
}
|
|
||||||
entry.count++;
|
|
||||||
recentsChanged = true;
|
|
||||||
g_recentItems.SetArray(model, entry, sizeof(entry));
|
|
||||||
}
|
|
||||||
public Action OnClientSayCommand(int client, const char[] command, const char[] sArgs) {
|
|
||||||
if(g_PropData[client].chatPrompt == Prompt_None) {
|
|
||||||
return Plugin_Continue;
|
|
||||||
}
|
|
||||||
switch(g_PropData[client].chatPrompt) {
|
|
||||||
case Prompt_Search: DoSearch(client, sArgs);
|
|
||||||
case Prompt_SaveScene: {
|
|
||||||
if(CreateSceneSave(sArgs)) {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Saved as \x05%s/%s.txt", g_currentMap, sArgs);
|
|
||||||
} else {
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Unable to save. Sorry.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case Prompt_SaveSchematic: {
|
|
||||||
g_PropData[client].StartSchematic(client, sArgs);
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 Not implemented.");
|
|
||||||
}
|
|
||||||
g_PropData[client].chatPrompt = Prompt_None;
|
|
||||||
return Plugin_Handled;
|
|
||||||
}
|
|
||||||
void DoSearch(int client, const char[] query) {
|
|
||||||
ArrayList results = SearchItems(query);
|
|
||||||
if(results.Length == 0) {
|
|
||||||
CPrintToChat(client, "\x04[Editor]\x01 No results found. :(");
|
|
||||||
} else {
|
|
||||||
char title[64];
|
|
||||||
Format(title, sizeof(title), "Results for \"%s\"", query);
|
|
||||||
ShowTempItemMenu(client, results, title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Gets the index of the spawned item, starting at index. negative to go from back
|
|
||||||
int GetSpawnedIndex(int client, int index) {
|
|
||||||
int userid = GetClientUserId(client);
|
|
||||||
if(index >= 0) {
|
|
||||||
for(int i = index; i < g_spawnedItems.Length; i++) {
|
|
||||||
int spawnedBy = g_spawnedItems.Get(i, 1);
|
|
||||||
if(spawnedBy == userid) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for(int i = g_spawnedItems.Length + index; i >= 0; i--) {
|
|
||||||
int spawnedBy = g_spawnedItems.Get(i, 1);
|
|
||||||
if(spawnedBy == userid) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#define MAX_SEARCH_RESULTS 10
|
|
||||||
ArrayList SearchItems(const char[] query) {
|
|
||||||
// We have to put it into SearchData enum struct, then convert it back to ItemResult
|
|
||||||
LoadCategories();
|
|
||||||
ArrayList results = new ArrayList(sizeof(SearchData));
|
|
||||||
_searchCategory(results, ROOT_CATEGORY.items, query);
|
|
||||||
results.SortCustom(SortSearch);
|
|
||||||
ArrayList items = new ArrayList(sizeof(ItemData));
|
|
||||||
ItemData item;
|
|
||||||
SearchData data;
|
|
||||||
for(int i = 0; i < results.Length; i++) {
|
|
||||||
results.GetArray(i, data);
|
|
||||||
item.FromSearchData(data);
|
|
||||||
items.PushArray(item);
|
|
||||||
}
|
|
||||||
delete results;
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SortSearch(int index1, int index2, ArrayList array, Handle handle) {
|
|
||||||
SearchData data1;
|
|
||||||
array.GetArray(index1, data1);
|
|
||||||
SearchData data2;
|
|
||||||
array.GetArray(index2, data2);
|
|
||||||
return data1.index - data2.index;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _searchCategory(ArrayList results, ArrayList categories, const char[] query) {
|
|
||||||
CategoryData cat;
|
|
||||||
if(categories == null) return;
|
|
||||||
for(int i = 0; i < categories.Length; i++) {
|
|
||||||
categories.GetArray(i, cat);
|
|
||||||
if(cat.hasItems) {
|
|
||||||
//cat.items is of CatetoryData
|
|
||||||
if(!_searchItems(results, cat.items, query)) return;
|
|
||||||
} else {
|
|
||||||
//cat.items is of ItemData
|
|
||||||
_searchCategory(results, cat.items, query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool _searchItems(ArrayList results, ArrayList items, const char[] query) {
|
|
||||||
ItemData item;
|
|
||||||
SearchData search;
|
|
||||||
if(items == null) return false;
|
|
||||||
for(int i = 0; i < items.Length; i++) {
|
|
||||||
items.GetArray(i, item);
|
|
||||||
int searchIndex = StrContains(item.name, query, false);
|
|
||||||
if(searchIndex > -1) {
|
|
||||||
search.FromItemData(item);
|
|
||||||
search.index = searchIndex;
|
|
||||||
results.PushArray(search);
|
|
||||||
if(results.Length > MAX_SEARCH_RESULTS) return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetSpawnedItem(int index) {
|
|
||||||
if(index < 0 || index >= g_spawnedItems.Length) return -1;
|
|
||||||
int ref = g_spawnedItems.Get(index);
|
|
||||||
if(!IsValidEntity(ref)) {
|
|
||||||
g_spawnedItems.Erase(index);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RemoveSpawnedProp(int ref) {
|
|
||||||
// int ref = EntIndexToEntRef(entity);
|
|
||||||
int index = g_spawnedItems.FindValue(ref);
|
|
||||||
if(index > -1) {
|
|
||||||
g_spawnedItems.Erase(index);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnDeleteToolEnd(int client, ArrayList entities) {
|
|
||||||
int count;
|
|
||||||
for(int i = 0; i < entities.Length; i++) {
|
|
||||||
int ref = entities.Get(i);
|
|
||||||
if(IsValidEntity(ref)) {
|
|
||||||
count++;
|
|
||||||
RemoveSpawnedProp(ref);
|
|
||||||
RemoveEntity(ref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete entities;
|
|
||||||
PrintToChat(client, "\x04[Editor]\x01 \x05%d\x01 entities deleted", count);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnManagerSelectorEnd(int client, ArrayList entities) {
|
|
||||||
// TODO: implement manager selector cb
|
|
||||||
ReplyToCommand(client, "Not Implemented");
|
|
||||||
delete entities;
|
|
||||||
}
|
|
||||||
void OnManagerSelectorSelect(int client, int entity) {
|
|
||||||
// update entity count
|
|
||||||
// ShowManagerSelectorMenu(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
if(IsValidEntity(ref)) {
|
|
||||||
RemoveEntity(ref);
|
|
||||||
}
|
|
||||||
// TODO: erasing while removing
|
|
||||||
g_spawnedItems.Erase(i);
|
|
||||||
i--; // go back up one
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SHOW_HINT_MIN_DURATION 600 // 600 s (10min)
|
|
||||||
void ShowHint(int client) {
|
|
||||||
int time = GetTime();
|
|
||||||
int lastActive = g_PropData[client].lastActiveTime;
|
|
||||||
g_PropData[client].lastActiveTime = time;
|
|
||||||
if(time - lastActive < SHOW_HINT_MIN_DURATION) return;
|
|
||||||
PrintToChat(client, "\x05ZOOM: \x01Change Mode");
|
|
||||||
PrintToChat(client, "\x05USE: \x01Place \x05Shift + USE: \x01Cancel \x05Ctrl + USE: \x01Change Type");
|
|
||||||
PrintToChat(client, "\x05R: \x01Rotate (hold, use mouse) \x05Left Click: \x01Change Axis \x05Right Click: \x01Snap Angle");
|
|
||||||
PrintToChat(client, "Type \x05/prop favorite\x01 to (un)favorite.");
|
|
||||||
PrintToChat(client, "More information & cheatsheat: \x05%s", "https://admin.jackz.me/docs/props");
|
|
||||||
}
|
|
||||||
|
|
||||||
void ToggleFavorite(int client, const char[] model, const char[] name = "") {
|
|
||||||
char query[256];
|
|
||||||
GetClientAuthId(client, AuthId_Steam2, query, sizeof(query));
|
|
||||||
DataPack pack;
|
|
||||||
pack.WriteCell(GetClientUserId(client));
|
|
||||||
pack.WriteString(model);
|
|
||||||
pack.WriteString(name);
|
|
||||||
g_db.Format(query, sizeof(query), "SELECT name FROM editor_favorites WHERE steamid = '%s' AND model = '%s'", query, model);
|
|
||||||
g_db.Query(DB_ToggleFavoriteCallback, query, pack);
|
|
||||||
}
|
|
|
@ -1,155 +0,0 @@
|
||||||
#if defined _editor_included_
|
|
||||||
#endinput
|
|
||||||
#endif
|
|
||||||
#define _editor_included_
|
|
||||||
|
|
||||||
public SharedPlugin __pl_editor_ = {
|
|
||||||
name = "editor",
|
|
||||||
file = "hats.smx",
|
|
||||||
#if defined REQUIRE_PLUGIN
|
|
||||||
required = 1,
|
|
||||||
#else
|
|
||||||
required = 0,
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
#if !defined REQUIRE_PLUGIN
|
|
||||||
public void __pl_editor__SetNTVOptional()
|
|
||||||
{
|
|
||||||
MarkNativeAsOptional("SpawnSchematic");
|
|
||||||
MarkNativeAsOptional("StartEdit");
|
|
||||||
MarkNativeAsOptional("StartSpawner");
|
|
||||||
MarkNativeAsOptional("CancelEdit");
|
|
||||||
MarkNativeAsOptional("IsEditorActive");
|
|
||||||
|
|
||||||
MarkNativeAsOptional("StartSelector");
|
|
||||||
MarkNativeAsOptional("CancelSelector");
|
|
||||||
MarkNativeAsOptional("IsSelectorActive");
|
|
||||||
|
|
||||||
MarkNativeAsOptional("Selector.Count.get");
|
|
||||||
MarkNativeAsOptional("Selector.Active.get");
|
|
||||||
MarkNativeAsOptional("Selector.Start");
|
|
||||||
MarkNativeAsOptional("Selector.SetOnEnd");
|
|
||||||
MarkNativeAsOptional("Selector.SetOnPreSelect");
|
|
||||||
MarkNativeAsOptional("Selector.SetOnPostSelect");
|
|
||||||
MarkNativeAsOptional("Selector.SetOnUnselect");
|
|
||||||
MarkNativeAsOptional("Selector.AddEntity");
|
|
||||||
MarkNativeAsOptional("Selector.RemoveEntity");
|
|
||||||
MarkNativeAsOptional("Selector.Cancel");
|
|
||||||
MarkNativeAsOptional("Selector.End");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
native bool SpawnSchematic(const char name[32], const float pos[3], const float angles[3] = NULL_VECTOR);
|
|
||||||
|
|
||||||
/** Called when edit is done or cancelled
|
|
||||||
* @param client - client doing the edit
|
|
||||||
* @param entity - The entity edited
|
|
||||||
* @param result - Result of the edit, or cancelled
|
|
||||||
* @return boolean - only for StartSpawner, true to continue, false to end spawning
|
|
||||||
*/
|
|
||||||
typeset EditorDoneCallback {
|
|
||||||
function void (int client, int entity, CompleteType result);
|
|
||||||
function bool (int client, int entity, CompleteType result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Called when an item is to be selected.
|
|
||||||
* @return boolean - TRUE to allow item to be selected, FALSE to reject
|
|
||||||
*/
|
|
||||||
typedef SelectPreAddCallback = function bool (int client, int entity);
|
|
||||||
/** Called when an item has been selected */
|
|
||||||
typedef SelectPostAddCallback = function void (int client, int entity);
|
|
||||||
|
|
||||||
/** Called when an item is to be unselected. */
|
|
||||||
typedef SelectRemoveCallback = function void (int client, int entity);
|
|
||||||
/** Called when a user is done selecting items
|
|
||||||
* @param client - client doing the selection
|
|
||||||
* @param entities - if null, selection was cancelled. if not null, contains list of entity references, must be deleted.
|
|
||||||
*/
|
|
||||||
typedef SelectDoneCallback = function void (int client, ArrayList entities);
|
|
||||||
|
|
||||||
/** Starts editing an entity
|
|
||||||
* @param client - The client that is editing
|
|
||||||
* @param entity - The entity to edit
|
|
||||||
* @param doneCallback - Called when edit is done
|
|
||||||
*/
|
|
||||||
native void StartEdit(int client, int entity, EditorDoneCallback doneCallback);
|
|
||||||
/** Let client pick prop(s) to spawn
|
|
||||||
* @param client - The client that is editing
|
|
||||||
* @param entity - The entity to edit
|
|
||||||
* @param doneCallback - Called when edit is done
|
|
||||||
*/
|
|
||||||
native void StartSpawner(int client, EditorDoneCallback doneCallback);
|
|
||||||
native void CancelEdit(int client);
|
|
||||||
// Includes non-plugin started edits
|
|
||||||
native bool IsEditorActive(int client);
|
|
||||||
|
|
||||||
/** Starts a selection, where the client can click on entities to select or deselect them.
|
|
||||||
* @param client - the client that can select
|
|
||||||
* @param callback - called when user is done seleting or cancelled
|
|
||||||
* @param highlightColor - the color to highlight selected items, default solid green
|
|
||||||
* @param maxEntities - the max number of selections, 0 for infinite
|
|
||||||
*/
|
|
||||||
native void StartSelector(int client, SelectDoneCallback callback, int highlightColor[3] = { 0, 255, 0 }, int maxEntities = 0);
|
|
||||||
|
|
||||||
methodmap EntitySelector {
|
|
||||||
public EntitySelector(int client) {
|
|
||||||
return view_as<EntitySelector>(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static EntitySelector FromClient(int client) {
|
|
||||||
return view_as<EntitySelector>(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Starts a new selector for client
|
|
||||||
* @param highlightColor - the color to highlight selected items, default solid green
|
|
||||||
* @param flags - not used.
|
|
||||||
* @param maxEntities - the max number of selections, 0 for infinite
|
|
||||||
*/
|
|
||||||
public native EntitySelector Start(int highlightColor[3], int flags = 0, int maxEntities = 0);
|
|
||||||
|
|
||||||
|
|
||||||
property int Count {
|
|
||||||
/** Returns the number of entities in selector. Returns -1 if not active */
|
|
||||||
public native get();
|
|
||||||
}
|
|
||||||
|
|
||||||
property bool Active {
|
|
||||||
public native get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets the callback for when the selector is ended (or cancelled) */
|
|
||||||
public native void SetOnEnd(SelectDoneCallback callback);
|
|
||||||
|
|
||||||
/** Sets the callback for when an item is to be added to the selector. */
|
|
||||||
public native void SetOnPreSelect(SelectPreAddCallback callback);
|
|
||||||
|
|
||||||
/** Sets the callback for when an item has been added to the selector. */
|
|
||||||
public native void SetOnPostSelect(SelectPostAddCallback callback);
|
|
||||||
|
|
||||||
/** Sets the callback for when an item is removed from selector. */
|
|
||||||
public native void SetOnUnselect(SelectRemoveCallback callback);
|
|
||||||
|
|
||||||
/** Adds an entity to selection. Does not call SelectAddCallback */
|
|
||||||
public native void AddEntity(int entity);
|
|
||||||
|
|
||||||
/** Removes an entity from selection. Does not call SelectAddCallback */
|
|
||||||
public native void RemoveEntity(int entity);
|
|
||||||
|
|
||||||
public native void Cancel();
|
|
||||||
|
|
||||||
public native void End();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
native void CancelSelector(int client);
|
|
||||||
native bool IsSelectorActive(int client);
|
|
||||||
|
|
||||||
enum CompleteType {
|
|
||||||
Complete_WallSuccess,
|
|
||||||
Complete_WallError,
|
|
||||||
Complete_PropSpawned,
|
|
||||||
Complete_PropError,
|
|
||||||
Complete_EditSuccess
|
|
||||||
}
|
|
591
scripting/l4d2_editor.sp
Normal file
591
scripting/l4d2_editor.sp
Normal file
|
@ -0,0 +1,591 @@
|
||||||
|
#pragma semicolon 1
|
||||||
|
#pragma newdecls required
|
||||||
|
|
||||||
|
#define PLUGIN_VERSION "1.0"
|
||||||
|
|
||||||
|
#define DUMMY_MODEL "models/props/cs_office/vending_machine.mdl"
|
||||||
|
|
||||||
|
#include <sourcemod>
|
||||||
|
#include <sdktools>
|
||||||
|
#include <left4dhooks>
|
||||||
|
#include <clientprefs>
|
||||||
|
#include <jutils>
|
||||||
|
#include <gamemodes/ents>
|
||||||
|
#include <smlib/effects>
|
||||||
|
#include <multicolors>
|
||||||
|
#include <adminmenu>
|
||||||
|
|
||||||
|
int g_iLaserIndex;
|
||||||
|
|
||||||
|
float cmdThrottle[MAXPLAYERS+1];
|
||||||
|
|
||||||
|
TopMenu g_topMenu;
|
||||||
|
|
||||||
|
char g_currentMap[64];
|
||||||
|
|
||||||
|
//int g_markedMode
|
||||||
|
|
||||||
|
#include <editor/editor.sp>
|
||||||
|
#include <editor/props/base.sp>
|
||||||
|
#include <editor/natives.sp>
|
||||||
|
#include <editor>
|
||||||
|
|
||||||
|
public Plugin myinfo = {
|
||||||
|
name = "L4D2 Hats & Editor",
|
||||||
|
author = "jackzmc",
|
||||||
|
description = "",
|
||||||
|
version = PLUGIN_VERSION,
|
||||||
|
url = "https://github.com/Jackzmc/sourcemod-plugins"
|
||||||
|
};
|
||||||
|
|
||||||
|
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) {
|
||||||
|
RegPluginLibrary("editor");
|
||||||
|
// CreateNative("SpawnSchematic", Native_SpawnSchematic);
|
||||||
|
CreateNative("StartEdit", Native_StartEdit);
|
||||||
|
CreateNative("StartSpawner", Native_StartSpawner);
|
||||||
|
CreateNative("CancelEdit", Native_CancelEdit);
|
||||||
|
CreateNative("IsEditorActive", Native_IsEditorActive);
|
||||||
|
|
||||||
|
|
||||||
|
CreateNative("StartSelector", Native_StartSelector);
|
||||||
|
CreateNative("CancelSelector", Native_CancelSelector);
|
||||||
|
CreateNative("IsSelectorActive", Native_IsSelectorActive);
|
||||||
|
|
||||||
|
CreateNative("EntitySelector.Start", Native_Selector_Start);
|
||||||
|
CreateNative("EntitySelector.Count.get", Native_Selector_GetCount);
|
||||||
|
CreateNative("EntitySelector.Active.get", Native_Selector_GetActive);
|
||||||
|
CreateNative("EntitySelector.SetOnEnd", Native_Selector_SetOnEnd);
|
||||||
|
CreateNative("EntitySelector.SetOnPreSelect", Native_Selector_SetOnPreSelect);
|
||||||
|
CreateNative("EntitySelector.SetOnPostSelect", Native_Selector_SetOnPostSelect);
|
||||||
|
CreateNative("EntitySelector.SetOnUnselect", Native_Selector_SetOnUnselect);
|
||||||
|
CreateNative("EntitySelector.AddEntity", Native_Selector_AddEntity);
|
||||||
|
CreateNative("EntitySelector.RemoveEntity", Native_Selector_RemoveEntity);
|
||||||
|
CreateNative("EntitySelector.Cancel", Native_Selector_Cancel);
|
||||||
|
CreateNative("EntitySelector.End", Native_Selector_End);
|
||||||
|
return APLRes_Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void OnPluginStart() {
|
||||||
|
EngineVersion g_Game = GetEngineVersion();
|
||||||
|
if(g_Game != Engine_Left4Dead && g_Game != Engine_Left4Dead2) {
|
||||||
|
SetFailState("This plugin is for L4D/L4D2 only.");
|
||||||
|
}
|
||||||
|
|
||||||
|
createdWalls = new ArrayList();
|
||||||
|
g_spawnedItems = new ArrayList(2);
|
||||||
|
ROOT_CATEGORY.name = "Categories";
|
||||||
|
|
||||||
|
LoadTranslations("common.phrases");
|
||||||
|
HookEvent("player_spawn", Event_PlayerSpawn);
|
||||||
|
|
||||||
|
RegAdminCmd("sm_mkwall", Command_MakeWall, ADMFLAG_CUSTOM2);
|
||||||
|
RegAdminCmd("sm_edit", Command_Editor, ADMFLAG_CUSTOM2);
|
||||||
|
RegAdminCmd("sm_wall", Command_Editor, ADMFLAG_CUSTOM2);
|
||||||
|
RegAdminCmd("sm_prop", Command_Props, ADMFLAG_CUSTOM2);
|
||||||
|
|
||||||
|
if(SQL_CheckConfig(DATABASE_CONFIG_NAME)) {
|
||||||
|
if(!ConnectDB()) {
|
||||||
|
LogError("Failed to connect to database.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int entity = -1;
|
||||||
|
char targetName[32];
|
||||||
|
while((entity = FindEntityByClassname(entity, "func_brush")) != INVALID_ENT_REFERENCE) {
|
||||||
|
GetEntPropString(entity, Prop_Data, "m_iName", targetName, sizeof(targetName));
|
||||||
|
if(StrContains(targetName, "l4d2_hats_") == 0) {
|
||||||
|
createdWalls.Push(EntIndexToEntRef(entity));
|
||||||
|
SDKHook(entity, SDKHook_Use, OnWallClicked);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 1; i <= MaxClients; i++) {
|
||||||
|
Editor[i].client = i;
|
||||||
|
Editor[i].Reset(true);
|
||||||
|
g_PropData[i].Init(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
TopMenu topMenu;
|
||||||
|
if (LibraryExists("adminmenu") && ((topMenu = GetAdminTopMenu()) != null)) {
|
||||||
|
OnAdminMenuReady(topMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnLibraryRemoved(const char[] name) {
|
||||||
|
if (StrEqual(name, "adminmenu", false)) {
|
||||||
|
g_topMenu = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
// Get the cursor location
|
||||||
|
GetClientEyePosition(client, start);
|
||||||
|
GetClientEyeAngles(client, angle);
|
||||||
|
TR_TraceRayFilter(start, angle, MASK_SOLID, RayType_Infinite, Filter_NoPlayers, client);
|
||||||
|
if(TR_DidHit()) {
|
||||||
|
TR_GetEndPosition(outPos);
|
||||||
|
// Check if the position is a wall
|
||||||
|
TR_GetPlaneNormal(null, normal);
|
||||||
|
if(normal[2] < 0.1) {
|
||||||
|
|
||||||
|
// Find a suitable position above
|
||||||
|
start[0] = outPos[0];
|
||||||
|
start[1] = outPos[1];
|
||||||
|
start[2] = outPos[2] += 100.0;
|
||||||
|
TR_TraceRayFilter(outPos, start, MASK_SOLID, RayType_EndPoint, TraceEntityFilterPlayer, client);
|
||||||
|
bool ceilCollided = TR_DidHit();
|
||||||
|
bool ceilOK = !TR_AllSolid();
|
||||||
|
TR_GetEndPosition(ceilPos);
|
||||||
|
// float distCeil = GetVectorDistance(outPos, ceilPos, true);
|
||||||
|
|
||||||
|
// Find a suitable position backwards
|
||||||
|
angle[0] = 70.0;
|
||||||
|
angle[1] += 180.0;
|
||||||
|
TR_TraceRayFilter(outPos, angle, MASK_SOLID, RayType_Infinite, TraceEntityFilterPlayer, client);
|
||||||
|
bool wallCollided = TR_DidHit();
|
||||||
|
TR_GetEndPosition(wallPos);
|
||||||
|
float distWall = GetVectorDistance(outPos, wallPos, true);
|
||||||
|
|
||||||
|
if(ceilCollided && wallCollided)
|
||||||
|
|
||||||
|
if(wallCollided && distWall < 62500) {
|
||||||
|
outPos = wallPos;
|
||||||
|
} else if(ceilOK) {
|
||||||
|
outPos = ceilPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool g_inRotate[MAXPLAYERS+1];
|
||||||
|
public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3], float angles[3], int& weapon, int& subtype, int& cmdnum, int& tickcount, int& seed, int mouse[2]) {
|
||||||
|
float tick = GetGameTime();
|
||||||
|
int oldButtons = GetEntProp(client, Prop_Data, "m_nOldButtons");
|
||||||
|
if(g_pendingSaveClient == client) {
|
||||||
|
if(g_PropData[client].pendingSaveType == Save_Schematic) {
|
||||||
|
// move cursor? or should be editor anyway
|
||||||
|
}
|
||||||
|
} else if(g_PropData[client].Selector.IsActive()) {
|
||||||
|
SetWeaponDelay(client, 0.5);
|
||||||
|
if(tick - cmdThrottle[client] >= 0.20) {
|
||||||
|
if(buttons & IN_ATTACK) {
|
||||||
|
int entity = GetLookingEntity(client, Filter_ValidHats);
|
||||||
|
if(entity > 0) {
|
||||||
|
if(g_PropData[client].Selector.AddEntity(entity) != -1) {
|
||||||
|
PrecacheSound("ui/beep07.wav");
|
||||||
|
EmitSoundToClient(client, "ui/beep07.wav", entity, SND_CHANGEVOL, .volume = 0.5);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PrintHintText(client, "No entity found");
|
||||||
|
}
|
||||||
|
} else if(buttons & IN_ATTACK2) {
|
||||||
|
int entity = GetLookingEntity(client, Filter_ValidHats);
|
||||||
|
if(entity > 0) {
|
||||||
|
if(g_PropData[client].Selector.RemoveEntity(entity)) {
|
||||||
|
PrecacheSound("ui/beep22.wav");
|
||||||
|
EmitSoundToClient(client, "ui/beep22.wav", entity, SND_CHANGEVOL, .volume = 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(buttons & IN_USE) {
|
||||||
|
if(buttons & IN_SPEED) {
|
||||||
|
//Delete
|
||||||
|
g_PropData[client].Selector.End();
|
||||||
|
} else if(buttons & IN_DUCK) {
|
||||||
|
//Cancel
|
||||||
|
g_PropData[client].Selector.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmdThrottle[client] = tick;
|
||||||
|
}
|
||||||
|
} else if(Editor[client].IsActive()) {
|
||||||
|
// if(buttons & IN_USE && buttons & IN_RELOAD) {
|
||||||
|
// ClientCommand(client, "sm_wall done");
|
||||||
|
// return Plugin_Handled;
|
||||||
|
// }
|
||||||
|
bool allowMove = true;
|
||||||
|
switch(Editor[client].mode) {
|
||||||
|
case MOVE_ORIGIN: {
|
||||||
|
SetWeaponDelay(client, 0.5);
|
||||||
|
|
||||||
|
bool isRotate;
|
||||||
|
int flags = GetEntityFlags(client);
|
||||||
|
if(buttons & IN_RELOAD) {
|
||||||
|
if(!g_inRotate[client]) {
|
||||||
|
g_inRotate[client] = true;
|
||||||
|
}
|
||||||
|
if(!(oldButtons & IN_JUMP) && (buttons & IN_JUMP)) {
|
||||||
|
buttons &= ~IN_JUMP;
|
||||||
|
Editor[client].CycleStacker();
|
||||||
|
} else if(!(oldButtons & IN_SPEED) && (buttons & IN_SPEED)) {
|
||||||
|
Editor[client].ToggleCollision();
|
||||||
|
return Plugin_Handled;
|
||||||
|
} else if(!(oldButtons & IN_DUCK) && (buttons & IN_DUCK)) {
|
||||||
|
Editor[client].ToggleCollisionRotate();
|
||||||
|
return Plugin_Handled;
|
||||||
|
} else {
|
||||||
|
PrintCenterText(client, "%.1f %.1f %.1f", Editor[client].angles[0], Editor[client].angles[1], Editor[client].angles[2]);
|
||||||
|
isRotate = true;
|
||||||
|
SetEntityFlags(client, flags |= FL_FROZEN);
|
||||||
|
if(!(oldButtons & IN_ATTACK) && (buttons & IN_ATTACK)) Editor[client].CycleAxis();
|
||||||
|
else if(!(oldButtons & IN_ATTACK2) && (buttons & IN_ATTACK2)) Editor[client].CycleSnapAngle(tick);
|
||||||
|
|
||||||
|
// Rotation control:
|
||||||
|
// Turn off rotate when player wants rotate
|
||||||
|
Editor[client].hasCollisionRotate = false;
|
||||||
|
if(tick - cmdThrottle[client] > 0.1) {
|
||||||
|
if(Editor[client].axis == 0) {
|
||||||
|
int mouseXAbs = IntAbs(mouse[0]);
|
||||||
|
int mouseYAbs = IntAbs(mouse[1]);
|
||||||
|
bool XOverY = mouseXAbs > mouseYAbs;
|
||||||
|
if(mouseYAbs > 10 && !XOverY) {
|
||||||
|
Editor[client].IncrementAxis(0, mouse[1]);
|
||||||
|
} else if(mouseXAbs > 10 && XOverY) {
|
||||||
|
Editor[client].IncrementAxis(1, mouse[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(Editor[client].axis == 1) {
|
||||||
|
if(mouse[0] > 10) Editor[client].angles[2] += Editor[client].snapAngle;
|
||||||
|
else if(mouse[0] < -10) Editor[client].angles[2] -= Editor[client].snapAngle;
|
||||||
|
}
|
||||||
|
cmdThrottle[client] = tick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(g_inRotate[client]) {
|
||||||
|
g_inRotate[client] = false;
|
||||||
|
}
|
||||||
|
// Move position
|
||||||
|
float moveAmount = (buttons & IN_SPEED) ? 2.0 : 1.0;
|
||||||
|
if(buttons & IN_ATTACK) Editor[client].moveDistance += moveAmount;
|
||||||
|
else if(buttons & IN_ATTACK2) Editor[client].moveDistance -= moveAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear IN_FROZEN when no longer rotate
|
||||||
|
if(!isRotate && flags & FL_FROZEN) {
|
||||||
|
flags = flags & ~FL_FROZEN;
|
||||||
|
SetEntityFlags(client, flags);
|
||||||
|
}
|
||||||
|
if(Editor[client].stackerDirection == Stack_Off)
|
||||||
|
CalculateEditorPosition(client, Filter_IgnorePlayerAndWall);
|
||||||
|
}
|
||||||
|
case SCALE: {
|
||||||
|
SetWeaponDelay(client, 0.5);
|
||||||
|
allowMove = false;
|
||||||
|
if(buttons & IN_USE) {
|
||||||
|
Editor[client].CycleSpeed(tick);
|
||||||
|
} else {
|
||||||
|
if(buttons & IN_MOVELEFT) {
|
||||||
|
Editor[client].IncrementSize(0, -1.0);
|
||||||
|
} else if(buttons & IN_MOVERIGHT) {
|
||||||
|
Editor[client].IncrementSize(0, 1.0);
|
||||||
|
Editor[client].size[0] += Editor[client].moveSpeed;
|
||||||
|
}
|
||||||
|
if(buttons & IN_FORWARD) {
|
||||||
|
Editor[client].IncrementSize(0, 1.0);
|
||||||
|
} else if(buttons & IN_BACK) {
|
||||||
|
Editor[client].IncrementSize(0, -1.0);
|
||||||
|
}
|
||||||
|
if(buttons & IN_JUMP) {
|
||||||
|
Editor[client].IncrementSize(0, 1.0);
|
||||||
|
} else if(buttons & IN_DUCK) {
|
||||||
|
Editor[client].IncrementSize(0, -1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case COLOR: {
|
||||||
|
SetWeaponDelay(client, 0.5);
|
||||||
|
PrintHintText(client, "%d %d %d %d", Editor[client].color[0], Editor[client].color[1], Editor[client].color[2], Editor[client].color[3]);
|
||||||
|
if(buttons & IN_USE) {
|
||||||
|
Editor[client].CycleColorComponent(tick);
|
||||||
|
} else if(buttons & IN_ATTACK2) {
|
||||||
|
Editor[client].IncreaseColor(1);
|
||||||
|
allowMove = false;
|
||||||
|
} else if(buttons & IN_ATTACK) {
|
||||||
|
Editor[client].IncreaseColor(-1);
|
||||||
|
allowMove = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(buttons & IN_DUCK) {
|
||||||
|
|
||||||
|
}
|
||||||
|
if(Editor[client].mode != COLOR && !(oldButtons & IN_USE) && buttons & IN_USE) {
|
||||||
|
if(buttons & IN_SPEED) {
|
||||||
|
Editor[client].Cancel();
|
||||||
|
} else if(buttons & IN_DUCK) {
|
||||||
|
Editor[client].CycleBuildType();
|
||||||
|
// Editor[client].ShowExtraOptions();
|
||||||
|
} else {
|
||||||
|
int entity;
|
||||||
|
Editor[client].Done(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(!(oldButtons & IN_ZOOM) && buttons & IN_ZOOM) {
|
||||||
|
Editor[client].CycleMode(); // ZOOM: Cycle forward
|
||||||
|
}
|
||||||
|
|
||||||
|
Editor[client].Draw(BUILDER_COLOR, 0.1, 0.1);
|
||||||
|
return allowMove ? Plugin_Continue : Plugin_Handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Plugin_Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IntAbs(int a) {
|
||||||
|
if(a < 0) {
|
||||||
|
return a * -1;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) {
|
||||||
|
int client = GetClientOfUserId(event.GetInt("userid"));
|
||||||
|
if(client > 0) {
|
||||||
|
SDKHook(client, SDKHook_WeaponCanUse, OnWeaponUse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Action OnWeaponUse(int client, int weapon) {
|
||||||
|
int ref = EntIndexToEntRef(weapon);
|
||||||
|
// Prevent picking up weapons that are previews
|
||||||
|
for(int i = 1; i <= MaxClients; i++) {
|
||||||
|
if(Editor[i].entity == ref && Editor[i].flags & Edit_Preview) {
|
||||||
|
return Plugin_Handled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Plugin_Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnClientDisconnect(int client) {
|
||||||
|
Editor[client].Reset();
|
||||||
|
g_PropData[client].Reset();
|
||||||
|
if(g_pendingSaveClient == client) {
|
||||||
|
g_pendingSaveClient = 0;
|
||||||
|
ClearSavePreview();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnMapStart() {
|
||||||
|
PrecacheModel(DUMMY_MODEL);
|
||||||
|
g_iLaserIndex = PrecacheModel("materials/sprites/laserbeam.vmt");
|
||||||
|
for(int i = 1; i <= MaxClients; i++) {
|
||||||
|
cmdThrottle[i] = 0.0;
|
||||||
|
}
|
||||||
|
GetCurrentMap(g_currentMap, sizeof(g_currentMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void OnMapEnd() {
|
||||||
|
g_spawnedItems.Clear();
|
||||||
|
for(int i = 1; i <= createdWalls.Length; i++) {
|
||||||
|
DeleteWall(i);
|
||||||
|
}
|
||||||
|
createdWalls.Clear();
|
||||||
|
UnloadCategories();
|
||||||
|
UnloadSave();
|
||||||
|
SaveRecents();
|
||||||
|
}
|
||||||
|
public void OnPluginEnd() {
|
||||||
|
for(int i = 1; i <= MaxClients; i++) {
|
||||||
|
if(IsClientConnected(i) && IsClientInGame(i)) {
|
||||||
|
int flags = GetEntityFlags(i) & ~FL_FROZEN;
|
||||||
|
SetEntityFlags(i, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(g_spawnedItems != null) {
|
||||||
|
delete g_spawnedItems;
|
||||||
|
}
|
||||||
|
TriggerInput("prop_preview", "Kill");
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
GetClientEyeAngles(client, ang);
|
||||||
|
TR_TraceRayFilter(pos, ang, MASK_SOLID, RayType_Infinite, filter, client);
|
||||||
|
if(TR_DidHit()) {
|
||||||
|
return TR_GetEntityIndex();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
stock bool Filter_OnlyPlayers(int entity, int mask, int data) {
|
||||||
|
return entity > 0 && entity <= MaxClients && entity != data;
|
||||||
|
}
|
||||||
|
|
||||||
|
stock bool Filter_NoPlayers(int entity, int mask, int data) {
|
||||||
|
return entity > MaxClients && entity != data;
|
||||||
|
}
|
||||||
|
|
||||||
|
stock bool Filter_IgnorePlayerAndWall(int entity, int mask, int data) {
|
||||||
|
if(entity > MaxClients && entity != data && EntRefToEntIndex(Editor[data].entity) != entity) {
|
||||||
|
static char classname[16];
|
||||||
|
GetEntityClassname(entity, classname, sizeof(classname));
|
||||||
|
// Ignore infected
|
||||||
|
return !StrEqual(classname, "infected");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Filter_ValidHats(int entity, int mask, int data) {
|
||||||
|
if(entity == data) return false;
|
||||||
|
if(entity <= MaxClients && entity > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return CheckBlacklist(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define MAX_FORBIDDEN_CLASSNAMES 10
|
||||||
|
static char FORBIDDEN_CLASSNAMES[MAX_FORBIDDEN_CLASSNAMES][] = {
|
||||||
|
// "env_physics_blocker",
|
||||||
|
// "env_player_blocker",
|
||||||
|
"func_brush",
|
||||||
|
"func_simpleladder",
|
||||||
|
"func_button",
|
||||||
|
"func_elevator",
|
||||||
|
"func_button_timed",
|
||||||
|
"func_movelinear",
|
||||||
|
"func_tracktrain",
|
||||||
|
// "infected",
|
||||||
|
"func_lod",
|
||||||
|
"prop_ragdoll",
|
||||||
|
"move_rope"
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAX_FORBIDDEN_MODELS 2
|
||||||
|
char FORBIDDEN_MODELS[MAX_FORBIDDEN_MODELS][] = {
|
||||||
|
"models/props_vehicles/c130.mdl",
|
||||||
|
"models/props_vehicles/helicopter_rescue.mdl"
|
||||||
|
};
|
||||||
|
|
||||||
|
bool CheckBlacklist(int entity) {
|
||||||
|
if(entity == 0) return false;
|
||||||
|
static char buffer[64];
|
||||||
|
GetEntityClassname(entity, buffer, sizeof(buffer));
|
||||||
|
for(int i = 0; i < MAX_FORBIDDEN_CLASSNAMES; i++) {
|
||||||
|
if(StrEqual(FORBIDDEN_CLASSNAMES[i], buffer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(StrContains(buffer, "prop_") > -1) {
|
||||||
|
GetEntPropString(entity, Prop_Data, "m_ModelName", buffer, sizeof(buffer));
|
||||||
|
for(int i = 0; i < MAX_FORBIDDEN_MODELS; i++) {
|
||||||
|
if(StrEqual(FORBIDDEN_MODELS[i], buffer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GetEntPropString(entity, Prop_Data, "m_iName", buffer, sizeof(buffer));
|
||||||
|
if(StrEqual(buffer, "l4d2_randomizer")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
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]) {
|
||||||
|
float angle[3];
|
||||||
|
angle[0] = 90.0;
|
||||||
|
|
||||||
|
Handle trace = TR_TraceRayEx(start, angle, MASK_SHOT, RayType_Infinite);
|
||||||
|
if(!TR_DidHit(trace)) {
|
||||||
|
delete trace;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TR_GetEndPosition(end, trace);
|
||||||
|
delete trace;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
stock bool L4D_IsPlayerCapped(int client) {
|
||||||
|
if(GetEntPropEnt(client, Prop_Send, "m_pummelAttacker") > 0 ||
|
||||||
|
GetEntPropEnt(client, Prop_Send, "m_carryAttacker") > 0 ||
|
||||||
|
GetEntPropEnt(client, Prop_Send, "m_pounceAttacker") > 0 ||
|
||||||
|
GetEntPropEnt(client, Prop_Send, "m_jockeyAttacker") > 0 ||
|
||||||
|
GetEntPropEnt(client, Prop_Send, "m_pounceAttacker") > 0 ||
|
||||||
|
GetEntPropEnt(client, Prop_Send, "m_tongueOwner") > 0)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
stock void LookAtPoint(int entity, const float destination[3]){
|
||||||
|
float angles[3], pos[3], result[3];
|
||||||
|
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", pos);
|
||||||
|
MakeVectorFromPoints(destination, pos, result);
|
||||||
|
GetVectorAngles(result, angles);
|
||||||
|
if(angles[0] >= 270){
|
||||||
|
angles[0] -= 270;
|
||||||
|
angles[0] = (90-angles[0]);
|
||||||
|
} else {
|
||||||
|
if(angles[0] <= 90){
|
||||||
|
angles[0] *= -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
angles[1] -= 180;
|
||||||
|
TeleportEntity(entity, NULL_VECTOR, angles, NULL_VECTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
stock float SnapTo(const float value, const float degree) {
|
||||||
|
return float(RoundFloat(value / degree)) * degree;
|
||||||
|
}
|
||||||
|
|
||||||
|
stock bool CalculateEditorPosition(int client, TraceEntityFilter filter) {
|
||||||
|
if (client > 0 && client <= MaxClients && IsClientInGame(client)) {
|
||||||
|
float clientEye[3], clientAngle[3], direction[3];
|
||||||
|
GetClientEyePosition(client, clientEye);
|
||||||
|
GetClientEyeAngles(client, clientAngle);
|
||||||
|
|
||||||
|
GetAngleVectors(clientAngle, direction, NULL_VECTOR, NULL_VECTOR);
|
||||||
|
ScaleVector(direction, Editor[client].moveDistance);
|
||||||
|
AddVectors(clientEye, direction, Editor[client].origin);
|
||||||
|
|
||||||
|
if(Editor[client].hasCollision) {
|
||||||
|
TR_TraceRayFilter(clientEye, Editor[client].origin, MASK_OPAQUE, RayType_EndPoint, filter, client);
|
||||||
|
if (TR_DidHit(INVALID_HANDLE)) {
|
||||||
|
TR_GetEndPosition(Editor[client].origin);
|
||||||
|
GetEntPropVector(Editor[client].entity, Prop_Send, "m_vecMins", direction);
|
||||||
|
Editor[client].origin[2] -= direction[2];
|
||||||
|
if(Editor[client].hasCollisionRotate) {
|
||||||
|
TR_GetPlaneNormal(INVALID_HANDLE, Editor[client].angles);
|
||||||
|
GetVectorAngles(Editor[client].angles, Editor[client].angles);
|
||||||
|
Editor[client].angles[0] += 90.0; //need to rotate for some reason
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -22,8 +22,6 @@ static float EMPTY_ANG[3] = { 0.0, 0.0, 0.0 };
|
||||||
bool tempGod[MAXPLAYERS+1];
|
bool tempGod[MAXPLAYERS+1];
|
||||||
bool inSaferoom[MAXPLAYERS+1];
|
bool inSaferoom[MAXPLAYERS+1];
|
||||||
|
|
||||||
int g_iLaserIndex;
|
|
||||||
|
|
||||||
float cmdThrottle[MAXPLAYERS+1];
|
float cmdThrottle[MAXPLAYERS+1];
|
||||||
static bool onLadder[MAXPLAYERS+1];
|
static bool onLadder[MAXPLAYERS+1];
|
||||||
|
|
||||||
|
@ -36,18 +34,12 @@ ConVar cvar_sm_hats_rainbow_speed;
|
||||||
ConVar cvar_sm_hats_blacklist_enabled;
|
ConVar cvar_sm_hats_blacklist_enabled;
|
||||||
ConVar cvar_sm_hats_max_distance;
|
ConVar cvar_sm_hats_max_distance;
|
||||||
|
|
||||||
TopMenu g_topMenu;
|
|
||||||
|
|
||||||
char g_currentMap[64];
|
char g_currentMap[64];
|
||||||
|
|
||||||
//int g_markedMode
|
//int g_markedMode
|
||||||
|
|
||||||
#include <hats/editor.sp>
|
|
||||||
#include <hats/hats.sp>
|
#include <hats/hats.sp>
|
||||||
#include <hats/hat_presets.sp>
|
#include <hats/hat_presets.sp>
|
||||||
#include <hats/props/base.sp>
|
|
||||||
#include <hats/natives.sp>
|
|
||||||
#include <hats_editor>
|
|
||||||
|
|
||||||
public Plugin myinfo = {
|
public Plugin myinfo = {
|
||||||
name = "L4D2 Hats & Editor",
|
name = "L4D2 Hats & Editor",
|
||||||
|
@ -59,29 +51,6 @@ public Plugin myinfo = {
|
||||||
|
|
||||||
ArrayList NavAreas;
|
ArrayList NavAreas;
|
||||||
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) {
|
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) {
|
||||||
RegPluginLibrary("editor");
|
|
||||||
// CreateNative("SpawnSchematic", Native_SpawnSchematic);
|
|
||||||
CreateNative("StartEdit", Native_StartEdit);
|
|
||||||
CreateNative("StartSpawner", Native_StartSpawner);
|
|
||||||
CreateNative("CancelEdit", Native_CancelEdit);
|
|
||||||
CreateNative("IsEditorActive", Native_IsEditorActive);
|
|
||||||
|
|
||||||
|
|
||||||
CreateNative("StartSelector", Native_StartSelector);
|
|
||||||
CreateNative("CancelSelector", Native_CancelSelector);
|
|
||||||
CreateNative("IsSelectorActive", Native_IsSelectorActive);
|
|
||||||
|
|
||||||
CreateNative("EntitySelector.Start", Native_Selector_Start);
|
|
||||||
CreateNative("EntitySelector.Count.get", Native_Selector_GetCount);
|
|
||||||
CreateNative("EntitySelector.Active.get", Native_Selector_GetActive);
|
|
||||||
CreateNative("EntitySelector.SetOnEnd", Native_Selector_SetOnEnd);
|
|
||||||
CreateNative("EntitySelector.SetOnPreSelect", Native_Selector_SetOnPreSelect);
|
|
||||||
CreateNative("EntitySelector.SetOnPostSelect", Native_Selector_SetOnPostSelect);
|
|
||||||
CreateNative("EntitySelector.SetOnUnselect", Native_Selector_SetOnUnselect);
|
|
||||||
CreateNative("EntitySelector.AddEntity", Native_Selector_AddEntity);
|
|
||||||
CreateNative("EntitySelector.RemoveEntity", Native_Selector_RemoveEntity);
|
|
||||||
CreateNative("EntitySelector.Cancel", Native_Selector_Cancel);
|
|
||||||
CreateNative("EntitySelector.End", Native_Selector_End);
|
|
||||||
return APLRes_Success;
|
return APLRes_Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,10 +61,6 @@ public void OnPluginStart() {
|
||||||
SetFailState("This plugin is for L4D/L4D2 only.");
|
SetFailState("This plugin is for L4D/L4D2 only.");
|
||||||
}
|
}
|
||||||
|
|
||||||
createdWalls = new ArrayList();
|
|
||||||
g_spawnedItems = new ArrayList(2);
|
|
||||||
ROOT_CATEGORY.name = "Categories";
|
|
||||||
|
|
||||||
LoadTranslations("common.phrases");
|
LoadTranslations("common.phrases");
|
||||||
HookEvent("player_entered_checkpoint", OnEnterSaferoom);
|
HookEvent("player_entered_checkpoint", OnEnterSaferoom);
|
||||||
HookEvent("player_left_checkpoint", OnLeaveSaferoom);
|
HookEvent("player_left_checkpoint", OnLeaveSaferoom);
|
||||||
|
@ -105,10 +70,6 @@ public void OnPluginStart() {
|
||||||
|
|
||||||
RegConsoleCmd("sm_hat", Command_DoAHat, "Hats");
|
RegConsoleCmd("sm_hat", Command_DoAHat, "Hats");
|
||||||
RegAdminCmd("sm_hatf", Command_DoAHat, ADMFLAG_ROOT, "Hats");
|
RegAdminCmd("sm_hatf", Command_DoAHat, ADMFLAG_ROOT, "Hats");
|
||||||
RegAdminCmd("sm_mkwall", Command_MakeWall, ADMFLAG_CUSTOM2);
|
|
||||||
RegAdminCmd("sm_edit", Command_Editor, ADMFLAG_CUSTOM2);
|
|
||||||
RegAdminCmd("sm_wall", Command_Editor, ADMFLAG_CUSTOM2);
|
|
||||||
RegAdminCmd("sm_prop", Command_Props, ADMFLAG_CUSTOM2);
|
|
||||||
RegConsoleCmd("sm_hatp", Command_DoAHatPreset);
|
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);
|
cvar_sm_hats_blacklist_enabled = CreateConVar("sm_hats_blacklist_enabled", "1", "Is the prop blacklist enabled", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||||
|
@ -118,48 +79,16 @@ public void OnPluginStart() {
|
||||||
cvar_sm_hats_rainbow_speed = CreateConVar("sm_hats_rainbow_speed", "1", "Speed of rainbow", FCVAR_NONE, true, 0.0);
|
cvar_sm_hats_rainbow_speed = CreateConVar("sm_hats_rainbow_speed", "1", "Speed of rainbow", FCVAR_NONE, true, 0.0);
|
||||||
cvar_sm_hats_max_distance = CreateConVar("sm_hats_distance", "240", "The max distance away you can hat something. 0 = disable", FCVAR_NONE, true, 0.0);
|
cvar_sm_hats_max_distance = CreateConVar("sm_hats_distance", "240", "The max distance away you can hat something. 0 = disable", FCVAR_NONE, true, 0.0);
|
||||||
|
|
||||||
if(SQL_CheckConfig(DATABASE_CONFIG_NAME)) {
|
|
||||||
if(!ConnectDB()) {
|
|
||||||
LogError("Failed to connect to database.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
noHatVictimCookie = new Cookie("hats_no_target", "Disables other players from making you their hat", CookieAccess_Public);
|
noHatVictimCookie = new Cookie("hats_no_target", "Disables other players from making you their hat", CookieAccess_Public);
|
||||||
noHatVictimCookie.SetPrefabMenu(CookieMenu_OnOff_Int, "Disable player hats for self", OnLocalPlayerHatCookieSelect);
|
noHatVictimCookie.SetPrefabMenu(CookieMenu_OnOff_Int, "Disable player hats for self", OnLocalPlayerHatCookieSelect);
|
||||||
|
|
||||||
hatPresetCookie = new Cookie("hats_preset", "Sets the preset hat you spawn with", CookieAccess_Public);
|
hatPresetCookie = new Cookie("hats_preset", "Sets the preset hat you spawn with", CookieAccess_Public);
|
||||||
|
|
||||||
int entity = -1;
|
|
||||||
char targetName[32];
|
|
||||||
while((entity = FindEntityByClassname(entity, "func_brush")) != INVALID_ENT_REFERENCE) {
|
|
||||||
GetEntPropString(entity, Prop_Data, "m_iName", targetName, sizeof(targetName));
|
|
||||||
if(StrContains(targetName, "l4d2_hats_") == 0) {
|
|
||||||
createdWalls.Push(EntIndexToEntRef(entity));
|
|
||||||
SDKHook(entity, SDKHook_Use, OnWallClicked);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i = 1; i <= MaxClients; i++) {
|
for(int i = 1; i <= MaxClients; i++) {
|
||||||
Editor[i].client = i;
|
|
||||||
Editor[i].Reset(true);
|
|
||||||
g_PropData[i].Init(i);
|
|
||||||
hatData[i].yeetGroundTimer = null;
|
hatData[i].yeetGroundTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadPresets();
|
LoadPresets();
|
||||||
|
|
||||||
TopMenu topMenu;
|
|
||||||
if (LibraryExists("adminmenu") && ((topMenu = GetAdminTopMenu()) != null)) {
|
|
||||||
OnAdminMenuReady(topMenu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnLibraryRemoved(const char[] name) {
|
|
||||||
if (StrEqual(name, "adminmenu", false)) {
|
|
||||||
g_topMenu = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -499,13 +428,11 @@ void ChooseRandomPosition(float pos[3], int ignoreClient = 0) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool g_inRotate[MAXPLAYERS+1];
|
|
||||||
public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3], float angles[3], int& weapon, int& subtype, int& cmdnum, int& tickcount, int& seed, int mouse[2]) {
|
public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3], float angles[3], int& weapon, int& subtype, int& cmdnum, int& tickcount, int& seed, int mouse[2]) {
|
||||||
float tick = GetGameTime();
|
float tick = GetGameTime();
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
// OnPlayerRunCmd :: HATS
|
// OnPlayerRunCmd :: HATS
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
int oldButtons = GetEntProp(client, Prop_Data, "m_nOldButtons");
|
|
||||||
if(IsHatsEnabled(client)) {
|
if(IsHatsEnabled(client)) {
|
||||||
int entity = GetHat(client);
|
int entity = GetHat(client);
|
||||||
int visibleEntity = EntRefToEntIndex(hatData[client].visibleEntity);
|
int visibleEntity = EntRefToEntIndex(hatData[client].visibleEntity);
|
||||||
|
@ -600,187 +527,9 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
|
||||||
return Plugin_Handled;
|
return Plugin_Handled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////
|
|
||||||
// OnPlayerRunCmd :: ENTITY EDITOR
|
|
||||||
/////////////////////////////
|
|
||||||
if(g_pendingSaveClient == client) {
|
|
||||||
if(g_PropData[client].pendingSaveType == Save_Schematic) {
|
|
||||||
// move cursor? or should be editor anyway
|
|
||||||
}
|
|
||||||
} else if(g_PropData[client].Selector.IsActive()) {
|
|
||||||
SetWeaponDelay(client, 0.5);
|
|
||||||
if(tick - cmdThrottle[client] >= 0.20) {
|
|
||||||
if(buttons & IN_ATTACK) {
|
|
||||||
int entity = GetLookingEntity(client, Filter_ValidHats);
|
|
||||||
if(entity > 0) {
|
|
||||||
if(g_PropData[client].Selector.AddEntity(entity) != -1) {
|
|
||||||
PrecacheSound("ui/beep07.wav");
|
|
||||||
EmitSoundToClient(client, "ui/beep07.wav", entity, SND_CHANGEVOL, .volume = 0.5);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PrintHintText(client, "No entity found");
|
|
||||||
}
|
|
||||||
} else if(buttons & IN_ATTACK2) {
|
|
||||||
int entity = GetLookingEntity(client, Filter_ValidHats);
|
|
||||||
if(entity > 0) {
|
|
||||||
if(g_PropData[client].Selector.RemoveEntity(entity)) {
|
|
||||||
PrecacheSound("ui/beep22.wav");
|
|
||||||
EmitSoundToClient(client, "ui/beep22.wav", entity, SND_CHANGEVOL, .volume = 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if(buttons & IN_USE) {
|
|
||||||
if(buttons & IN_SPEED) {
|
|
||||||
//Delete
|
|
||||||
g_PropData[client].Selector.End();
|
|
||||||
} else if(buttons & IN_DUCK) {
|
|
||||||
//Cancel
|
|
||||||
g_PropData[client].Selector.Cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cmdThrottle[client] = tick;
|
|
||||||
}
|
|
||||||
} else if(Editor[client].IsActive()) {
|
|
||||||
// if(buttons & IN_USE && buttons & IN_RELOAD) {
|
|
||||||
// ClientCommand(client, "sm_wall done");
|
|
||||||
// return Plugin_Handled;
|
|
||||||
// }
|
|
||||||
bool allowMove = true;
|
|
||||||
switch(Editor[client].mode) {
|
|
||||||
case MOVE_ORIGIN: {
|
|
||||||
SetWeaponDelay(client, 0.5);
|
|
||||||
|
|
||||||
bool isRotate;
|
|
||||||
int flags = GetEntityFlags(client);
|
|
||||||
if(buttons & IN_RELOAD) {
|
|
||||||
if(!g_inRotate[client]) {
|
|
||||||
g_inRotate[client] = true;
|
|
||||||
}
|
|
||||||
if(!(oldButtons & IN_JUMP) && (buttons & IN_JUMP)) {
|
|
||||||
buttons &= ~IN_JUMP;
|
|
||||||
Editor[client].CycleStacker();
|
|
||||||
} else if(!(oldButtons & IN_SPEED) && (buttons & IN_SPEED)) {
|
|
||||||
Editor[client].ToggleCollision();
|
|
||||||
return Plugin_Handled;
|
|
||||||
} else if(!(oldButtons & IN_DUCK) && (buttons & IN_DUCK)) {
|
|
||||||
Editor[client].ToggleCollisionRotate();
|
|
||||||
return Plugin_Handled;
|
|
||||||
} else {
|
|
||||||
PrintCenterText(client, "%.1f %.1f %.1f", Editor[client].angles[0], Editor[client].angles[1], Editor[client].angles[2]);
|
|
||||||
isRotate = true;
|
|
||||||
SetEntityFlags(client, flags |= FL_FROZEN);
|
|
||||||
if(!(oldButtons & IN_ATTACK) && (buttons & IN_ATTACK)) Editor[client].CycleAxis();
|
|
||||||
else if(!(oldButtons & IN_ATTACK2) && (buttons & IN_ATTACK2)) Editor[client].CycleSnapAngle(tick);
|
|
||||||
|
|
||||||
// Rotation control:
|
|
||||||
// Turn off rotate when player wants rotate
|
|
||||||
Editor[client].hasCollisionRotate = false;
|
|
||||||
if(tick - cmdThrottle[client] > 0.1) {
|
|
||||||
if(Editor[client].axis == 0) {
|
|
||||||
int mouseXAbs = IntAbs(mouse[0]);
|
|
||||||
int mouseYAbs = IntAbs(mouse[1]);
|
|
||||||
bool XOverY = mouseXAbs > mouseYAbs;
|
|
||||||
if(mouseYAbs > 10 && !XOverY) {
|
|
||||||
Editor[client].IncrementAxis(0, mouse[1]);
|
|
||||||
} else if(mouseXAbs > 10 && XOverY) {
|
|
||||||
Editor[client].IncrementAxis(1, mouse[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(Editor[client].axis == 1) {
|
|
||||||
if(mouse[0] > 10) Editor[client].angles[2] += Editor[client].snapAngle;
|
|
||||||
else if(mouse[0] < -10) Editor[client].angles[2] -= Editor[client].snapAngle;
|
|
||||||
}
|
|
||||||
cmdThrottle[client] = tick;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(g_inRotate[client]) {
|
|
||||||
g_inRotate[client] = false;
|
|
||||||
}
|
|
||||||
// Move position
|
|
||||||
float moveAmount = (buttons & IN_SPEED) ? 2.0 : 1.0;
|
|
||||||
if(buttons & IN_ATTACK) Editor[client].moveDistance += moveAmount;
|
|
||||||
else if(buttons & IN_ATTACK2) Editor[client].moveDistance -= moveAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear IN_FROZEN when no longer rotate
|
|
||||||
if(!isRotate && flags & FL_FROZEN) {
|
|
||||||
flags = flags & ~FL_FROZEN;
|
|
||||||
SetEntityFlags(client, flags);
|
|
||||||
}
|
|
||||||
if(Editor[client].stackerDirection == Stack_Off)
|
|
||||||
CalculateEditorPosition(client, Filter_IgnorePlayerAndWall);
|
|
||||||
}
|
|
||||||
case SCALE: {
|
|
||||||
SetWeaponDelay(client, 0.5);
|
|
||||||
allowMove = false;
|
|
||||||
if(buttons & IN_USE) {
|
|
||||||
Editor[client].CycleSpeed(tick);
|
|
||||||
} else {
|
|
||||||
if(buttons & IN_MOVELEFT) {
|
|
||||||
Editor[client].IncrementSize(0, -1.0);
|
|
||||||
} else if(buttons & IN_MOVERIGHT) {
|
|
||||||
Editor[client].IncrementSize(0, 1.0);
|
|
||||||
Editor[client].size[0] += Editor[client].moveSpeed;
|
|
||||||
}
|
|
||||||
if(buttons & IN_FORWARD) {
|
|
||||||
Editor[client].IncrementSize(0, 1.0);
|
|
||||||
} else if(buttons & IN_BACK) {
|
|
||||||
Editor[client].IncrementSize(0, -1.0);
|
|
||||||
}
|
|
||||||
if(buttons & IN_JUMP) {
|
|
||||||
Editor[client].IncrementSize(0, 1.0);
|
|
||||||
} else if(buttons & IN_DUCK) {
|
|
||||||
Editor[client].IncrementSize(0, -1.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case COLOR: {
|
|
||||||
SetWeaponDelay(client, 0.5);
|
|
||||||
PrintHintText(client, "%d %d %d %d", Editor[client].color[0], Editor[client].color[1], Editor[client].color[2], Editor[client].color[3]);
|
|
||||||
if(buttons & IN_USE) {
|
|
||||||
Editor[client].CycleColorComponent(tick);
|
|
||||||
} else if(buttons & IN_ATTACK2) {
|
|
||||||
Editor[client].IncreaseColor(1);
|
|
||||||
allowMove = false;
|
|
||||||
} else if(buttons & IN_ATTACK) {
|
|
||||||
Editor[client].IncreaseColor(-1);
|
|
||||||
allowMove = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(buttons & IN_DUCK) {
|
|
||||||
|
|
||||||
}
|
|
||||||
if(Editor[client].mode != COLOR && !(oldButtons & IN_USE) && buttons & IN_USE) {
|
|
||||||
if(buttons & IN_SPEED) {
|
|
||||||
Editor[client].Cancel();
|
|
||||||
} else if(buttons & IN_DUCK) {
|
|
||||||
Editor[client].CycleBuildType();
|
|
||||||
// Editor[client].ShowExtraOptions();
|
|
||||||
} else {
|
|
||||||
int entity;
|
|
||||||
Editor[client].Done(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if(!(oldButtons & IN_ZOOM) && buttons & IN_ZOOM) {
|
|
||||||
Editor[client].CycleMode(); // ZOOM: Cycle forward
|
|
||||||
}
|
|
||||||
|
|
||||||
Editor[client].Draw(BUILDER_COLOR, 0.1, 0.1);
|
|
||||||
return allowMove ? Plugin_Continue : Plugin_Handled;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Plugin_Continue;
|
return Plugin_Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int IntAbs(int a) {
|
|
||||||
if(a < 0) {
|
|
||||||
return a * -1;
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't show real entity to hat wearer (Show for ALL but hat wearer)
|
// Don't show real entity to hat wearer (Show for ALL but hat wearer)
|
||||||
Action OnRealTransmit(int entity, int client) {
|
Action OnRealTransmit(int entity, int client) {
|
||||||
#if defined DEBUG_HAT_SHOW_FAKE
|
#if defined DEBUG_HAT_SHOW_FAKE
|
||||||
|
@ -826,31 +575,13 @@ void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) {
|
||||||
ReplyToCommand(client, "[Hats] Applied your hat preset! Clear it with /hatp");
|
ReplyToCommand(client, "[Hats] Applied your hat preset! Clear it with /hatp");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SDKHook(client, SDKHook_WeaponCanUse, OnWeaponUse);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Action OnWeaponUse(int client, int weapon) {
|
|
||||||
int ref = EntIndexToEntRef(weapon);
|
|
||||||
// Prevent picking up weapons that are previews
|
|
||||||
for(int i = 1; i <= MaxClients; i++) {
|
|
||||||
if(Editor[i].entity == ref && Editor[i].flags & Edit_Preview) {
|
|
||||||
return Plugin_Handled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Plugin_Continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnClientDisconnect(int client) {
|
public void OnClientDisconnect(int client) {
|
||||||
tempGod[client] = false;
|
tempGod[client] = false;
|
||||||
Editor[client].Reset();
|
|
||||||
g_PropData[client].Reset();
|
|
||||||
if(hatData[client].yeetGroundTimer != null)
|
if(hatData[client].yeetGroundTimer != null)
|
||||||
delete hatData[client].yeetGroundTimer;
|
delete hatData[client].yeetGroundTimer;
|
||||||
if(g_pendingSaveClient == client) {
|
|
||||||
g_pendingSaveClient = 0;
|
|
||||||
ClearSavePreview();
|
|
||||||
}
|
|
||||||
ClearHat(client, true);
|
ClearHat(client, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -867,8 +598,6 @@ public void OnEntityDestroyed(int entity) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void OnMapStart() {
|
public void OnMapStart() {
|
||||||
PrecacheModel(DUMMY_MODEL);
|
|
||||||
g_iLaserIndex = PrecacheModel("materials/sprites/laserbeam.vmt");
|
|
||||||
CreateTimer(30.0, Timer_RemountHats, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
|
CreateTimer(30.0, Timer_RemountHats, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
|
||||||
for(int i = 1; i <= MaxClients; i++) {
|
for(int i = 1; i <= MaxClients; i++) {
|
||||||
cmdThrottle[i] = 0.0;
|
cmdThrottle[i] = 0.0;
|
||||||
|
@ -881,18 +610,7 @@ public void OnMapStart() {
|
||||||
|
|
||||||
public void OnMapEnd() {
|
public void OnMapEnd() {
|
||||||
delete NavAreas;
|
delete NavAreas;
|
||||||
g_spawnedItems.Clear();
|
|
||||||
for(int i = 1; i <= createdWalls.Length; i++) {
|
|
||||||
if(hatData[i].yeetGroundTimer != null) {
|
|
||||||
delete hatData[i].yeetGroundTimer;
|
|
||||||
}
|
|
||||||
DeleteWall(i);
|
|
||||||
}
|
|
||||||
createdWalls.Clear();
|
|
||||||
ClearHats();
|
ClearHats();
|
||||||
UnloadCategories();
|
|
||||||
UnloadSave();
|
|
||||||
SaveRecents();
|
|
||||||
}
|
}
|
||||||
public void OnPluginEnd() {
|
public void OnPluginEnd() {
|
||||||
ClearHats();
|
ClearHats();
|
||||||
|
@ -902,10 +620,6 @@ public void OnPluginEnd() {
|
||||||
SetEntityFlags(i, flags);
|
SetEntityFlags(i, flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(g_spawnedItems != null) {
|
|
||||||
delete g_spawnedItems;
|
|
||||||
}
|
|
||||||
TriggerInput("prop_preview", "Kill");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TraceEntityFilterPlayer(int entity, int contentsMask, any data) {
|
public bool TraceEntityFilterPlayer(int entity, int contentsMask, any data) {
|
||||||
|
@ -938,7 +652,7 @@ stock bool Filter_NoPlayers(int entity, int mask, int data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
stock bool Filter_IgnorePlayerAndWall(int entity, int mask, int data) {
|
stock bool Filter_IgnorePlayerAndWall(int entity, int mask, int data) {
|
||||||
if(entity > MaxClients && entity != data && EntRefToEntIndex(Editor[data].entity) != entity && EntRefToEntIndex(hatData[data].entity) != entity) {
|
if(entity > MaxClients && entity != data && EntRefToEntIndex(hatData[data].entity) != entity) {
|
||||||
static char classname[16];
|
static char classname[16];
|
||||||
GetEntityClassname(entity, classname, sizeof(classname));
|
GetEntityClassname(entity, classname, sizeof(classname));
|
||||||
// Ignore infected
|
// Ignore infected
|
||||||
|
@ -1038,36 +752,3 @@ stock void LookAtPoint(int entity, const float destination[3]){
|
||||||
angles[1] -= 180;
|
angles[1] -= 180;
|
||||||
TeleportEntity(entity, NULL_VECTOR, angles, NULL_VECTOR);
|
TeleportEntity(entity, NULL_VECTOR, angles, NULL_VECTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
stock float SnapTo(const float value, const float degree) {
|
|
||||||
return float(RoundFloat(value / degree)) * degree;
|
|
||||||
}
|
|
||||||
|
|
||||||
stock bool CalculateEditorPosition(int client, TraceEntityFilter filter) {
|
|
||||||
if (client > 0 && client <= MaxClients && IsClientInGame(client)) {
|
|
||||||
float clientEye[3], clientAngle[3], direction[3];
|
|
||||||
GetClientEyePosition(client, clientEye);
|
|
||||||
GetClientEyeAngles(client, clientAngle);
|
|
||||||
|
|
||||||
GetAngleVectors(clientAngle, direction, NULL_VECTOR, NULL_VECTOR);
|
|
||||||
ScaleVector(direction, Editor[client].moveDistance);
|
|
||||||
AddVectors(clientEye, direction, Editor[client].origin);
|
|
||||||
|
|
||||||
if(Editor[client].hasCollision) {
|
|
||||||
TR_TraceRayFilter(clientEye, Editor[client].origin, MASK_OPAQUE, RayType_EndPoint, filter, client);
|
|
||||||
if (TR_DidHit(INVALID_HANDLE)) {
|
|
||||||
TR_GetEndPosition(Editor[client].origin);
|
|
||||||
GetEntPropVector(Editor[client].entity, Prop_Send, "m_vecMins", direction);
|
|
||||||
Editor[client].origin[2] -= direction[2];
|
|
||||||
if(Editor[client].hasCollisionRotate) {
|
|
||||||
TR_GetPlaneNormal(INVALID_HANDLE, Editor[client].angles);
|
|
||||||
GetVectorAngles(Editor[client].angles, Editor[client].angles);
|
|
||||||
Editor[client].angles[0] += 90.0; //need to rotate for some reason
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
|
@ -16,7 +16,7 @@
|
||||||
#include <jutils>
|
#include <jutils>
|
||||||
#include <entitylump>
|
#include <entitylump>
|
||||||
#undef REQUIRE_PLUGIN
|
#undef REQUIRE_PLUGIN
|
||||||
#include <hats_editor>
|
#include <editor>
|
||||||
|
|
||||||
int g_iLaserIndex;
|
int g_iLaserIndex;
|
||||||
#if defined DEBUG_BLOCKERS
|
#if defined DEBUG_BLOCKERS
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue