forked from electronicarts/CnC_Remastered_Collection
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDRIVE.CPP
2298 lines (2090 loc) · 90.1 KB
/
DRIVE.CPP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//
// Copyright 2020 Electronic Arts Inc.
//
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
/* $Header: F:\projects\c&c\vcs\code\drive.cpv 2.17 16 Oct 1995 16:51:16 JOE_BOSTIC $ */
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : Command & Conquer *
* *
* File Name : DRIVE.CPP *
* *
* Programmer : Joe L. Bostic *
* *
* Start Date : April 22, 1994 *
* *
* Last Update : July 30, 1995 [JLB] *
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* DriveClass::AI -- Processes unit movement and rotation. *
* DriveClass::Approach_Target -- Handles approaching the target in order to attack it. *
* DriveClass::Assign_Destination -- Set the unit's NavCom. *
* DriveClass::Class_Of -- Fetches a reference to the class type for this object. *
* DriveClass::Debug_Dump -- Displays status information to monochrome screen. *
* DriveClass::Do_Turn -- Tries to turn the vehicle to the specified direction. *
* DriveClass::DriveClass -- Constructor for drive class object. *
* DriveClass::Exit_Map -- Give the unit a movement order to exit the map. *
* DriveClass::Fixup_Path -- Adds smooth start path to normal movement path. *
* DriveClass::Force_Track -- Forces the unit to use the indicated track. *
* DriveClass::Lay_Track -- Handles track laying logic for the unit. *
* DriveClass::Offload_Tiberium_Bail -- Offloads one Tiberium quantum from the object. *
* DriveClass::Ok_To_Move -- Checks to see if this object can begin moving. *
* DriveClass::Overrun_Square -- Handles vehicle overrun of a cell. *
* DriveClass::Per_Cell_Process -- Handles when unit finishes movement into a cell. *
* DriveClass::Smooth_Turn -- Handles the low level coord calc for smooth turn logic. *
* DriveClass::Start_Of_Move -- Tries to get a unit to advance toward cell. *
* DriveClass::Tiberium_Load -- Determine the Tiberium load as a percentage. *
* DriveClass::While_Moving -- Processes unit movement. *
* DriveClass::Mark_Track -- Marks the midpoint of the track as occupied. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "function.h"
DriveClass::DriveClass(void) : Class(0), SimLeptonX(0), SimLeptonY(0) {}; // Added SimLeptonX and Y. ST - 4/30/2019 8:06AM
/***********************************************************************************************
* DriveClass::Do_Turn -- Tries to turn the vehicle to the specified direction. *
* *
* This routine will set the vehicle to rotate to the direction specified. For tracked *
* vehicles, it is just a simple rotation. For wheeled vehicles, it performs a series *
* of short drives (three point turn) to face the desired direction. *
* *
* INPUT: dir -- The direction that this vehicle should face. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/29/1995 JLB : Created. *
*=============================================================================================*/
void DriveClass::Do_Turn(DirType dir)
{
if (dir != PrimaryFacing) {
/*
** Special rotation track is needed for units that
** cannot rotate in place.
*/
if (Special.IsThreePoint && TrackNumber == -1 && Class->Speed == SPEED_WHEEL) {
int facediff; // Signed difference between current and desired facing.
FacingType face; // Current facing (ordinal value).
facediff = PrimaryFacing.Difference(dir) >> 5;
facediff = Bound(facediff, -2, 2);
if (facediff) {
face = Dir_Facing(PrimaryFacing);
IsOnShortTrack = true;
Force_Track(face*FACING_COUNT + (face + facediff), Coord);
Path[0] = FACING_NONE;
Set_Speed(0xFF); // Full speed.
}
} else {
PrimaryFacing.Set_Desired(dir);
//if (Special.IsJurassic && AreThingiesEnabled && What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Class->IsPieceOfEight) PrimaryFacing.Set_Current(dir);
if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Class->IsPieceOfEight) PrimaryFacing.Set_Current(dir);
}
}
}
/***********************************************************************************************
* DriveClass::Force_Track -- Forces the unit to use the indicated track. *
* *
* This override (nuclear bomb) style routine is to be used when a unit needs to start *
* on a movement track but is outside the normal movement system. This occurs when a *
* harvester starts driving off of a refinery. *
* *
* INPUT: track -- The track number to start on. *
* *
* coord -- The coordinate that the unit will end up at when the movement track *
* is completed. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 03/17/1995 JLB : Created. *
*=============================================================================================*/
void DriveClass::Force_Track(int track, COORDINATE coord)
{
TrackNumber = track;
TrackIndex = 0;
Start_Driver(coord);
}
/***********************************************************************************************
* DriveClass::Tiberium_Load -- Determine the Tiberium load as a percentage. *
* *
* Use this routine to determine what the Tiberium load is (as a fixed point percentage). *
* *
* INPUT: none *
* *
* OUTPUT: Returns with the current "fullness" rating for the object. This will be 0x0000 for *
* empty and 0x0100 for full. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 03/17/1995 JLB : Created. *
*=============================================================================================*/
int DriveClass::Tiberium_Load(void) const
{
if (*this == UNIT_HARVESTER) {
return(Cardinal_To_Fixed(UnitTypeClass::STEP_COUNT, Tiberium));
}
return(0x0000);
}
/***********************************************************************************************
* DriveClass::Approach_Target -- Handles approaching the target in order to attack it. *
* *
* This routine will check to see if the target is infantry and it can be overrun. It will *
* try to overrun the infantry rather than attack it. This only applies to computer *
* controlled vehicles. If it isn't the infantry overrun case, then it falls into the *
* base class for normal (complex) approach algorithm. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 03/17/1995 JLB : Created. *
* 07/12/1995 JLB : Flamethrower tanks don't overrun -- their weapon is better. *
*=============================================================================================*/
void DriveClass::Approach_Target(void)
{
/*
** Only if there is a legal target should the approach check occur.
*/
if (!House->IsHuman && Target_Legal(TarCom) && !Target_Legal(NavCom)) {
/*
** Special case:
** If this is for a unit that can crush infantry, and the target is
** infantry, AND the infantry is pretty darn close, then just try
** to drive over the infantry instead of firing on it.
*/
TechnoClass * target = As_Techno(TarCom);
if (Class->Primary != WEAPON_FLAME_TONGUE && Class->IsCrusher && Distance(TarCom) < 0x0180 && target && ((TechnoTypeClass const &)(target->Class_Of())).IsCrushable) {
Assign_Destination(TarCom);
return;
}
}
/*
** In the other cases, uses the more complex "get to just within weapon range"
** algorithm.
*/
FootClass::Approach_Target();
}
/***********************************************************************************************
* DriveClass::Overrun_Square -- Handles vehicle overrun of a cell. *
* *
* This routine is called when a vehicle enters a square or when it is about to enter a *
* square (controlled by parameter). When a vehicle that can crush infantry enters a *
* cell that contains infantry, then the infantry will be destroyed (regardless of *
* affiliation). When a vehicle threatens to overrun a square, all occupying infantry *
* will attempt to get out of the way. *
* *
* INPUT: cell -- The cell that is, or soon will be, entered by a vehicle. *
* *
* threaten -- Don't kill, but just threaten to enter the cell. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 01/19/1995 JLB : Created. *
*=============================================================================================*/
void DriveClass::Overrun_Square(CELL cell, bool threaten)
{
CellClass * cellptr = &Map[cell];
if (Class->IsCrusher) {
if (threaten) {
/*
** If the cell contains infantry, then they will panic when a vehicle tries
** drive over them. Have the infantry run away instead.
*/
if (cellptr->Flag.Composite & 0x1F) {
/*
** Scattering is controlled by the game difficulty level.
*/
if (((GameToPlay == GAME_NORMAL && PlayerPtr->Difficulty == DIFF_HARD) || Special.IsScatter || Scenario > 8) &&
!(GameToPlay == GAME_NORMAL && PlayerPtr->Difficulty == DIFF_EASY)) {
cellptr->Incoming(0, true);
}
}
} else {
ObjectClass * object = cellptr->Cell_Occupier();
int crushed = false;
while (object) {
if (object->Class_Of().IsCrushable && !House->Is_Ally(object) && Distance(object->Center_Coord()) < 0x80) {
ObjectClass * next = object->Next;
crushed = true;
/*
** Record credit for the kill(s)
*/
Sound_Effect(VOC_SQUISH2, Coord);
object->Record_The_Kill(this);
object->Mark(MARK_UP);
object->Limbo();
delete object;
new OverlayClass(OVERLAY_SQUISH, Coord_Cell(Coord));
object = next;
} else {
object = object->Next;
}
}
if (crushed) Do_Uncloak();
}
}
}
/***********************************************************************************************
* DriveClass::DriveClass -- Constructor for drive class object. *
* *
* This will initialize the drive class to its default state. It is called as a result *
* of creating a unit. *
* *
* INPUT: classid -- The unit's ID class. It is passed on to the foot class constructor. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 07/13/1994 JLB : Created. *
*=============================================================================================*/
DriveClass::DriveClass(UnitType classid, HousesType house) :
Class(&UnitTypeClass::As_Reference(classid)),
FootClass(house)
{
/*
** For two shooters, clear out the second shot flag -- it will be set the first time
** the object fires. For non two shooters, set the flag since it will never be cleared
** and the second shot flag tells the system that normal rearm times apply -- this is
** what is desired for non two shooters.
*/
if (Class->IsTwoShooter) {
IsSecondShot = false;
} else {
IsSecondShot = true;
}
IsHarvesting = false;
IsTurretLockedDown = false;
IsOnShortTrack = false;
IsReturning = false;
TrackNumber = -1;
TrackIndex = 0;
SpeedAccum = 0;
Tiberium = 0;
Strength = Class->MaxStrength;
}
#ifdef CHEAT_KEYS
/***********************************************************************************************
* DriveClass::Debug_Dump -- Displays status information to monochrome screen. *
* *
* This debug utility function will display the status of the drive class to the mono *
* screen. It is through this information that bugs can be tracked down. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/31/1994 JLB : Created. *
*=============================================================================================*/
void DriveClass::Debug_Dump(MonoClass *mono) const
{
mono->Set_Cursor(33, 7);
mono->Printf("%2d:%2d", TrackNumber, TrackIndex);
mono->Text_Print("X", 16 + (IsTurretLockedDown?2:0), 10);
// mono->Text_Print("X", 16 + (IsOnShortTrack?2:0), 11);
mono->Set_Cursor(41, 7);mono->Printf("%d", Fixed_To_Cardinal(100, Tiberium_Load()));
FootClass::Debug_Dump(mono);
}
#endif
/***********************************************************************************************
* DriveClass::Exit_Map -- Give the unit a movement order to exit the map. *
* *
* This routine is used to assign an appropriate movement destination for the unit so that *
* it will leave the map. The scripts are usually the one to call this routine when it *
* is determined that the unit has fulfilled its mission and must "depart". *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 05/31/1994 JLB : Created. *
*=============================================================================================*/
void DriveClass::Exit_Map(void)
{
CELL cell; // Map exit cell number.
if (*this == UNIT_HOVER && !Target_Legal(NavCom)) {
/*
** Scan a swath of cells from current position to the edge of the map and if
** there is any blocking object, just wait so to try again later.
*/
Mark(MARK_UP);
for (int x = Cell_X(Coord_Cell(Center_Coord()))-1; x <= Cell_X(Coord_Cell(Center_Coord()))+1; x++) {
for (int y = Cell_Y(Coord_Cell(Center_Coord()))+1; y < Map.MapCellY+Map.MapCellHeight; y++) {
cell = XY_Cell(x, y);
if (Map[cell].Cell_Techno()) {
Mark(MARK_DOWN);
return;
}
}
}
Mark(MARK_DOWN);
/*
** A clear path to the map edge exists. Assign it as the navigation computer
** destination and let the transport move.
*/
cell = XY_Cell(Cell_X(Coord_Cell(Coord)), Map.MapCellY+Map.MapCellHeight);
IsReturning = true;
Assign_Destination(::As_Target(cell));
}
}
/***********************************************************************************************
* DriveClass::Smooth_Turn -- Handles the low level coord calc for smooth turn logic. *
* *
* This routine calculates the new coordinate value needed for the *
* smooth turn logic. The adjustment and flag values must be *
* determined prior to entering this routine. *
* *
* INPUT: adj -- The adjustment coordinate as lifted from the *
* correct smooth turn table. *
* *
* dir -- Pointer to dir for possible modification *
* according to the flag bits. *
* *
* OUTPUT: Returns with the coordinate the unit should positioned to. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 03/14/1994 JLB : Created. *
* 07/13/1994 JLB : Converted to member function. *
*=============================================================================================*/
COORDINATE DriveClass::Smooth_Turn(COORDINATE adj, DirType *dir)
{
DirType workdir = *dir;
int x,y;
int temp;
TrackControlType flags = TrackControl[TrackNumber].Flag;
x = Coord_X(adj);
y = Coord_Y(adj);
if (flags & F_T) {
temp = x;
x = y;
y = temp;
workdir = (DirType)(DIR_W - workdir);
}
if (flags & F_X) {
x = -x;
workdir = (DirType)-workdir;
}
if (flags & F_Y) {
y = -y;
workdir = (DirType)(DIR_S - workdir);
}
*dir = workdir;
return(XY_Coord( Coord_X(Head_To_Coord()) + x, Coord_Y(Head_To_Coord()) + y));
}
/***********************************************************************************************
* DriveClass::Assign_Destination -- Set the unit's NavCom. *
* *
* This routine is used to set the unit's navigation computer to the *
* specified target. Once the navigation computer is set, the unit *
* will start planning and moving toward the destination. *
* *
* INPUT: target -- The destination target for the unit to head to. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 09/07/1992 JLB : Created. *
* 04/15/1994 JLB : Converted to member function. *
*=============================================================================================*/
void DriveClass::Assign_Destination(TARGET target)
{
/*
** Abort early if there is anything wrong with the parameters
** or the unit already is assigned the specified destination.
*/
if (target == NavCom) return;
#ifdef NEVER
UnitClass *tunit; // Destination unit pointer.
/*
** When in move mode, a map position may really indicate
** a unit to guard.
*/
if (Is_Target_Cell(target)) {
cell = As_Cell(target);
tunit = Map[cell].Cell_Unit();
if (tunit) {
/*
** Prevent targeting of itself.
*/
if (tunit != this) {
target = tunit->As_Target();
}
} else {
tbuilding = Map[cell].Cell_Building();
if (tbuilding) {
target = tbuilding->As_Target();
}
}
}
#endif
/*
** For harvesting type vehicles, it might go into a dock and unload procedure
** when the harvester is full and an empty refinery is selected as a target.
*/
BuildingClass * b = As_Building(target);
/*
** Transport vehicles must tell all passengers that are about to load, that they
** cannot proceed. This is accomplished with a radio message to this effect.
*/
//if (tunit && In_Radio_Contact() && Class->IsTransporter && Contact_With_Whom()->Is_Infantry()) {
if (In_Radio_Contact() && Class->IsTransporter && Contact_With_Whom()->Is_Infantry()) {
Transmit_Message(RADIO_OVER_OUT);
}
/*
** If the player clicked on a friendly repair facility and the repair
** facility is currently not involved with some other unit (radio or unloading).
*/
if (b && *b == STRUCT_REPAIR) {
if (b->In_Radio_Contact() && (b->Contact_With_Whom() != this)) {
ArchiveTarget = target;
} else {
/*
** Establish radio contact protocol. If the facility responds correctly,
** then remain in radio contact and proceed toward the desired destination.
*/
if (Transmit_Message(RADIO_HELLO, b) == RADIO_ROGER) {
/*
** Last check to make sure that the loading square is free from permanent
** occupation (such as a building).
*/
CELL cell = Coord_Cell(b->Center_Coord()) + (MAP_CELL_W-1);
if (Ground[Map[cell].Land_Type()].Cost[Class->Speed] ) {
if (Transmit_Message(RADIO_DOCKING) == RADIO_ROGER) {
FootClass::Assign_Destination(target);
Path[0] = FACING_NONE;
return;
}
/*
** Failure to establish a docking relationship with the refinery.
** Bail & await further instructions.
*/
Transmit_Message(RADIO_OVER_OUT);
}
}
}
}
/*
** Set the unit's navigation computer.
*/
FootClass::Assign_Destination(target);
Path[0] = FACING_NONE; // Force recalculation of path.
if (!IsDriving) {
Start_Of_Move();
}
}
/***********************************************************************************************
* DriveClass::While_Moving -- Processes unit movement. *
* *
* This routine is used to process movement for the units as they move. *
* It is called many times for each cell's worth of movement. This *
* routine only applies after the next cell HeadTo has been determined. *
* *
* INPUT: none *
* *
* OUTPUT: true/false; Should this routine be called again? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 02/02/1992 JLB : Created. *
* 04/15/1994 JLB : Converted to member function. *
*=============================================================================================*/
bool DriveClass::While_Moving(void)
{
int actual; // Working movement addition value.
/*
** Perform quick legality checks.
*/
if (!IsDriving || TrackNumber == -1 || (IsRotating && !Class->IsTurretEquipped)) {
SpeedAccum = 0; // Kludge? No speed should accumulate if movement is on hold.
return(false);
}
/*
** If enough movement has accumulated so that the unit can
** visibly move on the map, then process accordingly.
** Slow the unit down if he's carrying a flag.
*/
MPHType maxspeed = MPHType(min((int)(Class->MaxSpeed * House->GroundspeedBias), (int)MPH_LIGHT_SPEED));
if (((UnitClass *)this)->Flagged != HOUSE_NONE) {
actual = SpeedAccum + Fixed_To_Cardinal(maxspeed /2, Speed);
} else {
actual = SpeedAccum + Fixed_To_Cardinal(maxspeed, Speed);
}
if (actual > PIXEL_LEPTON_W) {
TurnTrackType const *track; // Track control pointer.
TrackType const *ptr; // Pointer to coord offset values.
int tracknum; // The track number being processed.
FacingType nextface; // Next facing queued in path.
bool adj; // Is a turn coming up?
track = &TrackControl[TrackNumber];
if (IsOnShortTrack) {
tracknum = track->StartTrack;
} else {
tracknum = track->Track;
}
ptr = RawTracks[tracknum-1].Track;
nextface = Path[0];
/*
** Determine if there is a turn coming up. If there is
** a turn, then track jumping might occur.
*/
adj = false;
if (nextface != FACING_NONE && Dir_Facing(track->Facing) != nextface) {
adj = true;
}
/*
** Skip ahead the number of track steps required (limited only
** by track length). Set the unit to the new position and
** flag the unit accordingly.
*/
Mark(MARK_UP);
while (actual > PIXEL_LEPTON_W) {
COORDINATE offset;
DirType dir;
actual -= PIXEL_LEPTON_W;
offset = ptr[TrackIndex].Offset;
if (offset || !TrackIndex) {
dir = ptr[TrackIndex].Facing;
Coord = Smooth_Turn(offset, &dir);
PrimaryFacing.Set(dir);
/*
** See if "per cell" processing is necessary.
*/
if (TrackIndex && RawTracks[tracknum-1].Cell == TrackIndex) {
Per_Cell_Process(false);
if (!IsActive) {
return(false);
}
}
/*
** The unit could "jump tracks". Check to see if the unit should
** do so.
*/
if (*this != UNIT_GUNBOAT && nextface != FACING_NONE && adj && RawTracks[tracknum-1].Jump == TrackIndex && TrackIndex) {
TurnTrackType const *newtrack; // Proposed jump-to track.
int tnum;
tnum = Dir_Facing(track->Facing)*FACING_COUNT + nextface;
newtrack = &TrackControl[tnum];
if (newtrack->Track && RawTracks[newtrack->Track-1].Entry) {
COORDINATE c = Head_To_Coord();
int oldspeed = Speed;
c = Adjacent_Cell(c, nextface);
switch(Can_Enter_Cell(Coord_Cell(c), nextface)) {
case MOVE_OK:
IsOnShortTrack = false; // Shouldn't be necessary, but...
TrackNumber = tnum;
track = newtrack;
// Mono_Printf("**Jumping from track %d to track %d. **\n", tracknum, track->Track);Keyboard::Get();
tracknum = track->Track;
TrackIndex = RawTracks[tracknum-1].Entry-1; // Anticipate increment.
ptr = RawTracks[tracknum-1].Track;
adj = false;
Stop_Driver();
Per_Cell_Process(true);
if (Start_Driver(c)) {
Set_Speed(oldspeed);
memcpy(&Path[0], &Path[1], CONQUER_PATH_MAX-1);
Path[CONQUER_PATH_MAX-1] = FACING_NONE;
} else {
Path[0] = FACING_NONE;
TrackNumber = -1;
actual = 0;
}
break;
case MOVE_CLOAK:
Map[Coord_Cell(c)].Shimmer();
break;
case MOVE_TEMP:
if (*this == UNIT_HARVESTER || !House->IsHuman) {
bool old = Special.IsScatter;
Special.IsScatter = true;
Map[Coord_Cell(c)].Incoming(0, true);
Special.IsScatter = old;
}
break;
}
}
}
TrackIndex++;
} else {
actual = 0;
Coord = Head_To_Coord();
Stop_Driver();
TrackNumber = -1;
TrackIndex = NULL;
/*
** Perform "per cell" activities.
*/
Per_Cell_Process(true);
break;
}
}
if (IsActive) {
Mark(MARK_DOWN);
}
}
/*
** NEW 4/30/2019 7:59AM
**
** When we don't have enough speed accumulated to move another pixel, it would be good to know at a sub-pixel (lepton) level
** how far we would move if we could. It didn't matter in the original when it was 320x200 pixels, but on a 3840x2160
** screen, what was half a pixel could now be several pixels.
**
** ST
**
*/
if (actual && actual <= PIXEL_LEPTON_W) {
TurnTrackType const *track; // Track control pointer.
TrackType const *ptr; // Pointer to coord offset values.
int tracknum; // The track number being processed.
FacingType nextface; // Next facing queued in path.
bool adj; // Is a turn coming up?
track = &TrackControl[TrackNumber];
if (IsOnShortTrack) {
tracknum = track->StartTrack;
} else {
tracknum = track->Track;
}
ptr = RawTracks[tracknum-1].Track;
nextface = Path[0];
/*
** Determine if there is a turn coming up. If there is
** a turn, then track jumping might occur.
*/
adj = false;
if (nextface != FACING_NONE && Dir_Facing(track->Facing) != nextface) {
adj = true;
}
COORDINATE simulated_pos = Coord;
COORDINATE offset;
DirType dir;
offset = ptr[TrackIndex].Offset;
if (offset || !TrackIndex) {
dir = ptr[TrackIndex].Facing;
simulated_pos = Smooth_Turn(offset, &dir);
}
int x_diff = Coord_X(simulated_pos) - Coord_X(Coord);
int y_diff = Coord_Y(simulated_pos) - Coord_Y(Coord);
SimLeptonX = (x_diff * actual) / PIXEL_LEPTON_W;
SimLeptonY = (y_diff * actual) / PIXEL_LEPTON_W;
} else {
SimLeptonX = 0;
SimLeptonY = 0;
}
/*
** Replace any remainder back into the unit's movement
** accumulator to be processed next pass.
*/
SpeedAccum = actual;
return(true);
}
/***********************************************************************************************
* DriveClass::Per_Cell_Process -- Handles when unit finishes movement into a cell. *
* *
* This routine is called when a unit has mostly or completely *
* entered a cell. The unit might be in the middle of a movement track *
* when this routine is called. It's primary purpose is to perform *
* sighting and other "per cell" activities. *
* *
* INPUT: center -- Is the unit safely at the center of a cell? If it is merely "close" *
* to the center, then this parameter will be false. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/03/1993 JLB : Created. *
* 03/30/1994 JLB : Revamped for track system. *
* 04/15/1994 JLB : Converted to member function. *
* 06/18/1994 JLB : Converted to virtual function. *
* 06/18/1994 JLB : Distinguishes between center and near-center conditions. *
*=============================================================================================*/
void DriveClass::Per_Cell_Process(bool center)
{
CELL cell = Coord_Cell(Coord);
/*
** Check to see if it has reached its destination. If so, then clear the NavCom
** regardless of the remaining path list.
*/
if (center && As_Cell(NavCom) == cell) {
IsTurretLockedDown = false;
NavCom = TARGET_NONE;
Path[0] = FACING_NONE;
}
#ifdef NEVER
/*
** A "lemon" vehicle will have a tendency to break down as
** it moves about the terrain.
*/
if (Is_A_Lemon) {
if (Random_Pick(1, 4) == 1) {
Take_Damage(1);
}
}
#endif
Lay_Track();
FootClass::Per_Cell_Process(center);
}
/***********************************************************************************************
* DriveClass::Start_Of_Move -- Tries to get a unit to advance toward cell. *
* *
* This will try to start a unit advancing toward the cell it is *
* facing. It will check for and handle legality and reserving of the *
* necessary cell. *
* *
* INPUT: none *
* *
* OUTPUT: true/false; Should this routine be called again because *
* initial start operation is temporarily delayed? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 02/02/1992 JLB : Created. *
* 10/18/1993 JLB : This should be called repeatedly until HeadTo is not NULL. *
* 03/16/1994 JLB : Revamped for track logic. *
* 04/15/1994 JLB : Converted to member function. *
* 06/19/1995 JLB : Fixed so that it won't fire on ground unnecessarily. *
* 07/13/1995 JLB : Handles bumping into cloaked objects. *
*=============================================================================================*/
bool DriveClass::Start_Of_Move(void)
{
FacingType facing; // Direction movement will commence.
DirType dir; // Desired actual facing toward destination.
int facediff; // Difference between current and desired facing.
int speed; // Speed of unit.
CELL destcell; // Cell of destination.
LandType ground; // Ground unit is entering.
COORDINATE dest; // Destination coordinate.
facing = Path[0];
if (!Target_Legal(NavCom) && facing == FACING_NONE) {
IsTurretLockedDown = false;
Stop_Driver();
if (Mission == MISSION_MOVE) {
Enter_Idle_Mode();
}
return(false); // Why is it calling this routine!?!
}
#ifdef NEVER
/*
** Movement start logic can't begin until a unit that requires
** a locked down turret gets to a locked down state (i.e., the
** turret rotation stops.
*/
if (ClassF & CLASSF_LOCKTURRET) {
Set_Secondary_Facing(facing<<5);
if (Is_Rotating) {
return(true);
}
}
#endif
/*
** Reduce the path length if the target is a unit and the
** range to the unit is less than the precalculated path steps.
*/
if (facing != FACING_NONE) {
int dist;
if (Is_Target_Unit(NavCom) || Is_Target_Infantry(NavCom)) {
dist = Lepton_To_Cell(Distance(NavCom));
// if (dist > CELL_LEPTON_W ||
// !As_Techno(NavCom)->Techno_Type_Class()->IsCrushable ||
// !Class->IsCrusher) {
if (dist < CONQUER_PATH_MAX) {
Path[dist] = FACING_NONE;
facing = Path[0]; // Maybe needed.
}
// }
}
}
/*
** If the path is invalid at this point, then generate one. If
** generating a new path fails, then abort NavCom.
*/
if (facing == FACING_NONE) {
/*
** If after a path search, there is still no valid path, then set the
** NavCom to null and let the script take care of assigning a new
** navigation target.
*/
if (!PathDelay.Expired()) {
return(false);
}
if (!Basic_Path()) {
if (Distance(NavCom) < 0x0280 && (Mission == MISSION_MOVE || Mission == MISSION_GUARD_AREA)) {
Assign_Destination(TARGET_NONE);
} else {
/*
** If a basic path could be found, but the immediate move destination is
** blocked by a friendly temporary blockage, then cause that blockage
** to scatter. If the destination is also one cell away, then scatter
** regardless of direction.
*/
CELL ourcell = Coord_Cell(Center_Coord());
CELL navcell = As_Cell(NavCom);
CELL cell = -1;
if (::Distance(ourcell, navcell) < 2) {
cell = navcell;
} else {
cell = Adjacent_Cell(ourcell, PrimaryFacing.Current());
}
if (Map.In_Radar(cell)) {
if (Can_Enter_Cell(cell) == MOVE_TEMP) {
CellClass * cellptr = &Map[cell];
TechnoClass * blockage = cellptr->Cell_Techno();
if (blockage && House->Is_Ally(blockage)) {
bool old = Special.IsScatter;
Special.IsScatter = true;
cellptr->Incoming(0, true);
Special.IsScatter = old;
}
}
}
if (TryTryAgain) {
TryTryAgain--;
} else {
Assign_Destination(TARGET_NONE);
if (IsNewNavCom) Sound_Effect(VOC_SCOLD);
IsNewNavCom = false;
}
}
Stop_Driver();
TrackNumber = -1;
IsTurretLockedDown = false;
return(false);