mirror of
https://github.com/Jackzmc/sourcemod-plugins.git
synced 2025-05-05 20:33:20 +00:00
Updates
This commit is contained in:
parent
fd2367f41f
commit
79d37bdd34
45 changed files with 5587 additions and 3877 deletions
|
@ -36,7 +36,7 @@ int g_BeamSprite;
|
|||
int g_HaloSprite;
|
||||
int g_iLaserIndex;
|
||||
|
||||
#define MAX_FORBIDDEN_CLASSNAMES 9
|
||||
#define MAX_FORBIDDEN_CLASSNAMES 10
|
||||
static char FORBIDDEN_CLASSNAMES[MAX_FORBIDDEN_CLASSNAMES][] = {
|
||||
// "env_physics_blocker",
|
||||
// "env_player_blocker",
|
||||
|
@ -49,7 +49,8 @@ static char FORBIDDEN_CLASSNAMES[MAX_FORBIDDEN_CLASSNAMES][] = {
|
|||
"func_tracktrain",
|
||||
// "infected",
|
||||
"func_lod",
|
||||
"prop_ragdoll"
|
||||
"prop_ragdoll",
|
||||
"move_rope"
|
||||
};
|
||||
|
||||
#define MAX_FORBIDDEN_MODELS 2
|
||||
|
@ -65,9 +66,11 @@ static char HIGHLIGHTED_CLASSNAMES[MAX_HIGHLIGHTED_CLASSNAMES][] = {
|
|||
"func_brush"
|
||||
}
|
||||
|
||||
ConVar g_cvarEnabled;
|
||||
|
||||
public void OnPluginStart()
|
||||
{
|
||||
g_cvarEnabled = CreateConVar("sm_grabent_allow", "1", "Is grabent allowed", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
RegAdminCmd("sm_grabent_freeze", Cmd_ReleaseFreeze, ADMFLAG_CHEATS, "<0/1> - Toggle entity freeze/unfreeze on release.");
|
||||
RegAdminCmd("sm_grab", Cmd_Grab, ADMFLAG_CHEATS, "Toggle Grab the entity in your crosshair.");
|
||||
RegAdminCmd("+grabent", Cmd_Grab, ADMFLAG_CHEATS, "Grab the entity in your crosshair.");
|
||||
|
@ -131,11 +134,13 @@ public Action Cmd_ReleaseFreeze(client, args)
|
|||
//============================================================================
|
||||
// GRAB ENTITY COMMAND //
|
||||
//============================================================================
|
||||
public Action Cmd_Grab(client, args) {
|
||||
if (client < 1 || client > MaxClients || !IsClientInGame(client))
|
||||
Action Cmd_Grab(int client, int args) {
|
||||
if(!g_cvarEnabled.BoolValue) {
|
||||
ReplyToCommand(client, "[SM] Grabent is disabled");
|
||||
return Plugin_Handled;
|
||||
|
||||
if (g_pGrabbedEnt[client] > 0 && IsValidEntity(g_pGrabbedEnt[client])) {
|
||||
} else if (client < 1 || client > MaxClients || !IsClientInGame(client)) {
|
||||
return Plugin_Handled;
|
||||
} else if (g_pGrabbedEnt[client] > 0 && IsValidEntity(g_pGrabbedEnt[client])) {
|
||||
Cmd_Release(client, 0);
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
@ -572,6 +577,7 @@ bool Filter_IgnoreForbidden(int entity, int mask, int data) {
|
|||
}
|
||||
|
||||
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++) {
|
||||
|
@ -591,5 +597,6 @@ bool CheckBlacklist(int entity) {
|
|||
if(StrEqual(buffer, "l4d2_randomizer")) {
|
||||
return false;
|
||||
}
|
||||
GetEntityClassname(entity, buffer, sizeof(buffer));
|
||||
return true;
|
||||
}
|
|
@ -461,6 +461,10 @@ Action Command_SetClientModel(int client, int args) {
|
|||
|
||||
void SetCharacter(int target, int survivorIndex, L4DModelId modelIndex, bool keepModel) {
|
||||
SetEntProp(target, Prop_Send, "m_survivorCharacter", survivorIndex);
|
||||
if(!PrecacheModel(MODELS[view_as<int>(modelIndex)])) {
|
||||
LogError("SetCharacter: INVALID MODEL: %s", MODELS[view_as<int>(modelIndex)]);
|
||||
return;
|
||||
}
|
||||
SetEntityModel(target, MODELS[view_as<int>(modelIndex)]);
|
||||
if (IsFakeClient(target)) {
|
||||
char name[32];
|
||||
|
@ -629,6 +633,9 @@ public void OnMapStart() {
|
|||
PrecacheSound(PRECACHE_SOUNDS[i]);
|
||||
}
|
||||
#endif
|
||||
for(int i = 0; i < 8; i++) {
|
||||
PrecacheModel(MODELS[i]);
|
||||
}
|
||||
|
||||
HookEntityOutput("info_changelevel", "OnStartTouch", EntityOutput_OnStartTouchSaferoom);
|
||||
HookEntityOutput("trigger_changelevel", "OnStartTouch", EntityOutput_OnStartTouchSaferoom);
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
#include <multicolors>
|
||||
#include <jutils>
|
||||
#include <socket>
|
||||
#include <SteamWorks>
|
||||
#undef REQUIRE_PLUGIN
|
||||
#tryinclude <SteamWorks>
|
||||
|
||||
#pragma newdecls required
|
||||
|
||||
|
@ -85,7 +86,7 @@ public void OnPluginStart() {
|
|||
g_socket.SetOption(SocketSendBuffer, BUFFER_SIZE);
|
||||
|
||||
uptime = GetTime();
|
||||
cvar_debug = CreateConVar("sm_adminpanel_debug", "1", "Turn on debug mode", FCVAR_DONTRECORD, true, 0.0, true, 1.0);
|
||||
cvar_debug = CreateConVar("sm_adminpanel_debug", "0", "Turn on debug mode", FCVAR_DONTRECORD, true, 0.0, true, 1.0);
|
||||
|
||||
cvar_authToken = CreateConVar("sm_adminpanel_authtoken", "", "The token for authentication", FCVAR_PROTECTED);
|
||||
cvar_authToken.AddChangeHook(OnCvarChanged);
|
||||
|
@ -172,10 +173,7 @@ void OnSocketError(Socket socket, int errorType, int errorNumber, int any) {
|
|||
}
|
||||
|
||||
void SendFullSync() {
|
||||
PrintToServer("SendFullSync");
|
||||
if(StartPayload(true)) {
|
||||
PrintToServer("SendFullSync : Started");
|
||||
|
||||
AddGameRecord();
|
||||
int stage = L4D2_GetCurrentFinaleStage();
|
||||
if(stage != 0)
|
||||
|
@ -254,15 +252,19 @@ void OnSocketReceive(Socket socket, const char[] receiveData, int dataSize, int
|
|||
}
|
||||
|
||||
void ProcessCommand(int id, const char[] command, const char[] cmdNamespace = "") {
|
||||
char output[128];
|
||||
char output[1024];
|
||||
if(!StartPayload(true)) return;
|
||||
if(cmdNamespace[0] == '\0' || StrEqual(cmdNamespace, "default")) {
|
||||
SplitString(command, " ", output, sizeof(output));
|
||||
// If command has no spaces, we need to manually copy the command to the split part
|
||||
if(SplitString(command, " ", output, sizeof(output)) == -1) {
|
||||
strcopy(output, sizeof(output), command);
|
||||
}
|
||||
if(CommandExists(output)) {
|
||||
ServerCommandEx(output, sizeof(output), "%s", command);
|
||||
AddCommandResponseRecord(id, Result_Boolean, 1, output);
|
||||
} else {
|
||||
AddCommandResponseRecord(id, Result_Error, -1, "Command does not exist");
|
||||
Format(output, sizeof(output), "Command \"%s\" does not exist", output);
|
||||
AddCommandResponseRecord(id, Result_Error, -1, output);
|
||||
}
|
||||
} else if(StrEqual(cmdNamespace, "builtin")) {
|
||||
CommandResultType type;
|
||||
|
|
|
@ -50,7 +50,9 @@ stock void EntFire(const char[] name, const char[] input) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void CreateCoinEntity(float pos[3]) {
|
||||
// int ent = CreateProp("prop_dynamic", "")
|
||||
}
|
||||
|
||||
void SetupEntities(bool blockers = true, bool props = true, bool portals = true) {
|
||||
#if defined DEBUG_BLOCKERS
|
||||
|
|
|
@ -125,6 +125,12 @@ methodmap GuessWhoGame < BaseGame {
|
|||
}
|
||||
}
|
||||
|
||||
property int TargetCoinCount {
|
||||
public get() {
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
public void Start() {
|
||||
|
||||
}
|
||||
|
@ -301,6 +307,14 @@ methodmap GuessWhoGame < BaseGame {
|
|||
SDKUnhook(client, SDKHook_OnTakeDamageAlive, OnTakeDamageAlive);
|
||||
SDKUnhook(client, SDKHook_WeaponEquip, OnWeaponEquip);
|
||||
}
|
||||
|
||||
public void PopulateCoins() {
|
||||
float pos[3];
|
||||
for(int i = 0; i < this.TargetCoinCount; i++) {
|
||||
movePoints.GetRandomPoint(pos);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stock bool ArePlayersJoining() {
|
||||
|
|
|
@ -13,13 +13,18 @@ Action Timer_RecordPoints(Handle h, int i) {
|
|||
vecLastLocation[i] = meta.pos;
|
||||
}
|
||||
}
|
||||
Game.MapTime++;
|
||||
PrintHintText(i, "Points: %d / %d", movePoints.Length, MAX_VALID_LOCATIONS);
|
||||
return Plugin_Continue;
|
||||
}
|
||||
|
||||
|
||||
bool firstCheckDone = false;
|
||||
Action Timer_WaitForPlayers(Handle h) {
|
||||
if(!isEnabled) return Plugin_Stop;
|
||||
if(!isEnabled) {
|
||||
waitTimer = null;
|
||||
return Plugin_Stop;
|
||||
}
|
||||
if(!ArePlayersJoining()) {
|
||||
Game.Debug("No players pending, ready to go");
|
||||
if(!firstCheckDone) {
|
||||
|
@ -28,6 +33,7 @@ Action Timer_WaitForPlayers(Handle h) {
|
|||
} else {
|
||||
firstCheckDone = false;
|
||||
InitGamemode();
|
||||
waitTimer = null;
|
||||
return Plugin_Stop;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
native bool SpawnSchematic(const char name[32], const float pos[3], const float angles[3] = NULL_VECTOR);
|
|
@ -1,7 +1,6 @@
|
|||
int BUILDER_COLOR[4] = { 0, 255, 0, 235 };
|
||||
int GLOW_BLUE[4] = { 3, 148, 252 };
|
||||
int GLOW_RED_ALPHA[4] = { 255, 0, 0, 235 };
|
||||
int GLOW_RED[3] = { 255, 0, 0};
|
||||
int GLOW_WHITE[4] = { 255, 255, 255, 255 };
|
||||
int GLOW_GREEN[4] = { 3, 252, 53 };
|
||||
float ORIGIN_SIZE[3] = { 2.0, 2.0, 2.0 };
|
||||
|
@ -27,26 +26,21 @@ char MODE_NAME[5][] = {
|
|||
"Freelook"
|
||||
}
|
||||
|
||||
enum editFlag {
|
||||
enum {
|
||||
Edit_None,
|
||||
Edit_Copy = 1,
|
||||
Edit_Preview = 2,
|
||||
Edit_WallCreator = 4
|
||||
Edit_WallCreator = 4,
|
||||
Edit_Manager = 8
|
||||
}
|
||||
|
||||
enum buildType {
|
||||
Build_Solid,
|
||||
Build_Physics,
|
||||
Build_NonSolid,
|
||||
// TODO: Build_Weapon (spawn as weapon?)
|
||||
}
|
||||
|
||||
enum CompleteType {
|
||||
Complete_WallSuccess,
|
||||
Complete_WallError,
|
||||
Complete_PropSpawned,
|
||||
Complete_PropError,
|
||||
Complete_EditSuccess
|
||||
}
|
||||
|
||||
enum StackerDirection {
|
||||
Stack_Off,
|
||||
|
@ -88,6 +82,7 @@ enum struct EditorData {
|
|||
int colorIndex;
|
||||
int axis;
|
||||
int snapAngle;
|
||||
float rotateSpeed;
|
||||
int moveSpeed;
|
||||
float moveDistance;
|
||||
int entity;
|
||||
|
@ -97,11 +92,14 @@ enum struct EditorData {
|
|||
|
||||
editMode mode;
|
||||
buildType buildType;
|
||||
editFlag flags;
|
||||
int flags;
|
||||
|
||||
PrivateForward callback;
|
||||
bool isEditCallback;
|
||||
|
||||
void Reset(bool initial = false) {
|
||||
// Clear preview entity
|
||||
if(this.entity != INVALID_ENT_REFERENCE && this.flags & Edit_Preview && IsValidEntity(this.entity)) {
|
||||
if(this.entity != INVALID_ENT_REFERENCE && (this.flags & Edit_Preview) && IsValidEntity(this.entity)) {
|
||||
RemoveEntity(this.entity);
|
||||
}
|
||||
this.stackerDirection = Stack_Off;
|
||||
|
@ -111,12 +109,13 @@ enum struct EditorData {
|
|||
this.size[0] = this.size[1] = this.size[2] = 5.0;
|
||||
this.angles[0] = this.angles[1] = this.angles[2] = 0.0;
|
||||
this.colorIndex = 0;
|
||||
this.axis = 1;
|
||||
this.axis = 0;
|
||||
this.moveDistance = 200.0;
|
||||
this.flags = Edit_None;
|
||||
this.classname[0] = '\0';
|
||||
this.CalculateMins();
|
||||
this.SetMode(INACTIVE);
|
||||
this.rotateSpeed = 0.1;
|
||||
// Settings that don't get reset on new spawns:
|
||||
if(initial) {
|
||||
this.color[0] = this.color[1] = this.color[2] = this.color[3] = 255;
|
||||
|
@ -138,6 +137,12 @@ enum struct EditorData {
|
|||
if(this.flags & Edit_WallCreator || this.entity == INVALID_ENT_REFERENCE) {
|
||||
Effect_DrawBeamBoxRotatableToAll(this.origin, this.mins, this.size, this.angles, g_iLaserIndex, 0, 0, 30, lifetime, 0.4, 0.4, 0, amplitude, color, 0);
|
||||
} else {
|
||||
if(this.snapAngle != 1) {
|
||||
this.angles[0] = RoundToNearestInterval(this.angles[0], this.snapAngle);
|
||||
this.angles[1] = RoundToNearestInterval(this.angles[1], this.snapAngle);
|
||||
this.angles[2] = RoundToNearestInterval(this.angles[2], this.snapAngle);
|
||||
|
||||
}
|
||||
TeleportEntity(this.entity, this.origin, this.angles, NULL_VECTOR);
|
||||
}
|
||||
Effect_DrawAxisOfRotationToAll(this.origin, this.angles, ORIGIN_SIZE, g_iLaserIndex, 0, 0, 30, 0.2, 0.1, 0.1, 0, 0.0, 0);
|
||||
|
@ -147,9 +152,6 @@ enum struct EditorData {
|
|||
void UpdateEntity() {
|
||||
int alpha = this.color[3];
|
||||
// Keep previews transparent
|
||||
if(this.flags & Edit_Preview) {
|
||||
alpha = 200;
|
||||
}
|
||||
SetEntityRenderColor(this.entity, this.color[0], this.color[1], this.color[2], alpha);
|
||||
}
|
||||
|
||||
|
@ -178,6 +180,10 @@ enum struct EditorData {
|
|||
void SetName(const char[] name) {
|
||||
strcopy(this.name, sizeof(this.name), name);
|
||||
}
|
||||
void SetCallback(PrivateForward callback, bool isEditCallback) {
|
||||
this.callback = callback;
|
||||
this.isEditCallback = isEditCallback;
|
||||
}
|
||||
|
||||
void CycleMode() {
|
||||
// Remove frozen state when cycling
|
||||
|
@ -210,43 +216,43 @@ enum struct EditorData {
|
|||
PrintToChat(this.client, "\x04[Editor]\x01 Mode: \x05%s\x01 (Press \x04RELOAD\x01 to change)", MODE_NAME[this.mode]);
|
||||
}
|
||||
|
||||
void CycleStacker(float tick) {
|
||||
if(tick - cmdThrottle[this.client] <= 0.10) return;
|
||||
void CycleStacker() {
|
||||
int newDirection = view_as<int>(this.stackerDirection) + 1;
|
||||
if(newDirection == view_as<int>(Stack_Down)) newDirection = 0;
|
||||
this.stackerDirection = view_as<StackerDirection>(newDirection);
|
||||
|
||||
PrintToChat(this.client, "\x04[Editor]\x01 Stacker: %s\x01", STACK_DIRECTION_NAME[this.stackerDirection]);
|
||||
cmdThrottle[this.client] = tick;
|
||||
}
|
||||
|
||||
void ToggleCollision(float tick) {
|
||||
if(tick - cmdThrottle[this.client] <= 0.25) return;
|
||||
void ToggleCollision() {
|
||||
this.hasCollision = !this.hasCollision
|
||||
PrintToChat(this.client, "\x04[Editor]\x01 Collision: %s", ON_OFF_STRING[view_as<int>(this.hasCollision)]);
|
||||
cmdThrottle[this.client] = tick;
|
||||
}
|
||||
|
||||
void ToggleCollisionRotate(float tick) {
|
||||
if(tick - cmdThrottle[this.client] <= 0.20) return;
|
||||
void ToggleCollisionRotate() {
|
||||
this.hasCollisionRotate = !this.hasCollisionRotate
|
||||
PrintToChat(this.client, "\x04[Editor]\x01 Rotate with Collision: %s", ON_OFF_STRING[view_as<int>(this.hasCollisionRotate)]);
|
||||
cmdThrottle[this.client] = tick;
|
||||
}
|
||||
|
||||
void CycleAxis(float tick) {
|
||||
if(tick - cmdThrottle[this.client] <= 0.1) return;
|
||||
void CycleAxis() {
|
||||
// if(tick - cmdThrottle[this.client] <= 0.1) return;
|
||||
if(this.axis == 0) {
|
||||
this.axis = 1;
|
||||
PrintToChat(this.client, "\x04[Editor]\x01 Rotate Axis: \x05HEADING (Y)\x01");
|
||||
} else if(this.axis == 1) {
|
||||
this.axis = 2;
|
||||
PrintToChat(this.client, "\x04[Editor]\x01 Rotate Axis: \x05PITCH (X)\x01");
|
||||
PrintToChat(this.client, "\x04[Editor]\x01 Rotate Axis: \x05ROLL (Z)\x01");
|
||||
} else {
|
||||
this.axis = 0;
|
||||
PrintToChat(this.client, "\x04[Editor]\x01 Rotate Axis: \x05ROLL (Z)\x01");
|
||||
PrintToChat(this.client, "\x04[Editor]\x01 Rotate Axis: \x05PITCH AND HEADING (X, Y)\x01");
|
||||
}
|
||||
// cmdThrottle[this.client] = tick;
|
||||
}
|
||||
|
||||
void IncrementAxis(int axis, int mouse) {
|
||||
if(this.snapAngle == 1) {
|
||||
this.angles[axis] += mouse * this.rotateSpeed;
|
||||
} else {
|
||||
if(mouse > 0) this.angles[axis] += this.snapAngle;
|
||||
else if(mouse < 0) this.angles[axis] -= this.snapAngle;
|
||||
}
|
||||
cmdThrottle[this.client] = tick;
|
||||
}
|
||||
|
||||
void CycleSnapAngle(float tick) {
|
||||
|
@ -328,18 +334,38 @@ enum struct EditorData {
|
|||
|
||||
// Complete the edit, wall creation, or spawning
|
||||
CompleteType Done(int& entity) {
|
||||
CompleteType type;
|
||||
if(this.flags & Edit_WallCreator) {
|
||||
return this._FinishWall(entity) ? Complete_WallSuccess : Complete_WallError;
|
||||
type = this._FinishWall(entity) ? Complete_WallSuccess : Complete_WallError;
|
||||
} else if(this.flags & Edit_Preview) {
|
||||
return this._FinishPreview(entity) ? Complete_PropSpawned : Complete_PropError;
|
||||
type = this._FinishPreview(entity) ? Complete_PropSpawned : Complete_PropError;
|
||||
} else {
|
||||
// Is edit, do nothing, just reset
|
||||
PrintHintText(this.client, "Edit Complete");
|
||||
this.Reset();
|
||||
entity = 0;
|
||||
|
||||
return Complete_EditSuccess;
|
||||
type = Complete_EditSuccess;
|
||||
}
|
||||
if(this.callback) {
|
||||
Call_StartForward(this.callback);
|
||||
Call_PushCell(this.client);
|
||||
Call_PushCell(entity);
|
||||
Call_PushCell(type);
|
||||
bool result;
|
||||
Call_Finish(result);
|
||||
// Cancel menu:
|
||||
if(this.isEditCallback) delete this.callback;
|
||||
if(this.isEditCallback || !result) {
|
||||
// No native way to close a menu, so open a dummy menu and close it:
|
||||
// Handler doesn't matter, no options are added
|
||||
Menu menu = new Menu(Spawn_RootHandler);
|
||||
menu.Display(this.client, 1);
|
||||
} else {
|
||||
delete this.callback;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
bool _FinishWall(int& id) {
|
||||
|
@ -569,7 +595,7 @@ enum struct EditorData {
|
|||
DispatchKeyValue(entity, "targetname", "prop_preview");
|
||||
DispatchKeyValue(entity, "solid", "0");
|
||||
DispatchKeyValue(entity, "rendercolor", "255 128 255");
|
||||
DispatchKeyValue(entity, "renderamt", "200");
|
||||
DispatchKeyValue(entity, "renderamt", "255");
|
||||
DispatchKeyValue(entity, "rendermode", "1");
|
||||
TeleportEntity(entity, this.origin, NULL_VECTOR, NULL_VECTOR);
|
||||
if(!DispatchSpawn(entity)) {
|
||||
|
@ -583,8 +609,11 @@ enum struct EditorData {
|
|||
return IsValidEntity(entity);
|
||||
}
|
||||
|
||||
// Adds an existing entity to the editor, to move it.
|
||||
// asWallCopy: to instead copy the wall's size and position (walls only)
|
||||
/**
|
||||
* Adds an existing entity to the editor, to move it.
|
||||
* asWallCopy: to instead copy the wall's size and position (walls only)
|
||||
* @deprecated
|
||||
*/
|
||||
void Import(int entity, bool asWallCopy = false, editMode mode = SCALE) {
|
||||
this.Reset();
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", this.origin);
|
||||
|
@ -599,6 +628,22 @@ enum struct EditorData {
|
|||
this.SetMode(mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports an entity
|
||||
*/
|
||||
void ImportEntity(int entity, int flags = 0, editMode mode = SCALE) {
|
||||
this.Reset();
|
||||
this.flags = flags;
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", this.origin);
|
||||
GetEntPropVector(entity, Prop_Send, "m_angRotation", this.angles);
|
||||
this.prevOrigin = this.origin;
|
||||
this.prevAngles = this.angles;
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecMins", this.mins);
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecMaxs", this.size);
|
||||
this.entity = entity;
|
||||
this.SetMode(mode);
|
||||
}
|
||||
|
||||
// Cancels the current placement. If the edit is a copy/preview, the entity is also deleted
|
||||
// If entity is not a wall, it will be returned
|
||||
void Cancel() {
|
||||
|
@ -611,9 +656,22 @@ enum struct EditorData {
|
|||
}
|
||||
this.SetMode(INACTIVE);
|
||||
PrintHintText(this.client, "Cancelled");
|
||||
if(this.callback) {
|
||||
delete this.callback;
|
||||
}
|
||||
// CPrintToChat(this.client, "\x04[Editor]\x01 Cancelled.");
|
||||
}
|
||||
}
|
||||
|
||||
void SendEditorMessage(int client, const char[] format, any ...) {
|
||||
char message[256];
|
||||
VFormat(message, sizeof(message), format, 3);
|
||||
CPrintToChat(client, "\x04`[Editor]\x01 %s", message);
|
||||
}
|
||||
|
||||
stock float RoundToNearestInterval(float value, int interval) {
|
||||
return float(RoundFloat(value / float(interval)) * interval);
|
||||
}
|
||||
EditorData Editor[MAXPLAYERS+1];
|
||||
|
||||
Action OnWallClicked(int entity, int activator, int caller, UseType type, float value) {
|
||||
|
|
|
@ -43,7 +43,7 @@ int lastHatRequestTime[MAXPLAYERS+1];
|
|||
HatInstance hatData[MAXPLAYERS+1];
|
||||
StringMap g_HatPresets;
|
||||
|
||||
#define MAX_FORBIDDEN_CLASSNAMES 14
|
||||
#define MAX_FORBIDDEN_CLASSNAMES 15
|
||||
char FORBIDDEN_CLASSNAMES[MAX_FORBIDDEN_CLASSNAMES][] = {
|
||||
"prop_door_rotating_checkpoint",
|
||||
"env_physics_blocker",
|
||||
|
@ -59,7 +59,8 @@ char FORBIDDEN_CLASSNAMES[MAX_FORBIDDEN_CLASSNAMES][] = {
|
|||
// "infected",
|
||||
"func_lod",
|
||||
"func_door",
|
||||
"prop_ragdoll"
|
||||
"prop_ragdoll",
|
||||
"move_rope"
|
||||
};
|
||||
|
||||
#define MAX_FORBIDDEN_MODELS 2
|
||||
|
@ -73,7 +74,7 @@ char FORBIDDEN_MODELS[MAX_FORBIDDEN_MODELS][] = {
|
|||
// Classnames that should automatically trigger reverse infected
|
||||
static char REVERSE_CLASSNAMES[MAX_REVERSE_CLASSNAMES][] = {
|
||||
"infected",
|
||||
"func_movelinear"
|
||||
"func_movelinear",
|
||||
};
|
||||
|
||||
Action Command_DoAHat(int client, int args) {
|
||||
|
|
122
scripting/include/hats/natives.sp
Normal file
122
scripting/include/hats/natives.sp
Normal file
|
@ -0,0 +1,122 @@
|
|||
|
||||
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;
|
||||
}
|
|
@ -25,6 +25,8 @@ enum SaveType {
|
|||
Save_Schematic
|
||||
}
|
||||
|
||||
int GLOW_MANAGER[3] = { 52, 174, 235 };
|
||||
|
||||
enum struct Schematic {
|
||||
char name[64];
|
||||
char creatorSteamid[32];
|
||||
|
@ -134,6 +136,197 @@ public any Native_SpawnSchematic(Handle plugin, int numParams) {
|
|||
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) {
|
||||
PrintToServer("Selector.AddEntity: PRE CALLBACK");
|
||||
// FIXME: crashes server, sourcemod bug
|
||||
/*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");
|
||||
//FIXME: crashes server, sourcemod bug
|
||||
/*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;
|
||||
|
@ -144,14 +337,19 @@ enum struct PlayerPropData {
|
|||
int lastActiveTime;
|
||||
char classnameOverride[64];
|
||||
ChatPrompt chatPrompt;
|
||||
ArrayList markedProps;
|
||||
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.markedProps != null) delete this.markedProps;
|
||||
if(this.Selector.IsActive()) this.Selector.Cancel();
|
||||
this.chatPrompt = Prompt_None;
|
||||
this.clearListBuffer = false;
|
||||
this.lastCategoryIndex = 0;
|
||||
|
@ -161,6 +359,19 @@ enum struct PlayerPropData {
|
|||
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) {
|
||||
|
|
|
@ -8,6 +8,8 @@ public void OnAdminMenuReady(Handle topMenuHandle) {
|
|||
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;
|
||||
}
|
||||
|
@ -24,6 +26,15 @@ void Category_Handler(TopMenu topmenu, TopMenuAction action, TopMenuObject topob
|
|||
}
|
||||
}
|
||||
|
||||
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");
|
||||
|
@ -79,6 +90,14 @@ void AdminMenu_SaveLoad(TopMenu topmenu, TopMenuAction action, TopMenuObject obj
|
|||
}
|
||||
}
|
||||
|
||||
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];
|
||||
|
@ -87,7 +106,7 @@ int SaveLoadMainMenuHandler(Menu menu, MenuAction action, int client, int param2
|
|||
ShowSaves(client, type);
|
||||
} else if (action == MenuAction_Cancel) {
|
||||
if(param2 == MenuCancel_ExitBack) {
|
||||
Spawn_ShowSaveLoadMainMenu(client);
|
||||
DisplayTopMenuCategory(g_topMenu, g_propSpawnerCategory, client);
|
||||
}
|
||||
} else if (action == MenuAction_End)
|
||||
delete menu;
|
||||
|
@ -127,6 +146,7 @@ int SaveLoadSceneHandler(Menu menu, MenuAction action, int client, int param2) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int SaveLoadSchematicHandler(Menu menu, MenuAction action, int client, int param2) {
|
||||
if (action == MenuAction_Select) {
|
||||
char saveName[64];
|
||||
|
@ -189,40 +209,160 @@ int SaveLoadConfirmHandler(Menu menu, MenuAction action, int client, int param2)
|
|||
delete menu;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DeleteHandler(Menu menu, MenuAction action, int client, int param2) {
|
||||
int ManagerHandler(Menu menu, MenuAction action, int client, int param2) {
|
||||
if (action == MenuAction_Select) {
|
||||
char info[8];
|
||||
menu.GetItem(param2, info, sizeof(info));
|
||||
int index = StringToInt(info);
|
||||
if(index == -1) {
|
||||
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(index == -2) {
|
||||
} 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(index == -3) {
|
||||
if(g_PropData[client].markedProps != null) {
|
||||
EndDeleteTool(client, false);
|
||||
} 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].markedProps = new ArrayList();
|
||||
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 ref = g_spawnedItems.Get(index);
|
||||
// TODO: add delete confirm
|
||||
int index = g_spawnedItems.FindValue(ref);
|
||||
if(IsValidEntity(ref)) {
|
||||
RemoveEntity(ref);
|
||||
}
|
||||
g_spawnedItems.Erase(index);
|
||||
if(index > 0) {
|
||||
if(index > -1) {
|
||||
g_spawnedItems.Erase(index);
|
||||
index--;
|
||||
}
|
||||
} else { index = 0; }
|
||||
ShowDeleteList(client, index);
|
||||
}
|
||||
} else if (action == MenuAction_Cancel) {
|
||||
|
@ -317,16 +457,18 @@ int EditHandler(Menu menu, MenuAction action, int client, int param2) {
|
|||
if (action == MenuAction_Select) {
|
||||
char info[8];
|
||||
menu.GetItem(param2, info, sizeof(info));
|
||||
int index = StringToInt(info);
|
||||
int ref = g_spawnedItems.Get(index);
|
||||
int 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.");
|
||||
g_spawnedItems.Erase(index);
|
||||
index--;
|
||||
if(index > -1) {
|
||||
g_spawnedItems.Erase(index);
|
||||
index--;
|
||||
} else { index = 0; }
|
||||
}
|
||||
ShowEditList(client, index);
|
||||
} else if (action == MenuAction_Cancel) {
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
/////////////
|
||||
void ShowSpawnRoot(int client) {
|
||||
Menu menu = new Menu(Spawn_RootHandler);
|
||||
menu.SetTitle("Choose list:");
|
||||
menu.AddItem("f", "Favorites (WIP)");
|
||||
menu.AddItem("r", "Recents");
|
||||
menu.AddItem("s", "Search");
|
||||
menu.AddItem("n", "Prop List");
|
||||
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);
|
||||
|
@ -17,6 +17,7 @@ void Spawn_ShowRecents(int client) {
|
|||
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");
|
||||
|
@ -26,6 +27,11 @@ void Spawn_ShowSearch(int client) {
|
|||
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");
|
||||
|
||||
|
@ -38,7 +44,7 @@ void ShowDeleteList(int client, int index = -3) {
|
|||
for(int i = 0; i < g_spawnedItems.Length; i++) {
|
||||
int ref = GetSpawnedItem(i);
|
||||
if(ref == -1) continue;
|
||||
IntToString(i, info, sizeof(info));
|
||||
Format(info, sizeof(info), "0|%d", ref);
|
||||
GetEntPropString(ref, Prop_Data, "m_ModelName", buffer, sizeof(buffer));
|
||||
index = FindCharInString(buffer, '/', true);
|
||||
if(index != -1)
|
||||
|
@ -52,6 +58,11 @@ void ShowDeleteList(int client, int index = -3) {
|
|||
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");
|
||||
|
||||
|
@ -60,7 +71,7 @@ void ShowEditList(int client, int index = 0) {
|
|||
for(int i = 0; i < g_spawnedItems.Length; i++) {
|
||||
int ref = GetSpawnedItem(i);
|
||||
if(ref == -1) continue;
|
||||
IntToString(i, info, sizeof(info));
|
||||
Format(info, sizeof(info), "%d", ref);
|
||||
GetEntPropString(ref, Prop_Data, "m_ModelName", buffer, sizeof(buffer));
|
||||
index = FindCharInString(buffer, '/', true);
|
||||
if(index != -1)
|
||||
|
@ -104,6 +115,8 @@ void _showItemMenu(int client, ArrayList items, const char[] title = "", bool cl
|
|||
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
|
||||
|
@ -194,6 +207,81 @@ void Spawn_ShowSaveLoadMainMenu(int client) {
|
|||
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;
|
||||
|
|
|
@ -378,9 +378,7 @@ ArrayList SearchItems(const char[] query) {
|
|||
SearchData data;
|
||||
for(int i = 0; i < results.Length; i++) {
|
||||
results.GetArray(i, data);
|
||||
PrintToConsoleAll("%d | data=\"%s\"", i, data.model);
|
||||
item.FromSearchData(data);
|
||||
PrintToConsoleAll("%d | item=\"%s\"", i, item.model);
|
||||
items.PushArray(item);
|
||||
}
|
||||
delete results;
|
||||
|
@ -446,26 +444,28 @@ bool RemoveSpawnedProp(int ref) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void EndDeleteTool(int client, bool deleteEntities = false) {
|
||||
if(g_PropData[client].markedProps != null) {
|
||||
int count;
|
||||
for(int i = 0; i < g_PropData[client].markedProps.Length; i++) {
|
||||
int ref = g_PropData[client].markedProps.Get(i);
|
||||
if(IsValidEntity(ref)) {
|
||||
count++;
|
||||
if(deleteEntities) {
|
||||
RemoveSpawnedProp(ref);
|
||||
RemoveEntity(ref);
|
||||
}
|
||||
else L4D2_RemoveEntityGlow(EntRefToEntIndex(ref));
|
||||
}
|
||||
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 g_PropData[client].markedProps;
|
||||
if(deleteEntities)
|
||||
PrintToChat(client, "\x04[Editor]\x01 \x05%d\x01 entities deleted", count);
|
||||
else
|
||||
PrintToChat(client, "\x04[Editor]\x01 Delete tool cancelled");
|
||||
}
|
||||
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) {
|
||||
|
|
155
scripting/include/hats_editor.inc
Normal file
155
scripting/include/hats_editor.inc
Normal file
|
@ -0,0 +1,155 @@
|
|||
#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
|
||||
}
|
|
@ -2,21 +2,50 @@
|
|||
#endinput
|
||||
#endif
|
||||
#define _overlay_included
|
||||
#include <ripext>
|
||||
|
||||
native bool SendTempUI(int client, const char[] id, int lifetime, JSONObject element);
|
||||
public SharedPlugin __pl_overlay = {
|
||||
name = "overlay",
|
||||
file = "overlay.smx",
|
||||
#if defined REQUIRE_PLUGIN
|
||||
required = 1,
|
||||
#else
|
||||
required = 0,
|
||||
#endif
|
||||
};
|
||||
|
||||
native bool ShowUI(int client, const char[] elemNamespace, const char[] elemId, JSONObject variables);
|
||||
#define ACTION_ARG_LENGTH 128 // The length each arg (separated by space) can be
|
||||
|
||||
native bool HideUI(int client, const char[] elemNamespace, const char[] elemId);
|
||||
// typedef ActionFallbackHandlerCallback = function void (const char[] actionName, const char[][] args, int numArgs);
|
||||
// typedef ActionHandlerCallback = function void (const char[][] args, int numArgs);
|
||||
typedef ActionFallbackHandlerCallback = function void (const char[] actionName, UIActionEvent event, int client);
|
||||
typedef ActionHandlerCallback = function void (UIActionEvent event, int client);
|
||||
|
||||
native bool PlayAudio(int client, const char[] url);
|
||||
|
||||
native bool IsOverlayConnected();
|
||||
|
||||
forward void OnUIAction(const char[] elemNamespace, const char[] elemId, const char[] action);
|
||||
// myplugin:action_name
|
||||
// Handles any action for actionNamespace and actionName
|
||||
native void RegisterActionHandler(const char[] actionNamespace, const char[] actionName, ActionFallbackHandlerCallback cb);
|
||||
// Handles all actions for namespace that were not caught by RegisterActionHandler
|
||||
native void RegisterActionAnyHandler(const char[] actionNamespace, ActionHandlerCallback cb);
|
||||
|
||||
typedef UIActionCallback = function void (const char[][] args, int numArgs);
|
||||
methodmap UIActionEvent {
|
||||
public UIActionEvent(ArrayList list) {
|
||||
return view_as<UIActionEvent>(list);
|
||||
}
|
||||
|
||||
public void GetArg(int argNum, char[] output, int maxlen) {
|
||||
view_as<ArrayList>(this).GetString(argNum, output, maxlen);
|
||||
}
|
||||
|
||||
public void _Delete() {
|
||||
delete view_as<ArrayList>(this);
|
||||
}
|
||||
|
||||
property int Args {
|
||||
public get() { return view_as<ArrayList>(this).Length; }
|
||||
}
|
||||
}
|
||||
|
||||
methodmap UIElement < JSONObject {
|
||||
public UIElement(const char[] elemNamespace, const char[] elemId) {
|
||||
|
@ -24,6 +53,7 @@ methodmap UIElement < JSONObject {
|
|||
obj.SetString("namespace", elemNamespace);
|
||||
obj.SetString("elem_id", elemId);
|
||||
obj.SetBool("visibility", false);
|
||||
obj.Set("steamids", new JSONArray());
|
||||
obj.Set("variables", new JSONObject());
|
||||
|
||||
return view_as<UIElement>(obj);
|
||||
|
@ -35,16 +65,6 @@ methodmap UIElement < JSONObject {
|
|||
}
|
||||
public set(bool value) {
|
||||
view_as<JSONObject>(this).SetBool("visibility", value);
|
||||
this.Send();
|
||||
}
|
||||
}
|
||||
|
||||
/** Is the UI element globally sent to all connected players?
|
||||
* Specify players with .AddClient() or clear with .ClearClients()
|
||||
*/
|
||||
property bool Global {
|
||||
public get() {
|
||||
return !view_as<JSONObject>(this).HasKey("steamids")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,35 +88,13 @@ methodmap UIElement < JSONObject {
|
|||
view_as<JSONObject>(this).SetBool(id, value);
|
||||
}
|
||||
|
||||
public void SetActionCallback(UIActionCallback callback) {}
|
||||
|
||||
public void AddClient(const char[] steamid) {
|
||||
// if(!IsClientInGame(client) || steamidCache[client][0] == '\0') ThrowError("Client %d is not connected, ingame, or authorized");
|
||||
JSONObject obj = view_as<JSONObject>(this);
|
||||
JSONArray steamids = view_as<JSONArray>(obj.Get("steamids"));
|
||||
if(steamids == null) {
|
||||
steamids = new JSONArray();
|
||||
obj.Set("steamids", steamids)
|
||||
public native bool SendAll();
|
||||
public native bool SendTo(int client);
|
||||
public bool SendToMultiple(int[] clientIds, int numClients) {
|
||||
for(int i = 0; i < numClients; i++) {
|
||||
this.SendTo(clientIds[i]);
|
||||
}
|
||||
steamids.PushString(steamid);
|
||||
}
|
||||
|
||||
public void ClearClients() {
|
||||
view_as<JSONObject>(this).Remove("steamids");
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
view_as<JSONObject>(this).Clear();
|
||||
}
|
||||
|
||||
public void Hide() {
|
||||
this.Visibility = false;
|
||||
}
|
||||
public void Show() {
|
||||
this.Visibility = true;
|
||||
}
|
||||
|
||||
public native bool Send();
|
||||
}
|
||||
|
||||
methodmap UIPosition < JSONObject {
|
||||
|
@ -118,6 +116,25 @@ methodmap UIPosition < JSONObject {
|
|||
}
|
||||
}
|
||||
|
||||
methodmap UISize < JSONObject {
|
||||
public UISize(int width, int height) {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.SetInt("width", width);
|
||||
obj.SetInt("height", height);
|
||||
return view_as<UISize>(obj);
|
||||
}
|
||||
|
||||
property int Width {
|
||||
public get() { return view_as<JSONObject>(this).GetInt("width"); }
|
||||
public set(int value) { view_as<JSONObject>(this).SetInt("height", value); }
|
||||
}
|
||||
|
||||
property int Height {
|
||||
public get() { return view_as<JSONObject>(this).GetInt("height"); }
|
||||
public set(int value) { view_as<JSONObject>(this).SetInt("height", value); }
|
||||
}
|
||||
}
|
||||
|
||||
methodmap UIColor < JSONObject {
|
||||
/// Creates a new UIColor with RGB between 0-255, alpha is normalized 0.0-1.0
|
||||
public UIColor(int r = 255, int g = 255, int b = 255) {
|
||||
|
@ -168,11 +185,15 @@ methodmap TempUIElementDefaults < JSONObject {
|
|||
}
|
||||
property UIPosition Position {
|
||||
public get() { return view_as<UIPosition>(view_as<JSONObject>(this).Get("position")); }
|
||||
public set(UIPosition pos) { view_as<JSONObject>(this).Set("position", view_as<JSON>(pos)); }
|
||||
// public set(UIPosition pos) { view_as<JSONObject>(this).Set("position", view_as<JSON>(pos)); }
|
||||
}
|
||||
property UIColor BackgroundColor {
|
||||
public get() { return view_as<UIColor>(view_as<JSONObject>(this).Get("bgColor")); }
|
||||
public set(UIColor color) { view_as<JSONObject>(this).Set("bgColor", view_as<JSON>(color)); }
|
||||
// public set(UIColor color) { view_as<JSONObject>(this).Set("bgColor", view_as<JSON>(color)); }
|
||||
}
|
||||
property UISize Size {
|
||||
public get() { return view_as<UISize>(view_as<JSONObject>(this).Get("size")); }
|
||||
// public set(UISize size) { view_as<JSONObject>(this).Set("size", view_as<JSON>(size)); }
|
||||
}
|
||||
/// Returns or sets opacity, -1 is not set
|
||||
property int Opacity {
|
||||
|
@ -203,6 +224,7 @@ enum UIType {
|
|||
Element_Unknown = -1,
|
||||
Element_Text,
|
||||
Element_List,
|
||||
Element_Audio
|
||||
}
|
||||
enum UIFlags {
|
||||
Element_None
|
||||
|
@ -287,27 +309,29 @@ methodmap TempUI {
|
|||
public TempUI(const char[] elemId, const char[] type, int lifetime = 0) {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.SetString("elem_id", elemId);
|
||||
obj.Set("steamids", new JSONArray());
|
||||
obj.SetInt("expires_seconds", 0);
|
||||
TempUIElement element = new TempUIElement(type);
|
||||
obj.Set("element", element);
|
||||
return view_as<TempUI>(obj);
|
||||
}
|
||||
|
||||
/// How long the temp UI lasts, 0 for never.
|
||||
property int Duration {
|
||||
public get() {
|
||||
return view_as<JSONObject>(this).GetInt("expires_seconds");
|
||||
}
|
||||
public set(int value) {
|
||||
view_as<JSONObject>(this).SetInt("expires_seconds", value);
|
||||
}
|
||||
}
|
||||
|
||||
property bool Visible {
|
||||
public get() {
|
||||
return view_as<JSONObject>(this).GetBool("visibility");
|
||||
}
|
||||
public set(bool value) {
|
||||
view_as<JSONObject>(this).SetBool("visibility", value);
|
||||
this.Send();
|
||||
}
|
||||
}
|
||||
|
||||
/** Is the UI element globally sent to all connected players?
|
||||
* Specify players with .AddClient() or clear with .ClearClients()
|
||||
*/
|
||||
property bool Global {
|
||||
public get() {
|
||||
return !view_as<JSONObject>(this).HasKey("steamids")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -316,38 +340,127 @@ methodmap TempUI {
|
|||
return view_as<TempUIElement>(view_as<JSONObject>(this).Get("element"));
|
||||
}
|
||||
public set(TempUIElement newElement) {
|
||||
// Delete old element
|
||||
JSON elem = view_as<JSONObject>(this).Get("element");
|
||||
if(elem != null) delete elem;
|
||||
|
||||
view_as<JSONObject>(this).Set("element", view_as<JSON>(newElement));
|
||||
}
|
||||
}
|
||||
|
||||
public void SetActionCallback(UIActionCallback callback) {}
|
||||
|
||||
public void AddClient(const char[] steamid) {
|
||||
// if(!IsClientInGame(client) || steamidCache[client][0] == '\0') ThrowError("Client %d is not connected, ingame, or authorized");
|
||||
JSONObject obj = view_as<JSONObject>(this);
|
||||
JSONArray steamids = view_as<JSONArray>(obj.Get("steamids"));
|
||||
if(steamids == null) {
|
||||
steamids = new JSONArray();
|
||||
obj.Set("steamids", steamids)
|
||||
}
|
||||
steamids.PushString(steamid);
|
||||
}
|
||||
|
||||
public void ClearClients() {
|
||||
view_as<JSONObject>(this).Remove("steamids");
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
view_as<JSONObject>(this).Clear();
|
||||
}
|
||||
|
||||
public void Hide() {
|
||||
this.Visibility = false;
|
||||
public void Hide() {
|
||||
this.Visible = false;
|
||||
}
|
||||
public void Show() {
|
||||
this.Visibility = true;
|
||||
this.Visible = true;
|
||||
}
|
||||
|
||||
public native bool Send();
|
||||
public native bool SendAll();
|
||||
public native bool SendTo(int client);
|
||||
public bool SendToMultiple(int[] clientIds, int numClients) {
|
||||
for(int i = 0; i < numClients; i++) {
|
||||
this.SendTo(clientIds[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
enum AudioState {
|
||||
// Audio stopped, reset to startTime
|
||||
Audio_Stopped,
|
||||
// Pauses audio at current time
|
||||
Audio_Paused,
|
||||
Audio_Play
|
||||
}
|
||||
|
||||
methodmap ClientList < JSONArray {
|
||||
public ClientList() {
|
||||
return view_as<ClientList>(new JSONArray());
|
||||
}
|
||||
|
||||
property int Length {
|
||||
public get() { return view_as<JSONArray>(this).Length; }
|
||||
}
|
||||
|
||||
public native void AddClient(int client);
|
||||
|
||||
public native bool HasClient(int client);
|
||||
|
||||
public void Clear() {
|
||||
view_as<JSONArray>(this).Clear();
|
||||
}
|
||||
}
|
||||
|
||||
methodmap AudioResource < JSONObject {
|
||||
public AudioResource(const char[] url, float volume = 0.5) {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.SetString("source", url);
|
||||
obj.SetFloat("volume", volume);
|
||||
obj.SetInt("state", 0);
|
||||
obj.Set("steamids", new JSONArray());
|
||||
obj.SetBool("repeat", false)
|
||||
return view_as<AudioResource>(obj);
|
||||
}
|
||||
|
||||
property AudioState State {
|
||||
public get() {
|
||||
return view_as<AudioState>(view_as<JSONObject>(this).GetInt("state"));
|
||||
}
|
||||
public set(AudioState state) {
|
||||
view_as<JSONObject>(this).SetInt("state", view_as<int>(state));
|
||||
}
|
||||
}
|
||||
|
||||
property float Volume {
|
||||
public get() {
|
||||
return view_as<JSONObject>(this).GetFloat("volume");
|
||||
}
|
||||
public set(float volume) {
|
||||
view_as<JSONObject>(this).SetFloat("volume", volume);
|
||||
}
|
||||
}
|
||||
|
||||
property bool Repeat {
|
||||
public get() {
|
||||
return view_as<JSONObject>(this).GetBool("repeat");
|
||||
}
|
||||
public set(bool repeat) {
|
||||
view_as<JSONObject>(this).SetBool("repeat", repeat);
|
||||
}
|
||||
}
|
||||
|
||||
property ClientList Clients {
|
||||
public get() {
|
||||
return view_as<ClientList>(view_as<JSONObject>(this).Get("steamids"));
|
||||
}
|
||||
}
|
||||
/// Plays or resumes playing
|
||||
public native void Play();
|
||||
/// Stops playing audio, clients will reset to beginning
|
||||
public native void Stop();
|
||||
/// Pauses audio, resuming to current play duration
|
||||
public native void Pause();
|
||||
|
||||
public void Clear() {
|
||||
view_as<JSONObject>(this).Clear();
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined REQUIRE_PLUGIN
|
||||
public void __pl_overlay_SetNTVOptional() {
|
||||
MarkNativeAsOptional("IsOverlayConnected");
|
||||
MarkNativeAsOptional("RegisterActionAnyHandler");
|
||||
MarkNativeAsOptional("RegisterActionHandler");
|
||||
|
||||
MarkNativeAsOptional("UIElement.SendAll");
|
||||
MarkNativeAsOptional("UIElement.SendTo");
|
||||
MarkNativeAsOptional("TempUI.SendAll");
|
||||
MarkNativeAsOptional("TempUI.SendTo");
|
||||
MarkNativeAsOptional("AudioResource.Play");
|
||||
MarkNativeAsOptional("AudioResource.Stop");
|
||||
MarkNativeAsOptional("AudioResource.Pause");
|
||||
}
|
||||
#endif
|
135
scripting/include/randomizer/rbuild.sp
Normal file
135
scripting/include/randomizer/rbuild.sp
Normal file
|
@ -0,0 +1,135 @@
|
|||
/// MENUS
|
||||
public void OpenMainMenu(int client) {
|
||||
Menu menu = new Menu(BuilderHandler_MainMenu);
|
||||
menu.SetTitle("Randomizer Builder");
|
||||
if(g_builder.mapData == null) {
|
||||
menu.AddItem("load", "Load Map Data");
|
||||
menu.AddItem("new", "New Map Data");
|
||||
} else {
|
||||
menu.AddItem("save", "Save Map Data");
|
||||
menu.AddItem("selector", "Start Selector");
|
||||
menu.AddItem("spawner", "Start Spawner");
|
||||
menu.AddItem("cursor", "Add Entity At Cursor");
|
||||
menu.AddItem("scenes", "Scenes");
|
||||
}
|
||||
menu.Display(client, 0);
|
||||
}
|
||||
|
||||
void OpenScenesMenu(int client) {
|
||||
Menu menu = new Menu(BuilderHandler_ScenesMenu);
|
||||
menu.SetTitle("Select a scene");
|
||||
char id[64], display[32];
|
||||
JSONObjectKeys iterator = g_builder.mapData.Keys();
|
||||
while(iterator.ReadKey(id, sizeof(id))) {
|
||||
if(StrEqual(id, g_builder.selectedSceneId)) {
|
||||
Format(display, sizeof(display), "%s (selected)", id);
|
||||
} else {
|
||||
Format(display, sizeof(display), "%s", id);
|
||||
}
|
||||
menu.AddItem(id, display);
|
||||
}
|
||||
menu.Display(client, 0);
|
||||
}
|
||||
|
||||
void OpenVariantsMenu(int client) {
|
||||
Menu menu = new Menu(BuilderHandler_VariantsMenu);
|
||||
menu.SetTitle("%s > Variants", g_builder.selectedSceneId);
|
||||
char id[8], display[32];
|
||||
menu.AddItem("new", "New");
|
||||
menu.AddItem("-1", "None (Shared Scene)");
|
||||
|
||||
JSONArray variants = view_as<JSONArray>(g_builder.selectedSceneData.Get("variants"));
|
||||
JSONObject varObj;
|
||||
JSONArray entities;
|
||||
for(int i = 0; i < variants.Length; i++) {
|
||||
varObj = view_as<JSONObject>(variants.Get(i));
|
||||
entities = view_as<JSONArray>(varObj.Get("entities"));
|
||||
if(i == g_builder.selectedVariantIndex) {
|
||||
Format(display, sizeof(display), "%d entities (selected)", entities.Length);
|
||||
} else {
|
||||
Format(display, sizeof(display), "%d entities", entities.Length);
|
||||
}
|
||||
IntToString(i, id, sizeof(id));
|
||||
menu.AddItem(id, display);
|
||||
}
|
||||
menu.Display(client, 0);
|
||||
}
|
||||
|
||||
/// HANDLERS
|
||||
|
||||
int BuilderHandler_MainMenu(Menu menu, MenuAction action, int client, int param2) {
|
||||
if (action == MenuAction_Select) {
|
||||
char info[32];
|
||||
menu.GetItem(param2, info, sizeof(info));
|
||||
if(StrEqual(info, "new")) {
|
||||
JSONObject temp = LoadMapJson(currentMap);
|
||||
GetCmdArg(2, info, sizeof(info));
|
||||
if(temp != null) {
|
||||
Menu nMenu = new Menu(BuilderHandler_MainMenu);
|
||||
nMenu.SetTitle("Existing map data exists");
|
||||
nMenu.AddItem("new_confirm", "Overwrite");
|
||||
nMenu.Display(client, 0);
|
||||
delete temp;
|
||||
return 0;
|
||||
} else {
|
||||
FakeClientCommand(client, "sm_rbuild new");
|
||||
}
|
||||
} else if(StrEqual(info, "new_confirm")) {
|
||||
FakeClientCommand(client, "sm_rbuild new confirm");
|
||||
} else if(StrEqual(info, "scenes")) {
|
||||
OpenScenesMenu(client);
|
||||
return 0;
|
||||
} else {
|
||||
FakeClientCommand(client, "sm_rbuild %s", info);
|
||||
} /*else if(StrEqual(info, "cursor")) {
|
||||
Menu menu = new Menu(BuilderHandler_)
|
||||
}*/
|
||||
OpenMainMenu(client);
|
||||
} else if(action == MenuAction_Cancel) {
|
||||
if(param2 == MenuCancel_ExitBack) {
|
||||
}
|
||||
} else if (action == MenuAction_End) {
|
||||
delete menu;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int BuilderHandler_ScenesMenu(Menu menu, MenuAction action, int client, int param2) {
|
||||
if (action == MenuAction_Select) {
|
||||
char info[64];
|
||||
menu.GetItem(param2, info, sizeof(info));
|
||||
if(StrEqual(info, "new")) {
|
||||
FakeClientCommand(client, "sm_rbuild scenes new");
|
||||
OpenScenesMenu(client);
|
||||
} else {
|
||||
FakeClientCommand(client, "sm_rbuild scenes select %s", info);
|
||||
OpenVariantsMenu(client);
|
||||
}
|
||||
} else if(action == MenuAction_Cancel) {
|
||||
if(param2 == MenuCancel_ExitBack) {
|
||||
}
|
||||
} else if (action == MenuAction_End) {
|
||||
delete menu;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int BuilderHandler_VariantsMenu(Menu menu, MenuAction action, int client, int param2) {
|
||||
if (action == MenuAction_Select) {
|
||||
char info[64];
|
||||
menu.GetItem(param2, info, sizeof(info));
|
||||
if(StrEqual(info, "new")) {
|
||||
FakeClientCommand(client, "sm_rbuild scenes variants new");
|
||||
} else {
|
||||
FakeClientCommand(client, "sm_rbuild scenes variants select %s", info);
|
||||
}
|
||||
OpenVariantsMenu(client);
|
||||
} else if(action == MenuAction_Cancel) {
|
||||
if(param2 == MenuCancel_ExitBack) {
|
||||
}
|
||||
} else if (action == MenuAction_End) {
|
||||
delete menu;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -249,8 +249,8 @@ void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast)
|
|||
CPrintChatToAdmins("{olive}%N{default} has been banned for %d minutes (marked as troll). If this was a mistake, you can discard their ban from the admin panel at {yellow}https://admin.jackz.me", client, hBanTime.IntValue);
|
||||
else
|
||||
CPrintChatToAdmins("{olive}%N{default} has been permanently banned (marked as troll). If this was a mistake, you can discard their ban from the admin panel at {yellow}https://admin.jackz.me", client);
|
||||
pData[client].isTroll = false;
|
||||
}
|
||||
pData[client].isTroll = false;
|
||||
|
||||
if(!IsFakeClient(client)) {
|
||||
float minutesSinceiLastFFTime = GetLastFFMinutes(client);
|
||||
|
|
|
@ -90,7 +90,7 @@ ConVar cvEPICommonCountScale, cvEPICommonCountScaleMax;
|
|||
ConVar g_ffFactorCvar, hExtraTankThreshold;
|
||||
|
||||
|
||||
ConVar cvZCommonLimit; int zCommonLimitPrevValue;
|
||||
ConVar cvZCommonLimit; int commonLimitBase; bool isSettingLimit;
|
||||
|
||||
int g_extraKitsAmount, g_extraKitsStart, g_saferoomDoorEnt, g_prevPlayerCount;
|
||||
bool g_forcedSurvivorCount, g_extraKitsSpawnedFinale;
|
||||
|
@ -306,25 +306,29 @@ public void OnPluginStart() {
|
|||
|
||||
hExtraItemBasePercentage = CreateConVar("epi_item_chance", "0.034", "The base chance (multiplied by player count) of an extra item being spawned.", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
hExtraSpawnBasePercentage = CreateConVar("epi_spawn_chance", "0.01", "The base chance (multiplied by player count) of an extra item spawner being created.", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
hAddExtraKits = CreateConVar("epi_kitmode", "0", "Decides how extra kits should be added.\n0 -> Overwrites previous extra kits\n1 -> Adds onto previous extra kits", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
hUpdateMinPlayers = CreateConVar("epi_updateminplayers", "1", "Should the plugin update abm\'s cvar min_players convar to the player count?\n 0 -> NO\n1 -> YES", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
hMinPlayersSaferoomDoor = CreateConVar("epi_doorunlock_percent", "0.75", "The percent of players that need to be loaded in before saferoom door is opened.\n 0 to disable", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
hSaferoomDoorWaitSeconds = CreateConVar("epi_doorunlock_wait", "25", "How many seconds after to unlock saferoom door. 0 to disable", FCVAR_NONE, true, 0.0);
|
||||
hSaferoomDoorAutoOpen = CreateConVar("epi_doorunlock_open", "0", "Controls when the door automatically opens after unlocked. Add bits together.\n0 = Never, 1 = When timer expires, 2 = When all players loaded in", FCVAR_NONE, true, 0.0);
|
||||
hEPIHudState = CreateConVar("epi_hudstate", "1", "Controls when the hud displays.\n0 -> OFF, 1 = When 5+ players, 2 = ALWAYS", FCVAR_NONE, true, 0.0, true, 3.0);
|
||||
hExtraFinaleTank = CreateConVar("epi_extra_tanks", "3", "Add bits together. 0 = Normal tank spawning, 1 = 50% tank split on non-finale (half health), 2 = Tank split (full health) on finale ", FCVAR_NONE, true, 0.0, true, 3.0);
|
||||
hExtraTankThreshold = CreateConVar("epi_extra_tanks_min_players", "6", "The minimum number of players for extra tanks to spawn. When disabled, normal 5+ tank health applies", FCVAR_NONE, true, 0.0);
|
||||
hSplitTankChance = CreateConVar("epi_splittank_chance", "0.65", "The % chance of a split tank occurring in non-finales", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
cvDropDisconnectTime = CreateConVar("epi_disconnect_time", "120.0", "The amount of seconds after a player has actually disconnected, where their character slot will be void. 0 to disable", FCVAR_NONE, true, 0.0);
|
||||
cvFFDecreaseRate = CreateConVar("epi_ff_decrease_rate", "0.3", "The friendly fire factor is subtracted from the formula (playerCount-4) * this rate. Effectively reduces ff penalty when more players. 0.0 to subtract none", FCVAR_NONE, true, 0.0);
|
||||
cvEPIHudFlags = CreateConVar("epi_hud_flags", "3", "Add together.\n1 = Scrolling hud, 2 = Show ping", FCVAR_NONE, true, 0.0);
|
||||
cvEPISpecialSpawning = CreateConVar("epi_sp_spawning", "2", "Determines what specials are spawned. Add bits together.\n1 = Normal specials\n2 = Witches\n4 = Tanks", FCVAR_NONE, true, 0.0);
|
||||
cvEPITankHealth = CreateConVar("epi_tank_chunkhp", "2500", "The amount of health added to tank, for each extra player", FCVAR_NONE, true, 0.0);
|
||||
cvEPIGamemodes = CreateConVar("epi_gamemodes", "coop,realism,versus", "Gamemodes where plugin is active. Comma-separated", FCVAR_NONE);
|
||||
cvEPIEnabledMode = CreateConVar("epi_enabled", "1", "Is EPI enabled?\n0=OFF\n1=Auto (Official Maps Only)(5+)\n2=Auto (Any map) (5+)\n3=Forced on", FCVAR_NONE, true, 0.0, true, 3.0);
|
||||
cvEPICommonCountScale = CreateConVar("epi_commons_scale_multiplier", "0", "This value is multiplied by the number of extra players playing. It's then added to z_common_limit. 5 players with value 5 would be z_common_limit + ", FCVAR_NONE, true, 0.0);
|
||||
cvEPICommonCountScaleMax = CreateConVar("epi_commons_scale_max", "60", "The maximum amount that z_common_limit can be scaled to.", FCVAR_NONE, true, 0.0);
|
||||
hAddExtraKits = CreateConVar("epi_kitmode", "0", "Decides how extra kits should be added.\n0 -> Overwrites previous extra kits\n1 -> Adds onto previous extra kits", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
hUpdateMinPlayers = CreateConVar("epi_updateminplayers", "1", "Should the plugin update abm\'s cvar min_players convar to the player count?\n 0 -> NO\n1 -> YES", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
hMinPlayersSaferoomDoor = CreateConVar("epi_doorunlock_percent", "0.75", "The percent of players that need to be loaded in before saferoom door is opened.\n 0 to disable", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
hSaferoomDoorWaitSeconds = CreateConVar("epi_doorunlock_wait", "25", "How many seconds after to unlock saferoom door. 0 to disable", FCVAR_NONE, true, 0.0);
|
||||
hSaferoomDoorAutoOpen = CreateConVar("epi_doorunlock_open", "0", "Controls when the door automatically opens after unlocked. Add bits together.\n0 = Never, 1 = When timer expires, 2 = When all players loaded in", FCVAR_NONE, true, 0.0);
|
||||
hEPIHudState = CreateConVar("epi_hudstate", "1", "Controls when the hud displays.\n0 -> OFF, 1 = When 5+ players, 2 = ALWAYS", FCVAR_NONE, true, 0.0, true, 3.0);
|
||||
hExtraFinaleTank = CreateConVar("epi_extra_tanks", "3", "Add bits together. 0 = Normal tank spawning, 1 = 50% tank split on non-finale (half health), 2 = Tank split (full health) on finale ", FCVAR_NONE, true, 0.0, true, 3.0);
|
||||
hExtraTankThreshold = CreateConVar("epi_extra_tanks_min_players", "6", "The minimum number of players for extra tanks to spawn. When disabled, normal 5+ tank health applies", FCVAR_NONE, true, 0.0);
|
||||
hSplitTankChance = CreateConVar("epi_splittank_chance", "0.65", "The % chance of a split tank occurring in non-finales", FCVAR_NONE, true, 0.0, true, 1.0);
|
||||
cvDropDisconnectTime = CreateConVar("epi_disconnect_time", "120.0", "The amount of seconds after a player has actually disconnected, where their character slot will be void. 0 to disable", FCVAR_NONE, true, 0.0);
|
||||
cvFFDecreaseRate = CreateConVar("epi_ff_decrease_rate", "0.3", "The friendly fire factor is subtracted from the formula (playerCount-4) * this rate. Effectively reduces ff penalty when more players. 0.0 to subtract none", FCVAR_NONE, true, 0.0);
|
||||
cvEPIHudFlags = CreateConVar("epi_hud_flags", "3", "Add together.\n1 = Scrolling hud, 2 = Show ping", FCVAR_NONE, true, 0.0);
|
||||
cvEPISpecialSpawning = CreateConVar("epi_sp_spawning", "2", "Determines what specials are spawned. Add bits together.\n1 = Normal specials\n2 = Witches\n4 = Tanks", FCVAR_NONE, true, 0.0);
|
||||
cvEPITankHealth = CreateConVar("epi_tank_chunkhp", "2500", "The amount of health added to tank, for each extra player", FCVAR_NONE, true, 0.0);
|
||||
cvEPIGamemodes = CreateConVar("epi_gamemodes", "coop,realism,versus", "Gamemodes where plugin is active. Comma-separated", FCVAR_NONE);
|
||||
cvEPIEnabledMode = CreateConVar("epi_enabled", "1", "Is EPI enabled?\n0=OFF\n1=Auto (Official Maps Only)(5+)\n2=Auto (Any map) (5+)\n3=Forced on", FCVAR_NONE, true, 0.0, true, 3.0);
|
||||
cvEPICommonCountScale = CreateConVar("epi_commons_scale_multiplier", "0", "This value is multiplied by the number of extra players playing. It's then added to z_common_limit. 5 players with value 5 would be z_common_limit + ", FCVAR_NONE, true, 0.0);
|
||||
cvEPICommonCountScaleMax = CreateConVar("epi_commons_scale_max", "60", "The maximum amount that z_common_limit can be scaled to.", FCVAR_NONE, true, 0.0);
|
||||
cvZCommonLimit = FindConVar("z_common_limit");
|
||||
|
||||
cvEPICommonCountScale.AddChangeHook(Cvar_CommonScaleChange);
|
||||
cvEPICommonCountScaleMax.AddChangeHook(Cvar_CommonScaleChange);
|
||||
cvZCommonLimit.AddChangeHook(Cvar_CommonScaleChange);
|
||||
|
||||
// TODO: hook flags, reset name index / ping mode
|
||||
cvEPIHudFlags.AddChangeHook(Cvar_HudStateChange);
|
||||
|
@ -428,6 +432,7 @@ public void OnPluginEnd() {
|
|||
delete weaponMaxClipSizes;
|
||||
delete g_ammoPacks;
|
||||
L4D2_ExecVScriptCode(HUD_SCRIPT_CLEAR);
|
||||
_UnsetCommonLimit();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -482,6 +487,19 @@ void Cvar_HudStateChange(ConVar convar, const char[] oldValue, const char[] newV
|
|||
TryStartHud();
|
||||
}
|
||||
}
|
||||
void Cvar_CommonScaleChange(ConVar convar, const char[] oldValue, const char[] newValue) {
|
||||
if(convar == cvZCommonLimit) {
|
||||
PrintToServer("z_common_limit changed [value=%d] [isSettingLimit=%b]", convar.IntValue, isSettingLimit);
|
||||
// Ignore our own changes:
|
||||
if(isSettingLimit) {
|
||||
isSettingLimit = false;
|
||||
return;
|
||||
}
|
||||
commonLimitBase = convar.IntValue;
|
||||
}
|
||||
_SetCommonLimit();
|
||||
|
||||
}
|
||||
void TryStartHud() {
|
||||
int threshold = 0;
|
||||
// Default to 0 for state == 2 (force)
|
||||
|
@ -572,6 +590,7 @@ Action Command_EpiVal(int client, int args) {
|
|||
PrintToConsole(client, "restCount = %d", g_restCount);
|
||||
PrintToConsole(client, "extraFinaleTankEnabled = %b", g_extraFinaleTankEnabled);
|
||||
PrintToConsole(client, "g_areItemsPopulated = %b", g_areItemsPopulated);
|
||||
PrintToConsole(client, "commonLimitBase = %d", commonLimitBase);
|
||||
ReplyToCommand(client, "Values printed to console");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
@ -1343,7 +1362,7 @@ public void OnMapStart() {
|
|||
if(L4D_IsMissionFinalMap()) {
|
||||
// Disable tank split on hard rain finale
|
||||
g_extraFinaleTankEnabled = true;
|
||||
if(StrEqual(map, "c4m5_milltown_escape")) {
|
||||
if(StrEqual(map, "c4m5_milltown_escape") || StrEqual(map, "c14m2_lighthouse")) {
|
||||
g_extraFinaleTankEnabled = false;
|
||||
}
|
||||
}
|
||||
|
@ -1454,6 +1473,7 @@ public void OnConfigsExecuted() {
|
|||
if(hUpdateMinPlayers.BoolValue && hMinPlayers != null) {
|
||||
hMinPlayers.IntValue = g_realSurvivorCount;
|
||||
}
|
||||
_SetCommonLimit();
|
||||
}
|
||||
|
||||
public void OnMapEnd() {
|
||||
|
@ -2102,29 +2122,42 @@ void UpdateSurvivorCount() {
|
|||
} else if(wasActive) {
|
||||
OnEPIInactive();
|
||||
}
|
||||
|
||||
if(isActive)
|
||||
SetFFFactor(g_epiEnabled);
|
||||
if(isActive) {
|
||||
SetFFFactor(g_epiEnabled);
|
||||
_SetCommonLimit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OnEPIActive() {
|
||||
zCommonLimitPrevValue = cvZCommonLimit.IntValue;
|
||||
_SetCommonLimit();
|
||||
}
|
||||
|
||||
void OnEPIInactive() {
|
||||
_UnsetCommonLimit();
|
||||
}
|
||||
|
||||
void _UnsetCommonLimit() {
|
||||
if(commonLimitBase > 0) {
|
||||
cvZCommonLimit.IntValue = commonLimitBase;
|
||||
}
|
||||
commonLimitBase = 0;
|
||||
}
|
||||
void _SetCommonLimit() {
|
||||
if(!g_epiEnabled || commonLimitBase <= 0) return;
|
||||
// TODO: lag check for common limit
|
||||
if(cvEPICommonCountScale.IntValue > 0) {
|
||||
int newLimit = zCommonLimitPrevValue + RoundFloat(cvEPICommonCountScale.FloatValue * float(g_realSurvivorCount));
|
||||
if(cvEPICommonCountScale.IntValue > 0 && commonLimitBase > 0) {
|
||||
int newLimit = commonLimitBase + RoundFloat(cvEPICommonCountScale.FloatValue * float(g_realSurvivorCount - 4));
|
||||
PrintDebug(DEBUG_INFO, "Setting common scale: %d + (%f * %d) [max=%d] = %d", commonLimitBase, cvEPICommonCountScale.FloatValue, g_realSurvivorCount - 4, cvEPICommonCountScaleMax.IntValue, newLimit);
|
||||
if(newLimit > 0) {
|
||||
if(newLimit > cvEPICommonCountScaleMax.IntValue) {
|
||||
newLimit = cvEPICommonCountScaleMax.IntValue;
|
||||
}
|
||||
isSettingLimit = true;
|
||||
cvZCommonLimit.IntValue = newLimit;
|
||||
}
|
||||
cvZCommonLimit.IntValue = newLimit;
|
||||
}
|
||||
}
|
||||
|
||||
void OnEPIInactive() {
|
||||
cvZCommonLimit.IntValue = zCommonLimitPrevValue;
|
||||
}
|
||||
void SetFFFactor(bool enabled) {
|
||||
static float prevValue;
|
||||
// Restore the previous value (we use the value for the calculations of new value)
|
||||
|
|
|
@ -213,6 +213,7 @@ public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[
|
|||
HookEvent("player_bot_replace", Event_PlayerToBot);
|
||||
HookEvent("player_ledge_grab", Event_LedgeGrab);
|
||||
AddCommandListener(OnGoAwayFromKeyboard, "go_away_from_keyboard");
|
||||
InitGamemode();
|
||||
} else if(!lateLoaded) {
|
||||
cvarStorage.Restore();
|
||||
delete cvarStorage;
|
||||
|
@ -462,7 +463,8 @@ Action Timer_SpawnBots(Handle h, int max) {
|
|||
if(AddSurvivor()) {
|
||||
count++;
|
||||
return Plugin_Continue;
|
||||
} else {
|
||||
} else if(count < 0) {
|
||||
// Fail if we couldnt make enough bots
|
||||
PrintToChatAll("GUESS WHO: FATAL ERROR: AddSurvivor() failed");
|
||||
LogError("Guess Who: Fatal Error: AddSurvivor() failed");
|
||||
count = 0;
|
||||
|
@ -561,6 +563,7 @@ Action Timer_WaitForStart(Handle h) {
|
|||
Game.State = State_Starting;
|
||||
Game.Tick = 0;
|
||||
Game.MapTime = RoundFloat(seedTime);
|
||||
Game.PopulateCoins();
|
||||
CreateTimer(seedTime, Timer_StartSeeker);
|
||||
return Plugin_Stop;
|
||||
}
|
||||
|
@ -585,6 +588,7 @@ Action Timer_StartSeeker(Handle h) {
|
|||
Action Timer_TimesUp(Handle h) {
|
||||
Game.Broadcast("The seeker ran out of time. Hiders win!");
|
||||
Game.End(State_HidersWin);
|
||||
timesUpTimer = null;
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,8 @@ char g_currentMap[64];
|
|||
#include <hats/hats.sp>
|
||||
#include <hats/hat_presets.sp>
|
||||
#include <hats/props/base.sp>
|
||||
#include <hats/natives.sp>
|
||||
#include <hats_editor>
|
||||
|
||||
public Plugin myinfo = {
|
||||
name = "L4D2 Hats & Editor",
|
||||
|
@ -56,6 +58,32 @@ public Plugin myinfo = {
|
|||
};
|
||||
|
||||
ArrayList NavAreas;
|
||||
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() {
|
||||
|
@ -116,6 +144,7 @@ public void OnPluginStart() {
|
|||
for(int i = 1; i <= MaxClients; i++) {
|
||||
Editor[i].client = i;
|
||||
Editor[i].Reset(true);
|
||||
g_PropData[i].Init(i);
|
||||
hatData[i].yeetGroundTimer = null;
|
||||
}
|
||||
|
||||
|
@ -456,6 +485,7 @@ ArrayList GetSpawnLocations() {
|
|||
return newList;
|
||||
}
|
||||
|
||||
|
||||
void ChooseRandomPosition(float pos[3], int ignoreClient = 0) {
|
||||
if(NavAreas.Length > 0 && GetURandomFloat() > 0.5) {
|
||||
int nav = NavAreas.Get(GetURandomInt() % (NavAreas.Length - 1));
|
||||
|
@ -578,34 +608,34 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
|
|||
if(g_PropData[client].pendingSaveType == Save_Schematic) {
|
||||
// move cursor? or should be editor anyway
|
||||
}
|
||||
} else if(g_PropData[client].markedProps != null) {
|
||||
} 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) {
|
||||
g_PropData[client].markedProps.Push(EntIndexToEntRef(entity));
|
||||
GlowEntity(entity, 0.0, GLOW_RED);
|
||||
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) {
|
||||
int ref = EntIndexToEntRef(entity);
|
||||
int index = g_PropData[client].markedProps.FindValue(ref);
|
||||
if(index > -1) {
|
||||
g_PropData[client].markedProps.Erase(index);
|
||||
L4D2_RemoveEntityGlow(entity);
|
||||
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
|
||||
EndDeleteTool(client, true);
|
||||
g_PropData[client].Selector.End();
|
||||
} else if(buttons & IN_DUCK) {
|
||||
//Cancel
|
||||
EndDeleteTool(client, false);
|
||||
g_PropData[client].Selector.Cancel();
|
||||
}
|
||||
}
|
||||
cmdThrottle[client] = tick;
|
||||
|
@ -628,31 +658,37 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
|
|||
}
|
||||
if(!(oldButtons & IN_JUMP) && (buttons & IN_JUMP)) {
|
||||
buttons &= ~IN_JUMP;
|
||||
Editor[client].CycleStacker(tick);
|
||||
Editor[client].CycleStacker();
|
||||
} else if(!(oldButtons & IN_SPEED) && (buttons & IN_SPEED)) {
|
||||
Editor[client].ToggleCollision(tick);
|
||||
Editor[client].ToggleCollision();
|
||||
return Plugin_Handled;
|
||||
} else if(!(oldButtons & IN_DUCK) && (buttons & IN_DUCK)) {
|
||||
Editor[client].ToggleCollisionRotate(tick);
|
||||
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(tick);
|
||||
else if(buttons & IN_ATTACK2) Editor[client].CycleSnapAngle(tick);
|
||||
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 == 2) {
|
||||
if(mouse[1] > 10) Editor[client].angles[2] += Editor[client].snapAngle;
|
||||
else if(mouse[1] < -10) Editor[client].angles[2] -= Editor[client].snapAngle;
|
||||
} else {
|
||||
if(mouse[0] > 10) Editor[client].angles[Editor[client].axis] += Editor[client].snapAngle;
|
||||
else if(mouse[0] < -10) Editor[client].angles[Editor[client].axis] -= Editor[client].snapAngle;
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -701,24 +737,27 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
|
|||
}
|
||||
case COLOR: {
|
||||
SetWeaponDelay(client, 0.5);
|
||||
PrintCenterText(client, "%d %d %d %d", Editor[client].color[0], Editor[client].color[1], Editor[client].color[2], Editor[client].color[3]);
|
||||
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_ATTACK) {
|
||||
} else if(buttons & IN_ATTACK2) {
|
||||
Editor[client].IncreaseColor(1);
|
||||
allowMove = false;
|
||||
} else if(buttons & IN_ATTACK2) {
|
||||
} else if(buttons & IN_ATTACK) {
|
||||
Editor[client].IncreaseColor(-1);
|
||||
allowMove = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!(oldButtons & IN_USE) && buttons & IN_USE) {
|
||||
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) {
|
||||
if(Editor[client].flags & Edit_Preview)
|
||||
Editor[client].CycleBuildType();
|
||||
Editor[client].CycleBuildType();
|
||||
// Editor[client].ShowExtraOptions();
|
||||
} else {
|
||||
int entity;
|
||||
Editor[client].Done(entity);
|
||||
|
@ -735,6 +774,12 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
|
|||
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)
|
||||
Action OnRealTransmit(int entity, int client) {
|
||||
|
@ -914,6 +959,7 @@ bool Filter_ValidHats(int entity, int mask, int data) {
|
|||
}
|
||||
|
||||
bool CheckBlacklist(int entity) {
|
||||
if(entity == 0) return false;
|
||||
if(cvar_sm_hats_blacklist_enabled.BoolValue) {
|
||||
static char buffer[64];
|
||||
GetEntityClassname(entity, buffer, sizeof(buffer));
|
||||
|
@ -1024,4 +1070,4 @@ stock bool CalculateEditorPosition(int client, TraceEntityFilter filter) {
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -11,9 +11,12 @@
|
|||
#include <sdktools>
|
||||
//#include <sdkhooks>
|
||||
#include <profiler>
|
||||
#include <json>
|
||||
// #include <json>
|
||||
#include <ripext>
|
||||
#include <jutils>
|
||||
#include <entitylump>
|
||||
#undef REQUIRE_PLUGIN
|
||||
#include <hats_editor>
|
||||
|
||||
int g_iLaserIndex;
|
||||
#if defined DEBUG_BLOCKERS
|
||||
|
@ -27,6 +30,77 @@ int g_iLaserIndex;
|
|||
#define MAX_SCENE_NAME_LENGTH 32
|
||||
#define MAX_INPUTS_CLASSNAME_LENGTH 64
|
||||
|
||||
|
||||
ConVar cvarEnabled;
|
||||
enum struct ActiveSceneData {
|
||||
char name[MAX_SCENE_NAME_LENGTH];
|
||||
int variantIndex;
|
||||
}
|
||||
MapData g_MapData;
|
||||
BuilderData g_builder;
|
||||
char currentMap[64];
|
||||
|
||||
enum struct BuilderData {
|
||||
JSONObject mapData;
|
||||
|
||||
JSONObject selectedSceneData;
|
||||
char selectedSceneId[64];
|
||||
|
||||
JSONObject selectedVariantData;
|
||||
int selectedVariantIndex;
|
||||
|
||||
void Cleanup() {
|
||||
this.selectedSceneData = null;
|
||||
this.selectedVariantData = null;
|
||||
this.selectedVariantIndex = -1;
|
||||
this.selectedSceneId[0] = '\0';
|
||||
if(this.mapData != null)
|
||||
delete this.mapData;
|
||||
// JSONcleanup_and_delete(this.mapData);
|
||||
}
|
||||
|
||||
bool SelectScene(const char[] group) {
|
||||
if(!g_builder.mapData.HasKey(group)) return false;
|
||||
this.selectedSceneData = view_as<JSONObject>(g_builder.mapData.Get(group));
|
||||
strcopy(this.selectedSceneId, sizeof(this.selectedSceneId), group);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a variant, enter -1 to not select any (scene's entities)
|
||||
*/
|
||||
bool SelectVariant(int index = -1) {
|
||||
if(this.selectedSceneData == null) LogError("SelectVariant called, but no group selected");
|
||||
JSONArray variants = view_as<JSONArray>(this.selectedSceneData.Get("variants"));
|
||||
if(index >= variants.Length) return false;
|
||||
else if(index < -1) return false;
|
||||
else if(index > -1) {
|
||||
this.selectedVariantData = view_as<JSONObject>(variants.Get(index));
|
||||
} else {
|
||||
this.selectedVariantData = null;
|
||||
}
|
||||
this.selectedVariantIndex = index;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AddEntity(int entity, ExportType exportType = Export_Model) {
|
||||
JSONArray entities;
|
||||
if(g_builder.selectedVariantData == null) {
|
||||
// Create <scene>.entities if doesn't exist:
|
||||
if(!g_builder.selectedSceneData.HasKey("entities")) {
|
||||
g_builder.selectedSceneData.Set("entities", new JSONArray());
|
||||
}
|
||||
entities = view_as<JSONArray>(g_builder.selectedSceneData.Get("entities"));
|
||||
} else {
|
||||
entities = view_as<JSONArray>(g_builder.selectedVariantData.Get("entities"));
|
||||
}
|
||||
JSONObject entityData = ExportEntity(entity, Export_Model);
|
||||
entities.Push(entityData);
|
||||
}
|
||||
}
|
||||
|
||||
#include <randomizer/rbuild.sp>
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "L4D2 Randomizer",
|
||||
|
@ -36,13 +110,6 @@ public Plugin myinfo =
|
|||
url = "https://github.com/Jackzmc/sourcemod-plugins"
|
||||
};
|
||||
|
||||
ConVar cvarEnabled;
|
||||
enum struct ActiveSceneData {
|
||||
char name[MAX_SCENE_NAME_LENGTH];
|
||||
int variantIndex;
|
||||
}
|
||||
MapData g_MapData;
|
||||
|
||||
public void OnPluginStart() {
|
||||
EngineVersion g_Game = GetEngineVersion();
|
||||
if(g_Game != Engine_Left4Dead && g_Game != Engine_Left4Dead2) {
|
||||
|
@ -51,15 +118,14 @@ public void OnPluginStart() {
|
|||
|
||||
RegAdminCmd("sm_rcycle", Command_CycleRandom, ADMFLAG_CHEATS);
|
||||
RegAdminCmd("sm_expent", Command_ExportEnt, ADMFLAG_GENERIC);
|
||||
RegAdminCmd("sm_rbuild", Command_RandomizerBuild, ADMFLAG_CHEATS);
|
||||
|
||||
cvarEnabled = CreateConVar("sm_randomizer_enabled", "0");
|
||||
|
||||
g_MapData.activeScenes = new ArrayList(sizeof(ActiveSceneData));
|
||||
}
|
||||
|
||||
char currentMap[64];
|
||||
|
||||
bool hasRan;
|
||||
// TODO: on round start
|
||||
public void OnMapStart() {
|
||||
g_iLaserIndex = PrecacheModel("materials/sprites/laserbeam.vmt", true);
|
||||
|
@ -69,6 +135,7 @@ public void OnMapStart() {
|
|||
}
|
||||
|
||||
public void OnMapEnd() {
|
||||
g_builder.Cleanup();
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
|
@ -124,9 +191,7 @@ public Action Command_CycleRandom(int client, int args) {
|
|||
if(args > 0) {
|
||||
DeleteCustomEnts();
|
||||
|
||||
char arg1[8];
|
||||
GetCmdArg(1, arg1, sizeof(arg1));
|
||||
int flags = StringToInt(arg1) | view_as<int>(FLAG_REFRESH);
|
||||
int flags = GetCmdArgInt(1) | view_as<int>(FLAG_REFRESH);
|
||||
RunMap(currentMap, flags);
|
||||
if(client > 0)
|
||||
PrintCenterText(client, "Cycled flags=%d", flags);
|
||||
|
@ -141,7 +206,7 @@ public Action Command_CycleRandom(int client, int args) {
|
|||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
public Action Command_ExportEnt(int client, int args) {
|
||||
Action Command_ExportEnt(int client, int args) {
|
||||
float origin[3];
|
||||
int entity = GetLookingPosition(client, Filter_IgnorePlayer, origin);
|
||||
float angles[3];
|
||||
|
@ -188,6 +253,271 @@ public Action Command_ExportEnt(int client, int args) {
|
|||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
Action Command_RandomizerBuild(int client, int args) {
|
||||
char arg[64];
|
||||
GetCmdArg(1, arg, sizeof(arg));
|
||||
if(StrEqual(arg, "new")) {
|
||||
JSONObject temp = LoadMapJson(currentMap);
|
||||
GetCmdArg(2, arg, sizeof(arg));
|
||||
if(temp != null && !StrEqual(arg, "confirm")) {
|
||||
delete temp;
|
||||
ReplyToCommand(client, "Existing map data found, enter /rbuild new confirm to overwrite.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
g_builder.Cleanup();
|
||||
g_builder.mapData = new JSONObject();
|
||||
SaveMapJson(currentMap, g_builder.mapData);
|
||||
ReplyToCommand(client, "Started new map data for %s", currentMap);
|
||||
} else if(StrEqual(arg, "load")) {
|
||||
if(args >= 2) {
|
||||
GetCmdArg(2, arg, sizeof(arg));
|
||||
} else {
|
||||
strcopy(arg, sizeof(arg), currentMap);
|
||||
}
|
||||
g_builder.Cleanup();
|
||||
g_builder.mapData = LoadMapJson(arg);
|
||||
if(g_builder.mapData != null) {
|
||||
ReplyToCommand(client, "Loaded map data for %s", arg);
|
||||
} else {
|
||||
ReplyToCommand(client, "No map data found for %s", arg);
|
||||
}
|
||||
} else if(StrEqual(arg, "menu")) {
|
||||
OpenMainMenu(client);
|
||||
} else if(g_builder.mapData == null) {
|
||||
ReplyToCommand(client, "No map data for %s, either load with /rbuild load, or start new /rbuild new", currentMap);
|
||||
return Plugin_Handled;
|
||||
} else if(StrEqual(arg, "save")) {
|
||||
SaveMapJson(currentMap, g_builder.mapData);
|
||||
ReplyToCommand(client, "Saved %s", currentMap);
|
||||
} else if(StrEqual(arg, "scenes")) {
|
||||
Command_RandomizerBuild_Scenes(client, args);
|
||||
} else if(StrEqual(arg, "sel") || StrEqual(arg, "selector")) {
|
||||
if(g_builder.selectedVariantData == null) {
|
||||
ReplyToCommand(client, "Please load map data, select a scene and a variant.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
StartSelector(client, OnSelectorDone);
|
||||
} else if(StrEqual(arg, "spawner")) {
|
||||
if(g_builder.selectedVariantData == null) {
|
||||
ReplyToCommand(client, "Please load map data, select a scene and a variant.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
StartSpawner(client, OnSpawnerDone);
|
||||
ReplyToCommand(client, "Spawn props to add to variant");
|
||||
} else if(StrEqual(arg, "cursor")) {
|
||||
if(g_builder.selectedVariantData == null) {
|
||||
ReplyToCommand(client, "Please load map data, select a scene and a variant.");
|
||||
return Plugin_Handled;
|
||||
}
|
||||
float origin[3];
|
||||
char arg1[32];
|
||||
int entity = GetLookingPosition(client, Filter_IgnorePlayer, origin);
|
||||
GetCmdArg(2, arg1, sizeof(arg1));
|
||||
ExportType exportType = Export_Model;
|
||||
if(StrEqual(arg1, "hammerid")) {
|
||||
exportType = Export_HammerId;
|
||||
} else if(StrEqual(arg1, "targetname")) {
|
||||
exportType = Export_TargetName;
|
||||
}
|
||||
if(entity > 0) {
|
||||
g_builder.AddEntity(entity, exportType);
|
||||
ReplyToCommand(client, "Added entity #%d to variant #%d", entity, g_builder.selectedVariantIndex);
|
||||
} else {
|
||||
ReplyToCommand(client, "No entity found");
|
||||
}
|
||||
} else if(StrEqual(arg, "entityid")) {
|
||||
char arg1[32];
|
||||
int entity = GetCmdArgInt(2);
|
||||
GetCmdArg(3, arg1, sizeof(arg));
|
||||
ExportType exportType = Export_Model;
|
||||
if(StrEqual(arg1, "hammerid")) {
|
||||
exportType = Export_HammerId;
|
||||
} else if(StrEqual(arg1, "targetname")) {
|
||||
exportType = Export_TargetName;
|
||||
}
|
||||
if(entity > 0) {
|
||||
g_builder.AddEntity(entity, exportType);
|
||||
ReplyToCommand(client, "Added entity #%d to variant #%d", entity, g_builder.selectedVariantIndex);
|
||||
} else {
|
||||
ReplyToCommand(client, "No entity found");
|
||||
}
|
||||
} else {
|
||||
ReplyToCommand(client, "Unknown arg. Try: new, load, save, scenes, cursor");
|
||||
}
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
enum ExportType {
|
||||
Export_HammerId,
|
||||
Export_TargetName,
|
||||
Export_Model
|
||||
}
|
||||
JSONObject ExportEntity(int entity, ExportType exportType = Export_Model) {
|
||||
float origin[3], angles[3], size[3];
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", origin);
|
||||
GetEntPropVector(entity, Prop_Send, "m_angRotation", angles);
|
||||
GetEntPropVector(entity, Prop_Send, "m_vecMaxs", size);
|
||||
|
||||
char model[64];
|
||||
JSONObject entityData = new JSONObject();
|
||||
GetEntityClassname(entity, model, sizeof(model));
|
||||
if(StrContains(model, "prop_") == -1) {
|
||||
entityData.Set("scale", VecToArray(size));
|
||||
}
|
||||
if(exportType == Export_HammerId) {
|
||||
int hammerid = GetEntProp(entity, Prop_Data, "m_iHammerID");
|
||||
entityData.SetString("type", "hammerid");
|
||||
char id[16];
|
||||
IntToString(hammerid, id, sizeof(id));
|
||||
entityData.SetString("model", id);
|
||||
} else if(exportType == Export_TargetName) {
|
||||
GetEntPropString(entity, Prop_Data, "m_iName", model, sizeof(model));
|
||||
entityData.SetString("type", "targetname");
|
||||
entityData.SetString("model", model);
|
||||
} else {
|
||||
GetEntPropString(entity, Prop_Data, "m_ModelName", model, sizeof(model));
|
||||
entityData.SetString("model", model);
|
||||
}
|
||||
entityData.Set("origin", VecToArray(origin));
|
||||
entityData.Set("angles", VecToArray(angles));
|
||||
return entityData;
|
||||
}
|
||||
|
||||
bool OnSpawnerDone(int client, int entity, CompleteType result) {
|
||||
PrintToServer("Randomizer OnSpawnerDone");
|
||||
if(result == Complete_PropSpawned && entity > 0) {
|
||||
JSONObject entityData = ExportEntity(entity, Export_Model);
|
||||
JSONArray entities = view_as<JSONArray>(g_builder.selectedVariantData.Get("entities"));
|
||||
entities.Push(entityData);
|
||||
ReplyToCommand(client, "Added entity to variant");
|
||||
RemoveEntity(entity);
|
||||
}
|
||||
return result == Complete_PropSpawned;
|
||||
}
|
||||
void OnSelectorDone(int client, ArrayList entities) {
|
||||
JSONArray entArray = view_as<JSONArray>(g_builder.selectedVariantData.Get("entities"));
|
||||
if(entities != null) {
|
||||
JSONObject entityData;
|
||||
for(int i = 0; i < entities.Length; i++) {
|
||||
int ref = entities.Get(i);
|
||||
entityData = ExportEntity(ref, Export_Model);
|
||||
entArray.Push(entityData);
|
||||
delete entityData; //?
|
||||
RemoveEntity(ref);
|
||||
}
|
||||
PrintToChat(client, "Added %d entities to variant", entities.Length);
|
||||
delete entities;
|
||||
}
|
||||
}
|
||||
|
||||
JSONArray VecToArray(float vec[3]) {
|
||||
JSONArray arr = new JSONArray();
|
||||
arr.PushFloat(vec[0]);
|
||||
arr.PushFloat(vec[1]);
|
||||
arr.PushFloat(vec[2]);
|
||||
return arr;
|
||||
}
|
||||
|
||||
void Command_RandomizerBuild_Scenes(int client, int args) {
|
||||
char arg[16];
|
||||
GetCmdArg(2, arg, sizeof(arg));
|
||||
if(StrEqual(arg, "new")) {
|
||||
if(args < 4) {
|
||||
ReplyToCommand(client, "Syntax: /rbuild scenes new <name> <chance 0.0-1.0>");
|
||||
} else {
|
||||
char name[64];
|
||||
GetCmdArg(3, name, sizeof(name));
|
||||
GetCmdArg(4, arg, sizeof(arg));
|
||||
float chance = StringToFloat(arg);
|
||||
JSONObject scene = new JSONObject();
|
||||
scene.SetFloat("chance", chance);
|
||||
scene.Set("variants", new JSONArray());
|
||||
g_builder.mapData.Set(name, scene);
|
||||
g_builder.SelectScene(name);
|
||||
JSONArray variants = view_as<JSONArray>(g_builder.selectedSceneData.Get("variants"));
|
||||
|
||||
JSONObject variantObj = new JSONObject();
|
||||
variantObj.SetInt("weight", 1);
|
||||
variantObj.Set("entities", new JSONArray());
|
||||
variants.Push(variantObj);
|
||||
g_builder.SelectVariant(0);
|
||||
ReplyToCommand(client, "Created & selected scene & variant %s#0", name);
|
||||
StartSelector(client, OnSelectorDone);
|
||||
}
|
||||
} else if(StrEqual(arg, "select") || StrEqual(arg, "load") || StrEqual(arg, "choose")) {
|
||||
GetCmdArg(3, arg, sizeof(arg));
|
||||
if(g_builder.SelectScene(arg)) {
|
||||
int variantIndex;
|
||||
if(GetCmdArgIntEx(4, variantIndex)) {
|
||||
if(g_builder.SelectVariant(variantIndex)) {
|
||||
ReplyToCommand(client, "Selected scene: %s#%d", arg, variantIndex);
|
||||
} else {
|
||||
ReplyToCommand(client, "Unknown variant for scene");
|
||||
}
|
||||
} else {
|
||||
ReplyToCommand(client, "Selected scene: %s", arg);
|
||||
}
|
||||
} else {
|
||||
ReplyToCommand(client, "No scene found");
|
||||
}
|
||||
} else if(StrEqual(arg, "variants")) {
|
||||
Command_RandomizerBuild_Variants(client, args);
|
||||
} else if(args > 1) {
|
||||
ReplyToCommand(client, "Unknown argument, try: new, select, variants");
|
||||
} else {
|
||||
ReplyToCommand(client, "Scenes:");
|
||||
JSONObjectKeys iterator = g_builder.mapData.Keys();
|
||||
while(iterator.ReadKey(arg, sizeof(arg))) {
|
||||
if(StrEqual(arg, g_builder.selectedSceneId)) {
|
||||
ReplyToCommand(client, "\t%s (selected)", arg);
|
||||
} else {
|
||||
ReplyToCommand(client, "\t%s", arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Command_RandomizerBuild_Variants(int client, int args) {
|
||||
if(g_builder.selectedSceneId[0] == '\0') {
|
||||
ReplyToCommand(client, "No scene selected, select with /rbuild groups select <group>");
|
||||
return;
|
||||
}
|
||||
char arg[16];
|
||||
GetCmdArg(3, arg, sizeof(arg));
|
||||
if(StrEqual(arg, "new")) {
|
||||
// /rbuild group variants new [weight]
|
||||
int weight;
|
||||
if(!GetCmdArgIntEx(4, weight)) {
|
||||
weight = 1;
|
||||
}
|
||||
JSONArray variants = view_as<JSONArray>(g_builder.selectedSceneData.Get("variants"));
|
||||
JSONObject variantObj = new JSONObject();
|
||||
variantObj.SetInt("weight", weight);
|
||||
variantObj.Set("entities", new JSONArray());
|
||||
int index = variants.Push(variantObj);
|
||||
g_builder.SelectVariant(index);
|
||||
ReplyToCommand(client, "Created variant #%d", index);
|
||||
} else if(StrEqual(arg, "select")) {
|
||||
int index = GetCmdArgInt(4);
|
||||
if(g_builder.SelectVariant(index)) {
|
||||
ReplyToCommand(client, "Selected variant: %s#%d", g_builder.selectedSceneId, index);
|
||||
} else {
|
||||
ReplyToCommand(client, "No variant found");
|
||||
}
|
||||
} else {
|
||||
ReplyToCommand(client, "Variants:");
|
||||
JSONObject variantObj;
|
||||
JSONArray variants = view_as<JSONArray>(g_builder.selectedSceneData.Get("variants"));
|
||||
for(int i = 0; i < variants.Length; i++) {
|
||||
variantObj = view_as<JSONObject>(variants.Get(i));
|
||||
int weight = 1;
|
||||
if(variantObj.HasKey("weight"))
|
||||
weight = variantObj.GetInt("weight");
|
||||
JSONArray entities = view_as<JSONArray>(variantObj.Get("entities"));
|
||||
ReplyToCommand(client, " #%d. [W:%d] [#E:%d]", i, weight, entities.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum struct SceneData {
|
||||
|
@ -211,10 +541,12 @@ enum struct SceneVariantData {
|
|||
int weight;
|
||||
ArrayList inputsList;
|
||||
ArrayList entities;
|
||||
ArrayList forcedScenes;
|
||||
|
||||
void Cleanup() {
|
||||
delete this.inputsList;
|
||||
delete this.entities;
|
||||
delete this.forcedScenes;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -366,6 +698,7 @@ enum struct LumpEditData {
|
|||
}
|
||||
|
||||
enum struct MapData {
|
||||
StringMap scenesKv;
|
||||
ArrayList scenes;
|
||||
ArrayList lumpEdits;
|
||||
ArrayList activeScenes;
|
||||
|
@ -376,30 +709,40 @@ enum loadFlags {
|
|||
FLAG_ALL_SCENES = 1, // Pick all scenes, no random chance
|
||||
FLAG_ALL_VARIANTS = 2, // Pick all variants (for debug purposes),
|
||||
FLAG_REFRESH = 4, // Load data bypassing cache
|
||||
FLAG_FORCE_ACTIVE = 8 // Similar to ALL_SCENES, bypasses % chance
|
||||
}
|
||||
|
||||
// Reads (mapname).json file and parses it
|
||||
public bool LoadMapData(const char[] map, int flags) {
|
||||
public JSONObject LoadMapJson(const char[] map) {
|
||||
Debug("Loading config for %s", map);
|
||||
char filePath[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, filePath, sizeof(filePath), "data/randomizer/%s.json", map);
|
||||
if(!FileExists(filePath)) {
|
||||
Log("[Randomizer] No map config file (data/randomizer/%s.json), not loading", map);
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
char buffer[65536];
|
||||
File file = OpenFile(filePath, "r");
|
||||
if(file == null) {
|
||||
LogError("Could not open map config file (data/randomizer/%s.json)", map);
|
||||
return false;
|
||||
}
|
||||
file.ReadString(buffer, sizeof(buffer));
|
||||
JSON_Object data = json_decode(buffer);
|
||||
JSONObject data = JSONObject.FromFile(filePath);
|
||||
if(data == null) {
|
||||
LogError("Could not parse map config file (data/randomizer/%s.json)", map);
|
||||
return null;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
public void SaveMapJson(const char[] map, JSONObject json) {
|
||||
Debug("Saving config for %s", map);
|
||||
char filePath[PLATFORM_MAX_PATH], filePathTemp[PLATFORM_MAX_PATH];
|
||||
BuildPath(Path_SM, filePathTemp, sizeof(filePath), "data/randomizer/%s.json.tmp", map);
|
||||
BuildPath(Path_SM, filePath, sizeof(filePath), "data/randomizer/%s.json", map);
|
||||
|
||||
json.ToFile(filePathTemp, JSON_INDENT(4));
|
||||
RenameFile(filePath, filePathTemp);
|
||||
SetFilePermissions(filePath, FPERM_U_WRITE | FPERM_U_READ | FPERM_G_WRITE | FPERM_G_READ | FPERM_O_READ);
|
||||
}
|
||||
|
||||
public bool LoadMapData(const char[] map, int flags) {
|
||||
JSONObject data = LoadMapJson(map);
|
||||
if(data == null) {
|
||||
json_get_last_error(buffer, sizeof(buffer));
|
||||
LogError("Could not parse map config file (data/randomizer/%s.json): %s", map, buffer);
|
||||
delete file;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -407,51 +750,51 @@ public bool LoadMapData(const char[] map, int flags) {
|
|||
|
||||
Cleanup();
|
||||
g_MapData.scenes = new ArrayList(sizeof(SceneData));
|
||||
g_MapData.scenesKv = new StringMap();
|
||||
g_MapData.lumpEdits = new ArrayList(sizeof(LumpEditData));
|
||||
g_MapData.activeScenes.Clear();
|
||||
|
||||
Profiler profiler = new Profiler();
|
||||
profiler.Start();
|
||||
|
||||
int length = data.Length;
|
||||
JSONObjectKeys iterator = data.Keys();
|
||||
char key[32];
|
||||
for (int i = 0; i < length; i += 1) {
|
||||
data.GetKey(i, key, sizeof(key));
|
||||
|
||||
while(iterator.ReadKey(key, sizeof(key))) {
|
||||
if(key[0] == '_') {
|
||||
if(StrEqual(key, "_lumps")) {
|
||||
JSON_Array lumpsList = view_as<JSON_Array>(data.GetObject(key));
|
||||
JSONArray lumpsList = view_as<JSONArray>(data.Get(key));
|
||||
if(lumpsList != null) {
|
||||
for(int l = 0; l < lumpsList.Length; l++) {
|
||||
loadLumpData(g_MapData.lumpEdits, lumpsList.GetObject(l));
|
||||
loadLumpData(g_MapData.lumpEdits, view_as<JSONObject>(lumpsList.Get(l)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Debug("Unknown special entry \"%s\", skipping", key);
|
||||
}
|
||||
} else {
|
||||
if(data.GetType(key) != JSON_Type_Object) {
|
||||
Debug("Invalid normal entry \"%s\" (not an object), skipping", key);
|
||||
continue;
|
||||
}
|
||||
JSON_Object scene = data.GetObject(key);
|
||||
// if(data.GetType(key) != JSONType_Object) {
|
||||
// Debug("Invalid normal entry \"%s\" (not an object), skipping", key);
|
||||
// continue;
|
||||
// }
|
||||
JSONObject scene = view_as<JSONObject>(data.Get(key));
|
||||
// Parses scene data and inserts to scenes
|
||||
loadScene(key, scene);
|
||||
}
|
||||
}
|
||||
|
||||
json_cleanup_and_delete(data);
|
||||
delete data;
|
||||
profiler.Stop();
|
||||
Log("Parsed map file for %s(%d) and found %d scenes in %.4f seconds", map, flags, g_MapData.scenes.Length, profiler.Time);
|
||||
delete profiler;
|
||||
delete file;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Calls LoadMapData (read&parse (mapname).json) then select scenes
|
||||
public bool RunMap(const char[] map, int flags) {
|
||||
if(g_MapData.scenes == null || flags & view_as<int>(FLAG_REFRESH)) {
|
||||
LoadMapData(map, flags);
|
||||
if(!LoadMapData(map, flags)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Profiler profiler = new Profiler();
|
||||
|
||||
|
@ -463,7 +806,7 @@ public bool RunMap(const char[] map, int flags) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void loadScene(const char key[MAX_SCENE_NAME_LENGTH], JSON_Object sceneData) {
|
||||
void loadScene(const char key[MAX_SCENE_NAME_LENGTH], JSONObject sceneData) {
|
||||
SceneData scene;
|
||||
scene.name = key;
|
||||
scene.chance = sceneData.GetFloat("chance");
|
||||
|
@ -471,38 +814,73 @@ void loadScene(const char key[MAX_SCENE_NAME_LENGTH], JSON_Object sceneData) {
|
|||
LogError("Scene \"%s\" has invalid chance (%f)", scene.name, scene.chance);
|
||||
return;
|
||||
}
|
||||
// TODO: load "entities", merge with choice.entities
|
||||
sceneData.GetString("group", scene.group, sizeof(scene.group));
|
||||
scene.variants = new ArrayList(sizeof(SceneVariantData));
|
||||
JSON_Array variants = view_as<JSON_Array>(sceneData.GetObject("variants"));
|
||||
if(!sceneData.HasKey("variants")) {
|
||||
ThrowError("Failed to load: Scene \"%s\" has missing \"variants\" array", scene.name);
|
||||
return;
|
||||
}
|
||||
JSONArray entities;
|
||||
if(sceneData.HasKey("entities")) {
|
||||
entities = view_as<JSONArray>(sceneData.Get("entities"));
|
||||
}
|
||||
|
||||
JSONArray variants = view_as<JSONArray>(sceneData.Get("variants"));
|
||||
for(int i = 0; i < variants.Length; i++) {
|
||||
// Parses choice and loads to scene.choices
|
||||
loadChoice(scene, variants.GetObject(i));
|
||||
loadChoice(scene, view_as<JSONObject>(variants.Get(i)), entities);
|
||||
}
|
||||
g_MapData.scenes.PushArray(scene);
|
||||
g_MapData.scenesKv.SetArray(scene.name, scene, sizeof(scene));
|
||||
}
|
||||
|
||||
void loadChoice(SceneData scene, JSON_Object choiceData) {
|
||||
void loadChoice(SceneData scene, JSONObject choiceData, JSONArray extraEntities) {
|
||||
SceneVariantData choice;
|
||||
choice.weight = choiceData.GetInt("weight", 1);
|
||||
choice.weight = 1;
|
||||
if(choiceData.HasKey("weight"))
|
||||
choice.weight = choiceData.GetInt("weight");
|
||||
choice.entities = new ArrayList(sizeof(VariantEntityData));
|
||||
choice.inputsList = new ArrayList(sizeof(VariantInputData));
|
||||
JSON_Array entities = view_as<JSON_Array>(choiceData.GetObject("entities"));
|
||||
if(entities != null) {
|
||||
choice.forcedScenes = new ArrayList(ByteCountToCells(MAX_SCENE_NAME_LENGTH));
|
||||
// Load in any variant-based entities
|
||||
if(choiceData.HasKey("entities")) {
|
||||
JSONArray entities = view_as<JSONArray>(choiceData.Get("entities"));
|
||||
for(int i = 0; i < entities.Length; i++) {
|
||||
// Parses entities and loads to choice.entities
|
||||
loadChoiceEntity(choice.entities, entities.GetObject(i));
|
||||
loadChoiceEntity(choice.entities, view_as<JSONObject>(entities.Get(i)));
|
||||
}
|
||||
delete entities;
|
||||
}
|
||||
JSON_Array inputsList = view_as<JSON_Array>(choiceData.GetObject("inputs"));
|
||||
if(inputsList != null) {
|
||||
for(int i = 0; i < inputsList.Length; i++) {
|
||||
loadChoiceInput(choice.inputsList, inputsList.GetObject(i));
|
||||
// Load in any entities that the scene has
|
||||
if(extraEntities != null) {
|
||||
for(int i = 0; i < extraEntities.Length; i++) {
|
||||
// Parses entities and loads to choice.entities
|
||||
loadChoiceEntity(choice.entities, view_as<JSONObject>(extraEntities.Get(i)));
|
||||
}
|
||||
delete extraEntities;
|
||||
}
|
||||
// Load all inputs
|
||||
if(choiceData.HasKey("inputs")) {
|
||||
JSONArray inputsList = view_as<JSONArray>(choiceData.Get("inputs"));
|
||||
for(int i = 0; i < inputsList.Length; i++) {
|
||||
loadChoiceInput(choice.inputsList, view_as<JSONObject>(inputsList.Get(i)));
|
||||
}
|
||||
delete inputsList;
|
||||
}
|
||||
if(choiceData.HasKey("force_scenes")) {
|
||||
JSONArray scenes = view_as<JSONArray>(choiceData.Get("force_scenes"));
|
||||
char sceneId[32];
|
||||
for(int i = 0; i < scenes.Length; i++) {
|
||||
scenes.GetString(i, sceneId, sizeof(sceneId));
|
||||
choice.forcedScenes.PushString(sceneId);
|
||||
}
|
||||
delete scenes;
|
||||
}
|
||||
scene.variants.PushArray(choice);
|
||||
}
|
||||
|
||||
void loadChoiceInput(ArrayList list, JSON_Object inputData) {
|
||||
void loadChoiceInput(ArrayList list, JSONObject inputData) {
|
||||
VariantInputData input;
|
||||
// Check classname -> targetname -> hammerid
|
||||
if(!inputData.GetString("classname", input.name, sizeof(input.name))) {
|
||||
|
@ -527,7 +905,7 @@ void loadChoiceInput(ArrayList list, JSON_Object inputData) {
|
|||
list.PushArray(input);
|
||||
}
|
||||
|
||||
void loadLumpData(ArrayList list, JSON_Object inputData) {
|
||||
void loadLumpData(ArrayList list, JSONObject inputData) {
|
||||
LumpEditData input;
|
||||
// Check classname -> targetname -> hammerid
|
||||
if(!inputData.GetString("classname", input.name, sizeof(input.name))) {
|
||||
|
@ -553,7 +931,7 @@ void loadLumpData(ArrayList list, JSON_Object inputData) {
|
|||
list.PushArray(input);
|
||||
}
|
||||
|
||||
void loadChoiceEntity(ArrayList list, JSON_Object entityData) {
|
||||
void loadChoiceEntity(ArrayList list, JSONObject entityData) {
|
||||
VariantEntityData entity;
|
||||
entityData.GetString("model", entity.model, sizeof(entity.model));
|
||||
if(!entityData.GetString("type", entity.type, sizeof(entity.type))) {
|
||||
|
@ -569,18 +947,20 @@ void loadChoiceEntity(ArrayList list, JSON_Object entityData) {
|
|||
list.PushArray(entity);
|
||||
}
|
||||
|
||||
void GetVector(JSON_Object obj, const char[] key, float out[3]) {
|
||||
JSON_Array vecArray = view_as<JSON_Array>(obj.GetObject(key));
|
||||
bool GetVector(JSONObject obj, const char[] key, float out[3]) {
|
||||
if(!obj.HasKey(key)) return false;
|
||||
JSONArray vecArray = view_as<JSONArray>(obj.Get(key));
|
||||
if(vecArray != null) {
|
||||
out[0] = vecArray.GetFloat(0);
|
||||
out[1] = vecArray.GetFloat(1);
|
||||
out[2] = vecArray.GetFloat(2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GetColor(JSON_Object obj, const char[] key, int out[4]) {
|
||||
JSON_Array vecArray = view_as<JSON_Array>(obj.GetObject(key));
|
||||
if(vecArray != null) {
|
||||
void GetColor(JSONObject obj, const char[] key, int out[4], int defaultColor[4] = { 255, 255, 255, 255 }) {
|
||||
if(obj.HasKey(key)) {
|
||||
JSONArray vecArray = view_as<JSONArray>(obj.Get(key));
|
||||
out[0] = vecArray.GetInt(0);
|
||||
out[1] = vecArray.GetInt(1);
|
||||
out[2] = vecArray.GetInt(2);
|
||||
|
@ -589,10 +969,7 @@ void GetColor(JSON_Object obj, const char[] key, int out[4]) {
|
|||
else
|
||||
out[3] = 255;
|
||||
} else {
|
||||
out[0] = 255;
|
||||
out[1] = 255;
|
||||
out[2] = 255;
|
||||
out[3] = 255;
|
||||
out = defaultColor;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -600,6 +977,8 @@ void selectScenes(int flags = 0) {
|
|||
SceneData scene;
|
||||
StringMap groups = new StringMap();
|
||||
ArrayList list;
|
||||
// Select and spawn non-group scenes
|
||||
// TODO: refactor to use .scenesKv
|
||||
for(int i = 0; i < g_MapData.scenes.Length; i++) {
|
||||
g_MapData.scenes.GetArray(i, scene);
|
||||
// TODO: Exclusions
|
||||
|
@ -607,6 +986,7 @@ void selectScenes(int flags = 0) {
|
|||
if(scene.group[0] == '\0') {
|
||||
selectScene(scene, flags);
|
||||
} else {
|
||||
// Load it into group list
|
||||
if(!groups.GetValue(scene.group, list)) {
|
||||
list = new ArrayList();
|
||||
}
|
||||
|
@ -615,25 +995,61 @@ void selectScenes(int flags = 0) {
|
|||
}
|
||||
}
|
||||
|
||||
// Iterate through groups and select a random scene:
|
||||
StringMapSnapshot snapshot = groups.Snapshot();
|
||||
char key[MAX_SCENE_NAME_LENGTH];
|
||||
for(int i = 0; i < snapshot.Length; i++) {
|
||||
snapshot.GetKey(i, key, sizeof(key));
|
||||
groups.GetValue(key, list);
|
||||
// Select a random scene from the group:
|
||||
int index = GetURandomInt() % list.Length;
|
||||
index = list.Get(index);
|
||||
g_MapData.scenes.GetArray(index, scene);
|
||||
|
||||
Debug("Selected scene \"%s\" for group %s (%d members)", scene.name, key, list.Length);
|
||||
selectScene(scene, flags);
|
||||
delete list;
|
||||
}
|
||||
// Traverse active scenes, loading any other scene it requires (via .force_scenes)
|
||||
ActiveSceneData aScene;
|
||||
SceneVariantData choice;
|
||||
ArrayList forcedScenes = new ArrayList(ByteCountToCells(MAX_SCENE_NAME_LENGTH));
|
||||
for(int i = 0; i < g_MapData.activeScenes.Length; i++) {
|
||||
g_MapData.activeScenes.GetArray(i, aScene);
|
||||
g_MapData.scenes.GetArray(i, scene);
|
||||
scene.variants.GetArray(aScene.variantIndex, choice);
|
||||
if(choice.forcedScenes != null) {
|
||||
for(int j = 0; j < choice.forcedScenes.Length; j++) {
|
||||
choice.forcedScenes.GetString(j, key, sizeof(key));
|
||||
forcedScenes.PushString(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Iterate and activate any forced scenes
|
||||
for(int i = 0; i < forcedScenes.Length; i++) {
|
||||
forcedScenes.GetString(i, key, sizeof(key));
|
||||
// Check if scene was already loaded
|
||||
bool isSceneAlreadyLoaded = false;
|
||||
for(int j = 0; j < g_MapData.activeScenes.Length; i++) {
|
||||
g_MapData.activeScenes.GetArray(j, aScene);
|
||||
if(StrEqual(aScene.name, key)) {
|
||||
isSceneAlreadyLoaded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(isSceneAlreadyLoaded) continue;
|
||||
g_MapData.scenesKv.GetArray(key, scene, sizeof(scene));
|
||||
selectScene(scene, flags | view_as<int>(FLAG_FORCE_ACTIVE));
|
||||
}
|
||||
|
||||
delete forcedScenes;
|
||||
delete snapshot;
|
||||
delete groups;
|
||||
}
|
||||
|
||||
void selectScene(SceneData scene, int flags) {
|
||||
// Use the .chance field unless FLAG_ALL_SCENES is set
|
||||
if(~flags & view_as<int>(FLAG_ALL_SCENES) && GetURandomFloat() > scene.chance) {
|
||||
// Use the .chance field unless FLAG_ALL_SCENES or FLAG_FORCE_ACTIVE is set
|
||||
if(~flags & view_as<int>(FLAG_ALL_SCENES) && ~flags & view_as<int>(FLAG_FORCE_ACTIVE) && GetURandomFloat() > scene.chance) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -645,20 +1061,26 @@ void selectScene(SceneData scene, int flags) {
|
|||
ArrayList choices = new ArrayList();
|
||||
SceneVariantData choice;
|
||||
int index;
|
||||
Debug("Scene %s has %d variants", scene.name, scene.variants.Length);
|
||||
// Weighted random: Push N times dependent on weight
|
||||
for(int i = 0; i < scene.variants.Length; i++) {
|
||||
scene.variants.GetArray(i, choice);
|
||||
if(flags & view_as<int>(FLAG_ALL_VARIANTS)) {
|
||||
spawnVariant(choice);
|
||||
} else {
|
||||
if(choice.weight <= 0) {
|
||||
PrintToServer("Warn: Variant %d in scene %s has invalid weight", i, scene.name);
|
||||
continue;
|
||||
}
|
||||
for(int c = 0; c < choice.weight; c++) {
|
||||
choices.Push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
Debug("Total choices: %d", choices.Length);
|
||||
if(flags & view_as<int>(FLAG_ALL_VARIANTS)) {
|
||||
delete choices;
|
||||
} else {
|
||||
} else if(choices.Length > 0) {
|
||||
index = GetURandomInt() % choices.Length;
|
||||
index = choices.Get(index);
|
||||
delete choices;
|
||||
|
@ -666,7 +1088,6 @@ void selectScene(SceneData scene, int flags) {
|
|||
scene.variants.GetArray(index, choice);
|
||||
spawnVariant(choice);
|
||||
}
|
||||
|
||||
ActiveSceneData aScene;
|
||||
strcopy(aScene.name, sizeof(aScene.name), scene.name);
|
||||
aScene.variantIndex = index;
|
||||
|
|
|
@ -20,6 +20,9 @@ int connectAttempts;
|
|||
authState g_authState;
|
||||
JSONObject g_globalVars;
|
||||
|
||||
StringMap actionFallbackHandlers; // namespace -> action name has no handler, falls to this.
|
||||
StringMap actionNamespaceHandlers; // { namespace: { [action name] -> handler } }
|
||||
|
||||
enum authState {
|
||||
AuthError = -1,
|
||||
NotAuthorized,
|
||||
|
@ -53,8 +56,16 @@ char OUT_EVENT_IDS[view_as<int>(Event_Invalid)][] = {
|
|||
char steamidCache[MAXPLAYERS+1][32];
|
||||
|
||||
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) {
|
||||
CreateNative("UIElement.Send", Native_UpdateUI);
|
||||
CreateNative("TempUI.Send", Native_UpdateTempUI);
|
||||
RegPluginLibrary("overlay");
|
||||
|
||||
CreateNative("IsOverlayConnected", Native_IsOverlayConnected);
|
||||
CreateNative("RegisterActionAnyHandler", Native_ActionHandler);
|
||||
CreateNative("RegisterActionHandler", Native_ActionHandler);
|
||||
|
||||
CreateNative("UIElement.SendAll", Native_UpdateUI);
|
||||
CreateNative("UIElement.SendTo", Native_UpdateUI);
|
||||
CreateNative("TempUI.SendAll", Native_UpdateTempUI);
|
||||
CreateNative("TempUI.SendTo", Native_UpdateTempUI);
|
||||
return APLRes_Success;
|
||||
}
|
||||
|
||||
|
@ -64,6 +75,9 @@ public void OnPluginStart() {
|
|||
SetFailState("This plugin is for L4D/L4D2 only.");
|
||||
}
|
||||
|
||||
actionFallbackHandlers = new StringMap();
|
||||
actionNamespaceHandlers = new StringMap();
|
||||
|
||||
g_globalVars = new JSONObject();
|
||||
|
||||
cvarManagerUrl = CreateConVar("sm_overlay_manager_url", "ws://localhost:3011/socket", "");
|
||||
|
@ -171,6 +185,7 @@ void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast)
|
|||
if(GetClientCount(false) == 0) {
|
||||
DisconnectManager();
|
||||
}
|
||||
steamidCache[client][0] = '\0';
|
||||
}
|
||||
|
||||
void OnWSConnect(WebSocket ws, any arg) {
|
||||
|
@ -210,12 +225,78 @@ void OnWSJson(WebSocket ws, JSON message, any data) {
|
|||
message.ToString(buffer, sizeof(buffer));
|
||||
PrintToServer("[Overlay] Error: %s", buffer);
|
||||
} else {
|
||||
char type[32];
|
||||
obj.GetString("type", type, sizeof(type));
|
||||
if(StrEqual(type, "action")) {
|
||||
OnAction(obj);
|
||||
}
|
||||
|
||||
char buffer[2048];
|
||||
message.ToString(buffer, sizeof(buffer));
|
||||
PrintToServer("[Overlay] Got JSON: %s", buffer);
|
||||
}
|
||||
}
|
||||
|
||||
stock int ExplodeStringToArrayList(const char[] text, const char[] split, ArrayList buffers, int maxStringLength) {
|
||||
int reloc_idx, idx, total;
|
||||
|
||||
if (buffers == null || !split[0]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char[] item = new char[maxStringLength];
|
||||
while ((idx = SplitString(text[reloc_idx], split, item, maxStringLength)) != -1) {
|
||||
reloc_idx += idx;
|
||||
++total;
|
||||
buffers.PushString(item);
|
||||
}
|
||||
++total;
|
||||
buffers.PushString(text[reloc_idx]);
|
||||
|
||||
return buffers.Length;
|
||||
}
|
||||
|
||||
void OnAction(JSONObject obj) {
|
||||
char steamid[32];
|
||||
obj.GetString("steamid", steamid, sizeof(steamid));
|
||||
char ns[64];
|
||||
obj.GetString("namespace", ns, sizeof(ns));
|
||||
char id[64];
|
||||
obj.GetString("elem_id", id, sizeof(id));
|
||||
char action[256];
|
||||
obj.GetString("action", action, sizeof(action));
|
||||
|
||||
int client = FindClientBySteamId2(steamid);
|
||||
|
||||
StringMap nsHandler;
|
||||
PrivateForward fwd;
|
||||
if(!actionNamespaceHandlers.GetValue(ns, nsHandler) || !nsHandler.GetValue(id, fwd)) {
|
||||
if(!actionFallbackHandlers.GetValue(ns, fwd)) {
|
||||
// No handler or catch all namespace handler
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList args = new ArrayList(ACTION_ARG_LENGTH);
|
||||
ExplodeStringToArrayList(action, " ", args, ACTION_ARG_LENGTH);
|
||||
UIActionEvent event = UIActionEvent(args);
|
||||
|
||||
Call_StartForward(fwd);
|
||||
Call_PushCell(event);
|
||||
Call_PushCell(client);
|
||||
Call_Finish();
|
||||
event._Delete();
|
||||
}
|
||||
|
||||
int FindClientBySteamId2(const char[] steamid) {
|
||||
for(int i = 1; i <= MaxClients; i++) {
|
||||
if(StrEqual(steamidCache[i], steamid)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void ConnectManager() {
|
||||
DisconnectManager();
|
||||
|
@ -281,11 +362,15 @@ void SendEvent_UpdateUI(const char[] elemNamespace, const char[] elemId, bool vi
|
|||
}
|
||||
|
||||
//SendTempUI(int client, const char[] id, int lifetime, JSONObject element);
|
||||
bool Native_SendTempUI(Handle plugin, int numParams) {
|
||||
any Native_SendTempUI(Handle plugin, int numParams) {
|
||||
if(!isManagerReady()) return false;
|
||||
|
||||
int client = GetNativeCell(1);
|
||||
if(steamidCache[client][0] == '\0') return false;
|
||||
if (client <= 0 || client > MaxClients) {
|
||||
return ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d)", client);
|
||||
} else if (!IsClientConnected(client) || steamidCache[client][0] == '\0') {
|
||||
return ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not connected/authorized yet", client);
|
||||
}
|
||||
|
||||
char id[64];
|
||||
GetNativeString(2, id, sizeof(id));
|
||||
|
@ -299,7 +384,7 @@ bool Native_SendTempUI(Handle plugin, int numParams) {
|
|||
}
|
||||
|
||||
//ShowUI(int client, const char[] elemNamespace, const char[] elemId, JSONObject variables);
|
||||
bool Native_ShowUI(Handle plugin, int numParams) {
|
||||
any Native_ShowUI(Handle plugin, int numParams) {
|
||||
if(!isManagerReady()) return false;
|
||||
|
||||
char elemNamespace[64], id[64];
|
||||
|
@ -313,7 +398,7 @@ bool Native_ShowUI(Handle plugin, int numParams) {
|
|||
}
|
||||
|
||||
//HideUI(int client, const char[] elemNamespace, const char[] elemId);
|
||||
bool Native_HideUI(Handle plugin, int numParams) {
|
||||
any Native_HideUI(Handle plugin, int numParams) {
|
||||
if(!isManagerReady()) return false;
|
||||
|
||||
char elemNamespace[64], id[64];
|
||||
|
@ -325,7 +410,7 @@ bool Native_HideUI(Handle plugin, int numParams) {
|
|||
}
|
||||
|
||||
//PlayAudio(int client, const char[] url);
|
||||
bool Native_PlayAudio(Handle plugin, int numParams) {
|
||||
any Native_PlayAudio(Handle plugin, int numParams) {
|
||||
if(!isManagerReady()) return false;
|
||||
|
||||
char url[256];
|
||||
|
@ -335,27 +420,92 @@ bool Native_PlayAudio(Handle plugin, int numParams) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Native_UpdateUI(Handle plugin, int numParams) {
|
||||
any Native_UpdateUI(Handle plugin, int numParams) {
|
||||
if(!isManagerReady()) return false;
|
||||
|
||||
UIElement elem = GetNativeCell(1);
|
||||
JSONObject obj = view_as<JSONObject>(elem);
|
||||
|
||||
JSONArray arr = view_as<JSONArray>(obj.Get("steamids"));
|
||||
if(numParams == 0) {
|
||||
arr.Clear();
|
||||
} else if(numParams == 1) {
|
||||
int client = GetNativeCell(2);
|
||||
if (client <= 0 || client > MaxClients) {
|
||||
return ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d)", client);
|
||||
} else if (!IsClientConnected(client) || steamidCache[client][0] == '\0') {
|
||||
return ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not connected/authorized yet", client);
|
||||
}
|
||||
arr.PushString(steamidCache[client]);
|
||||
}
|
||||
|
||||
g_ws.Write(view_as<JSON>(elem));
|
||||
arr.Clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Native_UpdateTempUI(Handle plugin, int numParams) {
|
||||
any Native_UpdateTempUI(Handle plugin, int numParams) {
|
||||
if(!isManagerReady()) return false;
|
||||
|
||||
TempUI elem = GetNativeCell(1);
|
||||
JSONObject obj = view_as<JSONObject>(elem);
|
||||
|
||||
JSONArray arr = view_as<JSONArray>(obj.Get("steamids"));
|
||||
if(numParams == 0) {
|
||||
arr.Clear();
|
||||
} else if(numParams == 1) {
|
||||
int client = GetNativeCell(2);
|
||||
if (client <= 0 || client > MaxClients) {
|
||||
return ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d)", client);
|
||||
} else if (!IsClientConnected(client) || steamidCache[client][0] == '\0') {
|
||||
return ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not connected/authorized yet", client);
|
||||
}
|
||||
arr.PushString(steamidCache[client]);
|
||||
}
|
||||
|
||||
g_ws.Write(view_as<JSON>(elem));
|
||||
arr.Clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//IsOverlayConnected();
|
||||
bool Native_IsOverlayConnected(Handle plugin, int numParams) {
|
||||
any Native_IsOverlayConnected(Handle plugin, int numParams) {
|
||||
return isManagerReady();
|
||||
}
|
||||
|
||||
//RegisterActionHandler
|
||||
//RegisterActionAnyHandler
|
||||
any Native_ActionHandler(Handle plugin, int numParams) {
|
||||
char ns[64];
|
||||
GetNativeString(1, ns, sizeof(ns));
|
||||
|
||||
if(numParams == 3) {
|
||||
// RegisterActionHandler
|
||||
StringMap nsHandlers;
|
||||
if(!actionNamespaceHandlers.GetValue(ns, nsHandlers)) {
|
||||
nsHandlers = new StringMap();
|
||||
}
|
||||
|
||||
char actionId[64];
|
||||
GetNativeString(2, actionId, sizeof(actionId));
|
||||
|
||||
PrivateForward fwd;
|
||||
if(!nsHandlers.GetValue(actionId, fwd)) {
|
||||
fwd = new PrivateForward(ET_Ignore, Param_Cell);
|
||||
}
|
||||
fwd.AddFunction(INVALID_HANDLE, GetNativeFunction(3));
|
||||
nsHandlers.SetValue(actionId, fwd);
|
||||
} else {
|
||||
// RegisterActionAnyHandler
|
||||
|
||||
PrivateForward fwd;
|
||||
if(!actionFallbackHandlers.GetValue(ns, fwd)) {
|
||||
fwd = new PrivateForward(ET_Ignore, Param_Cell);
|
||||
}
|
||||
fwd.AddFunction(INVALID_HANDLE, GetNativeFunction(2));
|
||||
actionFallbackHandlers.SetValue(ns, fwd);
|
||||
}
|
||||
return 1;
|
||||
}
|
|
@ -242,15 +242,12 @@ public Action OnClientSayCommand(int client, const char[] command, const char[]
|
|||
PrintToChat(client, "Note cancelled.");
|
||||
} else {
|
||||
int size = 2 * strlen(sArgs) + 1;
|
||||
char[] sArgsTrimmed = new char[size];
|
||||
DB.Escape(sArgs, sArgsTrimmed, size);
|
||||
TrimString(sArgsTrimmed);
|
||||
char buffer[32];
|
||||
GetClientAuthId(client, AuthId_Steam2, buffer, sizeof(buffer));
|
||||
// TODO: escape content
|
||||
DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", menuNoteTarget, buffer, sArgsTrimmed);
|
||||
DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", menuNoteTarget, buffer, sArgs);
|
||||
DB.Query(DB_AddNote, query);
|
||||
LogAction(client, -1, "added note for \"%s\" (%s): \"%s\"", client, menuNoteTargetName, menuNoteTarget, sArgsTrimmed);
|
||||
LogAction(client, -1, "added note for \"%s\" (%s): \"%s\"", client, menuNoteTargetName, menuNoteTarget, sArgs);
|
||||
Format(buffer, sizeof(buffer), "%N: ", client);
|
||||
CShowActivity2(client, buffer, "added a note for {green}%s: {default}\"%s\"", menuNoteTargetName, sArgs);
|
||||
}
|
||||
|
@ -310,10 +307,7 @@ public Action Command_AddNote(int client, int args) {
|
|||
char authMarker[32];
|
||||
if(client > 0)
|
||||
GetClientAuthId(client, AuthId_Steam2, authMarker, sizeof(authMarker));
|
||||
int size = 2 * strlen(reason) + 1;
|
||||
char[] content = new char[size];
|
||||
DB.Escape(reason, content, size);
|
||||
DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", auth, authMarker, content);
|
||||
DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", auth, authMarker, reason);
|
||||
DB.Query(DB_AddNote, query);
|
||||
LogAction(client, target_list[0], "\"%L\" added note for \"%L\": \"%s\"", client, target_list[0], reason);
|
||||
CShowActivity(client, "added a note for {green}%N: {default}\"%s\"", target_list[0], reason);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue