hexen2-hw-hc/plats.hc

1125 lines
24 KiB
C++

/*
* $Header: /HexenWorld/HCode/PLATS.hc 4 4/01/98 5:49p Mgummelt $
*/
void()func_train_mp;
void() newplat_center_touch;
void() newplat_go_up;
void() newplat_go_down;
void() plat_center_touch;
void() plat_outside_touch;
void() plat_trigger_use;
void() plat_go_up;
void() plat_go_down;
void() plat_crush;
float PLAT_LOW_TRIGGER = 1;
void() crusher_hit_bottom;
void() crusher_hit_top;
void() crusher_trigger_use;
void() crusher_go_up;
void() crusher_go_down;
float CRUSH_MULT = 1;
float CRUSH_SLIDE = 2;
float CRUSH_START_OPEN = 4;
float CRUSH_ENDPOS = 8;
float START_BOTTOM = 1;
float START_RTRN= 2;
float CONTINUE= 4;
void() train_wait;
float TRAIN_GLOW = 1;
float TRAIN_WAITTRIG = 2;
float TRAIN_RETURN = 4;
void() plat_spawn_inside_trigger =
{
local entity trigger;
local vector tmin, tmax;
//middle trigger
trigger = spawn();
if (self.classname == "newplat")
trigger.touch = newplat_center_touch;
else
trigger.touch = plat_center_touch;
trigger.movetype = MOVETYPE_NONE;
trigger.solid = SOLID_TRIGGER;
trigger.enemy = self;
tmin = self.mins + '25 25 0';
tmax = self.maxs - '25 25 -8';
tmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8);
if (self.spawnflags & PLAT_LOW_TRIGGER)
tmax_z = tmin_z + 8;
if (self.size_x <= 50)
{
tmin_x = (self.mins_x + self.maxs_x) / 2;
tmax_x = tmin_x + 1;
}
if (self.size_y <= 50)
{
tmin_y = (self.mins_y + self.maxs_y) / 2;
tmax_y = tmin_y + 1;
}
setsize (trigger, tmin, tmax);
};
void() plat_hit_top =
{
sound (self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise1, 1, ATTN_NORM);
self.state = STATE_TOP;
self.think = plat_go_down;
self.nextthink = self.ltime + 3;
};
void() plat_hit_bottom =
{
sound (self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise1, 1, ATTN_NORM);
self.state = STATE_BOTTOM;
};
void() plat_go_down =
{
sound (self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise, 1, ATTN_LOOP);
self.state = STATE_DOWN;
SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom);
};
void() plat_go_up =
{
sound (self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise, 1, ATTN_LOOP);
self.state = STATE_UP;
SUB_CalcMove (self.pos1, self.speed, plat_hit_top);
};
void() plat_center_touch =
{
if (other.classname != "player"&&other.movetype!=MOVETYPE_PUSHPULL)//Monsters too?
return;
if (other.health <= 0)
return;
self = self.enemy;
if (self.state == STATE_BOTTOM)
plat_go_up ();
else if (self.state == STATE_TOP)
self.nextthink = self.ltime + 1; // delay going down
};
void() plat_outside_touch =
{
if (other.classname != "player"&&other.movetype!=MOVETYPE_PUSHPULL)
return;
if (other.health <= 0)
return;
//dprint ("plat_outside_touch\n");
self = self.enemy;
if (self.state == STATE_TOP)
plat_go_down ();
};
void() plat_trigger_use =
{
if (self.think)
return; // allready activated
plat_go_down();
};
void() plat_crush =
{
T_Damage (other, self, self, 1);
if (self.state == STATE_UP)
plat_go_down ();
else if (self.state == STATE_DOWN)
plat_go_up ();
else
objerror ("plat_crush: bad self.state\n");
};
void() plat_use =
{
self.use = SUB_Null;
if (self.state != STATE_UP)
objerror ("plat_use: not in up state");
plat_go_down();
};
/*QUAKED func_plat (0 .5 .8) ? PLAT_LOW_TRIGGER
speed default 150
Plats are always drawn in the extended position, so they will light correctly.
If the plat is the target of another trigger or button, it will start out disabled in the extended position until it is trigger, when it will lower and become a normal plat.
If the "height" key is set, that will determine the amount the plat moves, instead of being implicitly determined by the model's height.
Set "soundtype" to one of the following:
1) pulley
2) chain
*/
void() func_plat =
{
if (!self.t_length)
self.t_length = 80;
if (!self.t_width)
self.t_width = 10;
if (self.soundtype == 0)
self.soundtype = 2;
// FIX THIS TO LOAD A GENERIC PLAT SOUND
if (self.soundtype == 1)
{
precache_sound ("plats/pulyplt1.wav");
precache_sound ("plats/pulyplt2.wav");
self.noise = "plats/pulyplt1.wav";
self.noise1 = "plats/pulyplt2.wav";
}
if (self.soundtype == 2)
{
precache_sound ("plats/chainplt1.wav");
precache_sound ("plats/chainplt2.wav");
self.noise = "plats/chainplt1.wav";
self.noise1 = "plats/chainplt2.wav";
}
self.mangle = self.angles;
self.angles = '0 0 0';
self.classname = "plat";
self.solid = SOLID_BSP;
self.movetype = MOVETYPE_PUSH;
setorigin (self, self.origin);
setmodel (self, self.model);
setsize (self, self.mins , self.maxs);
self.blocked = plat_crush;
if (!self.speed)
self.speed = 150;
// pos1 is the top position, pos2 is the bottom
self.pos1 = self.origin;
self.pos2 = self.origin;
if (self.height)
self.pos2_z = self.origin_z - self.height;
else
self.pos2_z = self.origin_z - self.size_z + 8;
self.use = plat_trigger_use;
plat_spawn_inside_trigger (); // the "start moving" trigger
if (self.targetname)
{
self.state = STATE_UP;
self.use = plat_use;
}
else
{
setorigin (self, self.pos2);
self.state = STATE_BOTTOM;
}
};
//============================================================================
void() train_next;
void() func_train_find;
void() train_blocked =
{
if (time < self.attack_finished) return;
self.attack_finished = time + 0.5;
T_Damage (other, self, self, self.dmg);
};
void() train_use =
{
if(self.wait==-1)
self.use=SUB_Null;
if (self.spawnflags & TRAIN_GLOW)
{
self.effects (+) EF_BRIGHTLIGHT;
}
if (self.decap != 1)
{
self.decap = 1;
if (self.think != train_next)
{
self.think = func_train_find;
train_next();
}
}
else
{
if (self.spawnflags & TRAIN_RETURN) self.decap = 0;
else self.decap = 2;
}
};
void() train_wait =
{
//Check to make sure train is active
if(self.decap!=2)
{
self.think = train_next;
if(self.wait==-2)
{
if(self.th_die)
{
if(self.pausetime)
{
self.think=self.th_die;
self.nextthink=self.ltime+self.pausetime;
}
else
{
self.th_die();
return;
}
}
else
{
if(self.pausetime)
{
self.think=chunk_death;
self.nextthink=self.ltime+self.pausetime;
}
else
{
chunk_death();
return;
}
}
}
else if(self.wait==-1)
self.nextthink=-1;
else if (self.wait)
{
self.nextthink = self.ltime + self.wait;
sound (self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise, 1, ATTN_LOOP);
}
else self.nextthink = self.ltime + 0.1;
}
if (self.decap == 0 || self.decap == 2)
self.effects (-) EF_BRIGHTLIGHT;
};
void() train_rotate =
{
local entity targ;
local vector dir;
targ = self.enemy;
if (targ.mangle_x != 0 || targ.mangle_y != 0 || targ.mangle_z != 0)
{
dir = self.angles;
dir += targ.mangle;
SUB_CalcAngleMove (dir, self.speed, train_wait);
}
else
train_wait();
};
void() train_next =
{
local entity targ;
local vector dir;
float targ_speed, targ_aspeed;
targ = find (world, targetname, self.target);
self.target = targ.target;
if (!self.decap && self.spawnflags & TRAIN_RETURN)
if (self.netname == targ.targetname)
self.decap = 2;
if (!self.target)
objerror ("train_next: no next target");
if (targ.wait)
self.wait = targ.wait;
else
self.wait = 0;
self.enemy = targ;
sound (self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise1, 1, ATTN_NORM);
if (targ.mangle_x != 0 || targ.mangle_y != 0 || targ.mangle_z != 0)
{
dir = self.angles;
dir += targ.mangle;
if(targ.speed)
targ_speed=targ.speed;
else
targ_speed = self.speed;
if(targ.anglespeed)
targ_aspeed=targ.anglespeed;
else
targ_aspeed = self.anglespeed;
if(targ.spawnflags&SYNCH)
SUB_CalcMoveAndAngleInit (targ.origin - self.mins, targ_speed, dir, targ_aspeed, train_wait,TRUE);
else
SUB_CalcMoveAndAngleInit (targ.origin - self.mins, targ_speed, dir, targ_aspeed, train_wait,FALSE);
}
else
SUB_CalcMove (targ.origin - self.mins, self.speed, train_rotate);
if (self.spawnflags & TRAIN_WAITTRIG)
self.decap = 2;
};
void() func_train_find =
{
local entity targ;
targ = find (world, targetname, self.target);
self.target = targ.target;
setorigin (self, targ.origin - self.mins);
if (!self.targetname)
{ // not triggered, so start immediately
self.decap = 1;
self.nextthink = self.ltime + 0.1;
self.think = train_next;
}
};
/*QUAKED func_train (0 .5 .8) ? GLOW TOGGLE RETURN TRANSLUCENT
Trains are moving platforms that players can ride.
The targets origin specifies the min point of the train at each corner.
The train spawns at the first target it is pointing at.
If the train is the target of a button or trigger, it will not begin moving until activated.
speed default 100
dmg default 2
soundtype
1) ratchet metal
if train is only moving to one spot
"angle" - to tell it's direction
"distance" - in pixels, how far to move
"speed" - how fast it moves between spots (default=100)
"anglespeed" - how fast it rotates to a new angle (default = 100)
"wait" - -1 will make it stop forever, -2 will make it blow up (you can put the waits on the pathcorners and it will take the wait from there.
"pausetime" - How long to wait after getting to the end of it's path before blowing up, default is 0
NOTE: If you give it a wait of -2, be sure to set the thingtype.
thingtype - type of chunks and sprites it will generate
0 - glass
1 - grey stone (default for trains)
2 - wood
3 - metal
4 - flesh
5 - fire
6 - clay
7 - leaves
8 - hay
9 - brown stone
10 - cloth
11 - wood & leaf
12 - wood & metal
13 - wood stone
14 - metal stone
15 - metal cloth
The train will modify it's angles by whatever angles it's next path point has, so if it heads towards a path corner with an angle of '0 90 0', the train will rotate '0 90 0' on it's way to the pathpoint. If you make the anglespeed the same as the angle, the turn should finish right as the train gets to the new spot.
NOTE: A path_corner using spawnflag "SYNCH" will make the train automatically calculate a new anglespeed based on the distance it's going and will finish the turn at the same time the move is done.
As usual, any rotating brush needs an origin brush.
"abslight" - to set the absolute light level
if TRAIN_GLOW is checked, changes to a light globe sprite and lights up an area
*/
void() func_train =
{
local entity targ;
if(world.spawnflags&MISSIONPACK)
{
func_train_mp();
return;
}
self.decap = 0;
if (self.spawnflags & TRAIN_GLOW) {
self.solid = SOLID_NOT;
setmodel (self, "models/s_light.spr");
}
else
{
self.solid = SOLID_BSP;
setmodel (self, self.model);
}
if (!self.speed) self.speed = 100;
if (!self.anglespeed) self.anglespeed = 100;
if (!self.target) objerror ("func_train without a target");
if (!self.dmg) self.dmg = 2;
if (self.soundtype == 1)
{
self.noise = ("plats/train2.wav");
precache_sound ("plats/train2.wav");
self.noise1 = ("plats/train1.wav");
precache_sound ("plats/train1.wav");
}
else
{
self.noise = self.noise1 = "misc/null.wav";
precache_sound ("misc/null.wav");
}
if(self.wait==-2)
{
if(!self.thingtype)
self.thingtype=1;
if(!self.th_die)
self.th_die=chunk_death;
}
self.cnt = 1;
self.movetype = MOVETYPE_PUSH;
self.blocked = train_blocked;
self.use = train_use;
self.classname = "train";
setsize (self, self.mins , self.maxs);
setorigin (self, self.origin);
targ = find(world, target, self.target);
self.netname = targ.target;
if (self.abslight)
self.drawflags(+)MLS_ABSLIGHT;
if (self.spawnflags & 8)
{
self.drawflags(+)DRF_TRANSLUCENT;
self.solid = SOLID_NOT;
}
// start trains on the second frame, to make sure their targets have had
// a chance to spawn
self.nextthink = self.ltime + 0.1;
self.think = func_train_find;
};
/*QUAK-ED misc_teleporttrain (0 .5 .8) (-8 -8 -8) (8 8 8)
This is used for the final bos
*/
/*
void() misc_teleporttrain =
{
if (!self.speed)
self.speed = 100;
if (!self.target)
objerror ("func_train without a target");
self.cnt = 1;
self.solid = SOLID_NOT;
self.movetype = MOVETYPE_PUSH;
self.blocked = train_blocked;
self.use = train_use;
self.avelocity = '100 200 300';
self.noise = ("misc/null.wav");
precache_sound ("misc/null.wav");
self.noise1 = ("misc/null.wav");
precache_sound ("misc/null.wav");
precache_model2 ("models/teleport.mdl");
setmodel (self, "models/teleport.mdl");
setsize (self, self.mins , self.maxs);
setorigin (self, self.origin);
// start trains on the second frame, to make sure their targets have had
// a chance to spawn
self.nextthink = self.ltime + 0.1;
self.think = func_train_find;
};
*/
void() newplat_hit_bottom =
{
sound (self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise1, 1, ATTN_NORM);
self.state = STATE_BOTTOM;
self.lifetime = time + self.wait;
if (((self.spawnflags & START_RTRN) && !(self.spawnflags & START_BOTTOM)) ||
(self.spawnflags & CONTINUE))
{
self.nextthink = self.ltime + self.wait;
self.think=newplat_go_up;
}
setorigin (self.enemy, self.origin);
};
void() newplat_hit_top =
{
sound (self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise1, 1, ATTN_NORM);
self.state = STATE_TOP;
self.lifetime = time + self.wait;
if (((self.spawnflags & START_RTRN) && (self.spawnflags & START_BOTTOM)) ||
(self.spawnflags & CONTINUE))
{
self.nextthink = self.ltime + self.wait;
self.think=newplat_go_down;
}
setorigin (self.enemy, self.origin);
};
void() newplat_trigger_use =
{
if (self.think)
return; // already activated
if ((self.state==STATE_MOVING) || (self.lifetime > time))
return;
if (self.state == STATE_BOTTOM)
newplat_go_up ();
else
newplat_go_down ();
};
void() newplat_calc_down =
{
self.state=STATE_MOVING;
SUB_CalcMove (self.pos2, self.speed, newplat_hit_bottom);
};
void() newplat_go_down =
{
sound (self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise, 1, ATTN_LOOP);
newplat_calc_down();
};
void() newplat_calc_up =
{
self.state=STATE_MOVING;
SUB_CalcMove (self.pos1, self.speed, newplat_hit_top);
};
void() newplat_go_up =
{
sound (self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise, 1, ATTN_LOOP);
newplat_calc_up();
};
void() newplat_crush =
{
T_Damage (other, self, self, 1);
if (self.velocity_z < 0)
newplat_calc_down ();
else if (self.velocity_z > 0)
newplat_calc_up();
else
objerror ("newplat_crush: bad self.state\n");
};
void() newplat_center_touch =
{
if ((other.classname != "player"&&other.movetype!=MOVETYPE_PUSHPULL) || (other.health <= 0))
return;
self = self.enemy;
if ((self.state==STATE_MOVING) || (self.lifetime > time))
return;
if (self.state == STATE_BOTTOM)
newplat_go_up ();
else
newplat_go_down ();
};
void() newplat_spawn_inside_trigger =
{
local entity trigger;
//middle trigger
trigger = spawn();
trigger.touch = newplat_center_touch;
trigger.movetype = MOVETYPE_PUSH;
trigger.solid = SOLID_TRIGGER;
trigger.enemy = self;
self.enemy = trigger;
setsize (trigger, self.mins,self.maxs);
};
/*QUAKED func_newplat (0 .5 .8) ? START_BOTTOM STRT_RTRN CONTINUE
speed default 150
If the "height" key is set, that will determine the amount the plat moves, instead of being implicitly determined by the model's height.
Set "soundtype" to one of the following:
1) base fast
2) chain slow
START_BOTTOM - where plat starts at
if checked plat starts at the bottom of it's movement
START_RTRN - if check will return plat to start position.
CONTINUE - plat will never stop moving
height - distance plat moves up or down
wait - amount of time plat waits before moving (default 3)
*/
void() func_newplat =
{
if (!self.t_length)
self.t_length = 80;
if (!self.t_width)
self.t_width = 10;
if (self.soundtype == 0)
self.soundtype = 2;
if (self.soundtype == 1)
{
precache_sound ("plats/pulyplt1.wav");
precache_sound ("plats/pulyplt2.wav");
self.noise = "plats/pulyplt1.wav";
self.noise1 = "plats/pulyplt2.wav";
}
if (self.soundtype == 2)
{
precache_sound ("plats/chainplt1.wav");
precache_sound ("plats/chainplt2.wav");
self.noise = "plats/chainplt1.wav";
self.noise1 = "plats/chainplt2.wav";
}
self.mangle = self.angles;
self.angles = '0 0 0';
self.classname = "newplat";
self.solid = SOLID_BSP;
self.movetype = MOVETYPE_PUSH;
setorigin (self, self.origin);
setmodel (self, self.model);
setsize (self, self.mins , self.maxs);
if (!self.speed)
self.speed = 150;
if (!self.wait)
self.wait = 3;
// pos1 is the top position, pos2 is the bottom
self.pos1 = self.origin;
self.pos2 = self.origin;
if (self.spawnflags & START_BOTTOM)
self.state=STATE_BOTTOM;
else
self.state=STATE_TOP;
if (self.state==STATE_BOTTOM)
{
self.pos1_z = self.origin_z + self.height;
self.pos2_z = self.origin_z;
}
else
{
self.pos1_z = self.origin_z;
self.pos2_z = self.origin_z - self.height;
}
self.use = newplat_trigger_use;
self.blocked = newplat_crush;
newplat_spawn_inside_trigger (); //set the "start moving" trigger
};
/*
===============================================================================
FUNC_CRUSHER
===============================================================================
*/
void() crusher_slide_next =
{
local vector vdestdelta;
local float len, tspeed;
tspeed = self.speed;
if (!tspeed) objerror("No speed 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 = crusher_hit_bottom;
else if (self.state == STATE_UP) self.think = crusher_hit_top;
self.nextthink = self.ltime + 0.1;
return;
}
//Set destdelta to the vector needed to move
vdestdelta = self.finaldest - self.origin;
//Get the length of the vector
len = vlen (vdestdelta);
//If the length is this small, just stop
if (len < 1)
{
if (self.state == STATE_DOWN) crusher_hit_bottom();
else if (self.state == STATE_UP) crusher_hit_top();
else dprint("NO STATE\n");
return;
}
self.nextthink = self.ltime + 0.1;
self.think = crusher_slide_next;
self.velocity = vdestdelta * ((len / (len / 3)) / (self.speed / 100));
};
void(vector tdest) crusher_slide =
{
self.finaldest = tdest;
crusher_slide_next();
};
void() crusher_hit_top =
{
sound (self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise1, 1, ATTN_NORM);
self.state = STATE_TOP;
if (self.spawnflags & CRUSH_MULT)
return;
if (!self.level)
{
self.think = crusher_go_down;
self.nextthink = self.ltime + 1;
}
else
self.nextthink = -1;
};
void() crusher_hit_bottom =
{
sound (self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise1, 1, ATTN_NORM);
self.state = STATE_BOTTOM;
if (self.level && self.spawnflags & CRUSH_ENDPOS) return;
self.think = crusher_go_up;
self.nextthink = self.ltime + 1;
};
void() crusher_go_down =
{
sound (self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise, 1, ATTN_LOOP);
self.state = STATE_DOWN;
if (self.spawnflags & CRUSH_SLIDE) crusher_slide(self.pos2);
else SUB_CalcMove (self.pos2, self.speed, crusher_hit_bottom);
};
void() crusher_go_up =
{
sound (self, CHAN_UPDATE+PHS_OVERRIDE_R, self.noise, 1, ATTN_LOOP);
self.state = STATE_UP;
if (self.spawnflags & CRUSH_SLIDE) crusher_slide(self.pos1);
else SUB_CalcMove (self.pos1, self.speed, crusher_hit_top);
};
void() crusher_trigger_use =
{
if (!self.level)
self.level = TRUE;
else
self.level = FALSE;
//HCC doesn't like these together in one statement
if (self.think)
if (self.spawnflags & CRUSH_MULT)
return; // already activated
crusher_go_down();
};
void() crusher_crush =
{
//Crusher does not return like a plat, it just keeps on crushin'
T_Damage (other, self, self, self.dmg);
};
void() crusher_use =
{
if (!self.level)
self.level = TRUE;
else
self.level = FALSE;
crusher_go_down();
};
/*QUAKED func_crusher (0 .5 .8) ? multiple slide start_open end_open
speed default 150
dmg default 10
If not targetname is given, crushers will start working immediatly
Crushers are always drawn in the extended position, so they will light correctly.
start_open = start in open position
multiple = go once, return, and wait to be triggered again
slide = slide move (like doors)
end_open = stop in the position opposite what they were drawn in
"lip" same as doors
"speed" speed of the crusher
"wait pause until going in the other direction
"targetname" if set, no trigger is needed (use with multiple)
"dmg" damage the crusher does to a victim
Set "soundtype" to one of the following:
1) base fast
2) chain slow
3) Guillotine
*/
void() func_crusher =
{
SetMovedir();
if (self.soundtype == 0) self.soundtype = 2;
if (self.soundtype == 1)
{
precache_sound ("plats/plat1.wav");
precache_sound ("plats/plat2.wav");
self.noise = "plats/plat1.wav";
self.noise1 = "plats/plat2.wav";
}
else if (self.soundtype == 2)
{
precache_sound ("plats/medplat1.wav");
precache_sound ("plats/medplat2.wav");
self.noise = "plats/medplat1.wav";
self.noise1 = "plats/medplat2.wav";
}
else if (self.soundtype == 3)
{
precache_sound3 ("plats/guiltin1.wav");
precache_sound3 ("plats/guiltin2.wav");
self.noise = "plats/guiltin1.wav";
self.noise1 = "plats/guiltin2.wav";
}
self.mangle = self.angles;
self.angles = '0 0 0';
self.classname = "crusher";
self.solid = SOLID_BSP;
self.movetype = MOVETYPE_PUSH;
setorigin (self, self.origin);
setmodel (self, self.model);
setsize (self, self.mins , self.maxs);
self.level = TRUE;
if (!self.dmg) self.dmg = 10;
self.blocked = crusher_crush;
if (!self.speed) self.speed = 150;
self.pos1 = self.origin;
self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
if (self.spawnflags & CRUSH_START_OPEN)
{
setorigin (self, self.pos2);
self.pos2 = self.pos1;
self.pos1 = self.origin;
}
self.use = crusher_trigger_use;
if (self.targetname)
{
self.state = STATE_UP;
self.use = crusher_use;
}
else
{
setorigin (self, self.pos2);
self.state = STATE_BOTTOM;
self.nextthink = self.ltime + 0.1;
self.think = crusher_use;
}
};
void reset_solid (void)
{
}
void rot_mov_dmg (void)
{
if(other==world)
return;
if(other.classname=="player")
{
self.solid=SOLID_TRIGGER;
self.think=reset_solid;
thinktime self : 0.1;
}
if(other.takedamage)
{
if(self.noise1)
sound(self,CHAN_UPDATE+PHS_OVERRIDE_R,self.noise1,1,ATTN_NORM);
self.pain_finished=time+self.wait;
T_Damage(other,self,self.owner,self.dmg);
}
}
void rot_mov_snd (void)
{
if(self.pain_finished<=time)
{
sound(self,CHAN_UPDATE+PHS_OVERRIDE_R,self.noise,1,ATTN_LOOP);
self.pain_finished=time+self.wait;
}
self.think=rot_mov_snd;
thinktime self : self.wait;
}
void rot_mov_activate (void)
{
if(self.dmg)
self.touch=rot_mov_dmg;
if(!self.avelocity)
self.avelocity=self.o_angle;
if(self.noise)
if(!self.wait)
objerror ("func_rotating_movechain: sound, but no delay time");
else
{
self.think=rot_mov_snd;
thinktime self : 0;
}
}
float NOANGLECHAIN = 1;
/*QUAKED func_rotating_movechain (0 .5 .8) ? NOANGLECHAIN
Only one other object in the world should have the same netname. If not, it will find and use only the first one it finds!
If you're making multiple sawblades, for instance, label the mover and the rotater "sawblade1" for the first one, "sawblade2" for the second, and so on.
If you give it a targetname, it will wait to be activated, this can be seperate from the object it's attached to.
It will not do damage until it's been activated.
NOANGLECHAIN = Setting this flag will stop it from modifying it's angles by the owner's angles, but will still movechain.
dmg = How much damage it should do when it touches.
noise = Noise it should make, if any, be sure to set the wait time
noise1 = noise it should make when it hits something
wait = Length of the sound so it knows when to loop it.
avelocity = The direction and speed it should spin: pitch yaw roll (this is relative to it's own axis, not the world)
netname = the name of the object it's linked to, that object must have a matching netname!!!
Needs something to tell it to stop?
A way to make it die at the end of a path or if triggered again?
Maybe make movechain_angle optional spawnflag?
What do YOU think? We'd like to know...
*/
void func_rotating_movechain (void)
{
if(!self.netname)
objerror ("func_rotating_movechain has no netname");
self.owner=find(world,netname,self.netname);
if(self.owner.classname=="world")
objerror ("func_rotating_movechain has no owner!");
self.solid=SOLID_TRIGGER;
self.movetype=MOVETYPE_NOCLIP;
setmodel(self,self.model);
setsize(self,self.mins,self.maxs);
setorigin(self,self.origin);
//dprint(vtos(self.avelocity));
self.owner.movechain=self;
if(!self.spawnflags&NOANGLECHAIN)
self.flags+=FL_MOVECHAIN_ANGLE;
if(self.targetname)
{
self.use=rot_mov_activate;
self.o_angle=self.avelocity;
self.avelocity='0 0 0';
}
else
{
self.think=rot_mov_activate;
thinktime self : 3;//wait a few secs for board to start
}
}