]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/mutators/sandbox.qc
Ah, it seems tag counting should start from 1
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / sandbox.qc
index 96b29acabb5b28832889d240274cb8db9de99371..7de8f4cb053cbee05b02deef4eb03984d5d20db3 100644 (file)
@@ -1,3 +1,4 @@
+const float MAX_STORAGE_ATTACHMENTS = 16;
 float object_count;
 .string object_clipboard;
 .entity object_attach;
@@ -29,9 +30,10 @@ void sandbox_ObjectFunction_Touch()
        pointparticles(particleeffectnum(strcat("impact_", self.material)), self.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10
 }
 
-entity sandbox_ObjectEdit_Get()
+entity sandbox_ObjectEdit_Get(float permissions)
 {
        // returns the traced entity if the player can edit it, and world if not
+       // if permissions if FALSE, the object is returned regardless of editing rights
        // attached objects are SOLID_NOT and don't risk getting traced
 
        makevectors(self.v_angle);
@@ -39,9 +41,11 @@ entity sandbox_ObjectEdit_Get()
 
        if(trace_ent.classname != "object")
                return world; // entity is not an object
+       if(!permissions)
+               return trace_ent; // don't check permissions, anyone can edit this object
        if(!trace_ent.crypto_idfp)
                return trace_ent; // the player who spawned this object did not have an UID, so anyone can edit it
-       else if not(trace_ent.crypto_idfp != self.crypto_idfp && !autocvar_g_sandbox_editor_free)
+       if not(trace_ent.crypto_idfp != self.crypto_idfp && autocvar_g_sandbox_editor_free < 2)
                return trace_ent; // object does not belong to the player, and players can only edit their own objects on this server
        return world;
 }
@@ -56,12 +60,13 @@ void sandbox_ObjectEdit_Scale(entity e, float f)
        }
 }
 
-.float old_movetype;
+void sandbox_ObjectAttach_Remove(entity e);
 void sandbox_ObjectAttach_Set(entity e, entity parent, string s)
 {
        // attaches e to parent on string s
 
-       e.old_movetype = e.movetype; // persist this
+       // we can't attach to an attachment, for obvious reasons
+       sandbox_ObjectAttach_Remove(e);
 
        e.movetype = MOVETYPE_FOLLOW;
        e.solid = SOLID_NOT;
@@ -80,7 +85,7 @@ void sandbox_ObjectAttach_Remove(entity e)
        {
                if(head.owner == e)
                {
-                       head.movetype = head.old_movetype; // revert to previous physics
+                       head.movetype = MOVETYPE_TOSS; // default
                        head.solid = SOLID_BBOX;
                        head.takedamage = DAMAGE_AIM;
 
@@ -149,52 +154,106 @@ void sandbox_ObjectRemove(entity e)
        object_count -= 1;
 }
 
+string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't defined as a global
+
 string sandbox_ObjectPort_Save(entity e, float database)
 {
-       // save object properties
+       // TODO: Store attachement bone for attached objects as well!
+
+       // save object properties, and return them as a string
+       float i;
        string s;
+       entity head;
+
+       for(head = world; (head = find(head, classname, "object")); )
+       {
+               // the main object needs to be first in the array [0], with attached objects following
+               float slot;
+               if(head == e) // this is the main object, make it first
+                       slot = 0;
+               else if(head.owner == e) // child object, list them in order
+               {
+                       i += 1; // children start from 1
+                       slot = i;
+               }
+               else
+                       continue;
 
-       s = strcat(e.model, " ");
-       s = strcat(s, ftos(e.skin), " ");
-       s = strcat(s, ftos(e.alpha), " ");
-       s = strcat(s, sprintf("\"%.9v\"", e.colormod), " ");
-       s = strcat(s, sprintf("\"%.9v\"", e.glowmod), " ");
-       s = strcat(s, ftos(e.frame), " ");
-       s = strcat(s, ftos(e.scale), " ");
-       s = strcat(s, ftos(e.movetype), " ");
-       s = strcat(s, ftos(e.damageforcescale), " ");
-       if(e.material)  s = strcat(s, e.material, " "); else    s = strcat(s, "- "); // none
-       if(database)
+               port_string[slot] = strcat(port_string[slot], head.model, " ");
+               port_string[slot] = strcat(port_string[slot], ftos(head.skin), " ");
+               port_string[slot] = strcat(port_string[slot], ftos(head.alpha), " ");
+               port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.colormod), " ");
+               port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.glowmod), " ");
+               port_string[slot] = strcat(port_string[slot], ftos(head.frame), " ");
+               port_string[slot] = strcat(port_string[slot], ftos(head.scale), " ");
+               port_string[slot] = strcat(port_string[slot], ftos(head.movetype), " ");
+               port_string[slot] = strcat(port_string[slot], ftos(head.damageforcescale), " ");
+               if(head.material)       port_string[slot] = strcat(port_string[slot], head.material, " ");      else    port_string[slot] = strcat(port_string[slot], "- "); // none
+               if(database)
+               {
+                       if(head.crypto_idfp)    port_string[slot] = strcat(port_string[slot], head.crypto_idfp, " ");   else    port_string[slot] = strcat(port_string[slot], "- "); // none
+                       port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.origin), " ");
+                       port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.angles), " ");
+               }
+       }
+
+       // now apply the array to a simple string, with the ; symbol separating objects
+       for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i)
        {
-               if(e.crypto_idfp)       s = strcat(s, e.crypto_idfp, " ");      else    s = strcat(s, "- "); // none
-               s = strcat(s, sprintf("\"%.9v\"", e.origin), " ");
-               s = strcat(s, sprintf("\"%.9v\"", e.angles), " ");
+               if(port_string[i])
+                       s = strcat(s, port_string[i], "; ");
+               port_string[i] = string_null; // fully clear the string
        }
 
        return s;
 }
 
