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
00031
00032
00033
00034
00035 #include <stdio.h>
00036 #include <stdlib.h>
00037 #include <string.h>
00038 #include <time.h>
00039
00040 #include "stratagus.h"
00041 #include "version.h"
00042 #include "unittype.h"
00043 #include "animation.h"
00044 #include "player.h"
00045 #include "unit.h"
00046 #include "actions.h"
00047 #include "missile.h"
00048 #include "interface.h"
00049 #include "map.h"
00050 #include "sound.h"
00051 #include "spells.h"
00052
00053
00054
00055
00056
00057 unsigned SyncHash;
00058
00059
00060
00061
00062
00063
00064
00065
00066
00073 static void UnitRotate(CUnit *unit, int rotate)
00074 {
00075 unit->Direction += rotate * 256 / unit->Type->NumDirections;
00076 UnitUpdateHeading(unit);
00077 }
00078
00087 int UnitShowAnimation(CUnit *unit, const CAnimation *anim)
00088 {
00089 return UnitShowAnimationScaled(unit, anim, 8);
00090 }
00091
00101 int UnitShowAnimationScaled(CUnit *unit, const CAnimation *anim, int scale)
00102 {
00103 int move;
00104
00105
00106 if (unit->Anim.CurrAnim != anim) {
00107
00108 Assert(!unit->Anim.Unbreakable);
00109 unit->Anim.Anim = unit->Anim.CurrAnim = anim;
00110 unit->Anim.Wait = 0;
00111 }
00112
00113
00114 if (unit->Anim.Wait) {
00115 --unit->Anim.Wait;
00116 if (!unit->Anim.Wait) {
00117
00118 unit->Anim.Anim = unit->Anim.Anim->Next;
00119 if (!unit->Anim.Anim) {
00120 unit->Anim.Anim = unit->Anim.CurrAnim;
00121 }
00122 }
00123 return 0;
00124 }
00125
00126 move = 0;
00127 while (!unit->Anim.Wait) {
00128 switch (unit->Anim.Anim->Type) {
00129 case AnimationFrame:
00130 unit->Frame = unit->Anim.Anim->D.Frame.Frame;
00131 UnitUpdateHeading(unit);
00132 break;
00133 case AnimationExactFrame:
00134 unit->Frame = unit->Anim.Anim->D.Frame.Frame;
00135 break;
00136 case AnimationRandomFrame:
00137 unit->Frame = unit->Anim.Anim->D.RandomFrame.MinFrame +
00138 SyncRand() % (unit->Anim.Anim->D.RandomFrame.MaxFrame - unit->Anim.Anim->D.RandomFrame.MinFrame + 1);
00139 UnitUpdateHeading(unit);
00140 break;
00141
00142 case AnimationWait:
00143 unit->Anim.Wait = unit->Anim.Anim->D.Wait.Wait << scale >> 8;
00144 if (unit->Anim.Wait <= 0)
00145 unit->Anim.Wait = 1;
00146 break;
00147 case AnimationRandomWait:
00148 unit->Anim.Wait = unit->Anim.Anim->D.RandomWait.MinWait +
00149 SyncRand() % (unit->Anim.Anim->D.RandomWait.MaxWait - unit->Anim.Anim->D.RandomWait.MinWait + 1);
00150 break;
00151
00152 case AnimationSound:
00153 if (unit->IsVisible(ThisPlayer) || ReplayRevealMap) {
00154 PlayUnitSound(unit, unit->Anim.Anim->D.Sound.Sound);
00155 }
00156 break;
00157 case AnimationRandomSound:
00158 if (unit->IsVisible(ThisPlayer) || ReplayRevealMap) {
00159 int sound;
00160 sound = SyncRand() % unit->Anim.Anim->D.RandomSound.NumSounds;
00161 PlayUnitSound(unit, unit->Anim.Anim->D.RandomSound.Sound[sound]);
00162 }
00163 break;
00164
00165 case AnimationAttack:
00166 if (unit->Orders[0]->Action == UnitActionSpellCast) {
00167 if (unit->Orders[0]->Goal &&
00168 !unit->Orders[0]->Goal->IsVisibleAsGoal(unit->Player)) {
00169 unit->ReCast = 0;
00170 } else {
00171 unit->ReCast = SpellCast(unit, unit->Orders[0]->Arg1.Spell,
00172 unit->Orders[0]->Goal, unit->Orders[0]->X, unit->Orders[0]->Y);
00173 }
00174 } else {
00175 FireMissile(unit);
00176 }
00177 break;
00178
00179 case AnimationRotate:
00180 UnitRotate(unit, unit->Anim.Anim->D.Rotate.Rotate);
00181 break;
00182 case AnimationRandomRotate:
00183 if ((SyncRand() >> 8) & 1) {
00184 UnitRotate(unit, -unit->Anim.Anim->D.Rotate.Rotate);
00185 } else {
00186 UnitRotate(unit, unit->Anim.Anim->D.Rotate.Rotate);
00187 }
00188 break;
00189
00190 case AnimationMove:
00191 Assert(!move);
00192 move = unit->Anim.Anim->D.Move.Move;
00193 break;
00194
00195 case AnimationUnbreakable:
00196 Assert(unit->Anim.Unbreakable ^ unit->Anim.Anim->D.Unbreakable.Begin);
00197 unit->Anim.Unbreakable = unit->Anim.Anim->D.Unbreakable.Begin;
00198 break;
00199
00200 case AnimationNone:
00201 case AnimationLabel:
00202 break;
00203
00204 case AnimationGoto:
00205 unit->Anim.Anim = unit->Anim.Anim->D.Goto.Goto;
00206 break;
00207 case AnimationRandomGoto:
00208 if (SyncRand() % 100 < unit->Anim.Anim->D.RandomGoto.Random) {
00209 unit->Anim.Anim = unit->Anim.Anim->D.RandomGoto.Goto;
00210 }
00211 break;
00212 }
00213
00214 if (!unit->Anim.Wait) {
00215
00216 unit->Anim.Anim = unit->Anim.Anim->Next;
00217 if (!unit->Anim.Anim) {
00218 unit->Anim.Anim = unit->Anim.CurrAnim;
00219 }
00220 }
00221 }
00222
00223 --unit->Anim.Wait;
00224 if (!unit->Anim.Wait) {
00225
00226 unit->Anim.Anim = unit->Anim.Anim->Next;
00227 if (!unit->Anim.Anim) {
00228 unit->Anim.Anim = unit->Anim.CurrAnim;
00229 }
00230 }
00231 return move;
00232 }
00233
00234
00235
00236
00237
00243 static void HandleActionNone(CUnit *unit)
00244 {
00245 DebugPrint("FIXME: Should not happen!\n");
00246 DebugPrint("FIXME: Unit (%d) %s has action none.!\n" _C_
00247 UnitNumber(unit) _C_ unit->Type->Ident.c_str());
00248 }
00249
00255 static void (*HandleActionTable[])(CUnit *) = {
00256 HandleActionNone,
00257 HandleActionStill,
00258 HandleActionStandGround,
00259 HandleActionFollow,
00260 HandleActionMove,
00261 HandleActionAttack,
00262 HandleActionAttack,
00263 HandleActionDie,
00264 HandleActionSpellCast,
00265 HandleActionTrain,
00266 HandleActionBuilt,
00267 HandleActionBoard,
00268 HandleActionUnload,
00269 HandleActionPatrol,
00270 HandleActionBuild,
00271 HandleActionRepair,
00272 HandleActionResource,
00273 };
00274
00280 static void HandleRegenerations(CUnit *unit)
00281 {
00282 int f = 0;
00283
00284
00285 if (!unit->Removed && !unit->Destroyed && unit->Variable[HP_INDEX].Max &&
00286 unit->Orders[0]->Action != UnitActionBuilt &&
00287 unit->Orders[0]->Action != UnitActionDie) {
00288 f = (100 * unit->Variable[HP_INDEX].Value) / unit->Variable[HP_INDEX].Max;
00289 if (f <= unit->Type->BurnPercent && unit->Type->BurnDamageRate) {
00290 HitUnit(NoUnitP, unit, unit->Type->BurnDamageRate);
00291 f = 1;
00292 } else {
00293 f = 0;
00294 }
00295 }
00296
00297
00298 unit->Variable[HP_INDEX].Increase = f ? 0 : unit->Stats->Variables[HP_INDEX].Increase;
00299 }
00300
00307 static void HandleBuffs(CUnit *unit, int amount)
00308 {
00309
00310
00311
00312 if (unit->TTL && unit->TTL < (GameCycle - unit->Variable[HP_INDEX].Value)) {
00313 DebugPrint("Unit must die %lu %lu!\n" _C_ unit->TTL _C_ GameCycle);
00314
00315
00316
00317 unit->Variable[HP_INDEX].Value -= amount;
00318 if (unit->Variable[HP_INDEX].Value <= 0) {
00319 LetUnitDie(unit);
00320 }
00321 }
00322
00323
00324 for (int i = 0; i < UnitTypeVar.NumberVariable; i++) {
00325 if (unit->Variable[i].Enable && unit->Variable[i].Increase) {
00326 unit->Variable[i].Value += unit->Variable[i].Increase;
00327 if (unit->Variable[i].Value <= 0) {
00328 unit->Variable[i].Value = 0;
00329 } else if (unit->Variable[i].Value > unit->Variable[i].Max) {
00330 unit->Variable[i].Value = unit->Variable[i].Max;
00331 }
00332 }
00333 }
00334 }
00335
00336
00337 static void RunAction(unsigned char action, CUnit *unit)
00338 {
00339 HandleActionTable[action](unit);
00340 }
00341
00342
00348 static void HandleUnitAction(CUnit *unit)
00349 {
00350
00351
00352
00353 if (!unit->Anim.Unbreakable) {
00354
00355
00356
00357
00358 if (unit->OrderCount > 1 &&
00359 (unit->Orders[0]->Action == UnitActionStill || unit->OrderFlush)) {
00360
00361 if (unit->Removed) {
00362 DebugPrint("Flushing removed unit\n");
00363
00364 return;
00365 }
00366
00367
00368
00369
00370 if (unit->Orders[0]->Goal) {
00371
00372 Assert(!(unit->Orders[0]->Action == UnitActionStill && !unit->SubAction));
00373 unit->Orders[0]->Goal->RefsDecrease();
00374 }
00375
00376 UnitRemoveConsumingResources(unit);
00377
00378
00379
00380
00381 unit->OrderCount--;
00382 unit->OrderFlush = 0;
00383 delete unit->Orders[0];
00384 for (int z = 0; z < unit->OrderCount; ++z) {
00385 unit->Orders[z] = unit->Orders[z + 1];
00386 }
00387 unit->Orders.pop_back();
00388
00389
00390
00391
00392 unit->SubAction = unit->State = 0;
00393 unit->Wait = 0;
00394
00395 if (IsOnlySelected(unit)) {
00396 SelectedUnitChanged();
00397 }
00398 }
00399 }
00400
00401
00402
00403
00404 RunAction(unit->Orders[0]->Action, unit);
00405 }
00406
00412 void UnitActions(void)
00413 {
00414 CUnit *table[UnitMax];
00415 CUnit *unit;
00416 int blinkthiscycle;
00417 int buffsthiscycle;
00418 int regenthiscycle;
00419 int i;
00420 int tabsize;
00421
00422 buffsthiscycle = regenthiscycle = blinkthiscycle =
00423 !(GameCycle % CYCLES_PER_SECOND);
00424
00425 memcpy(table, Units, NumUnits * sizeof(CUnit *));
00426 tabsize = NumUnits;
00427
00428
00429
00430
00431
00432
00433
00434 if (blinkthiscycle) {
00435 for (i = 0; i < tabsize; ++i) {
00436 if (table[i]->Destroyed) {
00437 table[i--] = table[--tabsize];
00438 continue;
00439 }
00440 if (table[i]->Blink) {
00441 --table[i]->Blink;
00442 }
00443 }
00444 }
00445
00446
00447 if (buffsthiscycle) {
00448 for (i = 0; i < tabsize; ++i) {
00449 if (table[i]->Destroyed) {
00450 table[i--] = table[--tabsize];
00451 continue;
00452 }
00453 HandleBuffs(table[i], CYCLES_PER_SECOND);
00454 }
00455 }
00456
00457
00458 if (regenthiscycle) {
00459 for (i = 0; i < tabsize; ++i) {
00460 if (table[i]->Destroyed) {
00461 table[i--] = table[--tabsize];
00462 continue;
00463 }
00464 HandleRegenerations(table[i]);
00465 }
00466 }
00467
00468
00469
00470
00471 for (i = 0; i < tabsize; ++i) {
00472 while (table[i]->Destroyed) {
00473 table[i] = table[--tabsize];
00474 }
00475 unit = table[i];
00476
00477 HandleUnitAction(unit);
00478
00479 #ifdef DEBUG_LOG
00480
00481
00482
00483 {
00484 static FILE *logf;
00485
00486 if (!logf) {
00487 time_t now;
00488 char buf[256];
00489
00490 sprintf_s(buf, sizeof(buf), "log_of_boswars_%d.log", ThisPlayer->Index);
00491 logf = fopen(buf, "wb");
00492 if (!logf) {
00493 return;
00494 }
00495 fprintf(logf, ";;; Log file generated by Bos Wars Version "
00496 VERSION "\n");
00497 time(&now);
00498 fprintf(logf, ";;;\tDate: %s", ctime(&now));
00499 fprintf(logf, ";;;\tMap: %s\n\n", Map.Info.Description.c_str());
00500 }
00501
00502 fprintf(logf, "%lu: ", GameCycle);
00503 fprintf(logf, "%d %s S%d/%d-%d P%d Refs %d: %X %d,%d %d,%d\n",
00504 UnitNumber(unit), unit->Type ? unit->Type->Ident.c_str() : "unit-killed",
00505 unit->State, unit->SubAction,
00506 !unit->Orders.empty() ? unit->Orders[0]->Action : -1,
00507 unit->Player ? unit->Player->Index : -1, unit->Refs,SyncRandSeed,
00508 unit->X, unit->Y, unit->IX, unit->IY);
00509
00510
00511 fflush(NULL);
00512 }
00513 #endif
00514
00515
00516
00517 SyncHash = (SyncHash << 5) | (SyncHash >> 27);
00518 SyncHash ^= !unit->Orders.empty() ? unit->Orders[0]->Action << 18 : 0;
00519 SyncHash ^= unit->State << 12;
00520 SyncHash ^= unit->SubAction << 6;
00521 SyncHash ^= unit->Refs << 3;
00522 }
00523 }
00524