Xonotic
sv_sandbox.qc
Go to the documentation of this file.
1 #include "sv_sandbox.qh"
2 
3 #include <server/intermission.qh>
4 
20 
24 
26 {
28  {
29  g_sandbox_objects = IL_NEW();
30  autosave_time = time + autocvar_g_sandbox_storage_autosave; // don't save the first server frame
33  }
34 }
35 
36 const float MAX_STORAGE_ATTACHMENTS = 16;
37 float object_count;
38 .float object_flood;
39 .entity object_attach;
40 .string material;
41 
42 .float touch_timer;
44 {
45  // apply material impact effects
46 
47  if(!this.material)
48  return;
49  if(this.touch_timer > time)
50  return; // don't execute each frame
51  this.touch_timer = time + 0.1;
52 
53  // make particle count and sound volume depend on impact speed
54  float intensity;
55  intensity = vlen(this.velocity) + vlen(toucher.velocity);
56  if(intensity) // avoid divisions by 0
57  intensity /= 2; // average the two velocities
59  return; // impact not strong enough to do anything
60  // now offset intensity and apply it to the effects
61  intensity -= autocvar_g_sandbox_object_material_velocity_min; // start from minimum velocity, not actual velocity
62  intensity = bound(0, intensity * autocvar_g_sandbox_object_material_velocity_factor, 1);
63 
64  _sound(this, CH_TRIGGER, strcat("object/impact_", this.material, "_", ftos(ceil(random() * 5)) , ".wav"), VOL_BASE * intensity, ATTEN_NORM);
65  Send_Effect_(strcat("impact_", this.material), this.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10
66 }
67 
69 {
70  // decide if and how this object can be grabbed
72  this.grab = 0; // no grabbing
73  else if(autocvar_g_sandbox_editor_free < 2 && this.crypto_idfp)
74  this.grab = 1; // owner only
75  else
76  this.grab = 3; // anyone
77 
78  // Object owner is stored via player UID, but we also need the owner as an entity (if the player is available on the server).
79  // Therefore, scan for all players, and update the owner as long as the player is present. We must always do this,
80  // since if the owning player disconnects, the object's owner should also be reset.
81 
82  // bots can't have objects
84  if(this.crypto_idfp == it.crypto_idfp)
85  {
86  this.realowner = it;
87  break;
88  }
89  this.realowner = NULL;
90  });
91 
92  this.nextthink = time;
93 
94  CSQCMODEL_AUTOUPDATE(this);
95 }
96 
98 entity sandbox_ObjectEdit_Get(entity this, float permissions)
99 {
100  // Returns the traced entity if the player can edit it, and NULL if not.
101  // If permissions if false, the object is returned regardless of editing rights.
102  // Attached objects are SOLID_NOT and do not get traced.
103 
106  return NULL; // out of trace range
107  if(trace_ent.classname != "object")
108  return NULL; // entity is not an object
109  if(!permissions)
110  return trace_ent; // don't check permissions, anyone can edit this object
111  if(trace_ent.crypto_idfp == "")
112  return trace_ent; // the player who spawned this object did not have an UID, so anyone can edit it
113  if (!(trace_ent.realowner != this && autocvar_g_sandbox_editor_free < 2))
114  return trace_ent; // object does not belong to the player, and players can only edit their own objects on this server
115  return NULL;
116 }
117 
119 {
120  e.scale = f;
121  if(e.scale)
122  {
124  _setmodel(e, e.model); // reset mins and maxs based on mesh
125  setsize(e, e.mins * e.scale, e.maxs * e.scale); // adapt bounding box size to model size
126  }
127 }
128 
131 {
132  // attaches e to parent on string s
133 
134  // we can't attach to an attachment, for obvious reasons
136 
137  e.old_solid = e.solid; // persist solidity
138  e.old_movetype = e.move_movetype; // persist physics
140  e.solid = SOLID_NOT;
141  e.takedamage = DAMAGE_NO;
142 
143  setattachment(e, parent, s);
144  e.owner = parent;
145 }
146 
148 {
149  // detaches any object attached to e
150 
151  IL_EACH(g_sandbox_objects, it.owner == e,
152  {
153  vector org = gettaginfo(it, 0);
154  setattachment(it, NULL, "");
155  it.owner = NULL;
156 
157  // objects change origin and angles when detached, so apply previous position
158  setorigin(it, org);
159  it.angles = e.angles; // don't allow detached objects to spin or roll
160 
161  it.solid = it.old_solid; // restore persisted solidity
162  set_movetype(it, it.old_movetype); // restore persisted physics
163  it.takedamage = DAMAGE_AIM;
164  });
165 }
166 
167 entity sandbox_ObjectSpawn(entity this, float database)
168 {
169  // spawn a new object with default properties
170 
171  entity e = new(object);
172  IL_PUSH(g_sandbox_objects, e);
173  e.takedamage = DAMAGE_AIM;
174  e.damageforcescale = 1;
175  e.solid = SOLID_BBOX; // SOLID_BSP would be best, but can lag the server badly
177  e.frame = 0;
178  e.skin = 0;
179  e.material = string_null;
180  settouch(e, sandbox_ObjectFunction_Touch);
182  e.nextthink = time;
183  //e.effects |= EF_SELECTABLE; // don't do this all the time, maybe just when editing objects?
184 
185  if(!database)
186  {
187  // set the object's owner via player UID
188  // if the player does not have an UID, the owner cannot be stored and his objects may be edited by anyone
189  if(this.crypto_idfp != "")
190  e.crypto_idfp = strzone(this.crypto_idfp);
191  else
192  print_to(this, "^1SANDBOX - WARNING: ^7You spawned an object, but lack a player UID. ^1Your objects are not secured and can be edited by any player!");
193 
194  // set public object information
195  e.netname = strzone(this.netname); // name of the owner
196  e.message = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // creation time
197  e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // last editing time
198 
199  // set origin and direction based on player position and view angle
200  makevectors(this.v_angle);
203  e.angles_y = this.v_angle.y;
204  }
205 
206  CSQCMODEL_AUTOINIT(e);
207 
208  object_count += 1;
209  return e;
210 }
211 
213 {
214  sandbox_ObjectAttach_Remove(e); // detach child objects
215 
216  // if the object being removed has been selected for attachment by a player, unset it
217  FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it.object_attach == e, { it.object_attach = NULL; });
218 
219  strfree(e.material);
220  strfree(e.crypto_idfp);
221  strfree(e.netname);
222  strfree(e.message);
223  strfree(e.message2);
224  delete(e);
225  e = NULL;
226 
227  object_count -= 1;
228 }
229 
230 string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't defined as a global
231 
232 string sandbox_ObjectPort_Save(entity e, bool database)
233 {
234  // save object properties, and return them as a string
235  int o = 0;
236 
237  // order doesn't really matter, as we're writing the file fresh
238  IL_EACH(g_sandbox_objects, it == e || it.owner == e, LAMBDA(
239  // the main object needs to be first in the array [0] with attached objects following
240  int slot, physics, solidity;
241  if(it == e) // this is the main object, place it first
242  {
243  slot = 0;
244  solidity = it.solid; // applied solidity is normal solidity for children
245  physics = it.move_movetype; // applied physics are normal physics for parents
246  }
247  else if(it.owner == e) // child object, list them in order
248  {
249  o += 1; // children start from 1
250  slot = o;
251  solidity = it.old_solid; // persisted solidity is normal solidity for children
252  physics = it.old_movetype; // persisted physics are normal physics for children
253  gettaginfo(it.owner, it.tag_index); // get the name of the tag our object is attached to, used further below
254  }
255  else
256  continue;
257 
258  // ---------------- OBJECT PROPERTY STORAGE: SAVE ----------------
259  if(slot)
260  {
261  // properties stored only for child objects
262  if(gettaginfo_name)
263  port_string[slot] = strcat(port_string[slot], "\"", gettaginfo_name, "\" ");
264  else
265  port_string[slot] = strcat(port_string[slot], "\"\" "); // none
266  }
267  else
268  {
269  // properties stored only for parent objects
270  if(database)
271  {
272  port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", it.origin), " ");
273  port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", it.angles), " ");
274  }
275  }
276  // properties stored for all objects
277  port_string[slot] = strcat(port_string[slot], "\"", it.model, "\" ");
278  port_string[slot] = strcat(port_string[slot], ftos(it.skin), " ");
279  port_string[slot] = strcat(port_string[slot], ftos(it.alpha), " ");
280  port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", it.colormod), " ");
281  port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", it.glowmod), " ");
282  port_string[slot] = strcat(port_string[slot], ftos(it.frame), " ");
283  port_string[slot] = strcat(port_string[slot], ftos(it.scale), " ");
284  port_string[slot] = strcat(port_string[slot], ftos(solidity), " ");
285  port_string[slot] = strcat(port_string[slot], ftos(physics), " ");
286  port_string[slot] = strcat(port_string[slot], ftos(it.damageforcescale), " ");
287  if(it.material)
288  port_string[slot] = strcat(port_string[slot], "\"", it.material, "\" ");
289  else
290  port_string[slot] = strcat(port_string[slot], "\"\" "); // none
291  if(database)
292  {
293  // properties stored only for the database
294  if(it.crypto_idfp)
295  port_string[slot] = strcat(port_string[slot], "\"", it.crypto_idfp, "\" ");
296  else
297  port_string[slot] = strcat(port_string[slot], "\"\" "); // none
298  port_string[slot] = strcat(port_string[slot], "\"", e.netname, "\" ");
299  port_string[slot] = strcat(port_string[slot], "\"", e.message, "\" ");
300  port_string[slot] = strcat(port_string[slot], "\"", e.message2, "\" ");
301  }
302  ));
303 
304  // now apply the array to a simple string, with the ; symbol separating objects
305  string s = "";
306  for(int j = 0; j <= MAX_STORAGE_ATTACHMENTS; ++j)
307  {
308  if(port_string[j])
309  s = strcat(s, port_string[j], "; ");
310  port_string[j] = string_null; // fully clear the string
311  }
312 
313  return s;
314 }
315 
316 entity sandbox_ObjectPort_Load(entity this, string s, float database)
317 {
318  // load object properties, and spawn a new object with them
319  int n, i;
320  entity e = NULL, parent = NULL;
321  string arg = string_null;
322 
323  // separate objects between the ; symbols
324  n = tokenizebyseparator(s, "; ");
325  for(i = 0; i < n; ++i)
326  port_string[i] = argv(i);
327 
328  // now separate and apply the properties of each object
329  for(i = 0; i < n; ++i)
330  {
331  #define SANDBOX_GETARG arg = argv(++argv_num);
332  int argv_num = -1; // starts at -1 so I don't need postincrement
333 
334  string tagname = string_null;
336  e = sandbox_ObjectSpawn(this, database);
337 
338  // ---------------- OBJECT PROPERTY STORAGE: LOAD ----------------
339  if(i)
340  {
341  // properties stored only for child objects
342  SANDBOX_GETARG; tagname = (arg != "") ? arg : string_null;
343  }
344  else
345  {
346  // properties stored only for parent objects
347  if(database)
348  {
349  SANDBOX_GETARG; setorigin(e, stov(arg));
350  SANDBOX_GETARG; e.angles = stov(arg);
351  }
352  parent = e; // mark parent objects as such
353  }
354  // properties stored for all objects
355  SANDBOX_GETARG; _setmodel(e, arg);
356  SANDBOX_GETARG; e.skin = stof(arg);
357  SANDBOX_GETARG; e.alpha = stof(arg);
358  SANDBOX_GETARG; e.colormod = stov(arg);
359  SANDBOX_GETARG; e.glowmod = stov(arg);
360  SANDBOX_GETARG; e.frame = stof(arg);
362  SANDBOX_GETARG; e.solid = e.old_solid = stof(arg);
363  SANDBOX_GETARG; e.old_movetype = stof(arg);
364  set_movetype(e, e.old_movetype);
365  SANDBOX_GETARG; e.damageforcescale = stof(arg);
366  strfree(e.material);
367  SANDBOX_GETARG; e.material = (arg != "") ? strzone(arg) : string_null;
368  if(database)
369  {
370  // properties stored only for the database
371  strfree(e.crypto_idfp);
372  SANDBOX_GETARG; e.crypto_idfp = (arg != "") ? strzone(arg) : string_null;
373  SANDBOX_GETARG; strcpy(e.netname, arg);
374  SANDBOX_GETARG; strcpy(e.message, arg);
375  SANDBOX_GETARG; strcpy(e.message2, arg);
376  }
377 
378  // attach last
379  if(i)
380  sandbox_ObjectAttach_Set(e, parent, tagname);
381  }
382 
383  for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i)
384  port_string[i] = string_null; // fully clear the string
385 
386  return e;
387 }
388 
390 {
391  // saves all objects to the database file
392  string file_name;
393  float file_get;
394 
395  file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt");
396  file_get = fopen(file_name, FILE_WRITE);
397  fputs(file_get, strcat("// sandbox storage \"", autocvar_g_sandbox_storage_name, "\" for map \"", GetMapname(), "\" last updated ", strftime(true, "%d-%m-%Y %H:%M:%S")));
398  fputs(file_get, strcat(" containing ", ftos(object_count), " objects\n"));
399 
400  IL_EACH(g_sandbox_objects, !it.owner, // attached objects are persisted separately, ignore them here
401  {
402  // use a line of text for each object, listing all properties
403  fputs(file_get, strcat(sandbox_ObjectPort_Save(it, true), "\n"));
404  });
405  fclose(file_get);
406 }
407 
409 {
410  // loads all objects from the database file
411  string file_read, file_name;
412  float file_get, i;
413 
414  file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt");
415  file_get = fopen(file_name, FILE_READ);
416  if(file_get < 0)
417  {
419  LOG_INFO("^3SANDBOX - SERVER: ^7could not find storage file ^3", file_name, "^7, no objects were loaded");
420  }
421  else
422  {
423  for (;;)
424  {
425  file_read = fgets(file_get);
426  if(file_read == "")
427  break;
428  if(substring(file_read, 0, 2) == "//")
429  continue;
430  if(substring(file_read, 0, 1) == "#")
431  continue;
432 
433  entity e;
434  e = sandbox_ObjectPort_Load(NULL, file_read, true);
435 
436  if(e.material)
437  {
438  // since objects are being loaded for the first time, precache material sounds for each
439  for (i = 1; i <= 5; i++) // 5 sounds in total
440  precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".wav"));
441  }
442  }
444  LOG_INFO("^3SANDBOX - SERVER: ^7successfully loaded storage file ^3", file_name);
445  }
446  fclose(file_get);
447 }
448 
450 {
451  if(MUTATOR_RETURNVALUE) // command was already handled?
452  return;
453 
454  entity player = M_ARGV(0, entity);
455  string cmd_name = M_ARGV(1, string);
456  int cmd_argc = M_ARGV(2, int);
457 
458  if(cmd_name == "g_sandbox")
459  {
461  {
462  print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active, but in read-only mode. Sandbox commands cannot be used");
463  return true;
464  }
465  if(cmd_argc < 2)
466  {
467  print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active. For usage information, type 'sandbox help'");
468  return true;
469  }
470 
471  switch(argv(1))
472  {
473  entity e;
474  int j;
475  string s;
476 
477  // ---------------- COMMAND: HELP ----------------
478  case "help":
479  print_to(player, "You can use the following sandbox commands:");
480  print_to(player, "^7\"^2object_spawn ^3models/foo/bar.md3^7\" spawns a new object in front of the player, and gives it the specified model");
481  print_to(player, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects");
482  print_to(player, "^7\"^2object_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original");
483  print_to(player, "^3copy value ^7- copies the properties of the object to the specified client cvar");
484  print_to(player, "^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\"");
485  print_to(player, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects");
486  print_to(player, "^3get ^7- selects the object you are facing as the object to be attached");
487  print_to(player, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone");
488  print_to(player, "^3remove ^7- detaches all objects from the object you are facing");
489  print_to(player, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects");
490  print_to(player, "^3skin value ^7- changes the skin of the object");
491  print_to(player, "^3alpha value ^7- sets object transparency");
492  print_to(player, "^3colormod \"value_x value_y value_z\" ^7- main object color");
493  print_to(player, "^3glowmod \"value_x value_y value_z\" ^7- glow object color");
494  print_to(player, "^3frame value ^7- object animation frame, for self-animated models");
495  print_to(player, "^3scale value ^7- changes object scale. 0.5 is half size and 2 is double size");
496  print_to(player, "^3solidity value ^7- object collisions, 0 = non-solid, 1 = solid");
497  print_to(player, "^3physics value ^7- object physics, 0 = static, 1 = movable, 2 = physical");
498  print_to(player, "^3force value ^7- amount of force applied to objects that are shot");
499  print_to(player, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh");
500  print_to(player, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it");
501  print_to(player, "^7\"^2object_info ^3value^7\" shows public information about the object");
502  print_to(player, "^3object ^7- prints general information about the object, such as owner and creation / editing date");
503  print_to(player, "^3mesh ^7- prints information about the object's mesh, including skeletal bones");
504  print_to(player, "^3attachments ^7- prints information about the object's attachments");
505  print_to(player, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects");
506  return true;
507 
508  // ---------------- COMMAND: OBJECT, SPAWN ----------------
509  case "object_spawn":
510  if(time < player.object_flood)
511  {
512  print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object"));
513  return true;
514  }
515  player.object_flood = time + autocvar_g_sandbox_editor_flood;
517  {
518  print_to(player, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time"));
519  return true;
520  }
521  if(cmd_argc < 3)
522  {
523  print_to(player, "^1SANDBOX - WARNING: ^7Attempted to spawn an object without specifying a model. Please specify the path to your model file after the 'object_spawn' command");
524  return true;
525  }
526  if (!(fexists(argv(2))))
527  {
528  print_to(player, "^1SANDBOX - WARNING: ^7Attempted to spawn an object with a non-existent model. Make sure the path to your model file is correct");
529  return true;
530  }
531 
532  e = sandbox_ObjectSpawn(player, false);
533  _setmodel(e, argv(2));
534 
536  LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " spawned an object at origin ^3", vtos(e.origin));
537  return true;
538 
539  // ---------------- COMMAND: OBJECT, REMOVE ----------------
540  case "object_remove":
541  e = sandbox_ObjectEdit_Get(player, true);
542  if(e != NULL)
543  {
545  LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " removed an object at origin ^3", vtos(e.origin));
547  return true;
548  }
549 
550  print_to(player, "^1SANDBOX - WARNING: ^7Object could not be removed. Make sure you are facing an object that you have edit rights over");
551  return true;
552 
553  // ---------------- COMMAND: OBJECT, DUPLICATE ----------------
554  case "object_duplicate":
555  switch(argv(2))
556  {
557  case "copy":
558  // copies customizable properties of the selected object to the clipboard cvar
559  e = sandbox_ObjectEdit_Get(player, autocvar_g_sandbox_editor_free); // can we copy objects we can't edit?
560  if(e != NULL)
561  {
562  s = sandbox_ObjectPort_Save(e, false);
563  s = strreplace("\"", "\\\"", s);
564  stuffcmd(player, strcat("set ", argv(3), " \"", s, "\""));
565 
566  print_to(player, "^2SANDBOX - INFO: ^7Object copied to clipboard");
567  return true;
568  }
569  print_to(player, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have copy rights over");
570  return true;
571 
572  case "paste":
573  // spawns a new object using the properties in the player's clipboard cvar
574  if(time < player.object_flood)
575  {
576  print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object"));
577  return true;
578  }
579  player.object_flood = time + autocvar_g_sandbox_editor_flood;
580  if(argv(3) == "") // no object in clipboard
581  {
582  print_to(player, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it");
583  return true;
584  }
586  {
587  print_to(player, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time"));
588  return true;
589  }
590  e = sandbox_ObjectPort_Load(player, argv(3), false);
591 
592  print_to(player, "^2SANDBOX - INFO: ^7Object pasted successfully");
594  LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " pasted an object at origin ^3", vtos(e.origin));
595  return true;
596  }
597  return true;
598 
599  // ---------------- COMMAND: OBJECT, ATTACH ----------------
600  case "object_attach":
601  switch(argv(2))
602  {
603  case "get":
604  // select e as the object as meant to be attached
605  e = sandbox_ObjectEdit_Get(player, true);
606  if(e != NULL)
607  {
608  player.object_attach = e;
609  print_to(player, "^2SANDBOX - INFO: ^7Object selected for attachment");
610  return true;
611  }
612  print_to(player, "^1SANDBOX - WARNING: ^7Object could not be selected for attachment. Make sure you are facing an object that you have edit rights over");
613  return true;
614  case "set":
615  if(player.object_attach == NULL)
616  {
617  print_to(player, "^1SANDBOX - WARNING: ^7No object selected for attachment. Please select an object to be attached first.");
618  return true;
619  }
620 
621  // attaches the previously selected object to e
622  e = sandbox_ObjectEdit_Get(player, true);
623  if(e != NULL)
624  {
625  sandbox_ObjectAttach_Set(player.object_attach, e, argv(3));
626  player.object_attach = NULL; // object was attached, no longer keep it scheduled for attachment
627  print_to(player, "^2SANDBOX - INFO: ^7Object attached successfully");
629  LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " attached objects at origin ^3", vtos(e.origin));
630  return true;
631  }
632  print_to(player, "^1SANDBOX - WARNING: ^7Object could not be attached to the parent. Make sure you are facing an object that you have edit rights over");
633  return true;
634  case "remove":
635  // removes e if it was attached
636  e = sandbox_ObjectEdit_Get(player, true);
637  if(e != NULL)
638  {
640  print_to(player, "^2SANDBOX - INFO: ^7Child objects detached successfully");
642  LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " detached objects at origin ^3", vtos(e.origin));
643  return true;
644  }
645  print_to(player, "^1SANDBOX - WARNING: ^7Child objects could not be detached. Make sure you are facing an object that you have edit rights over");
646  return true;
647  }
648  return true;
649 
650  // ---------------- COMMAND: OBJECT, EDIT ----------------
651  case "object_edit":
652  if(argv(2) == "")
653  {
654  print_to(player, "^1SANDBOX - WARNING: ^7Too few parameters. You must specify a property to edit");
655  return true;
656  }
657 
658  e = sandbox_ObjectEdit_Get(player, true);
659  if(e != NULL)
660  {
661  switch(argv(2))
662  {
663  case "skin":
664  e.skin = stof(argv(3));
665  break;
666  case "alpha":
667  e.alpha = stof(argv(3));
668  break;
669  case "color_main":
670  e.colormod = stov(argv(3));
671  break;
672  case "color_glow":
673  e.glowmod = stov(argv(3));
674  break;
675  case "frame":
676  e.frame = stof(argv(3));
677  break;
678  case "scale":
680  break;
681  case "solidity":
682  switch(argv(3))
683  {
684  case "0": // non-solid
685  e.solid = SOLID_TRIGGER;
686  break;
687  case "1": // solid
688  e.solid = SOLID_BBOX;
689  break;
690  default:
691  break;
692  }
693  case "physics":
694  switch(argv(3))
695  {
696  case "0": // static
698  break;
699  case "1": // movable
701  break;
702  case "2": // physical
704  break;
705  default:
706  break;
707  }
708  break;
709  case "force":
710  e.damageforcescale = stof(argv(3));
711  break;
712  case "material":
713  strfree(e.material);
714  if(argv(3))
715  {
716  for (j = 1; j <= 5; j++) // precache material sounds, 5 in total
717  precache_sound(strcat("object/impact_", argv(3), "_", ftos(j), ".wav"));
718  e.material = strzone(argv(3));
719  }
720  else
721  e.material = string_null; // no material
722  break;
723  default:
724  print_to(player, "^1SANDBOX - WARNING: ^7Invalid object property. For usage information, type 'sandbox help'");
725  return true;
726  }
727 
728  // update last editing time
729  strcpy(e.message2, strftime(true, "%d-%m-%Y %H:%M:%S"));
730 
732  LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " edited property ^3", argv(2), " ^7of an object at origin ^3", vtos(e.origin));
733  return true;
734  }
735 
736  print_to(player, "^1SANDBOX - WARNING: ^7Object could not be edited. Make sure you are facing an object that you have edit rights over");
737  return true;
738 
739  // ---------------- COMMAND: OBJECT, CLAIM ----------------
740  case "object_claim":
741  // if the player can edit an object but is not its owner, this can be used to claim that object
742  if(player.crypto_idfp == "")
743  {
744  print_to(player, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects");
745  return true;
746  }
747  e = sandbox_ObjectEdit_Get(player, true);
748  if(e != NULL)
749  {
750  // update the owner's name
751  // Do this before checking if you're already the owner and skipping if such, so we
752  // also update the player's nickname if he changed it (but has the same player UID)
753  if(e.netname != player.netname)
754  {
755  strcpy(e.netname, player.netname);
756  print_to(player, "^2SANDBOX - INFO: ^7Object owner name updated");
757  }
758 
759  if(e.crypto_idfp == player.crypto_idfp)
760  {
761  print_to(player, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim");
762  return true;
763  }
764 
765  strcpy(e.crypto_idfp, player.crypto_idfp);
766 
767  print_to(player, "^2SANDBOX - INFO: ^7Object claimed successfully");
768  }
769  print_to(player, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over");
770  return true;
771 
772  // ---------------- COMMAND: OBJECT, INFO ----------------
773  case "object_info":
774  // prints public information about the object to the player
775  e = sandbox_ObjectEdit_Get(player, false);
776  if(e != NULL)
777  {
778  switch(argv(2))
779  {
780  case "object":
781  print_to(player, strcat("^2SANDBOX - INFO: ^7Object is owned by \"^7", e.netname, "^7\", created \"^3", e.message, "^7\", last edited \"^3", e.message2, "^7\""));
782  return true;
783  case "mesh":
784  s = "";
785  FOR_EACH_TAG(e)
786  s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", ");
787  print_to(player, strcat("^2SANDBOX - INFO: ^7Object mesh is \"^3", e.model, "^7\" at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", s));
788  return true;
789  case "attachments":
790  // this should show the same info as 'mesh' but for attachments
791  s = "";
792  j = 0;
793  IL_EACH(g_sandbox_objects, it.owner == e,
794  {
795  ++j; // start from 1
796  gettaginfo(e, it.tag_index);
797  s = strcat(s, "^1attachment ", ftos(j), "^7 has mesh \"^3", it.model, "^7\" at animation frame ^3", ftos(it.frame));
798  s = strcat(s, "^7 and is attached to bone \"^5", gettaginfo_name, "^7\", ");
799  });
800  if(j) // object contains attachments
801  print_to(player, strcat("^2SANDBOX - INFO: ^7Object contains the following ^1", ftos(j), "^7 attachment(s): ", s));
802  else
803  print_to(player, "^2SANDBOX - INFO: ^7Object contains no attachments");
804  return true;
805  }
806  }
807  print_to(player, "^1SANDBOX - WARNING: ^7No information could be found. Make sure you are facing an object");
808  return true;
809 
810  // ---------------- COMMAND: DEFAULT ----------------
811  default:
812  print_to(player, "Invalid command. For usage information, type 'sandbox help'");
813  return true;
814  }
815  }
816 }
817 
818 MUTATOR_HOOKFUNCTION(sandbox, SV_StartFrame)
819 {
821  return;
822  if(time < autosave_time)
823  return;
825 
827 
828  return true;
829 }
const float SOLID_NOT
Definition: csprogsdefs.qc:244
#define IL_EACH(this, cond, body)
float MOVETYPE_NONE
Definition: progsdefs.qc:246
string gettaginfo_name
void sandbox_Database_Save()
Definition: sv_sandbox.qc:389
string string_null
Definition: nil.qh:9
float object_flood
Definition: sv_sandbox.qc:38
float autocvar_g_sandbox_object_scale_max
Definition: sv_sandbox.qc:17
string autocvar_g_sandbox
Definition: sv_sandbox.qc:5
#define REGISTER_MUTATOR(id, dependence)
Definition: base.qh:263
vector view_ofs
Definition: progsdefs.qc:151
float autocvar_g_sandbox_object_scale_min
Definition: sv_sandbox.qc:16
const float MOVETYPE_PHYSICS
float MOVETYPE_TOSS
Definition: progsdefs.qc:252
string GetMapname()
Definition: intermission.qc:18
#define IL_NEW()
float object_count
Definition: sv_sandbox.qc:26
entity parent
Definition: animhost.qc:7
void sandbox_ObjectAttach_Set(entity e, entity parent, string s)
Definition: sv_sandbox.qc:130
float autocvar_g_sandbox_editor_flood
Definition: sv_sandbox.qc:11
entity sandbox_ObjectSpawn(entity this, float database)
Definition: sv_sandbox.qc:167
entity() spawn
IntrusiveList g_sandbox_objects
Definition: sv_sandbox.qc:21
float DAMAGE_AIM
Definition: progsdefs.qc:284
const float MOVE_NORMAL
Definition: csprogsdefs.qc:252
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
vector v_angle
Definition: progsdefs.qc:161
float autocvar_g_sandbox_storage_autosave
Definition: sv_sandbox.qc:9
#define SANDBOX_GETARG
string material
Definition: sv_sandbox.qc:40
string netname
Definition: powerups.qc:20
const float FILE_READ
Definition: csprogsdefs.qc:231
#define LAMBDA(...)
Definition: misc.qh:37
entity sandbox_ObjectPort_Load(entity this, string s, float database)
Definition: sv_sandbox.qc:316
void print_to(entity to, string input)
Definition: common.qc:172
limitations: NULL cannot be present elements can only be present once a maximum of IL_MAX lists can e...
origin
Definition: ent_cs.qc:114
void sandbox_Database_Load()
Definition: sv_sandbox.qc:408
int autocvar_g_sandbox_editor_free
Definition: sv_sandbox.qc:13
MUTATOR_HOOKFUNCTION(sandbox, SV_ParseClientCommand)
Definition: sv_sandbox.qc:449
float autocvar_g_sandbox_object_material_velocity_min
Definition: sv_sandbox.qc:18
void sandbox_ObjectEdit_Scale(entity e, float f)
Definition: sv_sandbox.qc:118
void sandbox_ObjectFunction_Touch(entity this, entity toucher)
Definition: sv_sandbox.qc:43
#define IS_REAL_CLIENT(v)
Definition: utils.qh:17
entity trace_ent
Definition: csprogsdefs.qc:40
entity sandbox_ObjectEdit_Get(entity this, float permissions)
Definition: sv_sandbox.qc:98
#define strcpy(this, s)
Definition: string.qh:49
void sandbox_ObjectAttach_Remove(entity e)
Definition: sv_sandbox.qc:147
string sandbox_ObjectPort_Save(entity e, bool database)
Definition: sv_sandbox.qc:232
void sandbox_ObjectRemove(entity e)
Definition: sv_sandbox.qc:212
void SV_ParseClientCommand(entity this, string command)
Definition: cmd.qc:866
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
float autocvar_g_sandbox_editor_distance_edit
Definition: sv_sandbox.qc:15
int autocvar_g_sandbox_editor_maxobjects
Definition: sv_sandbox.qc:12
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1 s1 strcat(_("Level %s: "), "^BG%s\3\, _("^BGPress ^F2%s^BG to enter the game"))
bool autocvar_g_sandbox_storage_autoload
Definition: sv_sandbox.qc:10
const int CH_TRIGGER
Definition: sound.qh:12
string autocvar_g_sandbox_storage_name
Definition: sv_sandbox.qc:8
#define NULL
Definition: post.qh:17
#define LOG_INFO(...)
Definition: log.qh:70
const float VOL_BASE
Definition: sound.qh:36
vector trace_endpos
Definition: csprogsdefs.qc:37
float autosave_time
Definition: sv_sandbox.qc:22
#define MUTATOR_RETURNVALUE
Definition: base.qh:303
float old_movetype
Definition: sv_sandbox.qc:97
#define M_ARGV(x, type)
Definition: events.qh:17
const float ATTEN_NORM
Definition: sound.qh:30
float nextthink
Definition: csprogsdefs.qc:121
float old_solid
Definition: sv_sandbox.qc:97
#define stuffcmd(cl,...)
Definition: progsdefs.qh:23
#define tokenize_console
Definition: dpextensions.qh:24
int autocvar_g_sandbox_info
Definition: sv_sandbox.qc:6
const float FILE_WRITE
Definition: csprogsdefs.qc:233
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
Definition: vector.qh:8
float MOVETYPE_FOLLOW
void WarpZone_TraceLine(vector org, vector end, float nomonsters, entity forent)
Definition: common.qc:338
entity object_attach
Definition: sv_sandbox.qc:39
#define _sound(e, c, s, v, a)
Definition: sound.qh:50
bool autocvar_g_sandbox_readonly
Definition: sv_sandbox.qc:7
const float SOLID_TRIGGER
Definition: csprogsdefs.qc:245
#define tokenizebyseparator
Definition: dpextensions.qh:21
const float SOLID_BBOX
Definition: csprogsdefs.qc:246
setorigin(ent, v)
string port_string[MAX_STORAGE_ATTACHMENTS]
Definition: sv_sandbox.qc:230
#define setthink(e, f)
#define strfree(this)
Definition: string.qh:56
void crosshair_trace_plusvisibletriggers(entity pl)
Definition: tracing.qc:513
float autocvar_g_sandbox_object_material_velocity_factor
Definition: sv_sandbox.qc:19
#define MUTATOR_ONADD
Definition: base.qh:284
float time
Definition: csprogsdefs.qc:16
vector velocity
Definition: csprogsdefs.qc:103
ERASEABLE bool fexists(string f)
Definition: file.qh:4
float touch_timer
Definition: sv_sandbox.qc:42
void sandbox_ObjectFunction_Think(entity this)
Definition: sv_sandbox.qc:68
float autocvar_g_sandbox_editor_distance_spawn
Definition: sv_sandbox.qc:14
#define makevectors
Definition: post.qh:21
string crypto_idfp
float DAMAGE_NO
Definition: progsdefs.qc:282
void set_movetype(entity this, int mt)
#define IS_PLAYER(v)
Definition: utils.qh:9
int grab
Definition: cheats.qh:25
vector v_forward
Definition: csprogsdefs.qc:31
ERASEABLE bool expr_evaluate(string s)
Evaluate an expression of the form: [+ | -]? [var[op]val | [op]var | val | var] ...
Definition: cvar.qh:48