--- /dev/null
+void generic_plat_blocked(entity this, entity blocker)
+{
+#ifdef SVQC
+ if(this.dmg && blocker.takedamage != DAMAGE_NO)
+ {
+ if(this.dmgtime2 < time)
+ {
+ Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0');
+ this.dmgtime2 = time + this.dmgtime;
+ }
+
+ // Gib dead/dying stuff
+ if(IS_DEAD(blocker))
+ Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0');
+ }
+#endif
+}
+
+void plat_spawn_inside_trigger(entity this)
+{
+ entity trigger;
+ vector tmin, tmax;
+
+ trigger = spawn();
+ settouch(trigger, plat_center_touch);
+ trigger.movetype = MOVETYPE_NONE;
+ trigger.solid = SOLID_TRIGGER;
+ trigger.enemy = this;
+
+ tmin = this.absmin + '25 25 0';
+ tmax = this.absmax - '25 25 -8';
+ tmin_z = tmax_z - (this.pos1_z - this.pos2_z + 8);
+ if (this.spawnflags & PLAT_LOW_TRIGGER)
+ tmax_z = tmin_z + 8;
+
+ if (this.size_x <= 50)
+ {
+ tmin_x = (this.mins_x + this.maxs_x) / 2;
+ tmax_x = tmin_x + 1;
+ }
+ if (this.size_y <= 50)
+ {
+ tmin_y = (this.mins_y + this.maxs_y) / 2;
+ tmax_y = tmin_y + 1;
+ }
+
+ 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(this, "plat_spawn_inside_trigger: platform has odd size or lip, can't spawn");
+}
+
+void plat_hit_top(entity this)
+{
+ _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM);
+ this.state = 1;
+
+ SUB_THINK(this, plat_go_down);
+ this.SUB_NEXTTHINK = this.SUB_LTIME + 3;
+}
+
+void plat_hit_bottom(entity this)
+{
+ _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM);
+ this.state = 2;
+}
+
+void plat_go_down(entity this)
+{
+ _sound (this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_NORM);
+ this.state = 3;
+ SUB_CalcMove (this, this.pos2, TSPEED_LINEAR, this.speed, plat_hit_bottom);
+}
+
+void plat_go_up(entity this)
+{
+ _sound (this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_NORM);
+ this.state = 4;
+ SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, plat_hit_top);
+}
+
+void plat_center_touch(entity this, entity toucher)
+{
+#ifdef SVQC
+ if (!toucher.iscreature)
+ return;
+
+ if (toucher.health <= 0)
+ return;
+#elif defined(CSQC)
+ if (!IS_PLAYER(toucher))
+ return;
+ if(IS_DEAD(toucher))
+ return;
+#endif
+
+ if (this.enemy.state == 2) {
+ plat_go_up(this.enemy);
+ } else if (this.enemy.state == 1)
+ this.enemy.SUB_NEXTTHINK = this.enemy.SUB_LTIME + 1;
+}
+
+void plat_outside_touch(entity this, entity toucher)
+{
+#ifdef SVQC
+ if (!toucher.iscreature)
+ return;
+
+ if (toucher.health <= 0)
+ return;
+#elif defined(CSQC)
+ if (!IS_PLAYER(toucher))
+ return;
+#endif
+
+ if (this.enemy.state == 1) {
+ entity e = this.enemy;
+ plat_go_down(e);
+ }
+}
+
+void plat_trigger_use(entity this, entity actor, entity trigger)
+{
+#ifdef SVQC
+ if (getthink(this))
+ return; // already activated
+#elif defined(CSQC)
+ if(this.move_think)
+ return;
+#endif
+ plat_go_down(this);
+}
+
+
+void plat_crush(entity this, entity blocker)
+{
+ if((this.spawnflags & 4) && (blocker.takedamage != DAMAGE_NO))
+ { // KIll Kill Kill!!
+#ifdef SVQC
+ Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0');
+#endif
+ }
+ else
+ {
+#ifdef SVQC
+ if((this.dmg) && (blocker.takedamage != DAMAGE_NO))
+ { // Shall we bite?
+ Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0');
+ // Gib dead/dying stuff
+ if(IS_DEAD(blocker))
+ Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0');
+ }
+#endif
+
+ if (this.state == 4)
+ plat_go_down (this);
+ else if (this.state == 3)
+ plat_go_up (this);
+ // when in other states, then the plat_crush event came delayed after
+ // plat state already had changed
+ // this isn't a bug per se!
+ }
+}
+
+void plat_use(entity this, entity actor, entity trigger)
+{
+ this.use = func_null;
+ if (this.state != 4)
+ objerror (this, "plat_use: not in up state");
+ plat_go_down(this);
+}
+
+.string sound1, sound2;
+
+void plat_reset(entity this)
+{
+ IFTARGETED
+ {
+ setorigin(this, this.pos1);
+ this.state = 4;
+ this.use = plat_use;
+ }
+ else
+ {
+ setorigin(this, this.pos2);
+ this.state = 2;
+ this.use = plat_trigger_use;
+ }
+
+#ifdef SVQC
+ this.SendFlags |= SF_TRIGGER_RESET;
+#endif
+}
+
+.float platmovetype_start_default, platmovetype_end_default;
+bool set_platmovetype(entity e, string s)
+{
+ // sets platmovetype_start and platmovetype_end based on a string consisting of two values
+
+ int n = tokenize_console(s);
+ if(n > 0)
+ e.platmovetype_start = stof(argv(0));
+ else
+ e.platmovetype_start = 0;
+
+ if(n > 1)
+ e.platmovetype_end = stof(argv(1));
+ else
+ e.platmovetype_end = e.platmovetype_start;
+
+ if(n > 2)
+ if(argv(2) == "force")
+ return true; // no checking, return immediately
+
+ if(!cubic_speedfunc_is_sane(e.platmovetype_start, e.platmovetype_end))
+ {
+ objerror(e, "Invalid platform move type; platform would go in reverse, which is not allowed.");
+ return false;
+ }
+
+ return true;
+}