func_train: implement most (if not all used) functionality. Needs more

testing, but on the maps on which it does work, it seems to work well.
Keep in mind that the player physics don't take ground entities into
account yet - so you'll still bounce off the platforms when they're moving
down, etc. That's a problem with the physics, but not the entity itself.
This commit is contained in:
Marco Cawthorne 2019-09-09 18:55:58 +02:00
parent e64bc2ca8e
commit a802c3bf3e
3 changed files with 187 additions and 62 deletions

View file

@ -16,6 +16,8 @@ server/env_render.cpp
server/env_shake.cpp
server/env_message.cpp
server/game_text.cpp
server/path_corner.cpp
server/path_track.cpp
server/func_recharge.cpp
server/func_healthcharger.cpp
server/func_breakable.cpp
@ -52,8 +54,6 @@ server/env_shooter.cpp
server/env_beverage.cpp
server/env_global.cpp
server/item_food.cpp
server/path_corner.cpp
server/path_track.cpp
server/multi_manager.cpp
server/monster_furniture.cpp
server/monster_generic.cpp

View file

@ -14,131 +14,216 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*QUAKED func_train (0 .5 .8) ?
/*QUAKED func_train (0 .5 .8) ? x x x TRAIN_NOTSOLID
"targetname" Name
"target" Target when triggered.
"target" First node.
"killtarget" Target to kill when triggered.
"dmg" Damage to inflict upon a person blocking the way.
"snd_move" Path to sound sample which plays when it's moving.
"snd_stop" Path to sound sample which plays when it stops moving.
Moving platform following along path_* entities.
Very unfinished.
Moving platform following along path_corner entities, aka nodes.
Most of its behaviour is controlled by the path_corner entities it passes over.
See the entity definition for path_corner to find out more.
Upon level entry, the func_train will spawn right where its first path_corner
node is. This is so you can light the func_train somewhere else - like a lonely
box somewhere outside the playable area.
*/
#define TRAIN_NOTSOLID 8
string g_strTrainMoveSnd[] = {
"common/null.wav",
"plats/bigmove1.wav",
"plats/bigmove2.wav",
"plats/elevmove1.wav",
"plats/elevmove2.wav",
"plats/elevmove3.wav",
"plats/freightmove1.wav",
"plats/freightmove2.wav",
"plats/heavymove1.wav",
"plats/rackmove1.wav",
"plats/railmove1.wav",
"plats/squeekmove1.wav",
"plats/talkmove1.wav",
"plats/talkmove2.wav"
};
string g_strTrainStopSnd[] = {
"common/null.wav",
"plats/bigstop1.wav",
"plats/bigstop2.wav",
"plats/freightstop1.wav",
"plats/heavystop2.wav",
"plats/rackstop1.wav",
"plats/railstop1.wav",
"plats/squeekstop1.wav",
"plats/talkstop1.wav"
};
class func_train:CBaseTrigger
{
float m_flWait;
float m_flSpeed;
float m_flDamage;
string m_strMoveSnd;
string m_strStopSnd;
void() func_train;
virtual void() Find;
virtual void() NextPath;
virtual void() GoToTarget;
virtual void() Trigger;
virtual void() Respawn;
virtual void() Blocked;
};
void func_train::GoToTarget(void)
void
func_train::Blocked(void)
{
Damage_Apply(other, this, m_flDamage, other.origin, TRUE);
}
void
func_train::GoToTarget(void)
{
entity eNode;
float flTravelTime;
vector vel_to_pos;
entity f;
vector vecVelocity;
vector vecWorldPos;
f = find(world, CBaseTrigger::m_strTargetName, m_strTarget);
eNode = find(world, CBaseTrigger::m_strTargetName, m_strTarget);
if (!f) {
print("^1func_train^7: Trigger-Target not found! Removing.\n");
if (!eNode) {
return;
}
vector vecWorldPos;
vecWorldPos[0] = absmin[0] + (0.5 * (absmax[0] - absmin[0]));
vecWorldPos[1] = absmin[1] + (0.5 * (absmax[1] - absmin[1]));
vecWorldPos[2] = absmin[2] + (0.5 * (absmax[2] - absmin[2]));
vel_to_pos = (f.origin - vecWorldPos);
flTravelTime = (vlen(vel_to_pos) / m_flSpeed);
vecVelocity = (eNode.origin - vecWorldPos);
flTravelTime = (vlen(vecVelocity) / m_flSpeed);
if (!flTravelTime) {
NextPath();
print(sprintf("TRAIN %s SPEED: %f\n", m_strTargetName, flTravelTime));
return;
}
velocity = (vel_to_pos * (1 / flTravelTime));
/* more stuff for the ears */
if (m_strMoveSnd) {
sound(this, CHAN_VOICE, m_strMoveSnd, 1.0, ATTN_NORM);
}
velocity = (vecVelocity * (1 / flTravelTime));
think = NextPath;
nextthink = (ltime + flTravelTime);
}
void func_train::NextPath(void)
void
func_train::NextPath(void)
{
CBaseTrigger current_target;
path_corner eNode;
eNode = (path_corner)find(world, CBaseTrigger::m_strTargetName, m_strTarget);
print(sprintf("^2func_train^7: Talking to current target %s... ", m_strTarget));
current_target = (CBaseTrigger)find(world, CBaseTrigger::m_strTargetName, m_strTarget);
if (!current_target) {
print("^1FAILED.\n");
} else {
print("^2SUCCESS.\n");
if (!eNode) {
return;
}
m_strTarget = current_target.m_strTarget;
/* fire the path_corners' target */
if (eNode.m_strMessage) {
eNode.Trigger();
}
/* stuff for the ears */
if (m_strStopSnd) {
sound(this, CHAN_BODY, m_strStopSnd, 1.0, ATTN_NORM);
}
/* make the loopy noise stop */
if (m_strMoveSnd) {
sound(this, CHAN_VOICE, "common/null.wav", 0.0, ATTN_NORM);
}
setorigin(this, eNode.origin - (mins + maxs) * 0.5);
m_flSpeed = eNode.m_flSpeed;
m_flWait = eNode.m_flWait;
m_strTarget = eNode.m_strTarget;
velocity = [0,0,0];
if (m_strTarget) {
/* warp */
if (eNode.spawnflags & PC_TELEPORT) {
NextPath();
return;
}
/* stop until triggered again */
if (eNode.spawnflags & PC_WAIT) {
return;
}
if (m_flWait > 0) {
think = GoToTarget;
nextthink = ltime + m_flWait;
} else {
GoToTarget();
}
}
void func_train::Trigger(void)
void
func_train::Trigger(void)
{
GoToTarget();
}
void func_train::Find(void)
void
func_train::Respawn(void)
{
entity f = find(world, CBaseTrigger::m_strTargetName, m_strTarget);
if (!f) {
print(sprintf("^1func_train^7: End-Target %s not found! Removing.\n",m_strTarget));
remove(this);
return;
}
print("^2func_train^7: Successfully found first target.\n");
vector vecWorldPos;
vecWorldPos[0] = absmin[0] + (0.5 * (absmax[0] - absmin[0]));
vecWorldPos[1] = absmin[1] + (0.5 * (absmax[1] - absmin[1]));
vecWorldPos[2] = absmin[2] + (0.5 * (absmax[2] - absmin[2]));
vecWorldPos = f.origin - vecWorldPos;
setorigin(this, vecWorldPos);
}
void func_train::Respawn(void)
{
solid = SOLID_BSP;
solid = spawnflags & TRAIN_NOTSOLID ? SOLID_NOT : SOLID_BSP;
movetype = MOVETYPE_PUSH;
//blocked = Blocked;
blocked = Blocked;
setmodel(this, m_oldModel);
setorigin(this, m_oldOrigin);
/* Make sure we got some time for the paths to spawn */
nextthink = ltime + 0.1f;
think = Find;
/* let's wait 1/4 a second to give the path_corner entities a chance to
* spawn in case they're after us in the ent lump */
think = NextPath;
nextthink = ltime + 0.25f;
}
void func_train::func_train(void)
void
func_train::func_train(void)
{
int a;
for (int i = 1; i < (tokenize(__fullspawndata) - 1); i += 2) {
switch (argv(i)) {
case "speed":
m_flSpeed = stof(argv(i+1));
case "dmg":
m_flDamage = stof(argv(i+1));
break;
case "movesnd":
a = bound(0, stof(argv(i+1)), g_strTrainMoveSnd.length);
m_strMoveSnd = g_strTrainMoveSnd[a];
break;
case "stopsnd":
a = bound(0, stof(argv(i+1)), g_strTrainStopSnd.length);
m_strStopSnd = g_strTrainStopSnd[a];
break;
case "snd_move":
m_strMoveSnd = argv(i+1);
break;
case "snd_stop":
m_strStopSnd = argv(i+1);
break;
default:
break;
}
}
if (m_strMoveSnd) {
precache_sound(m_strMoveSnd);
}
if (m_strStopSnd) {
precache_sound(m_strStopSnd);
}
if (!m_flSpeed) {
m_flSpeed = 100;
}

View file

@ -22,17 +22,57 @@
STUB!
*/
enumflags {
PC_WAIT,
PC_TELEPORT,
PC_FIREONCE
};
class path_corner:CBaseTrigger
{
float m_flSpeed;
float m_flYawSpeed;
float m_flWait;
void() path_corner;
virtual void() Trigger;
};
void path_corner::Trigger(void)
{
for ( entity eFind = world; ( eFind = find( eFind, CBaseTrigger::m_strTargetName, m_strMessage));) {
CBaseTrigger trigger = (CBaseTrigger) eFind;
trigger.Trigger();
}
}
void path_corner::path_corner(void)
{
CBaseTrigger::CBaseTrigger();
m_flSpeed = 100;
m_flWait = 1.0f;
for ( int i = 1; i < ( tokenize( __fullspawndata ) - 1 ); i += 2 ) {
switch ( argv( i ) ) {
case "speed":
m_flSpeed = stof(argv( i + 1 ));
break;
case "yaw_speed":
m_flYawSpeed = stof(argv(i+1));
break;
case "wait":
m_flWait = stof(argv( i + 1 ));
break;
case "message":
m_strMessage = argv( i + 1);
break;
default:
break;
}
}
if (!m_flSpeed)
m_flSpeed = 100;
if (!m_flWait)
m_flWait = 1.0f;
}