mirror of
https://git.code.sf.net/p/quake/game-source
synced 2024-11-28 22:52:26 +00:00
1007 lines
20 KiB
C++
1007 lines
20 KiB
C++
#include "common.qh"
|
|
#include "server.qh"
|
|
#include "weapon.qh"
|
|
#include "misc.qh"
|
|
|
|
#include "mapents_util.qh"
|
|
#include "damage.qh"
|
|
|
|
#include "mapents_movewall.qh"
|
|
|
|
/* movewalls! The replacement for doors/plats/trains. */
|
|
/* Internally:
|
|
when used:
|
|
|
|
a movewall will start to go self.goalentity.pos2, at
|
|
self.goalentity.speed. You may change pos2 when the
|
|
state event occurs.
|
|
|
|
self.state will reflect the current state, the first state
|
|
being 0, between the first and second being 0.5, and
|
|
increasing to 1 once it reaches the second. it will increase
|
|
the same way until it reaches the first goalentity, which is 0.
|
|
states are in the goalentity's .style
|
|
|
|
self.goalentity will be set to self.goalentity.goalentity
|
|
|
|
a state event will occur.
|
|
(state events are triggered by calling self.w_think, if it exists.)
|
|
|
|
after it reaches the position, self.state will reflect the
|
|
self.goalentity.style of the new position, and a state
|
|
event will be triggered.
|
|
|
|
-- snip --
|
|
|
|
movewalls start with an undefined state (i.e., it will be
|
|
0 if it just spawned and you don't set it)
|
|
|
|
You must provide your own self.blocked function.
|
|
|
|
You must also provide a means of calling self.use(),
|
|
or rely on it being a target.
|
|
*/
|
|
|
|
void() _deathmsg_squish = {
|
|
bprint (PRINT_DEATH, name(self), " was squished.\n");
|
|
};
|
|
|
|
.vector finaldest;
|
|
void() _move_to_pos_done = {
|
|
self.velocity = '0 0 0';
|
|
setorigin (self, self.finaldest);
|
|
self.nextthink = -1;
|
|
|
|
if (self.think1)
|
|
self.think1 ();
|
|
};
|
|
|
|
/* calculate velocity and direction to move to position at spd. */
|
|
/* _ONLY_ use this with MOVETYPE_PUSH's! */
|
|
void(vector position, float spd, void() think_func) util_move_to_pos = {
|
|
local float dist;
|
|
local vector offs;
|
|
|
|
self.finaldest = position;
|
|
self.speed = spd;
|
|
|
|
offs = position - self.origin;
|
|
dist = vlen (offs);
|
|
offs *= (1/ dist);
|
|
|
|
self.velocity = offs * spd;
|
|
|
|
self.think1 = think_func;
|
|
self.think = _move_to_pos_done;
|
|
self.nextthink = self.ltime + (dist / spd);
|
|
|
|
if (self.nextthink <= self.ltime)
|
|
self.nextthink = self.ltime + sv_mintic;
|
|
};
|
|
|
|
void() _movewall_done = {
|
|
self.state += 0.5;
|
|
|
|
if (self.w_think)
|
|
self.w_think ();
|
|
};
|
|
|
|
void() _movewall_use = {
|
|
local float spd;
|
|
|
|
if (!self.goalentity) {
|
|
/* Ugh. */
|
|
dprint("Activated unfinalized movewall\n");
|
|
return;
|
|
}
|
|
|
|
/* Avoid multiple activations per frame */
|
|
if (self.origin == self.goalentity.pos2)
|
|
return;
|
|
|
|
spd = self.goalentity.speed;
|
|
if (!spd)
|
|
spd = self.speed;
|
|
util_move_to_pos (self.goalentity.pos2 - self.view_ofs, spd,
|
|
_movewall_done);
|
|
|
|
self.state = self.goalentity.style - 0.5;
|
|
self.goalentity = self.goalentity.goalentity;
|
|
|
|
if (self.w_think)
|
|
self.w_think ();
|
|
};
|
|
|
|
void() movewall_init = {
|
|
self.solid = SOLID_BSP;
|
|
self.movetype = MOVETYPE_PUSH;
|
|
self.deadflag = DEAD_NONLIVING;
|
|
|
|
self.use = _movewall_use;
|
|
};
|
|
|
|
// ========================================================================
|
|
/* Two states, no more, no less. pos2 contains the next position. */
|
|
/* Plays self.noise3 when starting to move, and self.noise4 when stopping. */
|
|
/* damages blocking entities if self.dmg (per second if it doesn't go back) */
|
|
/* when state is 1, waits for self.wait seconds, then activates again. */
|
|
|
|
void() _movewall_wait_think = {
|
|
if (self.pain_finished <= self.ltime) {
|
|
_movewall_use ();
|
|
return;
|
|
}
|
|
|
|
self.nextthink = self.pain_finished;
|
|
};
|
|
|
|
void() _movewall_state0_use = {
|
|
if (self.state != 0)
|
|
return;
|
|
_movewall_use ();
|
|
};
|
|
|
|
void() _movewall_binary_think = {
|
|
if (floor(self.state) != self.state) {
|
|
local vector tmp;
|
|
|
|
if (self.noise3)
|
|
sound (self, CHAN_BODY, self.noise3, 1, ATTN_NORM);
|
|
|
|
tmp = self.pos2;
|
|
self.pos2 = self.pos1;
|
|
self.pos1 = tmp;
|
|
self.style = 1 - self.style;
|
|
} else {
|
|
if (self.noise4)
|
|
sound (self, CHAN_BODY|CHAN_NO_PHS_ADD, self.noise4, 1, ATTN_NORM);
|
|
|
|
if (self.state == 1) {
|
|
self.think = _movewall_wait_think;
|
|
self.nextthink = self.ltime + self.wait;
|
|
}
|
|
}
|
|
};
|
|
|
|
void() _movewall_block_ignore = {
|
|
if (self.dmg)
|
|
damage (other, self, self, self.dmg * frametime, _deathmsg_squish);
|
|
|
|
/* recalculate the move.. */
|
|
if (self.nextthink && self.think == _movewall_done)
|
|
util_move_to_pos (self.finaldest, self.speed, self.think1);
|
|
};
|
|
|
|
void() _movewall_block_goback = {
|
|
if (self.dmg)
|
|
damage(other, self, self, self.dmg, _deathmsg_squish);
|
|
_movewall_use();
|
|
};
|
|
|
|
void() _movewall_trigger_touch = {
|
|
self = self.owner;
|
|
self.flags |= FL_JUMPRELEASED;
|
|
self.touch ();
|
|
self.flags &= ~FL_JUMPRELEASED;
|
|
};
|
|
|
|
entity(vector org, vector tmin, vector tmax) _movewall_spawn_trigger = {
|
|
local entity trigger;
|
|
|
|
trigger = spawn ("TRIGGER");
|
|
|
|
trigger.solid = SOLID_TRIGGER;
|
|
trigger.movetype = MOVETYPE_NONE;
|
|
|
|
trigger.touch = _movewall_trigger_touch;
|
|
|
|
trigger.owner = self;
|
|
|
|
setsize (trigger, tmin, tmax);
|
|
setorigin (trigger, org);
|
|
|
|
return trigger;
|
|
};
|
|
|
|
// ========================================================================
|
|
|
|
/* doors... state 0 is closed, state 1 is open. */
|
|
/* self.owner is the head of a list of linked doors. */
|
|
/* self.enemy is the next in the list. */
|
|
|
|
#define SPAWNFLAGS_DOOR_START_OPEN 1
|
|
#define SPAWNFLAGS_DOOR_DONT_LINK 4
|
|
#define SPAWNFLAGS_DOOR_GOLD_KEY 8 // legacy
|
|
#define SPAWNFLAGS_DOOR_SILVER_KEY 16 // legacy
|
|
#define SPAWNFLAGS_DOOR_TOGGLE 32
|
|
|
|
float(float d) _movewall_door_takedamage = {
|
|
local entity oldself;
|
|
|
|
if (self.owner.state != 0) {
|
|
deathmsg_nodisplay ();
|
|
return FALSE;
|
|
}
|
|
|
|
oldself = self;
|
|
self = self.owner;
|
|
|
|
self.health -= d;
|
|
|
|
if (self.health <= 0) {
|
|
self.health = self.max_health;
|
|
|
|
while (self) {
|
|
if (util_use_targets ()) {
|
|
self.use ();
|
|
|
|
/* Hack for id compat... */
|
|
/* Use target if you need it to go more
|
|
than once */
|
|
self.message = "";
|
|
self.noise1 = nil;
|
|
self.noise2 = nil;
|
|
}
|
|
|
|
self = self.enemy;
|
|
}
|
|
}
|
|
|
|
self = oldself;
|
|
|
|
deathmsg_nodisplay ();
|
|
return FALSE; // lie
|
|
};
|
|
|
|
void() _movewall_door_touch = {
|
|
local entity oldself;
|
|
|
|
if (!is_living (other))
|
|
return;
|
|
|
|
oldself = self;
|
|
|
|
self = self.owner;
|
|
|
|
if (self.state != 0) {
|
|
while (self) {
|
|
self.pain_finished = self.ltime + self.wait;
|
|
self = self.enemy;
|
|
}
|
|
} else {
|
|
while (self) {
|
|
if (util_use_targets ()) {
|
|
_movewall_use ();
|
|
|
|
/* Hack for id compat... */
|
|
/* Use target if you need it to go more
|
|
than once */
|
|
self.message = "";
|
|
self.noise1 = nil;
|
|
self.noise2 = nil;
|
|
}
|
|
self = self.enemy;
|
|
}
|
|
}
|
|
|
|
self = oldself;
|
|
};
|
|
|
|
void() _door_link = {
|
|
local entity head, tail;
|
|
|
|
tail = self;
|
|
tail.owner = self;
|
|
|
|
head = self;
|
|
while (1) {
|
|
head = find (head, classname, self.classname);
|
|
if (!head)
|
|
break;
|
|
|
|
if (head.spawnflags & SPAWNFLAGS_DOOR_DONT_LINK)
|
|
continue;
|
|
|
|
if (!util_entities_touch (self, head))
|
|
continue;
|
|
|
|
if (head.owner != head)
|
|
objerror ("Crosslinked doors\n");
|
|
|
|
tail.enemy = head;
|
|
tail = head;
|
|
|
|
tail.nextthink = -1; // Avoid trying to link again.
|
|
tail.owner = self;
|
|
|
|
/* *Sigh*. Id compat. */
|
|
if (tail.noise1)
|
|
self.noise1 = tail.noise1;
|
|
if (tail.noise2)
|
|
self.noise2 = tail.noise2;
|
|
if (tail.noise3)
|
|
self.noise3 = tail.noise3;
|
|
if (tail.noise4)
|
|
self.noise4 = tail.noise4;
|
|
if (tail.noise5)
|
|
self.noise5 = tail.noise5;
|
|
if (tail.noise6)
|
|
self.noise6 = tail.noise6;
|
|
if (tail.message)
|
|
self.message = tail.message;
|
|
|
|
if (tail.health)
|
|
self.health = tail.health;
|
|
if (tail.targetname)
|
|
self.targetname = tail.targetname;
|
|
|
|
tail.message = "";
|
|
|
|
tail.noise1 = nil;
|
|
tail.noise2 = nil;
|
|
tail.noise3 = nil;
|
|
tail.noise4 = nil;
|
|
tail.noise5 = nil;
|
|
tail.noise6 = nil;
|
|
}
|
|
|
|
tail.enemy = world;
|
|
|
|
head = self;
|
|
while (self) {
|
|
self.health = head.health;
|
|
self.targetname = head.targetname;
|
|
|
|
if (!self.health && !self.targetname) {
|
|
if (!self.items) {
|
|
local vector tmin, tmax;
|
|
|
|
tmin = self.mins - '60 60 8';
|
|
tmax = self.maxs + '60 60 8';
|
|
_movewall_spawn_trigger(self.origin, tmin, tmax);
|
|
}
|
|
|
|
self.touch = _movewall_door_touch;
|
|
} else {
|
|
if (self.health) {
|
|
self.takedamage = DAMAGE_YES;
|
|
self.th_takedamage = _movewall_door_takedamage;
|
|
}
|
|
}
|
|
|
|
self = self.enemy;
|
|
}
|
|
|
|
self = head;
|
|
};
|
|
|
|
void() func_door = {
|
|
if (!self.noise5) {
|
|
switch (world.worldtype) {
|
|
case 0:
|
|
self.noise5 = "doors/medtry.wav";
|
|
self.noise6 = "doors/meduse.wav";
|
|
break;
|
|
case 1:
|
|
self.noise5 = "doors/runetry.wav";
|
|
self.noise6 = "doors/runeuse.wav";
|
|
break;
|
|
case 2:
|
|
self.noise5 = "doors/basetry.wav";
|
|
self.noise6 = "doors/baseuse.wav";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!self.noise3) {
|
|
switch (self.sounds) {
|
|
case 1:
|
|
self.noise3 = "doors/doormv1.wav";
|
|
self.noise4 = "doors/drclos4.wav";
|
|
break;
|
|
case 2:
|
|
self.noise3 = "doors/hydro1.wav";
|
|
self.noise4 = "doors/hydro2.wav";
|
|
break;
|
|
case 3:
|
|
self.noise3 = "doors/stndr1.wav";
|
|
self.noise4 = "doors/stndr2.wav";
|
|
break;
|
|
case 4:
|
|
self.noise3 = "doors/ddoor1.wav";
|
|
self.noise4 = "doors/ddoor2.wav";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (self.noise3)
|
|
precache_sound(self.noise3);
|
|
if (self.noise4)
|
|
precache_sound(self.noise4);
|
|
|
|
if (self.message != "" && !self.noise1 && !self.noise2)
|
|
self.noise2 = "misc/talk.wav";
|
|
|
|
if (!self.speed)
|
|
self.speed = 100;
|
|
if (!self.wait)
|
|
self.wait = 3;
|
|
if (!self.lip)
|
|
self.lip = 8;
|
|
|
|
if (!self.dmg)
|
|
self.dmg = 2;
|
|
if (self.dmg < 0)
|
|
self.dmg = 0;
|
|
|
|
if (self.spawnflags & SPAWNFLAGS_DOOR_SILVER_KEY) {
|
|
self.spawnflags |= SPAWNFLAGS_CHECK_ITEMS;
|
|
self.items |= IT_KEY1;
|
|
self.wait = -1;
|
|
}
|
|
|
|
if (self.spawnflags & SPAWNFLAGS_DOOR_GOLD_KEY) {
|
|
self.spawnflags |= SPAWNFLAGS_CHECK_ITEMS;
|
|
self.items |= IT_KEY2;
|
|
self.wait = -1;
|
|
}
|
|
|
|
self.max_health = self.health;
|
|
|
|
movewall_init ();
|
|
|
|
util_map_entity_init ();
|
|
|
|
util_set_movedir ();
|
|
|
|
self.pos1 = self.origin;
|
|
self.pos2 = self.pos1 + self.movedir * (fabs (self.movedir * self.size)
|
|
- self.lip);
|
|
|
|
if (self.spawnflags & SPAWNFLAGS_DOOR_START_OPEN) {
|
|
setorigin (self, self.pos2);
|
|
self.pos2 = self.pos1;
|
|
self.pos1 = self.origin;
|
|
}
|
|
|
|
self.state = 0;
|
|
self.style = 1 - self.state;
|
|
|
|
self.touch = NOTHING_function;
|
|
|
|
self.goalentity = self;
|
|
|
|
self.use = _movewall_door_touch;
|
|
|
|
self.blocked = _movewall_block_goback;
|
|
self.w_think = _movewall_binary_think;
|
|
|
|
self.owner = self; // In case we get triggered before 0.1
|
|
|
|
self.think = _door_link;
|
|
self.nextthink = self.ltime + 0.1;
|
|
};
|
|
|
|
// ========================================================================
|
|
|
|
/* plats.. state 0 is the 'top' position, state 1 is 'bottom' */
|
|
|
|
#define SPAWNFLAGS_PLAT_LOW_TRIGGER 1
|
|
|
|
void() _movewall_plat_touch = {
|
|
if (!(self.flags & FL_JUMPRELEASED))
|
|
return;
|
|
|
|
_movewall_door_touch ();
|
|
};
|
|
|
|
void() func_plat = {
|
|
local vector tmin, tmax;
|
|
|
|
if (!self.noise3) {
|
|
if (self.sounds == 1) {
|
|
self.noise3 = "plats/plat1.wav";
|
|
self.noise4 = "plats/plat2.wav";
|
|
} else if (self.sounds == 0 || self.sounds == 2) {
|
|
self.noise3 = "plats/medplat1.wav";
|
|
self.noise4 = "plats/medplat2.wav";
|
|
}
|
|
}
|
|
|
|
if (self.noise3)
|
|
precache_sound(self.noise3);
|
|
if (self.noise4)
|
|
precache_sound(self.noise4);
|
|
|
|
if (!self.speed)
|
|
self.speed = 150;
|
|
if (!self.wait)
|
|
self.wait = 1;
|
|
|
|
movewall_init ();
|
|
|
|
util_map_entity_init ();
|
|
|
|
if (!self.height)
|
|
self.height = self.size_z - 8;
|
|
|
|
self.pos1 = self.origin;
|
|
self.pos2 = self.origin;
|
|
self.pos2_z -= self.height;
|
|
|
|
// =============
|
|
tmin = self.mins + '25 25 0';
|
|
tmax = self.maxs - '25 25 -8';
|
|
tmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8);
|
|
|
|
if (self.spawnflags & SPAWNFLAGS_PLAT_LOW_TRIGGER)
|
|
tmax_z = tmin_z + 8;
|
|
|
|
if (self.size_x <= 50)
|
|
tmin_x = (self.mins_x + self.maxs_x) * 0.5 + 1;
|
|
if (self.size_y <= 50)
|
|
tmin_y = (self.mins_y + self.maxs_y) * 0.5 + 1;
|
|
|
|
_movewall_spawn_trigger(self.pos1, tmin, tmax);
|
|
// =============
|
|
|
|
if (!self.targetname) {
|
|
setorigin (self, self.pos2);
|
|
self.pos2 = self.pos1;
|
|
self.pos1 = self.origin;
|
|
|
|
self.style = 1;
|
|
} else {
|
|
self.style = 0;
|
|
}
|
|
|
|
self.state = 1 - self.style;
|
|
|
|
self.owner = self;
|
|
|
|
self.goalentity = self;
|
|
|
|
self.use = _movewall_plat_touch;
|
|
|
|
self.touch = _movewall_plat_touch;
|
|
self.blocked = _movewall_block_goback;
|
|
self.w_think = _movewall_binary_think;
|
|
};
|
|
|
|
// ========================================================================
|
|
|
|
/* buttons, out pos1, in pos2 */
|
|
|
|
void() _movewall_button_think = {
|
|
if (floor (self.state) != self.state) {
|
|
local vector tmp;
|
|
|
|
if (self.state == 0.5) {
|
|
if (self.noise3)
|
|
sound (self, CHAN_BODY, self.noise3, 1, ATTN_NORM);
|
|
}
|
|
|
|
tmp = self.pos2;
|
|
self.pos2 = self.pos1;
|
|
self.pos1 = tmp;
|
|
self.style = 1 - self.style;
|
|
} else {
|
|
if (self.state == 1) {
|
|
if (self.noise4)
|
|
sound (self, CHAN_BODY|CHAN_NO_PHS_ADD, self.noise4, 1,
|
|
ATTN_NORM);
|
|
|
|
self.think = _movewall_wait_think;
|
|
|
|
if (util_use_targets ()) {
|
|
self.frame = 1;
|
|
self.nextthink = self.ltime + self.wait;
|
|
} else {
|
|
self.nextthink = self.ltime + 0.5;
|
|
}
|
|
} else {
|
|
self.frame = 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
void() _movewall_button_touch = {
|
|
if (!is_living (other))
|
|
return;
|
|
if (self.state != 0)
|
|
return;
|
|
|
|
self.use ();
|
|
};
|
|
|
|
float(float d) _movewall_button_takedamage = {
|
|
deathmsg_nodisplay ();
|
|
|
|
if (self.state != 0)
|
|
return FALSE;
|
|
|
|
self.health -= d;
|
|
|
|
if (self.health <= 0) {
|
|
self.health = self.max_health;
|
|
self.use ();
|
|
}
|
|
|
|
return FALSE; // lie
|
|
};
|
|
|
|
void() func_button = {
|
|
if (!self.noise3) {
|
|
switch (self.sounds) {
|
|
case 0:
|
|
self.noise3 = "buttons/airbut1.wav";
|
|
break;
|
|
case 1:
|
|
self.noise3 = "buttons/switch21.wav";
|
|
break;
|
|
case 2:
|
|
self.noise3 = "buttons/switch02.wav";
|
|
break;
|
|
case 3:
|
|
self.noise3 = "buttons/switch04.wav";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (self.noise3)
|
|
precache_sound (self.noise3);
|
|
if (self.noise4)
|
|
precache_sound (self.noise4);
|
|
|
|
if (!self.speed)
|
|
self.speed = 40;
|
|
if (!self.wait)
|
|
self.wait = 1;
|
|
if (!self.lip)
|
|
self.lip = 4;
|
|
|
|
movewall_init ();
|
|
|
|
util_map_entity_init ();
|
|
|
|
util_set_movedir ();
|
|
|
|
self.pos1 = self.origin;
|
|
self.pos2 = self.pos1 + self.movedir * (fabs (self.movedir * self.size)
|
|
- self.lip);
|
|
|
|
self.style = 1;
|
|
self.state = 1 - self.style;
|
|
|
|
if (!self.health && !self.targetname) {
|
|
self.touch = _movewall_button_touch;
|
|
} else {
|
|
if (self.health) {
|
|
self.takedamage = DAMAGE_YES;
|
|
self.th_takedamage = _movewall_button_takedamage;
|
|
}
|
|
|
|
self.touch = NOTHING_function;
|
|
}
|
|
|
|
self.goalentity = self;
|
|
|
|
self.blocked = _movewall_block_ignore;
|
|
self.w_think = _movewall_button_think;
|
|
};
|
|
|
|
// ========================================================================
|
|
|
|
#define SPAWNFLAGS_SECRET_ONCE 1
|
|
#define SPAWNFLAGS_SECRET_1ST_LEFT 2
|
|
#define SPAWNFLAGS_SECRET_1ST_DOWN 4
|
|
#define SPAWNFLAGS_SECRET_NO_SHOOT 8
|
|
#define SPAWNFLAGS_SECRET_YES_SHOOT 16
|
|
|
|
/* 'secret door', has several points it moves to. */
|
|
/*
|
|
state 0 is closed
|
|
state 1 is back
|
|
state 2 is to the side (fully open)
|
|
*/
|
|
/* noise3 is played when transitioning */
|
|
/* noise4 is played when the transition is complete */
|
|
|
|
/* pos1 is state 0 position */
|
|
|
|
void() _movewall_secret_use = {
|
|
// Stay open...
|
|
if (self.count == 2 && self.state == 2)
|
|
self.nextthink = self.ltime + self.wait;
|
|
|
|
if (self.state != 0)
|
|
return;
|
|
|
|
_movewall_use ();
|
|
};
|
|
|
|
.vector dest1, dest2;
|
|
void() _movewall_secret_think = {
|
|
if (floor (self.state) != self.state) {
|
|
if (self.count == 0 && self.state == 0.5) {
|
|
self.message = nil;
|
|
self.noise2 = nil;
|
|
|
|
if (!util_use_targets ()) {
|
|
self.velocity = '0 0 0';
|
|
setorigin (self, self.pos1);
|
|
|
|
self.state = 0;
|
|
self.nextthink = -1;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (self.noise3)
|
|
sound (self, CHAN_BODY, self.noise3, 1, ATTN_NORM);
|
|
|
|
return;
|
|
}
|
|
|
|
if (self.noise4)
|
|
sound (self, CHAN_BODY|CHAN_NO_PHS_ADD, self.noise4, 1, ATTN_NORM);
|
|
|
|
self.think = _movewall_use;
|
|
self.nextthink = self.ltime + 1.0;
|
|
|
|
if (self.count == 0) {
|
|
if (self.state == 0) {
|
|
self.style = 1;
|
|
self.pos2 = self.dest1;
|
|
} else if (self.state == 1) {
|
|
self.style = 2;
|
|
self.pos2 = self.dest2;
|
|
}
|
|
} else if (self.count == 1 && self.state == 2) {
|
|
if (self.spawnflags & SPAWNFLAGS_SECRET_ONCE) {
|
|
self.nextthink = -1;
|
|
} else {
|
|
self.style = 1;
|
|
self.pos2 = self.dest1;
|
|
|
|
self.nextthink = self.ltime + self.wait;
|
|
}
|
|
} else if (self.count == 2 && self.state == 1) {
|
|
self.style = 0;
|
|
self.pos2 = self.pos1;
|
|
} else if (self.count == 1 && self.state == 0) {
|
|
self.style = 1;
|
|
self.pos2 = self.dest1;
|
|
|
|
self.nextthink = -1;
|
|
}
|
|
|
|
self.attack_finished = self.nextthink;
|
|
|
|
self.count = self.state;
|
|
};
|
|
|
|
float(float d) _movewall_secret_takedamage = {
|
|
self.use ();
|
|
deathmsg_nodisplay ();
|
|
return FALSE;
|
|
};
|
|
|
|
void() _movewall_secret_touch = {
|
|
if (!is_living (other))
|
|
return;
|
|
|
|
if (time < self.attack_finished)
|
|
return;
|
|
|
|
self.attack_finished = time + 2;
|
|
|
|
if (self.message) {
|
|
if (is_cl (other))
|
|
centerprint (other, self.message);
|
|
|
|
if (self.noise2)
|
|
sound (other, CHAN_ITEM, self.noise2, self.volume,
|
|
self.attenuation);
|
|
}
|
|
};
|
|
|
|
/*QUAKED func_door_secret (0 .5 .8) ? SECRET_ONCE SECRET_1ST_LEFT SECRET_1ST_DOWN SECRET_NO_SHOOT SECRET_YES_SHOOT
|
|
Secret door. Slides back, then to the side. Angle determines direction.
|
|
wait # of seconds before coming back
|
|
1st_left 1st move is left of arrow
|
|
1st_down 1st move is down from arrow
|
|
no_shoot not shootable
|
|
yes_shoot even if targeted, keep shootable
|
|
|
|
t_width override WIDTH to move back (or height if going down)
|
|
t_length override LENGTH to move sideways
|
|
dmg damage to inflict when blocked (default 2)
|
|
*/
|
|
|
|
void() func_door_secret = {
|
|
if (!self.noise3) {
|
|
switch (self.sounds) {
|
|
case 1:
|
|
self.noise1 = "doors/latch2.wav";
|
|
self.noise3 = "doors/winch2.wav";
|
|
self.noise4 = "doors/drclos4.wav";
|
|
break;
|
|
case 2:
|
|
self.noise3 = "doors/airdoor1.wav";
|
|
self.noise4 = "doors/airdoor2.wav";
|
|
break;
|
|
case 0:
|
|
case 3:
|
|
self.noise3 = "doors/basesec1.wav";
|
|
self.noise4 = "doors/basesec2.wav";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (self.message != "" && !self.noise2)
|
|
self.noise2 = "misc/talk.wav";
|
|
|
|
if (self.noise3)
|
|
precache_sound(self.noise3);
|
|
if (self.noise4)
|
|
precache_sound(self.noise4);
|
|
|
|
self.solid = SOLID_BSP;
|
|
self.movetype = MOVETYPE_PUSH;
|
|
|
|
if (!self.speed)
|
|
self.speed = 50;
|
|
if (!self.wait)
|
|
self.wait = 5;
|
|
if (!self.dmg)
|
|
self.dmg = 2;
|
|
|
|
movewall_init ();
|
|
|
|
util_map_entity_init ();
|
|
|
|
self.use = _movewall_secret_use;
|
|
|
|
// ===============
|
|
|
|
util_set_movedir();
|
|
|
|
if (!self.t_width) {
|
|
if (self.spawnflags & SPAWNFLAGS_SECRET_1ST_DOWN)
|
|
self.t_width = fabs (v_up * self.size);
|
|
else
|
|
self.t_width = fabs (v_right * self.size);
|
|
}
|
|
|
|
if (!self.t_length) {
|
|
self.t_length = fabs (v_forward * self.size);
|
|
}
|
|
|
|
self.pos1 = self.origin;
|
|
|
|
if (self.spawnflags & SPAWNFLAGS_SECRET_1ST_DOWN) {
|
|
self.dest1 = self.pos1 - v_up * self.t_width;
|
|
} else {
|
|
if (self.spawnflags & SPAWNFLAGS_SECRET_1ST_LEFT)
|
|
v_right = '0 0 0' - v_right;
|
|
|
|
self.dest1 = self.pos1 + v_right * self.t_width;
|
|
}
|
|
|
|
self.dest2 = self.dest1 + v_forward * self.t_length;
|
|
|
|
self.state = 0;
|
|
self.style = 1 - self.state;
|
|
|
|
self.pos2 = self.dest1;
|
|
|
|
self.sounds = 0;
|
|
|
|
// ===============
|
|
|
|
if (!self.targetname || (self.spawnflags & SPAWNFLAGS_SECRET_YES_SHOOT)) {
|
|
self.takedamage = DAMAGE_YES;
|
|
self.th_takedamage = _movewall_secret_takedamage;
|
|
}
|
|
|
|
self.goalentity = self;
|
|
|
|
self.blocked = _movewall_block_ignore;
|
|
self.touch = _movewall_secret_touch;
|
|
self.w_think = _movewall_secret_think;
|
|
};
|
|
|
|
// ========================================================================
|
|
|
|
/* Trains. Joy. */
|
|
|
|
void() _movewall_train_think = {
|
|
if (floor(self.state) != self.state) {
|
|
if (self.count != self.state && self.noise3)
|
|
sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
|
|
} else {
|
|
if (self.noise4)
|
|
sound (self, CHAN_VOICE|CHAN_NO_PHS_ADD, self.noise4, 1,
|
|
ATTN_NORM);
|
|
|
|
self.think = _movewall_wait_think;
|
|
self.nextthink = self.ltime + self.wait;
|
|
}
|
|
|
|
self.count = self.state;
|
|
};
|
|
|
|
void() _train_finalize = {
|
|
local entity oldself;
|
|
|
|
oldself = self;
|
|
while (self) {
|
|
if (self.goalentity)
|
|
break; // Looped. FIXME: Use short circuit when compiler supports
|
|
|
|
if (!self.target)
|
|
objerror ("Train without target\n");
|
|
self.goalentity = find (world, targetname, self.target);
|
|
if (!self.goalentity)
|
|
objerror ("Unable to find train target\n");
|
|
|
|
self = self.goalentity;
|
|
self.pos2 = self.origin;
|
|
}
|
|
self = oldself;
|
|
|
|
setorigin (self, self.goalentity.origin - self.view_ofs);
|
|
|
|
if (!self.targetname) {
|
|
self.think = _movewall_wait_think;
|
|
self.nextthink = self.ltime + sv_mintic;
|
|
}
|
|
};
|
|
|
|
/*QUAKED func_train (0 .5 .8)
|
|
*/
|
|
void() func_train = {
|
|
if (!self.noise3) {
|
|
if (self.sounds == 1) {
|
|
self.noise3 = "plats/train1.wav";
|
|
self.noise4 = "plats/train2.wav";
|
|
}
|
|
}
|
|
|
|
if (self.noise3)
|
|
precache_sound(self.noise3);
|
|
if (self.noise4)
|
|
precache_sound(self.noise4);
|
|
|
|
if (!self.speed)
|
|
self.speed = 100;
|
|
if (!self.wait)
|
|
self.wait = 0.1;
|
|
if (!self.dmg)
|
|
self.dmg = 2;
|
|
if (self.dmg < 0)
|
|
self.dmg = 0;
|
|
|
|
self.view_ofs = self.mins;
|
|
|
|
self.blocked = _movewall_block_ignore;
|
|
self.w_think = _movewall_train_think;
|
|
|
|
movewall_init();
|
|
|
|
util_map_entity_init();
|
|
|
|
// Make sure all the targetnames are set
|
|
self.think = _train_finalize;
|
|
self.nextthink = self.ltime + sv_mintic;
|
|
};
|