-void sandbox_ObjectPort_Load(entity e, string s, float database)
+entity sandbox_ObjectPort_Load(string s, float database)
 {
-       // load object properties
-       tokenize_console(s);
-
-       setmodel(e, argv(0));
-       e.skin = stof(argv(1));
-       e.alpha = stof(argv(2));
-       e.colormod = stov(argv(3));
-       e.glowmod = stov(argv(4));
-       e.frame = stof(argv(5));
-       sandbox_ObjectEdit_Scale(e, stof(argv(6)));
-       e.movetype = stof(argv(7));
-       e.damageforcescale = stof(argv(8));
-       if(e.material)  strunzone(e.material);  if(argv(9) != "-")      e.material = strzone(argv(9));  else    e.material = string_null;
-       if(database)
+       // TODO: Store attachement bone for attached objects as well!
+
+       // load object properties, and spawn a new object with them
+       float n, i;
+       entity e, parent;
+
+       // separate objects between the ; symbols
+       n = tokenizebyseparator(s, "; ");
+       for(i = 0; i < n; ++i)
+               port_string[i] = argv(i);
+
+       // now separate and apply the properties of each object
+       for(i = 0; i < n; ++i)
        {
-               if(e.crypto_idfp)       strunzone(e.crypto_idfp);       if(argv(10) != "-")     e.crypto_idfp = strzone(argv(10));      else    e.crypto_idfp = string_null;
-               setorigin(e, stov(argv(11)));
-               e.angles = stov(argv(12));
+               tokenize_console(port_string[i]);
+               e = sandbox_ObjectSpawn(database);
+
+               setmodel(e, argv(0));
+               e.skin = stof(argv(1));
+               e.alpha = stof(argv(2));
+               e.colormod = stov(argv(3));
+               e.glowmod = stov(argv(4));
+               e.frame = stof(argv(5));
+               sandbox_ObjectEdit_Scale(e, stof(argv(6)));
+               e.movetype = stof(argv(7));
+               e.damageforcescale = stof(argv(8));
+               if(e.material)  strunzone(e.material);  if(argv(9) != "-")      e.material = strzone(argv(9));  else    e.material = string_null;
+               if(database)
+               {
+                       if(e.crypto_idfp)       strunzone(e.crypto_idfp);       if(argv(10) != "-")     e.crypto_idfp = strzone(argv(10));      else    e.crypto_idfp = string_null;
+                       setorigin(e, stov(argv(11)));
+                       e.angles = stov(argv(12));
+               }
+
+               if(!i) // parent object, set it as such and leave it be
+                       parent = e;
+               else // child object, attach it to the parent
+                       sandbox_ObjectAttach_Set(e, parent, "");
        }
+
+       for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i)
+               port_string[i] = string_null; // fully clear the string
+
+       return e;
 }
 
 void sandbox_Database_Save()
@@ -211,13 +270,11 @@ void sandbox_Database_Save()
 
        for(head = world; (head = find(head, classname, "object")); )
        {
-               // Unfortunately, attached objects cannot be persisted yet. That's because the parent is specified as an entity,
-               // but only properties can be saved to this file, leaving no way to identify the owner once all objects are spawned
-               // TODO: Find some way to fix this!
+               // attached objects are persisted separately, ignore them here
                if(head.owner != world)
                        continue;
 
-               // use a line for each object, listing all properties
+               // use a line of text for each object, listing all properties
                fputs(file_get, strcat(sandbox_ObjectPort_Save(head, TRUE), "\n"));
        }
        fclose(file_get);
