~ubuntu-branches/ubuntu/lucid/warzone2100/lucid

« back to all changes in this revision

Viewing changes to src/droid.c

  • Committer: Bazaar Package Importer
  • Author(s): Christoph Egger, Paul Wise, Christoph Egger
  • Date: 2009-06-29 17:12:52 UTC
  • mfrom: (1.1.11 upstream) (2.1.7 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090629171252-5ddnlfg3zfchrega
Tags: 2.2.1+dfsg1-1
[ Paul Wise ]
* New upstream release (Closes: #534962)
* Adjust the flex build-depends to take account of the conflict
  with all the versions of flex 2.5.34 (LP: #372872)
* Make the -music Recommends more strict, 2.1 music doesn't work
  with 2.2.
* Upstream moved the downloads to sourceforge, update the watch file
* Bump Standards-Version, no changes needed
* Drop use of dh_desktop since it no longer does anything
* Recommend the new warzone2100-video package, version 2.2 or similar
* Mention the warzone2100 crash reports in the -dbg package description

[ Christoph Egger ]
* Replace CC-2.0 graphic from cybersphinx, create a new tarball
* Add myself to uploaders

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
2
        This file is part of Warzone 2100.
3
3
        Copyright (C) 1999-2004  Eidos Interactive
4
 
        Copyright (C) 2005-2007  Warzone Resurrection Project
 
4
        Copyright (C) 2005-2009  Warzone Resurrection Project
5
5
 
6
6
        Warzone 2100 is free software; you can redistribute it and/or modify
7
7
        it under the terms of the GNU General Public License as published by
24
24
 *
25
25
 */
26
26
#include "lib/framework/frame.h"
27
 
#include "lib/framework/math-help.h"
 
27
#include "lib/framework/math_ext.h"
28
28
#include "lib/framework/strres.h"
29
29
 
30
30
#include "lib/gamelib/gtime.h"
31
31
#include "lib/gamelib/animobj.h"
32
32
#include "lib/ivis_opengl/piematrix.h"
33
33
#include "lib/ivis_opengl/screen.h"
 
34
#include "lib/framework/fixedpoint.h"
34
35
#include "lib/script/script.h"
35
36
#include "lib/sound/audio.h"
36
37
#include "lib/sound/audio_id.h"
57
58
#include "component.h"
58
59
#include "function.h"
59
60
#include "lighting.h"
60
 
#include "gateway.h"
61
61
#include "multiplay.h"
62
62
#include "formationdef.h"
63
63
#include "formation.h"
211
211
                        destroyDroid(psDroid);
212
212
                }
213
213
        }
214
 
        
 
214
 
215
215
        return relativeDamage;
216
216
}
217
217
 
218
 
// Check that psVictimDroid is not referred to by any other object in the game
 
218
// Check that psVictimDroid is not referred to by any other object in the game. We can dump out some
 
219
// extra data in debug builds that help track down sources of dangling pointer errors.
 
220
#ifdef DEBUG
 
221
#define DROIDREF(func, line) "Illegal reference to droid from %s line %d", func, line
 
222
#else
 
223
#define DROIDREF(func, line) "Illegal reference to droid"
 
224
#endif
219
225
BOOL droidCheckReferences(DROID *psVictimDroid)
220
226
{
221
 
        int plr, i;
 
227
        int plr;
222
228
 
223
229
        for (plr = 0; plr < MAX_PLAYERS; plr++)
224
230
        {
227
233
 
228
234
                for (psStruct = apsStructLists[plr]; psStruct != NULL; psStruct = psStruct->psNext)
229
235
                {
 
236
                        unsigned int i;
 
237
 
230
238
                        for (i = 0; i < psStruct->numWeaps; i++)
231
239
                        {
232
 
                                if ((DROID *)psStruct->psTarget[i] == psVictimDroid)
233
 
                                {
234
 
#ifndef DEBUG
235
 
                                        debug(LOG_ERROR, "droidCheckReferences: Illegal reference to droid");
236
 
#else
237
 
                                        ASSERT(!"Illegal reference to droid", "Illegal reference to droid from %s line %d",
238
 
                                               psStruct->targetFunc[i], psStruct->targetLine[i]);
239
 
#endif
240
 
                                        return false;
241
 
                                }
 
240
                                ASSERT_OR_RETURN(false, (DROID *)psStruct->psTarget[i] != psVictimDroid, DROIDREF(psStruct->targetFunc[i], psStruct->targetLine[i]));
242
241
                        }
243
242
                }
244
243
                for (psDroid = apsDroidLists[plr]; psDroid != NULL; psDroid = psDroid->psNext)
245
244
                {
246
 
                        if ((DROID *)psDroid->psTarget == psVictimDroid && psVictimDroid != psDroid)
247
 
                        {
248
 
#ifndef DEBUG
249
 
                                debug(LOG_ERROR, "droidCheckReferences: Illegal reference to droid");
250
 
#else
251
 
                                ASSERT(!"Illegal reference to droid", "Illegal reference to droid from %s line %d",
252
 
                                       psDroid->targetFunc, psDroid->targetLine);
253
 
#endif
254
 
                                return false;
255
 
                        }
 
245
                        unsigned int i;
 
246
 
 
247
                        ASSERT_OR_RETURN(false, (DROID *)psDroid->psTarget != psVictimDroid || psVictimDroid == psDroid, DROIDREF(psDroid->targetFunc, psDroid->targetLine));
256
248
                        for (i = 0; i < psDroid->numWeaps; i++)
257
249
                        {
258
 
                                if ((DROID *)psDroid->psActionTarget[i] == psVictimDroid && psVictimDroid != psDroid)
259
 
                                {
260
 
#ifndef DEBUG
261
 
                                        debug(LOG_ERROR, "droidCheckReferences: Illegal action reference to droid");
262
 
#else
263
 
                                        ASSERT(!"Illegal reference to droid", "Illegal action reference to droid from %s line %d",
264
 
                                               psDroid->actionTargetFunc[i], psDroid->actionTargetLine[i]);
265
 
#endif
266
 
                                        return false;
267
 
                                }
 
250
                                ASSERT_OR_RETURN(false, (DROID *)psDroid->psActionTarget[i] != psVictimDroid || psVictimDroid == psDroid, 
 
251
                                                 DROIDREF(psDroid->actionTargetFunc[i], psDroid->actionTargetLine[i]));
268
252
                        }
269
253
                }
270
254
        }
271
255
        return true;
272
256
}
 
257
#undef DROIDREF
273
258
 
274
259
/* droidRelease: release all resources associated with a droid -
275
260
 * should only be called by objmem - use vanishDroid preferably
281
266
        /* remove animation if present */
282
267
        if (psDroid->psCurAnim != NULL)
283
268
        {
284
 
                animObj_Remove(&psDroid->psCurAnim, psDroid->psCurAnim->psAnim->uwID);
 
269
                animObj_Remove(psDroid->psCurAnim, psDroid->psCurAnim->psAnim->uwID);
285
270
                psDroid->psCurAnim = NULL;
286
271
        }
287
272
 
300
285
                }
301
286
        }
302
287
 
 
288
        fpathRemoveDroidData(psDroid->id);
 
289
 
303
290
        // leave the current formation if any
304
291
        if (psDroid->sMove.psFormation)
305
292
        {
317
304
 
318
305
        // remove the droid from the cluster systerm
319
306
        clustRemoveObject((BASE_OBJECT *)psDroid);
 
307
 
 
308
        if (psDroid->sMove.asPath)
 
309
        {
 
310
                free(psDroid->sMove.asPath);
 
311
        }
320
312
}
321
313
 
322
314
 
