thirtyflightsofloving/game/g_crane.c
Knightmare66 d16b46e3cf Added re-implementation of func_plat2 and func_door2 from rogue to default Lazarus DLL.
Overhauled child entity movement in default Lazarus DLL.
Added bbox versions of various triggers to default Lazarus DLL.
Added level.maptype field to default Lazarus DLL.
Added entity class IDs to default Lazarus DLL.
Incremented savegame version for default Lazarus DLL.
2020-10-27 02:00:05 -04:00

1762 lines
47 KiB
C

/*
===========================================================================
Copyright (C) 1997-2001 Id Software, Inc.
Copyright (C) 2000-2002 Mr. Hyde and Mad Dog
This file is part of Lazarus Quake 2 Mod source code.
Lazarus Quake 2 Mod source code 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.
Lazarus Quake 2 Mod source code 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 Lazarus Quake 2 Mod source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "g_local.h"
void box_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
// g_crane.c
//
// Utility functions for manipulating overhead crane
//
#define STEPSIZE 8
#define MAX_PICKUP_DISTANCE 64
#define BUFFER 1 // Bonk buffer
#define CABLE_SEGMENT 32.0
#define SPOT_SEGMENT 32.0
#define CARGO_BUFFER 0.125
qboolean box_movestep (edict_t *ent, vec3_t move, qboolean relink);
void Crane_Move_Begin (edict_t *);
void Moving_Speaker_Think(edict_t *speaker)
{
qboolean moved;
vec3_t offset;
edict_t *owner;
owner = speaker->owner;
if (!owner)
{
G_FreeEdict(speaker);
return;
}
if (!owner->inuse)
{
G_FreeEdict(speaker);
return;
}
if (speaker->spawnflags & 15)
{
if ((speaker->spawnflags & 8) && (
(!speaker->owner->groundentity) || // not on the ground
(!speaker->owner->activator) || // not "activated" by anything
(!speaker->owner->activator->client) ) // not activated by client
) {
moved = false;
}
else
{
moved = false;
VectorSubtract(speaker->s.origin,owner->s.origin,offset);
if ((speaker->spawnflags & 1) && (fabs(offset[0] - speaker->offset[0]) > 0.125) )
moved = true;
if ((speaker->spawnflags & 2) && (fabs(offset[1] - speaker->offset[1]) > 0.125) )
moved = true;
if ((speaker->spawnflags & 4) && (fabs(offset[2] - speaker->offset[2]) > 0.125) )
moved = true;
}
if (moved) {
speaker->s.sound = speaker->owner->noise_index;
#ifdef LOOP_SOUND_ATTENUATION
speaker->s.attenuation = speaker->attenuation;
#endif
}
else
speaker->s.sound = 0;
}
else {
speaker->s.sound = speaker->owner->noise_index;
#ifdef LOOP_SOUND_ATTENUATION
speaker->s.attenuation = speaker->attenuation;
#endif
}
VectorAdd(owner->s.origin,speaker->offset,speaker->s.origin);
speaker->nextthink = level.time + FRAMETIME;
gi.linkentity(speaker);
}
edict_t *CrateOnTop (edict_t *from, edict_t *ent)
{
float maxdist;
if (!from)
from = g_edicts;
else
from++;
for ( ; from < &g_edicts[globals.num_edicts] ; from++)
{
if (from == ent)
continue;
if (!from->inuse)
continue;
if (from->movetype != MOVETYPE_PUSHABLE)
continue;
if (from->absmin[0] >= ent->absmax[0]) continue;
if (from->absmax[0] <= ent->absmin[0]) continue;
if (from->absmin[1] >= ent->absmax[1]) continue;
if (from->absmax[1] <= ent->absmin[1]) continue;
maxdist = VectorLength(ent->velocity)*FRAMETIME + 2.0;
if (fabs(from->absmin[2] - ent->absmax[2]) > maxdist) continue;
return from;
}
return NULL;
}
void Cargo_Stop (edict_t *ent)
{
vec3_t v;
VectorClear (ent->velocity);
// Sanity check: force cargo to correct elevation
ent->s.origin[2] += ent->crane_hook->absmin[2] - CARGO_BUFFER - ent->absmax[2];
ent->think = NULL;
ent->nextthink = 0;
ent->gravity = 0.0;
VectorAdd(ent->absmax,ent->absmin,v);
VectorScale(v,0.5,v);
v[2] = ent->absmax[2];
gi.positioned_sound (v, ent, CHAN_VOICE, gi.soundindex("tank/thud.wav"), 1, 1, 0);
gi.linkentity(ent);
ent->crane_control->busy = false;
}
void cargo_blocked (edict_t *cargo, edict_t *obstacle )
{
vec3_t origin;
VectorAdd(obstacle->s.origin,obstacle->origin_offset,origin);
cargo->gravity = 1.0;
cargo->movetype = MOVETYPE_PUSHABLE;
cargo->velocity[2] = 0;
box_movestep (cargo, vec3_origin, true);
cargo->crane_control->busy = false;
cargo->crane_hook->crane_cargo = NULL;
cargo->blocked = NULL;
cargo->touch = box_touch;
cargo->nextthink = 0;
gi.linkentity(cargo);
}
void Cargo_Float_Up (edict_t *cargo)
{
cargo->velocity[2] += sv_gravity->value * FRAMETIME;
cargo->velocity[0] = cargo->velocity[1] = 0;
if (cargo->absmax[2] + cargo->velocity[2]*FRAMETIME >=
cargo->crane_hook->absmin[2]-CARGO_BUFFER)
{
cargo->attracted = false;
cargo->think = Cargo_Stop;
}
cargo->nextthink = level.time + FRAMETIME;
gi.linkentity(cargo);
}
void SetCableLength(edict_t *cable)
{
int frame;
float length;
length = cable->s.origin[2] - cable->crane_hook->absmax[2];
frame = (int)(length/CABLE_SEGMENT);
if ((frame+1)*CABLE_SEGMENT < length) frame++;
frame = max(0,min(frame,19));
cable->s.frame = frame;
}
void SetSpotlightLength(edict_t *hook)
{
trace_t tr;
vec3_t start, end;
start[0] = (hook->absmin[0] + hook->absmax[0])/2;
start[1] = (hook->absmin[1] + hook->absmax[1])/2;
start[2] = hook->absmin[2] + 1;
end[0] = start[0];
end[1] = start[1];
end[2] = start[2] - WORLD_SIZE; // was 8192
tr = gi.trace(start,NULL,NULL,end,hook,MASK_SOLID);
hook->crane_light->s.origin[2] = tr.endpos[2] + 1;
}
void Cable_Think(edict_t *cable)
{
SetCableLength(cable);
cable->nextthink = level.time + FRAMETIME;
gi.linkentity(cable);
}
void crane_light_off(edict_t *light)
{
light->svflags |= SVF_NOCLIENT;
}
void Crane_Move_Done (edict_t *ent)
{
if (!Q_stricmp(ent->classname,"crane_hook"))
{
edict_t *cable;
edict_t *light;
// Sanity checks - force hook to correct relative location from hoist...
ent->s.origin[0] = ent->crane_hoist->s.origin[0] + ent->offset[0];
ent->s.origin[1] = ent->crane_hoist->s.origin[1] + ent->offset[1];
// ... and force cargo to correct elevation ...
if (ent->crane_cargo)
{
ent->crane_cargo->s.origin[2] +=
ent->absmin[2] - CARGO_BUFFER - ent->crane_cargo->absmax[2];
gi.linkentity(ent->crane_cargo);
}
// ... and finally, stop cable and move to correct position
cable = ent->crane_cable;
VectorClear(cable->velocity);
cable->s.origin[0] = ent->s.origin[0] + cable->offset[0];
cable->s.origin[1] = ent->s.origin[1] + cable->offset[1];
SetCableLength(cable);
gi.linkentity(cable);
light = ent->crane_light;
if (light)
{
VectorClear(light->velocity);
light->think = crane_light_off;
light->nextthink = level.time + 1.0;
gi.linkentity(light);
}
}
// Lazarus: ACK! If crate is being carried, it's NOT a MOVETYPE_PUSHABLE!!!!
// if (ent->movetype == MOVETYPE_PUSHABLE)
if (!Q_stricmp(ent->classname,"func_pushable"))
{
edict_t *e;
ent->s.origin[2] += ent->crane_hook->absmin[2] - CARGO_BUFFER - ent->absmax[2];
// Check to see if any OTHER crates are stacked on this one. If so,
// adjust their position and velocity as well.
e = NULL;
while ((e = CrateOnTop(e, ent)) != NULL)
{
VectorClear(e->velocity);
e->s.origin[2] += ent->crane_hook->absmin[2] - e->absmin[2];
gi.linkentity(e);
}
}
VectorClear (ent->velocity);
ent->busy = false;
ent->think = NULL;
ent->nextthink = 0;
gi.linkentity(ent);
}
void Crane_Stop(edict_t *control)
{
if (control->crane_beam->crane_onboard_control)
Crane_Move_Done(control->crane_beam->crane_onboard_control);
Crane_Move_Done(control->crane_beam);
Crane_Move_Done(control->crane_hoist);
Crane_Move_Done(control->crane_hook);
if (control->crane_hook->crane_cargo) Crane_Move_Done(control->crane_hook->crane_cargo);
}
qboolean Crane_Hook_Bonk(edict_t *hook, int axis, int dir, vec3_t bonk)
{
float fraction, cargo_fraction;
int i1,i2;
edict_t *cargo;
vec3_t cargo_origin, cargo_bonk, origin, end, forward, start;
vec3_t mins, maxs;
trace_t tr;
VectorClear(end);
VectorClear(start);
VectorClear(forward);
forward[axis] = (float)dir;
switch (axis)
{
case 0:
// X
i1 = 1;
i2 = 2;
break;
case 1:
// Y
i1 = 0;
i2 = 2;
break;
default:
// Z
i1 = 0;
i2 = 1;
break;
}
cargo = hook->crane_cargo;
VectorAdd(hook->s.origin,hook->origin_offset,origin);
VectorCopy(origin,start);
if (dir > 0)
start[axis] = origin[axis] + hook->size[axis]/2;
else
start[axis] = origin[axis] - hook->size[axis]/2;
fraction = 1.0;
mins[axis] = 0;
mins[i1] = -hook->size[i1]/2;
mins[i2] = -hook->size[i2]/2;
maxs[axis] = 0;
maxs[i1] = hook->size[i1]/2;
maxs[i2] = hook->size[i2]/2;
VectorMA(start, WORLD_SIZE, forward, end); // was 8192
tr = gi.trace(start,mins,maxs,end,cargo,MASK_PLAYERSOLID);
if (tr.fraction < fraction && tr.ent != hook->crane_beam &&
tr.ent != hook->crane_hoist && tr.ent != cargo )
{
VectorCopy(tr.endpos,bonk);
bonk[axis] -= dir*BUFFER;
fraction = tr.fraction;
} else {
VectorCopy(end,bonk);
}
if (cargo)
{
VectorAdd(cargo->s.origin,cargo->origin_offset,cargo_origin);
VectorCopy(cargo_origin,start);
if (dir > 0)
start[axis] = cargo_origin[axis] + cargo->size[axis]/2;
else
start[axis] = cargo_origin[axis] - cargo->size[axis]/2;
cargo_fraction = 1.0;
mins[axis] = 0;
mins[i1] = -cargo->size[i1]/2+1;
mins[i2] = -cargo->size[i2]/2+1;
maxs[axis] = 0;
maxs[i1] = cargo->size[i1]/2-1;
maxs[i2] = cargo->size[i2]/2-1;
VectorMA(start, WORLD_SIZE, forward, end); // was 8192
tr=gi.trace(start, mins, maxs, end, cargo, MASK_PLAYERSOLID );
if (tr.fraction < cargo_fraction && tr.ent != hook->crane_beam &&
tr.ent != hook->crane_hoist && tr.ent != hook )
{
VectorCopy(tr.endpos,cargo_bonk);
cargo_bonk[axis] -= dir*BUFFER;
cargo_fraction = tr.fraction;
} else {
VectorCopy(end,cargo_bonk);
}
if (cargo_fraction < 1)
{
fraction = cargo_fraction;
if (dir > 0)
{
cargo_bonk[axis] += hook->absmax[axis] - cargo->absmax[axis];
bonk[axis] = min(bonk[axis],cargo_bonk[axis]);
}
else
{
cargo_bonk[axis] += hook->absmin[axis] - cargo->absmin[axis];
bonk[axis] = max(bonk[axis],cargo_bonk[axis]);
}
}
}
if (fraction < 1)
return true;
else
return false;
}
void Crane_blocked (edict_t *self, edict_t *other)
{
if ( (other->classname) && (other->movetype == MOVETYPE_PUSHABLE))
{
// treat func_pushable like a world brush - attempt to stop
// crane
// This *shouldn't* be necessary, but I'm a pessimist
Crane_Stop(self->crane_control);
return;
}
if (self->crane_control->crane_hook == other)
return;
if (!(other->svflags & SVF_MONSTER) && (!other->client) )
{
// give it a chance to go away on it's own terms (like gibs)
T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
// if it's still there, nuke it
if (other)
{
// Lazarus: Some of our ents don't have origin near the model
vec3_t save;
VectorCopy(other->s.origin,save);
VectorMA (other->absmin, 0.5, other->size, other->s.origin);
BecomeExplosion1 (other);
}
return;
}
if (level.time < self->touch_debounce_time)
return;
if (!self->dmg)
return;
self->touch_debounce_time = level.time + 0.5;
T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
}
void Crane_Move_Final (edict_t *ent)
{
float bonk_distance;
if (ent->crane_control->activator->client->use)
{
// Use key is still pressed
bonk_distance = ent->crane_control->crane_increment *
(ent->crane_bonk - ent->absmin[ent->crane_dir]);
if (ent->moveinfo.remaining_distance > 0)
bonk_distance -= ent->moveinfo.remaining_distance;
bonk_distance = min(bonk_distance,STEPSIZE);
if (bonk_distance > 0)
{
ent->moveinfo.remaining_distance += bonk_distance;
Crane_Move_Begin(ent);
return;
}
}
if (ent->moveinfo.remaining_distance == 0)
{
Crane_Move_Done (ent);
return;
}
VectorScale (ent->moveinfo.dir, ent->moveinfo.remaining_distance / FRAMETIME, ent->velocity);
if (!Q_stricmp(ent->classname,"crane_hook"))
{
VectorCopy(ent->velocity,ent->crane_cable->velocity);
ent->crane_cable->velocity[2] = 0;
gi.linkentity(ent);
if (ent->crane_light != NULL)
{
VectorCopy(ent->velocity,ent->crane_light->velocity);
ent->crane_light->velocity[2] = 0;
gi.linkentity(ent->crane_light);
}
}
ent->think = Crane_Move_Done;
ent->nextthink = level.time + FRAMETIME;
gi.linkentity(ent);
}
void Crane_Move_Begin (edict_t *ent)
{
float frames;
if ((ent->moveinfo.speed * FRAMETIME) >= ent->moveinfo.remaining_distance)
{
Crane_Move_Final (ent);
return;
}
VectorScale (ent->moveinfo.dir, ent->moveinfo.speed, ent->velocity);
frames = floor((ent->moveinfo.remaining_distance / ent->moveinfo.speed) / FRAMETIME);
ent->moveinfo.remaining_distance -= frames * ent->moveinfo.speed * FRAMETIME;
if (!Q_stricmp(ent->classname,"crane_hook"))
{
if ((ent->crane_light) && (ent->crane_cargo==NULL))
{
SetSpotlightLength(ent);
ent->crane_light->svflags &= ~SVF_NOCLIENT;
}
VectorCopy(ent->velocity,ent->crane_cable->velocity);
ent->crane_cable->velocity[2] = 0;
gi.linkentity(ent->crane_cable);
if (ent->crane_light != NULL)
{
VectorCopy(ent->velocity,ent->crane_light->velocity);
ent->crane_light->velocity[2] = 0;
gi.linkentity(ent->crane_light);
}
}
ent->nextthink = level.time + (frames * FRAMETIME);
ent->think = Crane_Move_Final;
ent->blocked = Crane_blocked;
gi.linkentity(ent);
}
void G_FindCraneParts()
{
vec3_t dist;
edict_t *cable;
edict_t *control;
edict_t *beam;
edict_t *hoist;
edict_t *hook;
edict_t *light;
edict_t *p1, *p2;
edict_t *e;
int direction;
int i;
for (i=1, e=g_edicts+i ; i < globals.num_edicts ; i++,e++)
{
if (!e->inuse)
continue;
if (!e->classname)
continue;
if (Q_stricmp(e->classname,"crane_control"))
continue;
control = e;
beam = G_Find(NULL,FOFS(targetname),control->target);
if (!beam)
{
gi.dprintf("Crane_control with no target\n");
G_FreeEdict(control);
return;
}
// get path_corner locations to determine movement direction
p1 = G_Find(NULL,FOFS(targetname),beam->pathtarget);
if (!p1->target)
{
gi.dprintf("Only 1 path_corner in pathtarget sequence for crane_beam\n"
"(2 are required)\n");
G_FreeEdict(control);
G_FreeEdict(beam);
G_FreeEdict(p1);
return;
}
p2 = G_Find(NULL,FOFS(targetname),p1->target);
VectorSubtract(p1->s.origin,p2->s.origin,dist);
if (fabs(dist[0]) > fabs(dist[1]))
{
VectorSet(beam->movedir, 1.0,0.0,0.0);
direction = 0;
if (p1->s.origin[0] < p2->s.origin[0])
{
VectorCopy(p1->s.origin,beam->pos1);
VectorCopy(p2->s.origin,beam->pos2);
}
else
{
VectorCopy(p2->s.origin,beam->pos1);
VectorCopy(p1->s.origin,beam->pos2);
}
}
else
{
VectorSet(beam->movedir, 0.0,1.0,0.0);
direction = 1;
if (p1->s.origin[1] < p2->s.origin[1])
{
VectorCopy(p1->s.origin,beam->pos1);
VectorCopy(p2->s.origin,beam->pos2);
}
else
{
VectorCopy(p2->s.origin,beam->pos1);
VectorCopy(p1->s.origin,beam->pos2);
}
}
hoist = G_Find(NULL,FOFS(targetname),beam->target);
if (!hoist)
{
gi.dprintf("Crane_beam with no target\n");
G_FreeEdict(control);
G_FreeEdict(beam);
return;
}
// get path_corner locations to determine movement direction
p1 = G_Find(NULL,FOFS(targetname),hoist->pathtarget);
if (!p1->target)
{
gi.dprintf("Only 1 path_corner in pathtarget sequence for crane_hoist\n"
"(2 are required)\n");
G_FreeEdict(control);
G_FreeEdict(beam);
G_FreeEdict(hoist);
G_FreeEdict(p1);
return;
}
p2 = G_Find(NULL,FOFS(targetname),p1->target);
VectorSubtract(p1->s.origin,p2->s.origin,dist);
if (fabs(dist[0]) > fabs(dist[1]))
{
VectorSet(hoist->movedir, 1.0,0.0,0.0);
if (p1->s.origin[0] < p2->s.origin[0])
{
VectorCopy(p1->s.origin,hoist->pos1);
VectorCopy(p2->s.origin,hoist->pos2);
}
else
{
VectorCopy(p2->s.origin,hoist->pos1);
VectorCopy(p1->s.origin,hoist->pos2);
}
}
else
{
VectorSet(hoist->movedir, 0.0,1.0,0.0);
if (p1->s.origin[1] < p2->s.origin[1])
{
VectorCopy(p1->s.origin,hoist->pos1);
VectorCopy(p2->s.origin,hoist->pos2);
}
else
{
VectorCopy(p2->s.origin,hoist->pos1);
VectorCopy(p1->s.origin,hoist->pos2);
}
}
// correct spawnflags for beam and hoist speakers
if (beam->speaker)
{
if (direction)
beam->speaker->spawnflags = 2;
else
beam->speaker->spawnflags = 1;
}
if (hoist->speaker)
{
if (direction)
hoist->speaker->spawnflags = 1;
else
hoist->speaker->spawnflags = 2;
}
hook = G_Find(NULL,FOFS(targetname),hoist->target);
if (!hook)
{
gi.dprintf("Crane hoist with no target\n");
G_FreeEdict(control);
G_FreeEdict(beam);
G_FreeEdict(hoist);
return;
}
// Turn on hook ambient sound if control is on
// We use a trick here... since hook by definition cannot
// be moving unless control is on, then with control off
// we set hook speaker spawnflag to require hook to be
// moving to play
if (hook->speaker)
hook->speaker->spawnflags = 1 - (control->spawnflags & 1);
// Get offset from hook origin to hoist origin, so we can
// correct timing problems
VectorSubtract(hook->s.origin,hoist->s.origin,hook->offset);
// get path_corner locations to determine movement direction
p1 = G_Find(NULL,FOFS(targetname),hook->pathtarget);
if (!p1->target)
{
gi.dprintf("Only 1 path_corner in pathtarget sequence for crane_hook\n"
"(2 are required)\n");
G_FreeEdict(control);
G_FreeEdict(beam);
G_FreeEdict(hoist);
G_FreeEdict(hook);
G_FreeEdict(p1);
return;
}
p2 = G_Find(NULL,FOFS(targetname),p1->target);
VectorSet(hook->movedir,0.0,0.0,1.0);
if (p1->s.origin[2] < p2->s.origin[2])
{
VectorCopy(p1->s.origin,hook->pos1);
VectorCopy(p2->s.origin,hook->pos2);
}
else
{
VectorCopy(p2->s.origin,hook->pos1);
VectorCopy(p1->s.origin,hook->pos2);
}
control->crane_control = control;
control->crane_beam = beam;
control->crane_hoist = hoist;
control->crane_hook = hook;
if (!beam->crane_control) beam->crane_control = control;
if (control->team)
{
beam->crane_control = control;
if (beam->flags & FL_TEAMSLAVE)
beam->crane_onboard_control = beam->teammaster;
else
beam->crane_onboard_control = beam->teamchain;
}
else
beam->crane_onboard_control = NULL;
beam->crane_hoist = hoist;
beam->crane_hook = hook;
hoist->crane_control = beam->crane_control;
hoist->crane_beam = beam;
hoist->crane_hook = hook;
hook->crane_control = beam->crane_control;
hook->crane_beam = beam;
hook->crane_hoist = hoist;
if (control->spawnflags & 4)
{
beam->dmg = 0;
hoist->dmg = 0;
hook->dmg = 0;
}
if (hook->crane_cable == NULL)
{
int frame;
float length;
cable = G_Spawn();
cable->classname = "crane_cable";
VectorAdd(hook->absmin,hook->absmax,cable->s.origin);
VectorScale(cable->s.origin,0.5,cable->s.origin);
VectorAdd(cable->s.origin,hook->move_origin,cable->s.origin);
VectorSubtract(cable->s.origin,hook->s.origin,cable->offset);
cable->s.origin[2] = hoist->absmax[2] - 2;
cable->model = "models/cable/tris.md2";
gi.setmodel(cable,cable->model);
cable->s.skinnum = 0;
#ifdef KMQUAKE2_ENGINE_MOD
cable->s.renderfx |= RF_NOSHADOW; // Knightmare- no shadow for crane cable
#endif
length = hoist->absmax[2]-1 - hook->absmax[2];
frame = (int)(length/CABLE_SEGMENT);
if ((frame+1)*CABLE_SEGMENT < length) frame++;
frame = max(0,min(frame,19));
cable->s.frame = frame;
cable->solid = SOLID_NOT;
cable->movetype = MOVETYPE_STOP;
VectorSet(cable->mins,-2,-2,length);
VectorSet(cable->maxs, 2, 2,0);
gi.linkentity(cable);
beam->crane_cable = cable;
hoist->crane_cable = cable;
hook->crane_cable = cable;
cable->crane_control = control;
cable->crane_beam = beam;
cable->crane_hoist = hoist;
cable->crane_hook = hook;
}
control->crane_cable = hook->crane_cable;
if ((hook->spawnflags & 1) && (hook->crane_light == NULL))
{
light = G_Spawn();
light->s.origin[0] = (hook->absmin[0] + hook->absmax[0])/2;
light->s.origin[1] = (hook->absmin[1] + hook->absmax[1])/2;
light->s.origin[2] = hook->absmin[2] + 8;
VectorSet(light->mins,-32,-32,-512);
VectorSet(light->maxs, 32, 32, 0);
light->solid = SOLID_NOT;
light->movetype = MOVETYPE_NOCLIP;
light->s.effects = EF_SPHERETRANS;
light->s.modelindex = gi.modelindex("sprites/point.sp2");
light->s.effects = EF_HYPERBLASTER;
light->svflags = SVF_NOCLIENT;
VectorSubtract(light->s.origin,hook->s.origin,light->offset);
gi.linkentity(light);
beam->crane_light = light;
hook->crane_light = light;
cable->crane_light = light;
}
control->crane_light = hook->crane_light;
// If control is NOT onboard, move beam speaker (if any) to end of
// beam closest to control
if (!beam->crane_onboard_control && beam->speaker) {
if (beam->movedir[0] > 0) {
if (control->absmin[1]+control->absmax[1] < beam->absmin[1]+beam->absmax[1])
beam->speaker->s.origin[1] = beam->absmin[1];
else
beam->speaker->s.origin[1] = beam->absmax[1];
} else {
if (control->absmin[0]+control->absmax[0] < beam->absmin[0]+beam->absmax[0])
beam->speaker->s.origin[0] = beam->absmin[0];
else
beam->speaker->s.origin[0] = beam->absmax[0];
}
beam->speaker->s.origin[2] = control->s.origin[2] + 32;
VectorSubtract(beam->speaker->s.origin,beam->s.origin,beam->speaker->offset);
}
}
}
void Crane_AdjustSpeed(edict_t *ent)
{
float frames;
// Adjust speed so that travel time is an integral multiple
// of FRAMETIME
if (ent->moveinfo.remaining_distance > 0)
{
ent->moveinfo.speed = ent->speed;
frames = floor((ent->moveinfo.remaining_distance / ent->moveinfo.speed) / FRAMETIME);
if (frames < 1) frames = 1;
ent->moveinfo.speed = ent->moveinfo.remaining_distance/(frames*FRAMETIME);
}
}
void crane_control_action(edict_t *control, edict_t *activator, vec3_t point)
{
float Z;
int dir;
int row, column;
int content;
edict_t *beam, *cable, *cargo, *hoist, *hook;
trace_t tr;
vec3_t center, v;
vec3_t end, forward, start, pt;
vec3_t bonk, mins, maxs;
if (!(control->spawnflags & 1))
{
if (control->message)
safe_centerprintf(activator,"%s\n",control->message);
else
safe_centerprintf(activator,"No power\n");
return;
}
if (control->busy) return;
// First make sure player (activator) is on the panel side of the
// control panel
// Also get center point of panel side
switch (control->style)
{
case 0:
if (activator->s.origin[0] > control->absmax[0]) return;
center[0] = control->absmin[0];
center[1] = (control->absmin[1] + control->absmax[1])/2;
center[2] = (control->absmin[2] + control->absmax[2])/2;
break;
case 1:
if (activator->s.origin[1] > control->absmax[1]) return;
center[0] = (control->absmin[0] + control->absmax[0])/2;
center[1] = control->absmin[1];
center[2] = (control->absmin[2] + control->absmax[2])/2;
break;
case 2:
if (activator->s.origin[0] < control->absmin[0]) return;
center[0] = control->absmax[0];
center[1] = (control->absmin[1] + control->absmax[1])/2;
center[2] = (control->absmin[2] + control->absmax[2])/2;
break;
case 3:
if (activator->s.origin[1] < control->absmin[1]) return;
center[0] = (control->absmin[0] + control->absmax[0])/2;
center[1] = control->absmax[1];
center[2] = (control->absmin[2] + control->absmax[2])/2;
break;
}
// now check distance from player to panel
VectorSubtract(activator->s.origin,center,v);
if (VectorLength(v) > 64) return;
beam = control->crane_beam;
cable = control->crane_cable;
hoist = control->crane_hoist;
hook = control->crane_hook;
cargo = hook->crane_cargo;
if (cargo) cargo->gravity = 0.0; // reset after making it float up,
// otherwise things get jammed up
control->activator = activator;
// if any part of crane is currently moving, do nothing.
if (VectorLength(control->velocity) > 0.) return;
if (VectorLength(beam->velocity) > 0.) return;
if (VectorLength(hoist->velocity) > 0.) return;
if (VectorLength(hook->velocity) > 0.) return;
// now find which row and column of buttons corresponds to "point"
row = (2*(point[2] - control->absmin[2]))/(control->absmax[2]-control->absmin[2]);
if (row < 0) row = 0;
if (row > 1) row = 1;
switch (control->style)
{
case 1:
column = (4*(point[0]-control->absmin[0]))/(control->absmax[0]-control->absmin[0]);
break;
case 2:
column = (4*(point[1]-control->absmin[1]))/(control->absmax[1]-control->absmin[1]);
break;
case 3:
column = (4*(point[0]-control->absmax[0]))/(control->absmin[0]-control->absmax[0]);
break;
default:
column = (4*(point[1]-control->absmax[1]))/(control->absmin[1]-control->absmax[1]);
break;
}
if (column < 0) column = 0;
if (column > 3) column = 3;
// adjust for controller facing beam movement direction
if ( beam->movedir[0] > 0 && (control->style == 0 || control->style == 2)) {
if (column == 0 || column == 1) {
column = 1-column;
row = 1-row;
}
}
if ( beam->movedir[1] > 0 && (control->style == 1 || control->style == 3)) {
if (column == 0 || column == 1) {
column = 1-column;
row = 1-row;
}
}
switch(column)
{
case 0:
//==================
// move hoist
//==================
if (row)
{
// hoist away
if (control->style == 0 || control->style == 1)
control->crane_increment = 1;
else
control->crane_increment = -1;
}
else
{
// hoist toward
if (control->style == 0 || control->style == 1)
control->crane_increment = -1;
else
control->crane_increment = 1;
}
if (hoist->movedir[0] > 0)
{
// hoist travels in X
dir = 0;
if (control->crane_increment > 0)
{
if (Crane_Hook_Bonk(hook,0,1,bonk))
{
bonk[0] += hoist->absmax[0] - hook->absmax[0];
hoist->crane_bonk = min(bonk[0],hoist->pos2[0]);
}
else
hoist->crane_bonk = hoist->pos2[0];
hoist->crane_bonk += hoist->absmin[0] - hoist->absmax[0];
}
else
{
if (Crane_Hook_Bonk(hook,0,-1,bonk))
{
bonk[0] += hoist->absmin[0] - hook->absmin[0];
hoist->crane_bonk = max(bonk[0],hoist->pos1[0]);
}
else
hoist->crane_bonk = hoist->pos1[0];
}
}
else
{
// travels in Y
dir = 1;
if (control->crane_increment > 0)
{
if (Crane_Hook_Bonk(hook,1,1,bonk))
{
bonk[1] += hoist->absmax[1] - hook->absmax[1];
hoist->crane_bonk = min(bonk[1],hoist->pos2[1]);
}
else
hoist->crane_bonk = hoist->pos2[1];
hoist->crane_bonk += hoist->absmin[1] - hoist->absmax[1];
}
else
{
if (Crane_Hook_Bonk(hook,1,-1,bonk))
{
bonk[1] += hoist->absmin[1] - hook->absmin[1];
hoist->crane_bonk = max(bonk[1],hoist->pos1[1]);
}
else
hoist->crane_bonk = hoist->pos1[1];
}
}
hoist->crane_dir = dir;
hoist->moveinfo.remaining_distance = control->crane_increment *
(hoist->crane_bonk - hoist->absmin[dir]);
if (hoist->moveinfo.remaining_distance <= 0) return;
hoist->moveinfo.remaining_distance = min(hoist->moveinfo.remaining_distance,STEPSIZE);
Crane_AdjustSpeed(hoist);
VectorSet(hoist->moveinfo.dir,
hoist->movedir[0]*control->crane_increment,
hoist->movedir[1]*control->crane_increment,
0);
hoist->crane_control = control;
hook->crane_dir = dir;
hook->crane_bonk = hoist->crane_bonk + hook->absmin[dir] -
hoist->absmin[dir];
hook->crane_control = control;
memcpy(&hook->moveinfo,&hoist->moveinfo,sizeof(moveinfo_t));
cable->crane_dir = dir;
cable->crane_bonk = hoist->crane_bonk + cable->absmin[dir] -
hoist->absmin[dir];
cable->crane_control = control;
memcpy(&cable->moveinfo,&hoist->moveinfo,sizeof(moveinfo_t));
if (cargo)
{
cargo->movetype = MOVETYPE_PUSH;
cargo->crane_dir = dir;
cargo->crane_bonk = hoist->crane_bonk + cargo->absmin[dir] -
hoist->absmin[dir];
cargo->crane_control = control;
memcpy(&cargo->moveinfo,&hoist->moveinfo,sizeof(moveinfo_t));
}
Crane_Move_Begin(hoist);
Crane_Move_Begin(hook);
if (cargo) Crane_Move_Begin(cargo);
break;
case 1:
//==================
// move beam
//==================
// first re-parent associated speaker, if any
if (beam->speaker && control == beam->crane_onboard_control)
{
beam->speaker->owner = control;
VectorAdd(control->absmin,control->absmax,beam->speaker->s.origin);
VectorScale(beam->speaker->s.origin,0.5,beam->speaker->s.origin);
VectorSubtract(beam->speaker->s.origin,control->s.origin,beam->speaker->offset);
control->noise_index = beam->noise_index;
}
if (row)
{
// left arrow
if (control->style == 0 || control->style == 3)
control->crane_increment = 1;
else
control->crane_increment = -1;
}
else
{
// right arrow
if (control->style == 0 || control->style == 3)
control->crane_increment = -1;
else
control->crane_increment = 1;
}
if (beam->movedir[0] > 0)
{
// travels in X
dir = 0;
if (control->crane_increment > 0)
{
if (Crane_Hook_Bonk(hook,0,1,bonk))
{
bonk[0] += beam->absmax[0] - hook->absmax[0];
beam->crane_bonk = min(bonk[0],beam->pos2[0]);
}
else
beam->crane_bonk = beam->pos2[0];
beam->crane_bonk += beam->absmin[0] - beam->absmax[0];
}
else
{
if (Crane_Hook_Bonk(hook,0,-1,bonk))
{
bonk[0] += beam->absmin[0] - hook->absmin[0];
beam->crane_bonk = max(bonk[0],beam->pos1[0]);
}
else
beam->crane_bonk = beam->pos1[0];
}
}
else
{
// travels in Y
dir = 1;
if (control->crane_increment > 0)
{
if (Crane_Hook_Bonk(hook,1,1,bonk))
{
bonk[1] += beam->absmax[1] - hook->absmax[1];
beam->crane_bonk = min(bonk[1],beam->pos2[1]);
}
else
beam->crane_bonk = beam->pos2[1];
beam->crane_bonk += beam->absmin[1] - beam->absmax[1];
}
else
{
if (Crane_Hook_Bonk(hook,1,-1,bonk))
{
bonk[1] += beam->absmin[1] - hook->absmin[1];
beam->crane_bonk = max(bonk[1],beam->pos1[1]);
}
else
beam->crane_bonk = beam->pos1[1];
}
}
beam->crane_dir = dir;
beam->moveinfo.remaining_distance = control->crane_increment *
(beam->crane_bonk - beam->absmin[dir]);
// gi.dprintf("remaining distance = %g\n",beam->moveinfo.remaining_distance);
if (beam->moveinfo.remaining_distance <= 0) return;
beam->moveinfo.remaining_distance = min(beam->moveinfo.remaining_distance,STEPSIZE);
Crane_AdjustSpeed(beam);
VectorSet(beam->moveinfo.dir,
beam->movedir[0]*control->crane_increment,
beam->movedir[1]*control->crane_increment,
0);
beam->crane_control = control;
hoist->crane_dir = dir;
hoist->crane_bonk = beam->crane_bonk + hoist->absmin[dir] - beam->absmin[dir];
hoist->crane_control = control;
memcpy(&hoist->moveinfo,&beam->moveinfo,sizeof(moveinfo_t));
hook->crane_dir = dir;
hook->crane_bonk = beam->crane_bonk + hook->absmin[dir] - beam->absmin[dir];
hook->crane_control = control;
memcpy(&hook->moveinfo,&beam->moveinfo,sizeof(moveinfo_t));
cable->crane_dir = dir;
cable->crane_bonk = beam->crane_bonk + cable->absmin[dir] -
beam->absmin[dir];
cable->crane_control = control;
memcpy(&cable->moveinfo,&beam->moveinfo,sizeof(moveinfo_t));
if (beam->crane_onboard_control)
{
beam->crane_onboard_control->crane_dir = dir;
beam->crane_onboard_control->crane_bonk = beam->crane_bonk +
beam->crane_onboard_control->absmin[dir] -
beam->absmin[dir];
beam->crane_onboard_control->crane_control = control;
memcpy(&beam->crane_onboard_control->moveinfo,&beam->moveinfo,sizeof(moveinfo_t));
}
if (cargo)
{
cargo->movetype = MOVETYPE_PUSH;
cargo->crane_dir = dir;
cargo->crane_bonk = beam->crane_bonk + cargo->absmin[dir] - beam->absmin[dir];
cargo->crane_control = control;
memcpy(&cargo->moveinfo,&beam->moveinfo,sizeof(moveinfo_t));
}
Crane_Move_Begin(beam);
Crane_Move_Begin(hoist);
Crane_Move_Begin(hook);
if (beam->crane_onboard_control)
Crane_Move_Begin(beam->crane_onboard_control);
if (cargo) Crane_Move_Begin(cargo);
break;
case 2:
//==================
// hook up/down
//==================
hook->crane_dir = dir = 2;
if (row)
{
// hook up
control->crane_increment = 1;
if (Crane_Hook_Bonk(hook,2,1,bonk))
hook->crane_bonk = min(bonk[2],hook->pos2[2]);
else
hook->crane_bonk = hook->pos2[2];
hook->crane_bonk += hook->absmin[2] - hook->absmax[2];
}
else
{
// hook down
if (cargo)
{
pt[0] = (cargo->absmin[0] + cargo->absmax[0])/2;
pt[1] = (cargo->absmin[1] + cargo->absmax[1])/2;
pt[2] = cargo->absmin[2] - 0.125;
content = gi.pointcontents(pt);
if (content & MASK_SOLID)
{
BeepBeep(activator);
return;
}
}
control->crane_increment = -1;
if (Crane_Hook_Bonk(hook,2,-1,bonk))
hook->crane_bonk = max(bonk[2],hook->pos1[2]);
else
hook->crane_bonk = hook->pos1[2];
}
hook->moveinfo.remaining_distance = control->crane_increment *
(hook->crane_bonk - hook->absmin[hook->crane_dir]);
if (hook->moveinfo.remaining_distance <= 0)
{
BeepBeep(activator);
return;
}
hook->moveinfo.remaining_distance = min(hook->moveinfo.remaining_distance,STEPSIZE);
Crane_AdjustSpeed(hook);
VectorSet(hook->moveinfo.dir,0.,0.,(float)(control->crane_increment));
hook->crane_control = control;
if (cargo)
{
cargo->movetype = MOVETYPE_PUSH;
cargo->crane_dir = dir;
cargo->crane_bonk = hook->crane_bonk + cargo->absmin[dir] - hook->absmin[dir];
cargo->crane_control = control;
VectorSubtract(cargo->s.origin,hook->s.origin,cargo->offset);
memcpy(&cargo->moveinfo,&hook->moveinfo,sizeof(moveinfo_t));
}
cable->think = Cable_Think;
cable->nextthink = level.time + FRAMETIME;
Crane_Move_Begin(hook);
if (cargo) Crane_Move_Begin(cargo);
break;
case 3:
//==================
// hook/unhook
//==================
if (row)
{
// pickup cargo
if (hook->crane_cargo)
{
// already carrying something
BeepBeep(activator);
return;
}
VectorAdd(hook->absmin,hook->absmax,start);
VectorScale(start,0.5,start);
VectorSet(forward,0.,0.,-1.);
VectorMA(start, WORLD_SIZE, forward, end); // was 8192
VectorSubtract(hook->absmin,start,mins);
VectorSubtract(hook->absmax,start,maxs);
// 06/03/00 change: Use 1/3 the bounding box to force a better hit
VectorScale(mins,0.3333,mins);
VectorScale(maxs,0.3333,maxs);
// end 06/03/00 change
tr=gi.trace(start, mins, maxs, end, hook, MASK_SOLID);
if ((tr.fraction < 1) && (tr.ent) && (tr.ent->classname) &&
(tr.ent->movetype == MOVETYPE_PUSHABLE) )
{
Z = hook->absmin[2] - tr.ent->absmax[2];
if (Z > MAX_PICKUP_DISTANCE)
{
safe_centerprintf(activator,"Too far\n");
return;
}
if (CrateOnTop(NULL,tr.ent))
{
BeepBeep(activator);
gi.dprintf("Too heavy\n");
return;
}
// run a trace from top of cargo up... if first entity hit is NOT
// the hook, we can't get there from here.
if ( Z > 0 )
{
trace_t tr2;
VectorMA(tr.ent->mins,0.5,tr.ent->size,start);
start[2] = tr.ent->maxs[2];
VectorCopy(tr.ent->size,mins);
VectorScale(mins,-0.5,mins);
VectorCopy(tr.ent->size,maxs);
VectorScale(maxs,0.5,maxs);
mins[2] = maxs[2] = 0;
mins[0] += 1; mins[1] += 1; maxs[0] -= 1; maxs[1] -= 1;
VectorCopy(start,end);
end[2] += Z + 1;
tr2=gi.trace(start, mins, maxs, end, hook, MASK_SOLID);
if ((tr2.fraction < 1) && tr2.ent && (tr2.ent != hook))
{
safe_centerprintf(activator,"Blocked!\n");
return;
}
}
Z -= CARGO_BUFFER; // leave a buffer between hook and cargo
hook->crane_cargo = cargo = tr.ent;
cargo->groundentity = NULL;
cargo->crane_control = control;
cargo->crane_hook = hook;
cargo->movetype = MOVETYPE_PUSH;
cargo->touch = NULL;
// Make cargo float up to the hook
if (Z > 0)
{
control->busy = true;
cargo->attracted = true;
cargo->gravity = 0.0;
cargo->velocity[2] = 0.0;
cargo->think = Cargo_Float_Up;
cargo->blocked = cargo_blocked;
cargo->goal_frame = level.framenum;
cargo->nextthink = level.time + FRAMETIME;
gi.linkentity(cargo);
}
else
{
gi.positioned_sound (start, cargo, CHAN_VOICE,
gi.soundindex("tank/thud.wav"), 1, 1, 0);
}
}
else
BeepBeep(activator);
}
else
{
// drop cargo
if (hook->crane_cargo)
{
hook->crane_cargo->gravity = 1.0;
hook->crane_cargo->movetype = MOVETYPE_PUSHABLE;
hook->crane_cargo->touch = box_touch;
hook->crane_cargo->crane_control = NULL;
gi.linkentity(hook->crane_cargo);
box_movestep (hook->crane_cargo, vec3_origin, true);
hook->crane_cargo = NULL;
}
else
BeepBeep(activator);
}
}
}
void Use_Crane_Control (edict_t *ent, edict_t *other, edict_t *activator)
{
ent->spawnflags ^= 1;
if (ent->crane_hook->speaker)
ent->crane_hook->speaker->spawnflags = 1 - (ent->spawnflags & 1);
}
void SP_crane_control (edict_t *self)
{
if (!self->target)
{
gi.dprintf ("crane_control with no target at %s\n", vtos(self->s.origin));
G_FreeEdict (self);
return;
}
self->class_id = ENTITY_CRANE_CONTROL;
self->classname = "crane_control";
self->solid = SOLID_BSP;
self->movetype = MOVETYPE_PUSH;
gi.setmodel (self, self->model);
self->use = Use_Crane_Control;
gi.linkentity (self);
}
void SP_crane_hook (edict_t *self)
{
vec3_t origin;
edict_t *speaker;
self->class_id = ENTITY_CRANE_HOOK;
gi.setmodel (self, self->model);
VectorAdd(self->absmin,self->absmax,origin);
VectorScale(origin,0.5,origin);
if (!self->targetname)
{
gi.dprintf ("crane_hook with no targetname at %s\n", vtos(origin));
G_FreeEdict (self);
return;
}
self->classname = "crane_hook";
self->solid = SOLID_BSP;
self->movetype = MOVETYPE_PUSH;
if (st.noise)
self->noise_index = gi.soundindex(st.noise);
else
self->noise_index = 0;
#ifdef LOOP_SOUND_ATTENUATION
if (self->attenuation <= 0)
self->attenuation = ATTN_IDLE;
#endif
if (!self->speed) self->speed = 160;
self->moveinfo.speed = self->speed;
gi.linkentity (self);
if (self->noise_index && !VectorLength(self->s.origin) )
{
speaker = G_Spawn();
speaker->classname = "moving_speaker";
speaker->s.sound = 0;
speaker->volume = 1;
speaker->attenuation = self->attenuation; // was 1
speaker->owner = self;
speaker->think = Moving_Speaker_Think;
speaker->nextthink = level.time + 2*FRAMETIME;
speaker->spawnflags = 0; // plays constantly
self->speaker = speaker;
VectorAdd(self->absmin,self->absmax,speaker->s.origin);
VectorScale(speaker->s.origin,0.5,speaker->s.origin);
VectorSubtract(speaker->s.origin,self->s.origin,speaker->offset);
}
}
void SP_crane_hoist (edict_t *self)
{
vec3_t origin;
edict_t *speaker;
self->class_id = ENTITY_CRANE_HOIST;
gi.setmodel (self, self->model);
VectorAdd(self->absmin,self->absmax,origin);
VectorScale(origin,0.5,origin);
if (!self->targetname)
{
gi.dprintf ("crane_hoist with no targetname at %s\n", vtos(origin));
G_FreeEdict (self);
return;
}
if (!self->target)
{
gi.dprintf ("crane_hoist with no target at %s\n", vtos(origin));
G_FreeEdict (self);
return;
}
self->classname = "crane_hoist";
self->solid = SOLID_BSP;
self->movetype = MOVETYPE_PUSH;
if (!self->speed) self->speed = 160;
self->moveinfo.speed = self->speed;
if (st.noise)
self->noise_index = gi.soundindex(st.noise);
else
self->noise_index = 0;
#ifdef LOOP_SOUND_ATTENUATION
if (self->attenuation <= 0)
self->attenuation = ATTN_IDLE;
#endif
gi.linkentity (self);
if (self->noise_index && !VectorLength(self->s.origin) )
{
speaker = G_Spawn();
speaker->classname = "moving_speaker";
speaker->s.sound = 0;
speaker->volume = 1;
speaker->attenuation = self->attenuation; // was 1
speaker->owner = self;
speaker->think = Moving_Speaker_Think;
speaker->nextthink = level.time + 2*FRAMETIME;
speaker->spawnflags = 7; // owner must be moving to play
self->speaker = speaker;
VectorAdd(self->absmin,self->absmax,speaker->s.origin);
VectorScale(speaker->s.origin,0.5,speaker->s.origin);
VectorSubtract(speaker->s.origin,self->s.origin,speaker->offset);
}
}
void SP_crane_beam (edict_t *self)
{
vec3_t origin;
edict_t *speaker;
self->class_id = ENTITY_CRANE_BEAM;
gi.setmodel (self, self->model);
VectorAdd(self->absmin,self->absmax,origin);
VectorScale(origin,0.5,origin);
if (!self->targetname)
{
gi.dprintf ("crane_beam with no targetname at %s\n", vtos(origin));
G_FreeEdict (self);
return;
}
if (!self->target)
{
gi.dprintf ("crane_beam with no target at %s\n", vtos(origin));
G_FreeEdict (self);
return;
}
if (!self->pathtarget)
{
gi.dprintf ("crane_beam with no pathtarget at %s\n", vtos(origin));
G_FreeEdict (self);
return;
}
self->classname = "crane_beam";
self->solid = SOLID_BSP;
self->movetype = MOVETYPE_PUSH;
if (!self->speed) self->speed = 160;
self->moveinfo.speed = self->speed;
if (st.noise)
self->noise_index = gi.soundindex(st.noise);
else
self->noise_index = 0;
#ifdef LOOP_SOUND_ATTENUATION
if (self->attenuation <= 0)
self->attenuation = ATTN_IDLE;
#endif
gi.linkentity (self);
if (self->noise_index && !VectorLength(self->s.origin) )
{
speaker = G_Spawn();
speaker->classname = "moving_speaker";
speaker->s.sound = 0;
speaker->volume = 1;
speaker->attenuation = self->attenuation; // was 1
speaker->think = Moving_Speaker_Think;
speaker->nextthink = level.time + 2*FRAMETIME;
speaker->spawnflags = 7; // owner must be moving to play
speaker->owner = self; // this will be changed later when we know
// controls are spawned
self->speaker = speaker;
VectorAdd(self->absmin,self->absmax,speaker->s.origin);
VectorScale(speaker->s.origin,0.5,speaker->s.origin);
VectorSubtract(speaker->s.origin,self->s.origin,speaker->offset);
}
}
//===================================================================================
//QUAKED crane_reset - special purpose trigger_relay that calls the associated crane
// to the beam extent closest to the crane_reset. Typically crane_reset is targeted
// by a func_button
void crane_reset_go (edict_t *temp)
{
edict_t *control;
control = temp->owner;
Crane_Move_Begin(control->crane_beam);
Crane_Move_Begin(control->crane_hoist);
Crane_Move_Begin(control->crane_hook);
if (control->crane_beam->crane_onboard_control)
Crane_Move_Begin(control->crane_beam->crane_onboard_control);
if (control->crane_hook->crane_cargo) Crane_Move_Begin(control->crane_hook->crane_cargo);
G_FreeEdict(temp);
}
void crane_reset_use (edict_t *self, edict_t *other, edict_t *activator)
{
float d1, d2;
int dir;
edict_t *delay;
edict_t *crane;
edict_t *control, *beam, *cable, *cargo, *hoist, *hook;
vec3_t bonk, v1, v2;
crane = G_Find (NULL, FOFS(targetname), self->target);
if (!crane)
{
gi.dprintf("Cannot find target of crane_reset at %s\n",vtos(self->s.origin));
return;
}
control = crane->crane_control;
control->activator = activator;
if (!(control->spawnflags & 1))
{
if (control->message)
safe_centerprintf(activator,"%s\n",control->message);
else
safe_centerprintf(activator,"No power\n");
return;
}
beam = control->crane_beam;
hoist = control->crane_hoist;
hook = control->crane_hook;
cable = control->crane_cable;
cargo = hook->crane_cargo;
VectorSubtract(beam->pos1,self->s.origin,v1);
VectorSubtract(beam->pos2,self->s.origin,v2);
d1 = VectorLength(v1);
d2 = VectorLength(v2);
if (d2 < d1)
control->crane_increment = 1;
else
control->crane_increment = -1;
if (beam->movedir[0] > 0)
{
// travels in X
dir = 0;
if (control->crane_increment > 0)
{
if (Crane_Hook_Bonk(hook,0,1,bonk))
{
bonk[0] += beam->absmax[0] - hook->absmax[0];
beam->crane_bonk = min(bonk[0],beam->pos2[0]);
}
else
beam->crane_bonk = beam->pos2[0];
beam->crane_bonk += beam->absmin[0] - beam->absmax[0];
}
else
{
if (Crane_Hook_Bonk(hook,0,-1,bonk))
{
bonk[0] += beam->absmin[0] - hook->absmin[0];
beam->crane_bonk = max(bonk[0],beam->pos1[0]);
}
else
beam->crane_bonk = beam->pos1[0];
}
}
else
{
// travels in Y
dir = 1;
if (control->crane_increment > 0)
{
if (Crane_Hook_Bonk(hook,1,1,bonk))
{
bonk[1] += beam->absmax[1] - hook->absmax[1];
beam->crane_bonk = min(bonk[1],beam->pos2[1]);
}
else
beam->crane_bonk = beam->pos2[1];
beam->crane_bonk += beam->absmin[1] - beam->absmax[1];
}
else
{
if (Crane_Hook_Bonk(hook,1,-1,bonk))
{
bonk[1] += beam->absmin[1] - hook->absmin[1];
beam->crane_bonk = max(bonk[1],beam->pos1[1]);
}
else
beam->crane_bonk = beam->pos1[1];
}
}
if (beam->speaker && beam->crane_onboard_control)
{
beam->speaker->owner = beam->crane_onboard_control;
VectorAdd(beam->crane_onboard_control->absmin,
beam->crane_onboard_control->absmax,
beam->speaker->s.origin);
VectorScale(beam->speaker->s.origin,0.5,beam->speaker->s.origin);
VectorSubtract(beam->speaker->s.origin,
beam->crane_onboard_control->s.origin,beam->speaker->offset);
beam->speaker->owner->noise_index = beam->noise_index;
}
beam->crane_dir = dir;
beam->moveinfo.remaining_distance = control->crane_increment *
(beam->crane_bonk - beam->absmin[dir]);
if (beam->moveinfo.remaining_distance <= 0) return;
Crane_AdjustSpeed(beam);
VectorSet(beam->moveinfo.dir,
beam->movedir[0]*control->crane_increment,
beam->movedir[1]*control->crane_increment,
0);
beam->crane_control = control;
hoist->crane_dir = dir;
hoist->crane_bonk = beam->crane_bonk + hoist->absmin[dir] - beam->absmin[dir];
hoist->crane_control = control;
memcpy(&hoist->moveinfo,&beam->moveinfo,sizeof(moveinfo_t));
hook->crane_dir = dir;
hook->crane_bonk = beam->crane_bonk + hook->absmin[dir] - beam->absmin[dir];
hook->crane_control = control;
memcpy(&hook->moveinfo,&beam->moveinfo,sizeof(moveinfo_t));
cable->crane_dir = dir;
cable->crane_bonk = beam->crane_bonk + cable->absmin[dir] - beam->absmin[dir];
cable->crane_control = control;
memcpy(&cable->moveinfo,&beam->moveinfo,sizeof(moveinfo_t));
if (beam->crane_onboard_control)
{
beam->crane_onboard_control->crane_dir = dir;
beam->crane_onboard_control->crane_bonk = beam->crane_bonk +
beam->crane_onboard_control->absmin[dir] -
beam->absmin[dir];
beam->crane_onboard_control->crane_control = control;
memcpy(&beam->crane_onboard_control->moveinfo,&beam->moveinfo,sizeof(moveinfo_t));
}
if (cargo)
{
cargo->crane_dir = dir;
cargo->crane_bonk = beam->crane_bonk + cargo->absmin[dir] - beam->absmin[dir];
cargo->crane_control = control;
memcpy(&cargo->moveinfo,&beam->moveinfo,sizeof(moveinfo_t));
}
delay = G_Spawn();
delay->owner = control;
delay->think = crane_reset_go;
delay->nextthink = level.time + FRAMETIME;
gi.linkentity(delay);
self->count--;
if (!self->count) {
self->think = G_FreeEdict;
self->nextthink = level.time + 1;
}
}
void SP_crane_reset (edict_t *self)
{
if (!self->targetname)
{
gi.dprintf ("crane_reset with no targetname at %s\n", vtos(self->s.origin));
G_FreeEdict (self);
return;
}
if (!self->target)
{
gi.dprintf ("crane_reset with no target at %s\n", vtos(self->s.origin));
G_FreeEdict (self);
return;
}
self->class_id = ENTITY_CRANE_RESET;
self->use = crane_reset_use;
}