]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/mutators/sandbox.qc
Add a maximum number of objects each player can place at a time. By default, players...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / sandbox.qc
index bf47b1753fd876b38dabf225e59021cf088182bc..6a9a7ed5869c1774c486a7dbad13467429eb3db3 100644 (file)
@@ -1,5 +1,6 @@
 .string object_clipboard;
 .entity object_attach;
+.float object_count;
 .float material;
 
 const float MATERIAL_NONE = 0;
@@ -54,6 +55,7 @@ void sandbox_Object_Touch()
 entity sandbox_EditObject_Get()
 {
        // returns the traced entity if the player can edit it, and world if not
+       // attached objects are SOLID_NOT and don't risk getting traced
 
        makevectors(self.v_angle);
        WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * autocvar_g_sandbox_editor_distance_edit, MOVE_NORMAL, self);
@@ -73,15 +75,17 @@ void sandbox_EditObject_Scale(entity e, float f)
        }
 }
 
+.float old_movetype;
 void sandbox_AttachObject_Set(entity e, entity parent, string s)
 {
        // attaches e to parent on string s
 
+       e.old_movetype = e.movetype; // persist this
+
        e.movetype = MOVETYPE_FOLLOW;
        e.solid = SOLID_NOT;
-       e.takedamage = DAMAGE_NO; // no longer trace it either
+       e.takedamage = DAMAGE_NO;
 
-       setorigin(e, parent.origin);
        setattachment(e, parent, s);
        e.owner = parent;
 }
@@ -95,13 +99,16 @@ void sandbox_AttachObject_Remove(entity e)
        {
                if(head.owner == e)
                {
-                       head.movetype = MOVETYPE_TOSS;
+                       head.movetype = head.old_movetype; // revert to previous physics
                        head.solid = SOLID_BBOX;
                        head.takedamage = DAMAGE_AIM;
 
                        setattachment(head, world, "");
-                       setorigin(head, e.origin); // prevents a bug
                        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;
                }
        }
 }
@@ -130,9 +137,19 @@ entity sandbox_SpawnObject()
        setorigin(e, trace_endpos);
        e.angles_y = self.v_angle_y;
 
+       self.object_count += 1;
+
        return e;
 }
 
+void sandbox_RemoveObject(entity e)
+{
+       remove(e);
+       e = world;
+
+       self.object_count -= 1;
+}
+
 string sandbox_Storage_Save(entity e)
 {
        // save object properties
@@ -193,6 +210,11 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                print_to(self, "^7\"^2remove_object^7\" removes the object the player is looking at. Players can only remove their own objects");
                                print_to(self, "^7\"^2duplicate_object_copy^7\" copies the object the player is looking at. Players can only copy their own objects");
                                print_to(self, "^7\"^2duplicate_object_paste^7\" pastes the copied object in front of the player");
+                               print_to(self, "^7\"^2attach_object ^3property value^7\" attaches one object to another. Players can only attach their own objects");
+                               print_to(self, "^7Attachment properties for ^2attach_object^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\"^2edit_object ^3property value^7\" edits the given property of the object. Players can only edit their own objects");
                                print_to(self, "^7Object properties for ^2edit_object^7:");
                                print_to(self, "^3skin value ^7- changes the skin of the object");
@@ -239,7 +261,11 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
 
                        // ---------------- COMMAND: SPAWN OBJECT ----------------
                        case "spawn_object":
-                               // don't allow spawning objects without a model
+                               if(self.object_count >= autocvar_g_sandbox_maxplayerobjects)
+                               {
+                                       print_to(self, strcat("WARNING: Cannot spawn any more objects. Each player may have up to ^3", ftos(autocvar_g_sandbox_maxplayerobjects), " ^7objects at a time"));
+                                       return TRUE;
+                               }
                                if(cmd_argc < 3)
                                {
                                        print_to(self, "WARNING: Attempted to spawn an object without specifying a model. Please specify the path to your model file after the 'spawn_object' command");
@@ -266,8 +292,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                {
                                        if(autocvar_g_sandbox_info)
                                                print(strcat(self.netname, " removed an object at origin ", vtos(e.origin), "\n"));
-                                       remove(e);
-                                       e = world;
+                                       sandbox_RemoveObject(e);
                                        return TRUE;
                                }
 
@@ -301,11 +326,16 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                        print_to(self, "WARNING: No object in clipboard. You must copy an object before you can paste it");
                                        return TRUE;
                                }
+                               if(self.object_count >= autocvar_g_sandbox_maxplayerobjects)
+                               {
+                                       print_to(self, strcat("WARNING: Cannot spawn any more objects. Each player may have up to ^3", ftos(autocvar_g_sandbox_maxplayerobjects), " ^7objects at a time"));
+                                       return TRUE;
+                               }
 
                                e = sandbox_SpawnObject();
                                sandbox_Storage_Load(e, self.object_clipboard);
 
-                               print_to(self, "Object pasted");
+                               print_to(self, "Object pasted successfully");
                                if(autocvar_g_sandbox_info)
                                        print(strcat(self.netname, " pasted an object at origin ", vtos(e.origin), "\n"));
 
@@ -319,19 +349,41 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                                // select e as the object as meant to be attached
                                                e = sandbox_EditObject_Get();
                                                if(e != world)
+                                               {
                                                        self.object_attach = e;
+                                                       print_to(self, "Object selected for attachment");
+                                                       return TRUE;
+                                               }
+                                               print_to(self, "WARNING: Object could not be selected for attachment. Make sure you are facing an object that belongs to you");
                                                return TRUE;
                                        case "set":
+                                               if(self.object_attach == world)
+                                               {
+                                                       print_to(self, "WARNING: No object selected for attachment. Please select an object to be attached first.");
+                                                       return TRUE;
+                                               }
+
                                                // attaches the previously selected object to e
                                                e = sandbox_EditObject_Get();
                                                if(e != world)
+                                               {
                                                        sandbox_AttachObject_Set(self.object_attach, e, argv(3));
+                                                       print_to(self, "Object attached successfully");
+                                                       self.object_attach = world; // object was attached, no longer keep it scheduled for attachment
+                                                       return TRUE;
+                                               }
+                                               print_to(self, "WARNING: Object could not be attached to the parent. Make sure you are facing an object that belongs to you");
                                                return TRUE;
                                        case "remove":
                                                // removes e if it was attached
                                                e = sandbox_EditObject_Get();
                                                if(e != world)
+                                               {
                                                        sandbox_AttachObject_Remove(e);
+                                                       print_to(self, "Child objects detached successfully");
+                                                       return TRUE;
+                                               }
+                                               print_to(self, "WARNING: Child objects could not be detached. Make sure you are facing an object that belongs to you");
                                                return TRUE;
                                }
                                return TRUE;