]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
rewrite LinkDoors to be a LOT more robust
authorRudolf Polzer <divverent@xonotic.org>
Thu, 13 Sep 2012 09:32:24 +0000 (11:32 +0200)
committerRudolf Polzer <divverent@xonotic.org>
Thu, 13 Sep 2012 09:32:54 +0000 (11:32 +0200)
qcsrc/common/util.qc
qcsrc/common/util.qh
qcsrc/server/t_plats.qc

index 6e534ffc19464a92777b956353b926f48ddd578e..e81e27bec78de5fb463420f35f2f354e4e3bbaa9 100644 (file)
@@ -2416,3 +2416,45 @@ float cubic_speedfunc_is_sane(float startspeedfactor, float endspeedfactor)
        // (3.5, [0.2..2.3])
        // (4, 1)
 }
+
+.float FindConnectedComponent_processing;
+void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass)
+{
+       entity queue_start, queue_end;
+
+       // we build a queue of to-be-processed entities.
+       // queue_start is the next entity to be checked for neighbors
+       // queue_end is the last entity added
+
+       if(e.FindConnectedComponent_processing)
+               error("recursion or broken cleanup");
+
+       // start with a 1-element queue
+       queue_start = queue_end = e;
+       queue_end.fld = world;
+       queue_end.FindConnectedComponent_processing = 1;
+
+       // for each queued item:
+       for(; queue_start; queue_start = queue_start.fld)
+       {
+               // find all neighbors of queue_start
+               entity t;
+               for(t = world; (t = nxt(t, queue_start, pass)); )
+               {
+                       if(t.FindConnectedComponent_processing)
+                               continue;
+                       if(iscon(t, queue_start, pass))
+                       {
+                               // it is connected? ADD IT. It will look for neighbors soon too.
+                               queue_end.fld = t;
+                               queue_end = t;
+                               queue_end.fld = world;
+                               queue_end.FindConnectedComponent_processing = 1;
+                       }
+               }
+       }
+
+       // unmark
+       for(queue_start = e; queue_start; queue_start = queue_start.fld)
+               queue_start.FindConnectedComponent_processing = 0;
+}
index 3ce173bf638e95d705a70042fc6bfae62f4838aa..5a89e17a198a6a7af5d32d32a7f585a572b7a06f 100644 (file)
@@ -334,3 +334,7 @@ float cubic_speedfunc(float startspeedfactor, float endspeedfactor, float x);
 // because if this is the case, the function is not usable for platforms
 // as it may exceed 0..1 bounds, or go in reverse
 float cubic_speedfunc_is_sane(float startspeedfactor, float endspeedfactor);
+
+typedef entity(entity cur, entity near, entity pass) findNextEntityNearFunction_t;
+typedef float(entity a, entity b, entity pass) isConnectedFunction_t;
+void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass);
index 8d81ca263d60f04ff8df54a6535876d64e0e6e33..cd3c8d10607b54642413455633077681f03e17f2 100644 (file)
@@ -1225,24 +1225,32 @@ entity spawn_field(vector fmins, vector fmaxs)
 }
 
 
-float EntitiesTouching(entity e1, entity e2)
+entity LinkDoors_nextent(entity cur, entity near, entity pass)
 {
-       if (e1.absmin_x > e2.absmax_x)
+       while((cur = find(cur, classname, self.classname)) && ((cur.spawnflags & 4) || cur.enemy))
+       {
+       }
+       return cur;
+}
+
+float LinkDoors_isconnected(entity e1, entity e2, entity pass)
+{
+       float DELTA = 4;
+       if (e1.absmin_x > e2.absmax_x + DELTA)
                return FALSE;
-       if (e1.absmin_y > e2.absmax_y)
+       if (e1.absmin_y > e2.absmax_y + DELTA)
                return FALSE;
-       if (e1.absmin_z > e2.absmax_z)
+       if (e1.absmin_z > e2.absmax_z + DELTA)
                return FALSE;
-       if (e1.absmax_x < e2.absmin_x)
+       if (e2.absmin_x > e1.absmax_x + DELTA)
                return FALSE;
-       if (e1.absmax_y < e2.absmin_y)
+       if (e2.absmin_y > e1.absmax_y + DELTA)
                return FALSE;
-       if (e1.absmax_z < e2.absmin_z)
+       if (e2.absmin_z > e1.absmax_z + DELTA)
                return FALSE;
        return TRUE;
 }
 