@@ -249,8 +306,7 @@ void sandbox_Database_Load()
                                continue;
 
                        entity e;
-                       e = sandbox_ObjectSpawn(TRUE);
-                       sandbox_ObjectPort_Load(e, file_read, TRUE);
+                       e = sandbox_ObjectPort_Load(file_read, TRUE);
 
                        if(e.material)
                        {
@@ -287,7 +343,6 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                print_to(self, "^7\"^2object_spawn ^3models/foo/bar.md3^7\" spawns a new object in front of the player, and gives it the specified model");
                                print_to(self, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects");
                                print_to(self, "^7\"^2object_duplicate ^3value^7\" duplicates the object. 'copy' copies the object, 'paste' puts it in front of the player");
-                               print_to(self, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it");
                                print_to(self, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects");
                                print_to(self, "^7Attachment properties for ^2object_attach^7:");
                                print_to(self, "^3get ^7- selects the object you are facing as the object to be attached");
@@ -304,10 +359,12 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                print_to(self, "^3physics value ^7- object physics, 0 = static, 1 = movable, 2 = physical");
                                print_to(self, "^3force value ^7- amount of force applied to objects that are shot");
                                print_to(self, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh");
+                               print_to(self, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it");
+                               print_to(self, "^7\"^2object_info ^3value^7\" shows public information about the object. 'mesh' shows model information");
                                print_to(self, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects");
                                return TRUE;
 
-                       // ---------------- COMMAND: SPAWN OBJECT ----------------
+                       // ---------------- COMMAND: OBJECT, SPAWN ----------------
                        case "object_spawn":
                                if(object_count >= autocvar_g_sandbox_editor_maxobjects)
                                {
@@ -332,9 +389,9 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                        print(strcat("^3SANDBOX - SERVER: ^7", self.netname, " spawned an object at origin ^3", vtos(e.origin), "\n"));
                                return TRUE;
 
-                       // ---------------- COMMAND: REMOVE OBJECT ----------------
+                       // ---------------- COMMAND: OBJECT, REMOVE ----------------
                        case "object_remove":
-                               e = sandbox_ObjectEdit_Get();
+                               e = sandbox_ObjectEdit_Get(TRUE);
                                if(e != world)
                                {
                                        if(autocvar_g_sandbox_info > 0)
@@ -346,13 +403,13 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                print_to(self, "^1SANDBOX - WARNING: ^7Object could not be removed. Make sure you are facing an object that you have edit rights over");
                                return TRUE;
 
-                       // ---------------- COMMAND: DUPLICATE OBJECT ----------------
+                       // ---------------- COMMAND: OBJECT, DUPLICATE ----------------
                        case "object_duplicate":
                                switch(argv(2))
                                {
                                        case "copy":
                                                // copies customizable properties of the selected object to the clipboard
-                                               e = sandbox_ObjectEdit_Get(); // you can only copy objects you can edit, so this works
+                                               e = sandbox_ObjectEdit_Get(autocvar_g_sandbox_editor_free); // can we copy objects we can't edit?
                                                if(e != world)
                                                {
                                                        if(self.object_clipboard)
@@ -362,7 +419,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                                        print_to(self, "^2SANDBOX - INFO: ^7Object copied to clipboard");
                                                        return TRUE;
                                                }
-                                               print_to(self, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have edit rights over");
+                                               print_to(self, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have copy rights over");
                                                return TRUE;
 
                                        case "paste":
@@ -378,8 +435,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                                        return TRUE;
                                                }
 
-                                               e = sandbox_ObjectSpawn(FALSE);
-                                               sandbox_ObjectPort_Load(e, self.object_clipboard, FALSE);
+                                               e = sandbox_ObjectPort_Load(self.object_clipboard, FALSE);
 
                                                print_to(self, "^2SANDBOX - INFO: ^7Object pasted successfully");
                                                if(autocvar_g_sandbox_info > 0)
@@ -388,38 +444,13 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                }
                                return TRUE;
 
-                       // ---------------- COMMAND: CLAIM OBJECT ----------------
-                       case "object_claim":
-                               // if the player can edit an object but is not its owner, this can be used to claim that object
-                               if(self.crypto_idfp == "")
-                               {
-                                       print_to(self, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects");
-                                       return TRUE;
-                               }
-                               e = sandbox_ObjectEdit_Get();
-                               if(e != world)
-                               {
-                                       if(e.crypto_idfp == self.crypto_idfp)
-                                       {
-                                               print_to(self, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim");
-                                               return TRUE;
-                                       }
-
-                                       if(e.crypto_idfp)
-                                               strunzone(e.crypto_idfp);
-                                       e.crypto_idfp = self.crypto_idfp;
-                                       print_to(self, "^2SANDBOX - INFO: ^7Object claimed successfully");
-                               }
-                               print_to(self, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over");
-                               return TRUE;
-
-                       // ---------------- COMMAND: ATTACH OBJECT ----------------
+                       // ---------------- COMMAND: OBJECT, ATTACH ----------------
                        case "object_attach":
                                switch(argv(2))
                                {
                                        case "get":
                                                // select e as the object as meant to be attached
-                                               e = sandbox_ObjectEdit_Get();
+                                               e = sandbox_ObjectEdit_Get(TRUE);
                                                if(e != world)
                                                {
                                                        self.object_attach = e;
@@ -436,7 +467,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                                }
 
                                                // attaches the previously selected object to e
-                                               e = sandbox_ObjectEdit_Get();
+                                               e = sandbox_ObjectEdit_Get(TRUE);
                                                if(e != world)
                                                {
                                                        sandbox_ObjectAttach_Set(self.object_attach, e, argv(3));
@@ -450,7 +481,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                                return TRUE;
                                        case "remove":
                                                // removes e if it was attached
-                                               e = sandbox_ObjectEdit_Get();
+                                               e = sandbox_ObjectEdit_Get(TRUE);
                                                if(e != world)
                                                {
                                                        sandbox_ObjectAttach_Remove(e);
@@ -464,7 +495,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                }
                                return TRUE;
 
-                       // ---------------- COMMAND: EDIT OBJECT ----------------
+                       // ---------------- COMMAND: OBJECT, EDIT ----------------
                        case "object_edit":
                                if(!argv(2))
                                {
@@ -472,7 +503,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                        return TRUE;
                                }
 
-                               e = sandbox_ObjectEdit_Get();
+                               e = sandbox_ObjectEdit_Get(TRUE);
                                if(e != world)
                                {
                                        switch(argv(2))
@@ -538,6 +569,50 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                print_to(self, "^1SANDBOX - WARNING: ^7Object could not be edited. Make sure you are facing an object that you have edit rights over");
                                return TRUE;
 
+                       // ---------------- COMMAND: OBJECT, CLAIM ----------------
+                       case "object_claim":
+                               // if the player can edit an object but is not its owner, this can be used to claim that object
+                               if(self.crypto_idfp == "")
+                               {
+                                       print_to(self, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects");
+                                       return TRUE;
+                               }
+                               e = sandbox_ObjectEdit_Get(TRUE);
+                               if(e != world)
+                               {
+                                       if(e.crypto_idfp == self.crypto_idfp)
+                                       {
+                                               print_to(self, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim");
+                                               return TRUE;
+                                       }
+
+                                       if(e.crypto_idfp)
+                                               strunzone(e.crypto_idfp);
+                                       e.crypto_idfp = self.crypto_idfp;
+                                       print_to(self, "^2SANDBOX - INFO: ^7Object claimed successfully");
+                               }
+                               print_to(self, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over");
+                               return TRUE;
+
+                       // ---------------- COMMAND: OBJECT, INFO ----------------
+                       case "object_info":
+                               // prints public information about the object to the player
+                               e = sandbox_ObjectEdit_Get(FALSE);
+                               if(e != world)
+                               {
+                                       switch(argv(2))
+                                       {
+                                               case "mesh":
+                                                       string tags;
+                                                       for(i = 1; gettaginfo(e, i); i++)
+                                                               tags = strcat(tags, "^5", gettaginfo_name, "^7, ");
+                                                       print_to(self, strcat("^2SANDBOX - INFO: ^7Object mesh is ^3", e.model, "^7 at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", tags));
+                                                       return TRUE;
+                                       }
+                               }
+                               print_to(self, "^1SANDBOX - WARNING: ^7No information could not be found. Make sure you are facing an object");
+                               return TRUE;
+
                        // ---------------- COMMAND: DEFAULT ----------------
                        default:
                                print_to(self, "Invalid command. For usage information, type 'sandbox help'");
@@ -562,7 +637,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerPreThink)
        entity e;
        float grab;
 
-       e = sandbox_ObjectEdit_Get();
+       e = sandbox_ObjectEdit_Get(TRUE);
        if(e != world && vlen(e.origin - self.origin) <= autocvar_g_sandbox_editor_distance_edit)
                grab = TRUE;
 
@@ -589,7 +664,7 @@ MUTATOR_HOOKFUNCTION(sandbox_StartFrame)
 {
        if(!autocvar_g_sandbox_storage_autosave)
                return FALSE;
-       if(time < autocvar_g_sandbox_storage_autosave)
+       if(time < autosave_time)
                return FALSE;
        autosave_time = time + autocvar_g_sandbox_storage_autosave;