707 lines
15 KiB
C++
707 lines
15 KiB
C++
/*QUAKED rider_path (0.5 0.3 0) (-8 -8 -8) (8 8 8)
|
|
Specifies a path point for a rider. You can specify
|
|
up to 4 other places that the rider can go after this
|
|
point.
|
|
-------------------------FIELDS-------------------------
|
|
path_id: number (id) of this path point
|
|
next_path_1: number (id) of a possible next point
|
|
next_path_2: number (id) of a possible next point
|
|
next_path_3: number (id) of a possible next point
|
|
next_path_4: number (id) of a possible next point
|
|
--------------------------------------------------------
|
|
|
|
*/
|
|
void rider_path(void)
|
|
{
|
|
}
|
|
|
|
void riderpath_init(void)
|
|
{
|
|
entity search,found;
|
|
|
|
found = world;
|
|
search = find(world, classname, "rider_path");
|
|
while(search != world && found == world)
|
|
{
|
|
if (search.path_id == 1)
|
|
found = search;
|
|
else
|
|
search = find(search, classname, "rider_path");
|
|
}
|
|
|
|
if (found==world)
|
|
{
|
|
dprint("No starting point for rider\n");
|
|
remove(self);
|
|
return;
|
|
}
|
|
self.path_current=self.path_last = found;
|
|
}
|
|
|
|
void riderpath_findnext(void)
|
|
{
|
|
entity search,found;
|
|
float next,num_points,position;
|
|
|
|
num_points = 0;
|
|
if (self.path_current.next_path_1)
|
|
num_points += 1;
|
|
if (self.path_current.next_path_2)
|
|
num_points += 1;
|
|
if (self.path_current.next_path_3)
|
|
num_points += 1;
|
|
if (self.path_current.next_path_4)
|
|
num_points += 1;
|
|
|
|
if (!num_points)
|
|
{
|
|
dprintf("rider path %s has no next points\n",self.path_id);
|
|
remove(self);
|
|
return;
|
|
}
|
|
|
|
position = random(num_points);
|
|
// dprintf("Num_points is %s ",num_points);
|
|
// dprintf("position is %s\n",position);
|
|
|
|
num_points = next = 0;
|
|
if (self.path_current.next_path_1)
|
|
{
|
|
num_points += 1;
|
|
if (position <= num_points && !next)
|
|
next = self.path_current.next_path_1;
|
|
}
|
|
if (self.path_current.next_path_2)
|
|
{
|
|
num_points += 1;
|
|
if (position <= num_points && !next)
|
|
next = self.path_current.next_path_2;
|
|
}
|
|
|
|
if (self.path_current.next_path_3)
|
|
{
|
|
num_points += 1;
|
|
if (position <= num_points && !next)
|
|
next = self.path_current.next_path_3;
|
|
}
|
|
|
|
if (self.path_current.next_path_4)
|
|
{
|
|
num_points += 1;
|
|
if (position <= num_points && !next)
|
|
next = self.path_current.next_path_4;
|
|
}
|
|
|
|
if (!next)
|
|
{
|
|
dprint("Internal error for rider path\n");
|
|
dprintf(" Next is %s\n",next);
|
|
dprintf(" Num_points is %s\n",num_points);
|
|
dprintf(" position is %s\n",position);
|
|
return;
|
|
}
|
|
|
|
found = world;
|
|
search = find(world, classname, "rider_path");
|
|
while(search != world && found == world)
|
|
{
|
|
if (search.path_id == next)
|
|
found = search;
|
|
else
|
|
search = find(search, classname, "rider_path");
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
dprintf("Could not find rider path %s\n",next);
|
|
remove(self);
|
|
return;
|
|
}
|
|
else
|
|
self.path_current = found;
|
|
|
|
}
|
|
|
|
void rider_die(void);
|
|
|
|
void riderpath_move(float move_speed)
|
|
{
|
|
float distance, altitude, temp;
|
|
vector displace;
|
|
entity save_ent;
|
|
|
|
if(self.velocity!='0 0 0')
|
|
self.velocity='0 0 0';
|
|
|
|
self.ideal_yaw = vectoyaw(self.path_current.origin - self.origin);
|
|
self.rider_last_y_change = self.rider_y_change;
|
|
self.rider_y_change = ChangeYaw();
|
|
|
|
//rj rider_die();
|
|
|
|
if (self.movetype == MOVETYPE_FLY)
|
|
{
|
|
distance = vhlen(self.origin - self.path_current.origin);
|
|
altitude = self.path_current.origin_z - self.origin_z;
|
|
|
|
if (distance < 400)
|
|
{
|
|
// altitude *= 0.06;
|
|
temp = (distance - self.rider_path_distance) / (400-self.rider_path_distance);
|
|
temp = 1-temp;
|
|
temp = temp / 6;
|
|
|
|
if (altitude > 30)
|
|
{
|
|
self.angles_x = temp * 200;
|
|
self.rider_move_adjustment -= 0.1;
|
|
}
|
|
else if (altitude < -30)
|
|
{
|
|
self.angles_x = 0 - (temp * 200);
|
|
self.rider_move_adjustment += 0.15;
|
|
}
|
|
|
|
if (altitude > 60)
|
|
{
|
|
self.rider_move_adjustment -= 0.1;
|
|
}
|
|
else if (altitude < -60)
|
|
{
|
|
self.rider_move_adjustment += 0.15;
|
|
}
|
|
|
|
altitude *= temp;
|
|
}
|
|
else
|
|
{
|
|
altitude = 0;
|
|
self.angles_x -= self.angles_x / 10;
|
|
self.rider_move_adjustment -= self.rider_move_adjustment / 15;
|
|
}
|
|
// dprintf("Move adjustment %s\n",self.rider_move_adjustment);
|
|
|
|
self.origin_z += altitude;
|
|
|
|
// altitude = self.path_current.origin_z - self.origin_z;
|
|
// dprintf("Flying: distance %s",distance);
|
|
// dprintf(" Alt is %s\n",altitude);
|
|
}
|
|
|
|
move_speed += self.rider_move_adjustment;
|
|
|
|
if(!walkmove(self.angles_y, move_speed, TRUE))
|
|
{
|
|
save_ent=self.goalentity;
|
|
self.goalentity=self.path_current;
|
|
movetogoal(move_speed);
|
|
self.goalentity=save_ent;
|
|
}
|
|
|
|
if (self.classname != "rider_famine") // Famine doesn't do radius pain
|
|
{
|
|
if (trace_ent)//!=world&&trace_ent.classname!="ghost")
|
|
{
|
|
displace = normalize(trace_ent.origin - self.origin);
|
|
if (infront(trace_ent))
|
|
{
|
|
trace_ent.velocity += displace*random(1000,1600);
|
|
T_Damage (trace_ent, self, self, random(25,35));
|
|
if(trace_ent.flags&FL_CLIENT)
|
|
{
|
|
trace_ent.punchangle_x = random(-9,-1);
|
|
trace_ent.punchangle_y = random(-10,10);
|
|
trace_ent.punchangle_z = random(-10,10);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
trace_ent.velocity += displace*random(700,900);
|
|
T_Damage (trace_ent, self, self, random(15,20));
|
|
if(trace_ent.flags&FL_CLIENT)
|
|
{
|
|
trace_ent.punchangle_x = random(-3,2);
|
|
trace_ent.punchangle_y = random(-5,5);
|
|
trace_ent.punchangle_z = random(-5,5);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
distance = vhlen(self.origin - self.path_current.origin);
|
|
if (distance < self.rider_path_distance)
|
|
riderpath_findnext();
|
|
}
|
|
|
|
|
|
|
|
void rider_multi_wait(void)
|
|
{
|
|
if (self.max_health)
|
|
{
|
|
self.health = self.max_health;
|
|
self.takedamage = DAMAGE_NO_GRENADE;
|
|
self.solid = SOLID_BBOX;
|
|
}
|
|
}
|
|
|
|
void rider_multi_trigger(void)
|
|
{
|
|
if (self.nextthink > time)
|
|
{
|
|
return; // allready been triggered
|
|
}
|
|
if (self.enemy.classname != "rider_war")
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (random() <= self.rt_chance)
|
|
{
|
|
if (self.noise) sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
|
|
|
|
// don't trigger again until reset
|
|
self.takedamage = DAMAGE_NO;
|
|
|
|
activator = self.enemy;
|
|
|
|
SUB_UseTargets();
|
|
}
|
|
|
|
if (self.wait > 0)
|
|
{
|
|
self.think = rider_multi_wait;
|
|
thinktime self : self.wait;
|
|
}
|
|
else
|
|
{ // we can't just remove (self) here, because this is a touch function
|
|
// called while C code is looping through area links...
|
|
self.touch = SUB_Null;
|
|
thinktime self : 0.1;
|
|
self.think = SUB_Remove;
|
|
}
|
|
}
|
|
|
|
|
|
void rider_multi_use(void)
|
|
{
|
|
if (time < self.attack_finished)
|
|
return;
|
|
|
|
self.enemy = activator;
|
|
rider_multi_trigger();
|
|
}
|
|
|
|
void rider_multi_touch(void)
|
|
{
|
|
if (time < self.attack_finished)
|
|
return;
|
|
|
|
// if the trigger has an angles field, check player's facing direction
|
|
|
|
if (self.movedir != '0 0 0')
|
|
{
|
|
makevectors (other.angles);
|
|
if (v_forward * self.movedir < 0)
|
|
return; // not facing the right way
|
|
}
|
|
|
|
self.enemy = other;
|
|
|
|
rider_multi_trigger ();
|
|
}
|
|
|
|
/*QUAKED rider_trigger_multiple (0.5 0.15 0) ?
|
|
-------------------------FIELDS-------------------------
|
|
rt_chance: chance (0-1) that the trigger will be run
|
|
--------------------------------------------------------
|
|
|
|
*/
|
|
void rider_trigger_multiple(void)
|
|
{
|
|
if (!self.wait)
|
|
self.wait = 0.2;
|
|
self.use = rider_multi_use;
|
|
|
|
InitTrigger ();
|
|
|
|
if (self.health)
|
|
{
|
|
self.max_health = self.health;
|
|
self.th_die = multi_killed;
|
|
self.takedamage = DAMAGE_NO_GRENADE;
|
|
self.solid = SOLID_BBOX;
|
|
setorigin (self, self.origin); // make sure it links into the world
|
|
}
|
|
else
|
|
{
|
|
self.touch = rider_multi_touch;
|
|
}
|
|
}
|
|
|
|
/*QUAKED rider_trigger_once (0.5 0.15 0) ?
|
|
-------------------------FIELDS-------------------------
|
|
rt_chance: chance (0-1) that the trigger will be run
|
|
--------------------------------------------------------
|
|
|
|
*/
|
|
void rider_trigger_once(void)
|
|
{
|
|
self.wait = -1;
|
|
rider_trigger_multiple();
|
|
}
|
|
|
|
|
|
void beam_move(void)
|
|
{
|
|
thinktime self : HX_FRAME_TIME;
|
|
|
|
if (self.scale < self.beam_max_scale)
|
|
self.scale += 0.6;
|
|
else
|
|
self.scale = self.beam_max_scale;
|
|
|
|
if (self.beam_direction)
|
|
{
|
|
self.beam_angle_a += self.beam_speed;
|
|
if (self.beam_angle_a >= 360) self.beam_angle_a -= 360;
|
|
}
|
|
else
|
|
{
|
|
self.beam_angle_a -= self.beam_speed;
|
|
if (self.beam_angle_a < 0) self.beam_angle_a += 360;
|
|
}
|
|
|
|
self.angles_x = cos(self.beam_angle_a) * self.beam_angle_b;
|
|
self.angles_z = sin(self.beam_angle_a) * self.beam_angle_b;
|
|
}
|
|
|
|
void star_think(void)
|
|
{
|
|
thinktime self : HX_FRAME_TIME;
|
|
|
|
self.velocity = self.velocity * 1.05;
|
|
|
|
if (self.scale < 2)
|
|
self.scale *= 1.08;
|
|
}
|
|
|
|
void rider_star(void)
|
|
{
|
|
entity star;
|
|
float angle;
|
|
|
|
if (random() < 0.5)
|
|
return;
|
|
|
|
star = spawn();
|
|
|
|
setmodel(star,"models/boss/star.mdl");
|
|
setorigin(star,self.origin);
|
|
|
|
setsize (star, '0 0 0', '0 0 0');
|
|
star.owner = self.owner;
|
|
star.movetype = MOVETYPE_FLYMISSILE;
|
|
star.solid = SOLID_BBOX;
|
|
star.avelocity = randomv('40 40 40', '100 100 100');
|
|
star.scale = 0.1;
|
|
|
|
angle = random(360);
|
|
star.velocity_x = cos(angle)*300;
|
|
star.velocity_y = sin(angle)*300;
|
|
|
|
star.classname = "rider_temp";
|
|
star.think = star_think;
|
|
star.touch = SUB_Remove;
|
|
|
|
thinktime star : HX_FRAME_TIME;
|
|
}
|
|
|
|
void circle_think(void)
|
|
{
|
|
thinktime self : HX_FRAME_TIME;
|
|
|
|
rider_star();
|
|
|
|
if (self.monster_stage == 0)
|
|
{
|
|
self.scale *= 1.0275;
|
|
if (self.scale >= 2.5)
|
|
{
|
|
self.monster_stage = 1;
|
|
self.scale = 2.5;
|
|
}
|
|
}
|
|
else if (self.monster_stage == 1)
|
|
{
|
|
self.scale -= random(.1);
|
|
if (self.scale < 1.5 || random() < 0.1)
|
|
self.monster_stage = 2;
|
|
}
|
|
else if (self.monster_stage == 2)
|
|
{
|
|
self.scale += random(.1);
|
|
if (self.scale >= 2.5)
|
|
{
|
|
self.monster_stage = 1;
|
|
self.scale = 2.5;
|
|
}
|
|
else if (random() < 0.1)
|
|
self.monster_stage = 1;
|
|
}
|
|
}
|
|
|
|
void rider_eol(void)
|
|
{
|
|
entity search;
|
|
|
|
if (self.count == 0)
|
|
{
|
|
self.count = 1;
|
|
self.effects (-) EF_BRIGHTLIGHT;
|
|
self.effects (+) EF_NODRAW;
|
|
if(self.movechain!=world)
|
|
self.movechain.effects (+) EF_NODRAW;
|
|
|
|
thinktime self : 5;
|
|
|
|
search = find(world, classname, "rider_temp");
|
|
while (search != world)
|
|
{
|
|
remove(search);
|
|
search = find(search, classname, "rider_temp");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// return;
|
|
|
|
nextmap = self.map;
|
|
nextstartspot = self.target;
|
|
//rj nextmap = "rick1";
|
|
|
|
intermission_running = 1;
|
|
|
|
if (deathmatch)
|
|
intermission_exittime = time + 5;
|
|
else if (self.classname == "monster_eidolon")
|
|
intermission_exittime = time + 99999;
|
|
else
|
|
intermission_exittime = time + 2;
|
|
|
|
|
|
//Remove cross-level trigger server flags for next hub
|
|
serverflags(-)(SFL_CROSS_TRIGGER_1|
|
|
SFL_CROSS_TRIGGER_2|
|
|
SFL_CROSS_TRIGGER_3|
|
|
SFL_CROSS_TRIGGER_4|
|
|
SFL_CROSS_TRIGGER_5|
|
|
SFL_CROSS_TRIGGER_6|
|
|
SFL_CROSS_TRIGGER_7|
|
|
SFL_CROSS_TRIGGER_8);
|
|
|
|
search=find(world,classname,"player");
|
|
while(search)
|
|
{//Take away all their goodies
|
|
search.puzzle_inv1 = string_null;
|
|
search.puzzle_inv2 = string_null;
|
|
search.puzzle_inv3 = string_null;
|
|
search.puzzle_inv4 = string_null;
|
|
search.puzzle_inv5 = string_null;
|
|
search.puzzle_inv6 = string_null;
|
|
search.puzzle_inv7 = string_null;
|
|
search.puzzle_inv8 = string_null;
|
|
search=find(search,classname,"player");
|
|
}
|
|
|
|
WriteByte (MSG_ALL, SVC_INTERMISSION);
|
|
if (!cvar("registered") && cvar("oem"))
|
|
{
|
|
WriteByte (MSG_ALL, 9);
|
|
intermission_exittime = time + 99999;
|
|
}
|
|
else if (self.classname == "rider_famine")
|
|
WriteByte (MSG_ALL, 1);
|
|
else if (self.classname == "rider_death")
|
|
WriteByte (MSG_ALL, 2);
|
|
else if (self.classname == "rider_pestilence")
|
|
WriteByte (MSG_ALL, 3);
|
|
else if (self.classname == "rider_war")
|
|
WriteByte (MSG_ALL, 4);
|
|
else if (self.classname == "monster_eidolon")
|
|
WriteByte (MSG_ALL, 6);
|
|
else
|
|
dprint("Invalid boss creature\n");
|
|
|
|
FreezeAllEntities();
|
|
}
|
|
|
|
void rider_die(void)
|
|
{
|
|
entity beam;
|
|
entity save;
|
|
entity found;
|
|
vector new_origin;
|
|
|
|
if (self.think != rider_die)
|
|
{
|
|
SUB_UseTargets();
|
|
|
|
self.think = rider_die;
|
|
self.count = 0;
|
|
thinktime self : HX_FRAME_TIME;
|
|
self.rider_death_speed = 0.2;
|
|
// self.effects = EF_NODRAW;
|
|
|
|
return;
|
|
}
|
|
|
|
if (self.count == 0)
|
|
{
|
|
sound (self, CHAN_AUTO, "famine/flashdie.wav", 1, ATTN_NONE); // Start of the death flash
|
|
found=find(world,classname,"player");
|
|
while(found)
|
|
{//Give them all the exp
|
|
AwardExperience(found,self,self.experience_value);
|
|
found=find(found,classname,"player");
|
|
}
|
|
self.experience_value=FALSE;
|
|
self.drawflags = self.drawflags | MLS_ABSLIGHT;
|
|
self.abslight = 3;
|
|
if(self.noise)
|
|
sound(self,CHAN_VOICE,self.noise,1,ATTN_NONE);
|
|
self.movetype=MOVETYPE_NONE;
|
|
self.velocity='0 0 0';
|
|
}
|
|
|
|
thinktime self : self.rider_death_speed;
|
|
self.rider_death_speed += 0.1;
|
|
|
|
if (self.count >= 10)
|
|
{
|
|
if (self.count == 3)
|
|
{
|
|
beam = spawn();
|
|
|
|
new_origin = self.origin + '0 0 50';
|
|
|
|
setmodel(beam,"models/boss/circle.mdl");
|
|
setorigin(beam,new_origin);
|
|
|
|
setsize (beam, '0 0 0', '0 0 0');
|
|
beam.owner = self;
|
|
beam.movetype = MOVETYPE_FLYMISSILE;
|
|
beam.solid = SOLID_NOT;
|
|
beam.drawflags = SCALE_TYPE_UNIFORM;
|
|
beam.scale = .1;
|
|
beam.skin = 0;
|
|
beam.avelocity = '0 0 300';
|
|
beam.think = circle_think;
|
|
thinktime beam : HX_FRAME_TIME;
|
|
self.count = 13;
|
|
}
|
|
|
|
self.count = 0;
|
|
self.think = rider_eol;
|
|
thinktime self : .5;
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
self.effects = EF_BRIGHTLIGHT;
|
|
// self.effects = EF_NODRAW;
|
|
|
|
if (self.count == 3)
|
|
{
|
|
beam = spawn();
|
|
|
|
new_origin = self.origin + '0 0 50';
|
|
|
|
setmodel(beam,"models/boss/circle.mdl");
|
|
setorigin(beam,new_origin);
|
|
|
|
setsize (beam, '0 0 0', '0 0 0');
|
|
beam.owner = self;
|
|
beam.movetype = MOVETYPE_FLYMISSILE;
|
|
beam.solid = SOLID_NOT;
|
|
beam.drawflags = SCALE_TYPE_UNIFORM;
|
|
beam.scale = .1;
|
|
beam.skin = 0;
|
|
beam.avelocity = '0 0 300';
|
|
beam.classname = "rider_temp";
|
|
beam.think = circle_think;
|
|
thinktime beam : HX_FRAME_TIME;
|
|
}
|
|
else if (self.count == 0)
|
|
{
|
|
starteffect(18, self.origin + '0 0 40');
|
|
}
|
|
|
|
}
|
|
|
|
self.count += 1;
|
|
|
|
beam = spawn();
|
|
|
|
makevectors(self.angles);
|
|
// new_origin = v_factorrange('-3 -25 45', '3 25 50') + self.origin;
|
|
new_origin = self.origin + '0 0 50';
|
|
|
|
setmodel(beam,"models/boss/shaft.mdl");
|
|
setorigin(beam,new_origin);
|
|
|
|
setsize (beam, '0 0 0', '0 0 0');
|
|
beam.owner = self;
|
|
beam.drawflags = SCALE_ORIGIN_BOTTOM | SCALE_TYPE_XYONLY;
|
|
beam.movetype = MOVETYPE_FLYMISSILE;
|
|
beam.solid = SOLID_NOT;
|
|
beam.think = beam_move;
|
|
beam.angles = '0 0 0';
|
|
beam.angles_x = random(-50,50);
|
|
beam.angles_z = random(-50,50);
|
|
beam.beam_angle_a = random(360);
|
|
beam.beam_angle_b = random(20,130);
|
|
beam.scale = .1;
|
|
beam.beam_max_scale = random(.5,1.5);
|
|
beam.classname = "rider_temp";
|
|
if (random() > 0.5)
|
|
beam.beam_direction = 1;
|
|
|
|
beam.beam_speed = random(2,4.5);
|
|
|
|
save = self;
|
|
self = beam;
|
|
beam_move();
|
|
self = save;
|
|
}
|
|
|
|
void rider_use(void)
|
|
{
|
|
thinktime self : 0.2; // wait for path points to spawn
|
|
}
|
|
|
|
void rider_init(void)
|
|
{
|
|
precache_model3 ("models/boss/shaft.mdl");
|
|
precache_model3 ("models/boss/circle.mdl");
|
|
precache_model3 ("models/boss/star.mdl");
|
|
precache_sound3 ("famine/flashdie.wav");
|
|
|
|
total_monsters += 1;
|
|
|
|
self.takedamage = DAMAGE_YES;
|
|
self.flags(+)FL_MONSTER;
|
|
self.flags2(+)FL_ALIVE;
|
|
self.thingtype=THINGTYPE_FLESH;
|
|
|
|
if(self.classname!="monster_eidolon")
|
|
self.monsterclass=CLASS_BOSS;
|
|
|
|
self.th_die = rider_die;
|
|
self.use = rider_use;
|
|
|
|
if (!(self.spawnflags & 1))
|
|
thinktime self : 0.2; // wait for path points to spawn
|
|
}
|