902 lines
31 KiB
Plaintext
902 lines
31 KiB
Plaintext
/*======================================================================
|
|
SIMPLE BMODELS
|
|
======================================================================*/
|
|
|
|
float FUNC_AFRAME = 2; // Start with A frame (animated textures)
|
|
float FUNC_MODCHECK = 16; // Will remove this entity if THIS mod is active
|
|
|
|
float FUNC_LASERSOLID = 2; // Func bmodel blocks on all sides
|
|
float FUNC_LASERNODMG = 4; // Func bmodel does no touch damage
|
|
|
|
float FUNC_SKILLSTARTOPEN = 1; // Reverse setup for lighting issues
|
|
|
|
float BOB_COLLISION = 2; // Collision for misc_bob
|
|
float BOB_NONSOLID = 4; // Non solid for func_bob
|
|
|
|
/*======================================================================
|
|
/*QUAKED func_wall (0 .5 .8) ? x AFRAME x x MODCHECK STATIC STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
|
|
A SOLID bmodel with toggled animated texture
|
|
-------- KEYS --------
|
|
targetname : trigger entity (works with entity state system)
|
|
_dirt : -1 = will be excluded from dirtmapping
|
|
_minlight : Minimum light level for any surface of the brush model
|
|
_mincolor : Minimum light color for any surface (def='1 1 1' RGB)
|
|
_shadow : Will cast shadows on other models and itself
|
|
_shadowself : Will cast shadows on itself
|
|
-------- SPAWNFLAGS --------
|
|
AFRAME : Start with the A frame animated texture
|
|
MODCHECK : Will remove this entity if THIS mod is active
|
|
STATIC : Turn entity into static upon spawn (frame 0)
|
|
STARTOFF : Starts off and waits for trigger
|
|
-------- NOTES --------
|
|
A SOLID bmodel with toggled animated texture
|
|
|
|
======================================================================*/
|
|
void() func_wall_use =
|
|
{
|
|
// Deal with STARTOFF functionality first
|
|
if (self.spawnflags & ENT_STARTOFF) self.estate_on();
|
|
else {
|
|
// Block USE functionality if state wrong
|
|
if (self.estate & ESTATE_BLOCK) return;
|
|
|
|
// toggle alternate textures
|
|
else self.frame = 1 - self.frame;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_wall_on =
|
|
{
|
|
// No longer need this spawnflag, remove it
|
|
self.spawnflags = self.spawnflags - (self.spawnflags & ENT_STARTOFF);
|
|
|
|
self.estate = ESTATE_ON;
|
|
self.movetype = MOVETYPE_PUSH;
|
|
self.solid = SOLID_BSP;
|
|
setmodel (self, self.mdl);
|
|
|
|
// Check for spawning conditions (nightmare, coop)
|
|
// Needs to exist after entity has been added to work for BSPorigin
|
|
if (check_nightmare() == TRUE) return;
|
|
if (check_coop() == TRUE) return;
|
|
|
|
if (self.switchshadstyle != 0) lightstyle(self.switchshadstyle, "a");
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_wall_off =
|
|
{
|
|
self.estate = ESTATE_OFF;
|
|
self.movetype = MOVETYPE_NONE;
|
|
self.solid = SOLID_NOT;
|
|
setmodel (self, "");
|
|
|
|
if (self.switchshadstyle != 0) lightstyle(self.switchshadstyle, "m");
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_wall_aframe = { };
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_wall =
|
|
{
|
|
if (check_bmodel_keys()) return; // Check for bmodel errors
|
|
|
|
self.classtype = CT_FUNCWALL;
|
|
self.bsporigin = TRUE;
|
|
self.angles = '0 0 0';
|
|
self.mdl = self.model;
|
|
if (self.spawnflags & FUNC_AFRAME) self.frame = 1;
|
|
if (self.spawnflags & FUNC_MODCHECK) remove(self);
|
|
|
|
// Check for static entity option first
|
|
if (self.spawnflags & ENT_SPNSTATIC) {
|
|
self.movetype = MOVETYPE_PUSH;
|
|
self.solid = SOLID_BSP;
|
|
setmodel (self, self.mdl);
|
|
makestatic(self);
|
|
}
|
|
else {
|
|
// Setup Entity State functionality
|
|
if (self.targetname != "") self.use = entity_state_use;
|
|
self.estate_on = func_wall_on;
|
|
self.estate_off = func_wall_off;
|
|
self.estate_use = func_wall_use;
|
|
self.estate_aframe = func_wall_aframe;
|
|
if (self.spawnflags & ENT_STARTOFF) self.estate_off();
|
|
else self.estate_on();
|
|
}
|
|
};
|
|
|
|
/*======================================================================
|
|
/*QUAKED func_illusionary (0 .5 .8) ? x AFRAME x x x STATIC STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
|
|
A NON SOLID bmodel with texture toggle
|
|
-------- KEYS --------
|
|
targetname : trigger entity (works with entity state system)
|
|
_dirt : -1 = will be excluded from dirtmapping
|
|
_minlight : Minimum light level for any surface of the brush model
|
|
_mincolor : Minimum light color for any surface (def='1 1 1' RGB)
|
|
_shadow : Will cast shadows on other models and itself
|
|
_shadowself : Will cast shadows on itself
|
|
-------- SPAWNFLAGS --------
|
|
AFRAME : Start with the A frame animated texture
|
|
STATIC : Turn entity into static upon spawn (frame 0)
|
|
STARTOFF : Requires trigger to activate
|
|
-------- NOTES --------
|
|
A NON SOLID bmodel with texture toggle
|
|
|
|
======================================================================*/
|
|
void() func_illusionary_on =
|
|
{
|
|
// No longer need this spawnflag, remove it
|
|
self.spawnflags = self.spawnflags - (self.spawnflags & ENT_STARTOFF);
|
|
|
|
self.estate = ESTATE_ON;
|
|
self.movetype = MOVETYPE_NONE;
|
|
self.solid = SOLID_NOT;
|
|
setmodel (self, self.mdl);
|
|
|
|
// Check for spawning conditions (nightmare, coop)
|
|
// Needs to exist after entity has been added to work for BSPorigin
|
|
if (check_nightmare() == TRUE) return;
|
|
if (check_coop() == TRUE) return;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_illusionary_aframe = { };
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_illusionary =
|
|
{
|
|
if (check_bmodel_keys()) return; // Check for bmodel errors
|
|
|
|
self.classtype = CT_FUNCILLUSIONARY;
|
|
self.bsporigin = TRUE;
|
|
self.angles = '0 0 0';
|
|
self.mdl = self.model;
|
|
if (self.spawnflags & FUNC_AFRAME) self.frame = 1;
|
|
|
|
// Check for static entity option first
|
|
if (self.spawnflags & ENT_SPNSTATIC) {
|
|
self.movetype = MOVETYPE_NONE;
|
|
self.solid = SOLID_NOT;
|
|
setmodel (self, self.mdl);
|
|
makestatic(self);
|
|
}
|
|
else {
|
|
// Setup Entity State functionality
|
|
if (self.targetname != "") self.use = entity_state_use;
|
|
self.estate_on = func_illusionary_on;
|
|
self.estate_off = func_wall_off;
|
|
self.estate_use = func_wall_use;
|
|
self.estate_aframe = func_illusionary_aframe;
|
|
if (self.spawnflags & ENT_STARTOFF) self.estate_off();
|
|
else self.estate_on();
|
|
}
|
|
};
|
|
|
|
/*======================================================================
|
|
/*QUAKED func_episodegate (0 .5 .8) ? E1 E2 E3 E4 x x x x Not_Easy Not_Normal Not_Hard Not_DM
|
|
SOLID bmodel when player has SELECTED rune(s)
|
|
-------- KEYS --------
|
|
targetname : trigger entity (works with entity state system)
|
|
_dirt : -1 = will be excluded from dirtmapping
|
|
_minlight : Minimum light level for any surface of the brush model
|
|
_mincolor : Minimum light color for any surface (def='1 1 1' RGB)
|
|
_shadow : Will cast shadows on other models and itself
|
|
_shadowself : Will cast shadows on itself
|
|
-------- SPAWNFLAGS --------
|
|
E1 : Episode 1
|
|
E2 : Episode 2
|
|
E3 : Episode 3
|
|
E4 : Episode 4
|
|
-------- NOTES --------
|
|
SOLID bmodel when player has SELECTED rune(s)
|
|
|
|
======================================================================*/
|
|
void() func_episodegate_use =
|
|
{
|
|
// The RUNE condition has to be active
|
|
if (query_configflag(self.customkey) == self.customkey) self.estate_on();
|
|
else self.estate_off();
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_episodegate_aframe = { };
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_episodegate =
|
|
{
|
|
if (check_bmodel_keys()) return; // Check for bmodel errors
|
|
|
|
self.classtype = CT_FUNCEPISODEGATE;
|
|
self.bsporigin = TRUE;
|
|
self.angles = '0 0 0';
|
|
self.mdl = self.model;
|
|
|
|
// Check for spawning conditions (nightmare, coop)
|
|
// Needs to exist after entity has been added to work for BSPorigin
|
|
if (check_nightmare() == TRUE) return;
|
|
if (check_coop() == TRUE) return;
|
|
|
|
// Remove any extra spawnkey stuff like skill restrictions
|
|
self.customkey = self.spawnflags & SVR_RUNE_ALL;
|
|
|
|
// Setup Entity State functionality
|
|
if (self.targetname != "") self.use = entity_state_use;
|
|
self.estate_on = func_wall_on;
|
|
self.estate_off = func_wall_off;
|
|
self.estate_use = func_episodegate_use;
|
|
self.estate_aframe = func_episodegate_aframe;
|
|
if (self.spawnflags & ENT_STARTOFF) self.estate_off();
|
|
else {
|
|
// Wait 1 frame before checking for rune keys
|
|
self.think = func_episodegate_use;
|
|
self.nextthink = time + 0.1;
|
|
}
|
|
};
|
|
|
|
/*======================================================================
|
|
/*QUAKED func_bossgate (0 .5 .8) ? x x x x x x x x Not_Easy Not_Normal Not_Hard Not_DM
|
|
A NON SOLID bmodel when player has ALL runes
|
|
-------- KEYS --------
|
|
targetname : trigger entity (works with entity state system)
|
|
_dirt : -1 = will be excluded from dirtmapping
|
|
_minlight : Minimum light level for any surface of the brush model
|
|
_mincolor : Minimum light color for any surface (def='1 1 1' RGB)
|
|
_shadow : Will cast shadows on other models and itself
|
|
_shadowself : Will cast shadows on itself
|
|
-------- SPAWNFLAGS --------
|
|
-------- NOTES --------
|
|
A NON SOLID bmodel when player has ALL runes
|
|
|
|
======================================================================*/
|
|
void() func_bossgate_use =
|
|
{
|
|
// The RUNE condition has to be active
|
|
if (query_configflag(SVR_RUNE_ALL) == SVR_RUNE_ALL) self.estate_off();
|
|
else self.estate_on();
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_bossgate_aframe = { };
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_bossgate =
|
|
{
|
|
if (check_bmodel_keys()) return; // Check for bmodel errors
|
|
|
|
self.classtype = CT_FUNCBOSSGATE;
|
|
self.bsporigin = TRUE;
|
|
self.angles = '0 0 0';
|
|
self.mdl = self.model;
|
|
|
|
// Check for spawning conditions (nightmare, coop)
|
|
// Needs to exist after entity has been added to work for BSPorigin
|
|
if (check_nightmare() == TRUE) return;
|
|
if (check_coop() == TRUE) return;
|
|
|
|
// Setup Entity State functionality
|
|
if (self.targetname != "") self.use = entity_state_use;
|
|
self.estate_on = func_wall_on;
|
|
self.estate_off = func_wall_off;
|
|
self.estate_use = func_bossgate_use;
|
|
self.estate_aframe = func_bossgate_aframe;
|
|
if (self.spawnflags & ENT_STARTOFF) self.estate_off();
|
|
else {
|
|
// Wait 1 frame before checking for rune keys
|
|
self.think = func_bossgate_use;
|
|
self.nextthink = time + 0.1;
|
|
}
|
|
};
|
|
|
|
/*======================================================================
|
|
BModel laser (originally from Rubicon2 codebase by JohnFitz)
|
|
- Extended code to have different states and toggle function
|
|
- Added custom/silent sounds for lasers on/off
|
|
- Added on/off message once function
|
|
- Added collision and nodmg spawnflags
|
|
|
|
/*======================================================================
|
|
/*QUAKED func_laser (0 .5 .8) ? x SOLID NODMG x x x STARTOFF x
|
|
A togglable laser, hurts to touch, can be used to block players
|
|
-------- KEYS --------
|
|
targetname : trigger entity (works with entity state system)
|
|
message : message to display when switched ON
|
|
message2 : message to display when switched OFF
|
|
wait : -1 = will display the switching state messages only once
|
|
dmg : damage to do on touch. default 1 per 0.1s
|
|
alpha : alpha value will vary +/- 20% (def=0.5)
|
|
sounds : 1=laser, 2=power, 4=silent, 5=custom
|
|
noise1 : switched ON sound
|
|
noise2 : switched OFF sound
|
|
spr_frame : 1=Yellow, 2=Green, 4=Red, 8=Blue, 16=Purple, 32=Fire, 64=White
|
|
angle : Direction of particles to move (def=-2 down)
|
|
part_limit : Maximum active quantity of particles (def=25)
|
|
part_life : Lifetime of particle (def=2s)
|
|
part_velrand: Random movement of particles (def='4 4 4')
|
|
part_vol : Size of area to spawn particles in (def=bmodel)
|
|
wakeup_dist : Distance to wakeup particle emitter (def=768)
|
|
spawn_base : Minimum time frame to spawn particles (def=0.1s)
|
|
spawn_rand : Random time amount to add to spawning times (def=0.1s)
|
|
_dirt : -1 = will be excluded from dirtmapping
|
|
_minlight : Minimum light level for any surface of the brush model
|
|
_mincolor : Minimum light color for any surface (def='1 1 1' RGB)
|
|
_shadow : Will cast shadows on other models and itself
|
|
_shadowself : Will cast shadows on itself
|
|
-------- SPAWNFLAGS --------
|
|
SOLID : Will block anything
|
|
NODMG : Touch damage disabled
|
|
STARTOFF : Requires trigger to activate
|
|
-------- NOTES --------
|
|
A togglable laser, hurts to touch, can be used to block players
|
|
|
|
======================================================================*/
|
|
void() func_laser_touch =
|
|
{
|
|
if (self.estate & ESTATE_BLOCK) return;
|
|
if (self.spawnflags & FUNC_LASERNODMG) return;
|
|
if (other.takedamage == DAMAGE_NO) return;
|
|
if (self.attack_finished < time) {
|
|
T_Damage (other, self, self, self.dmg, DAMARMOR);
|
|
self.attack_finished = time + 0.1;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_laser_think =
|
|
{
|
|
if (self.estate == ESTATE_BLOCK) return;
|
|
// Change the density of the alpha to make laser beam flicker
|
|
self.alpha = self.height*0.8 + (self.alpha * (random()*0.4));
|
|
self.nextthink = time + 0.05;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_laser_on =
|
|
{
|
|
// No longer need this spawnflag, remove it
|
|
self.spawnflags = self.spawnflags - (self.spawnflags & ENT_STARTOFF);
|
|
|
|
// Switch laser to active
|
|
self.estate = ESTATE_ON;
|
|
|
|
// Only play the ON sound and display message when not spawning
|
|
if (self.waitmin == TRUE) {
|
|
// Play sound on extra emitter if sounds been defined
|
|
if (self.sound_emitter && self.noise1 != "")
|
|
sound (self.sound_emitter, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
|
|
if (activator.flags & FL_CLIENT && self.message != "")
|
|
centerprint(activator,self.message);
|
|
if (self.wait == TRUE) self.message = "";
|
|
}
|
|
else self.waitmin = TRUE;
|
|
|
|
// Got to set solid/movetype before model
|
|
// Otherwise trigger state does not register properly
|
|
if (self.spawnflags & FUNC_LASERSOLID) {
|
|
self.solid = SOLID_BSP;
|
|
self.movetype = MOVETYPE_PUSH;
|
|
}
|
|
else {
|
|
self.solid = SOLID_TRIGGER;
|
|
self.movetype = MOVETYPE_NONE;
|
|
}
|
|
|
|
// Add bmodel back to world (visually)
|
|
setmodel (self, self.mdl);
|
|
|
|
// Check for spawning conditions (nightmare, coop)
|
|
// Needs to exist after entity has been added to work for BSPorigin
|
|
if (check_nightmare() == TRUE) return;
|
|
if (check_coop() == TRUE) return;
|
|
|
|
// Switch ON any particle emitter
|
|
if (self.part_emitter) misc_particle_on(self.part_emitter);
|
|
|
|
// Reset touch and laser blink effect
|
|
self.touch = func_laser_touch;
|
|
self.think = func_laser_think;
|
|
self.nextthink = time + 0.1;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_laser_off =
|
|
{
|
|
// Switch laser to active
|
|
self.estate = ESTATE_OFF;
|
|
|
|
// Only play the OFF sound and display message when not spawning
|
|
if (self.waitmin == TRUE) {
|
|
// Play sound on extra emitter if sounds been defined
|
|
if (self.sound_emitter && self.noise2 != "")
|
|
sound (self.sound_emitter, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
|
|
if (activator.flags & FL_CLIENT && self.message2 != "")
|
|
centerprint(activator,self.message2);
|
|
if (self.wait == TRUE) self.message2 = "";
|
|
}
|
|
else self.waitmin = TRUE;
|
|
|
|
// Switch OFF any particle emitter
|
|
if (self.part_emitter) misc_particle_off(self.part_emitter);
|
|
|
|
self.movetype = MOVETYPE_NONE;
|
|
self.solid = SOLID_NOT;
|
|
setmodel(self, "");
|
|
};
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_laser =
|
|
{
|
|
if (check_bmodel_keys()) return; // Check for bmodel errors
|
|
|
|
self.classtype = CT_FUNCLASER; // Classtype
|
|
self.mdl = self.model; // Save for later (on function)
|
|
if (self.spr_frame > 0 && CheckZeroVector(self.angles)) self.angles = '0 -2 0';
|
|
InitTrigger(); // Add to work and initialize
|
|
|
|
if (!self.alpha) self.alpha = 0.5; // Lasers always transparent
|
|
self.height = self.alpha; // Used for think function
|
|
if (!self.dmg) self.dmg = 1; // Default tickle
|
|
if (self.wait < 0) self.wait = TRUE;// Display message once?
|
|
else self.wait = FALSE;
|
|
self.waitmin = FALSE; // Spawning no message check
|
|
|
|
// Pre-cache sounds and setup sound emitter
|
|
if (self.sounds > 0 && self.sounds < 3) {
|
|
if (self.sounds == 1) {
|
|
self.noise1 = "misc/laseron.wav";
|
|
self.noise2 = "misc/laseroff.wav";
|
|
}
|
|
else if (self.sounds == 2) {
|
|
self.noise1 = "ambience/power_on.wav";
|
|
self.noise2 = "ambience/power_off.wav";
|
|
}
|
|
precache_sound(self.noise1);
|
|
precache_sound(self.noise2);
|
|
|
|
// Setup sound emitter where bmodel is correctly located
|
|
self.sound_emitter = spawn();
|
|
self.sound_emitter.origin = bmodel_origin(self);
|
|
setorigin(self.sound_emitter, self.sound_emitter.origin);
|
|
self.classtype = CT_SOUNDEMITTER;
|
|
}
|
|
|
|
// Check for particles emitter type
|
|
if (self.spr_frame > 0) {
|
|
self.part_active = PARTICLE_STYLE_FFIELD;
|
|
// Volume/direction need to be set early, just in case
|
|
// the entity starts off and then it has no volume!
|
|
self.part_vol = self.size * 0.5;
|
|
self.part_vel = self.movedir * 4;
|
|
if (self.spawnflags & ENT_STARTOFF) self.lip = PARTICLE_START_OFF;
|
|
else self.lip = PARTICLE_START_ON;
|
|
self.part_emitter = spawn_pemitter(self, self, self.part_active, self.lip);
|
|
}
|
|
|
|
// Setup Entity State functionality
|
|
if (self.targetname != "") self.use = entity_state_use;
|
|
self.estate_on = func_laser_on;
|
|
self.estate_off = func_laser_off;
|
|
|
|
if (self.spawnflags & ENT_STARTOFF) entity_state_off();
|
|
else entity_state_on();
|
|
};
|
|
|
|
/*======================================================================
|
|
/*QUAKED func_skill (0 .5 .8) ? STARTOPEN x x x x START_OFF x
|
|
This is a special bmodel that changes texture based on current skill level
|
|
Will keep checking skill level until triggered (based on door QC code)
|
|
-------- KEYS --------
|
|
targetname : trigger entity (works with entity state system)
|
|
style : Skill Level - 0 = easy, 1 = normal, 2 = hard, 3 = nightmare
|
|
message2 : Override message to display when bmodel is touched/damaged
|
|
target : Particle emitters to switch on/off
|
|
target2 : target(s) to fire when touched/damaged
|
|
health : Can be damaged instead of touched
|
|
angle : movement direction for trigger event
|
|
speed : movement speed (100 default)
|
|
lip : lip remaining at end of move (8 default)
|
|
noise1 : Custom sound - Stop moving (doors/drclos4.wav)
|
|
noise2 : Custom sound - Start/Loop moving (doors/stndr1.wav)
|
|
noise3 : Custom sound - Touching (plats/medplat2.wav)
|
|
_dirt : -1 = will be excluded from dirtmapping
|
|
_minlight : Minimum light level for any surface of the brush model
|
|
_mincolor : Minimum light color for any surface (def='1 1 1' RGB)
|
|
_shadow : Will cast shadows on other models and itself
|
|
_shadowself : Will cast shadows on itself
|
|
-------- SPAWNFLAGS --------
|
|
STARTOPEN : bmodel works in reverse state (moved to dest at spawn)
|
|
STARTOFF : Starts off and waits for trigger
|
|
-------- NOTES --------
|
|
Visual skill selection/update function
|
|
|
|
======================================================================*/
|
|
void() func_skill_check =
|
|
{
|
|
// If blocked by entity state system do nothing
|
|
if (self.estate & ESTATE_BLOCK) return;
|
|
|
|
// Has the skill level been changed?
|
|
if (self.attack_finished != skill) {
|
|
// Stop this entity constantly changing
|
|
self.attack_finished = skill;
|
|
|
|
if (self.target != "") {
|
|
self.enemy = find(world,targetname,self.target);
|
|
while (self.enemy) {
|
|
// Found a particle emitter?
|
|
if (self.enemy.classtype == CT_PARTICLEEMIT) {
|
|
if (self.style == skill) misc_particle_on(self.enemy);
|
|
else misc_particle_off(self.enemy);
|
|
}
|
|
|
|
// Keep searching for more targets in chain
|
|
self.enemy = find(self.enemy,targetname,self.target);
|
|
}
|
|
}
|
|
|
|
// Is the current skill level active?
|
|
if (self.style == skill) self.frame = 0;
|
|
else self.frame = 1; // Blank texture (off)
|
|
}
|
|
self.nextthink = time + 0.1;
|
|
self.think = func_skill_check;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_skill_touch =
|
|
{
|
|
// If blocked by entity state system do nothing
|
|
if (self.estate & ESTATE_BLOCK) return;
|
|
if (self.spawnflags & ENT_STARTOFF) return;
|
|
if ( !(other.flags & FL_CLIENT) ) return;
|
|
if ( other.health < 1 ) return;
|
|
|
|
// If skill level is changing, say so on console
|
|
if (skill != self.style) {
|
|
if (self.message2 != "") sprint(other, self.message2);
|
|
else {
|
|
sprint(other,"Skill level ");
|
|
if (self.style == SKILL_EASY) sprint(other,"EASY");
|
|
else if (self.style == SKILL_NORMAL) sprint(other,"NORMAL");
|
|
else if (self.style == SKILL_HARD) sprint(other,"HARD");
|
|
else sprint(other,"NIGHTMARE");
|
|
sprint(other," has been selected!");
|
|
}
|
|
sprint(other,"\n");
|
|
stuffcmd (other, "bf\n");
|
|
|
|
// Change skill level
|
|
cvar_set ("skill", ftos(self.style));
|
|
skill = self.style;
|
|
|
|
// Force quick update
|
|
func_skill_check();
|
|
|
|
// Any other targets to fire when touch/damaged
|
|
if (self.target2 != "") trigger_strs(self.target2, other);
|
|
}
|
|
|
|
// Play touch sound regardless of skill change
|
|
if (self.pausetime < time) {
|
|
sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM);
|
|
self.pausetime = time + 1; // No constant sound
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_skill_killed =
|
|
{
|
|
other = damage_attacker; // The attacker
|
|
self.health = self.max_health; // Reset health to original value
|
|
|
|
// All other exceptions are in the touch function
|
|
func_skill_touch();
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_skill_on =
|
|
{
|
|
// No longer need this spawnflag, remove it
|
|
self.spawnflags = self.spawnflags - (self.spawnflags & ENT_STARTOFF);
|
|
|
|
self.estate = ESTATE_ON;
|
|
func_skill_check();
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_skill_stop =
|
|
{
|
|
// Stop sound, ON and particles!
|
|
sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
|
|
// Need to reset local entity time, otherwise timer is broken
|
|
// it because of the SUB_CalcMove function changes it
|
|
self.ltime = time;
|
|
func_skill_on();
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_skill_use =
|
|
{
|
|
// Trigger once functionality
|
|
self.estate_use = SUB_Null;
|
|
self.estate = ESTATE_OFF;
|
|
sound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM);
|
|
SUB_CalcMove (self.pos2, self.speed, func_skill_stop);
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_skill_off =
|
|
{
|
|
self.estate = ESTATE_OFF;
|
|
self.frame = 1;
|
|
|
|
// Switch off any particle emitters
|
|
if (self.target != "") {
|
|
self.enemy = find(world,targetname,self.target);
|
|
while (self.enemy) {
|
|
// Found a particle emitter?
|
|
if (self.enemy.classtype == CT_PARTICLEEMIT)
|
|
misc_particle_off(self.enemy);
|
|
|
|
// Keep searching for more targets in chain
|
|
self.enemy = find(self.enemy,targetname,self.target);
|
|
}
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_skill =
|
|
{
|
|
if (check_bmodel_keys()) return; // Check for bmodel errors
|
|
|
|
// Setup default sounds if no custom sounds exist
|
|
if (self.noise1 == "") self.noise1 = "doors/drclos4.wav";
|
|
if (self.noise2 == "") self.noise2 = "doors/stndr1.wav";
|
|
if (self.noise3 == "") self.noise3 = "plats/medplat2.wav";
|
|
|
|
//Pre-cache all sounds
|
|
precache_sound(self.noise1); // stop sound (big clunk sound)
|
|
precache_sound(self.noise2); // moving sound (door moving)
|
|
precache_sound(self.noise3); // Touch sound
|
|
|
|
self.classtype = CT_FUNCSKILL; // Type
|
|
self.bsporigin = TRUE; // Bmodel (0,0,0 origin)
|
|
self.mdl = self.model;
|
|
SetMovedir ();
|
|
|
|
if (!self.speed) self.speed = 100; // Default movement speed
|
|
if (!self.lip) self.lip = 8; // End movement distance
|
|
|
|
//----------------------------------------------------------------------
|
|
// Is the skill level within proper range?
|
|
if (self.style < SKILL_EASY || self.style > SKILL_NIGHTMARE)
|
|
self.style = 0; // Default skill = EASY
|
|
self.attack_finished = -1; // Always check first time
|
|
self.lefty = cvar("skill"); // Read current setting
|
|
if (self.style != self.lefty) self.frame = 1;
|
|
|
|
//----------------------------------------------------------------------
|
|
// Add bmodel to world and work out movement positions
|
|
self.solid = SOLID_BSP;
|
|
self.movetype = MOVETYPE_PUSH;
|
|
setmodel (self, self.mdl);
|
|
setorigin (self, self.origin);
|
|
setsize (self, self.mins , self.maxs);
|
|
self.pos1 = self.origin;
|
|
self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
|
|
|
|
// Check for spawning conditions (nightmare, coop)
|
|
// Needs to exist after entity has been added to work for BSPorigin
|
|
if (check_nightmare() == TRUE) return;
|
|
if (check_coop() == TRUE) return;
|
|
|
|
//----------------------------------------------------------------------
|
|
// FUNC_SKILL_START_OPEN is design is hide a skill pillar
|
|
// until it is triggered (nightmare skill)
|
|
if (self.spawnflags & FUNC_SKILLSTARTOPEN) {
|
|
setorigin (self, self.pos2);
|
|
self.pos2 = self.pos1;
|
|
self.pos1 = self.origin;
|
|
}
|
|
|
|
// If the skill pillar is setup with a targetname then wait for
|
|
// a trigger event before becoming active (updates+particles)
|
|
if (self.targetname != "") {
|
|
self.spawnflags = self.spawnflags | ENT_STARTOFF;
|
|
self.estate_use = func_skill_use;
|
|
self.frame = 1; // Always starts off
|
|
}
|
|
|
|
// Setup Entity State functionality
|
|
self.use = entity_state_use;
|
|
self.estate_on = func_skill_on;
|
|
self.estate_off = func_skill_off;
|
|
self.estate = ESTATE_OFF;
|
|
|
|
// Can either be touched or damaged, never both!
|
|
if (self.health != 0) {
|
|
self.health = self.max_health = 1;
|
|
self.th_die = func_skill_killed;
|
|
self.takedamage = DAMAGE_YES;
|
|
self.touch = SUB_Null;
|
|
}
|
|
else {
|
|
self.touch = func_skill_touch;
|
|
self.takedamage = DAMAGE_NO;
|
|
}
|
|
|
|
// Wait random amount for all entities to spawn
|
|
self.nextthink = time + 0.1 + random()*0.5;
|
|
if (self.spawnflags & ENT_STARTOFF) self.think = self.estate_off;
|
|
else self.think = self.estate_on;
|
|
};
|
|
|
|
/*======================================================================
|
|
/*QUAKED func_bob (0 .5 .8) ? x x x x x x STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
|
|
A SOLID bmodel that gently moves back and forth
|
|
-------- KEYS --------
|
|
targetname : trigger entity (works with entity state system)
|
|
angle : direction movement, use "360" for angle 0
|
|
height : direction intensity (def=4)
|
|
count : direction cycle timer (def=2s, minimum=1s)
|
|
waitmin : Speed up scale (def=1) 1+=non linear
|
|
waitmin2 : Slow down scale (def=0.75)
|
|
delay : Starting time delay (def=0, -1=random)
|
|
_dirt : -1 = will be excluded from dirtmapping
|
|
_minlight : Minimum light level for any surface of the brush model
|
|
_mincolor : Minimum light color for any surface (def='1 1 1' RGB)
|
|
_shadow : Will cast shadows on other models and itself
|
|
_shadowself : Will cast shadows on itself
|
|
-------- SPAWNFLAGS --------
|
|
STARTOFF : Starts off and waits for trigger
|
|
-------- NOTES --------
|
|
A SOLID bmodel that gently moves back and forth
|
|
|
|
======================================================================*/
|
|
void() func_bob_use =
|
|
{
|
|
// Deal with STARTOFF functionality first
|
|
if (self.spawnflags & ENT_STARTOFF) self.estate_on();
|
|
else {
|
|
// Toggle state (switch on or disable)
|
|
if (self.estate & ESTATE_BLOCK) self.estate = ESTATE_ON;
|
|
else self.estate = ESTATE_DISABLE;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_bob_timer =
|
|
{
|
|
// Keep ticking in background, use local timer (faster)
|
|
self.think = func_bob_timer;
|
|
if (self.bsporigin) self.nextthink = self.ltime + 0.1;
|
|
else self.nextthink = time + 0.1;
|
|
|
|
// Do nothing if entity state is off
|
|
if (self.estate & ESTATE_OFF) return;
|
|
|
|
// Has the cycle completed?
|
|
if (self.attack_timer < time) {
|
|
// Don't reset bmodel if disabled
|
|
if (self.estate & ESTATE_DISABLE) {
|
|
self.attack_timer = LARGE_TIMER;
|
|
self.lefty = -1;
|
|
}
|
|
else {
|
|
// Setup bob cycle and half way point for slowdown
|
|
self.attack_timer = time + self.count;
|
|
self.distance = time + (self.count * 0.5);
|
|
// Flip direction of bmodel bob
|
|
self.lefty = 1 - self.lefty;
|
|
if (self.lefty < 1) self.t_length = self.height;
|
|
else self.t_length = -self.height;
|
|
}
|
|
// Always reset velocity and flags
|
|
self.velocity = '0 0 0';
|
|
self.flags = 0;
|
|
}
|
|
|
|
// Is the direction set?
|
|
// This is a block condition to prevent the bmodel moving
|
|
if (self.lefty != -1) {
|
|
// Slow down velocity (gradually)
|
|
if (self.distance < time)
|
|
self.velocity = self.velocity * self.waitmin2;
|
|
else {
|
|
// Speed up velocity (linear/exponentially)
|
|
self.t_length = self.t_length * self.waitmin;
|
|
self.velocity = self.velocity + (self.movedir * self.t_length);
|
|
}
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_bob_on =
|
|
{
|
|
// No longer need this spawnflag, remove it
|
|
self.spawnflags = self.spawnflags - (self.spawnflags & ENT_STARTOFF);
|
|
|
|
self.estate = ESTATE_ON;
|
|
if (self.bsporigin) {
|
|
// Check for solid spawnflag
|
|
if (self.spawnflags & BOB_NONSOLID) {
|
|
self.movetype = MOVETYPE_PUSH;
|
|
self.solid = SOLID_NOT;
|
|
}
|
|
else {
|
|
self.movetype = MOVETYPE_PUSH;
|
|
self.solid = SOLID_BSP;
|
|
}
|
|
}
|
|
else {
|
|
self.movetype = MOVETYPE_FLY;
|
|
if (self.spawnflags & BOB_COLLISION) self.solid = SOLID_BBOX;
|
|
else self.solid = SOLID_NOT;
|
|
self.flags = 0; // Reset any onground flags
|
|
}
|
|
setmodel (self, self.mdl);
|
|
setsize (self, self.mins , self.maxs);
|
|
|
|
// Check for spawning conditions (nightmare, coop)
|
|
// Needs to exist after entity has been added to work for BSPorigin
|
|
if (check_nightmare() == TRUE) return;
|
|
if (check_coop() == TRUE) return;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_bob_off =
|
|
{
|
|
self.estate = ESTATE_OFF;
|
|
self.movetype = MOVETYPE_NONE;
|
|
self.solid = SOLID_NOT;
|
|
setmodel (self, "");
|
|
self.velocity = '0 0 0';
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() func_bob =
|
|
{
|
|
self.classtype = CT_FUNCBOB;
|
|
// Using a custom model?
|
|
if (self.mdl == "") {
|
|
self.bsporigin = TRUE;
|
|
self.mdl = self.model;
|
|
}
|
|
else {
|
|
self.bsporigin = FALSE;
|
|
self.modelindex = 0;
|
|
self.model = "";
|
|
}
|
|
|
|
SetMovedir ();
|
|
self.movedir = normalize(self.movedir);
|
|
|
|
if (self.height <=0) self.height = 8; // Direction intensity
|
|
if (self.count <1) self.count = 2; // Direction switch timer
|
|
if (self.waitmin <=0) self.waitmin = 1; // Speed up
|
|
if (self.waitmin2 <=0) self.waitmin2 = 0.75; // Slow down
|
|
if (self.delay < 0) self.delay = random() + random() + random();
|
|
|
|
// Setup Entity State functionality
|
|
if (self.targetname != "") self.use = entity_state_use;
|
|
self.estate_on = func_bob_on;
|
|
self.estate_off = func_bob_off;
|
|
if (self.spawnflags & ENT_STARTOFF) self.estate_off();
|
|
else self.estate_on();
|
|
|
|
self.think = func_bob_timer;
|
|
self.nextthink = time + 0.1 + self.delay;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() misc_bob =
|
|
{
|
|
if (self.mdl == "") self.mdl = MODEL_BROKEN;
|
|
precache_model(self.mdl);
|
|
|
|
func_bob();
|
|
}; |