/**
**  Enemy units in distance.
**
**  @param player  Find enemies of this player
**  @param type    Optional unit type to check if enemy can target this
**  @param pos     location
**  @param range   Distance range to look.
**
**  @return       Number of enemy units.
*/
int AiEnemyUnitsInDistance(const CPlayer *player,
		const CUnitType *type, const Vec2i &pos, unsigned range)
{
	// Select all units in range.
	CUnit *table[UnitMax];
	const unsigned int n = Map.Select(pos.x - range, pos.y - range,
		pos.x + range + (type ? type->TileWidth :0),
		pos.y + range + (type ? type->TileHeight:0), table);

	// Find the enemy units which can attack
	int e = 0;
	for (unsigned int i = 0; i < n; ++i) {
		const CUnit *dest = table[i];
		// Those can't attack anyway.
		if (dest->Removed || dest->Variable[INVISIBLE_INDEX].Value ||
			dest->CurrentAction() == UnitActionDie) {
			continue;
		}
		if (!player->IsEnemy(*dest)) { // a friend or neutral
			continue;
		}
		// Unit can attack back?
		if (!type || CanTarget(dest->Type, type)) {
			++e;
		}
	}
	return e;
}
void
FighterTacticalAI::SelectTargetDirected(Ship* tgt)
{
	Ship* potential_target = tgt;

	if (!tgt) {
		// try to target one of the element's objectives
		// (if it shows up in the contact list)

		Element* elem = ship->GetElement();

		if (elem) {
			Instruction* objective = elem->GetTargetObjective();

			if (objective) {
				SimObject* obj_sim_obj = objective->GetTarget();
				Ship*      obj_tgt     = 0;

				if (obj_sim_obj && obj_sim_obj->Type() == SimObject::SIM_SHIP)
				obj_tgt = (Ship*) obj_sim_obj;

				if (obj_tgt && ship->FindContact(obj_tgt))
				potential_target = obj_tgt;
			}
		}
	}

	if (!CanTarget(potential_target))
	potential_target = 0;

	ship_ai->SetTarget(potential_target);
	SelectSecondaryForTarget(potential_target);
}
/**
**  Choose target on map area.
**
**  @param source  Unit which want to attack.
**  @param x1      X position on map, tile-based.
**  @param y1      Y position on map, tile-based.
**  @param x2      X position on map, tile-based.
**  @param y2      Y position on map, tile-based.
**
**  @return        Returns ideal target on map tile.
*/
CUnit *TargetOnMap(const CUnit *source, int x1, int y1, int x2, int y2)
{
	CUnit *table[UnitMax];
	CUnit *best = NoUnitP;
	int n;

	n = UnitCache.Select(x1, y1, x2, y2, table, UnitMax);
	for (int i = 0; i < n; ++i) {
		CUnit *unit = table[i];
		if (!unit->IsVisibleAsGoal(source->Player)) {
			continue;
		}
		if (x2 < unit->X || x1 >= unit->X + unit->Type->TileWidth ||
				y2 < unit->Y || y1 >= unit->Y + unit->Type->TileHeight) {
			continue;
		}
		if (!CanTarget(source->Type, unit->Type)) {
			continue;
		}

		//
		// Choose the best target.
		//
		if (!best || best->Type->Priority < unit->Type->Priority) {
			best = unit;
		}
	}
	return best;
}
Exemple #4
0
/**
**  Called if a member of Group is Attacked
**
**  @param attacker  Pointer to attacker unit.
**  @param defender  Pointer to unit that is being attacked.
*/
void GroupHelpMe(CUnit *attacker, CUnit &defender)
{
	/* Freandly Fire - typical splash */
	if (!attacker || attacker->Player->Index == defender.Player->Index) {
		return;
	}

	DebugPrint("%d: GroupHelpMe %d(%s) attacked at %d,%d\n" _C_
		defender.Player->Index _C_ UnitNumber(defender) _C_
		defender.Type->Ident.c_str() _C_ defender.tilePos.x _C_ defender.tilePos.y);

	//
	//  Don't send help to scouts (zeppelin,eye of vision).
	//
	if (!defender.Type->CanAttack && defender.Type->UnitType == UnitTypeFly) {
		return;
	}

	if (defender.GroupId) {
		int mask = 0;
		CUnitGroup *group;
		for (int num = 0; num < NUM_GROUPS; ++num) {

			//  Unit belongs to an group, check if brothers in arms can help
			if (defender.GroupId & (1<<num)) {
				mask |= (1<<num);
				group = &Groups[num];

				for (int i = 0; i < group->NumUnits; ++i) {
					CUnit &gunit = *group->Units[i];

					if (&defender == &gunit) {
						continue;
					}

					// if brother is idle or attack no-agressive target and
					// can attack our attacker then ask for help
					if (gunit.IsAgressive() && (gunit.IsIdle() ||
						!(gunit.CurrentAction() == UnitActionAttack &&
						gunit.CurrentOrder()->HasGoal() &&
						gunit.CurrentOrder()->GetGoal()->IsAgressive()))
						&& CanTarget(gunit.Type, attacker->Type)) {

						if (gunit.SavedOrder.Action == UnitActionStill) {
							// FIXME: should rewrite command handling
							CommandAttack(gunit, gunit.tilePos, NoUnitP, FlushCommands);
							gunit.SavedOrder = *gunit.Orders[1];
						}
						CommandAttack(gunit, attacker->tilePos, attacker, FlushCommands);
					}
				}
				if (!(defender.GroupId & ~mask)) {
					return;
				}
			}
		}
	}
}
void
TacticalAI::SelectTargetDirected(Ship* tgt)
{
    Ship* potential_target = tgt;

    // try to target one of the element's objectives
    // (if it shows up in the contact list)

    if (!tgt) {
        Element* elem = ship->GetElement();

        if (elem) {
            Instruction* objective = elem->GetTargetObjective();

            if (objective) {
                SimObject* obj_sim_obj = objective->GetTarget();
                Ship*      obj_tgt     = 0;

                if (obj_sim_obj && obj_sim_obj->Type() == SimObject::SIM_SHIP)
                obj_tgt = (Ship*) obj_sim_obj;

                if (obj_tgt) {
                    ListIter<Contact> contact = ship->ContactList();
                    while (++contact && !potential_target) {
                        Ship* test = contact->GetShip();

                        if (obj_tgt == test) {
                            potential_target = test;
                        }
                    }
                }
            }
        }
    }

    if (!CanTarget(potential_target))
    potential_target = 0;

    ship_ai->SetTarget(potential_target);

    if (tgt && tgt == ship_ai->GetTarget())
    directed_tgtid = tgt->Identity();
    else
    directed_tgtid = 0;
}
Exemple #6
0
/**
**	Choose target at pixel map coordinates.
**
**	@param source	Unit which wants to attack.
**	@param x	X position on the display map, pixel-based.
**	@param y	Y position on the display map, pixel-based.
**
**	@return		Returns ideal target
*/
global Unit* TargetOnScreenMapPosition(const Unit* source,int x,int y)
{
    Unit* table[UnitMax];
    Unit* unit;
    Unit* best;
    int tx;
    int ty;
    int n;
    int i;

    //	this code runs upon right button action only so it can affort being a
    //	little inefficient.

    tx = x / TileSizeX;
    ty = y / TileSizeY;

    //	 ships and flyers could be 2 fields away
    n = UnitCacheSelect(tx-2,ty-2, tx+2, ty+2, table);
    best=NoUnitP;
    for( i=0; i<n; ++i ) {
	unit=table[i];
	// unusable unit ?
	// if( UnitUnusable(unit) ) can't attack constructions
	// FIXME: did SelectUnitsOnTile already filter this?
	// Invisible and not Visible
	if( unit->Removed || unit->Invisible
		|| !(unit->Visible&(1<<source->Player->Player))
		|| unit->Orders[0].Action==UnitActionDie ) {
	    continue;
	}
	if ( !InsideUnitSprite(table[i], x, y)) {
	    continue;
	}
	if( !CanTarget(source->Type,unit->Type) ) {
	    continue;
	}
	//
	//	Choose the best target.
	//
	if( !best || best->Type->Priority < unit->Type->Priority ) {
	    best=unit;
	}
    }
    return best;
}
Exemple #7
0
/**
**	Choose target on map tile.
**
**	@param source	Unit which want to attack.
**	@param tx	X position on map, tile-based.
**	@param ty	Y position on map, tile-based.
**
**	@return		Returns ideal target on map tile.
*/
global Unit* TargetOnMapTile(const Unit* source,int tx,int ty)
{
    Unit* table[UnitMax];
    Unit* unit;
    Unit* best;
    const UnitType* type;
    int n;
    int i;

    n=SelectUnitsOnTile(tx,ty,table);
    best=NoUnitP;
    for( i=0; i<n; ++i ) {
	unit=table[i];
	// unusable unit ?
	// if( UnitUnusable(unit) ) can't attack constructions
	// FIXME: did SelectUnitsOnTile already filter this?
	// Invisible and not Visible
	if( unit->Removed || unit->Invisible || !unit->HP
		|| !(unit->Visible&(1<<source->Player->Player))
		|| unit->Orders[0].Action==UnitActionDie ) {
	    continue;
	}
	type=unit->Type;
	if( tx<unit->X || tx>=unit->X+type->TileWidth
		|| ty<unit->Y || ty>=unit->Y+type->TileHeight ) {
	    continue;
	}
	if( !CanTarget(source->Type,unit->Type) ) {
	    continue;
	}

	//
	//	Choose the best target.
	//
	if( !best || best->Type->Priority<unit->Type->Priority ) {
	    best=unit;
	}
    }
    return best;
}
Exemple #8
0
/**
**  Choose target on map area.
**
**  @param source  Unit which want to attack.
**  @param pos1    position on map, tile-based.
**  @param pos2    position on map, tile-based.
**
**  @return        Returns ideal target on map tile.
*/
CUnit *TargetOnMap(const CUnit &source, const Vec2i &pos1, const Vec2i &pos2)
{
	std::vector<CUnit *> table;

	Select(pos1, pos2, table);
	CUnit *best = NULL;
	for (size_t i = 0; i != table.size(); ++i) {
		CUnit &unit = *table[i];

		if (!unit.IsVisibleAsGoal(*source.Player)) {
			continue;
		}
		if (!CanTarget(*source.Type, *unit.Type)) {
			continue;
		}

		// Choose the best target.
		if (!best || best->Variable[PRIORITY_INDEX].Value < unit.Variable[PRIORITY_INDEX].Value) {
			best = &unit;
		}
	}
	return best;
}
Exemple #9
0
		void Compute(CUnit *const dest)
		{
			const CPlayer &player = *attacker->Player;

			if (!dest->IsVisibleAsGoal(player)) {
				dest->CacheLock = 1;
				return;
			}

			const CUnitType &type =  *attacker->Type;
			const CUnitType &dtype = *dest->Type;
			// won't be a target...
			if (!CanTarget(type, dtype)) { // can't be attacked.
				dest->CacheLock = 1;
				return;
			}
			// Don't attack invulnerable units
			if (dtype.BoolFlag[INDESTRUCTIBLE_INDEX].value || dest->Variable[UNHOLYARMOR_INDEX].Value) {
				dest->CacheLock = 1;
				return;
			}

			//  Calculate the costs to attack the unit.
			//  Unit with the smallest attack costs will be taken.

			int cost = 0;
			int hp_damage_evaluate;
			if (Damage) {
				hp_damage_evaluate = CalculateDamage(*attacker, *dest, Damage);
			} else {
				hp_damage_evaluate = attacker->Stats->Variables[BASICDAMAGE_INDEX].Value
									 + attacker->Stats->Variables[PIERCINGDAMAGE_INDEX].Value;
			}
			if (!player.IsEnemy(*dest)) { // a friend or neutral
				dest->CacheLock = 1;

				// Calc a negative cost
				// The gost is more important when the unit would be killed
				// by our fire.

				// It costs (is positive) if hp_damage_evaluate>dest->HP ...)
				// FIXME : assume that PRIORITY_FACTOR>HEALTH_FACTOR
				cost = HEALTH_FACTOR * (2 * hp_damage_evaluate -
										dest->Variable[HP_INDEX].Value) /
					   (dtype.TileWidth * dtype.TileWidth);
				cost = std::max(cost, 1);
				cost = -cost;
			} else {
				//  Priority 0-255
				cost += dtype.DefaultStat.Variables[PRIORITY_INDEX].Value * PRIORITY_FACTOR;

				for (unsigned int i = 0; i < UnitTypeVar.GetNumberBoolFlag(); i++) {
					if (type.BoolFlag[i].AiPriorityTarget != CONDITION_TRUE) {
						if ((type.BoolFlag[i].AiPriorityTarget == CONDITION_ONLY) &
							(dtype.BoolFlag[i].value)) {
							cost -= AIPRIORITY_BONUS;
						} else if ((type.BoolFlag[i].AiPriorityTarget == CONDITION_FALSE) &
								   (dtype.BoolFlag[i].value)) {
							cost += AIPRIORITY_BONUS;
						}
					}
				}

				//  Remaining HP (Health) 0-65535
				// Give a boost to unit we can kill in one shoot only

				// calculate HP which will remain in the enemy unit, after hit
				int effective_hp = (dest->Variable[HP_INDEX].Value - 2 * hp_damage_evaluate);

				// Unit we won't kill are evaluated the same
				// Unit we are sure to kill are all evaluated the same (except PRIORITY)
				clamp(&effective_hp, -hp_damage_evaluate, 0);

				// Here, effective_hp vary from -hp_damage_evaluate (unit will be killed) to 0 (unit can't be killed)
				// => we prefer killing rather than only hitting...
				cost += -effective_hp * HEALTH_FACTOR;

				//  Unit can attack back.
				if (CanTarget(dtype, type)) {
					cost += CANATTACK_BONUS;
				}

				// the cost may be divided across multiple cells
				cost = cost / (dtype.TileWidth * dtype.TileWidth);
				cost = std::max(cost, 1);

				// Removed Unit's are in bunkers
				int d;
				if (attacker->Removed) {
					d = attacker->Container->MapDistanceTo(*dest);
				} else {
					d = attacker->MapDistanceTo(*dest);
				}

				int attackrange = attacker->Stats->Variables[ATTACKRANGE_INDEX].Max;
				if (d <= attackrange ||
					(d <= range && UnitReachable(*attacker, *dest, attackrange))) {
					++enemy_count;
				} else {
					dest->CacheLock = 1;
				}
				// Attack walls only if we are stuck in them
				if (dtype.BoolFlag[WALL_INDEX].value && d > 1) {
					dest->CacheLock = 1;
				}
			}

			// cost map is relative to attacker position
			const int x = dest->tilePos.x - attacker->tilePos.x + (size / 2);
			const int y = dest->tilePos.y - attacker->tilePos.y + (size / 2);
			Assert(x >= 0 && y >= 0);

			// Mark the good/bad array...
			for (int yy = 0; yy < dtype.TileHeight; ++yy) {
				for (int xx = 0; xx < dtype.TileWidth; ++xx) {
					int pos = (y + yy) * (size / 2) + (x + xx);
					if (pos >= good->size()) {
						printf("BUG: RangeTargetFinder::FillBadGood.Compute out of range. "\
						       "size: %d, pos: %d, " \
						       "x: %d, xx: %d, y: %d, yy: %d",
						       size, pos, x, xx, y, yy);
						break;
					}
					if (cost < 0) {
						good->at(pos) -= cost;
					} else {
						bad->at(pos) += cost;
					}
				}
			}
		}
