hexen2-hw-hc/doors.hc

1784 lines
42 KiB
C++

/*
* $Header: /HexenWorld/HCode/Doors.hc 5 4/23/98 5:15p Mgummelt $
*/
float DOOR_START_OPEN = 1;
float REVERSE = 2;
float DOOR_DONT_LINK = 4;
float DOOR_TOGGLE = 8;
float DOOR_SLIDE = 16;
float DOOR_NORMAL = 32;
float DOOR_REMOVE_PP = 64;
float DOOR_NO_PP = 128;
/*
Doors are similar to buttons, but can spawn a fat trigger field around them
to open without a touch, and they link together to form simultanious
double/quad doors.
Door.owner is the master door. If there is only one door, it points to itself.
If multiple doors, all will point to a single one.
Door.enemy chains from the master door through all doors linked in the chain.
*/
void door_hit_bottom();
void door_hit_top();
/*
===========================================================================
door_slide
===========================================================================
*/
void door_slide_next()
{
local vector vdestdelta, odelta;
local float len, tspeed;//, len2;
tspeed = self.speed;
if(!tspeed)
objerror("No speed is defined!");
// Make sure we're not already at the destination
if(self.finaldest == self.origin)
{
self.velocity = '0 0 0';
if(self.state == STATE_DOWN)
self.think = door_hit_bottom;
else if(self.state == STATE_UP)
self.think = door_hit_top;
self.nextthink = self.ltime + 0.1;
return;
}
vdestdelta = self.finaldest - self.origin; // Set destdelta to the vector needed to move
odelta = self.origin - self.pos1;
len = vlen(odelta); // Get the length of the vector
// If the length is this small, just stop
if(vlen(vdestdelta) < 0.1)
{
if(self.state == STATE_DOWN)
door_hit_bottom();
else if(self.state == STATE_UP)
door_hit_top();
else
dprint("Bad door state!\n");
return;
}
self.nextthink = self.ltime + 0.1;
self.think = door_slide_next;
tspeed = ((self.speed - (len / 10)) / 20);
if(tspeed < 2)
{
if(self.state == STATE_DOWN)
SUB_CalcMove(self.finaldest, self.speed, door_hit_bottom);
else if(self.state == STATE_UP)
SUB_CalcMove(self.finaldest, self.speed, door_hit_top);
else
dprint("Bad door state!\n");
return;
}
self.velocity = vdestdelta * tspeed;
}
void door_slide(vector tdest)
{
self.finaldest = tdest;
door_slide_next();
}
/*
===========================================================================
door_crash
===========================================================================
*/
void door_crash_next()
{
local vector vdestdelta, nextvect, testvect;
local vector tdest;
local float len, nextlen, testlen;
local float tspeed;
tdest = self.finaldest;
tspeed = self.speed;
if(!tspeed)
objerror("No speed is defined!");
if(tdest == self.origin)
{
self.velocity = '0 0 0';
if(self.state == STATE_DOWN)
self.think = door_hit_bottom;
else if(self.state == STATE_UP)
self.think = door_hit_top;
else
dprint("Bad door state!\n");
self.nextthink = self.ltime + 0.1;
return;
}
// set destdelta to the vector needed to move
vdestdelta = self.finaldest - self.origin;
nextvect = self.pos2 - self.origin;
testvect = self.pos2 - self.finaldest;
// calculate length of vector
len = vlen (vdestdelta);
nextlen = vlen(nextvect) + 1;
testlen = vlen(testvect);
if(len < 0.1 || nextlen > testlen)
{
door_hit_bottom;
return;
}
else
{
self.velocity = vdestdelta * (nextlen / len) * 4;
nextvect = self.origin + self.velocity;
nextlen = vlen(nextvect);
if(nextlen >= testlen * 2)
{
SUB_CalcMove(self.finaldest, self.speed * (len / (len / 3)), door_hit_bottom);
return;
}
}
self.nextthink = self.ltime + 0.1;
self.think = door_crash_next;
}
void door_crash(vector tdest)
{
self.finaldest = tdest;
door_crash_next();
}
/*
=============================================================================
THINK FUNCTIONS
=============================================================================
*/
void door_go_down();
void door_go_up();
void door_damage()
{
T_Damage (other, self, self, self.dmg);
}
void door_blocked()
{
//FIXME: Rotating doors sem to think they're being blocked
// even if they're rotating down and the object is above them
if(self.wait>-2&&self.strength<=0)
if(self.dmg==666)
{
if(other.classname=="player"&&other.flags2&FL_ALIVE)
{
other.decap=TRUE;
T_Damage (other, self, self, other.health+300);
}
else
T_Damage (other, self, self, other.health+50);
}
else
{
T_Damage (other, self, self, self.dmg);
}
//Rotating doors rotating around a x or z axis push you up and in the direction they're turning
/* if(self.strength==2)
{
other.flags(-)FL_ONGROUND;
other.velocity=normalize(self.origin-(other.absmin+other.absmax)*0.5)*100;
}
else*/ if(other.flags&FL_ONGROUND&&(self.movedir_x||self.movedir_z)&&self.strength==1)//&&other.origin_z>self.origin_z
{
other.flags(-)FL_ONGROUND;
other.velocity_z+=self.speed*2;
other.velocity_x-=self.speed*self.movedir_x;
other.velocity_y-=self.speed*self.movedir_z;
}
else
{
other.flags(-)FL_ONGROUND;
other.velocity_z+=10;
}
// if a door has a negative wait, it would never come back if blocked,
// so let it just squash the object to death real fast
if (other.health>0)
if (self.wait >= 0)
{
if (self.state == STATE_DOWN)
door_go_up ();
else
door_go_down ();
}
}
void door_blocked_mp()
{
float do_dmg;
/*
dprint("Blocked Door: \n");
dprint(other.classname);
dprint("\n");
dprintv("Velocity: %s\n",self.velocity);
dprintv("Avelocity: %s\n",self.avelocity);
*/
//FIXME: Rotating doors seem to think they're being blocked
// even if they're rotating down and the object is above them
// if(self.classname=="door_rotating")
// self.nextthink+=HX_FRAME_TIME;//self.wait?
/* dprint("door blocked\n");
dprintf("dmg = %s\n",self.dmg);
dprintf("strength = %s\n",self.strength);
dprintf("wait = %s\n",self.wait);
dprint("other = ");
dprint(other.classname);
dprint("\n");*/
if(self.dmg==-1)
{
if(other.classname=="player" && other.flags2&FL_ALIVE)
{
if (self.wait >= 0)
{
if (self.state == STATE_DOWN)
door_go_up ();
else
door_go_down ();
}
return;
}
else
do_dmg=2;
}
else
do_dmg=self.dmg;
// dprintf("Door dmg = %s\n",do_dmg);
if(self.wait>-2)//&&self.strength<=0)
{
if(do_dmg==666)
{
if(other.classname=="player"&&other.flags2&FL_ALIVE)
{
other.decap=TRUE;
T_Damage (other, self, self, other.health+300);
}
else
T_Damage (other, self, self, other.health+50);
}
else
{
// dprintf("crushing- %s\n",do_dmg);
T_Damage (other, self, self, do_dmg);//FIXME: Rotating doors get stuck open and never try to return
}
}
// else
// dprint("Door wait <= -2\n");
//Rotating doors rotating around a x or z axis push you up and in the direction they're turning
/* if(self.strength==2)
{
other.flags(-)FL_ONGROUND;
other.velocity=normalize(self.origin-(other.absmin+other.absmax)*0.5)*100;
}
else*/
/*NOT in MP maps?
if(other.flags&FL_ONGROUND&&(self.movedir_x||self.movedir_z)&&self.strength==1)//&&other.origin_z>self.origin_z
{//This is not neccessary anymore
other.flags(-)FL_ONGROUND;
other.velocity_z+=self.speed*2;
other.velocity_x-=self.speed*self.movedir_x;
other.velocity_y-=self.speed*self.movedir_z;
}
else
{
other.flags(-)FL_ONGROUND;
other.velocity_z+=10;
}
*/
// if a door has a negative wait, it would never come back if blocked,
// so let it just squash the object to death real fast
if (other.health>0)
if (self.wait >= 0)//&&self.wait!=1.5)
{
if (self.state == STATE_DOWN)
{
// dprint("Going up...\n");
door_go_up ();
}
else
{
// dprint("Going down...\n");
door_go_down ();
}
}
}
void door_hit_top()
{
self.velocity = '0 0 0';
sound (self,CHAN_UPDATE+PHS_OVERRIDE_R, self.noise1, 1, ATTN_NORM);
self.effects(-)EF_UPDATESOUND;
self.state = STATE_TOP;
if (self.spawnflags & DOOR_TOGGLE)
return; // don't come down automatically
if(self.wait== -2)
self.th_die();
else if(self.wait== -1)
self.nextthink = -1;
else
{
self.think = door_go_down;
self.nextthink = self.ltime + self.wait;
}
}
void door_hit_bottom()
{
self.velocity = '0 0 0';
sound (self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise1, 1, ATTN_NORM);
self.effects(-)EF_UPDATESOUND;
self.state = STATE_BOTTOM;
}
void door_go_down()
{
string hold_target;
sound(self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise2, 1, ATTN_LOOP);
self.effects(+)EF_UPDATESOUND;
if(!self.thingtype && self.max_health)
{
self.takedamage = DAMAGE_YES;
self.health = self.max_health;
}
self.state = STATE_DOWN;
if(self.classname == "door")
{
// dprintv("rotation: %s\n",self.v_angle);
if(self.spawnflags & DOOR_SLIDE)
door_slide(self.pos1);
else if(self.spawnflags & DOOR_NORMAL)
if(self.v_angle!='0 0 0')
if(self.speed)
if(self.anglespeed)
SUB_CalcMoveAndAngleInit (self.pos1, self.speed, self.o_angle, self.anglespeed, door_hit_bottom,FALSE);
else
SUB_CalcMoveAndAngleInit (self.pos1, self.speed, self.o_angle, self.anglespeed, door_hit_bottom,TRUE);
else
SUB_CalcAngleMove(self.o_angle, self.anglespeed, door_hit_bottom);
else
SUB_CalcMove(self.pos1, self.speed, door_hit_bottom);
else
door_crash(self.pos1);
}
else if (self.classname == "door_rotating")
SUB_CalcAngleMove(self.pos1, self.speed, door_hit_bottom);
if(self.close_target!="")
{//Use second target when closing
hold_target=self.target;
self.target=self.close_target;
SUB_UseTargets();
self.target=hold_target;
}
}
void new_movedir (vector movin,float dir)
{
self.movedir = movin;
// check for clockwise rotation
if (dir<0)
self.movedir = self.movedir * -1;
self.pos1 = self.angles;
self.pos2 = self.angles + self.movedir * self.dflags;
}
void door_go_up()
{
if(self.state == STATE_UP) /* Already going up */
{
// dprint("UP: Tried to go up while already going up\n");
return;
}
if(self.state == STATE_TOP) /* Reset top wait time */
{
self.nextthink = self.ltime + self.wait;
// dprint("TOP: Tried to go up while already at top\n");
return;
}
sound(self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise2, 1, ATTN_LOOP);
self.effects(+)EF_UPDATESOUND;
self.state = STATE_UP;
if(self.classname == "door")
{
if(self.spawnflags & DOOR_NORMAL)
{
if(self.v_angle!='0 0 0')
{
if(self.speed)
{
if(self.anglespeed)
SUB_CalcMoveAndAngleInit (self.pos2, self.speed, self.v_angle, self.anglespeed, door_hit_top,FALSE);
else
SUB_CalcMoveAndAngleInit (self.pos2, self.speed, self.v_angle, self.anglespeed, door_hit_top,TRUE);
}
else
SUB_CalcAngleMove(self.v_angle, self.anglespeed, door_hit_top);
}
else
SUB_CalcMove(self.pos2, self.speed, door_hit_top);
}
else
door_slide(self.pos2);
}
else if(self.classname == "door_rotating")
SUB_CalcAngleMove(self.pos2, self.speed, door_hit_top);
SUB_UseTargets();
}
/*
=============================================================================
ACTIVATION FUNCTIONS
=============================================================================
*/
void door_fire()
{
entity oself;
entity starte;
// if(self.wait<=-1 || self.wait==1.5)//not supposed to return
// if(self.velocity!='0 0 0'||self.avelocity!='0 0 0')//Moving
// return;
if (self.owner != self)
objerror ("door_fire: self.owner != self");
self.no_puzzle_msg = 0;
// play use key sound
self.message = 0; // no more message
oself = self;
if (self.spawnflags & DOOR_TOGGLE)
{
if (self.state == STATE_UP || self.state == STATE_TOP)
{
starte = self;
do
{
door_go_down ();
self = self.enemy;
} while ( (self != starte) && (self != world) );
self = oself;
return;
}
}
// trigger all paired doors
starte = self;
do
{
door_go_up ();
self = self.enemy;
} while ( (self != starte) && (self != world) );
self = oself;
}
/*
* door_use() -- Called whenever a door is opened.
*/
void door_use()
{
entity oself;
/*
dprint("Door Used by: ");
dprint(other.classname);
dprint("\n");
dprint("Door's Activator: ");
dprint(activator.classname);
dprint("\n");
*/
if(self.inactive)
{
// dprint("Door not active\n");
return;
}
self.message = 0; // door messages are for touch only
self.owner.message = 0;
self.enemy.message = 0;
oself = self;
self = self.owner;
door_fire ();
self = oself;
}
// defined in triggers.hc
float check_puzzle_pieces(entity client, float remove_pieces, float inverse);
/*
* door_trigger_touch() -- Called when someone touches a door.
*/
void door_trigger_touch()
{
entity door;
string temp;
float removepp, inversepp;
// if(!other.flags2&FL_ALIVE)
// return;
if(!other.flags&FL_CLIENT&&!other.flags&FL_MONSTER)
return;
if(other.flags&FL_MONSTER&&world.spawnflags&MISSIONPACK)
return;
if(time < self.attack_finished)
return;
door = self;
self = self.owner;
if(!deathmatch)
{
removepp = (self.spawnflags & DOOR_REMOVE_PP);
inversepp = (self.spawnflags & DOOR_NO_PP);
if (!check_puzzle_pieces(other,removepp,inversepp))
{
if (self.no_puzzle_msg && !deathmatch)
{
temp = getstring(self.no_puzzle_msg);
centerprint (other, temp);
door.attack_finished = time + 2;
}
return;
}
}
self.attack_finished = time + 1;
activator = other;
door_use();
}
/*
* door_killed() -- Used to open doors that open when shot.
*/
void door_killed()
{
local entity oself;
oself = self;
self = self.owner;
self.health = self.max_health;
self.takedamage = DAMAGE_NO; // wil be reset upon return
door_use();
self = oself;
}
/*
* door_touch() -- Prints messages and opens key doors.
*/
void door_touch()
{
string temp;
float removepp, inversepp;
// dprint("Door hit!\n");
// if(!other.flags2&FL_ALIVE)
// return;
if(!other.flags&FL_CLIENT&&!other.flags&FL_MONSTER)
return;
if(other.flags&FL_MONSTER&&world.spawnflags&MISSIONPACK)
return;
if(self.dmg==666&&(self.velocity!='0 0 0'||self.avelocity!='0 0 0'))
{
if(other.classname=="player"&&other.flags2&FL_ALIVE)
{
other.decap=TRUE;
T_Damage (other, self, self, other.health+300);
}
else
T_Damage (other, self, self, other.health+50);
}
if(self.owner.attack_finished > time)
return;
if (self.owner)
self.owner.attack_finished = time + 2;
if(self.owner.message != 0 && !deathmatch && self.owner != world)
{
temp = getstring(self.owner.message);
centerprint (other, temp);
sound (other, CHAN_UPDATE+PHS_OVERRIDE_R, "misc/comm.wav", 1, ATTN_NORM);
}
// key door stuff
if (!self.puzzle_piece_1 && !self.puzzle_piece_2 && !self.puzzle_piece_3 && !self.puzzle_piece_4)
return;
// FIXME: blink key on player's status bar
removepp = (self.spawnflags & DOOR_REMOVE_PP);
inversepp = (self.spawnflags & DOOR_NO_PP);
if (!check_puzzle_pieces(other,removepp,inversepp))
{
if (self.no_puzzle_msg && !deathmatch)
{
temp = getstring(self.no_puzzle_msg);
centerprint (other, temp);
}
return;
}
self.touch = SUB_Null;
if (self.enemy)
self.enemy.touch = SUB_Null; // get paired door
door_use ();
}
/*
=============================================================================
SPAWNING FUNCTIONS
=============================================================================
*/
entity spawn_field(vector fmins, vector fmaxs, entity door)
{//FIXME: THIS ENTITY NEEDS TO REMOVE ITSELF IF IT'S OWNER IS
//REMOVED!
entity trigger;
vector t1, t2;
trigger = spawn();
trigger.movetype = MOVETYPE_NONE;
trigger.solid = SOLID_TRIGGER;
trigger.owner = door;
trigger.touch = door_trigger_touch;
t1 = fmins;
t2 = fmaxs;
// if (door.classname == "door")
// {
// if(self.v_angle!='0 0 0')
// {
t1 += door.origin;
t2 += door.origin;
setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
/* }
else
setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
}
else if (door.classname == "door_rotating")
{
t1 += door.origin;
t2 += door.origin;
setsize (trigger, t1 - '60 60 8', t2 + '60 60 8');
}
*/ return (trigger);
}
float EntitiesTouching(entity e1, entity e2)
{
local vector e1max, e1min, e2max, e2min;
//Rotating door's mins and maxs aren't based on their world positions,
//so the origin needs to be applied to make sure they are checking their
//real positions
if (e1.classname == "door_rotating"||(e1.classname=="door"&&e1.v_angle!='0 0 0'))
{
e1max = e1.maxs + e1.origin;
e1min = e1.mins + e1.origin;
e2max = e2.maxs + e2.origin;
e2min = e2.mins + e2.origin;
}
else
{
e1max = e1.maxs;
e1min = e1.mins;
e2max = e2.maxs;
e2min = e2.mins;
}
if (e1min_x > e2max_x)
return FALSE;
if (e1min_y > e2max_y)
return FALSE;
if (e1min_z > e2max_z)
return FALSE;
if (e1max_x < e2min_x)
return FALSE;
if (e1max_y < e2min_y)
return FALSE;
if (e1max_z < e2min_z)
return FALSE;
return TRUE;
}
/*
* LinkDoors()
*/
void LinkDoors()
{
entity t, starte;
vector cmins, cmaxs;
if (self.enemy)
return; // already linked by another door
if (self.spawnflags & 4)
{
self.owner = self.enemy = self;
return; // don't want to link this door
}
cmins = self.mins;
cmaxs = self.maxs;
starte = self;
t = self;
do
{
self.owner = starte; // master door
if (!self.thingtype && self.health)
starte.health = self.health;
if (self.targetname)
starte.targetname = self.targetname;
if (self.message != 0)
starte.message = self.message;
t = find (t, classname, self.classname);
if (!t)
{
self.enemy = starte; // make the chain a loop
// shootable, fired, or key doors just needed the owner/enemy links,
// they don't spawn a field
self = self.owner;
if (!self.thingtype && self.health)
return;
if (self.targetname)
return;
if (self.puzzle_piece_1 != string_null ||
self.puzzle_piece_2 != string_null ||
self.puzzle_piece_3 != string_null ||
self.puzzle_piece_4 != string_null)
return;
self.owner.trigger_field = spawn_field(cmins, cmaxs, self.owner);
return;
}
if (EntitiesTouching(self,t))
{
if (t.enemy)
objerror ("cross connected doors");
self.enemy = t;
self = t;
if (t.mins_x < cmins_x)
cmins_x = t.mins_x;
if (t.mins_y < cmins_y)
cmins_y = t.mins_y;
if (t.mins_z < cmins_z)
cmins_z = t.mins_z;
if (t.maxs_x > cmaxs_x)
cmaxs_x = t.maxs_x;
if (t.maxs_y > cmaxs_y)
cmaxs_y = t.maxs_y;
if (t.maxs_z > cmaxs_z)
cmaxs_z = t.maxs_z;
}
} while (1);
}
void door_sounds(void)
{
self.noise3 = "doors/baddoor.wav";
if (self.soundtype == 0) // No sound
{
self.noise1 = "misc/null.wav";
self.noise2 = "misc/null.wav";
self.noise4 = "misc/null.wav";
}
else if (self.soundtype == 1) // Big Metal door, swinging
{
precache_sound ("doors/gatestop.wav");
precache_sound ("doors/gateswng.wav");
precache_sound ("doors/gatestrt.wav");
self.noise1 = "doors/gatestop.wav";
self.noise2 = "doors/gateswng.wav";
self.noise4 = "doors/gatestrt.wav";
}
else if (self.soundtype == 2) // Big Stone Door, sliding
{
precache_sound ("doors/doorstop.wav");
precache_sound ("doors/stonslid.wav");
precache_sound ("doors/dorstart.wav");
self.noise1 = "doors/doorstop.wav";
self.noise2 = "doors/stonslid.wav";
self.noise4 = "doors/dorstart.wav";
}
else if (self.soundtype == 3) // Big Wood Door, Swinging
{
precache_sound ("doors/swngstop.wav");
precache_sound ("doors/wdswngbg.wav");
precache_sound ("doors/dorstart.wav");
self.noise1 = "doors/swngstop.wav";
self.noise2 = "doors/wdswngbg.wav";
self.noise4 = "doors/dorstart.wav";
}
else if (self.soundtype == 4) // Normal Wood Door, Swinging
{
precache_sound ("doors/swngstop.wav");
precache_sound ("doors/wdswngsm.wav");
precache_sound ("doors/dorstart.wav");
self.noise1 = "doors/swngstop.wav";
self.noise2 = "doors/wdswngsm.wav";
self.noise4 = "doors/dorstart.wav";
}
else if (self.soundtype == 5) // Big Wood Door, Sliding
{
precache_sound ("doors/swngstop.wav");
precache_sound ("doors/woodslid.wav");
precache_sound ("doors/dorstart.wav");
self.noise1 = "doors/swngstop.wav";
self.noise2 = "doors/woodslid.wav";
self.noise4 = "doors/dorstart.wav";
}
else if (self.soundtype == 6) // Drawbridge, Falling and crushing innocent peasants who are
// basically slave labor, who toil away their lives so that the rich upperclass
// can keep busy inbreeding with each other. Damn the aristocracy! Freedom to
// the common man! Burn the castle! Death to the tyrants!
{
precache_sound ("doors/doorstop.wav");
precache_sound ("doors/drawmove.wav");
precache_sound ("doors/drawstrt.wav");
self.noise1 = "doors/doorstop.wav";
self.noise2 = "doors/drawmove.wav";
self.noise4 = "doors/dorstart.wav";
}
else if (self.soundtype == 7) // Rotating Walkway
{
precache_sound ("doors/doorstop.wav");
precache_sound ("doors/stonslid.wav");
precache_sound ("doors/dorstart.wav");
self.noise1 = "doors/doorstop.wav";
self.noise2 = "doors/stonslid.wav";
self.noise4 = "doors/dorstart.wav";
}
else if (self.soundtype == 8) // Big Metal door, sliding
{
precache_sound ("doors/mtlstop.wav");
precache_sound ("doors/mtlslide.wav");
precache_sound ("doors/mtlstart.wav");
self.noise1 = "doors/mtlstop.wav";
self.noise2 = "doors/mtlslide.wav";
self.noise4 = "doors/mtlstart.wav";
}
else if (self.soundtype == 9) // Pendulum
{
precache_sound2 ("doors/penstop.wav");
precache_sound2 ("doors/penswing.wav");
precache_sound2 ("doors/penstart.wav");
self.noise1 = "doors/penstop.wav";
self.noise2 = "doors/penswing.wav";
self.noise4 = "doors/penstart.wav";
}
}
void door_rotate_incr_done()
{
// if(self.strength==2)
// cvar_set ("sv_gravity", "800");
}
void() door_rotate_incr =
{
vector newvect;
if(self.strength==2)
cvar_set ("sv_gravity", "100");
self.dflags=self.flags;
self.flags=0;
if(self.v_angle!='0 0 0')
{
if(random()>0.5&&self.v_angle_x!=0)
{
self.cnt=self.dflags=self.v_angle_x;
new_movedir('1 0 0',self.cnt);
}
else if(random()>0.5&&self.v_angle_y!=0)
{
self.cnt=self.dflags=self.v_angle_y;
new_movedir('0 1 0',self.cnt);
}
else if(self.v_angle_z!=0)
{
self.cnt=self.dflags=self.v_angle_z;
new_movedir('0 0 1',self.cnt);
}
}
newvect = self.movedir * self.cnt;
SUB_CalcAngleMove(self.angles + newvect, self.speed, door_rotate_incr_done);
};
/*QUAKED func_door (0 .5 .8) ? START_OPEN REVERSE DOOR_DONT_LINK TOGGLE SLIDE NORMAL_MOVE remove_pp no_pp
NOTE: Doors can now be activated and deactivated with the appropriate triggers.
"inactive" set to 1 to start the door deactivated.
if two doors touch, they are assumed to be connected and operate as a unit.
-----------------------FIELDS-------------------------
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 usefull for touch or takedamage doors).
Key doors are allways wait -1.
"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
"level" how far (in map units) to move in the specified angle- overrides default movement that is size of door
"targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
"health" if set, door must be shot open
"speed" movement speed (100 default), -1 will not move, just rotate
"wait" wait before returning (3 default, -1 = never return)
"lip" lip remaining at end of move (8 default)
"dmg" damage to inflict when blocked (2 default) If you make it
666, it will gib anything it touches, and behead players.
-1 it will hurt other things, but not players
"close_target" secondary target to fire when door closes
ROTATING DOORS: MUST HAVE AN ORIGIN BRUSH
"v_angle" Angle to turn, in: pitch yaw roll, '0 0 0' will not rotate, just move (default = '0 0 0')
"anglespeed" how quickly to turn in that direction. no anglespeed will force it to choose one that will synch the completion of the rotation iwth the completion of the move. (default = 0)
"strength" When set to 1, it will throw something if it gets in the way
"soundtype"
0) no sound
1) Big metal door, swinging
2) Big stone door, sliding
3) Big wood door, swinging
4) Normal wood door, swinging
5) Big wood door, sliding
6) Drawbridge
7) Rotating walkway
8) Big metal door, sliding
9) Pendulum swinging
Puzzle Pieces (use the puzzle_id value from the pieces)
puzzle_piece_1
puzzle_piece_2
puzzle_piece_3
puzzle_piece_4
no_puzzle_msg: message when player doesn't have the right pieces
--------------------------------------------------------
*/
void func_door()
{
float movedist, num_axes;
door_sounds();
SetMovedir ();
// check for clockwise rotation
if (self.spawnflags & REVERSE)
{
self.movedir = self.movedir * -1;
self.v_angle = self.v_angle * -1;
}
self.max_health = self.health;
self.solid = SOLID_BSP;
self.movetype = MOVETYPE_PUSH;
setorigin (self, self.origin);
setmodel (self, self.model);
self.classname = "door";
self.use = door_use;
if (self.abslight)
self.drawflags = self.drawflags | MLS_ABSLIGHT;
if (self.speed==-1)
self.speed=0;
else if(!self.speed)
self.speed = 100;
if (!self.wait)
self.wait = 3;
if (!self.lip)
self.lip = 8;
if(world.spawnflags&MISSIONPACK)
self.blocked = door_blocked_mp;
else
self.blocked = door_blocked;
if (!self.dmg)
// self.dmg = -1;
self.dmg = 2;
self.pos1 = self.origin;
// dprintf("Worldspawn.spawnflags = %s\n",world.spawnflags);
if(world.spawnflags&MISSIONPACK)
{
// dprint("Using new door code\n");
if(self.level)
movedist = self.level - self.lip;
else
{
//was: movedist=fabs(self.movedir*self.size) - self.lip;
// dprintv("Door movedir = %s\n",self.movedir);
// dprintv("Door size = %s\n",self.size);
num_axes=0;
movedist=0;
if(fabs(self.movedir_x)>0.001)
{
// dprint("X axis\n");
movedist+=fabs(self.movedir_x*self.size_x);
num_axes+=1;
}
if(fabs(self.movedir_y)>0.001)
{
// dprint("Y axis\n");
movedist+=fabs(self.movedir_y*self.size_y);
num_axes+=1;
}
if(fabs(self.movedir_z)>0.001)
{
// dprint("Z axis\n");
movedist+=fabs(self.movedir_z*self.size_z);
num_axes+=1;
}
movedist=movedist/num_axes - self.lip;
// dprintf("Door movedist = %s\n",movedist);
}
self.pos2 = self.pos1 + self.movedir*movedist;
}
else
{
// dprint("Using old door code\n");
if(self.level)
self.pos2 = self.pos1 + self.movedir*(self.level - self.lip);
else
self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
}
// dprintv("Pos1: %s\n",self.pos1);
// dprintv("Pos2: %s\n",self.pos2);
if(self.v_angle!='0 0 0')
{
self.o_angle=self.angles;
self.v_angle+=self.angles;
}
if(self.wait== -2)
{
self.th_die = chunk_death;
// self.takedamage = DAMAGE_YES;
if (!self.health)
{
if ((self.thingtype == THINGTYPE_GLASS) || (self.thingtype == THINGTYPE_CLEARGLASS))
self.max_health = self.health = 25;
else if ((self.thingtype == THINGTYPE_GREYSTONE) || (self.thingtype == THINGTYPE_BROWNSTONE)||self.thingtype == THINGTYPE_DIRT)
self.max_health = self.health = 75;
else if (self.thingtype == THINGTYPE_WOOD)
self.max_health = self.health = 50;
else if (self.thingtype == THINGTYPE_METAL)
self.max_health = self.health = 100;
else if (self.thingtype == THINGTYPE_FLESH)
self.max_health = self.health = 30;
else
self.max_health = self.health = 25;
}
}
// DOOR_START_OPEN is to allow an entity to be lighted in the closed position
// but spawn in the open position
if (self.spawnflags & DOOR_START_OPEN)
{
if(self.v_angle!='0 0 0')
{
self.angles = self.v_angle;
self.v_angle = self.o_angle;
self.o_angle = self.angles;
}
setorigin (self, self.pos2);
self.pos2 = self.pos1;
self.pos1 = self.origin;
}
self.state = STATE_BOTTOM;
if (self.health)
{
self.takedamage = DAMAGE_YES;
self.th_die =self.th_pain= door_killed;
}
if (self.puzzle_piece_1 != string_null ||
self.puzzle_piece_2 != string_null ||
self.puzzle_piece_3 != string_null ||
self.puzzle_piece_4 != string_null)
self.wait = -1;
self.touch = door_touch;
// LinkDoors can't be done until all of the doors have been spawned, so
// the sizes can be detected properly.
self.think = LinkDoors;
self.nextthink = self.ltime + 0.1;
if (self.cnt)
{
self.touch = SUB_Null;
self.use = door_rotate_incr;
}
}
/*QUAKED func_door_smashing (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK TOGGLE SLIDE NORMAL_MOVE remove_pp no_pp
NOTE: Doors can now be activated and deactivated with the appropriate triggers.
"inactive" set to 1 to start the door deactivated.
if two doors touch, they are assumed to be connected and operate as a unit.
-----------------------FIELDS-------------------------
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 usefull for touch or takedamage doors).
Key doors are allways wait -1.
"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.
"speed" movement speed (100 default)
"wait" wait before returning (3 default, -1 = never return)
"lip" lip remaining at end of move (8 default)
"dmg" damage to inflict when blocked (2 default)
"close_target" secondary target to fire when door closes
"soundtype"
0) no sound
1) Big metal door, swinging
2) Big stone door, sliding
3) Big wood door, swinging
4) Normal wood door, swinging
5) Big wood door, sliding
6) Drawbridge
7) Rotating walkway
8) Big metal door, sliding
9) Pendulum swinging
Puzzle Pieces (use the puzzle_id value from the pieces)
puzzle_piece_1
puzzle_piece_2
puzzle_piece_3
puzzle_piece_4
no_puzzle_msg: message when player doesn't have the right pieces
--------------------------------------------------------
void func_door_smashing()
{
door_sounds();
SetMovedir ();
self.solid = SOLID_BSP;
self.movetype = MOVETYPE_PUSH;
setorigin (self, self.origin);
setmodel (self, self.model);
self.classname = "door";
if(world.spawnflags&MISSIONPACK)
self.blocked = door_blocked_mp;
else
self.blocked = door_blocked;
self.use = door_use;
if (!self.speed)
self.speed = 100;
if (!self.wait)
self.wait = 3;
if (!self.lip)
self.lip = 8;
if (!self.dmg)
self.dmg = 2;
self.pos1 = self.origin;
self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
// DOOR_START_OPEN is to allow an entity to be lighted in the closed position
// but spawn in the open position
if (self.spawnflags & DOOR_START_OPEN)
{
setorigin (self, self.pos2);
self.pos2 = self.pos1;
self.pos1 = self.origin;
}
self.state = STATE_BOTTOM;
self.takedamage = DAMAGE_YES;
self.th_die = chunk_death;
if(self.soundtype == 1)
{
self.thingtype = THINGTYPE_GREYSTONE;
self.max_health = self.health = 75;
}
else if(self.soundtype == 4)
{
self.thingtype = THINGTYPE_METAL;
self.max_health = self.health = 100;
}
else {
self.thingtype = THINGTYPE_WOOD;
self.max_health = self.health = 50;
}
self.touch = door_touch;
// LinkDoors can't be done until all of the doors have been spawned, so
// the sizes can be detected properly.
self.think = LinkDoors;
self.nextthink = self.ltime + 0.1;
}*/
/*
=============================================================================
SECRET DOORS
=============================================================================
*/
void fd_secret_move1();
void fd_secret_move2();
void fd_secret_move3();
void fd_secret_move4();
void fd_secret_move5();
void fd_secret_move6();
void fd_secret_done();
float SECRET_OPEN_ONCE = 1; // stays open
float SECRET_1ST_LEFT = 2; // 1st move is left of arrow
float SECRET_1ST_DOWN = 4; // 1st move is down from arrow
float SECRET_NO_SHOOT = 8; // only opened by trigger
float SECRET_YES_SHOOT = 16; // shootable even if targeted
void fd_secret_use()
{
local float temp;
self.health = 10000;
// exit if still moving around...
if(self.origin != self.oldorigin)
return;
if(self.inactive)
return;
self.message = 0; // no more message
SUB_UseTargets(); // fire all targets / killtargets
if(!self.spawnflags & SECRET_NO_SHOOT)
{
self.th_pain = SUB_Null;
self.takedamage = DAMAGE_NO;
}
self.velocity = '0 0 0';
// Make a sound, wait a little...
sound(self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise1, 1, ATTN_NORM);
self.effects(-)EF_UPDATESOUND;
self.nextthink = self.ltime + 0.1;
temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
makevectors(self.mangle);
if(!self.t_width)
{
if (self.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);
if (self.spawnflags & SECRET_1ST_DOWN)
self.dest1 = self.origin - v_up * self.t_width;
else
self.dest1 = self.origin + v_right * (self.t_width * temp);
self.dest2 = self.dest1 + v_forward * self.t_length;
SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
sound(self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise2, 1, ATTN_LOOP);
self.effects(+)EF_UPDATESOUND;
}
/* Wait after the first movement... */
void fd_secret_move1()
{
self.nextthink = self.ltime + 1;
self.think = fd_secret_move2;
sound(self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise3, 1, ATTN_LOOP);
self.effects(+)EF_UPDATESOUND;
}
/* Start moving sideways with sound... */
void fd_secret_move2()
{
sound(self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise2, 1, ATTN_LOOP);
self.effects(+)EF_UPDATESOUND;
SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
}
/* Wait here until it's time to go back... */
void fd_secret_move3()
{
sound(self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise3, 1, ATTN_LOOP);
self.effects(+)EF_UPDATESOUND;
if(!self.spawnflags & SECRET_OPEN_ONCE)
{
self.nextthink = self.ltime + self.wait;
self.think = fd_secret_move4;
}
}
/* Move backward... */
void fd_secret_move4()
{
sound(self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise2, 1, ATTN_LOOP);
self.effects(+)EF_UPDATESOUND;
SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
}
/* Wait for one second... */
void fd_secret_move5()
{
self.nextthink = self.ltime + 1;
self.think = fd_secret_move6;
sound(self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise3, 1, ATTN_LOOP);
self.effects(+)EF_UPDATESOUND;
}
void fd_secret_move6()
{
sound(self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise2, 1, ATTN_LOOP);
self.effects(+)EF_UPDATESOUND;
SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
}
void fd_secret_done()
{
if (!self.targetname || self.spawnflags&SECRET_YES_SHOOT)
{
self.health = 10000;
self.takedamage = DAMAGE_YES;
self.th_pain = fd_secret_use;
}
sound(self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise3, 1, ATTN_LOOP);
self.effects(+)EF_UPDATESOUND;
}
void secret_blocked()
{
if (time < self.attack_finished)
return;
self.attack_finished = time + 0.5;
T_Damage (other, self, self, self.dmg);
}
/*
* secret_touch() -- Prints messages.
*/
void secret_touch()
{
string s;
if (other.classname != "player")
return;
if (self.attack_finished > time)
return;
self.attack_finished = time + 2;
if (self.message)
{
s = getstring(self.message);
centerprint (other, s);
sound (other, CHAN_BODY, "misc/comm.wav", 1, ATTN_NORM);
}
}
/*QUAKED func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot x remove_pp no_pp
NOTE: Doors can now be activated and deactivated with the appropriate triggers.
"inactive" set to 1 to start the door deactivated.
Basic secret door. Slides back, then to the side. Angle determines direction.
-----------------------FIELDS-------------------------
wait = # of seconds before coming back
1st_left = 1st move is left of arrow
1st_down = 1st move is down from arrow
always_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 (2 default)
"close_target" secondary target to fire when door closes
If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage.
"soundtype"
0) no sound
1) Big metal door, swinging
2) Big stone door, sliding
3) Big wood door, swinging
4) Normal wood door, swinging
5) Big wood door, sliding
6) Drawbridge
7) Rotating walkway
8) Big metal door, sliding
9) Pendulum swinging
Puzzle Pieces (use the puzzle_id value from the pieces)
puzzle_piece_1
puzzle_piece_2
puzzle_piece_3
puzzle_piece_4
no_puzzle_msg: message when player doesn't have the right pieces
--------------------------------------------------------
*/
void func_door_secret()
{
door_sounds();
if (!self.dmg)
self.dmg = 2;
// Magic formula...
self.mangle = self.angles;
self.angles = '0 0 0';
if(world.spawnflags&MISSIONPACK)
{
if(self.mangle=='0 -1 0')
{
self.mangle='-90 0 0';
}
else if(self.mangle=='0 -2 0')
{
self.mangle='90 0 0';
}
}
self.solid = SOLID_BSP;
self.movetype = MOVETYPE_PUSH;
self.classname = "door";
setmodel (self, self.model);
setorigin (self, self.origin);
self.touch = secret_touch;
self.blocked = secret_blocked;
self.speed = 50;
self.use = fd_secret_use;
if ( !self.targetname || self.spawnflags&SECRET_YES_SHOOT)
{
self.health = 10000;
self.takedamage = DAMAGE_YES;
self.th_pain = fd_secret_use;
self.th_die = fd_secret_use;
}
self.oldorigin = self.origin;
if (!self.wait)
self.wait = 5; // 5 seconds before closing
}
/*QUAKED func_door_rotating (0 .5 .8) ? START_OPEN REVERSE DOOR_DONT_LINK remove_pp no_pp TOGGLE X_AXIS Y_AXIS
NOTE: Doors can now be activated and deactivated with the appropriate triggers.
"inactive" set to 1 to start the door deactivated.
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 usefull for touch or
takedamage doors).
Key doors are allways wait -1.
You need to have an origin brush as part of this entity. The
center of that brush will be
the point around which it is rotated. It will rotate around the Z
axis by default. You can
check either the X_AXIS or Y_AXIS box to change that.
REVERSE will cause the door to rotate in the opposite direction.
"flags" is how many degrees the door will be rotated.
"message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
"targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
"health" if set, door must be shot open
"speed" movement speed (100 default)
"wait" wait before returning (3 default, -1 = never return)
"dmg" damage to inflict when blocked (2 default)
a "dmg" of -1 will make it only hurt non-players
"flags2" will damage the object that touches it
"strength" When set to 1, it will throw something if it gets in the way
"close_target" secondary target to fire when door closes
"soundtype"
0) no sound
1) Big metal door, swinging
2) Big stone door, sliding
3) Big wood door, swinging
4) Normal wood door, swinging
5) Big wood door, sliding
6) Drawbridge
7) Rotating walkway
8) Big metal door, sliding
9) Pendulum swinging
"abslight" - to set the absolute light level
Puzzle Pieces (use the puzzle_id value from the pieces)
puzzle_piece_1
puzzle_piece_2
puzzle_piece_3
puzzle_piece_4
no_puzzle_msg: message when player doesn't have the right pieces
*/
void func_door_rotating()
{
vector vec;
self.dflags=self.flags;//don't ask
self.flags=0;
// set the axis of rotation
if (self.spawnflags & 64)
self.movedir = '0 0 1';
else if (self.spawnflags & 128)
self.movedir = '1 0 0';
else
self.movedir = '0 1 0';
// check for clockwise rotation
if (self.spawnflags & 2)
self.movedir = self.movedir * -1;
// CHEAT hack to get the puzzle piece flags stored in the
// same area, without re-arranging the fields so that the
// designers don't complain
self.spawnflags (-) 192;
if (self.spawnflags & 8)
self.spawnflags (+) DOOR_REMOVE_PP;
if (self.spawnflags & 16)
self.spawnflags (+) DOOR_NO_PP;
if (self.spawnflags & 32)
self.spawnflags (+) DOOR_TOGGLE;
self.pos1 = self.angles;
self.pos2 = self.angles + self.movedir * self.dflags;
self.max_health = self.health;
self.solid = SOLID_BSP;
self.movetype = MOVETYPE_PUSH;
setorigin (self, self.origin);
setmodel (self, self.model);
self.classname = "door_rotating";
if (self.abslight)
self.drawflags (+) MLS_ABSLIGHT;
if (!self.speed)
self.speed = 100;
if (self.wait==0)
self.wait = 3;
if (!self.dmg)
// self.dmg = -1;
self.dmg = 2;
if(self.wait== -2)
{
self.th_die = chunk_death;
// self.takedamage = DAMAGE_YES;
if (!self.health)
{
if ((self.thingtype == THINGTYPE_GLASS) || (self.thingtype == THINGTYPE_CLEARGLASS))
self.max_health = self.health = 25;
else if ((self.thingtype == THINGTYPE_GREYSTONE) || (self.thingtype == THINGTYPE_BROWNSTONE)||self.thingtype == THINGTYPE_DIRT)
self.max_health = self.health = 75;
else if (self.thingtype == THINGTYPE_WOOD)
self.max_health = self.health = 50;
else if (self.thingtype == THINGTYPE_METAL)
self.max_health = self.health = 100;
else if (self.thingtype == THINGTYPE_FLESH)
self.max_health = self.health = 30;
else
self.max_health = self.health = 25;
}
}
door_sounds();
// DOOR_START_OPEN is to allow an entity to be lighted in the closed position
// but spawn in the open position
if (self.spawnflags & DOOR_START_OPEN)
{
self.angles = self.pos2;
vec = self.pos2;
self.pos2 = self.pos1;
self.pos1 = vec;
self.movedir = self.movedir * -1;
}
self.state = STATE_BOTTOM;
self.touch = door_touch;
if(world.spawnflags&MISSIONPACK)
self.blocked = door_blocked_mp;
else
self.blocked = door_blocked;
self.use = door_use;
if (self.puzzle_piece_1 != string_null ||
self.puzzle_piece_2 != string_null ||
self.puzzle_piece_3 != string_null ||
self.puzzle_piece_4 != string_null)
self.wait = -1;
// LinkDoors can't be done until all of the doors have been spawned, so
// the sizes can be detected properly.
self.think = LinkDoors;
self.nextthink = self.ltime + 0.1;
if (self.cnt)
{
self.touch = SUB_Null;
self.use = door_rotate_incr;
}
if (self.flags2)
{
self.touch = door_damage;
self.flags2=FALSE;
}
}