375
367
void    removeDroidBase(DROID *psDel)
376
368
{
377
369
        DROID   *psCurr, *psNext;
378
 
        BOOL    bRet;
379
370
        DROID_GROUP     *psGroup;
380
371
        STRUCTURE       *psStruct;
381
372
 
394
385
        if (bMultiPlayer
395
386
         && !(psDel->player != selectedPlayer && psDel->order == DORDER_RECYCLE))
396
387
        {
397
 
                ASSERT(droidOnMap(psDel), "Asking other players to destroy droid driving off the map");
 
388
                ASSERT_OR_RETURN( , droidOnMap(psDel), "Asking other players to destroy droid driving off the map");
398
389
                SendDestroyDroid(psDel);
399
390
        }
400
391
 
402
393
        /* remove animation if present */
403
394
        if (psDel->psCurAnim != NULL)
404
395
        {
405
 
                bRet = animObj_Remove( &psDel->psCurAnim, psDel->psCurAnim->psAnim->uwID );
406
 
                ASSERT( bRet == true, "destroyUnit: animObj_Remove failed" );
 
396
                const bool bRet = animObj_Remove(psDel->psCurAnim, psDel->psCurAnim->psAnim->uwID);
 
397
                ASSERT(bRet, "animObj_Remove failed");
407
398
                psDel->psCurAnim = NULL;
408
399
        }
409
400
 
622
613
{
623
614
        DROID   *psDroid;
624
615
 
625
 
        ASSERT( psObj != NULL,
626
 
                "unitFlameFallCallback: invalid anim object pointer\n" );
 
616
        ASSERT_OR_RETURN( , psObj != NULL, "invalid anim object pointer");
627
617
        psDroid = (DROID *) psObj->psParent;
628
 
        ASSERT( psDroid != NULL,
629
 
                "unitFlameFallCallback: invalid Unit pointer\n" );
630
618
 
 
619
        ASSERT_OR_RETURN( , psDroid != NULL, "invalid Unit pointer");
631
620
        psDroid->psCurAnim = NULL;
632
621
 
633
622
        debug(LOG_DEATH, "droidFlameFallCallback: Droid %d destroyed", (int)psDroid->id);
638
627
{
639
628
        DROID   *psDroid;
640
629
 
641
 
        ASSERT( psObj != NULL,
642
 
                "unitBurntCallback: invalid anim object pointer\n" );
 
630
        ASSERT_OR_RETURN( , psObj != NULL, "invalid anim object pointer");
643
631
        psDroid = (DROID *) psObj->psParent;
644
 
        ASSERT( psDroid != NULL,
645
 
                "unitBurntCallback: invalid Unit pointer\n" );
 
632
 
 
633
        ASSERT_OR_RETURN( , psDroid != NULL, "invalid Unit pointer");
646
634
 
647
635
        /* add falling anim */
648
636
        psDroid->psCurAnim = animObj_Add((BASE_OBJECT *)psDroid, ID_ANIM_DROIDFLAMEFALL, 0, 1);
649
637
        if (!psDroid->psCurAnim)
650
638
        {
651
 
                debug( LOG_ERROR, "unitBurntCallback: couldn't add fall over anim\n" );
652
 
                abort();
 
639
                debug( LOG_ERROR, "couldn't add fall over anim");
653
640
                return;
654
641
        }
655
642
 
658
645
 
659
646
void droidBurn(DROID *psDroid)
660
647
{
661
 
        BOOL    bRet;
662
 
 
663
648
        CHECK_DROID(psDroid);
664
649
 
665
650
        if ( psDroid->droidType != DROID_PERSON )
666
651
        {
667
 
                ASSERT(LOG_ERROR, "unitBurn: can't burn anything except babarians currently!");
 
652
                ASSERT(LOG_ERROR, "can't burn anything except babarians currently!");
668
653
                return;
669
654
        }
670
655
 
678
663
                }
679
664
                else
680
665
                {
681
 
                        bRet = animObj_Remove( &psDroid->psCurAnim, psDroid->psCurAnim->psAnim->uwID );
682
 
                        ASSERT( bRet == true, "unitBurn: animObj_Remove failed" );
 
666
                        const bool bRet = animObj_Remove(psDroid->psCurAnim, psDroid->psCurAnim->psAnim->uwID);
 
667
                        ASSERT(bRet, "animObj_Remove failed");
683
668
                        psDroid->psCurAnim = NULL;
684
669
                }
685
670
        }
689
674
                                                                                        ID_ANIM_DROIDBURN, 0, 3 );
690
675
        if ( psDroid->psCurAnim == NULL )
691
676
        {
692
 
                debug( LOG_ERROR, "unitBurn: couldn't add burn anim\n" );
693
 
                abort();
 
677
                debug( LOG_ERROR, "couldn't add burn anim" );
694
678
                return;
695
679
        }
696
680
 
698
682
        animObj_SetDoneFunc( psDroid->psCurAnim, droidBurntCallback );
699
683
 
700
684
        /* add scream */
701
 
        debug( LOG_NEVER, "baba burn\n" );
 
685
        debug( LOG_NEVER, "baba burn" );
702
686
 
703
687
        audio_PlayObjDynamicTrack( psDroid, ID_SOUND_BARB_SCREAM+(rand()%3), NULL );
704
688
 
712
696
{
713
697
        UDWORD  pos;
714
698
 
715
 
        if (numNaybors >= MAX_NAYBORS)
716
 
        {
717
 
                return;
718
 
        }
719
 
        
720
 
        else if (numNaybors == 0)
 
699
        if (numNaybors == 0)
721
700
        {
722
701
                // No objects in the list
723
702
                asDroidNaybors[0].psObj = psObj;
744
723
                asDroidNaybors[pos].distSqr = distSqr;
745
724
                numNaybors++;
746
725
        }
747
 
 
748
 
        ASSERT( numNaybors <= MAX_NAYBORS,
749
 
                "addNaybor: numNaybors > MAX_NAYBORS" );
750
726
}
751
727
 
752
728
 
753
729
static DROID    *CurrentNaybors = NULL;
754
730
static UDWORD   nayborTime = 0;
755
731
 
756
 
// macro to see if an object is in NAYBOR_RANGE
757
 
// used by droidGetNayb
758
 
#define IN_NAYBOR_RANGE(psObj) \
759
 
        xdiff = dx - (SDWORD)psObj->pos.x; \
760
 
        if (xdiff < 0) \
761
 
        { \
762
 
                xdiff = -xdiff; \
763
 
        } \
764
 
        if (xdiff > NAYBOR_RANGE) \
765
 
        { \
766
 
                continue; \
767
 
        } \
768
 
\
769
 
        ydiff = dy - (SDWORD)psObj->pos.y; \
770
 
        if (ydiff < 0) \
771
 
        { \
772
 
                ydiff = -ydiff; \
773
 
        } \
774
 
        if (ydiff > NAYBOR_RANGE) \
775
 
        { \
776
 
                continue; \
777
 
        } \
778
 
\
779
 
        distSqr = xdiff*xdiff + ydiff*ydiff; \
780
 
        if (distSqr > NAYBOR_RANGE*NAYBOR_RANGE) \
781
 
        { \
782
 
                continue; \
783
 
        } \
784
 
 
785
 
 
786
732
/* Find all the objects close to the droid */
787
733
void droidGetNaybors(DROID *psDroid)
788
734
{
793
739
        CHECK_DROID(psDroid);
794
740
 
795
741
        // Ensure only called max of once per droid per game cycle.
796
 
        if(CurrentNaybors == psDroid && nayborTime == gameTime) {
 
742
        if (CurrentNaybors == psDroid && nayborTime == gameTime)
 
743
        {
797
744
                return;
798
745
        }
799
746
        CurrentNaybors = psDroid;
801
748
 
802
749
        // reset the naybor array
803
750
        numNaybors = 0;
804
 
#ifdef DEBUG
805
 
        memset(asDroidNaybors, 0xcd, sizeof(asDroidNaybors));
806
 
#endif
807
751
 
808
752
        // search for naybor objects
809
753
        dx = psDroid->pos.x;
814
758
        {
815
759
                if (psObj != (BASE_OBJECT *)psDroid && !psObj->died)
816
760
                {
817
 
                        IN_NAYBOR_RANGE(psObj);
 
761
                        xdiff = dx - (SDWORD)psObj->pos.x;
 
762
                        if (xdiff < 0)
 
763
                        {
 
764
                                xdiff = -xdiff;
 
765
                        }
 
766
                        if (xdiff > NAYBOR_RANGE)
 
767
                        {
 
768
                                continue;
 
769
                        }
 
770
 
 
771
                        ydiff = dy - (SDWORD)psObj->pos.y;
 
772
                        if (ydiff < 0)
 
773
                        {
 
774
                                ydiff = -ydiff;
 
775
                        }
 
776
                        if (ydiff > NAYBOR_RANGE)
 
777
                        {
 
778
                                continue;
 
779
                        }
 
780
 
 
781
                        distSqr = xdiff*xdiff + ydiff*ydiff;
 
782
                        if (distSqr > NAYBOR_RANGE*NAYBOR_RANGE)
 
783
                        {
 
784
                                continue;
 
785
                        }
818
786
 
819
787
                        addNaybor(psObj, distSqr);
 
788
                        if (numNaybors >= MAX_NAYBORS)
 
789
                        {
 
790
                                break;
 
791
                        }
 
792
 
820
793
                }
821
794
        }
822
795
}
840
813
        // May need power
841
814
        if (droidUsesPower(psDroid))
842
815
        {
843
 
                if (checkPower(psDroid->player, POWER_PER_CYCLE, false))
 
816
                if (checkPower(psDroid->player, POWER_PER_CYCLE))
844
817
                {
845
818
                        // Check if this droid is due some power
846
819
                        if (getLastPowered((BASE_OBJECT *)psDroid))
875
848
        {
876
849
                // need to clip this value to prevent overflow condition
877
850
                percentDamage = 100 - clip(PERCENT(psDroid->body, psDroid->originalBody), 0, 100);
878
 
                
 
851
 
879
852
                // Is there any damage?
880
853
                if(percentDamage>=25)
881
854
                {
883
856
                        {
884
857
                                percentDamage = 99;
885
858
                        }
886
 
                        
 
859
 
887
860
                        emissionInterval = CALC_DROID_SMOKE_INTERVAL(percentDamage);
888
 
                        
 
861
 
889
862
                        if(gameTime > (psDroid->lastEmission + emissionInterval))
890
863
                        {
891
864
                                dv.x = psDroid->pos.x + DROID_DAMAGE_SPREAD;
970
943
                                {
971
944
                                        psDroid->burnDamage += damageToDo;
972
945
 
973
 
                                        //Watermelon:just assume the burn damage is from FRONT
 
946
                                        //just assume the burn damage is from FRONT
974
947
                                        droidDamage(psDroid, damageToDo, WC_HEAT,WSC_FLAME, HIT_SIDE_FRONT);
975
948
                                }
976
949
                        }
1116
1089
                        intBuildFinished(psDroid);
1117
1090
                        return false;
1118
1091
                }
 
1092
                // Can't build on burning oil derricks.
 
1093
                if (psStructStat->type == REF_RESOURCE_EXTRACTOR && fireOnLocation(psDroid->orderX,psDroid->orderY))
 
1094
                {
 
1095
                        intBuildFinished(psDroid);
 
1096
                        return false;
 
1097
                }
1119
1098
                //ok to build
1120
1099
                psStruct = buildStructure(psStructStat, psDroid->orderX,psDroid->orderY, psDroid->player,false);
1121
1100
                if (!psStruct)
1140
1119
                if (!droidNextToStruct(psDroid, (BASE_OBJECT *)psStruct))
1141
1120
                {
1142
1121
                        /* Nope - stop building */
1143
 
                        debug( LOG_NEVER, "unitStartBuild: not next to structure\n" );
 
1122
                        debug( LOG_NEVER, "not next to structure" );
1144
1123
                }
1145
1124
        }
1146
1125
 
1204
1183
 
1205
1184
        CHECK_DROID(psDroid);
1206
1185
 
1207
 
        ASSERT( psDroid->action == DACTION_BUILD,
1208
 
                "unitUpdateBuild: unit is not building" );
 
1186
        ASSERT_OR_RETURN(false, psDroid->action == DACTION_BUILD, "unit is not building" );
 
1187
        ASSERT_OR_RETURN(false, psDroid->psTarget, "Trying to update a construction, but no target!");
 
1188
 
1209
1189
        psStruct = (STRUCTURE *)psDroid->psTarget;
1210
 
        ASSERT( psStruct->type == OBJ_STRUCTURE,
1211
 
                "unitUpdateBuild: target is not a structure" );
1212
 
        ASSERT( psDroid->asBits[COMP_CONSTRUCT].nStat < numConstructStats,
1213
 
                "unitUpdateBuild: Invalid construct pointer for unit" );
 
1190
        ASSERT_OR_RETURN(false, psStruct->type == OBJ_STRUCTURE, "target is not a structure" );
 
1191
        ASSERT_OR_RETURN(false, psDroid->asBits[COMP_CONSTRUCT].nStat < numConstructStats, "Invalid construct pointer for unit" );
1214
1192
 
1215
1193
        // First check the structure hasn't been completed by another droid
1216
1194
        if (psStruct->status == SS_BUILT)
1218
1196
                // Update the interface
1219
1197
                intBuildFinished(psDroid);
1220
1198
 
1221
 
                debug( LOG_NEVER, "DACTION_BUILD: done\n");
 
1199
                debug( LOG_NEVER, "DACTION_BUILD: done");
1222
1200
                psDroid->action = DACTION_NONE;
1223
1201
 
1224
1202
                return false;
1225
1203
        }
1226
1204
 
1227
 
    // For now wait until have enough power to build
1228
 
    if (psStruct->currentPowerAccrued < (SWORD) structPowerToBuild(psStruct))
1229
 
    {
1230
 
        psDroid->actionStarted = gameTime;
1231
 
        return true;
1232
 
    }
 
1205
        // For now wait until have enough power to build
 
1206
        if (psStruct->currentPowerAccrued < (SWORD) structPowerToBuild(psStruct))
 
1207
        {
 
1208
                psDroid->actionStarted = gameTime;
 
1209
                return true;
 
1210
        }
1233
1211
 
1234
1212
        constructPoints = constructorPoints(asConstructStats + psDroid->
1235
1213
                asBits[COMP_CONSTRUCT].nStat, psDroid->player);
1273
1251
 
1274
1252
                /* must reset here before the callback, droid must have DACTION_NONE
1275
1253
                     in order to be able to start a new built task, doubled in actionUpdateDroid() */
1276
 
                debug( LOG_NEVER, "DACTION_NONE: done\n");
 
1254
                debug( LOG_NEVER, "DACTION_NONE: done");
1277
1255
                psDroid->action = DACTION_NONE;
1278
1256
 
1279
1257
                /* Notify scripts we just finished building a structure, pass builder and what was built */
1289
1267
        {
1290
1268
                addConstructorEffect(psStruct);
1291
1269
        }
1292
 
        
 
1270
 
1293
1271
        return true;
1294
1272
}
1295
1273
 
1299
1277
 
1300
1278
        CHECK_DROID(psDroid);
1301
1279
 
1302
 
        ASSERT( psDroid->order == DORDER_DEMOLISH,
1303
 
                "unitStartDemolishing: unit is not demolishing" );
 
1280
        ASSERT_OR_RETURN(false, psDroid->order == DORDER_DEMOLISH, "unit is not demolishing");
1304
1281
        psStruct = (STRUCTURE *)psDroid->psTarget;
1305
 
        ASSERT( psStruct->type == OBJ_STRUCTURE,
1306
 
                "unitStartDemolishing: target is not a structure" );
 
1282
        ASSERT_OR_RETURN(false, psStruct->type == OBJ_STRUCTURE, "target is not a structure");
1307
1283
 
1308
1284
        psDroid->actionStarted = gameTime;
1309
1285
        psDroid->actionPoints  = 0;
1334
1310
 
1335
1311
        CHECK_DROID(psDroid);
1336
1312
 
1337
 
        ASSERT( psDroid->action == DACTION_DEMOLISH,
1338
 
                "unitUpdateDemolishing: unit is not demolishing" );
 
1313
        ASSERT_OR_RETURN(false, psDroid->action == DACTION_DEMOLISH, "unit is not demolishing");
1339
1314
        psStruct = (STRUCTURE *)psDroid->psTarget;
1340
 
        ASSERT( psStruct->type == OBJ_STRUCTURE,
1341
 
                "unitUpdateDemolishing: target is not a structure" );
 
1315
        ASSERT_OR_RETURN(false, psStruct->type == OBJ_STRUCTURE, "target is not a structure");
1342
1316
 
1343
1317
        //constructPoints = (asConstructStats + psDroid->asBits[COMP_CONSTRUCT].nStat)->
1344
1318
        //      constructPoints;
1367
1341
 
1368
1342
                if(psStruct->pStructureType->type == REF_POWER_GEN)
1369
1343
                {
1370
 
            //if had module attached - the base must have been completely built
1371
 
            if (psStruct->pFunctionality->powerGenerator.capacity)
1372
 
            {
1373
 
                //so add the power required to build the base struct
1374
 
                addPower(psStruct->player, psStruct->pStructureType->powerToBuild);
1375
 
            }
1376
 
            //add the currentAccruedPower since this may or may not be all required
1377
 
            addPower(psStruct->player, psStruct->currentPowerAccrued);
 
1344
                        //if had module attached - the base must have been completely built
 
1345
                        if (psStruct->pFunctionality->powerGenerator.capacity)
 
1346
                        {
 
1347
                                //so add the power required to build the base struct
 
1348
                                addPower(psStruct->player, psStruct->pStructureType->powerToBuild);
 
1349
                        }
 
1350
                        //add the currentAccruedPower since this may or may not be all required
 
1351
                        addPower(psStruct->player, psStruct->currentPowerAccrued);
1378
1352
                }
1379
1353
                else
1380
1354
                {
1381
 
            //if it had a module attached, need to add the power for the base struct as well
1382
 
            if (StructIsFactory(psStruct))
1383
 
            {
1384
 
                if (psStruct->pFunctionality->factory.capacity)
1385
 
                {
1386
 
                    //add half power for base struct
1387
 
                    addPower(psStruct->player, psStruct->pStructureType->
1388
 
                        powerToBuild / 2);
1389
 
                    //if large factory - add half power for one upgrade
1390
 
                    if (psStruct->pFunctionality->factory.capacity > SIZE_MEDIUM)
1391
 
                    {
1392
 
                        addPower(psStruct->player, structPowerToBuild(psStruct) / 2);
1393
 
                    }
1394
 
                }
1395
 
            }
1396
 
            else if (psStruct->pStructureType->type == REF_RESEARCH)
1397
 
            {
1398
 
                if (psStruct->pFunctionality->researchFacility.capacity)
1399
 
                {
1400
 
                    //add half power for base struct
1401
 
                    addPower(psStruct->player, psStruct->pStructureType->powerToBuild / 2);
1402
 
                }
1403
 
            }
1404
 
            //add currentAccrued for the current layer of the structure
1405
 
            addPower(psStruct->player, psStruct->currentPowerAccrued / 2);
1406
 
        }
 
1355
                        //if it had a module attached, need to add the power for the base struct as well
 
1356
                        if (StructIsFactory(psStruct))
 
1357
                        {
 
1358
                                if (psStruct->pFunctionality->factory.capacity)
 
1359
                                {
 
1360
                                        //add half power for base struct
 
1361
                                        addPower(psStruct->player, psStruct->pStructureType->
 
1362
                                                powerToBuild / 2);
 
1363
                                        //if large factory - add half power for one upgrade
 
1364
                                        if (psStruct->pFunctionality->factory.capacity > SIZE_MEDIUM)
 
1365
                                        {
 
1366
                                                addPower(psStruct->player, structPowerToBuild(psStruct) / 2);
 
1367
                                        }
 
1368
                                }
 
1369
                        }
 
1370
                        else if (psStruct->pStructureType->type == REF_RESEARCH)
 
1371
                        {
 
1372
                                if (psStruct->pFunctionality->researchFacility.capacity)
 
1373
                                {
 
1374
                                        //add half power for base struct
 
1375
                                        addPower(psStruct->player, psStruct->pStructureType->powerToBuild / 2);
 
1376
                                }
 
1377
                        }
 
1378
                        //add currentAccrued for the current layer of the structure
 
1379
                        addPower(psStruct->player, psStruct->currentPowerAccrued / 2);
 
1380
                }
1407
1381
                /* remove structure and foundation */
1408
1382
                removeStruct( psStruct, true );
1409
1383
 
1410
1384
                /* reset target stats*/
1411
 
            psDroid->psTarStats = NULL;
 
1385
                psDroid->psTarStats = NULL;
1412
1386
 
1413
1387
                return false;
1414
1388
        }
1415
 
    else