Exemple #10
0
	int ComputeCost(CUnit *const dest) const
	{
		const CPlayer &player = *attacker->Player;
		const CUnitType &type = *attacker->Type;
		const CUnitType &dtype = *dest->Type;
		const int attackrange = attacker->Stats->Variables[ATTACKRANGE_INDEX].Max;

		if (!player.IsEnemy(*dest) // a friend or neutral
			|| !dest->IsVisibleAsGoal(player)
			|| !CanTarget(type, dtype)) {
			return INT_MAX;
		}
		// Don't attack invulnerable units
		if (dtype.BoolFlag[INDESTRUCTIBLE_INDEX].value || dest->Variable[UNHOLYARMOR_INDEX].Value) {
			return INT_MAX;
		}
		// Unit in range ?
		const int d = attacker->MapDistanceTo(*dest);

		if (d > attackrange && !UnitReachable(*attacker, *dest, attackrange)) {
			return INT_MAX;
		}

		// Attack walls only if we are stuck in them
		if (dtype.BoolFlag[WALL_INDEX].value && d > 1) {
			return INT_MAX;
		}

		if (dtype.UnitType == UnitTypeFly && dest->IsAgressive() == false) {
			return INT_MAX / 2;
		}

		// Calculate the costs to attack the unit.
		// Unit with the smallest attack costs will be taken.
		int cost = 0;

		// Priority 0-255
		cost -= dtype.DefaultStat.Variables[PRIORITY_INDEX].Value * PRIORITY_FACTOR;
		// Remaining HP (Health) 0-65535
		cost += dest->Variable[HP_INDEX].Value * 100 / dest->Variable[HP_INDEX].Max * HEALTH_FACTOR;

		if (d <= attackrange && d >= type.MinAttackRange) {
			cost += d * INRANGE_FACTOR;
			cost -= INRANGE_BONUS;
		} else {
			cost += d * DISTANCE_FACTOR;
		}

		for (unsigned int i = 0; i < UnitTypeVar.GetNumberBoolFlag(); i++) {
			if (type.BoolFlag[i].AiPriorityTarget != CONDITION_TRUE) {
				if ((type.BoolFlag[i].AiPriorityTarget == CONDITION_ONLY) &
					(dtype.BoolFlag[i].value)) {
					cost -= AIPRIORITY_BONUS;
				}
				if ((type.BoolFlag[i].AiPriorityTarget == CONDITION_FALSE) &
					(dtype.BoolFlag[i].value)) {
					cost += AIPRIORITY_BONUS;
				}
			}
		}

		// Unit can attack back.
		if (CanTarget(dtype, type)) {
			cost -= CANATTACK_BONUS;
		}
		return cost;
	}
