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"
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"
211
211
destroyDroid(psDroid);
215
215
return relativeDamage;
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.
221
#define DROIDREF(func, line) "Illegal reference to droid from %s line %d", func, line
223
#define DROIDREF(func, line) "Illegal reference to droid"
219
225
BOOL droidCheckReferences(DROID *psVictimDroid)
223
229
for (plr = 0; plr < MAX_PLAYERS; plr++)
228
234
for (psStruct = apsStructLists[plr]; psStruct != NULL; psStruct = psStruct->psNext)
230
238
for (i = 0; i < psStruct->numWeaps; i++)
232
if ((DROID *)psStruct->psTarget[i] == psVictimDroid)
235
debug(LOG_ERROR, "droidCheckReferences: Illegal reference to droid");
237
ASSERT(!"Illegal reference to droid", "Illegal reference to droid from %s line %d",
238
psStruct->targetFunc[i], psStruct->targetLine[i]);
240
ASSERT_OR_RETURN(false, (DROID *)psStruct->psTarget[i] != psVictimDroid, DROIDREF(psStruct->targetFunc[i], psStruct->targetLine[i]));
244
243
for (psDroid = apsDroidLists[plr]; psDroid != NULL; psDroid = psDroid->psNext)
246
if ((DROID *)psDroid->psTarget == psVictimDroid && psVictimDroid != psDroid)
249
debug(LOG_ERROR, "droidCheckReferences: Illegal reference to droid");
251
ASSERT(!"Illegal reference to droid", "Illegal reference to droid from %s line %d",
252
psDroid->targetFunc, psDroid->targetLine);
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++)
258
if ((DROID *)psDroid->psActionTarget[i] == psVictimDroid && psVictimDroid != psDroid)
261
debug(LOG_ERROR, "droidCheckReferences: Illegal action reference to droid");
263
ASSERT(!"Illegal reference to droid", "Illegal action reference to droid from %s line %d",
264
psDroid->actionTargetFunc[i], psDroid->actionTargetLine[i]);
250
ASSERT_OR_RETURN(false, (DROID *)psDroid->psActionTarget[i] != psVictimDroid || psVictimDroid == psDroid,
251
DROIDREF(psDroid->actionTargetFunc[i], psDroid->actionTargetLine[i]));
274
259
/* droidRelease: release all resources associated with a droid -
275
260
* should only be called by objmem - use vanishDroid preferably
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" );
619
ASSERT_OR_RETURN( , psDroid != NULL, "invalid Unit pointer");
631
620
psDroid->psCurAnim = NULL;
633
622
debug(LOG_DEATH, "droidFlameFallCallback: Droid %d destroyed", (int)psDroid->id);
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" );
633
ASSERT_OR_RETURN( , psDroid != NULL, "invalid Unit pointer");
647
635
/* add falling anim */
648
636
psDroid->psCurAnim = animObj_Add((BASE_OBJECT *)psDroid, ID_ANIM_DROIDFLAMEFALL, 0, 1);
649
637
if (!psDroid->psCurAnim)
651
debug( LOG_ERROR, "unitBurntCallback: couldn't add fall over anim\n" );
639
debug( LOG_ERROR, "couldn't add fall over anim");
744
723
asDroidNaybors[pos].distSqr = distSqr;
748
ASSERT( numNaybors <= MAX_NAYBORS,
749
"addNaybor: numNaybors > MAX_NAYBORS" );
753
729
static DROID *CurrentNaybors = NULL;
754
730
static UDWORD nayborTime = 0;
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; \
764
if (xdiff > NAYBOR_RANGE) \
769
ydiff = dy - (SDWORD)psObj->pos.y; \
774
if (ydiff > NAYBOR_RANGE) \
779
distSqr = xdiff*xdiff + ydiff*ydiff; \
780
if (distSqr > NAYBOR_RANGE*NAYBOR_RANGE) \
786
732
/* Find all the objects close to the droid */
787
733
void droidGetNaybors(DROID *psDroid)
1205
1184
CHECK_DROID(psDroid);
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!");
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" );
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);
1221
debug( LOG_NEVER, "DACTION_BUILD: done\n");
1199
debug( LOG_NEVER, "DACTION_BUILD: done");
1222
1200
psDroid->action = DACTION_NONE;
1227
// For now wait until have enough power to build
1228
if (psStruct->currentPowerAccrued < (SWORD) structPowerToBuild(psStruct))
1230
psDroid->actionStarted = gameTime;
1205
// For now wait until have enough power to build
1206
if (psStruct->currentPowerAccrued < (SWORD) structPowerToBuild(psStruct))
1208
psDroid->actionStarted = gameTime;
1234
1212
constructPoints = constructorPoints(asConstructStats + psDroid->
1235
1213
asBits[COMP_CONSTRUCT].nStat, psDroid->player);
1300
1278
CHECK_DROID(psDroid);
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");
1308
1284
psDroid->actionStarted = gameTime;
1309
1285
psDroid->actionPoints = 0;
1335
1311
CHECK_DROID(psDroid);
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");
1343
1317
//constructPoints = (asConstructStats + psDroid->asBits[COMP_CONSTRUCT].nStat)->
1344
1318
// constructPoints;
1368
1342
if(psStruct->pStructureType->type == REF_POWER_GEN)
1370
//if had module attached - the base must have been completely built
1371
if (psStruct->pFunctionality->powerGenerator.capacity)
1373
//so add the power required to build the base struct
1374
addPower(psStruct->player, psStruct->pStructureType->powerToBuild);
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)
1347
//so add the power required to build the base struct
1348
addPower(psStruct->player, psStruct->pStructureType->powerToBuild);
1350
//add the currentAccruedPower since this may or may not be all required
1351
addPower(psStruct->player, psStruct->currentPowerAccrued);
1381
//if it had a module attached, need to add the power for the base struct as well
1382
if (StructIsFactory(psStruct))
1384
if (psStruct->pFunctionality->factory.capacity)
1386
//add half power for base struct
1387
addPower(psStruct->player, psStruct->pStructureType->
1389
//if large factory - add half power for one upgrade
1390
if (psStruct->pFunctionality->factory.capacity > SIZE_MEDIUM)
1392
addPower(psStruct->player, structPowerToBuild(psStruct) / 2);
1396
else if (psStruct->pStructureType->type == REF_RESEARCH)
1398
if (psStruct->pFunctionality->researchFacility.capacity)
1400
//add half power for base struct
1401
addPower(psStruct->player, psStruct->pStructureType->powerToBuild / 2);
1404
//add currentAccrued for the current layer of the structure
1405
addPower(psStruct->player, psStruct->currentPowerAccrued / 2);
1355
//if it had a module attached, need to add the power for the base struct as well
1356
if (StructIsFactory(psStruct))
1358
if (psStruct->pFunctionality->factory.capacity)
1360
//add half power for base struct
1361
addPower(psStruct->player, psStruct->pStructureType->
1363
//if large factory - add half power for one upgrade
1364
if (psStruct->pFunctionality->factory.capacity > SIZE_MEDIUM)
1366
addPower(psStruct->player, structPowerToBuild(psStruct) / 2);
1370
else if (psStruct->pStructureType->type == REF_RESEARCH)
1372
if (psStruct->pFunctionality->researchFacility.capacity)
1374
//add half power for base struct
1375
addPower(psStruct->player, psStruct->pStructureType->powerToBuild / 2);
1378
//add currentAccrued for the current layer of the structure
1379
addPower(psStruct->player, psStruct->currentPowerAccrued / 2);
1407
1381
/* remove structure and foundation */
1408
1382
removeStruct( psStruct, true );
1410
1384
/* reset target stats*/
1411
psDroid->psTarStats = NULL;
1385
psDroid->psTarStats = NULL;
1417
1391
addConstructorEffect(psStruct);
1430
1404
CHECK_DROID(psDroid);
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");
1440
1411
psDroid->actionStarted = gameTime;
1441
1412
psDroid->actionPoints = 0;
1455
1426
CHECK_DROID(psDroid);
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");
1465
1433
if (psFeature->body > 0)
1564
1530
CHECK_DROID(psDroid);
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");
1572
1536
psDroid->actionStarted = gameTime;
1573
1537
psDroid->actionPoints = 0;
1587
1551
CHECK_DROID(psDroid);
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");
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");
1600
1560
psStats = asWeaponStats + psDroid->asWeaps[0].nStat;
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");
1605
1564
restorePoints = calcDamage(weaponDamage(psStats, psDroid->player),
1606
1565
psStats->weaponEffect,(BASE_OBJECT *)psStruct);
1714
1673
CHECK_DROID(psDroid);
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" );
1678
ASSERT_OR_RETURN(false, psStruct->type == OBJ_STRUCTURE, "target is not a structure");
1722
1680
iRepairPoints = constructorPoints(asConstructStats + psDroid->
1723
1681
asBits[COMP_CONSTRUCT].nStat, psDroid->player);
1753
1711
CHECK_DROID(psRepairDroid);
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");
1760
1716
psDroidToRepair = (DROID *)psRepairDroid->psActionTarget[0];
1761
ASSERT( psDroidToRepair->type == OBJ_DROID,
1762
"unitUpdateUnitRepair: target is not a unit" );
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)
1772
//check if enough power to do any
1773
powerCost = powerReqForDroidRepair(psDroidToRepair);
1774
if (powerCost > psDroidToRepair->powerAccrued)
1776
powerCost = (powerCost - psDroidToRepair->powerAccrued) / POWER_FACTOR;
1785
//if the power cost is 0 (due to rounding) then do for free!
1788
if (!psDroidToRepair->powerAccrued)
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");
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)
1727
//check if enough power to do any
1728
powerCost = powerReqForDroidRepair(psDroidToRepair);
1729
if (powerCost > psDroidToRepair->powerAccrued)
1731
powerCost = (powerCost - psDroidToRepair->powerAccrued) / POWER_FACTOR;
1740
//if the power cost is 0 (due to rounding) then do for free!
1743
if (!psDroidToRepair->powerAccrued)
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;
1748
//init so repair points to add won't be huge when start up again
1749
psRepairDroid->actionPoints = 0;
1799
1754
iRepairPoints = repairPoints(asRepairStats + psRepairDroid->
1800
1755
asBits[COMP_REPAIRUNIT].nStat, psRepairDroid->player);
1811
1766
GAME_TICKS_PER_SEC;
1814
iPointsToAdd -= psRepairDroid->actionPoints;
1769
iPointsToAdd -= psRepairDroid->actionPoints;
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))
1822
//anothe HACK but sorts out all the rounding errors when values get small
1823
psDroidToRepair->body += iPointsToAdd;
1824
psRepairDroid->actionPoints += iPointsToAdd;
1828
//see if we have enough power to do this amount of repair
1829
powerCost = iPointsToAdd * repairPowerPoint(psDroidToRepair);
1830
if (powerCost <= psDroidToRepair->powerAccrued)
1832
psDroidToRepair->body += iPointsToAdd;
1833
psRepairDroid->actionPoints += iPointsToAdd;
1834
//subtract the power cost for these points
1835
psDroidToRepair->powerAccrued -= powerCost;
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;
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))
1777
//anothe HACK but sorts out all the rounding errors when values get small
1778
psDroidToRepair->body += iPointsToAdd;
1779
psRepairDroid->actionPoints += iPointsToAdd;
1783
//see if we have enough power to do this amount of repair
1784
powerCost = iPointsToAdd * repairPowerPoint(psDroidToRepair);
1785
if (powerCost <= psDroidToRepair->powerAccrued)
1787
psDroidToRepair->body += iPointsToAdd;
1788
psRepairDroid->actionPoints += iPointsToAdd;
1789
//subtract the power cost for these points
1790
psDroidToRepair->powerAccrued -= powerCost;
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;
1847
1802
/* add plasma repair effect whilst being repaired */
1848
1803
if ((ONEINFIVE) && (psDroidToRepair->visible[selectedPlayer]))
2673
2613
UDWORD calcDroidBaseSpeed(DROID_TEMPLATE *psTemplate, UDWORD weight, UBYTE player)
2676
//Watermelon:engine output bonus? 150%
2616
//engine output bonus? 150%
2677
2617
float eoBonus = 1.5f;
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)
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;
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;
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)
2700
2640
if ((asBodyStats + psTemplate->asParts[COMP_BODY])->size == SIZE_HEAVY)
3229
3165
//check the factory can cope with this sized body
3230
3166
if (!((asBodyStats + psCurr->asParts[COMP_BODY])->size > iCapacity) )
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)
3240
3176
if ( apCompLists[psFactory->player][COMP_BODY]
3241
3177
[psCurr->asParts[COMP_BODY]] != AVAILABLE )
3474
3410
* calculate muzzle tip location in 3d world
3476
BOOL calcDroidMuzzleLocation(DROID *psDroid, Vector3i *muzzle, int weapon_slot)
3412
BOOL calcDroidMuzzleLocation(DROID *psDroid, Vector3f *muzzle, int weapon_slot)
3479
iIMDShape *psShape, *psWeapon, *psWeaponMount;
3414
iIMDShape *psShape, *psWeaponImd;
3481
3416
CHECK_DROID(psDroid);
3483
psShape = BODY_IMD(psDroid,psDroid->player);
3418
psShape = BODY_IMD(psDroid, psDroid->player);
3419
psWeaponImd = (asWeaponStats[psDroid->asWeaps[weapon_slot].nStat]).pIMD;
3485
psWeapon = (asWeaponStats[psDroid->asWeaps[weapon_slot].nStat]).pIMD;
3486
psWeaponMount = (asWeaponStats[psDroid->asWeaps[weapon_slot].nStat]).pMountGraphic;
3487
3421
if(psShape && psShape->nconnectors)
3423
Vector3f barrel = {0.0f, 0.0f, 0.0f};
3489
3425
pie_MatBegin();
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);
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
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));
3503
3441
//matrix = the muzzle mount on turret
3504
if( psWeapon && psWeapon->nconnectors )
3506
barrel.x = psWeapon->connectors->x;
3507
barrel.y = -psWeapon->connectors->y;
3508
barrel.z = -psWeapon->connectors->z;
3442
if( psWeaponImd && psWeaponImd->nconnectors )
3444
barrel = Vector3f_Init(psWeaponImd->connectors->x, -psWeaponImd->connectors->y, -psWeaponImd->connectors->z);
3517
pie_RotateTranslate3iv(&barrel, muzzle);
3447
pie_RotateTranslate3f(&barrel, muzzle);
3518
3448
muzzle->z = -muzzle->z;
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);
3529
3457
CHECK_DROID(psDroid);
3677
3606
{512, 2048, N_("Hero")}
3680
UDWORD getDroidLevel(DROID *psDroid)
3609
unsigned int getDroidLevel(const DROID* psDroid)
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;
3688
3616
// Commanders don't need as much kills for ranks in multiplayer
3689
if (isCommander && cmdGetDroidMultiExpBoost())
3617
if (isCommander && cmdGetDroidMultiExpBoost() && bMultiPlayer)
3731
3660
const char *getDroidNameForRank(UDWORD rank)
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)) );
3736
3665
return PE_("rank", arrRank[rank].name);
3781
3710
// returns true when no droid on x,y square.
3782
3711
BOOL noDroid(UDWORD x, UDWORD y)
3786
3715
// check each droid list
3787
for(i=0;i<MAX_PLAYERS;i++)
3716
for (i = 0; i < MAX_PLAYERS; ++i)
3789
for(pD = apsDroidLists[i]; pD ; pD= pD->psNext)
3718
const DROID* psDroid;
3719
for (psDroid = apsDroidLists[i]; psDroid; psDroid = psDroid->psNext)
3791
if (map_coord(pD->pos.x) == x)
3721
if (map_coord(psDroid->pos.x) == x
3722
&& map_coord(psDroid->pos.y) == y)
3793
if (map_coord(pD->pos.y) == y)
3843
3767
if((y < TOO_NEAR_EDGE) || (y > (SDWORD)(mapHeight - TOO_NEAR_EDGE)))
3846
//check no features there
3770
// check no features there
3847
3771
if(TileHasFeature(mapTile(x,y)))
3852
3776
// not on a blocking tile.
3853
if( fpathBlockingTile(x,y) )
3777
if (fpathBlockingTile(x, y, propulsion))
3856
3782
// shouldn't next to more than one blocking tile, to avoid windy paths.
3857
if( fpathBlockingTile(x-1 ,y-1) )
3859
if( fpathBlockingTile(x,y-1) )
3861
if( fpathBlockingTile(x+1,y-1) )
3863
if( fpathBlockingTile(x-1,y) )
3865
if( fpathBlockingTile(x+1,y) )
3867
if( fpathBlockingTile(x-1,y+1) )
3869
if( fpathBlockingTile(x,y+1) )
3871
if( fpathBlockingTile(x+1,y+1) )
3783
if (fpathBlockingTile(x - 1, y - 1, propulsion))
3785
if (fpathBlockingTile(x, y - 1, propulsion))
3787
if (fpathBlockingTile(x + 1, y - 1, propulsion))
3789
if (fpathBlockingTile(x - 1, y, propulsion))
3791
if (fpathBlockingTile(x + 1, y, propulsion))
3793
if (fpathBlockingTile(x -1, y + 1, propulsion))
3795
if (fpathBlockingTile(x, y + 1, propulsion))
3797
if (fpathBlockingTile(x +1, y + 1, propulsion))
3881
// ------------------------------------------------------------------------------------
3882
BOOL normalPAT(UDWORD x, UDWORD y)
3884
if(sensiblePlace(x,y) && noDroid(x,y))
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)
3897
if (sensiblePlace(x,y) && noDroid(x,y) && gwZoneReachable(gwGetZone(x,y)))
3907
// ------------------------------------------------------------------------------------
3810
return sensiblePlace(x, y, PROPULSION_TYPE_WHEELED) && noDroid(x,y);
3813
static BOOL canFitDroid(UDWORD x, UDWORD y)
3815
return sensiblePlace(x, y, PROPULSION_TYPE_WHEELED) && (noDroid(x,y) || oneDroid(x, y));
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))
3912
SDWORD startX,endX,startY,endY;
3916
ASSERT( *x<mapWidth,"x coordinate is off-map for pickATileGen" );
3917
ASSERT( *y<mapHeight,"y coordinate is off-map for pickATileGen" );
3919
/* Exit if they're fine! */
3920
if(sensiblePlace(*x,*y) && noDroid(*x,*y))
3925
/* Initial box dimensions and set iteration count to zero */
3926
startX = endX = *x; startY = endY = *y; passes = 0;
3928
/* Keep going until we get a tile or we exceed distance */
3929
while(passes<numIterations)
3931
/* Process whole box */
3932
for(i = startX; i <= endX; i++)
3934
for(j = startY; j<= endY; j++)
3936
/* Test only perimeter as internal tested previous iteration */
3937
if(i==startX || i==endX || j==startY || j==endY)
3942
/* Set exit conditions and get out NOW */
3949
/* Expand the box out in all directions - off map handled by tileAcceptable */
3950
startX--; startY--; endX++; endY++; passes++;
3952
/* If we got this far, then we failed - passed in values will be unchanged */
3822
return pickATileGenThreat(x, y, numIterations, -1, -1, function);
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))
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" );
3969
3837
if(function(*x,*y) && ((threatRange <=0) || (!ThreatInRange(player, threatRange, *x, *y, false)))) //TODO: vtol check really not needed?
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)
4012
SDWORD startX,endX,startY,endY;
4015
ASSERT( *x<mapWidth,"x coordinate is off-map for pickATile" );
4016
ASSERT( *y<mapHeight,"y coordinate is off-map for pickATile" );
4018
/* Exit if they're fine! */
4019
if(sensiblePlace(*x,*y) && noDroid(*x,*y))
4024
/* Initial box dimensions and set iteration count to zero */
4025
startX = endX = *x; startY = endY = *y; passes = 0;
4027
/* Keep going until we get a tile or we exceed distance */
4028
while(passes<numIterations)
4030
/* Process whole box */
4031
for(i = startX; i <= endX; i++)
4033
for(j = startY; j<= endY; j++)
4035
/* Test only perimeter as internal tested previous iteration */
4036
if(i==startX || i==endX || j==startY || j==endY)
4039
if(sensiblePlace(i,j) && noDroid(i,j))
4041
/* Set exit conditions and get out NOW */
4048
/* Expand the box out in all directions - off map handled by tileAcceptable */
4049
startX--; startY--; endX++; endY++; passes++;
4051
/* If we got this far, then we failed - passed in values will be unchanged */
3877
return pickATileGen(x, y, numIterations, zonedPAT);
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)
4059
SDWORD startX,endX,startY,endY;
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))
4071
/* Exit if they're fine! */
4072
if (sensiblePlace(*x, *y) && oneDroid(*x, *y))
4074
return HALF_FREE_TILE;
4077
/* Initial box dimensions and set iteration count to zero */
4078
startX = endX = *x; startY = endY = *y; passes = 0;
4082
/* Keep going until we get a tile or we exceed distance */
4083
while(passes<numIterations)
4085
/* Process whole box */
4086
for(i = startX; i <= endX; i++)
4088
for(j = startY; j<= endY; j++)
4090
/* Test only perimeter as internal tested previous iteration */
4091
if(i==startX || i==endX || j==startY || j==endY)
4094
if(sensiblePlace(i,j) && oneDroid(i,j))
4096
/* Set exit conditions and get out NOW */
4098
return HALF_FREE_TILE;
4103
/* Expand the box out in all directions - off map handled by tileAcceptable */
4104
startX--; startY--; endX++; endY++; passes++;
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);
4110
3886
/* Looks through the players list of droids to see if any of them are
4153
3929
BOOL order = false;
4156
ASSERT(psStruct != NULL, "buildModule: Invalid structure pointer");
3932
ASSERT(psStruct != NULL && psStruct->pStructureType != NULL, "Invalid structure pointer");
3933
if (!psStruct || !psStruct->pStructureType)
4158
3938
switch (psStruct->pStructureType->type)
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)
4165
3945
i = powerModuleStat;
4269
4049
return getTemplateName(&sTemplate);
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)
4278
const char *pNameID = psTemplate->aName;
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))
4285
//get the string from the id
4286
const char *pName = strresGetString(psStringRes, id);
4292
//if haven't found a resource, return the name passed in
4054
return psTemplate->aName;
4296
4057
/* Just returns true if the droid's present body points aren't as high as the original*/
4310
4071
BOOL getDroidResourceName(char *pName)
4073
/* See if the name has a string resource associated with it by trying
4074
* to get the string resource.
4076
const char * const name = strresGetString(psStringRes, pName);
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))
4317
debug( LOG_ERROR, "Unable to find string resource for %s", pName );
4080
debug(LOG_ERROR, "Unable to find string resource for string with ID \"%s\"", pName);
4321
//get the string from the id
4322
strcpy(pName, strresGetString(psStringRes, id));
4084
// Copy the retrieved string into the output parameter
4085
strcpy(pName, name);
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)
4576
UWORD numAttackRuns;
4578
ASSERT(isVtolDroid(psDroid), "not a VTOL Droid");
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)
4584
numAttackRuns = (UWORD)(asWeaponStats[psDroid->asWeaps[weapon_slot].nStat].numRounds *
4585
asWeaponStats[psDroid->asWeaps[weapon_slot].nStat].vtolAttackRuns);
4589
numAttackRuns = asWeaponStats[psDroid->asWeaps[weapon_slot].nStat].vtolAttackRuns;
4592
return numAttackRuns;
4339
UWORD numAttackRuns;
4341
ASSERT_OR_RETURN(0, isVtolDroid(psDroid), "not a VTOL Droid");
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)
4347
numAttackRuns = (UWORD)(asWeaponStats[psDroid->asWeaps[weapon_slot].nStat].numRounds *
4348
asWeaponStats[psDroid->asWeaps[weapon_slot].nStat].vtolAttackRuns);
4352
numAttackRuns = asWeaponStats[psDroid->asWeaps[weapon_slot].nStat].vtolAttackRuns;
4355
return numAttackRuns;
4595
4358
/*Checks a vtol for being fully armed and fully repaired to see if ready to
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)
4640
if (isVtolDroid(psDroid))
4403
if (isVtolDroid(psDroid))
4642
4405
if (psDroid->numWeaps > 0)
4644
4407
if (asWeaponStats[psDroid->asWeaps[weapon_slot].nStat].vtolAttackRuns > 0)
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");
4654
4417
/*this mends the VTOL when it has been returned to home base whilst on an
4678
4441
//assign rearmPad to the VTOL
4679
4442
void assignVTOLPad(DROID *psNewDroid, STRUCTURE *psReArmPad)
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" );
4686
4448
setDroidBase(psNewDroid, psReArmPad);
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)
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)
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))
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)
4959
4718
/*calculates the electronic resistance of a droid based on its experience level*/
4960
4719
SWORD droidResistance(DROID *psDroid)
4964
4723
CHECK_DROID(psDroid);
4966
resistance = (SWORD)(psDroid->experience * DROID_RESISTANCE_FACTOR);
4968
//ensure base minimum in MP before the upgrade effect
4971
//ensure resistance is a base minimum
4972
if (resistance < DROID_RESISTANCE_FACTOR)
4974
resistance = DROID_RESISTANCE_FACTOR;
4978
//structure resistance upgrades are passed on to droids
4979
resistance = (SWORD)(resistance + resistance * (
4980
asStructureUpgrade[psDroid->player].resistance/100));
4982
//ensure resistance is a base minimum
4983
if (resistance < DROID_RESISTANCE_FACTOR)
4985
resistance = DROID_RESISTANCE_FACTOR;
4725
resistance = (SWORD)(psDroid->experience * DROID_RESISTANCE_FACTOR);
4727
//ensure base minimum in MP before the upgrade effect
4730
//ensure resistance is a base minimum
4731
if (resistance < DROID_RESISTANCE_FACTOR)
4733
resistance = DROID_RESISTANCE_FACTOR;
4737
//structure resistance upgrades are passed on to droids
4738
resistance = (SWORD)(resistance + resistance * (
4739
asStructureUpgrade[psDroid->player].resistance/100));
4741
//ensure resistance is a base minimum
4742
if (resistance < DROID_RESISTANCE_FACTOR)
4744
resistance = DROID_RESISTANCE_FACTOR;
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.
4756
// FIXME: This routine is a mess
4997
4757
BOOL checkValidWeaponForProp(DROID_TEMPLATE *psTemplate)
4999
4759
PROPULSION_STATS *psPropStats;
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" );
4765
ASSERT_OR_RETURN(false, psPropStats != NULL, "invalid propulsion stats pointer");
4767
// if there are no weapons, then don't even bother continuing
4768
if (psTemplate->numWeaps == 0)
4773
// FIXME -- why are we checking vtolAttackRuns on non AIR units?
5006
4774
if (asPropulsionTypes[psPropStats->propulsionType].travel == AIR)
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)
5012
4780
bValid = false;
5149
4917
/*calculate the power cost to repair a droid*/
5150
4918
UWORD powerReqForDroidRepair(DROID *psDroid)
5152
UWORD powerReq;//powerPercent;
5154
powerReq = (UWORD)(repairPowerPoint(psDroid) * (psDroid->originalBody - psDroid->body));
4920
UWORD powerReq;//powerPercent;
4922
powerReq = (UWORD)(repairPowerPoint(psDroid) * (psDroid->originalBody - psDroid->body));
5159
4927
/*power cost for One repair point*/
5160
4928
UWORD repairPowerPoint(DROID *psDroid)
5162
return (UWORD)(((POWER_FACTOR * calcDroidPower(psDroid)) / psDroid->originalBody) *
5163
REPAIR_POWER_FACTOR);
4930
ASSERT( psDroid->originalBody != 0, "Droid's originalBody is 0!");
4932
// if the body is 0, then it shouldn't cost anything?
4933
if( psDroid->originalBody == 0 )
4939
return (UWORD)(((POWER_FACTOR * calcDroidPower(psDroid)) / psDroid->originalBody) *
4940
REPAIR_POWER_FACTOR);
5166
4944
/** Callback function for stopped audio tracks
5200
4978
BOOL droidOnMap(const DROID *psDroid)
5202
4980
if (psDroid->died == NOT_CURRENT_LIST || psDroid->droidType == DROID_TRANSPORTER
5203
|| psDroid->sMove.fx == INVALID_XY || psDroid->pos.x == INVALID_XY || missionIsOffworld()
4981
|| psDroid->sMove.fx == INVALID_XY || psDroid->pos.x == INVALID_XY || missionIsOffworld()
5206
4984
// Off world or on a transport or is a transport or in mission list, or on a mission, or no map - ignore
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));
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);
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)
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");
5020
for (i = 0; i < DROID_MAXWEAPS; ++i)
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])
5026
ASSERT_HELPER(droid->psActionTarget[i]->direction >= 0.0f, location, function, "CHECK_DROID: Bad direction of turret %u's target", i);