const float MAX_STORAGE_ATTACHMENTS = 16;
float object_count;
+.float object_flood;
.entity object_attach;
.string material;
pointparticles(particleeffectnum(strcat("impact_", self.material)), self.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10
}
+void sandbox_ObjectFunction_Think()
+{
+ entity e;
+
+ // decide if and how this object can be grabbed
+ if(autocvar_g_sandbox_readonly)
+ self.grab = 0; // no grabbing
+ else if(autocvar_g_sandbox_editor_free < 2 && self.crypto_idfp)
+ self.grab = 1; // owner only
+ else
+ self.grab = 3; // anyone
+
+ // Object owner is stored via player UID, but we also need the owner as an entity (if the player is available on the server).
+ // Therefore, scan for all players, and update the owner as long as the player is present. We must always do this,
+ // since if the owning player disconnects, the object's owner should also be reset.
+ FOR_EACH_REALPLAYER(e) // bots can't have objects
+ {
+ if(self.crypto_idfp == e.crypto_idfp)
+ {
+ self.realowner = e;
+ break;
+ }
+ self.realowner = world;
+ }
+
+ self.nextthink = time;
+}
+
entity sandbox_ObjectEdit_Get(float permissions)
{
// returns the traced entity if the player can edit it, and world if not
return trace_ent; // don't check permissions, anyone can edit this object
if(!trace_ent.crypto_idfp)
return trace_ent; // the player who spawned this object did not have an UID, so anyone can edit it
- if not(trace_ent.crypto_idfp != self.crypto_idfp && autocvar_g_sandbox_editor_free < 2)
+ if not(trace_ent.realowner != self && autocvar_g_sandbox_editor_free < 2)
return trace_ent; // object does not belong to the player, and players can only edit their own objects on this server
return world;
}
e.skin = 0;
e.material = string_null;
e.touch = sandbox_ObjectFunction_Touch;
+ e.think = sandbox_ObjectFunction_Think;
+ e.nextthink = time;
//e.effects |= EF_SELECTABLE; // don't do this all the time, maybe just when editing objects?
if(!database)
// set public object information
e.netname = strzone(self.netname); // name of the owner
- e.message = e.message2 = strzone(strftime(TRUE, "%d-%m-%Y %H:%M:%S")); // creation and edit time
+ e.message = strzone(strftime(TRUE, "%d-%m-%Y %H:%M:%S")); // creation time
+ e.message2 = strzone(strftime(TRUE, "%d-%m-%Y %H:%M:%S")); // last editing time
// set origin and direction based on player position and view angle
makevectors(self.v_angle);
return FALSE;
if(cmd_name == "g_sandbox")
{
+ if(autocvar_g_sandbox_readonly)
+ {
+ print_to(self, "^2SANDBOX - INFO: ^7Sandbox mode is active, but in read-only mode. Sandbox commands cannot be used");
+ return TRUE;
+ }
if(cmd_argc < 2)
{
- print_to(self, "Sandbox mode is active. For usage information, type 'sandbox help'");
+ print_to(self, "^2SANDBOX - INFO: ^7Sandbox mode is active. For usage information, type 'sandbox help'");
return TRUE;
}
print_to(self, "^7\"^2object_spawn ^3models/foo/bar.md3^7\" spawns a new object in front of the player, and gives it the specified model");
print_to(self, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects");
print_to(self, "^7\"^2object_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original");
- print_to(self, "^Properties for ^2object_duplicate^7:");
print_to(self, "^3copy value ^7- copies the properties of the object to the specified client cvar");
print_to(self, "^3paste value ^7- spawns an object with the given properties. Properties or cvars must be specified as follows; eg1: \"0 1 2 ...\", eg2: \"$cl_cvar\"");
print_to(self, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects");
- print_to(self, "^7Properties for ^2object_attach^7:");
print_to(self, "^3get ^7- selects the object you are facing as the object to be attached");
print_to(self, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone");
print_to(self, "^3remove ^7- detaches all objects from the object you are facing");
print_to(self, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects");
- print_to(self, "^7Properties for ^2object_edit^7:");
print_to(self, "^3skin value ^7- changes the skin of the object");
print_to(self, "^3alpha value ^7- sets object transparency");
print_to(self, "^3colormod \"value_x value_y value_z\" ^7- main object color");
print_to(self, "^3force value ^7- amount of force applied to objects that are shot");
print_to(self, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh");
print_to(self, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it");
- print_to(self, "^7\"^2object_info ^3value^7\" shows public information about the object. 'mesh' shows model information");
+ print_to(self, "^7\"^2object_info ^3value^7\" shows public information about the object");
+ print_to(self, "^3object ^7- prints general information about the object, such as owner and creation / editing date");
+ print_to(self, "^3mesh ^7- prints information about the object's mesh, including skeletal bones");
+ print_to(self, "^3attachments ^7- prints information about the object's attachments");
print_to(self, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects");
return TRUE;
// ---------------- COMMAND: OBJECT, SPAWN ----------------
case "object_spawn":
+ if(time < self.object_flood)
+ {
+ print_to(self, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(self.object_flood - time), " ^7seconds beofore spawning another object"));
+ return TRUE;
+ }
+ self.object_flood = time + autocvar_g_sandbox_editor_flood;
if(object_count >= autocvar_g_sandbox_editor_maxobjects)
{
print_to(self, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time"));
print_to(self, "^1SANDBOX - WARNING: ^7Attempted to spawn an object without specifying a model. Please specify the path to your model file after the 'object_spawn' command");
return TRUE;
}
- else if not(fexists(argv(2)))
+ if not(fexists(argv(2)))
{
print_to(self, "^1SANDBOX - WARNING: ^7Attempted to spawn an object with a non-existent model. Make sure the path to your model file is correct");
return TRUE;
case "paste":
// spawns a new object using the properties in the player's clipboard cvar
+ if(time < self.object_flood)
+ {
+ print_to(self, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(self.object_flood - time), " ^7seconds beofore spawning another object"));
+ return TRUE;
+ }
+ self.object_flood = time + autocvar_g_sandbox_editor_flood;
if(!argv(3)) // no object in clipboard
{
print_to(self, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it");
return TRUE;
}
- // update last edit time
+ // update last editing time
if(e.message2) strunzone(e.message2);
e.message2 = strzone(strftime(TRUE, "%d-%m-%Y %H:%M:%S"));
{
switch(argv(2))
{
+ case "object":
+ print_to(self, strcat("^2SANDBOX - INFO: ^7Object is owned by \"^7", e.netname, "^7\", created \"^3", e.message, "^7\", last edited \"^3", e.message2, "^7\""));
+ return TRUE;
case "mesh":
- string tags;
for(i = 1; gettaginfo(e, i); i++)
- tags = strcat(tags, "^5", gettaginfo_name, "^7, ");
- print_to(self, strcat("^2SANDBOX - INFO: ^7Object mesh is ^3", e.model, "^7 at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", tags));
+ s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", ");
+ print_to(self, strcat("^2SANDBOX - INFO: ^7Object mesh is \"^3", e.model, "^7\" at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", s));
return TRUE;
- case "object":
- print_to(self, strcat("^2SANDBOX - INFO: ^7Object is owned by ^7", e.netname, "^7, created ^3", e.message, "^7, last edited ^3", e.message2));
+ case "attachments":
+ // this should show the same info as 'mesh' but for attachments
+ entity head;
+ for(head = world; (head = find(head, classname, "object")); )
+ {
+ if(head.owner == e)
+ {
+ ++i; // start from 1
+ gettaginfo(e, head.tag_index);
+ s = strcat(s, "^1attachment ", ftos(i), "^7 has mesh \"^3", head.model, "^7\" at animation frame ^3", ftos(head.frame));
+ s = strcat(s, "^7 and is attached to bone \"^5", gettaginfo_name, "^7\", ");
+ }
+ }
+ if(i) // object contains attachments
+ print_to(self, strcat("^2SANDBOX - INFO: ^7Object contains the following ^1", ftos(i), "^7 attachment(s): ", s));
+ else
+ print_to(self, "^2SANDBOX - INFO: ^7Object contains no attachments");
return TRUE;
}
}
- print_to(self, "^1SANDBOX - WARNING: ^7No information could not be found. Make sure you are facing an object");
+ print_to(self, "^1SANDBOX - WARNING: ^7No information could be found. Make sure you are facing an object");
return TRUE;
// ---------------- COMMAND: DEFAULT ----------------
return FALSE;
}
-MUTATOR_HOOKFUNCTION(sandbox_PlayerPreThink)
-{
- // if the player is close enough to their object, they can drag it
-
- if(autocvar_sv_cheats)
- return FALSE; // cheat dragging is used instead
-
- // grab is TRUE if the object can be picked up. While an object is being carried, the Drag() function
- // must execute for it either way, otherwise it would cause bugs if it went out of the player's trace.
- // This also makes sure that an object can only pe picked up if in range, but does not get dropped if
- // it goes out of range while slinging it around.
-
- entity e;
- float grab;
-
- e = sandbox_ObjectEdit_Get(TRUE);
- if(e != world && vlen(e.origin - self.origin) <= autocvar_g_sandbox_editor_distance_edit)
- grab = TRUE;
-
- if(Drag(e, grab)) // execute dragging
- return TRUE;
-
- return FALSE;
-}
-
float autosave_time;
MUTATOR_HOOKFUNCTION(sandbox_StartFrame)
{
{
MUTATOR_HOOK(SV_ParseClientCommand, sandbox_PlayerCommand, CBC_ORDER_ANY);
MUTATOR_HOOK(SV_StartFrame, sandbox_StartFrame, CBC_ORDER_ANY);
- MUTATOR_HOOK(PlayerPreThink, sandbox_PlayerPreThink, CBC_ORDER_ANY);
MUTATOR_ONADD
{