From e1750180c974fe0f5dd23f5a2d3ae70a9f420c19 Mon Sep 17 00:00:00 2001 From: Rudolf Polzer Date: Thu, 13 Sep 2012 11:32:24 +0200 Subject: [PATCH] rewrite LinkDoors to be a LOT more robust --- qcsrc/common/util.qc | 42 +++++++++++++ qcsrc/common/util.qh | 4 ++ qcsrc/server/t_plats.qc | 136 +++++++++++++++++++++------------------- 3 files changed, 119 insertions(+), 63 deletions(-) diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index 6e534ffc1..e81e27bec 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -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; +} diff --git a/qcsrc/common/util.qh b/qcsrc/common/util.qh index 3ce173bf6..5a89e17a1 100644 --- a/qcsrc/common/util.qh +++ b/qcsrc/common/util.qh @@ -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); diff --git a/qcsrc/server/t_plats.qc b/qcsrc/server/t_plats.qc index 8d81ca263..cd3c8d106 100644 --- a/qcsrc/server/t_plats.qc +++ b/qcsrc/server/t_plats.qc @@ -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); } -- 2.39.2