/**
**  Attack units in distance.
**
**  If the unit can attack must be handled by caller.
**  Choose the best target, that can be attacked.
**
**  @param unit   Find in distance for this unit.
**  @param range  Distance range to look.
**
**  @return       Unit to be attacked.
**
*/
CUnit *AttackUnitsInDistance(const CUnit *unit, int range)
{
	CUnit *dest;
	const CUnitType *type;
	const CUnitType *dtype;
	CUnit *table[UnitMax];
	int x;
	int y;
	int n;
	int i;
	int d;
	int attackrange;
	int cost;
	int best_cost;
	const CPlayer *player;
	CUnit *best_unit;

	// if necessary, take possible damage on allied units into account...
	if (unit->Type->Missile.Missile->Range > 1 &&
			(range + unit->Type->Missile.Missile->Range < 15)) {
		return FindRangeAttack(unit, range);
	}

	//
	// Select all units in range.
	//
	x = unit->X;
	y = unit->Y;
	type = unit->Type;
	n = UnitCache.Select(x - range, y - range, x + range + type->TileWidth,
		y + range + type->TileHeight, table, UnitMax);
	
	if (range > 25 && n > 9) {
		referenceunit = unit;
		qsort((void*)table, n, sizeof(CUnit*), &CompareUnitDistance);
	}

	best_unit = NoUnitP;
	best_cost = INT_MAX;

	player = unit->Player;
	attackrange = unit->Stats->Variables[ATTACKRANGE_INDEX].Max;

	//
	// Find the best unit to attack
	//

	for (i = 0; i < n; ++i) {
		dest = table[i];

		if (!dest->IsVisibleAsGoal(unit->Player)) {
			continue;
		}

		if (!player->IsEnemy(dest)) { // a friend or neutral
			continue;
		}

		dtype = dest->Type;
		if (!CanTarget(type, dtype)) { // can't be attacked.
			continue;
		}

		//
		// Calculate the costs to attack the unit.
		// Unit with the smallest attack costs will be taken.
		//
		cost = 0;
		//
		// Priority 0-255
		//
		cost -= dtype->Priority * PRIORITY_FACTOR;
		//
		// Remaining HP (Health) 0-65535
		//
		cost += dest->Variable[HP_INDEX].Value * HEALTH_FACTOR;
		//
		// Unit in attack range?
		//
		d = MapDistanceBetweenUnits(unit, dest);

		// Use Circle, not square :)
		if (d > range) {
			continue;
		}
		if (d <= attackrange && d >= type->MinAttackRange) {
			cost += d * INRANGE_FACTOR;
			cost -= INRANGE_BONUS;
		} else {
			cost += d * DISTANCE_FACTOR;
		}
		//
		// Unit can attack back.
		//
		if (CanTarget(dtype, type)) {
			cost -= CANATTACK_BONUS;
		}

		//
		// Take this target?
		//
		if (cost < best_cost && (d <= attackrange ||
				UnitReachable(unit, dest, attackrange))) {
			best_unit = dest;
			best_cost = cost;
		}
	}

	return best_unit;
}
Exemple #12
0
/**
**  Called if a Unit is Attacked
**
**  @param attacker  Pointer to attacker unit.
**  @param defender  Pointer to unit that is being attacked.
*/
void AiHelpMe(const CUnit *attacker, CUnit &defender)
{
	/* Friendly Fire - typical splash */
	if (!attacker || attacker->Player->Index == defender.Player->Index) {
		//FIXME - try react somehow
		return;
	}

	DebugPrint("%d: %d(%s) attacked at %d,%d\n" _C_
			   defender.Player->Index _C_ UnitNumber(defender) _C_
			   defender.Type->Ident.c_str() _C_ defender.tilePos.x _C_ defender.tilePos.y);

	//  Don't send help to scouts (zeppelin,eye of vision).
	if (!defender.Type->CanAttack && defender.Type->UnitType == UnitTypeFly) {
		return;
	}
	// Summoned unit, don't help
	if (defender.Summoned) {
		return;
	}

	PlayerAi &pai = *defender.Player->Ai;
	AiPlayer = &pai;

	//  If unit belongs to an attacking force, check if force members can help.
	if (defender.GroupId) {
		AiForce &aiForce = pai.Force[defender.GroupId - 1];

		//  Unit belongs to an force, check if brothers in arms can help
		for (unsigned int i = 0; i < aiForce.Units.size(); ++i) {
			CUnit &aiunit = *aiForce.Units[i];

			if (&defender == &aiunit) {
				continue;
			}

			// if brother is idle or attack no-agressive target and
			// can attack our attacker then ask for help
			// FIXME ad support for help from Coward type units
			if (aiunit.IsAgressive() && CanTarget(*aiunit.Type, *attacker->Type)
				&& aiunit.CurrentOrder()->GetGoal() != attacker) {
				bool shouldAttack = aiunit.IsIdle() && aiunit.Threshold == 0;

				if (aiunit.CurrentAction() == UnitActionAttack) {
					const COrder_Attack &orderAttack = *static_cast<COrder_Attack *>(aiunit.CurrentOrder());
					const CUnit *oldGoal = orderAttack.GetGoal();

					if (oldGoal == NULL || (ThreatCalculate(defender, *attacker) < ThreatCalculate(defender, *oldGoal)
											&& aiunit.MapDistanceTo(defender) <= aiunit.Stats->Variables[ATTACKRANGE_INDEX].Max)) {
						shouldAttack = true;
					}
				}

				if (shouldAttack) {
					CommandAttack(aiunit, attacker->tilePos, const_cast<CUnit *>(attacker), FlushCommands);
					COrder *savedOrder = COrder::NewActionAttack(aiunit, attacker->tilePos);

					if (aiunit.CanStoreOrder(savedOrder) == false) {
						delete savedOrder;
						savedOrder = NULL;
					} else {
						aiunit.SavedOrder = savedOrder;
					}
				}
			}
		}
		if (!aiForce.Defending && aiForce.State > 0) {
			DebugPrint("%d: %d(%s) belong to attacking force, don't defend it\n" _C_
					   defender.Player->Index _C_ UnitNumber(defender) _C_ defender.Type->Ident.c_str());
			// unit belongs to an attacking force,
			// so don't send others force in such case.
			// FIXME: there may be other attacking the same place force who can help
			return;
		}
	}

	// Send defending forces, also send attacking forces if they are home/traning.
	// This is still basic model where we suspect only one base ;(
	const Vec2i &pos = attacker->tilePos;

	for (unsigned int i = 0; i < pai.Force.Size(); ++i) {
		AiForce &aiForce = pai.Force[i];

		if (aiForce.Size() > 0
			&& ((aiForce.Role == AiForceRoleDefend && !aiForce.Attacking)
				|| (aiForce.Role == AiForceRoleAttack && !aiForce.Attacking && !aiForce.State))) {  // none attacking
			aiForce.Defending = true;
			aiForce.Attack(pos);
		}
	}
}
Exemple #13
0
 bool operator()(const CUnit *unit) const {
     return unit->IsVisibleAsGoal(*player)
            && unit->IsEnemy(*player)
            && CanTarget(unit->Type, type);
 }
