]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/t_plats.qc
link to new/better playerstats response format idea
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / t_plats.qc
index 9ac9df289243bd36ba3028cec515d4f5394117cb..8c2766295e7ca3cad676c15247e82c9581404a11 100644 (file)
@@ -57,7 +57,17 @@ void plat_spawn_inside_trigger()
                tmax_y = tmin_y + 1;
        }
 
-       setsize (trigger, tmin, tmax);
+       if(tmin_x < tmax_x)
+               if(tmin_y < tmax_y)
+                       if(tmin_z < tmax_z)
+                       {
+                               setsize (trigger, tmin, tmax);
+                               return;
+                       }
+
+       // otherwise, something is fishy...
+       remove(trigger);
+       objerror("plat_spawn_inside_trigger: platform has odd size or lip, can't spawn");
 }
 
 void plat_hit_top()
@@ -175,11 +185,6 @@ void plat_reset()
 void spawnfunc_path_corner() { }
 void spawnfunc_func_plat()
 {
-       if (!self.t_length)
-               self.t_length = 80;
-       if (!self.t_width)
-               self.t_width = 10;
-
        if (self.sounds == 0)
                self.sounds = 2;
 
@@ -231,15 +236,19 @@ void spawnfunc_func_plat()
 
        if (!self.speed)
                self.speed = 150;
+       if (!self.lip)
+               self.lip = 16;
+       if (!self.height)
+               self.height = self.size_z - self.lip;
 
        self.pos1 = self.origin;
        self.pos2 = self.origin;
-       self.pos2_z = self.origin_z - self.size_z + 8;
-
-       plat_spawn_inside_trigger ();   // the "start moving" trigger
+       self.pos2_z = self.origin_z - self.height;
 
        self.reset = plat_reset;
        plat_reset();
+
+       plat_spawn_inside_trigger ();   // the "start moving" trigger
 }
 
 
@@ -885,6 +894,7 @@ void door_go_up()
 }
 
 
+
 /*
 =============================================================================
 
@@ -893,6 +903,50 @@ ACTIVATION FUNCTIONS
 =============================================================================
 */
 
