# HG changeset patch
# User Peewee
# Date 1286311450 14400
# Node ID e07cde3a6f89a46d0acfbc6eb04f44e06af8582a
# Parent  e9fac2f07888cb5f0f2b250e7536eec8ef72245e
Tutorial 4: Fixed aiming through solids.

diff -r e9fac2f07888 -r e07cde3a6f89 planet/Tutorial.c4f/Tutorial04.c4s/System.c4g/AIBowAttack.c
--- a/planet/Tutorial.c4f/Tutorial04.c4s/System.c4g/AIBowAttack.c	Tue Oct 05 16:05:31 2010 -0400
+++ b/planet/Tutorial.c4f/Tutorial04.c4s/System.c4g/AIBowAttack.c	Tue Oct 05 16:44:10 2010 -0400
@@ -2,7 +2,11 @@
 
 #appendto Bow
 
-public func AI_WeaponSpray() { return 5; }
+local spread_x;
+local spread_y;
+local lob_shot;
+
+public func AI_Spread(){return 5;}
 public func AI_IsRangedWeapon() { return true; }
 public func AI_IsLoaded() { return !!FindObject(Find_Container(this), Find_ID(Arrow)); }
 public func AI_CommandString() { return "AI_BowAttack"; }
@@ -10,9 +14,38 @@
 {
 	var x = target->GetX() - Contained()->GetX();
 	var y = target->GetY() - Contained()->GetY();
-	if (AI_AimPos(x, y, 100) == nil)
-		return false;
-	return true;
+	lob_shot = false;
+	
+	var angle = AI_AimPos(x, y, 100, lob_shot, true);
+	if (angle == nil) return false;
+	//Log("angle: %d, %d, %d",angle[0],angle[1],angle[2]);
+	
+	var tolerance = 20**2; // tolerance = margin for error (in px) squared
+	
+	var end = SimFlight(nil, nil, angle[0], angle[1], 1, nil, angle[2]);
+	//Log("end: %d, %d, %d, %d, %d", end[0], end[1], end[2], end[3], end[4]);
+	
+	var error = (target->GetX() - end[0])**2 + (target->GetY() - end[1])**2;
+	//Log("error:%d", error);
+	
+	if(error < tolerance) return true;
+	
+	//Log("Low shot misses");
+	// Oh, the straight shot didn't work. 
+	// Can I shoot over the obstacle instead?
+	lob_shot = true;
+	angle = AI_AimPos(x, y, 100, lob_shot, true);
+	//Log("angle: %d, %d, %d",angle[0],angle[1],angle[2]);
+	
+	end = SimFlight(nil, nil, angle[0], angle[1], 1, nil, angle[2]);
+	if (end == nil) return false;
+	//Log("end: %d, %d, %d, %d, %d",end[0], end[1], end[2], end[3], end[4]);
+	
+	error = (target->GetX() - end[0])**2 + (target->GetY() - end[1])**2;
+	//Log("error:%d", error);
+	
+	if(error < tolerance) return true;
+	return false;
 }
 
 protected func AI_BowAttack(object clonk, int x, int y, object target)
@@ -26,6 +59,7 @@
 		clonk->AddCommand("Acquire", nil, nil, nil, nil, nil, Arrow);
 		return;	
 	}
+	
 	AddEffect("AI_BowAim", clonk, 100, 1, this, nil, target);
 
 	clonk->AppendCommand("Wait", nil, 50, nil, nil, nil, 50);
@@ -40,13 +74,19 @@
 	EffectVar(0, clonk, num) = target;
 	var dx = target->GetX() - clonk->GetX();
 	var dy = target->GetY() - clonk->GetY();
-	var angle = AI_AimPos(dx, dy, 100, AI_WeaponSpray());
+	var angle = AI_AimPos(dx, dy, 100, lob_shot);
+	
 	if (angle == nil)
 	{
 		ControlUseCancel(clonk);
 		return -1;
 	}
