00001
00002
00003
00004
00005
00006
00007
00008
00009
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00030
00036
00037
00038
00039
00040 #include <stdio.h>
00041 #include <stdlib.h>
00042 #include <string.h>
00043
00044 #include "stratagus.h"
00045 #include "unittype.h"
00046 #include "animation.h"
00047 #include "player.h"
00048 #include "unit.h"
00049 #include "missile.h"
00050 #include "actions.h"
00051 #include "sound.h"
00052 #include "map.h"
00053 #include "pathfinder.h"
00054
00055
00056
00057
00058
00059 #define WEAK_TARGET 2
00060 #define MOVE_TO_TARGET 4
00061 #define ATTACK_TARGET 5
00062
00063
00064
00065
00066
00074 void AnimateActionAttack(CUnit *unit)
00075 {
00076
00077
00078
00079 if (!unit->Type->Animations || !unit->Type->Animations->Attack) {
00080 FireMissile(unit);
00081 return;
00082 }
00083 UnitShowAnimation(unit, unit->Type->Animations->Attack);
00084 }
00085
00098 static int CheckForDeadGoal(CUnit *unit)
00099 {
00100 CUnit *goal;
00101
00102 goal = unit->Orders[0]->Goal;
00103
00104 if (!goal || goal->IsVisibleAsGoal(unit->Player)) {
00105 return 0;
00106 }
00107
00108
00109 unit->Orders[0]->X = goal->X;
00110 unit->Orders[0]->Y = goal->Y;
00111 unit->Orders[0]->MinRange = 0;
00112 unit->Orders[0]->Range = 0;
00113
00114 goal->RefsDecrease();
00115 unit->Orders[0]->Goal = NoUnitP;
00116
00117
00118
00119
00120 if (unit->SavedOrder.Action != UnitActionStill) {
00121 unit->ClearAction();
00122 *unit->Orders[0] = unit->SavedOrder;
00123 unit->SavedOrder.Action = UnitActionStill;
00124 unit->SavedOrder.Goal = NoUnitP;
00125
00126 unit->State = 0;
00127 NewResetPath(unit);
00128 return 1;
00129 }
00130
00131 return 0;
00132 }
00133
00141 static int CheckForTargetInRange(CUnit *unit)
00142 {
00143 CUnit *goal;
00144 CUnit *temp;
00145
00146
00147
00148
00149 if (CheckForDeadGoal(unit)) {
00150 return 1;
00151 }
00152 goal = unit->Orders[0]->Goal;
00153
00154
00155
00156
00157 if (!goal && unit->Orders[0]->Action != UnitActionAttackGround) {
00158 goal = AttackUnitsInReactRange(unit);
00159 if (goal) {
00160 if (unit->SavedOrder.Action == UnitActionStill) {
00161
00162 Assert(!unit->Orders[0]->Goal);
00163 unit->SavedOrder = *unit->Orders[0];
00164 }
00165 goal->RefsIncrease();
00166 unit->Orders[0]->Goal = goal;
00167 unit->Orders[0]->MinRange = unit->Type->MinAttackRange;
00168 unit->Orders[0]->Range = unit->Stats->Variables[ATTACKRANGE_INDEX].Max;
00169 unit->Orders[0]->X = unit->Orders[0]->Y = -1;
00170 unit->SubAction |= WEAK_TARGET;
00171 NewResetPath(unit);
00172 }
00173
00174
00175
00176
00177 } else if (goal && (unit->SubAction & WEAK_TARGET)) {
00178 temp = AttackUnitsInReactRange(unit);
00179 if (temp && temp->Type->Priority > goal->Type->Priority) {
00180 goal->RefsDecrease();
00181 temp->RefsIncrease();
00182 if (unit->SavedOrder.Action == UnitActionStill) {
00183
00184 unit->SavedOrder = *unit->Orders[0];
00185 if ((goal = unit->SavedOrder.Goal)) {
00186 unit->SavedOrder.X = goal->X + goal->Type->TileWidth / 2;
00187 unit->SavedOrder.Y = goal->Y + goal->Type->TileHeight / 2;
00188 unit->SavedOrder.MinRange = 0;
00189 unit->SavedOrder.Range = 0;
00190 unit->SavedOrder.Goal = NoUnitP;
00191 }
00192 }
00193 unit->Orders[0]->Goal = goal = temp;
00194 unit->Orders[0]->X = unit->Orders[0]->Y = -1;
00195 NewResetPath(unit);
00196 }
00197 }
00198
00199 Assert(!unit->Type->Vanishes && !unit->Destroyed && !unit->Removed);
00200 Assert(unit->Orders[0]->Action == UnitActionAttack ||
00201 unit->Orders[0]->Action == UnitActionAttackGround);
00202
00203 return 0;
00204 }
00205
00211 static void MoveToTarget(CUnit *unit)
00212 {
00213 CUnit *goal;
00214 int err;
00215
00216 Assert(unit);
00217 Assert(!unit->Type->Vanishes && !unit->Destroyed && !unit->Removed);
00218 Assert(unit->Orders[0]->Action == UnitActionAttack ||
00219 unit->Orders[0]->Action == UnitActionAttackGround);
00220 Assert(CanMove(unit));
00221 Assert(unit->Orders[0]->Goal || (unit->Orders[0]->X != -1 && unit->Orders[0]->Y != -1));
00222
00223 err = DoActionMove(unit);
00224
00225 if (unit->Anim.Unbreakable) {
00226 return;
00227 }
00228
00229
00230
00231 goal = unit->Orders[0]->Goal;
00232 if (err == 0 && !goal) {
00233
00234 if (MapDistanceToUnit(unit->Orders[0]->X, unit->Orders[0]->Y, unit) <
00235 unit->Stats->Variables[ATTACKRANGE_INDEX].Max) {
00236 err = PF_REACHED;
00237 }
00238 }
00239 if (err >= 0) {
00240 if (CheckForTargetInRange(unit)) {
00241 return;
00242 }
00243 return;
00244 }
00245 if (err == PF_REACHED) {
00246
00247
00248
00249 if (goal && MapDistanceBetweenUnits(unit, goal) <=
00250 unit->Stats->Variables[ATTACKRANGE_INDEX].Max) {
00251
00252 unit->State = 0;
00253 UnitHeadingFromDeltaXY(unit,
00254 goal->X + (goal->Type->TileWidth - 1) / 2 - unit->X,
00255 goal->Y + (goal->Type->TileHeight - 1) / 2 - unit->Y);
00256 unit->SubAction++;
00257 return;
00258 }
00259
00260
00261
00262 if (!goal && unit->Orders[0]->Action == UnitActionAttackGround &&
00263 MapDistanceToUnit(unit->Orders[0]->X, unit->Orders[0]->Y, unit) <=
00264 unit->Stats->Variables[ATTACKRANGE_INDEX].Max) {
00265
00266 unit->State = 0;
00267 UnitHeadingFromDeltaXY(unit, unit->Orders[0]->X - unit->X,
00268 unit->Orders[0]->Y - unit->Y);
00269 unit->SubAction &= WEAK_TARGET;
00270 unit->SubAction |= ATTACK_TARGET;
00271 return;
00272 }
00273 }
00274
00275
00276
00277 if (err == PF_UNREACHABLE) {
00278 unit->State = 0;
00279 if (!goal) {
00280
00281
00282
00283 if (unit->Orders[0]->Range < Map.Info.MapWidth ||
00284 unit->Orders[0]->Range < Map.Info.MapHeight) {
00285
00286 unit->Orders[0]->Range++;
00287 unit->Wait = 5;
00288 return;
00289 }
00290 }
00291 }
00292
00293
00294
00295 unit->State = unit->SubAction = 0;
00296 if (unit->Orders[0]->Goal) {
00297 unit->Orders[0]->Goal->RefsDecrease();
00298 }
00299 *unit->Orders[0] = unit->SavedOrder;
00300 NewResetPath(unit);
00301
00302
00303 unit->SavedOrder.Action = UnitActionStill;
00304 unit->SavedOrder.Goal = NoUnitP;
00305 }
00306
00312 static void AttackTarget(CUnit *unit)
00313 {
00314 CUnit *goal;
00315 CUnit *temp;
00316
00317 Assert(unit);
00318 Assert(unit->Orders[0]->Goal || (unit->Orders[0]->X != -1 && unit->Orders[0]->Y != -1));
00319
00320 AnimateActionAttack(unit);
00321 if (unit->Anim.Unbreakable) {
00322 return;
00323 }
00324
00325
00326
00327 goal = unit->Orders[0]->Goal;
00328 if (!goal && unit->Orders[0]->Action == UnitActionAttackGround) {
00329 return;
00330 }
00331
00332
00333
00334
00335 if (CheckForDeadGoal(unit)) {
00336 return;
00337 }
00338 goal = unit->Orders[0]->Goal;
00339
00340
00341
00342
00343 if (!goal) {
00344 unit->State = 0;
00345 goal = AttackUnitsInReactRange(unit);
00346
00347
00348
00349 if (!goal) {
00350
00351 if (unit->SavedOrder.Action != UnitActionStill) {
00352 unit->SubAction = 0;
00353 Assert(unit->Orders[0]->Goal == NoUnitP);
00354 *unit->Orders[0] = unit->SavedOrder;
00355 NewResetPath(unit);
00356
00357 unit->SavedOrder.Action = UnitActionStill;
00358
00359
00360 Assert(unit->SavedOrder.Goal == NoUnitP);
00361 return;
00362 }
00363 unit->SubAction = MOVE_TO_TARGET;
00364 return;
00365 }
00366
00367
00368
00369
00370 if (unit->SavedOrder.Action == UnitActionStill) {
00371 Assert(unit->Orders[0]->Goal == NULL);
00372 unit->SavedOrder = *unit->Orders[0];
00373 }
00374
00375 goal->RefsIncrease();
00376 unit->Orders[0]->Goal = goal;
00377 unit->Orders[0]->X = unit->Orders[0]->Y = -1;
00378 unit->Orders[0]->MinRange = unit->Type->MinAttackRange;
00379 unit->Orders[0]->Range = unit->Stats->Variables[ATTACKRANGE_INDEX].Max;
00380 NewResetPath(unit);
00381 unit->SubAction |= WEAK_TARGET;
00382
00383
00384
00385
00386
00387 } else if (goal && (unit->SubAction & WEAK_TARGET)) {
00388 temp = AttackUnitsInReactRange(unit);
00389 if (temp && temp->Type->Priority > goal->Type->Priority) {
00390 goal->RefsDecrease();
00391 temp->RefsIncrease();
00392
00393 if (unit->SavedOrder.Action == UnitActionStill) {
00394
00395 unit->SavedOrder = *unit->Orders[0];
00396 if ((goal = unit->SavedOrder.Goal)) {
00397 DebugPrint("Have goal to come back %d\n" _C_
00398 UnitNumber(goal));
00399 unit->SavedOrder.X = goal->X + goal->Type->TileWidth / 2;
00400 unit->SavedOrder.Y = goal->Y + goal->Type->TileHeight / 2;
00401 unit->SavedOrder.MinRange = 0;
00402 unit->SavedOrder.Range = 0;
00403 unit->SavedOrder.Goal = NoUnitP;
00404 }
00405 }
00406 unit->Orders[0]->Goal = goal = temp;
00407 unit->Orders[0]->X = unit->Orders[0]->Y = -1;
00408 unit->Orders[0]->MinRange = unit->Type->MinAttackRange;
00409 unit->SubAction = MOVE_TO_TARGET;
00410 NewResetPath(unit);
00411 }
00412 }
00413
00414
00415
00416
00417 if (MapDistanceBetweenUnits(unit, goal) > unit->Stats->Variables[ATTACKRANGE_INDEX].Max) {
00418 if (unit->SavedOrder.Action == UnitActionStill) {
00419
00420 unit->SavedOrder = *unit->Orders[0];
00421 if ((temp = unit->SavedOrder.Goal)) {
00422 DebugPrint("Have goal to come back %d\n" _C_
00423 UnitNumber(temp));
00424 unit->SavedOrder.X = temp->X + temp->Type->TileWidth / 2;
00425 unit->SavedOrder.Y = temp->Y + temp->Type->TileHeight / 2;
00426 unit->SavedOrder.MinRange = 0;
00427 unit->SavedOrder.Range = 0;
00428 unit->SavedOrder.Goal = NoUnitP;
00429 }
00430 }
00431 NewResetPath(unit);
00432 unit->Frame = 0;
00433 unit->State = 0;
00434 unit->SubAction &= WEAK_TARGET;
00435 unit->SubAction |= MOVE_TO_TARGET;
00436 }
00437 if (MapDistanceBetweenUnits(unit, goal) < unit->Type->MinAttackRange) {
00438 unit->SubAction = MOVE_TO_TARGET;
00439 }
00440
00441
00442
00443
00444 if (goal) {
00445 UnitHeadingFromDeltaXY(unit,
00446 goal->X + (goal->Type->TileWidth - 1) / 2 - unit->X,
00447 goal->Y + (goal->Type->TileHeight - 1) / 2 - unit->Y);
00448 }
00449 }
00450
00464 void HandleActionAttack(CUnit *unit)
00465 {
00466 int dist;
00467
00468 Assert(unit->Orders[0]->Action == UnitActionAttackGround ||
00469 unit->Orders[0]->Action == UnitActionAttack);
00470 Assert(unit->Orders[0]->Goal || (unit->Orders[0]->X != -1 && unit->Orders[0]->Y != -1));
00471
00472 if (unit->Wait) {
00473 unit->Wait--;
00474 return;
00475 }
00476
00477 switch (unit->SubAction) {
00478
00479
00480
00481 case 0:
00482
00483 if (CheckForTargetInRange(unit)) {
00484 return;
00485 }
00486
00487 if (unit->Orders[0]->Goal) {
00488 dist = MapDistanceBetweenUnits(unit, unit->Orders[0]->Goal);
00489 if (unit->Type->MinAttackRange < dist && dist <= unit->Stats->Variables[ATTACKRANGE_INDEX].Max) {
00490 UnitHeadingFromDeltaXY(unit,
00491 unit->Orders[0]->Goal->X + (unit->Orders[0]->Goal->Type->TileWidth - 1) / 2 - unit->X,
00492 unit->Orders[0]->Goal->Y + (unit->Orders[0]->Goal->Type->TileHeight - 1) / 2 - unit->Y);
00493 unit->SubAction = ATTACK_TARGET;
00494 AttackTarget(unit);
00495 return;
00496 }
00497 }
00498 unit->SubAction = MOVE_TO_TARGET;
00499 NewResetPath(unit);
00500
00501
00502
00503 Assert(unit->State == 0);
00504
00505
00506
00507
00508 case MOVE_TO_TARGET:
00509 case MOVE_TO_TARGET + WEAK_TARGET:
00510 if (!CanMove(unit)) {
00511
00512 if (unit->Orders[0]->Goal) {
00513 unit->Orders[0]->Goal->RefsDecrease();
00514 }
00515 *unit->Orders[0] = unit->SavedOrder;
00516 unit->SavedOrder.Action = UnitActionStill;
00517 unit->SavedOrder.Goal = NoUnitP;
00518 unit->State = 0;
00519 unit->SubAction = 0;
00520 NewResetPath(unit);
00521 return;
00522 }
00523 MoveToTarget(unit);
00524 break;
00525
00526
00527
00528
00529 case ATTACK_TARGET:
00530 case ATTACK_TARGET + WEAK_TARGET:
00531 AttackTarget(unit);
00532 break;
00533
00534 case WEAK_TARGET:
00535 DebugPrint("FIXME: wrong entry.\n");
00536 break;
00537 }
00538 }
00539