1416
 
    {
 
1389
        else
 
1390
        {
1417
1391
                addConstructorEffect(psStruct);
1418
1392
        }
1419
1393
 
1429
1403
 
1430
1404
        CHECK_DROID(psDroid);
1431
1405
 
1432
 
        ASSERT( psDroid->order == DORDER_CLEARWRECK,
1433
 
                "unitStartClearing: unit is not clearing wreckage" );
 
1406
        ASSERT_OR_RETURN(false, psDroid->order == DORDER_CLEARWRECK, "unit is not clearing wreckage");
1434
1407
        psFeature = (FEATURE *)psDroid->psTarget;
1435
 
        ASSERT( psFeature->type == OBJ_FEATURE,
1436
 
                "unitStartClearing: target is not a feature" );
1437
 
        ASSERT( psFeature->psStats->subType == FEAT_BUILD_WRECK,
1438
 
                "unitStartClearing: feature is not a wrecked building" );
 
1408
        ASSERT_OR_RETURN(false, psFeature->type == OBJ_FEATURE, "target is not a feature");
 
1409
        ASSERT_OR_RETURN(false, psFeature->psStats->subType == FEAT_BUILD_WRECK, "feature is not a wrecked building");
1439
1410
 
1440
1411
        psDroid->actionStarted = gameTime;
1441
1412
        psDroid->actionPoints  = 0;
1454
1425
 
1455
1426
        CHECK_DROID(psDroid);
1456
1427
 
1457
 
        ASSERT( psDroid->action == DACTION_CLEARWRECK,
1458
 
                "unitUpdateClearing: unit is not clearing wreckage" );
 
1428
        ASSERT_OR_RETURN(false, psDroid->action == DACTION_CLEARWRECK, "unit is not clearing wreckage");
1459
1429
        psFeature = (FEATURE *)psDroid->psTarget;
1460
 
        ASSERT( psFeature->type == OBJ_FEATURE,
1461
 
                "unitStartClearing: target is not a feature" );
1462
 
        ASSERT( psFeature->psStats->subType == FEAT_BUILD_WRECK,
1463
 
                "unitStartClearing: feature is not a wrecked building" );
 
1430
        ASSERT_OR_RETURN(false, psFeature->type == OBJ_FEATURE, "target is not a feature");
 
1431
        ASSERT_OR_RETURN(false, psFeature->psStats->subType == FEAT_BUILD_WRECK, "feature is not a wrecked building");
1464
1432
 
1465
1433
        if (psFeature->body > 0)
1466
1434
        {
1502
1470
        CHECK_DROID(psDroid);
1503
1471
 
1504
1472
        psStruct = (STRUCTURE *)psDroid->psActionTarget[0];
1505
 
        ASSERT( psStruct->type == OBJ_STRUCTURE,
1506
 
                "unitStartRepair: target is not a structure" );
 
1473
        ASSERT_OR_RETURN(false, psStruct->type == OBJ_STRUCTURE, "target is not a structure");
1507
1474
 
1508
1475
        psDroid->actionStarted = gameTime;
1509
1476
        psDroid->actionPoints  = 0;
1522
1489
        CHECK_DROID(psDroid);
1523
1490
 
1524
1491
        psDroidToRepair = (DROID *)psDroid->psActionTarget[0];
1525
 
        ASSERT( psDroidToRepair->type == OBJ_DROID,
1526
 
                "unitStartUnitRepair: target is not a unit" );
 
1492
        ASSERT_OR_RETURN(false, psDroidToRepair->type == OBJ_DROID, "target is not a unit");
1527
1493
 
1528
1494
        psDroid->actionStarted = gameTime;
1529
1495
        psDroid->actionPoints  = 0;
1563
1529
 
1564
1530
        CHECK_DROID(psDroid);
1565
1531
 
1566
 
        ASSERT( psDroid->order == DORDER_RESTORE,
1567
 
                "unitStartRestore: unit is not restoring" );
 
1532
        ASSERT_OR_RETURN(false, psDroid->order == DORDER_RESTORE, "unit is not restoring");
1568
1533
        psStruct = (STRUCTURE *)psDroid->psTarget;
1569
 
        ASSERT( psStruct->type == OBJ_STRUCTURE,
1570
 
                "unitStartRestore: target is not a structure" );
 
1534
        ASSERT_OR_RETURN(false, psStruct->type == OBJ_STRUCTURE, "target is not a structure");
1571
1535
 
1572
1536
        psDroid->actionStarted = gameTime;
1573
1537
        psDroid->actionPoints  = 0;
1586
1550
 
1587
1551
        CHECK_DROID(psDroid);
1588
1552
 
1589
 
        ASSERT( psDroid->action == DACTION_RESTORE,
1590
 
                "unitUpdateRestore: unit is not restoring" );
 
1553
        ASSERT_OR_RETURN(false, psDroid->action == DACTION_RESTORE, "unit is not restoring");
1591
1554
        psStruct = (STRUCTURE *)psDroid->psTarget;
1592
 
        ASSERT( psStruct->type == OBJ_STRUCTURE,
1593
 
                "unitUpdateRestore: target is not a structure" );
1594
 
        ASSERT( psStruct->pStructureType->resistance != 0,
1595
 
                "unitUpdateRestore: invalid structure for EW" );
 
1555
        ASSERT_OR_RETURN(false, psStruct->type == OBJ_STRUCTURE, "target is not a structure");
 
1556
        ASSERT_OR_RETURN(false, psStruct->pStructureType->resistance != 0, "invalid structure for EW");
1596
1557
 
1597
 
        ASSERT( psDroid->asWeaps[0].nStat > 0,
1598
 
                "unitUpdateRestore: droid doean't have any weapons" );
 
1558
        ASSERT_OR_RETURN(false, psDroid->asWeaps[0].nStat > 0, "droid doesn't have any weapons");
1599
1559
 
1600
1560
        psStats = asWeaponStats + psDroid->asWeaps[0].nStat;
1601
1561
 
1602
 
        ASSERT( psStats->weaponSubClass == WSC_ELECTRONIC,
1603
 
                "unitUpdateRestore: unit's weapon is not EW" );
 
1562
        ASSERT_OR_RETURN(false, psStats->weaponSubClass == WSC_ELECTRONIC, "unit's weapon is not EW");
1604
1563
 
1605
1564
        restorePoints = calcDamage(weaponDamage(psStats, psDroid->player),
1606
1565
                psStats->weaponEffect,(BASE_OBJECT *)psStruct);
1636
1595
        UDWORD  percent;
1637
1596
        UDWORD  recoil;
1638
1597
        float   fraction;
1639
 
        //Watermelon:added multiple weapon update
 
1598
        //added multiple weapon update
1640
1599
        UBYTE   i = 0;
1641
1600
        UBYTE   num_weapons = 0;
1642
1601
 
1713
1672
 
1714
1673
        CHECK_DROID(psDroid);
1715
1674
 
1716
 
        ASSERT( psDroid->action == DACTION_REPAIR,
1717
 
                "unitUpdateRepair: unit does not have repair order" );
 
1675
        ASSERT_OR_RETURN(false, psDroid->action == DACTION_REPAIR, "unit does not have repair order");
1718
1676
        psStruct = (STRUCTURE *)psDroid->psActionTarget[0];
1719
 
        ASSERT( psStruct->type == OBJ_STRUCTURE,
1720
 
                "unitUpdateRepair: target is not a structure" );
 
1677
 
 
1678
        ASSERT_OR_RETURN(false, psStruct->type == OBJ_STRUCTURE, "target is not a structure");
1721
1679
 
1722
1680
        iRepairPoints = constructorPoints(asConstructStats + psDroid->
1723
1681
                asBits[COMP_CONSTRUCT].nStat, psDroid->player);
1752
1710
 
1753
1711
        CHECK_DROID(psRepairDroid);
1754
1712
 
1755
 
        ASSERT( psRepairDroid->action == DACTION_DROIDREPAIR,
1756
 
                "unitUpdateUnitRepair: unit does not have unit repair order" );
1757
 
        ASSERT( psRepairDroid->asBits[COMP_REPAIRUNIT].nStat != 0,
1758
 
                "unitUpdateUnitRepair: unit does not have a repair turret" );
 
1713
        ASSERT_OR_RETURN(false, psRepairDroid->action == DACTION_DROIDREPAIR, "unit does not have unit repair order");
 
1714
        ASSERT_OR_RETURN(false, psRepairDroid->asBits[COMP_REPAIRUNIT].nStat != 0, "unit does not have a repair turret");
1759
1715
 
1760
1716
        psDroidToRepair = (DROID *)psRepairDroid->psActionTarget[0];
1761
 
        ASSERT( psDroidToRepair->type == OBJ_DROID,
1762
 
                "unitUpdateUnitRepair: target is not a unit" );
1763
 
 
1764
 
    //the amount of power accrued limits how much of the work can be done
1765
 
    //self-repair doesn't cost power
1766
 
    if (psRepairDroid == psDroidToRepair)
1767
 
    {
1768
 
        powerCost = 0;
1769
 
    }
1770
 
    else
1771
 
    {
1772
 
        //check if enough power to do any
1773
 
        powerCost = powerReqForDroidRepair(psDroidToRepair);
1774
 
        if (powerCost > psDroidToRepair->powerAccrued)
1775
 
        {
1776
 
            powerCost = (powerCost - psDroidToRepair->powerAccrued) / POWER_FACTOR;
1777
 
        }
1778
 
        else
1779
 
        {
1780
 
            powerCost = 0;
1781
 
        }
1782
 
 
1783
 
    }
1784
 
 
1785
 
    //if the power cost is 0 (due to rounding) then do for free!
1786
 
    if (powerCost)
1787
 
    {
1788
 
        if (!psDroidToRepair->powerAccrued)
1789
 
        {
1790
 
            //reset the actionStarted time and actionPoints added so the correct
1791
 
            //amount of points are added when there is more power
 
1717
        ASSERT_OR_RETURN(false, psDroidToRepair->type == OBJ_DROID, "unitUpdateUnitRepair: target is not a unit");
 
1718
 
 
1719
        //the amount of power accrued limits how much of the work can be done
 
1720
        //self-repair doesn't cost power
 
1721
        if (psRepairDroid == psDroidToRepair)
 
1722
        {
 
1723
                powerCost = 0;
 
1724
        }
 
1725
        else
 
1726
        {
 
1727
                //check if enough power to do any
 
1728
                powerCost = powerReqForDroidRepair(psDroidToRepair);
 
1729
                if (powerCost > psDroidToRepair->powerAccrued)
 
1730
                {
 
1731
                        powerCost = (powerCost - psDroidToRepair->powerAccrued) / POWER_FACTOR;
 
1732
                }
 
1733
                else
 
1734
                {
 
1735
                        powerCost = 0;
 
1736
                }
 
1737
 
 
1738
        }
 
1739
 
 
1740
        //if the power cost is 0 (due to rounding) then do for free!
 
1741
        if (powerCost)
 
1742
        {
 
1743
                if (!psDroidToRepair->powerAccrued)
 
1744
                {
 
1745
                        //reset the actionStarted time and actionPoints added so the correct
 
1746
                        //amount of points are added when there is more power
1792
1747
                        psRepairDroid->actionStarted = gameTime;
1793
 
            //init so repair points to add won't be huge when start up again
1794
 
            psRepairDroid->actionPoints = 0;
1795
 
            return true;
1796
 
        }
1797
 
    }
 
1748
                        //init so repair points to add won't be huge when start up again
 
1749
                        psRepairDroid->actionPoints = 0;
 
1750
                        return true;
 
1751
                }
 
1752
        }
1798
1753
 
1799
1754
        iRepairPoints = repairPoints(asRepairStats + psRepairDroid->
1800
1755
                asBits[COMP_REPAIRUNIT].nStat, psRepairDroid->player);
1811
1766
                        GAME_TICKS_PER_SEC;
1812
1767
        }
1813
1768
 
1814
 
    iPointsToAdd -= psRepairDroid->actionPoints;
 
1769
        iPointsToAdd -= psRepairDroid->actionPoints;
1815
1770
 
1816
 
    if (iPointsToAdd)
1817
 
    {
1818
 
        //just add the points if the power cost is negligable
1819
 
        //if these points would make the droid healthy again then just add
1820
 
        if (!powerCost || (psDroidToRepair->body + iPointsToAdd >= psDroidToRepair->originalBody))
1821
 
        {
1822
 
            //anothe HACK but sorts out all the rounding errors when values get small
1823
 
            psDroidToRepair->body += iPointsToAdd;
1824
 
            psRepairDroid->actionPoints += iPointsToAdd;
1825
 
        }
1826
 
        else
1827
 
        {
1828
 
            //see if we have enough power to do this amount of repair
1829
 
            powerCost = iPointsToAdd * repairPowerPoint(psDroidToRepair);
1830
 
            if (powerCost <= psDroidToRepair->powerAccrued)
1831
 
            {
1832
 
                psDroidToRepair->body += iPointsToAdd;
1833
 
                psRepairDroid->actionPoints += iPointsToAdd;
1834
 
                //subtract the power cost for these points
1835
 
                psDroidToRepair->powerAccrued -= powerCost;
1836
 
            }
1837
 
            else
1838
 
            {
1839
 
                /*reset the actionStarted time and actionPoints added so the correct
1840
 
                amount of points are added when there is more power*/
1841
 
                psRepairDroid->actionStarted = gameTime;
1842
 
                psRepairDroid->actionPoints = 0;
1843
 
            }
1844
 
        }
1845
 
    }
 
1771
        if (iPointsToAdd)
 
1772
        {
 
1773
                //just add the points if the power cost is negligable
 
1774
                //if these points would make the droid healthy again then just add
 
1775
                if (!powerCost || (psDroidToRepair->body + iPointsToAdd >= psDroidToRepair->originalBody))
 
1776
                {
 
1777
                        //anothe HACK but sorts out all the rounding errors when values get small
 
1778
                        psDroidToRepair->body += iPointsToAdd;
 
1779
                        psRepairDroid->actionPoints += iPointsToAdd;
 
1780
                }
 
1781
                else
 
1782
                {
 
1783
                        //see if we have enough power to do this amount of repair
 
1784
                        powerCost = iPointsToAdd * repairPowerPoint(psDroidToRepair);
 
1785
                        if (powerCost <= psDroidToRepair->powerAccrued)
 
1786
                        {
 
1787
                                psDroidToRepair->body += iPointsToAdd;
 
1788
                                psRepairDroid->actionPoints += iPointsToAdd;
 
1789
                                //subtract the power cost for these points
 
1790
                                psDroidToRepair->powerAccrued -= powerCost;
 
1791
                        }
 
1792
                        else
 
1793
                        {
 
1794
                                /*reset the actionStarted time and actionPoints added so the correct
 
1795
                                amount of points are added when there is more power*/
 
1796
                                psRepairDroid->actionStarted = gameTime;
 
1797
                                psRepairDroid->actionPoints = 0;
 
1798
                        }
 
1799
                }
 
1800
        }
1846
1801
 
1847
1802
        /* add plasma repair effect whilst being repaired */
1848
1803
        if ((ONEINFIVE) && (psDroidToRepair->visible[selectedPlayer]))
1864
1819
        }
1865
1820
        else
1866
1821
        {
1867
 
        //reset the power accrued
1868
 
        psDroidToRepair->powerAccrued = 0;
 
1822
                //reset the power accrued
 
1823
                psDroidToRepair->powerAccrued = 0;
1869
1824
                psDroidToRepair->body = psDroidToRepair->originalBody;
1870
1825
                return false;
1871
1826
        }
1888
1843
                DROID_TEMPLATE *pDroidDesign = malloc(sizeof(DROID_TEMPLATE));
1889
1844
                if (pDroidDesign == NULL)
1890
1845
                {
1891
 
                        debug(LOG_ERROR, "loadDroidTemplates: pDroidDesign: Out of memory");
 
1846
                        debug(LOG_ERROR, "pDroidDesign: Out of memory");
1892
1847
                        return false;
1893
1848
                }
1894
1849
                memset(pDroidDesign, 0, sizeof(DROID_TEMPLATE));
1920
1875
                }
1921
1876
                else
1922
1877
                {
1923
 
                        COMP_BASE_STATS *pStats = (COMP_BASE_STATS *)asBodyStats;
 
1878
                        COMPONENT_STATS *pStats = (COMPONENT_STATS *)asBodyStats;
1924
1879
                        const size_t size = sizeof(BODY_STATS);
1925
1880
                        unsigned int inc = 0;
1926
1881
                        BOOL found = false;
1941
1896
                                        found = true;
1942
1897
                                        break;
1943
1898
                                }
1944
 
                                pStats = ((COMP_BASE_STATS*)((UBYTE*)pStats + size));
 
1899
                                pStats = ((COMPONENT_STATS*)((UBYTE*)pStats + size));
1945
1900
                        }
1946
1901
                        if (!found)
1947
1902
                        {
1948
1903
                                debug( LOG_ERROR, "Body component not found for droid %s", getTemplateName(pDroidDesign) );
1949
 
                                abort();
1950
1904
                                free(pDroidDesign->pName);
1951
1905
                                free(pDroidDesign);
1952
1906
                                return false;
1964
1918
                }
1965
1919
                else
1966
1920
                {
1967
 
                        COMP_BASE_STATS *pStats = (COMP_BASE_STATS *)asBrainStats;
 
1921
                        COMPONENT_STATS *pStats = (COMPONENT_STATS *)asBrainStats;
1968
1922
                        const size_t size = sizeof(BRAIN_STATS);
1969
1923
                        unsigned int inc = 0;
1970
1924
                        BOOL found = false;
1985
1939
                                        found = true;
1986
1940
                                        break;
1987
1941
                                }
1988
 
                                pStats = ((COMP_BASE_STATS*)((UBYTE*)pStats + size));
 
1942
                                pStats = ((COMPONENT_STATS*)((UBYTE*)pStats + size));
1989
1943
                        }
1990
1944
                        if (!found)
1991
1945
                        {
1992
1946
                                debug( LOG_ERROR, "Brain component not found for droid %s", getTemplateName(pDroidDesign) );
1993
 
                                abort();
1994
1947
//                              DBERROR(("Brain component not found for droid %s", pDroidDesign->pName));
1995
1948
                                free(pDroidDesign->pName);
1996
1949
                                free(pDroidDesign);
2009
1962
                }
2010
1963
                else
2011
1964
                {
2012
 
                        COMP_BASE_STATS *pStats = (COMP_BASE_STATS *)asConstructStats;
 
1965
                        COMPONENT_STATS *pStats = (COMPONENT_STATS *)asConstructStats;
2013
1966
                        const size_t size = sizeof(CONSTRUCT_STATS);
2014
1967
                        unsigned int inc = 0;
2015
1968
                        BOOL found = false;
2029
1982
                                        found = true;
2030
1983
                                        break;
2031
1984
                                }
2032
 
                                pStats = ((COMP_BASE_STATS*)((UBYTE*)pStats + size));
 