-	ControlUseStart(clonk, angle[0], angle[1]);
+	
+	//set random factors here, so it doesn't stutter all over the place.
+	spread_x = (Random(2*AI_Spread())-AI_Spread()); 
+	spread_y = (Random(2*AI_Spread())-AI_Spread());
+	
+	ControlUseStart(clonk, angle[0]+spread_x, angle[1]+spread_y);
 	return 1;
 }
 
@@ -57,13 +97,13 @@
 	var target = EffectVar(0, clonk, num);
 	var dx = target->GetX() - clonk->GetX();
 	var dy = target->GetY() - clonk->GetY();
-	var angle = AI_AimPos(dx, dy, 100, AI_WeaponSpray());
+	var angle = AI_AimPos(dx, dy, 100, lob_shot);
 	if (angle == nil)
 	{
 		ControlUseCancel(clonk);
 		return -1;
 	}
-	ControlUseHolding(clonk, angle[0], angle[1]);
+	ControlUseHolding(clonk, angle[0]+spread_x, angle[1]+spread_y);
 	return 1;
 }
 
@@ -72,25 +112,37 @@
 	var target = EffectVar(0, clonk, num);
 	var dx = target->GetX() - clonk->GetX();
 	var dy = target->GetY() - clonk->GetY();
-	var angle = AI_AimPos(dx, dy, 100, AI_WeaponSpray());
+	var angle = AI_AimPos(dx, dy, 100, lob_shot);
 	if (angle == nil)
 	{
 		ControlUseCancel(clonk);
 		return -1;
 	}
-	ControlUseStop(clonk, angle[0], angle[1]);
+	ControlUseStop(clonk, angle[0]+spread_x, angle[1]+spread_y);
 	return 1;
 }
 
 //v = "muzzle speed" (speed at which the projectile is launched)
-//spread = variation in aim
-private func AI_AimPos(int x, int y, int v, int spread)
+//returns an array of x, y or x, y, t
+private func AI_AimPos(int x, int y, int v, bool lob, bool t)
 {
 	var g = GetGravity()/5; //FnGetGravity() multiplies actual gravity by 500
 	var root = (v**4 - g*(g*x*x - 2*y*v*v));
 	if(root < 0)
-		return -1;
-	var angle = Angle(0,0,(g*x),v*v-Sqrt(root));
-	//allows for finer variation than adding Random(2*spread)-spread to the angle
-    return [Sin(angle, 100)+Random(2*spread)-spread, Cos(angle, 100)+Random(2*spread)-spread];
+		return nil;
+	if(lob)
+	{
+		var angle = Angle(0, 0, (g*x), v*v+Sqrt(root));
+	} else {
+		var angle = Angle(0, 0, (g*x), v*v-Sqrt(root));
+	}
+	if(t){
+		var xAng = Sin(angle, 100);
+		var yAng = Cos(angle, 100);
+		angle = Angle(0,0,xAng,yAng);
+		var yDir = Cos( Normalize(angle, -180), -v);
+		var time = 10*( -yDir + Sqrt( (yDir**2) + 2*g*y ) ) / (g) + 1;
+		return [xAng, yAng, time];
+	}
+	return [Sin(angle, 100), Cos(angle, 100)];
 }
