Git Repos / fte_dogmode / qc / func / door.qc
Last update to this file was on 2024-07-17 at 11:21.
Show door.qc
//==============================================================================
// func_door
//==============================================================================
//----------------------------------------------------------------------
// 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 simultaneous
// double/quad doors.
//
// Linked doors are a chain with prev_door and next_door being the previous
// and next links in that chain respectively. The chain is terminated when
// prev/next link connects to __NULL__ or back to self/this.
//----------------------------------------------------------------------
//======================================================================
// constants
//======================================================================
#ifdef SSQC
const float DOOR_START_OPEN = 1; // spawnflags
const float DOOR_DONT_LINK = 4;
const float DOOR_GOLD_KEY = 8;
const float DOOR_SILVER_KEY = 16;
const float DOOR_TOGGLE = 32;
const float DOOR_DOOM_STYLE_UNLOCK = 64;
#endif
//======================================================================
// fields
//======================================================================
#ifdef SSQC
.float last_setstate_frame = light_lev;
.entity last_setstate = aiment;
#endif
//======================================================================
// forward declarations
//======================================================================
#ifdef SSQC
// temp_door_trigger
void() temp_door_trigger_touch;
entity(entity own, vector nmins, vector nmaxs) spawn_temp_door_trigger;
void(entity e) temp_door_trigger_init;
strip void() temp_door_trigger;
#endif
// func_door
#ifdef CSQC
void(float isnew) func_door_netreceive;
#endif
#ifdef SSQC
float(entity e1, entity e2) func_door_entities_touching;
void() func_door_blocked;
void() func_door_hit_top;
void() func_door_hit_bottom;
void() func_door_think_go_down;
void() func_door_think_go_up;
void() func_door_group_go_down;
void() func_door_group_go_up;
void() func_door_fire;
void() func_door_use;
void(vector dir) func_door_destroy;
void() func_door_unlock;
void() func_door_touch;
void(entity e, float closealldoors) func_door_estate_lock;
void(entity e, float openalldoors) func_door_estate_unlock;
void() func_door_think_link;
#endif
#if defined(CSQC) || defined(SSQC)
void(entity e) func_door_init;
#endif
#ifdef SSQC
void() func_door;
#endif
//------------------------------------------------------------------------------
#ifdef SSQC
//----------------------------------------------------------------------
// class temp_door_trigger: base_tempentity
// {
//--------------------------------------------------------------
void() temp_door_trigger_touch =
{
// don't trigger on non-explosive projectiles -- CEV
if (other.classgroup & CG_PROJECTILE)
if (!(other.aflag & PROJECTILE_EXPLOSIVE))
return;
if (other.health <= 0)
return;
if (other.movetype == MOVETYPE_NOCLIP)
// from copper -- dumptruck_ds
return;
if (time < self.attack_finished)
return;
self.attack_finished = time + 1;
activator = other;
if (self.owner)
sub_runvoidas (self.owner, func_door_use);
};
//--------------------------------------------------------------
entity(entity own, vector nmins, vector nmaxs)
spawn_temp_door_trigger =
{
local entity e = spawn ();
e.owner = own;
setsize (e, nmins, nmaxs);
temp_door_trigger_init (e);
return e;
};
//--------------------------------------------------------------
void(entity e) temp_door_trigger_init =
{
e.classtype = CT_TEMP_DOOR_TRIGGER;
e.movetype = MOVETYPE_NONE;
e.solid = SOLID_TRIGGER;
base_tempentity_init (e);
e.touch = temp_door_trigger_touch;
};
//--------------------------------------------------------------
strip void() temp_door_trigger =
{
temp_door_trigger_init (self);
};
// };
#endif
/*QUAKED func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE X X NOT_ON_EASY NOT_ON_NORMAL NOT_ON_HARD_OR_NIGHTMARE NOT_IN_DEATHMATCH NOT_IN_COOP NOT_IN_SINGLEPLAYER X NOT_ON_HARD_ONLY NOT_ON_NIGHTMARE_ONLY
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).
Key doors are always 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.
"health" if set, door must be shot open
"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)
"cnt" if 1, leave a used key in the player's inventory (0 default)
"keyname" if set, this is the keyname of the item_key_custom which unlocks this entity
"noise1" sound file for the "stop moving" sound (if set, overrides "sounds")
"noise2" sound file for the "move" sound (if set, overrides "sounds")
"noise3" sound file for the "key required" sound (default is per worldtype)
"noise4" sound file for the "key used" sound (default is per worldtype)
"sounds"
0) no sound
1) stone
2) base
3) stone chain
4) screechy metal
*/
//----------------------------------------------------------------------
// class func_door: base_func
// {
#ifdef CSQC
//--------------------------------------------------------------
void(float isnew) func_door_netreceive =
{
local float netflags = ReadFloat ();
base_func_netreceive_read (isnew, netflags);
if (isnew)
func_door_init (self);
if (netflags & BASE_FUNC_NET_VELOCITY)
{
if (self.velocity != self.velocity_net)
{
if (self.velocity)
{
self.origin_start = self.origin;
self.movetime = time;
}
else
{
self.movetime = 0;
}
}
}
self.origin_net = self.origin;
self.velocity_net = self.velocity;
if (netflags & BASE_FUNC_NET_MODEL)
setmodelindex (self, self.modelindex);
if (netflags & BASE_FUNC_NET_SIZE)
setsize (self, self.mins, self.maxs);
if (netflags & BASE_FUNC_NET_ORIGIN)
setorigin (self, self.origin);
};
#endif
#ifdef SSQC
//--------------------------------------------------------------
float(entity e1, entity e2) func_door_entities_touching =
{
if (e1.mins_x > e2.maxs_x)
return FALSE;
if (e1.mins_y > e2.maxs_y)
return FALSE;
if (e1.mins_z > e2.maxs_z)
return FALSE;
if (e1.maxs_x < e2.mins_x)
return FALSE;
if (e1.maxs_y < e2.mins_y)
return FALSE;
if (e1.maxs_z < e2.mins_z)
return FALSE;
return TRUE;
};
//--------------------------------------------------------------
// THINK FUNCTIONS
//--------------------------------------------------------------
//--------------------------------------------------------------
void() func_door_blocked =
{
// don't block on projectiles -- CEV
if (other.classgroup & CG_PROJECTILE)
{
if (other.mins != '0 0 0' || other.maxs != '0 0 0')
setsize (other, '0 0 0', '0 0 0');
// continue on the current path -- CEV
if (self.state == FUNC_STATE_DOWN)
func_door_think_go_down ();
else
func_door_think_go_up ();
return;
}
if (other.classgroup & CG_CORPSE &&
other.takedamage >= DAMAGE_YES)
{
t_damage2 (other, self, self, self.dmg * 10);
}
else
{
t_damage2 (other, self, self, self.dmg);
}
// 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 (self.wait >= 0)
{
if (self.state == FUNC_STATE_DOWN)
func_door_think_go_up ();
else
func_door_think_go_down ();
}
};
//--------------------------------------------------------------
void() func_door_hit_top =
{
sound (self, CHAN_VOICE, self.noise1, VOL_MHI, ATTN_NORM);
self.state = FUNC_STATE_TOP;
// automatically return if DOOR_TOGGLE spawnflag not set
if (!(self.spawnflags & DOOR_TOGGLE))
{
self.think = func_door_think_go_down;
self.nextthink = self.ltime + self.wait;
}
self.SendFlags |= BASE_FUNC_NET_SPEED |
BASE_FUNC_NET_ORIGIN | BASE_FUNC_NET_VELOCITY;
};
//--------------------------------------------------------------
void() func_door_hit_bottom =
{
sound (self, CHAN_VOICE, self.noise1, VOL_MHI, ATTN_NORM);
self.state = FUNC_STATE_BOTTOM;
self.SendFlags |= BASE_FUNC_NET_SPEED |
BASE_FUNC_NET_ORIGIN | BASE_FUNC_NET_VELOCITY;
};
//--------------------------------------------------------------
void() func_door_think_go_down =
{
sound (self, CHAN_VOICE, self.noise2, VOL_MHI, ATTN_NORM);
if (self.max_health)
{
self.takedamage = DAMAGE_YES;
self.health = self.max_health;
}
self.state = FUNC_STATE_DOWN;
sub_calcmove (self, self.pos1, self.speed,
func_door_hit_bottom);
self.SendFlags |= BASE_FUNC_NET_SPEED |
BASE_FUNC_NET_ORIGIN | BASE_FUNC_NET_VELOCITY;
if (self.switchshadstyle && self.shadowcontroller)
{
if (self.spawnflags & DOOR_START_OPEN)
{
sub_runvoidas (self.shadowcontroller,
misc_shadowcontroller_fade_out);
self.shadowcontroller.shadowoff = 1;
}
else
{
sub_runvoidas (self.shadowcontroller,
misc_shadowcontroller_fade_in);
self.shadowcontroller.shadowoff = 0;
}
}
};
//--------------------------------------------------------------
void() func_door_think_go_up =
{
if (self.state == FUNC_STATE_UP)
// allready going up
return;
if (self.state == FUNC_STATE_TOP)
{
// reset top wait time
self.nextthink = self.ltime + self.wait;
return;
}
sound (self, CHAN_VOICE, self.noise2, VOL_MHI, ATTN_NORM);
self.state = FUNC_STATE_UP;
sub_calcmove (self, self.pos2, self.speed, func_door_hit_top);
self.SendFlags |= BASE_FUNC_NET_SPEED |
BASE_FUNC_NET_ORIGIN | BASE_FUNC_NET_VELOCITY;
sub_usetargets ();
if (self.switchshadstyle && self.shadowcontroller)
{
if (self.spawnflags & DOOR_START_OPEN)
{
sub_runvoidas (self.shadowcontroller,
misc_shadowcontroller_fade_in);
self.shadowcontroller.shadowoff = 0;
}
else
{
sub_runvoidas (self.shadowcontroller,
misc_shadowcontroller_fade_out);
self.shadowcontroller.shadowoff = 1;
}
}
};
//--------------------------------------------------------------
void() func_door_group_go_down =
{
local entity oself, starte;
oself = starte = self;
do
{
func_door_think_go_down ();
self = self.enemy;
}
while ((self != starte) && (self != world));
self = oself;
};
//--------------------------------------------------------------
void() func_door_group_go_up =
{
local entity oself, starte;
oself = starte = self;
do
{
func_door_think_go_up ();
self = self.enemy;
}
while ((self != starte) && (self != world));
self = oself;
};
//--------------------------------------------------------------
// ACTIVATION FUNCTIONS
//--------------------------------------------------------------
//--------------------------------------------------------------
void() func_door_fire =
{
if (self.owner != self)
objerror ("func_door_fire: self.owner != self\n");
if (self.estate != STATE_ACTIVE)
return;
// noise4 is now played in keylock_try_to_unlock -- iw
// no more message
self.message = "";
if (self.spawnflags & DOOR_TOGGLE)
{
if (self.state == FUNC_STATE_UP ||
self.state == FUNC_STATE_TOP)
{
func_door_group_go_down ();
return;
}
}
// trigger all paired doors
func_door_group_go_up ();
};
//--------------------------------------------------------------
void() func_door_use =
{
local entity oself = self;
// door messages are for touch only
self.message = "";
if (self.enemy)
self.enemy.message = "";
self.owner.message = "";
self = self.owner;
func_door_fire ();
self = oself;
};
//--------------------------------------------------------------
void(vector dir) func_door_destroy =
{
local entity oself = self;
self = self.owner;
self.health = self.max_health;
// will be reset upon return
self.takedamage = DAMAGE_NO;
func_door_use ();
self = oself;
};
//--------------------------------------------------------------
// door_unlock
//
// Perform the actions which should be performed when this is
// successfully unlocked with a key.
//
// This function exists so that it can be passed as an argument
// to the new keylock_try_to_unlock function. This code was
// previously part of the door_touch function. -- iw
//--------------------------------------------------------------
void() func_door_unlock =
{
if (!(self.spawnflags & DOOR_DOOM_STYLE_UNLOCK))
{
// door has been unlocked, don't allow player to
// touch / activate it again
local entity n = self;
do
{
n.touch = sub_null;
n = n.enemy;
}
while (n && n != self && n != world);
}
func_door_use ();
};
//--------------------------------------------------------------
// door_touch -- Prints messages and opens key doors
//--------------------------------------------------------------
void() func_door_touch =
{
// from Copper -- dumptruck_ds
if (sub_checkvalidtouch(other) == FALSE)
return;
if (self.owner.attack_finished > time)
return;
self.owner.attack_finished = time + 2;
if (self.owner.message != __NULL__ && self.owner.message != "")
{
centerprint (other, self.owner.message);
sound (other, CHAN_VOICE, "misc/talk.wav",
VOL_HIGH, ATTN_NORM);
}
// key door stuff
if (!keylock_has_key_set(self))
return;
keylock_try_to_unlock (other, "", func_door_unlock);
};
//--------------------------------------------------------------
// ENTITY STATE FUNCTIONS
//--------------------------------------------------------------
//--------------------------------------------------------------
void(entity e, float closealldoors) func_door_estate_lock =
{
if (e.owner.estate == STATE_INACTIVE)
return;
// blocks linked doors from updating the same owner repeatedly
if (e.owner.last_setstate == self &&
e.owner.last_setstate_frame == framecount)
{
return;
}
local entity oself, next;
oself = self;
self = e.owner;
self.estate = STATE_INACTIVE;
self.last_setstate = oself;
self.last_setstate_frame = framecount;
self.prevstate = self.estate;
if (self.state == FUNC_STATE_UP || self.state == FUNC_STATE_TOP)
{
// don't close wait -1 nor toggleable doors...
// ...unless the trigger_setstate has a "Close all
// doors" spawnflag on
if (!(self.wait == -1 ||
(self.spawnflags & DOOR_TOGGLE)) ||
closealldoors)
{
func_door_group_go_down ();
}
}
if (self.max_health)
{
next = self;
do
{
next.takedamage = DAMAGE_NO;
next = next.enemy;
}
while ((next != self) && (next != world));
}
self = oself;
};
//--------------------------------------------------------------
void(entity e, float openalldoors) func_door_estate_unlock =
{
if (e.owner.estate == STATE_ACTIVE)
return;
// blocks linked doors from updating the same owner repeatedly
if (e.owner.last_setstate == self &&
e.owner.last_setstate_frame == framecount)
{
return;
}
local entity oself, next;
oself = self;
self = e.owner;
self.last_setstate = oself;
self.last_setstate_frame = framecount;
if (self.prevstate == FUNC_STATE_UP ||
self.prevstate == FUNC_STATE_TOP)
{
if ((self.wait == -1 ||
(self.spawnflags & DOOR_TOGGLE)) &&
openalldoors)
{
func_door_group_go_up ();
}
}
if (self.max_health)
{
next = self;
do
{
next.health = next.max_health;
next.takedamage = DAMAGE_YES;
next = next.enemy;
}
while ((next != self) && (next != world));
}
self.estate = STATE_ACTIVE;
self = oself;
};
//--------------------------------------------------------------
// SPAWNING FUNCTIONS
//--------------------------------------------------------------
//--------------------------------------------------------------
// LinkDoors
//--------------------------------------------------------------
void() func_door_think_link =
{
// current, next, previous
local entity t, starte;
local vector cmins, cmaxs;
if (self.enemy)
// already linked by another door
return;
if (self.spawnflags & DOOR_DONT_LINK)
{
// don't want to link this door
self.owner = self.enemy = self;
return;
}
cmins = self.mins;
cmaxs = self.maxs;
starte = t = self;
while (1)
{
// master door
self.owner = starte;
if (self.health)
starte.health = self.health;
if (self.targetname != "")
starte.targetname = self.targetname;
if (self.message != "")
starte.message = self.message;
t = findfloat (t, classtype, CT_FUNC_DOOR);
if (!t)
{
// make the chain a loop
self.enemy = starte;
// shootable, fired, or key doors just needed
// the owner/enemy links, they don't spawn a
// field
self = self.owner;
if (self.health)
return;
if (self.targetname != "")
return;
if (keylock_has_key_set(self))
return;
// create area trigger
self.owner.trigger_field =
spawn_temp_door_trigger (self,
cmins - '60 60 8',
cmaxs + '60 60 8');
return;
}
if (func_door_entities_touching(self, t))
{
if (t.enemy)
objerror ("func_door_link: "
"cross-connected doors!\n");
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;
}
}
};
#endif
#if defined(CSQC) || defined(SSQC)
//--------------------------------------------------------------
void(entity e) func_door_init =
{
e.classname = "func_door";
e.classtype = CT_FUNC_DOOR;
base_func_init (e);
#ifdef SSQC
local string default_noise1;
local string default_noise2;
// this.noise3 and this.noise4 can now be overridden by
// the mapper, but will be set to default values in
// keylock_init if necessary -- iw
keylock_init (e);
// e.noise1 and e.noise2 can now be overridden by
// the mapper -- iw
default_noise1 = "misc/null.wav";
default_noise2 = "misc/null.wav";
if (e.sounds == 1)
{
default_noise1 = "doors/drclos4.wav";
default_noise2 = "doors/doormv1.wav";
}
else if (e.sounds == 2)
{
default_noise1 = "doors/hydro2.wav";
default_noise2 = "doors/hydro1.wav";
}
else if (e.sounds == 3)
{
default_noise1 = "doors/stndr2.wav";
default_noise2 = "doors/stndr1.wav";
}
else if (e.sounds == 4)
{
default_noise1 = "doors/ddoor2.wav";
default_noise2 = "doors/ddoor1.wav";
}
if (e.noise1 == __NULL__ || e.noise1 == "")
e.noise1 = default_noise1;
if (e.noise1 == __NULL__ || e.noise2 == "")
e.noise2 = default_noise2;
precache_sound (e.noise1);
precache_sound (e.noise2);
sub_setmovedir (e);
e.max_health = e.health;
#endif
e.solid = SOLID_BSP;
e.movetype = MOVETYPE_PUSH;
setorigin (e, e.origin);
#if defined(SSQC)
setmodel (e, e.model);
#elif defined(CSQC)
setmodelindex (e, e.modelindex);
#endif
#ifdef SSQC
e.blocked = func_door_blocked;
e.use = func_door_use;
if (e.spawnflags & DOOR_SILVER_KEY)
{
keylock_set_silver_key (e);
if (e.keyname != "")
{
e.netname = e.keyname;
e.keyname = "";
}
}
if (e.spawnflags & DOOR_GOLD_KEY)
{
keylock_set_gold_key (e);
if (e.keyname != "")
{
e.netname = e.keyname;
e.keyname = "";
}
}
// support for item_key_custom -- iw
if (e.keyname != "")
{
keylock_set_custom_key (e, e.keyname);
// e should not be referenced again
e.keyname = "";
}
if (!e.speed)
e.speed = 100;
if (!e.wait)
e.wait = 3;
if (!e.lip)
e.lip = 8;
if (!e.dmg)
e.dmg = 2;
e.pos1 = e.origin;
e.pos2 = e.pos1 + e.movedir *
(fabs(e.movedir * e.size) - e.lip);
// DOOR_START_OPEN is to allow an entity to be lighted in
// the closed position but spawn in the open position
if (e.spawnflags & DOOR_START_OPEN)
{
setorigin (e, e.pos2);
e.pos2 = e.pos1;
e.pos1 = e.origin;
}
e.state = FUNC_STATE_BOTTOM;
if (e.health)
{
e.takedamage = DAMAGE_YES;
e.destroy = func_door_destroy;
}
if (e.spawnflags & DOOR_DOOM_STYLE_UNLOCK)
e.cnt = 1;
if (keylock_has_key_set(e))
{
if (!(e.spawnflags & DOOR_DOOM_STYLE_UNLOCK))
{
e.wait = -1;
}
}
e.touch = func_door_touch;
// LinkDoors can't be done until all of the doors have
// been spawned, so the sizes can be detected properly.
e.think = func_door_think_link;
e.nextthink = e.ltime + 0.1;
// creates a shadow controller entity for the door if it
// has switchable shadows
if (e.switchshadstyle)
{
// forgive me for using the ternary operator -- CEV
e.shadowcontroller = spawn_misc_shadowcontroller (
e, e.switchshadstyle,
vlen (e.pos2 - e.pos1) / e.speed,
e.spawnflags & DOOR_START_OPEN ? 1 : 0);
}
// network func_door to the CSQC client -- CEV
// e.SendEntity = base_func_netsend;
e.SendFlags = BASE_FUNC_NET_ORIGIN | BASE_FUNC_NET_SIZE |
BASE_FUNC_NET_MODEL | BASE_FUNC_NET_SPEED;
#endif
#ifdef CSQC
e.drawmask = DRAWMASK_NORMAL;
e.predraw = base_func_predraw_solidpush;
#endif
};
#endif
#ifdef SSQC
//--------------------------------------------------------------
void() func_door =
{
// new spawnflags for all entities -- iw
if (SUB_Inhibit())
return;
func_door_init (self);
};
#endif
// };
Return to the top of this page or return to the overview of this repo.
Log door.qc
Date | Commit Message | Author | + | - |
---|---|---|---|---|
2024-07-17 | pmove changes, smooth crouching | cev | +3 | -3 |
2024-06-26 | pmove fixes, GL now a faux tribolt, wall climbing | cev | +1 | -1 |
2024-06-15 | Major update, committing as-is, will have bugs | cev | +114 | -11 |
2024-04-12 | Moveable gibs, heads, some bugfixes | cev | +2 | -2 |
2024-04-08 | Registered monsters, projectile bugfixes | cev | +3 | -2 |
2024-03-24 | Fix projectile and func_ blocked interaction | cev | +11 | -2 |
2024-03-24 | 2nd pass refactor, rework QC class structure | cev | +389 | -386 |
2024-02-18 | Client/player, projectiles, entrypoints refactor | cev | +4 | -4 |
2024-01-31 | Class based monster refactor & start projectiles | cev | +27 | -9 |
2024-01-13 | Refactored items into classes, fix teleporttrain | cev | +1 | -1 |
2024-01-09 | Continue OO / Class-based refactor | cev | +627 | -592 |
2023-11-27 | Code reorg, minor movement changes, misc | cev | +737 |
Return to the top of this page or return to the overview of this repo.