1985
                                pStats = ((COMPONENT_STATS*)((UBYTE*)pStats + size));
2033
1986
                        }
2034
1987
                        if (!found)
2035
1988
                        {
2036
1989
                                debug( LOG_ERROR, "Construct component not found for droid %s", getTemplateName(pDroidDesign) );
2037
 
                                abort();
2038
1990
                                free(pDroidDesign->pName);
2039
1991
                                free(pDroidDesign);
2040
1992
                                return false;
2052
2004
                }
2053
2005
                else
2054
2006
                {
2055
 
                        COMP_BASE_STATS *pStats = (COMP_BASE_STATS *)asECMStats;
 
2007
                        COMPONENT_STATS *pStats = (COMPONENT_STATS *)asECMStats;
2056
2008
                        const size_t size = sizeof(ECM_STATS);
2057
2009
                        unsigned int inc = 0;
2058
2010
                        BOOL found = false;
2072
2024
                                        found = true;
2073
2025
                                        break;
2074
2026
                                }
2075
 
                                pStats = ((COMP_BASE_STATS*)((UBYTE*)pStats + size));
 
2027
                                pStats = ((COMPONENT_STATS*)((UBYTE*)pStats + size));
2076
2028
                        }
2077
2029
                        if (!found)
2078
2030
                        {
2079
2031
                                debug( LOG_ERROR, "ECM component not found for droid %s", getTemplateName(pDroidDesign) );
2080
 
                                abort();
2081
2032
                                free(pDroidDesign->pName);
2082
2033
                                free(pDroidDesign);
2083
2034
                                return false;
2091
2042
                if (getTemplateFromUniqueName(pDroidDesign->pName, player))
2092
2043
                {
2093
2044
                        debug( LOG_ERROR, "Duplicate template %s", pDroidDesign->pName );
2094
 
                        abort();
2095
2045
                        free(pDroidDesign->pName);
2096
2046
                        free(pDroidDesign);
2097
2047
                        return false;
2108
2058
                }
2109
2059
                else
2110
2060
                {
2111
 
                        COMP_BASE_STATS *pStats = (COMP_BASE_STATS *)asPropulsionStats;
 
2061
                        COMPONENT_STATS *pStats = (COMPONENT_STATS *)asPropulsionStats;
2112
2062
                        const size_t size = sizeof(PROPULSION_STATS);
2113
2063
                        unsigned int inc = 0;
2114
2064
                        BOOL found = false;
2128
2078
                                        found = true;
2129
2079
                                        break;
2130
2080
                                }
2131
 
                                pStats = ((COMP_BASE_STATS*)((UBYTE*)pStats + size));
 
2081
                                pStats = ((COMPONENT_STATS*)((UBYTE*)pStats + size));
2132
2082
                        }
2133
2083
                        if (!found)
2134
2084
                        {
2135
2085
                                debug( LOG_ERROR, "Propulsion component not found for droid %s", getTemplateName(pDroidDesign) );
2136
 
                                abort();
2137
2086
                                free(pDroidDesign->pName);
2138
2087
                                free(pDroidDesign);
2139
2088
                                return false;
2151
2100
                }
2152
2101
                else
2153
2102
                {
2154
 
                        COMP_BASE_STATS *pStats = (COMP_BASE_STATS *)asRepairStats;
 
2103
                        COMPONENT_STATS *pStats = (COMPONENT_STATS *)asRepairStats;
2155
2104
                        const size_t size = sizeof(REPAIR_STATS);
2156
2105
                        unsigned int inc = 0;
2157
2106
                        BOOL found = false;
2171
2120
                                        found = true;
2172
2121
                                        break;
2173
2122
                                }
2174
 
                                pStats = ((COMP_BASE_STATS*)((UBYTE*)pStats + size));
 
2123
                                pStats = ((COMPONENT_STATS*)((UBYTE*)pStats + size));
2175
2124
                        }
2176
2125
                        if (!found)
2177
2126
                        {
2178
2127
                                debug( LOG_ERROR, "Repair component not found for droid %s", getTemplateName(pDroidDesign) );
2179
 
                                abort();
2180
2128
                                free(pDroidDesign->pName);
2181
2129
                                free(pDroidDesign);
2182
2130
                                return false;
2228
2176
                }
2229
2177
                else
2230
2178
                {
2231
 
                        COMP_BASE_STATS *pStats = (COMP_BASE_STATS *)asSensorStats;
 
2179
                        COMPONENT_STATS *pStats = (COMPONENT_STATS *)asSensorStats;
2232
2180
                        const size_t size = sizeof(SENSOR_STATS);
2233
2181
                        unsigned int inc = 0;
2234
2182
                        BOOL found = false;
2249
2197
                                        found = true;
2250
2198
                                        break;
2251
2199
                                }
2252
 
                                pStats = ((COMP_BASE_STATS*)((UBYTE*)pStats + size));
 
2200
                                pStats = ((COMPONENT_STATS*)((UBYTE*)pStats + size));
2253
2201
                        }
2254
2202
                        if (!found)