\ No newline at end of file
diff -r e9fac2f07888 -r e07cde3a6f89 planet/Tutorial.c4f/Tutorial04.c4s/System.c4g/AIGuardArea.c
--- a/planet/Tutorial.c4f/Tutorial04.c4s/System.c4g/AIGuardArea.c	Tue Oct 05 16:05:31 2010 -0400
+++ b/planet/Tutorial.c4f/Tutorial04.c4s/System.c4g/AIGuardArea.c	Tue Oct 05 16:44:10 2010 -0400
@@ -4,7 +4,7 @@
 
 public func AI_GuardArea(int x, int y, int wdt, int hgt)
 {
-	var effect = AddEffect("IntAIGuardArea", this, 100, 5, this);
+	var effect = AddEffect("IntAIGuardArea", this, 100, 20, this);
 	EffectVar(0, this, effect) = x;
 	EffectVar(1, this, effect) = y;
 	EffectVar(2, this, effect) = wdt;
diff -r e9fac2f07888 -r e07cde3a6f89 planet/Tutorial.c4f/Tutorial04.c4s/System.c4g/AIJavelinAttack.c
--- a/planet/Tutorial.c4f/Tutorial04.c4s/System.c4g/AIJavelinAttack.c	Tue Oct 05 16:05:31 2010 -0400
+++ b/planet/Tutorial.c4f/Tutorial04.c4s/System.c4g/AIJavelinAttack.c	Tue Oct 05 16:44:10 2010 -0400
@@ -2,8 +2,11 @@
 
 #appendto Javelin
 
+local spread_x;
+local spread_y;
+local lob_shot;
 
-public func AI_WeaponSpray() { return 10; }
+public func AI_Spread(){return 10;}
 public func AI_IsRangedWeapon() { return true; }
 public func AI_IsLoaded() { return true; }
 public func AI_CommandString() { return "AI_JavelinAttack"; }
@@ -11,15 +14,42 @@
 {
 	var v = Contained()->GetPhysical("Throw") / 800;
 	var x = target->GetX() - Contained()->GetX();
-	var y = target->GetY() - Contained()->GetY() + 10;
-	if (AI_AimPos(x, y, v) == nil)
-		return false;
-	return true;
+	var y = target->GetY() - Contained()->GetY();
+	
+	lob_shot = false;
+	var angle = AI_AimPos(x, y, v, lob_shot, true);
+	if (angle == nil) return false;
+	
+	var tolerance = 20**2; // tolerance = margin for error (in px) squared
+	
+	var end = SimFlight(nil, nil, angle[0], angle[1], 1, nil, angle[2]);
+	//Log("end: %d, %d, %d, %d, %d", end[0], end[1], end[2], end[3], end[4]);
+	
+	var error = (target->GetX() - end[0])**2 + (target->GetY() - end[1])**2;
+	//Log("error:%d", error);
+	
+	if(error < tolerance) return true;
+	
+	//Log("Low shot misses");
+	// Oh, the straight shot didn't work. 
+	// Can I shoot over the obstacle instead?
+	lob_shot = true;
+	angle = AI_AimPos(x, y, v, lob_shot, true);
+	//Log("angle: %d, %d, %d",angle[0],angle[1],angle[2]);
+	
+	end = SimFlight(nil, nil, angle[0], angle[1], 1, nil, angle[2]);
+	if (end == nil) return false;
+	
+	error = (target->GetX() - end[0])**2 + (target->GetY() - end[1])**2;
+	
+	if(error < tolerance) return true;
+	return false;
 }
 
 protected func AI_JavelinAttack(object clonk, int x, int y, object target)
 {
 	clonk->AI_Log("Javelin attack on %v", target);
+	
 	// Throw a javelin.
 	AddEffect("AI_JavelinAim", clonk, 100, 1, this, nil, target);
 
@@ -35,13 +65,19 @@
 	EffectVar(0, clonk, num) = target;
 	var dx = target->GetX() - clonk->GetX();
 	var dy = target->GetY() - clonk->GetY() + 10;
-	var angle = AI_AimPos(dx, dy, clonk->GetPhysical("Throw") / 800, AI_WeaponSpray());
+	var angle = AI_AimPos(dx, dy, clonk->GetPhysical("Throw") / 800, lob_shot);
+	
 	if (angle == nil)
 	{
 		ControlUseCancel(clonk);
 		return -1;
 	}
-	ControlUseStart(clonk, angle[0], angle[1]);
+	
+	//set random factors here, so it doesn't stutter all over the place.
+	spread_x = Random(2*AI_Spread())-AI_Spread();
+	spread_y = Random(2*AI_Spread())-AI_Spread();
+	
+	ControlUseStart(clonk, angle[0]+spread_x, angle[1]+spread_y);
 	return 1;
 }
 
@@ -52,13 +88,13 @@
 	var target = EffectVar(0, clonk, num);
 	var dx = target->GetX() - clonk->GetX();
 	var dy = target->GetY() - clonk->GetY() + 10;
-	var angle = AI_AimPos(dx, dy, clonk->GetPhysical("Throw") / 800, AI_WeaponSpray());
+	var angle = AI_AimPos(dx, dy, clonk->GetPhysical("Throw") / 800, lob_shot);
 	if (angle == nil)
 	{
 		ControlUseCancel(clonk);
 		return -1;
 	}
-	ControlUseHolding(clonk, angle[0], angle[1]);
+	ControlUseHolding(clonk, angle[0]+spread_x, angle[1]+spread_y);
 	return 1;
 }
 
