2024-04-24 00:12 CEST

View Issue Details Jump to Notes ]
IDProjectCategoryView StatusLast Update
0000363Spring engineGeneralpublic2007-01-28 05:42
Reporterlippy 
Assigned ToILMTitan 
PrioritynormalSeverityminorReproducibilityalways
StatusresolvedResolutionfixed 
Product Version 
Target VersionFixed in Version 
Summary0000363: [patch] Attacking from height with unit with projectile weapon
DescriptionPosted here on forum: http://taspring.clan-sy.com/phpbb/viewtopic.php?t=8748

Here is my original post:

When you have a unit with a projectile weapon on high ground it allows it to shoot further (The attack range reflects this) When the unit sees an enemy within it's range it will automatically fire at it. However when you try and manually tell it to fire at the enemy within its extended range, it will start moving towards the unit until it comes into its range as it would be on flat ground.
For example: place armfido and radar on top of hill in small divide, place an enemy just within its extended range; the fido will fire at it automatically, but if you manually try to attack it, the fido walks ALL the way down the hill and then fires.

The fire stance does not affect this behaviour (i.e. using hold position does not help) nor does LOS (i.e. same thing happens with LOS or just radar dot)
TagsNo tags attached.
Checked infolog.txt for Errors
Attached Files
  • patch file icon MobileCAI.patch (7,407 bytes) 2007-01-07 04:01 -
    Index: MobileCAI.cpp
    ===================================================================
    --- MobileCAI.cpp	(revision 3084)
    +++ MobileCAI.cpp	(working copy)
    @@ -455,32 +455,43 @@
     void CMobileCAI::ExecuteAttack(Command &c)
     {
     	assert(owner->unitDef->canAttack);
    -	if(tempOrder && owner->moveState < 2){		//limit how far away we fly
    -		if(orderTarget && LinePointDist(commandPos1,commandPos2,orderTarget->pos) > 500 + owner->maxRange){
    +
    +	if ((tempOrder) && (owner->moveState < 2)) {
    +		// limit how far away we fly
    +		if ((orderTarget) && LinePointDist(commandPos1, commandPos2, orderTarget->pos) > (500 + owner->maxRange)) {
     			StopMove();
     			FinishCommand();
     			return;
     		}
     	}
    -	if(!inCommand){
    -		if(c.params.size()==1){
    -			if(uh->units[int(c.params[0])] != 0 && uh->units[int(c.params[0])] != owner){
    -				float3 fix=uh->units[int(c.params[0])]->pos+owner->posErrorVector*128;
    +
    +	// check if we are in direct command of attacker
    +	if (!inCommand) {
    +		if (c.params.size() == 1) {
    +			// check if we have valid target parameter and that we aren't attacking ourselves
    +			if (uh->units[int(c.params[0])] != 0 && uh->units[int(c.params[0])] != owner) {
    +				float3 fix = uh->units[int(c.params[0])]->pos + owner->posErrorVector * 128;
     				SetGoal(fix, owner->pos);
    -				orderTarget=uh->units[int(c.params[0])];
    +				// get ID of attack-order target unit
    +				orderTarget = uh->units[int(c.params[0])];
     				AddDeathDependence(orderTarget);
    -				inCommand=true;
    -			} else {
    -				StopMove();		//cancel keeppointingto
    +				inCommand = true;
    +			}
    +			else {
    +				// unit may not fire on itself
    +				StopMove();
     				FinishCommand();
     				return;
     			}
    -		} else {
    -			float3 pos(c.params[0],c.params[1],c.params[2]);
    +		}
    +		else {
    +			// user gave force-fire attack command
    +			float3 pos(c.params[0], c.params[1], c.params[2]);
     			SetGoal(pos, owner->pos);
    -			inCommand=true;
    +			inCommand = true;
     		}
    -	} else if ((c.params.size() == 3) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) {
    +	}
    +	else if ((c.params.size() == 3) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) {
     		// the trailing CMD_SET_WANTED_MAX_SPEED in a command pair does not count
     		if ((commandQue.size() != 2) || (commandQue.back().id != CMD_SET_WANTED_MAX_SPEED)) {
     			StopMove();
    @@ -489,45 +500,102 @@
     		}
     	}
     
    -	if(targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)){
    -		StopMove();		//cancel keeppointingto
    +	// if our target is dead or we lost it then stop attacking
    +	if (targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)) {
    +		// cancel keeppointingto
    +		StopMove();
     		FinishCommand();
     		return;
     	}
    -	if(orderTarget){
    -		//note that we handle aircrafts slightly differently
    -		if((((owner->AttackUnit(orderTarget, c.id==CMD_DGUN)
    -				&& owner->weapons.size() > 0 
    -				&& owner->weapons.front()->range -
    -					owner->weapons.front()->relWeaponPos.Length() >
    -					orderTarget->pos.distance(owner->pos))
    -				|| dynamic_cast<CTAAirMoveType*>(owner->moveType))
    -				&& (owner->pos-orderTarget->pos).Length2D() <
    -					owner->maxRange*0.9f)
    -				|| (owner->pos-orderTarget->pos).SqLength2D()<1024){
    +
    +
    +	// user clicked on enemy unit (note that we handle aircrafts slightly differently)
    +	if (orderTarget) {
    +		CWeapon* w;
    +		bool b1 = owner->AttackUnit(orderTarget, c.id == CMD_DGUN);
    +		bool b2 = owner->weapons.size() > 0;
    +		bool b3 = false;
    +		bool b4 = dynamic_cast<CTAAirMoveType*>(owner->moveType);
    +		bool b5 = (owner->pos - orderTarget->pos).Length2D() < (owner->maxRange * 0.9f);
    +		bool b6 = (owner->pos - orderTarget->pos).SqLength2D() < 1024;
    +
    +		if (b2) {
    +			// only do this if we have at least one weapon
    +			w = owner->weapons.front();
    +			b3 = (w->range - (w->relWeaponPos).Length()) > (orderTarget->pos.distance(owner->pos));
    +		}
    +
    +		float d1 = (orderTarget->pos + owner->posErrorVector * 128).distance2D(goalPos);
    +		float d2 = (10 + orderTarget->pos.distance2D(owner->pos) * 0.2f);
    +
    +		// if AttackUnit() returned true then we are already
    +		// in range with at least one weapon so stop moving
    +		// NOTE: add HoldPosition condition check as well?
    +		if (b1 && b2) {
     			StopMove();
    -			owner->moveType->KeepPointingTo(orderTarget,
    -				min((float)(owner->losRadius*SQUARE_SIZE*2),
    -				owner->maxRange*0.9f), true);
    -		} else if((orderTarget->pos+owner->posErrorVector*128).distance2D(goalPos) > 10+orderTarget->pos.distance2D(owner->pos)*0.2f){
    -			float3 fix=orderTarget->pos+owner->posErrorVector*128;
    +			owner->moveType->KeepPointingTo(orderTarget, min((float) (owner->losRadius * SQUARE_SIZE * 2), owner->maxRange * 0.9f), true);
    +		}
    +
    +//		if (((((b1 && b2 && b3)) || b4) && b5) || b6) { StopMove(); KeepPointingTo(); }
    +
    +		// if (((first weapon range minus first weapon length greater than distance to target
    +		// or our movetype has type TAAirMoveType) and length of 2D vector from us to target
    +		// less than 90% of our maximum range) OR squared length of 2D vector from us to target
    +		// less than 1024) then we are close enough
    +		else if (((((b3)) || b4) && b5) || b6) {
    +			StopMove();
    +			owner->moveType->KeepPointingTo(orderTarget, min((float) (owner->losRadius * SQUARE_SIZE * 2), owner->maxRange * 0.9f), true);
    +		}
    +
    +		// if 2D distance of (target position plus attacker error vector times 128) to goal position
    +		// greater than (10 plus 20% of 2D distance between attacker and target) then we need to close
    +		// in on target more
    +		else if (d1 > d2) {
    +			float3 fix = orderTarget->pos + owner->posErrorVector * 128;
     			SetGoal(fix, owner->pos);
     		}
    -	} else {
    -		float3 pos(c.params[0],c.params[1],c.params[2]);
    -		if((owner->AttackGround(pos,c.id==CMD_DGUN) && owner->weapons.size() > 0
    -				&& (owner->pos-pos).Length() < 
    -					owner->weapons.front()->range -
    -					owner->weapons.front()->relWeaponPos.Length())
    -				|| (owner->pos-pos).SqLength2D()<1024){
    +	}
    +
    +	// user is attacking ground
    +	else {
    +		float3 pos(c.params[0], c.params[1], c.params[2]);
    +
    +		CWeapon* w;
    +		bool b1 = owner->AttackGround(pos, c.id == CMD_DGUN);
    +		bool b2 = owner->weapons.size() > 0;
    +		bool b3 = false;
    +		bool b4 = (owner->pos - pos).SqLength2D() < 1024;
    +
    +		if (b2) {
    +			// only do this if we have at least one weapon
    +			w = owner->weapons.front();
    +			b3 = (owner->pos - pos).Length() < (w->range - (w->relWeaponPos).Length());
    +		}
    +
    +		// if AttackGround() returned true then we are already
    +		// in range with at least one weapon so stop moving
    +		if (b1 && b2) {
     			StopMove();
    -			owner->moveType->KeepPointingTo(pos, owner->maxRange*0.9f, true);
    -		} else if(pos.distance2D(goalPos)>10){
    +			owner->moveType->KeepPointingTo(pos, owner->maxRange * 0.9f, true);
    +		}
    +
    +//		if ((b1 && b2 && b3) || b4) { StopMove(); KeepPointingTo(); }
    +
    +		// if (length of 3D vector from our pos. to attack pos. less than first weapon range minus first weapon length
    +		// OR squared length of 2D vector from our pos. to attack pos. less than 1024) then we are close enough
    +		else if ((b3) || b4) {
    +			StopMove();
    +			owner->moveType->KeepPointingTo(pos, owner->maxRange * 0.9f, true);
    +		}
    +
    +		// if we are more than 10 units distant from target position then keeping moving closer
    +		else if (pos.distance2D(goalPos) > 10) {
     			SetGoal(pos, owner->pos);
     		}
     	}
     }
     
    +
     int CMobileCAI::GetDefaultCmd(CUnit* pointed, CFeature* feature)
     {
     	if (pointed) {
    
    patch file icon MobileCAI.patch (7,407 bytes) 2007-01-07 04:01 +
  • patch file icon MobileCAIv2.patch (7,610 bytes) 2007-01-28 01:48 -
    Index: MobileCAI.cpp
    ===================================================================
    --- MobileCAI.cpp	(revision 3337)
    +++ MobileCAI.cpp	(working copy)
    @@ -449,38 +449,54 @@
     }
     
     
    +
    +
     /**
     * @brief Causes this CMobileCAI to execute the attack order c
     */
     void CMobileCAI::ExecuteAttack(Command &c)
     {
     	assert(owner->unitDef->canAttack);
    -	if(tempOrder && owner->moveState < 2){		//limit how far away we fly
    -		if(orderTarget && LinePointDist(commandPos1,commandPos2,orderTarget->pos) > 500 + owner->maxRange){
    +
    +	if ((tempOrder) && (owner->moveState < 2)) {
    +		// limit how far away we fly
    +		if ((orderTarget) && LinePointDist(commandPos1, commandPos2, orderTarget->pos) > (500 + owner->maxRange)) {
     			StopMove();
     			FinishCommand();
     			return;
     		}
     	}
    -	if(!inCommand){
    -		if(c.params.size()==1){
    -			if(uh->units[int(c.params[0])] != 0 && uh->units[int(c.params[0])] != owner){
    -				float3 fix=uh->units[int(c.params[0])]->pos+owner->posErrorVector*128;
    +
    +
    +	// check if we are in direct command of attacker
    +	if (!inCommand) {
    +		if (c.params.size() == 1) {
    +			int unitID = int(c.params[0]);
    +
    +			// check if we have valid target parameter and that we aren't attacking ourselves
    +			if (uh->units[unitID] != 0 && uh->units[unitID] != owner) {
    +				float3 fix = uh->units[unitID]->pos + owner->posErrorVector * 128;
     				SetGoal(fix, owner->pos);
    -				orderTarget=uh->units[int(c.params[0])];
    +				// get ID of attack-order target unit
    +				orderTarget = uh->units[unitID];
     				AddDeathDependence(orderTarget);
    -				inCommand=true;
    -			} else {
    -				StopMove();		//cancel keeppointingto
    +				inCommand = true;
    +			}
    +			else {
    +				// unit may not fire on itself, cancel order
    +				StopMove();
     				FinishCommand();
     				return;
     			}
    -		} else {
    -			float3 pos(c.params[0],c.params[1],c.params[2]);
    -			SetGoal(pos, owner->pos);
    -			inCommand=true;
     		}
    -	} else if ((c.params.size() == 3) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) {
    +		else {
    +			// user gave force-fire attack command
    +			float3 pos(c.params[0], c.params[1], c.params[2]);
    + 			SetGoal(pos, owner->pos);
    +			inCommand = true;
    +		}
    +	}
    +	else if ((c.params.size() == 3) && (owner->commandShotCount > 0) && (commandQue.size() > 1)) {
     		// the trailing CMD_SET_WANTED_MAX_SPEED in a command pair does not count
     		if ((commandQue.size() != 2) || (commandQue.back().id != CMD_SET_WANTED_MAX_SPEED)) {
     			StopMove();
    @@ -489,45 +505,102 @@
     		}
     	}
     
    -	if(targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)){
    -		StopMove();		//cancel keeppointingto
    +	// if our target is dead or we lost it then stop attacking
    +	// NOTE: unit should actually just continue to target area!
    +	if (targetDied || (c.params.size() == 1 && UpdateTargetLostTimer(int(c.params[0])) == 0)) {
    +		// cancel keeppointingto
    +		StopMove();
     		FinishCommand();
     		return;
     	}
    -	if(orderTarget){
    -		//note that we handle aircrafts slightly differently
    -		if((((owner->AttackUnit(orderTarget, c.id==CMD_DGUN)
    -				&& owner->weapons.size() > 0 
    -				&& owner->weapons.front()->range -
    -					owner->weapons.front()->relWeaponPos.Length() >
    -					orderTarget->pos.distance(owner->pos))
    -				|| dynamic_cast<CTAAirMoveType*>(owner->moveType))
    -				&& (owner->pos-orderTarget->pos).Length2D() <
    -					owner->maxRange*0.9f)
    -				|| (owner->pos-orderTarget->pos).SqLength2D()<1024){
    +
    +
    +	// user clicked on enemy unit (note that we handle aircrafts slightly differently)
    +	if (orderTarget) {
    +		bool b1 = owner->AttackUnit(orderTarget, c.id == CMD_DGUN);
    +		bool b2 = false;
    +		bool b3 = false;
    +		bool b4 = dynamic_cast<CTAAirMoveType*>(owner->moveType);
    +		bool b5 = (owner->pos - orderTarget->pos).Length2D() < (owner->maxRange * 0.9f);
    +		bool b6 = (owner->pos - orderTarget->pos).SqLength2D() < 1024;
    +
    +		float d1 = (orderTarget->pos + owner->posErrorVector * 128).distance2D(goalPos);
    +		float d2 = (10 + orderTarget->pos.distance2D(owner->pos) * 0.2f);
    +
    +		if (owner->weapons.size() > 0) {
    +			// if we have at least one weapon then check if we
    +			// can hit target with our first (meanest) one
    +			CWeapon* w = owner->weapons.front();
    +			b2 = w->AttackUnit(orderTarget, c.id == CMD_DGUN);
    +			b3 = (w->range - (w->relWeaponPos).Length()) > (orderTarget->pos.distance(owner->pos));
    +		}
    +
    +		// if w->AttackUnit() returned true then we are already
    +		// in range with our biggest weapon so stop moving
    +		if (b1 && b2 && b3) {
     			StopMove();
    -			owner->moveType->KeepPointingTo(orderTarget,
    -				min((float)(owner->losRadius*SQUARE_SIZE*2),
    -				owner->maxRange*0.9f), true);
    -		} else if((orderTarget->pos+owner->posErrorVector*128).distance2D(goalPos) > 10+orderTarget->pos.distance2D(owner->pos)*0.2f){
    -			float3 fix=orderTarget->pos+owner->posErrorVector*128;
    +			owner->moveType->KeepPointingTo(orderTarget, min((float) (owner->losRadius * SQUARE_SIZE * 2), owner->maxRange * 0.9f), true);
    +		}
    +
    +		// if (((first weapon range minus first weapon length greater than distance to target
    +		// or our movetype has type TAAirMoveType) and length of 2D vector from us to target
    +		// less than 90% of our maximum range) OR squared length of 2D vector from us to target
    +		// less than 1024) then we are close enough
    +		else if (((b3 || b4) && b5) || b6) {
    +			StopMove();
    +			owner->moveType->KeepPointingTo(orderTarget, min((float) (owner->losRadius * SQUARE_SIZE * 2), owner->maxRange * 0.9f), true);
    +		}
    +
    +		// if 2D distance of (target position plus attacker error vector times 128) to goal position
    +		// greater than (10 plus 20% of 2D distance between attacker and target) then we need to close
    +		// in on target more
    +		else if (d1 > d2) {
    +			float3 fix = orderTarget->pos + owner->posErrorVector * 128;
     			SetGoal(fix, owner->pos);
     		}
    -	} else {
    -		float3 pos(c.params[0],c.params[1],c.params[2]);
    -		if((owner->AttackGround(pos,c.id==CMD_DGUN) && owner->weapons.size() > 0
    -				&& (owner->pos-pos).Length() < 
    -					owner->weapons.front()->range -
    -					owner->weapons.front()->relWeaponPos.Length())
    -				|| (owner->pos-pos).SqLength2D()<1024){
    +	}
    +
    +	// user is attacking ground
    +	else {
    +		float3 pos(c.params[0], c.params[1], c.params[2]);
    +
    +		bool b1 = owner->AttackGround(pos, c.id == CMD_DGUN);
    +		bool b2 = false;
    +		bool b3 = false;
    +		bool b4 = (owner->pos - pos).SqLength2D() < 1024;
    +
    +		if (owner->weapons.size() > 0) {
    +			// if we have at least one weapon then check if
    +			// we can hit position with our first (meanest) one
    +			CWeapon* w = owner->weapons.front();
    +			b2 = w->AttackGround(pos, c.id == CMD_DGUN);
    +			b3 = (owner->pos - pos).Length() < (w->range - (w->relWeaponPos).Length());
    +		}
    +
    +		// if w->AttackGround() returned true then we are already
    +		// in range with our biggest weapon so stop moving
    +		if (b1 && b2 && b3) {
     			StopMove();
    -			owner->moveType->KeepPointingTo(pos, owner->maxRange*0.9f, true);
    -		} else if(pos.distance2D(goalPos)>10){
    +			owner->moveType->KeepPointingTo(pos, owner->maxRange * 0.9f, true);
    +		}
    +
    +		// if (length of 3D vector from our pos. to attack pos. less than first weapon range minus first weapon length
    +		// OR squared length of 2D vector from our pos. to attack pos. less than 1024) then we are close enough
    +		else if (b3 || b4) {
    +			StopMove();
    +			owner->moveType->KeepPointingTo(pos, owner->maxRange * 0.9f, true);
    +		}
    +
    +		// if we are more than 10 units distant from target position then keeping moving closer
    +		else if (pos.distance2D(goalPos) > 10) {
     			SetGoal(pos, owner->pos);
     		}
     	}
     }
     
    +
    +
    +
     int CMobileCAI::GetDefaultCmd(CUnit* pointed, CFeature* feature)
     {
     	if (pointed) {
    
    patch file icon MobileCAIv2.patch (7,610 bytes) 2007-01-28 01:48 +

-Relationships
+Relationships

-Notes

~0000536

Kloot (developer)

Last edited: 2007-01-07 12:00

Patch uploaded.

Important note: ALL units will now only move as close to a target as necessary for their longest-ranged weapon to hit it (if the target isn't already in range) when told to attack; you'll have to tell them explicitly to get closer if you don't want the range penalties.

(Sorry for again mixing whitespace cleanup with the actual patch code, but this function wasn't very readable in its original form.)

EDIT: just realized the two CWeapon* temp vars are not initialized to zero, so the patch needs to be patched a bit I'm afraid :]

~0000551

Kloot (developer)

Will this still be committed? I have another patch which prevents units from stopping in their tracks when told to attack a target and the target dies while they are enroute (as many people consider this a bug), but it modifies the same function, so having this one applied first would be preferable.

~0000552

lippy (reporter)

Was going to ask the same thing. Kloot, has the patch been patched, to fix the initialization of the temp vars? (the Issue history has no record of an updated MobileCAI)

BTW thanks a lot for fixing this! (It was very annoying indeed)

~0000553

ILMTitan (reporter)

For most of my additions having to do with weapons, I had been working under the convention that weapon.front() was the one that mattered.

For instance, if we give an attack order to a lightning tank, we want it to run up and blast away with it's lightning gun, not peck from a distance with it's AA missiles.

Conversely, we don't want an Artillery piece to run up to a tank because it has a light machine gun on it as well.

I agree that neither firing nor movement stance should affect this behavior.

~0000556

KDR_11k (reporter)

The first weapon should always be the one that matters. Would be nice if it was script-adjustable to use another weapon as the reference in case your units have weapons that get disabled or enabled under certain conditions.

~0000589

lippy (reporter)

So how's progress (if any) on this going?

~0000633

Kloot (developer)

New patch uploaded by request. The code now checks if a unit can hit its target with the weapon that comes first in its weapon-list (which is assumed to be the one that matters, eg. the Panther's lightning gun) rather than with *any* weapon, and will only move closer if it cannot.

~0000636

ILMTitan (reporter)

Patch committed. Thank you.

I removed the weapon range check from the unit stop condition (but kept the weapon can hit check). I also inlined some variables so they are only calculated when needed.
+Notes

-Issue History
Date Modified Username Field Change
2007-01-05 03:30 lippy New Issue
2007-01-07 04:01 Kloot Note Added: 0000536
2007-01-07 04:01 Kloot File Added: MobileCAI.patch
2007-01-07 12:00 Kloot Note Edited: 0000536
2007-01-11 21:38 Kloot Note Added: 0000551
2007-01-12 01:43 lippy Note Added: 0000552
2007-01-12 06:11 ILMTitan Note Added: 0000553
2007-01-13 10:40 KDR_11k Note Added: 0000556
2007-01-20 02:07 lippy Note Added: 0000589
2007-01-27 16:05 tvo Summary Attacking from height with unit with projectile weapon => [patch] Attacking from height with unit with projectile weapon
2007-01-28 01:48 Kloot File Added: MobileCAIv2.patch
2007-01-28 01:53 Kloot Note Added: 0000633
2007-01-28 04:24 ILMTitan Status new => assigned
2007-01-28 04:24 ILMTitan Assigned To => ILMTitan
2007-01-28 05:42 ILMTitan Status assigned => resolved
2007-01-28 05:42 ILMTitan Resolution open => fixed
2007-01-28 05:42 ILMTitan Note Added: 0000636
+Issue History