-
 /*
 =============
 LinkDoors
@@ -1252,7 +1260,7 @@ LinkDoors
 */
 void LinkDoors()
 {
-       entity  t, starte;
+       entity  t;
        vector  cmins, cmaxs;
 
        if (self.enemy)
@@ -1272,68 +1280,70 @@ void LinkDoors()
                return;         // don't want to link this door
        }
 
-       cmins = self.absmin;
-       cmaxs = self.absmax;
-
-       starte = self;
-       t = self;
+       FindConnectedComponent(self, enemy, LinkDoors_nextent, LinkDoors_isconnected, world);
 
-       do
+       // set owner, and make a loop of the chain
+       dprint("LinkDoors: linking doors:");
+       for(t = self; ; t = t.enemy)
        {
-               self.owner = starte;                    // master door
-
-               if (self.health)
-                       starte.health = self.health;
-               IFTARGETED
-                       starte.targetname = self.targetname;
-               if (self.message != "")
-                       starte.message = self.message;
-
-               t = find(t, classname, self.classname);
-               if (!t)
+               dprint(" ", etos(t));
+               t.owner = self;
+               if(t.enemy == world)
                {
-                       self.enemy = starte;            // make the chain a loop
-
-               // shootable, or triggered doors just needed the owner/enemy links,
-               // they don't spawn a field
-
-                       self = self.owner;
+                       t.enemy = self;
+                       break;
+               }
+       }
+       dprint("\n");
 
-                       if (self.health)
-                               return;
-                       IFTARGETED
-                               return;
-                       if (self.items)
-                               return;
+       // collect health, targetname, message, size
+       cmins = self.absmin;
+       cmaxs = self.absmax;
+       for(t = self; ; t = t.enemy)
+       {
+               if(t.health && !self.health)
+                       self.health = t.health;
+               if(t.targetname && !self.targetname)
+                       self.targetname = t.targetname;
+               if(t.message != "" && self.message == "")
+                       self.message = t.message;
+               if (t.absmin_x < cmins_x)
+                       cmins_x = t.absmin_x;
+               if (t.absmin_y < cmins_y)
+                       cmins_y = t.absmin_y;
+               if (t.absmin_z < cmins_z)
+                       cmins_z = t.absmin_z;
+               if (t.absmax_x > cmaxs_x)
+                       cmaxs_x = t.absmax_x;
+               if (t.absmax_y > cmaxs_y)
+                       cmaxs_y = t.absmax_y;
+               if (t.absmax_z > cmaxs_z)
+                       cmaxs_z = t.absmax_z;
+               if(t.enemy == self)
+                       break;
+       }
 
-                       self.owner.trigger_field = spawn_field(cmins, cmaxs);
+       // distribute health, targetname, message
+       for(t = self; t; t = t.enemy)
+       {
+               t.health = self.health;
+               t.targetname = self.targetname;
+               t.message = self.message;
+               if(t.enemy == self)
+                       break;
+       }
 
-                       return;
-               }
+       // shootable, or triggered doors just needed the owner/enemy links,
+       // they don't spawn a field
 
-               if (EntitiesTouching(self,t))
-               {
-                       if (t.enemy)
-                               objerror ("cross connected doors");
-
-                       self.enemy = t;
-                       self = t;
-
-                       if (t.absmin_x < cmins_x)
-                               cmins_x = t.absmin_x;
-                       if (t.absmin_y < cmins_y)
-                               cmins_y = t.absmin_y;
-                       if (t.absmin_z < cmins_z)
-                               cmins_z = t.absmin_z;
-                       if (t.absmax_x > cmaxs_x)
-                               cmaxs_x = t.absmax_x;
-                       if (t.absmax_y > cmaxs_y)
-                               cmaxs_y = t.absmax_y;
-                       if (t.absmax_z > cmaxs_z)
-                               cmaxs_z = t.absmax_z;
-               }
-       } while (1 );
+       if (self.health)
+               return;
+       IFTARGETED
+               return;
+       if (self.items)
+               return;
 
+       self.trigger_field = spawn_field(cmins, cmaxs);
 }