/** ** 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; }
/** ** 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; }