mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-15 08:51:22 +00:00
1749 lines
46 KiB
C
1749 lines
46 KiB
C
/*
|
|
Copyright (C) 1997-2001 Id Software, Inc.
|
|
Copyright (C) 2000-2002 Mr. Hyde and Mad Dog
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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->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;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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->use = crane_reset_use;
|
|
}
|
|
|