quakec/source/server/entities/func.qc

452 lines
8.3 KiB
C++

/*
server/entities/func.qc
Source file for func_* map entities.
Copyright (C) 2021-2024 NZ:P Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
// =================================
// func_illusionary
// =================================
void() func_illusionary =
{
self.angles = '0 0 0';
self.movetype = MOVETYPE_NONE;
self.solid = SOLID_NOT;
setmodel (self, self.model);
#ifdef FTE
HalfLife_DoRender();
#endif // FTE
};
// =================================
// func_detail
// =================================
void() func_detail =
{
self.angles = '0 0 0';
self.movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything
self.solid = SOLID_BSP;
setmodel (self, self.model);
#ifdef FTE
HalfLife_DoRender();
#endif // FTE
};
void() func_wall = { func_detail(); }
// =================================
// func_rotating
// =================================
.vector spawnorigin;
void () hl_rotating =
{
float mvspd, realmvspd;
mvspd = 180;
if (!self.speed)
realmvspd = 300;
else
realmvspd = self.speed;
if (self.spawnflags & 2)
mvspd *= -1;
if (self.spawnflags & 4)
self.pos1_z += mvspd;
else if (self.spawnflags & 8)
self.pos1_x += mvspd;
else {
self.pos1_y += mvspd;
}
SUB_CalcAngleMove (self.pos1, realmvspd, hl_rotating);
};
void () func_rotating_use =
{
if (self.electro_targeted == 0) {
hl_rotating();
self.electro_targeted = 1;
} else {
self.avelocity = '0 0 0';
self.think = SUB_Null;
self.electro_targeted = 0;
}
};
void() rot_crush =
{
DamageHandler(self, other, 1, DMG_TYPE_OTHER);
}
void () func_rotating =
{
self.angles = '0 0 0';
self.movetype = MOVETYPE_PUSH;
#ifdef FTE
HalfLife_DoRender();
#endif // FTE
self.blocked = rot_crush;
if (self.spawnflags & 64)
self.solid = 0;
else
self.solid = 4;
setmodel(self, self.model);
if (self.spawnflags & 1) { // start on
func_rotating_use();
}
self.use = func_rotating_use;
if (self.spawnorigin != '0 0 0')
setorigin(self, self.spawnorigin);
};
// =================================
// func_train
// =================================
void() train_next;
void() func_train_find;
void() train_blocked =
{
if (time < self.death_timer)
return;
self.death_timer = time + 0.5;
DamageHandler(self, other, self.dmg, DMG_TYPE_OTHER);
};
void() train_use =
{
if (self.think != func_train_find)
return; // already activated
train_next();
};
void() train_wait =
{
if (self.wait) {
self.nextthink = self.ltime + self.wait;
sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
} else {
self.nextthink = self.ltime + 0.1;
}
self.think = train_next;
};
void() train_next =
{
entity targ;
// motolegacy -- looks like id liked reusing ent fields too!
if (self.electro_targeted == 1) {
train_wait();
return;
}
targ = find (world, targetname, self.target);
self.target = targ.target;
if (!self.target) {
objerror ("train_next: no next target");
}
if (targ.wait)
self.wait = targ.wait;
else
self.wait = 0;
sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM);
SUB_CalcMove (targ.origin - self.mins, self.speed, train_wait);
};
void() func_train_find =
{
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.nextthink = self.ltime + 0.1;
self.think = train_next;
}
};
void() func_train =
{
if (!self.speed)
self.speed = 100;
if (!self.target) {
objerror ("func_train without a target");
}
if (!self.dmg)
self.dmg = 2;
if (self.sounds == 0) {
self.noise = ("sounds/null.wav");
precache_sound ("sounds/null.wav");
self.noise1 = ("sounds/null.wav");
precache_sound ("sounds/null.wav");
}
if (self.sounds == 1) {
self.noise = ("sounds/misc/debris.wav");
precache_sound ("sounds/misc/debris.wav");
self.noise1 = ("sounds/misc/debris.wav");
precache_sound ("sounds/misc/debris.wav");
}
self.cnt = 1;
self.solid = SOLID_BSP;
self.movetype = MOVETYPE_PUSH;
self.blocked = train_blocked;
self.use = train_use;
self.classname = "train";
setmodel (self, self.model);
setsize (self, self.mins , self.maxs);
setorigin (self, self.origin);
#ifdef FTE
HalfLife_DoRender();
#endif // FTE
// 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;
};
// =================================
// func_button
// =================================
void() button_wait;
void() button_return;
void() button_wait =
{
self.state = STATE_TOP;
self.nextthink = self.ltime + self.wait;
self.think = button_return;
activator = self.enemy;
SUB_UseTargets();
self.frame = 1; // use alternate textures
};
void() button_done =
{
self.state = STATE_BOTTOM;
};
void() button_return =
{
self.state = STATE_DOWN;
SUB_CalcMove (self.pos1, self.speed, button_done);
self.frame = 0; // use normal textures
if (self.health)
self.takedamage = DAMAGE_YES; // can be shot again
};
void() button_blocked =
{ // do nothing, just don't ome all the way back out
};
void() button_fire =
{
if (self.state == STATE_UP || self.state == STATE_TOP)
return;
sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM);
self.state = STATE_UP;
SUB_CalcMove (self.pos2, self.speed, button_wait);
};
void() button_use =
{
self.enemy = activator;
button_fire ();
};
void() button_touch =
{
if (other.classname != "player")
return;
if(self.cost) {
if(self.state == STATE_BOTTOM||self.state == STATE_DOWN) {
centerprint(other,"Press use to buy [cost:");
centerprint(other,ftos(self.cost));
centerprint(other,"]\n");
if (other.button7 && !(other.semi_actions & SEMIACTION_USE)) {
other.semi_actions |= SEMIACTION_USE;
if(other.points >= self.cost) {
self.enemy = other;
Player_RemoveScore(other, self.cost);
button_fire();
return;
} else {
centerprint(other, STR_NOTENOUGHPOINTS);
sound(other, 0, "sounds/misc/denybuy.wav", 1, 1);
}
}
}
} else {
self.enemy = other;
button_fire ();
}
};
void() button_killed =
{
self.health = self.max_health;
self.takedamage = DAMAGE_NO; // wil be reset upon return
button_fire ();
};
void() func_button =
{
SetMovedir ();
self.movetype = MOVETYPE_PUSH;
self.solid = SOLID_BSP;
setmodel (self, self.model);
self.blocked = button_blocked;
self.use = button_use;
if (self.health) {
self.max_health = self.health;
self.th_die = button_killed;
self.takedamage = DAMAGE_YES;
} else {
self.touch = button_touch;
}
if (!self.speed)
self.speed = 40;
if (!self.wait)
self.wait = 1;
if (!self.lip)
self.lip = 4;
self.state = STATE_BOTTOM;
self.pos1 = self.origin;
self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);
#ifdef FTE
HalfLife_DoRender();
#endif // FTE
};
// =================================
// func_ending
// =================================
void() touch_ending =
{
if (other.classname != "player" || self.activated)
return;
useprint(other, 20, self.cost, 0);
if (other.button7) {
if (other.points < self.cost)
return;
Player_RemoveScore(other, self.cost);
entity tempe;
entity players = find(world, classname, "player");
while(players != world) {
tempe = self;
self = players;
self.downed = true;
EndGameSetup();
self = tempe;
players = find(players, classname, "player");
}
self.activated = true;
}
}
void() func_ending =
{
precache_model (self.model);
self.movetype = MOVETYPE_NONE; // so it doesn't get pushed by anything
self.solid = SOLID_TRIGGER;
self.classname = "func_ending";
setmodel(self, self.model);
setsize(self, VEC_HULL2_MIN, VEC_HULL2_MAX);
self.touch = touch_ending;
};