Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
sv_sandbox.qc
Go to the documentation of this file.
1#include "sv_sandbox.qh"
2
4
20
24
26{
28 {
30 autosave_time = time + autocvar_g_sandbox_storage_autosave; // don't save the first server frame
33 }
34}
35
36const float MAX_STORAGE_ATTACHMENTS = 16;
40.string material;
41
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 = vlen(this.velocity) + vlen(toucher.velocity);
55 if(intensity) // avoid divisions by 0
56 intensity /= 2; // average the two velocities
58 return; // impact not strong enough to do anything
59 // now offset intensity and apply it to the effects
60 intensity -= autocvar_g_sandbox_object_material_velocity_min; // start from minimum velocity, not actual velocity
61 intensity = bound(0, intensity * autocvar_g_sandbox_object_material_velocity_factor, 1);
62
63 _sound(this, CH_TRIGGER, strcat("object/impact_", this.material, "_", ftos(ceil(random() * 5)) , ".wav"), VOL_BASE * intensity, ATTEN_NORM);
64 Send_Effect_(strcat("impact_", this.material), this.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10
65}
66
68{
69 // decide if and how this object can be grabbed
70 if(autocvar_g_sandbox_readonly || MUTATOR_CALLHOOK(Sandbox_DragAllowed, this))
71 this.grab = 0; // no grabbing
73 this.grab = 1; // owner only
74 else
75 this.grab = 3; // anyone
76
77 // Object owner is stored via player UID, but we also need the owner as an entity (if the player is available on the server).
78 // Therefore, scan for all players, and update the owner as long as the player is present. We must always do this,
79 // since if the owning player disconnects, the object's owner should also be reset.
80
81 // bots can't have objects
83 if(this.crypto_idfp == it.crypto_idfp)
84 {
85 this.realowner = it;
86 break;
87 }
88 this.realowner = NULL;
89 });
90
91 this.nextthink = time;
92
94}
95
97entity sandbox_ObjectEdit_Get(entity this, float permissions)
98{
99 // Returns the traced entity if the player can edit it, and NULL if not.
100 // If permissions if false, the object is returned regardless of editing rights.
101 // Attached objects are SOLID_NOT and do not get traced.
102
105 return NULL; // out of trace range
106 if(trace_ent.classname != "object")
107 return NULL; // entity is not an object
108 if(!permissions)
109 return trace_ent; // don't check permissions, anyone can edit this object
110 if(trace_ent.crypto_idfp == "")
111 return trace_ent; // the player who spawned this object did not have an UID, so anyone can edit it
112 if (!(trace_ent.realowner != this && autocvar_g_sandbox_editor_free < 2))
113 return trace_ent; // object does not belong to the player, and players can only edit their own objects on this server
114 return NULL;
115}
116
118{
119 e.scale = f;
120 if(e.scale)
121 {
123 _setmodel(e, e.model); // reset mins and maxs based on mesh
124 // apply object scaling and prevent any float precision issues like #2742
125 setsize(e, RoundPerfectVector(e.mins * e.scale), RoundPerfectVector(e.maxs * e.scale));
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
167entity sandbox_ObjectSpawn(entity this, float database)
168{
169 // spawn a new object with default properties
170
171 entity e = new(object);
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;
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 their 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);
202 setorigin(e, trace_endpos);
203 e.angles_y = this.v_angle.y;
204 }
205
207
208 ++object_count;
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;
228}
229
230string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't defined as a global
231
232string 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; // 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
316entity 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; precache_model(arg); _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 if(MUTATOR_CALLHOOK(Sandbox_SaveAllowed))
392 return;
393
394 // saves all objects to the database file
395 string file_name;
396 float file_get;
397
398 file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt");
399 file_get = fopen(file_name, FILE_WRITE);
400 fputs(file_get, strcat("// sandbox storage \"", autocvar_g_sandbox_storage_name, "\" for map \"", GetMapname(), "\" last updated ", strftime(true, "%d-%m-%Y %H:%M:%S")));
401 fputs(file_get, strcat(" containing ", ftos(object_count), " objects\n"));
402
403 IL_EACH(g_sandbox_objects, !it.owner, // attached objects are persisted separately, ignore them here
404 {
405 // use a line of text for each object, listing all properties
406 fputs(file_get, strcat(sandbox_ObjectPort_Save(it, true), "\n"));
407 });
408 fclose(file_get);
409}
410
412{
413 // loads all objects from the database file
414 string file_read, file_name;
415 float file_get, i;
416
417 file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt");
418 file_get = fopen(file_name, FILE_READ);
419 if(file_get < 0)
420 {
422 LOG_INFO("^3SANDBOX - SERVER: ^7could not find storage file ^3", file_name, "^7, no objects were loaded");
423 return;
424 }
425 else
426 {
427 for (;;)
428 {
429 file_read = fgets(file_get);
430 if(file_read == "")
431 break;
432 if(substring(file_read, 0, 2) == "//")
433 continue;
434 if(substring(file_read, 0, 1) == "#")
435 continue;
436
437 entity e = sandbox_ObjectPort_Load(NULL, file_read, true);
438
439 if(e.material)
440 {
441 // since objects are being loaded for the first time, precache material sounds for each
442 for (i = 1; i <= 5; ++i) // 5 sounds in total
443 precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".wav"));
444 }
445 }
447 LOG_INFO("^3SANDBOX - SERVER: ^7successfully loaded storage file ^3", file_name);
448 }
449 fclose(file_get);
450}
451
453{
454 if(MUTATOR_RETURNVALUE) // command was already handled?
455 return;
456
457 entity player = M_ARGV(0, entity);
458 string cmd_name = M_ARGV(1, string);
459 int cmd_argc = M_ARGV(2, int);
460
461 if(cmd_name == "g_sandbox")
462 {
463 if(autocvar_g_sandbox_readonly || MUTATOR_CALLHOOK(Sandbox_EditAllowed, player))
464 {
465 print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active, but in read-only mode. Sandbox commands cannot be used");
466 return true;
467 }
468 if(cmd_argc < 2)
469 {
470 print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active. For usage information, type 'sandbox help'");
471 return true;
472 }
473
474 switch(argv(1))
475 {
476 entity e;
477 int j;
478 string s;
479
480 // ---------------- COMMAND: HELP ----------------
481 case "help":
482 print_to(player, "You can use the following sandbox commands:");
483 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");
484 print_to(player, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects");
485 print_to(player, "^7\"^2object_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original");
486 print_to(player, "^3copy value ^7- copies the properties of the object to the specified client cvar");
487 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\"");
488 print_to(player, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects");
489 print_to(player, "^3get ^7- selects the object you are facing as the object to be attached");
490 print_to(player, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone");
491 print_to(player, "^3remove ^7- detaches all objects from the object you are facing");
492 print_to(player, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects");
493 print_to(player, "^3skin value ^7- changes the skin of the object");
494 print_to(player, "^3alpha value ^7- sets object transparency");
495 print_to(player, "^3colormod \"value_x value_y value_z\" ^7- main object color");
496 print_to(player, "^3glowmod \"value_x value_y value_z\" ^7- glow object color");
497 print_to(player, "^3frame value ^7- object animation frame, for self-animated models");
498 print_to(player, "^3scale value ^7- changes object scale. 0.5 is half size and 2 is double size");
499 print_to(player, "^3solidity value ^7- object collisions, 0 = non-solid, 1 = solid");
500 print_to(player, "^3physics value ^7- object physics, 0 = static, 1 = movable, 2 = physical");
501 print_to(player, "^3force value ^7- amount of force applied to objects that are shot");
502 print_to(player, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh");
503 print_to(player, "^7\"^2object_claim^7\" sets the player as the owner of the object, if they have the right to edit it");
504 print_to(player, "^7\"^2object_info ^3value^7\" shows public information about the object");
505 print_to(player, "^3object ^7- prints general information about the object, such as owner and creation / editing date");
506 print_to(player, "^3mesh ^7- prints information about the object's mesh, including skeletal bones");
507 print_to(player, "^3attachments ^7- prints information about the object's attachments");
508 print_to(player, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects");
509 return true;
510
511 // ---------------- COMMAND: OBJECT, SPAWN ----------------
512 case "object_spawn":
513 if(time < player.object_flood)
514 {
515 print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object"));
516 return true;
517 }
518 player.object_flood = time + autocvar_g_sandbox_editor_flood;
520 {
521 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"));
522 return true;
523 }
524 if(cmd_argc < 3)
525 {
526 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");
527 return true;
528 }
529 if (!(fexists(argv(2))))
530 {
531 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");
532 return true;
533 }
534
535 e = sandbox_ObjectSpawn(player, false);
536 precache_model(argv(2));
537 _setmodel(e, argv(2));
538
540 LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " spawned an object at origin ^3", vtos(e.origin));
541 return true;
542
543 // ---------------- COMMAND: OBJECT, REMOVE ----------------
544 case "object_remove":
545 e = sandbox_ObjectEdit_Get(player, true);
546 if(e != NULL)
547 {
549 LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " removed an object at origin ^3", vtos(e.origin));
551 return true;
552 }
553
554 print_to(player, "^1SANDBOX - WARNING: ^7Object could not be removed. Make sure you are facing an object that you have edit rights over");
555 return true;
556
557 // ---------------- COMMAND: OBJECT, DUPLICATE ----------------
558 case "object_duplicate":
559 switch(argv(2))
560 {
561 case "copy":
562 // copies customizable properties of the selected object to the clipboard cvar
563 e = sandbox_ObjectEdit_Get(player, autocvar_g_sandbox_editor_free); // can we copy objects we can't edit?
564 if(e != NULL)
565 {
566 s = sandbox_ObjectPort_Save(e, false);
567 s = strreplace("\"", "\\\"", s);
568 stuffcmd(player, strcat("set ", argv(3), " \"", s, "\""));
569
570 print_to(player, "^2SANDBOX - INFO: ^7Object copied to clipboard");
571 return true;
572 }
573 print_to(player, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have copy rights over");
574 return true;
575
576 case "paste":
577 // spawns a new object using the properties in the player's clipboard cvar
578 if(time < player.object_flood)
579 {
580 print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object"));
581 return true;
582 }
583 player.object_flood = time + autocvar_g_sandbox_editor_flood;
584 if(argv(3) == "") // no object in clipboard
585 {
586 print_to(player, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it");
587 return true;
588 }
590 {
591 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"));
592 return true;
593 }
594 e = sandbox_ObjectPort_Load(player, argv(3), false);
595
596 print_to(player, "^2SANDBOX - INFO: ^7Object pasted successfully");
598 LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " pasted an object at origin ^3", vtos(e.origin));
599 return true;
600 }
601 return true;
602
603 // ---------------- COMMAND: OBJECT, ATTACH ----------------
604 case "object_attach":
605 switch(argv(2))
606 {
607 case "get":
608 // select e as the object as meant to be attached
609 e = sandbox_ObjectEdit_Get(player, true);
610 if(e != NULL)
611 {
612 player.object_attach = e;
613 print_to(player, "^2SANDBOX - INFO: ^7Object selected for attachment");
614 return true;
615 }
616 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");
617 return true;
618 case "set":
619 if(player.object_attach == NULL)
620 {
621 print_to(player, "^1SANDBOX - WARNING: ^7No object selected for attachment. Please select an object to be attached first.");
622 return true;
623 }
624
625 // attaches the previously selected object to e
626 e = sandbox_ObjectEdit_Get(player, true);
627 if(e != NULL)
628 {
629 sandbox_ObjectAttach_Set(player.object_attach, e, argv(3));
630 player.object_attach = NULL; // object was attached, no longer keep it scheduled for attachment
631 print_to(player, "^2SANDBOX - INFO: ^7Object attached successfully");
633 LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " attached objects at origin ^3", vtos(e.origin));
634 return true;
635 }
636 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");
637 return true;
638 case "remove":
639 // removes e if it was attached
640 e = sandbox_ObjectEdit_Get(player, true);
641 if(e != NULL)
642 {
644 print_to(player, "^2SANDBOX - INFO: ^7Child objects detached successfully");
646 LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " detached objects at origin ^3", vtos(e.origin));
647 return true;
648 }
649 print_to(player, "^1SANDBOX - WARNING: ^7Child objects could not be detached. Make sure you are facing an object that you have edit rights over");
650 return true;
651 }
652 return true;
653
654 // ---------------- COMMAND: OBJECT, EDIT ----------------
655 case "object_edit":
656 if(argv(2) == "")
657 {
658 print_to(player, "^1SANDBOX - WARNING: ^7Too few parameters. You must specify a property to edit");
659 return true;
660 }
661
662 e = sandbox_ObjectEdit_Get(player, true);
663 if(e != NULL)
664 {
665 switch(argv(2))
666 {
667 case "skin":
668 e.skin = stof(argv(3));
669 break;
670 case "alpha":
671 e.alpha = stof(argv(3));
672 break;
673 case "color_main":
674 e.colormod = stov(argv(3));
675 break;
676 case "color_glow":
677 e.glowmod = stov(argv(3));
678 break;
679 case "frame":
680 e.frame = stof(argv(3));
681 break;
682 case "scale":
684 break;
685 case "solidity":
686 switch(argv(3))
687 {
688 case "0": // non-solid
689 e.solid = SOLID_TRIGGER;
690 break;
691 case "1": // solid
692 e.solid = SOLID_BBOX;
693 break;
694 default:
695 break;
696 }
697 case "physics":
698 switch(argv(3))
699 {
700 case "0": // static
702 break;
703 case "1": // movable
705 break;
706 case "2": // physical
708 break;
709 default:
710 break;
711 }
712 break;
713 case "force":
714 e.damageforcescale = stof(argv(3));
715 break;
716 case "material":
717 strfree(e.material);
718 if(argv(3))
719 {
720 for (j = 1; j <= 5; ++j) // precache material sounds, 5 in total
721 precache_sound(strcat("object/impact_", argv(3), "_", ftos(j), ".wav"));
722 e.material = strzone(argv(3));
723 }
724 else
725 e.material = string_null; // no material
726 break;
727 default:
728 print_to(player, "^1SANDBOX - WARNING: ^7Invalid object property. For usage information, type 'sandbox help'");
729 return true;
730 }
731
732 // update last editing time
733 strcpy(e.message2, strftime(true, "%d-%m-%Y %H:%M:%S"));
734
736 LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " edited property ^3", argv(2), " ^7of an object at origin ^3", vtos(e.origin));
737 return true;
738 }
739
740 print_to(player, "^1SANDBOX - WARNING: ^7Object could not be edited. Make sure you are facing an object that you have edit rights over");
741 return true;
742
743 // ---------------- COMMAND: OBJECT, CLAIM ----------------
744 case "object_claim":
745 // if the player can edit an object but is not its owner, this can be used to claim that object
746 if(player.crypto_idfp == "")
747 {
748 print_to(player, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects");
749 return true;
750 }
751 e = sandbox_ObjectEdit_Get(player, true);
752 if(e != NULL)
753 {
754 // update the owner's name
755 // Do this before checking if you're already the owner and skipping if such, so we
756 // also update the player's nickname if they changed it (but has the same player UID)
757 if(e.netname != player.netname)
758 {
759 strcpy(e.netname, player.netname);
760 print_to(player, "^2SANDBOX - INFO: ^7Object owner name updated");
761 }
762
763 if(e.crypto_idfp == player.crypto_idfp)
764 {
765 print_to(player, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim");
766 return true;
767 }
768
769 strcpy(e.crypto_idfp, player.crypto_idfp);
770
771 print_to(player, "^2SANDBOX - INFO: ^7Object claimed successfully");
772 }
773 print_to(player, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over");
774 return true;
775
776 // ---------------- COMMAND: OBJECT, INFO ----------------
777 case "object_info":
778 // prints public information about the object to the player
779 e = sandbox_ObjectEdit_Get(player, false);
780 if(e != NULL)
781 {
782 switch(argv(2))
783 {
784 case "object":
785 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\""));
786 return true;
787 case "mesh":
788 s = "";
789 FOR_EACH_TAG(e)
790 s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", ");
791 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));
792 return true;
793 case "attachments":
794 // this should show the same info as 'mesh' but for attachments
795 s = "";
796 j = 0;
797 IL_EACH(g_sandbox_objects, it.owner == e,
798 {
799 ++j; // start from 1
800 gettaginfo(e, it.tag_index);
801 s = strcat(s, "^1attachment ", ftos(j), "^7 has mesh \"^3", it.model, "^7\" at animation frame ^3", ftos(it.frame));
802 s = strcat(s, "^7 and is attached to bone \"^5", gettaginfo_name, "^7\", ");
803 });
804 if(j) // object contains attachments
805 print_to(player, strcat("^2SANDBOX - INFO: ^7Object contains the following ^1", ftos(j), "^7 attachment(s): ", s));
806 else
807 print_to(player, "^2SANDBOX - INFO: ^7Object contains no attachments");
808 return true;
809 }
810 }
811 print_to(player, "^1SANDBOX - WARNING: ^7No information could be found. Make sure you are facing an object");
812 return true;
813
814 // ---------------- COMMAND: DEFAULT ----------------
815 default:
816 print_to(player, "Invalid command. For usage information, type 'sandbox help'");
817 return true;
818 }
819 }
820}
821
822MUTATOR_HOOKFUNCTION(sandbox, SV_StartFrame)
823{
825 return;
826 if(time < autosave_time)
827 return;
829
831
832 return true;
833}
entity parent
Definition animhost.qc:7
#define MUTATOR_ONADD
Definition base.qh:309
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
#define REGISTER_MUTATOR(...)
Definition base.qh:295
#define MUTATOR_HOOKFUNCTION(...)
Definition base.qh:335
#define MUTATOR_RETURNVALUE
Definition base.qh:328
int grab
Definition cheats.qh:26
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
limitations: NULL cannot be present elements can only be present once a maximum of IL_MAX lists can e...
string netname
Definition powerups.qc:20
#define M_ARGV(x, type)
Definition events.qh:17
vector v_angle
Definition player.qh:236
#define IS_PLAYER(s)
Definition player.qh:242
#define FOR_EACH_TAG(v)
Definition util.qh:216
entity trace_ent
const float SOLID_TRIGGER
const float MOVE_NORMAL
vector velocity
const float SOLID_BBOX
const float SOLID_NOT
string gettaginfo_name
const float FILE_READ
const float FILE_WRITE
float time
vector trace_endpos
float nextthink
vector v_forward
vector origin
#define CSQCMODEL_AUTOUPDATE(e)
#define CSQCMODEL_AUTOINIT(e)
ERASEABLE bool expr_evaluate(string s)
Evaluate an expression of the form: [+ | -]?
Definition cvar.qh:48
string crypto_idfp
#define tokenize_console
#define tokenizebyseparator
void Send_Effect_(string eff_name, vector eff_loc, vector eff_vel, int eff_cnt)
Definition all.qc:125
ERASEABLE bool fexists(string f)
Definition file.qh:4
string GetMapname()
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
#define IL_NEW()
#define IL_EACH(this, cond, body)
#define SV_ParseClientCommand
Definition _all.inc:284
void WarpZone_TraceLine(vector org, vector end, float nomonsters, entity forent)
Definition common.qc:348
#define LOG_INFO(...)
Definition log.qh:65
string cmd_name
Definition events.qh:12
int cmd_argc
Definition events.qh:13
string fgets(float fhandle)
void fclose(float fhandle)
float ceil(float f)
float stof(string val,...)
void fputs(float fhandle, string s)
float bound(float min, float value, float max)
string substring(string s, float start, float length)
float fopen(string filename, float mode)
vector stov(string s)
float random(void)
float vlen(vector v)
string precache_sound(string sample)
string vtos(vector v)
string ftos(float f)
string strzone(string s)
string argv(float n)
#define LAMBDA(...)
Definition misc.qh:34
void set_movetype(entity this, int mt)
Definition movetypes.qc:4
const int MOVETYPE_NONE
Definition movetypes.qh:129
const int MOVETYPE_FOLLOW
Definition movetypes.qh:141
const int MOVETYPE_PHYSICS
Definition movetypes.qh:142
const int MOVETYPE_TOSS
Definition movetypes.qh:135
string string_null
Definition nil.qh:9
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
#define NULL
Definition post.qh:14
#define makevectors
Definition post.qh:21
vector view_ofs
Definition progsdefs.qc:151
#define stuffcmd(cl,...)
Definition progsdefs.qh:23
#define setthink(e, f)
entity entity toucher
Definition self.qh:72
#define settouch(e, f)
Definition self.qh:73
void print_to(entity to, string input)
Definition common.qc:171
const int CH_TRIGGER
Definition sound.qh:12
const float VOL_BASE
Definition sound.qh:36
#define _sound(e, c, s, v, a)
Definition sound.qh:43
const float ATTEN_NORM
Definition sound.qh:30
#define strfree(this)
Definition string.qh:59
#define strcpy(this, s)
Definition string.qh:52
const int DAMAGE_NO
Definition subs.qh:79
const int DAMAGE_AIM
Definition subs.qh:81
void sandbox_ObjectRemove(entity e)
float touch_timer
Definition sv_sandbox.qc:42
bool autocvar_g_sandbox_readonly
Definition sv_sandbox.qc:7
entity sandbox_ObjectSpawn(entity this, float database)
float autocvar_g_sandbox_editor_flood
Definition sv_sandbox.qc:11
entity sandbox_ObjectEdit_Get(entity this, float permissions)
Definition sv_sandbox.qc:97
string sandbox_ObjectPort_Save(entity e, bool database)
#define SANDBOX_GETARG
float autocvar_g_sandbox_object_material_velocity_min
Definition sv_sandbox.qc:18
void sandbox_ObjectAttach_Remove(entity e)
entity object_attach
Definition sv_sandbox.qc:39
void sandbox_ObjectEdit_Scale(entity e, float f)
float autocvar_g_sandbox_object_scale_max
Definition sv_sandbox.qc:17
float object_count
Definition sv_sandbox.qc:37
string port_string[MAX_STORAGE_ATTACHMENTS]
float object_flood
Definition sv_sandbox.qc:38
float old_movetype
Definition sv_sandbox.qc:96
string material
Definition sv_sandbox.qc:40
string autocvar_g_sandbox_storage_name
Definition sv_sandbox.qc:8
IntrusiveList g_sandbox_objects
Definition sv_sandbox.qc:21
float old_solid
Definition sv_sandbox.qc:96
void sandbox_Database_Save()
entity sandbox_ObjectPort_Load(entity this, string s, float database)
void sandbox_ObjectFunction_Think(entity this)
Definition sv_sandbox.qc:67
float autocvar_g_sandbox_object_scale_min
Definition sv_sandbox.qc:16
int autocvar_g_sandbox_info
Definition sv_sandbox.qc:6
float autocvar_g_sandbox_storage_autosave
Definition sv_sandbox.qc:9
float autosave_time
Definition sv_sandbox.qc:22
float autocvar_g_sandbox_editor_distance_spawn
Definition sv_sandbox.qc:14
float autocvar_g_sandbox_editor_distance_edit
Definition sv_sandbox.qc:15
void sandbox_ObjectFunction_Touch(entity this, entity toucher)
Definition sv_sandbox.qc:43
int autocvar_g_sandbox_editor_maxobjects
Definition sv_sandbox.qc:12
string autocvar_g_sandbox
Definition sv_sandbox.qc:5
void sandbox_Database_Load()
int autocvar_g_sandbox_editor_free
Definition sv_sandbox.qc:13
float autocvar_g_sandbox_object_material_velocity_factor
Definition sv_sandbox.qc:19
void sandbox_ObjectAttach_Set(entity e, entity parent, string s)
bool autocvar_g_sandbox_storage_autoload
Definition sv_sandbox.qc:10
entity realowner
void crosshair_trace_plusvisibletriggers(entity pl)
Definition tracing.qc:547
#define IS_REAL_CLIENT(v)
Definition utils.qh:17
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:52
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
Definition vector.qh:8
ERASEABLE vector RoundPerfectVector(vector v)
Definition vector.qh:209