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;
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
71 if(autocvar_g_sandbox_readonly || MUTATOR_CALLHOOK(Sandbox_DragAllowed, this))
72 this.grab = 0; // no grabbing
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
95}
96
98entity 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 // apply object scaling and prevent any float precision issues like #2742
126 setsize(e, RoundPerfectVector(e.mins * e.scale), RoundPerfectVector(e.maxs * e.scale));
127 }
128}
129
132{
133 // attaches e to parent on string s
134
135 // we can't attach to an attachment, for obvious reasons
137
138 e.old_solid = e.solid; // persist solidity
139 e.old_movetype = e.move_movetype; // persist physics
141 e.solid = SOLID_NOT;
142 e.takedamage = DAMAGE_NO;
143
144 setattachment(e, parent, s);
145 e.owner = parent;
146}
147
149{
150 // detaches any object attached to e
151
152 IL_EACH(g_sandbox_objects, it.owner == e,
153 {
154 vector org = gettaginfo(it, 0);
155 setattachment(it, NULL, "");
156 it.owner = NULL;
157
158 // objects change origin and angles when detached, so apply previous position
159 setorigin(it, org);
160 it.angles = e.angles; // don't allow detached objects to spin or roll
161
162 it.solid = it.old_solid; // restore persisted solidity
163 set_movetype(it, it.old_movetype); // restore persisted physics
164 it.takedamage = DAMAGE_AIM;
165 });
166}
167
168entity sandbox_ObjectSpawn(entity this, float database)
169{
170 // spawn a new object with default properties
171
172 entity e = new(object);
174 e.takedamage = DAMAGE_AIM;
175 e.damageforcescale = 1;
176 e.solid = SOLID_BBOX; // SOLID_BSP would be best, but can lag the server badly
178 e.frame = 0;
179 e.skin = 0;
180 e.material = string_null;
183 e.nextthink = time;
184 //e.effects |= EF_SELECTABLE; // don't do this all the time, maybe just when editing objects?
185
186 if(!database)
187 {
188 // set the object's owner via player UID
189 // if the player does not have an UID, the owner cannot be stored and their objects may be edited by anyone
190 if(this.crypto_idfp != "")
191 e.crypto_idfp = strzone(this.crypto_idfp);
192 else
193 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!");
194
195 // set public object information
196 e.netname = strzone(this.netname); // name of the owner
197 e.message = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // creation time
198 e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // last editing time
199
200 // set origin and direction based on player position and view angle
201 makevectors(this.v_angle);
203 setorigin(e, trace_endpos);
204 e.angles_y = this.v_angle.y;
205 }
206
208
209 object_count += 1;
210 return e;
211}
212
214{
215 sandbox_ObjectAttach_Remove(e); // detach child objects
216
217 // if the object being removed has been selected for attachment by a player, unset it
218 FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it.object_attach == e, { it.object_attach = NULL; });
219
220 strfree(e.material);
221 strfree(e.crypto_idfp);
222 strfree(e.netname);
223 strfree(e.message);
224 strfree(e.message2);
225 delete(e);
226 e = NULL;
227
228 object_count -= 1;
229}
230
231string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't defined as a global
232
233string sandbox_ObjectPort_Save(entity e, bool database)
234{
235 // save object properties, and return them as a string
236 int o = 0;
237
238 // order doesn't really matter, as we're writing the file fresh
239 IL_EACH(g_sandbox_objects, it == e || it.owner == e, LAMBDA(
240 // the main object needs to be first in the array [0] with attached objects following
241 int slot, physics, solidity;
242 if(it == e) // this is the main object, place it first
243 {
244 slot = 0;
245 solidity = it.solid; // applied solidity is normal solidity for children
246 physics = it.move_movetype; // applied physics are normal physics for parents
247 }
248 else if(it.owner == e) // child object, list them in order
249 {
250 o += 1; // children start from 1
251 slot = o;
252 solidity = it.old_solid; // persisted solidity is normal solidity for children
253 physics = it.old_movetype; // persisted physics are normal physics for children
254 gettaginfo(it.owner, it.tag_index); // get the name of the tag our object is attached to, used further below
255 }
256 else
257 continue;
258
259 // ---------------- OBJECT PROPERTY STORAGE: SAVE ----------------
260 if(slot)
261 {
262 // properties stored only for child objects
263 if(gettaginfo_name)
264 port_string[slot] = strcat(port_string[slot], "\"", gettaginfo_name, "\" ");
265 else
266 port_string[slot] = strcat(port_string[slot], "\"\" "); // none
267 }
268 else
269 {
270 // properties stored only for parent objects
271 if(database)
272 {
273 port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", it.origin), " ");
274 port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", it.angles), " ");
275 }
276 }
277 // properties stored for all objects
278 port_string[slot] = strcat(port_string[slot], "\"", it.model, "\" ");
279 port_string[slot] = strcat(port_string[slot], ftos(it.skin), " ");
280 port_string[slot] = strcat(port_string[slot], ftos(it.alpha), " ");
281 port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", it.colormod), " ");
282 port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", it.glowmod), " ");
283 port_string[slot] = strcat(port_string[slot], ftos(it.frame), " ");
284 port_string[slot] = strcat(port_string[slot], ftos(it.scale), " ");
285 port_string[slot] = strcat(port_string[slot], ftos(solidity), " ");
286 port_string[slot] = strcat(port_string[slot], ftos(physics), " ");
287 port_string[slot] = strcat(port_string[slot], ftos(it.damageforcescale), " ");
288 if(it.material)
289 port_string[slot] = strcat(port_string[slot], "\"", it.material, "\" ");
290 else
291 port_string[slot] = strcat(port_string[slot], "\"\" "); // none
292 if(database)
293 {
294 // properties stored only for the database
295 if(it.crypto_idfp)
296 port_string[slot] = strcat(port_string[slot], "\"", it.crypto_idfp, "\" ");
297 else
298 port_string[slot] = strcat(port_string[slot], "\"\" "); // none
299 port_string[slot] = strcat(port_string[slot], "\"", e.netname, "\" ");
300 port_string[slot] = strcat(port_string[slot], "\"", e.message, "\" ");
301 port_string[slot] = strcat(port_string[slot], "\"", e.message2, "\" ");
302 }
303 ));
304
305 // now apply the array to a simple string, with the ; symbol separating objects
306 string s = "";
307 for(int j = 0; j <= MAX_STORAGE_ATTACHMENTS; ++j)
308 {
309 if(port_string[j])
310 s = strcat(s, port_string[j], "; ");
311 port_string[j] = string_null; // fully clear the string
312 }
313
314 return s;
315}
316
317entity sandbox_ObjectPort_Load(entity this, string s, float database)
318{
319 // load object properties, and spawn a new object with them
320 int n, i;
321 entity e = NULL, parent = NULL;
322 string arg = string_null;
323
324 // separate objects between the ; symbols
325 n = tokenizebyseparator(s, "; ");
326 for(i = 0; i < n; ++i)
327 port_string[i] = argv(i);
328
329 // now separate and apply the properties of each object
330 for(i = 0; i < n; ++i)
331 {
332 #define SANDBOX_GETARG arg = argv(++argv_num);
333 int argv_num = -1; // starts at -1 so I don't need postincrement
334
335 string tagname = string_null;
337 e = sandbox_ObjectSpawn(this, database);
338
339 // ---------------- OBJECT PROPERTY STORAGE: LOAD ----------------
340 if(i)
341 {
342 // properties stored only for child objects
343 SANDBOX_GETARG; tagname = (arg != "") ? arg : string_null;
344 }
345 else
346 {
347 // properties stored only for parent objects
348 if(database)
349 {
350 SANDBOX_GETARG; setorigin(e, stov(arg));
351 SANDBOX_GETARG; e.angles = stov(arg);
352 }
353 parent = e; // mark parent objects as such
354 }
355 // properties stored for all objects
356 SANDBOX_GETARG; precache_model(arg); _setmodel(e, arg);
357 SANDBOX_GETARG; e.skin = stof(arg);
358 SANDBOX_GETARG; e.alpha = stof(arg);
359 SANDBOX_GETARG; e.colormod = stov(arg);
360 SANDBOX_GETARG; e.glowmod = stov(arg);
361 SANDBOX_GETARG; e.frame = stof(arg);
363 SANDBOX_GETARG; e.solid = e.old_solid = stof(arg);
364 SANDBOX_GETARG; e.old_movetype = stof(arg);
365 set_movetype(e, e.old_movetype);
366 SANDBOX_GETARG; e.damageforcescale = stof(arg);
367 strfree(e.material);
368 SANDBOX_GETARG; e.material = (arg != "") ? strzone(arg) : string_null;
369 if(database)
370 {
371 // properties stored only for the database
372 strfree(e.crypto_idfp);
373 SANDBOX_GETARG; e.crypto_idfp = (arg != "") ? strzone(arg) : string_null;
374 SANDBOX_GETARG; strcpy(e.netname, arg);
375 SANDBOX_GETARG; strcpy(e.message, arg);
376 SANDBOX_GETARG; strcpy(e.message2, arg);
377 }
378
379 // attach last
380 if(i)
381 sandbox_ObjectAttach_Set(e, parent, tagname);
382 }
383
384 for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i)
385 port_string[i] = string_null; // fully clear the string
386
387 return e;
388}
389
391{
392 if(MUTATOR_CALLHOOK(Sandbox_SaveAllowed))
393 return;
394
395 // saves all objects to the database file
396 string file_name;
397 float file_get;
398
399 file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt");
400 file_get = fopen(file_name, FILE_WRITE);
401 fputs(file_get, strcat("// sandbox storage \"", autocvar_g_sandbox_storage_name, "\" for map \"", GetMapname(), "\" last updated ", strftime(true, "%d-%m-%Y %H:%M:%S")));
402 fputs(file_get, strcat(" containing ", ftos(object_count), " objects\n"));
403
404 IL_EACH(g_sandbox_objects, !it.owner, // attached objects are persisted separately, ignore them here
405 {
406 // use a line of text for each object, listing all properties
407 fputs(file_get, strcat(sandbox_ObjectPort_Save(it, true), "\n"));
408 });
409 fclose(file_get);
410}
411
413{
414 // loads all objects from the database file
415 string file_read, file_name;
416 float file_get, i;
417
418 file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt");
419 file_get = fopen(file_name, FILE_READ);
420 if(file_get < 0)
421 {
423 LOG_INFO("^3SANDBOX - SERVER: ^7could not find storage file ^3", file_name, "^7, no objects were loaded");
424 return;
425 }
426 else
427 {
428 for (;;)
429 {
430 file_read = fgets(file_get);
431 if(file_read == "")
432 break;
433 if(substring(file_read, 0, 2) == "//")
434 continue;
435 if(substring(file_read, 0, 1) == "#")
436 continue;
437
438 entity e;
439 e = sandbox_ObjectPort_Load(NULL, file_read, true);
440
441 if(e.material)
442 {
443 // since objects are being loaded for the first time, precache material sounds for each
444 for (i = 1; i <= 5; ++i) // 5 sounds in total
445 precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".wav"));
446 }
447 }
449 LOG_INFO("^3SANDBOX - SERVER: ^7successfully loaded storage file ^3", file_name);
450 }
451 fclose(file_get);
452}
453
455{
456 if(MUTATOR_RETURNVALUE) // command was already handled?
457 return;
458
459 entity player = M_ARGV(0, entity);
460 string cmd_name = M_ARGV(1, string);
461 int cmd_argc = M_ARGV(2, int);
462
463 if(cmd_name == "g_sandbox")
464 {
465 if(autocvar_g_sandbox_readonly || MUTATOR_CALLHOOK(Sandbox_EditAllowed, player))
466 {
467 print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active, but in read-only mode. Sandbox commands cannot be used");
468 return true;
469 }
470 if(cmd_argc < 2)
471 {
472 print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active. For usage information, type 'sandbox help'");
473 return true;
474 }
475
476 switch(argv(1))
477 {
478 entity e;
479 int j;
480 string s;
481
482 // ---------------- COMMAND: HELP ----------------
483 case "help":
484 print_to(player, "You can use the following sandbox commands:");
485 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");
486 print_to(player, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects");
487 print_to(player, "^7\"^2object_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original");
488 print_to(player, "^3copy value ^7- copies the properties of the object to the specified client cvar");
489 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\"");
490 print_to(player, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects");
491 print_to(player, "^3get ^7- selects the object you are facing as the object to be attached");
492 print_to(player, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone");
493 print_to(player, "^3remove ^7- detaches all objects from the object you are facing");
494 print_to(player, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects");
495 print_to(player, "^3skin value ^7- changes the skin of the object");
496 print_to(player, "^3alpha value ^7- sets object transparency");
497 print_to(player, "^3colormod \"value_x value_y value_z\" ^7- main object color");
498 print_to(player, "^3glowmod \"value_x value_y value_z\" ^7- glow object color");
499 print_to(player, "^3frame value ^7- object animation frame, for self-animated models");
500 print_to(player, "^3scale value ^7- changes object scale. 0.5 is half size and 2 is double size");
501 print_to(player, "^3solidity value ^7- object collisions, 0 = non-solid, 1 = solid");
502 print_to(player, "^3physics value ^7- object physics, 0 = static, 1 = movable, 2 = physical");
503 print_to(player, "^3force value ^7- amount of force applied to objects that are shot");
504 print_to(player, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh");
505 print_to(player, "^7\"^2object_claim^7\" sets the player as the owner of the object, if they have the right to edit it");
506 print_to(player, "^7\"^2object_info ^3value^7\" shows public information about the object");
507 print_to(player, "^3object ^7- prints general information about the object, such as owner and creation / editing date");
508 print_to(player, "^3mesh ^7- prints information about the object's mesh, including skeletal bones");
509 print_to(player, "^3attachments ^7- prints information about the object's attachments");
510 print_to(player, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects");
511 return true;
512
513 // ---------------- COMMAND: OBJECT, SPAWN ----------------
514 case "object_spawn":
515 if(time < player.object_flood)
516 {
517 print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object"));
518 return true;
519 }
520 player.object_flood = time + autocvar_g_sandbox_editor_flood;
522 {
523 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"));
524 return true;
525 }
526 if(cmd_argc < 3)
527 {
528 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");
529 return true;
530 }
531 if (!(fexists(argv(2))))
532 {
533 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");
534 return true;
535 }
536
537 e = sandbox_ObjectSpawn(player, false);
538 precache_model(argv(2));
539 _setmodel(e, argv(2));
540
542 LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " spawned an object at origin ^3", vtos(e.origin));
543 return true;
544
545 // ---------------- COMMAND: OBJECT, REMOVE ----------------
546 case "object_remove":
547 e = sandbox_ObjectEdit_Get(player, true);
548 if(e != NULL)
549 {
551 LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " removed an object at origin ^3", vtos(e.origin));
553 return true;
554 }
555
556 print_to(player, "^1SANDBOX - WARNING: ^7Object could not be removed. Make sure you are facing an object that you have edit rights over");
557 return true;
558
559 // ---------------- COMMAND: OBJECT, DUPLICATE ----------------
560 case "object_duplicate":
561 switch(argv(2))
562 {
563 case "copy":
564 // copies customizable properties of the selected object to the clipboard cvar
565 e = sandbox_ObjectEdit_Get(player, autocvar_g_sandbox_editor_free); // can we copy objects we can't edit?
566 if(e != NULL)
567 {
568 s = sandbox_ObjectPort_Save(e, false);
569 s = strreplace("\"", "\\\"", s);
570 stuffcmd(player, strcat("set ", argv(3), " \"", s, "\""));
571
572 print_to(player, "^2SANDBOX - INFO: ^7Object copied to clipboard");
573 return true;
574 }
575 print_to(player, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have copy rights over");
576 return true;
577
578 case "paste":
579 // spawns a new object using the properties in the player's clipboard cvar
580 if(time < player.object_flood)
581 {
582 print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object"));
583 return true;
584 }
585 player.object_flood = time + autocvar_g_sandbox_editor_flood;
586 if(argv(3) == "") // no object in clipboard
587 {
588 print_to(player, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it");
589 return true;
590 }
592 {
593 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"));
594 return true;
595 }
596 e = sandbox_ObjectPort_Load(player, argv(3), false);
597
598 print_to(player, "^2SANDBOX - INFO: ^7Object pasted successfully");
600 LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " pasted an object at origin ^3", vtos(e.origin));
601 return true;
602 }
603 return true;
604
605 // ---------------- COMMAND: OBJECT, ATTACH ----------------
606 case "object_attach":
607 switch(argv(2))
608 {
609 case "get":
610 // select e as the object as meant to be attached
611 e = sandbox_ObjectEdit_Get(player, true);
612 if(e != NULL)
613 {
614 player.object_attach = e;
615 print_to(player, "^2SANDBOX - INFO: ^7Object selected for attachment");
616 return true;
617 }
618 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");
619 return true;
620 case "set":
621 if(player.object_attach == NULL)
622 {
623 print_to(player, "^1SANDBOX - WARNING: ^7No object selected for attachment. Please select an object to be attached first.");
624 return true;
625 }
626
627 // attaches the previously selected object to e
628 e = sandbox_ObjectEdit_Get(player, true);
629 if(e != NULL)
630 {
631 sandbox_ObjectAttach_Set(player.object_attach, e, argv(3));
632 player.object_attach = NULL; // object was attached, no longer keep it scheduled for attachment
633 print_to(player, "^2SANDBOX - INFO: ^7Object attached successfully");
635 LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " attached objects at origin ^3", vtos(e.origin));
636 return true;
637 }
638 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");
639 return true;
640 case "remove":
641 // removes e if it was attached
642 e = sandbox_ObjectEdit_Get(player, true);
643 if(e != NULL)
644 {
646 print_to(player, "^2SANDBOX - INFO: ^7Child objects detached successfully");
648 LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " detached objects at origin ^3", vtos(e.origin));
649 return true;
650 }
651 print_to(player, "^1SANDBOX - WARNING: ^7Child objects could not be detached. Make sure you are facing an object that you have edit rights over");
652 return true;
653 }
654 return true;
655
656 // ---------------- COMMAND: OBJECT, EDIT ----------------
657 case "object_edit":
658 if(argv(2) == "")
659 {
660 print_to(player, "^1SANDBOX - WARNING: ^7Too few parameters. You must specify a property to edit");
661 return true;
662 }
663
664 e = sandbox_ObjectEdit_Get(player, true);
665 if(e != NULL)
666 {
667 switch(argv(2))
668 {
669 case "skin":
670 e.skin = stof(argv(3));
671 break;
672 case "alpha":
673 e.alpha = stof(argv(3));
674 break;
675 case "color_main":
676 e.colormod = stov(argv(3));
677 break;
678 case "color_glow":
679 e.glowmod = stov(argv(3));
680 break;
681 case "frame":
682 e.frame = stof(argv(3));
683 break;
684 case "scale":
686 break;
687 case "solidity":
688 switch(argv(3))
689 {
690 case "0": // non-solid
691 e.solid = SOLID_TRIGGER;
692 break;
693 case "1": // solid
694 e.solid = SOLID_BBOX;
695 break;
696 default:
697 break;
698 }
699 case "physics":
700 switch(argv(3))
701 {
702 case "0": // static
704 break;
705 case "1": // movable
707 break;
708 case "2": // physical
710 break;
711 default:
712 break;
713 }
714 break;
715 case "force":
716 e.damageforcescale = stof(argv(3));
717 break;
718 case "material":
719 strfree(e.material);
720 if(argv(3))
721 {
722 for (j = 1; j <= 5; ++j) // precache material sounds, 5 in total
723 precache_sound(strcat("object/impact_", argv(3), "_", ftos(j), ".wav"));
724 e.material = strzone(argv(3));
725 }
726 else
727 e.material = string_null; // no material
728 break;
729 default:
730 print_to(player, "^1SANDBOX - WARNING: ^7Invalid object property. For usage information, type 'sandbox help'");
731 return true;
732 }
733
734 // update last editing time
735 strcpy(e.message2, strftime(true, "%d-%m-%Y %H:%M:%S"));
736
738 LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " edited property ^3", argv(2), " ^7of an object at origin ^3", vtos(e.origin));
739 return true;
740 }
741
742 print_to(player, "^1SANDBOX - WARNING: ^7Object could not be edited. Make sure you are facing an object that you have edit rights over");
743 return true;
744
745 // ---------------- COMMAND: OBJECT, CLAIM ----------------
746 case "object_claim":
747 // if the player can edit an object but is not its owner, this can be used to claim that object
748 if(player.crypto_idfp == "")
749 {
750 print_to(player, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects");
751 return true;
752 }
753 e = sandbox_ObjectEdit_Get(player, true);
754 if(e != NULL)
755 {
756 // update the owner's name
757 // Do this before checking if you're already the owner and skipping if such, so we
758 // also update the player's nickname if they changed it (but has the same player UID)
759 if(e.netname != player.netname)
760 {
761 strcpy(e.netname, player.netname);
762 print_to(player, "^2SANDBOX - INFO: ^7Object owner name updated");
763 }
764
765 if(e.crypto_idfp == player.crypto_idfp)
766 {
767 print_to(player, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim");
768 return true;
769 }
770
771 strcpy(e.crypto_idfp, player.crypto_idfp);
772
773 print_to(player, "^2SANDBOX - INFO: ^7Object claimed successfully");
774 }
775 print_to(player, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over");
776 return true;
777
778 // ---------------- COMMAND: OBJECT, INFO ----------------
779 case "object_info":
780 // prints public information about the object to the player
781 e = sandbox_ObjectEdit_Get(player, false);
782 if(e != NULL)
783 {
784 switch(argv(2))
785 {
786 case "object":
787 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\""));
788 return true;
789 case "mesh":
790 s = "";
791 FOR_EACH_TAG(e)
792 s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", ");
793 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));
794 return true;
795 case "attachments":
796 // this should show the same info as 'mesh' but for attachments
797 s = "";
798 j = 0;
799 IL_EACH(g_sandbox_objects, it.owner == e,
800 {
801 ++j; // start from 1
802 gettaginfo(e, it.tag_index);
803 s = strcat(s, "^1attachment ", ftos(j), "^7 has mesh \"^3", it.model, "^7\" at animation frame ^3", ftos(it.frame));
804 s = strcat(s, "^7 and is attached to bone \"^5", gettaginfo_name, "^7\", ");
805 });
806 if(j) // object contains attachments
807 print_to(player, strcat("^2SANDBOX - INFO: ^7Object contains the following ^1", ftos(j), "^7 attachment(s): ", s));
808 else
809 print_to(player, "^2SANDBOX - INFO: ^7Object contains no attachments");
810 return true;
811 }
812 }
813 print_to(player, "^1SANDBOX - WARNING: ^7No information could be found. Make sure you are facing an object");
814 return true;
815
816 // ---------------- COMMAND: DEFAULT ----------------
817 default:
818 print_to(player, "Invalid command. For usage information, type 'sandbox help'");
819 return true;
820 }
821 }
822}
823
824MUTATOR_HOOKFUNCTION(sandbox, SV_StartFrame)
825{
827 return;
828 if(time < autosave_time)
829 return;
831
833
834 return true;
835}
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:237
#define IS_PLAYER(s)
Definition player.qh:243
#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:129
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:98
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:97
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:97
void sandbox_Database_Save()
entity sandbox_ObjectPort_Load(entity this, string s, float database)
void sandbox_ObjectFunction_Think(entity this)
Definition sv_sandbox.qc:68
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:554
#define IS_REAL_CLIENT(v)
Definition utils.qh:17
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:50
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
Definition vector.qh:8
ERASEABLE vector RoundPerfectVector(vector v)
Definition vector.qh:206