Exemple #14
0
/**
**  Select the target for the autocast.
**
**  @param caster    Unit who would cast the spell.
**  @param spell     Spell-type pointer.
**
**  @return          Target* chosen target or Null if spell can't be cast.
**  @todo FIXME: should be global (for AI) ???
**  @todo FIXME: write for position target.
*/
static Target *SelectTargetUnitsOfAutoCast(CUnit &caster, const SpellType &spell)
{
	AutoCastInfo *autocast;

	// Ai cast should be a lot better. Use autocast if not found.
	if (caster.Player->AiEnabled && spell.AICast) {
		autocast = spell.AICast;
	} else {
		autocast = spell.AutoCast;
	}
	Assert(autocast);
	const Vec2i &pos = caster.tilePos;
	int range = autocast->Range;

	// Select all units aroung the caster
	std::vector<CUnit *> table;
	SelectAroundUnit(caster, range, table);

	// Check generic conditions. FIXME: a better way to do this?
	if (autocast->Combat != CONDITION_TRUE) {
		// Check each unit if it is hostile.
		bool inCombat = false;
		for (size_t i = 0; i < table.size(); ++i) {
			const CUnit &target = *table[i];

			// Note that CanTarget doesn't take into account (offensive) spells...
			if (target.IsVisibleAsGoal(*caster.Player) && caster.IsEnemy(target)
				&& (CanTarget(*caster.Type, *target.Type) || CanTarget(*target.Type, *caster.Type))) {
				inCombat = true;
				break;
			}
		}
		if ((autocast->Combat == CONDITION_ONLY) ^ (inCombat)) {
			return NULL;
		}
	}

	switch (spell.Target) {
		case TargetSelf :
			if (PassCondition(caster, spell, &caster, pos, spell.Condition)
				&& PassCondition(caster, spell, &caster, pos, autocast->Condition)) {
				return NewTargetUnit(caster);
			}
			return NULL;
		case TargetPosition:
			return 0;
			//  Autocast with a position? That's hard
			//  Possibilities: cast reveal-map on a dark region
			//  Cast raise dead on a bunch of corpses. That would rule.
			//  Cast summon until out of mana in the heat of battle. Trivial?
			//  Find a tight group of units and cast area-damage spells. HARD,
			//  but it is a must-have for AI. What about area-heal?
		case TargetUnit: {
			// The units are already selected.
			//  Check every unit if it is a possible target

			int n = 0;
			for (size_t i = 0; i != table.size(); ++i) {
				// Check if unit in battle
				if (autocast->Attacker == CONDITION_ONLY) {
					if (table[i]->CurrentAction() != UnitActionAttack
						&& table[i]->CurrentAction() != UnitActionAttackGround
						&& table[i]->CurrentAction() != UnitActionSpellCast) {
						continue;
					}
				}
				if (PassCondition(caster, spell, table[i], pos, spell.Condition)
					&& PassCondition(caster, spell, table[i], pos, autocast->Condition)) {
					table[n++] = table[i];
				}
			}
			// Now select the best unit to target.
			if (n != 0) {
				// For the best target???
				if (autocast->PriorytyVar != ACP_NOVALUE) {
					std::sort(table.begin(), table.begin() + n,
							  AutoCastPrioritySort(caster, autocast->PriorytyVar, autocast->ReverseSort));
					return NewTargetUnit(*table[0]);
				} else { // Use the old behavior
					return NewTargetUnit(*table[SyncRand() % n]);
				}
			}
			break;
		}
		default:
			// Something is wrong
			DebugPrint("Spell is screwed up, unknown target type\n");
			Assert(0);
			return NULL;
			break;
	}
	return NULL; // Can't spell the auto-cast.
}
Exemple #15
0
/**
**	Attack units in distance.
**
**		If the unit can attack must be handled by caller.
**		Choose the best target, that can be attacked.
**
**	@param unit	Find in distance for this unit.
**	@param range	Distance range to look.
**
**	@return		Unit to be attacked.
**
**	@note	This could be improved, for better performance.
*/
global Unit* AttackUnitsInDistance(const Unit* unit,int range)
{
    const Unit* dest;
    const UnitType* type;
    const UnitType* dtype;
    Unit* table[UnitMax];
    int x;
    int y;
    int n;
    int i;
    int d;
    int attackrange;
    int cost;
    const Player* player;
    const Unit* best_unit;
    int best_cost;

    DebugLevel3Fn("(%d)%s\n" _C_ UnitNumber(unit) _C_ unit->Type->Ident);

    // if necessary, take possible damage on allied units into account...
    if (unit->Type->Missile.Missile->Range>1
	    && (range+unit->Type->Missile.Missile->Range<15)) {
        return FindRangeAttack(unit,range);
    }

    //
    //	Select all units in range.
    //
    x=unit->X;
    y=unit->Y;
    n=SelectUnits(x-range,y-range,x+range+1,y+range+1,table);

    best_unit=NoUnitP;
    best_cost=INT_MAX;

    player=unit->Player;
    type=unit->Type;
    attackrange=unit->Stats->AttackRange;
    //
    //	Find the best unit to attack
    //
    for( i=0; i<n; ++i ) {
	dest=table[i];
	//
	//	unusable unit
	//
	// FIXME: did SelectUnits already filter this.
	if( dest->Removed || dest->Invisible || !unit->HP
		|| !(dest->Visible&(1<<player->Player))
		|| dest->Orders[0].Action==UnitActionDie ) {
	    continue;
	}

	if( !IsEnemy(player,dest) ) {	// a friend or neutral
	    continue;
	}

	dtype=dest->Type;
	if( !CanTarget(type,dtype) ) {	// can't be attacked.
	    continue;
	}

	//
	//	Calculate the costs to attack the unit.
	//	Unit with the smallest attack costs will be taken.
	//
	cost=0;
	//
	//	Priority 0-255
	//
	cost-=dtype->Priority*PRIORITY_FACTOR;
	//
	//	Remaining HP (Health) 0-65535
	//
	cost+=dest->HP*HEALTH_FACTOR;
	//
	//	Unit in attack range?
	//
	d=MapDistanceBetweenUnits(unit,dest);
	if( d<type->MinAttackRange ) {	// FIXME: we don't support moving away!
	    continue;
	}
	if( d<attackrange && d>type->MinAttackRange ) {
	    cost+=d*INRANGE_FACTOR;
	    cost-=INRANGE_BONUS;
	} else {
	    cost+=d*DISTANCE_FACTOR;
	}
	//
	//	Unit can attack back.
	//
	if( CanTarget(dtype,type) ) {
	    cost-=CANATTACK_BONUS;
	}

	DebugLevel3Fn("%s -> %s\t%08x\n" _C_ type->Ident _C_ dtype->Ident _C_ cost);
	//
	//	Take this target?
	//
	if( cost<best_cost
		&& (d<attackrange || UnitReachable(unit,dest,attackrange)) ) {
	    best_unit=dest;
	    best_cost=cost;
	}
    }

/*
    if( best_unit ) {
	DebugLevel3Fn("Attacking (%d)%s -> %s\n" _C_ UnitNumber(unit) _C_
		unit->Type->Ident _C_ best_unit->Type->Ident);
    }
*/

    // FIXME: No idea how to make this correct, without cast!!
    return (Unit*)best_unit;
}
Exemple #16
0
/**
**	Attack units in distance, with large missile
**
**		Choose the best target, that can be attacked. It takes into
**		account allied unit which could be hit by the missile
**
**	@param u	Find in distance for this unit.
**	@param range	Distance range to look.
**
**	@return		Unit to be attacked.
**
**	@note	This could be improved, for better performance / better trade.
**      @note   Limited to attack range smaller than 16.
**	@note	Will be moved to unit_ai.c soon.
*/
local Unit* FindRangeAttack(const Unit* u, int range)
{
    int x, y, n, cost,d,effective_hp,enemy_count;
    int missile_range,attackrange,hp_damage_evaluate;
    int good[32][32], bad[32][32];
    Unit* table[UnitMax];
    Unit* dest;
    const UnitType* dtype;
    const UnitType* type;
    const Player* player;
    int xx, yy;
    int best_x, best_y, best_cost;
    int i, sbad, sgood;
    Unit* best;

    type = u->Type;
    player = u->Player;

    //  If catapult, count units near the target...
    //      FIXME : make it configurable
    //

    missile_range = type->Missile.Missile->Range + range - 1;
    attackrange=u->Stats->AttackRange;
    // Evaluation of possible damage...
    hp_damage_evaluate=u->Stats->BasicDamage+u->Stats->PiercingDamage;

    DebugCheck(2 * missile_range + 1 >= 32);

    x = u->X;
    y = u->Y;
    n = SelectUnits(x - missile_range, y - missile_range,
	x + missile_range + 1, y + missile_range + 1, table);

    if (!n) {
	return NoUnitP;
    }

    for (y = 0; y < 2 * missile_range + 1; y++) {
	for (x = 0; x < 2 * missile_range + 1; x++) {
	    good[y][x] = 0;
	    bad[y][x] = 0;
	}
    }

    enemy_count=0;
    // FILL good/bad...
    for (i = 0; i < n; i++) {
	dest = table[i];
	dtype = dest->Type;
	//
	//      unusable unit
	//
	// FIXME: did SelectUnits already filter this.
	if (dest->Removed || dest->Invisible || !u->HP
		|| !(dest->Visible & (1 << player->Player))
		|| dest->Orders[0].Action == UnitActionDie) {
	    table[i] = 0;
	    continue;
	}

        // won't be a target...
        if (!CanTarget(type, dtype)) {	// can't be attacked.
	    table[i] = 0;
	    continue;
	}

	if (!IsEnemy(player, dest)) {	// a friend or neutral
	    table[i] = 0;

	    // Calc a negative cost
            // The gost is more important when the unit would be killed
            // by our fire.

            // It costs ( is positive ) if hp_damage_evaluate>dest->HP ... )
	    // FIXME : assume that PRIORITY_FACTOR>HEALTH_FACTOR
	    cost = HEALTH_FACTOR*(2*hp_damage_evaluate-dest->HP) / (dtype->TileWidth * dtype->TileWidth);
	    if (cost < 1) {
		cost = 1;
	    }
	    cost = (-cost);
	} else {
	    //
	    //  Calculate the costs to attack the unit.
	    //  Unit with the smallest attack costs will be taken.
	    //
	    cost = 0;
	    //
	    //  Priority 0-255
	    //
	    cost += dtype->Priority * PRIORITY_FACTOR;
	    //
	    //  Remaining HP (Health) 0-65535
	    //
	    // Give a boost to unit we can kill in one shoot only

	    //
	    // calculate HP which will remain in the enemy unit, after hit
	    //
	    effective_hp=(dest->HP-2*hp_damage_evaluate);

	    //
	    // Unit we won't kill are evaluated the same
	    //
	    if (effective_hp>0){
	      effective_hp=0;
	    }

	    //
	    // Unit we are sure to kill are all evaluated the same ( except PRIORITY )
	    //
	    if (effective_hp<(-hp_damage_evaluate)){
	      effective_hp=(-hp_damage_evaluate);
	    }

	    //
	    // Here, effective_hp vary from -hp_damage_evaluate ( unit will be killed) to 0 ( unit can't be killed )
	    // => we prefer killing rather than only hitting...
	    //
	    cost += (-effective_hp) * HEALTH_FACTOR;

	    //
	    //  Unit can attack back.
	    //
	    if (CanTarget(dtype, type)) {
		cost += CANATTACK_BONUS;
	    }

            //
            // the cost may be divided accros multiple cells
            //
	    cost=cost / (dtype->TileWidth * dtype->TileWidth);
	    if (cost < 1) {
		cost = 1;
	    }
     	    d=MapDistanceBetweenUnits(u,dest);

            // FIXME: we don't support moving away!
            if((d<type->MinAttackRange)||(!UnitReachable(u,dest,attackrange))) {
	    	table[i]=0;
	    } else {
	    	enemy_count++;
	    }
	}

	x = dest->X - u->X + missile_range+1;
	y = dest->Y - u->Y + missile_range+1;

	// Mark the good/bad array...
	for (xx = 0; xx < dtype->TileWidth; xx++) {
	    for (yy = 0; yy < dtype->TileWidth; yy++) {
		if ((x + xx < 0) || (y + yy < 0)
			|| (x + xx >= 2 * missile_range + 1)
			|| (y + yy >= 2 * missile_range + 1)) {
		    continue;
		}
		if (cost < 0) {
		    good[y + yy][x + xx] -= cost;
		} else {
		    bad[y + yy][x + xx] += cost;
		}
	    }
	}
    }

    if (!enemy_count) {
	return NoUnitP;
    }

    // Find the best area...
    // The target which provide the best bad/good ratio is choosen...
    best_x = -1;
    best_y = -1;
    best_cost = -1;
    best = NoUnitP;
    for (i = 0; i < n; i++) {
	if (!table[i]) {
	    continue;
	}
	dest = table[i];
	dtype = dest->Type;

	// put in x-y the real point which will be hit...
	// ( only meaningfull when dtype->TileWidth>1 )
        if (u->X<dest->X){
	    x=dest->X;
	} else if (u->X>dest->X+dtype->TileWidth-1){
	    x=dest->X+dtype->TileWidth-1;
	} else {
	    x=u->X;
	}

	if (u->Y<dest->Y){
	    y=dest->Y;
	} else if(u->Y>dest->Y+dtype->TileWidth-1){
	    y=dest->Y+dtype->TileWidth-1;
	} else {
	    y=u->Y;
	}

 	// Make x,y relative to u->x...
	x = x - u->X + missile_range+1;
	y = y - u->Y + missile_range+1;

	sbad = 0;
	sgood = 0;
	for (yy = -(type->Missile.Missile->Range - 1);
	    yy <= type->Missile.Missile->Range - 1; yy++) {
	    for (xx = -(type->Missile.Missile->Range - 1);
		xx <= type->Missile.Missile->Range - 1; xx++) {
		if ((x + xx < 0) || (y + yy < 0)
			|| ((x + xx) >= 2 * missile_range + 1)
			|| ((y + yy) >= 2 * missile_range + 1)) {
		    continue;
		}

		sbad += bad[y + yy][x + xx];
		sgood += good[y + yy][x + xx];
		if ((!yy) && (!xx)) {
		    sbad += bad[y + yy][x + xx];
		    sgood += good[y + yy][x + xx];
		}
	    }
	}

	// don't consider small damages...
	if (sgood < 20) {
	    sgood = 20;
	}

	cost = sbad / sgood;
	if (cost > best_cost) {
	    best_cost = cost;
	    best = dest;
	}
    }
    return best;
}
/**
**  Attack units in distance, with large missile
**
**  Choose the best target, that can be attacked. It takes into
**  account allied unit which could be hit by the missile
**
**  @param u      Find in distance for this unit.
**  @param range  Distance range to look.
**
**  @return       Unit to be attacked.
**
**  @note This could be improved, for better performance / better trade.
**  @note   Limited to attack range smaller than 16.
**  @note Will be moved to unit_ai.c soon.
*/
static CUnit *FindRangeAttack(const CUnit *u, int range)
{
	int x;
	int y;
	int n;
	int cost;
	int d;
	int effective_hp;
	int enemy_count;
	int missile_range;
	int attackrange;
	int hp_damage_evaluate;
	int good[32][32];
	int bad[32][32];
	CUnit *table[UnitMax];
	CUnit *dest;
	const CUnitType *dtype;
	const CUnitType *type;
	const CPlayer *player;
	int xx;
	int yy;
	int best_cost;
	int i;
	int sbad;
	int sgood;
	CUnit *best;

	type = u->Type;
	player = u->Player;

	//  If catapult, count units near the target...
	//   FIXME : make it configurable
	//

	missile_range = type->Missile.Missile->Range + range - 1;
	attackrange = u->Stats->Variables[ATTACKRANGE_INDEX].Max;
	// Evaluation of possible damage...
	hp_damage_evaluate = u->Stats->Variables[BASICDAMAGE_INDEX].Value
						+ u->Stats->Variables[PIERCINGDAMAGE_INDEX].Value;

	Assert(2 * missile_range + 1 < 32);

	//
	// If unit is removed, use containers x and y
	if (u->Removed) {
		x = u->Container->X;
		y = u->Container->Y;
		n = UnitCache.Select(x - missile_range, y - missile_range,
			x + missile_range + u->Container->Type->TileWidth,
			y + missile_range + u->Container->Type->TileHeight, table, UnitMax);
	} else {
		x = u->X;
		y = u->Y;
		n = UnitCache.Select(x - missile_range, y - missile_range,
			x + missile_range + u->Type->TileWidth,
			y + missile_range + u->Type->TileHeight, table, UnitMax);
	}

	if (!n) {
		return NoUnitP;
	}

	for (y = 0; y < 2 * missile_range + 1; ++y) {
		for (x = 0; x < 2 * missile_range + 1; ++x) {
			good[y][x] = 0;
			bad[y][x] = 0;
		}
	}

	enemy_count = 0;
	// FILL good/bad...
	for (i = 0; i < n; ++i) {
		dest = table[i];
		dtype = dest->Type;
		if (!dest->IsVisibleAsGoal(u->Player)) {
			table[i] = 0;
			continue;
		}

		// won't be a target...
		if (!CanTarget(type, dtype)) { // can't be attacked.
			table[i] = 0;
			continue;
		}

		if (!player->IsEnemy(dest)) { // a friend or neutral
			table[i] = 0;

			// Calc a negative cost
			// The gost is more important when the unit would be killed
			// by our fire.

			// It costs (is positive) if hp_damage_evaluate>dest->HP ...)
			// FIXME : assume that PRIORITY_FACTOR>HEALTH_FACTOR
			cost = HEALTH_FACTOR * (2 * hp_damage_evaluate - dest->Variable[HP_INDEX].Value) /
				(dtype->TileWidth * dtype->TileWidth);
			if (cost < 1) {
				cost = 1;
			}
			cost = (-cost);
		} else {
			//
			//  Calculate the costs to attack the unit.
			//  Unit with the smallest attack costs will be taken.
			//
			cost = 0;
			//
			//  Priority 0-255
			//
			cost += dtype->Priority * PRIORITY_FACTOR;
			//
			//  Remaining HP (Health) 0-65535
			//
			// Give a boost to unit we can kill in one shoot only

			//
			// calculate HP which will remain in the enemy unit, after hit
			//
			effective_hp = (dest->Variable[HP_INDEX].Value - 2 * hp_damage_evaluate);

			//
			// Unit we won't kill are evaluated the same
			//
			if (effective_hp > 0) {
				effective_hp = 0;
			}

			//
			// Unit we are sure to kill are all evaluated the same (except PRIORITY)
			//
			if (effective_hp < -hp_damage_evaluate) {
				effective_hp = -hp_damage_evaluate;
			}

			//
			// Here, effective_hp vary from -hp_damage_evaluate (unit will be killed) to 0 (unit can't be killed)
			// => we prefer killing rather than only hitting...
			//
			cost += -effective_hp * HEALTH_FACTOR;

			//
			//  Unit can attack back.
			//
			if (CanTarget(dtype, type)) {
				cost += CANATTACK_BONUS;
			}

			//
			// the cost may be divided accros multiple cells
			//
			cost = cost / (dtype->TileWidth * dtype->TileWidth);
			if (cost < 1) {
				cost = 1;
			}

			//
			// Removed Unit's are in bunkers
			//
			if (u->Removed) {
				d = MapDistanceBetweenUnits(u->Container, dest);
			} else {
				d = MapDistanceBetweenUnits(u, dest);
			}

			if (d <= attackrange || (d <= range && UnitReachable(u, dest, attackrange))) {
				++enemy_count;
			} else {
					table[i] = 0;
			}
		}

		x = dest->X - u->X + missile_range + 1;
		y = dest->Y - u->Y + missile_range + 1;

		// Mark the good/bad array...
		for (xx = 0; xx < dtype->TileWidth; ++xx) {
			for (yy = 0; yy < dtype->TileWidth; ++yy) {
				if ((x + xx < 0) || (y + yy < 0) ||
						(x + xx >= 2 * missile_range + 1) ||
						(y + yy >= 2 * missile_range + 1)) {
					continue;
				}
				if (cost < 0) {
					good[y + yy][x + xx] -= cost;
				} else {
					bad[y + yy][x + xx] += cost;
				}
			}
		}
	}

	if (!enemy_count) {
		return NoUnitP;
	}

	// Find the best area...
	// The target which provide the best bad/good ratio is choosen...
	best_cost = -1;
	best = NoUnitP;
	for (i = 0; i < n; ++i) {
		if (!table[i]) {
			continue;
		}
		dest = table[i];
		dtype = dest->Type;

		// put in x-y the real point which will be hit...
		// (only meaningful when dtype->TileWidth > 1)
		if (u->X < dest->X) {
			x = dest->X;
		} else if (u->X > dest->X + dtype->TileWidth - 1) {
			x = dest->X + dtype->TileWidth - 1;
		} else {
			x = u->X;
		}

		if (u->Y < dest->Y) {
			y = dest->Y;
		} else if (u->Y > dest->Y + dtype->TileHeight - 1) {
			y = dest->Y + dtype->TileHeight - 1;
		} else {
			y = u->Y;
		}

		// Make x,y relative to u->x...
		x = x - u->X + missile_range + 1;
		y = y - u->Y + missile_range + 1;

		sbad = 0;
		sgood = 0;
		for (yy = -(type->Missile.Missile->Range - 1);
			yy <= type->Missile.Missile->Range - 1; ++yy) {
			for (xx = -(type->Missile.Missile->Range - 1);
				xx <= type->Missile.Missile->Range - 1; ++xx) {
				if ((x + xx < 0) || (y + yy < 0) ||
						((x + xx) >= 2 * missile_range + 1) ||
						((y + yy) >= 2 * missile_range + 1)) {
					continue;
				}

				sbad += bad[y + yy][x + xx];
				sgood += good[y + yy][x + xx];
				if (!yy && !xx) {
					sbad += bad[y + yy][x + xx];
					sgood += good[y + yy][x + xx];
				}
			}
		}

		// don't consider small damages...
		if (sgood < 20) {
			sgood = 20;
		}

		cost = sbad / sgood;
		if (cost > best_cost) {
			best_cost = cost;
			best = dest;
		}
	}
	return best;
}
bool
TacticalAI::ProcessOrders()
{
    if (ship_ai)
        ship_ai->ClearPatrol();

    if (orders && orders->EMCON() > 0) {
        int desired_emcon = orders->EMCON();

        if (ship_ai && (ship_ai->GetThreat() || ship_ai->GetThreatMissile()))
        desired_emcon = 3;

        if (ship->GetEMCON() != desired_emcon)
        ship->SetEMCON(desired_emcon);
    }

    if (orders && orders->Action()) {
        switch (orders->Action()) {
        case RadioMessage::ATTACK:
        case RadioMessage::BRACKET:
        case RadioMessage::IDENTIFY:
            {
                bool        tgt_ok   = false;
                SimObject*  tgt      = orders->GetTarget();

                if (tgt && tgt->Type() == SimObject::SIM_SHIP) {
                    Ship* tgt_ship = (Ship*) tgt;

                    if (CanTarget(tgt_ship)) {
                        roe = DIRECTED;
                        SelectTargetDirected((Ship*) tgt);

                        ship_ai->SetBracket(orders->Action() == RadioMessage::BRACKET);
                        ship_ai->SetIdentify(orders->Action() == RadioMessage::IDENTIFY);
                        ship_ai->SetNavPoint(0);

                        tgt_ok = true;
                    }
                }

                if (!tgt_ok)
                ClearRadioOrders();
            }
            break;

        case RadioMessage::ESCORT:
        case RadioMessage::COVER_ME:
            {
                SimObject* tgt = orders->GetTarget();
                if (tgt && tgt->Type() == SimObject::SIM_SHIP) {
                    roe = DEFENSIVE;
                    ship_ai->SetWard((Ship*) tgt);
                    ship_ai->SetNavPoint(0);
                }
                else {
                    ClearRadioOrders();
                }
            }
            break;

        case RadioMessage::WEP_FREE:
            roe = AGRESSIVE;
            ship_ai->DropTarget(0.1);
            break;

        case RadioMessage::WEP_HOLD:
        case RadioMessage::FORM_UP:
            roe = NONE;
            ship_ai->DropTarget(5);
            break;

        case RadioMessage::MOVE_PATROL:
            roe = SELF_DEFENSIVE;
            ship_ai->SetPatrol(orders->Location());
            ship_ai->SetNavPoint(0);
            ship_ai->DropTarget(Random(5, 10));
            break;

        case RadioMessage::RTB:
        case RadioMessage::DOCK_WITH:
            roe = NONE;

            ship_ai->DropTarget(10);
            
            if (!ship->GetInbound()) {
                RadioMessage* msg = 0;
                Ship* controller = ship->GetController();

                if (orders->Action() == RadioMessage::DOCK_WITH && orders->GetTarget()) {
                    controller = (Ship*) orders->GetTarget();
                }

                if (!controller) {
                    Element* elem = ship->GetElement();
                    if (elem && elem->GetCommander()) {
                        Element* cmdr = elem->GetCommander();
                        controller    = cmdr->GetShip(1);
                    }
                }

                if (controller && controller->GetHangar() &&
                        controller->GetHangar()->CanStow(ship)) {
                    SimRegion*  self_rgn = ship->GetRegion();
                    SimRegion*  rtb_rgn  = controller->GetRegion();

                    if (self_rgn == rtb_rgn) {
                        double range = Point(controller->Location() - ship->Location()).length();

                        if (range < 50e3) {
                            msg = new(__FILE__,__LINE__) RadioMessage(controller, ship, RadioMessage::CALL_INBOUND);
                            RadioTraffic::Transmit(msg);
                        }
                    }
                }
                else {
                    ship->ClearRadioOrders();
                }

                ship_ai->SetNavPoint(0);
            }
            break;

        case RadioMessage::QUANTUM_TO:
        case RadioMessage::FARCAST_TO:
            roe = NONE;
            ship_ai->DropTarget(10);
            break;

        }

        action = orders->Action();
        return true;
    }

    // if we had an action before, this must be a "cancel orders" 
    else if (action) {
        ClearRadioOrders();
    }

    return false;
}
Exemple #19
0
/**
**  Work for missile hit.
*/
void Missile::MissileHit(CUnit *unit)
{
	const MissileType &mtype = *this->Type;

	if (mtype.ImpactSound.Sound) {
		PlayMissileSound(*this, mtype.ImpactSound.Sound);
	}
	const PixelPos pixelPos = this->position + this->Type->size / 2;

	//
	// The impact generates a new missile.
	//
	if (mtype.Impact.empty() == false) {
		for (std::vector<MissileConfig *>::const_iterator it = mtype.Impact.begin(); it != mtype.Impact.end(); ++it) {
			const MissileConfig &mc = **it;
			Missile *impact = MakeMissile(*mc.Missile, pixelPos, pixelPos);
			if (impact && impact->Type->Damage) {
				impact->SourceUnit = this->SourceUnit;
			}
		}
	}
	if (mtype.ImpactParticle) {
		mtype.ImpactParticle->pushPreamble();
		mtype.ImpactParticle->pushInteger(pixelPos.x);
		mtype.ImpactParticle->pushInteger(pixelPos.y);
		mtype.ImpactParticle->run();
	}

	if (!this->SourceUnit) {  // no owner - green-cross ...
		return;
	}

	const Vec2i pos = Map.MapPixelPosToTilePos(pixelPos);

	if (!Map.Info.IsPointOnMap(pos)) {
		// FIXME: this should handled by caller?
		DebugPrint("Missile gone outside of map!\n");
		return;  // outside the map.
	}

	//
	// Choose correct goal.
	//
	if (unit) {
		if (unit->Destroyed) {
			return;
		}
		if (mtype.Pierce && mtype.PierceOnce) {
			if (IsPiercedUnit(*this, *unit)) {
				return;
			} else {
				PiercedUnits.insert(this->PiercedUnits.begin(), unit);
			}
		}
		MissileHitsGoal(*this, *unit, 1);
		return;
	}
	if (!mtype.Range) {
		if (this->TargetUnit && (mtype.FriendlyFire == false
								 || this->TargetUnit->Player->Index != this->SourceUnit->Player->Index)) {
			//
			// Missiles without range only hits the goal always.
			//
			CUnit &goal = *this->TargetUnit;
			if (mtype.Pierce && mtype.PierceOnce) {
				if (IsPiercedUnit(*this, goal)) {
					return;
				} else {
					PiercedUnits.insert(this->PiercedUnits.begin(), &goal);
				}
			}
			if (goal.Destroyed) {
				this->TargetUnit = NULL;
				return;
			}
			MissileHitsGoal(*this, goal, 1);
			return;
		}
		MissileHitsWall(*this, pos, 1);
		return;
	}

	{
		//
		// Hits all units in range.
		//
		const Vec2i range(mtype.Range - 1, mtype.Range - 1);
		std::vector<CUnit *> table;
		Select(pos - range, pos + range, table);
		Assert(this->SourceUnit != NULL);
		for (size_t i = 0; i != table.size(); ++i) {
			CUnit &goal = *table[i];
			//
			// Can the unit attack this unit-type?
			// NOTE: perhaps this should be come a property of the missile.
			// Also check CorrectSphashDamage so land explosions can't hit the air units
			//
			if (CanTarget(*this->SourceUnit->Type, *goal.Type)
				&& (mtype.FriendlyFire == false || goal.Player->Index != this->SourceUnit->Player->Index)) {
				bool shouldHit = true;

				if (mtype.Pierce && mtype.PierceOnce) {
					if (IsPiercedUnit(*this, goal)) {
						shouldHit = false;
					} else {
						PiercedUnits.insert(this->PiercedUnits.begin(), &goal);
					}
				}

				if (mtype.CorrectSphashDamage == true) {
					bool isPositionSpell = false;
					if (this->TargetUnit == NULL && this->SourceUnit->CurrentAction() == UnitActionSpellCast) {
						const COrder_SpellCast &order = *static_cast<COrder_SpellCast *>(this->SourceUnit->CurrentOrder());
						if (order.GetSpell().Target == TargetPosition) {
							isPositionSpell = true;
						}
					}
					if (isPositionSpell || this->SourceUnit->CurrentAction() == UnitActionAttackGround) {
						if (goal.Type->UnitType != this->SourceUnit->Type->UnitType) {
							shouldHit = false;
						}
					} else {
						if (this->TargetUnit == NULL || goal.Type->UnitType != this->TargetUnit->Type->UnitType) {
							shouldHit = false;
						}
					}
				}
				if (shouldHit) {
					int splash = goal.MapDistanceTo(pos);

					if (splash) {
						splash *= mtype.SplashFactor;
					} else {
						splash = 1;
					}
					MissileHitsGoal(*this, goal, splash);
				}
			}
		}
	}

	// Missile hits ground.
	const Vec2i offset(mtype.Range, mtype.Range);
	const Vec2i posmin = pos - offset;
	for (int i = mtype.Range * 2; --i;) {
		for (int j = mtype.Range * 2; --j;) {
			const Vec2i posIt(posmin.x + i, posmin.y + j);

			if (Map.Info.IsPointOnMap(posIt)) {
				int d = Distance(pos, posIt);
				d *= mtype.SplashFactor;
				if (d == 0) {
					d = 1;
				}
				MissileHitsWall(*this, posIt, d);
			}
		}
	}
}
Exemple #20
0
/**
**  Work for missile hit.
**
**  @param missile  Missile reaching end-point.
*/
void MissileHit(Missile *missile)
{
	CUnit *goal;
	int x;
	int y;
	CUnit *table[UnitMax];
	int n;
	int i;
	int splash;

	if (missile->Type->ImpactSound.Sound) {
		PlayMissileSound(missile, missile->Type->ImpactSound.Sound);
	}

	x = missile->X + missile->Type->Width / 2;
	y = missile->Y + missile->Type->Height / 2;

	//
	// The impact generates a new missile.
	//
	if (missile->Type->ImpactMissile) {
		MakeMissile(missile->Type->ImpactMissile, x, y, x, y);
	}
	if (missile->Type->ImpactParticle) {
		missile->Type->ImpactParticle->pushPreamble();
		missile->Type->ImpactParticle->pushInteger(x);
		missile->Type->ImpactParticle->pushInteger(y);
		missile->Type->ImpactParticle->run();
	}

	if (!missile->SourceUnit) {  // no owner - green-cross ...
		return;
	}

	x /= TileSizeX;
	y /= TileSizeY;

	if (x < 0 || y < 0 || x >= Map.Info.MapWidth || y >= Map.Info.MapHeight) {
		// FIXME: this should handled by caller?
		DebugPrint("Missile gone outside of map!\n");
		return;  // outside the map.
	}

	//
	// Choose correct goal.
	//
	if (!missile->Type->Range) {
		if (missile->TargetUnit) {
			//
			// Missiles without range only hits the goal always.
			//
			goal = missile->TargetUnit;
			if (goal->Destroyed) {  // Destroyed
				goal->RefsDecrease();
				missile->TargetUnit = NoUnitP;
				return;
			}
			MissileHitsGoal(missile, goal, 1);
			return;
		}
		return;
	}

	//
	// Hits all units in range.
	//
	i = missile->Type->Range;
	n = UnitCache.Select(x - i + 1, y - i + 1, x + i, y + i, table, UnitMax);
	Assert(missile->SourceUnit != NULL);
	for (i = 0; i < n; ++i) {
		goal = table[i];
		//
		// Can the unit attack the this unit-type?
		// NOTE: perhaps this should be come a property of the missile.
		//
		if (CanTarget(missile->SourceUnit->Type, goal->Type)) {
			splash = MapDistanceToUnit(x, y, goal);
			if (splash) {
				splash *= missile->Type->SplashFactor;
			} else {
				splash = 1;
			}
			MissileHitsGoal(missile, goal, splash);
		}
	}
}