/** ** Returns a value less then 0, 0 or bigger then 0, ** when the first unit is repectively nearer, at the same distance ** or further away then the 2nd from the referenceunit. */ static int CompareUnitDistance(const void *v1, const void *v2) { CUnit *c1 = *(CUnit **)v1; CUnit *c2 = *(CUnit **)v2; int d1 = MapDistanceBetweenUnits(referenceunit, c1); int d2 = MapDistanceBetweenUnits(referenceunit, c2); if (d1 - d2 != 0) { return d1 - d2; } else { return c1->Slot - c2->Slot; } }
/** ** Wait for transporter. ** ** @param unit Pointer to unit. ** ** @return True if ship arrived/present, False otherwise. */ static int WaitForTransporter(CUnit *unit) { CUnit *trans; if (unit->Wait) { unit->Wait--; return 0; } trans = unit->Orders[0]->Goal; if (!trans || !CanTransport(trans, unit)) { // FIXME: destination destroyed?? unit->Wait = 6; return 0; } if (!trans->IsVisibleAsGoal(unit->Player)) { DebugPrint("Transporter Gone\n"); trans->RefsDecrease(); unit->Orders[0]->Goal = NoUnitP; unit->Wait = 6; return 0; } if (MapDistanceBetweenUnits(unit, trans) == 1) { // enter transporter return 1; } // // FIXME: any enemies in range attack them, while waiting. // // n0b0dy: This means we have to search with a smaller range. // It happens only when you reach the shore,and the transporter // is not there. The unit searches with a big range, so it thinks // it's there. This is why we reset the search. The transporter // should be a lot closer now, so it's not as bad as it seems. unit->SubAction = 0; unit->Orders[0]->Range = 1; // Uhh wait a bit. unit->Wait = 10; return 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. ** */ 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, 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; }
/** ** Unit repairs ** ** @param unit Unit, for that the attack is handled. */ void HandleActionRepair(CUnit *unit) { CUnit *goal; int err; switch (unit->SubAction) { case 0: NewResetPath(unit); unit->SubAction = 1; // FALL THROUGH // // Move near to target. // case 1: // FIXME: RESET FIRST!! Why? We move first and than check if // something is in sight. err = DoActionMove(unit); if (!unit->Anim.Unbreakable) { // // No goal: if meeting damaged building repair it. // goal = unit->Orders[0]->Goal; // // Target is dead, choose new one. // // Check if goal is correct unit. if (goal) { if (!goal->IsVisibleAsGoal(unit->Player)) { DebugPrint("repair target gone.\n"); unit->Orders[0]->X = goal->X; unit->Orders[0]->Y = goal->Y; goal->RefsDecrease(); // FIXME: should I clear this here? unit->Orders[0]->Goal = goal = NULL; NewResetPath(unit); } } else if (unit->Player->AiEnabled) { // Ai players workers should stop if target is killed err = -1; } // // Have reached target? FIXME: could use return value // if (goal && MapDistanceBetweenUnits(unit, goal) <= unit->Type->RepairRange && goal->Variable[HP_INDEX].Value < goal->Variable[HP_INDEX].Max) { unit->State = 0; unit->SubAction = 2; unit->Data.Repair.Cycles = 0; UnitHeadingFromDeltaXY(unit, goal->X + (goal->Type->TileWidth - 1) / 2 - unit->X, goal->Y + (goal->Type->TileHeight - 1) / 2 - unit->Y); } else if (err < 0) { if (goal) { // release reference goal->RefsDecrease(); unit->Orders[0]->Goal = NoUnitP; } unit->Orders[0]->Action = UnitActionStill; unit->State = unit->SubAction = 0; if (unit->Selected) { // update display for new action SelectedUnitChanged(); } return; } // FIXME: Should be it already? Assert(unit->Orders[0]->Action == UnitActionRepair); } break; // // Repair the target. // case 2: AnimateActionRepair(unit); unit->Data.Repair.Cycles++; if (!unit->Anim.Unbreakable) { goal = unit->Orders[0]->Goal; // // Target is dead, choose new one. // // Check if goal is correct unit. // FIXME: should I do a function for this? if (goal) { if (!goal->IsVisibleAsGoal(unit->Player)) { DebugPrint("repair goal is gone\n"); unit->Orders[0]->X = goal->X; unit->Orders[0]->Y = goal->Y; goal->RefsDecrease(); // FIXME: should I clear this here? unit->Orders[0]->Goal = goal = NULL; NewResetPath(unit); } } if (goal && MapDistanceBetweenUnits(unit, goal) <= unit->Type->RepairRange) { RepairUnit(unit, goal); goal = unit->Orders[0]->Goal; } else if (goal && MapDistanceBetweenUnits(unit, goal) > unit->Type->RepairRange) { // If goal has move, chase after it unit->State = 0; unit->SubAction = 0; } // // Target is fine, choose new one. // if (!goal || goal->Variable[HP_INDEX].Value >= goal->Variable[HP_INDEX].Max) { if (goal) { // release reference goal->RefsDecrease(); unit->Orders[0]->Goal = NULL; } unit->Orders[0]->Action = UnitActionStill; unit->SubAction = unit->State = 0; if (unit->Selected) { // update display for new action SelectedUnitChanged(); } return; } // FIXME: automatic repair } break; } }
/** ** 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; }
/** ** 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; }
/** ** Fire missile. ** ** @param unit Unit that fires the missile. */ void FireMissile(CUnit *unit) { int x; int y; int dx; int dy; CUnit *goal; Missile *missile; // // Goal dead? // goal = unit->Orders[0]->Goal; if (goal) { // Better let the caller/action handle this. if (goal->Destroyed) { DebugPrint("destroyed unit\n"); return; } if (goal->Removed || goal->Orders[0]->Action == UnitActionDie) { return; } // FIXME: Some missile hit the field of the target and all units on it. // FIXME: goal is already dead, but missile could hit others? } // // No missile hits immediately! // if (unit->Type->Missile.Missile->Class == MissileClassNone) { // No goal, take target coordinates if (!goal) { DebugPrint("Missile-none hits no unit, shouldn't happen!\n"); return; } HitUnit(unit, goal, CalculateDamage(unit, goal)); return; } // If Firing from inside a Bunker if (unit->Container) { x = unit->Container->X * TileSizeX + TileSizeX / 2; // missile starts in tile middle y = unit->Container->Y * TileSizeY + TileSizeY / 2; } else { x = unit->X * TileSizeX + TileSizeX / 2; // missile starts in tile middle y = unit->Y * TileSizeY + TileSizeY / 2; } if (goal) { Assert(goal->Type); // Target invalid? // // Moved out of attack range? // if (MapDistanceBetweenUnits(unit, goal) < unit->Type->MinAttackRange) { DebugPrint("Missile target too near %d,%d\n" _C_ MapDistanceBetweenUnits(unit, goal) _C_ unit->Type->MinAttackRange); // FIXME: do something other? return; } // Fire to nearest point of the unit! // If Firing from inside a Bunker if (unit->Container) { NearestOfUnit(goal, unit->Container->X, unit->Container->Y, &dx, &dy); } else { NearestOfUnit(goal, unit->X, unit->Y, &dx, &dy); } } else { dx = unit->Orders[0]->X; dy = unit->Orders[0]->Y; // FIXME: Can this be too near?? } // Fire to the tile center of the destination. dx = dx * TileSizeX + TileSizeX / 2; dy = dy * TileSizeY + TileSizeY / 2; missile = MakeMissile(unit->Type->Missile.Missile, x, y, dx, dy); // // Damage of missile // if (goal) { missile->TargetUnit = goal; goal->RefsIncrease(); } missile->SourceUnit = unit; unit->RefsIncrease(); }