2255
2203
                        {
2256
2204
                                debug( LOG_ERROR, "Sensor not found for droid Template: %s", pDroidDesign->aName );
2257
 
                                abort();
2258
2205
                                free(pDroidDesign->pName);
2259
2206
                                free(pDroidDesign);
2260
2207
                                return false;
2267
2214
                if (pDroidDesign->numWeaps > DROID_MAXWEAPS)
2268
2215
                {
2269
2216
                        debug( LOG_ERROR, "Too many weapons have been allocated for droid Template: %s", pDroidDesign->aName );
2270
 
                        abort();
2271
2217
                        free(pDroidDesign->pName);
2272
2218
                        free(pDroidDesign);
2273
2219
                        return false;
2302
2248
 
2303
2249
        if ( bDefaultTemplateFound == false )
2304
2250
        {
2305
 
                debug( LOG_ERROR, "loadUnitTemplates: default template not found\n" );
2306
 
                abort();
 
2251
                debug( LOG_ERROR, "default template not found" );
2307
2252
                return false;
2308
2253
        }
2309
2254
 
2333
2278
// return whether a droid is IDF
2334
2279
BOOL idfDroid(DROID *psDroid)
2335
2280
{
2336
 
    //add Cyborgs
 
2281
        //add Cyborgs
2337
2282
        //if (psDroid->droidType != DROID_WEAPON)
2338
 
    if (!(psDroid->droidType == DROID_WEAPON || psDroid->droidType == DROID_CYBORG ||
2339
 
        psDroid->droidType == DROID_CYBORG_SUPER))
 
2283
        if (!(psDroid->droidType == DROID_WEAPON || psDroid->droidType == DROID_CYBORG ||
 
2284
                psDroid->droidType == DROID_CYBORG_SUPER))
2340
2285
        {
2341
2286
                return false;
2342
2287
        }
2352
2297
// return whether a template is for an IDF droid
2353
2298
BOOL templateIsIDF(DROID_TEMPLATE *psTemplate)
2354
2299
{
2355
 
    //add Cyborgs
2356
 
    if (!(psTemplate->droidType == DROID_WEAPON || psTemplate->droidType == DROID_CYBORG ||
2357
 
        psTemplate->droidType == DROID_CYBORG_SUPER))
 
2300
        //add Cyborgs
 
2301
        if (!(psTemplate->droidType == DROID_WEAPON || psTemplate->droidType == DROID_CYBORG ||
 
2302
                psTemplate->droidType == DROID_CYBORG_SUPER))
2358
2303
        {
2359
2304
                return false;
2360
2305
        }
2427
2372
        {
2428
2373
                type = DROID_WEAPON;
2429
2374
        }
2430
 
        /* Watermelon:with more than weapon is still a DROID_WEAPON */
 
2375
        /* with more than weapon is still a DROID_WEAPON */
2431
2376
        else if ( psTemplate->numWeaps > 1)
2432
2377
        {
2433
2378
                type = DROID_WEAPON;
2447
2392
        unsigned int SkippedWeaponCount = 0;
2448
2393
        unsigned int line = 0;
2449
2394
 
2450
 
        ASSERT(NumWeapons > 0, "template without weapons");
 
2395
        ASSERT_OR_RETURN(false, NumWeapons > 0, "template without weapons");
2451
2396
 
2452
2397
        for (line = 0; line < NumWeapons; line++)
2453
2398
        {
2475
2420
                                else
2476
2421
                                {
2477
2422
                                        debug( LOG_ERROR, "Unable to find Template - %s", TemplateName );
2478
 
                                        abort();
2479
2423
                                        return false;
2480
2424
                                }
2481
2425
                        }
2482
2426
 
2483
 
                        ASSERT(pTemplate->numWeaps <= DROID_MAXWEAPS, "stack corruption unavoidable");
 
2427
                        ASSERT_OR_RETURN(false, pTemplate->numWeaps <= DROID_MAXWEAPS, "stack corruption unavoidable");
2484
2428
 
2485
2429
                        for (j = 0; j < pTemplate->numWeaps; j++)
2486
2430
                        {
2496
2440
                                //if weapon not found - error
2497
2441
                                if (incWpn == -1)
2498
2442
                                {
2499
 
                                        debug( LOG_ERROR, "Unable to find Weapon %s for template %s",
2500
 
                                                WeaponName[j], TemplateName );
2501
 
                                        abort();
 
2443
                                        debug( LOG_ERROR, "Unable to find Weapon %s for template %s", WeaponName[j], TemplateName );
2502
2444
                                        return false;
2503
2445
                                }
2504
2446
                                else
2510
2452
                                        if (pTemplate->storeCount > pTemplate->numWeaps)
2511
2453
                                        {
2512
2454
                                                debug( LOG_ERROR, "Trying to allocate more weapons than allowed for Template - %s", TemplateName );
2513
 
                                                abort();
2514
2455
                                                return false;
2515
2456
                                        }
2516
2457
                                        //check valid weapon/propulsion
2517
2458
                                        if (!checkValidWeaponForProp(pTemplate))
2518
2459
                                        {
2519
2460
                                                debug( LOG_ERROR, "Weapon is invalid for air propulsion for template %s", pTemplate->aName );
2520
 
                                                abort();
2521
 
 
2522
2461
                                                return false;
2523
2462
                                        }
2524
2463
 
2538
2477
        if (SkippedWeaponCount > 0)
2539
2478
        {
2540
2479
                debug( LOG_ERROR, "Illegal player number in %d droid weapons", SkippedWeaponCount );
2541
 
                abort();
 
2480
                return false;
2542
2481
        }
2543
2482
 
2544
2483
        return true;
2556
2495
                for (pTemplate = apsDroidTemplates[player]; pTemplate != NULL; pTemplate = pNext)
2557
2496
                {
2558
2497
                        pNext = pTemplate->psNext;
 
2498
                        //FIX ME:
2559
2499
                        ASSERT(sDefaultDesignTemplate.pName != pTemplate->pName, "We'll soon be getting a double free()!!!");
2560
2500
                        if (pTemplate->pName != sDefaultDesignTemplate.pName)
2561
2501
                        {
2635
2575
/* Calculate the base body points of a droid without upgrades*/
2636
2576
UDWORD calcDroidBaseBody(DROID *psDroid)
2637
2577
{
2638
 
        //Watermelon:re-enabled i;
 
2578
        //re-enabled i;
2639
2579
        UDWORD      body, i;
2640
2580
 
2641
2581
        if (psDroid == NULL)
2673
2613
UDWORD calcDroidBaseSpeed(DROID_TEMPLATE *psTemplate, UDWORD weight, UBYTE player)
2674
2614
{
2675
2615
        UDWORD  speed;
2676
 
        //Watermelon:engine output bonus? 150%
 
2616
        //engine output bonus? 150%
2677
2617
        float eoBonus = 1.5f;
2678
2618
 
2679
2619
        if (psTemplate->droidType == DROID_CYBORG ||
2680
2620
                psTemplate->droidType == DROID_CYBORG_SUPER ||
2681
 
        psTemplate->droidType == DROID_CYBORG_CONSTRUCT ||
2682
 
        psTemplate->droidType == DROID_CYBORG_REPAIR)
 
2621
                psTemplate->droidType == DROID_CYBORG_CONSTRUCT ||
 
2622
                psTemplate->droidType == DROID_CYBORG_REPAIR)
2683
2623
        {
2684
2624
                speed = (asPropulsionTypes[(asPropulsionStats + psTemplate->
2685
 
            asParts[COMP_PROPULSION])->propulsionType].powerRatioMult *
2686
 
            bodyPower(asBodyStats + psTemplate->asParts[COMP_BODY],
2687
 
            player, CYBORG_BODY_UPGRADE)) / weight;
 
2625
                        asParts[COMP_PROPULSION])->propulsionType].powerRatioMult *
 
2626
                        bodyPower(asBodyStats + psTemplate->asParts[COMP_BODY],
 
2627
                        player, CYBORG_BODY_UPGRADE)) / weight;
2688
2628
        }
2689
2629
        else
2690
2630
        {
2691
2631
                speed = (asPropulsionTypes[(asPropulsionStats + psTemplate->
2692
 
            asParts[COMP_PROPULSION])->propulsionType].powerRatioMult *
2693
 
            bodyPower(asBodyStats + psTemplate->asParts[COMP_BODY],
 
2632
                        asParts[COMP_PROPULSION])->propulsionType].powerRatioMult *
 
2633
                        bodyPower(asBodyStats + psTemplate->asParts[COMP_BODY],
2694
2634
                        player, DROID_BODY_UPGRADE)) / weight;
2695
2635
        }
2696
2636
 
2697
2637
        // reduce the speed of medium/heavy VTOLs
2698
 
        if (asPropulsionStats[psTemplate->asParts[COMP_PROPULSION]].propulsionType == LIFT)
 
2638
        if (asPropulsionStats[psTemplate->asParts[COMP_PROPULSION]].propulsionType == PROPULSION_TYPE_LIFT)
2699
2639
        {
2700
2640
                if ((asBodyStats + psTemplate->asParts[COMP_BODY])->size == SIZE_HEAVY)
2701
2641
                {
2723
2663
        PROPULSION_STATS        *propulsion = asPropulsionStats + propIndex;
2724
2664
        UDWORD                          speed;
2725
2665
 
2726
 
    speed  = baseSpeed;
2727
 
    // Factor in terrain
2728
 
    speed *= getSpeedFactor(terrainType, propulsion->propulsionType);
2729
 
    speed /= 100; 
2730
 
    // Factor in experience
2731
 
    speed *= (100 + EXP_SPEED_BONUS * level);
2732
 
    speed /= 100;
 
2666
        speed  = baseSpeed;
 
2667
        // Factor in terrain
 
2668
        speed *= getSpeedFactor(terrainType, propulsion->propulsionType);
 
2669
        speed /= 100;
2733
2670
 
2734
2671
        // Need to ensure doesn't go over the max speed possible for this propulsion
2735
2672
        if (speed > propulsion->maxSpeed)
2736
2673
        {
2737
2674
                speed = propulsion->maxSpeed;
2738
2675
        }
2739
 
        
 
2676
 
 
2677
        // Factor in experience
 
2678
        speed *= (100 + EXP_SPEED_BONUS * level);
 
2679
        speed /= 100;
 
2680
 
2740
2681
        return speed;
2741
2682
}
2742
2683
 
2759
2700
        //add weapon power
2760
2701
        for(i=0; i<psTemplate->numWeaps; i++)
2761
2702
        {
 
2703
                // FIX ME:
2762
2704
                ASSERT( psTemplate->asWeaps[i]<numWeaponStats,
2763
2705
                        //"Invalid Template weapon for %s", psTemplate->pName );
2764
2706
                        "Invalid Template weapon for %s", getTemplateName(psTemplate) );
2799
2741
/* Calculate the power points required to build/maintain a droid */
2800
2742
UDWORD  calcDroidPower(DROID *psDroid)
2801
2743
{
2802
 
        //Watermelon:re-enabled i
 
2744
        //re-enabled i
2803
2745
        UDWORD      power, i;
2804
2746
 
2805
2747
        //get the component power
2830
2772
 
2831
2773
UDWORD calcDroidPoints(DROID *psDroid)
2832
2774
{
2833
 
        int points, i;
 
2775
        unsigned int i;
 
2776
        int points;
2834
2777
 
2835
2778
        points  = getBodyStats(psDroid)->buildPoints;
2836
2779
        points += getBrainStats(psDroid)->buildPoints;
2866
2809
        psDroid = createDroid(player);
2867
2810
        if (psDroid == NULL)
2868
2811
        {
2869
 
                debug(LOG_NEVER, "unit build: unable to create\n");
 
2812
                debug(LOG_NEVER, "unit build: unable to create");
2870
2813
                ASSERT(!"out of memory", "Cannot get the memory for the unit");
2871
2814
                return NULL;
2872
2815
        }
 
2816
        psDroid->sMove.asPath = NULL;
2873
2817
 
2874
2818
        //fill in other details
2875
2819
 
2896
2840
        {
2897
2841
                if (!grpCreate(&psGrp))
2898
2842
                {
2899
 
                        debug(LOG_NEVER, "unit build: unable to create group\n");
 
2843
                        debug(LOG_NEVER, "unit build: unable to create group");
2900
2844
                        ASSERT(!"unable to create group", "Can't create unit because can't create group");
2901
2845
                        free(psDroid);
2902
2846
                        return NULL;
2923
2867
                psDroid->asWeaps[i].nStat = 0;
2924
2868
                psDroid->asWeaps[i].ammo = 0;
2925
2869
                psDroid->asWeaps[i].recoilValue = 0;
2926
 
                psDroid->asWeaps[i].hitPoints = 0;
 
2870
                psDroid->asWeaps[i].rotation = 0;
 
2871
                psDroid->asWeaps[i].pitch = 0;
2927
2872
        }
2928
2873
 
2929
2874
        psDroid->listSize = 0;
2938
2883
 
2939
2884
        // find the highest stored experience
2940
2885
        if ((psDroid->droidType != DROID_CONSTRUCT) &&
2941
 
        (psDroid->droidType != DROID_CYBORG_CONSTRUCT) &&
 
2886
                (psDroid->droidType != DROID_CYBORG_CONSTRUCT) &&
2942
2887
                (psDroid->droidType != DROID_REPAIR) &&
2943
 
        (psDroid->droidType != DROID_CYBORG_REPAIR) &&
 
2888
                (psDroid->droidType != DROID_CYBORG_REPAIR) &&
2944
2889
                (psDroid->droidType != DROID_TRANSPORTER))
2945
2890
        {
2946
2891
                numKills = 0;
2974
2919
        psDroid->direction = 0;
2975
2920
        psDroid->pitch =  0;
2976
2921
        psDroid->roll = 0;
2977
 
        //Watermelon:initialize all weapon turrent rotation pitch info
2978
 
        for(i = 0;i < DROID_MAXWEAPS; i++)
2979
 
        {
2980
 
                psDroid->turretRotation[i] = 0;
2981
 
                psDroid->turretPitch[i] = 0;
2982
 
        }
2983
2922
        psDroid->selected = false;
2984
2923
        psDroid->lastEmission = 0;
2985
 
                // ffs AM
2986
2924
        psDroid->bTargetted = false;
2987
2925
        psDroid->timeLastHit = UDWORD_MAX;
2988
2926
        psDroid->lastHitWeapon = UDWORD_MAX;    // no such weapon
3002
2940
 
3003
2941
        if (cyborgDroid(psDroid))
3004
2942
        {
3005
 
                for (inc = 0; inc < NUM_WEAPON_CLASS; inc++)
 
2943
                for (inc = 0; inc < WC_NUM_WEAPON_CLASSES; inc++)
3006
2944
                {
3007
2945
                        for (impact_side = 0;impact_side < NUM_HIT_SIDES;impact_side=impact_side+1)
3008
2946
                        {
3013
2951
        }
3014
2952
        else
3015
2953
        {
3016
 
                for (inc = 0; inc < NUM_WEAPON_CLASS; inc++)
 
2954
                for (inc = 0; inc < WC_NUM_WEAPON_CLASSES; inc++)
3017
2955
                {
3018
2956
                        for (impact_side = 0;impact_side < NUM_HIT_SIDES;impact_side=impact_side+1)
3019
2957
                        {
3048
2986
                {
3049
2987
                        updateDroidOrientation(psDroid);
3050
2988
                }
3051
 
                visTilesUpdate((BASE_OBJECT *)psDroid);
 
2989
                visTilesUpdate((BASE_OBJECT *)psDroid, rayTerrainCallback);
3052
2990
                gridAddObject((BASE_OBJECT *)psDroid);
3053
2991
                clustNewDroid(psDroid);
3054
2992
        }
3111
3049
        psDroid->numWeaps = pTemplate->numWeaps;
3112
3050
        for (inc = 0;inc < psDroid->numWeaps;inc++)
3113
3051
        {
3114
 
                psDroid->turretRotation[inc] = 0;
3115
 
                psDroid->turretPitch[inc] = 0;
 
3052
                psDroid->asWeaps[inc].rotation = 0;
 
3053
                psDroid->asWeaps[inc].pitch = 0;
3116
3054
        }
3117
3055
 
3118
3056
        psDroid->body = calcTemplateBody(pTemplate, psDroid->player);
3126
3064
                {
3127
3065
                        psDroid->asWeaps[inc].lastFired=0;
3128
3066
                        psDroid->asWeaps[inc].nStat = pTemplate->asWeaps[inc];
3129
 
                        psDroid->asWeaps[inc].hitPoints = (asWeaponStats + psDroid->
3130
 
                                asWeaps[inc].nStat)->hitPoints;
3131
3067
                        psDroid->asWeaps[inc].recoilValue = 0;
3132
3068
                        psDroid->asWeaps[inc].ammo = (asWeaponStats + psDroid->
3133
3069
                                asWeaps[inc].nStat)->numRounds;
3164
3100
 
3165
3101
        psTemplate->droidType = psDroid->droidType;
3166
3102
 
3167
 
    //Watermelon:can only have DROID_MAXWEAPS weapon now
 
3103
        //can only have DROID_MAXWEAPS weapon now
3168
3104
        for (inc = 0; inc < DROID_MAXWEAPS; inc++)
3169
3105
        {
3170
 
                //Watermelon:this should fix the NULL weapon stats for empty weaponslots
 
3106
                //this should fix the NULL weapon stats for empty weaponslots
3171
3107
                psTemplate->asWeaps[inc] = 0;
3172
3108
                if (psDroid->asWeaps[inc].nStat > 0)
3173
3109
                {
3229
3165
                //check the factory can cope with this sized body
3230
3166
                if (!((asBodyStats + psCurr->asParts[COMP_BODY])->size > iCapacity) )
3231
3167
                {
3232
 
            //cyborg templates are available when the body has been research
3233
 
            //-same for Transporter in multiPlayer
 
3168
                        //cyborg templates are available when the body has been research
 
3169
                        //-same for Transporter in multiPlayer
3234
3170
                        if ( psCurr->droidType == DROID_CYBORG ||
3235
3171
                                 psCurr->droidType == DROID_CYBORG_SUPER ||
3236
 
                 psCurr->droidType == DROID_CYBORG_CONSTRUCT ||
3237
 
                 psCurr->droidType == DROID_CYBORG_REPAIR ||
3238
 
                 psCurr->droidType == DROID_TRANSPORTER)
 
3172
                                 psCurr->droidType == DROID_CYBORG_CONSTRUCT ||
 
3173
                                 psCurr->droidType == DROID_CYBORG_REPAIR ||
 
3174
                                 psCurr->droidType == DROID_TRANSPORTER)
3239
3175
                        {
3240
3176
                                if ( apCompLists[psFactory->player][COMP_BODY]
3241
3177
                                         [psCurr->asParts[COMP_BODY]] != AVAILABLE )
3418
3354
void    groupConsoleInformOfCreation( UDWORD groupNumber )
3419
3355
{
3420
3356
        char groupInfo[255];
3421
 
        
 
3357
 
3422
3358
        if (!getWarCamStatus())
3423
3359
        {
3424
3360
                unsigned int num_selected = selNumSelected(selectedPlayer);
3442
3378
        {
3443
3379
                ssprintf(groupInfo, ngettext("Aligning with Group %u - %u Unit", "Aligning with Group %u - %u Units", num_selected), groupNumber, num_selected);
3444
3380
        }
3445
 
        
 
3381
 
3446
3382
        addConsoleMessage(groupInfo,RIGHT_JUSTIFY,SYSTEM_MESSAGE);
3447
3383
}
3448
3384
 
3473
3409
/**
3474
3410
 * calculate muzzle tip location in 3d world
3475
3411
 */
3476
 
BOOL calcDroidMuzzleLocation(DROID *psDroid, Vector3i *muzzle, int weapon_slot)
 
3412
BOOL calcDroidMuzzleLocation(DROID *psDroid, Vector3f *muzzle, int weapon_slot)
3477
3413
{
3478
 
        Vector3i barrel;
3479
 
        iIMDShape *psShape, *psWeapon, *psWeaponMount;
 
3414
        iIMDShape *psShape, *psWeaponImd;
3480
3415
 
3481
3416
        CHECK_DROID(psDroid);
3482
3417
 
3483
 
        psShape = BODY_IMD(psDroid,psDroid->player);
 
3418
        psShape = BODY_IMD(psDroid, psDroid->player);
 
3419
        psWeaponImd = (asWeaponStats[psDroid->asWeaps[weapon_slot].nStat]).pIMD;
3484
3420
 
3485
 
        psWeapon          = (asWeaponStats[psDroid->asWeaps[weapon_slot].nStat]).pIMD;
3486
 
        psWeaponMount = (asWeaponStats[psDroid->asWeaps[weapon_slot].nStat]).pMountGraphic;
3487
3421
        if(psShape && psShape->nconnectors)
3488
3422
        {
 
3423
                Vector3f barrel = {0.0f, 0.0f, 0.0f};
 
3424
 
3489
3425
                pie_MatBegin();
3490
3426
 
3491
 
                pie_TRANSLATE(psDroid->pos.x,-(SDWORD)psDroid->pos.z,psDroid->pos.y);
 
3427
                pie_TRANSLATE(psDroid->pos.x, -psDroid->pos.z, psDroid->pos.y);
 
3428
 
3492
3429
                //matrix = the center of droid
3493
 
                pie_MatRotY( DEG( (SDWORD)psDroid->direction ) );
 
3430
                pie_MatRotY( DEG( psDroid->direction ) );
3494
3431
                pie_MatRotX( DEG( psDroid->pitch ) );
3495
 
                pie_MatRotZ( DEG( -(SDWORD)psDroid->roll ) );
 
3432
                pie_MatRotZ( DEG( -psDroid->roll ) );
3496
3433
                pie_TRANSLATE( psShape->connectors[weapon_slot].x, -psShape->connectors[weapon_slot].z,
3497
3434
                                          -psShape->connectors[weapon_slot].y);//note y and z flipped
3498
3435
 
3499
3436
                //matrix = the gun and turret mount on the body
3500
 
                pie_MatRotY(DEG((SDWORD)psDroid->turretRotation[weapon_slot]));//+ve anticlockwise
3501
 
                pie_MatRotX(DEG(psDroid->turretPitch[weapon_slot]));//+ve up
 
3437
                pie_MatRotY(DEG(psDroid->asWeaps[weapon_slot].rotation));       // +ve anticlockwise
 
3438
                pie_MatRotX(DEG(psDroid->asWeaps[weapon_slot].pitch));          // +ve up
3502
3439
                pie_MatRotZ(DEG(0));
 
3440
 
3503
3441
                //matrix = the muzzle mount on turret
3504
 
                if( psWeapon && psWeapon->nconnectors )
3505
 
                {
3506
 
                        barrel.x = psWeapon->connectors->x;
3507
 
                        barrel.y = -psWeapon->connectors->y;
3508
 
                        barrel.z = -psWeapon->connectors->z;
3509
 
                }
3510
 
                else
3511
 
                {
3512
 
                        barrel.x = 0;
3513
 
                        barrel.y = 0;
3514
 
                        barrel.z = 0;
 
3442
                if( psWeaponImd && psWeaponImd->nconnectors )
 
3443
                {
 
3444
                        barrel = Vector3f_Init(psWeaponImd->connectors->x, -psWeaponImd->connectors->y, -psWeaponImd->connectors->z);
3515
3445
                }
3516
3446
 
3517
 
                pie_RotateTranslate3iv(&barrel, muzzle);
 
3447
                pie_RotateTranslate3f(&barrel, muzzle);
3518
3448
                muzzle->z = -muzzle->z;
3519
3449
 
3520
3450
                pie_MatEnd();
3521
3451
        }
3522
3452
        else
3523
3453
        {
3524
 
                muzzle->x = psDroid->pos.x;
3525
 
                muzzle->y = psDroid->pos.y;
3526
 
                muzzle->z = psDroid->pos.z+32;
 
3454
                *muzzle = Vector3f_Init(psDroid->pos.x, psDroid->pos.y, psDroid->pos.z + psDroid->sDisplay.imd->max.y);
3527
3455
        }
3528
3456
 
3529
3457
        CHECK_DROID(psDroid);
3530
 
  return true;
 
3458
 
 
3459
        return true;
3531
3460
}
3532
3461
 
3533
3462
 
3646
3575
                        break;
3647
3576
                }
3648
3577
        }
3649
 
        
 
3578
 
3650
3579
        if (psCurr)
3651
3580
        {
3652
3581
                clearSelection();
3653
3582
                SelectDroid(psCurr);
3654
3583
                return true;
3655
3584
        }
3656
 
        
 
3585
 
3657
3586
        return false;
3658
3587
}
3659
3588
 
3677
3606
        {512, 2048, N_("Hero")}
3678
3607
};
3679
3608
 
3680
 
UDWORD  getDroidLevel(DROID *psDroid)
 
3609
unsigned int getDroidLevel(const DROID* psDroid)
3681
3610
{
3682
 
        static const unsigned int lastRank = sizeof(arrRank) / sizeof(struct rankMap);
3683
3611
        bool isCommander = (psDroid->droidType == DROID_COMMAND ||
3684
 
                            psDroid->droidType == DROID_SENSOR) ? true : false;
 
3612
                                                psDroid->droidType == DROID_SENSOR) ? true : false;
3685
3613
        unsigned int numKills = psDroid->experience;
3686
3614
        unsigned int i;
3687
3615
 
3688
3616
        // Commanders don't need as much kills for ranks in multiplayer
3689
 
        if (isCommander && cmdGetDroidMultiExpBoost())
 
3617
        if (isCommander && cmdGetDroidMultiExpBoost() && bMultiPlayer)
3690
3618
        {
3691
3619
                numKills *= 2;
3692
3620
        }
3694
3622
        // Search through the array of ranks until one is found
3695
3623
        // which requires more kills than the droid has.
3696
3624
        // Then fall back to the previous rank.
3697
 
        for (i = 1; i != lastRank; ++i)
 
3625
        for (i = 1; i < ARRAY_SIZE(arrRank); ++i)
3698
3626
        {
3699
 
                unsigned int requiredKills = isCommander ? arrRank[i].commanderKills : arrRank[i].kills;
 
3627
                const unsigned int requiredKills = isCommander ? arrRank[i].commanderKills : arrRank[i].kills;
 
3628
 
3700
3629
                if (numKills < requiredKills)
3701
3630
                {
3702
3631
                        return i - 1;
3704
3633
        }
3705
3634
 
3706
3635
        // If the criteria of the last rank are met, then select the last one
3707
 
        return lastRank - 1;
 
3636
        return ARRAY_SIZE(arrRank) - 1;
3708
3637
}
3709
3638
 
3710
3639
UDWORD getDroidEffectiveLevel(DROID *psDroid)
3730
3659
 
3731
3660
const char *getDroidNameForRank(UDWORD rank)
3732
3661
{
3733
 
        ASSERT( rank < (sizeof(arrRank) / sizeof(struct rankMap)),
3734
 
                "getDroidNameForRank: given rank number (%d) out of bounds, we only have %zu ranks\n", rank, (sizeof(arrRank) / sizeof(struct rankMap)) );
 
3662
        ASSERT_OR_RETURN(PE_("rank", "invalid"), rank < (sizeof(arrRank) / sizeof(struct rankMap)),
 
3663
                        "given rank number (%d) out of bounds, we only have %lu ranks", rank, (unsigned long) (sizeof(arrRank) / sizeof(struct rankMap)) );
3735
3664
 
3736
3665
        return PE_("rank", arrRank[rank].name);
3737
3666
}
3760
3689
 
3761
3690
// Get the name of a droid from it's DROID structure.
3762
3691
//
3763
 
char *droidGetName(DROID *psDroid)
 
3692
const char *droidGetName(const DROID *psDroid)
3764
3693
{
3765
3694
        return psDroid->aName;
3766
3695
}
3781
3710
// returns true when no droid on x,y square.
3782
3711
BOOL noDroid(UDWORD x, UDWORD y)
3783
3712
{
3784
 
        UDWORD i;
3785
 
        DROID *pD;
 
3713
        unsigned int i;
 
3714
 
3786
3715
        // check each droid list
3787
 
        for(i=0;i<MAX_PLAYERS;i++)
 
3716
        for (i = 0; i < MAX_PLAYERS; ++i)
3788
3717
        {
3789
 
                for(pD = apsDroidLists[i]; pD ; pD= pD->psNext)
 
3718
                const DROID* psDroid;
 
3719
                for (psDroid = apsDroidLists[i]; psDroid; psDroid = psDroid->psNext)
3790
3720
                {
3791
 
                        if (map_coord(pD->pos.x) == x)
 
3721
                        if (map_coord(psDroid->pos.x) == x
 
3722
                         && map_coord(psDroid->pos.y) == y)
3792
3723
                        {
3793
 
                                if (map_coord(pD->pos.y) == y)
3794
 
                                {
3795
3724
                                        return false;
3796
 
                                }
3797
3725
                        }
3798
3726
                }
3799
3727
        }
3812
3740
        {
3813
3741
                for(pD = apsDroidLists[i]; pD ; pD= pD->psNext)
3814
3742
                {
3815
 
                        if (map_coord(pD->pos.x) == x)
 
3743
                        if (map_coord(pD->pos.x) == x
 
3744
                         && map_coord(pD->pos.y) == y)
3816
3745
                        {
3817
 
                                if (map_coord(pD->pos.y) == y)
 
3746
                                if (bFound)
3818
3747
                                {
3819
 
                                        if (bFound)
3820
 
                                        {
3821
 
                                                return false;
3822
 
                                        }
3823
 
                                        else
3824
 
                                        {
3825
 
                                                bFound = true;//first droid on this square so continue
3826
 
                                        }
 
3748
                                        return false;
3827
3749
                                }
 
3750
 
 
3751
                                bFound = true;//first droid on this square so continue
3828
3752
                        }
3829
3753
                }
3830
3754
        }
3833
3757
 
3834
3758
// ////////////////////////////////////////////////////////////////////////////
3835
3759
// returns true if it's a sensible place to put that droid.
3836
 
BOOL sensiblePlace(SDWORD x, SDWORD y)
 
3760
static BOOL sensiblePlace(SDWORD x, SDWORD y, PROPULSION_TYPE propulsion)
3837
3761
{
3838
3762
        UDWORD count=0;
3839
3763
 
3843
3767
        if((y < TOO_NEAR_EDGE) || (y > (SDWORD)(mapHeight - TOO_NEAR_EDGE)))
3844
3768
                return false;
3845
3769
 
3846
 
    //check no features there
 
3770
        // check no features there
3847
3771
        if(TileHasFeature(mapTile(x,y)))
3848
3772
        {
3849
3773
                return false;
3850
3774
        }
3851
3775
 
3852
3776
        // not on a blocking tile.
3853
 
        if( fpathBlockingTile(x,y) )
 
3777
        if (fpathBlockingTile(x, y, propulsion))
 
3778
        {
3854
3779
                return false;
 
3780
        }
3855
3781
 
3856
3782
        // shouldn't next to more than one blocking tile, to avoid windy paths.
3857
 
        if( fpathBlockingTile(x-1 ,y-1) )
3858
 
                count++;
3859
 
        if( fpathBlockingTile(x,y-1) )
3860
 
                count++;
3861
 
        if( fpathBlockingTile(x+1,y-1) )
3862
 
                count++;
3863
 
        if( fpathBlockingTile(x-1,y) )
3864
 
                count++;
3865
 
        if( fpathBlockingTile(x+1,y) )
3866
 
                count++;
3867
 
        if( fpathBlockingTile(x-1,y+1) )
3868
 
                count++;
3869
 
        if( fpathBlockingTile(x,y+1) )
3870
 
                count++;
3871
 
        if( fpathBlockingTile(x+1,y+1) )
 
3783
        if (fpathBlockingTile(x - 1, y - 1, propulsion))
 
3784
                count++;
 
3785
        if (fpathBlockingTile(x, y - 1, propulsion))
 
3786
                count++;
 
3787
        if (fpathBlockingTile(x + 1, y - 1, propulsion))
 
3788
                count++;
 
3789
        if (fpathBlockingTile(x - 1, y, propulsion))
 
3790
                count++;
 
3791
        if (fpathBlockingTile(x + 1, y, propulsion))
 
3792
                count++;
 
3793
        if (fpathBlockingTile(x -1, y + 1, propulsion))
 
3794
                count++;
 
3795
        if (fpathBlockingTile(x, y + 1, propulsion))
 
3796
                count++;
 
3797
        if (fpathBlockingTile(x +1, y + 1, propulsion))
3872
3798
                count++;
3873
3799
 
3874
3800
        if(count > 1)
3877
3803
        return true;
3878
3804
}
3879
3805
 
3880
 
 
3881
 
// ------------------------------------------------------------------------------------
3882
 
BOOL    normalPAT(UDWORD x, UDWORD y)
3883
 
{
3884
 
        if(sensiblePlace(x,y) && noDroid(x,y))
3885
 
        {
3886
 
                return(true);
3887
 
        }
3888
 
        else
3889
 
        {
3890
 
                return(false);
3891
 
        }
3892
 
}
3893
 
// ------------------------------------------------------------------------------------
3894
 
// Should stop things being placed in inaccessible areas?
 
3806
// ------------------------------------------------------------------------------------
 
3807
// Should stop things being placed in inaccessible areas? Assume wheeled propulsion.
3895
3808
BOOL    zonedPAT(UDWORD x, UDWORD y)
3896
3809
{
3897
 
        if (sensiblePlace(x,y) && noDroid(x,y) && gwZoneReachable(gwGetZone(x,y)))
3898
 
        {
3899
 
                return(true);
3900
 
        }
3901
 
        else
3902
 
        {
3903
 
                return(false);
3904
 
        }
3905
 
}
3906
 
 
3907
 
// ------------------------------------------------------------------------------------
 
3810
        return sensiblePlace(x, y, PROPULSION_TYPE_WHEELED) && noDroid(x,y);
 
3811
}
 
3812
 
 
3813
static BOOL canFitDroid(UDWORD x, UDWORD y)
 
3814
{
 
3815
        return sensiblePlace(x, y, PROPULSION_TYPE_WHEELED) && (noDroid(x,y) || oneDroid(x, y));
 
3816
}
 
3817
 
 
3818
/// find a tile for which the function will return true
3908
3819
BOOL    pickATileGen(UDWORD *x, UDWORD *y, UBYTE numIterations,
3909
3820
                                         BOOL (*function)(UDWORD x, UDWORD y))
3910
3821
{
3911
 
        SDWORD  i,j;
3912
 
        SDWORD  startX,endX,startY,endY;
3913
 
        UDWORD  passes;
3914
 
 
3915
 
 
3916
 
        ASSERT( *x<mapWidth,"x coordinate is off-map for pickATileGen" );
3917
 
        ASSERT( *y<mapHeight,"y coordinate is off-map for pickATileGen" );
3918
 
 
3919
 
        /* Exit if they're fine! */
3920
 
        if(sensiblePlace(*x,*y) && noDroid(*x,*y))
3921
 
        {
3922
 
                return(true);
3923
 
        }
3924
 
 
3925
 
        /* Initial box dimensions and set iteration count to zero */
3926
 
        startX = endX = *x;     startY = endY = *y;     passes = 0;
3927
 
 
3928
 
        /* Keep going until we get a tile or we exceed distance */
3929
 
        while(passes<numIterations)
3930
 
        {
3931
 
                /* Process whole box */
3932
 
                for(i = startX; i <= endX; i++)
3933
 
                {
3934
 
                        for(j = startY; j<= endY; j++)
3935
 
                        {
3936
 
                                /* Test only perimeter as internal tested previous iteration */
3937
 
                                if(i==startX || i==endX || j==startY || j==endY)
3938
 
                                {
3939
 
                                        /* Good enough? */
3940
 
                                        if(function(i,j))
3941
 
                                        {
3942
 
                                                /* Set exit conditions and get out NOW */
3943
 
                                                *x = i; *y = j;
3944
 
                                                return(true);
3945
 
                                        }
3946
 
                                }
3947
 
                        }
3948
 
                }
3949
 
                /* Expand the box out in all directions - off map handled by tileAcceptable */
3950
 
                startX--; startY--;     endX++; endY++; passes++;
3951
 
        }
3952
 
        /* If we got this far, then we failed - passed in values will be unchanged */
3953
 
        return(false);
3954
 
 
 
3822
        return pickATileGenThreat(x, y, numIterations, -1, -1, function);
3955
3823
}
3956
3824
 
3957
 
//same as orig, but with threat check
 
3825
/// find a tile for which the passed function will return true without any threat in the specified range 
3958
3826
BOOL    pickATileGenThreat(UDWORD *x, UDWORD *y, UBYTE numIterations, SDWORD threatRange,
3959
3827
                                         SDWORD player, BOOL (*function)(UDWORD x, UDWORD y))
3960
3828
{
3963
3831
UDWORD  passes;
3964
3832
 
3965
3833
 
3966
 
        ASSERT( *x<mapWidth,"x coordinate is off-map for pickATileGen" );
3967
 
        ASSERT( *y<mapHeight,"y coordinate is off-map for pickATileGen" );
 
3834
        ASSERT_OR_RETURN(false, *x<mapWidth,"x coordinate is off-map for pickATileGen" );
 
3835
        ASSERT_OR_RETURN(false, *y<mapHeight,"y coordinate is off-map for pickATileGen" );
3968
3836
 
3969
3837
        if(function(*x,*y) && ((threatRange <=0) || (!ThreatInRange(player, threatRange, *x, *y, false))))      //TODO: vtol check really not needed?
3970
3838
        {
4003
3871
 
4004
3872
}
4005
3873
 
4006
 
// ------------------------------------------------------------------------------------
4007
 
/* Improved pickATile - Replaces truly scary existing one. */
4008
 
/* AM 22 - 10 - 98 */
 
3874
/// find an empty tile accessible to a wheeled droid 
4009
3875
BOOL    pickATile(UDWORD *x, UDWORD *y, UBYTE numIterations)
4010
3876
{
4011
 
        SDWORD  i,j;
4012
 
        SDWORD  startX,endX,startY,endY;
4013
 
        UDWORD  passes;
4014
 
 
4015
 
        ASSERT( *x<mapWidth,"x coordinate is off-map for pickATile" );
4016
 
        ASSERT( *y<mapHeight,"y coordinate is off-map for pickATile" );
4017
 
 
4018
 
        /* Exit if they're fine! */
4019
 
        if(sensiblePlace(*x,*y) && noDroid(*x,*y))
4020
 
        {
4021
 
                return(true);
4022
 
        }
4023
 
 
4024
 
        /* Initial box dimensions and set iteration count to zero */
4025
 
        startX = endX = *x;     startY = endY = *y;     passes = 0;
4026
 
 
4027
 
        /* Keep going until we get a tile or we exceed distance */
4028
 
        while(passes<numIterations)
4029
 
        {
4030
 
                /* Process whole box */
4031
 
                for(i = startX; i <= endX; i++)
4032
 
                {
4033
 
                        for(j = startY; j<= endY; j++)
4034
 
                        {
4035
 
                                /* Test only perimeter as internal tested previous iteration */
4036
 
                                if(i==startX || i==endX || j==startY || j==endY)
4037
 
                                {
4038
 
                                        /* Good enough? */
4039
 
                                        if(sensiblePlace(i,j) && noDroid(i,j))
4040
 
                                        {
4041
 
                                                /* Set exit conditions and get out NOW */
4042
 
                                                *x = i; *y = j;
4043
 
                                                return(true);
4044
 
                                        }
4045
 
                                }
4046
 
                        }
4047
 
                }
4048
 
                /* Expand the box out in all directions - off map handled by tileAcceptable */
4049
 
                startX--; startY--;     endX++; endY++; passes++;
4050
 
        }
4051
 
        /* If we got this far, then we failed - passed in values will be unchanged */
4052
 
        return(false);
 
3877
        return pickATileGen(x, y, numIterations, zonedPAT);
4053
3878
}
4054
3879
 
4055
 
// pickHalfATile just like improved pickATile but with Double Density Droid Placement
 
3880
/// find a tile for a wheeled droid with only one other droid present
4056
3881
PICKTILE pickHalfATile(UDWORD *x, UDWORD *y, UBYTE numIterations)
4057
3882
{
4058
 
        SDWORD  i,j;
4059
 
        SDWORD  startX,endX,startY,endY;
4060
 
        UDWORD  passes;
4061
 
 
4062
 
        /*
4063
 
                Why was this written - I wrote pickATileGen to take a function
4064
 
                pointer for what qualified as a valid tile - could use that.
4065
 
                I'm not going to change it in case I'm missing the point */
4066
 
        if (pickATileGen(x, y, numIterations,zonedPAT))
4067
 
        {
4068
 
                return FREE_TILE;
4069
 
        }
4070
 
 
4071
 
        /* Exit if they're fine! */
4072
 
        if (sensiblePlace(*x, *y) && oneDroid(*x, *y))
4073
 
        {
4074
 
                return HALF_FREE_TILE;
4075
 
        }
4076
 
 
4077
 
        /* Initial box dimensions and set iteration count to zero */
4078
 
        startX = endX = *x;     startY = endY = *y;     passes = 0;
4079
 
 
4080
 
 
4081
 
 
4082
 
        /* Keep going until we get a tile or we exceed distance */
4083
 
        while(passes<numIterations)
4084
 
        {
4085
 
                /* Process whole box */
4086
 
                for(i = startX; i <= endX; i++)
4087
 
                {
4088
 
                        for(j = startY; j<= endY; j++)
4089
 
                        {
4090
 
                                /* Test only perimeter as internal tested previous iteration */
4091
 
                                if(i==startX || i==endX || j==startY || j==endY)
4092
 
                                {
4093
 
                                        /* Good enough? */
4094
 
                                        if(sensiblePlace(i,j) && oneDroid(i,j))
4095
 
                                        {
4096
 
                                                /* Set exit conditions and get out NOW */
4097
 
                                                *x = i; *y = j;
4098
 
                                                return HALF_FREE_TILE;
4099
 
                                        }
4100
 
                                }
4101
 
                        }
4102
 
                }
4103
 
                /* Expand the box out in all directions - off map handled by tileAcceptable */
4104
 
                startX--; startY--;     endX++; endY++; passes++;
4105
 
        }
4106
 
        /* If we got this far, then we failed - passed in values will be unchanged */
4107
 
        return NO_FREE_TILE;
 
3883
        return pickATileGen(x, y, numIterations, canFitDroid);
4108
3884
}
4109
3885
 
4110
3886
/* Looks through the players list of droids to see if any of them are
4153
3929
        BOOL    order = false;
4154
3930
        UDWORD  i = 0;
4155
3931
 
4156
 
        ASSERT(psStruct != NULL, "buildModule: Invalid structure pointer");
 
3932
        ASSERT(psStruct != NULL && psStruct->pStructureType != NULL, "Invalid structure pointer");
 
3933
        if (!psStruct || !psStruct->pStructureType)
 
3934
        {
 
3935
                return false;
 
3936
        }
4157
3937
 
4158
3938
        switch (psStruct->pStructureType->type)
4159
3939
        {
4160
3940
                case REF_POWER_GEN:
4161
3941
                        //check room for one more!
4162
 
                        ASSERT(psStruct->pFunctionality, "buildModule: Functionality missing for power!");
 
3942
                        ASSERT_OR_RETURN(false, psStruct->pFunctionality, "Functionality missing for power!");
4163
3943
                        if (psStruct->pFunctionality->powerGenerator.capacity < NUM_POWER_MODULES)
4164
3944
                        {
4165
3945
                                i = powerModuleStat;
4169
3949
                case REF_FACTORY:
4170
3950
                case REF_VTOL_FACTORY:
4171
3951
                        //check room for one more!
4172
 
                        ASSERT(psStruct->pFunctionality, "buildModule: Functionality missing for factory!");
 
3952
                        ASSERT_OR_RETURN(false, psStruct->pFunctionality, "Functionality missing for factory!");
4173
3953
                        if (psStruct->pFunctionality->factory.capacity < NUM_FACTORY_MODULES)
4174
3954
                        {
4175
3955
                                i = factoryModuleStat;
4178
3958
                        break;
4179
3959
                case REF_RESEARCH:
4180
3960
                        //check room for one more!
4181
 
                        ASSERT(psStruct->pFunctionality, "buildModule: Functionality missing for research!");
 
3961
                        ASSERT_OR_RETURN(false, psStruct->pFunctionality, "Functionality missing for research!");
4182
3962
                        if (psStruct->pFunctionality->researchFacility.capacity < NUM_RESEARCH_MODULES)
4183
3963
                        {
4184
3964
                                i = researchModuleStat;
4269
4049
        return getTemplateName(&sTemplate);
4270
4050
}
4271
4051
 
4272
 
 
4273
 
 
4274
 
/*return the name to display for the interface - we don't know if this is
4275
 
a string ID or something the user types in*/
4276
4052
const char* getTemplateName(const DROID_TEMPLATE *psTemplate)
4277
4053
{
4278
 
        const char *pNameID = psTemplate->aName;
4279
 
        UDWORD id;
4280
 
 
4281
 
        /*see if the name has a resource associated with it by trying to get
4282
 
        the ID for the string*/
4283
 
        if (strresGetIDNum(psStringRes, pNameID, &id))
4284
 
        {
4285
 
                //get the string from the id
4286
 
                const char *pName = strresGetString(psStringRes, id);
4287
 
                if (pName)
4288
 
                {
4289
 
                        return pName;
4290
 
                }
4291
 
        }
4292
 
        //if haven't found a resource, return the name passed in
4293
 
        return pNameID;
 
4054
        return psTemplate->aName;
4294
4055
}
4295
4056
 
4296
4057
/* Just returns true if the droid's present body points aren't as high as the original*/
4309
4070
 
4310
4071
BOOL getDroidResourceName(char *pName)
4311
4072
{
4312
 
        UDWORD id;
 
4073
        /* See if the name has a string resource associated with it by trying
 
4074
         * to get the string resource.
 
4075
         */
 
4076
        const char * const name = strresGetString(psStringRes, pName);
4313
4077
 
4314
 
        //see if the name has a resource associated with it by trying to get the ID for the string
4315
 
        if (!strresGetIDNum(psStringRes, pName, &id))
 
4078
        if (!name)
4316
4079
        {
4317
 
                debug( LOG_ERROR, "Unable to find string resource for %s", pName );
4318
 
                abort();
 
4080
                debug(LOG_ERROR, "Unable to find string resource for string with ID \"%s\"", pName);
4319
4081
                return false;
4320
4082
        }
4321
 
        //get the string from the id
4322
 
        strcpy(pName, strresGetString(psStringRes, id));
 
4083
 
 
4084
        // Copy the retrieved string into the output parameter
 
4085
        strcpy(pName, name);
4323
4086
 
4324
4087
        return true;
4325
4088
}
4332
4095
 
4333
4096
        CHECK_DROID(psDroid);
4334
4097
 
4335
 
        //Watermelon:use slot 0 for now
 
4098
        //use slot 0 for now
4336
4099
        //if (psDroid->numWeaps && asWeaponStats[psDroid->asWeaps[0].nStat].
4337
 
    if (psDroid->numWeaps > 0 && asWeaponStats[psDroid->asWeaps[0].nStat].
 
4100
        if (psDroid->numWeaps > 0 && asWeaponStats[psDroid->asWeaps[0].nStat].
4338
4101
                weaponSubClass == WSC_ELECTRONIC)
4339
4102
        {
4340
4103
                return true;
4369
4132
                for (psCurr = apsDroidLists[psDroid->player]; psCurr != NULL; psCurr = psCurr->psNext)
4370
4133
                {
4371
4134
                        //if (psCurr->droidType == DROID_REPAIR && psCurr->action ==
4372
 
            if ((psCurr->droidType == DROID_REPAIR || psCurr->droidType ==
4373
 
                DROID_CYBORG_REPAIR) && psCurr->action ==
 
4135
                        if ((psCurr->droidType == DROID_REPAIR || psCurr->droidType ==
 
4136
                                DROID_CYBORG_REPAIR) && psCurr->action ==
4374
4137
                                DACTION_DROIDREPAIR && psCurr->psTarget == (BASE_OBJECT *)psDroid)
4375
4138
                        {
4376
4139
                                return true;
4399
4162
//access functions for vtols
4400
4163
BOOL isVtolDroid(const DROID* psDroid)
4401
4164
{
4402
 
        return asPropulsionStats[psDroid->asBits[COMP_PROPULSION].nStat].propulsionType == LIFT
 
4165
        return asPropulsionStats[psDroid->asBits[COMP_PROPULSION].nStat].propulsionType == PROPULSION_TYPE_LIFT
4403
4166
            && psDroid->droidType != DROID_TRANSPORTER;
4404
4167
}
4405
4168
 
4505
4268
 
4506
4269
        if (psDroid->action == DACTION_MOVETOREARM ||
4507
4270
                psDroid->action == DACTION_WAITFORREARM ||
4508
 
        psDroid->action == DACTION_MOVETOREARMPOINT ||
 
4271
                psDroid->action == DACTION_MOVETOREARMPOINT ||
4509
4272
                psDroid->action == DACTION_WAITDURINGREARM)
4510
4273
        {
4511
4274
                return true;
4519
4282
{
4520
4283
        CHECK_DROID(psDroid);
4521
4284
 
4522
 
    //what about cyborgs?
4523
 
    if (!(psDroid->droidType == DROID_WEAPON || psDroid->droidType == DROID_CYBORG ||
4524
 
        psDroid->droidType == DROID_CYBORG_SUPER))
 
4285
        //what about cyborgs?
 
4286
        if (!(psDroid->droidType == DROID_WEAPON || psDroid->droidType == DROID_CYBORG ||
 
4287
                psDroid->droidType == DROID_CYBORG_SUPER))
4525
4288
        {
4526
4289
                return false;
4527
4290
        }
4570
4333
 
4571
4334
 
4572
4335
/*returns a count of the base number of attack runs for the weapon attached to the droid*/
4573
 
//Watermelon:adds int weapon_slot
 
4336
//adds int weapon_slot
4574
4337
UWORD   getNumAttackRuns(DROID *psDroid, int weapon_slot)
4575
4338
{
4576
 
    UWORD   numAttackRuns;
4577
 
 
4578
 
    ASSERT(isVtolDroid(psDroid), "not a VTOL Droid");
4579
 
 
4580
 
    /*if weapon attached to the droid is a salvo weapon, then number of shots that
4581
 
    can be fired = vtolAttackRuns*numRounds */
4582
 
    if (asWeaponStats[psDroid->asWeaps[weapon_slot].nStat].reloadTime)
4583
 
    {
4584
 
        numAttackRuns = (UWORD)(asWeaponStats[psDroid->asWeaps[weapon_slot].nStat].numRounds *
4585
 
            asWeaponStats[psDroid->asWeaps[weapon_slot].nStat].vtolAttackRuns);
4586
 
    }
4587
 
    else
4588
 
    {
4589
 
        numAttackRuns = asWeaponStats[psDroid->asWeaps[weapon_slot].nStat].vtolAttackRuns;
4590
 
    }
4591
 
 
4592
 
    return numAttackRuns;
 
4339
        UWORD   numAttackRuns;
 
4340
 
 
4341
        ASSERT_OR_RETURN(0, isVtolDroid(psDroid), "not a VTOL Droid");
 
4342
 
 
4343
        /*if weapon attached to the droid is a salvo weapon, then number of shots that
 
4344
        can be fired = vtolAttackRuns*numRounds */
 
4345
        if (asWeaponStats[psDroid->asWeaps[weapon_slot].nStat].reloadTime)
 
4346
        {
 
4347
                numAttackRuns = (UWORD)(asWeaponStats[psDroid->asWeaps[weapon_slot].nStat].numRounds *
 
4348
                        asWeaponStats[psDroid->asWeaps[weapon_slot].nStat].vtolAttackRuns);
 
4349
        }
 
4350
        else
 
4351
        {
 
4352
                numAttackRuns = asWeaponStats[psDroid->asWeaps[weapon_slot].nStat].vtolAttackRuns;
 
4353
        }
 
4354
 
 
4355
        return numAttackRuns;
4593
4356
}
4594
4357
 
4595
4358
/*Checks a vtol for being fully armed and fully repaired to see if ready to
4600
4363
 
4601
4364
        CHECK_DROID(psDroid);
4602
4365
 
4603
 
        ASSERT(isVtolDroid(psDroid), "not a VTOL droid");
 
4366
        ASSERT_OR_RETURN(false, isVtolDroid(psDroid), "not a VTOL droid");
4604
4367
 
4605
4368
        if (psDroid->body < psDroid->originalBody)
4606
4369
        {
4619
4382
         *       also not sure if weapon droids (see the above droidType check)
4620
4383
         *       can even have zero weapons. -- Giel
4621
4384
         */
4622
 
        ASSERT(psDroid->numWeaps > 0, "VTOL weapon droid without weapons found!");
 
4385
        ASSERT_OR_RETURN(false, psDroid->numWeaps > 0, "VTOL weapon droid without weapons found!");
4623
4386
 
4624
4387
        //check full complement of ammo
4625
4388
        for (i = 0; i < psDroid->numWeaps; ++i)
4637
4400
/*checks if the droid is a VTOL droid and updates the attack runs as required*/
4638
4401
void updateVtolAttackRun(DROID *psDroid , int weapon_slot)
4639
4402
{
4640
 
    if (isVtolDroid(psDroid))
4641
 
    {
 
4403
        if (isVtolDroid(psDroid))
 
4404
        {
4642
4405
                if (psDroid->numWeaps > 0)
4643
4406
                {
4644
4407
                        if (asWeaponStats[psDroid->asWeaps[weapon_slot].nStat].vtolAttackRuns > 0)
4645
4408
                        {
4646
4409
                                psDroid->sMove.iAttackRuns[weapon_slot]++;
4647
4410
                                //quick check doesn't go over limit
4648
 
                                ASSERT( psDroid->sMove.iAttackRuns[weapon_slot] < UWORD_MAX, "updateVtolAttackRun: too many attack runs" );
 
4411
                                ASSERT( psDroid->sMove.iAttackRuns[weapon_slot] < UWORD_MAX, "too many attack runs");
4649
4412
                        }
4650
4413
                }
4651
 
    }
 
4414
        }
4652
4415
}
4653
4416
 
4654
4417
/*this mends the VTOL when it has been returned to home base whilst on an
4656
4419
void mendVtol(DROID *psDroid)
4657
4420
{
4658
4421
        UBYTE   i;
4659
 
        ASSERT( vtolEmpty(psDroid), "mendVtol: droid is not an empty weapon VTOL!" );
 
4422
        ASSERT_OR_RETURN( , vtolEmpty(psDroid), "droid is not an empty weapon VTOL!");
4660
4423
 
4661
4424
        CHECK_DROID(psDroid);
4662
4425
 
4678
4441
//assign rearmPad to the VTOL
4679
4442
void assignVTOLPad(DROID *psNewDroid, STRUCTURE *psReArmPad)
4680
4443
{
4681
 
    ASSERT(isVtolDroid(psNewDroid), "not a vtol droid");
4682
 
    ASSERT( psReArmPad->type == OBJ_STRUCTURE &&
4683
 
                        psReArmPad->pStructureType->type == REF_REARM_PAD,
4684
 
        "assignVTOLPad: not a ReArm Pad" );
 
4444
        ASSERT_OR_RETURN( , isVtolDroid(psNewDroid), "not a vtol droid");
 
4445
        ASSERT_OR_RETURN( ,  psReArmPad->type == OBJ_STRUCTURE
 
4446
                && psReArmPad->pStructureType->type == REF_REARM_PAD, "not a ReArm Pad" );
4685
4447
 
4686
4448
        setDroidBase(psNewDroid, psReArmPad);
4687
4449
}
4692
4454
{
4693
4455
        SENSOR_STATS    *psStats = NULL;
4694
4456
 
4695
 
        if (!psObj || !psDroid)
 
4457
        CHECK_DROID(psDroid);
 
4458
 
 
4459
        if(!psObj || !psDroid)
4696
4460
        {
4697
4461
                return false;
4698
4462
        }
4732
4496
        }
4733
4497
 
4734
4498
        //check droid is a weapon droid - or Cyborg!!
4735
 
    if (!(psDroid->droidType == DROID_WEAPON || psDroid->droidType ==
4736
 
        DROID_CYBORG || psDroid->droidType == DROID_CYBORG_SUPER))
 
4499
        if (!(psDroid->droidType == DROID_WEAPON || psDroid->droidType ==
 
4500
                DROID_CYBORG || psDroid->droidType == DROID_CYBORG_SUPER))
4737
4501
        {
4738
4502
                return false;
4739
4503
        }
4747
4511
        }
4748
4512
 
4749
4513
        //check vtol droid with vtol sensor
4750
 
    if (isVtolDroid(psDroid) && psDroid->asWeaps[0].nStat > 0)
 
4514
        if (isVtolDroid(psDroid) && psDroid->asWeaps[0].nStat > 0)
4751
4515
        {
4752
 
                if (psStats->type == VTOL_INTERCEPT_SENSOR ||
4753
 
                        psStats->type == VTOL_CB_SENSOR ||
4754
 
            psStats->type == SUPER_SENSOR)
 
4516
                if (psStats->type == VTOL_INTERCEPT_SENSOR || psStats->type == VTOL_CB_SENSOR || psStats->type == SUPER_SENSOR || psStats->type == RADAR_DETECTOR_SENSOR)
4755
4517
                {
4756
4518
                        return true;
4757
4519
                }
4758
4520
                return false;
4759
4521
        }
4760
4522
 
4761
 
        //check indirect weapon droid with standard/cb sensor
4762
 
    /*Super Sensor works as any type*/
 
4523
        // Check indirect weapon droid with standard/CB/radar detector sensor
4763
4524
        if (!proj_Direct(asWeaponStats + psDroid->asWeaps[0].nStat))
4764
4525
        {
4765
 
                if (psStats->type == STANDARD_SENSOR ||
4766
 
                        psStats->type == INDIRECT_CB_SENSOR ||
4767
 
            psStats->type == SUPER_SENSOR)
 
4526
                if (psStats->type == STANDARD_SENSOR || psStats->type == INDIRECT_CB_SENSOR || psStats->type == SUPER_SENSOR || psStats->type == RADAR_DETECTOR_SENSOR)
4768
4527
                {
4769
4528
                        return true;
4770
4529
                }
4781
4540
                return false;
4782
4541
        }
4783
4542
 
4784
 
    /*Super Sensor works as any type*/
 
4543
        /*Super Sensor works as any type*/
4785
4544
        if (asSensorStats[psDroid->asBits[COMP_SENSOR].nStat].type ==
4786
4545
                VTOL_CB_SENSOR ||
4787
4546
                asSensorStats[psDroid->asBits[COMP_SENSOR].nStat].type ==
4857
4616
                                        psD->asBits[COMP_REPAIRUNIT].nStat = (UBYTE)aDefaultRepair[psD->player];
4858
4617
                                }
4859
4618
                        }
4860
 
                }
 
4619
                        }
4861
4620
                // add back into cluster system
4862
4621
                clustNewDroid(psD);
4863
4622
 
4864
4623
                // Update visibility
4865
 
                visTilesUpdate((BASE_OBJECT*)psD);
 
4624
                visTilesUpdate((BASE_OBJECT*)psD, rayTerrainCallback);
4866
4625
 
4867
4626
                // add back into the grid system
4868
4627
                gridAddObject((BASE_OBJECT *)psD);
4935
4694
                vanishDroid(psD);
4936
4695
                // create a new droid
4937
4696
                psNewDroid = buildDroid(&sTemplate, x, y, to, false);
4938
 
                ASSERT(psNewDroid != NULL, "giftSingleUnit: unable to build a unit" );
 
4697
                ASSERT(psNewDroid != NULL, "unable to build a unit");
4939
4698
                if (psNewDroid)
4940
4699
                {
4941
4700
                        addDroid(psNewDroid, apsDroidLists);
4959
4718
/*calculates the electronic resistance of a droid based on its experience level*/
4960
4719
SWORD   droidResistance(DROID *psDroid)
4961
4720
{
4962
 
    SWORD   resistance;
 
4721
        SWORD   resistance;
4963
4722
 
4964
4723
        CHECK_DROID(psDroid);
4965
4724
 
4966
 
    resistance = (SWORD)(psDroid->experience * DROID_RESISTANCE_FACTOR);
4967
 
 
4968
 
    //ensure base minimum in MP before the upgrade effect
4969
 
    if (bMultiPlayer)
4970
 
    {
4971
 
        //ensure resistance is a base minimum
4972
 
        if (resistance < DROID_RESISTANCE_FACTOR)
4973
 
        {
4974
 
            resistance = DROID_RESISTANCE_FACTOR;
4975
 
        }
4976
 
    }
4977
 
 
4978
 
    //structure resistance upgrades are passed on to droids
4979
 
    resistance = (SWORD)(resistance  + resistance * (
4980
 
        asStructureUpgrade[psDroid->player].resistance/100));
4981
 
 
4982
 
    //ensure resistance is a base minimum
4983
 
    if (resistance < DROID_RESISTANCE_FACTOR)
4984
 
    {
4985
 
        resistance = DROID_RESISTANCE_FACTOR;
4986
 
    }
4987
 
 
4988
 
    return resistance;
 
4725
        resistance = (SWORD)(psDroid->experience * DROID_RESISTANCE_FACTOR);
 
4726
 
 
4727
        //ensure base minimum in MP before the upgrade effect
 
4728
        if (bMultiPlayer)
 
4729
        {
 
4730
                //ensure resistance is a base minimum
 
4731
                if (resistance < DROID_RESISTANCE_FACTOR)
 
4732
                {
 
4733
                        resistance = DROID_RESISTANCE_FACTOR;
 
4734
                }
 
4735
        }
 
4736
 
 
4737
        //structure resistance upgrades are passed on to droids
 
4738
        resistance = (SWORD)(resistance  + resistance * (
 
4739
                asStructureUpgrade[psDroid->player].resistance/100));
 
4740
 
 
4741
        //ensure resistance is a base minimum
 
4742
        if (resistance < DROID_RESISTANCE_FACTOR)
 
4743
        {
 
4744
                resistance = DROID_RESISTANCE_FACTOR;
 
4745
        }
 
4746
 
 
4747
        return resistance;
4989
4748
}
4990
4749
 
4991
4750
/*this is called to check the weapon is 'allowed'. Check if VTOL, the weapon is
4992
4751
direct fire. Also check numVTOLattackRuns for the weapon is not zero - return
4993
4752
true if valid weapon*/
4994
 
/* Watermelon:this will be buggy if the droid being checked has both AA weapon and non-AA weapon
 
4753
/* this will be buggy if the droid being checked has both AA weapon and non-AA weapon
4995
4754
Cannot think of a solution without adding additional return value atm.
4996
4755
*/
 
4756
// FIXME: This routine is a mess
4997
4757
BOOL checkValidWeaponForProp(DROID_TEMPLATE *psTemplate)
4998
4758
{
4999
4759
        PROPULSION_STATS        *psPropStats;
5001
4761
 
5002
4762
        //check propulsion stat for vtol
5003
4763
        psPropStats = asPropulsionStats + psTemplate->asParts[COMP_PROPULSION];
5004
 
        ASSERT( psPropStats != NULL,
5005
 
                "checkValidWeaponForProp: invalid propulsion stats pointer" );
 
4764
 
 
4765
        ASSERT_OR_RETURN(false, psPropStats != NULL, "invalid propulsion stats pointer");
 
4766
 
 
4767
        // if there are no weapons, then don't even bother continuing
 
4768
        if (psTemplate->numWeaps == 0)
 
4769
        {
 
4770
                return false;
 
4771
        }
 
4772
 
 
4773
        // FIXME -- why are we checking vtolAttackRuns on non AIR units?
5006
4774
        if (asPropulsionTypes[psPropStats->propulsionType].travel == AIR)
5007
4775
        {
5008
4776
                //check weapon stat for indirect
5009
4777
                if (!proj_Direct(asWeaponStats + psTemplate->asWeaps[0])
5010
 
                    || !asWeaponStats[psTemplate->asWeaps[0]].vtolAttackRuns)
 
4778
                        || !asWeaponStats[psTemplate->asWeaps[0]].vtolAttackRuns)
5011
4779
                {
5012
4780
                        bValid = false;
5013
4781
                }
5020
4788
                }
5021
4789
        }
5022
4790
 
5023
 
        //also checks that there is only a weapon attached and no other system component
5024
 
        if (psTemplate->numWeaps == 0)
5025
 
        {
5026
 
                bValid = false;
5027
 
        }
 
4791
        //also checks that there is no other system component
5028
4792
        if (psTemplate->asParts[COMP_BRAIN] != 0
5029
 
            && asWeaponStats[psTemplate->asWeaps[0]].weaponSubClass != WSC_COMMAND)
 
4793
                && asWeaponStats[psTemplate->asWeaps[0]].weaponSubClass != WSC_COMMAND)
5030
4794
        {
5031
4795
                assert(false);
5032
4796
                bValid = false;
5038
4802
/*called when a Template is deleted in the Design screen*/
5039
4803
void deleteTemplateFromProduction(DROID_TEMPLATE *psTemplate, UBYTE player)
5040
4804
{
5041
 
    STRUCTURE   *psStruct;
5042
 
    UDWORD      inc, i;
 
4805
        STRUCTURE   *psStruct;
 
4806
        UDWORD      inc, i;
5043
4807
        STRUCTURE       *psList;
5044
4808
 
5045
 
    //see if any factory is currently using the template
 
4809
        //see if any factory is currently using the template
5046
4810
        for (i=0; i<2; i++)
5047
4811
        {
5048
4812
                psList = NULL;
5068
4832
                                        for (inc = 0; inc < MAX_PROD_RUN; inc++)
5069
4833
                                        {
5070
4834
                                                if (asProductionRun[psFactory->psAssemblyPoint->factoryType][
5071
 
                                                psFactory->psAssemblyPoint->factoryInc][inc].psTemplate == psTemplate)
 
4835
                                                        psFactory->psAssemblyPoint->factoryInc][inc].psTemplate == psTemplate)
5072
4836
                                                {
5073
4837
                                                        //if this is the template currently being worked on
5074
4838
                                                        if (psTemplate == (DROID_TEMPLATE *)psFactory->psSubject)
5133
4897
//
5134
4898
void SelectDroid(DROID *psDroid)
5135
4899
{
5136
 
        psDroid->selected = true;
5137
 
        intRefreshScreen();
 
4900
        // we shouldn't ever control the transporter in SP games
 
4901
        if (psDroid->droidType != DROID_TRANSPORTER || bMultiPlayer)
 
4902
        {
 
4903
                psDroid->selected = true;
 
4904
                intRefreshScreen();
 
4905
        }
5138
4906
}
5139
4907
 
5140
4908
 
5149
4917
/*calculate the power cost to repair a droid*/
5150
4918
UWORD powerReqForDroidRepair(DROID *psDroid)
5151
4919
{
5152
 
    UWORD   powerReq;//powerPercent;
5153
 
 
5154
 
    powerReq = (UWORD)(repairPowerPoint(psDroid) * (psDroid->originalBody - psDroid->body));
5155
 
 
5156
 
    return powerReq;
 
4920
        UWORD   powerReq;//powerPercent;
 
4921
 
 
4922
        powerReq = (UWORD)(repairPowerPoint(psDroid) * (psDroid->originalBody - psDroid->body));
 
4923
 
 
4924
        return powerReq;
5157
4925
}
5158
4926
 
5159
4927
/*power cost for One repair point*/
5160
4928
UWORD repairPowerPoint(DROID *psDroid)
5161
4929
{
5162
 
    return (UWORD)(((POWER_FACTOR * calcDroidPower(psDroid)) / psDroid->originalBody) *
5163
 
        REPAIR_POWER_FACTOR);
 
4930
        ASSERT( psDroid->originalBody != 0, "Droid's originalBody is 0!");
 
4931
 
 
4932
        // if the body is 0, then it shouldn't cost anything?
 
4933
        if( psDroid->originalBody == 0 )
 
4934
        {
 
4935
                return 0;
 
4936
        }
 
4937
        else
 
4938
        {
 
4939
                return (UWORD)(((POWER_FACTOR * calcDroidPower(psDroid)) / psDroid->originalBody) *
 
4940
                        REPAIR_POWER_FACTOR);
 
4941
        }
5164
4942
}
5165
4943
 
5166
4944
/** Callback function for stopped audio tracks
5174
4952
        psDroid = (DROID*)psObj;
5175
4953
        if (psDroid == NULL)
5176
4954
        {
5177
 
                debug( LOG_ERROR, "droidAudioTrackStopped: droid pointer invalid\n" );
 
4955
                debug( LOG_ERROR, "droid pointer invalid" );
5178
4956
                return false;
5179
4957
        }
5180
4958
 
5191
4969
/*returns true if droid type is one of the Cyborg types*/
5192
4970
BOOL cyborgDroid(const DROID* psDroid)
5193
4971
{
5194
 
    return (psDroid->droidType == DROID_CYBORG
5195
 
         || psDroid->droidType == DROID_CYBORG_CONSTRUCT
 
4972
        return (psDroid->droidType == DROID_CYBORG
 
4973
                 || psDroid->droidType == DROID_CYBORG_CONSTRUCT
5196
4974
         || psDroid->droidType == DROID_CYBORG_REPAIR
5197
4975
         || psDroid->droidType == DROID_CYBORG_SUPER);
5198
4976
}
5200
4978
BOOL droidOnMap(const DROID *psDroid)
5201
4979
{
5202
4980
        if (psDroid->died == NOT_CURRENT_LIST || psDroid->droidType == DROID_TRANSPORTER
5203
 
            || psDroid->sMove.fx == INVALID_XY || psDroid->pos.x == INVALID_XY || missionIsOffworld()
5204
 
            || mapHeight == 0)
 
4981
                || psDroid->sMove.fx == INVALID_XY || psDroid->pos.x == INVALID_XY || missionIsOffworld()
 
4982
                || mapHeight == 0)
5205
4983
        {
5206
4984
                // Off world or on a transport or is a transport or in mission list, or on a mission, or no map - ignore
5207
4985
                return true;
5208
4986
        }
5209
4987
        return (worldOnMap(psDroid->sMove.fx, psDroid->sMove.fy)
5210
 
                && worldOnMap(psDroid->pos.x, psDroid->pos.y));
 
4988
                        && worldOnMap(psDroid->pos.x, psDroid->pos.y));
5211
4989
}
5212
4990
 
5213
4991
/** Teleport a droid to a new position on the map */
5217
4995
        psDroid->pos.y = y;
5218
4996
        psDroid->pos.z = map_Height(psDroid->pos.x, psDroid->pos.y);
5219
4997
        initDroidMovement(psDroid);
5220
 
        visTilesUpdate((BASE_OBJECT *)psDroid);
 
4998
        visTilesUpdate((BASE_OBJECT *)psDroid, rayTerrainCallback);
 
4999
}
 
5000
 
 
5001
/** Check validity of a droid. Crash hard if it fails. */
 
5002
void checkDroid(const DROID *droid, const char *const location, const char *function, const int recurse)
 
5003
{
 
5004
        int i;
 
5005
 
 
5006
        if (recurse < 0)
 
5007
        {
 
5008
                return;
 
5009
        }
 
5010
 
 
5011
        ASSERT_HELPER(droid != NULL, location, function, "CHECK_DROID: NULL pointer");
 
5012
        ASSERT_HELPER(droid->type == OBJ_DROID, location, function, "CHECK_DROID: Not droid (type %d)", (int)droid->type);
 
5013
        ASSERT_HELPER(droid->direction <= 360.0f && droid->direction >= 0.0f, location, function, "CHECK_DROID: Bad droid direction %f", droid->direction);
 
5014
        ASSERT_HELPER(droid->numWeaps <= DROID_MAXWEAPS, location, function, "CHECK_DROID: Bad number of droid weapons %d", (int)droid->numWeaps);
 
5015
        ASSERT_HELPER(droid->listSize <= ORDER_LIST_MAX, location, function, "CHECK_DROID: Bad number of droid orders %d", (int)droid->listSize);
 
5016
        ASSERT_HELPER(droid->player < MAX_PLAYERS, location, function, "CHECK_DROID: Bad droid owner %d", (int)droid->player);
 
5017
        ASSERT_HELPER(droidOnMap(droid), location, function, "CHECK_DROID: Droid off map");
 
5018
        ASSERT_HELPER((!droid->psTarStats || ((STRUCTURE_STATS *)droid->psTarStats)->type != REF_DEMOLISH), location, function, "CHECK_DROID: Cannot build demolition");
 
5019
 
 
5020
        for (i = 0; i < DROID_MAXWEAPS; ++i)
 
5021
        {
 
5022
                ASSERT_HELPER(droid->asWeaps[i].rotation <= 360, location, function, "CHECK_DROID: Bad turret rotation of turret %u", i);
 
5023
                ASSERT_HELPER(droid->asWeaps[i].lastFired <= gameTime, location, function, "CHECK_DROID: Bad last fired time for turret %u", i);
 
5024
                if (droid->psActionTarget[i])
 
5025
                {
 
5026
                        ASSERT_HELPER(droid->psActionTarget[i]->direction >= 0.0f, location, function, "CHECK_DROID: Bad direction of turret %u's target", i);
 
5027
                }
 
5028
        }
5221
5029
}