]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/mutators/sandbox.qc
For now though, comment EF_SELECTABLE out. Don't want objects always turning bright...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / sandbox.qc
index e68a24a20e062b24ae20b4a63fa8198f76079102..ea0823ef73346e1f0806c294135cec7230fb0dc2 100644 (file)
@@ -1,6 +1,5 @@
 const float MAX_STORAGE_ATTACHMENTS = 16;
 float object_count;
-.string object_clipboard;
 .entity object_attach;
 .string material;
 
@@ -45,7 +44,7 @@ entity sandbox_ObjectEdit_Get(float 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
-       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;
 }
@@ -60,6 +59,7 @@ 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)
 {
@@ -68,6 +68,7 @@ void sandbox_ObjectAttach_Set(entity e, entity parent, string s)
        // we can't attach to an attachment, for obvious reasons
        sandbox_ObjectAttach_Remove(e);
 
+       e.old_movetype = e.movetype; // persist physics
        e.movetype = MOVETYPE_FOLLOW;
        e.solid = SOLID_NOT;
        e.takedamage = DAMAGE_NO;
@@ -85,16 +86,18 @@ void sandbox_ObjectAttach_Remove(entity e)
        {
                if(head.owner == e)
                {
-                       head.movetype = MOVETYPE_TOSS; // default
+                       vector org;
+                       head.movetype = head.old_movetype; // restore persisted physics
                        head.solid = SOLID_BBOX;
                        head.takedamage = DAMAGE_AIM;
 
+                       org = gettaginfo(head, 0);
                        setattachment(head, world, "");
                        head.owner = world;
 
-                       // objects reset origin and angles when detached, so apply the parent's to prevent teleporting
-                       setorigin(head, e.origin);
-                       head.angles = e.angles;
+                       // objects change origin and angles when detached, so apply previous position
+                       setorigin(head, org);
+                       head.angles = e.angles; // don't allow detached objects to spin or roll
                }
        }
 }
@@ -114,6 +117,7 @@ entity sandbox_ObjectSpawn(float database)
        e.skin = 0;
        e.material = string_null;
        e.touch = sandbox_ObjectFunction_Touch;
+       //e.effects |= EF_SELECTABLE;
 
        if(!database)
        {
@@ -158,8 +162,6 @@ string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't def
 
 string sandbox_ObjectPort_Save(entity e, float database)
 {
-       // TODO: Store attachement bone for attached objects as well!
-
        // save object properties, and return them as a string
        float i;
        string s;
@@ -167,33 +169,53 @@ string sandbox_ObjectPort_Save(entity e, float database)
 
        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
+               // the main object needs to be first in the array [0] with attached objects following
+               float slot, physics;
+               if(head == e) // this is the main object, place it first
+               {
                        slot = 0;
+                       physics = head.movetype; // applied physics are normal physics for parents
+               }
                else if(head.owner == e) // child object, list them in order
                {
                        i += 1; // children start from 1
                        slot = i;
+                       physics = head.old_movetype; // persisted physics are normal physics for children
+                       gettaginfo(head.owner, head.tag_index); // get the name of the tag our object is attached to, used further below
                }
                else
                        continue;
 
-               port_string[slot] = strcat(port_string[slot], head.model, " ");
+               // ---------------- OBJECT PROPERTY STORAGE: SAVE ----------------
+               if(slot)
+               {
+                       // properties stored only for child objects
+                       if(gettaginfo_name)     port_string[slot] = strcat(port_string[slot], "\"", gettaginfo_name, "\" ");    else    port_string[slot] = strcat(port_string[slot], "- "); // none
+               }
+               else
+               {
+                       // properties stored only for parent objects
+                       if(database)
+                       {
+                               port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.origin), " ");
+                               port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.angles), " ");
+                       }
+               }
+               // properties stored for all objects
+               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(physics), " ");
                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(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), " ");
+                       // properties stored only for the database
+                       if(head.crypto_idfp)    port_string[slot] = strcat(port_string[slot], "\"", head.crypto_idfp, "\" ");   else    port_string[slot] = strcat(port_string[slot], "- "); // none
                }
        }
 
@@ -210,8 +232,6 @@ string sandbox_ObjectPort_Save(entity e, float database)
 
 entity sandbox_ObjectPort_Load(string s, float 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;
@@ -224,30 +244,48 @@ entity sandbox_ObjectPort_Load(string s, float database)
        // now separate and apply the properties of each object
        for(i = 0; i < n; ++i)
        {
+               float argv_num;
+               string tagname;
+               argv_num = 0;
                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;
+               // ---------------- OBJECT PROPERTY STORAGE: LOAD ----------------
+               if(i)
+               {
+                       // properties stored only for child objects
+                       if(argv(argv_num) != "-")       tagname = argv(argv_num);       else tagname = string_null;     ++argv_num;
+               }
+               else
+               {
+                       // properties stored only for parent objects
+                       if(database)
+                       {
+                               setorigin(e, stov(argv(argv_num)));     ++argv_num;
+                               e.angles = stov(argv(argv_num));        ++argv_num;
+                       }
+                       parent = e; // mark parent objects as such
+               }
+               // properties stored for all objects
+               setmodel(e, argv(argv_num));    ++argv_num;
+               e.skin = stof(argv(argv_num));  ++argv_num;
+               e.alpha = stof(argv(argv_num)); ++argv_num;
+               e.colormod = stov(argv(argv_num));      ++argv_num;
+               e.glowmod = stov(argv(argv_num));       ++argv_num;
+               e.frame = stof(argv(argv_num)); ++argv_num;
+               sandbox_ObjectEdit_Scale(e, stof(argv(argv_num)));      ++argv_num;
+               e.movetype = e.old_movetype = stof(argv(argv_num));     ++argv_num;
+               e.damageforcescale = stof(argv(argv_num));      ++argv_num;
+               if(e.material)  strunzone(e.material);  if(argv(argv_num) != "-")       e.material = strzone(argv(argv_num));   else    e.material = string_null;       ++argv_num;
                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));