+float door_check_keys(void) {
+       local entity door;
+       
+       
+       if (self.owner)
+               door = self.owner;
+       else
+               door = self;
+       
+       // no key needed
+       if not(door.itemkeys)
+               return TRUE;
+
+       // this door require a key
+       // only a player can have a key
+       if (other.classname != "player")
+               return FALSE;
+       
+       if (item_keys_usekey(door, other)) {
+               // some keys were used
+               if (other.key_door_messagetime <= time) {
+                       play2(other, "misc/talk.wav");
+                       centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
+                       other.key_door_messagetime = time + 2;
+               }
+       } else {
+               // no keys were used
+               if (other.key_door_messagetime <= time) {
+                       play2(other, "misc/talk.wav");
+                       centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
+                       other.key_door_messagetime = time + 2;
+               }
+       }
+
+       if (door.itemkeys) {
+               // door is now unlocked
+               play2(other, "misc/talk.wav");
+               centerprint(other, "Door unlocked!");
+               return TRUE;
+       } else
+               return FALSE;
+}
+
+
 void door_fire()
 {
        entity  oself;
@@ -958,6 +1012,7 @@ void door_use()
        entity oself;
 
        //dprint("door_use (model: ");dprint(self.model);dprint(")\n");
+       
        if (self.owner)
        {
                oself = self;
@@ -971,11 +1026,16 @@ void door_use()
 void door_trigger_touch()
 {
        if (other.health < 1)
-       if not(other.iscreature && other.deadflag == DEAD_NO)
-               return;
+               if not(other.iscreature && other.deadflag == DEAD_NO)
+                       return;
 
        if (time < self.attack_finished_single)
                return;
+       
+       // check if door is locked
+       if (!door_check_keys())
+               return;
+       
        self.attack_finished_single = time + 1;
 
        activator = other;
@@ -992,6 +1052,12 @@ void door_damage(entity inflictor, entity attacker, float damage, float deathtyp
                if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
                        return;
        self.health = self.health - damage;
+       
+       if (self.itemkeys) {
+               // don't allow opening doors through damage if keys are required
+               return;
+       }
+       
        if (self.health <= 0)
        {
                oself = self;
@@ -1159,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)
+{
+       while((cur = find(cur, classname, self.classname)) && ((cur.spawnflags & 4) || cur.enemy))
+       {
+       }
+       return cur;
+}
+
+float LinkDoors_isconnected(entity e1, entity e2, entity pass)
 {
-       if (e1.absmin_x > e2.absmax_x)
+       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
@@ -1186,7 +1260,7 @@ LinkDoors
 */
 void LinkDoors()
 {
-       entity  t, starte;
+       entity  t;
        vector  cmins, cmaxs;
 
        if (self.enemy)
@@ -1206,78 +1280,84 @@ 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);
 }
 
 
-/*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK x x TOGGLE
+/*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
 if two doors touch, they are assumed to be connected and operate as a unit.
 
 TOGGLE causes the door to wait in both the start and end states for a trigger event.
 
 START_OPEN causes the door to move to its destination when spawned, and operate in reverse.  It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors).
 
+GOLD_KEY causes the door to open only if the activator holds a gold key.
+
+SILVER_KEY causes the door to open only if the activator holds a silver key.
+
 "message"      is printed when the door is touched if it is a trigger door and it hasn't been fired yet
 "angle"                determines the opening direction
 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
@@ -1311,8 +1391,17 @@ void door_reset()
        self.think = SUB_Null;
 }
 
+// spawnflags require key (for now only func_door)
+#define SPAWNFLAGS_GOLD_KEY 8
+#define SPAWNFLAGS_SILVER_KEY 16
 void spawnfunc_func_door()
 {
+       // Quake 1 keys compatibility
+       if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
+               self.itemkeys |= ITEM_KEY_BIT(0);
+       if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
+               self.itemkeys |= ITEM_KEY_BIT(1);
+               
        //if (!self.deathtype) // map makers can override this
        //      self.deathtype = " got in the way";
        SetMovedir ();
@@ -1326,8 +1415,9 @@ void spawnfunc_func_door()
        self.blocked = door_blocked;
        self.use = door_use;
 
-    if(self.spawnflags & 8)
-        self.dmg = 10000;
+       // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
+       // if(self.spawnflags & 8)
+       //      self.dmg = 10000;
 
     if(self.dmg && (!self.message))
                self.message = "was squished";
@@ -1916,7 +2006,7 @@ void func_vectormamamam_findtarget()
        if(!self.wp00 && !self.wp01 && !self.wp02 && !self.wp03)
                objerror("No reference entity found, so there is nothing to move. Aborting.");
 
-       self.destvec = self.origin - func_vectormamamam_origin(self.owner, 0);
+       self.destvec = self.origin - func_vectormamamam_origin(self, 0);
 
        entity controller;
        controller = spawn();
@@ -1984,3 +2074,91 @@ void spawnfunc_func_vectormamamam()
 
        InitializeEntity(self, func_vectormamamam_findtarget, INITPRIO_FINDTARGET);
 }
+
+void conveyor_think()
+{
+       entity e;
+
+       // set myself as current conveyor where possible
+       for(e = world; (e = findentity(e, conveyor, self)); )
+               e.conveyor = world;
+
+       if(self.state)
+       {
+               for(e = findradius((self.absmin + self.absmax) * 0.5, vlen(self.absmax - self.absmin) * 0.5 + 1); e; e = e.chain)
+                       if(!e.conveyor.state)
+                               if(isPushable(e))
+                               {
+                                       vector emin = e.absmin;
+                                       vector emax = e.absmax;
+                                       if(self.solid == SOLID_BSP)
+                                       {
+                                               emin -= '1 1 1';
+                                               emax += '1 1 1';
+                                       }
+                                       if(boxesoverlap(emin, emax, self.absmin, self.absmax)) // quick
+                                               if(WarpZoneLib_BoxTouchesBrush(emin, emax, self, e)) // accurate
+                                                       e.conveyor = self;
+                               }
+
+               for(e = world; (e = findentity(e, conveyor, self)); )
+               {
+                       if(e.flags & FL_CLIENT) // doing it via velocity has quite some advantages
+                               continue; // done in SV_PlayerPhysics
+
+                       setorigin(e, e.origin + self.movedir * sys_frametime);
+                       move_out_of_solid(e);
+                       UpdateCSQCProjectile(e);
+                       /*
+                       // stupid conveyor code
+                       tracebox(e.origin, e.mins, e.maxs, e.origin + self.movedir * sys_frametime, MOVE_NORMAL, e);
+                       if(trace_fraction > 0)
+                               setorigin(e, trace_endpos);
+                       */
+               }
+       }
+
+       self.nextthink = time;
+}
+
+void conveyor_use()
+{
+       self.state = !self.state;
+}
+
+void conveyor_reset()
+{
+       self.state = (self.spawnflags & 1);
+}
+
+void conveyor_init()
+{
+       if (!self.speed)
+               self.speed = 200;
+       self.movedir = self.movedir * self.speed;
+       self.think = conveyor_think;
+       self.nextthink = time;
+       IFTARGETED
+       {
+               self.use = conveyor_use;
+               self.reset = conveyor_reset;
+               conveyor_reset();
+       }
+       else
+               self.state = 1;
+}
+
+void spawnfunc_trigger_conveyor()
+{
+       SetMovedir();
+       EXACTTRIGGER_INIT;
+       conveyor_init();
+}
+
+void spawnfunc_func_conveyor()
+{
+       SetMovedir();
+       InitMovingBrushTrigger();
+       self.movetype = MOVETYPE_NONE;
+       conveyor_init();
+}