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);
201 vector view_org = this.origin + this.view_ofs;
203 setorigin(e, trace_endpos);
205 }
206
208
209 ++object_count;
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;
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; // 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 = sandbox_ObjectPort_Load(NULL, file_read, true);
439
440 if(e.material)
441 {
442 // since objects are being loaded for the first time, precache material sounds for each
443 for (i = 1; i <= 5; ++i) // 5 sounds in total
444 precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".wav"));
445 }
446 }
448 LOG_INFO("^3SANDBOX - SERVER: ^7successfully loaded storage file ^3", file_name);
449 }
450 fclose(file_get);
451}
452
454{
455 if(MUTATOR_RETURNVALUE) // command was already handled?
456 return;
457
458 entity player = M_ARGV(0, entity);
459 string cmd_name = M_ARGV(1, string);
460 int cmd_argc = M_ARGV(2, int);
461
462 if(cmd_name == "g_sandbox")
463 {
464 if(autocvar_g_sandbox_readonly || MUTATOR_CALLHOOK(Sandbox_EditAllowed, player))
465 {
466 print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active, but in read-only mode. Sandbox commands cannot be used");
467 return true;
468 }
469 if(cmd_argc < 2)
470 {
471 print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active. For usage information, type 'sandbox help'");
472 return true;
473 }
474
475 switch(argv(1))
476 {
477 entity e;
478 int j;
479 string s;
480
481 // ---------------- COMMAND: HELP ----------------
482 case "help":
483 print_to(player, "You can use the following sandbox commands:");
484 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");
485 print_to(player, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects");
486 print_to(player, "^7\"^2object_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original");
487 print_to(player, "^3copy value ^7- copies the properties of the object to the specified client cvar");
488 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\"");
489 print_to(player, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects");
490 print_to(player, "^3get ^7- selects the object you are facing as the object to be attached");
491 print_to(player, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone");
492 print_to(player, "^3remove ^7- detaches all objects from the object you are facing");
493 print_to(player, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects");
494 print_to(player, "^3skin value ^7- changes the skin of the object");
495 print_to(player, "^3alpha value ^7- sets object transparency");
496 print_to(player, "^3colormod \"value_x value_y value_z\" ^7- main object color");
497 print_to(player, "^3glowmod \"value_x value_y value_z\" ^7- glow object color");
498 print_to(player, "^3frame value ^7- object animation frame, for self-animated models");
499 print_to(player, "^3scale value ^7- changes object scale. 0.5 is half size and 2 is double size");
500 print_to(player, "^3solidity value ^7- object collisions, 0 = non-solid, 1 = solid");
501 print_to(player, "^3physics value ^7- object physics, 0 = static, 1 = movable, 2 = physical");
502 print_to(player, "^3force value ^7- amount of force applied to objects that are shot");
503 print_to(player, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh");
504 print_to(player, "^7\"^2object_claim^7\" sets the player as the owner of the object, if they have the right to edit it");
505 print_to(player, "^7\"^2object_info ^3value^7\" shows public information about the object");
506 print_to(player, "^3object ^7- prints general information about the object, such as owner and creation / editing date");
507 print_to(player, "^3mesh ^7- prints information about the object's mesh, including skeletal bones");
508 print_to(player, "^3attachments ^7- prints information about the object's attachments");
509 print_to(player, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects");
510 return true;
511
512 // ---------------- COMMAND: OBJECT, SPAWN ----------------
513 case "object_spawn":
514 if(time < player.object_flood)
515 {
516 print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object"));
517 return true;
518 }
519 player.object_flood = time + autocvar_g_sandbox_editor_flood;
521 {
522 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"));
523 return true;
524 }
525 if(cmd_argc < 3)
526 {
527 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");
528 return true;
529 }
530 if (!(fexists(argv(2))))
531 {
532 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");
533 return true;
534 }
535
536 e = sandbox_ObjectSpawn(player, false);
537 precache_model(argv(2));
538 _setmodel(e, argv(2));
539
541 LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " spawned an object at origin ^3", vtos(e.origin));
542 return true;
543
544 // ---------------- COMMAND: OBJECT, REMOVE ----------------
545 case "object_remove":
546 e = sandbox_ObjectEdit_Get(player, true);
547 if(e != NULL)
548 {
550 LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " removed an object at origin ^3", vtos(e.origin));
552 return true;
553 }
554
555 print_to(player, "^1SANDBOX - WARNING: ^7Object could not be removed. Make sure you are facing an object that you have edit rights over");
556 return true;
557
558 // ---------------- COMMAND: OBJECT, DUPLICATE ----------------
559 case "object_duplicate":
560 switch(argv(2))
561 {
562 case "copy":
563 // copies customizable properties of the selected object to the clipboard cvar
564 e = sandbox_ObjectEdit_Get(player, autocvar_g_sandbox_editor_free); // can we copy objects we can't edit?
565 if(e != NULL)
566 {
567 s = sandbox_ObjectPort_Save(e, false);
568 s = strreplace("\"", "\\\"", s);
569 stuffcmd(player, strcat("set ", argv(3), " \"", s, "\""));
570
571 print_to(player, "^2SANDBOX - INFO: ^7Object copied to clipboard");
572 return true;
573 }
574 print_to(player, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have copy rights over");
575 return true;
576
577 case "paste":
578 // spawns a new object using the properties in the player's clipboard cvar
579 if(time < player.object_flood)
580 {
581 print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object"));
582 return true;
583 }
584 player.object_flood = time + autocvar_g_sandbox_editor_flood;
585 if(argv(3) == "") // no object in clipboard
586 {
587 print_to(player, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it");
588 return true;
589 }
591 {
592 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"));
593 return true;
594 }
595 e = sandbox_ObjectPort_Load(player, argv(3), false);
596
597 print_to(player, "^2SANDBOX - INFO: ^7Object pasted successfully");
599 LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " pasted an object at origin ^3", vtos(e.origin));
600 return true;
601 }
602 return true;
603
604 // ---------------- COMMAND: OBJECT, ATTACH ----------------
605 case "object_attach":
606 switch(argv(2))
607 {
608 case "get":
609 // select e as the object as meant to be attached
610 e = sandbox_ObjectEdit_Get(player, true);
611 if(e != NULL)
612 {
613 player.object_attach = e;
614 print_to(player, "^2SANDBOX - INFO: ^7Object selected for attachment");
615 return true;
616 }
617 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");
618 return true;
619 case "set":
620 if(player.object_attach == NULL)
621 {
622 print_to(player, "^1SANDBOX - WARNING: ^7No object selected for attachment. Please select an object to be attached first.");
623 return true;
624 }
625
626 // attaches the previously selected object to e
627 e = sandbox_ObjectEdit_Get(player, true);
628 if(e != NULL)
629 {
630 sandbox_ObjectAttach_Set(player.object_attach, e, argv(3));
631 player.object_attach = NULL; // object was attached, no longer keep it scheduled for attachment
632 print_to(player, "^2SANDBOX - INFO: ^7Object attached successfully");
634 LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " attached objects at origin ^3", vtos(e.origin));
635 return true;
636 }
637 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");
638 return true;
639 case "remove":
640 // removes e if it was attached
641 e = sandbox_ObjectEdit_Get(player, true);
642 if(e != NULL)
643 {
645 print_to(player, "^2SANDBOX - INFO: ^7Child objects detached successfully");
647 LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " detached objects at origin ^3", vtos(e.origin));
648 return true;
649 }
650 print_to(player, "^1SANDBOX - WARNING: ^7Child objects could not be detached. Make sure you are facing an object that you have edit rights over");
651 return true;
652 }
653 return true;
654
655 // ---------------- COMMAND: OBJECT, EDIT ----------------
656 case "object_edit":
657 if(argv(2) == "")
658 {
659 print_to(player, "^1SANDBOX - WARNING: ^7Too few parameters. You must specify a property to edit");
660 return true;
661 }
662
663 e = sandbox_ObjectEdit_Get(player, true);
664 if(e != NULL)
665 {
666 switch(argv(2))
667 {
668 case "skin":
669 e.skin = stof(argv(3));
670 break;
671 case "alpha":
672 e.alpha = stof(argv(3));
673 break;
674 case "color_main":
675 e.colormod = stov(argv(3));
676 break;
677 case "color_glow":
678 e.glowmod = stov(argv(3));
679 break;
680 case "frame":
681 e.frame = stof(argv(3));
682 break;
683 case "scale":
685 break;
686 case "solidity":
687 switch(argv(3))
688 {
689 case "0": // non-solid
690 e.solid = SOLID_TRIGGER;
691 break;
692 case "1": // solid
693 e.solid = SOLID_BBOX;
694 break;
695 default:
696 break;
697 }
698 case "physics":
699 switch(argv(3))
700 {
701 case "0": // static
703 break;
704 case "1": // movable
706 break;
707 case "2": // physical
709 break;
710 default:
711 break;
712 }
713 break;
714 case "force":
715 e.damageforcescale = stof(argv(3));
716 break;
717 case "material":
718 strfree(e.material);
719 if(argv(3))
720 {
721 for (j = 1; j <= 5; ++j) // precache material sounds, 5 in total
722 precache_sound(strcat("object/impact_", argv(3), "_", ftos(j), ".wav"));
723 e.material = strzone(argv(3));
724 }
725 else
726 e.material = string_null; // no material
727 break;
728 default:
729 print_to(player, "^1SANDBOX - WARNING: ^7Invalid object property. For usage information, type 'sandbox help'");
730 return true;
731 }
732
733 // update last editing time
734 strcpy(e.message2, strftime(true, "%d-%m-%Y %H:%M:%S"));
735
737 LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " edited property ^3", argv(2), " ^7of an object at origin ^3", vtos(e.origin));
738 return true;
739 }
740
741 print_to(player, "^1SANDBOX - WARNING: ^7Object could not be edited. Make sure you are facing an object that you have edit rights over");
742 return true;
743
744 // ---------------- COMMAND: OBJECT, CLAIM ----------------
745 case "object_claim":
746 // if the player can edit an object but is not its owner, this can be used to claim that object
747 if(player.crypto_idfp == "")
748 {
749 print_to(player, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects");
750 return true;
751 }
752 e = sandbox_ObjectEdit_Get(player, true);
753 if(e != NULL)
754 {
755 // update the owner's name
756 // Do this before checking if you're already the owner and skipping if such, so we
757 // also update the player's nickname if they changed it (but has the same player UID)
758 if(e.netname != player.netname)
759 {
760 strcpy(e.netname, player.netname);
761 print_to(player, "^2SANDBOX - INFO: ^7Object owner name updated");
762 }
763
764 if(e.crypto_idfp == player.crypto_idfp)
765 {
766 print_to(player, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim");
767 return true;
768 }
769
770 strcpy(e.crypto_idfp, player.crypto_idfp);
771
772 print_to(player, "^2SANDBOX - INFO: ^7Object claimed successfully");
773 }
774 print_to(player, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over");
775 return true;
776
777 // ---------------- COMMAND: OBJECT, INFO ----------------
778 case "object_info":
779 // prints public information about the object to the player
780 e = sandbox_ObjectEdit_Get(player, false);
781 if(e != NULL)
782 {
783 switch(argv(2))
784 {
785 case "object":
786 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\""));
787 return true;
788 case "mesh":
789 s = "";
790 FOR_EACH_TAG(e)
791 s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", ");
792 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));
793 return true;
794 case "attachments":
795 // this should show the same info as 'mesh' but for attachments
796 s = "";
797 j = 0;
798 IL_EACH(g_sandbox_objects, it.owner == e,
799 {
800 ++j; // start from 1
801 gettaginfo(e, it.tag_index);
802 s = strcat(s, "^1attachment ", ftos(j), "^7 has mesh \"^3", it.model, "^7\" at animation frame ^3", ftos(it.frame));
803 s = strcat(s, "^7 and is attached to bone \"^5", gettaginfo_name, "^7\", ");
804 });
805 if(j) // object contains attachments
806 print_to(player, strcat("^2SANDBOX - INFO: ^7Object contains the following ^1", ftos(j), "^7 attachment(s): ", s));
807 else
808 print_to(player, "^2SANDBOX - INFO: ^7Object contains no attachments");
809 return true;
810 }
811 }
812 print_to(player, "^1SANDBOX - WARNING: ^7No information could be found. Make sure you are facing an object");
813 return true;
814
815 // ---------------- COMMAND: DEFAULT ----------------
816 default:
817 print_to(player, "Invalid command. For usage information, type 'sandbox help'");
818 return true;
819 }
820 }
821}
822
823MUTATOR_HOOKFUNCTION(sandbox, SV_StartFrame)
824{
826 return;
827 if(time < autosave_time)
828 return;
830
832
833 return true;
834}
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:218
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:160
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:289
vector WarpZone_UnTransformOrigin(entity wz, vector org)
Transforms origin org backwards through warpzone wz, as the inverse of WarpZone_TransformOrigin.
Definition common.qc:526
vector WarpZone_TransformVAngles(entity wz, vector ang)
Transforms v_angle ang across warpzone wz.
Definition common.qc:505
#define WarpZone_TraceLine(org, end, nomonsters, forent)
Definition common.qh:51
entity WarpZone_trace_transform
Transform accumulator during a trace.
Definition common.qh:40
#define LOG_INFO(...)
Definition log.qh:62
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:133
const int MOVETYPE_FOLLOW
Definition movetypes.qh:145
const int MOVETYPE_PHYSICS
Definition movetypes.qh:146
const int MOVETYPE_TOSS
Definition movetypes.qh:139
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)
vector
Definition self.qh:96
entity entity toucher
Definition self.qh:76
#define settouch(e, f)
Definition self.qh:77
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:57
#define strcpy(this, s)
Definition string.qh:51
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 WarpZone_crosshair_trace_plusvisibletriggers(entity pl)
Definition tracing.qc:576
#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:215