+                       // properties stored only for the database
+                       if(e.crypto_idfp)       strunzone(e.crypto_idfp);       if(argv(argv_num) != "-")       e.crypto_idfp = strzone(argv(argv_num));        else    e.crypto_idfp = string_null;    ++argv_num;
                }
 
-               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, "");
+               // attach last
+               if(i)
+                       sandbox_ObjectAttach_Set(e, parent, tagname);
        }
 
        for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i)
@@ -336,20 +374,24 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                {
                        entity e;
                        float i;
+                       string s;
 
                        // ---------------- COMMAND: HELP ----------------
                        case "help":
                                print_to(self, "You can use the following sandbox commands:");
                                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_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original");
+                               print_to(self, "^Properties for ^2object_duplicate^7:");
+                               print_to(self, "^3copy value ^7- copies the properties of the object to the specified client cvar");
+                               print_to(self, "^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\"");
                                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, "^7Properties for ^2object_attach^7:");
                                print_to(self, "^3get ^7- selects the object you are facing as the object to be attached");
                                print_to(self, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone");
                                print_to(self, "^3remove ^7- detaches all objects from the object you are facing");
                                print_to(self, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects");
-                               print_to(self, "^7Object properties for ^2object_edit^7:");
+                               print_to(self, "^7Properties for ^2object_edit^7:");
                                print_to(self, "^3skin value ^7- changes the skin of the object");
                                print_to(self, "^3alpha value ^7- sets object transparency");
                                print_to(self, "^3colormod \"value_x value_y value_z\" ^7- main object color");
@@ -360,6 +402,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                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;
 
@@ -407,23 +450,23 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                switch(argv(2))
                                {
                                        case "copy":
-                                               // copies customizable properties of the selected object to the clipboard
-                                               e = sandbox_ObjectEdit_Get(TRUE); // you can only copy objects you can edit, so this works
+                                               // copies customizable properties of the selected object to the clipboard cvar
+                                               e = sandbox_ObjectEdit_Get(autocvar_g_sandbox_editor_free); // can we copy objects we can't edit?
                                                if(e != world)
                                                {
-                                                       if(self.object_clipboard)
-                                                               strunzone(self.object_clipboard);
-                                                       self.object_clipboard = strzone(sandbox_ObjectPort_Save(e, FALSE));
+                                                       s = sandbox_ObjectPort_Save(e, FALSE);
+                                                       s = strreplace("\"", "\\\"", s);
+                                                       stuffcmd(self, strcat("set ", argv(3), " \"", s, "\""));
 
                                                        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":
-                                               // spawns a new object using the properties in the player's clipboard
-                                               if(!self.object_clipboard) // no object in clipboard
+                                               // spawns a new object using the properties in the player's clipboard cvar
+                                               if(!argv(3)) // no object in clipboard
                                                {
                                                        print_to(self, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it");
                                                        return TRUE;
@@ -433,8 +476,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                                        print_to(self, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time"));
                                                        return TRUE;
                                                }
-
-                                               e = sandbox_ObjectPort_Load(self.object_clipboard, FALSE);
+                                               e = sandbox_ObjectPort_Load(argv(3), FALSE);
 
                                                print_to(self, "^2SANDBOX - INFO: ^7Object pasted successfully");
                                                if(autocvar_g_sandbox_info > 0)
@@ -603,7 +645,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                        {
                                                case "mesh":
                                                        string tags;
-                                                       for(i = 0; gettaginfo(e, i); i++)
+                                                       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;
@@ -646,18 +688,6 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerPreThink)
        return FALSE;
 }
 
-MUTATOR_HOOKFUNCTION(sandbox_ClientDisconnect)
-{
-       // unzone the player's clipboard if it's not empty
-       if(self.object_clipboard)
-       {
-               strunzone(self.object_clipboard);
-               self.object_clipboard = string_null;
-       }
-
-       return FALSE;
-}
-
 float autosave_time;
 MUTATOR_HOOKFUNCTION(sandbox_StartFrame)
 {
@@ -677,7 +707,6 @@ MUTATOR_DEFINITION(sandbox)
        MUTATOR_HOOK(SV_ParseClientCommand, sandbox_PlayerCommand, CBC_ORDER_ANY);
        MUTATOR_HOOK(SV_StartFrame, sandbox_StartFrame, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerPreThink, sandbox_PlayerPreThink, CBC_ORDER_ANY);
-       MUTATOR_HOOK(ClientDisconnect, sandbox_ClientDisconnect, CBC_ORDER_ANY);
 
        MUTATOR_ONADD
        {