#include "../idlib/precompiled.h" #pragma hdrstop #include "Game_local.h" #define WHEEL_STEP 40 #define YAW_STEP 0.7f #define MAX_FORCE 1024 //was 1024 #define MIN_FORCE 96 #define MULTIPLIER_MIN 1.1f #define MULTIPLIER_MAX 2.2f #define MIN_PITCH -10 #define MAX_PITCH -89 #define RESETTIME 50 #define WHEELSPEED 2 #define WHEELTIME 300 #define RISETIME 300 #define BEAMWIDTH 8 #define JUMPPADWIDTH 32 #define JUMPPADHEIGHT 128 const idEventDef EV_launcherkill( "launcherkill" ); CLASS_DECLARATION( idMoveableItem, idLauncher ) EVENT( EV_launcherkill, idLauncher::launcherkill) END_CLASS void idLauncher::Save( idSaveGame *savefile ) const { savefile->WriteObject(peakEnt); savefile->WriteObject(jumpPad); savefile->WriteObject(gatorEnt); savefile->WriteObject(remoteEnt); savefile->WriteObject(hitpointEnt); savefile->WriteObject(displaymodel); savefile->WriteObject(particles); savefile->WriteInt(state); savefile->WriteVec3(lastLaunchdir); savefile->WriteFloat(lastForce); savefile->WriteInt(nextAimrecord); savefile->WriteInt(remoteLerpStart); savefile->WriteInt(remoteLerpEnd); savefile->WriteVec3(baseGatorPos); savefile->WriteInt(resettime_up); savefile->WriteInt(resettime_down); savefile->WriteInt(resettime_left); savefile->WriteInt(resettime_right); savefile->WriteInt(wheelValue); savefile->WriteInt(wheelMovetime); savefile->WriteInt(wheelMovedir); savefile->WriteInt(beamUpdateTime); savefile->WriteFloat(remoteRaiseLerp); savefile->WriteInt(lastGametime); for (int i = 0; i < LINECOUNT; i++) { savefile->WriteObject(beamStarts[i]); savefile->WriteObject(beamEnds[i]); } savefile->WriteVec3(hitpointPos); savefile->WriteVec3(launchDir); savefile->WriteFloat(force); forcefieldIgnore.Save(savefile); } void idLauncher::Restore( idRestoreGame *savefile ) { savefile->ReadObject(reinterpret_cast(peakEnt)); savefile->ReadObject(reinterpret_cast(jumpPad)); savefile->ReadObject(reinterpret_cast(gatorEnt)); savefile->ReadObject(reinterpret_cast(remoteEnt)); savefile->ReadObject(reinterpret_cast(hitpointEnt)); savefile->ReadObject(reinterpret_cast(displaymodel)); savefile->ReadObject(reinterpret_cast(particles)); savefile->ReadInt(state); savefile->ReadVec3(lastLaunchdir); savefile->ReadFloat(lastForce); savefile->ReadInt(nextAimrecord); savefile->ReadInt(remoteLerpStart); savefile->ReadInt(remoteLerpEnd); savefile->ReadVec3(baseGatorPos); savefile->ReadInt(resettime_up); savefile->ReadInt(resettime_down); savefile->ReadInt(resettime_left); savefile->ReadInt(resettime_right); savefile->ReadInt(wheelValue); savefile->ReadInt(wheelMovetime); savefile->ReadInt(wheelMovedir); savefile->ReadInt(beamUpdateTime); savefile->ReadFloat(remoteRaiseLerp); savefile->ReadInt(lastGametime); for (int i = 0; i < LINECOUNT; i++) { savefile->ReadObject(reinterpret_cast(beamStarts[i])); savefile->ReadObject(reinterpret_cast(beamEnds[i])); } savefile->ReadVec3(hitpointPos); savefile->ReadVec3(launchDir); savefile->ReadFloat(force); forcefieldIgnore.Restore(savefile); //this->forcefieldIgnore = jumpPad; } void idLauncher::Spawn( void ) { int i; idVec3 forward, right, up; idDict args; this->GetPhysics()->GetAxis().ToAngles().ToVectors(&forward, &right, &up); args.Clear(); args.SetVector( "origin", this->GetPhysics()->GetOrigin() + idVec3(0,0,0.1f) ); args.Set( "classname", "info_location" ); gameLocal.SpawnEntityDef( args, &peakEnt ); peakEnt->Bind(this, false); args.Clear(); args.SetVector( "origin", this->GetPhysics()->GetOrigin() ); args.SetVector( "mins", idVec3(-JUMPPADWIDTH, -JUMPPADWIDTH, 3) ); args.SetVector( "maxs", idVec3(JUMPPADWIDTH, JUMPPADWIDTH, JUMPPADHEIGHT) ); args.Set( "target", peakEnt->GetName() ); args.Set("snd_jump", "launcher_fire"); args.Set("smoke_jump", spawnArgs.GetString("smoke_burst")); args.Set("owner", this->GetName()); jumpPad = ( idJumpPad * )gameLocal.SpawnEntityType( idJumpPad::Type, &args ); jumpPad->Bind(this, false); args.Clear(); args.SetVector("origin", this->GetPhysics()->GetOrigin() + (forward * -16) + (up * 4) ); args.Set("skin", "skins/cable/nophone_brown"); args.Set("classname", spawnArgs.GetString("model_cable")); args.SetInt("isremote", 1); args.Set("master", this->GetName()); gameLocal.SpawnEntityDef(args, &gatorEnt); gatorEnt->GetPhysics()->SetAxis(forward.ToMat3()); gatorEnt->Bind( this, false ); //gatorEnt->ApplyImpulse( gatorEnt, 13 , gatorEnt->GetPhysics()->GetOrigin(), idVec3(0, 0, 4096)); //gatorEnt->GetPhysics()->SetLinearVelocity(idVec3(0,0,4096)); args.Clear(); args.SetVector("origin", this->GetPhysics()->GetOrigin()); args.Set("classname", "moveable_launcherremote"); gameLocal.SpawnEntityDef(args, &remoteEnt); args.Clear(); args.SetVector("origin", this->GetPhysics()->GetOrigin()); args.SetInt("solid", 0); args.SetBool("noclipmodel", 1); args.Set("model", spawnArgs.GetString("model_hitpoint")); hitpointEnt = ( idStaticEntity * )gameLocal.SpawnEntityType( idStaticEntity::Type, &args ); //hitpointEnt->BecomeActive(TH_UPDATEVISUALS); hitpointEnt->Bind(this, false); args.Clear(); args.SetVector("origin", this->GetPhysics()->GetOrigin()); args.Set("classname", "env_launchermodel"); gameLocal.SpawnEntityDef(args, &displaymodel); displaymodel->SetAxis(this->GetPhysics()->GetAxis()); displaymodel->Bind(this, true); args.Clear(); args.SetVector("origin", this->GetPhysics()->GetOrigin()); args.Set("classname", "func_emitter"); args.Set( "model", spawnArgs.GetString("smoke_fan") ); gameLocal.SpawnEntityDef(args, &particles); particles->Bind(this, true); this->forcefieldIgnore = jumpPad; force = 192; launchDir = idVec3(-45, 0, 0); nextAimrecord = 0; remoteLerpStart = 0; remoteLerpEnd = 0; remoteRaiseLerp = 0; lastGametime = 0; for (i = 0;i < LINECOUNT; i++) { idDict args; args.Clear(); args.SetVector("origin", this->GetPhysics()->GetOrigin()); beamEnds[i] = ( idBeam * )gameLocal.SpawnEntityType( idBeam::Type, &args ); args.Clear(); args.Set("skin", "skins/beam_launcher"); args.SetInt("width", BEAMWIDTH); args.SetVector("origin", this->GetPhysics()->GetOrigin()); args.Set("target", beamEnds[i]->GetName() ); beamStarts[i] = ( idBeam * )gameLocal.SpawnEntityType( idBeam::Type, &args ); if (i > 0) { beamEnds[i]->Bind(this, false); beamStarts[i]->Bind(this, false); } } //bind first beam to launcher model. beamStarts[0]->SetOrigin(this->GetPhysics()->GetOrigin()); beamStarts[0]->Bind(this, false); beamEnds[0]->SetOrigin(peakEnt->GetPhysics()->GetOrigin()); beamEnds[0]->Bind(peakEnt, false); beamStarts[0]->BecomeActive(TH_PHYSICS); resettime_up = 0; resettime_down = 0; resettime_left = 0; resettime_right = 0; wheelMovetime = 0; wheelValue = 0; beamUpdateTime = 0; UpdateBeams(); UpdateMultiplier(); BecomeActive( TH_THINK ); StartSound( "snd_idle", SND_CHANNEL_HEART, 0, false, NULL ); } void idLauncher::SetLaunchDir(idVec3 newdir, float newForce) { launchDir = newdir; force = newForce; UpdateBeams(); } void idLauncher::UpdateBeams( void ) { int i; idAngles launchdirNormalized = idAngles(launchDir.x, launchDir.y, launchDir.z); idVec3 forward = launchdirNormalized.ToForward(); idVec3 peakPos = forward * force; peakEnt->SetOrigin(peakPos); idAngles particleDir = forward.ToAngles(); //particleDir.yaw += 180; particleDir.pitch = particleDir.pitch + 90; particles->GetPhysics()->SetAxis(particleDir.ToMat3()); //determine whether first beam hits a wall. trace_t trPeak; peakEnt->Unbind(); gameLocal.clip.TracePoint(trPeak, GetPhysics()->GetOrigin(), peakEnt->GetPhysics()->GetOrigin(), MASK_SOLID, this); peakEnt->Bind(this, true); if (trPeak.fraction < 1) { peakEnt->Unbind(); peakEnt->SetOrigin(trPeak.endpos); peakEnt->Bind(this, true); hitpointEnt->Unbind(); hitpointEnt->GetPhysics()->SetOrigin(trPeak.endpos); hitpointEnt->GetPhysics()->SetAxis(trPeak.c.normal.ToMat3()); hitpointEnt->Bind(this, false); hitpointPos = trPeak.endpos; //hide the rest of the beams. for (i = 1; i < LINECOUNT; i++) { beamStarts[i]->Hide(); } return; } //set up the first descent beam. idAngles beam1Normalized = idAngles(0, launchDir.y, 0); idVec3 forwardbeam1 = beam1Normalized.ToForward(); float horizontalDist = idVec3(peakPos.x, peakPos.y, 0).Length(); float verticalDist = peakPos.z; idVec3 lastPos = peakPos; bool placedHitpoint = false; for (i = 1; i < LINECOUNT; i++) { idVec3 endPos = lastPos + (forwardbeam1 * horizontalDist) + (idVec3(0,0,-1) * verticalDist); beamStarts[i]->SetOrigin(lastPos); beamEnds[i]->SetOrigin(endPos); lastPos = endPos; verticalDist = verticalDist * 2; } if (gameLocal.time > nextAimrecord) { nextAimrecord = gameLocal.time + 200; if (lastLaunchdir.x != launchDir.x || lastLaunchdir.y != launchDir.y || lastLaunchdir.z != launchDir.z || lastForce != force) { idEntity *recordEnt; recordEnt = gameLocal.FindEntity( "world1" ); if (recordEnt) { int id = this->spawnArgs.GetInt("id", "-1"); static_cast( recordEnt )->RecordLaunchAim(id, launchDir, force); } lastLaunchdir.x = launchDir.x; lastLaunchdir.y = launchDir.y; lastLaunchdir.z = launchDir.z; lastForce = force; } } int lastValidTraceIndex = 0; for (i = 1; i < LINECOUNT; i++) { trace_t tr; gameLocal.clip.TracePoint(tr, beamStarts[i]->GetPhysics()->GetOrigin(), beamEnds[i]->GetPhysics()->GetOrigin(), MASK_SOLID, this); if (tr.fraction >= 1) continue; lastValidTraceIndex = i; hitpointEnt->Unbind(); hitpointEnt->GetPhysics()->SetOrigin(tr.endpos); hitpointEnt->GetPhysics()->SetAxis(tr.c.normal.ToMat3()); hitpointEnt->UpdateVisuals(); hitpointEnt->Bind(this, false); hitpointPos = tr.endpos; beamEnds[i]->Unbind(); beamEnds[i]->GetPhysics()->SetOrigin(tr.endpos); beamEnds[i]->Bind(this, true); break; } for (i = 0; i < LINECOUNT; i++) { if (i > lastValidTraceIndex) beamStarts[i]->Hide(); else beamStarts[i]->Show(); } } void idLauncher::OnFrob( idEntity* activator ) { gameLocal.GetLocalPlayer()->Event_usePicker(gatorEnt); } void idLauncher::OnYaw(int value) { launchDir.y += value * YAW_STEP; idAngles ang = displaymodel->GetPhysics()->GetAxis().ToAngles(); ang.yaw = launchDir.y; jointHandle_t jointHandle; jointHandle = displaymodel->GetAnimator()->GetJointHandle( "body" ); static_cast< idAnimatedEntity * >( displaymodel )->GetAnimator()->SetJointAxis(jointHandle, JOINTMOD_WORLD, ang.ToMat3()); if (value > 0) { MoveBone("a", idVec3(-0.7f,0,0)); if (resettime_right < gameLocal.time) { remoteEnt->StartSound( "snd_button", SND_CHANNEL_ANY, 0, false, NULL ); } resettime_right = gameLocal.time + RESETTIME; } else { MoveBone("d", idVec3(-0.7f,0,0)); if (resettime_left < gameLocal.time) { remoteEnt->StartSound( "snd_button", SND_CHANNEL_ANY, 0, false, NULL ); } resettime_left = gameLocal.time + RESETTIME; } UpdateBeams(); } void idLauncher::OnPitch(int value) { bool playError = false; launchDir.x += value; if (launchDir.x < MAX_PITCH) { launchDir.x = MAX_PITCH; playError = true; } else if (launchDir.x > MIN_PITCH) { launchDir.x = MIN_PITCH; playError = true; } if (value > 0) { MoveBone("w", idVec3(-0.7f,0,0)); if (resettime_up < gameLocal.time) { remoteEnt->StartSound( "snd_button", SND_CHANNEL_ANY, 0, false, NULL ); } resettime_up = gameLocal.time + RESETTIME; } else { MoveBone("s", idVec3(-0.7f,0,0)); if (resettime_down < gameLocal.time) { remoteEnt->StartSound( "snd_button", SND_CHANNEL_ANY, 0, false, NULL ); } resettime_down = gameLocal.time + RESETTIME; } if (playError) { remoteEnt->StartSound( "snd_error", SND_CHANNEL_ANY, 0, false, NULL ); } UpdateBeams(); } void idLauncher::OnWheelMove(int value) { bool playError = false; force += WHEEL_STEP * value; if (force > MAX_FORCE) { force = MAX_FORCE; playError = true; } else if (force < MIN_FORCE) { force = MIN_FORCE; playError = true; } if (value < 0) wheelMovedir = WHEELSPEED; else wheelMovedir = -WHEELSPEED; if (wheelMovetime < gameLocal.time) { remoteEnt->StartSound( "snd_dial", SND_CHANNEL_ANY, 0, false, NULL ); } if (playError) { remoteEnt->StartSound( "snd_error", SND_CHANNEL_ANY, 0, false, NULL ); } wheelMovetime = gameLocal.time + WHEELTIME; UpdateBeams(); UpdateMultiplier(); } void idLauncher::UpdateMultiplier() { //magic numbers hack........ //determine force based on mousewheel power. float forceLerp = (force - MIN_FORCE) / (float)(MAX_FORCE - MIN_FORCE); float forceMultiplier = idMath::Lerp(MULTIPLIER_MIN, MULTIPLIER_MAX, forceLerp); //adjust force based on altitude delta. float altitudeDelta = this->GetPhysics()->GetOrigin().z - hitpointPos.z; float altitude_minMultiplier = 0.75f; float altitude_maxMultiplier = 1.0f; float altitude_distanceThreshold = 384.0f; if (altitudeDelta <= 0) { forceMultiplier *= altitude_minMultiplier; } else if (altitudeDelta >= altitude_distanceThreshold) { //use 100% force, so do nothing here. } else { //scale the force. float altitudeLerp = altitudeDelta / altitude_distanceThreshold; float scaleAmount = idMath::Lerp(altitude_minMultiplier, altitude_maxMultiplier, altitudeLerp); forceMultiplier *= scaleAmount; } jumpPad->Event_UpdateMultiplier(forceMultiplier); //common->Printf("new force multiplier: %f\n", forceMultiplier); } void idLauncher::MoveBone(const char *bonename, idVec3 direction) { if (remoteEnt == NULL) return; jointHandle_t jointHandle; jointHandle = remoteEnt->GetAnimator()->GetJointHandle( bonename ); static_cast< idAnimatedEntity * >( remoteEnt )->GetAnimator()->SetJointPos(jointHandle, JOINTMOD_LOCAL, direction); } idVec3 idLauncher::GetGatorPos( void ) { jointHandle_t jointHandle; idVec3 jointPos; idMat3 jointAxis; jointHandle = gatorEnt->GetAnimator()->GetJointHandle( "bone16" ); static_cast< idAnimatedEntity * >(gatorEnt)->GetJointWorldTransform(jointHandle, gameLocal.time, jointPos, jointAxis ); return jointPos; } void idLauncher::Think( void ) { int i; jumpPad->Event_UpdatePeak(); if (gameLocal.time > resettime_up) { MoveBone("w", idVec3(0,0,0)); } if (gameLocal.time > resettime_down) { MoveBone("s", idVec3(0,0,0)); } if (gameLocal.time > resettime_left) { MoveBone("a", idVec3(0,0,0)); } if (gameLocal.time > resettime_right) { MoveBone("d", idVec3(0,0,0)); } if (gameLocal.time < wheelMovetime) { wheelValue += wheelMovedir; idAngles ang; ang.pitch = wheelValue; ang.yaw = 0; ang.roll = 0; jointHandle_t jointHandle; jointHandle = remoteEnt->GetAnimator()->GetJointHandle( "wheel" ); static_cast< idAnimatedEntity * >( remoteEnt )->GetAnimator()->SetJointAxis(jointHandle, JOINTMOD_LOCAL, ang.ToMat3()); } if (gameLocal.GetLocalPlayer()->Event_isHolding(gatorEnt)) { if (remoteRaiseLerp <= 0) { baseGatorPos = GetGatorPos(); } remoteRaiseLerp += gameLocal.time - lastGametime; if (remoteRaiseLerp >= RISETIME) { remoteRaiseLerp = RISETIME; } if (remoteEnt->IsBound()) { remoteEnt->Unbind(); } //gameLocal.GetLocalPlayer()->viewAngles idAngles remoteAng = gameLocal.GetLocalPlayer()->viewAngles; remoteAng.yaw += 180; remoteAng.pitch *= -1; remoteAng.pitch += 3 * idMath::Sin(gameLocal.time * 0.0005f); //sway remoteAng.yaw += 5 * idMath::Sin(gameLocal.time * 0.0003f); //sway remoteEnt->SetAngles( remoteAng ); idVec3 up; remoteAng.ToVectors( NULL, NULL, &up ); idVec3 finalPos = gameLocal.GetLocalPlayer()->GetEyePosition() + (gameLocal.GetLocalPlayer()->viewAngles.ToForward() * 6) + (up * -3.8f ); if (remoteRaiseLerp < RISETIME) { //is raising. float amt = remoteRaiseLerp / RISETIME; finalPos.x = idMath::Lerp(baseGatorPos.x, finalPos.x, amt); finalPos.y = idMath::Lerp(baseGatorPos.y, finalPos.y, amt); finalPos.z = idMath::Lerp(baseGatorPos.z, finalPos.z, amt); } //remoteEnt->SetOrigin( gameLocal.GetLocalPlayer()->GetEyePosition() + (gameLocal.GetLocalPlayer()->viewAngles.ToForward() * 6) + (up * -3.8f )); remoteEnt->SetOrigin(finalPos); } else { remoteRaiseLerp -= gameLocal.time - lastGametime; if (remoteRaiseLerp <= 0) { remoteRaiseLerp = 0; } if (!remoteEnt->IsBound()) { idVec3 jointPos = GetGatorPos(); jointHandle_t jointHandle; jointHandle = gatorEnt->GetAnimator()->GetJointHandle( "bone16" ); remoteEnt->SetOrigin(jointPos); remoteEnt->BindToJoint(gatorEnt, jointHandle, true); } } //common->Printf("%f\n", remoteRaiseLerp); lastGametime = gameLocal.time; if (gameLocal.time > beamUpdateTime) { beamUpdateTime = gameLocal.time + 300; UpdateBeams(); } //RunPhysics(); idMoveableItem::Think(); //idMoveableItem::Present(); } void idLauncher::OnGet( void ) { StopSound( SND_CHANNEL_HEART, false ); StopSound( SND_CHANNEL_ANY, false ); //particle effect. idDict args; args.Clear(); args.SetVector( "origin", GetPhysics()->GetOrigin()); args.Set( "model", spawnArgs.GetString("smoke_pickup") ); args.SetBool( "start_off", false ); gameLocal.SpawnEntityType( idExplodable::Type, &args ); hitpointEnt->PostEventMS( &EV_Remove, 0 ); this->PostEventMS( &EV_Remove, 0 ); gameLocal.GetLocalPlayer()->GiveInventoryItem("weapon_launcher"); gameLocal.GetLocalPlayer()->GiveInventoryItem("ammo_launchers_small"); gameLocal.GetLocalPlayer()->StartSound( "snd_get" , SND_CHANNEL_ANY, 0, false, NULL ); } void idLauncher::launcherkill( void ) { StopSound( SND_CHANNEL_HEART, false ); StopSound( SND_CHANNEL_ANY, false ); }