@@ -67,25 +103,37 @@
 	var target = EffectVar(0, clonk, num);
 	var dx = target->GetX() - clonk->GetX();
 	var dy = target->GetY() - clonk->GetY() + 10;
-	var angle = AI_AimPos(dx, dy, clonk->GetPhysical("Throw") / 800, AI_WeaponSpray());
+	var angle = AI_AimPos(dx, dy, clonk->GetPhysical("Throw") / 800, lob_shot);
 	if (angle == nil)
 	{
 		ControlUseCancel(clonk);
 		return -1;
 	}
-	ControlUseStop(clonk, angle[0], angle[1]);
+	ControlUseStop(clonk, angle[0]+spread_x, angle[1]+spread_y);
 	return 1;
 }
 
 //v = "muzzle speed" (speed at which the projectile is launched)
-//spread = variation in aim
-private func AI_AimPos(int x, int y, int v, int spread)
+//returns an array of x, y or x, y, t
+private func AI_AimPos(int x, int y, int v, bool lob, bool t)
 {
 	var g = GetGravity()/5; //FnGetGravity() multiplies actual gravity by 500
 	var root = (v**4 - g*(g*x*x - 2*y*v*v));
-	if (root < 0)
+	if(root < 0)
 		return nil;
-	var angle = Angle(0,0,(g*x),v*v-Sqrt(root));
-	//allows for finer variation than adding Random(2*spread)-spread to the angle
-    return [Sin(angle, 100)+Random(2*spread)-spread, Cos(angle, 100)+Random(2*spread)-spread];
+	if(lob)
+	{
+		var angle = Angle(0, 0, (g*x), v*v+Sqrt(root));
+	} else {
+		var angle = Angle(0, 0, (g*x), v*v-Sqrt(root));
+	}
+	if(t){
+		var xAng = Sin(angle, v);
+		var yAng = Cos(angle, v);
+		angle = Angle(0,0,xAng,yAng);
+		var yDir = -Cos( Normalize(angle, -180), v);
+		var time = 10*( -yDir + Sqrt( (yDir**2) + 2*g*y ) ) / (g) + 1;
+		return [xAng, yAng, time];
+	}
+	return [Sin(angle, v), Cos(angle, v)];
 }
\ No newline at end of file
diff -r e9fac2f07888 -r e07cde3a6f89 planet/Tutorial.c4f/Tutorial04.c4s/System.c4g/AIMusketAttack.c
--- a/planet/Tutorial.c4f/Tutorial04.c4s/System.c4g/AIMusketAttack.c	Tue Oct 05 16:05:31 2010 -0400
+++ b/planet/Tutorial.c4f/Tutorial04.c4s/System.c4g/AIMusketAttack.c	Tue Oct 05 16:44:10 2010 -0400
@@ -8,7 +8,9 @@
 public func AI_CanHitTarget(object target) 
 {
 	var dist = ObjectDistance(target, Contained());
-	return dist < 400;
+	if(dist < 400) return false;
+	//TODO: prevent shooting through solids
+	return true;
 }
 
 protected func AI_MusketAttack(object clonk, int x, int y, object target)
