void plat_hit_top()
{
- sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
+ sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
self.state = 1;
self.think = plat_go_down;
self.nextthink = self.ltime + 3;
void plat_hit_bottom()
{
- sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
+ sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
self.state = 2;
};
void plat_go_down()
{
- sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
+ sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
self.state = 3;
SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom);
};
void plat_go_up()
{
- sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
+ sound (self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_NORM);
self.state = 4;
SUB_CalcMove (self.pos1, self.speed, plat_hit_top);
};
plat_go_down ();
else if (self.state == 3)
plat_go_up ();
- else
- objerror ("plat_crush: bad self.state\n");
+ // 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() train_next;
void train_wait()
{
- self.think = train_next;
- self.nextthink = self.ltime + self.wait;
-
if(self.noise != "")
- stopsoundto(MSG_BROADCAST, self, CHAN_TRIGGER); // send this as unreliable only, as the train will resume operation shortly anyway
+ stopsoundto(MSG_BROADCAST, self, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway
+
+ if(self.wait < 0)
+ {
+ train_next();
+ }
+ else
+ {
+ self.think = train_next;
+ self.nextthink = self.ltime + self.wait;
+ }
+
+ entity oldself;
+ oldself = self;
+ self = self.enemy;
+ SUB_UseTargets();
+ self = oldself;
+ self.enemy = world;
};
void train_next()
{
local entity targ;
targ = find(world, targetname, self.target);
+ self.enemy = targ;
self.target = targ.target;
if (!self.target)
objerror("train_next: no next target");
self.wait = targ.wait;
if (!self.wait)
self.wait = 0.1;
- if(self.wait < 0)
- {
- if (targ.speed)
- SUB_CalcMove(targ.origin - self.mins, targ.speed, train_next);
- else
- SUB_CalcMove(targ.origin - self.mins, self.speed, train_next);
- }
+
+ if (targ.speed)
+ SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
else
- {
- if (targ.speed)
- SUB_CalcMove(targ.origin - self.mins, targ.speed, train_wait);
- else
- SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
- }
+ SUB_CalcMove(targ.origin - self.mins, self.speed, train_wait);
if(self.noise != "")
- sound(self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_IDLE);
+ sound(self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
};
void func_train_find()
self.owner.velocity = (v - self.owner.origin) * 10;
};
-void bobbing_blocked()
-{
- // no need to duplicate code
- generic_plat_blocked();
-}
-
/*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
Brush model that moves back and forth on one axis (default Z).
speed : how long one cycle takes in seconds (default 4)
if (self.noise != "")
{
precache_sound(self.noise);
- soundto(MSG_INIT, self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_IDLE);
+ soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
}
if (!self.speed)
self.speed = 4;
self.active = ACTIVE_ACTIVE;
// damage when blocked
- self.blocked = bobbing_blocked;
+ self.blocked = generic_plat_blocked;
if(self.dmg & (!self.message))
self.message = " was squished";
if(self.dmg && (!self.message2))
// TODO make a reset function for this one
};
+.float freq;
+void func_pendulum_controller_think()
+{
+ local float v;
+ self.nextthink = time + 0.1;
+
+ if not (self.owner.active == ACTIVE_ACTIVE)
+ {
+ self.owner.avelocity_x = 0;
+ return;
+ }
+
+ // calculate sinewave using makevectors
+ makevectors((self.nextthink * self.owner.freq + self.owner.phase) * '0 360 0');
+ v = self.owner.speed * v_forward_y + self.cnt;
+ if(self.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed
+ {
+ // * 10 so it will arrive in 0.1 sec
+ self.owner.avelocity_z = (remainder(v - self.owner.angles_z, 360)) * 10;
+ }
+};
+
+void spawnfunc_func_pendulum()
+{
+ local entity controller;
+ if (self.noise != "")
+ {
+ precache_sound(self.noise);
+ soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
+ }
+
+ self.active = ACTIVE_ACTIVE;
+
+ // keys: angle, speed, phase, noise, freq
+
+ if(!self.speed)
+ self.speed = 30;
+ // not initializing self.dmg to 2, to allow damageless pendulum
+
+ if(self.dmg & (!self.message))
+ self.message = " was squished";
+ if(self.dmg && (!self.message2))
+ self.message2 = "was squished by";
+ if(self.dmg && (!self.dmgtime))
+ self.dmgtime = 0.25;
+ self.dmgtime2 = time;
+
+ self.blocked = generic_plat_blocked;
+
+ self.avelocity_z = 0.0000001;
+ if not(InitMovingBrushTrigger())
+ return;
+
+ if(!self.freq)
+ {
+ // find pendulum length (same formula as Q3A)
+ self.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(self.mins_z))));
+ }
+
+ // copy initial angle
+ self.cnt = self.angles_z;
+
+ // wait for targets to spawn
+ controller = spawn();
+ controller.classname = "func_pendulum_controller";
+ controller.owner = self;
+ controller.nextthink = time + 1;
+ controller.think = func_pendulum_controller_think;
+ self.nextthink = self.ltime + 999999999;
+ self.think = SUB_Null;
+
+ //self.effects |= EF_LOWPRECISION;
+
+ // TODO make a reset function for this one
+};
+
// button and multiple button
void() button_wait;
return;
if (self.noise != "")
- sound (self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
+ sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
self.state = STATE_UP;
SUB_CalcMove (self.pos2, self.speed, button_wait);
void door_hit_top()
{
if (self.noise1 != "")
- sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
+ sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
self.state = STATE_TOP;
if (self.spawnflags & DOOR_TOGGLE)
return; // don't come down automatically
void door_hit_bottom()
{
if (self.noise1 != "")
- sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
+ sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
self.state = STATE_BOTTOM;
};
void door_go_down()
{
if (self.noise2 != "")
- sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
+ sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
if (self.max_health)
{
self.takedamage = DAMAGE_YES;
}
if (self.noise2 != "")
- sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
+ sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
self.state = STATE_UP;
SUB_CalcMove (self.pos2, self.speed, door_hit_top);
};
+
/*
=============================================================================
=============================================================================
*/
+float door_check_keys(void) {
+ local entity door, p;
+
+
+ if (self.owner)
+ door = self.owner;
+ else
+ door = self;
+
+ if (other.owner)
+ p = other.owner;
+ else
+ p = other;
+
+ if (door.spawnflags & (SPAWNFLAGS_GOLD_KEY | SPAWNFLAGS_SILVER_KEY)) {
+ // this door require a key
+ // only a player can have a key
+ if (p.classname != "player")
+ return FALSE;
+
+ // check gold key
+ if (self.owner.spawnflags & SPAWNFLAGS_GOLD_KEY) {
+ if (!(other.itemkeys & KEYS_GOLD_KEY)) {
+ if (p.key_door_messagetime <= time) {
+ play2(other, "misc/talk.wav");
+ centerprint(other, "You don't have the gold key!");
+ p.key_door_messagetime = time + 2;
+ }
+ return FALSE;
+ } else {
+ self.owner.spawnflags &~= SPAWNFLAGS_GOLD_KEY;
+ }
+ }
+
+ // check silver key
+ if (self.owner.spawnflags & SPAWNFLAGS_SILVER_KEY) {
+ if (!(other.itemkeys & KEYS_SILVER_KEY)) {
+ if (p.key_door_messagetime <= time) {
+ play2(other, "misc/talk.wav");
+ centerprint(other, "You don't have the silver key!");
+ p.key_door_messagetime = time + 2;
+ }
+ return FALSE;
+ } else {
+ self.owner.spawnflags &~= SPAWNFLAGS_SILVER_KEY;
+ }
+ }
+
+ // door is now unlocked
+ play2(other, "misc/talk.wav");
+ centerprint(other, "Door unlocked!");
+ }
+
+ return TRUE;
+}
+
+
void door_fire()
{
local entity oself;
local entity oself;
//dprint("door_use (model: ");dprint(self.model);dprint(")\n");
+
if (self.owner)
{
oself = self;
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;
if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
return;
self.health = self.health - damage;
+
+ if (self.spawnflags & SPAWNFLAGS_GOLD_KEY || self.spawnflags & SPAWNFLAGS_SILVER_KEY) {
+ // don't allow opening doors through damage if keys are required
+ return;
+ }
+
if (self.health <= 0)
{
oself = self;
void door_rotating_hit_top()
{
if (self.noise1 != "")
- sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
+ sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
self.state = STATE_TOP;
if (self.spawnflags & DOOR_TOGGLE)
return; // don't come down automatically
void door_rotating_hit_bottom()
{
if (self.noise1 != "")
- sound (self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
+ sound (self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
if (self.lip==666) // self.lip is used to remember reverse opening direction for door_rotating
{
self.pos2 = '0 0 0' - self.pos2;
void door_rotating_go_down()
{
if (self.noise2 != "")
- sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
+ sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
if (self.max_health)
{
self.takedamage = DAMAGE_YES;
return;
}
if (self.noise2 != "")
- sound (self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
+ sound (self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
self.state = STATE_UP;
SUB_CalcAngleMove (self.pos2, self.speed, door_rotating_hit_top);
};
-/*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.
void spawnfunc_func_door()
{
+ print("spawnfunc_func_door() spawnflags=", ftos(self.spawnflags));
+ print(", gold_key=", ftos(self.spawnflags & SPAWNFLAGS_GOLD_KEY));
+ print(", silver_key=", ftos(self.spawnflags & SPAWNFLAGS_SILVER_KEY), "\n");
+
//if (!self.deathtype) // map makers can override this
// self.deathtype = " got in the way";
SetMovedir ();
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";
self.angles = '0 0 0';
self.max_health = self.health;
+ self.avelocity = self.movedir;
if not(InitMovingBrushTrigger())
return;
+ self.velocity = '0 0 0';
//self.effects |= EF_LOWPRECISION;
self.classname = "door_rotating";
// Make a sound, wait a little...
if (self.noise1 != "")
- sound(self, CHAN_TRIGGER, self.noise1, VOL_BASE, ATTN_NORM);
+ sound(self, CH_TRIGGER_SINGLE, self.noise1, VOL_BASE, ATTN_NORM);
self.nextthink = self.ltime + 0.1;
temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1
self.dest2 = self.dest1 + v_forward * self.t_length;
SUB_CalcMove(self.dest1, self.speed, fd_secret_move1);
if (self.noise2 != "")
- sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
+ sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
};
// Wait after first movement...
self.nextthink = self.ltime + 1.0;
self.think = fd_secret_move2;
if (self.noise3 != "")
- sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
+ sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
};
// Start moving sideways w/sound...
void fd_secret_move2()
{
if (self.noise2 != "")
- sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
+ sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
SUB_CalcMove(self.dest2, self.speed, fd_secret_move3);
};
void fd_secret_move3()
{
if (self.noise3 != "")
- sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
+ sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
if (!(self.spawnflags & SECRET_OPEN_ONCE))
{
self.nextthink = self.ltime + self.wait;
void fd_secret_move4()
{
if (self.noise2 != "")
- sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
+ sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
SUB_CalcMove(self.dest1, self.speed, fd_secret_move5);
};
self.nextthink = self.ltime + 1.0;
self.think = fd_secret_move6;
if (self.noise3 != "")
- sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
+ sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
};
void fd_secret_move6()
{
if (self.noise2 != "")
- sound(self, CHAN_TRIGGER, self.noise2, VOL_BASE, ATTN_NORM);
+ sound(self, CH_TRIGGER_SINGLE, self.noise2, VOL_BASE, ATTN_NORM);
SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done);
};
//self.th_pain = fd_secret_use;
}
if (self.noise3 != "")
- sound(self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NORM);
+ sound(self, CH_TRIGGER_SINGLE, self.noise3, VOL_BASE, ATTN_NORM);
};
void secret_blocked()
if (self.noise != "")
{
precache_sound(self.noise);
- soundto(MSG_INIT, self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_IDLE);
+ soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
}
if (!self.speed)
if (self.noise != "")
{
precache_sound(self.noise);
- soundto(MSG_INIT, self, CHAN_TRIGGER, self.noise, VOL_BASE, ATTN_IDLE);
+ soundto(MSG_INIT, self, CH_TRIGGER_SINGLE, self.noise, VOL_BASE, ATTN_IDLE);
}
if(!self.targetfactor)