#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; }