void CMissileProjectile::Update()
{
	if (--ttl > 0) {
		if (!luaMoveCtrl) {
			if (curSpeed < maxSpeed) {
				curSpeed += weaponDef->weaponacceleration;
			}

			float3 targSpeed(ZeroVector);

			if (weaponDef->tracks && target) {
				CSolidObject* so = dynamic_cast<CSolidObject*>(target);
				CWeaponProjectile* po = dynamic_cast<CWeaponProjectile*>(target);

				targetPos = target->pos;
				if (so) {
					targetPos = so->aimPos;
					targSpeed = so->speed;

					if (owner()) {
						CUnit* u = dynamic_cast<CUnit*>(so);
						if (u) {
							targetPos = CGameHelper::GetUnitErrorPos(u, owner()->allyteam, true);
						}
					}
				} if (po) {
					targSpeed = po->speed;
				}
			}


			if (isWobbling) {
				--wobbleTime;
				if (wobbleTime == 0) {
					float3 newWob = gs->randVector();
					wobbleDif = (newWob - wobbleDir) * (1.0f / 16);
					wobbleTime = 16;
				}

				wobbleDir += wobbleDif;

				dir += wobbleDir * weaponDef->wobble * (owner()? (1.0f - owner()->limExperience * 0.5f): 1);
				dir.Normalize();
			}

			if (isDancing) {
				--danceTime;
				if (danceTime <= 0) {
					danceMove = gs->randVector() * weaponDef->dance - danceCenter;
					danceCenter += danceMove;
					danceTime = 8;
				}

				pos += danceMove;
			}

			const float3 orgTargPos = targetPos;
			const float3 targetDir = (targetPos - pos).SafeNormalize();
			const float dist = pos.distance(targetPos) + 0.1f;

			if (extraHeightTime > 0) {
				extraHeight -= extraHeightDecay;
				--extraHeightTime;

				targetPos.y += extraHeight;

				if (dir.y <= 0.0f) {
					// missile has reached apex, smoothly transition
					// to targetDir (can still overshoot when target
					// is too close or height difference too large)
					const float horDiff = (targetPos - pos).Length2D() + 0.01f;
					const float verDiff = (targetPos.y - pos.y) + 0.01f;
					const float dirDiff = math::fabs(targetDir.y - dir.y);
					const float ratio = math::fabs(verDiff / horDiff);

					dir.y -= (dirDiff * ratio);
				} else {
					// missile is still ascending
					dir.y -= (extraHeightDecay / dist);
				}
			}


			float3 dif = (targetPos + targSpeed * (dist / maxSpeed) * 0.7f - pos).SafeNormalize();
			float3 dif2 = dif - dir;

			if (dif2.SqLength() < Square(weaponDef->turnrate)) {
				dir = dif;
			} else {
				dif2 -= (dir * (dif2.dot(dir)));
				dif2.SafeNormalize();
				dir += (dif2 * weaponDef->turnrate);
				dir.SafeNormalize();
			}

			targetPos = orgTargPos;
			speed = dir * curSpeed;
		}

		gCEG->Explosion(cegID, pos, ttl, areaOfEffect, NULL, 0.0f, NULL, dir);
	} else {
		if (weaponDef->selfExplode) {
			Collision();
		} else {
			// only when TTL <= 0 do we (missiles)
			// get influenced by gravity and drag
			if (!luaMoveCtrl) {
				speed *= 0.98f;
				speed.y += mygravity;
				dir = speed;
				dir.SafeNormalize();
			}
		}
	}

	if (!luaMoveCtrl) {
		pos += speed;
	}

	age++;
	numParts++;
	if (weaponDef->visuals.smokeTrail && !(age & 7)) {
		CSmokeTrailProjectile* tp = new CSmokeTrailProjectile(
			pos, oldSmoke,
			dir, oldDir, owner(), age == 8, false, 7, SMOKE_TIME, 0.6f, drawTrail, 0,
			weaponDef->visuals.texture2
		);

		oldSmoke = pos;
		oldDir = dir;
		numParts = 0;
		useAirLos = tp->useAirLos;

		if (!drawTrail) {
			const float3 camDir = (pos - camera->pos).ANormalize();

			if ((camera->pos.distance(pos) * 0.2f + (1 - math::fabs(camDir.dot(dir))) * 3000) > 300) {
				drawTrail = true;
			}
		}
	}

	UpdateInterception();
	UpdateGroundBounce();
}
void CMissileProjectile::Update(void)
{
	ttl--;
	if(ttl>0){
		if(curSpeed<maxSpeed)
			curSpeed+=weaponDef->weaponacceleration;

		float3 targSpeed(0,0,0);
		if(weaponDef->tracks && (decoyTarget || target)){	
			if(decoyTarget){
				targPos=decoyTarget->pos;
				targSpeed=decoyTarget->speed;
			} else {
				targSpeed=target->speed;
				if((target->physicalState==CSolidObject::Flying && (target->midPos-pos).SqLength()<150*150) || !owner)
					targPos=target->midPos;
				else
					targPos=helper->GetUnitErrorPos(target,owner->allyteam);
			}
		}

		if(isWobbling){
			--wobbleTime;
			if(wobbleTime==0){
				float3 newWob=gs->randVector();
				wobbleDif=(newWob-wobbleDir)*(1.0f/16);
				wobbleTime=16;
			}
			wobbleDir+=wobbleDif;
			dir+=wobbleDir*weaponDef->wobble*(owner?(1-owner->limExperience*0.5f):1);
			dir.Normalize();
		}

		float3 orgTargPos(targPos);
		float dist=targPos.distance(pos);
		if(dist==0)
			dist=0.1f;
		if(extraHeightTime){
			extraHeight-=extraHeightDecay;
			--extraHeightTime;
			targPos.y+=extraHeight;
			dir.y-=extraHeightDecay/dist;
			//geometricObjects->AddLine(pos,targPos,3,1,1);
		}
		float3 dif(targPos + targSpeed*(dist/maxSpeed)*0.7f - pos);
		dif.Normalize();
		float3 dif2=dif-dir;
		float tracking=weaponDef->turnrate;
		if(dif2.Length()<tracking){
			dir=dif;
		} else {
			dif2-=dir*(dif2.dot(dir));
			dif2.Normalize();
			dir+=dif2*tracking;
			dir.Normalize();
		}

		speed=dir*curSpeed;

		targPos=orgTargPos;
	} else {
		speed*=0.995f;
		speed.y+=gs->gravity;
		dir=speed;
		dir.Normalize();
	}
	pos+=speed;

	age++;
	numParts++;
	if(!(age&7)){
		CSmokeTrailProjectile* tp=SAFE_NEW CSmokeTrailProjectile(pos,oldSmoke,dir,oldDir,owner,age==8,false,7,Smoke_Time,0.6f,drawTrail);
		oldSmoke=pos;
		oldDir=dir;
		numParts=0;
		useAirLos=tp->useAirLos;
		if(!drawTrail){
			ENTER_MIXED;
			float3 camDir=(pos-camera->pos).Normalize();
			if(camera->pos.distance(pos)*0.2f+(1-fabs(camDir.dot(dir)))*3000 > 300)
				drawTrail=true;
			ENTER_SYNCED;
		}
	}

	//CWeaponProjectile::Update();
}
void CTorpedoProjectile::Update()
{
	if (!weaponDef->submissile && pos.y > -3.0f) {
		// tracking etc only works when we are underwater
		if (!luaMoveCtrl) {
			speed.y += mygravity;
			dir = speed;
			dir.Normalize();
		}
	} else {
		if (!weaponDef->submissile && pos.y-speed.y > -3.0f) {
			// level out torpedo a bit when hitting water
			if (!luaMoveCtrl) {
				dir.y *= 0.5f;
				dir.Normalize();
			}
		}

		if (--ttl > 0) {
			if (!luaMoveCtrl) {
				if (curSpeed < maxSpeed) {
					curSpeed += std::max(0.2f, tracking);
				}

				if (target) {
					CSolidObject* so = dynamic_cast<CSolidObject*>(target);
					CWeaponProjectile* po = dynamic_cast<CWeaponProjectile*>(target);

					targetPos = target->pos;
					float3 targSpeed(ZeroVector);
					if (so) {
						targetPos = so->aimPos;
						targSpeed = so->speed;

						if (pos.SqDistance(so->aimPos) > 150 * 150 && owner()) {
							CUnit* u = dynamic_cast<CUnit*>(so);
							if (u) {
								targetPos = helper->GetUnitErrorPos(u, owner()->allyteam, true);
							}
						}
					} if (po) {
						targSpeed = po->speed;
					}

					if (!weaponDef->submissile && targetPos.y > 0) {
						targetPos.y = 0;
					}

					const float dist = pos.distance(targetPos);
					float3 dif = (targetPos + targSpeed * (dist / maxSpeed) * 0.7f - pos).Normalize();
					float3 dif2 = dif - dir;

					if (dif2.Length() < tracking) {
						dir = dif;
					} else {
						dif2 -= dir * (dif2.dot(dir));
						dif2.SafeNormalize();
						dir += dif2 * tracking;
						dir.SafeNormalize();
					}
				}

				speed = dir * curSpeed;
			}

			gCEG->Explosion(cegID, pos, ttl, areaOfEffect, NULL, 0.0f, NULL, speed);
		} else {
			if (!luaMoveCtrl) {
				speed *= 0.98f;
				speed.y += mygravity;
				dir = speed;
				dir.SafeNormalize();
			}
		}
	}

	if (!luaMoveCtrl) {
		pos += speed;
	}

	if (pos.y < -2.0f) {
		--nextBubble;

		if (nextBubble == 0) {
			nextBubble = 1 + (int) (gs->randFloat() * 1.5f);

			const float3 pspeed = (gs->randVector() * 0.1f) + float3(0.0f, 0.2f, 0.0f);

			new CBubbleProjectile(
				pos + gs->randVector(), pspeed, 40 + gs->randFloat() * 30,
				1 + gs->randFloat() * 2, 0.01f, owner(), 0.3f + gs->randFloat() * 0.3f
			);
		}
	}

	UpdateGroundBounce();
	UpdateInterception();
}