thirtyflightsofloving/missionpack/g_crane.c
Knightmare66 7b6f4281ad Added POSTTHINK_CHILD_MOVEMENT macro to new movewith host entities in g_spawn.c->SpawnEntities() in default Lazarus DLL.
Added Zaero flare gun to no-autoswitch in p_weapon.c->Pickup_Weapon() in missionpack DLL.
Added LM plasma rifle to p_weapon.c->NoAmmoWeaponChange() in missionpack DLL.
Added output of modelindex5&6, alpha, and attenuation to properties command  in missionpack DLL.
Added nogib and environment spawnflags to trigger_hurt and trigger_hurt_boox in missionpack DLL.
Added MONSTER_KNOWS_MIRRORS flag to berserker, barracuda shark, and mutant in missionpack DLL.
Added entity class IDs to misc_actor and target_actor in missionpack DLL.
Added syntax whitespacing to some files in missionpack DLL.
Added backpack drop to certain monsters in missionpack DLL.
Changed some sound paths for new monsters and weapons in missionpack DLL.
2020-10-29 13:03:20 -04:00

1782 lines
47 KiB
C

#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 *self)
{
qboolean moved;
edict_t *owner;
//vec3_t forward, right, up
vec3_t offset;
owner = self->owner;
if (!owner) //paranoia
{
G_FreeEdict(self);
return;
}
if (!owner->inuse)
{
G_FreeEdict(self);
return;
}
/* self->movetype = MOVETYPE_PUSH;
AngleVectors(owner->s.angles, forward, right, up);
VectorNegate(right, right);
VectorMA(owner->s.origin, self->movewith_offset[0], forward, self->s.origin);
VectorMA(self->s.origin, self->movewith_offset[1], right, self->s.origin);
VectorMA(self->s.origin, self->movewith_offset[2], up, self->s.origin);
VectorCopy(owner->velocity, self->velocity);
// If parent is spinning, add appropriate velocities
VectorSubtract(self->s.origin, owner->s.origin, offset);
if (owner->avelocity[PITCH] != 0)
{
self->velocity[2] -= offset[0] * owner->avelocity[PITCH] * M_PI / 180;
self->velocity[0] += offset[2] * owner->avelocity[PITCH] * M_PI / 180;
}
if (owner->avelocity[YAW] != 0)
{
self->velocity[0] -= offset[1] * owner->avelocity[YAW] * M_PI / 180.;
self->velocity[1] += offset[0] * owner->avelocity[YAW] * M_PI / 180.;
}
if (owner->avelocity[ROLL] != 0)
{
self->velocity[1] -= offset[2] * owner->avelocity[ROLL] * M_PI / 180;
self->velocity[2] += offset[1] * owner->avelocity[ROLL] * M_PI / 180;
}
*/
if (self->spawnflags & 15)
{
if ((self->spawnflags & 8) && (
(!self->owner->groundentity) || // not on the ground
(!self->owner->activator) || // not "activated" by anything
(!self->owner->activator->client) ) // not activated by client
)
{
moved = false;
}
else
{
moved = false;
VectorSubtract(self->s.origin,owner->s.origin,offset);
if ((self->spawnflags & 1) && (fabs(offset[0] - self->offset[0]) > 0.125) )
moved = true;
if ((self->spawnflags & 2) && (fabs(offset[1] - self->offset[1]) > 0.125) )
moved = true;
if ((self->spawnflags & 4) && (fabs(offset[2] - self->offset[2]) > 0.125) )
moved = true;
}
if (moved) {
self->s.sound = self->owner->noise_index;
#ifdef LOOP_SOUND_ATTENUATION
self->s.attenuation = self->attenuation;
#endif
}
else
self->s.sound = 0;
}
else
{
if (!VectorLength(owner->velocity)) // only play if owner is moving
self->s.sound = 0;
else {
self->s.sound = self->noise_index;
#ifdef LOOP_SOUND_ATTENUATION
self->s.attenuation = self->attenuation;
#endif
}
}
VectorAdd(owner->s.origin,self->offset,self->s.origin);
self->nextthink = level.time + FRAMETIME;
gi.linkentity(self);
}
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->s.renderfx |= RF_NOSHADOW; //Knightmare- noshadow flag
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)
gi.centerprintf(activator,"%s\n",control->message);
else
gi.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; //crashes here
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)
{
gi.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))
{
gi.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 && ent->crane_hook->speaker) //crashes here
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 moving_speaker_think (edict_t *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; //crashes here
if (!(control->spawnflags & 1))
{
if (control->message)
gi.centerprintf(activator,"%s\n",control->message);
else
gi.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 == 0)
{
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;
}