DayZ 1.24
Loading...
Searching...
No Matches
BaseBuildingBase.c File Reference

Go to the source code of this file.

Classes

class  ItemBase
 

Functions

class BaseBuildingBase extends ItemBase bsbDebugPrint (string s)
 
void BaseBuildingBase ()
 
override void EEDelete (EntityAI parent)
 
override string GetInvulnerabilityTypeString ()
 
override bool CanObstruct ()
 
override int GetHideIconMask ()
 
void SynchronizeBaseState ()
 
override void OnVariablesSynchronized ()
 
void OnSynchronizedClient ()
 
void RegisterPartForSync (int part_id)
 
void UnregisterPartForSync (int part_id)
 
bool IsPartBuildInSyncData (int part_id)
 
void RegisterActionForSync (int part_id, int action_id)
 
void ResetActionSyncData ()
 
void SetActionFromSyncData ()
 
void SetPartFromSyncData (ConstructionPart part)
 
void SetPartsFromSyncData ()
 
ConstructionPart GetConstructionPartById (int id)
 
bool HasBase ()
 
void SetBaseState (bool has_base)
 
override bool IsDeployable ()
 
bool IsOpened ()
 
ItemBase CreateConstructionKit ()
 
void CreateConstructionKitInHands (notnull PlayerBase player)
 
vector GetKitSpawnPosition ()
 
string GetConstructionKitType ()
 
void DestroyConstructionKit (ItemBase construction_kit)
 
void DestroyConstruction ()
 
override void OnStoreSave (ParamsWriteContext ctx)
 
override bool OnStoreLoad (ParamsReadContext ctx, int version)
 
override void AfterStoreLoad ()
 
void SetPartsAfterStoreLoad ()
 
override void EEHealthLevelChanged (int oldLevel, int newLevel, string zone)
 
override void EEOnAfterLoad ()
 
override void EEInit ()
 
override void EEItemAttached (EntityAI item, string slot_name)
 
override void EEItemDetached (EntityAI item, string slot_name)
 
void OnSetSlotLock (int slotId, bool locked, bool was_locked)
 
override bool IgnoreOutOfReachCondition ()
 
void OnPartBuiltServer (notnull Man player, string part_name, int action_id)
 
void OnPartBuiltClient (string part_name, int action_id)
 
void OnPartDismantledServer (notnull Man player, string part_name, int action_id)
 
void OnPartDismantledClient (string part_name, int action_id)
 
void OnPartDestroyedServer (Man player, string part_name, int action_id, bool destroyed_by_connected_part=false)
 
void OnPartDestroyedClient (string part_name, int action_id)
 
void InitBaseState ()
 
void InitVisuals ()
 
void UpdateVisuals ()
 
void UpdateAttachmentVisuals (string slot_name, bool is_locked)
 
void UpdatePhysics ()
 
void UpdateAttachmentPhysics (string slot_name, bool is_locked)
 
void UpdateNavmesh ()
 
override bool CanUseConstruction ()
 
override bool CanUseConstructionBuild ()
 
bool IsAttachmentSlotLocked (EntityAI attachment)
 
bool IsAttachmentSlotLocked (string slot_name)
 
void GetAttachmentSlots (EntityAI entity, out array< string > attachment_slots)
 
bool CheckSlotVerticalDistance (int slot_id, PlayerBase player)
 
bool CheckMemoryPointVerticalDistance (float max_dist, string selection, PlayerBase player)
 
bool CheckLevelVerticalDistance (float max_dist, string selection, PlayerBase player)
 
void ConstructionInit ()
 
Construction GetConstruction ()
 
override bool CanReceiveAttachment (EntityAI attachment, int slotId)
 
bool HasAttachmentsBesidesBase ()
 
override bool ShowZonesHealth ()
 
override bool CanPutInCargo (EntityAI parent)
 
override bool CanRemoveFromCargo (EntityAI parent)
 
override bool CanPutIntoHands (EntityAI parent)
 
override bool IsFacingPlayer (PlayerBase player, string selection)
 
override bool IsPlayerInside (PlayerBase player, string selection)
 
bool MustBeBuiltFromOutside ()
 Some buildings can only be built from outside.
 
bool IsFacingCamera (string selection)
 
bool PerformRoofCheckForBase (string partName, PlayerBase player, out bool result)
 
bool HasProperDistance (string selection, PlayerBase player)
 
bool CanFoldBaseBuildingObject ()
 
ItemBase FoldBaseBuildingObject ()
 
void CreateAreaDamage (string slot_name, float rotation_angle=0)
 
void CalcDamageAreaRotation (float angle_deg, out vector center, out vector orientation)
 
void DestroyAreaDamage (string slot_name)
 
override bool IsIgnoredByConstruction ()
 
void SoundBuildStart (string part_name)
 
void SoundDismantleStart (string part_name)
 
void SoundDestroyStart (string part_name)
 
string GetBuildSoundByMaterial (string part_name)
 
string GetDismantleSoundByMaterial (string part_name)
 
void CheckForHybridAttachments (EntityAI item, string slot_name)
 
override int GetDamageSystemVersionChange ()
 
override void SetActions ()
 
void DebugCustomState ()
 
array< stringOnDebugSpawnBuildExcludes ()
 Excludes certain parts from being built by OnDebugSpawn, uses Contains to compare.
 
override void OnDebugSpawn ()
 
void FullyBuild ()
 
void bsbDebugSpam (string s)
 

Variables

const string ANIMATION_DEPLOYED = "Deployed"
 
float m_ConstructionKitHealth
 
ref Construction m_Construction
 
bool m_HasBase
 
int m_SyncParts01
 
int m_SyncParts02
 
int m_SyncParts03
 
int m_InteractedPartId
 
int m_PerformedActionId
 
const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet"
 
const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet"
 
const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet"
 
const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet"
 
const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet"
 
const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet"
 
const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet"
 
const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet"
 
const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet"
 
const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet"
 
EffectSound m_Sound
 
ref map< string, ref AreaDamageManagerm_DamageTriggers
 
ref array< stringm_HybridAttachments
 
ref array< stringm_Mountables
 

Function Documentation

◆ AfterStoreLoad()

override void bsbDebugPrint::AfterStoreLoad ( )
protected

Definition at line 1599 of file BaseBuildingBase.c.

◆ BaseBuildingBase()

void bsbDebugPrint::BaseBuildingBase ( )
protected

Definition at line 1212 of file BaseBuildingBase.c.

1214{
1215 const string ANIMATION_DEPLOYED = "Deployed";
1216
1217 float m_ConstructionKitHealth; //stored health value for used construction kit
1218
1220
1221 bool m_HasBase;
1222 //variables for synchronization of base building parts (2x31 is the current limit)
1223 int m_SyncParts01; //synchronization for already built parts (31 parts)
1224 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1225 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1226 int m_InteractedPartId; //construction part id that an action was performed on
1227 int m_PerformedActionId; //action id that was performed on a construction part
1228
1229 //Sounds
1230 //build
1231 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1232 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1233 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1234 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1235 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1236 //dismantle
1237 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1238 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1239 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1240 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1241 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1242
1243 protected EffectSound m_Sound;
1244
1248
1249 // Constructor
1250 void BaseBuildingBase()
1251 {
1253
1254 //synchronized variables
1255 RegisterNetSyncVariableInt("m_SyncParts01");
1256 RegisterNetSyncVariableInt("m_SyncParts02");
1257 RegisterNetSyncVariableInt("m_SyncParts03");
1258 RegisterNetSyncVariableInt("m_InteractedPartId");
1259 RegisterNetSyncVariableInt("m_PerformedActionId");
1260 RegisterNetSyncVariableBool("m_HasBase");
1261
1262 //Construction init
1264
1265 if (ConfigIsExisting("hybridAttachments"))
1266 {
1268 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1269 }
1270 if (ConfigIsExisting("mountables"))
1271 {
1273 ConfigGetTextArray("mountables", m_Mountables);
1274 }
1275
1276 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1277 }
1278
1279 override void EEDelete(EntityAI parent)
1280 {
1281 super.EEDelete(parent);
1282
1283 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1285
1286 }
1287
1288 override string GetInvulnerabilityTypeString()
1289 {
1290 return "disableBaseDamage";
1291 }
1292
1293 override bool CanObstruct()
1294 {
1295 return true;
1296 }
1297
1298 override int GetHideIconMask()
1299 {
1300 return EInventoryIconVisibility.HIDE_VICINITY;
1301 }
1302
1303 // --- SYNCHRONIZATION
1305 {
1306 if (GetGame().IsServer())
1307 SetSynchDirty();
1308 }
1309
1310 override void OnVariablesSynchronized()
1311 {
1312 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1313 super.OnVariablesSynchronized();
1314
1315 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1316 }
1317
1318 protected void OnSynchronizedClient()
1319 {
1320 //update parts
1322
1323 //update action on part
1325
1326 //update visuals (client)
1327 UpdateVisuals();
1328 }
1329
1330 //parts synchronization
1332 {
1333 //part_id must starts from index = 1
1334 int offset;
1335 int mask;
1336
1337 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1338 {
1339 offset = part_id - 1;
1340 mask = 1 << offset;
1341
1343 }
1344 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1345 {
1346 offset = (part_id % 32);
1347 mask = 1 << offset;
1348
1350 }
1351 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1352 {
1353 offset = (part_id % 63);
1354 mask = 1 << offset;
1355
1357 }
1358 }
1359
1361 {
1362 //part_id must starts from index = 1
1363 int offset;
1364 int mask;
1365
1366 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1367 {
1368 offset = part_id - 1;
1369 mask = 1 << offset;
1370
1372 }
1373 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1374 {
1375 offset = (part_id % 32);
1376 mask = 1 << offset;
1377
1379 }
1380 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1381 {
1382 offset = (part_id % 63);
1383 mask = 1 << offset;
1384
1386 }
1387 }
1388
1390 {
1391 //part_id must starts from index = 1
1392 int offset;
1393 int mask;
1394
1395 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1396 {
1397 offset = part_id - 1;
1398 mask = 1 << offset;
1399
1400 if ((m_SyncParts01 & mask) > 0)
1401 return true;
1402 }
1403 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1404 {
1405 offset = (part_id % 32);
1406 mask = 1 << offset;
1407
1408 if ((m_SyncParts02 & mask) > 0)
1409 return true;
1410 }
1411 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1412 {
1413 offset = (part_id % 63);
1414 mask = 1 << offset;
1415
1416 if ((m_SyncParts03 & mask) > 0)
1417 return true;
1418 }
1419
1420 return false;
1421 }
1422
1423 protected void RegisterActionForSync(int part_id, int action_id)
1424 {
1427 }
1428
1429 protected void ResetActionSyncData()
1430 {
1431 //reset data
1432 m_InteractedPartId = -1;
1434 }
1435
1436 protected void SetActionFromSyncData()
1437 {
1438 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1439 {
1442
1443 switch (build_action_id)
1444 {
1448 }
1449 }
1450 }
1451 //------
1452
1454 {
1455 string key = part.m_PartName;
1456 bool is_base = part.IsBase();
1458 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1460 {
1461 if (!part.IsBuilt())
1462 {
1463 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1464 GetConstruction().AddToConstructedParts(key);
1465 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1466
1467 if (is_base)
1468 {
1470 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1471 }
1472 }
1473 }
1474 else
1475 {
1476 if (part.IsBuilt())
1477 {
1478 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1479 GetConstruction().RemoveFromConstructedParts(key);
1480 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1481
1482 if (is_base)
1483 {
1485 AddProxyPhysics(ANIMATION_DEPLOYED);
1486 }
1487 }
1488 }
1489
1490 //check slot lock for material attachments
1491 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1492 }
1493
1494 //set construction parts based on synchronized data
1496 {
1499
1500 for (int i = 0; i < construction_parts.Count(); ++i)
1501 {
1502 string key = construction_parts.GetKey(i);
1505 }
1506
1507 //regenerate navmesh
1508 UpdateNavmesh();
1509 }
1510
1512 {
1515
1516 for (int i = 0; i < construction_parts.Count(); ++i)
1517 {
1518 string key = construction_parts.GetKey(i);
1520
1521 if (value.GetId() == id)
1522 return value;
1523 }
1524
1525 return NULL;
1526 }
1527 //
1528
1529 //Base
1530 bool HasBase()
1531 {
1532 return m_HasBase;
1533 }
1534
1535 void SetBaseState(bool has_base)
1536 {
1538 }
1539
1540 override bool IsDeployable()
1541 {
1542 return true;
1543 }
1544
1545 bool IsOpened()
1546 {
1547 return false;
1548 }
1549
1550 //--- CONSTRUCTION KIT
1552 {
1556
1557 return construction_kit;
1558 }
1559
1561 {
1562 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1565 }
1566
1567 protected vector GetKitSpawnPosition()
1568 {
1569 return GetPosition();
1570 }
1571
1572 protected string GetConstructionKitType()
1573 {
1574 return "";
1575 }
1576
1578 {
1580 GetGame().ObjectDelete(construction_kit);
1581 }
1582
1583 //--- CONSTRUCTION
1584 void DestroyConstruction()
1585 {
1586 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1587 GetGame().ObjectDelete(this);
1588 }
1589
1590 // --- EVENTS
1591 override void OnStoreSave(ParamsWriteContext ctx)
1592 {
1593 super.OnStoreSave(ctx);
1594
1595 //sync parts 01
1596 ctx.Write(m_SyncParts01);
1597 ctx.Write(m_SyncParts02);
1598 ctx.Write(m_SyncParts03);
1599
1600 ctx.Write(m_HasBase);
1601 }
1602
1603 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1604 {
1605 if (!super.OnStoreLoad(ctx, version))
1606 return false;
1607
1608 //--- Base building data ---
1609 //Restore synced parts data
1610 if (!ctx.Read(m_SyncParts01))
1611 {
1612 m_SyncParts01 = 0; //set default
1613 return false;
1614 }
1615 if (!ctx.Read(m_SyncParts02))
1616 {
1617 m_SyncParts02 = 0; //set default
1618 return false;
1619 }
1620 if (!ctx.Read(m_SyncParts03))
1621 {
1622 m_SyncParts03 = 0; //set default
1623 return false;
1624 }
1625
1626 //has base
1627 if (!ctx.Read(m_HasBase))
1628 {
1629 m_HasBase = false;
1630 return false;
1631 }
1632 //---
1633
1634 return true;
1635 }
1636
1637 override void AfterStoreLoad()
1638 {
1639 super.AfterStoreLoad();
1640
1643 }
1644
1646 {
1647 //update server data
1649
1650 //set base state
1651 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1652 SetBaseState(construction_part.IsBuilt()) ;
1653
1654 //synchronize after load
1656 }
1657
1658 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1659 {
1661 return;
1662
1663 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1664
1665 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1666 return;
1667
1669 string part_name = zone;
1670 part_name.ToLower();
1671
1673 {
1675
1676 if (construction_part && construction.IsPartConstructed(part_name))
1677 {
1678 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1679 construction.DestroyConnectedParts(part_name);
1680 }
1681
1682 //barbed wire handling (hack-ish)
1683 if (part_name.Contains("barbed"))
1684 {
1685 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1686 if (barbed_wire)
1687 barbed_wire.SetMountedState(false);
1688 }
1689 }
1690 }
1691
1692 override void EEOnAfterLoad()
1693 {
1695 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1696
1697 super.EEOnAfterLoad();
1698 }
1699
1700 override void EEInit()
1701 {
1702 super.EEInit();
1703
1704 // init visuals and physics
1705 InitBaseState();
1706
1707 //debug
1708#ifdef DEVELOPER
1710#endif
1711 }
1712
1713 override void EEItemAttached(EntityAI item, string slot_name)
1714 {
1715 super.EEItemAttached(item, slot_name);
1716
1718 UpdateVisuals();
1720 }
1721
1722 override void EEItemDetached(EntityAI item, string slot_name)
1723 {
1724 super.EEItemDetached(item, slot_name);
1725
1726 UpdateVisuals();
1728 }
1729
1730 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
1731 {
1733 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
1734
1737 }
1738
1739 //ignore out of reach condition
1740 override bool IgnoreOutOfReachCondition()
1741 {
1742 return true;
1743 }
1744
1745 //CONSTRUCTION EVENTS
1746 //Build
1747 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
1748 {
1750
1751 //check base state
1752 if (construtionPart.IsBase())
1753 {
1754 SetBaseState(true);
1755
1756 //spawn kit
1758 }
1759
1760 //register constructed parts for synchronization
1762
1763 //register action that was performed on part
1765
1766 //synchronize
1768
1769 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
1770
1771 UpdateNavmesh();
1772
1773 //update visuals
1774 UpdateVisuals();
1775
1776 //reset action sync data
1777 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1778 }
1779
1780 void OnPartBuiltClient(string part_name, int action_id)
1781 {
1782 //play sound
1784 }
1785
1786 //Dismantle
1788 {
1789 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
1791
1792 //register constructed parts for synchronization
1794
1795 //register action that was performed on part
1797
1798 //synchronize
1800
1801 // server part of sync, client will be synced from SetPartsFromSyncData
1803
1804 UpdateNavmesh();
1805
1806 //update visuals
1807 UpdateVisuals();
1808
1809 //reset action sync data
1810 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1811
1812 //check base state
1813 if (construtionPart.IsBase())
1814 {
1815 //Destroy construction
1816 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1817 }
1818 }
1819
1821 {
1822 //play sound
1824 }
1825
1826 //Destroy
1828 {
1829 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
1831
1832 //register constructed parts for synchronization
1834
1835 //register action that was performed on part
1837
1838 //synchronize
1840
1841 // server part of sync, client will be synced from SetPartsFromSyncData
1843
1844 UpdateNavmesh();
1845
1846 //update visuals
1847 UpdateVisuals();
1848
1849 //reset action sync data
1850 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1851
1852 //check base state
1853 if (construtionPart.IsBase())
1854 {
1855 //Destroy construction
1856 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1857 }
1858 }
1859
1860 void OnPartDestroyedClient(string part_name, int action_id)
1861 {
1862 //play sound
1864 }
1865
1866 // --- UPDATE
1867 void InitBaseState()
1868 {
1869 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
1870
1871 InitVisuals();
1872 UpdateNavmesh(); //regenerate navmesh
1873 GetConstruction().InitBaseState();
1874 }
1875
1876 void InitVisuals()
1877 {
1878 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
1879 //check base
1880 if (!HasBase())
1881 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1882 else
1883 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1884
1885 GetConstruction().UpdateVisuals();
1886 }
1887
1888 void UpdateVisuals()
1889 {
1891
1893 foreach (string slotName : attachmentSlots)
1895
1896 //check base
1897 if (!HasBase())
1898 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1899 else
1900 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1901
1902 GetConstruction().UpdateVisuals();
1903 }
1904
1906 {
1907 string slotNameMounted = slot_name + "_Mounted";
1908 EntityAI attachment = FindAttachmentBySlotName(slot_name);
1909
1910 if (attachment)
1911 {
1912 BarbedWire barbedWire = BarbedWire.Cast(attachment);
1913 if (barbedWire && barbedWire.IsMounted())
1915 else
1917
1918 if (is_locked)
1919 {
1920 SetAnimationPhase(slotNameMounted, 0);
1921 SetAnimationPhase(slot_name, 1);
1922 }
1923 else
1924 {
1925 SetAnimationPhase(slotNameMounted, 1);
1926 SetAnimationPhase(slot_name, 0);
1927 }
1928 }
1929 else
1930 {
1931 SetAnimationPhase(slotNameMounted, 1);
1932 SetAnimationPhase(slot_name, 1);
1933
1935 }
1936 }
1937
1938 // avoid calling this function on frequent occasions, it's a massive performance hit
1939 void UpdatePhysics()
1940 {
1942 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
1943
1946
1948 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
1949
1950 foreach (string slotName : attachmentSlots)
1952
1953 //check base
1954 if (!HasBase())
1955 {
1957 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
1958
1959 AddProxyPhysics(ANIMATION_DEPLOYED);
1960 }
1961 else
1962 {
1964 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
1965
1966 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1967 }
1968
1969 GetConstruction().UpdatePhysics();
1970 UpdateNavmesh();
1971 }
1972
1974 {
1975 //checks for invalid appends; hotfix
1976 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
1977 return;
1978 //----------------------------------
1979 string slot_name_mounted = slot_name + "_Mounted";
1980 EntityAI attachment = FindAttachmentBySlotName(slot_name);
1981
1982 //remove proxy physics
1983 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
1984 RemoveProxyPhysics(slot_name_mounted);
1985 RemoveProxyPhysics(slot_name);
1986
1987 if (attachment)
1988 {
1989 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
1990 if (is_locked)
1991 {
1992 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
1993 AddProxyPhysics(slot_name_mounted);
1994 }
1995 else
1996 {
1997 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
1998 AddProxyPhysics(slot_name);
1999 }
2000 }
2001 }
2002
2003 protected void UpdateNavmesh()
2004 {
2005 SetAffectPathgraph(true, false);
2006 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2007 }
2008
2009 override bool CanUseConstruction()
2010 {
2011 return true;
2012 }
2013
2014 override bool CanUseConstructionBuild()
2015 {
2016 return true;
2017 }
2018
2020 {
2021 if (attachment)
2022 {
2024 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2025
2026 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2027 }
2028
2029 return false;
2030 }
2031
2032 protected bool IsAttachmentSlotLocked(string slot_name)
2033 {
2034 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2035 }
2036
2037 //--- ATTACHMENT SLOTS
2039 {
2040 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2041 if (GetGame().ConfigIsExisting(config_path))
2042 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2043 }
2044
2046 {
2047 return true;
2048 }
2049
2050 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2051 {
2052 return true;
2053 }
2054
2055 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2056 {
2057 return true;
2058 }
2059
2060 // --- INIT
2061 void ConstructionInit()
2062 {
2063 if (!m_Construction)
2064 m_Construction = new Construction(this);
2065
2066 GetConstruction().Init();
2067 }
2068
2070 {
2071 return m_Construction;
2072 }
2073
2074 //--- INVENTORY/ATTACHMENTS CONDITIONS
2075 //attachments
2077 {
2078 return super.CanReceiveAttachment(attachment, slotId);
2079 }
2080
2082 {
2083 int attachment_count = GetInventory().AttachmentCount();
2084 if (attachment_count > 0)
2085 {
2086 if (HasBase() && attachment_count == 1)
2087 return false;
2088
2089 return true;
2090 }
2091
2092 return false;
2093 }
2094
2095 override bool ShowZonesHealth()
2096 {
2097 return true;
2098 }
2099
2100 //this into/outo parent.Cargo
2101 override bool CanPutInCargo(EntityAI parent)
2102 {
2103 return false;
2104 }
2105
2106 override bool CanRemoveFromCargo(EntityAI parent)
2107 {
2108 return false;
2109 }
2110
2111 //hands
2112 override bool CanPutIntoHands(EntityAI parent)
2113 {
2114 return false;
2115 }
2116
2117 //--- ACTION CONDITIONS
2118 //direction
2119 override bool IsFacingPlayer(PlayerBase player, string selection)
2120 {
2121 return true;
2122 }
2123
2124 override bool IsPlayerInside(PlayerBase player, string selection)
2125 {
2126 return true;
2127 }
2128
2131 {
2132 return false;
2133 }
2134
2135 //camera direction check
2136 bool IsFacingCamera(string selection)
2137 {
2138 return true;
2139 }
2140
2141 //roof check
2143 {
2144 return false;
2145 }
2146
2147 //selection->player distance check
2148 bool HasProperDistance(string selection, PlayerBase player)
2149 {
2150 return true;
2151 }
2152
2153 //folding
2155 {
2156 if (HasBase() || GetInventory().AttachmentCount() > 0)
2157 return false;
2158
2159 return true;
2160 }
2161
2163 {
2166
2167 return item;
2168 }
2169
2170 //Damage triggers (barbed wire)
2171 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2172 {
2173 if (GetGame() && GetGame().IsServer())
2174 {
2175 //destroy area damage if some already exists
2177
2178 //create new area damage
2180 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2181
2182 vector min_max[2];
2183 if (MemoryPointExists(slot_name + "_min"))
2184 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2185 if (MemoryPointExists(slot_name + "_max"))
2186 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2187
2188 //get proper trigger extents (min<max)
2189 vector extents[2];
2190 GetConstruction().GetTriggerExtents(min_max, extents);
2191
2192 //get box center
2193 vector center;
2194 center = GetConstruction().GetBoxCenter(min_max);
2195 center = ModelToWorld(center);
2196
2197 //rotate center if needed
2200
2201 areaDamage.SetExtents(extents[0], extents[1]);
2202 areaDamage.SetAreaPosition(center);
2203 areaDamage.SetAreaOrientation(orientation);
2204 areaDamage.SetLoopInterval(1.0);
2205 areaDamage.SetDeferDuration(0.2);
2206 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2207 areaDamage.SetAmmoName("BarbedWireHit");
2208 areaDamage.Spawn();
2209
2211 }
2212 }
2213
2215 {
2216 if (angle_deg != 0)
2217 {
2218 //orientation
2220
2221 //center
2223 if (MemoryPointExists("rotate_axis"))
2224 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2227 center[0] = r_center_x;
2228 center[2] = r_center_z;
2229 }
2230 }
2231
2232 void DestroyAreaDamage(string slot_name)
2233 {
2234 if (GetGame() && GetGame().IsServer())
2235 {
2238 {
2239 if (areaDamage)
2240 areaDamage.Destroy();
2241
2243 }
2244 }
2245 }
2246
2247 override bool IsIgnoredByConstruction()
2248 {
2249 return true;
2250 }
2251
2252 //================================================================
2253 // SOUNDS
2254 //================================================================
2255 protected void SoundBuildStart(string part_name)
2256 {
2257 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2258 }
2259
2260 protected void SoundDismantleStart(string part_name)
2261 {
2262 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2263 }
2264
2265 protected void SoundDestroyStart(string part_name)
2266 {
2267 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2268 }
2269
2270 protected string GetBuildSoundByMaterial(string part_name)
2271 {
2273
2274 switch (material_type)
2275 {
2276 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2277 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2278 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2279 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2280 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2281 }
2282
2283 return "";
2284 }
2285
2286 protected string GetDismantleSoundByMaterial(string part_name)
2287 {
2289
2290 switch (material_type)
2291 {
2292 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2293 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2294 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2295 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2296 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2297 }
2298
2299 return "";
2300 }
2301
2302 //misc
2304 {
2305 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2306 {
2307 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2309 SetHealth(slot_name, "Health", item.GetHealth());
2310 }
2311 }
2312
2313 override int GetDamageSystemVersionChange()
2314 {
2315 return 111;
2316 }
2317
2318 override void SetActions()
2319 {
2320 super.SetActions();
2321
2323 //AddAction(ActionTakeHybridAttachment);
2324 //AddAction(ActionTakeHybridAttachmentToHands);
2327 }
2328
2329 //================================================================
2330 // DEBUG
2331 //================================================================
2332 protected void DebugCustomState()
2333 {
2334 }
2335
2338 {
2339 return null;
2340 }
2341
2342 override void OnDebugSpawn()
2343 {
2344 FullyBuild();
2345 }
2346
2347 void FullyBuild()
2348 {
2350 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2351
2352 Man p;
2353
2354#ifdef SERVER
2356 GetGame().GetWorld().GetPlayerList(players);
2357 if (players.Count())
2358 p = players[0];
2359#else
2360 p = GetGame().GetPlayer();
2361#endif
2362
2363 foreach (ConstructionPart part : parts)
2364 {
2365 bool excluded = false;
2366 string partName = part.GetPartName();
2367 if (excludes)
2368 {
2369 foreach (string exclude : excludes)
2370 {
2371 if (partName.Contains(exclude))
2372 {
2373 excluded = true;
2374 break;
2375 }
2376 }
2377 }
2378
2379 if (!excluded)
2381 }
2382
2383 GetConstruction().UpdateVisuals();
2384 }
2385}
2386
2387void bsbDebugPrint(string s)
2388{
2389#ifdef BSB_DEBUG
2390 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2391#else
2392 //Print("" + s); // comment/uncomment to hide/see debug logs
2393#endif
2394}
2395void bsbDebugSpam(string s)
2396{
2397#ifdef BSB_DEBUG_SPAM
2398 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2399#else
2400 //Print("" + s); // comment/uncomment to hide/see debug logs
2401#endif
2402}
const int AT_DISMANTLE_PART
Definition _constants.c:7
const int AT_DESTROY_PART
Definition _constants.c:8
const int AT_BUILD_PART
Definition _constants.c:6
void AddAction(typename actionName)
void RemoveAction(typename actionName)
vector GetOrientation()
void OnPartDismantledClient(string part_name, int action_id)
const string ANIMATION_DEPLOYED
override int GetHideIconMask()
override void OnStoreSave(ParamsWriteContext ctx)
void FullyBuild()
void UpdateVisuals()
void SetActionFromSyncData()
int m_SyncParts01
void UpdateNavmesh()
bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
override void OnDebugSpawn()
array< string > OnDebugSpawnBuildExcludes()
Excludes certain parts from being built by OnDebugSpawn, uses Contains to compare.
void SoundDestroyStart(string part_name)
const string SOUND_BUILD_WOOD_LOG
void SoundBuildStart(string part_name)
void UpdateAttachmentVisuals(string slot_name, bool is_locked)
void CalcDamageAreaRotation(float angle_deg, out vector center, out vector orientation)
override bool CanObstruct()
bool HasAttachmentsBesidesBase()
void RegisterPartForSync(int part_id)
const string SOUND_DISMANTLE_METAL
void InitBaseState()
override bool CanReceiveAttachment(EntityAI attachment, int slotId)
void BaseBuildingBase()
void SetPartsAfterStoreLoad()
void DestroyConstruction()
ref array< string > m_HybridAttachments
int m_InteractedPartId
bool m_HasBase
string GetDismantleSoundByMaterial(string part_name)
bool IsAttachmentSlotLocked(EntityAI attachment)
void UpdatePhysics()
override bool IsFacingPlayer(PlayerBase player, string selection)
void OnPartBuiltClient(string part_name, int action_id)
bool HasProperDistance(string selection, PlayerBase player)
void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
void RegisterActionForSync(int part_id, int action_id)
const string SOUND_BUILD_WOOD_PLANK
void SetPartsFromSyncData()
ref array< string > m_Mountables
const string SOUND_BUILD_WIRE
int m_SyncParts03
void ResetActionSyncData()
override string GetInvulnerabilityTypeString()
void OnPartDismantledServer(notnull Man player, string part_name, int action_id)
void CheckForHybridAttachments(EntityAI item, string slot_name)
override void EEOnAfterLoad()
override void EEItemDetached(EntityAI item, string slot_name)
vector GetKitSpawnPosition()
ref Construction m_Construction
bool IsFacingCamera(string selection)
bool CheckSlotVerticalDistance(int slot_id, PlayerBase player)
void GetAttachmentSlots(EntityAI entity, out array< string > attachment_slots)
override bool IsDeployable()
void OnPartDestroyedClient(string part_name, int action_id)
override void EEInit()
void UnregisterPartForSync(int part_id)
void bsbDebugSpam(string s)
override void OnVariablesSynchronized()
bool PerformRoofCheckForBase(string partName, PlayerBase player, out bool result)
Construction GetConstruction()
void SoundDismantleStart(string part_name)
void DebugCustomState()
ConstructionPart GetConstructionPartById(int id)
string GetConstructionKitType()
bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
EffectSound m_Sound
void OnPartDestroyedServer(Man player, string part_name, int action_id, bool destroyed_by_connected_part=false)
bool CanFoldBaseBuildingObject()
override int GetDamageSystemVersionChange()
const string SOUND_DISMANTLE_WOOD_PLANK
override void EEItemAttached(EntityAI item, string slot_name)
const string SOUND_DISMANTLE_WOOD_LOG
void InitVisuals()
class BaseBuildingBase extends ItemBase bsbDebugPrint(string s)
const string SOUND_BUILD_WOOD_STAIRS
ItemBase FoldBaseBuildingObject()
override bool CanPutInCargo(EntityAI parent)
override bool CanUseConstruction()
override void AfterStoreLoad()
void SetPartFromSyncData(ConstructionPart part)
override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
override bool IgnoreOutOfReachCondition()
override bool OnStoreLoad(ParamsReadContext ctx, int version)
override bool ShowZonesHealth()
void UpdateAttachmentPhysics(string slot_name, bool is_locked)
override bool IsPlayerInside(PlayerBase player, string selection)
string GetBuildSoundByMaterial(string part_name)
ItemBase CreateConstructionKit()
const string SOUND_DISMANTLE_WIRE
bool MustBeBuiltFromOutside()
Some buildings can only be built from outside.
float m_ConstructionKitHealth
override void EEDelete(EntityAI parent)
override void SetActions()
void DestroyConstructionKit(ItemBase construction_kit)
void CreateConstructionKitInHands(notnull PlayerBase player)
bool IsOpened()
int m_PerformedActionId
override bool CanUseConstructionBuild()
bool HasBase()
void OnSynchronizedClient()
void SynchronizeBaseState()
override bool IsIgnoredByConstruction()
ref map< string, ref AreaDamageManager > m_DamageTriggers
void SetBaseState(bool has_base)
const string SOUND_BUILD_METAL
void OnSetSlotLock(int slotId, bool locked, bool was_locked)
override bool CanPutIntoHands(EntityAI parent)
bool IsPartBuildInSyncData(int part_id)
override bool CanRemoveFromCargo(EntityAI parent)
void ConstructionInit()
int m_SyncParts02
const string SOUND_DISMANTLE_WOOD_STAIRS
const int ECE_PLACE_ON_SURFACE
PlayerSpawnPreset slotName
ConstructionMaterialType
Definition Construction.c:2
void Construction(BaseBuildingBase parent)
void DestroyAreaDamage()
void CreateAreaDamage()
bool m_FixDamageSystemInit
Definition ItemBase.c:4740
class JsonUndergroundAreaTriggerData GetPosition
A particular version of the deferred loop used to not damage players inside vehicles.
Wrapper class for managing sound through SEffectManager.
Definition EffectSound.c:5
InventoryLocation.
provides access to slot configuration
static proto native owned string GetSlotName(int id)
converts slot_id to string
static proto native int GetSlotIdFromString(string slot_name)
converts string to slot_id
static bool IsBaseBuildingLogEnable()
Definition Debug.c:779
Definition EnMath.c:7
Serialization general interface. Serializer API works with:
Definition Serializer.c:56
override string GetDebugName()
proto native CGame GetGame()
proto void PrintToRPT(void var)
Prints content of variable to RPT file (performance warning - each write means fflush!...
proto native void Destroy()
Cleans up the Effect, including unregistering if needed.
Definition Effect.c:207
const int STATE_RUINED
Definition constants.c:757
static proto float Cos(float angle)
Returns cosinus of angle in radians.
static proto float Sin(float angle)
Returns sinus of angle in radians.
static const float DEG2RAD
Definition EnMath.c:17
const int CALL_CATEGORY_GAMEPLAY
Definition tools.c:10
const int CALL_CATEGORY_SYSTEM
Definition tools.c:8

◆ bsbDebugPrint()

◆ bsbDebugSpam()

void bsbDebugSpam ( string s)

Definition at line 1184 of file BaseBuildingBase.c.

1185{
1186#ifdef BSB_DEBUG_SPAM
1187 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
1188#else
1189 //Print("" + s); // comment/uncomment to hide/see debug logs
1190#endif
1191}

Referenced by ItemBase::SetPartFromSyncData().

◆ CalcDamageAreaRotation()

void bsbDebugPrint::CalcDamageAreaRotation ( float angle_deg,
out vector center,
out vector orientation )
protected

Definition at line 2176 of file BaseBuildingBase.c.

2178{
2179 const string ANIMATION_DEPLOYED = "Deployed";
2180
2181 float m_ConstructionKitHealth; //stored health value for used construction kit
2182
2184
2185 bool m_HasBase;
2186 //variables for synchronization of base building parts (2x31 is the current limit)
2187 int m_SyncParts01; //synchronization for already built parts (31 parts)
2188 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2189 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2190 int m_InteractedPartId; //construction part id that an action was performed on
2191 int m_PerformedActionId; //action id that was performed on a construction part
2192
2193 //Sounds
2194 //build
2195 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2196 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2197 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2198 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2199 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2200 //dismantle
2201 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2202 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2203 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2204 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2205 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2206
2207 protected EffectSound m_Sound;
2208
2212
2213 // Constructor
2214 void BaseBuildingBase()
2215 {
2217
2218 //synchronized variables
2219 RegisterNetSyncVariableInt("m_SyncParts01");
2220 RegisterNetSyncVariableInt("m_SyncParts02");
2221 RegisterNetSyncVariableInt("m_SyncParts03");
2222 RegisterNetSyncVariableInt("m_InteractedPartId");
2223 RegisterNetSyncVariableInt("m_PerformedActionId");
2224 RegisterNetSyncVariableBool("m_HasBase");
2225
2226 //Construction init
2228
2229 if (ConfigIsExisting("hybridAttachments"))
2230 {
2232 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2233 }
2234 if (ConfigIsExisting("mountables"))
2235 {
2237 ConfigGetTextArray("mountables", m_Mountables);
2238 }
2239
2240 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2241 }
2242
2243 override void EEDelete(EntityAI parent)
2244 {
2245 super.EEDelete(parent);
2246
2247 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2249
2250 }
2251
2252 override string GetInvulnerabilityTypeString()
2253 {
2254 return "disableBaseDamage";
2255 }
2256
2257 override bool CanObstruct()
2258 {
2259 return true;
2260 }
2261
2262 override int GetHideIconMask()
2263 {
2264 return EInventoryIconVisibility.HIDE_VICINITY;
2265 }
2266
2267 // --- SYNCHRONIZATION
2269 {
2270 if (GetGame().IsServer())
2271 SetSynchDirty();
2272 }
2273
2274 override void OnVariablesSynchronized()
2275 {
2276 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2277 super.OnVariablesSynchronized();
2278
2279 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2280 }
2281
2282 protected void OnSynchronizedClient()
2283 {
2284 //update parts
2286
2287 //update action on part
2289
2290 //update visuals (client)
2291 UpdateVisuals();
2292 }
2293
2294 //parts synchronization
2296 {
2297 //part_id must starts from index = 1
2298 int offset;
2299 int mask;
2300
2301 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2302 {
2303 offset = part_id - 1;
2304 mask = 1 << offset;
2305
2307 }
2308 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2309 {
2310 offset = (part_id % 32);
2311 mask = 1 << offset;
2312
2314 }
2315 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2316 {
2317 offset = (part_id % 63);
2318 mask = 1 << offset;
2319
2321 }
2322 }
2323
2325 {
2326 //part_id must starts from index = 1
2327 int offset;
2328 int mask;
2329
2330 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2331 {
2332 offset = part_id - 1;
2333 mask = 1 << offset;
2334
2336 }
2337 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2338 {
2339 offset = (part_id % 32);
2340 mask = 1 << offset;
2341
2343 }
2344 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2345 {
2346 offset = (part_id % 63);
2347 mask = 1 << offset;
2348
2350 }
2351 }
2352
2354 {
2355 //part_id must starts from index = 1
2356 int offset;
2357 int mask;
2358
2359 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2360 {
2361 offset = part_id - 1;
2362 mask = 1 << offset;
2363
2364 if ((m_SyncParts01 & mask) > 0)
2365 return true;
2366 }
2367 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2368 {
2369 offset = (part_id % 32);
2370 mask = 1 << offset;
2371
2372 if ((m_SyncParts02 & mask) > 0)
2373 return true;
2374 }
2375 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2376 {
2377 offset = (part_id % 63);
2378 mask = 1 << offset;
2379
2380 if ((m_SyncParts03 & mask) > 0)
2381 return true;
2382 }
2383
2384 return false;
2385 }
2386
2387 protected void RegisterActionForSync(int part_id, int action_id)
2388 {
2391 }
2392
2393 protected void ResetActionSyncData()
2394 {
2395 //reset data
2396 m_InteractedPartId = -1;
2398 }
2399
2400 protected void SetActionFromSyncData()
2401 {
2402 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2403 {
2406
2407 switch (build_action_id)
2408 {
2412 }
2413 }
2414 }
2415 //------
2416
2418 {
2419 string key = part.m_PartName;
2420 bool is_base = part.IsBase();
2422 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2424 {
2425 if (!part.IsBuilt())
2426 {
2427 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2428 GetConstruction().AddToConstructedParts(key);
2429 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2430
2431 if (is_base)
2432 {
2434 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2435 }
2436 }
2437 }
2438 else
2439 {
2440 if (part.IsBuilt())
2441 {
2442 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2443 GetConstruction().RemoveFromConstructedParts(key);
2444 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2445
2446 if (is_base)
2447 {
2449 AddProxyPhysics(ANIMATION_DEPLOYED);
2450 }
2451 }
2452 }
2453
2454 //check slot lock for material attachments
2455 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2456 }
2457
2458 //set construction parts based on synchronized data
2460 {
2463
2464 for (int i = 0; i < construction_parts.Count(); ++i)
2465 {
2466 string key = construction_parts.GetKey(i);
2469 }
2470
2471 //regenerate navmesh
2472 UpdateNavmesh();
2473 }
2474
2476 {
2479
2480 for (int i = 0; i < construction_parts.Count(); ++i)
2481 {
2482 string key = construction_parts.GetKey(i);
2484
2485 if (value.GetId() == id)
2486 return value;
2487 }
2488
2489 return NULL;
2490 }
2491 //
2492
2493 //Base
2494 bool HasBase()
2495 {
2496 return m_HasBase;
2497 }
2498
2499 void SetBaseState(bool has_base)
2500 {
2502 }
2503
2504 override bool IsDeployable()
2505 {
2506 return true;
2507 }
2508
2509 bool IsOpened()
2510 {
2511 return false;
2512 }
2513
2514 //--- CONSTRUCTION KIT
2516 {
2520
2521 return construction_kit;
2522 }
2523
2525 {
2526 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2529 }
2530
2531 protected vector GetKitSpawnPosition()
2532 {
2533 return GetPosition();
2534 }
2535
2536 protected string GetConstructionKitType()
2537 {
2538 return "";
2539 }
2540
2542 {
2544 GetGame().ObjectDelete(construction_kit);
2545 }
2546
2547 //--- CONSTRUCTION
2548 void DestroyConstruction()
2549 {
2550 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2551 GetGame().ObjectDelete(this);
2552 }
2553
2554 // --- EVENTS
2555 override void OnStoreSave(ParamsWriteContext ctx)
2556 {
2557 super.OnStoreSave(ctx);
2558
2559 //sync parts 01
2560 ctx.Write(m_SyncParts01);
2561 ctx.Write(m_SyncParts02);
2562 ctx.Write(m_SyncParts03);
2563
2564 ctx.Write(m_HasBase);
2565 }
2566
2567 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2568 {
2569 if (!super.OnStoreLoad(ctx, version))
2570 return false;
2571
2572 //--- Base building data ---
2573 //Restore synced parts data
2574 if (!ctx.Read(m_SyncParts01))
2575 {
2576 m_SyncParts01 = 0; //set default
2577 return false;
2578 }
2579 if (!ctx.Read(m_SyncParts02))
2580 {
2581 m_SyncParts02 = 0; //set default
2582 return false;
2583 }
2584 if (!ctx.Read(m_SyncParts03))
2585 {
2586 m_SyncParts03 = 0; //set default
2587 return false;
2588 }
2589
2590 //has base
2591 if (!ctx.Read(m_HasBase))
2592 {
2593 m_HasBase = false;
2594 return false;
2595 }
2596 //---
2597
2598 return true;
2599 }
2600
2601 override void AfterStoreLoad()
2602 {
2603 super.AfterStoreLoad();
2604
2607 }
2608
2610 {
2611 //update server data
2613
2614 //set base state
2615 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2616 SetBaseState(construction_part.IsBuilt()) ;
2617
2618 //synchronize after load
2620 }
2621
2622 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2623 {
2625 return;
2626
2627 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2628
2629 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2630 return;
2631
2633 string part_name = zone;
2634 part_name.ToLower();
2635
2637 {
2639
2640 if (construction_part && construction.IsPartConstructed(part_name))
2641 {
2642 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2643 construction.DestroyConnectedParts(part_name);
2644 }
2645
2646 //barbed wire handling (hack-ish)
2647 if (part_name.Contains("barbed"))
2648 {
2649 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2650 if (barbed_wire)
2651 barbed_wire.SetMountedState(false);
2652 }
2653 }
2654 }
2655
2656 override void EEOnAfterLoad()
2657 {
2659 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2660
2661 super.EEOnAfterLoad();
2662 }
2663
2664 override void EEInit()
2665 {
2666 super.EEInit();
2667
2668 // init visuals and physics
2669 InitBaseState();
2670
2671 //debug
2672#ifdef DEVELOPER
2674#endif
2675 }
2676
2677 override void EEItemAttached(EntityAI item, string slot_name)
2678 {
2679 super.EEItemAttached(item, slot_name);
2680
2682 UpdateVisuals();
2684 }
2685
2686 override void EEItemDetached(EntityAI item, string slot_name)
2687 {
2688 super.EEItemDetached(item, slot_name);
2689
2690 UpdateVisuals();
2692 }
2693
2694 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2695 {
2697 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2698
2701 }
2702
2703 //ignore out of reach condition
2704 override bool IgnoreOutOfReachCondition()
2705 {
2706 return true;
2707 }
2708
2709 //CONSTRUCTION EVENTS
2710 //Build
2711 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2712 {
2714
2715 //check base state
2716 if (construtionPart.IsBase())
2717 {
2718 SetBaseState(true);
2719
2720 //spawn kit
2722 }
2723
2724 //register constructed parts for synchronization
2726
2727 //register action that was performed on part
2729
2730 //synchronize
2732
2733 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2734
2735 UpdateNavmesh();
2736
2737 //update visuals
2738 UpdateVisuals();
2739
2740 //reset action sync data
2741 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2742 }
2743
2744 void OnPartBuiltClient(string part_name, int action_id)
2745 {
2746 //play sound
2748 }
2749
2750 //Dismantle
2752 {
2753 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2755
2756 //register constructed parts for synchronization
2758
2759 //register action that was performed on part
2761
2762 //synchronize
2764
2765 // server part of sync, client will be synced from SetPartsFromSyncData
2767
2768 UpdateNavmesh();
2769
2770 //update visuals
2771 UpdateVisuals();
2772
2773 //reset action sync data
2774 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2775
2776 //check base state
2777 if (construtionPart.IsBase())
2778 {
2779 //Destroy construction
2780 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2781 }
2782 }
2783
2785 {
2786 //play sound
2788 }
2789
2790 //Destroy
2792 {
2793 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2795
2796 //register constructed parts for synchronization
2798
2799 //register action that was performed on part
2801
2802 //synchronize
2804
2805 // server part of sync, client will be synced from SetPartsFromSyncData
2807
2808 UpdateNavmesh();
2809
2810 //update visuals
2811 UpdateVisuals();
2812
2813 //reset action sync data
2814 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2815
2816 //check base state
2817 if (construtionPart.IsBase())
2818 {
2819 //Destroy construction
2820 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2821 }
2822 }
2823
2824 void OnPartDestroyedClient(string part_name, int action_id)
2825 {
2826 //play sound
2828 }
2829
2830 // --- UPDATE
2831 void InitBaseState()
2832 {
2833 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2834
2835 InitVisuals();
2836 UpdateNavmesh(); //regenerate navmesh
2837 GetConstruction().InitBaseState();
2838 }
2839
2840 void InitVisuals()
2841 {
2842 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2843 //check base
2844 if (!HasBase())
2845 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2846 else
2847 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2848
2849 GetConstruction().UpdateVisuals();
2850 }
2851
2852 void UpdateVisuals()
2853 {
2855
2857 foreach (string slotName : attachmentSlots)
2859
2860 //check base
2861 if (!HasBase())
2862 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2863 else
2864 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2865
2866 GetConstruction().UpdateVisuals();
2867 }
2868
2870 {
2871 string slotNameMounted = slot_name + "_Mounted";
2872 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2873
2874 if (attachment)
2875 {
2876 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2877 if (barbedWire && barbedWire.IsMounted())
2879 else
2881
2882 if (is_locked)
2883 {
2884 SetAnimationPhase(slotNameMounted, 0);
2885 SetAnimationPhase(slot_name, 1);
2886 }
2887 else
2888 {
2889 SetAnimationPhase(slotNameMounted, 1);
2890 SetAnimationPhase(slot_name, 0);
2891 }
2892 }
2893 else
2894 {
2895 SetAnimationPhase(slotNameMounted, 1);
2896 SetAnimationPhase(slot_name, 1);
2897
2899 }
2900 }
2901
2902 // avoid calling this function on frequent occasions, it's a massive performance hit
2903 void UpdatePhysics()
2904 {
2906 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2907
2910
2912 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2913
2914 foreach (string slotName : attachmentSlots)
2916
2917 //check base
2918 if (!HasBase())
2919 {
2921 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2922
2923 AddProxyPhysics(ANIMATION_DEPLOYED);
2924 }
2925 else
2926 {
2928 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2929
2930 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2931 }
2932
2933 GetConstruction().UpdatePhysics();
2934 UpdateNavmesh();
2935 }
2936
2938 {
2939 //checks for invalid appends; hotfix
2940 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2941 return;
2942 //----------------------------------
2943 string slot_name_mounted = slot_name + "_Mounted";
2944 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2945
2946 //remove proxy physics
2947 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2948 RemoveProxyPhysics(slot_name_mounted);
2949 RemoveProxyPhysics(slot_name);
2950
2951 if (attachment)
2952 {
2953 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2954 if (is_locked)
2955 {
2956 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2957 AddProxyPhysics(slot_name_mounted);
2958 }
2959 else
2960 {
2961 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2962 AddProxyPhysics(slot_name);
2963 }
2964 }
2965 }
2966
2967 protected void UpdateNavmesh()
2968 {
2969 SetAffectPathgraph(true, false);
2970 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2971 }
2972
2973 override bool CanUseConstruction()
2974 {
2975 return true;
2976 }
2977
2978 override bool CanUseConstructionBuild()
2979 {
2980 return true;
2981 }
2982
2984 {
2985 if (attachment)
2986 {
2988 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2989
2990 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2991 }
2992
2993 return false;
2994 }
2995
2996 protected bool IsAttachmentSlotLocked(string slot_name)
2997 {
2998 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2999 }
3000
3001 //--- ATTACHMENT SLOTS
3003 {
3004 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
3005 if (GetGame().ConfigIsExisting(config_path))
3006 GetGame().ConfigGetTextArray(config_path, attachment_slots);
3007 }
3008
3010 {
3011 return true;
3012 }
3013
3014 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
3015 {
3016 return true;
3017 }
3018
3019 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
3020 {
3021 return true;
3022 }
3023
3024 // --- INIT
3025 void ConstructionInit()
3026 {
3027 if (!m_Construction)
3028 m_Construction = new Construction(this);
3029
3030 GetConstruction().Init();
3031 }
3032
3034 {
3035 return m_Construction;
3036 }
3037
3038 //--- INVENTORY/ATTACHMENTS CONDITIONS
3039 //attachments
3041 {
3042 return super.CanReceiveAttachment(attachment, slotId);
3043 }
3044
3046 {
3047 int attachment_count = GetInventory().AttachmentCount();
3048 if (attachment_count > 0)
3049 {
3050 if (HasBase() && attachment_count == 1)
3051 return false;
3052
3053 return true;
3054 }
3055
3056 return false;
3057 }
3058
3059 override bool ShowZonesHealth()
3060 {
3061 return true;
3062 }
3063
3064 //this into/outo parent.Cargo
3065 override bool CanPutInCargo(EntityAI parent)
3066 {
3067 return false;
3068 }
3069
3070 override bool CanRemoveFromCargo(EntityAI parent)
3071 {
3072 return false;
3073 }
3074
3075 //hands
3076 override bool CanPutIntoHands(EntityAI parent)
3077 {
3078 return false;
3079 }
3080
3081 //--- ACTION CONDITIONS
3082 //direction
3083 override bool IsFacingPlayer(PlayerBase player, string selection)
3084 {
3085 return true;
3086 }
3087
3088 override bool IsPlayerInside(PlayerBase player, string selection)
3089 {
3090 return true;
3091 }
3092
3095 {
3096 return false;
3097 }
3098
3099 //camera direction check
3100 bool IsFacingCamera(string selection)
3101 {
3102 return true;
3103 }
3104
3105 //roof check
3107 {
3108 return false;
3109 }
3110
3111 //selection->player distance check
3112 bool HasProperDistance(string selection, PlayerBase player)
3113 {
3114 return true;
3115 }
3116
3117 //folding
3119 {
3120 if (HasBase() || GetInventory().AttachmentCount() > 0)
3121 return false;
3122
3123 return true;
3124 }
3125
3127 {
3130
3131 return item;
3132 }
3133
3134 //Damage triggers (barbed wire)
3135 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3136 {
3137 if (GetGame() && GetGame().IsServer())
3138 {
3139 //destroy area damage if some already exists
3141
3142 //create new area damage
3144 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3145
3146 vector min_max[2];
3147 if (MemoryPointExists(slot_name + "_min"))
3148 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3149 if (MemoryPointExists(slot_name + "_max"))
3150 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3151
3152 //get proper trigger extents (min<max)
3153 vector extents[2];
3154 GetConstruction().GetTriggerExtents(min_max, extents);
3155
3156 //get box center
3157 vector center;
3158 center = GetConstruction().GetBoxCenter(min_max);
3159 center = ModelToWorld(center);
3160
3161 //rotate center if needed
3164
3165 areaDamage.SetExtents(extents[0], extents[1]);
3166 areaDamage.SetAreaPosition(center);
3167 areaDamage.SetAreaOrientation(orientation);
3168 areaDamage.SetLoopInterval(1.0);
3169 areaDamage.SetDeferDuration(0.2);
3170 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3171 areaDamage.SetAmmoName("BarbedWireHit");
3172 areaDamage.Spawn();
3173
3175 }
3176 }
3177
3179 {
3180 if (angle_deg != 0)
3181 {
3182 //orientation
3184
3185 //center
3187 if (MemoryPointExists("rotate_axis"))
3188 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3191 center[0] = r_center_x;
3192 center[2] = r_center_z;
3193 }
3194 }
3195
3196 void DestroyAreaDamage(string slot_name)
3197 {
3198 if (GetGame() && GetGame().IsServer())
3199 {
3202 {
3203 if (areaDamage)
3204 areaDamage.Destroy();
3205
3207 }
3208 }
3209 }
3210
3211 override bool IsIgnoredByConstruction()
3212 {
3213 return true;
3214 }
3215
3216 //================================================================
3217 // SOUNDS
3218 //================================================================
3219 protected void SoundBuildStart(string part_name)
3220 {
3221 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3222 }
3223
3224 protected void SoundDismantleStart(string part_name)
3225 {
3226 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3227 }
3228
3229 protected void SoundDestroyStart(string part_name)
3230 {
3231 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3232 }
3233
3234 protected string GetBuildSoundByMaterial(string part_name)
3235 {
3237
3238 switch (material_type)
3239 {
3240 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3241 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3242 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3243 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3244 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3245 }
3246
3247 return "";
3248 }
3249
3250 protected string GetDismantleSoundByMaterial(string part_name)
3251 {
3253
3254 switch (material_type)
3255 {
3256 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3257 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3258 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3259 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3260 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3261 }
3262
3263 return "";
3264 }
3265
3266 //misc
3268 {
3269 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3270 {
3271 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3273 SetHealth(slot_name, "Health", item.GetHealth());
3274 }
3275 }
3276
3277 override int GetDamageSystemVersionChange()
3278 {
3279 return 111;
3280 }
3281
3282 override void SetActions()
3283 {
3284 super.SetActions();
3285
3287 //AddAction(ActionTakeHybridAttachment);
3288 //AddAction(ActionTakeHybridAttachmentToHands);
3291 }
3292
3293 //================================================================
3294 // DEBUG
3295 //================================================================
3296 protected void DebugCustomState()
3297 {
3298 }
3299
3302 {
3303 return null;
3304 }
3305
3306 override void OnDebugSpawn()
3307 {
3308 FullyBuild();
3309 }
3310
3311 void FullyBuild()
3312 {
3314 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3315
3316 Man p;
3317
3318#ifdef SERVER
3320 GetGame().GetWorld().GetPlayerList(players);
3321 if (players.Count())
3322 p = players[0];
3323#else
3324 p = GetGame().GetPlayer();
3325#endif
3326
3327 foreach (ConstructionPart part : parts)
3328 {
3329 bool excluded = false;
3330 string partName = part.GetPartName();
3331 if (excludes)
3332 {
3333 foreach (string exclude : excludes)
3334 {
3335 if (partName.Contains(exclude))
3336 {
3337 excluded = true;
3338 break;
3339 }
3340 }
3341 }
3342
3343 if (!excluded)
3345 }
3346
3347 GetConstruction().UpdateVisuals();
3348 }
3349}
3350
3351void bsbDebugPrint(string s)
3352{
3353#ifdef BSB_DEBUG
3354 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3355#else
3356 //Print("" + s); // comment/uncomment to hide/see debug logs
3357#endif
3358}
3359void bsbDebugSpam(string s)
3360{
3361#ifdef BSB_DEBUG_SPAM
3362 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3363#else
3364 //Print("" + s); // comment/uncomment to hide/see debug logs
3365#endif
3366}

Referenced by ItemBase::CreateAreaDamage().

◆ CanFoldBaseBuildingObject()

bool bsbDebugPrint::CanFoldBaseBuildingObject ( )
protected

Definition at line 2116 of file BaseBuildingBase.c.

2118{
2119 const string ANIMATION_DEPLOYED = "Deployed";
2120
2121 float m_ConstructionKitHealth; //stored health value for used construction kit
2122
2124
2125 bool m_HasBase;
2126 //variables for synchronization of base building parts (2x31 is the current limit)
2127 int m_SyncParts01; //synchronization for already built parts (31 parts)
2128 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2129 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2130 int m_InteractedPartId; //construction part id that an action was performed on
2131 int m_PerformedActionId; //action id that was performed on a construction part
2132
2133 //Sounds
2134 //build
2135 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2136 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2137 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2138 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2139 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2140 //dismantle
2141 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2142 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2143 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2144 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2145 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2146
2147 protected EffectSound m_Sound;
2148
2152
2153 // Constructor
2154 void BaseBuildingBase()
2155 {
2157
2158 //synchronized variables
2159 RegisterNetSyncVariableInt("m_SyncParts01");
2160 RegisterNetSyncVariableInt("m_SyncParts02");
2161 RegisterNetSyncVariableInt("m_SyncParts03");
2162 RegisterNetSyncVariableInt("m_InteractedPartId");
2163 RegisterNetSyncVariableInt("m_PerformedActionId");
2164 RegisterNetSyncVariableBool("m_HasBase");
2165
2166 //Construction init
2168
2169 if (ConfigIsExisting("hybridAttachments"))
2170 {
2172 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2173 }
2174 if (ConfigIsExisting("mountables"))
2175 {
2177 ConfigGetTextArray("mountables", m_Mountables);
2178 }
2179
2180 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2181 }
2182
2183 override void EEDelete(EntityAI parent)
2184 {
2185 super.EEDelete(parent);
2186
2187 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2189
2190 }
2191
2192 override string GetInvulnerabilityTypeString()
2193 {
2194 return "disableBaseDamage";
2195 }
2196
2197 override bool CanObstruct()
2198 {
2199 return true;
2200 }
2201
2202 override int GetHideIconMask()
2203 {
2204 return EInventoryIconVisibility.HIDE_VICINITY;
2205 }
2206
2207 // --- SYNCHRONIZATION
2209 {
2210 if (GetGame().IsServer())
2211 SetSynchDirty();
2212 }
2213
2214 override void OnVariablesSynchronized()
2215 {
2216 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2217 super.OnVariablesSynchronized();
2218
2219 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2220 }
2221
2222 protected void OnSynchronizedClient()
2223 {
2224 //update parts
2226
2227 //update action on part
2229
2230 //update visuals (client)
2231 UpdateVisuals();
2232 }
2233
2234 //parts synchronization
2236 {
2237 //part_id must starts from index = 1
2238 int offset;
2239 int mask;
2240
2241 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2242 {
2243 offset = part_id - 1;
2244 mask = 1 << offset;
2245
2247 }
2248 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2249 {
2250 offset = (part_id % 32);
2251 mask = 1 << offset;
2252
2254 }
2255 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2256 {
2257 offset = (part_id % 63);
2258 mask = 1 << offset;
2259
2261 }
2262 }
2263
2265 {
2266 //part_id must starts from index = 1
2267 int offset;
2268 int mask;
2269
2270 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2271 {
2272 offset = part_id - 1;
2273 mask = 1 << offset;
2274
2276 }
2277 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2278 {
2279 offset = (part_id % 32);
2280 mask = 1 << offset;
2281
2283 }
2284 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2285 {
2286 offset = (part_id % 63);
2287 mask = 1 << offset;
2288
2290 }
2291 }
2292
2294 {
2295 //part_id must starts from index = 1
2296 int offset;
2297 int mask;
2298
2299 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2300 {
2301 offset = part_id - 1;
2302 mask = 1 << offset;
2303
2304 if ((m_SyncParts01 & mask) > 0)
2305 return true;
2306 }
2307 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2308 {
2309 offset = (part_id % 32);
2310 mask = 1 << offset;
2311
2312 if ((m_SyncParts02 & mask) > 0)
2313 return true;
2314 }
2315 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2316 {
2317 offset = (part_id % 63);
2318 mask = 1 << offset;
2319
2320 if ((m_SyncParts03 & mask) > 0)
2321 return true;
2322 }
2323
2324 return false;
2325 }
2326
2327 protected void RegisterActionForSync(int part_id, int action_id)
2328 {
2331 }
2332
2333 protected void ResetActionSyncData()
2334 {
2335 //reset data
2336 m_InteractedPartId = -1;
2338 }
2339
2340 protected void SetActionFromSyncData()
2341 {
2342 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2343 {
2346
2347 switch (build_action_id)
2348 {
2352 }
2353 }
2354 }
2355 //------
2356
2358 {
2359 string key = part.m_PartName;
2360 bool is_base = part.IsBase();
2362 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2364 {
2365 if (!part.IsBuilt())
2366 {
2367 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2368 GetConstruction().AddToConstructedParts(key);
2369 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2370
2371 if (is_base)
2372 {
2374 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2375 }
2376 }
2377 }
2378 else
2379 {
2380 if (part.IsBuilt())
2381 {
2382 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2383 GetConstruction().RemoveFromConstructedParts(key);
2384 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2385
2386 if (is_base)
2387 {
2389 AddProxyPhysics(ANIMATION_DEPLOYED);
2390 }
2391 }
2392 }
2393
2394 //check slot lock for material attachments
2395 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2396 }
2397
2398 //set construction parts based on synchronized data
2400 {
2403
2404 for (int i = 0; i < construction_parts.Count(); ++i)
2405 {
2406 string key = construction_parts.GetKey(i);
2409 }
2410
2411 //regenerate navmesh
2412 UpdateNavmesh();
2413 }
2414
2416 {
2419
2420 for (int i = 0; i < construction_parts.Count(); ++i)
2421 {
2422 string key = construction_parts.GetKey(i);
2424
2425 if (value.GetId() == id)
2426 return value;
2427 }
2428
2429 return NULL;
2430 }
2431 //
2432
2433 //Base
2434 bool HasBase()
2435 {
2436 return m_HasBase;
2437 }
2438
2439 void SetBaseState(bool has_base)
2440 {
2442 }
2443
2444 override bool IsDeployable()
2445 {
2446 return true;
2447 }
2448
2449 bool IsOpened()
2450 {
2451 return false;
2452 }
2453
2454 //--- CONSTRUCTION KIT
2456 {
2460
2461 return construction_kit;
2462 }
2463
2465 {
2466 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2469 }
2470
2471 protected vector GetKitSpawnPosition()
2472 {
2473 return GetPosition();
2474 }
2475
2476 protected string GetConstructionKitType()
2477 {
2478 return "";
2479 }
2480
2482 {
2484 GetGame().ObjectDelete(construction_kit);
2485 }
2486
2487 //--- CONSTRUCTION
2488 void DestroyConstruction()
2489 {
2490 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2491 GetGame().ObjectDelete(this);
2492 }
2493
2494 // --- EVENTS
2495 override void OnStoreSave(ParamsWriteContext ctx)
2496 {
2497 super.OnStoreSave(ctx);
2498
2499 //sync parts 01
2500 ctx.Write(m_SyncParts01);
2501 ctx.Write(m_SyncParts02);
2502 ctx.Write(m_SyncParts03);
2503
2504 ctx.Write(m_HasBase);
2505 }
2506
2507 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2508 {
2509 if (!super.OnStoreLoad(ctx, version))
2510 return false;
2511
2512 //--- Base building data ---
2513 //Restore synced parts data
2514 if (!ctx.Read(m_SyncParts01))
2515 {
2516 m_SyncParts01 = 0; //set default
2517 return false;
2518 }
2519 if (!ctx.Read(m_SyncParts02))
2520 {
2521 m_SyncParts02 = 0; //set default
2522 return false;
2523 }
2524 if (!ctx.Read(m_SyncParts03))
2525 {
2526 m_SyncParts03 = 0; //set default
2527 return false;
2528 }
2529
2530 //has base
2531 if (!ctx.Read(m_HasBase))
2532 {
2533 m_HasBase = false;
2534 return false;
2535 }
2536 //---
2537
2538 return true;
2539 }
2540
2541 override void AfterStoreLoad()
2542 {
2543 super.AfterStoreLoad();
2544
2547 }
2548
2550 {
2551 //update server data
2553
2554 //set base state
2555 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2556 SetBaseState(construction_part.IsBuilt()) ;
2557
2558 //synchronize after load
2560 }
2561
2562 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2563 {
2565 return;
2566
2567 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2568
2569 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2570 return;
2571
2573 string part_name = zone;
2574 part_name.ToLower();
2575
2577 {
2579
2580 if (construction_part && construction.IsPartConstructed(part_name))
2581 {
2582 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2583 construction.DestroyConnectedParts(part_name);
2584 }
2585
2586 //barbed wire handling (hack-ish)
2587 if (part_name.Contains("barbed"))
2588 {
2589 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2590 if (barbed_wire)
2591 barbed_wire.SetMountedState(false);
2592 }
2593 }
2594 }
2595
2596 override void EEOnAfterLoad()
2597 {
2599 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2600
2601 super.EEOnAfterLoad();
2602 }
2603
2604 override void EEInit()
2605 {
2606 super.EEInit();
2607
2608 // init visuals and physics
2609 InitBaseState();
2610
2611 //debug
2612#ifdef DEVELOPER
2614#endif
2615 }
2616
2617 override void EEItemAttached(EntityAI item, string slot_name)
2618 {
2619 super.EEItemAttached(item, slot_name);
2620
2622 UpdateVisuals();
2624 }
2625
2626 override void EEItemDetached(EntityAI item, string slot_name)
2627 {
2628 super.EEItemDetached(item, slot_name);
2629
2630 UpdateVisuals();
2632 }
2633
2634 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2635 {
2637 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2638
2641 }
2642
2643 //ignore out of reach condition
2644 override bool IgnoreOutOfReachCondition()
2645 {
2646 return true;
2647 }
2648
2649 //CONSTRUCTION EVENTS
2650 //Build
2651 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2652 {
2654
2655 //check base state
2656 if (construtionPart.IsBase())
2657 {
2658 SetBaseState(true);
2659
2660 //spawn kit
2662 }
2663
2664 //register constructed parts for synchronization
2666
2667 //register action that was performed on part
2669
2670 //synchronize
2672
2673 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2674
2675 UpdateNavmesh();
2676
2677 //update visuals
2678 UpdateVisuals();
2679
2680 //reset action sync data
2681 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2682 }
2683
2684 void OnPartBuiltClient(string part_name, int action_id)
2685 {
2686 //play sound
2688 }
2689
2690 //Dismantle
2692 {
2693 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2695
2696 //register constructed parts for synchronization
2698
2699 //register action that was performed on part
2701
2702 //synchronize
2704
2705 // server part of sync, client will be synced from SetPartsFromSyncData
2707
2708 UpdateNavmesh();
2709
2710 //update visuals
2711 UpdateVisuals();
2712
2713 //reset action sync data
2714 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2715
2716 //check base state
2717 if (construtionPart.IsBase())
2718 {
2719 //Destroy construction
2720 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2721 }
2722 }
2723
2725 {
2726 //play sound
2728 }
2729
2730 //Destroy
2732 {
2733 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2735
2736 //register constructed parts for synchronization
2738
2739 //register action that was performed on part
2741
2742 //synchronize
2744
2745 // server part of sync, client will be synced from SetPartsFromSyncData
2747
2748 UpdateNavmesh();
2749
2750 //update visuals
2751 UpdateVisuals();
2752
2753 //reset action sync data
2754 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2755
2756 //check base state
2757 if (construtionPart.IsBase())
2758 {
2759 //Destroy construction
2760 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2761 }
2762 }
2763
2764 void OnPartDestroyedClient(string part_name, int action_id)
2765 {
2766 //play sound
2768 }
2769
2770 // --- UPDATE
2771 void InitBaseState()
2772 {
2773 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2774
2775 InitVisuals();
2776 UpdateNavmesh(); //regenerate navmesh
2777 GetConstruction().InitBaseState();
2778 }
2779
2780 void InitVisuals()
2781 {
2782 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2783 //check base
2784 if (!HasBase())
2785 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2786 else
2787 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2788
2789 GetConstruction().UpdateVisuals();
2790 }
2791
2792 void UpdateVisuals()
2793 {
2795
2797 foreach (string slotName : attachmentSlots)
2799
2800 //check base
2801 if (!HasBase())
2802 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2803 else
2804 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2805
2806 GetConstruction().UpdateVisuals();
2807 }
2808
2810 {
2811 string slotNameMounted = slot_name + "_Mounted";
2812 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2813
2814 if (attachment)
2815 {
2816 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2817 if (barbedWire && barbedWire.IsMounted())
2819 else
2821
2822 if (is_locked)
2823 {
2824 SetAnimationPhase(slotNameMounted, 0);
2825 SetAnimationPhase(slot_name, 1);
2826 }
2827 else
2828 {
2829 SetAnimationPhase(slotNameMounted, 1);
2830 SetAnimationPhase(slot_name, 0);
2831 }
2832 }
2833 else
2834 {
2835 SetAnimationPhase(slotNameMounted, 1);
2836 SetAnimationPhase(slot_name, 1);
2837
2839 }
2840 }
2841
2842 // avoid calling this function on frequent occasions, it's a massive performance hit
2843 void UpdatePhysics()
2844 {
2846 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2847
2850
2852 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2853
2854 foreach (string slotName : attachmentSlots)
2856
2857 //check base
2858 if (!HasBase())
2859 {
2861 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2862
2863 AddProxyPhysics(ANIMATION_DEPLOYED);
2864 }
2865 else
2866 {
2868 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2869
2870 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2871 }
2872
2873 GetConstruction().UpdatePhysics();
2874 UpdateNavmesh();
2875 }
2876
2878 {
2879 //checks for invalid appends; hotfix
2880 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2881 return;
2882 //----------------------------------
2883 string slot_name_mounted = slot_name + "_Mounted";
2884 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2885
2886 //remove proxy physics
2887 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2888 RemoveProxyPhysics(slot_name_mounted);
2889 RemoveProxyPhysics(slot_name);
2890
2891 if (attachment)
2892 {
2893 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2894 if (is_locked)
2895 {
2896 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2897 AddProxyPhysics(slot_name_mounted);
2898 }
2899 else
2900 {
2901 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2902 AddProxyPhysics(slot_name);
2903 }
2904 }
2905 }
2906
2907 protected void UpdateNavmesh()
2908 {
2909 SetAffectPathgraph(true, false);
2910 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2911 }
2912
2913 override bool CanUseConstruction()
2914 {
2915 return true;
2916 }
2917
2918 override bool CanUseConstructionBuild()
2919 {
2920 return true;
2921 }
2922
2924 {
2925 if (attachment)
2926 {
2928 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2929
2930 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2931 }
2932
2933 return false;
2934 }
2935
2936 protected bool IsAttachmentSlotLocked(string slot_name)
2937 {
2938 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2939 }
2940
2941 //--- ATTACHMENT SLOTS
2943 {
2944 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2945 if (GetGame().ConfigIsExisting(config_path))
2946 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2947 }
2948
2950 {
2951 return true;
2952 }
2953
2954 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2955 {
2956 return true;
2957 }
2958
2959 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2960 {
2961 return true;
2962 }
2963
2964 // --- INIT
2965 void ConstructionInit()
2966 {
2967 if (!m_Construction)
2968 m_Construction = new Construction(this);
2969
2970 GetConstruction().Init();
2971 }
2972
2974 {
2975 return m_Construction;
2976 }
2977
2978 //--- INVENTORY/ATTACHMENTS CONDITIONS
2979 //attachments
2981 {
2982 return super.CanReceiveAttachment(attachment, slotId);
2983 }
2984
2986 {
2987 int attachment_count = GetInventory().AttachmentCount();
2988 if (attachment_count > 0)
2989 {
2990 if (HasBase() && attachment_count == 1)
2991 return false;
2992
2993 return true;
2994 }
2995
2996 return false;
2997 }
2998
2999 override bool ShowZonesHealth()
3000 {
3001 return true;
3002 }
3003
3004 //this into/outo parent.Cargo
3005 override bool CanPutInCargo(EntityAI parent)
3006 {
3007 return false;
3008 }
3009
3010 override bool CanRemoveFromCargo(EntityAI parent)
3011 {
3012 return false;
3013 }
3014
3015 //hands
3016 override bool CanPutIntoHands(EntityAI parent)
3017 {
3018 return false;
3019 }
3020
3021 //--- ACTION CONDITIONS
3022 //direction
3023 override bool IsFacingPlayer(PlayerBase player, string selection)
3024 {
3025 return true;
3026 }
3027
3028 override bool IsPlayerInside(PlayerBase player, string selection)
3029 {
3030 return true;
3031 }
3032
3035 {
3036 return false;
3037 }
3038
3039 //camera direction check
3040 bool IsFacingCamera(string selection)
3041 {
3042 return true;
3043 }
3044
3045 //roof check
3047 {
3048 return false;
3049 }
3050
3051 //selection->player distance check
3052 bool HasProperDistance(string selection, PlayerBase player)
3053 {
3054 return true;
3055 }
3056
3057 //folding
3059 {
3060 if (HasBase() || GetInventory().AttachmentCount() > 0)
3061 return false;
3062
3063 return true;
3064 }
3065
3067 {
3070
3071 return item;
3072 }
3073
3074 //Damage triggers (barbed wire)
3075 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3076 {
3077 if (GetGame() && GetGame().IsServer())
3078 {
3079 //destroy area damage if some already exists
3081
3082 //create new area damage
3084 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3085
3086 vector min_max[2];
3087 if (MemoryPointExists(slot_name + "_min"))
3088 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3089 if (MemoryPointExists(slot_name + "_max"))
3090 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3091
3092 //get proper trigger extents (min<max)
3093 vector extents[2];
3094 GetConstruction().GetTriggerExtents(min_max, extents);
3095
3096 //get box center
3097 vector center;
3098 center = GetConstruction().GetBoxCenter(min_max);
3099 center = ModelToWorld(center);
3100
3101 //rotate center if needed
3104
3105 areaDamage.SetExtents(extents[0], extents[1]);
3106 areaDamage.SetAreaPosition(center);
3107 areaDamage.SetAreaOrientation(orientation);
3108 areaDamage.SetLoopInterval(1.0);
3109 areaDamage.SetDeferDuration(0.2);
3110 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3111 areaDamage.SetAmmoName("BarbedWireHit");
3112 areaDamage.Spawn();
3113
3115 }
3116 }
3117
3119 {
3120 if (angle_deg != 0)
3121 {
3122 //orientation
3124
3125 //center
3127 if (MemoryPointExists("rotate_axis"))
3128 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3131 center[0] = r_center_x;
3132 center[2] = r_center_z;
3133 }
3134 }
3135
3136 void DestroyAreaDamage(string slot_name)
3137 {
3138 if (GetGame() && GetGame().IsServer())
3139 {
3142 {
3143 if (areaDamage)
3144 areaDamage.Destroy();
3145
3147 }
3148 }
3149 }
3150
3151 override bool IsIgnoredByConstruction()
3152 {
3153 return true;
3154 }
3155
3156 //================================================================
3157 // SOUNDS
3158 //================================================================
3159 protected void SoundBuildStart(string part_name)
3160 {
3161 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3162 }
3163
3164 protected void SoundDismantleStart(string part_name)
3165 {
3166 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3167 }
3168
3169 protected void SoundDestroyStart(string part_name)
3170 {
3171 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3172 }
3173
3174 protected string GetBuildSoundByMaterial(string part_name)
3175 {
3177
3178 switch (material_type)
3179 {
3180 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3181 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3182 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3183 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3184 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3185 }
3186
3187 return "";
3188 }
3189
3190 protected string GetDismantleSoundByMaterial(string part_name)
3191 {
3193
3194 switch (material_type)
3195 {
3196 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3197 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3198 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3199 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3200 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3201 }
3202
3203 return "";
3204 }
3205
3206 //misc
3208 {
3209 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3210 {
3211 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3213 SetHealth(slot_name, "Health", item.GetHealth());
3214 }
3215 }
3216
3217 override int GetDamageSystemVersionChange()
3218 {
3219 return 111;
3220 }
3221
3222 override void SetActions()
3223 {
3224 super.SetActions();
3225
3227 //AddAction(ActionTakeHybridAttachment);
3228 //AddAction(ActionTakeHybridAttachmentToHands);
3231 }
3232
3233 //================================================================
3234 // DEBUG
3235 //================================================================
3236 protected void DebugCustomState()
3237 {
3238 }
3239
3242 {
3243 return null;
3244 }
3245
3246 override void OnDebugSpawn()
3247 {
3248 FullyBuild();
3249 }
3250
3251 void FullyBuild()
3252 {
3254 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3255
3256 Man p;
3257
3258#ifdef SERVER
3260 GetGame().GetWorld().GetPlayerList(players);
3261 if (players.Count())
3262 p = players[0];
3263#else
3264 p = GetGame().GetPlayer();
3265#endif
3266
3267 foreach (ConstructionPart part : parts)
3268 {
3269 bool excluded = false;
3270 string partName = part.GetPartName();
3271 if (excludes)
3272 {
3273 foreach (string exclude : excludes)
3274 {
3275 if (partName.Contains(exclude))
3276 {
3277 excluded = true;
3278 break;
3279 }
3280 }
3281 }
3282
3283 if (!excluded)
3285 }
3286
3287 GetConstruction().UpdateVisuals();
3288 }
3289}
3290
3291void bsbDebugPrint(string s)
3292{
3293#ifdef BSB_DEBUG
3294 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3295#else
3296 //Print("" + s); // comment/uncomment to hide/see debug logs
3297#endif
3298}
3299void bsbDebugSpam(string s)
3300{
3301#ifdef BSB_DEBUG_SPAM
3302 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3303#else
3304 //Print("" + s); // comment/uncomment to hide/see debug logs
3305#endif
3306}

◆ CanObstruct()

override bool bsbDebugPrint::CanObstruct ( )
protected

Definition at line 1255 of file BaseBuildingBase.c.

1257{
1258 const string ANIMATION_DEPLOYED = "Deployed";
1259
1260 float m_ConstructionKitHealth; //stored health value for used construction kit
1261
1263
1264 bool m_HasBase;
1265 //variables for synchronization of base building parts (2x31 is the current limit)
1266 int m_SyncParts01; //synchronization for already built parts (31 parts)
1267 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1268 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1269 int m_InteractedPartId; //construction part id that an action was performed on
1270 int m_PerformedActionId; //action id that was performed on a construction part
1271
1272 //Sounds
1273 //build
1274 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1275 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1276 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1277 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1278 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1279 //dismantle
1280 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1281 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1282 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1283 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1284 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1285
1286 protected EffectSound m_Sound;
1287
1291
1292 // Constructor
1293 void BaseBuildingBase()
1294 {
1296
1297 //synchronized variables
1298 RegisterNetSyncVariableInt("m_SyncParts01");
1299 RegisterNetSyncVariableInt("m_SyncParts02");
1300 RegisterNetSyncVariableInt("m_SyncParts03");
1301 RegisterNetSyncVariableInt("m_InteractedPartId");
1302 RegisterNetSyncVariableInt("m_PerformedActionId");
1303 RegisterNetSyncVariableBool("m_HasBase");
1304
1305 //Construction init
1307
1308 if (ConfigIsExisting("hybridAttachments"))
1309 {
1311 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1312 }
1313 if (ConfigIsExisting("mountables"))
1314 {
1316 ConfigGetTextArray("mountables", m_Mountables);
1317 }
1318
1319 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1320 }
1321
1322 override void EEDelete(EntityAI parent)
1323 {
1324 super.EEDelete(parent);
1325
1326 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1328
1329 }
1330
1331 override string GetInvulnerabilityTypeString()
1332 {
1333 return "disableBaseDamage";
1334 }
1335
1336 override bool CanObstruct()
1337 {
1338 return true;
1339 }
1340
1341 override int GetHideIconMask()
1342 {
1343 return EInventoryIconVisibility.HIDE_VICINITY;
1344 }
1345
1346 // --- SYNCHRONIZATION
1348 {
1349 if (GetGame().IsServer())
1350 SetSynchDirty();
1351 }
1352
1353 override void OnVariablesSynchronized()
1354 {
1355 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1356 super.OnVariablesSynchronized();
1357
1358 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1359 }
1360
1361 protected void OnSynchronizedClient()
1362 {
1363 //update parts
1365
1366 //update action on part
1368
1369 //update visuals (client)
1370 UpdateVisuals();
1371 }
1372
1373 //parts synchronization
1375 {
1376 //part_id must starts from index = 1
1377 int offset;
1378 int mask;
1379
1380 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1381 {
1382 offset = part_id - 1;
1383 mask = 1 << offset;
1384
1386 }
1387 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1388 {
1389 offset = (part_id % 32);
1390 mask = 1 << offset;
1391
1393 }
1394 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1395 {
1396 offset = (part_id % 63);
1397 mask = 1 << offset;
1398
1400 }
1401 }
1402
1404 {
1405 //part_id must starts from index = 1
1406 int offset;
1407 int mask;
1408
1409 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1410 {
1411 offset = part_id - 1;
1412 mask = 1 << offset;
1413
1415 }
1416 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1417 {
1418 offset = (part_id % 32);
1419 mask = 1 << offset;
1420
1422 }
1423 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1424 {
1425 offset = (part_id % 63);
1426 mask = 1 << offset;
1427
1429 }
1430 }
1431
1433 {
1434 //part_id must starts from index = 1
1435 int offset;
1436 int mask;
1437
1438 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1439 {
1440 offset = part_id - 1;
1441 mask = 1 << offset;
1442
1443 if ((m_SyncParts01 & mask) > 0)
1444 return true;
1445 }
1446 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1447 {
1448 offset = (part_id % 32);
1449 mask = 1 << offset;
1450
1451 if ((m_SyncParts02 & mask) > 0)
1452 return true;
1453 }
1454 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1455 {
1456 offset = (part_id % 63);
1457 mask = 1 << offset;
1458
1459 if ((m_SyncParts03 & mask) > 0)
1460 return true;
1461 }
1462
1463 return false;
1464 }
1465
1466 protected void RegisterActionForSync(int part_id, int action_id)
1467 {
1470 }
1471
1472 protected void ResetActionSyncData()
1473 {
1474 //reset data
1475 m_InteractedPartId = -1;
1477 }
1478
1479 protected void SetActionFromSyncData()
1480 {
1481 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1482 {
1485
1486 switch (build_action_id)
1487 {
1491 }
1492 }
1493 }
1494 //------
1495
1497 {
1498 string key = part.m_PartName;
1499 bool is_base = part.IsBase();
1501 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1503 {
1504 if (!part.IsBuilt())
1505 {
1506 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1507 GetConstruction().AddToConstructedParts(key);
1508 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1509
1510 if (is_base)
1511 {
1513 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1514 }
1515 }
1516 }
1517 else
1518 {
1519 if (part.IsBuilt())
1520 {
1521 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1522 GetConstruction().RemoveFromConstructedParts(key);
1523 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1524
1525 if (is_base)
1526 {
1528 AddProxyPhysics(ANIMATION_DEPLOYED);
1529 }
1530 }
1531 }
1532
1533 //check slot lock for material attachments
1534 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1535 }
1536
1537 //set construction parts based on synchronized data
1539 {
1542
1543 for (int i = 0; i < construction_parts.Count(); ++i)
1544 {
1545 string key = construction_parts.GetKey(i);
1548 }
1549
1550 //regenerate navmesh
1551 UpdateNavmesh();
1552 }
1553
1555 {
1558
1559 for (int i = 0; i < construction_parts.Count(); ++i)
1560 {
1561 string key = construction_parts.GetKey(i);
1563
1564 if (value.GetId() == id)
1565 return value;
1566 }
1567
1568 return NULL;
1569 }
1570 //
1571
1572 //Base
1573 bool HasBase()
1574 {
1575 return m_HasBase;
1576 }
1577
1578 void SetBaseState(bool has_base)
1579 {
1581 }
1582
1583 override bool IsDeployable()
1584 {
1585 return true;
1586 }
1587
1588 bool IsOpened()
1589 {
1590 return false;
1591 }
1592
1593 //--- CONSTRUCTION KIT
1595 {
1599
1600 return construction_kit;
1601 }
1602
1604 {
1605 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1608 }
1609
1610 protected vector GetKitSpawnPosition()
1611 {
1612 return GetPosition();
1613 }
1614
1615 protected string GetConstructionKitType()
1616 {
1617 return "";
1618 }
1619
1621 {
1623 GetGame().ObjectDelete(construction_kit);
1624 }
1625
1626 //--- CONSTRUCTION
1627 void DestroyConstruction()
1628 {
1629 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1630 GetGame().ObjectDelete(this);
1631 }
1632
1633 // --- EVENTS
1634 override void OnStoreSave(ParamsWriteContext ctx)
1635 {
1636 super.OnStoreSave(ctx);
1637
1638 //sync parts 01
1639 ctx.Write(m_SyncParts01);
1640 ctx.Write(m_SyncParts02);
1641 ctx.Write(m_SyncParts03);
1642
1643 ctx.Write(m_HasBase);
1644 }
1645
1646 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1647 {
1648 if (!super.OnStoreLoad(ctx, version))
1649 return false;
1650
1651 //--- Base building data ---
1652 //Restore synced parts data
1653 if (!ctx.Read(m_SyncParts01))
1654 {
1655 m_SyncParts01 = 0; //set default
1656 return false;
1657 }
1658 if (!ctx.Read(m_SyncParts02))
1659 {
1660 m_SyncParts02 = 0; //set default
1661 return false;
1662 }
1663 if (!ctx.Read(m_SyncParts03))
1664 {
1665 m_SyncParts03 = 0; //set default
1666 return false;
1667 }
1668
1669 //has base
1670 if (!ctx.Read(m_HasBase))
1671 {
1672 m_HasBase = false;
1673 return false;
1674 }
1675 //---
1676
1677 return true;
1678 }
1679
1680 override void AfterStoreLoad()
1681 {
1682 super.AfterStoreLoad();
1683
1686 }
1687
1689 {
1690 //update server data
1692
1693 //set base state
1694 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1695 SetBaseState(construction_part.IsBuilt()) ;
1696
1697 //synchronize after load
1699 }
1700
1701 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1702 {
1704 return;
1705
1706 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1707
1708 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1709 return;
1710
1712 string part_name = zone;
1713 part_name.ToLower();
1714
1716 {
1718
1719 if (construction_part && construction.IsPartConstructed(part_name))
1720 {
1721 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1722 construction.DestroyConnectedParts(part_name);
1723 }
1724
1725 //barbed wire handling (hack-ish)
1726 if (part_name.Contains("barbed"))
1727 {
1728 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1729 if (barbed_wire)
1730 barbed_wire.SetMountedState(false);
1731 }
1732 }
1733 }
1734
1735 override void EEOnAfterLoad()
1736 {
1738 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1739
1740 super.EEOnAfterLoad();
1741 }
1742
1743 override void EEInit()
1744 {
1745 super.EEInit();
1746
1747 // init visuals and physics
1748 InitBaseState();
1749
1750 //debug
1751#ifdef DEVELOPER
1753#endif
1754 }
1755
1756 override void EEItemAttached(EntityAI item, string slot_name)
1757 {
1758 super.EEItemAttached(item, slot_name);
1759
1761 UpdateVisuals();
1763 }
1764
1765 override void EEItemDetached(EntityAI item, string slot_name)
1766 {
1767 super.EEItemDetached(item, slot_name);
1768
1769 UpdateVisuals();
1771 }
1772
1773 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
1774 {
1776 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
1777
1780 }
1781
1782 //ignore out of reach condition
1783 override bool IgnoreOutOfReachCondition()
1784 {
1785 return true;
1786 }
1787
1788 //CONSTRUCTION EVENTS
1789 //Build
1790 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
1791 {
1793
1794 //check base state
1795 if (construtionPart.IsBase())
1796 {
1797 SetBaseState(true);
1798
1799 //spawn kit
1801 }
1802
1803 //register constructed parts for synchronization
1805
1806 //register action that was performed on part
1808
1809 //synchronize
1811
1812 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
1813
1814 UpdateNavmesh();
1815
1816 //update visuals
1817 UpdateVisuals();
1818
1819 //reset action sync data
1820 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1821 }
1822
1823 void OnPartBuiltClient(string part_name, int action_id)
1824 {
1825 //play sound
1827 }
1828
1829 //Dismantle
1831 {
1832 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
1834
1835 //register constructed parts for synchronization
1837
1838 //register action that was performed on part
1840
1841 //synchronize
1843
1844 // server part of sync, client will be synced from SetPartsFromSyncData
1846
1847 UpdateNavmesh();
1848
1849 //update visuals
1850 UpdateVisuals();
1851
1852 //reset action sync data
1853 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1854
1855 //check base state
1856 if (construtionPart.IsBase())
1857 {
1858 //Destroy construction
1859 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1860 }
1861 }
1862
1864 {
1865 //play sound
1867 }
1868
1869 //Destroy
1871 {
1872 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
1874
1875 //register constructed parts for synchronization
1877
1878 //register action that was performed on part
1880
1881 //synchronize
1883
1884 // server part of sync, client will be synced from SetPartsFromSyncData
1886
1887 UpdateNavmesh();
1888
1889 //update visuals
1890 UpdateVisuals();
1891
1892 //reset action sync data
1893 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1894
1895 //check base state
1896 if (construtionPart.IsBase())
1897 {
1898 //Destroy construction
1899 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1900 }
1901 }
1902
1903 void OnPartDestroyedClient(string part_name, int action_id)
1904 {
1905 //play sound
1907 }
1908
1909 // --- UPDATE
1910 void InitBaseState()
1911 {
1912 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
1913
1914 InitVisuals();
1915 UpdateNavmesh(); //regenerate navmesh
1916 GetConstruction().InitBaseState();
1917 }
1918
1919 void InitVisuals()
1920 {
1921 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
1922 //check base
1923 if (!HasBase())
1924 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1925 else
1926 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1927
1928 GetConstruction().UpdateVisuals();
1929 }
1930
1931 void UpdateVisuals()
1932 {
1934
1936 foreach (string slotName : attachmentSlots)
1938
1939 //check base
1940 if (!HasBase())
1941 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1942 else
1943 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1944
1945 GetConstruction().UpdateVisuals();
1946 }
1947
1949 {
1950 string slotNameMounted = slot_name + "_Mounted";
1951 EntityAI attachment = FindAttachmentBySlotName(slot_name);
1952
1953 if (attachment)
1954 {
1955 BarbedWire barbedWire = BarbedWire.Cast(attachment);
1956 if (barbedWire && barbedWire.IsMounted())
1958 else
1960
1961 if (is_locked)
1962 {
1963 SetAnimationPhase(slotNameMounted, 0);
1964 SetAnimationPhase(slot_name, 1);
1965 }
1966 else
1967 {
1968 SetAnimationPhase(slotNameMounted, 1);
1969 SetAnimationPhase(slot_name, 0);
1970 }
1971 }
1972 else
1973 {
1974 SetAnimationPhase(slotNameMounted, 1);
1975 SetAnimationPhase(slot_name, 1);
1976
1978 }
1979 }
1980
1981 // avoid calling this function on frequent occasions, it's a massive performance hit
1982 void UpdatePhysics()
1983 {
1985 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
1986
1989
1991 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
1992
1993 foreach (string slotName : attachmentSlots)
1995
1996 //check base
1997 if (!HasBase())
1998 {
2000 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2001
2002 AddProxyPhysics(ANIMATION_DEPLOYED);
2003 }
2004 else
2005 {
2007 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2008
2009 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2010 }
2011
2012 GetConstruction().UpdatePhysics();
2013 UpdateNavmesh();
2014 }
2015
2017 {
2018 //checks for invalid appends; hotfix
2019 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2020 return;
2021 //----------------------------------
2022 string slot_name_mounted = slot_name + "_Mounted";
2023 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2024
2025 //remove proxy physics
2026 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2027 RemoveProxyPhysics(slot_name_mounted);
2028 RemoveProxyPhysics(slot_name);
2029
2030 if (attachment)
2031 {
2032 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2033 if (is_locked)
2034 {
2035 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2036 AddProxyPhysics(slot_name_mounted);
2037 }
2038 else
2039 {
2040 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2041 AddProxyPhysics(slot_name);
2042 }
2043 }
2044 }
2045
2046 protected void UpdateNavmesh()
2047 {
2048 SetAffectPathgraph(true, false);
2049 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2050 }
2051
2052 override bool CanUseConstruction()
2053 {
2054 return true;
2055 }
2056
2057 override bool CanUseConstructionBuild()
2058 {
2059 return true;
2060 }
2061
2063 {
2064 if (attachment)
2065 {
2067 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2068
2069 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2070 }
2071
2072 return false;
2073 }
2074
2075 protected bool IsAttachmentSlotLocked(string slot_name)
2076 {
2077 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2078 }
2079
2080 //--- ATTACHMENT SLOTS
2082 {
2083 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2084 if (GetGame().ConfigIsExisting(config_path))
2085 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2086 }
2087
2089 {
2090 return true;
2091 }
2092
2093 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2094 {
2095 return true;
2096 }
2097
2098 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2099 {
2100 return true;
2101 }
2102
2103 // --- INIT
2104 void ConstructionInit()
2105 {
2106 if (!m_Construction)
2107 m_Construction = new Construction(this);
2108
2109 GetConstruction().Init();
2110 }
2111
2113 {
2114 return m_Construction;
2115 }
2116
2117 //--- INVENTORY/ATTACHMENTS CONDITIONS
2118 //attachments
2120 {
2121 return super.CanReceiveAttachment(attachment, slotId);
2122 }
2123
2125 {
2126 int attachment_count = GetInventory().AttachmentCount();
2127 if (attachment_count > 0)
2128 {
2129 if (HasBase() && attachment_count == 1)
2130 return false;
2131
2132 return true;
2133 }
2134
2135 return false;
2136 }
2137
2138 override bool ShowZonesHealth()
2139 {
2140 return true;
2141 }
2142
2143 //this into/outo parent.Cargo
2144 override bool CanPutInCargo(EntityAI parent)
2145 {
2146 return false;
2147 }
2148
2149 override bool CanRemoveFromCargo(EntityAI parent)
2150 {
2151 return false;
2152 }
2153
2154 //hands
2155 override bool CanPutIntoHands(EntityAI parent)
2156 {
2157 return false;
2158 }
2159
2160 //--- ACTION CONDITIONS
2161 //direction
2162 override bool IsFacingPlayer(PlayerBase player, string selection)
2163 {
2164 return true;
2165 }
2166
2167 override bool IsPlayerInside(PlayerBase player, string selection)
2168 {
2169 return true;
2170 }
2171
2174 {
2175 return false;
2176 }
2177
2178 //camera direction check
2179 bool IsFacingCamera(string selection)
2180 {
2181 return true;
2182 }
2183
2184 //roof check
2186 {
2187 return false;
2188 }
2189
2190 //selection->player distance check
2191 bool HasProperDistance(string selection, PlayerBase player)
2192 {
2193 return true;
2194 }
2195
2196 //folding
2198 {
2199 if (HasBase() || GetInventory().AttachmentCount() > 0)
2200 return false;
2201
2202 return true;
2203 }
2204
2206 {
2209
2210 return item;
2211 }
2212
2213 //Damage triggers (barbed wire)
2214 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2215 {
2216 if (GetGame() && GetGame().IsServer())
2217 {
2218 //destroy area damage if some already exists
2220
2221 //create new area damage
2223 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2224
2225 vector min_max[2];
2226 if (MemoryPointExists(slot_name + "_min"))
2227 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2228 if (MemoryPointExists(slot_name + "_max"))
2229 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2230
2231 //get proper trigger extents (min<max)
2232 vector extents[2];
2233 GetConstruction().GetTriggerExtents(min_max, extents);
2234
2235 //get box center
2236 vector center;
2237 center = GetConstruction().GetBoxCenter(min_max);
2238 center = ModelToWorld(center);
2239
2240 //rotate center if needed
2243
2244 areaDamage.SetExtents(extents[0], extents[1]);
2245 areaDamage.SetAreaPosition(center);
2246 areaDamage.SetAreaOrientation(orientation);
2247 areaDamage.SetLoopInterval(1.0);
2248 areaDamage.SetDeferDuration(0.2);
2249 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2250 areaDamage.SetAmmoName("BarbedWireHit");
2251 areaDamage.Spawn();
2252
2254 }
2255 }
2256
2258 {
2259 if (angle_deg != 0)
2260 {
2261 //orientation
2263
2264 //center
2266 if (MemoryPointExists("rotate_axis"))
2267 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2270 center[0] = r_center_x;
2271 center[2] = r_center_z;
2272 }
2273 }
2274
2275 void DestroyAreaDamage(string slot_name)
2276 {
2277 if (GetGame() && GetGame().IsServer())
2278 {
2281 {
2282 if (areaDamage)
2283 areaDamage.Destroy();
2284
2286 }
2287 }
2288 }
2289
2290 override bool IsIgnoredByConstruction()
2291 {
2292 return true;
2293 }
2294
2295 //================================================================
2296 // SOUNDS
2297 //================================================================
2298 protected void SoundBuildStart(string part_name)
2299 {
2300 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2301 }
2302
2303 protected void SoundDismantleStart(string part_name)
2304 {
2305 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2306 }
2307
2308 protected void SoundDestroyStart(string part_name)
2309 {
2310 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2311 }
2312
2313 protected string GetBuildSoundByMaterial(string part_name)
2314 {
2316
2317 switch (material_type)
2318 {
2319 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2320 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2321 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2322 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2323 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2324 }
2325
2326 return "";
2327 }
2328
2329 protected string GetDismantleSoundByMaterial(string part_name)
2330 {
2332
2333 switch (material_type)
2334 {
2335 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2336 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2337 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2338 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2339 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2340 }
2341
2342 return "";
2343 }
2344
2345 //misc
2347 {
2348 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2349 {
2350 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2352 SetHealth(slot_name, "Health", item.GetHealth());
2353 }
2354 }
2355
2356 override int GetDamageSystemVersionChange()
2357 {
2358 return 111;
2359 }
2360
2361 override void SetActions()
2362 {
2363 super.SetActions();
2364
2366 //AddAction(ActionTakeHybridAttachment);
2367 //AddAction(ActionTakeHybridAttachmentToHands);
2370 }
2371
2372 //================================================================
2373 // DEBUG
2374 //================================================================
2375 protected void DebugCustomState()
2376 {
2377 }
2378
2381 {
2382 return null;
2383 }
2384
2385 override void OnDebugSpawn()
2386 {
2387 FullyBuild();
2388 }
2389
2390 void FullyBuild()
2391 {
2393 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2394
2395 Man p;
2396
2397#ifdef SERVER
2399 GetGame().GetWorld().GetPlayerList(players);
2400 if (players.Count())
2401 p = players[0];
2402#else
2403 p = GetGame().GetPlayer();
2404#endif
2405
2406 foreach (ConstructionPart part : parts)
2407 {
2408 bool excluded = false;
2409 string partName = part.GetPartName();
2410 if (excludes)
2411 {
2412 foreach (string exclude : excludes)
2413 {
2414 if (partName.Contains(exclude))
2415 {
2416 excluded = true;
2417 break;
2418 }
2419 }
2420 }
2421
2422 if (!excluded)
2424 }
2425
2426 GetConstruction().UpdateVisuals();
2427 }
2428}
2429
2430void bsbDebugPrint(string s)
2431{
2432#ifdef BSB_DEBUG
2433 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2434#else
2435 //Print("" + s); // comment/uncomment to hide/see debug logs
2436#endif
2437}
2438void bsbDebugSpam(string s)
2439{
2440#ifdef BSB_DEBUG_SPAM
2441 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2442#else
2443 //Print("" + s); // comment/uncomment to hide/see debug logs
2444#endif
2445}

◆ CanPutInCargo()

override bool bsbDebugPrint::CanPutInCargo ( EntityAI parent)
protected

Definition at line 2063 of file BaseBuildingBase.c.

2065{
2066 const string ANIMATION_DEPLOYED = "Deployed";
2067
2068 float m_ConstructionKitHealth; //stored health value for used construction kit
2069
2071
2072 bool m_HasBase;
2073 //variables for synchronization of base building parts (2x31 is the current limit)
2074 int m_SyncParts01; //synchronization for already built parts (31 parts)
2075 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2076 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2077 int m_InteractedPartId; //construction part id that an action was performed on
2078 int m_PerformedActionId; //action id that was performed on a construction part
2079
2080 //Sounds
2081 //build
2082 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2083 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2084 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2085 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2086 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2087 //dismantle
2088 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2089 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2090 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2091 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2092 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2093
2094 protected EffectSound m_Sound;
2095
2099
2100 // Constructor
2101 void BaseBuildingBase()
2102 {
2104
2105 //synchronized variables
2106 RegisterNetSyncVariableInt("m_SyncParts01");
2107 RegisterNetSyncVariableInt("m_SyncParts02");
2108 RegisterNetSyncVariableInt("m_SyncParts03");
2109 RegisterNetSyncVariableInt("m_InteractedPartId");
2110 RegisterNetSyncVariableInt("m_PerformedActionId");
2111 RegisterNetSyncVariableBool("m_HasBase");
2112
2113 //Construction init
2115
2116 if (ConfigIsExisting("hybridAttachments"))
2117 {
2119 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2120 }
2121 if (ConfigIsExisting("mountables"))
2122 {
2124 ConfigGetTextArray("mountables", m_Mountables);
2125 }
2126
2127 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2128 }
2129
2130 override void EEDelete(EntityAI parent)
2131 {
2132 super.EEDelete(parent);
2133
2134 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2136
2137 }
2138
2139 override string GetInvulnerabilityTypeString()
2140 {
2141 return "disableBaseDamage";
2142 }
2143
2144 override bool CanObstruct()
2145 {
2146 return true;
2147 }
2148
2149 override int GetHideIconMask()
2150 {
2151 return EInventoryIconVisibility.HIDE_VICINITY;
2152 }
2153
2154 // --- SYNCHRONIZATION
2156 {
2157 if (GetGame().IsServer())
2158 SetSynchDirty();
2159 }
2160
2161 override void OnVariablesSynchronized()
2162 {
2163 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2164 super.OnVariablesSynchronized();
2165
2166 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2167 }
2168
2169 protected void OnSynchronizedClient()
2170 {
2171 //update parts
2173
2174 //update action on part
2176
2177 //update visuals (client)
2178 UpdateVisuals();
2179 }
2180
2181 //parts synchronization
2183 {
2184 //part_id must starts from index = 1
2185 int offset;
2186 int mask;
2187
2188 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2189 {
2190 offset = part_id - 1;
2191 mask = 1 << offset;
2192
2194 }
2195 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2196 {
2197 offset = (part_id % 32);
2198 mask = 1 << offset;
2199
2201 }
2202 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2203 {
2204 offset = (part_id % 63);
2205 mask = 1 << offset;
2206
2208 }
2209 }
2210
2212 {
2213 //part_id must starts from index = 1
2214 int offset;
2215 int mask;
2216
2217 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2218 {
2219 offset = part_id - 1;
2220 mask = 1 << offset;
2221
2223 }
2224 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2225 {
2226 offset = (part_id % 32);
2227 mask = 1 << offset;
2228
2230 }
2231 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2232 {
2233 offset = (part_id % 63);
2234 mask = 1 << offset;
2235
2237 }
2238 }
2239
2241 {
2242 //part_id must starts from index = 1
2243 int offset;
2244 int mask;
2245
2246 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2247 {
2248 offset = part_id - 1;
2249 mask = 1 << offset;
2250
2251 if ((m_SyncParts01 & mask) > 0)
2252 return true;
2253 }
2254 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2255 {
2256 offset = (part_id % 32);
2257 mask = 1 << offset;
2258
2259 if ((m_SyncParts02 & mask) > 0)
2260 return true;
2261 }
2262 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2263 {
2264 offset = (part_id % 63);
2265 mask = 1 << offset;
2266
2267 if ((m_SyncParts03 & mask) > 0)
2268 return true;
2269 }
2270
2271 return false;
2272 }
2273
2274 protected void RegisterActionForSync(int part_id, int action_id)
2275 {
2278 }
2279
2280 protected void ResetActionSyncData()
2281 {
2282 //reset data
2283 m_InteractedPartId = -1;
2285 }
2286
2287 protected void SetActionFromSyncData()
2288 {
2289 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2290 {
2293
2294 switch (build_action_id)
2295 {
2299 }
2300 }
2301 }
2302 //------
2303
2305 {
2306 string key = part.m_PartName;
2307 bool is_base = part.IsBase();
2309 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2311 {
2312 if (!part.IsBuilt())
2313 {
2314 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2315 GetConstruction().AddToConstructedParts(key);
2316 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2317
2318 if (is_base)
2319 {
2321 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2322 }
2323 }
2324 }
2325 else
2326 {
2327 if (part.IsBuilt())
2328 {
2329 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2330 GetConstruction().RemoveFromConstructedParts(key);
2331 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2332
2333 if (is_base)
2334 {
2336 AddProxyPhysics(ANIMATION_DEPLOYED);
2337 }
2338 }
2339 }
2340
2341 //check slot lock for material attachments
2342 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2343 }
2344
2345 //set construction parts based on synchronized data
2347 {
2350
2351 for (int i = 0; i < construction_parts.Count(); ++i)
2352 {
2353 string key = construction_parts.GetKey(i);
2356 }
2357
2358 //regenerate navmesh
2359 UpdateNavmesh();
2360 }
2361
2363 {
2366
2367 for (int i = 0; i < construction_parts.Count(); ++i)
2368 {
2369 string key = construction_parts.GetKey(i);
2371
2372 if (value.GetId() == id)
2373 return value;
2374 }
2375
2376 return NULL;
2377 }
2378 //
2379
2380 //Base
2381 bool HasBase()
2382 {
2383 return m_HasBase;
2384 }
2385
2386 void SetBaseState(bool has_base)
2387 {
2389 }
2390
2391 override bool IsDeployable()
2392 {
2393 return true;
2394 }
2395
2396 bool IsOpened()
2397 {
2398 return false;
2399 }
2400
2401 //--- CONSTRUCTION KIT
2403 {
2407
2408 return construction_kit;
2409 }
2410
2412 {
2413 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2416 }
2417
2418 protected vector GetKitSpawnPosition()
2419 {
2420 return GetPosition();
2421 }
2422
2423 protected string GetConstructionKitType()
2424 {
2425 return "";
2426 }
2427
2429 {
2431 GetGame().ObjectDelete(construction_kit);
2432 }
2433
2434 //--- CONSTRUCTION
2435 void DestroyConstruction()
2436 {
2437 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2438 GetGame().ObjectDelete(this);
2439 }
2440
2441 // --- EVENTS
2442 override void OnStoreSave(ParamsWriteContext ctx)
2443 {
2444 super.OnStoreSave(ctx);
2445
2446 //sync parts 01
2447 ctx.Write(m_SyncParts01);
2448 ctx.Write(m_SyncParts02);
2449 ctx.Write(m_SyncParts03);
2450
2451 ctx.Write(m_HasBase);
2452 }
2453
2454 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2455 {
2456 if (!super.OnStoreLoad(ctx, version))
2457 return false;
2458
2459 //--- Base building data ---
2460 //Restore synced parts data
2461 if (!ctx.Read(m_SyncParts01))
2462 {
2463 m_SyncParts01 = 0; //set default
2464 return false;
2465 }
2466 if (!ctx.Read(m_SyncParts02))
2467 {
2468 m_SyncParts02 = 0; //set default
2469 return false;
2470 }
2471 if (!ctx.Read(m_SyncParts03))
2472 {
2473 m_SyncParts03 = 0; //set default
2474 return false;
2475 }
2476
2477 //has base
2478 if (!ctx.Read(m_HasBase))
2479 {
2480 m_HasBase = false;
2481 return false;
2482 }
2483 //---
2484
2485 return true;
2486 }
2487
2488 override void AfterStoreLoad()
2489 {
2490 super.AfterStoreLoad();
2491
2494 }
2495
2497 {
2498 //update server data
2500
2501 //set base state
2502 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2503 SetBaseState(construction_part.IsBuilt()) ;
2504
2505 //synchronize after load
2507 }
2508
2509 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2510 {
2512 return;
2513
2514 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2515
2516 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2517 return;
2518
2520 string part_name = zone;
2521 part_name.ToLower();
2522
2524 {
2526
2527 if (construction_part && construction.IsPartConstructed(part_name))
2528 {
2529 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2530 construction.DestroyConnectedParts(part_name);
2531 }
2532
2533 //barbed wire handling (hack-ish)
2534 if (part_name.Contains("barbed"))
2535 {
2536 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2537 if (barbed_wire)
2538 barbed_wire.SetMountedState(false);
2539 }
2540 }
2541 }
2542
2543 override void EEOnAfterLoad()
2544 {
2546 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2547
2548 super.EEOnAfterLoad();
2549 }
2550
2551 override void EEInit()
2552 {
2553 super.EEInit();
2554
2555 // init visuals and physics
2556 InitBaseState();
2557
2558 //debug
2559#ifdef DEVELOPER
2561#endif
2562 }
2563
2564 override void EEItemAttached(EntityAI item, string slot_name)
2565 {
2566 super.EEItemAttached(item, slot_name);
2567
2569 UpdateVisuals();
2571 }
2572
2573 override void EEItemDetached(EntityAI item, string slot_name)
2574 {
2575 super.EEItemDetached(item, slot_name);
2576
2577 UpdateVisuals();
2579 }
2580
2581 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2582 {
2584 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2585
2588 }
2589
2590 //ignore out of reach condition
2591 override bool IgnoreOutOfReachCondition()
2592 {
2593 return true;
2594 }
2595
2596 //CONSTRUCTION EVENTS
2597 //Build
2598 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2599 {
2601
2602 //check base state
2603 if (construtionPart.IsBase())
2604 {
2605 SetBaseState(true);
2606
2607 //spawn kit
2609 }
2610
2611 //register constructed parts for synchronization
2613
2614 //register action that was performed on part
2616
2617 //synchronize
2619
2620 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2621
2622 UpdateNavmesh();
2623
2624 //update visuals
2625 UpdateVisuals();
2626
2627 //reset action sync data
2628 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2629 }
2630
2631 void OnPartBuiltClient(string part_name, int action_id)
2632 {
2633 //play sound
2635 }
2636
2637 //Dismantle
2639 {
2640 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2642
2643 //register constructed parts for synchronization
2645
2646 //register action that was performed on part
2648
2649 //synchronize
2651
2652 // server part of sync, client will be synced from SetPartsFromSyncData
2654
2655 UpdateNavmesh();
2656
2657 //update visuals
2658 UpdateVisuals();
2659
2660 //reset action sync data
2661 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2662
2663 //check base state
2664 if (construtionPart.IsBase())
2665 {
2666 //Destroy construction
2667 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2668 }
2669 }
2670
2672 {
2673 //play sound
2675 }
2676
2677 //Destroy
2679 {
2680 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2682
2683 //register constructed parts for synchronization
2685
2686 //register action that was performed on part
2688
2689 //synchronize
2691
2692 // server part of sync, client will be synced from SetPartsFromSyncData
2694
2695 UpdateNavmesh();
2696
2697 //update visuals
2698 UpdateVisuals();
2699
2700 //reset action sync data
2701 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2702
2703 //check base state
2704 if (construtionPart.IsBase())
2705 {
2706 //Destroy construction
2707 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2708 }
2709 }
2710
2711 void OnPartDestroyedClient(string part_name, int action_id)
2712 {
2713 //play sound
2715 }
2716
2717 // --- UPDATE
2718 void InitBaseState()
2719 {
2720 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2721
2722 InitVisuals();
2723 UpdateNavmesh(); //regenerate navmesh
2724 GetConstruction().InitBaseState();
2725 }
2726
2727 void InitVisuals()
2728 {
2729 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2730 //check base
2731 if (!HasBase())
2732 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2733 else
2734 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2735
2736 GetConstruction().UpdateVisuals();
2737 }
2738
2739 void UpdateVisuals()
2740 {
2742
2744 foreach (string slotName : attachmentSlots)
2746
2747 //check base
2748 if (!HasBase())
2749 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2750 else
2751 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2752
2753 GetConstruction().UpdateVisuals();
2754 }
2755
2757 {
2758 string slotNameMounted = slot_name + "_Mounted";
2759 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2760
2761 if (attachment)
2762 {
2763 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2764 if (barbedWire && barbedWire.IsMounted())
2766 else
2768
2769 if (is_locked)
2770 {
2771 SetAnimationPhase(slotNameMounted, 0);
2772 SetAnimationPhase(slot_name, 1);
2773 }
2774 else
2775 {
2776 SetAnimationPhase(slotNameMounted, 1);
2777 SetAnimationPhase(slot_name, 0);
2778 }
2779 }
2780 else
2781 {
2782 SetAnimationPhase(slotNameMounted, 1);
2783 SetAnimationPhase(slot_name, 1);
2784
2786 }
2787 }
2788
2789 // avoid calling this function on frequent occasions, it's a massive performance hit
2790 void UpdatePhysics()
2791 {
2793 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2794
2797
2799 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2800
2801 foreach (string slotName : attachmentSlots)
2803
2804 //check base
2805 if (!HasBase())
2806 {
2808 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2809
2810 AddProxyPhysics(ANIMATION_DEPLOYED);
2811 }
2812 else
2813 {
2815 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2816
2817 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2818 }
2819
2820 GetConstruction().UpdatePhysics();
2821 UpdateNavmesh();
2822 }
2823
2825 {
2826 //checks for invalid appends; hotfix
2827 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2828 return;
2829 //----------------------------------
2830 string slot_name_mounted = slot_name + "_Mounted";
2831 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2832
2833 //remove proxy physics
2834 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2835 RemoveProxyPhysics(slot_name_mounted);
2836 RemoveProxyPhysics(slot_name);
2837
2838 if (attachment)
2839 {
2840 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2841 if (is_locked)
2842 {
2843 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2844 AddProxyPhysics(slot_name_mounted);
2845 }
2846 else
2847 {
2848 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2849 AddProxyPhysics(slot_name);
2850 }
2851 }
2852 }
2853
2854 protected void UpdateNavmesh()
2855 {
2856 SetAffectPathgraph(true, false);
2857 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2858 }
2859
2860 override bool CanUseConstruction()
2861 {
2862 return true;
2863 }
2864
2865 override bool CanUseConstructionBuild()
2866 {
2867 return true;
2868 }
2869
2871 {
2872 if (attachment)
2873 {
2875 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2876
2877 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2878 }
2879
2880 return false;
2881 }
2882
2883 protected bool IsAttachmentSlotLocked(string slot_name)
2884 {
2885 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2886 }
2887
2888 //--- ATTACHMENT SLOTS
2890 {
2891 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2892 if (GetGame().ConfigIsExisting(config_path))
2893 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2894 }
2895
2897 {
2898 return true;
2899 }
2900
2901 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2902 {
2903 return true;
2904 }
2905
2906 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2907 {
2908 return true;
2909 }
2910
2911 // --- INIT
2912 void ConstructionInit()
2913 {
2914 if (!m_Construction)
2915 m_Construction = new Construction(this);
2916
2917 GetConstruction().Init();
2918 }
2919
2921 {
2922 return m_Construction;
2923 }
2924
2925 //--- INVENTORY/ATTACHMENTS CONDITIONS
2926 //attachments
2928 {
2929 return super.CanReceiveAttachment(attachment, slotId);
2930 }
2931
2933 {
2934 int attachment_count = GetInventory().AttachmentCount();
2935 if (attachment_count > 0)
2936 {
2937 if (HasBase() && attachment_count == 1)
2938 return false;
2939
2940 return true;
2941 }
2942
2943 return false;
2944 }
2945
2946 override bool ShowZonesHealth()
2947 {
2948 return true;
2949 }
2950
2951 //this into/outo parent.Cargo
2952 override bool CanPutInCargo(EntityAI parent)
2953 {
2954 return false;
2955 }
2956
2957 override bool CanRemoveFromCargo(EntityAI parent)
2958 {
2959 return false;
2960 }
2961
2962 //hands
2963 override bool CanPutIntoHands(EntityAI parent)
2964 {
2965 return false;
2966 }
2967
2968 //--- ACTION CONDITIONS
2969 //direction
2970 override bool IsFacingPlayer(PlayerBase player, string selection)
2971 {
2972 return true;
2973 }
2974
2975 override bool IsPlayerInside(PlayerBase player, string selection)
2976 {
2977 return true;
2978 }
2979
2982 {
2983 return false;
2984 }
2985
2986 //camera direction check
2987 bool IsFacingCamera(string selection)
2988 {
2989 return true;
2990 }
2991
2992 //roof check
2994 {
2995 return false;
2996 }
2997
2998 //selection->player distance check
2999 bool HasProperDistance(string selection, PlayerBase player)
3000 {
3001 return true;
3002 }
3003
3004 //folding
3006 {
3007 if (HasBase() || GetInventory().AttachmentCount() > 0)
3008 return false;
3009
3010 return true;
3011 }
3012
3014 {
3017
3018 return item;
3019 }
3020
3021 //Damage triggers (barbed wire)
3022 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3023 {
3024 if (GetGame() && GetGame().IsServer())
3025 {
3026 //destroy area damage if some already exists
3028
3029 //create new area damage
3031 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3032
3033 vector min_max[2];
3034 if (MemoryPointExists(slot_name + "_min"))
3035 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3036 if (MemoryPointExists(slot_name + "_max"))
3037 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3038
3039 //get proper trigger extents (min<max)
3040 vector extents[2];
3041 GetConstruction().GetTriggerExtents(min_max, extents);
3042
3043 //get box center
3044 vector center;
3045 center = GetConstruction().GetBoxCenter(min_max);
3046 center = ModelToWorld(center);
3047
3048 //rotate center if needed
3051
3052 areaDamage.SetExtents(extents[0], extents[1]);
3053 areaDamage.SetAreaPosition(center);
3054 areaDamage.SetAreaOrientation(orientation);
3055 areaDamage.SetLoopInterval(1.0);
3056 areaDamage.SetDeferDuration(0.2);
3057 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3058 areaDamage.SetAmmoName("BarbedWireHit");
3059 areaDamage.Spawn();
3060
3062 }
3063 }
3064
3066 {
3067 if (angle_deg != 0)
3068 {
3069 //orientation
3071
3072 //center
3074 if (MemoryPointExists("rotate_axis"))
3075 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3078 center[0] = r_center_x;
3079 center[2] = r_center_z;
3080 }
3081 }
3082
3083 void DestroyAreaDamage(string slot_name)
3084 {
3085 if (GetGame() && GetGame().IsServer())
3086 {
3089 {
3090 if (areaDamage)
3091 areaDamage.Destroy();
3092
3094 }
3095 }
3096 }
3097
3098 override bool IsIgnoredByConstruction()
3099 {
3100 return true;
3101 }
3102
3103 //================================================================
3104 // SOUNDS
3105 //================================================================
3106 protected void SoundBuildStart(string part_name)
3107 {
3108 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3109 }
3110
3111 protected void SoundDismantleStart(string part_name)
3112 {
3113 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3114 }
3115
3116 protected void SoundDestroyStart(string part_name)
3117 {
3118 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3119 }
3120
3121 protected string GetBuildSoundByMaterial(string part_name)
3122 {
3124
3125 switch (material_type)
3126 {
3127 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3128 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3129 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3130 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3131 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3132 }
3133
3134 return "";
3135 }
3136
3137 protected string GetDismantleSoundByMaterial(string part_name)
3138 {
3140
3141 switch (material_type)
3142 {
3143 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3144 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3145 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3146 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3147 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3148 }
3149
3150 return "";
3151 }
3152
3153 //misc
3155 {
3156 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3157 {
3158 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3160 SetHealth(slot_name, "Health", item.GetHealth());
3161 }
3162 }
3163
3164 override int GetDamageSystemVersionChange()
3165 {
3166 return 111;
3167 }
3168
3169 override void SetActions()
3170 {
3171 super.SetActions();
3172
3174 //AddAction(ActionTakeHybridAttachment);
3175 //AddAction(ActionTakeHybridAttachmentToHands);
3178 }
3179
3180 //================================================================
3181 // DEBUG
3182 //================================================================
3183 protected void DebugCustomState()
3184 {
3185 }
3186
3189 {
3190 return null;
3191 }
3192
3193 override void OnDebugSpawn()
3194 {
3195 FullyBuild();
3196 }
3197
3198 void FullyBuild()
3199 {
3201 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3202
3203 Man p;
3204
3205#ifdef SERVER
3207 GetGame().GetWorld().GetPlayerList(players);
3208 if (players.Count())
3209 p = players[0];
3210#else
3211 p = GetGame().GetPlayer();
3212#endif
3213
3214 foreach (ConstructionPart part : parts)
3215 {
3216 bool excluded = false;
3217 string partName = part.GetPartName();
3218 if (excludes)
3219 {
3220 foreach (string exclude : excludes)
3221 {
3222 if (partName.Contains(exclude))
3223 {
3224 excluded = true;
3225 break;
3226 }
3227 }
3228 }
3229
3230 if (!excluded)
3232 }
3233
3234 GetConstruction().UpdateVisuals();
3235 }
3236}
3237
3238void bsbDebugPrint(string s)
3239{
3240#ifdef BSB_DEBUG
3241 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3242#else
3243 //Print("" + s); // comment/uncomment to hide/see debug logs
3244#endif
3245}
3246void bsbDebugSpam(string s)
3247{
3248#ifdef BSB_DEBUG_SPAM
3249 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3250#else
3251 //Print("" + s); // comment/uncomment to hide/see debug logs
3252#endif
3253}

◆ CanPutIntoHands()

override bool bsbDebugPrint::CanPutIntoHands ( EntityAI parent)
protected

Definition at line 2074 of file BaseBuildingBase.c.

2076{
2077 const string ANIMATION_DEPLOYED = "Deployed";
2078
2079 float m_ConstructionKitHealth; //stored health value for used construction kit
2080
2082
2083 bool m_HasBase;
2084 //variables for synchronization of base building parts (2x31 is the current limit)
2085 int m_SyncParts01; //synchronization for already built parts (31 parts)
2086 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2087 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2088 int m_InteractedPartId; //construction part id that an action was performed on
2089 int m_PerformedActionId; //action id that was performed on a construction part
2090
2091 //Sounds
2092 //build
2093 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2094 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2095 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2096 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2097 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2098 //dismantle
2099 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2100 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2101 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2102 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2103 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2104
2105 protected EffectSound m_Sound;
2106
2110
2111 // Constructor
2112 void BaseBuildingBase()
2113 {
2115
2116 //synchronized variables
2117 RegisterNetSyncVariableInt("m_SyncParts01");
2118 RegisterNetSyncVariableInt("m_SyncParts02");
2119 RegisterNetSyncVariableInt("m_SyncParts03");
2120 RegisterNetSyncVariableInt("m_InteractedPartId");
2121 RegisterNetSyncVariableInt("m_PerformedActionId");
2122 RegisterNetSyncVariableBool("m_HasBase");
2123
2124 //Construction init
2126
2127 if (ConfigIsExisting("hybridAttachments"))
2128 {
2130 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2131 }
2132 if (ConfigIsExisting("mountables"))
2133 {
2135 ConfigGetTextArray("mountables", m_Mountables);
2136 }
2137
2138 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2139 }
2140
2141 override void EEDelete(EntityAI parent)
2142 {
2143 super.EEDelete(parent);
2144
2145 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2147
2148 }
2149
2150 override string GetInvulnerabilityTypeString()
2151 {
2152 return "disableBaseDamage";
2153 }
2154
2155 override bool CanObstruct()
2156 {
2157 return true;
2158 }
2159
2160 override int GetHideIconMask()
2161 {
2162 return EInventoryIconVisibility.HIDE_VICINITY;
2163 }
2164
2165 // --- SYNCHRONIZATION
2167 {
2168 if (GetGame().IsServer())
2169 SetSynchDirty();
2170 }
2171
2172 override void OnVariablesSynchronized()
2173 {
2174 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2175 super.OnVariablesSynchronized();
2176
2177 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2178 }
2179
2180 protected void OnSynchronizedClient()
2181 {
2182 //update parts
2184
2185 //update action on part
2187
2188 //update visuals (client)
2189 UpdateVisuals();
2190 }
2191
2192 //parts synchronization
2194 {
2195 //part_id must starts from index = 1
2196 int offset;
2197 int mask;
2198
2199 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2200 {
2201 offset = part_id - 1;
2202 mask = 1 << offset;
2203
2205 }
2206 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2207 {
2208 offset = (part_id % 32);
2209 mask = 1 << offset;
2210
2212 }
2213 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2214 {
2215 offset = (part_id % 63);
2216 mask = 1 << offset;
2217
2219 }
2220 }
2221
2223 {
2224 //part_id must starts from index = 1
2225 int offset;
2226 int mask;
2227
2228 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2229 {
2230 offset = part_id - 1;
2231 mask = 1 << offset;
2232
2234 }
2235 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2236 {
2237 offset = (part_id % 32);
2238 mask = 1 << offset;
2239
2241 }
2242 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2243 {
2244 offset = (part_id % 63);
2245 mask = 1 << offset;
2246
2248 }
2249 }
2250
2252 {
2253 //part_id must starts from index = 1
2254 int offset;
2255 int mask;
2256
2257 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2258 {
2259 offset = part_id - 1;
2260 mask = 1 << offset;
2261
2262 if ((m_SyncParts01 & mask) > 0)
2263 return true;
2264 }
2265 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2266 {
2267 offset = (part_id % 32);
2268 mask = 1 << offset;
2269
2270 if ((m_SyncParts02 & mask) > 0)
2271 return true;
2272 }
2273 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2274 {
2275 offset = (part_id % 63);
2276 mask = 1 << offset;
2277
2278 if ((m_SyncParts03 & mask) > 0)
2279 return true;
2280 }
2281
2282 return false;
2283 }
2284
2285 protected void RegisterActionForSync(int part_id, int action_id)
2286 {
2289 }
2290
2291 protected void ResetActionSyncData()
2292 {
2293 //reset data
2294 m_InteractedPartId = -1;
2296 }
2297
2298 protected void SetActionFromSyncData()
2299 {
2300 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2301 {
2304
2305 switch (build_action_id)
2306 {
2310 }
2311 }
2312 }
2313 //------
2314
2316 {
2317 string key = part.m_PartName;
2318 bool is_base = part.IsBase();
2320 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2322 {
2323 if (!part.IsBuilt())
2324 {
2325 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2326 GetConstruction().AddToConstructedParts(key);
2327 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2328
2329 if (is_base)
2330 {
2332 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2333 }
2334 }
2335 }
2336 else
2337 {
2338 if (part.IsBuilt())
2339 {
2340 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2341 GetConstruction().RemoveFromConstructedParts(key);
2342 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2343
2344 if (is_base)
2345 {
2347 AddProxyPhysics(ANIMATION_DEPLOYED);
2348 }
2349 }
2350 }
2351
2352 //check slot lock for material attachments
2353 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2354 }
2355
2356 //set construction parts based on synchronized data
2358 {
2361
2362 for (int i = 0; i < construction_parts.Count(); ++i)
2363 {
2364 string key = construction_parts.GetKey(i);
2367 }
2368
2369 //regenerate navmesh
2370 UpdateNavmesh();
2371 }
2372
2374 {
2377
2378 for (int i = 0; i < construction_parts.Count(); ++i)
2379 {
2380 string key = construction_parts.GetKey(i);
2382
2383 if (value.GetId() == id)
2384 return value;
2385 }
2386
2387 return NULL;
2388 }
2389 //
2390
2391 //Base
2392 bool HasBase()
2393 {
2394 return m_HasBase;
2395 }
2396
2397 void SetBaseState(bool has_base)
2398 {
2400 }
2401
2402 override bool IsDeployable()
2403 {
2404 return true;
2405 }
2406
2407 bool IsOpened()
2408 {
2409 return false;
2410 }
2411
2412 //--- CONSTRUCTION KIT
2414 {
2418
2419 return construction_kit;
2420 }
2421
2423 {
2424 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2427 }
2428
2429 protected vector GetKitSpawnPosition()
2430 {
2431 return GetPosition();
2432 }
2433
2434 protected string GetConstructionKitType()
2435 {
2436 return "";
2437 }
2438
2440 {
2442 GetGame().ObjectDelete(construction_kit);
2443 }
2444
2445 //--- CONSTRUCTION
2446 void DestroyConstruction()
2447 {
2448 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2449 GetGame().ObjectDelete(this);
2450 }
2451
2452 // --- EVENTS
2453 override void OnStoreSave(ParamsWriteContext ctx)
2454 {
2455 super.OnStoreSave(ctx);
2456
2457 //sync parts 01
2458 ctx.Write(m_SyncParts01);
2459 ctx.Write(m_SyncParts02);
2460 ctx.Write(m_SyncParts03);
2461
2462 ctx.Write(m_HasBase);
2463 }
2464
2465 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2466 {
2467 if (!super.OnStoreLoad(ctx, version))
2468 return false;
2469
2470 //--- Base building data ---
2471 //Restore synced parts data
2472 if (!ctx.Read(m_SyncParts01))
2473 {
2474 m_SyncParts01 = 0; //set default
2475 return false;
2476 }
2477 if (!ctx.Read(m_SyncParts02))
2478 {
2479 m_SyncParts02 = 0; //set default
2480 return false;
2481 }
2482 if (!ctx.Read(m_SyncParts03))
2483 {
2484 m_SyncParts03 = 0; //set default
2485 return false;
2486 }
2487
2488 //has base
2489 if (!ctx.Read(m_HasBase))
2490 {
2491 m_HasBase = false;
2492 return false;
2493 }
2494 //---
2495
2496 return true;
2497 }
2498
2499 override void AfterStoreLoad()
2500 {
2501 super.AfterStoreLoad();
2502
2505 }
2506
2508 {
2509 //update server data
2511
2512 //set base state
2513 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2514 SetBaseState(construction_part.IsBuilt()) ;
2515
2516 //synchronize after load
2518 }
2519
2520 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2521 {
2523 return;
2524
2525 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2526
2527 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2528 return;
2529
2531 string part_name = zone;
2532 part_name.ToLower();
2533
2535 {
2537
2538 if (construction_part && construction.IsPartConstructed(part_name))
2539 {
2540 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2541 construction.DestroyConnectedParts(part_name);
2542 }
2543
2544 //barbed wire handling (hack-ish)
2545 if (part_name.Contains("barbed"))
2546 {
2547 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2548 if (barbed_wire)
2549 barbed_wire.SetMountedState(false);
2550 }
2551 }
2552 }
2553
2554 override void EEOnAfterLoad()
2555 {
2557 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2558
2559 super.EEOnAfterLoad();
2560 }
2561
2562 override void EEInit()
2563 {
2564 super.EEInit();
2565
2566 // init visuals and physics
2567 InitBaseState();
2568
2569 //debug
2570#ifdef DEVELOPER
2572#endif
2573 }
2574
2575 override void EEItemAttached(EntityAI item, string slot_name)
2576 {
2577 super.EEItemAttached(item, slot_name);
2578
2580 UpdateVisuals();
2582 }
2583
2584 override void EEItemDetached(EntityAI item, string slot_name)
2585 {
2586 super.EEItemDetached(item, slot_name);
2587
2588 UpdateVisuals();
2590 }
2591
2592 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2593 {
2595 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2596
2599 }
2600
2601 //ignore out of reach condition
2602 override bool IgnoreOutOfReachCondition()
2603 {
2604 return true;
2605 }
2606
2607 //CONSTRUCTION EVENTS
2608 //Build
2609 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2610 {
2612
2613 //check base state
2614 if (construtionPart.IsBase())
2615 {
2616 SetBaseState(true);
2617
2618 //spawn kit
2620 }
2621
2622 //register constructed parts for synchronization
2624
2625 //register action that was performed on part
2627
2628 //synchronize
2630
2631 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2632
2633 UpdateNavmesh();
2634
2635 //update visuals
2636 UpdateVisuals();
2637
2638 //reset action sync data
2639 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2640 }
2641
2642 void OnPartBuiltClient(string part_name, int action_id)
2643 {
2644 //play sound
2646 }
2647
2648 //Dismantle
2650 {
2651 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2653
2654 //register constructed parts for synchronization
2656
2657 //register action that was performed on part
2659
2660 //synchronize
2662
2663 // server part of sync, client will be synced from SetPartsFromSyncData
2665
2666 UpdateNavmesh();
2667
2668 //update visuals
2669 UpdateVisuals();
2670
2671 //reset action sync data
2672 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2673
2674 //check base state
2675 if (construtionPart.IsBase())
2676 {
2677 //Destroy construction
2678 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2679 }
2680 }
2681
2683 {
2684 //play sound
2686 }
2687
2688 //Destroy
2690 {
2691 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2693
2694 //register constructed parts for synchronization
2696
2697 //register action that was performed on part
2699
2700 //synchronize
2702
2703 // server part of sync, client will be synced from SetPartsFromSyncData
2705
2706 UpdateNavmesh();
2707
2708 //update visuals
2709 UpdateVisuals();
2710
2711 //reset action sync data
2712 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2713
2714 //check base state
2715 if (construtionPart.IsBase())
2716 {
2717 //Destroy construction
2718 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2719 }
2720 }
2721
2722 void OnPartDestroyedClient(string part_name, int action_id)
2723 {
2724 //play sound
2726 }
2727
2728 // --- UPDATE
2729 void InitBaseState()
2730 {
2731 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2732
2733 InitVisuals();
2734 UpdateNavmesh(); //regenerate navmesh
2735 GetConstruction().InitBaseState();
2736 }
2737
2738 void InitVisuals()
2739 {
2740 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2741 //check base
2742 if (!HasBase())
2743 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2744 else
2745 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2746
2747 GetConstruction().UpdateVisuals();
2748 }
2749
2750 void UpdateVisuals()
2751 {
2753
2755 foreach (string slotName : attachmentSlots)
2757
2758 //check base
2759 if (!HasBase())
2760 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2761 else
2762 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2763
2764 GetConstruction().UpdateVisuals();
2765 }
2766
2768 {
2769 string slotNameMounted = slot_name + "_Mounted";
2770 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2771
2772 if (attachment)
2773 {
2774 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2775 if (barbedWire && barbedWire.IsMounted())
2777 else
2779
2780 if (is_locked)
2781 {
2782 SetAnimationPhase(slotNameMounted, 0);
2783 SetAnimationPhase(slot_name, 1);
2784 }
2785 else
2786 {
2787 SetAnimationPhase(slotNameMounted, 1);
2788 SetAnimationPhase(slot_name, 0);
2789 }
2790 }
2791 else
2792 {
2793 SetAnimationPhase(slotNameMounted, 1);
2794 SetAnimationPhase(slot_name, 1);
2795
2797 }
2798 }
2799
2800 // avoid calling this function on frequent occasions, it's a massive performance hit
2801 void UpdatePhysics()
2802 {
2804 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2805
2808
2810 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2811
2812 foreach (string slotName : attachmentSlots)
2814
2815 //check base
2816 if (!HasBase())
2817 {
2819 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2820
2821 AddProxyPhysics(ANIMATION_DEPLOYED);
2822 }
2823 else
2824 {
2826 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2827
2828 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2829 }
2830
2831 GetConstruction().UpdatePhysics();
2832 UpdateNavmesh();
2833 }
2834
2836 {
2837 //checks for invalid appends; hotfix
2838 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2839 return;
2840 //----------------------------------
2841 string slot_name_mounted = slot_name + "_Mounted";
2842 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2843
2844 //remove proxy physics
2845 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2846 RemoveProxyPhysics(slot_name_mounted);
2847 RemoveProxyPhysics(slot_name);
2848
2849 if (attachment)
2850 {
2851 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2852 if (is_locked)
2853 {
2854 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2855 AddProxyPhysics(slot_name_mounted);
2856 }
2857 else
2858 {
2859 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2860 AddProxyPhysics(slot_name);
2861 }
2862 }
2863 }
2864
2865 protected void UpdateNavmesh()
2866 {
2867 SetAffectPathgraph(true, false);
2868 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2869 }
2870
2871 override bool CanUseConstruction()
2872 {
2873 return true;
2874 }
2875
2876 override bool CanUseConstructionBuild()
2877 {
2878 return true;
2879 }
2880
2882 {
2883 if (attachment)
2884 {
2886 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2887
2888 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2889 }
2890
2891 return false;
2892 }
2893
2894 protected bool IsAttachmentSlotLocked(string slot_name)
2895 {
2896 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2897 }
2898
2899 //--- ATTACHMENT SLOTS
2901 {
2902 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2903 if (GetGame().ConfigIsExisting(config_path))
2904 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2905 }
2906
2908 {
2909 return true;
2910 }
2911
2912 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2913 {
2914 return true;
2915 }
2916
2917 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2918 {
2919 return true;
2920 }
2921
2922 // --- INIT
2923 void ConstructionInit()
2924 {
2925 if (!m_Construction)
2926 m_Construction = new Construction(this);
2927
2928 GetConstruction().Init();
2929 }
2930
2932 {
2933 return m_Construction;
2934 }
2935
2936 //--- INVENTORY/ATTACHMENTS CONDITIONS
2937 //attachments
2939 {
2940 return super.CanReceiveAttachment(attachment, slotId);
2941 }
2942
2944 {
2945 int attachment_count = GetInventory().AttachmentCount();
2946 if (attachment_count > 0)
2947 {
2948 if (HasBase() && attachment_count == 1)
2949 return false;
2950
2951 return true;
2952 }
2953
2954 return false;
2955 }
2956
2957 override bool ShowZonesHealth()
2958 {
2959 return true;
2960 }
2961
2962 //this into/outo parent.Cargo
2963 override bool CanPutInCargo(EntityAI parent)
2964 {
2965 return false;
2966 }
2967
2968 override bool CanRemoveFromCargo(EntityAI parent)
2969 {
2970 return false;
2971 }
2972
2973 //hands
2974 override bool CanPutIntoHands(EntityAI parent)
2975 {
2976 return false;
2977 }
2978
2979 //--- ACTION CONDITIONS
2980 //direction
2981 override bool IsFacingPlayer(PlayerBase player, string selection)
2982 {
2983 return true;
2984 }
2985
2986 override bool IsPlayerInside(PlayerBase player, string selection)
2987 {
2988 return true;
2989 }
2990
2993 {
2994 return false;
2995 }
2996
2997 //camera direction check
2998 bool IsFacingCamera(string selection)
2999 {
3000 return true;
3001 }
3002
3003 //roof check
3005 {
3006 return false;
3007 }
3008
3009 //selection->player distance check
3010 bool HasProperDistance(string selection, PlayerBase player)
3011 {
3012 return true;
3013 }
3014
3015 //folding
3017 {
3018 if (HasBase() || GetInventory().AttachmentCount() > 0)
3019 return false;
3020
3021 return true;
3022 }
3023
3025 {
3028
3029 return item;
3030 }
3031
3032 //Damage triggers (barbed wire)
3033 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3034 {
3035 if (GetGame() && GetGame().IsServer())
3036 {
3037 //destroy area damage if some already exists
3039
3040 //create new area damage
3042 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3043
3044 vector min_max[2];
3045 if (MemoryPointExists(slot_name + "_min"))
3046 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3047 if (MemoryPointExists(slot_name + "_max"))
3048 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3049
3050 //get proper trigger extents (min<max)
3051 vector extents[2];
3052 GetConstruction().GetTriggerExtents(min_max, extents);
3053
3054 //get box center
3055 vector center;
3056 center = GetConstruction().GetBoxCenter(min_max);
3057 center = ModelToWorld(center);
3058
3059 //rotate center if needed
3062
3063 areaDamage.SetExtents(extents[0], extents[1]);
3064 areaDamage.SetAreaPosition(center);
3065 areaDamage.SetAreaOrientation(orientation);
3066 areaDamage.SetLoopInterval(1.0);
3067 areaDamage.SetDeferDuration(0.2);
3068 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3069 areaDamage.SetAmmoName("BarbedWireHit");
3070 areaDamage.Spawn();
3071
3073 }
3074 }
3075
3077 {
3078 if (angle_deg != 0)
3079 {
3080 //orientation
3082
3083 //center
3085 if (MemoryPointExists("rotate_axis"))
3086 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3089 center[0] = r_center_x;
3090 center[2] = r_center_z;
3091 }
3092 }
3093
3094 void DestroyAreaDamage(string slot_name)
3095 {
3096 if (GetGame() && GetGame().IsServer())
3097 {
3100 {
3101 if (areaDamage)
3102 areaDamage.Destroy();
3103
3105 }
3106 }
3107 }
3108
3109 override bool IsIgnoredByConstruction()
3110 {
3111 return true;
3112 }
3113
3114 //================================================================
3115 // SOUNDS
3116 //================================================================
3117 protected void SoundBuildStart(string part_name)
3118 {
3119 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3120 }
3121
3122 protected void SoundDismantleStart(string part_name)
3123 {
3124 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3125 }
3126
3127 protected void SoundDestroyStart(string part_name)
3128 {
3129 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3130 }
3131
3132 protected string GetBuildSoundByMaterial(string part_name)
3133 {
3135
3136 switch (material_type)
3137 {
3138 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3139 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3140 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3141 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3142 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3143 }
3144
3145 return "";
3146 }
3147
3148 protected string GetDismantleSoundByMaterial(string part_name)
3149 {
3151
3152 switch (material_type)
3153 {
3154 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3155 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3156 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3157 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3158 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3159 }
3160
3161 return "";
3162 }
3163
3164 //misc
3166 {
3167 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3168 {
3169 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3171 SetHealth(slot_name, "Health", item.GetHealth());
3172 }
3173 }
3174
3175 override int GetDamageSystemVersionChange()
3176 {
3177 return 111;
3178 }
3179
3180 override void SetActions()
3181 {
3182 super.SetActions();
3183
3185 //AddAction(ActionTakeHybridAttachment);
3186 //AddAction(ActionTakeHybridAttachmentToHands);
3189 }
3190
3191 //================================================================
3192 // DEBUG
3193 //================================================================
3194 protected void DebugCustomState()
3195 {
3196 }
3197
3200 {
3201 return null;
3202 }
3203
3204 override void OnDebugSpawn()
3205 {
3206 FullyBuild();
3207 }
3208
3209 void FullyBuild()
3210 {
3212 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3213
3214 Man p;
3215
3216#ifdef SERVER
3218 GetGame().GetWorld().GetPlayerList(players);
3219 if (players.Count())
3220 p = players[0];
3221#else
3222 p = GetGame().GetPlayer();
3223#endif
3224
3225 foreach (ConstructionPart part : parts)
3226 {
3227 bool excluded = false;
3228 string partName = part.GetPartName();
3229 if (excludes)
3230 {
3231 foreach (string exclude : excludes)
3232 {
3233 if (partName.Contains(exclude))
3234 {
3235 excluded = true;
3236 break;
3237 }
3238 }
3239 }
3240
3241 if (!excluded)
3243 }
3244
3245 GetConstruction().UpdateVisuals();
3246 }
3247}
3248
3249void bsbDebugPrint(string s)
3250{
3251#ifdef BSB_DEBUG
3252 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3253#else
3254 //Print("" + s); // comment/uncomment to hide/see debug logs
3255#endif
3256}
3257void bsbDebugSpam(string s)
3258{
3259#ifdef BSB_DEBUG_SPAM
3260 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3261#else
3262 //Print("" + s); // comment/uncomment to hide/see debug logs
3263#endif
3264}

◆ CanReceiveAttachment()

override bool bsbDebugPrint::CanReceiveAttachment ( EntityAI attachment,
int slotId )
protected

Definition at line 2038 of file BaseBuildingBase.c.

2040{
2041 const string ANIMATION_DEPLOYED = "Deployed";
2042
2043 float m_ConstructionKitHealth; //stored health value for used construction kit
2044
2046
2047 bool m_HasBase;
2048 //variables for synchronization of base building parts (2x31 is the current limit)
2049 int m_SyncParts01; //synchronization for already built parts (31 parts)
2050 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2051 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2052 int m_InteractedPartId; //construction part id that an action was performed on
2053 int m_PerformedActionId; //action id that was performed on a construction part
2054
2055 //Sounds
2056 //build
2057 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2058 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2059 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2060 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2061 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2062 //dismantle
2063 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2064 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2065 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2066 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2067 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2068
2069 protected EffectSound m_Sound;
2070
2074
2075 // Constructor
2076 void BaseBuildingBase()
2077 {
2079
2080 //synchronized variables
2081 RegisterNetSyncVariableInt("m_SyncParts01");
2082 RegisterNetSyncVariableInt("m_SyncParts02");
2083 RegisterNetSyncVariableInt("m_SyncParts03");
2084 RegisterNetSyncVariableInt("m_InteractedPartId");
2085 RegisterNetSyncVariableInt("m_PerformedActionId");
2086 RegisterNetSyncVariableBool("m_HasBase");
2087
2088 //Construction init
2090
2091 if (ConfigIsExisting("hybridAttachments"))
2092 {
2094 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2095 }
2096 if (ConfigIsExisting("mountables"))
2097 {
2099 ConfigGetTextArray("mountables", m_Mountables);
2100 }
2101
2102 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2103 }
2104
2105 override void EEDelete(EntityAI parent)
2106 {
2107 super.EEDelete(parent);
2108
2109 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2111
2112 }
2113
2114 override string GetInvulnerabilityTypeString()
2115 {
2116 return "disableBaseDamage";
2117 }
2118
2119 override bool CanObstruct()
2120 {
2121 return true;
2122 }
2123
2124 override int GetHideIconMask()
2125 {
2126 return EInventoryIconVisibility.HIDE_VICINITY;
2127 }
2128
2129 // --- SYNCHRONIZATION
2131 {
2132 if (GetGame().IsServer())
2133 SetSynchDirty();
2134 }
2135
2136 override void OnVariablesSynchronized()
2137 {
2138 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2139 super.OnVariablesSynchronized();
2140
2141 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2142 }
2143
2144 protected void OnSynchronizedClient()
2145 {
2146 //update parts
2148
2149 //update action on part
2151
2152 //update visuals (client)
2153 UpdateVisuals();
2154 }
2155
2156 //parts synchronization
2158 {
2159 //part_id must starts from index = 1
2160 int offset;
2161 int mask;
2162
2163 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2164 {
2165 offset = part_id - 1;
2166 mask = 1 << offset;
2167
2169 }
2170 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2171 {
2172 offset = (part_id % 32);
2173 mask = 1 << offset;
2174
2176 }
2177 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2178 {
2179 offset = (part_id % 63);
2180 mask = 1 << offset;
2181
2183 }
2184 }
2185
2187 {
2188 //part_id must starts from index = 1
2189 int offset;
2190 int mask;
2191
2192 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2193 {
2194 offset = part_id - 1;
2195 mask = 1 << offset;
2196
2198 }
2199 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2200 {
2201 offset = (part_id % 32);
2202 mask = 1 << offset;
2203
2205 }
2206 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2207 {
2208 offset = (part_id % 63);
2209 mask = 1 << offset;
2210
2212 }
2213 }
2214
2216 {
2217 //part_id must starts from index = 1
2218 int offset;
2219 int mask;
2220
2221 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2222 {
2223 offset = part_id - 1;
2224 mask = 1 << offset;
2225
2226 if ((m_SyncParts01 & mask) > 0)
2227 return true;
2228 }
2229 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2230 {
2231 offset = (part_id % 32);
2232 mask = 1 << offset;
2233
2234 if ((m_SyncParts02 & mask) > 0)
2235 return true;
2236 }
2237 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2238 {
2239 offset = (part_id % 63);
2240 mask = 1 << offset;
2241
2242 if ((m_SyncParts03 & mask) > 0)
2243 return true;
2244 }
2245
2246 return false;
2247 }
2248
2249 protected void RegisterActionForSync(int part_id, int action_id)
2250 {
2253 }
2254
2255 protected void ResetActionSyncData()
2256 {
2257 //reset data
2258 m_InteractedPartId = -1;
2260 }
2261
2262 protected void SetActionFromSyncData()
2263 {
2264 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2265 {
2268
2269 switch (build_action_id)
2270 {
2274 }
2275 }
2276 }
2277 //------
2278
2280 {
2281 string key = part.m_PartName;
2282 bool is_base = part.IsBase();
2284 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2286 {
2287 if (!part.IsBuilt())
2288 {
2289 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2290 GetConstruction().AddToConstructedParts(key);
2291 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2292
2293 if (is_base)
2294 {
2296 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2297 }
2298 }
2299 }
2300 else
2301 {
2302 if (part.IsBuilt())
2303 {
2304 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2305 GetConstruction().RemoveFromConstructedParts(key);
2306 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2307
2308 if (is_base)
2309 {
2311 AddProxyPhysics(ANIMATION_DEPLOYED);
2312 }
2313 }
2314 }
2315
2316 //check slot lock for material attachments
2317 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2318 }
2319
2320 //set construction parts based on synchronized data
2322 {
2325
2326 for (int i = 0; i < construction_parts.Count(); ++i)
2327 {
2328 string key = construction_parts.GetKey(i);
2331 }
2332
2333 //regenerate navmesh
2334 UpdateNavmesh();
2335 }
2336
2338 {
2341
2342 for (int i = 0; i < construction_parts.Count(); ++i)
2343 {
2344 string key = construction_parts.GetKey(i);
2346
2347 if (value.GetId() == id)
2348 return value;
2349 }
2350
2351 return NULL;
2352 }
2353 //
2354
2355 //Base
2356 bool HasBase()
2357 {
2358 return m_HasBase;
2359 }
2360
2361 void SetBaseState(bool has_base)
2362 {
2364 }
2365
2366 override bool IsDeployable()
2367 {
2368 return true;
2369 }
2370
2371 bool IsOpened()
2372 {
2373 return false;
2374 }
2375
2376 //--- CONSTRUCTION KIT
2378 {
2382
2383 return construction_kit;
2384 }
2385
2387 {
2388 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2391 }
2392
2393 protected vector GetKitSpawnPosition()
2394 {
2395 return GetPosition();
2396 }
2397
2398 protected string GetConstructionKitType()
2399 {
2400 return "";
2401 }
2402
2404 {
2406 GetGame().ObjectDelete(construction_kit);
2407 }
2408
2409 //--- CONSTRUCTION
2410 void DestroyConstruction()
2411 {
2412 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2413 GetGame().ObjectDelete(this);
2414 }
2415
2416 // --- EVENTS
2417 override void OnStoreSave(ParamsWriteContext ctx)
2418 {
2419 super.OnStoreSave(ctx);
2420
2421 //sync parts 01
2422 ctx.Write(m_SyncParts01);
2423 ctx.Write(m_SyncParts02);
2424 ctx.Write(m_SyncParts03);
2425
2426 ctx.Write(m_HasBase);
2427 }
2428
2429 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2430 {
2431 if (!super.OnStoreLoad(ctx, version))
2432 return false;
2433
2434 //--- Base building data ---
2435 //Restore synced parts data
2436 if (!ctx.Read(m_SyncParts01))
2437 {
2438 m_SyncParts01 = 0; //set default
2439 return false;
2440 }
2441 if (!ctx.Read(m_SyncParts02))
2442 {
2443 m_SyncParts02 = 0; //set default
2444 return false;
2445 }
2446 if (!ctx.Read(m_SyncParts03))
2447 {
2448 m_SyncParts03 = 0; //set default
2449 return false;
2450 }
2451
2452 //has base
2453 if (!ctx.Read(m_HasBase))
2454 {
2455 m_HasBase = false;
2456 return false;
2457 }
2458 //---
2459
2460 return true;
2461 }
2462
2463 override void AfterStoreLoad()
2464 {
2465 super.AfterStoreLoad();
2466
2469 }
2470
2472 {
2473 //update server data
2475
2476 //set base state
2477 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2478 SetBaseState(construction_part.IsBuilt()) ;
2479
2480 //synchronize after load
2482 }
2483
2484 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2485 {
2487 return;
2488
2489 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2490
2491 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2492 return;
2493
2495 string part_name = zone;
2496 part_name.ToLower();
2497
2499 {
2501
2502 if (construction_part && construction.IsPartConstructed(part_name))
2503 {
2504 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2505 construction.DestroyConnectedParts(part_name);
2506 }
2507
2508 //barbed wire handling (hack-ish)
2509 if (part_name.Contains("barbed"))
2510 {
2511 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2512 if (barbed_wire)
2513 barbed_wire.SetMountedState(false);
2514 }
2515 }
2516 }
2517
2518 override void EEOnAfterLoad()
2519 {
2521 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2522
2523 super.EEOnAfterLoad();
2524 }
2525
2526 override void EEInit()
2527 {
2528 super.EEInit();
2529
2530 // init visuals and physics
2531 InitBaseState();
2532
2533 //debug
2534#ifdef DEVELOPER
2536#endif
2537 }
2538
2539 override void EEItemAttached(EntityAI item, string slot_name)
2540 {
2541 super.EEItemAttached(item, slot_name);
2542
2544 UpdateVisuals();
2546 }
2547
2548 override void EEItemDetached(EntityAI item, string slot_name)
2549 {
2550 super.EEItemDetached(item, slot_name);
2551
2552 UpdateVisuals();
2554 }
2555
2556 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2557 {
2559 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2560
2563 }
2564
2565 //ignore out of reach condition
2566 override bool IgnoreOutOfReachCondition()
2567 {
2568 return true;
2569 }
2570
2571 //CONSTRUCTION EVENTS
2572 //Build
2573 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2574 {
2576
2577 //check base state
2578 if (construtionPart.IsBase())
2579 {
2580 SetBaseState(true);
2581
2582 //spawn kit
2584 }
2585
2586 //register constructed parts for synchronization
2588
2589 //register action that was performed on part
2591
2592 //synchronize
2594
2595 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2596
2597 UpdateNavmesh();
2598
2599 //update visuals
2600 UpdateVisuals();
2601
2602 //reset action sync data
2603 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2604 }
2605
2606 void OnPartBuiltClient(string part_name, int action_id)
2607 {
2608 //play sound
2610 }
2611
2612 //Dismantle
2614 {
2615 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2617
2618 //register constructed parts for synchronization
2620
2621 //register action that was performed on part
2623
2624 //synchronize
2626
2627 // server part of sync, client will be synced from SetPartsFromSyncData
2629
2630 UpdateNavmesh();
2631
2632 //update visuals
2633 UpdateVisuals();
2634
2635 //reset action sync data
2636 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2637
2638 //check base state
2639 if (construtionPart.IsBase())
2640 {
2641 //Destroy construction
2642 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2643 }
2644 }
2645
2647 {
2648 //play sound
2650 }
2651
2652 //Destroy
2654 {
2655 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2657
2658 //register constructed parts for synchronization
2660
2661 //register action that was performed on part
2663
2664 //synchronize
2666
2667 // server part of sync, client will be synced from SetPartsFromSyncData
2669
2670 UpdateNavmesh();
2671
2672 //update visuals
2673 UpdateVisuals();
2674
2675 //reset action sync data
2676 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2677
2678 //check base state
2679 if (construtionPart.IsBase())
2680 {
2681 //Destroy construction
2682 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2683 }
2684 }
2685
2686 void OnPartDestroyedClient(string part_name, int action_id)
2687 {
2688 //play sound
2690 }
2691
2692 // --- UPDATE
2693 void InitBaseState()
2694 {
2695 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2696
2697 InitVisuals();
2698 UpdateNavmesh(); //regenerate navmesh
2699 GetConstruction().InitBaseState();
2700 }
2701
2702 void InitVisuals()
2703 {
2704 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2705 //check base
2706 if (!HasBase())
2707 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2708 else
2709 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2710
2711 GetConstruction().UpdateVisuals();
2712 }
2713
2714 void UpdateVisuals()
2715 {
2717
2719 foreach (string slotName : attachmentSlots)
2721
2722 //check base
2723 if (!HasBase())
2724 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2725 else
2726 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2727
2728 GetConstruction().UpdateVisuals();
2729 }
2730
2732 {
2733 string slotNameMounted = slot_name + "_Mounted";
2734 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2735
2736 if (attachment)
2737 {
2738 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2739 if (barbedWire && barbedWire.IsMounted())
2741 else
2743
2744 if (is_locked)
2745 {
2746 SetAnimationPhase(slotNameMounted, 0);
2747 SetAnimationPhase(slot_name, 1);
2748 }
2749 else
2750 {
2751 SetAnimationPhase(slotNameMounted, 1);
2752 SetAnimationPhase(slot_name, 0);
2753 }
2754 }
2755 else
2756 {
2757 SetAnimationPhase(slotNameMounted, 1);
2758 SetAnimationPhase(slot_name, 1);
2759
2761 }
2762 }
2763
2764 // avoid calling this function on frequent occasions, it's a massive performance hit
2765 void UpdatePhysics()
2766 {
2768 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2769
2772
2774 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2775
2776 foreach (string slotName : attachmentSlots)
2778
2779 //check base
2780 if (!HasBase())
2781 {
2783 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2784
2785 AddProxyPhysics(ANIMATION_DEPLOYED);
2786 }
2787 else
2788 {
2790 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2791
2792 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2793 }
2794
2795 GetConstruction().UpdatePhysics();
2796 UpdateNavmesh();
2797 }
2798
2800 {
2801 //checks for invalid appends; hotfix
2802 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2803 return;
2804 //----------------------------------
2805 string slot_name_mounted = slot_name + "_Mounted";
2806 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2807
2808 //remove proxy physics
2809 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2810 RemoveProxyPhysics(slot_name_mounted);
2811 RemoveProxyPhysics(slot_name);
2812
2813 if (attachment)
2814 {
2815 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2816 if (is_locked)
2817 {
2818 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2819 AddProxyPhysics(slot_name_mounted);
2820 }
2821 else
2822 {
2823 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2824 AddProxyPhysics(slot_name);
2825 }
2826 }
2827 }
2828
2829 protected void UpdateNavmesh()
2830 {
2831 SetAffectPathgraph(true, false);
2832 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2833 }
2834
2835 override bool CanUseConstruction()
2836 {
2837 return true;
2838 }
2839
2840 override bool CanUseConstructionBuild()
2841 {
2842 return true;
2843 }
2844
2846 {
2847 if (attachment)
2848 {
2850 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2851
2852 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2853 }
2854
2855 return false;
2856 }
2857
2858 protected bool IsAttachmentSlotLocked(string slot_name)
2859 {
2860 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2861 }
2862
2863 //--- ATTACHMENT SLOTS
2865 {
2866 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2867 if (GetGame().ConfigIsExisting(config_path))
2868 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2869 }
2870
2872 {
2873 return true;
2874 }
2875
2876 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2877 {
2878 return true;
2879 }
2880
2881 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2882 {
2883 return true;
2884 }
2885
2886 // --- INIT
2887 void ConstructionInit()
2888 {
2889 if (!m_Construction)
2890 m_Construction = new Construction(this);
2891
2892 GetConstruction().Init();
2893 }
2894
2896 {
2897 return m_Construction;
2898 }
2899
2900 //--- INVENTORY/ATTACHMENTS CONDITIONS
2901 //attachments
2903 {
2904 return super.CanReceiveAttachment(attachment, slotId);
2905 }
2906
2908 {
2909 int attachment_count = GetInventory().AttachmentCount();
2910 if (attachment_count > 0)
2911 {
2912 if (HasBase() && attachment_count == 1)
2913 return false;
2914
2915 return true;
2916 }
2917
2918 return false;
2919 }
2920
2921 override bool ShowZonesHealth()
2922 {
2923 return true;
2924 }
2925
2926 //this into/outo parent.Cargo
2927 override bool CanPutInCargo(EntityAI parent)
2928 {
2929 return false;
2930 }
2931
2932 override bool CanRemoveFromCargo(EntityAI parent)
2933 {
2934 return false;
2935 }
2936
2937 //hands
2938 override bool CanPutIntoHands(EntityAI parent)
2939 {
2940 return false;
2941 }
2942
2943 //--- ACTION CONDITIONS
2944 //direction
2945 override bool IsFacingPlayer(PlayerBase player, string selection)
2946 {
2947 return true;
2948 }
2949
2950 override bool IsPlayerInside(PlayerBase player, string selection)
2951 {
2952 return true;
2953 }
2954
2957 {
2958 return false;
2959 }
2960
2961 //camera direction check
2962 bool IsFacingCamera(string selection)
2963 {
2964 return true;
2965 }
2966
2967 //roof check
2969 {
2970 return false;
2971 }
2972
2973 //selection->player distance check
2974 bool HasProperDistance(string selection, PlayerBase player)
2975 {
2976 return true;
2977 }
2978
2979 //folding
2981 {
2982 if (HasBase() || GetInventory().AttachmentCount() > 0)
2983 return false;
2984
2985 return true;
2986 }
2987
2989 {
2992
2993 return item;
2994 }
2995
2996 //Damage triggers (barbed wire)
2997 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2998 {
2999 if (GetGame() && GetGame().IsServer())
3000 {
3001 //destroy area damage if some already exists
3003
3004 //create new area damage
3006 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3007
3008 vector min_max[2];
3009 if (MemoryPointExists(slot_name + "_min"))
3010 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3011 if (MemoryPointExists(slot_name + "_max"))
3012 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3013
3014 //get proper trigger extents (min<max)
3015 vector extents[2];
3016 GetConstruction().GetTriggerExtents(min_max, extents);
3017
3018 //get box center
3019 vector center;
3020 center = GetConstruction().GetBoxCenter(min_max);
3021 center = ModelToWorld(center);
3022
3023 //rotate center if needed
3026
3027 areaDamage.SetExtents(extents[0], extents[1]);
3028 areaDamage.SetAreaPosition(center);
3029 areaDamage.SetAreaOrientation(orientation);
3030 areaDamage.SetLoopInterval(1.0);
3031 areaDamage.SetDeferDuration(0.2);
3032 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3033 areaDamage.SetAmmoName("BarbedWireHit");
3034 areaDamage.Spawn();
3035
3037 }
3038 }
3039
3041 {
3042 if (angle_deg != 0)
3043 {
3044 //orientation
3046
3047 //center
3049 if (MemoryPointExists("rotate_axis"))
3050 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3053 center[0] = r_center_x;
3054 center[2] = r_center_z;
3055 }
3056 }
3057
3058 void DestroyAreaDamage(string slot_name)
3059 {
3060 if (GetGame() && GetGame().IsServer())
3061 {
3064 {
3065 if (areaDamage)
3066 areaDamage.Destroy();
3067
3069 }
3070 }
3071 }
3072
3073 override bool IsIgnoredByConstruction()
3074 {
3075 return true;
3076 }
3077
3078 //================================================================
3079 // SOUNDS
3080 //================================================================
3081 protected void SoundBuildStart(string part_name)
3082 {
3083 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3084 }
3085
3086 protected void SoundDismantleStart(string part_name)
3087 {
3088 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3089 }
3090
3091 protected void SoundDestroyStart(string part_name)
3092 {
3093 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3094 }
3095
3096 protected string GetBuildSoundByMaterial(string part_name)
3097 {
3099
3100 switch (material_type)
3101 {
3102 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3103 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3104 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3105 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3106 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3107 }
3108
3109 return "";
3110 }
3111
3112 protected string GetDismantleSoundByMaterial(string part_name)
3113 {
3115
3116 switch (material_type)
3117 {
3118 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3119 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3120 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3121 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3122 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3123 }
3124
3125 return "";
3126 }
3127
3128 //misc
3130 {
3131 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3132 {
3133 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3135 SetHealth(slot_name, "Health", item.GetHealth());
3136 }
3137 }
3138
3139 override int GetDamageSystemVersionChange()
3140 {
3141 return 111;
3142 }
3143
3144 override void SetActions()
3145 {
3146 super.SetActions();
3147
3149 //AddAction(ActionTakeHybridAttachment);
3150 //AddAction(ActionTakeHybridAttachmentToHands);
3153 }
3154
3155 //================================================================
3156 // DEBUG
3157 //================================================================
3158 protected void DebugCustomState()
3159 {
3160 }
3161
3164 {
3165 return null;
3166 }
3167
3168 override void OnDebugSpawn()
3169 {
3170 FullyBuild();
3171 }
3172
3173 void FullyBuild()
3174 {
3176 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3177
3178 Man p;
3179
3180#ifdef SERVER
3182 GetGame().GetWorld().GetPlayerList(players);
3183 if (players.Count())
3184 p = players[0];
3185#else
3186 p = GetGame().GetPlayer();
3187#endif
3188
3189 foreach (ConstructionPart part : parts)
3190 {
3191 bool excluded = false;
3192 string partName = part.GetPartName();
3193 if (excludes)
3194 {
3195 foreach (string exclude : excludes)
3196 {
3197 if (partName.Contains(exclude))
3198 {
3199 excluded = true;
3200 break;
3201 }
3202 }
3203 }
3204
3205 if (!excluded)
3207 }
3208
3209 GetConstruction().UpdateVisuals();
3210 }
3211}
3212
3213void bsbDebugPrint(string s)
3214{
3215#ifdef BSB_DEBUG
3216 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3217#else
3218 //Print("" + s); // comment/uncomment to hide/see debug logs
3219#endif
3220}
3221void bsbDebugSpam(string s)
3222{
3223#ifdef BSB_DEBUG_SPAM
3224 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3225#else
3226 //Print("" + s); // comment/uncomment to hide/see debug logs
3227#endif
3228}

◆ CanRemoveFromCargo()

override bool bsbDebugPrint::CanRemoveFromCargo ( EntityAI parent)
protected

Definition at line 2068 of file BaseBuildingBase.c.

2070{
2071 const string ANIMATION_DEPLOYED = "Deployed";
2072
2073 float m_ConstructionKitHealth; //stored health value for used construction kit
2074
2076
2077 bool m_HasBase;
2078 //variables for synchronization of base building parts (2x31 is the current limit)
2079 int m_SyncParts01; //synchronization for already built parts (31 parts)
2080 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2081 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2082 int m_InteractedPartId; //construction part id that an action was performed on
2083 int m_PerformedActionId; //action id that was performed on a construction part
2084
2085 //Sounds
2086 //build
2087 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2088 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2089 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2090 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2091 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2092 //dismantle
2093 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2094 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2095 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2096 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2097 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2098
2099 protected EffectSound m_Sound;
2100
2104
2105 // Constructor
2106 void BaseBuildingBase()
2107 {
2109
2110 //synchronized variables
2111 RegisterNetSyncVariableInt("m_SyncParts01");
2112 RegisterNetSyncVariableInt("m_SyncParts02");
2113 RegisterNetSyncVariableInt("m_SyncParts03");
2114 RegisterNetSyncVariableInt("m_InteractedPartId");
2115 RegisterNetSyncVariableInt("m_PerformedActionId");
2116 RegisterNetSyncVariableBool("m_HasBase");
2117
2118 //Construction init
2120
2121 if (ConfigIsExisting("hybridAttachments"))
2122 {
2124 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2125 }
2126 if (ConfigIsExisting("mountables"))
2127 {
2129 ConfigGetTextArray("mountables", m_Mountables);
2130 }
2131
2132 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2133 }
2134
2135 override void EEDelete(EntityAI parent)
2136 {
2137 super.EEDelete(parent);
2138
2139 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2141
2142 }
2143
2144 override string GetInvulnerabilityTypeString()
2145 {
2146 return "disableBaseDamage";
2147 }
2148
2149 override bool CanObstruct()
2150 {
2151 return true;
2152 }
2153
2154 override int GetHideIconMask()
2155 {
2156 return EInventoryIconVisibility.HIDE_VICINITY;
2157 }
2158
2159 // --- SYNCHRONIZATION
2161 {
2162 if (GetGame().IsServer())
2163 SetSynchDirty();
2164 }
2165
2166 override void OnVariablesSynchronized()
2167 {
2168 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2169 super.OnVariablesSynchronized();
2170
2171 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2172 }
2173
2174 protected void OnSynchronizedClient()
2175 {
2176 //update parts
2178
2179 //update action on part
2181
2182 //update visuals (client)
2183 UpdateVisuals();
2184 }
2185
2186 //parts synchronization
2188 {
2189 //part_id must starts from index = 1
2190 int offset;
2191 int mask;
2192
2193 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2194 {
2195 offset = part_id - 1;
2196 mask = 1 << offset;
2197
2199 }
2200 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2201 {
2202 offset = (part_id % 32);
2203 mask = 1 << offset;
2204
2206 }
2207 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2208 {
2209 offset = (part_id % 63);
2210 mask = 1 << offset;
2211
2213 }
2214 }
2215
2217 {
2218 //part_id must starts from index = 1
2219 int offset;
2220 int mask;
2221
2222 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2223 {
2224 offset = part_id - 1;
2225 mask = 1 << offset;
2226
2228 }
2229 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2230 {
2231 offset = (part_id % 32);
2232 mask = 1 << offset;
2233
2235 }
2236 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2237 {
2238 offset = (part_id % 63);
2239 mask = 1 << offset;
2240
2242 }
2243 }
2244
2246 {
2247 //part_id must starts from index = 1
2248 int offset;
2249 int mask;
2250
2251 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2252 {
2253 offset = part_id - 1;
2254 mask = 1 << offset;
2255
2256 if ((m_SyncParts01 & mask) > 0)
2257 return true;
2258 }
2259 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2260 {
2261 offset = (part_id % 32);
2262 mask = 1 << offset;
2263
2264 if ((m_SyncParts02 & mask) > 0)
2265 return true;
2266 }
2267 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2268 {
2269 offset = (part_id % 63);
2270 mask = 1 << offset;
2271
2272 if ((m_SyncParts03 & mask) > 0)
2273 return true;
2274 }
2275
2276 return false;
2277 }
2278
2279 protected void RegisterActionForSync(int part_id, int action_id)
2280 {
2283 }
2284
2285 protected void ResetActionSyncData()
2286 {
2287 //reset data
2288 m_InteractedPartId = -1;
2290 }
2291
2292 protected void SetActionFromSyncData()
2293 {
2294 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2295 {
2298
2299 switch (build_action_id)
2300 {
2304 }
2305 }
2306 }
2307 //------
2308
2310 {
2311 string key = part.m_PartName;
2312 bool is_base = part.IsBase();
2314 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2316 {
2317 if (!part.IsBuilt())
2318 {
2319 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2320 GetConstruction().AddToConstructedParts(key);
2321 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2322
2323 if (is_base)
2324 {
2326 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2327 }
2328 }
2329 }
2330 else
2331 {
2332 if (part.IsBuilt())
2333 {
2334 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2335 GetConstruction().RemoveFromConstructedParts(key);
2336 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2337
2338 if (is_base)
2339 {
2341 AddProxyPhysics(ANIMATION_DEPLOYED);
2342 }
2343 }
2344 }
2345
2346 //check slot lock for material attachments
2347 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2348 }
2349
2350 //set construction parts based on synchronized data
2352 {
2355
2356 for (int i = 0; i < construction_parts.Count(); ++i)
2357 {
2358 string key = construction_parts.GetKey(i);
2361 }
2362
2363 //regenerate navmesh
2364 UpdateNavmesh();
2365 }
2366
2368 {
2371
2372 for (int i = 0; i < construction_parts.Count(); ++i)
2373 {
2374 string key = construction_parts.GetKey(i);
2376
2377 if (value.GetId() == id)
2378 return value;
2379 }
2380
2381 return NULL;
2382 }
2383 //
2384
2385 //Base
2386 bool HasBase()
2387 {
2388 return m_HasBase;
2389 }
2390
2391 void SetBaseState(bool has_base)
2392 {
2394 }
2395
2396 override bool IsDeployable()
2397 {
2398 return true;
2399 }
2400
2401 bool IsOpened()
2402 {
2403 return false;
2404 }
2405
2406 //--- CONSTRUCTION KIT
2408 {
2412
2413 return construction_kit;
2414 }
2415
2417 {
2418 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2421 }
2422
2423 protected vector GetKitSpawnPosition()
2424 {
2425 return GetPosition();
2426 }
2427
2428 protected string GetConstructionKitType()
2429 {
2430 return "";
2431 }
2432
2434 {
2436 GetGame().ObjectDelete(construction_kit);
2437 }
2438
2439 //--- CONSTRUCTION
2440 void DestroyConstruction()
2441 {
2442 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2443 GetGame().ObjectDelete(this);
2444 }
2445
2446 // --- EVENTS
2447 override void OnStoreSave(ParamsWriteContext ctx)
2448 {
2449 super.OnStoreSave(ctx);
2450
2451 //sync parts 01
2452 ctx.Write(m_SyncParts01);
2453 ctx.Write(m_SyncParts02);
2454 ctx.Write(m_SyncParts03);
2455
2456 ctx.Write(m_HasBase);
2457 }
2458
2459 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2460 {
2461 if (!super.OnStoreLoad(ctx, version))
2462 return false;
2463
2464 //--- Base building data ---
2465 //Restore synced parts data
2466 if (!ctx.Read(m_SyncParts01))
2467 {
2468 m_SyncParts01 = 0; //set default
2469 return false;
2470 }
2471 if (!ctx.Read(m_SyncParts02))
2472 {
2473 m_SyncParts02 = 0; //set default
2474 return false;
2475 }
2476 if (!ctx.Read(m_SyncParts03))
2477 {
2478 m_SyncParts03 = 0; //set default
2479 return false;
2480 }
2481
2482 //has base
2483 if (!ctx.Read(m_HasBase))
2484 {
2485 m_HasBase = false;
2486 return false;
2487 }
2488 //---
2489
2490 return true;
2491 }
2492
2493 override void AfterStoreLoad()
2494 {
2495 super.AfterStoreLoad();
2496
2499 }
2500
2502 {
2503 //update server data
2505
2506 //set base state
2507 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2508 SetBaseState(construction_part.IsBuilt()) ;
2509
2510 //synchronize after load
2512 }
2513
2514 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2515 {
2517 return;
2518
2519 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2520
2521 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2522 return;
2523
2525 string part_name = zone;
2526 part_name.ToLower();
2527
2529 {
2531
2532 if (construction_part && construction.IsPartConstructed(part_name))
2533 {
2534 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2535 construction.DestroyConnectedParts(part_name);
2536 }
2537
2538 //barbed wire handling (hack-ish)
2539 if (part_name.Contains("barbed"))
2540 {
2541 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2542 if (barbed_wire)
2543 barbed_wire.SetMountedState(false);
2544 }
2545 }
2546 }
2547
2548 override void EEOnAfterLoad()
2549 {
2551 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2552
2553 super.EEOnAfterLoad();
2554 }
2555
2556 override void EEInit()
2557 {
2558 super.EEInit();
2559
2560 // init visuals and physics
2561 InitBaseState();
2562
2563 //debug
2564#ifdef DEVELOPER
2566#endif
2567 }
2568
2569 override void EEItemAttached(EntityAI item, string slot_name)
2570 {
2571 super.EEItemAttached(item, slot_name);
2572
2574 UpdateVisuals();
2576 }
2577
2578 override void EEItemDetached(EntityAI item, string slot_name)
2579 {
2580 super.EEItemDetached(item, slot_name);
2581
2582 UpdateVisuals();
2584 }
2585
2586 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2587 {
2589 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2590
2593 }
2594
2595 //ignore out of reach condition
2596 override bool IgnoreOutOfReachCondition()
2597 {
2598 return true;
2599 }
2600
2601 //CONSTRUCTION EVENTS
2602 //Build
2603 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2604 {
2606
2607 //check base state
2608 if (construtionPart.IsBase())
2609 {
2610 SetBaseState(true);
2611
2612 //spawn kit
2614 }
2615
2616 //register constructed parts for synchronization
2618
2619 //register action that was performed on part
2621
2622 //synchronize
2624
2625 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2626
2627 UpdateNavmesh();
2628
2629 //update visuals
2630 UpdateVisuals();
2631
2632 //reset action sync data
2633 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2634 }
2635
2636 void OnPartBuiltClient(string part_name, int action_id)
2637 {
2638 //play sound
2640 }
2641
2642 //Dismantle
2644 {
2645 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2647
2648 //register constructed parts for synchronization
2650
2651 //register action that was performed on part
2653
2654 //synchronize
2656
2657 // server part of sync, client will be synced from SetPartsFromSyncData
2659
2660 UpdateNavmesh();
2661
2662 //update visuals
2663 UpdateVisuals();
2664
2665 //reset action sync data
2666 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2667
2668 //check base state
2669 if (construtionPart.IsBase())
2670 {
2671 //Destroy construction
2672 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2673 }
2674 }
2675
2677 {
2678 //play sound
2680 }
2681
2682 //Destroy
2684 {
2685 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2687
2688 //register constructed parts for synchronization
2690
2691 //register action that was performed on part
2693
2694 //synchronize
2696
2697 // server part of sync, client will be synced from SetPartsFromSyncData
2699
2700 UpdateNavmesh();
2701
2702 //update visuals
2703 UpdateVisuals();
2704
2705 //reset action sync data
2706 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2707
2708 //check base state
2709 if (construtionPart.IsBase())
2710 {
2711 //Destroy construction
2712 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2713 }
2714 }
2715
2716 void OnPartDestroyedClient(string part_name, int action_id)
2717 {
2718 //play sound
2720 }
2721
2722 // --- UPDATE
2723 void InitBaseState()
2724 {
2725 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2726
2727 InitVisuals();
2728 UpdateNavmesh(); //regenerate navmesh
2729 GetConstruction().InitBaseState();
2730 }
2731
2732 void InitVisuals()
2733 {
2734 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2735 //check base
2736 if (!HasBase())
2737 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2738 else
2739 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2740
2741 GetConstruction().UpdateVisuals();
2742 }
2743
2744 void UpdateVisuals()
2745 {
2747
2749 foreach (string slotName : attachmentSlots)
2751
2752 //check base
2753 if (!HasBase())
2754 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2755 else
2756 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2757
2758 GetConstruction().UpdateVisuals();
2759 }
2760
2762 {
2763 string slotNameMounted = slot_name + "_Mounted";
2764 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2765
2766 if (attachment)
2767 {
2768 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2769 if (barbedWire && barbedWire.IsMounted())
2771 else
2773
2774 if (is_locked)
2775 {
2776 SetAnimationPhase(slotNameMounted, 0);
2777 SetAnimationPhase(slot_name, 1);
2778 }
2779 else
2780 {
2781 SetAnimationPhase(slotNameMounted, 1);
2782 SetAnimationPhase(slot_name, 0);
2783 }
2784 }
2785 else
2786 {
2787 SetAnimationPhase(slotNameMounted, 1);
2788 SetAnimationPhase(slot_name, 1);
2789
2791 }
2792 }
2793
2794 // avoid calling this function on frequent occasions, it's a massive performance hit
2795 void UpdatePhysics()
2796 {
2798 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2799
2802
2804 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2805
2806 foreach (string slotName : attachmentSlots)
2808
2809 //check base
2810 if (!HasBase())
2811 {
2813 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2814
2815 AddProxyPhysics(ANIMATION_DEPLOYED);
2816 }
2817 else
2818 {
2820 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2821
2822 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2823 }
2824
2825 GetConstruction().UpdatePhysics();
2826 UpdateNavmesh();
2827 }
2828
2830 {
2831 //checks for invalid appends; hotfix
2832 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2833 return;
2834 //----------------------------------
2835 string slot_name_mounted = slot_name + "_Mounted";
2836 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2837
2838 //remove proxy physics
2839 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2840 RemoveProxyPhysics(slot_name_mounted);
2841 RemoveProxyPhysics(slot_name);
2842
2843 if (attachment)
2844 {
2845 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2846 if (is_locked)
2847 {
2848 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2849 AddProxyPhysics(slot_name_mounted);
2850 }
2851 else
2852 {
2853 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2854 AddProxyPhysics(slot_name);
2855 }
2856 }
2857 }
2858
2859 protected void UpdateNavmesh()
2860 {
2861 SetAffectPathgraph(true, false);
2862 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2863 }
2864
2865 override bool CanUseConstruction()
2866 {
2867 return true;
2868 }
2869
2870 override bool CanUseConstructionBuild()
2871 {
2872 return true;
2873 }
2874
2876 {
2877 if (attachment)
2878 {
2880 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2881
2882 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2883 }
2884
2885 return false;
2886 }
2887
2888 protected bool IsAttachmentSlotLocked(string slot_name)
2889 {
2890 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2891 }
2892
2893 //--- ATTACHMENT SLOTS
2895 {
2896 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2897 if (GetGame().ConfigIsExisting(config_path))
2898 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2899 }
2900
2902 {
2903 return true;
2904 }
2905
2906 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2907 {
2908 return true;
2909 }
2910
2911 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2912 {
2913 return true;
2914 }
2915
2916 // --- INIT
2917 void ConstructionInit()
2918 {
2919 if (!m_Construction)
2920 m_Construction = new Construction(this);
2921
2922 GetConstruction().Init();
2923 }
2924
2926 {
2927 return m_Construction;
2928 }
2929
2930 //--- INVENTORY/ATTACHMENTS CONDITIONS
2931 //attachments
2933 {
2934 return super.CanReceiveAttachment(attachment, slotId);
2935 }
2936
2938 {
2939 int attachment_count = GetInventory().AttachmentCount();
2940 if (attachment_count > 0)
2941 {
2942 if (HasBase() && attachment_count == 1)
2943 return false;
2944
2945 return true;
2946 }
2947
2948 return false;
2949 }
2950
2951 override bool ShowZonesHealth()
2952 {
2953 return true;
2954 }
2955
2956 //this into/outo parent.Cargo
2957 override bool CanPutInCargo(EntityAI parent)
2958 {
2959 return false;
2960 }
2961
2962 override bool CanRemoveFromCargo(EntityAI parent)
2963 {
2964 return false;
2965 }
2966
2967 //hands
2968 override bool CanPutIntoHands(EntityAI parent)
2969 {
2970 return false;
2971 }
2972
2973 //--- ACTION CONDITIONS
2974 //direction
2975 override bool IsFacingPlayer(PlayerBase player, string selection)
2976 {
2977 return true;
2978 }
2979
2980 override bool IsPlayerInside(PlayerBase player, string selection)
2981 {
2982 return true;
2983 }
2984
2987 {
2988 return false;
2989 }
2990
2991 //camera direction check
2992 bool IsFacingCamera(string selection)
2993 {
2994 return true;
2995 }
2996
2997 //roof check
2999 {
3000 return false;
3001 }
3002
3003 //selection->player distance check
3004 bool HasProperDistance(string selection, PlayerBase player)
3005 {
3006 return true;
3007 }
3008
3009 //folding
3011 {
3012 if (HasBase() || GetInventory().AttachmentCount() > 0)
3013 return false;
3014
3015 return true;
3016 }
3017
3019 {
3022
3023 return item;
3024 }
3025
3026 //Damage triggers (barbed wire)
3027 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3028 {
3029 if (GetGame() && GetGame().IsServer())
3030 {
3031 //destroy area damage if some already exists
3033
3034 //create new area damage
3036 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3037
3038 vector min_max[2];
3039 if (MemoryPointExists(slot_name + "_min"))
3040 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3041 if (MemoryPointExists(slot_name + "_max"))
3042 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3043
3044 //get proper trigger extents (min<max)
3045 vector extents[2];
3046 GetConstruction().GetTriggerExtents(min_max, extents);
3047
3048 //get box center
3049 vector center;
3050 center = GetConstruction().GetBoxCenter(min_max);
3051 center = ModelToWorld(center);
3052
3053 //rotate center if needed
3056
3057 areaDamage.SetExtents(extents[0], extents[1]);
3058 areaDamage.SetAreaPosition(center);
3059 areaDamage.SetAreaOrientation(orientation);
3060 areaDamage.SetLoopInterval(1.0);
3061 areaDamage.SetDeferDuration(0.2);
3062 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3063 areaDamage.SetAmmoName("BarbedWireHit");
3064 areaDamage.Spawn();
3065
3067 }
3068 }
3069
3071 {
3072 if (angle_deg != 0)
3073 {
3074 //orientation
3076
3077 //center
3079 if (MemoryPointExists("rotate_axis"))
3080 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3083 center[0] = r_center_x;
3084 center[2] = r_center_z;
3085 }
3086 }
3087
3088 void DestroyAreaDamage(string slot_name)
3089 {
3090 if (GetGame() && GetGame().IsServer())
3091 {
3094 {
3095 if (areaDamage)
3096 areaDamage.Destroy();
3097
3099 }
3100 }
3101 }
3102
3103 override bool IsIgnoredByConstruction()
3104 {
3105 return true;
3106 }
3107
3108 //================================================================
3109 // SOUNDS
3110 //================================================================
3111 protected void SoundBuildStart(string part_name)
3112 {
3113 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3114 }
3115
3116 protected void SoundDismantleStart(string part_name)
3117 {
3118 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3119 }
3120
3121 protected void SoundDestroyStart(string part_name)
3122 {
3123 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3124 }
3125
3126 protected string GetBuildSoundByMaterial(string part_name)
3127 {
3129
3130 switch (material_type)
3131 {
3132 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3133 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3134 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3135 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3136 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3137 }
3138
3139 return "";
3140 }
3141
3142 protected string GetDismantleSoundByMaterial(string part_name)
3143 {
3145
3146 switch (material_type)
3147 {
3148 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3149 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3150 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3151 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3152 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3153 }
3154
3155 return "";
3156 }
3157
3158 //misc
3160 {
3161 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3162 {
3163 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3165 SetHealth(slot_name, "Health", item.GetHealth());
3166 }
3167 }
3168
3169 override int GetDamageSystemVersionChange()
3170 {
3171 return 111;
3172 }
3173
3174 override void SetActions()
3175 {
3176 super.SetActions();
3177
3179 //AddAction(ActionTakeHybridAttachment);
3180 //AddAction(ActionTakeHybridAttachmentToHands);
3183 }
3184
3185 //================================================================
3186 // DEBUG
3187 //================================================================
3188 protected void DebugCustomState()
3189 {
3190 }
3191
3194 {
3195 return null;
3196 }
3197
3198 override void OnDebugSpawn()
3199 {
3200 FullyBuild();
3201 }
3202
3203 void FullyBuild()
3204 {
3206 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3207
3208 Man p;
3209
3210#ifdef SERVER
3212 GetGame().GetWorld().GetPlayerList(players);
3213 if (players.Count())
3214 p = players[0];
3215#else
3216 p = GetGame().GetPlayer();
3217#endif
3218
3219 foreach (ConstructionPart part : parts)
3220 {
3221 bool excluded = false;
3222 string partName = part.GetPartName();
3223 if (excludes)
3224 {
3225 foreach (string exclude : excludes)
3226 {
3227 if (partName.Contains(exclude))
3228 {
3229 excluded = true;
3230 break;
3231 }
3232 }
3233 }
3234
3235 if (!excluded)
3237 }
3238
3239 GetConstruction().UpdateVisuals();
3240 }
3241}
3242
3243void bsbDebugPrint(string s)
3244{
3245#ifdef BSB_DEBUG
3246 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3247#else
3248 //Print("" + s); // comment/uncomment to hide/see debug logs
3249#endif
3250}
3251void bsbDebugSpam(string s)
3252{
3253#ifdef BSB_DEBUG_SPAM
3254 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3255#else
3256 //Print("" + s); // comment/uncomment to hide/see debug logs
3257#endif
3258}

◆ CanUseConstruction()

override bool bsbDebugPrint::CanUseConstruction ( )
protected

Definition at line 1971 of file BaseBuildingBase.c.

1973{
1974 const string ANIMATION_DEPLOYED = "Deployed";
1975
1976 float m_ConstructionKitHealth; //stored health value for used construction kit
1977
1979
1980 bool m_HasBase;
1981 //variables for synchronization of base building parts (2x31 is the current limit)
1982 int m_SyncParts01; //synchronization for already built parts (31 parts)
1983 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1984 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1985 int m_InteractedPartId; //construction part id that an action was performed on
1986 int m_PerformedActionId; //action id that was performed on a construction part
1987
1988 //Sounds
1989 //build
1990 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1991 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1992 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1993 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1994 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1995 //dismantle
1996 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1997 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1998 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1999 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2000 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2001
2002 protected EffectSound m_Sound;
2003
2007
2008 // Constructor
2009 void BaseBuildingBase()
2010 {
2012
2013 //synchronized variables
2014 RegisterNetSyncVariableInt("m_SyncParts01");
2015 RegisterNetSyncVariableInt("m_SyncParts02");
2016 RegisterNetSyncVariableInt("m_SyncParts03");
2017 RegisterNetSyncVariableInt("m_InteractedPartId");
2018 RegisterNetSyncVariableInt("m_PerformedActionId");
2019 RegisterNetSyncVariableBool("m_HasBase");
2020
2021 //Construction init
2023
2024 if (ConfigIsExisting("hybridAttachments"))
2025 {
2027 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2028 }
2029 if (ConfigIsExisting("mountables"))
2030 {
2032 ConfigGetTextArray("mountables", m_Mountables);
2033 }
2034
2035 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2036 }
2037
2038 override void EEDelete(EntityAI parent)
2039 {
2040 super.EEDelete(parent);
2041
2042 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2044
2045 }
2046
2047 override string GetInvulnerabilityTypeString()
2048 {
2049 return "disableBaseDamage";
2050 }
2051
2052 override bool CanObstruct()
2053 {
2054 return true;
2055 }
2056
2057 override int GetHideIconMask()
2058 {
2059 return EInventoryIconVisibility.HIDE_VICINITY;
2060 }
2061
2062 // --- SYNCHRONIZATION
2064 {
2065 if (GetGame().IsServer())
2066 SetSynchDirty();
2067 }
2068
2069 override void OnVariablesSynchronized()
2070 {
2071 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2072 super.OnVariablesSynchronized();
2073
2074 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2075 }
2076
2077 protected void OnSynchronizedClient()
2078 {
2079 //update parts
2081
2082 //update action on part
2084
2085 //update visuals (client)
2086 UpdateVisuals();
2087 }
2088
2089 //parts synchronization
2091 {
2092 //part_id must starts from index = 1
2093 int offset;
2094 int mask;
2095
2096 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2097 {
2098 offset = part_id - 1;
2099 mask = 1 << offset;
2100
2102 }
2103 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2104 {
2105 offset = (part_id % 32);
2106 mask = 1 << offset;
2107
2109 }
2110 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2111 {
2112 offset = (part_id % 63);
2113 mask = 1 << offset;
2114
2116 }
2117 }
2118
2120 {
2121 //part_id must starts from index = 1
2122 int offset;
2123 int mask;
2124
2125 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2126 {
2127 offset = part_id - 1;
2128 mask = 1 << offset;
2129
2131 }
2132 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2133 {
2134 offset = (part_id % 32);
2135 mask = 1 << offset;
2136
2138 }
2139 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2140 {
2141 offset = (part_id % 63);
2142 mask = 1 << offset;
2143
2145 }
2146 }
2147
2149 {
2150 //part_id must starts from index = 1
2151 int offset;
2152 int mask;
2153
2154 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2155 {
2156 offset = part_id - 1;
2157 mask = 1 << offset;
2158
2159 if ((m_SyncParts01 & mask) > 0)
2160 return true;
2161 }
2162 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2163 {
2164 offset = (part_id % 32);
2165 mask = 1 << offset;
2166
2167 if ((m_SyncParts02 & mask) > 0)
2168 return true;
2169 }
2170 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2171 {
2172 offset = (part_id % 63);
2173 mask = 1 << offset;
2174
2175 if ((m_SyncParts03 & mask) > 0)
2176 return true;
2177 }
2178
2179 return false;
2180 }
2181
2182 protected void RegisterActionForSync(int part_id, int action_id)
2183 {
2186 }
2187
2188 protected void ResetActionSyncData()
2189 {
2190 //reset data
2191 m_InteractedPartId = -1;
2193 }
2194
2195 protected void SetActionFromSyncData()
2196 {
2197 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2198 {
2201
2202 switch (build_action_id)
2203 {
2207 }
2208 }
2209 }
2210 //------
2211
2213 {
2214 string key = part.m_PartName;
2215 bool is_base = part.IsBase();
2217 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2219 {
2220 if (!part.IsBuilt())
2221 {
2222 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2223 GetConstruction().AddToConstructedParts(key);
2224 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2225
2226 if (is_base)
2227 {
2229 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2230 }
2231 }
2232 }
2233 else
2234 {
2235 if (part.IsBuilt())
2236 {
2237 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2238 GetConstruction().RemoveFromConstructedParts(key);
2239 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2240
2241 if (is_base)
2242 {
2244 AddProxyPhysics(ANIMATION_DEPLOYED);
2245 }
2246 }
2247 }
2248
2249 //check slot lock for material attachments
2250 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2251 }
2252
2253 //set construction parts based on synchronized data
2255 {
2258
2259 for (int i = 0; i < construction_parts.Count(); ++i)
2260 {
2261 string key = construction_parts.GetKey(i);
2264 }
2265
2266 //regenerate navmesh
2267 UpdateNavmesh();
2268 }
2269
2271 {
2274
2275 for (int i = 0; i < construction_parts.Count(); ++i)
2276 {
2277 string key = construction_parts.GetKey(i);
2279
2280 if (value.GetId() == id)
2281 return value;
2282 }
2283
2284 return NULL;
2285 }
2286 //
2287
2288 //Base
2289 bool HasBase()
2290 {
2291 return m_HasBase;
2292 }
2293
2294 void SetBaseState(bool has_base)
2295 {
2297 }
2298
2299 override bool IsDeployable()
2300 {
2301 return true;
2302 }
2303
2304 bool IsOpened()
2305 {
2306 return false;
2307 }
2308
2309 //--- CONSTRUCTION KIT
2311 {
2315
2316 return construction_kit;
2317 }
2318
2320 {
2321 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2324 }
2325
2326 protected vector GetKitSpawnPosition()
2327 {
2328 return GetPosition();
2329 }
2330
2331 protected string GetConstructionKitType()
2332 {
2333 return "";
2334 }
2335
2337 {
2339 GetGame().ObjectDelete(construction_kit);
2340 }
2341
2342 //--- CONSTRUCTION
2343 void DestroyConstruction()
2344 {
2345 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2346 GetGame().ObjectDelete(this);
2347 }
2348
2349 // --- EVENTS
2350 override void OnStoreSave(ParamsWriteContext ctx)
2351 {
2352 super.OnStoreSave(ctx);
2353
2354 //sync parts 01
2355 ctx.Write(m_SyncParts01);
2356 ctx.Write(m_SyncParts02);
2357 ctx.Write(m_SyncParts03);
2358
2359 ctx.Write(m_HasBase);
2360 }
2361
2362 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2363 {
2364 if (!super.OnStoreLoad(ctx, version))
2365 return false;
2366
2367 //--- Base building data ---
2368 //Restore synced parts data
2369 if (!ctx.Read(m_SyncParts01))
2370 {
2371 m_SyncParts01 = 0; //set default
2372 return false;
2373 }
2374 if (!ctx.Read(m_SyncParts02))
2375 {
2376 m_SyncParts02 = 0; //set default
2377 return false;
2378 }
2379 if (!ctx.Read(m_SyncParts03))
2380 {
2381 m_SyncParts03 = 0; //set default
2382 return false;
2383 }
2384
2385 //has base
2386 if (!ctx.Read(m_HasBase))
2387 {
2388 m_HasBase = false;
2389 return false;
2390 }
2391 //---
2392
2393 return true;
2394 }
2395
2396 override void AfterStoreLoad()
2397 {
2398 super.AfterStoreLoad();
2399
2402 }
2403
2405 {
2406 //update server data
2408
2409 //set base state
2410 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2411 SetBaseState(construction_part.IsBuilt()) ;
2412
2413 //synchronize after load
2415 }
2416
2417 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2418 {
2420 return;
2421
2422 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2423
2424 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2425 return;
2426
2428 string part_name = zone;
2429 part_name.ToLower();
2430
2432 {
2434
2435 if (construction_part && construction.IsPartConstructed(part_name))
2436 {
2437 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2438 construction.DestroyConnectedParts(part_name);
2439 }
2440
2441 //barbed wire handling (hack-ish)
2442 if (part_name.Contains("barbed"))
2443 {
2444 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2445 if (barbed_wire)
2446 barbed_wire.SetMountedState(false);
2447 }
2448 }
2449 }
2450
2451 override void EEOnAfterLoad()
2452 {
2454 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2455
2456 super.EEOnAfterLoad();
2457 }
2458
2459 override void EEInit()
2460 {
2461 super.EEInit();
2462
2463 // init visuals and physics
2464 InitBaseState();
2465
2466 //debug
2467#ifdef DEVELOPER
2469#endif
2470 }
2471
2472 override void EEItemAttached(EntityAI item, string slot_name)
2473 {
2474 super.EEItemAttached(item, slot_name);
2475
2477 UpdateVisuals();
2479 }
2480
2481 override void EEItemDetached(EntityAI item, string slot_name)
2482 {
2483 super.EEItemDetached(item, slot_name);
2484
2485 UpdateVisuals();
2487 }
2488
2489 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2490 {
2492 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2493
2496 }
2497
2498 //ignore out of reach condition
2499 override bool IgnoreOutOfReachCondition()
2500 {
2501 return true;
2502 }
2503
2504 //CONSTRUCTION EVENTS
2505 //Build
2506 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2507 {
2509
2510 //check base state
2511 if (construtionPart.IsBase())
2512 {
2513 SetBaseState(true);
2514
2515 //spawn kit
2517 }
2518
2519 //register constructed parts for synchronization
2521
2522 //register action that was performed on part
2524
2525 //synchronize
2527
2528 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2529
2530 UpdateNavmesh();
2531
2532 //update visuals
2533 UpdateVisuals();
2534
2535 //reset action sync data
2536 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2537 }
2538
2539 void OnPartBuiltClient(string part_name, int action_id)
2540 {
2541 //play sound
2543 }
2544
2545 //Dismantle
2547 {
2548 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2550
2551 //register constructed parts for synchronization
2553
2554 //register action that was performed on part
2556
2557 //synchronize
2559
2560 // server part of sync, client will be synced from SetPartsFromSyncData
2562
2563 UpdateNavmesh();
2564
2565 //update visuals
2566 UpdateVisuals();
2567
2568 //reset action sync data
2569 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2570
2571 //check base state
2572 if (construtionPart.IsBase())
2573 {
2574 //Destroy construction
2575 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2576 }
2577 }
2578
2580 {
2581 //play sound
2583 }
2584
2585 //Destroy
2587 {
2588 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2590
2591 //register constructed parts for synchronization
2593
2594 //register action that was performed on part
2596
2597 //synchronize
2599
2600 // server part of sync, client will be synced from SetPartsFromSyncData
2602
2603 UpdateNavmesh();
2604
2605 //update visuals
2606 UpdateVisuals();
2607
2608 //reset action sync data
2609 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2610
2611 //check base state
2612 if (construtionPart.IsBase())
2613 {
2614 //Destroy construction
2615 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2616 }
2617 }
2618
2619 void OnPartDestroyedClient(string part_name, int action_id)
2620 {
2621 //play sound
2623 }
2624
2625 // --- UPDATE
2626 void InitBaseState()
2627 {
2628 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2629
2630 InitVisuals();
2631 UpdateNavmesh(); //regenerate navmesh
2632 GetConstruction().InitBaseState();
2633 }
2634
2635 void InitVisuals()
2636 {
2637 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2638 //check base
2639 if (!HasBase())
2640 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2641 else
2642 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2643
2644 GetConstruction().UpdateVisuals();
2645 }
2646
2647 void UpdateVisuals()
2648 {
2650
2652 foreach (string slotName : attachmentSlots)
2654
2655 //check base
2656 if (!HasBase())
2657 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2658 else
2659 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2660
2661 GetConstruction().UpdateVisuals();
2662 }
2663
2665 {
2666 string slotNameMounted = slot_name + "_Mounted";
2667 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2668
2669 if (attachment)
2670 {
2671 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2672 if (barbedWire && barbedWire.IsMounted())
2674 else
2676
2677 if (is_locked)
2678 {
2679 SetAnimationPhase(slotNameMounted, 0);
2680 SetAnimationPhase(slot_name, 1);
2681 }
2682 else
2683 {
2684 SetAnimationPhase(slotNameMounted, 1);
2685 SetAnimationPhase(slot_name, 0);
2686 }
2687 }
2688 else
2689 {
2690 SetAnimationPhase(slotNameMounted, 1);
2691 SetAnimationPhase(slot_name, 1);
2692
2694 }
2695 }
2696
2697 // avoid calling this function on frequent occasions, it's a massive performance hit
2698 void UpdatePhysics()
2699 {
2701 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2702
2705
2707 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2708
2709 foreach (string slotName : attachmentSlots)
2711
2712 //check base
2713 if (!HasBase())
2714 {
2716 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2717
2718 AddProxyPhysics(ANIMATION_DEPLOYED);
2719 }
2720 else
2721 {
2723 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2724
2725 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2726 }
2727
2728 GetConstruction().UpdatePhysics();
2729 UpdateNavmesh();
2730 }
2731
2733 {
2734 //checks for invalid appends; hotfix
2735 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2736 return;
2737 //----------------------------------
2738 string slot_name_mounted = slot_name + "_Mounted";
2739 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2740
2741 //remove proxy physics
2742 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2743 RemoveProxyPhysics(slot_name_mounted);
2744 RemoveProxyPhysics(slot_name);
2745
2746 if (attachment)
2747 {
2748 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2749 if (is_locked)
2750 {
2751 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2752 AddProxyPhysics(slot_name_mounted);
2753 }
2754 else
2755 {
2756 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2757 AddProxyPhysics(slot_name);
2758 }
2759 }
2760 }
2761
2762 protected void UpdateNavmesh()
2763 {
2764 SetAffectPathgraph(true, false);
2765 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2766 }
2767
2768 override bool CanUseConstruction()
2769 {
2770 return true;
2771 }
2772
2773 override bool CanUseConstructionBuild()
2774 {
2775 return true;
2776 }
2777
2779 {
2780 if (attachment)
2781 {
2783 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2784
2785 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2786 }
2787
2788 return false;
2789 }
2790
2791 protected bool IsAttachmentSlotLocked(string slot_name)
2792 {
2793 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2794 }
2795
2796 //--- ATTACHMENT SLOTS
2798 {
2799 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2800 if (GetGame().ConfigIsExisting(config_path))
2801 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2802 }
2803
2805 {
2806 return true;
2807 }
2808
2809 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2810 {
2811 return true;
2812 }
2813
2814 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2815 {
2816 return true;
2817 }
2818
2819 // --- INIT
2820 void ConstructionInit()
2821 {
2822 if (!m_Construction)
2823 m_Construction = new Construction(this);
2824
2825 GetConstruction().Init();
2826 }
2827
2829 {
2830 return m_Construction;
2831 }
2832
2833 //--- INVENTORY/ATTACHMENTS CONDITIONS
2834 //attachments
2836 {
2837 return super.CanReceiveAttachment(attachment, slotId);
2838 }
2839
2841 {
2842 int attachment_count = GetInventory().AttachmentCount();
2843 if (attachment_count > 0)
2844 {
2845 if (HasBase() && attachment_count == 1)
2846 return false;
2847
2848 return true;
2849 }
2850
2851 return false;
2852 }
2853
2854 override bool ShowZonesHealth()
2855 {
2856 return true;
2857 }
2858
2859 //this into/outo parent.Cargo
2860 override bool CanPutInCargo(EntityAI parent)
2861 {
2862 return false;
2863 }
2864
2865 override bool CanRemoveFromCargo(EntityAI parent)
2866 {
2867 return false;
2868 }
2869
2870 //hands
2871 override bool CanPutIntoHands(EntityAI parent)
2872 {
2873 return false;
2874 }
2875
2876 //--- ACTION CONDITIONS
2877 //direction
2878 override bool IsFacingPlayer(PlayerBase player, string selection)
2879 {
2880 return true;
2881 }
2882
2883 override bool IsPlayerInside(PlayerBase player, string selection)
2884 {
2885 return true;
2886 }
2887
2890 {
2891 return false;
2892 }
2893
2894 //camera direction check
2895 bool IsFacingCamera(string selection)
2896 {
2897 return true;
2898 }
2899
2900 //roof check
2902 {
2903 return false;
2904 }
2905
2906 //selection->player distance check
2907 bool HasProperDistance(string selection, PlayerBase player)
2908 {
2909 return true;
2910 }
2911
2912 //folding
2914 {
2915 if (HasBase() || GetInventory().AttachmentCount() > 0)
2916 return false;
2917
2918 return true;
2919 }
2920
2922 {
2925
2926 return item;
2927 }
2928
2929 //Damage triggers (barbed wire)
2930 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2931 {
2932 if (GetGame() && GetGame().IsServer())
2933 {
2934 //destroy area damage if some already exists
2936
2937 //create new area damage
2939 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2940
2941 vector min_max[2];
2942 if (MemoryPointExists(slot_name + "_min"))
2943 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2944 if (MemoryPointExists(slot_name + "_max"))
2945 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2946
2947 //get proper trigger extents (min<max)
2948 vector extents[2];
2949 GetConstruction().GetTriggerExtents(min_max, extents);
2950
2951 //get box center
2952 vector center;
2953 center = GetConstruction().GetBoxCenter(min_max);
2954 center = ModelToWorld(center);
2955
2956 //rotate center if needed
2959
2960 areaDamage.SetExtents(extents[0], extents[1]);
2961 areaDamage.SetAreaPosition(center);
2962 areaDamage.SetAreaOrientation(orientation);
2963 areaDamage.SetLoopInterval(1.0);
2964 areaDamage.SetDeferDuration(0.2);
2965 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2966 areaDamage.SetAmmoName("BarbedWireHit");
2967 areaDamage.Spawn();
2968
2970 }
2971 }
2972
2974 {
2975 if (angle_deg != 0)
2976 {
2977 //orientation
2979
2980 //center
2982 if (MemoryPointExists("rotate_axis"))
2983 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2986 center[0] = r_center_x;
2987 center[2] = r_center_z;
2988 }
2989 }
2990
2991 void DestroyAreaDamage(string slot_name)
2992 {
2993 if (GetGame() && GetGame().IsServer())
2994 {
2997 {
2998 if (areaDamage)
2999 areaDamage.Destroy();
3000
3002 }
3003 }
3004 }
3005
3006 override bool IsIgnoredByConstruction()
3007 {
3008 return true;
3009 }
3010
3011 //================================================================
3012 // SOUNDS
3013 //================================================================
3014 protected void SoundBuildStart(string part_name)
3015 {
3016 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3017 }
3018
3019 protected void SoundDismantleStart(string part_name)
3020 {
3021 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3022 }
3023
3024 protected void SoundDestroyStart(string part_name)
3025 {
3026 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3027 }
3028
3029 protected string GetBuildSoundByMaterial(string part_name)
3030 {
3032
3033 switch (material_type)
3034 {
3035 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3036 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3037 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3038 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3039 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3040 }
3041
3042 return "";
3043 }
3044
3045 protected string GetDismantleSoundByMaterial(string part_name)
3046 {
3048
3049 switch (material_type)
3050 {
3051 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3052 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3053 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3054 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3055 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3056 }
3057
3058 return "";
3059 }
3060
3061 //misc
3063 {
3064 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3065 {
3066 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3068 SetHealth(slot_name, "Health", item.GetHealth());
3069 }
3070 }
3071
3072 override int GetDamageSystemVersionChange()
3073 {
3074 return 111;
3075 }
3076
3077 override void SetActions()
3078 {
3079 super.SetActions();
3080
3082 //AddAction(ActionTakeHybridAttachment);
3083 //AddAction(ActionTakeHybridAttachmentToHands);
3086 }
3087
3088 //================================================================
3089 // DEBUG
3090 //================================================================
3091 protected void DebugCustomState()
3092 {
3093 }
3094
3097 {
3098 return null;
3099 }
3100
3101 override void OnDebugSpawn()
3102 {
3103 FullyBuild();
3104 }
3105
3106 void FullyBuild()
3107 {
3109 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3110
3111 Man p;
3112
3113#ifdef SERVER
3115 GetGame().GetWorld().GetPlayerList(players);
3116 if (players.Count())
3117 p = players[0];
3118#else
3119 p = GetGame().GetPlayer();
3120#endif
3121
3122 foreach (ConstructionPart part : parts)
3123 {
3124 bool excluded = false;
3125 string partName = part.GetPartName();
3126 if (excludes)
3127 {
3128 foreach (string exclude : excludes)
3129 {
3130 if (partName.Contains(exclude))
3131 {
3132 excluded = true;
3133 break;
3134 }
3135 }
3136 }
3137
3138 if (!excluded)
3140 }
3141
3142 GetConstruction().UpdateVisuals();
3143 }
3144}
3145
3146void bsbDebugPrint(string s)
3147{
3148#ifdef BSB_DEBUG
3149 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3150#else
3151 //Print("" + s); // comment/uncomment to hide/see debug logs
3152#endif
3153}
3154void bsbDebugSpam(string s)
3155{
3156#ifdef BSB_DEBUG_SPAM
3157 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3158#else
3159 //Print("" + s); // comment/uncomment to hide/see debug logs
3160#endif
3161}

Referenced by IEntity::CanProxyObstruct().

◆ CanUseConstructionBuild()

override bool bsbDebugPrint::CanUseConstructionBuild ( )
protected

Definition at line 1976 of file BaseBuildingBase.c.

1978{
1979 const string ANIMATION_DEPLOYED = "Deployed";
1980
1981 float m_ConstructionKitHealth; //stored health value for used construction kit
1982
1984
1985 bool m_HasBase;
1986 //variables for synchronization of base building parts (2x31 is the current limit)
1987 int m_SyncParts01; //synchronization for already built parts (31 parts)
1988 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1989 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1990 int m_InteractedPartId; //construction part id that an action was performed on
1991 int m_PerformedActionId; //action id that was performed on a construction part
1992
1993 //Sounds
1994 //build
1995 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1996 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1997 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1998 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1999 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2000 //dismantle
2001 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2002 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2003 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2004 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2005 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2006
2007 protected EffectSound m_Sound;
2008
2012
2013 // Constructor
2014 void BaseBuildingBase()
2015 {
2017
2018 //synchronized variables
2019 RegisterNetSyncVariableInt("m_SyncParts01");
2020 RegisterNetSyncVariableInt("m_SyncParts02");
2021 RegisterNetSyncVariableInt("m_SyncParts03");
2022 RegisterNetSyncVariableInt("m_InteractedPartId");
2023 RegisterNetSyncVariableInt("m_PerformedActionId");
2024 RegisterNetSyncVariableBool("m_HasBase");
2025
2026 //Construction init
2028
2029 if (ConfigIsExisting("hybridAttachments"))
2030 {
2032 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2033 }
2034 if (ConfigIsExisting("mountables"))
2035 {
2037 ConfigGetTextArray("mountables", m_Mountables);
2038 }
2039
2040 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2041 }
2042
2043 override void EEDelete(EntityAI parent)
2044 {
2045 super.EEDelete(parent);
2046
2047 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2049
2050 }
2051
2052 override string GetInvulnerabilityTypeString()
2053 {
2054 return "disableBaseDamage";
2055 }
2056
2057 override bool CanObstruct()
2058 {
2059 return true;
2060 }
2061
2062 override int GetHideIconMask()
2063 {
2064 return EInventoryIconVisibility.HIDE_VICINITY;
2065 }
2066
2067 // --- SYNCHRONIZATION
2069 {
2070 if (GetGame().IsServer())
2071 SetSynchDirty();
2072 }
2073
2074 override void OnVariablesSynchronized()
2075 {
2076 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2077 super.OnVariablesSynchronized();
2078
2079 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2080 }
2081
2082 protected void OnSynchronizedClient()
2083 {
2084 //update parts
2086
2087 //update action on part
2089
2090 //update visuals (client)
2091 UpdateVisuals();
2092 }
2093
2094 //parts synchronization
2096 {
2097 //part_id must starts from index = 1
2098 int offset;
2099 int mask;
2100
2101 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2102 {
2103 offset = part_id - 1;
2104 mask = 1 << offset;
2105
2107 }
2108 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2109 {
2110 offset = (part_id % 32);
2111 mask = 1 << offset;
2112
2114 }
2115 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2116 {
2117 offset = (part_id % 63);
2118 mask = 1 << offset;
2119
2121 }
2122 }
2123
2125 {
2126 //part_id must starts from index = 1
2127 int offset;
2128 int mask;
2129
2130 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2131 {
2132 offset = part_id - 1;
2133 mask = 1 << offset;
2134
2136 }
2137 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2138 {
2139 offset = (part_id % 32);
2140 mask = 1 << offset;
2141
2143 }
2144 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2145 {
2146 offset = (part_id % 63);
2147 mask = 1 << offset;
2148
2150 }
2151 }
2152
2154 {
2155 //part_id must starts from index = 1
2156 int offset;
2157 int mask;
2158
2159 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2160 {
2161 offset = part_id - 1;
2162 mask = 1 << offset;
2163
2164 if ((m_SyncParts01 & mask) > 0)
2165 return true;
2166 }
2167 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2168 {
2169 offset = (part_id % 32);
2170 mask = 1 << offset;
2171
2172 if ((m_SyncParts02 & mask) > 0)
2173 return true;
2174 }
2175 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2176 {
2177 offset = (part_id % 63);
2178 mask = 1 << offset;
2179
2180 if ((m_SyncParts03 & mask) > 0)
2181 return true;
2182 }
2183
2184 return false;
2185 }
2186
2187 protected void RegisterActionForSync(int part_id, int action_id)
2188 {
2191 }
2192
2193 protected void ResetActionSyncData()
2194 {
2195 //reset data
2196 m_InteractedPartId = -1;
2198 }
2199
2200 protected void SetActionFromSyncData()
2201 {
2202 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2203 {
2206
2207 switch (build_action_id)
2208 {
2212 }
2213 }
2214 }
2215 //------
2216
2218 {
2219 string key = part.m_PartName;
2220 bool is_base = part.IsBase();
2222 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2224 {
2225 if (!part.IsBuilt())
2226 {
2227 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2228 GetConstruction().AddToConstructedParts(key);
2229 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2230
2231 if (is_base)
2232 {
2234 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2235 }
2236 }
2237 }
2238 else
2239 {
2240 if (part.IsBuilt())
2241 {
2242 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2243 GetConstruction().RemoveFromConstructedParts(key);
2244 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2245
2246 if (is_base)
2247 {
2249 AddProxyPhysics(ANIMATION_DEPLOYED);
2250 }
2251 }
2252 }
2253
2254 //check slot lock for material attachments
2255 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2256 }
2257
2258 //set construction parts based on synchronized data
2260 {
2263
2264 for (int i = 0; i < construction_parts.Count(); ++i)
2265 {
2266 string key = construction_parts.GetKey(i);
2269 }
2270
2271 //regenerate navmesh
2272 UpdateNavmesh();
2273 }
2274
2276 {
2279
2280 for (int i = 0; i < construction_parts.Count(); ++i)
2281 {
2282 string key = construction_parts.GetKey(i);
2284
2285 if (value.GetId() == id)
2286 return value;
2287 }
2288
2289 return NULL;
2290 }
2291 //
2292
2293 //Base
2294 bool HasBase()
2295 {
2296 return m_HasBase;
2297 }
2298
2299 void SetBaseState(bool has_base)
2300 {
2302 }
2303
2304 override bool IsDeployable()
2305 {
2306 return true;
2307 }
2308
2309 bool IsOpened()
2310 {
2311 return false;
2312 }
2313
2314 //--- CONSTRUCTION KIT
2316 {
2320
2321 return construction_kit;
2322 }
2323
2325 {
2326 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2329 }
2330
2331 protected vector GetKitSpawnPosition()
2332 {
2333 return GetPosition();
2334 }
2335
2336 protected string GetConstructionKitType()
2337 {
2338 return "";
2339 }
2340
2342 {
2344 GetGame().ObjectDelete(construction_kit);
2345 }
2346
2347 //--- CONSTRUCTION
2348 void DestroyConstruction()
2349 {
2350 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2351 GetGame().ObjectDelete(this);
2352 }
2353
2354 // --- EVENTS
2355 override void OnStoreSave(ParamsWriteContext ctx)
2356 {
2357 super.OnStoreSave(ctx);
2358
2359 //sync parts 01
2360 ctx.Write(m_SyncParts01);
2361 ctx.Write(m_SyncParts02);
2362 ctx.Write(m_SyncParts03);
2363
2364 ctx.Write(m_HasBase);
2365 }
2366
2367 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2368 {
2369 if (!super.OnStoreLoad(ctx, version))
2370 return false;
2371
2372 //--- Base building data ---
2373 //Restore synced parts data
2374 if (!ctx.Read(m_SyncParts01))
2375 {
2376 m_SyncParts01 = 0; //set default
2377 return false;
2378 }
2379 if (!ctx.Read(m_SyncParts02))
2380 {
2381 m_SyncParts02 = 0; //set default
2382 return false;
2383 }
2384 if (!ctx.Read(m_SyncParts03))
2385 {
2386 m_SyncParts03 = 0; //set default
2387 return false;
2388 }
2389
2390 //has base
2391 if (!ctx.Read(m_HasBase))
2392 {
2393 m_HasBase = false;
2394 return false;
2395 }
2396 //---
2397
2398 return true;
2399 }
2400
2401 override void AfterStoreLoad()
2402 {
2403 super.AfterStoreLoad();
2404
2407 }
2408
2410 {
2411 //update server data
2413
2414 //set base state
2415 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2416 SetBaseState(construction_part.IsBuilt()) ;
2417
2418 //synchronize after load
2420 }
2421
2422 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2423 {
2425 return;
2426
2427 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2428
2429 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2430 return;
2431
2433 string part_name = zone;
2434 part_name.ToLower();
2435
2437 {
2439
2440 if (construction_part && construction.IsPartConstructed(part_name))
2441 {
2442 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2443 construction.DestroyConnectedParts(part_name);
2444 }
2445
2446 //barbed wire handling (hack-ish)
2447 if (part_name.Contains("barbed"))
2448 {
2449 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2450 if (barbed_wire)
2451 barbed_wire.SetMountedState(false);
2452 }
2453 }
2454 }
2455
2456 override void EEOnAfterLoad()
2457 {
2459 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2460
2461 super.EEOnAfterLoad();
2462 }
2463
2464 override void EEInit()
2465 {
2466 super.EEInit();
2467
2468 // init visuals and physics
2469 InitBaseState();
2470
2471 //debug
2472#ifdef DEVELOPER
2474#endif
2475 }
2476
2477 override void EEItemAttached(EntityAI item, string slot_name)
2478 {
2479 super.EEItemAttached(item, slot_name);
2480
2482 UpdateVisuals();
2484 }
2485
2486 override void EEItemDetached(EntityAI item, string slot_name)
2487 {
2488 super.EEItemDetached(item, slot_name);
2489
2490 UpdateVisuals();
2492 }
2493
2494 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2495 {
2497 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2498
2501 }
2502
2503 //ignore out of reach condition
2504 override bool IgnoreOutOfReachCondition()
2505 {
2506 return true;
2507 }
2508
2509 //CONSTRUCTION EVENTS
2510 //Build
2511 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2512 {
2514
2515 //check base state
2516 if (construtionPart.IsBase())
2517 {
2518 SetBaseState(true);
2519
2520 //spawn kit
2522 }
2523
2524 //register constructed parts for synchronization
2526
2527 //register action that was performed on part
2529
2530 //synchronize
2532
2533 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2534
2535 UpdateNavmesh();
2536
2537 //update visuals
2538 UpdateVisuals();
2539
2540 //reset action sync data
2541 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2542 }
2543
2544 void OnPartBuiltClient(string part_name, int action_id)
2545 {
2546 //play sound
2548 }
2549
2550 //Dismantle
2552 {
2553 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2555
2556 //register constructed parts for synchronization
2558
2559 //register action that was performed on part
2561
2562 //synchronize
2564
2565 // server part of sync, client will be synced from SetPartsFromSyncData
2567
2568 UpdateNavmesh();
2569
2570 //update visuals
2571 UpdateVisuals();
2572
2573 //reset action sync data
2574 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2575
2576 //check base state
2577 if (construtionPart.IsBase())
2578 {
2579 //Destroy construction
2580 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2581 }
2582 }
2583
2585 {
2586 //play sound
2588 }
2589
2590 //Destroy
2592 {
2593 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2595
2596 //register constructed parts for synchronization
2598
2599 //register action that was performed on part
2601
2602 //synchronize
2604
2605 // server part of sync, client will be synced from SetPartsFromSyncData
2607
2608 UpdateNavmesh();
2609
2610 //update visuals
2611 UpdateVisuals();
2612
2613 //reset action sync data
2614 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2615
2616 //check base state
2617 if (construtionPart.IsBase())
2618 {
2619 //Destroy construction
2620 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2621 }
2622 }
2623
2624 void OnPartDestroyedClient(string part_name, int action_id)
2625 {
2626 //play sound
2628 }
2629
2630 // --- UPDATE
2631 void InitBaseState()
2632 {
2633 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2634
2635 InitVisuals();
2636 UpdateNavmesh(); //regenerate navmesh
2637 GetConstruction().InitBaseState();
2638 }
2639
2640 void InitVisuals()
2641 {
2642 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2643 //check base
2644 if (!HasBase())
2645 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2646 else
2647 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2648
2649 GetConstruction().UpdateVisuals();
2650 }
2651
2652 void UpdateVisuals()
2653 {
2655
2657 foreach (string slotName : attachmentSlots)
2659
2660 //check base
2661 if (!HasBase())
2662 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2663 else
2664 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2665
2666 GetConstruction().UpdateVisuals();
2667 }
2668
2670 {
2671 string slotNameMounted = slot_name + "_Mounted";
2672 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2673
2674 if (attachment)
2675 {
2676 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2677 if (barbedWire && barbedWire.IsMounted())
2679 else
2681
2682 if (is_locked)
2683 {
2684 SetAnimationPhase(slotNameMounted, 0);
2685 SetAnimationPhase(slot_name, 1);
2686 }
2687 else
2688 {
2689 SetAnimationPhase(slotNameMounted, 1);
2690 SetAnimationPhase(slot_name, 0);
2691 }
2692 }
2693 else
2694 {
2695 SetAnimationPhase(slotNameMounted, 1);
2696 SetAnimationPhase(slot_name, 1);
2697
2699 }
2700 }
2701
2702 // avoid calling this function on frequent occasions, it's a massive performance hit
2703 void UpdatePhysics()
2704 {
2706 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2707
2710
2712 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2713
2714 foreach (string slotName : attachmentSlots)
2716
2717 //check base
2718 if (!HasBase())
2719 {
2721 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2722
2723 AddProxyPhysics(ANIMATION_DEPLOYED);
2724 }
2725 else
2726 {
2728 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2729
2730 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2731 }
2732
2733 GetConstruction().UpdatePhysics();
2734 UpdateNavmesh();
2735 }
2736
2738 {
2739 //checks for invalid appends; hotfix
2740 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2741 return;
2742 //----------------------------------
2743 string slot_name_mounted = slot_name + "_Mounted";
2744 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2745
2746 //remove proxy physics
2747 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2748 RemoveProxyPhysics(slot_name_mounted);
2749 RemoveProxyPhysics(slot_name);
2750
2751 if (attachment)
2752 {
2753 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2754 if (is_locked)
2755 {
2756 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2757 AddProxyPhysics(slot_name_mounted);
2758 }
2759 else
2760 {
2761 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2762 AddProxyPhysics(slot_name);
2763 }
2764 }
2765 }
2766
2767 protected void UpdateNavmesh()
2768 {
2769 SetAffectPathgraph(true, false);
2770 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2771 }
2772
2773 override bool CanUseConstruction()
2774 {
2775 return true;
2776 }
2777
2778 override bool CanUseConstructionBuild()
2779 {
2780 return true;
2781 }
2782
2784 {
2785 if (attachment)
2786 {
2788 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2789
2790 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2791 }
2792
2793 return false;
2794 }
2795
2796 protected bool IsAttachmentSlotLocked(string slot_name)
2797 {
2798 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2799 }
2800
2801 //--- ATTACHMENT SLOTS
2803 {
2804 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2805 if (GetGame().ConfigIsExisting(config_path))
2806 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2807 }
2808
2810 {
2811 return true;
2812 }
2813
2814 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2815 {
2816 return true;
2817 }
2818
2819 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2820 {
2821 return true;
2822 }
2823
2824 // --- INIT
2825 void ConstructionInit()
2826 {
2827 if (!m_Construction)
2828 m_Construction = new Construction(this);
2829
2830 GetConstruction().Init();
2831 }
2832
2834 {
2835 return m_Construction;
2836 }
2837
2838 //--- INVENTORY/ATTACHMENTS CONDITIONS
2839 //attachments
2841 {
2842 return super.CanReceiveAttachment(attachment, slotId);
2843 }
2844
2846 {
2847 int attachment_count = GetInventory().AttachmentCount();
2848 if (attachment_count > 0)
2849 {
2850 if (HasBase() && attachment_count == 1)
2851 return false;
2852
2853 return true;
2854 }
2855
2856 return false;
2857 }
2858
2859 override bool ShowZonesHealth()
2860 {
2861 return true;
2862 }
2863
2864 //this into/outo parent.Cargo
2865 override bool CanPutInCargo(EntityAI parent)
2866 {
2867 return false;
2868 }
2869
2870 override bool CanRemoveFromCargo(EntityAI parent)
2871 {
2872 return false;
2873 }
2874
2875 //hands
2876 override bool CanPutIntoHands(EntityAI parent)
2877 {
2878 return false;
2879 }
2880
2881 //--- ACTION CONDITIONS
2882 //direction
2883 override bool IsFacingPlayer(PlayerBase player, string selection)
2884 {
2885 return true;
2886 }
2887
2888 override bool IsPlayerInside(PlayerBase player, string selection)
2889 {
2890 return true;
2891 }
2892
2895 {
2896 return false;
2897 }
2898
2899 //camera direction check
2900 bool IsFacingCamera(string selection)
2901 {
2902 return true;
2903 }
2904
2905 //roof check
2907 {
2908 return false;
2909 }
2910
2911 //selection->player distance check
2912 bool HasProperDistance(string selection, PlayerBase player)
2913 {
2914 return true;
2915 }
2916
2917 //folding
2919 {
2920 if (HasBase() || GetInventory().AttachmentCount() > 0)
2921 return false;
2922
2923 return true;
2924 }
2925
2927 {
2930
2931 return item;
2932 }
2933
2934 //Damage triggers (barbed wire)
2935 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2936 {
2937 if (GetGame() && GetGame().IsServer())
2938 {
2939 //destroy area damage if some already exists
2941
2942 //create new area damage
2944 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2945
2946 vector min_max[2];
2947 if (MemoryPointExists(slot_name + "_min"))
2948 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2949 if (MemoryPointExists(slot_name + "_max"))
2950 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2951
2952 //get proper trigger extents (min<max)
2953 vector extents[2];
2954 GetConstruction().GetTriggerExtents(min_max, extents);
2955
2956 //get box center
2957 vector center;
2958 center = GetConstruction().GetBoxCenter(min_max);
2959 center = ModelToWorld(center);
2960
2961 //rotate center if needed
2964
2965 areaDamage.SetExtents(extents[0], extents[1]);
2966 areaDamage.SetAreaPosition(center);
2967 areaDamage.SetAreaOrientation(orientation);
2968 areaDamage.SetLoopInterval(1.0);
2969 areaDamage.SetDeferDuration(0.2);
2970 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2971 areaDamage.SetAmmoName("BarbedWireHit");
2972 areaDamage.Spawn();
2973
2975 }
2976 }
2977
2979 {
2980 if (angle_deg != 0)
2981 {
2982 //orientation
2984
2985 //center
2987 if (MemoryPointExists("rotate_axis"))
2988 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2991 center[0] = r_center_x;
2992 center[2] = r_center_z;
2993 }
2994 }
2995
2996 void DestroyAreaDamage(string slot_name)
2997 {
2998 if (GetGame() && GetGame().IsServer())
2999 {
3002 {
3003 if (areaDamage)
3004 areaDamage.Destroy();
3005
3007 }
3008 }
3009 }
3010
3011 override bool IsIgnoredByConstruction()
3012 {
3013 return true;
3014 }
3015
3016 //================================================================
3017 // SOUNDS
3018 //================================================================
3019 protected void SoundBuildStart(string part_name)
3020 {
3021 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3022 }
3023
3024 protected void SoundDismantleStart(string part_name)
3025 {
3026 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3027 }
3028
3029 protected void SoundDestroyStart(string part_name)
3030 {
3031 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3032 }
3033
3034 protected string GetBuildSoundByMaterial(string part_name)
3035 {
3037
3038 switch (material_type)
3039 {
3040 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3041 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3042 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3043 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3044 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3045 }
3046
3047 return "";
3048 }
3049
3050 protected string GetDismantleSoundByMaterial(string part_name)
3051 {
3053
3054 switch (material_type)
3055 {
3056 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3057 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3058 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3059 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3060 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3061 }
3062
3063 return "";
3064 }
3065
3066 //misc
3068 {
3069 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3070 {
3071 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3073 SetHealth(slot_name, "Health", item.GetHealth());
3074 }
3075 }
3076
3077 override int GetDamageSystemVersionChange()
3078 {
3079 return 111;
3080 }
3081
3082 override void SetActions()
3083 {
3084 super.SetActions();
3085
3087 //AddAction(ActionTakeHybridAttachment);
3088 //AddAction(ActionTakeHybridAttachmentToHands);
3091 }
3092
3093 //================================================================
3094 // DEBUG
3095 //================================================================
3096 protected void DebugCustomState()
3097 {
3098 }
3099
3102 {
3103 return null;
3104 }
3105
3106 override void OnDebugSpawn()
3107 {
3108 FullyBuild();
3109 }
3110
3111 void FullyBuild()
3112 {
3114 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3115
3116 Man p;
3117
3118#ifdef SERVER
3120 GetGame().GetWorld().GetPlayerList(players);
3121 if (players.Count())
3122 p = players[0];
3123#else
3124 p = GetGame().GetPlayer();
3125#endif
3126
3127 foreach (ConstructionPart part : parts)
3128 {
3129 bool excluded = false;
3130 string partName = part.GetPartName();
3131 if (excludes)
3132 {
3133 foreach (string exclude : excludes)
3134 {
3135 if (partName.Contains(exclude))
3136 {
3137 excluded = true;
3138 break;
3139 }
3140 }
3141 }
3142
3143 if (!excluded)
3145 }
3146
3147 GetConstruction().UpdateVisuals();
3148 }
3149}
3150
3151void bsbDebugPrint(string s)
3152{
3153#ifdef BSB_DEBUG
3154 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3155#else
3156 //Print("" + s); // comment/uncomment to hide/see debug logs
3157#endif
3158}
3159void bsbDebugSpam(string s)
3160{
3161#ifdef BSB_DEBUG_SPAM
3162 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3163#else
3164 //Print("" + s); // comment/uncomment to hide/see debug logs
3165#endif
3166}

◆ CheckForHybridAttachments()

void bsbDebugPrint::CheckForHybridAttachments ( EntityAI item,
string slot_name )
protected

Definition at line 2265 of file BaseBuildingBase.c.

2267{
2268 const string ANIMATION_DEPLOYED = "Deployed";
2269
2270 float m_ConstructionKitHealth; //stored health value for used construction kit
2271
2273
2274 bool m_HasBase;
2275 //variables for synchronization of base building parts (2x31 is the current limit)
2276 int m_SyncParts01; //synchronization for already built parts (31 parts)
2277 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2278 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2279 int m_InteractedPartId; //construction part id that an action was performed on
2280 int m_PerformedActionId; //action id that was performed on a construction part
2281
2282 //Sounds
2283 //build
2284 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2285 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2286 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2287 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2288 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2289 //dismantle
2290 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2291 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2292 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2293 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2294 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2295
2296 protected EffectSound m_Sound;
2297
2301
2302 // Constructor
2303 void BaseBuildingBase()
2304 {
2306
2307 //synchronized variables
2308 RegisterNetSyncVariableInt("m_SyncParts01");
2309 RegisterNetSyncVariableInt("m_SyncParts02");
2310 RegisterNetSyncVariableInt("m_SyncParts03");
2311 RegisterNetSyncVariableInt("m_InteractedPartId");
2312 RegisterNetSyncVariableInt("m_PerformedActionId");
2313 RegisterNetSyncVariableBool("m_HasBase");
2314
2315 //Construction init
2317
2318 if (ConfigIsExisting("hybridAttachments"))
2319 {
2321 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2322 }
2323 if (ConfigIsExisting("mountables"))
2324 {
2326 ConfigGetTextArray("mountables", m_Mountables);
2327 }
2328
2329 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2330 }
2331
2332 override void EEDelete(EntityAI parent)
2333 {
2334 super.EEDelete(parent);
2335
2336 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2338
2339 }
2340
2341 override string GetInvulnerabilityTypeString()
2342 {
2343 return "disableBaseDamage";
2344 }
2345
2346 override bool CanObstruct()
2347 {
2348 return true;
2349 }
2350
2351 override int GetHideIconMask()
2352 {
2353 return EInventoryIconVisibility.HIDE_VICINITY;
2354 }
2355
2356 // --- SYNCHRONIZATION
2358 {
2359 if (GetGame().IsServer())
2360 SetSynchDirty();
2361 }
2362
2363 override void OnVariablesSynchronized()
2364 {
2365 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2366 super.OnVariablesSynchronized();
2367
2368 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2369 }
2370
2371 protected void OnSynchronizedClient()
2372 {
2373 //update parts
2375
2376 //update action on part
2378
2379 //update visuals (client)
2380 UpdateVisuals();
2381 }
2382
2383 //parts synchronization
2385 {
2386 //part_id must starts from index = 1
2387 int offset;
2388 int mask;
2389
2390 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2391 {
2392 offset = part_id - 1;
2393 mask = 1 << offset;
2394
2396 }
2397 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2398 {
2399 offset = (part_id % 32);
2400 mask = 1 << offset;
2401
2403 }
2404 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2405 {
2406 offset = (part_id % 63);
2407 mask = 1 << offset;
2408
2410 }
2411 }
2412
2414 {
2415 //part_id must starts from index = 1
2416 int offset;
2417 int mask;
2418
2419 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2420 {
2421 offset = part_id - 1;
2422 mask = 1 << offset;
2423
2425 }
2426 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2427 {
2428 offset = (part_id % 32);
2429 mask = 1 << offset;
2430
2432 }
2433 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2434 {
2435 offset = (part_id % 63);
2436 mask = 1 << offset;
2437
2439 }
2440 }
2441
2443 {
2444 //part_id must starts from index = 1
2445 int offset;
2446 int mask;
2447
2448 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2449 {
2450 offset = part_id - 1;
2451 mask = 1 << offset;
2452
2453 if ((m_SyncParts01 & mask) > 0)
2454 return true;
2455 }
2456 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2457 {
2458 offset = (part_id % 32);
2459 mask = 1 << offset;
2460
2461 if ((m_SyncParts02 & mask) > 0)
2462 return true;
2463 }
2464 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2465 {
2466 offset = (part_id % 63);
2467 mask = 1 << offset;
2468
2469 if ((m_SyncParts03 & mask) > 0)
2470 return true;
2471 }
2472
2473 return false;
2474 }
2475
2476 protected void RegisterActionForSync(int part_id, int action_id)
2477 {
2480 }
2481
2482 protected void ResetActionSyncData()
2483 {
2484 //reset data
2485 m_InteractedPartId = -1;
2487 }
2488
2489 protected void SetActionFromSyncData()
2490 {
2491 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2492 {
2495
2496 switch (build_action_id)
2497 {
2501 }
2502 }
2503 }
2504 //------
2505
2507 {
2508 string key = part.m_PartName;
2509 bool is_base = part.IsBase();
2511 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2513 {
2514 if (!part.IsBuilt())
2515 {
2516 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2517 GetConstruction().AddToConstructedParts(key);
2518 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2519
2520 if (is_base)
2521 {
2523 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2524 }
2525 }
2526 }
2527 else
2528 {
2529 if (part.IsBuilt())
2530 {
2531 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2532 GetConstruction().RemoveFromConstructedParts(key);
2533 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2534
2535 if (is_base)
2536 {
2538 AddProxyPhysics(ANIMATION_DEPLOYED);
2539 }
2540 }
2541 }
2542
2543 //check slot lock for material attachments
2544 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2545 }
2546
2547 //set construction parts based on synchronized data
2549 {
2552
2553 for (int i = 0; i < construction_parts.Count(); ++i)
2554 {
2555 string key = construction_parts.GetKey(i);
2558 }
2559
2560 //regenerate navmesh
2561 UpdateNavmesh();
2562 }
2563
2565 {
2568
2569 for (int i = 0; i < construction_parts.Count(); ++i)
2570 {
2571 string key = construction_parts.GetKey(i);
2573
2574 if (value.GetId() == id)
2575 return value;
2576 }
2577
2578 return NULL;
2579 }
2580 //
2581
2582 //Base
2583 bool HasBase()
2584 {
2585 return m_HasBase;
2586 }
2587
2588 void SetBaseState(bool has_base)
2589 {
2591 }
2592
2593 override bool IsDeployable()
2594 {
2595 return true;
2596 }
2597
2598 bool IsOpened()
2599 {
2600 return false;
2601 }
2602
2603 //--- CONSTRUCTION KIT
2605 {
2609
2610 return construction_kit;
2611 }
2612
2614 {
2615 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2618 }
2619
2620 protected vector GetKitSpawnPosition()
2621 {
2622 return GetPosition();
2623 }
2624
2625 protected string GetConstructionKitType()
2626 {
2627 return "";
2628 }
2629
2631 {
2633 GetGame().ObjectDelete(construction_kit);
2634 }
2635
2636 //--- CONSTRUCTION
2637 void DestroyConstruction()
2638 {
2639 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2640 GetGame().ObjectDelete(this);
2641 }
2642
2643 // --- EVENTS
2644 override void OnStoreSave(ParamsWriteContext ctx)
2645 {
2646 super.OnStoreSave(ctx);
2647
2648 //sync parts 01
2649 ctx.Write(m_SyncParts01);
2650 ctx.Write(m_SyncParts02);
2651 ctx.Write(m_SyncParts03);
2652
2653 ctx.Write(m_HasBase);
2654 }
2655
2656 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2657 {
2658 if (!super.OnStoreLoad(ctx, version))
2659 return false;
2660
2661 //--- Base building data ---
2662 //Restore synced parts data
2663 if (!ctx.Read(m_SyncParts01))
2664 {
2665 m_SyncParts01 = 0; //set default
2666 return false;
2667 }
2668 if (!ctx.Read(m_SyncParts02))
2669 {
2670 m_SyncParts02 = 0; //set default
2671 return false;
2672 }
2673 if (!ctx.Read(m_SyncParts03))
2674 {
2675 m_SyncParts03 = 0; //set default
2676 return false;
2677 }
2678
2679 //has base
2680 if (!ctx.Read(m_HasBase))
2681 {
2682 m_HasBase = false;
2683 return false;
2684 }
2685 //---
2686
2687 return true;
2688 }
2689
2690 override void AfterStoreLoad()
2691 {
2692 super.AfterStoreLoad();
2693
2696 }
2697
2699 {
2700 //update server data
2702
2703 //set base state
2704 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2705 SetBaseState(construction_part.IsBuilt()) ;
2706
2707 //synchronize after load
2709 }
2710
2711 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2712 {
2714 return;
2715
2716 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2717
2718 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2719 return;
2720
2722 string part_name = zone;
2723 part_name.ToLower();
2724
2726 {
2728
2729 if (construction_part && construction.IsPartConstructed(part_name))
2730 {
2731 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2732 construction.DestroyConnectedParts(part_name);
2733 }
2734
2735 //barbed wire handling (hack-ish)
2736 if (part_name.Contains("barbed"))
2737 {
2738 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2739 if (barbed_wire)
2740 barbed_wire.SetMountedState(false);
2741 }
2742 }
2743 }
2744
2745 override void EEOnAfterLoad()
2746 {
2748 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2749
2750 super.EEOnAfterLoad();
2751 }
2752
2753 override void EEInit()
2754 {
2755 super.EEInit();
2756
2757 // init visuals and physics
2758 InitBaseState();
2759
2760 //debug
2761#ifdef DEVELOPER
2763#endif
2764 }
2765
2766 override void EEItemAttached(EntityAI item, string slot_name)
2767 {
2768 super.EEItemAttached(item, slot_name);
2769
2771 UpdateVisuals();
2773 }
2774
2775 override void EEItemDetached(EntityAI item, string slot_name)
2776 {
2777 super.EEItemDetached(item, slot_name);
2778
2779 UpdateVisuals();
2781 }
2782
2783 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2784 {
2786 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2787
2790 }
2791
2792 //ignore out of reach condition
2793 override bool IgnoreOutOfReachCondition()
2794 {
2795 return true;
2796 }
2797
2798 //CONSTRUCTION EVENTS
2799 //Build
2800 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2801 {
2803
2804 //check base state
2805 if (construtionPart.IsBase())
2806 {
2807 SetBaseState(true);
2808
2809 //spawn kit
2811 }
2812
2813 //register constructed parts for synchronization
2815
2816 //register action that was performed on part
2818
2819 //synchronize
2821
2822 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2823
2824 UpdateNavmesh();
2825
2826 //update visuals
2827 UpdateVisuals();
2828
2829 //reset action sync data
2830 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2831 }
2832
2833 void OnPartBuiltClient(string part_name, int action_id)
2834 {
2835 //play sound
2837 }
2838
2839 //Dismantle
2841 {
2842 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2844
2845 //register constructed parts for synchronization
2847
2848 //register action that was performed on part
2850
2851 //synchronize
2853
2854 // server part of sync, client will be synced from SetPartsFromSyncData
2856
2857 UpdateNavmesh();
2858
2859 //update visuals
2860 UpdateVisuals();
2861
2862 //reset action sync data
2863 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2864
2865 //check base state
2866 if (construtionPart.IsBase())
2867 {
2868 //Destroy construction
2869 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2870 }
2871 }
2872
2874 {
2875 //play sound
2877 }
2878
2879 //Destroy
2881 {
2882 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2884
2885 //register constructed parts for synchronization
2887
2888 //register action that was performed on part
2890
2891 //synchronize
2893
2894 // server part of sync, client will be synced from SetPartsFromSyncData
2896
2897 UpdateNavmesh();
2898
2899 //update visuals
2900 UpdateVisuals();
2901
2902 //reset action sync data
2903 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2904
2905 //check base state
2906 if (construtionPart.IsBase())
2907 {
2908 //Destroy construction
2909 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2910 }
2911 }
2912
2913 void OnPartDestroyedClient(string part_name, int action_id)
2914 {
2915 //play sound
2917 }
2918
2919 // --- UPDATE
2920 void InitBaseState()
2921 {
2922 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2923
2924 InitVisuals();
2925 UpdateNavmesh(); //regenerate navmesh
2926 GetConstruction().InitBaseState();
2927 }
2928
2929 void InitVisuals()
2930 {
2931 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2932 //check base
2933 if (!HasBase())
2934 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2935 else
2936 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2937
2938 GetConstruction().UpdateVisuals();
2939 }
2940
2941 void UpdateVisuals()
2942 {
2944
2946 foreach (string slotName : attachmentSlots)
2948
2949 //check base
2950 if (!HasBase())
2951 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2952 else
2953 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2954
2955 GetConstruction().UpdateVisuals();
2956 }
2957
2959 {
2960 string slotNameMounted = slot_name + "_Mounted";
2961 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2962
2963 if (attachment)
2964 {
2965 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2966 if (barbedWire && barbedWire.IsMounted())
2968 else
2970
2971 if (is_locked)
2972 {
2973 SetAnimationPhase(slotNameMounted, 0);
2974 SetAnimationPhase(slot_name, 1);
2975 }
2976 else
2977 {
2978 SetAnimationPhase(slotNameMounted, 1);
2979 SetAnimationPhase(slot_name, 0);
2980 }
2981 }
2982 else
2983 {
2984 SetAnimationPhase(slotNameMounted, 1);
2985 SetAnimationPhase(slot_name, 1);
2986
2988 }
2989 }
2990
2991 // avoid calling this function on frequent occasions, it's a massive performance hit
2992 void UpdatePhysics()
2993 {
2995 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2996
2999
3001 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
3002
3003 foreach (string slotName : attachmentSlots)
3005
3006 //check base
3007 if (!HasBase())
3008 {
3010 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
3011
3012 AddProxyPhysics(ANIMATION_DEPLOYED);
3013 }
3014 else
3015 {
3017 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
3018
3019 RemoveProxyPhysics(ANIMATION_DEPLOYED);
3020 }
3021
3022 GetConstruction().UpdatePhysics();
3023 UpdateNavmesh();
3024 }
3025
3027 {
3028 //checks for invalid appends; hotfix
3029 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
3030 return;
3031 //----------------------------------
3032 string slot_name_mounted = slot_name + "_Mounted";
3033 EntityAI attachment = FindAttachmentBySlotName(slot_name);
3034
3035 //remove proxy physics
3036 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
3037 RemoveProxyPhysics(slot_name_mounted);
3038 RemoveProxyPhysics(slot_name);
3039
3040 if (attachment)
3041 {
3042 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
3043 if (is_locked)
3044 {
3045 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
3046 AddProxyPhysics(slot_name_mounted);
3047 }
3048 else
3049 {
3050 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
3051 AddProxyPhysics(slot_name);
3052 }
3053 }
3054 }
3055
3056 protected void UpdateNavmesh()
3057 {
3058 SetAffectPathgraph(true, false);
3059 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
3060 }
3061
3062 override bool CanUseConstruction()
3063 {
3064 return true;
3065 }
3066
3067 override bool CanUseConstructionBuild()
3068 {
3069 return true;
3070 }
3071
3073 {
3074 if (attachment)
3075 {
3077 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
3078
3079 return GetInventory().GetSlotLock(inventory_location.GetSlot());
3080 }
3081
3082 return false;
3083 }
3084
3085 protected bool IsAttachmentSlotLocked(string slot_name)
3086 {
3087 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
3088 }
3089
3090 //--- ATTACHMENT SLOTS
3092 {
3093 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
3094 if (GetGame().ConfigIsExisting(config_path))
3095 GetGame().ConfigGetTextArray(config_path, attachment_slots);
3096 }
3097
3099 {
3100 return true;
3101 }
3102
3103 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
3104 {
3105 return true;
3106 }
3107
3108 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
3109 {
3110 return true;
3111 }
3112
3113 // --- INIT
3114 void ConstructionInit()
3115 {
3116 if (!m_Construction)
3117 m_Construction = new Construction(this);
3118
3119 GetConstruction().Init();
3120 }
3121
3123 {
3124 return m_Construction;
3125 }
3126
3127 //--- INVENTORY/ATTACHMENTS CONDITIONS
3128 //attachments
3130 {
3131 return super.CanReceiveAttachment(attachment, slotId);
3132 }
3133
3135 {
3136 int attachment_count = GetInventory().AttachmentCount();
3137 if (attachment_count > 0)
3138 {
3139 if (HasBase() && attachment_count == 1)
3140 return false;
3141
3142 return true;
3143 }
3144
3145 return false;
3146 }
3147
3148 override bool ShowZonesHealth()
3149 {
3150 return true;
3151 }
3152
3153 //this into/outo parent.Cargo
3154 override bool CanPutInCargo(EntityAI parent)
3155 {
3156 return false;
3157 }
3158
3159 override bool CanRemoveFromCargo(EntityAI parent)
3160 {
3161 return false;
3162 }
3163
3164 //hands
3165 override bool CanPutIntoHands(EntityAI parent)
3166 {
3167 return false;
3168 }
3169
3170 //--- ACTION CONDITIONS
3171 //direction
3172 override bool IsFacingPlayer(PlayerBase player, string selection)
3173 {
3174 return true;
3175 }
3176
3177 override bool IsPlayerInside(PlayerBase player, string selection)
3178 {
3179 return true;
3180 }
3181
3184 {
3185 return false;
3186 }
3187
3188 //camera direction check
3189 bool IsFacingCamera(string selection)
3190 {
3191 return true;
3192 }
3193
3194 //roof check
3196 {
3197 return false;
3198 }
3199
3200 //selection->player distance check
3201 bool HasProperDistance(string selection, PlayerBase player)
3202 {
3203 return true;
3204 }
3205
3206 //folding
3208 {
3209 if (HasBase() || GetInventory().AttachmentCount() > 0)
3210 return false;
3211
3212 return true;
3213 }
3214
3216 {
3219
3220 return item;
3221 }
3222
3223 //Damage triggers (barbed wire)
3224 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3225 {
3226 if (GetGame() && GetGame().IsServer())
3227 {
3228 //destroy area damage if some already exists
3230
3231 //create new area damage
3233 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3234
3235 vector min_max[2];
3236 if (MemoryPointExists(slot_name + "_min"))
3237 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3238 if (MemoryPointExists(slot_name + "_max"))
3239 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3240
3241 //get proper trigger extents (min<max)
3242 vector extents[2];
3243 GetConstruction().GetTriggerExtents(min_max, extents);
3244
3245 //get box center
3246 vector center;
3247 center = GetConstruction().GetBoxCenter(min_max);
3248 center = ModelToWorld(center);
3249
3250 //rotate center if needed
3253
3254 areaDamage.SetExtents(extents[0], extents[1]);
3255 areaDamage.SetAreaPosition(center);
3256 areaDamage.SetAreaOrientation(orientation);
3257 areaDamage.SetLoopInterval(1.0);
3258 areaDamage.SetDeferDuration(0.2);
3259 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3260 areaDamage.SetAmmoName("BarbedWireHit");
3261 areaDamage.Spawn();
3262
3264 }
3265 }
3266
3268 {
3269 if (angle_deg != 0)
3270 {
3271 //orientation
3273
3274 //center
3276 if (MemoryPointExists("rotate_axis"))
3277 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3280 center[0] = r_center_x;
3281 center[2] = r_center_z;
3282 }
3283 }
3284
3285 void DestroyAreaDamage(string slot_name)
3286 {
3287 if (GetGame() && GetGame().IsServer())
3288 {
3291 {
3292 if (areaDamage)
3293 areaDamage.Destroy();
3294
3296 }
3297 }
3298 }
3299
3300 override bool IsIgnoredByConstruction()
3301 {
3302 return true;
3303 }
3304
3305 //================================================================
3306 // SOUNDS
3307 //================================================================
3308 protected void SoundBuildStart(string part_name)
3309 {
3310 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3311 }
3312
3313 protected void SoundDismantleStart(string part_name)
3314 {
3315 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3316 }
3317
3318 protected void SoundDestroyStart(string part_name)
3319 {
3320 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3321 }
3322
3323 protected string GetBuildSoundByMaterial(string part_name)
3324 {
3326
3327 switch (material_type)
3328 {
3329 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3330 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3331 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3332 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3333 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3334 }
3335
3336 return "";
3337 }
3338
3339 protected string GetDismantleSoundByMaterial(string part_name)
3340 {
3342
3343 switch (material_type)
3344 {
3345 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3346 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3347 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3348 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3349 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3350 }
3351
3352 return "";
3353 }
3354
3355 //misc
3357 {
3358 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3359 {
3360 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3362 SetHealth(slot_name, "Health", item.GetHealth());
3363 }
3364 }
3365
3366 override int GetDamageSystemVersionChange()
3367 {
3368 return 111;
3369 }
3370
3371 override void SetActions()
3372 {
3373 super.SetActions();
3374
3376 //AddAction(ActionTakeHybridAttachment);
3377 //AddAction(ActionTakeHybridAttachmentToHands);
3380 }
3381
3382 //================================================================
3383 // DEBUG
3384 //================================================================
3385 protected void DebugCustomState()
3386 {
3387 }
3388
3391 {
3392 return null;
3393 }
3394
3395 override void OnDebugSpawn()
3396 {
3397 FullyBuild();
3398 }
3399
3400 void FullyBuild()
3401 {
3403 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3404
3405 Man p;
3406
3407#ifdef SERVER
3409 GetGame().GetWorld().GetPlayerList(players);
3410 if (players.Count())
3411 p = players[0];
3412#else
3413 p = GetGame().GetPlayer();
3414#endif
3415
3416 foreach (ConstructionPart part : parts)
3417 {
3418 bool excluded = false;
3419 string partName = part.GetPartName();
3420 if (excludes)
3421 {
3422 foreach (string exclude : excludes)
3423 {
3424 if (partName.Contains(exclude))
3425 {
3426 excluded = true;
3427 break;
3428 }
3429 }
3430 }
3431
3432 if (!excluded)
3434 }
3435
3436 GetConstruction().UpdateVisuals();
3437 }
3438}
3439
3440void bsbDebugPrint(string s)
3441{
3442#ifdef BSB_DEBUG
3443 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3444#else
3445 //Print("" + s); // comment/uncomment to hide/see debug logs
3446#endif
3447}
3448void bsbDebugSpam(string s)
3449{
3450#ifdef BSB_DEBUG_SPAM
3451 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3452#else
3453 //Print("" + s); // comment/uncomment to hide/see debug logs
3454#endif
3455}

Referenced by ItemBase::EEItemAttached().

◆ CheckLevelVerticalDistance()

bool bsbDebugPrint::CheckLevelVerticalDistance ( float max_dist,
string selection,
PlayerBase player )
protected

Definition at line 2017 of file BaseBuildingBase.c.

2019{
2020 const string ANIMATION_DEPLOYED = "Deployed";
2021
2022 float m_ConstructionKitHealth; //stored health value for used construction kit
2023
2025
2026 bool m_HasBase;
2027 //variables for synchronization of base building parts (2x31 is the current limit)
2028 int m_SyncParts01; //synchronization for already built parts (31 parts)
2029 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2030 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2031 int m_InteractedPartId; //construction part id that an action was performed on
2032 int m_PerformedActionId; //action id that was performed on a construction part
2033
2034 //Sounds
2035 //build
2036 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2037 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2038 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2039 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2040 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2041 //dismantle
2042 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2043 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2044 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2045 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2046 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2047
2048 protected EffectSound m_Sound;
2049
2053
2054 // Constructor
2055 void BaseBuildingBase()
2056 {
2058
2059 //synchronized variables
2060 RegisterNetSyncVariableInt("m_SyncParts01");
2061 RegisterNetSyncVariableInt("m_SyncParts02");
2062 RegisterNetSyncVariableInt("m_SyncParts03");
2063 RegisterNetSyncVariableInt("m_InteractedPartId");
2064 RegisterNetSyncVariableInt("m_PerformedActionId");
2065 RegisterNetSyncVariableBool("m_HasBase");
2066
2067 //Construction init
2069
2070 if (ConfigIsExisting("hybridAttachments"))
2071 {
2073 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2074 }
2075 if (ConfigIsExisting("mountables"))
2076 {
2078 ConfigGetTextArray("mountables", m_Mountables);
2079 }
2080
2081 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2082 }
2083
2084 override void EEDelete(EntityAI parent)
2085 {
2086 super.EEDelete(parent);
2087
2088 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2090
2091 }
2092
2093 override string GetInvulnerabilityTypeString()
2094 {
2095 return "disableBaseDamage";
2096 }
2097
2098 override bool CanObstruct()
2099 {
2100 return true;
2101 }
2102
2103 override int GetHideIconMask()
2104 {
2105 return EInventoryIconVisibility.HIDE_VICINITY;
2106 }
2107
2108 // --- SYNCHRONIZATION
2110 {
2111 if (GetGame().IsServer())
2112 SetSynchDirty();
2113 }
2114
2115 override void OnVariablesSynchronized()
2116 {
2117 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2118 super.OnVariablesSynchronized();
2119
2120 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2121 }
2122
2123 protected void OnSynchronizedClient()
2124 {
2125 //update parts
2127
2128 //update action on part
2130
2131 //update visuals (client)
2132 UpdateVisuals();
2133 }
2134
2135 //parts synchronization
2137 {
2138 //part_id must starts from index = 1
2139 int offset;
2140 int mask;
2141
2142 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2143 {
2144 offset = part_id - 1;
2145 mask = 1 << offset;
2146
2148 }
2149 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2150 {
2151 offset = (part_id % 32);
2152 mask = 1 << offset;
2153
2155 }
2156 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2157 {
2158 offset = (part_id % 63);
2159 mask = 1 << offset;
2160
2162 }
2163 }
2164
2166 {
2167 //part_id must starts from index = 1
2168 int offset;
2169 int mask;
2170
2171 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2172 {
2173 offset = part_id - 1;
2174 mask = 1 << offset;
2175
2177 }
2178 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2179 {
2180 offset = (part_id % 32);
2181 mask = 1 << offset;
2182
2184 }
2185 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2186 {
2187 offset = (part_id % 63);
2188 mask = 1 << offset;
2189
2191 }
2192 }
2193
2195 {
2196 //part_id must starts from index = 1
2197 int offset;
2198 int mask;
2199
2200 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2201 {
2202 offset = part_id - 1;
2203 mask = 1 << offset;
2204
2205 if ((m_SyncParts01 & mask) > 0)
2206 return true;
2207 }
2208 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2209 {
2210 offset = (part_id % 32);
2211 mask = 1 << offset;
2212
2213 if ((m_SyncParts02 & mask) > 0)
2214 return true;
2215 }
2216 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2217 {
2218 offset = (part_id % 63);
2219 mask = 1 << offset;
2220
2221 if ((m_SyncParts03 & mask) > 0)
2222 return true;
2223 }
2224
2225 return false;
2226 }
2227
2228 protected void RegisterActionForSync(int part_id, int action_id)
2229 {
2232 }
2233
2234 protected void ResetActionSyncData()
2235 {
2236 //reset data
2237 m_InteractedPartId = -1;
2239 }
2240
2241 protected void SetActionFromSyncData()
2242 {
2243 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2244 {
2247
2248 switch (build_action_id)
2249 {
2253 }
2254 }
2255 }
2256 //------
2257
2259 {
2260 string key = part.m_PartName;
2261 bool is_base = part.IsBase();
2263 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2265 {
2266 if (!part.IsBuilt())
2267 {
2268 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2269 GetConstruction().AddToConstructedParts(key);
2270 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2271
2272 if (is_base)
2273 {
2275 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2276 }
2277 }
2278 }
2279 else
2280 {
2281 if (part.IsBuilt())
2282 {
2283 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2284 GetConstruction().RemoveFromConstructedParts(key);
2285 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2286
2287 if (is_base)
2288 {
2290 AddProxyPhysics(ANIMATION_DEPLOYED);
2291 }
2292 }
2293 }
2294
2295 //check slot lock for material attachments
2296 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2297 }
2298
2299 //set construction parts based on synchronized data
2301 {
2304
2305 for (int i = 0; i < construction_parts.Count(); ++i)
2306 {
2307 string key = construction_parts.GetKey(i);
2310 }
2311
2312 //regenerate navmesh
2313 UpdateNavmesh();
2314 }
2315
2317 {
2320
2321 for (int i = 0; i < construction_parts.Count(); ++i)
2322 {
2323 string key = construction_parts.GetKey(i);
2325
2326 if (value.GetId() == id)
2327 return value;
2328 }
2329
2330 return NULL;
2331 }
2332 //
2333
2334 //Base
2335 bool HasBase()
2336 {
2337 return m_HasBase;
2338 }
2339
2340 void SetBaseState(bool has_base)
2341 {
2343 }
2344
2345 override bool IsDeployable()
2346 {
2347 return true;
2348 }
2349
2350 bool IsOpened()
2351 {
2352 return false;
2353 }
2354
2355 //--- CONSTRUCTION KIT
2357 {
2361
2362 return construction_kit;
2363 }
2364
2366 {
2367 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2370 }
2371
2372 protected vector GetKitSpawnPosition()
2373 {
2374 return GetPosition();
2375 }
2376
2377 protected string GetConstructionKitType()
2378 {
2379 return "";
2380 }
2381
2383 {
2385 GetGame().ObjectDelete(construction_kit);
2386 }
2387
2388 //--- CONSTRUCTION
2389 void DestroyConstruction()
2390 {
2391 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2392 GetGame().ObjectDelete(this);
2393 }
2394
2395 // --- EVENTS
2396 override void OnStoreSave(ParamsWriteContext ctx)
2397 {
2398 super.OnStoreSave(ctx);
2399
2400 //sync parts 01
2401 ctx.Write(m_SyncParts01);
2402 ctx.Write(m_SyncParts02);
2403 ctx.Write(m_SyncParts03);
2404
2405 ctx.Write(m_HasBase);
2406 }
2407
2408 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2409 {
2410 if (!super.OnStoreLoad(ctx, version))
2411 return false;
2412
2413 //--- Base building data ---
2414 //Restore synced parts data
2415 if (!ctx.Read(m_SyncParts01))
2416 {
2417 m_SyncParts01 = 0; //set default
2418 return false;
2419 }
2420 if (!ctx.Read(m_SyncParts02))
2421 {
2422 m_SyncParts02 = 0; //set default
2423 return false;
2424 }
2425 if (!ctx.Read(m_SyncParts03))
2426 {
2427 m_SyncParts03 = 0; //set default
2428 return false;
2429 }
2430
2431 //has base
2432 if (!ctx.Read(m_HasBase))
2433 {
2434 m_HasBase = false;
2435 return false;
2436 }
2437 //---
2438
2439 return true;
2440 }
2441
2442 override void AfterStoreLoad()
2443 {
2444 super.AfterStoreLoad();
2445
2448 }
2449
2451 {
2452 //update server data
2454
2455 //set base state
2456 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2457 SetBaseState(construction_part.IsBuilt()) ;
2458
2459 //synchronize after load
2461 }
2462
2463 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2464 {
2466 return;
2467
2468 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2469
2470 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2471 return;
2472
2474 string part_name = zone;
2475 part_name.ToLower();
2476
2478 {
2480
2481 if (construction_part && construction.IsPartConstructed(part_name))
2482 {
2483 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2484 construction.DestroyConnectedParts(part_name);
2485 }
2486
2487 //barbed wire handling (hack-ish)
2488 if (part_name.Contains("barbed"))
2489 {
2490 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2491 if (barbed_wire)
2492 barbed_wire.SetMountedState(false);
2493 }
2494 }
2495 }
2496
2497 override void EEOnAfterLoad()
2498 {
2500 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2501
2502 super.EEOnAfterLoad();
2503 }
2504
2505 override void EEInit()
2506 {
2507 super.EEInit();
2508
2509 // init visuals and physics
2510 InitBaseState();
2511
2512 //debug
2513#ifdef DEVELOPER
2515#endif
2516 }
2517
2518 override void EEItemAttached(EntityAI item, string slot_name)
2519 {
2520 super.EEItemAttached(item, slot_name);
2521
2523 UpdateVisuals();
2525 }
2526
2527 override void EEItemDetached(EntityAI item, string slot_name)
2528 {
2529 super.EEItemDetached(item, slot_name);
2530
2531 UpdateVisuals();
2533 }
2534
2535 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2536 {
2538 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2539
2542 }
2543
2544 //ignore out of reach condition
2545 override bool IgnoreOutOfReachCondition()
2546 {
2547 return true;
2548 }
2549
2550 //CONSTRUCTION EVENTS
2551 //Build
2552 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2553 {
2555
2556 //check base state
2557 if (construtionPart.IsBase())
2558 {
2559 SetBaseState(true);
2560
2561 //spawn kit
2563 }
2564
2565 //register constructed parts for synchronization
2567
2568 //register action that was performed on part
2570
2571 //synchronize
2573
2574 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2575
2576 UpdateNavmesh();
2577
2578 //update visuals
2579 UpdateVisuals();
2580
2581 //reset action sync data
2582 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2583 }
2584
2585 void OnPartBuiltClient(string part_name, int action_id)
2586 {
2587 //play sound
2589 }
2590
2591 //Dismantle
2593 {
2594 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2596
2597 //register constructed parts for synchronization
2599
2600 //register action that was performed on part
2602
2603 //synchronize
2605
2606 // server part of sync, client will be synced from SetPartsFromSyncData
2608
2609 UpdateNavmesh();
2610
2611 //update visuals
2612 UpdateVisuals();
2613
2614 //reset action sync data
2615 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2616
2617 //check base state
2618 if (construtionPart.IsBase())
2619 {
2620 //Destroy construction
2621 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2622 }
2623 }
2624
2626 {
2627 //play sound
2629 }
2630
2631 //Destroy
2633 {
2634 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2636
2637 //register constructed parts for synchronization
2639
2640 //register action that was performed on part
2642
2643 //synchronize
2645
2646 // server part of sync, client will be synced from SetPartsFromSyncData
2648
2649 UpdateNavmesh();
2650
2651 //update visuals
2652 UpdateVisuals();
2653
2654 //reset action sync data
2655 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2656
2657 //check base state
2658 if (construtionPart.IsBase())
2659 {
2660 //Destroy construction
2661 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2662 }
2663 }
2664
2665 void OnPartDestroyedClient(string part_name, int action_id)
2666 {
2667 //play sound
2669 }
2670
2671 // --- UPDATE
2672 void InitBaseState()
2673 {
2674 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2675
2676 InitVisuals();
2677 UpdateNavmesh(); //regenerate navmesh
2678 GetConstruction().InitBaseState();
2679 }
2680
2681 void InitVisuals()
2682 {
2683 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2684 //check base
2685 if (!HasBase())
2686 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2687 else
2688 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2689
2690 GetConstruction().UpdateVisuals();
2691 }
2692
2693 void UpdateVisuals()
2694 {
2696
2698 foreach (string slotName : attachmentSlots)
2700
2701 //check base
2702 if (!HasBase())
2703 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2704 else
2705 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2706
2707 GetConstruction().UpdateVisuals();
2708 }
2709
2711 {
2712 string slotNameMounted = slot_name + "_Mounted";
2713 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2714
2715 if (attachment)
2716 {
2717 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2718 if (barbedWire && barbedWire.IsMounted())
2720 else
2722
2723 if (is_locked)
2724 {
2725 SetAnimationPhase(slotNameMounted, 0);
2726 SetAnimationPhase(slot_name, 1);
2727 }
2728 else
2729 {
2730 SetAnimationPhase(slotNameMounted, 1);
2731 SetAnimationPhase(slot_name, 0);
2732 }
2733 }
2734 else
2735 {
2736 SetAnimationPhase(slotNameMounted, 1);
2737 SetAnimationPhase(slot_name, 1);
2738
2740 }
2741 }
2742
2743 // avoid calling this function on frequent occasions, it's a massive performance hit
2744 void UpdatePhysics()
2745 {
2747 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2748
2751
2753 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2754
2755 foreach (string slotName : attachmentSlots)
2757
2758 //check base
2759 if (!HasBase())
2760 {
2762 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2763
2764 AddProxyPhysics(ANIMATION_DEPLOYED);
2765 }
2766 else
2767 {
2769 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2770
2771 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2772 }
2773
2774 GetConstruction().UpdatePhysics();
2775 UpdateNavmesh();
2776 }
2777
2779 {
2780 //checks for invalid appends; hotfix
2781 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2782 return;
2783 //----------------------------------
2784 string slot_name_mounted = slot_name + "_Mounted";
2785 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2786
2787 //remove proxy physics
2788 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2789 RemoveProxyPhysics(slot_name_mounted);
2790 RemoveProxyPhysics(slot_name);
2791
2792 if (attachment)
2793 {
2794 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2795 if (is_locked)
2796 {
2797 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2798 AddProxyPhysics(slot_name_mounted);
2799 }
2800 else
2801 {
2802 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2803 AddProxyPhysics(slot_name);
2804 }
2805 }
2806 }
2807
2808 protected void UpdateNavmesh()
2809 {
2810 SetAffectPathgraph(true, false);
2811 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2812 }
2813
2814 override bool CanUseConstruction()
2815 {
2816 return true;
2817 }
2818
2819 override bool CanUseConstructionBuild()
2820 {
2821 return true;
2822 }
2823
2825 {
2826 if (attachment)
2827 {
2829 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2830
2831 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2832 }
2833
2834 return false;
2835 }
2836
2837 protected bool IsAttachmentSlotLocked(string slot_name)
2838 {
2839 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2840 }
2841
2842 //--- ATTACHMENT SLOTS
2844 {
2845 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2846 if (GetGame().ConfigIsExisting(config_path))
2847 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2848 }
2849
2851 {
2852 return true;
2853 }
2854
2855 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2856 {
2857 return true;
2858 }
2859
2860 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2861 {
2862 return true;
2863 }
2864
2865 // --- INIT
2866 void ConstructionInit()
2867 {
2868 if (!m_Construction)
2869 m_Construction = new Construction(this);
2870
2871 GetConstruction().Init();
2872 }
2873
2875 {
2876 return m_Construction;
2877 }
2878
2879 //--- INVENTORY/ATTACHMENTS CONDITIONS
2880 //attachments
2882 {
2883 return super.CanReceiveAttachment(attachment, slotId);
2884 }
2885
2887 {
2888 int attachment_count = GetInventory().AttachmentCount();
2889 if (attachment_count > 0)
2890 {
2891 if (HasBase() && attachment_count == 1)
2892 return false;
2893
2894 return true;
2895 }
2896
2897 return false;
2898 }
2899
2900 override bool ShowZonesHealth()
2901 {
2902 return true;
2903 }
2904
2905 //this into/outo parent.Cargo
2906 override bool CanPutInCargo(EntityAI parent)
2907 {
2908 return false;
2909 }
2910
2911 override bool CanRemoveFromCargo(EntityAI parent)
2912 {
2913 return false;
2914 }
2915
2916 //hands
2917 override bool CanPutIntoHands(EntityAI parent)
2918 {
2919 return false;
2920 }
2921
2922 //--- ACTION CONDITIONS
2923 //direction
2924 override bool IsFacingPlayer(PlayerBase player, string selection)
2925 {
2926 return true;
2927 }
2928
2929 override bool IsPlayerInside(PlayerBase player, string selection)
2930 {
2931 return true;
2932 }
2933
2936 {
2937 return false;
2938 }
2939
2940 //camera direction check
2941 bool IsFacingCamera(string selection)
2942 {
2943 return true;
2944 }
2945
2946 //roof check
2948 {
2949 return false;
2950 }
2951
2952 //selection->player distance check
2953 bool HasProperDistance(string selection, PlayerBase player)
2954 {
2955 return true;
2956 }
2957
2958 //folding
2960 {
2961 if (HasBase() || GetInventory().AttachmentCount() > 0)
2962 return false;
2963
2964 return true;
2965 }
2966
2968 {
2971
2972 return item;
2973 }
2974
2975 //Damage triggers (barbed wire)
2976 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2977 {
2978 if (GetGame() && GetGame().IsServer())
2979 {
2980 //destroy area damage if some already exists
2982
2983 //create new area damage
2985 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2986
2987 vector min_max[2];
2988 if (MemoryPointExists(slot_name + "_min"))
2989 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2990 if (MemoryPointExists(slot_name + "_max"))
2991 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2992
2993 //get proper trigger extents (min<max)
2994 vector extents[2];
2995 GetConstruction().GetTriggerExtents(min_max, extents);
2996
2997 //get box center
2998 vector center;
2999 center = GetConstruction().GetBoxCenter(min_max);
3000 center = ModelToWorld(center);
3001
3002 //rotate center if needed
3005
3006 areaDamage.SetExtents(extents[0], extents[1]);
3007 areaDamage.SetAreaPosition(center);
3008 areaDamage.SetAreaOrientation(orientation);
3009 areaDamage.SetLoopInterval(1.0);
3010 areaDamage.SetDeferDuration(0.2);
3011 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3012 areaDamage.SetAmmoName("BarbedWireHit");
3013 areaDamage.Spawn();
3014
3016 }
3017 }
3018
3020 {
3021 if (angle_deg != 0)
3022 {
3023 //orientation
3025
3026 //center
3028 if (MemoryPointExists("rotate_axis"))
3029 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3032 center[0] = r_center_x;
3033 center[2] = r_center_z;
3034 }
3035 }
3036
3037 void DestroyAreaDamage(string slot_name)
3038 {
3039 if (GetGame() && GetGame().IsServer())
3040 {
3043 {
3044 if (areaDamage)
3045 areaDamage.Destroy();
3046
3048 }
3049 }
3050 }
3051
3052 override bool IsIgnoredByConstruction()
3053 {
3054 return true;
3055 }
3056
3057 //================================================================
3058 // SOUNDS
3059 //================================================================
3060 protected void SoundBuildStart(string part_name)
3061 {
3062 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3063 }
3064
3065 protected void SoundDismantleStart(string part_name)
3066 {
3067 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3068 }
3069
3070 protected void SoundDestroyStart(string part_name)
3071 {
3072 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3073 }
3074
3075 protected string GetBuildSoundByMaterial(string part_name)
3076 {
3078
3079 switch (material_type)
3080 {
3081 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3082 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3083 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3084 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3085 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3086 }
3087
3088 return "";
3089 }
3090
3091 protected string GetDismantleSoundByMaterial(string part_name)
3092 {
3094
3095 switch (material_type)
3096 {
3097 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3098 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3099 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3100 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3101 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3102 }
3103
3104 return "";
3105 }
3106
3107 //misc
3109 {
3110 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3111 {
3112 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3114 SetHealth(slot_name, "Health", item.GetHealth());
3115 }
3116 }
3117
3118 override int GetDamageSystemVersionChange()
3119 {
3120 return 111;
3121 }
3122
3123 override void SetActions()
3124 {
3125 super.SetActions();
3126
3128 //AddAction(ActionTakeHybridAttachment);
3129 //AddAction(ActionTakeHybridAttachmentToHands);
3132 }
3133
3134 //================================================================
3135 // DEBUG
3136 //================================================================
3137 protected void DebugCustomState()
3138 {
3139 }
3140
3143 {
3144 return null;
3145 }
3146
3147 override void OnDebugSpawn()
3148 {
3149 FullyBuild();
3150 }
3151
3152 void FullyBuild()
3153 {
3155 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3156
3157 Man p;
3158
3159#ifdef SERVER
3161 GetGame().GetWorld().GetPlayerList(players);
3162 if (players.Count())
3163 p = players[0];
3164#else
3165 p = GetGame().GetPlayer();
3166#endif
3167
3168 foreach (ConstructionPart part : parts)
3169 {
3170 bool excluded = false;
3171 string partName = part.GetPartName();
3172 if (excludes)
3173 {
3174 foreach (string exclude : excludes)
3175 {
3176 if (partName.Contains(exclude))
3177 {
3178 excluded = true;
3179 break;
3180 }
3181 }
3182 }
3183
3184 if (!excluded)
3186 }
3187
3188 GetConstruction().UpdateVisuals();
3189 }
3190}
3191
3192void bsbDebugPrint(string s)
3193{
3194#ifdef BSB_DEBUG
3195 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3196#else
3197 //Print("" + s); // comment/uncomment to hide/see debug logs
3198#endif
3199}
3200void bsbDebugSpam(string s)
3201{
3202#ifdef BSB_DEBUG_SPAM
3203 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3204#else
3205 //Print("" + s); // comment/uncomment to hide/see debug logs
3206#endif
3207}

◆ CheckMemoryPointVerticalDistance()

bool bsbDebugPrint::CheckMemoryPointVerticalDistance ( float max_dist,
string selection,
PlayerBase player )
protected

Definition at line 2012 of file BaseBuildingBase.c.

2014{
2015 const string ANIMATION_DEPLOYED = "Deployed";
2016
2017 float m_ConstructionKitHealth; //stored health value for used construction kit
2018
2020
2021 bool m_HasBase;
2022 //variables for synchronization of base building parts (2x31 is the current limit)
2023 int m_SyncParts01; //synchronization for already built parts (31 parts)
2024 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2025 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2026 int m_InteractedPartId; //construction part id that an action was performed on
2027 int m_PerformedActionId; //action id that was performed on a construction part
2028
2029 //Sounds
2030 //build
2031 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2032 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2033 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2034 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2035 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2036 //dismantle
2037 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2038 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2039 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2040 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2041 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2042
2043 protected EffectSound m_Sound;
2044
2048
2049 // Constructor
2050 void BaseBuildingBase()
2051 {
2053
2054 //synchronized variables
2055 RegisterNetSyncVariableInt("m_SyncParts01");
2056 RegisterNetSyncVariableInt("m_SyncParts02");
2057 RegisterNetSyncVariableInt("m_SyncParts03");
2058 RegisterNetSyncVariableInt("m_InteractedPartId");
2059 RegisterNetSyncVariableInt("m_PerformedActionId");
2060 RegisterNetSyncVariableBool("m_HasBase");
2061
2062 //Construction init
2064
2065 if (ConfigIsExisting("hybridAttachments"))
2066 {
2068 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2069 }
2070 if (ConfigIsExisting("mountables"))
2071 {
2073 ConfigGetTextArray("mountables", m_Mountables);
2074 }
2075
2076 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2077 }
2078
2079 override void EEDelete(EntityAI parent)
2080 {
2081 super.EEDelete(parent);
2082
2083 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2085
2086 }
2087
2088 override string GetInvulnerabilityTypeString()
2089 {
2090 return "disableBaseDamage";
2091 }
2092
2093 override bool CanObstruct()
2094 {
2095 return true;
2096 }
2097
2098 override int GetHideIconMask()
2099 {
2100 return EInventoryIconVisibility.HIDE_VICINITY;
2101 }
2102
2103 // --- SYNCHRONIZATION
2105 {
2106 if (GetGame().IsServer())
2107 SetSynchDirty();
2108 }
2109
2110 override void OnVariablesSynchronized()
2111 {
2112 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2113 super.OnVariablesSynchronized();
2114
2115 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2116 }
2117
2118 protected void OnSynchronizedClient()
2119 {
2120 //update parts
2122
2123 //update action on part
2125
2126 //update visuals (client)
2127 UpdateVisuals();
2128 }
2129
2130 //parts synchronization
2132 {
2133 //part_id must starts from index = 1
2134 int offset;
2135 int mask;
2136
2137 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2138 {
2139 offset = part_id - 1;
2140 mask = 1 << offset;
2141
2143 }
2144 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2145 {
2146 offset = (part_id % 32);
2147 mask = 1 << offset;
2148
2150 }
2151 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2152 {
2153 offset = (part_id % 63);
2154 mask = 1 << offset;
2155
2157 }
2158 }
2159
2161 {
2162 //part_id must starts from index = 1
2163 int offset;
2164 int mask;
2165
2166 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2167 {
2168 offset = part_id - 1;
2169 mask = 1 << offset;
2170
2172 }
2173 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2174 {
2175 offset = (part_id % 32);
2176 mask = 1 << offset;
2177
2179 }
2180 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2181 {
2182 offset = (part_id % 63);
2183 mask = 1 << offset;
2184
2186 }
2187 }
2188
2190 {
2191 //part_id must starts from index = 1
2192 int offset;
2193 int mask;
2194
2195 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2196 {
2197 offset = part_id - 1;
2198 mask = 1 << offset;
2199
2200 if ((m_SyncParts01 & mask) > 0)
2201 return true;
2202 }
2203 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2204 {
2205 offset = (part_id % 32);
2206 mask = 1 << offset;
2207
2208 if ((m_SyncParts02 & mask) > 0)
2209 return true;
2210 }
2211 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2212 {
2213 offset = (part_id % 63);
2214 mask = 1 << offset;
2215
2216 if ((m_SyncParts03 & mask) > 0)
2217 return true;
2218 }
2219
2220 return false;
2221 }
2222
2223 protected void RegisterActionForSync(int part_id, int action_id)
2224 {
2227 }
2228
2229 protected void ResetActionSyncData()
2230 {
2231 //reset data
2232 m_InteractedPartId = -1;
2234 }
2235
2236 protected void SetActionFromSyncData()
2237 {
2238 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2239 {
2242
2243 switch (build_action_id)
2244 {
2248 }
2249 }
2250 }
2251 //------
2252
2254 {
2255 string key = part.m_PartName;
2256 bool is_base = part.IsBase();
2258 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2260 {
2261 if (!part.IsBuilt())
2262 {
2263 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2264 GetConstruction().AddToConstructedParts(key);
2265 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2266
2267 if (is_base)
2268 {
2270 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2271 }
2272 }
2273 }
2274 else
2275 {
2276 if (part.IsBuilt())
2277 {
2278 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2279 GetConstruction().RemoveFromConstructedParts(key);
2280 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2281
2282 if (is_base)
2283 {
2285 AddProxyPhysics(ANIMATION_DEPLOYED);
2286 }
2287 }
2288 }
2289
2290 //check slot lock for material attachments
2291 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2292 }
2293
2294 //set construction parts based on synchronized data
2296 {
2299
2300 for (int i = 0; i < construction_parts.Count(); ++i)
2301 {
2302 string key = construction_parts.GetKey(i);
2305 }
2306
2307 //regenerate navmesh
2308 UpdateNavmesh();
2309 }
2310
2312 {
2315
2316 for (int i = 0; i < construction_parts.Count(); ++i)
2317 {
2318 string key = construction_parts.GetKey(i);
2320
2321 if (value.GetId() == id)
2322 return value;
2323 }
2324
2325 return NULL;
2326 }
2327 //
2328
2329 //Base
2330 bool HasBase()
2331 {
2332 return m_HasBase;
2333 }
2334
2335 void SetBaseState(bool has_base)
2336 {
2338 }
2339
2340 override bool IsDeployable()
2341 {
2342 return true;
2343 }
2344
2345 bool IsOpened()
2346 {
2347 return false;
2348 }
2349
2350 //--- CONSTRUCTION KIT
2352 {
2356
2357 return construction_kit;
2358 }
2359
2361 {
2362 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2365 }
2366
2367 protected vector GetKitSpawnPosition()
2368 {
2369 return GetPosition();
2370 }
2371
2372 protected string GetConstructionKitType()
2373 {
2374 return "";
2375 }
2376
2378 {
2380 GetGame().ObjectDelete(construction_kit);
2381 }
2382
2383 //--- CONSTRUCTION
2384 void DestroyConstruction()
2385 {
2386 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2387 GetGame().ObjectDelete(this);
2388 }
2389
2390 // --- EVENTS
2391 override void OnStoreSave(ParamsWriteContext ctx)
2392 {
2393 super.OnStoreSave(ctx);
2394
2395 //sync parts 01
2396 ctx.Write(m_SyncParts01);
2397 ctx.Write(m_SyncParts02);
2398 ctx.Write(m_SyncParts03);
2399
2400 ctx.Write(m_HasBase);
2401 }
2402
2403 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2404 {
2405 if (!super.OnStoreLoad(ctx, version))
2406 return false;
2407
2408 //--- Base building data ---
2409 //Restore synced parts data
2410 if (!ctx.Read(m_SyncParts01))
2411 {
2412 m_SyncParts01 = 0; //set default
2413 return false;
2414 }
2415 if (!ctx.Read(m_SyncParts02))
2416 {
2417 m_SyncParts02 = 0; //set default
2418 return false;
2419 }
2420 if (!ctx.Read(m_SyncParts03))
2421 {
2422 m_SyncParts03 = 0; //set default
2423 return false;
2424 }
2425
2426 //has base
2427 if (!ctx.Read(m_HasBase))
2428 {
2429 m_HasBase = false;
2430 return false;
2431 }
2432 //---
2433
2434 return true;
2435 }
2436
2437 override void AfterStoreLoad()
2438 {
2439 super.AfterStoreLoad();
2440
2443 }
2444
2446 {
2447 //update server data
2449
2450 //set base state
2451 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2452 SetBaseState(construction_part.IsBuilt()) ;
2453
2454 //synchronize after load
2456 }
2457
2458 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2459 {
2461 return;
2462
2463 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2464
2465 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2466 return;
2467
2469 string part_name = zone;
2470 part_name.ToLower();
2471
2473 {
2475
2476 if (construction_part && construction.IsPartConstructed(part_name))
2477 {
2478 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2479 construction.DestroyConnectedParts(part_name);
2480 }
2481
2482 //barbed wire handling (hack-ish)
2483 if (part_name.Contains("barbed"))
2484 {
2485 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2486 if (barbed_wire)
2487 barbed_wire.SetMountedState(false);
2488 }
2489 }
2490 }
2491
2492 override void EEOnAfterLoad()
2493 {
2495 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2496
2497 super.EEOnAfterLoad();
2498 }
2499
2500 override void EEInit()
2501 {
2502 super.EEInit();
2503
2504 // init visuals and physics
2505 InitBaseState();
2506
2507 //debug
2508#ifdef DEVELOPER
2510#endif
2511 }
2512
2513 override void EEItemAttached(EntityAI item, string slot_name)
2514 {
2515 super.EEItemAttached(item, slot_name);
2516
2518 UpdateVisuals();
2520 }
2521
2522 override void EEItemDetached(EntityAI item, string slot_name)
2523 {
2524 super.EEItemDetached(item, slot_name);
2525
2526 UpdateVisuals();
2528 }
2529
2530 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2531 {
2533 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2534
2537 }
2538
2539 //ignore out of reach condition
2540 override bool IgnoreOutOfReachCondition()
2541 {
2542 return true;
2543 }
2544
2545 //CONSTRUCTION EVENTS
2546 //Build
2547 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2548 {
2550
2551 //check base state
2552 if (construtionPart.IsBase())
2553 {
2554 SetBaseState(true);
2555
2556 //spawn kit
2558 }
2559
2560 //register constructed parts for synchronization
2562
2563 //register action that was performed on part
2565
2566 //synchronize
2568
2569 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2570
2571 UpdateNavmesh();
2572
2573 //update visuals
2574 UpdateVisuals();
2575
2576 //reset action sync data
2577 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2578 }
2579
2580 void OnPartBuiltClient(string part_name, int action_id)
2581 {
2582 //play sound
2584 }
2585
2586 //Dismantle
2588 {
2589 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2591
2592 //register constructed parts for synchronization
2594
2595 //register action that was performed on part
2597
2598 //synchronize
2600
2601 // server part of sync, client will be synced from SetPartsFromSyncData
2603
2604 UpdateNavmesh();
2605
2606 //update visuals
2607 UpdateVisuals();
2608
2609 //reset action sync data
2610 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2611
2612 //check base state
2613 if (construtionPart.IsBase())
2614 {
2615 //Destroy construction
2616 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2617 }
2618 }
2619
2621 {
2622 //play sound
2624 }
2625
2626 //Destroy
2628 {
2629 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2631
2632 //register constructed parts for synchronization
2634
2635 //register action that was performed on part
2637
2638 //synchronize
2640
2641 // server part of sync, client will be synced from SetPartsFromSyncData
2643
2644 UpdateNavmesh();
2645
2646 //update visuals
2647 UpdateVisuals();
2648
2649 //reset action sync data
2650 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2651
2652 //check base state
2653 if (construtionPart.IsBase())
2654 {
2655 //Destroy construction
2656 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2657 }
2658 }
2659
2660 void OnPartDestroyedClient(string part_name, int action_id)
2661 {
2662 //play sound
2664 }
2665
2666 // --- UPDATE
2667 void InitBaseState()
2668 {
2669 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2670
2671 InitVisuals();
2672 UpdateNavmesh(); //regenerate navmesh
2673 GetConstruction().InitBaseState();
2674 }
2675
2676 void InitVisuals()
2677 {
2678 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2679 //check base
2680 if (!HasBase())
2681 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2682 else
2683 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2684
2685 GetConstruction().UpdateVisuals();
2686 }
2687
2688 void UpdateVisuals()
2689 {
2691
2693 foreach (string slotName : attachmentSlots)
2695
2696 //check base
2697 if (!HasBase())
2698 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2699 else
2700 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2701
2702 GetConstruction().UpdateVisuals();
2703 }
2704
2706 {
2707 string slotNameMounted = slot_name + "_Mounted";
2708 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2709
2710 if (attachment)
2711 {
2712 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2713 if (barbedWire && barbedWire.IsMounted())
2715 else
2717
2718 if (is_locked)
2719 {
2720 SetAnimationPhase(slotNameMounted, 0);
2721 SetAnimationPhase(slot_name, 1);
2722 }
2723 else
2724 {
2725 SetAnimationPhase(slotNameMounted, 1);
2726 SetAnimationPhase(slot_name, 0);
2727 }
2728 }
2729 else
2730 {
2731 SetAnimationPhase(slotNameMounted, 1);
2732 SetAnimationPhase(slot_name, 1);
2733
2735 }
2736 }
2737
2738 // avoid calling this function on frequent occasions, it's a massive performance hit
2739 void UpdatePhysics()
2740 {
2742 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2743
2746
2748 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2749
2750 foreach (string slotName : attachmentSlots)
2752
2753 //check base
2754 if (!HasBase())
2755 {
2757 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2758
2759 AddProxyPhysics(ANIMATION_DEPLOYED);
2760 }
2761 else
2762 {
2764 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2765
2766 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2767 }
2768
2769 GetConstruction().UpdatePhysics();
2770 UpdateNavmesh();
2771 }
2772
2774 {
2775 //checks for invalid appends; hotfix
2776 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2777 return;
2778 //----------------------------------
2779 string slot_name_mounted = slot_name + "_Mounted";
2780 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2781
2782 //remove proxy physics
2783 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2784 RemoveProxyPhysics(slot_name_mounted);
2785 RemoveProxyPhysics(slot_name);
2786
2787 if (attachment)
2788 {
2789 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2790 if (is_locked)
2791 {
2792 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2793 AddProxyPhysics(slot_name_mounted);
2794 }
2795 else
2796 {
2797 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2798 AddProxyPhysics(slot_name);
2799 }
2800 }
2801 }
2802
2803 protected void UpdateNavmesh()
2804 {
2805 SetAffectPathgraph(true, false);
2806 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2807 }
2808
2809 override bool CanUseConstruction()
2810 {
2811 return true;
2812 }
2813
2814 override bool CanUseConstructionBuild()
2815 {
2816 return true;
2817 }
2818
2820 {
2821 if (attachment)
2822 {
2824 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2825
2826 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2827 }
2828
2829 return false;
2830 }
2831
2832 protected bool IsAttachmentSlotLocked(string slot_name)
2833 {
2834 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2835 }
2836
2837 //--- ATTACHMENT SLOTS
2839 {
2840 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2841 if (GetGame().ConfigIsExisting(config_path))
2842 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2843 }
2844
2846 {
2847 return true;
2848 }
2849
2850 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2851 {
2852 return true;
2853 }
2854
2855 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2856 {
2857 return true;
2858 }
2859
2860 // --- INIT
2861 void ConstructionInit()
2862 {
2863 if (!m_Construction)
2864 m_Construction = new Construction(this);
2865
2866 GetConstruction().Init();
2867 }
2868
2870 {
2871 return m_Construction;
2872 }
2873
2874 //--- INVENTORY/ATTACHMENTS CONDITIONS
2875 //attachments
2877 {
2878 return super.CanReceiveAttachment(attachment, slotId);
2879 }
2880
2882 {
2883 int attachment_count = GetInventory().AttachmentCount();
2884 if (attachment_count > 0)
2885 {
2886 if (HasBase() && attachment_count == 1)
2887 return false;
2888
2889 return true;
2890 }
2891
2892 return false;
2893 }
2894
2895 override bool ShowZonesHealth()
2896 {
2897 return true;
2898 }
2899
2900 //this into/outo parent.Cargo
2901 override bool CanPutInCargo(EntityAI parent)
2902 {
2903 return false;
2904 }
2905
2906 override bool CanRemoveFromCargo(EntityAI parent)
2907 {
2908 return false;
2909 }
2910
2911 //hands
2912 override bool CanPutIntoHands(EntityAI parent)
2913 {
2914 return false;
2915 }
2916
2917 //--- ACTION CONDITIONS
2918 //direction
2919 override bool IsFacingPlayer(PlayerBase player, string selection)
2920 {
2921 return true;
2922 }
2923
2924 override bool IsPlayerInside(PlayerBase player, string selection)
2925 {
2926 return true;
2927 }
2928
2931 {
2932 return false;
2933 }
2934
2935 //camera direction check
2936 bool IsFacingCamera(string selection)
2937 {
2938 return true;
2939 }
2940
2941 //roof check
2943 {
2944 return false;
2945 }
2946
2947 //selection->player distance check
2948 bool HasProperDistance(string selection, PlayerBase player)
2949 {
2950 return true;
2951 }
2952
2953 //folding
2955 {
2956 if (HasBase() || GetInventory().AttachmentCount() > 0)
2957 return false;
2958
2959 return true;
2960 }
2961
2963 {
2966
2967 return item;
2968 }
2969
2970 //Damage triggers (barbed wire)
2971 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2972 {
2973 if (GetGame() && GetGame().IsServer())
2974 {
2975 //destroy area damage if some already exists
2977
2978 //create new area damage
2980 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2981
2982 vector min_max[2];
2983 if (MemoryPointExists(slot_name + "_min"))
2984 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2985 if (MemoryPointExists(slot_name + "_max"))
2986 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2987
2988 //get proper trigger extents (min<max)
2989 vector extents[2];
2990 GetConstruction().GetTriggerExtents(min_max, extents);
2991
2992 //get box center
2993 vector center;
2994 center = GetConstruction().GetBoxCenter(min_max);
2995 center = ModelToWorld(center);
2996
2997 //rotate center if needed
3000
3001 areaDamage.SetExtents(extents[0], extents[1]);
3002 areaDamage.SetAreaPosition(center);
3003 areaDamage.SetAreaOrientation(orientation);
3004 areaDamage.SetLoopInterval(1.0);
3005 areaDamage.SetDeferDuration(0.2);
3006 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3007 areaDamage.SetAmmoName("BarbedWireHit");
3008 areaDamage.Spawn();
3009
3011 }
3012 }
3013
3015 {
3016 if (angle_deg != 0)
3017 {
3018 //orientation
3020
3021 //center
3023 if (MemoryPointExists("rotate_axis"))
3024 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3027 center[0] = r_center_x;
3028 center[2] = r_center_z;
3029 }
3030 }
3031
3032 void DestroyAreaDamage(string slot_name)
3033 {
3034 if (GetGame() && GetGame().IsServer())
3035 {
3038 {
3039 if (areaDamage)
3040 areaDamage.Destroy();
3041
3043 }
3044 }
3045 }
3046
3047 override bool IsIgnoredByConstruction()
3048 {
3049 return true;
3050 }
3051
3052 //================================================================
3053 // SOUNDS
3054 //================================================================
3055 protected void SoundBuildStart(string part_name)
3056 {
3057 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3058 }
3059
3060 protected void SoundDismantleStart(string part_name)
3061 {
3062 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3063 }
3064
3065 protected void SoundDestroyStart(string part_name)
3066 {
3067 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3068 }
3069
3070 protected string GetBuildSoundByMaterial(string part_name)
3071 {
3073
3074 switch (material_type)
3075 {
3076 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3077 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3078 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3079 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3080 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3081 }
3082
3083 return "";
3084 }
3085
3086 protected string GetDismantleSoundByMaterial(string part_name)
3087 {
3089
3090 switch (material_type)
3091 {
3092 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3093 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3094 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3095 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3096 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3097 }
3098
3099 return "";
3100 }
3101
3102 //misc
3104 {
3105 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3106 {
3107 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3109 SetHealth(slot_name, "Health", item.GetHealth());
3110 }
3111 }
3112
3113 override int GetDamageSystemVersionChange()
3114 {
3115 return 111;
3116 }
3117
3118 override void SetActions()
3119 {
3120 super.SetActions();
3121
3123 //AddAction(ActionTakeHybridAttachment);
3124 //AddAction(ActionTakeHybridAttachmentToHands);
3127 }
3128
3129 //================================================================
3130 // DEBUG
3131 //================================================================
3132 protected void DebugCustomState()
3133 {
3134 }
3135
3138 {
3139 return null;
3140 }
3141
3142 override void OnDebugSpawn()
3143 {
3144 FullyBuild();
3145 }
3146
3147 void FullyBuild()
3148 {
3150 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3151
3152 Man p;
3153
3154#ifdef SERVER
3156 GetGame().GetWorld().GetPlayerList(players);
3157 if (players.Count())
3158 p = players[0];
3159#else
3160 p = GetGame().GetPlayer();
3161#endif
3162
3163 foreach (ConstructionPart part : parts)
3164 {
3165 bool excluded = false;
3166 string partName = part.GetPartName();
3167 if (excludes)
3168 {
3169 foreach (string exclude : excludes)
3170 {
3171 if (partName.Contains(exclude))
3172 {
3173 excluded = true;
3174 break;
3175 }
3176 }
3177 }
3178
3179 if (!excluded)
3181 }
3182
3183 GetConstruction().UpdateVisuals();
3184 }
3185}
3186
3187void bsbDebugPrint(string s)
3188{
3189#ifdef BSB_DEBUG
3190 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3191#else
3192 //Print("" + s); // comment/uncomment to hide/see debug logs
3193#endif
3194}
3195void bsbDebugSpam(string s)
3196{
3197#ifdef BSB_DEBUG_SPAM
3198 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3199#else
3200 //Print("" + s); // comment/uncomment to hide/see debug logs
3201#endif
3202}

◆ CheckSlotVerticalDistance()

bool bsbDebugPrint::CheckSlotVerticalDistance ( int slot_id,
PlayerBase player )
protected

Definition at line 2007 of file BaseBuildingBase.c.

2009{
2010 const string ANIMATION_DEPLOYED = "Deployed";
2011
2012 float m_ConstructionKitHealth; //stored health value for used construction kit
2013
2015
2016 bool m_HasBase;
2017 //variables for synchronization of base building parts (2x31 is the current limit)
2018 int m_SyncParts01; //synchronization for already built parts (31 parts)
2019 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2020 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2021 int m_InteractedPartId; //construction part id that an action was performed on
2022 int m_PerformedActionId; //action id that was performed on a construction part
2023
2024 //Sounds
2025 //build
2026 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2027 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2028 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2029 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2030 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2031 //dismantle
2032 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2033 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2034 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2035 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2036 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2037
2038 protected EffectSound m_Sound;
2039
2043
2044 // Constructor
2045 void BaseBuildingBase()
2046 {
2048
2049 //synchronized variables
2050 RegisterNetSyncVariableInt("m_SyncParts01");
2051 RegisterNetSyncVariableInt("m_SyncParts02");
2052 RegisterNetSyncVariableInt("m_SyncParts03");
2053 RegisterNetSyncVariableInt("m_InteractedPartId");
2054 RegisterNetSyncVariableInt("m_PerformedActionId");
2055 RegisterNetSyncVariableBool("m_HasBase");
2056
2057 //Construction init
2059
2060 if (ConfigIsExisting("hybridAttachments"))
2061 {
2063 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2064 }
2065 if (ConfigIsExisting("mountables"))
2066 {
2068 ConfigGetTextArray("mountables", m_Mountables);
2069 }
2070
2071 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2072 }
2073
2074 override void EEDelete(EntityAI parent)
2075 {
2076 super.EEDelete(parent);
2077
2078 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2080
2081 }
2082
2083 override string GetInvulnerabilityTypeString()
2084 {
2085 return "disableBaseDamage";
2086 }
2087
2088 override bool CanObstruct()
2089 {
2090 return true;
2091 }
2092
2093 override int GetHideIconMask()
2094 {
2095 return EInventoryIconVisibility.HIDE_VICINITY;
2096 }
2097
2098 // --- SYNCHRONIZATION
2100 {
2101 if (GetGame().IsServer())
2102 SetSynchDirty();
2103 }
2104
2105 override void OnVariablesSynchronized()
2106 {
2107 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2108 super.OnVariablesSynchronized();
2109
2110 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2111 }
2112
2113 protected void OnSynchronizedClient()
2114 {
2115 //update parts
2117
2118 //update action on part
2120
2121 //update visuals (client)
2122 UpdateVisuals();
2123 }
2124
2125 //parts synchronization
2127 {
2128 //part_id must starts from index = 1
2129 int offset;
2130 int mask;
2131
2132 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2133 {
2134 offset = part_id - 1;
2135 mask = 1 << offset;
2136
2138 }
2139 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2140 {
2141 offset = (part_id % 32);
2142 mask = 1 << offset;
2143
2145 }
2146 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2147 {
2148 offset = (part_id % 63);
2149 mask = 1 << offset;
2150
2152 }
2153 }
2154
2156 {
2157 //part_id must starts from index = 1
2158 int offset;
2159 int mask;
2160
2161 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2162 {
2163 offset = part_id - 1;
2164 mask = 1 << offset;
2165
2167 }
2168 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2169 {
2170 offset = (part_id % 32);
2171 mask = 1 << offset;
2172
2174 }
2175 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2176 {
2177 offset = (part_id % 63);
2178 mask = 1 << offset;
2179
2181 }
2182 }
2183
2185 {
2186 //part_id must starts from index = 1
2187 int offset;
2188 int mask;
2189
2190 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2191 {
2192 offset = part_id - 1;
2193 mask = 1 << offset;
2194
2195 if ((m_SyncParts01 & mask) > 0)
2196 return true;
2197 }
2198 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2199 {
2200 offset = (part_id % 32);
2201 mask = 1 << offset;
2202
2203 if ((m_SyncParts02 & mask) > 0)
2204 return true;
2205 }
2206 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2207 {
2208 offset = (part_id % 63);
2209 mask = 1 << offset;
2210
2211 if ((m_SyncParts03 & mask) > 0)
2212 return true;
2213 }
2214
2215 return false;
2216 }
2217
2218 protected void RegisterActionForSync(int part_id, int action_id)
2219 {
2222 }
2223
2224 protected void ResetActionSyncData()
2225 {
2226 //reset data
2227 m_InteractedPartId = -1;
2229 }
2230
2231 protected void SetActionFromSyncData()
2232 {
2233 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2234 {
2237
2238 switch (build_action_id)
2239 {
2243 }
2244 }
2245 }
2246 //------
2247
2249 {
2250 string key = part.m_PartName;
2251 bool is_base = part.IsBase();
2253 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2255 {
2256 if (!part.IsBuilt())
2257 {
2258 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2259 GetConstruction().AddToConstructedParts(key);
2260 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2261
2262 if (is_base)
2263 {
2265 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2266 }
2267 }
2268 }
2269 else
2270 {
2271 if (part.IsBuilt())
2272 {
2273 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2274 GetConstruction().RemoveFromConstructedParts(key);
2275 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2276
2277 if (is_base)
2278 {
2280 AddProxyPhysics(ANIMATION_DEPLOYED);
2281 }
2282 }
2283 }
2284
2285 //check slot lock for material attachments
2286 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2287 }
2288
2289 //set construction parts based on synchronized data
2291 {
2294
2295 for (int i = 0; i < construction_parts.Count(); ++i)
2296 {
2297 string key = construction_parts.GetKey(i);
2300 }
2301
2302 //regenerate navmesh
2303 UpdateNavmesh();
2304 }
2305
2307 {
2310
2311 for (int i = 0; i < construction_parts.Count(); ++i)
2312 {
2313 string key = construction_parts.GetKey(i);
2315
2316 if (value.GetId() == id)
2317 return value;
2318 }
2319
2320 return NULL;
2321 }
2322 //
2323
2324 //Base
2325 bool HasBase()
2326 {
2327 return m_HasBase;
2328 }
2329
2330 void SetBaseState(bool has_base)
2331 {
2333 }
2334
2335 override bool IsDeployable()
2336 {
2337 return true;
2338 }
2339
2340 bool IsOpened()
2341 {
2342 return false;
2343 }
2344
2345 //--- CONSTRUCTION KIT
2347 {
2351
2352 return construction_kit;
2353 }
2354
2356 {
2357 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2360 }
2361
2362 protected vector GetKitSpawnPosition()
2363 {
2364 return GetPosition();
2365 }
2366
2367 protected string GetConstructionKitType()
2368 {
2369 return "";
2370 }
2371
2373 {
2375 GetGame().ObjectDelete(construction_kit);
2376 }
2377
2378 //--- CONSTRUCTION
2379 void DestroyConstruction()
2380 {
2381 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2382 GetGame().ObjectDelete(this);
2383 }
2384
2385 // --- EVENTS
2386 override void OnStoreSave(ParamsWriteContext ctx)
2387 {
2388 super.OnStoreSave(ctx);
2389
2390 //sync parts 01
2391 ctx.Write(m_SyncParts01);
2392 ctx.Write(m_SyncParts02);
2393 ctx.Write(m_SyncParts03);
2394
2395 ctx.Write(m_HasBase);
2396 }
2397
2398 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2399 {
2400 if (!super.OnStoreLoad(ctx, version))
2401 return false;
2402
2403 //--- Base building data ---
2404 //Restore synced parts data
2405 if (!ctx.Read(m_SyncParts01))
2406 {
2407 m_SyncParts01 = 0; //set default
2408 return false;
2409 }
2410 if (!ctx.Read(m_SyncParts02))
2411 {
2412 m_SyncParts02 = 0; //set default
2413 return false;
2414 }
2415 if (!ctx.Read(m_SyncParts03))
2416 {
2417 m_SyncParts03 = 0; //set default
2418 return false;
2419 }
2420
2421 //has base
2422 if (!ctx.Read(m_HasBase))
2423 {
2424 m_HasBase = false;
2425 return false;
2426 }
2427 //---
2428
2429 return true;
2430 }
2431
2432 override void AfterStoreLoad()
2433 {
2434 super.AfterStoreLoad();
2435
2438 }
2439
2441 {
2442 //update server data
2444
2445 //set base state
2446 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2447 SetBaseState(construction_part.IsBuilt()) ;
2448
2449 //synchronize after load
2451 }
2452
2453 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2454 {
2456 return;
2457
2458 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2459
2460 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2461 return;
2462
2464 string part_name = zone;
2465 part_name.ToLower();
2466
2468 {
2470
2471 if (construction_part && construction.IsPartConstructed(part_name))
2472 {
2473 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2474 construction.DestroyConnectedParts(part_name);
2475 }
2476
2477 //barbed wire handling (hack-ish)
2478 if (part_name.Contains("barbed"))
2479 {
2480 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2481 if (barbed_wire)
2482 barbed_wire.SetMountedState(false);
2483 }
2484 }
2485 }
2486
2487 override void EEOnAfterLoad()
2488 {
2490 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2491
2492 super.EEOnAfterLoad();
2493 }
2494
2495 override void EEInit()
2496 {
2497 super.EEInit();
2498
2499 // init visuals and physics
2500 InitBaseState();
2501
2502 //debug
2503#ifdef DEVELOPER
2505#endif
2506 }
2507
2508 override void EEItemAttached(EntityAI item, string slot_name)
2509 {
2510 super.EEItemAttached(item, slot_name);
2511
2513 UpdateVisuals();
2515 }
2516
2517 override void EEItemDetached(EntityAI item, string slot_name)
2518 {
2519 super.EEItemDetached(item, slot_name);
2520
2521 UpdateVisuals();
2523 }
2524
2525 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2526 {
2528 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2529
2532 }
2533
2534 //ignore out of reach condition
2535 override bool IgnoreOutOfReachCondition()
2536 {
2537 return true;
2538 }
2539
2540 //CONSTRUCTION EVENTS
2541 //Build
2542 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2543 {
2545
2546 //check base state
2547 if (construtionPart.IsBase())
2548 {
2549 SetBaseState(true);
2550
2551 //spawn kit
2553 }
2554
2555 //register constructed parts for synchronization
2557
2558 //register action that was performed on part
2560
2561 //synchronize
2563
2564 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2565
2566 UpdateNavmesh();
2567
2568 //update visuals
2569 UpdateVisuals();
2570
2571 //reset action sync data
2572 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2573 }
2574
2575 void OnPartBuiltClient(string part_name, int action_id)
2576 {
2577 //play sound
2579 }
2580
2581 //Dismantle
2583 {
2584 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2586
2587 //register constructed parts for synchronization
2589
2590 //register action that was performed on part
2592
2593 //synchronize
2595
2596 // server part of sync, client will be synced from SetPartsFromSyncData
2598
2599 UpdateNavmesh();
2600
2601 //update visuals
2602 UpdateVisuals();
2603
2604 //reset action sync data
2605 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2606
2607 //check base state
2608 if (construtionPart.IsBase())
2609 {
2610 //Destroy construction
2611 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2612 }
2613 }
2614
2616 {
2617 //play sound
2619 }
2620
2621 //Destroy
2623 {
2624 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2626
2627 //register constructed parts for synchronization
2629
2630 //register action that was performed on part
2632
2633 //synchronize
2635
2636 // server part of sync, client will be synced from SetPartsFromSyncData
2638
2639 UpdateNavmesh();
2640
2641 //update visuals
2642 UpdateVisuals();
2643
2644 //reset action sync data
2645 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2646
2647 //check base state
2648 if (construtionPart.IsBase())
2649 {
2650 //Destroy construction
2651 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2652 }
2653 }
2654
2655 void OnPartDestroyedClient(string part_name, int action_id)
2656 {
2657 //play sound
2659 }
2660
2661 // --- UPDATE
2662 void InitBaseState()
2663 {
2664 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2665
2666 InitVisuals();
2667 UpdateNavmesh(); //regenerate navmesh
2668 GetConstruction().InitBaseState();
2669 }
2670
2671 void InitVisuals()
2672 {
2673 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2674 //check base
2675 if (!HasBase())
2676 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2677 else
2678 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2679
2680 GetConstruction().UpdateVisuals();
2681 }
2682
2683 void UpdateVisuals()
2684 {
2686
2688 foreach (string slotName : attachmentSlots)
2690
2691 //check base
2692 if (!HasBase())
2693 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2694 else
2695 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2696
2697 GetConstruction().UpdateVisuals();
2698 }
2699
2701 {
2702 string slotNameMounted = slot_name + "_Mounted";
2703 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2704
2705 if (attachment)
2706 {
2707 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2708 if (barbedWire && barbedWire.IsMounted())
2710 else
2712
2713 if (is_locked)
2714 {
2715 SetAnimationPhase(slotNameMounted, 0);
2716 SetAnimationPhase(slot_name, 1);
2717 }
2718 else
2719 {
2720 SetAnimationPhase(slotNameMounted, 1);
2721 SetAnimationPhase(slot_name, 0);
2722 }
2723 }
2724 else
2725 {
2726 SetAnimationPhase(slotNameMounted, 1);
2727 SetAnimationPhase(slot_name, 1);
2728
2730 }
2731 }
2732
2733 // avoid calling this function on frequent occasions, it's a massive performance hit
2734 void UpdatePhysics()
2735 {
2737 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2738
2741
2743 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2744
2745 foreach (string slotName : attachmentSlots)
2747
2748 //check base
2749 if (!HasBase())
2750 {
2752 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2753
2754 AddProxyPhysics(ANIMATION_DEPLOYED);
2755 }
2756 else
2757 {
2759 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2760
2761 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2762 }
2763
2764 GetConstruction().UpdatePhysics();
2765 UpdateNavmesh();
2766 }
2767
2769 {
2770 //checks for invalid appends; hotfix
2771 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2772 return;
2773 //----------------------------------
2774 string slot_name_mounted = slot_name + "_Mounted";
2775 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2776
2777 //remove proxy physics
2778 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2779 RemoveProxyPhysics(slot_name_mounted);
2780 RemoveProxyPhysics(slot_name);
2781
2782 if (attachment)
2783 {
2784 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2785 if (is_locked)
2786 {
2787 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2788 AddProxyPhysics(slot_name_mounted);
2789 }
2790 else
2791 {
2792 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2793 AddProxyPhysics(slot_name);
2794 }
2795 }
2796 }
2797
2798 protected void UpdateNavmesh()
2799 {
2800 SetAffectPathgraph(true, false);
2801 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2802 }
2803
2804 override bool CanUseConstruction()
2805 {
2806 return true;
2807 }
2808
2809 override bool CanUseConstructionBuild()
2810 {
2811 return true;
2812 }
2813
2815 {
2816 if (attachment)
2817 {
2819 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2820
2821 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2822 }
2823
2824 return false;
2825 }
2826
2827 protected bool IsAttachmentSlotLocked(string slot_name)
2828 {
2829 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2830 }
2831
2832 //--- ATTACHMENT SLOTS
2834 {
2835 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2836 if (GetGame().ConfigIsExisting(config_path))
2837 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2838 }
2839
2841 {
2842 return true;
2843 }
2844
2845 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2846 {
2847 return true;
2848 }
2849
2850 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2851 {
2852 return true;
2853 }
2854
2855 // --- INIT
2856 void ConstructionInit()
2857 {
2858 if (!m_Construction)
2859 m_Construction = new Construction(this);
2860
2861 GetConstruction().Init();
2862 }
2863
2865 {
2866 return m_Construction;
2867 }
2868
2869 //--- INVENTORY/ATTACHMENTS CONDITIONS
2870 //attachments
2872 {
2873 return super.CanReceiveAttachment(attachment, slotId);
2874 }
2875
2877 {
2878 int attachment_count = GetInventory().AttachmentCount();
2879 if (attachment_count > 0)
2880 {
2881 if (HasBase() && attachment_count == 1)
2882 return false;
2883
2884 return true;
2885 }
2886
2887 return false;
2888 }
2889
2890 override bool ShowZonesHealth()
2891 {
2892 return true;
2893 }
2894
2895 //this into/outo parent.Cargo
2896 override bool CanPutInCargo(EntityAI parent)
2897 {
2898 return false;
2899 }
2900
2901 override bool CanRemoveFromCargo(EntityAI parent)
2902 {
2903 return false;
2904 }
2905
2906 //hands
2907 override bool CanPutIntoHands(EntityAI parent)
2908 {
2909 return false;
2910 }
2911
2912 //--- ACTION CONDITIONS
2913 //direction
2914 override bool IsFacingPlayer(PlayerBase player, string selection)
2915 {
2916 return true;
2917 }
2918
2919 override bool IsPlayerInside(PlayerBase player, string selection)
2920 {
2921 return true;
2922 }
2923
2926 {
2927 return false;
2928 }
2929
2930 //camera direction check
2931 bool IsFacingCamera(string selection)
2932 {
2933 return true;
2934 }
2935
2936 //roof check
2938 {
2939 return false;
2940 }
2941
2942 //selection->player distance check
2943 bool HasProperDistance(string selection, PlayerBase player)
2944 {
2945 return true;
2946 }
2947
2948 //folding
2950 {
2951 if (HasBase() || GetInventory().AttachmentCount() > 0)
2952 return false;
2953
2954 return true;
2955 }
2956
2958 {
2961
2962 return item;
2963 }
2964
2965 //Damage triggers (barbed wire)
2966 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2967 {
2968 if (GetGame() && GetGame().IsServer())
2969 {
2970 //destroy area damage if some already exists
2972
2973 //create new area damage
2975 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2976
2977 vector min_max[2];
2978 if (MemoryPointExists(slot_name + "_min"))
2979 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2980 if (MemoryPointExists(slot_name + "_max"))
2981 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2982
2983 //get proper trigger extents (min<max)
2984 vector extents[2];
2985 GetConstruction().GetTriggerExtents(min_max, extents);
2986
2987 //get box center
2988 vector center;
2989 center = GetConstruction().GetBoxCenter(min_max);
2990 center = ModelToWorld(center);
2991
2992 //rotate center if needed
2995
2996 areaDamage.SetExtents(extents[0], extents[1]);
2997 areaDamage.SetAreaPosition(center);
2998 areaDamage.SetAreaOrientation(orientation);
2999 areaDamage.SetLoopInterval(1.0);
3000 areaDamage.SetDeferDuration(0.2);
3001 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3002 areaDamage.SetAmmoName("BarbedWireHit");
3003 areaDamage.Spawn();
3004
3006 }
3007 }
3008
3010 {
3011 if (angle_deg != 0)
3012 {
3013 //orientation
3015
3016 //center
3018 if (MemoryPointExists("rotate_axis"))
3019 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3022 center[0] = r_center_x;
3023 center[2] = r_center_z;
3024 }
3025 }
3026
3027 void DestroyAreaDamage(string slot_name)
3028 {
3029 if (GetGame() && GetGame().IsServer())
3030 {
3033 {
3034 if (areaDamage)
3035 areaDamage.Destroy();
3036
3038 }
3039 }
3040 }
3041
3042 override bool IsIgnoredByConstruction()
3043 {
3044 return true;
3045 }
3046
3047 //================================================================
3048 // SOUNDS
3049 //================================================================
3050 protected void SoundBuildStart(string part_name)
3051 {
3052 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3053 }
3054
3055 protected void SoundDismantleStart(string part_name)
3056 {
3057 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3058 }
3059
3060 protected void SoundDestroyStart(string part_name)
3061 {
3062 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3063 }
3064
3065 protected string GetBuildSoundByMaterial(string part_name)
3066 {
3068
3069 switch (material_type)
3070 {
3071 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3072 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3073 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3074 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3075 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3076 }
3077
3078 return "";
3079 }
3080
3081 protected string GetDismantleSoundByMaterial(string part_name)
3082 {
3084
3085 switch (material_type)
3086 {
3087 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3088 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3089 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3090 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3091 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3092 }
3093
3094 return "";
3095 }
3096
3097 //misc
3099 {
3100 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3101 {
3102 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3104 SetHealth(slot_name, "Health", item.GetHealth());
3105 }
3106 }
3107
3108 override int GetDamageSystemVersionChange()
3109 {
3110 return 111;
3111 }
3112
3113 override void SetActions()
3114 {
3115 super.SetActions();
3116
3118 //AddAction(ActionTakeHybridAttachment);
3119 //AddAction(ActionTakeHybridAttachmentToHands);
3122 }
3123
3124 //================================================================
3125 // DEBUG
3126 //================================================================
3127 protected void DebugCustomState()
3128 {
3129 }
3130
3133 {
3134 return null;
3135 }
3136
3137 override void OnDebugSpawn()
3138 {
3139 FullyBuild();
3140 }
3141
3142 void FullyBuild()
3143 {
3145 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3146
3147 Man p;
3148
3149#ifdef SERVER
3151 GetGame().GetWorld().GetPlayerList(players);
3152 if (players.Count())
3153 p = players[0];
3154#else
3155 p = GetGame().GetPlayer();
3156#endif
3157
3158 foreach (ConstructionPart part : parts)
3159 {
3160 bool excluded = false;
3161 string partName = part.GetPartName();
3162 if (excludes)
3163 {
3164 foreach (string exclude : excludes)
3165 {
3166 if (partName.Contains(exclude))
3167 {
3168 excluded = true;
3169 break;
3170 }
3171 }
3172 }
3173
3174 if (!excluded)
3176 }
3177
3178 GetConstruction().UpdateVisuals();
3179 }
3180}
3181
3182void bsbDebugPrint(string s)
3183{
3184#ifdef BSB_DEBUG
3185 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3186#else
3187 //Print("" + s); // comment/uncomment to hide/see debug logs
3188#endif
3189}
3190void bsbDebugSpam(string s)
3191{
3192#ifdef BSB_DEBUG_SPAM
3193 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3194#else
3195 //Print("" + s); // comment/uncomment to hide/see debug logs
3196#endif
3197}

◆ ConstructionInit()

void bsbDebugPrint::ConstructionInit ( )
protected

Definition at line 2023 of file BaseBuildingBase.c.

2025{
2026 const string ANIMATION_DEPLOYED = "Deployed";
2027
2028 float m_ConstructionKitHealth; //stored health value for used construction kit
2029
2031
2032 bool m_HasBase;
2033 //variables for synchronization of base building parts (2x31 is the current limit)
2034 int m_SyncParts01; //synchronization for already built parts (31 parts)
2035 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2036 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2037 int m_InteractedPartId; //construction part id that an action was performed on
2038 int m_PerformedActionId; //action id that was performed on a construction part
2039
2040 //Sounds
2041 //build
2042 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2043 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2044 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2045 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2046 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2047 //dismantle
2048 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2049 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2050 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2051 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2052 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2053
2054 protected EffectSound m_Sound;
2055
2059
2060 // Constructor
2061 void BaseBuildingBase()
2062 {
2064
2065 //synchronized variables
2066 RegisterNetSyncVariableInt("m_SyncParts01");
2067 RegisterNetSyncVariableInt("m_SyncParts02");
2068 RegisterNetSyncVariableInt("m_SyncParts03");
2069 RegisterNetSyncVariableInt("m_InteractedPartId");
2070 RegisterNetSyncVariableInt("m_PerformedActionId");
2071 RegisterNetSyncVariableBool("m_HasBase");
2072
2073 //Construction init
2075
2076 if (ConfigIsExisting("hybridAttachments"))
2077 {
2079 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2080 }
2081 if (ConfigIsExisting("mountables"))
2082 {
2084 ConfigGetTextArray("mountables", m_Mountables);
2085 }
2086
2087 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2088 }
2089
2090 override void EEDelete(EntityAI parent)
2091 {
2092 super.EEDelete(parent);
2093
2094 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2096
2097 }
2098
2099 override string GetInvulnerabilityTypeString()
2100 {
2101 return "disableBaseDamage";
2102 }
2103
2104 override bool CanObstruct()
2105 {
2106 return true;
2107 }
2108
2109 override int GetHideIconMask()
2110 {
2111 return EInventoryIconVisibility.HIDE_VICINITY;
2112 }
2113
2114 // --- SYNCHRONIZATION
2116 {
2117 if (GetGame().IsServer())
2118 SetSynchDirty();
2119 }
2120
2121 override void OnVariablesSynchronized()
2122 {
2123 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2124 super.OnVariablesSynchronized();
2125
2126 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2127 }
2128
2129 protected void OnSynchronizedClient()
2130 {
2131 //update parts
2133
2134 //update action on part
2136
2137 //update visuals (client)
2138 UpdateVisuals();
2139 }
2140
2141 //parts synchronization
2143 {
2144 //part_id must starts from index = 1
2145 int offset;
2146 int mask;
2147
2148 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2149 {
2150 offset = part_id - 1;
2151 mask = 1 << offset;
2152
2154 }
2155 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2156 {
2157 offset = (part_id % 32);
2158 mask = 1 << offset;
2159
2161 }
2162 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2163 {
2164 offset = (part_id % 63);
2165 mask = 1 << offset;
2166
2168 }
2169 }
2170
2172 {
2173 //part_id must starts from index = 1
2174 int offset;
2175 int mask;
2176
2177 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2178 {
2179 offset = part_id - 1;
2180 mask = 1 << offset;
2181
2183 }
2184 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2185 {
2186 offset = (part_id % 32);
2187 mask = 1 << offset;
2188
2190 }
2191 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2192 {
2193 offset = (part_id % 63);
2194 mask = 1 << offset;
2195
2197 }
2198 }
2199
2201 {
2202 //part_id must starts from index = 1
2203 int offset;
2204 int mask;
2205
2206 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2207 {
2208 offset = part_id - 1;
2209 mask = 1 << offset;
2210
2211 if ((m_SyncParts01 & mask) > 0)
2212 return true;
2213 }
2214 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2215 {
2216 offset = (part_id % 32);
2217 mask = 1 << offset;
2218
2219 if ((m_SyncParts02 & mask) > 0)
2220 return true;
2221 }
2222 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2223 {
2224 offset = (part_id % 63);
2225 mask = 1 << offset;
2226
2227 if ((m_SyncParts03 & mask) > 0)
2228 return true;
2229 }
2230
2231 return false;
2232 }
2233
2234 protected void RegisterActionForSync(int part_id, int action_id)
2235 {
2238 }
2239
2240 protected void ResetActionSyncData()
2241 {
2242 //reset data
2243 m_InteractedPartId = -1;
2245 }
2246
2247 protected void SetActionFromSyncData()
2248 {
2249 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2250 {
2253
2254 switch (build_action_id)
2255 {
2259 }
2260 }
2261 }
2262 //------
2263
2265 {
2266 string key = part.m_PartName;
2267 bool is_base = part.IsBase();
2269 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2271 {
2272 if (!part.IsBuilt())
2273 {
2274 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2275 GetConstruction().AddToConstructedParts(key);
2276 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2277
2278 if (is_base)
2279 {
2281 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2282 }
2283 }
2284 }
2285 else
2286 {
2287 if (part.IsBuilt())
2288 {
2289 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2290 GetConstruction().RemoveFromConstructedParts(key);
2291 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2292
2293 if (is_base)
2294 {
2296 AddProxyPhysics(ANIMATION_DEPLOYED);
2297 }
2298 }
2299 }
2300
2301 //check slot lock for material attachments
2302 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2303 }
2304
2305 //set construction parts based on synchronized data
2307 {
2310
2311 for (int i = 0; i < construction_parts.Count(); ++i)
2312 {
2313 string key = construction_parts.GetKey(i);
2316 }
2317
2318 //regenerate navmesh
2319 UpdateNavmesh();
2320 }
2321
2323 {
2326
2327 for (int i = 0; i < construction_parts.Count(); ++i)
2328 {
2329 string key = construction_parts.GetKey(i);
2331
2332 if (value.GetId() == id)
2333 return value;
2334 }
2335
2336 return NULL;
2337 }
2338 //
2339
2340 //Base
2341 bool HasBase()
2342 {
2343 return m_HasBase;
2344 }
2345
2346 void SetBaseState(bool has_base)
2347 {
2349 }
2350
2351 override bool IsDeployable()
2352 {
2353 return true;
2354 }
2355
2356 bool IsOpened()
2357 {
2358 return false;
2359 }
2360
2361 //--- CONSTRUCTION KIT
2363 {
2367
2368 return construction_kit;
2369 }
2370
2372 {
2373 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2376 }
2377
2378 protected vector GetKitSpawnPosition()
2379 {
2380 return GetPosition();
2381 }
2382
2383 protected string GetConstructionKitType()
2384 {
2385 return "";
2386 }
2387
2389 {
2391 GetGame().ObjectDelete(construction_kit);
2392 }
2393
2394 //--- CONSTRUCTION
2395 void DestroyConstruction()
2396 {
2397 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2398 GetGame().ObjectDelete(this);
2399 }
2400
2401 // --- EVENTS
2402 override void OnStoreSave(ParamsWriteContext ctx)
2403 {
2404 super.OnStoreSave(ctx);
2405
2406 //sync parts 01
2407 ctx.Write(m_SyncParts01);
2408 ctx.Write(m_SyncParts02);
2409 ctx.Write(m_SyncParts03);
2410
2411 ctx.Write(m_HasBase);
2412 }
2413
2414 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2415 {
2416 if (!super.OnStoreLoad(ctx, version))
2417 return false;
2418
2419 //--- Base building data ---
2420 //Restore synced parts data
2421 if (!ctx.Read(m_SyncParts01))
2422 {
2423 m_SyncParts01 = 0; //set default
2424 return false;
2425 }
2426 if (!ctx.Read(m_SyncParts02))
2427 {
2428 m_SyncParts02 = 0; //set default
2429 return false;
2430 }
2431 if (!ctx.Read(m_SyncParts03))
2432 {
2433 m_SyncParts03 = 0; //set default
2434 return false;
2435 }
2436
2437 //has base
2438 if (!ctx.Read(m_HasBase))
2439 {
2440 m_HasBase = false;
2441 return false;
2442 }
2443 //---
2444
2445 return true;
2446 }
2447
2448 override void AfterStoreLoad()
2449 {
2450 super.AfterStoreLoad();
2451
2454 }
2455
2457 {
2458 //update server data
2460
2461 //set base state
2462 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2463 SetBaseState(construction_part.IsBuilt()) ;
2464
2465 //synchronize after load
2467 }
2468
2469 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2470 {
2472 return;
2473
2474 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2475
2476 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2477 return;
2478
2480 string part_name = zone;
2481 part_name.ToLower();
2482
2484 {
2486
2487 if (construction_part && construction.IsPartConstructed(part_name))
2488 {
2489 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2490 construction.DestroyConnectedParts(part_name);
2491 }
2492
2493 //barbed wire handling (hack-ish)
2494 if (part_name.Contains("barbed"))
2495 {
2496 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2497 if (barbed_wire)
2498 barbed_wire.SetMountedState(false);
2499 }
2500 }
2501 }
2502
2503 override void EEOnAfterLoad()
2504 {
2506 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2507
2508 super.EEOnAfterLoad();
2509 }
2510
2511 override void EEInit()
2512 {
2513 super.EEInit();
2514
2515 // init visuals and physics
2516 InitBaseState();
2517
2518 //debug
2519#ifdef DEVELOPER
2521#endif
2522 }
2523
2524 override void EEItemAttached(EntityAI item, string slot_name)
2525 {
2526 super.EEItemAttached(item, slot_name);
2527
2529 UpdateVisuals();
2531 }
2532
2533 override void EEItemDetached(EntityAI item, string slot_name)
2534 {
2535 super.EEItemDetached(item, slot_name);
2536
2537 UpdateVisuals();
2539 }
2540
2541 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2542 {
2544 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2545
2548 }
2549
2550 //ignore out of reach condition
2551 override bool IgnoreOutOfReachCondition()
2552 {
2553 return true;
2554 }
2555
2556 //CONSTRUCTION EVENTS
2557 //Build
2558 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2559 {
2561
2562 //check base state
2563 if (construtionPart.IsBase())
2564 {
2565 SetBaseState(true);
2566
2567 //spawn kit
2569 }
2570
2571 //register constructed parts for synchronization
2573
2574 //register action that was performed on part
2576
2577 //synchronize
2579
2580 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2581
2582 UpdateNavmesh();
2583
2584 //update visuals
2585 UpdateVisuals();
2586
2587 //reset action sync data
2588 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2589 }
2590
2591 void OnPartBuiltClient(string part_name, int action_id)
2592 {
2593 //play sound
2595 }
2596
2597 //Dismantle
2599 {
2600 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2602
2603 //register constructed parts for synchronization
2605
2606 //register action that was performed on part
2608
2609 //synchronize
2611
2612 // server part of sync, client will be synced from SetPartsFromSyncData
2614
2615 UpdateNavmesh();
2616
2617 //update visuals
2618 UpdateVisuals();
2619
2620 //reset action sync data
2621 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2622
2623 //check base state
2624 if (construtionPart.IsBase())
2625 {
2626 //Destroy construction
2627 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2628 }
2629 }
2630
2632 {
2633 //play sound
2635 }
2636
2637 //Destroy
2639 {
2640 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2642
2643 //register constructed parts for synchronization
2645
2646 //register action that was performed on part
2648
2649 //synchronize
2651
2652 // server part of sync, client will be synced from SetPartsFromSyncData
2654
2655 UpdateNavmesh();
2656
2657 //update visuals
2658 UpdateVisuals();
2659
2660 //reset action sync data
2661 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2662
2663 //check base state
2664 if (construtionPart.IsBase())
2665 {
2666 //Destroy construction
2667 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2668 }
2669 }
2670
2671 void OnPartDestroyedClient(string part_name, int action_id)
2672 {
2673 //play sound
2675 }
2676
2677 // --- UPDATE
2678 void InitBaseState()
2679 {
2680 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2681
2682 InitVisuals();
2683 UpdateNavmesh(); //regenerate navmesh
2684 GetConstruction().InitBaseState();
2685 }
2686
2687 void InitVisuals()
2688 {
2689 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2690 //check base
2691 if (!HasBase())
2692 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2693 else
2694 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2695
2696 GetConstruction().UpdateVisuals();
2697 }
2698
2699 void UpdateVisuals()
2700 {
2702
2704 foreach (string slotName : attachmentSlots)
2706
2707 //check base
2708 if (!HasBase())
2709 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2710 else
2711 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2712
2713 GetConstruction().UpdateVisuals();
2714 }
2715
2717 {
2718 string slotNameMounted = slot_name + "_Mounted";
2719 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2720
2721 if (attachment)
2722 {
2723 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2724 if (barbedWire && barbedWire.IsMounted())
2726 else
2728
2729 if (is_locked)
2730 {
2731 SetAnimationPhase(slotNameMounted, 0);
2732 SetAnimationPhase(slot_name, 1);
2733 }
2734 else
2735 {
2736 SetAnimationPhase(slotNameMounted, 1);
2737 SetAnimationPhase(slot_name, 0);
2738 }
2739 }
2740 else
2741 {
2742 SetAnimationPhase(slotNameMounted, 1);
2743 SetAnimationPhase(slot_name, 1);
2744
2746 }
2747 }
2748
2749 // avoid calling this function on frequent occasions, it's a massive performance hit
2750 void UpdatePhysics()
2751 {
2753 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2754
2757
2759 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2760
2761 foreach (string slotName : attachmentSlots)
2763
2764 //check base
2765 if (!HasBase())
2766 {
2768 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2769
2770 AddProxyPhysics(ANIMATION_DEPLOYED);
2771 }
2772 else
2773 {
2775 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2776
2777 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2778 }
2779
2780 GetConstruction().UpdatePhysics();
2781 UpdateNavmesh();
2782 }
2783
2785 {
2786 //checks for invalid appends; hotfix
2787 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2788 return;
2789 //----------------------------------
2790 string slot_name_mounted = slot_name + "_Mounted";
2791 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2792
2793 //remove proxy physics
2794 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2795 RemoveProxyPhysics(slot_name_mounted);
2796 RemoveProxyPhysics(slot_name);
2797
2798 if (attachment)
2799 {
2800 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2801 if (is_locked)
2802 {
2803 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2804 AddProxyPhysics(slot_name_mounted);
2805 }
2806 else
2807 {
2808 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2809 AddProxyPhysics(slot_name);
2810 }
2811 }
2812 }
2813
2814 protected void UpdateNavmesh()
2815 {
2816 SetAffectPathgraph(true, false);
2817 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2818 }
2819
2820 override bool CanUseConstruction()
2821 {
2822 return true;
2823 }
2824
2825 override bool CanUseConstructionBuild()
2826 {
2827 return true;
2828 }
2829
2831 {
2832 if (attachment)
2833 {
2835 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2836
2837 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2838 }
2839
2840 return false;
2841 }
2842
2843 protected bool IsAttachmentSlotLocked(string slot_name)
2844 {
2845 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2846 }
2847
2848 //--- ATTACHMENT SLOTS
2850 {
2851 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2852 if (GetGame().ConfigIsExisting(config_path))
2853 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2854 }
2855
2857 {
2858 return true;
2859 }
2860
2861 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2862 {
2863 return true;
2864 }
2865
2866 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2867 {
2868 return true;
2869 }
2870
2871 // --- INIT
2872 void ConstructionInit()
2873 {
2874 if (!m_Construction)
2875 m_Construction = new Construction(this);
2876
2877 GetConstruction().Init();
2878 }
2879
2881 {
2882 return m_Construction;
2883 }
2884
2885 //--- INVENTORY/ATTACHMENTS CONDITIONS
2886 //attachments
2888 {
2889 return super.CanReceiveAttachment(attachment, slotId);
2890 }
2891
2893 {
2894 int attachment_count = GetInventory().AttachmentCount();
2895 if (attachment_count > 0)
2896 {
2897 if (HasBase() && attachment_count == 1)
2898 return false;
2899
2900 return true;
2901 }
2902
2903 return false;
2904 }
2905
2906 override bool ShowZonesHealth()
2907 {
2908 return true;
2909 }
2910
2911 //this into/outo parent.Cargo
2912 override bool CanPutInCargo(EntityAI parent)
2913 {
2914 return false;
2915 }
2916
2917 override bool CanRemoveFromCargo(EntityAI parent)
2918 {
2919 return false;
2920 }
2921
2922 //hands
2923 override bool CanPutIntoHands(EntityAI parent)
2924 {
2925 return false;
2926 }
2927
2928 //--- ACTION CONDITIONS
2929 //direction
2930 override bool IsFacingPlayer(PlayerBase player, string selection)
2931 {
2932 return true;
2933 }
2934
2935 override bool IsPlayerInside(PlayerBase player, string selection)
2936 {
2937 return true;
2938 }
2939
2942 {
2943 return false;
2944 }
2945
2946 //camera direction check
2947 bool IsFacingCamera(string selection)
2948 {
2949 return true;
2950 }
2951
2952 //roof check
2954 {
2955 return false;
2956 }
2957
2958 //selection->player distance check
2959 bool HasProperDistance(string selection, PlayerBase player)
2960 {
2961 return true;
2962 }
2963
2964 //folding
2966 {
2967 if (HasBase() || GetInventory().AttachmentCount() > 0)
2968 return false;
2969
2970 return true;
2971 }
2972
2974 {
2977
2978 return item;
2979 }
2980
2981 //Damage triggers (barbed wire)
2982 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2983 {
2984 if (GetGame() && GetGame().IsServer())
2985 {
2986 //destroy area damage if some already exists
2988
2989 //create new area damage
2991 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2992
2993 vector min_max[2];
2994 if (MemoryPointExists(slot_name + "_min"))
2995 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2996 if (MemoryPointExists(slot_name + "_max"))
2997 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2998
2999 //get proper trigger extents (min<max)
3000 vector extents[2];
3001 GetConstruction().GetTriggerExtents(min_max, extents);
3002
3003 //get box center
3004 vector center;
3005 center = GetConstruction().GetBoxCenter(min_max);
3006 center = ModelToWorld(center);
3007
3008 //rotate center if needed
3011
3012 areaDamage.SetExtents(extents[0], extents[1]);
3013 areaDamage.SetAreaPosition(center);
3014 areaDamage.SetAreaOrientation(orientation);
3015 areaDamage.SetLoopInterval(1.0);
3016 areaDamage.SetDeferDuration(0.2);
3017 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3018 areaDamage.SetAmmoName("BarbedWireHit");
3019 areaDamage.Spawn();
3020
3022 }
3023 }
3024
3026 {
3027 if (angle_deg != 0)
3028 {
3029 //orientation
3031
3032 //center
3034 if (MemoryPointExists("rotate_axis"))
3035 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3038 center[0] = r_center_x;
3039 center[2] = r_center_z;
3040 }
3041 }
3042
3043 void DestroyAreaDamage(string slot_name)
3044 {
3045 if (GetGame() && GetGame().IsServer())
3046 {
3049 {
3050 if (areaDamage)
3051 areaDamage.Destroy();
3052
3054 }
3055 }
3056 }
3057
3058 override bool IsIgnoredByConstruction()
3059 {
3060 return true;
3061 }
3062
3063 //================================================================
3064 // SOUNDS
3065 //================================================================
3066 protected void SoundBuildStart(string part_name)
3067 {
3068 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3069 }
3070
3071 protected void SoundDismantleStart(string part_name)
3072 {
3073 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3074 }
3075
3076 protected void SoundDestroyStart(string part_name)
3077 {
3078 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3079 }
3080
3081 protected string GetBuildSoundByMaterial(string part_name)
3082 {
3084
3085 switch (material_type)
3086 {
3087 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3088 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3089 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3090 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3091 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3092 }
3093
3094 return "";
3095 }
3096
3097 protected string GetDismantleSoundByMaterial(string part_name)
3098 {
3100
3101 switch (material_type)
3102 {
3103 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3104 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3105 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3106 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3107 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3108 }
3109
3110 return "";
3111 }
3112
3113 //misc
3115 {
3116 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3117 {
3118 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3120 SetHealth(slot_name, "Health", item.GetHealth());
3121 }
3122 }
3123
3124 override int GetDamageSystemVersionChange()
3125 {
3126 return 111;
3127 }
3128
3129 override void SetActions()
3130 {
3131 super.SetActions();
3132
3134 //AddAction(ActionTakeHybridAttachment);
3135 //AddAction(ActionTakeHybridAttachmentToHands);
3138 }
3139
3140 //================================================================
3141 // DEBUG
3142 //================================================================
3143 protected void DebugCustomState()
3144 {
3145 }
3146
3149 {
3150 return null;
3151 }
3152
3153 override void OnDebugSpawn()
3154 {
3155 FullyBuild();
3156 }
3157
3158 void FullyBuild()
3159 {
3161 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3162
3163 Man p;
3164
3165#ifdef SERVER
3167 GetGame().GetWorld().GetPlayerList(players);
3168 if (players.Count())
3169 p = players[0];
3170#else
3171 p = GetGame().GetPlayer();
3172#endif
3173
3174 foreach (ConstructionPart part : parts)
3175 {
3176 bool excluded = false;
3177 string partName = part.GetPartName();
3178 if (excludes)
3179 {
3180 foreach (string exclude : excludes)
3181 {
3182 if (partName.Contains(exclude))
3183 {
3184 excluded = true;
3185 break;
3186 }
3187 }
3188 }
3189
3190 if (!excluded)
3192 }
3193
3194 GetConstruction().UpdateVisuals();
3195 }
3196}
3197
3198void bsbDebugPrint(string s)
3199{
3200#ifdef BSB_DEBUG
3201 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3202#else
3203 //Print("" + s); // comment/uncomment to hide/see debug logs
3204#endif
3205}
3206void bsbDebugSpam(string s)
3207{
3208#ifdef BSB_DEBUG_SPAM
3209 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3210#else
3211 //Print("" + s); // comment/uncomment to hide/see debug logs
3212#endif
3213}

Referenced by ItemBase::BaseBuildingBase().

◆ CreateAreaDamage()

void bsbDebugPrint::CreateAreaDamage ( string slot_name,
float rotation_angle = 0 )
protected

Definition at line 2133 of file BaseBuildingBase.c.

2135{
2136 const string ANIMATION_DEPLOYED = "Deployed";
2137
2138 float m_ConstructionKitHealth; //stored health value for used construction kit
2139
2141
2142 bool m_HasBase;
2143 //variables for synchronization of base building parts (2x31 is the current limit)
2144 int m_SyncParts01; //synchronization for already built parts (31 parts)
2145 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2146 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2147 int m_InteractedPartId; //construction part id that an action was performed on
2148 int m_PerformedActionId; //action id that was performed on a construction part
2149
2150 //Sounds
2151 //build
2152 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2153 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2154 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2155 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2156 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2157 //dismantle
2158 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2159 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2160 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2161 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2162 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2163
2164 protected EffectSound m_Sound;
2165
2169
2170 // Constructor
2171 void BaseBuildingBase()
2172 {
2174
2175 //synchronized variables
2176 RegisterNetSyncVariableInt("m_SyncParts01");
2177 RegisterNetSyncVariableInt("m_SyncParts02");
2178 RegisterNetSyncVariableInt("m_SyncParts03");
2179 RegisterNetSyncVariableInt("m_InteractedPartId");
2180 RegisterNetSyncVariableInt("m_PerformedActionId");
2181 RegisterNetSyncVariableBool("m_HasBase");
2182
2183 //Construction init
2185
2186 if (ConfigIsExisting("hybridAttachments"))
2187 {
2189 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2190 }
2191 if (ConfigIsExisting("mountables"))
2192 {
2194 ConfigGetTextArray("mountables", m_Mountables);
2195 }
2196
2197 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2198 }
2199
2200 override void EEDelete(EntityAI parent)
2201 {
2202 super.EEDelete(parent);
2203
2204 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2206
2207 }
2208
2209 override string GetInvulnerabilityTypeString()
2210 {
2211 return "disableBaseDamage";
2212 }
2213
2214 override bool CanObstruct()
2215 {
2216 return true;
2217 }
2218
2219 override int GetHideIconMask()
2220 {
2221 return EInventoryIconVisibility.HIDE_VICINITY;
2222 }
2223
2224 // --- SYNCHRONIZATION
2226 {
2227 if (GetGame().IsServer())
2228 SetSynchDirty();
2229 }
2230
2231 override void OnVariablesSynchronized()
2232 {
2233 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2234 super.OnVariablesSynchronized();
2235
2236 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2237 }
2238
2239 protected void OnSynchronizedClient()
2240 {
2241 //update parts
2243
2244 //update action on part
2246
2247 //update visuals (client)
2248 UpdateVisuals();
2249 }
2250
2251 //parts synchronization
2253 {
2254 //part_id must starts from index = 1
2255 int offset;
2256 int mask;
2257
2258 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2259 {
2260 offset = part_id - 1;
2261 mask = 1 << offset;
2262
2264 }
2265 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2266 {
2267 offset = (part_id % 32);
2268 mask = 1 << offset;
2269
2271 }
2272 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2273 {
2274 offset = (part_id % 63);
2275 mask = 1 << offset;
2276
2278 }
2279 }
2280
2282 {
2283 //part_id must starts from index = 1
2284 int offset;
2285 int mask;
2286
2287 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2288 {
2289 offset = part_id - 1;
2290 mask = 1 << offset;
2291
2293 }
2294 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2295 {
2296 offset = (part_id % 32);
2297 mask = 1 << offset;
2298
2300 }
2301 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2302 {
2303 offset = (part_id % 63);
2304 mask = 1 << offset;
2305
2307 }
2308 }
2309
2311 {
2312 //part_id must starts from index = 1
2313 int offset;
2314 int mask;
2315
2316 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2317 {
2318 offset = part_id - 1;
2319 mask = 1 << offset;
2320
2321 if ((m_SyncParts01 & mask) > 0)
2322 return true;
2323 }
2324 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2325 {
2326 offset = (part_id % 32);
2327 mask = 1 << offset;
2328
2329 if ((m_SyncParts02 & mask) > 0)
2330 return true;
2331 }
2332 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2333 {
2334 offset = (part_id % 63);
2335 mask = 1 << offset;
2336
2337 if ((m_SyncParts03 & mask) > 0)
2338 return true;
2339 }
2340
2341 return false;
2342 }
2343
2344 protected void RegisterActionForSync(int part_id, int action_id)
2345 {
2348 }
2349
2350 protected void ResetActionSyncData()
2351 {
2352 //reset data
2353 m_InteractedPartId = -1;
2355 }
2356
2357 protected void SetActionFromSyncData()
2358 {
2359 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2360 {
2363
2364 switch (build_action_id)
2365 {
2369 }
2370 }
2371 }
2372 //------
2373
2375 {
2376 string key = part.m_PartName;
2377 bool is_base = part.IsBase();
2379 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2381 {
2382 if (!part.IsBuilt())
2383 {
2384 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2385 GetConstruction().AddToConstructedParts(key);
2386 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2387
2388 if (is_base)
2389 {
2391 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2392 }
2393 }
2394 }
2395 else
2396 {
2397 if (part.IsBuilt())
2398 {
2399 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2400 GetConstruction().RemoveFromConstructedParts(key);
2401 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2402
2403 if (is_base)
2404 {
2406 AddProxyPhysics(ANIMATION_DEPLOYED);
2407 }
2408 }
2409 }
2410
2411 //check slot lock for material attachments
2412 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2413 }
2414
2415 //set construction parts based on synchronized data
2417 {
2420
2421 for (int i = 0; i < construction_parts.Count(); ++i)
2422 {
2423 string key = construction_parts.GetKey(i);
2426 }
2427
2428 //regenerate navmesh
2429 UpdateNavmesh();
2430 }
2431
2433 {
2436
2437 for (int i = 0; i < construction_parts.Count(); ++i)
2438 {
2439 string key = construction_parts.GetKey(i);
2441
2442 if (value.GetId() == id)
2443 return value;
2444 }
2445
2446 return NULL;
2447 }
2448 //
2449
2450 //Base
2451 bool HasBase()
2452 {
2453 return m_HasBase;
2454 }
2455
2456 void SetBaseState(bool has_base)
2457 {
2459 }
2460
2461 override bool IsDeployable()
2462 {
2463 return true;
2464 }
2465
2466 bool IsOpened()
2467 {
2468 return false;
2469 }
2470
2471 //--- CONSTRUCTION KIT
2473 {
2477
2478 return construction_kit;
2479 }
2480
2482 {
2483 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2486 }
2487
2488 protected vector GetKitSpawnPosition()
2489 {
2490 return GetPosition();
2491 }
2492
2493 protected string GetConstructionKitType()
2494 {
2495 return "";
2496 }
2497
2499 {
2501 GetGame().ObjectDelete(construction_kit);
2502 }
2503
2504 //--- CONSTRUCTION
2505 void DestroyConstruction()
2506 {
2507 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2508 GetGame().ObjectDelete(this);
2509 }
2510
2511 // --- EVENTS
2512 override void OnStoreSave(ParamsWriteContext ctx)
2513 {
2514 super.OnStoreSave(ctx);
2515
2516 //sync parts 01
2517 ctx.Write(m_SyncParts01);
2518 ctx.Write(m_SyncParts02);
2519 ctx.Write(m_SyncParts03);
2520
2521 ctx.Write(m_HasBase);
2522 }
2523
2524 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2525 {
2526 if (!super.OnStoreLoad(ctx, version))
2527 return false;
2528
2529 //--- Base building data ---
2530 //Restore synced parts data
2531 if (!ctx.Read(m_SyncParts01))
2532 {
2533 m_SyncParts01 = 0; //set default
2534 return false;
2535 }
2536 if (!ctx.Read(m_SyncParts02))
2537 {
2538 m_SyncParts02 = 0; //set default
2539 return false;
2540 }
2541 if (!ctx.Read(m_SyncParts03))
2542 {
2543 m_SyncParts03 = 0; //set default
2544 return false;
2545 }
2546
2547 //has base
2548 if (!ctx.Read(m_HasBase))
2549 {
2550 m_HasBase = false;
2551 return false;
2552 }
2553 //---
2554
2555 return true;
2556 }
2557
2558 override void AfterStoreLoad()
2559 {
2560 super.AfterStoreLoad();
2561
2564 }
2565
2567 {
2568 //update server data
2570
2571 //set base state
2572 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2573 SetBaseState(construction_part.IsBuilt()) ;
2574
2575 //synchronize after load
2577 }
2578
2579 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2580 {
2582 return;
2583
2584 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2585
2586 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2587 return;
2588
2590 string part_name = zone;
2591 part_name.ToLower();
2592
2594 {
2596
2597 if (construction_part && construction.IsPartConstructed(part_name))
2598 {
2599 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2600 construction.DestroyConnectedParts(part_name);
2601 }
2602
2603 //barbed wire handling (hack-ish)
2604 if (part_name.Contains("barbed"))
2605 {
2606 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2607 if (barbed_wire)
2608 barbed_wire.SetMountedState(false);
2609 }
2610 }
2611 }
2612
2613 override void EEOnAfterLoad()
2614 {
2616 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2617
2618 super.EEOnAfterLoad();
2619 }
2620
2621 override void EEInit()
2622 {
2623 super.EEInit();
2624
2625 // init visuals and physics
2626 InitBaseState();
2627
2628 //debug
2629#ifdef DEVELOPER
2631#endif
2632 }
2633
2634 override void EEItemAttached(EntityAI item, string slot_name)
2635 {
2636 super.EEItemAttached(item, slot_name);
2637
2639 UpdateVisuals();
2641 }
2642
2643 override void EEItemDetached(EntityAI item, string slot_name)
2644 {
2645 super.EEItemDetached(item, slot_name);
2646
2647 UpdateVisuals();
2649 }
2650
2651 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2652 {
2654 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2655
2658 }
2659
2660 //ignore out of reach condition
2661 override bool IgnoreOutOfReachCondition()
2662 {
2663 return true;
2664 }
2665
2666 //CONSTRUCTION EVENTS
2667 //Build
2668 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2669 {
2671
2672 //check base state
2673 if (construtionPart.IsBase())
2674 {
2675 SetBaseState(true);
2676
2677 //spawn kit
2679 }
2680
2681 //register constructed parts for synchronization
2683
2684 //register action that was performed on part
2686
2687 //synchronize
2689
2690 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2691
2692 UpdateNavmesh();
2693
2694 //update visuals
2695 UpdateVisuals();
2696
2697 //reset action sync data
2698 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2699 }
2700
2701 void OnPartBuiltClient(string part_name, int action_id)
2702 {
2703 //play sound
2705 }
2706
2707 //Dismantle
2709 {
2710 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2712
2713 //register constructed parts for synchronization
2715
2716 //register action that was performed on part
2718
2719 //synchronize
2721
2722 // server part of sync, client will be synced from SetPartsFromSyncData
2724
2725 UpdateNavmesh();
2726
2727 //update visuals
2728 UpdateVisuals();
2729
2730 //reset action sync data
2731 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2732
2733 //check base state
2734 if (construtionPart.IsBase())
2735 {
2736 //Destroy construction
2737 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2738 }
2739 }
2740
2742 {
2743 //play sound
2745 }
2746
2747 //Destroy
2749 {
2750 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2752
2753 //register constructed parts for synchronization
2755
2756 //register action that was performed on part
2758
2759 //synchronize
2761
2762 // server part of sync, client will be synced from SetPartsFromSyncData
2764
2765 UpdateNavmesh();
2766
2767 //update visuals
2768 UpdateVisuals();
2769
2770 //reset action sync data
2771 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2772
2773 //check base state
2774 if (construtionPart.IsBase())
2775 {
2776 //Destroy construction
2777 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2778 }
2779 }
2780
2781 void OnPartDestroyedClient(string part_name, int action_id)
2782 {
2783 //play sound
2785 }
2786
2787 // --- UPDATE
2788 void InitBaseState()
2789 {
2790 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2791
2792 InitVisuals();
2793 UpdateNavmesh(); //regenerate navmesh
2794 GetConstruction().InitBaseState();
2795 }
2796
2797 void InitVisuals()
2798 {
2799 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2800 //check base
2801 if (!HasBase())
2802 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2803 else
2804 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2805
2806 GetConstruction().UpdateVisuals();
2807 }
2808
2809 void UpdateVisuals()
2810 {
2812
2814 foreach (string slotName : attachmentSlots)
2816
2817 //check base
2818 if (!HasBase())
2819 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2820 else
2821 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2822
2823 GetConstruction().UpdateVisuals();
2824 }
2825
2827 {
2828 string slotNameMounted = slot_name + "_Mounted";
2829 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2830
2831 if (attachment)
2832 {
2833 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2834 if (barbedWire && barbedWire.IsMounted())
2836 else
2838
2839 if (is_locked)
2840 {
2841 SetAnimationPhase(slotNameMounted, 0);
2842 SetAnimationPhase(slot_name, 1);
2843 }
2844 else
2845 {
2846 SetAnimationPhase(slotNameMounted, 1);
2847 SetAnimationPhase(slot_name, 0);
2848 }
2849 }
2850 else
2851 {
2852 SetAnimationPhase(slotNameMounted, 1);
2853 SetAnimationPhase(slot_name, 1);
2854
2856 }
2857 }
2858
2859 // avoid calling this function on frequent occasions, it's a massive performance hit
2860 void UpdatePhysics()
2861 {
2863 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2864
2867
2869 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2870
2871 foreach (string slotName : attachmentSlots)
2873
2874 //check base
2875 if (!HasBase())
2876 {
2878 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2879
2880 AddProxyPhysics(ANIMATION_DEPLOYED);
2881 }
2882 else
2883 {
2885 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2886
2887 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2888 }
2889
2890 GetConstruction().UpdatePhysics();
2891 UpdateNavmesh();
2892 }
2893
2895 {
2896 //checks for invalid appends; hotfix
2897 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2898 return;
2899 //----------------------------------
2900 string slot_name_mounted = slot_name + "_Mounted";
2901 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2902
2903 //remove proxy physics
2904 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2905 RemoveProxyPhysics(slot_name_mounted);
2906 RemoveProxyPhysics(slot_name);
2907
2908 if (attachment)
2909 {
2910 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2911 if (is_locked)
2912 {
2913 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2914 AddProxyPhysics(slot_name_mounted);
2915 }
2916 else
2917 {
2918 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2919 AddProxyPhysics(slot_name);
2920 }
2921 }
2922 }
2923
2924 protected void UpdateNavmesh()
2925 {
2926 SetAffectPathgraph(true, false);
2927 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2928 }
2929
2930 override bool CanUseConstruction()
2931 {
2932 return true;
2933 }
2934
2935 override bool CanUseConstructionBuild()
2936 {
2937 return true;
2938 }
2939
2941 {
2942 if (attachment)
2943 {
2945 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2946
2947 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2948 }
2949
2950 return false;
2951 }
2952
2953 protected bool IsAttachmentSlotLocked(string slot_name)
2954 {
2955 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2956 }
2957
2958 //--- ATTACHMENT SLOTS
2960 {
2961 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2962 if (GetGame().ConfigIsExisting(config_path))
2963 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2964 }
2965
2967 {
2968 return true;
2969 }
2970
2971 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2972 {
2973 return true;
2974 }
2975
2976 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2977 {
2978 return true;
2979 }
2980
2981 // --- INIT
2982 void ConstructionInit()
2983 {
2984 if (!m_Construction)
2985 m_Construction = new Construction(this);
2986
2987 GetConstruction().Init();
2988 }
2989
2991 {
2992 return m_Construction;
2993 }
2994
2995 //--- INVENTORY/ATTACHMENTS CONDITIONS
2996 //attachments
2998 {
2999 return super.CanReceiveAttachment(attachment, slotId);
3000 }
3001
3003 {
3004 int attachment_count = GetInventory().AttachmentCount();
3005 if (attachment_count > 0)
3006 {
3007 if (HasBase() && attachment_count == 1)
3008 return false;
3009
3010 return true;
3011 }
3012
3013 return false;
3014 }
3015
3016 override bool ShowZonesHealth()
3017 {
3018 return true;
3019 }
3020
3021 //this into/outo parent.Cargo
3022 override bool CanPutInCargo(EntityAI parent)
3023 {
3024 return false;
3025 }
3026
3027 override bool CanRemoveFromCargo(EntityAI parent)
3028 {
3029 return false;
3030 }
3031
3032 //hands
3033 override bool CanPutIntoHands(EntityAI parent)
3034 {
3035 return false;
3036 }
3037
3038 //--- ACTION CONDITIONS
3039 //direction
3040 override bool IsFacingPlayer(PlayerBase player, string selection)
3041 {
3042 return true;
3043 }
3044
3045 override bool IsPlayerInside(PlayerBase player, string selection)
3046 {
3047 return true;
3048 }
3049
3052 {
3053 return false;
3054 }
3055
3056 //camera direction check
3057 bool IsFacingCamera(string selection)
3058 {
3059 return true;
3060 }
3061
3062 //roof check
3064 {
3065 return false;
3066 }
3067
3068 //selection->player distance check
3069 bool HasProperDistance(string selection, PlayerBase player)
3070 {
3071 return true;
3072 }
3073
3074 //folding
3076 {
3077 if (HasBase() || GetInventory().AttachmentCount() > 0)
3078 return false;
3079
3080 return true;
3081 }
3082
3084 {
3087
3088 return item;
3089 }
3090
3091 //Damage triggers (barbed wire)
3092 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3093 {
3094 if (GetGame() && GetGame().IsServer())
3095 {
3096 //destroy area damage if some already exists
3098
3099 //create new area damage
3101 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3102
3103 vector min_max[2];
3104 if (MemoryPointExists(slot_name + "_min"))
3105 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3106 if (MemoryPointExists(slot_name + "_max"))
3107 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3108
3109 //get proper trigger extents (min<max)
3110 vector extents[2];
3111 GetConstruction().GetTriggerExtents(min_max, extents);
3112
3113 //get box center
3114 vector center;
3115 center = GetConstruction().GetBoxCenter(min_max);
3116 center = ModelToWorld(center);
3117
3118 //rotate center if needed
3121
3122 areaDamage.SetExtents(extents[0], extents[1]);
3123 areaDamage.SetAreaPosition(center);
3124 areaDamage.SetAreaOrientation(orientation);
3125 areaDamage.SetLoopInterval(1.0);
3126 areaDamage.SetDeferDuration(0.2);
3127 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3128 areaDamage.SetAmmoName("BarbedWireHit");
3129 areaDamage.Spawn();
3130
3132 }
3133 }
3134
3136 {
3137 if (angle_deg != 0)
3138 {
3139 //orientation
3141
3142 //center
3144 if (MemoryPointExists("rotate_axis"))
3145 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3148 center[0] = r_center_x;
3149 center[2] = r_center_z;
3150 }
3151 }
3152
3153 void DestroyAreaDamage(string slot_name)
3154 {
3155 if (GetGame() && GetGame().IsServer())
3156 {
3159 {
3160 if (areaDamage)
3161 areaDamage.Destroy();
3162
3164 }
3165 }
3166 }
3167
3168 override bool IsIgnoredByConstruction()
3169 {
3170 return true;
3171 }
3172
3173 //================================================================
3174 // SOUNDS
3175 //================================================================
3176 protected void SoundBuildStart(string part_name)
3177 {
3178 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3179 }
3180
3181 protected void SoundDismantleStart(string part_name)
3182 {
3183 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3184 }
3185
3186 protected void SoundDestroyStart(string part_name)
3187 {
3188 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3189 }
3190
3191 protected string GetBuildSoundByMaterial(string part_name)
3192 {
3194
3195 switch (material_type)
3196 {
3197 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3198 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3199 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3200 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3201 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3202 }
3203
3204 return "";
3205 }
3206
3207 protected string GetDismantleSoundByMaterial(string part_name)
3208 {
3210
3211 switch (material_type)
3212 {
3213 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3214 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3215 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3216 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3217 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3218 }
3219
3220 return "";
3221 }
3222
3223 //misc
3225 {
3226 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3227 {
3228 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3230 SetHealth(slot_name, "Health", item.GetHealth());
3231 }
3232 }
3233
3234 override int GetDamageSystemVersionChange()
3235 {
3236 return 111;
3237 }
3238
3239 override void SetActions()
3240 {
3241 super.SetActions();
3242
3244 //AddAction(ActionTakeHybridAttachment);
3245 //AddAction(ActionTakeHybridAttachmentToHands);
3248 }
3249
3250 //================================================================
3251 // DEBUG
3252 //================================================================
3253 protected void DebugCustomState()
3254 {
3255 }
3256
3259 {
3260 return null;
3261 }
3262
3263 override void OnDebugSpawn()
3264 {
3265 FullyBuild();
3266 }
3267
3268 void FullyBuild()
3269 {
3271 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3272
3273 Man p;
3274
3275#ifdef SERVER
3277 GetGame().GetWorld().GetPlayerList(players);
3278 if (players.Count())
3279 p = players[0];
3280#else
3281 p = GetGame().GetPlayer();
3282#endif
3283
3284 foreach (ConstructionPart part : parts)
3285 {
3286 bool excluded = false;
3287 string partName = part.GetPartName();
3288 if (excludes)
3289 {
3290 foreach (string exclude : excludes)
3291 {
3292 if (partName.Contains(exclude))
3293 {
3294 excluded = true;
3295 break;
3296 }
3297 }
3298 }
3299
3300 if (!excluded)
3302 }
3303
3304 GetConstruction().UpdateVisuals();
3305 }
3306}
3307
3308void bsbDebugPrint(string s)
3309{
3310#ifdef BSB_DEBUG
3311 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3312#else
3313 //Print("" + s); // comment/uncomment to hide/see debug logs
3314#endif
3315}
3316void bsbDebugSpam(string s)
3317{
3318#ifdef BSB_DEBUG_SPAM
3319 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3320#else
3321 //Print("" + s); // comment/uncomment to hide/see debug logs
3322#endif
3323}

◆ CreateConstructionKit()

ItemBase bsbDebugPrint::CreateConstructionKit ( )
protected

Definition at line 1513 of file BaseBuildingBase.c.

1515{
1516 const string ANIMATION_DEPLOYED = "Deployed";
1517
1518 float m_ConstructionKitHealth; //stored health value for used construction kit
1519
1521
1522 bool m_HasBase;
1523 //variables for synchronization of base building parts (2x31 is the current limit)
1524 int m_SyncParts01; //synchronization for already built parts (31 parts)
1525 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1526 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1527 int m_InteractedPartId; //construction part id that an action was performed on
1528 int m_PerformedActionId; //action id that was performed on a construction part
1529
1530 //Sounds
1531 //build
1532 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1533 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1534 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1535 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1536 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1537 //dismantle
1538 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1539 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1540 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1541 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1542 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1543
1544 protected EffectSound m_Sound;
1545
1549
1550 // Constructor
1551 void BaseBuildingBase()
1552 {
1554
1555 //synchronized variables
1556 RegisterNetSyncVariableInt("m_SyncParts01");
1557 RegisterNetSyncVariableInt("m_SyncParts02");
1558 RegisterNetSyncVariableInt("m_SyncParts03");
1559 RegisterNetSyncVariableInt("m_InteractedPartId");
1560 RegisterNetSyncVariableInt("m_PerformedActionId");
1561 RegisterNetSyncVariableBool("m_HasBase");
1562
1563 //Construction init
1565
1566 if (ConfigIsExisting("hybridAttachments"))
1567 {
1569 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1570 }
1571 if (ConfigIsExisting("mountables"))
1572 {
1574 ConfigGetTextArray("mountables", m_Mountables);
1575 }
1576
1577 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1578 }
1579
1580 override void EEDelete(EntityAI parent)
1581 {
1582 super.EEDelete(parent);
1583
1584 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1586
1587 }
1588
1589 override string GetInvulnerabilityTypeString()
1590 {
1591 return "disableBaseDamage";
1592 }
1593
1594 override bool CanObstruct()
1595 {
1596 return true;
1597 }
1598
1599 override int GetHideIconMask()
1600 {
1601 return EInventoryIconVisibility.HIDE_VICINITY;
1602 }
1603
1604 // --- SYNCHRONIZATION
1606 {
1607 if (GetGame().IsServer())
1608 SetSynchDirty();
1609 }
1610
1611 override void OnVariablesSynchronized()
1612 {
1613 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1614 super.OnVariablesSynchronized();
1615
1616 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1617 }
1618
1619 protected void OnSynchronizedClient()
1620 {
1621 //update parts
1623
1624 //update action on part
1626
1627 //update visuals (client)
1628 UpdateVisuals();
1629 }
1630
1631 //parts synchronization
1633 {
1634 //part_id must starts from index = 1
1635 int offset;
1636 int mask;
1637
1638 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1639 {
1640 offset = part_id - 1;
1641 mask = 1 << offset;
1642
1644 }
1645 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1646 {
1647 offset = (part_id % 32);
1648 mask = 1 << offset;
1649
1651 }
1652 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1653 {
1654 offset = (part_id % 63);
1655 mask = 1 << offset;
1656
1658 }
1659 }
1660
1662 {
1663 //part_id must starts from index = 1
1664 int offset;
1665 int mask;
1666
1667 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1668 {
1669 offset = part_id - 1;
1670 mask = 1 << offset;
1671
1673 }
1674 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1675 {
1676 offset = (part_id % 32);
1677 mask = 1 << offset;
1678
1680 }
1681 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1682 {
1683 offset = (part_id % 63);
1684 mask = 1 << offset;
1685
1687 }
1688 }
1689
1691 {
1692 //part_id must starts from index = 1
1693 int offset;
1694 int mask;
1695
1696 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1697 {
1698 offset = part_id - 1;
1699 mask = 1 << offset;
1700
1701 if ((m_SyncParts01 & mask) > 0)
1702 return true;
1703 }
1704 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1705 {
1706 offset = (part_id % 32);
1707 mask = 1 << offset;
1708
1709 if ((m_SyncParts02 & mask) > 0)
1710 return true;
1711 }
1712 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1713 {
1714 offset = (part_id % 63);
1715 mask = 1 << offset;
1716
1717 if ((m_SyncParts03 & mask) > 0)
1718 return true;
1719 }
1720
1721 return false;
1722 }
1723
1724 protected void RegisterActionForSync(int part_id, int action_id)
1725 {
1728 }
1729
1730 protected void ResetActionSyncData()
1731 {
1732 //reset data
1733 m_InteractedPartId = -1;
1735 }
1736
1737 protected void SetActionFromSyncData()
1738 {
1739 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1740 {
1743
1744 switch (build_action_id)
1745 {
1749 }
1750 }
1751 }
1752 //------
1753
1755 {
1756 string key = part.m_PartName;
1757 bool is_base = part.IsBase();
1759 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1761 {
1762 if (!part.IsBuilt())
1763 {
1764 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1765 GetConstruction().AddToConstructedParts(key);
1766 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1767
1768 if (is_base)
1769 {
1771 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1772 }
1773 }
1774 }
1775 else
1776 {
1777 if (part.IsBuilt())
1778 {
1779 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1780 GetConstruction().RemoveFromConstructedParts(key);
1781 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1782
1783 if (is_base)
1784 {
1786 AddProxyPhysics(ANIMATION_DEPLOYED);
1787 }
1788 }
1789 }
1790
1791 //check slot lock for material attachments
1792 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1793 }
1794
1795 //set construction parts based on synchronized data
1797 {
1800
1801 for (int i = 0; i < construction_parts.Count(); ++i)
1802 {
1803 string key = construction_parts.GetKey(i);
1806 }
1807
1808 //regenerate navmesh
1809 UpdateNavmesh();
1810 }
1811
1813 {
1816
1817 for (int i = 0; i < construction_parts.Count(); ++i)
1818 {
1819 string key = construction_parts.GetKey(i);
1821
1822 if (value.GetId() == id)
1823 return value;
1824 }
1825
1826 return NULL;
1827 }
1828 //
1829
1830 //Base
1831 bool HasBase()
1832 {
1833 return m_HasBase;
1834 }
1835
1836 void SetBaseState(bool has_base)
1837 {
1839 }
1840
1841 override bool IsDeployable()
1842 {
1843 return true;
1844 }
1845
1846 bool IsOpened()
1847 {
1848 return false;
1849 }
1850
1851 //--- CONSTRUCTION KIT
1853 {
1857
1858 return construction_kit;
1859 }
1860
1862 {
1863 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1866 }
1867
1868 protected vector GetKitSpawnPosition()
1869 {
1870 return GetPosition();
1871 }
1872
1873 protected string GetConstructionKitType()
1874 {
1875 return "";
1876 }
1877
1879 {
1881 GetGame().ObjectDelete(construction_kit);
1882 }
1883
1884 //--- CONSTRUCTION
1885 void DestroyConstruction()
1886 {
1887 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1888 GetGame().ObjectDelete(this);
1889 }
1890
1891 // --- EVENTS
1892 override void OnStoreSave(ParamsWriteContext ctx)
1893 {
1894 super.OnStoreSave(ctx);
1895
1896 //sync parts 01
1897 ctx.Write(m_SyncParts01);
1898 ctx.Write(m_SyncParts02);
1899 ctx.Write(m_SyncParts03);
1900
1901 ctx.Write(m_HasBase);
1902 }
1903
1904 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1905 {
1906 if (!super.OnStoreLoad(ctx, version))
1907 return false;
1908
1909 //--- Base building data ---
1910 //Restore synced parts data
1911 if (!ctx.Read(m_SyncParts01))
1912 {
1913 m_SyncParts01 = 0; //set default
1914 return false;
1915 }
1916 if (!ctx.Read(m_SyncParts02))
1917 {
1918 m_SyncParts02 = 0; //set default
1919 return false;
1920 }
1921 if (!ctx.Read(m_SyncParts03))
1922 {
1923 m_SyncParts03 = 0; //set default
1924 return false;
1925 }
1926
1927 //has base
1928 if (!ctx.Read(m_HasBase))
1929 {
1930 m_HasBase = false;
1931 return false;
1932 }
1933 //---
1934
1935 return true;
1936 }
1937
1938 override void AfterStoreLoad()
1939 {
1940 super.AfterStoreLoad();
1941
1944 }
1945
1947 {
1948 //update server data
1950
1951 //set base state
1952 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1953 SetBaseState(construction_part.IsBuilt()) ;
1954
1955 //synchronize after load
1957 }
1958
1959 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1960 {
1962 return;
1963
1964 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1965
1966 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1967 return;
1968
1970 string part_name = zone;
1971 part_name.ToLower();
1972
1974 {
1976
1977 if (construction_part && construction.IsPartConstructed(part_name))
1978 {
1979 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1980 construction.DestroyConnectedParts(part_name);
1981 }
1982
1983 //barbed wire handling (hack-ish)
1984 if (part_name.Contains("barbed"))
1985 {
1986 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1987 if (barbed_wire)
1988 barbed_wire.SetMountedState(false);
1989 }
1990 }
1991 }
1992
1993 override void EEOnAfterLoad()
1994 {
1996 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1997
1998 super.EEOnAfterLoad();
1999 }
2000
2001 override void EEInit()
2002 {
2003 super.EEInit();
2004
2005 // init visuals and physics
2006 InitBaseState();
2007
2008 //debug
2009#ifdef DEVELOPER
2011#endif
2012 }
2013
2014 override void EEItemAttached(EntityAI item, string slot_name)
2015 {
2016 super.EEItemAttached(item, slot_name);
2017
2019 UpdateVisuals();
2021 }
2022
2023 override void EEItemDetached(EntityAI item, string slot_name)
2024 {
2025 super.EEItemDetached(item, slot_name);
2026
2027 UpdateVisuals();
2029 }
2030
2031 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2032 {
2034 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2035
2038 }
2039
2040 //ignore out of reach condition
2041 override bool IgnoreOutOfReachCondition()
2042 {
2043 return true;
2044 }
2045
2046 //CONSTRUCTION EVENTS
2047 //Build
2048 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2049 {
2051
2052 //check base state
2053 if (construtionPart.IsBase())
2054 {
2055 SetBaseState(true);
2056
2057 //spawn kit
2059 }
2060
2061 //register constructed parts for synchronization
2063
2064 //register action that was performed on part
2066
2067 //synchronize
2069
2070 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2071
2072 UpdateNavmesh();
2073
2074 //update visuals
2075 UpdateVisuals();
2076
2077 //reset action sync data
2078 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2079 }
2080
2081 void OnPartBuiltClient(string part_name, int action_id)
2082 {
2083 //play sound
2085 }
2086
2087 //Dismantle
2089 {
2090 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2092
2093 //register constructed parts for synchronization
2095
2096 //register action that was performed on part
2098
2099 //synchronize
2101
2102 // server part of sync, client will be synced from SetPartsFromSyncData
2104
2105 UpdateNavmesh();
2106
2107 //update visuals
2108 UpdateVisuals();
2109
2110 //reset action sync data
2111 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2112
2113 //check base state
2114 if (construtionPart.IsBase())
2115 {
2116 //Destroy construction
2117 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2118 }
2119 }
2120
2122 {
2123 //play sound
2125 }
2126
2127 //Destroy
2129 {
2130 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2132
2133 //register constructed parts for synchronization
2135
2136 //register action that was performed on part
2138
2139 //synchronize
2141
2142 // server part of sync, client will be synced from SetPartsFromSyncData
2144
2145 UpdateNavmesh();
2146
2147 //update visuals
2148 UpdateVisuals();
2149
2150 //reset action sync data
2151 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2152
2153 //check base state
2154 if (construtionPart.IsBase())
2155 {
2156 //Destroy construction
2157 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2158 }
2159 }
2160
2161 void OnPartDestroyedClient(string part_name, int action_id)
2162 {
2163 //play sound
2165 }
2166
2167 // --- UPDATE
2168 void InitBaseState()
2169 {
2170 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2171
2172 InitVisuals();
2173 UpdateNavmesh(); //regenerate navmesh
2174 GetConstruction().InitBaseState();
2175 }
2176
2177 void InitVisuals()
2178 {
2179 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2180 //check base
2181 if (!HasBase())
2182 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2183 else
2184 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2185
2186 GetConstruction().UpdateVisuals();
2187 }
2188
2189 void UpdateVisuals()
2190 {
2192
2194 foreach (string slotName : attachmentSlots)
2196
2197 //check base
2198 if (!HasBase())
2199 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2200 else
2201 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2202
2203 GetConstruction().UpdateVisuals();
2204 }
2205
2207 {
2208 string slotNameMounted = slot_name + "_Mounted";
2209 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2210
2211 if (attachment)
2212 {
2213 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2214 if (barbedWire && barbedWire.IsMounted())
2216 else
2218
2219 if (is_locked)
2220 {
2221 SetAnimationPhase(slotNameMounted, 0);
2222 SetAnimationPhase(slot_name, 1);
2223 }
2224 else
2225 {
2226 SetAnimationPhase(slotNameMounted, 1);
2227 SetAnimationPhase(slot_name, 0);
2228 }
2229 }
2230 else
2231 {
2232 SetAnimationPhase(slotNameMounted, 1);
2233 SetAnimationPhase(slot_name, 1);
2234
2236 }
2237 }
2238
2239 // avoid calling this function on frequent occasions, it's a massive performance hit
2240 void UpdatePhysics()
2241 {
2243 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2244
2247
2249 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2250
2251 foreach (string slotName : attachmentSlots)
2253
2254 //check base
2255 if (!HasBase())
2256 {
2258 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2259
2260 AddProxyPhysics(ANIMATION_DEPLOYED);
2261 }
2262 else
2263 {
2265 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2266
2267 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2268 }
2269
2270 GetConstruction().UpdatePhysics();
2271 UpdateNavmesh();
2272 }
2273
2275 {
2276 //checks for invalid appends; hotfix
2277 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2278 return;
2279 //----------------------------------
2280 string slot_name_mounted = slot_name + "_Mounted";
2281 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2282
2283 //remove proxy physics
2284 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2285 RemoveProxyPhysics(slot_name_mounted);
2286 RemoveProxyPhysics(slot_name);
2287
2288 if (attachment)
2289 {
2290 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2291 if (is_locked)
2292 {
2293 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2294 AddProxyPhysics(slot_name_mounted);
2295 }
2296 else
2297 {
2298 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2299 AddProxyPhysics(slot_name);
2300 }
2301 }
2302 }
2303
2304 protected void UpdateNavmesh()
2305 {
2306 SetAffectPathgraph(true, false);
2307 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2308 }
2309
2310 override bool CanUseConstruction()
2311 {
2312 return true;
2313 }
2314
2315 override bool CanUseConstructionBuild()
2316 {
2317 return true;
2318 }
2319
2321 {
2322 if (attachment)
2323 {
2325 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2326
2327 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2328 }
2329
2330 return false;
2331 }
2332
2333 protected bool IsAttachmentSlotLocked(string slot_name)
2334 {
2335 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2336 }
2337
2338 //--- ATTACHMENT SLOTS
2340 {
2341 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2342 if (GetGame().ConfigIsExisting(config_path))
2343 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2344 }
2345
2347 {
2348 return true;
2349 }
2350
2351 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2352 {
2353 return true;
2354 }
2355
2356 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2357 {
2358 return true;
2359 }
2360
2361 // --- INIT
2362 void ConstructionInit()
2363 {
2364 if (!m_Construction)
2365 m_Construction = new Construction(this);
2366
2367 GetConstruction().Init();
2368 }
2369
2371 {
2372 return m_Construction;
2373 }
2374
2375 //--- INVENTORY/ATTACHMENTS CONDITIONS
2376 //attachments
2378 {
2379 return super.CanReceiveAttachment(attachment, slotId);
2380 }
2381
2383 {
2384 int attachment_count = GetInventory().AttachmentCount();
2385 if (attachment_count > 0)
2386 {
2387 if (HasBase() && attachment_count == 1)
2388 return false;
2389
2390 return true;
2391 }
2392
2393 return false;
2394 }
2395
2396 override bool ShowZonesHealth()
2397 {
2398 return true;
2399 }
2400
2401 //this into/outo parent.Cargo
2402 override bool CanPutInCargo(EntityAI parent)
2403 {
2404 return false;
2405 }
2406
2407 override bool CanRemoveFromCargo(EntityAI parent)
2408 {
2409 return false;
2410 }
2411
2412 //hands
2413 override bool CanPutIntoHands(EntityAI parent)
2414 {
2415 return false;
2416 }
2417
2418 //--- ACTION CONDITIONS
2419 //direction
2420 override bool IsFacingPlayer(PlayerBase player, string selection)
2421 {
2422 return true;
2423 }
2424
2425 override bool IsPlayerInside(PlayerBase player, string selection)
2426 {
2427 return true;
2428 }
2429
2432 {
2433 return false;
2434 }
2435
2436 //camera direction check
2437 bool IsFacingCamera(string selection)
2438 {
2439 return true;
2440 }
2441
2442 //roof check
2444 {
2445 return false;
2446 }
2447
2448 //selection->player distance check
2449 bool HasProperDistance(string selection, PlayerBase player)
2450 {
2451 return true;
2452 }
2453
2454 //folding
2456 {
2457 if (HasBase() || GetInventory().AttachmentCount() > 0)
2458 return false;
2459
2460 return true;
2461 }
2462
2464 {
2467
2468 return item;
2469 }
2470
2471 //Damage triggers (barbed wire)
2472 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2473 {
2474 if (GetGame() && GetGame().IsServer())
2475 {
2476 //destroy area damage if some already exists
2478
2479 //create new area damage
2481 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2482
2483 vector min_max[2];
2484 if (MemoryPointExists(slot_name + "_min"))
2485 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2486 if (MemoryPointExists(slot_name + "_max"))
2487 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2488
2489 //get proper trigger extents (min<max)
2490 vector extents[2];
2491 GetConstruction().GetTriggerExtents(min_max, extents);
2492
2493 //get box center
2494 vector center;
2495 center = GetConstruction().GetBoxCenter(min_max);
2496 center = ModelToWorld(center);
2497
2498 //rotate center if needed
2501
2502 areaDamage.SetExtents(extents[0], extents[1]);
2503 areaDamage.SetAreaPosition(center);
2504 areaDamage.SetAreaOrientation(orientation);
2505 areaDamage.SetLoopInterval(1.0);
2506 areaDamage.SetDeferDuration(0.2);
2507 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2508 areaDamage.SetAmmoName("BarbedWireHit");
2509 areaDamage.Spawn();
2510
2512 }
2513 }
2514
2516 {
2517 if (angle_deg != 0)
2518 {
2519 //orientation
2521
2522 //center
2524 if (MemoryPointExists("rotate_axis"))
2525 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2528 center[0] = r_center_x;
2529 center[2] = r_center_z;
2530 }
2531 }
2532
2533 void DestroyAreaDamage(string slot_name)
2534 {
2535 if (GetGame() && GetGame().IsServer())
2536 {
2539 {
2540 if (areaDamage)
2541 areaDamage.Destroy();
2542
2544 }
2545 }
2546 }
2547
2548 override bool IsIgnoredByConstruction()
2549 {
2550 return true;
2551 }
2552
2553 //================================================================
2554 // SOUNDS
2555 //================================================================
2556 protected void SoundBuildStart(string part_name)
2557 {
2558 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2559 }
2560
2561 protected void SoundDismantleStart(string part_name)
2562 {
2563 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2564 }
2565
2566 protected void SoundDestroyStart(string part_name)
2567 {
2568 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2569 }
2570
2571 protected string GetBuildSoundByMaterial(string part_name)
2572 {
2574
2575 switch (material_type)
2576 {
2577 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2578 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2579 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2580 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2581 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2582 }
2583
2584 return "";
2585 }
2586
2587 protected string GetDismantleSoundByMaterial(string part_name)
2588 {
2590
2591 switch (material_type)
2592 {
2593 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2594 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2595 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2596 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2597 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2598 }
2599
2600 return "";
2601 }
2602
2603 //misc
2605 {
2606 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2607 {
2608 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2610 SetHealth(slot_name, "Health", item.GetHealth());
2611 }
2612 }
2613
2614 override int GetDamageSystemVersionChange()
2615 {
2616 return 111;
2617 }
2618
2619 override void SetActions()
2620 {
2621 super.SetActions();
2622
2624 //AddAction(ActionTakeHybridAttachment);
2625 //AddAction(ActionTakeHybridAttachmentToHands);
2628 }
2629
2630 //================================================================
2631 // DEBUG
2632 //================================================================
2633 protected void DebugCustomState()
2634 {
2635 }
2636
2639 {
2640 return null;
2641 }
2642
2643 override void OnDebugSpawn()
2644 {
2645 FullyBuild();
2646 }
2647
2648 void FullyBuild()
2649 {
2651 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2652
2653 Man p;
2654
2655#ifdef SERVER
2657 GetGame().GetWorld().GetPlayerList(players);
2658 if (players.Count())
2659 p = players[0];
2660#else
2661 p = GetGame().GetPlayer();
2662#endif
2663
2664 foreach (ConstructionPart part : parts)
2665 {
2666 bool excluded = false;
2667 string partName = part.GetPartName();
2668 if (excludes)
2669 {
2670 foreach (string exclude : excludes)
2671 {
2672 if (partName.Contains(exclude))
2673 {
2674 excluded = true;
2675 break;
2676 }
2677 }
2678 }
2679
2680 if (!excluded)
2682 }
2683
2684 GetConstruction().UpdateVisuals();
2685 }
2686}
2687
2688void bsbDebugPrint(string s)
2689{
2690#ifdef BSB_DEBUG
2691 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2692#else
2693 //Print("" + s); // comment/uncomment to hide/see debug logs
2694#endif
2695}
2696void bsbDebugSpam(string s)
2697{
2698#ifdef BSB_DEBUG_SPAM
2699 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2700#else
2701 //Print("" + s); // comment/uncomment to hide/see debug logs
2702#endif
2703}

Referenced by ItemBase::FoldBaseBuildingObject(), and ItemBase::OnPartBuiltServer().

◆ CreateConstructionKitInHands()

void bsbDebugPrint::CreateConstructionKitInHands ( notnull PlayerBase player)
protected

Definition at line 1522 of file BaseBuildingBase.c.

1524{
1525 const string ANIMATION_DEPLOYED = "Deployed";
1526
1527 float m_ConstructionKitHealth; //stored health value for used construction kit
1528
1530
1531 bool m_HasBase;
1532 //variables for synchronization of base building parts (2x31 is the current limit)
1533 int m_SyncParts01; //synchronization for already built parts (31 parts)
1534 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1535 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1536 int m_InteractedPartId; //construction part id that an action was performed on
1537 int m_PerformedActionId; //action id that was performed on a construction part
1538
1539 //Sounds
1540 //build
1541 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1542 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1543 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1544 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1545 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1546 //dismantle
1547 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1548 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1549 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1550 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1551 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1552
1553 protected EffectSound m_Sound;
1554
1558
1559 // Constructor
1560 void BaseBuildingBase()
1561 {
1563
1564 //synchronized variables
1565 RegisterNetSyncVariableInt("m_SyncParts01");
1566 RegisterNetSyncVariableInt("m_SyncParts02");
1567 RegisterNetSyncVariableInt("m_SyncParts03");
1568 RegisterNetSyncVariableInt("m_InteractedPartId");
1569 RegisterNetSyncVariableInt("m_PerformedActionId");
1570 RegisterNetSyncVariableBool("m_HasBase");
1571
1572 //Construction init
1574
1575 if (ConfigIsExisting("hybridAttachments"))
1576 {
1578 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1579 }
1580 if (ConfigIsExisting("mountables"))
1581 {
1583 ConfigGetTextArray("mountables", m_Mountables);
1584 }
1585
1586 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1587 }
1588
1589 override void EEDelete(EntityAI parent)
1590 {
1591 super.EEDelete(parent);
1592
1593 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1595
1596 }
1597
1598 override string GetInvulnerabilityTypeString()
1599 {
1600 return "disableBaseDamage";
1601 }
1602
1603 override bool CanObstruct()
1604 {
1605 return true;
1606 }
1607
1608 override int GetHideIconMask()
1609 {
1610 return EInventoryIconVisibility.HIDE_VICINITY;
1611 }
1612
1613 // --- SYNCHRONIZATION
1615 {
1616 if (GetGame().IsServer())
1617 SetSynchDirty();
1618 }
1619
1620 override void OnVariablesSynchronized()
1621 {
1622 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1623 super.OnVariablesSynchronized();
1624
1625 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1626 }
1627
1628 protected void OnSynchronizedClient()
1629 {
1630 //update parts
1632
1633 //update action on part
1635
1636 //update visuals (client)
1637 UpdateVisuals();
1638 }
1639
1640 //parts synchronization
1642 {
1643 //part_id must starts from index = 1
1644 int offset;
1645 int mask;
1646
1647 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1648 {
1649 offset = part_id - 1;
1650 mask = 1 << offset;
1651
1653 }
1654 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1655 {
1656 offset = (part_id % 32);
1657 mask = 1 << offset;
1658
1660 }
1661 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1662 {
1663 offset = (part_id % 63);
1664 mask = 1 << offset;
1665
1667 }
1668 }
1669
1671 {
1672 //part_id must starts from index = 1
1673 int offset;
1674 int mask;
1675
1676 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1677 {
1678 offset = part_id - 1;
1679 mask = 1 << offset;
1680
1682 }
1683 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1684 {
1685 offset = (part_id % 32);
1686 mask = 1 << offset;
1687
1689 }
1690 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1691 {
1692 offset = (part_id % 63);
1693 mask = 1 << offset;
1694
1696 }
1697 }
1698
1700 {
1701 //part_id must starts from index = 1
1702 int offset;
1703 int mask;
1704
1705 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1706 {
1707 offset = part_id - 1;
1708 mask = 1 << offset;
1709
1710 if ((m_SyncParts01 & mask) > 0)
1711 return true;
1712 }
1713 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1714 {
1715 offset = (part_id % 32);
1716 mask = 1 << offset;
1717
1718 if ((m_SyncParts02 & mask) > 0)
1719 return true;
1720 }
1721 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1722 {
1723 offset = (part_id % 63);
1724 mask = 1 << offset;
1725
1726 if ((m_SyncParts03 & mask) > 0)
1727 return true;
1728 }
1729
1730 return false;
1731 }
1732
1733 protected void RegisterActionForSync(int part_id, int action_id)
1734 {
1737 }
1738
1739 protected void ResetActionSyncData()
1740 {
1741 //reset data
1742 m_InteractedPartId = -1;
1744 }
1745
1746 protected void SetActionFromSyncData()
1747 {
1748 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1749 {
1752
1753 switch (build_action_id)
1754 {
1758 }
1759 }
1760 }
1761 //------
1762
1764 {
1765 string key = part.m_PartName;
1766 bool is_base = part.IsBase();
1768 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1770 {
1771 if (!part.IsBuilt())
1772 {
1773 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1774 GetConstruction().AddToConstructedParts(key);
1775 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1776
1777 if (is_base)
1778 {
1780 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1781 }
1782 }
1783 }
1784 else
1785 {
1786 if (part.IsBuilt())
1787 {
1788 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1789 GetConstruction().RemoveFromConstructedParts(key);
1790 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1791
1792 if (is_base)
1793 {
1795 AddProxyPhysics(ANIMATION_DEPLOYED);
1796 }
1797 }
1798 }
1799
1800 //check slot lock for material attachments
1801 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1802 }
1803
1804 //set construction parts based on synchronized data
1806 {
1809
1810 for (int i = 0; i < construction_parts.Count(); ++i)
1811 {
1812 string key = construction_parts.GetKey(i);
1815 }
1816
1817 //regenerate navmesh
1818 UpdateNavmesh();
1819 }
1820
1822 {
1825
1826 for (int i = 0; i < construction_parts.Count(); ++i)
1827 {
1828 string key = construction_parts.GetKey(i);
1830
1831 if (value.GetId() == id)
1832 return value;
1833 }
1834
1835 return NULL;
1836 }
1837 //
1838
1839 //Base
1840 bool HasBase()
1841 {
1842 return m_HasBase;
1843 }
1844
1845 void SetBaseState(bool has_base)
1846 {
1848 }
1849
1850 override bool IsDeployable()
1851 {
1852 return true;
1853 }
1854
1855 bool IsOpened()
1856 {
1857 return false;
1858 }
1859
1860 //--- CONSTRUCTION KIT
1862 {
1866
1867 return construction_kit;
1868 }
1869
1871 {
1872 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1875 }
1876
1877 protected vector GetKitSpawnPosition()
1878 {
1879 return GetPosition();
1880 }
1881
1882 protected string GetConstructionKitType()
1883 {
1884 return "";
1885 }
1886
1888 {
1890 GetGame().ObjectDelete(construction_kit);
1891 }
1892
1893 //--- CONSTRUCTION
1894 void DestroyConstruction()
1895 {
1896 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1897 GetGame().ObjectDelete(this);
1898 }
1899
1900 // --- EVENTS
1901 override void OnStoreSave(ParamsWriteContext ctx)
1902 {
1903 super.OnStoreSave(ctx);
1904
1905 //sync parts 01
1906 ctx.Write(m_SyncParts01);
1907 ctx.Write(m_SyncParts02);
1908 ctx.Write(m_SyncParts03);
1909
1910 ctx.Write(m_HasBase);
1911 }
1912
1913 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1914 {
1915 if (!super.OnStoreLoad(ctx, version))
1916 return false;
1917
1918 //--- Base building data ---
1919 //Restore synced parts data
1920 if (!ctx.Read(m_SyncParts01))
1921 {
1922 m_SyncParts01 = 0; //set default
1923 return false;
1924 }
1925 if (!ctx.Read(m_SyncParts02))
1926 {
1927 m_SyncParts02 = 0; //set default
1928 return false;
1929 }
1930 if (!ctx.Read(m_SyncParts03))
1931 {
1932 m_SyncParts03 = 0; //set default
1933 return false;
1934 }
1935
1936 //has base
1937 if (!ctx.Read(m_HasBase))
1938 {
1939 m_HasBase = false;
1940 return false;
1941 }
1942 //---
1943
1944 return true;
1945 }
1946
1947 override void AfterStoreLoad()
1948 {
1949 super.AfterStoreLoad();
1950
1953 }
1954
1956 {
1957 //update server data
1959
1960 //set base state
1961 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1962 SetBaseState(construction_part.IsBuilt()) ;
1963
1964 //synchronize after load
1966 }
1967
1968 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1969 {
1971 return;
1972
1973 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1974
1975 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1976 return;
1977
1979 string part_name = zone;
1980 part_name.ToLower();
1981
1983 {
1985
1986 if (construction_part && construction.IsPartConstructed(part_name))
1987 {
1988 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1989 construction.DestroyConnectedParts(part_name);
1990 }
1991
1992 //barbed wire handling (hack-ish)
1993 if (part_name.Contains("barbed"))
1994 {
1995 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1996 if (barbed_wire)
1997 barbed_wire.SetMountedState(false);
1998 }
1999 }
2000 }
2001
2002 override void EEOnAfterLoad()
2003 {
2005 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2006
2007 super.EEOnAfterLoad();
2008 }
2009
2010 override void EEInit()
2011 {
2012 super.EEInit();
2013
2014 // init visuals and physics
2015 InitBaseState();
2016
2017 //debug
2018#ifdef DEVELOPER
2020#endif
2021 }
2022
2023 override void EEItemAttached(EntityAI item, string slot_name)
2024 {
2025 super.EEItemAttached(item, slot_name);
2026
2028 UpdateVisuals();
2030 }
2031
2032 override void EEItemDetached(EntityAI item, string slot_name)
2033 {
2034 super.EEItemDetached(item, slot_name);
2035
2036 UpdateVisuals();
2038 }
2039
2040 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2041 {
2043 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2044
2047 }
2048
2049 //ignore out of reach condition
2050 override bool IgnoreOutOfReachCondition()
2051 {
2052 return true;
2053 }
2054
2055 //CONSTRUCTION EVENTS
2056 //Build
2057 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2058 {
2060
2061 //check base state
2062 if (construtionPart.IsBase())
2063 {
2064 SetBaseState(true);
2065
2066 //spawn kit
2068 }
2069
2070 //register constructed parts for synchronization
2072
2073 //register action that was performed on part
2075
2076 //synchronize
2078
2079 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2080
2081 UpdateNavmesh();
2082
2083 //update visuals
2084 UpdateVisuals();
2085
2086 //reset action sync data
2087 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2088 }
2089
2090 void OnPartBuiltClient(string part_name, int action_id)
2091 {
2092 //play sound
2094 }
2095
2096 //Dismantle
2098 {
2099 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2101
2102 //register constructed parts for synchronization
2104
2105 //register action that was performed on part
2107
2108 //synchronize
2110
2111 // server part of sync, client will be synced from SetPartsFromSyncData
2113
2114 UpdateNavmesh();
2115
2116 //update visuals
2117 UpdateVisuals();
2118
2119 //reset action sync data
2120 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2121
2122 //check base state
2123 if (construtionPart.IsBase())
2124 {
2125 //Destroy construction
2126 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2127 }
2128 }
2129
2131 {
2132 //play sound
2134 }
2135
2136 //Destroy
2138 {
2139 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2141
2142 //register constructed parts for synchronization
2144
2145 //register action that was performed on part
2147
2148 //synchronize
2150
2151 // server part of sync, client will be synced from SetPartsFromSyncData
2153
2154 UpdateNavmesh();
2155
2156 //update visuals
2157 UpdateVisuals();
2158
2159 //reset action sync data
2160 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2161
2162 //check base state
2163 if (construtionPart.IsBase())
2164 {
2165 //Destroy construction
2166 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2167 }
2168 }
2169
2170 void OnPartDestroyedClient(string part_name, int action_id)
2171 {
2172 //play sound
2174 }
2175
2176 // --- UPDATE
2177 void InitBaseState()
2178 {
2179 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2180
2181 InitVisuals();
2182 UpdateNavmesh(); //regenerate navmesh
2183 GetConstruction().InitBaseState();
2184 }
2185
2186 void InitVisuals()
2187 {
2188 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2189 //check base
2190 if (!HasBase())
2191 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2192 else
2193 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2194
2195 GetConstruction().UpdateVisuals();
2196 }
2197
2198 void UpdateVisuals()
2199 {
2201
2203 foreach (string slotName : attachmentSlots)
2205
2206 //check base
2207 if (!HasBase())
2208 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2209 else
2210 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2211
2212 GetConstruction().UpdateVisuals();
2213 }
2214
2216 {
2217 string slotNameMounted = slot_name + "_Mounted";
2218 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2219
2220 if (attachment)
2221 {
2222 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2223 if (barbedWire && barbedWire.IsMounted())
2225 else
2227
2228 if (is_locked)
2229 {
2230 SetAnimationPhase(slotNameMounted, 0);
2231 SetAnimationPhase(slot_name, 1);
2232 }
2233 else
2234 {
2235 SetAnimationPhase(slotNameMounted, 1);
2236 SetAnimationPhase(slot_name, 0);
2237 }
2238 }
2239 else
2240 {
2241 SetAnimationPhase(slotNameMounted, 1);
2242 SetAnimationPhase(slot_name, 1);
2243
2245 }
2246 }
2247
2248 // avoid calling this function on frequent occasions, it's a massive performance hit
2249 void UpdatePhysics()
2250 {
2252 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2253
2256
2258 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2259
2260 foreach (string slotName : attachmentSlots)
2262
2263 //check base
2264 if (!HasBase())
2265 {
2267 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2268
2269 AddProxyPhysics(ANIMATION_DEPLOYED);
2270 }
2271 else
2272 {
2274 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2275
2276 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2277 }
2278
2279 GetConstruction().UpdatePhysics();
2280 UpdateNavmesh();
2281 }
2282
2284 {
2285 //checks for invalid appends; hotfix
2286 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2287 return;
2288 //----------------------------------
2289 string slot_name_mounted = slot_name + "_Mounted";
2290 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2291
2292 //remove proxy physics
2293 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2294 RemoveProxyPhysics(slot_name_mounted);
2295 RemoveProxyPhysics(slot_name);
2296
2297 if (attachment)
2298 {
2299 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2300 if (is_locked)
2301 {
2302 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2303 AddProxyPhysics(slot_name_mounted);
2304 }
2305 else
2306 {
2307 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2308 AddProxyPhysics(slot_name);
2309 }
2310 }
2311 }
2312
2313 protected void UpdateNavmesh()
2314 {
2315 SetAffectPathgraph(true, false);
2316 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2317 }
2318
2319 override bool CanUseConstruction()
2320 {
2321 return true;
2322 }
2323
2324 override bool CanUseConstructionBuild()
2325 {
2326 return true;
2327 }
2328
2330 {
2331 if (attachment)
2332 {
2334 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2335
2336 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2337 }
2338
2339 return false;
2340 }
2341
2342 protected bool IsAttachmentSlotLocked(string slot_name)
2343 {
2344 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2345 }
2346
2347 //--- ATTACHMENT SLOTS
2349 {
2350 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2351 if (GetGame().ConfigIsExisting(config_path))
2352 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2353 }
2354
2356 {
2357 return true;
2358 }
2359
2360 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2361 {
2362 return true;
2363 }
2364
2365 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2366 {
2367 return true;
2368 }
2369
2370 // --- INIT
2371 void ConstructionInit()
2372 {
2373 if (!m_Construction)
2374 m_Construction = new Construction(this);
2375
2376 GetConstruction().Init();
2377 }
2378
2380 {
2381 return m_Construction;
2382 }
2383
2384 //--- INVENTORY/ATTACHMENTS CONDITIONS
2385 //attachments
2387 {
2388 return super.CanReceiveAttachment(attachment, slotId);
2389 }
2390
2392 {
2393 int attachment_count = GetInventory().AttachmentCount();
2394 if (attachment_count > 0)
2395 {
2396 if (HasBase() && attachment_count == 1)
2397 return false;
2398
2399 return true;
2400 }
2401
2402 return false;
2403 }
2404
2405 override bool ShowZonesHealth()
2406 {
2407 return true;
2408 }
2409
2410 //this into/outo parent.Cargo
2411 override bool CanPutInCargo(EntityAI parent)
2412 {
2413 return false;
2414 }
2415
2416 override bool CanRemoveFromCargo(EntityAI parent)
2417 {
2418 return false;
2419 }
2420
2421 //hands
2422 override bool CanPutIntoHands(EntityAI parent)
2423 {
2424 return false;
2425 }
2426
2427 //--- ACTION CONDITIONS
2428 //direction
2429 override bool IsFacingPlayer(PlayerBase player, string selection)
2430 {
2431 return true;
2432 }
2433
2434 override bool IsPlayerInside(PlayerBase player, string selection)
2435 {
2436 return true;
2437 }
2438
2441 {
2442 return false;
2443 }
2444
2445 //camera direction check
2446 bool IsFacingCamera(string selection)
2447 {
2448 return true;
2449 }
2450
2451 //roof check
2453 {
2454 return false;
2455 }
2456
2457 //selection->player distance check
2458 bool HasProperDistance(string selection, PlayerBase player)
2459 {
2460 return true;
2461 }
2462
2463 //folding
2465 {
2466 if (HasBase() || GetInventory().AttachmentCount() > 0)
2467 return false;
2468
2469 return true;
2470 }
2471
2473 {
2476
2477 return item;
2478 }
2479
2480 //Damage triggers (barbed wire)
2481 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2482 {
2483 if (GetGame() && GetGame().IsServer())
2484 {
2485 //destroy area damage if some already exists
2487
2488 //create new area damage
2490 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2491
2492 vector min_max[2];
2493 if (MemoryPointExists(slot_name + "_min"))
2494 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2495 if (MemoryPointExists(slot_name + "_max"))
2496 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2497
2498 //get proper trigger extents (min<max)
2499 vector extents[2];
2500 GetConstruction().GetTriggerExtents(min_max, extents);
2501
2502 //get box center
2503 vector center;
2504 center = GetConstruction().GetBoxCenter(min_max);
2505 center = ModelToWorld(center);
2506
2507 //rotate center if needed
2510
2511 areaDamage.SetExtents(extents[0], extents[1]);
2512 areaDamage.SetAreaPosition(center);
2513 areaDamage.SetAreaOrientation(orientation);
2514 areaDamage.SetLoopInterval(1.0);
2515 areaDamage.SetDeferDuration(0.2);
2516 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2517 areaDamage.SetAmmoName("BarbedWireHit");
2518 areaDamage.Spawn();
2519
2521 }
2522 }
2523
2525 {
2526 if (angle_deg != 0)
2527 {
2528 //orientation
2530
2531 //center
2533 if (MemoryPointExists("rotate_axis"))
2534 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2537 center[0] = r_center_x;
2538 center[2] = r_center_z;
2539 }
2540 }
2541
2542 void DestroyAreaDamage(string slot_name)
2543 {
2544 if (GetGame() && GetGame().IsServer())
2545 {
2548 {
2549 if (areaDamage)
2550 areaDamage.Destroy();
2551
2553 }
2554 }
2555 }
2556
2557 override bool IsIgnoredByConstruction()
2558 {
2559 return true;
2560 }
2561
2562 //================================================================
2563 // SOUNDS
2564 //================================================================
2565 protected void SoundBuildStart(string part_name)
2566 {
2567 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2568 }
2569
2570 protected void SoundDismantleStart(string part_name)
2571 {
2572 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2573 }
2574
2575 protected void SoundDestroyStart(string part_name)
2576 {
2577 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2578 }
2579
2580 protected string GetBuildSoundByMaterial(string part_name)
2581 {
2583
2584 switch (material_type)
2585 {
2586 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2587 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2588 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2589 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2590 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2591 }
2592
2593 return "";
2594 }
2595
2596 protected string GetDismantleSoundByMaterial(string part_name)
2597 {
2599
2600 switch (material_type)
2601 {
2602 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2603 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2604 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2605 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2606 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2607 }
2608
2609 return "";
2610 }
2611
2612 //misc
2614 {
2615 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2616 {
2617 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2619 SetHealth(slot_name, "Health", item.GetHealth());
2620 }
2621 }
2622
2623 override int GetDamageSystemVersionChange()
2624 {
2625 return 111;
2626 }
2627
2628 override void SetActions()
2629 {
2630 super.SetActions();
2631
2633 //AddAction(ActionTakeHybridAttachment);
2634 //AddAction(ActionTakeHybridAttachmentToHands);
2637 }
2638
2639 //================================================================
2640 // DEBUG
2641 //================================================================
2642 protected void DebugCustomState()
2643 {
2644 }
2645
2648 {
2649 return null;
2650 }
2651
2652 override void OnDebugSpawn()
2653 {
2654 FullyBuild();
2655 }
2656
2657 void FullyBuild()
2658 {
2660 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2661
2662 Man p;
2663
2664#ifdef SERVER
2666 GetGame().GetWorld().GetPlayerList(players);
2667 if (players.Count())
2668 p = players[0];
2669#else
2670 p = GetGame().GetPlayer();
2671#endif
2672
2673 foreach (ConstructionPart part : parts)
2674 {
2675 bool excluded = false;
2676 string partName = part.GetPartName();
2677 if (excludes)
2678 {
2679 foreach (string exclude : excludes)
2680 {
2681 if (partName.Contains(exclude))
2682 {
2683 excluded = true;
2684 break;
2685 }
2686 }
2687 }
2688
2689 if (!excluded)
2691 }
2692
2693 GetConstruction().UpdateVisuals();
2694 }
2695}
2696
2697void bsbDebugPrint(string s)
2698{
2699#ifdef BSB_DEBUG
2700 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2701#else
2702 //Print("" + s); // comment/uncomment to hide/see debug logs
2703#endif
2704}
2705void bsbDebugSpam(string s)
2706{
2707#ifdef BSB_DEBUG_SPAM
2708 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2709#else
2710 //Print("" + s); // comment/uncomment to hide/see debug logs
2711#endif
2712}

◆ DebugCustomState()

void bsbDebugPrint::DebugCustomState ( )
protected

Definition at line 2294 of file BaseBuildingBase.c.

2296{
2297 const string ANIMATION_DEPLOYED = "Deployed";
2298
2299 float m_ConstructionKitHealth; //stored health value for used construction kit
2300
2302
2303 bool m_HasBase;
2304 //variables for synchronization of base building parts (2x31 is the current limit)
2305 int m_SyncParts01; //synchronization for already built parts (31 parts)
2306 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2307 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2308 int m_InteractedPartId; //construction part id that an action was performed on
2309 int m_PerformedActionId; //action id that was performed on a construction part
2310
2311 //Sounds
2312 //build
2313 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2314 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2315 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2316 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2317 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2318 //dismantle
2319 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2320 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2321 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2322 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2323 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2324
2325 protected EffectSound m_Sound;
2326
2330
2331 // Constructor
2332 void BaseBuildingBase()
2333 {
2335
2336 //synchronized variables
2337 RegisterNetSyncVariableInt("m_SyncParts01");
2338 RegisterNetSyncVariableInt("m_SyncParts02");
2339 RegisterNetSyncVariableInt("m_SyncParts03");
2340 RegisterNetSyncVariableInt("m_InteractedPartId");
2341 RegisterNetSyncVariableInt("m_PerformedActionId");
2342 RegisterNetSyncVariableBool("m_HasBase");
2343
2344 //Construction init
2346
2347 if (ConfigIsExisting("hybridAttachments"))
2348 {
2350 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2351 }
2352 if (ConfigIsExisting("mountables"))
2353 {
2355 ConfigGetTextArray("mountables", m_Mountables);
2356 }
2357
2358 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2359 }
2360
2361 override void EEDelete(EntityAI parent)
2362 {
2363 super.EEDelete(parent);
2364
2365 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2367
2368 }
2369
2370 override string GetInvulnerabilityTypeString()
2371 {
2372 return "disableBaseDamage";
2373 }
2374
2375 override bool CanObstruct()
2376 {
2377 return true;
2378 }
2379
2380 override int GetHideIconMask()
2381 {
2382 return EInventoryIconVisibility.HIDE_VICINITY;
2383 }
2384
2385 // --- SYNCHRONIZATION
2387 {
2388 if (GetGame().IsServer())
2389 SetSynchDirty();
2390 }
2391
2392 override void OnVariablesSynchronized()
2393 {
2394 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2395 super.OnVariablesSynchronized();
2396
2397 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2398 }
2399
2400 protected void OnSynchronizedClient()
2401 {
2402 //update parts
2404
2405 //update action on part
2407
2408 //update visuals (client)
2409 UpdateVisuals();
2410 }
2411
2412 //parts synchronization
2414 {
2415 //part_id must starts from index = 1
2416 int offset;
2417 int mask;
2418
2419 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2420 {
2421 offset = part_id - 1;
2422 mask = 1 << offset;
2423
2425 }
2426 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2427 {
2428 offset = (part_id % 32);
2429 mask = 1 << offset;
2430
2432 }
2433 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2434 {
2435 offset = (part_id % 63);
2436 mask = 1 << offset;
2437
2439 }
2440 }
2441
2443 {
2444 //part_id must starts from index = 1
2445 int offset;
2446 int mask;
2447
2448 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2449 {
2450 offset = part_id - 1;
2451 mask = 1 << offset;
2452
2454 }
2455 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2456 {
2457 offset = (part_id % 32);
2458 mask = 1 << offset;
2459
2461 }
2462 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2463 {
2464 offset = (part_id % 63);
2465 mask = 1 << offset;
2466
2468 }
2469 }
2470
2472 {
2473 //part_id must starts from index = 1
2474 int offset;
2475 int mask;
2476
2477 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2478 {
2479 offset = part_id - 1;
2480 mask = 1 << offset;
2481
2482 if ((m_SyncParts01 & mask) > 0)
2483 return true;
2484 }
2485 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2486 {
2487 offset = (part_id % 32);
2488 mask = 1 << offset;
2489
2490 if ((m_SyncParts02 & mask) > 0)
2491 return true;
2492 }
2493 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2494 {
2495 offset = (part_id % 63);
2496 mask = 1 << offset;
2497
2498 if ((m_SyncParts03 & mask) > 0)
2499 return true;
2500 }
2501
2502 return false;
2503 }
2504
2505 protected void RegisterActionForSync(int part_id, int action_id)
2506 {
2509 }
2510
2511 protected void ResetActionSyncData()
2512 {
2513 //reset data
2514 m_InteractedPartId = -1;
2516 }
2517
2518 protected void SetActionFromSyncData()
2519 {
2520 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2521 {
2524
2525 switch (build_action_id)
2526 {
2530 }
2531 }
2532 }
2533 //------
2534
2536 {
2537 string key = part.m_PartName;
2538 bool is_base = part.IsBase();
2540 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2542 {
2543 if (!part.IsBuilt())
2544 {
2545 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2546 GetConstruction().AddToConstructedParts(key);
2547 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2548
2549 if (is_base)
2550 {
2552 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2553 }
2554 }
2555 }
2556 else
2557 {
2558 if (part.IsBuilt())
2559 {
2560 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2561 GetConstruction().RemoveFromConstructedParts(key);
2562 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2563
2564 if (is_base)
2565 {
2567 AddProxyPhysics(ANIMATION_DEPLOYED);
2568 }
2569 }
2570 }
2571
2572 //check slot lock for material attachments
2573 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2574 }
2575
2576 //set construction parts based on synchronized data
2578 {
2581
2582 for (int i = 0; i < construction_parts.Count(); ++i)
2583 {
2584 string key = construction_parts.GetKey(i);
2587 }
2588
2589 //regenerate navmesh
2590 UpdateNavmesh();
2591 }
2592
2594 {
2597
2598 for (int i = 0; i < construction_parts.Count(); ++i)
2599 {
2600 string key = construction_parts.GetKey(i);
2602
2603 if (value.GetId() == id)
2604 return value;
2605 }
2606
2607 return NULL;
2608 }
2609 //
2610
2611 //Base
2612 bool HasBase()
2613 {
2614 return m_HasBase;
2615 }
2616
2617 void SetBaseState(bool has_base)
2618 {
2620 }
2621
2622 override bool IsDeployable()
2623 {
2624 return true;
2625 }
2626
2627 bool IsOpened()
2628 {
2629 return false;
2630 }
2631
2632 //--- CONSTRUCTION KIT
2634 {
2638
2639 return construction_kit;
2640 }
2641
2643 {
2644 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2647 }
2648
2649 protected vector GetKitSpawnPosition()
2650 {
2651 return GetPosition();
2652 }
2653
2654 protected string GetConstructionKitType()
2655 {
2656 return "";
2657 }
2658
2660 {
2662 GetGame().ObjectDelete(construction_kit);
2663 }
2664
2665 //--- CONSTRUCTION
2666 void DestroyConstruction()
2667 {
2668 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2669 GetGame().ObjectDelete(this);
2670 }
2671
2672 // --- EVENTS
2673 override void OnStoreSave(ParamsWriteContext ctx)
2674 {
2675 super.OnStoreSave(ctx);
2676
2677 //sync parts 01
2678 ctx.Write(m_SyncParts01);
2679 ctx.Write(m_SyncParts02);
2680 ctx.Write(m_SyncParts03);
2681
2682 ctx.Write(m_HasBase);
2683 }
2684
2685 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2686 {
2687 if (!super.OnStoreLoad(ctx, version))
2688 return false;
2689
2690 //--- Base building data ---
2691 //Restore synced parts data
2692 if (!ctx.Read(m_SyncParts01))
2693 {
2694 m_SyncParts01 = 0; //set default
2695 return false;
2696 }
2697 if (!ctx.Read(m_SyncParts02))
2698 {
2699 m_SyncParts02 = 0; //set default
2700 return false;
2701 }
2702 if (!ctx.Read(m_SyncParts03))
2703 {
2704 m_SyncParts03 = 0; //set default
2705 return false;
2706 }
2707
2708 //has base
2709 if (!ctx.Read(m_HasBase))
2710 {
2711 m_HasBase = false;
2712 return false;
2713 }
2714 //---
2715
2716 return true;
2717 }
2718
2719 override void AfterStoreLoad()
2720 {
2721 super.AfterStoreLoad();
2722
2725 }
2726
2728 {
2729 //update server data
2731
2732 //set base state
2733 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2734 SetBaseState(construction_part.IsBuilt()) ;
2735
2736 //synchronize after load
2738 }
2739
2740 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2741 {
2743 return;
2744
2745 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2746
2747 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2748 return;
2749
2751 string part_name = zone;
2752 part_name.ToLower();
2753
2755 {
2757
2758 if (construction_part && construction.IsPartConstructed(part_name))
2759 {
2760 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2761 construction.DestroyConnectedParts(part_name);
2762 }
2763
2764 //barbed wire handling (hack-ish)
2765 if (part_name.Contains("barbed"))
2766 {
2767 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2768 if (barbed_wire)
2769 barbed_wire.SetMountedState(false);
2770 }
2771 }
2772 }
2773
2774 override void EEOnAfterLoad()
2775 {
2777 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2778
2779 super.EEOnAfterLoad();
2780 }
2781
2782 override void EEInit()
2783 {
2784 super.EEInit();
2785
2786 // init visuals and physics
2787 InitBaseState();
2788
2789 //debug
2790#ifdef DEVELOPER
2792#endif
2793 }
2794
2795 override void EEItemAttached(EntityAI item, string slot_name)
2796 {
2797 super.EEItemAttached(item, slot_name);
2798
2800 UpdateVisuals();
2802 }
2803
2804 override void EEItemDetached(EntityAI item, string slot_name)
2805 {
2806 super.EEItemDetached(item, slot_name);
2807
2808 UpdateVisuals();
2810 }
2811
2812 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2813 {
2815 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2816
2819 }
2820
2821 //ignore out of reach condition
2822 override bool IgnoreOutOfReachCondition()
2823 {
2824 return true;
2825 }
2826
2827 //CONSTRUCTION EVENTS
2828 //Build
2829 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2830 {
2832
2833 //check base state
2834 if (construtionPart.IsBase())
2835 {
2836 SetBaseState(true);
2837
2838 //spawn kit
2840 }
2841
2842 //register constructed parts for synchronization
2844
2845 //register action that was performed on part
2847
2848 //synchronize
2850
2851 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2852
2853 UpdateNavmesh();
2854
2855 //update visuals
2856 UpdateVisuals();
2857
2858 //reset action sync data
2859 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2860 }
2861
2862 void OnPartBuiltClient(string part_name, int action_id)
2863 {
2864 //play sound
2866 }
2867
2868 //Dismantle
2870 {
2871 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2873
2874 //register constructed parts for synchronization
2876
2877 //register action that was performed on part
2879
2880 //synchronize
2882
2883 // server part of sync, client will be synced from SetPartsFromSyncData
2885
2886 UpdateNavmesh();
2887
2888 //update visuals
2889 UpdateVisuals();
2890
2891 //reset action sync data
2892 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2893
2894 //check base state
2895 if (construtionPart.IsBase())
2896 {
2897 //Destroy construction
2898 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2899 }
2900 }
2901
2903 {
2904 //play sound
2906 }
2907
2908 //Destroy
2910 {
2911 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2913
2914 //register constructed parts for synchronization
2916
2917 //register action that was performed on part
2919
2920 //synchronize
2922
2923 // server part of sync, client will be synced from SetPartsFromSyncData
2925
2926 UpdateNavmesh();
2927
2928 //update visuals
2929 UpdateVisuals();
2930
2931 //reset action sync data
2932 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2933
2934 //check base state
2935 if (construtionPart.IsBase())
2936 {
2937 //Destroy construction
2938 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2939 }
2940 }
2941
2942 void OnPartDestroyedClient(string part_name, int action_id)
2943 {
2944 //play sound
2946 }
2947
2948 // --- UPDATE
2949 void InitBaseState()
2950 {
2951 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2952
2953 InitVisuals();
2954 UpdateNavmesh(); //regenerate navmesh
2955 GetConstruction().InitBaseState();
2956 }
2957
2958 void InitVisuals()
2959 {
2960 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2961 //check base
2962 if (!HasBase())
2963 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2964 else
2965 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2966
2967 GetConstruction().UpdateVisuals();
2968 }
2969
2970 void UpdateVisuals()
2971 {
2973
2975 foreach (string slotName : attachmentSlots)
2977
2978 //check base
2979 if (!HasBase())
2980 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2981 else
2982 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2983
2984 GetConstruction().UpdateVisuals();
2985 }
2986
2988 {
2989 string slotNameMounted = slot_name + "_Mounted";
2990 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2991
2992 if (attachment)
2993 {
2994 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2995 if (barbedWire && barbedWire.IsMounted())
2997 else
2999
3000 if (is_locked)
3001 {
3002 SetAnimationPhase(slotNameMounted, 0);
3003 SetAnimationPhase(slot_name, 1);
3004 }
3005 else
3006 {
3007 SetAnimationPhase(slotNameMounted, 1);
3008 SetAnimationPhase(slot_name, 0);
3009 }
3010 }
3011 else
3012 {
3013 SetAnimationPhase(slotNameMounted, 1);
3014 SetAnimationPhase(slot_name, 1);
3015
3017 }
3018 }
3019
3020 // avoid calling this function on frequent occasions, it's a massive performance hit
3021 void UpdatePhysics()
3022 {
3024 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
3025
3028
3030 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
3031
3032 foreach (string slotName : attachmentSlots)
3034
3035 //check base
3036 if (!HasBase())
3037 {
3039 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
3040
3041 AddProxyPhysics(ANIMATION_DEPLOYED);
3042 }
3043 else
3044 {
3046 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
3047
3048 RemoveProxyPhysics(ANIMATION_DEPLOYED);
3049 }
3050
3051 GetConstruction().UpdatePhysics();
3052 UpdateNavmesh();
3053 }
3054
3056 {
3057 //checks for invalid appends; hotfix
3058 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
3059 return;
3060 //----------------------------------
3061 string slot_name_mounted = slot_name + "_Mounted";
3062 EntityAI attachment = FindAttachmentBySlotName(slot_name);
3063
3064 //remove proxy physics
3065 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
3066 RemoveProxyPhysics(slot_name_mounted);
3067 RemoveProxyPhysics(slot_name);
3068
3069 if (attachment)
3070 {
3071 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
3072 if (is_locked)
3073 {
3074 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
3075 AddProxyPhysics(slot_name_mounted);
3076 }
3077 else
3078 {
3079 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
3080 AddProxyPhysics(slot_name);
3081 }
3082 }
3083 }
3084
3085 protected void UpdateNavmesh()
3086 {
3087 SetAffectPathgraph(true, false);
3088 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
3089 }
3090
3091 override bool CanUseConstruction()
3092 {
3093 return true;
3094 }
3095
3096 override bool CanUseConstructionBuild()
3097 {
3098 return true;
3099 }
3100
3102 {
3103 if (attachment)
3104 {
3106 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
3107
3108 return GetInventory().GetSlotLock(inventory_location.GetSlot());
3109 }
3110
3111 return false;
3112 }
3113
3114 protected bool IsAttachmentSlotLocked(string slot_name)
3115 {
3116 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
3117 }
3118
3119 //--- ATTACHMENT SLOTS
3121 {
3122 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
3123 if (GetGame().ConfigIsExisting(config_path))
3124 GetGame().ConfigGetTextArray(config_path, attachment_slots);
3125 }
3126
3128 {
3129 return true;
3130 }
3131
3132 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
3133 {
3134 return true;
3135 }
3136
3137 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
3138 {
3139 return true;
3140 }
3141
3142 // --- INIT
3143 void ConstructionInit()
3144 {
3145 if (!m_Construction)
3146 m_Construction = new Construction(this);
3147
3148 GetConstruction().Init();
3149 }
3150
3152 {
3153 return m_Construction;
3154 }
3155
3156 //--- INVENTORY/ATTACHMENTS CONDITIONS
3157 //attachments
3159 {
3160 return super.CanReceiveAttachment(attachment, slotId);
3161 }
3162
3164 {
3165 int attachment_count = GetInventory().AttachmentCount();
3166 if (attachment_count > 0)
3167 {
3168 if (HasBase() && attachment_count == 1)
3169 return false;
3170
3171 return true;
3172 }
3173
3174 return false;
3175 }
3176
3177 override bool ShowZonesHealth()
3178 {
3179 return true;
3180 }
3181
3182 //this into/outo parent.Cargo
3183 override bool CanPutInCargo(EntityAI parent)
3184 {
3185 return false;
3186 }
3187
3188 override bool CanRemoveFromCargo(EntityAI parent)
3189 {
3190 return false;
3191 }
3192
3193 //hands
3194 override bool CanPutIntoHands(EntityAI parent)
3195 {
3196 return false;
3197 }
3198
3199 //--- ACTION CONDITIONS
3200 //direction
3201 override bool IsFacingPlayer(PlayerBase player, string selection)
3202 {
3203 return true;
3204 }
3205
3206 override bool IsPlayerInside(PlayerBase player, string selection)
3207 {
3208 return true;
3209 }
3210
3213 {
3214 return false;
3215 }
3216
3217 //camera direction check
3218 bool IsFacingCamera(string selection)
3219 {
3220 return true;
3221 }
3222
3223 //roof check
3225 {
3226 return false;
3227 }
3228
3229 //selection->player distance check
3230 bool HasProperDistance(string selection, PlayerBase player)
3231 {
3232 return true;
3233 }
3234
3235 //folding
3237 {
3238 if (HasBase() || GetInventory().AttachmentCount() > 0)
3239 return false;
3240
3241 return true;
3242 }
3243
3245 {
3248
3249 return item;
3250 }
3251
3252 //Damage triggers (barbed wire)
3253 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3254 {
3255 if (GetGame() && GetGame().IsServer())
3256 {
3257 //destroy area damage if some already exists
3259
3260 //create new area damage
3262 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3263
3264 vector min_max[2];
3265 if (MemoryPointExists(slot_name + "_min"))
3266 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3267 if (MemoryPointExists(slot_name + "_max"))
3268 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3269
3270 //get proper trigger extents (min<max)
3271 vector extents[2];
3272 GetConstruction().GetTriggerExtents(min_max, extents);
3273
3274 //get box center
3275 vector center;
3276 center = GetConstruction().GetBoxCenter(min_max);
3277 center = ModelToWorld(center);
3278
3279 //rotate center if needed
3282
3283 areaDamage.SetExtents(extents[0], extents[1]);
3284 areaDamage.SetAreaPosition(center);
3285 areaDamage.SetAreaOrientation(orientation);
3286 areaDamage.SetLoopInterval(1.0);
3287 areaDamage.SetDeferDuration(0.2);
3288 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3289 areaDamage.SetAmmoName("BarbedWireHit");
3290 areaDamage.Spawn();
3291
3293 }
3294 }
3295
3297 {
3298 if (angle_deg != 0)
3299 {
3300 //orientation
3302
3303 //center
3305 if (MemoryPointExists("rotate_axis"))
3306 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3309 center[0] = r_center_x;
3310 center[2] = r_center_z;
3311 }
3312 }
3313
3314 void DestroyAreaDamage(string slot_name)
3315 {
3316 if (GetGame() && GetGame().IsServer())
3317 {
3320 {
3321 if (areaDamage)
3322 areaDamage.Destroy();
3323
3325 }
3326 }
3327 }
3328
3329 override bool IsIgnoredByConstruction()
3330 {
3331 return true;
3332 }
3333
3334 //================================================================
3335 // SOUNDS
3336 //================================================================
3337 protected void SoundBuildStart(string part_name)
3338 {
3339 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3340 }
3341
3342 protected void SoundDismantleStart(string part_name)
3343 {
3344 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3345 }
3346
3347 protected void SoundDestroyStart(string part_name)
3348 {
3349 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3350 }
3351
3352 protected string GetBuildSoundByMaterial(string part_name)
3353 {
3355
3356 switch (material_type)
3357 {
3358 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3359 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3360 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3361 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3362 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3363 }
3364
3365 return "";
3366 }
3367
3368 protected string GetDismantleSoundByMaterial(string part_name)
3369 {
3371
3372 switch (material_type)
3373 {
3374 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3375 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3376 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3377 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3378 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3379 }
3380
3381 return "";
3382 }
3383
3384 //misc
3386 {
3387 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3388 {
3389 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3391 SetHealth(slot_name, "Health", item.GetHealth());
3392 }
3393 }
3394
3395 override int GetDamageSystemVersionChange()
3396 {
3397 return 111;
3398 }
3399
3400 override void SetActions()
3401 {
3402 super.SetActions();
3403
3405 //AddAction(ActionTakeHybridAttachment);
3406 //AddAction(ActionTakeHybridAttachmentToHands);
3409 }
3410
3411 //================================================================
3412 // DEBUG
3413 //================================================================
3414 protected void DebugCustomState()
3415 {
3416 }
3417
3420 {
3421 return null;
3422 }
3423
3424 override void OnDebugSpawn()
3425 {
3426 FullyBuild();
3427 }
3428
3429 void FullyBuild()
3430 {
3432 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3433
3434 Man p;
3435
3436#ifdef SERVER
3438 GetGame().GetWorld().GetPlayerList(players);
3439 if (players.Count())
3440 p = players[0];
3441#else
3442 p = GetGame().GetPlayer();
3443#endif
3444
3445 foreach (ConstructionPart part : parts)
3446 {
3447 bool excluded = false;
3448 string partName = part.GetPartName();
3449 if (excludes)
3450 {
3451 foreach (string exclude : excludes)
3452 {
3453 if (partName.Contains(exclude))
3454 {
3455 excluded = true;
3456 break;
3457 }
3458 }
3459 }
3460
3461 if (!excluded)
3463 }
3464
3465 GetConstruction().UpdateVisuals();
3466 }
3467}
3468
3469void bsbDebugPrint(string s)
3470{
3471#ifdef BSB_DEBUG
3472 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3473#else
3474 //Print("" + s); // comment/uncomment to hide/see debug logs
3475#endif
3476}
3477void bsbDebugSpam(string s)
3478{
3479#ifdef BSB_DEBUG_SPAM
3480 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3481#else
3482 //Print("" + s); // comment/uncomment to hide/see debug logs
3483#endif
3484}

Referenced by ItemBase::EEInit().

◆ DestroyAreaDamage()

void bsbDebugPrint::DestroyAreaDamage ( string slot_name)
protected

Definition at line 2194 of file BaseBuildingBase.c.

2196{
2197 const string ANIMATION_DEPLOYED = "Deployed";
2198
2199 float m_ConstructionKitHealth; //stored health value for used construction kit
2200
2202
2203 bool m_HasBase;
2204 //variables for synchronization of base building parts (2x31 is the current limit)
2205 int m_SyncParts01; //synchronization for already built parts (31 parts)
2206 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2207 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2208 int m_InteractedPartId; //construction part id that an action was performed on
2209 int m_PerformedActionId; //action id that was performed on a construction part
2210
2211 //Sounds
2212 //build
2213 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2214 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2215 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2216 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2217 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2218 //dismantle
2219 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2220 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2221 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2222 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2223 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2224
2225 protected EffectSound m_Sound;
2226
2230
2231 // Constructor
2232 void BaseBuildingBase()
2233 {
2235
2236 //synchronized variables
2237 RegisterNetSyncVariableInt("m_SyncParts01");
2238 RegisterNetSyncVariableInt("m_SyncParts02");
2239 RegisterNetSyncVariableInt("m_SyncParts03");
2240 RegisterNetSyncVariableInt("m_InteractedPartId");
2241 RegisterNetSyncVariableInt("m_PerformedActionId");
2242 RegisterNetSyncVariableBool("m_HasBase");
2243
2244 //Construction init
2246
2247 if (ConfigIsExisting("hybridAttachments"))
2248 {
2250 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2251 }
2252 if (ConfigIsExisting("mountables"))
2253 {
2255 ConfigGetTextArray("mountables", m_Mountables);
2256 }
2257
2258 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2259 }
2260
2261 override void EEDelete(EntityAI parent)
2262 {
2263 super.EEDelete(parent);
2264
2265 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2267
2268 }
2269
2270 override string GetInvulnerabilityTypeString()
2271 {
2272 return "disableBaseDamage";
2273 }
2274
2275 override bool CanObstruct()
2276 {
2277 return true;
2278 }
2279
2280 override int GetHideIconMask()
2281 {
2282 return EInventoryIconVisibility.HIDE_VICINITY;
2283 }
2284
2285 // --- SYNCHRONIZATION
2287 {
2288 if (GetGame().IsServer())
2289 SetSynchDirty();
2290 }
2291
2292 override void OnVariablesSynchronized()
2293 {
2294 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2295 super.OnVariablesSynchronized();
2296
2297 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2298 }
2299
2300 protected void OnSynchronizedClient()
2301 {
2302 //update parts
2304
2305 //update action on part
2307
2308 //update visuals (client)
2309 UpdateVisuals();
2310 }
2311
2312 //parts synchronization
2314 {
2315 //part_id must starts from index = 1
2316 int offset;
2317 int mask;
2318
2319 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2320 {
2321 offset = part_id - 1;
2322 mask = 1 << offset;
2323
2325 }
2326 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2327 {
2328 offset = (part_id % 32);
2329 mask = 1 << offset;
2330
2332 }
2333 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2334 {
2335 offset = (part_id % 63);
2336 mask = 1 << offset;
2337
2339 }
2340 }
2341
2343 {
2344 //part_id must starts from index = 1
2345 int offset;
2346 int mask;
2347
2348 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2349 {
2350 offset = part_id - 1;
2351 mask = 1 << offset;
2352
2354 }
2355 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2356 {
2357 offset = (part_id % 32);
2358 mask = 1 << offset;
2359
2361 }
2362 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2363 {
2364 offset = (part_id % 63);
2365 mask = 1 << offset;
2366
2368 }
2369 }
2370
2372 {
2373 //part_id must starts from index = 1
2374 int offset;
2375 int mask;
2376
2377 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2378 {
2379 offset = part_id - 1;
2380 mask = 1 << offset;
2381
2382 if ((m_SyncParts01 & mask) > 0)
2383 return true;
2384 }
2385 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2386 {
2387 offset = (part_id % 32);
2388 mask = 1 << offset;
2389
2390 if ((m_SyncParts02 & mask) > 0)
2391 return true;
2392 }
2393 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2394 {
2395 offset = (part_id % 63);
2396 mask = 1 << offset;
2397
2398 if ((m_SyncParts03 & mask) > 0)
2399 return true;
2400 }
2401
2402 return false;
2403 }
2404
2405 protected void RegisterActionForSync(int part_id, int action_id)
2406 {
2409 }
2410
2411 protected void ResetActionSyncData()
2412 {
2413 //reset data
2414 m_InteractedPartId = -1;
2416 }
2417
2418 protected void SetActionFromSyncData()
2419 {
2420 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2421 {
2424
2425 switch (build_action_id)
2426 {
2430 }
2431 }
2432 }
2433 //------
2434
2436 {
2437 string key = part.m_PartName;
2438 bool is_base = part.IsBase();
2440 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2442 {
2443 if (!part.IsBuilt())
2444 {
2445 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2446 GetConstruction().AddToConstructedParts(key);
2447 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2448
2449 if (is_base)
2450 {
2452 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2453 }
2454 }
2455 }
2456 else
2457 {
2458 if (part.IsBuilt())
2459 {
2460 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2461 GetConstruction().RemoveFromConstructedParts(key);
2462 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2463
2464 if (is_base)
2465 {
2467 AddProxyPhysics(ANIMATION_DEPLOYED);
2468 }
2469 }
2470 }
2471
2472 //check slot lock for material attachments
2473 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2474 }
2475
2476 //set construction parts based on synchronized data
2478 {
2481
2482 for (int i = 0; i < construction_parts.Count(); ++i)
2483 {
2484 string key = construction_parts.GetKey(i);
2487 }
2488
2489 //regenerate navmesh
2490 UpdateNavmesh();
2491 }
2492
2494 {
2497
2498 for (int i = 0; i < construction_parts.Count(); ++i)
2499 {
2500 string key = construction_parts.GetKey(i);
2502
2503 if (value.GetId() == id)
2504 return value;
2505 }
2506
2507 return NULL;
2508 }
2509 //
2510
2511 //Base
2512 bool HasBase()
2513 {
2514 return m_HasBase;
2515 }
2516
2517 void SetBaseState(bool has_base)
2518 {
2520 }
2521
2522 override bool IsDeployable()
2523 {
2524 return true;
2525 }
2526
2527 bool IsOpened()
2528 {
2529 return false;
2530 }
2531
2532 //--- CONSTRUCTION KIT
2534 {
2538
2539 return construction_kit;
2540 }
2541
2543 {
2544 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2547 }
2548
2549 protected vector GetKitSpawnPosition()
2550 {
2551 return GetPosition();
2552 }
2553
2554 protected string GetConstructionKitType()
2555 {
2556 return "";
2557 }
2558
2560 {
2562 GetGame().ObjectDelete(construction_kit);
2563 }
2564
2565 //--- CONSTRUCTION
2566 void DestroyConstruction()
2567 {
2568 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2569 GetGame().ObjectDelete(this);
2570 }
2571
2572 // --- EVENTS
2573 override void OnStoreSave(ParamsWriteContext ctx)
2574 {
2575 super.OnStoreSave(ctx);
2576
2577 //sync parts 01
2578 ctx.Write(m_SyncParts01);
2579 ctx.Write(m_SyncParts02);
2580 ctx.Write(m_SyncParts03);
2581
2582 ctx.Write(m_HasBase);
2583 }
2584
2585 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2586 {
2587 if (!super.OnStoreLoad(ctx, version))
2588 return false;
2589
2590 //--- Base building data ---
2591 //Restore synced parts data
2592 if (!ctx.Read(m_SyncParts01))
2593 {
2594 m_SyncParts01 = 0; //set default
2595 return false;
2596 }
2597 if (!ctx.Read(m_SyncParts02))
2598 {
2599 m_SyncParts02 = 0; //set default
2600 return false;
2601 }
2602 if (!ctx.Read(m_SyncParts03))
2603 {
2604 m_SyncParts03 = 0; //set default
2605 return false;
2606 }
2607
2608 //has base
2609 if (!ctx.Read(m_HasBase))
2610 {
2611 m_HasBase = false;
2612 return false;
2613 }
2614 //---
2615
2616 return true;
2617 }
2618
2619 override void AfterStoreLoad()
2620 {
2621 super.AfterStoreLoad();
2622
2625 }
2626
2628 {
2629 //update server data
2631
2632 //set base state
2633 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2634 SetBaseState(construction_part.IsBuilt()) ;
2635
2636 //synchronize after load
2638 }
2639
2640 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2641 {
2643 return;
2644
2645 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2646
2647 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2648 return;
2649
2651 string part_name = zone;
2652 part_name.ToLower();
2653
2655 {
2657
2658 if (construction_part && construction.IsPartConstructed(part_name))
2659 {
2660 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2661 construction.DestroyConnectedParts(part_name);
2662 }
2663
2664 //barbed wire handling (hack-ish)
2665 if (part_name.Contains("barbed"))
2666 {
2667 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2668 if (barbed_wire)
2669 barbed_wire.SetMountedState(false);
2670 }
2671 }
2672 }
2673
2674 override void EEOnAfterLoad()
2675 {
2677 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2678
2679 super.EEOnAfterLoad();
2680 }
2681
2682 override void EEInit()
2683 {
2684 super.EEInit();
2685
2686 // init visuals and physics
2687 InitBaseState();
2688
2689 //debug
2690#ifdef DEVELOPER
2692#endif
2693 }
2694
2695 override void EEItemAttached(EntityAI item, string slot_name)
2696 {
2697 super.EEItemAttached(item, slot_name);
2698
2700 UpdateVisuals();
2702 }
2703
2704 override void EEItemDetached(EntityAI item, string slot_name)
2705 {
2706 super.EEItemDetached(item, slot_name);
2707
2708 UpdateVisuals();
2710 }
2711
2712 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2713 {
2715 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2716
2719 }
2720
2721 //ignore out of reach condition
2722 override bool IgnoreOutOfReachCondition()
2723 {
2724 return true;
2725 }
2726
2727 //CONSTRUCTION EVENTS
2728 //Build
2729 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2730 {
2732
2733 //check base state
2734 if (construtionPart.IsBase())
2735 {
2736 SetBaseState(true);
2737
2738 //spawn kit
2740 }
2741
2742 //register constructed parts for synchronization
2744
2745 //register action that was performed on part
2747
2748 //synchronize
2750
2751 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2752
2753 UpdateNavmesh();
2754
2755 //update visuals
2756 UpdateVisuals();
2757
2758 //reset action sync data
2759 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2760 }
2761
2762 void OnPartBuiltClient(string part_name, int action_id)
2763 {
2764 //play sound
2766 }
2767
2768 //Dismantle
2770 {
2771 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2773
2774 //register constructed parts for synchronization
2776
2777 //register action that was performed on part
2779
2780 //synchronize
2782
2783 // server part of sync, client will be synced from SetPartsFromSyncData
2785
2786 UpdateNavmesh();
2787
2788 //update visuals
2789 UpdateVisuals();
2790
2791 //reset action sync data
2792 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2793
2794 //check base state
2795 if (construtionPart.IsBase())
2796 {
2797 //Destroy construction
2798 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2799 }
2800 }
2801
2803 {
2804 //play sound
2806 }
2807
2808 //Destroy
2810 {
2811 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2813
2814 //register constructed parts for synchronization
2816
2817 //register action that was performed on part
2819
2820 //synchronize
2822
2823 // server part of sync, client will be synced from SetPartsFromSyncData
2825
2826 UpdateNavmesh();
2827
2828 //update visuals
2829 UpdateVisuals();
2830
2831 //reset action sync data
2832 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2833
2834 //check base state
2835 if (construtionPart.IsBase())
2836 {
2837 //Destroy construction
2838 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2839 }
2840 }
2841
2842 void OnPartDestroyedClient(string part_name, int action_id)
2843 {
2844 //play sound
2846 }
2847
2848 // --- UPDATE
2849 void InitBaseState()
2850 {
2851 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2852
2853 InitVisuals();
2854 UpdateNavmesh(); //regenerate navmesh
2855 GetConstruction().InitBaseState();
2856 }
2857
2858 void InitVisuals()
2859 {
2860 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2861 //check base
2862 if (!HasBase())
2863 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2864 else
2865 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2866
2867 GetConstruction().UpdateVisuals();
2868 }
2869
2870 void UpdateVisuals()
2871 {
2873
2875 foreach (string slotName : attachmentSlots)
2877
2878 //check base
2879 if (!HasBase())
2880 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2881 else
2882 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2883
2884 GetConstruction().UpdateVisuals();
2885 }
2886
2888 {
2889 string slotNameMounted = slot_name + "_Mounted";
2890 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2891
2892 if (attachment)
2893 {
2894 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2895 if (barbedWire && barbedWire.IsMounted())
2897 else
2899
2900 if (is_locked)
2901 {
2902 SetAnimationPhase(slotNameMounted, 0);
2903 SetAnimationPhase(slot_name, 1);
2904 }
2905 else
2906 {
2907 SetAnimationPhase(slotNameMounted, 1);
2908 SetAnimationPhase(slot_name, 0);
2909 }
2910 }
2911 else
2912 {
2913 SetAnimationPhase(slotNameMounted, 1);
2914 SetAnimationPhase(slot_name, 1);
2915
2917 }
2918 }
2919
2920 // avoid calling this function on frequent occasions, it's a massive performance hit
2921 void UpdatePhysics()
2922 {
2924 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2925
2928
2930 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2931
2932 foreach (string slotName : attachmentSlots)
2934
2935 //check base
2936 if (!HasBase())
2937 {
2939 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2940
2941 AddProxyPhysics(ANIMATION_DEPLOYED);
2942 }
2943 else
2944 {
2946 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2947
2948 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2949 }
2950
2951 GetConstruction().UpdatePhysics();
2952 UpdateNavmesh();
2953 }
2954
2956 {
2957 //checks for invalid appends; hotfix
2958 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2959 return;
2960 //----------------------------------
2961 string slot_name_mounted = slot_name + "_Mounted";
2962 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2963
2964 //remove proxy physics
2965 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2966 RemoveProxyPhysics(slot_name_mounted);
2967 RemoveProxyPhysics(slot_name);
2968
2969 if (attachment)
2970 {
2971 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2972 if (is_locked)
2973 {
2974 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2975 AddProxyPhysics(slot_name_mounted);
2976 }
2977 else
2978 {
2979 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2980 AddProxyPhysics(slot_name);
2981 }
2982 }
2983 }
2984
2985 protected void UpdateNavmesh()
2986 {
2987 SetAffectPathgraph(true, false);
2988 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2989 }
2990
2991 override bool CanUseConstruction()
2992 {
2993 return true;
2994 }
2995
2996 override bool CanUseConstructionBuild()
2997 {
2998 return true;
2999 }
3000
3002 {
3003 if (attachment)
3004 {
3006 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
3007
3008 return GetInventory().GetSlotLock(inventory_location.GetSlot());
3009 }
3010
3011 return false;
3012 }
3013
3014 protected bool IsAttachmentSlotLocked(string slot_name)
3015 {
3016 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
3017 }
3018
3019 //--- ATTACHMENT SLOTS
3021 {
3022 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
3023 if (GetGame().ConfigIsExisting(config_path))
3024 GetGame().ConfigGetTextArray(config_path, attachment_slots);
3025 }
3026
3028 {
3029 return true;
3030 }
3031
3032 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
3033 {
3034 return true;
3035 }
3036
3037 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
3038 {
3039 return true;
3040 }
3041
3042 // --- INIT
3043 void ConstructionInit()
3044 {
3045 if (!m_Construction)
3046 m_Construction = new Construction(this);
3047
3048 GetConstruction().Init();
3049 }
3050
3052 {
3053 return m_Construction;
3054 }
3055
3056 //--- INVENTORY/ATTACHMENTS CONDITIONS
3057 //attachments
3059 {
3060 return super.CanReceiveAttachment(attachment, slotId);
3061 }
3062
3064 {
3065 int attachment_count = GetInventory().AttachmentCount();
3066 if (attachment_count > 0)
3067 {
3068 if (HasBase() && attachment_count == 1)
3069 return false;
3070
3071 return true;
3072 }
3073
3074 return false;
3075 }
3076
3077 override bool ShowZonesHealth()
3078 {
3079 return true;
3080 }
3081
3082 //this into/outo parent.Cargo
3083 override bool CanPutInCargo(EntityAI parent)
3084 {
3085 return false;
3086 }
3087
3088 override bool CanRemoveFromCargo(EntityAI parent)
3089 {
3090 return false;
3091 }
3092
3093 //hands
3094 override bool CanPutIntoHands(EntityAI parent)
3095 {
3096 return false;
3097 }
3098
3099 //--- ACTION CONDITIONS
3100 //direction
3101 override bool IsFacingPlayer(PlayerBase player, string selection)
3102 {
3103 return true;
3104 }
3105
3106 override bool IsPlayerInside(PlayerBase player, string selection)
3107 {
3108 return true;
3109 }
3110
3113 {
3114 return false;
3115 }
3116
3117 //camera direction check
3118 bool IsFacingCamera(string selection)
3119 {
3120 return true;
3121 }
3122
3123 //roof check
3125 {
3126 return false;
3127 }
3128
3129 //selection->player distance check
3130 bool HasProperDistance(string selection, PlayerBase player)
3131 {
3132 return true;
3133 }
3134
3135 //folding
3137 {
3138 if (HasBase() || GetInventory().AttachmentCount() > 0)
3139 return false;
3140
3141 return true;
3142 }
3143
3145 {
3148
3149 return item;
3150 }
3151
3152 //Damage triggers (barbed wire)
3153 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3154 {
3155 if (GetGame() && GetGame().IsServer())
3156 {
3157 //destroy area damage if some already exists
3159
3160 //create new area damage
3162 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3163
3164 vector min_max[2];
3165 if (MemoryPointExists(slot_name + "_min"))
3166 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3167 if (MemoryPointExists(slot_name + "_max"))
3168 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3169
3170 //get proper trigger extents (min<max)
3171 vector extents[2];
3172 GetConstruction().GetTriggerExtents(min_max, extents);
3173
3174 //get box center
3175 vector center;
3176 center = GetConstruction().GetBoxCenter(min_max);
3177 center = ModelToWorld(center);
3178
3179 //rotate center if needed
3182
3183 areaDamage.SetExtents(extents[0], extents[1]);
3184 areaDamage.SetAreaPosition(center);
3185 areaDamage.SetAreaOrientation(orientation);
3186 areaDamage.SetLoopInterval(1.0);
3187 areaDamage.SetDeferDuration(0.2);
3188 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3189 areaDamage.SetAmmoName("BarbedWireHit");
3190 areaDamage.Spawn();
3191
3193 }
3194 }
3195
3197 {
3198 if (angle_deg != 0)
3199 {
3200 //orientation
3202
3203 //center
3205 if (MemoryPointExists("rotate_axis"))
3206 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3209 center[0] = r_center_x;
3210 center[2] = r_center_z;
3211 }
3212 }
3213
3214 void DestroyAreaDamage(string slot_name)
3215 {
3216 if (GetGame() && GetGame().IsServer())
3217 {
3220 {
3221 if (areaDamage)
3222 areaDamage.Destroy();
3223
3225 }
3226 }
3227 }
3228
3229 override bool IsIgnoredByConstruction()
3230 {
3231 return true;
3232 }
3233
3234 //================================================================
3235 // SOUNDS
3236 //================================================================
3237 protected void SoundBuildStart(string part_name)
3238 {
3239 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3240 }
3241
3242 protected void SoundDismantleStart(string part_name)
3243 {
3244 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3245 }
3246
3247 protected void SoundDestroyStart(string part_name)
3248 {
3249 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3250 }
3251
3252 protected string GetBuildSoundByMaterial(string part_name)
3253 {
3255
3256 switch (material_type)
3257 {
3258 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3259 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3260 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3261 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3262 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3263 }
3264
3265 return "";
3266 }
3267
3268 protected string GetDismantleSoundByMaterial(string part_name)
3269 {
3271
3272 switch (material_type)
3273 {
3274 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3275 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3276 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3277 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3278 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3279 }
3280
3281 return "";
3282 }
3283
3284 //misc
3286 {
3287 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3288 {
3289 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3291 SetHealth(slot_name, "Health", item.GetHealth());
3292 }
3293 }
3294
3295 override int GetDamageSystemVersionChange()
3296 {
3297 return 111;
3298 }
3299
3300 override void SetActions()
3301 {
3302 super.SetActions();
3303
3305 //AddAction(ActionTakeHybridAttachment);
3306 //AddAction(ActionTakeHybridAttachmentToHands);
3309 }
3310
3311 //================================================================
3312 // DEBUG
3313 //================================================================
3314 protected void DebugCustomState()
3315 {
3316 }
3317
3320 {
3321 return null;
3322 }
3323
3324 override void OnDebugSpawn()
3325 {
3326 FullyBuild();
3327 }
3328
3329 void FullyBuild()
3330 {
3332 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3333
3334 Man p;
3335
3336#ifdef SERVER
3338 GetGame().GetWorld().GetPlayerList(players);
3339 if (players.Count())
3340 p = players[0];
3341#else
3342 p = GetGame().GetPlayer();
3343#endif
3344
3345 foreach (ConstructionPart part : parts)
3346 {
3347 bool excluded = false;
3348 string partName = part.GetPartName();
3349 if (excludes)
3350 {
3351 foreach (string exclude : excludes)
3352 {
3353 if (partName.Contains(exclude))
3354 {
3355 excluded = true;
3356 break;
3357 }
3358 }
3359 }
3360
3361 if (!excluded)
3363 }
3364
3365 GetConstruction().UpdateVisuals();
3366 }
3367}
3368
3369void bsbDebugPrint(string s)
3370{
3371#ifdef BSB_DEBUG
3372 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3373#else
3374 //Print("" + s); // comment/uncomment to hide/see debug logs
3375#endif
3376}
3377void bsbDebugSpam(string s)
3378{
3379#ifdef BSB_DEBUG_SPAM
3380 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3381#else
3382 //Print("" + s); // comment/uncomment to hide/see debug logs
3383#endif
3384}

◆ DestroyConstruction()

void bsbDebugPrint::DestroyConstruction ( )
protected

Definition at line 1546 of file BaseBuildingBase.c.

1548{
1549 const string ANIMATION_DEPLOYED = "Deployed";
1550
1551 float m_ConstructionKitHealth; //stored health value for used construction kit
1552
1554
1555 bool m_HasBase;
1556 //variables for synchronization of base building parts (2x31 is the current limit)
1557 int m_SyncParts01; //synchronization for already built parts (31 parts)
1558 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1559 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1560 int m_InteractedPartId; //construction part id that an action was performed on
1561 int m_PerformedActionId; //action id that was performed on a construction part
1562
1563 //Sounds
1564 //build
1565 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1566 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1567 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1568 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1569 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1570 //dismantle
1571 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1572 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1573 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1574 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1575 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1576
1577 protected EffectSound m_Sound;
1578
1582
1583 // Constructor
1584 void BaseBuildingBase()
1585 {
1587
1588 //synchronized variables
1589 RegisterNetSyncVariableInt("m_SyncParts01");
1590 RegisterNetSyncVariableInt("m_SyncParts02");
1591 RegisterNetSyncVariableInt("m_SyncParts03");
1592 RegisterNetSyncVariableInt("m_InteractedPartId");
1593 RegisterNetSyncVariableInt("m_PerformedActionId");
1594 RegisterNetSyncVariableBool("m_HasBase");
1595
1596 //Construction init
1598
1599 if (ConfigIsExisting("hybridAttachments"))
1600 {
1602 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1603 }
1604 if (ConfigIsExisting("mountables"))
1605 {
1607 ConfigGetTextArray("mountables", m_Mountables);
1608 }
1609
1610 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1611 }
1612
1613 override void EEDelete(EntityAI parent)
1614 {
1615 super.EEDelete(parent);
1616
1617 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1619
1620 }
1621
1622 override string GetInvulnerabilityTypeString()
1623 {
1624 return "disableBaseDamage";
1625 }
1626
1627 override bool CanObstruct()
1628 {
1629 return true;
1630 }
1631
1632 override int GetHideIconMask()
1633 {
1634 return EInventoryIconVisibility.HIDE_VICINITY;
1635 }
1636
1637 // --- SYNCHRONIZATION
1639 {
1640 if (GetGame().IsServer())
1641 SetSynchDirty();
1642 }
1643
1644 override void OnVariablesSynchronized()
1645 {
1646 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1647 super.OnVariablesSynchronized();
1648
1649 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1650 }
1651
1652 protected void OnSynchronizedClient()
1653 {
1654 //update parts
1656
1657 //update action on part
1659
1660 //update visuals (client)
1661 UpdateVisuals();
1662 }
1663
1664 //parts synchronization
1666 {
1667 //part_id must starts from index = 1
1668 int offset;
1669 int mask;
1670
1671 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1672 {
1673 offset = part_id - 1;
1674 mask = 1 << offset;
1675
1677 }
1678 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1679 {
1680 offset = (part_id % 32);
1681 mask = 1 << offset;
1682
1684 }
1685 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1686 {
1687 offset = (part_id % 63);
1688 mask = 1 << offset;
1689
1691 }
1692 }
1693
1695 {
1696 //part_id must starts from index = 1
1697 int offset;
1698 int mask;
1699
1700 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1701 {
1702 offset = part_id - 1;
1703 mask = 1 << offset;
1704
1706 }
1707 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1708 {
1709 offset = (part_id % 32);
1710 mask = 1 << offset;
1711
1713 }
1714 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1715 {
1716 offset = (part_id % 63);
1717 mask = 1 << offset;
1718
1720 }
1721 }
1722
1724 {
1725 //part_id must starts from index = 1
1726 int offset;
1727 int mask;
1728
1729 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1730 {
1731 offset = part_id - 1;
1732 mask = 1 << offset;
1733
1734 if ((m_SyncParts01 & mask) > 0)
1735 return true;
1736 }
1737 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1738 {
1739 offset = (part_id % 32);
1740 mask = 1 << offset;
1741
1742 if ((m_SyncParts02 & mask) > 0)
1743 return true;
1744 }
1745 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1746 {
1747 offset = (part_id % 63);
1748 mask = 1 << offset;
1749
1750 if ((m_SyncParts03 & mask) > 0)
1751 return true;
1752 }
1753
1754 return false;
1755 }
1756
1757 protected void RegisterActionForSync(int part_id, int action_id)
1758 {
1761 }
1762
1763 protected void ResetActionSyncData()
1764 {
1765 //reset data
1766 m_InteractedPartId = -1;
1768 }
1769
1770 protected void SetActionFromSyncData()
1771 {
1772 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1773 {
1776
1777 switch (build_action_id)
1778 {
1782 }
1783 }
1784 }
1785 //------
1786
1788 {
1789 string key = part.m_PartName;
1790 bool is_base = part.IsBase();
1792 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1794 {
1795 if (!part.IsBuilt())
1796 {
1797 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1798 GetConstruction().AddToConstructedParts(key);
1799 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1800
1801 if (is_base)
1802 {
1804 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1805 }
1806 }
1807 }
1808 else
1809 {
1810 if (part.IsBuilt())
1811 {
1812 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1813 GetConstruction().RemoveFromConstructedParts(key);
1814 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1815
1816 if (is_base)
1817 {
1819 AddProxyPhysics(ANIMATION_DEPLOYED);
1820 }
1821 }
1822 }
1823
1824 //check slot lock for material attachments
1825 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1826 }
1827
1828 //set construction parts based on synchronized data
1830 {
1833
1834 for (int i = 0; i < construction_parts.Count(); ++i)
1835 {
1836 string key = construction_parts.GetKey(i);
1839 }
1840
1841 //regenerate navmesh
1842 UpdateNavmesh();
1843 }
1844
1846 {
1849
1850 for (int i = 0; i < construction_parts.Count(); ++i)
1851 {
1852 string key = construction_parts.GetKey(i);
1854
1855 if (value.GetId() == id)
1856 return value;
1857 }
1858
1859 return NULL;
1860 }
1861 //
1862
1863 //Base
1864 bool HasBase()
1865 {
1866 return m_HasBase;
1867 }
1868
1869 void SetBaseState(bool has_base)
1870 {
1872 }
1873
1874 override bool IsDeployable()
1875 {
1876 return true;
1877 }
1878
1879 bool IsOpened()
1880 {
1881 return false;
1882 }
1883
1884 //--- CONSTRUCTION KIT
1886 {
1890
1891 return construction_kit;
1892 }
1893
1895 {
1896 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1899 }
1900
1901 protected vector GetKitSpawnPosition()
1902 {
1903 return GetPosition();
1904 }
1905
1906 protected string GetConstructionKitType()
1907 {
1908 return "";
1909 }
1910
1912 {
1914 GetGame().ObjectDelete(construction_kit);
1915 }
1916
1917 //--- CONSTRUCTION
1918 void DestroyConstruction()
1919 {
1920 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1921 GetGame().ObjectDelete(this);
1922 }
1923
1924 // --- EVENTS
1925 override void OnStoreSave(ParamsWriteContext ctx)
1926 {
1927 super.OnStoreSave(ctx);
1928
1929 //sync parts 01
1930 ctx.Write(m_SyncParts01);
1931 ctx.Write(m_SyncParts02);
1932 ctx.Write(m_SyncParts03);
1933
1934 ctx.Write(m_HasBase);
1935 }
1936
1937 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1938 {
1939 if (!super.OnStoreLoad(ctx, version))
1940 return false;
1941
1942 //--- Base building data ---
1943 //Restore synced parts data
1944 if (!ctx.Read(m_SyncParts01))
1945 {
1946 m_SyncParts01 = 0; //set default
1947 return false;
1948 }
1949 if (!ctx.Read(m_SyncParts02))
1950 {
1951 m_SyncParts02 = 0; //set default
1952 return false;
1953 }
1954 if (!ctx.Read(m_SyncParts03))
1955 {
1956 m_SyncParts03 = 0; //set default
1957 return false;
1958 }
1959
1960 //has base
1961 if (!ctx.Read(m_HasBase))
1962 {
1963 m_HasBase = false;
1964 return false;
1965 }
1966 //---
1967
1968 return true;
1969 }
1970
1971 override void AfterStoreLoad()
1972 {
1973 super.AfterStoreLoad();
1974
1977 }
1978
1980 {
1981 //update server data
1983
1984 //set base state
1985 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1986 SetBaseState(construction_part.IsBuilt()) ;
1987
1988 //synchronize after load
1990 }
1991
1992 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1993 {
1995 return;
1996
1997 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1998
1999 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2000 return;
2001
2003 string part_name = zone;
2004 part_name.ToLower();
2005
2007 {
2009
2010 if (construction_part && construction.IsPartConstructed(part_name))
2011 {
2012 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2013 construction.DestroyConnectedParts(part_name);
2014 }
2015
2016 //barbed wire handling (hack-ish)
2017 if (part_name.Contains("barbed"))
2018 {
2019 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2020 if (barbed_wire)
2021 barbed_wire.SetMountedState(false);
2022 }
2023 }
2024 }
2025
2026 override void EEOnAfterLoad()
2027 {
2029 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2030
2031 super.EEOnAfterLoad();
2032 }
2033
2034 override void EEInit()
2035 {
2036 super.EEInit();
2037
2038 // init visuals and physics
2039 InitBaseState();
2040
2041 //debug
2042#ifdef DEVELOPER
2044#endif
2045 }
2046
2047 override void EEItemAttached(EntityAI item, string slot_name)
2048 {
2049 super.EEItemAttached(item, slot_name);
2050
2052 UpdateVisuals();
2054 }
2055
2056 override void EEItemDetached(EntityAI item, string slot_name)
2057 {
2058 super.EEItemDetached(item, slot_name);
2059
2060 UpdateVisuals();
2062 }
2063
2064 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2065 {
2067 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2068
2071 }
2072
2073 //ignore out of reach condition
2074 override bool IgnoreOutOfReachCondition()
2075 {
2076 return true;
2077 }
2078
2079 //CONSTRUCTION EVENTS
2080 //Build
2081 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2082 {
2084
2085 //check base state
2086 if (construtionPart.IsBase())
2087 {
2088 SetBaseState(true);
2089
2090 //spawn kit
2092 }
2093
2094 //register constructed parts for synchronization
2096
2097 //register action that was performed on part
2099
2100 //synchronize
2102
2103 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2104
2105 UpdateNavmesh();
2106
2107 //update visuals
2108 UpdateVisuals();
2109
2110 //reset action sync data
2111 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2112 }
2113
2114 void OnPartBuiltClient(string part_name, int action_id)
2115 {
2116 //play sound
2118 }
2119
2120 //Dismantle
2122 {
2123 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2125
2126 //register constructed parts for synchronization
2128
2129 //register action that was performed on part
2131
2132 //synchronize
2134
2135 // server part of sync, client will be synced from SetPartsFromSyncData
2137
2138 UpdateNavmesh();
2139
2140 //update visuals
2141 UpdateVisuals();
2142
2143 //reset action sync data
2144 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2145
2146 //check base state
2147 if (construtionPart.IsBase())
2148 {
2149 //Destroy construction
2150 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2151 }
2152 }
2153
2155 {
2156 //play sound
2158 }
2159
2160 //Destroy
2162 {
2163 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2165
2166 //register constructed parts for synchronization
2168
2169 //register action that was performed on part
2171
2172 //synchronize
2174
2175 // server part of sync, client will be synced from SetPartsFromSyncData
2177
2178 UpdateNavmesh();
2179
2180 //update visuals
2181 UpdateVisuals();
2182
2183 //reset action sync data
2184 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2185
2186 //check base state
2187 if (construtionPart.IsBase())
2188 {
2189 //Destroy construction
2190 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2191 }
2192 }
2193
2194 void OnPartDestroyedClient(string part_name, int action_id)
2195 {
2196 //play sound
2198 }
2199
2200 // --- UPDATE
2201 void InitBaseState()
2202 {
2203 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2204
2205 InitVisuals();
2206 UpdateNavmesh(); //regenerate navmesh
2207 GetConstruction().InitBaseState();
2208 }
2209
2210 void InitVisuals()
2211 {
2212 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2213 //check base
2214 if (!HasBase())
2215 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2216 else
2217 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2218
2219 GetConstruction().UpdateVisuals();
2220 }
2221
2222 void UpdateVisuals()
2223 {
2225
2227 foreach (string slotName : attachmentSlots)
2229
2230 //check base
2231 if (!HasBase())
2232 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2233 else
2234 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2235
2236 GetConstruction().UpdateVisuals();
2237 }
2238
2240 {
2241 string slotNameMounted = slot_name + "_Mounted";
2242 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2243
2244 if (attachment)
2245 {
2246 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2247 if (barbedWire && barbedWire.IsMounted())
2249 else
2251
2252 if (is_locked)
2253 {
2254 SetAnimationPhase(slotNameMounted, 0);
2255 SetAnimationPhase(slot_name, 1);
2256 }
2257 else
2258 {
2259 SetAnimationPhase(slotNameMounted, 1);
2260 SetAnimationPhase(slot_name, 0);
2261 }
2262 }
2263 else
2264 {
2265 SetAnimationPhase(slotNameMounted, 1);
2266 SetAnimationPhase(slot_name, 1);
2267
2269 }
2270 }
2271
2272 // avoid calling this function on frequent occasions, it's a massive performance hit
2273 void UpdatePhysics()
2274 {
2276 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2277
2280
2282 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2283
2284 foreach (string slotName : attachmentSlots)
2286
2287 //check base
2288 if (!HasBase())
2289 {
2291 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2292
2293 AddProxyPhysics(ANIMATION_DEPLOYED);
2294 }
2295 else
2296 {
2298 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2299
2300 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2301 }
2302
2303 GetConstruction().UpdatePhysics();
2304 UpdateNavmesh();
2305 }
2306
2308 {
2309 //checks for invalid appends; hotfix
2310 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2311 return;
2312 //----------------------------------
2313 string slot_name_mounted = slot_name + "_Mounted";
2314 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2315
2316 //remove proxy physics
2317 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2318 RemoveProxyPhysics(slot_name_mounted);
2319 RemoveProxyPhysics(slot_name);
2320
2321 if (attachment)
2322 {
2323 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2324 if (is_locked)
2325 {
2326 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2327 AddProxyPhysics(slot_name_mounted);
2328 }
2329 else
2330 {
2331 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2332 AddProxyPhysics(slot_name);
2333 }
2334 }
2335 }
2336
2337 protected void UpdateNavmesh()
2338 {
2339 SetAffectPathgraph(true, false);
2340 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2341 }
2342
2343 override bool CanUseConstruction()
2344 {
2345 return true;
2346 }
2347
2348 override bool CanUseConstructionBuild()
2349 {
2350 return true;
2351 }
2352
2354 {
2355 if (attachment)
2356 {
2358 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2359
2360 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2361 }
2362
2363 return false;
2364 }
2365
2366 protected bool IsAttachmentSlotLocked(string slot_name)
2367 {
2368 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2369 }
2370
2371 //--- ATTACHMENT SLOTS
2373 {
2374 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2375 if (GetGame().ConfigIsExisting(config_path))
2376 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2377 }
2378
2380 {
2381 return true;
2382 }
2383
2384 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2385 {
2386 return true;
2387 }
2388
2389 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2390 {
2391 return true;
2392 }
2393
2394 // --- INIT
2395 void ConstructionInit()
2396 {
2397 if (!m_Construction)
2398 m_Construction = new Construction(this);
2399
2400 GetConstruction().Init();
2401 }
2402
2404 {
2405 return m_Construction;
2406 }
2407
2408 //--- INVENTORY/ATTACHMENTS CONDITIONS
2409 //attachments
2411 {
2412 return super.CanReceiveAttachment(attachment, slotId);
2413 }
2414
2416 {
2417 int attachment_count = GetInventory().AttachmentCount();
2418 if (attachment_count > 0)
2419 {
2420 if (HasBase() && attachment_count == 1)
2421 return false;
2422
2423 return true;
2424 }
2425
2426 return false;
2427 }
2428
2429 override bool ShowZonesHealth()
2430 {
2431 return true;
2432 }
2433
2434 //this into/outo parent.Cargo
2435 override bool CanPutInCargo(EntityAI parent)
2436 {
2437 return false;
2438 }
2439
2440 override bool CanRemoveFromCargo(EntityAI parent)
2441 {
2442 return false;
2443 }
2444
2445 //hands
2446 override bool CanPutIntoHands(EntityAI parent)
2447 {
2448 return false;
2449 }
2450
2451 //--- ACTION CONDITIONS
2452 //direction
2453 override bool IsFacingPlayer(PlayerBase player, string selection)
2454 {
2455 return true;
2456 }
2457
2458 override bool IsPlayerInside(PlayerBase player, string selection)
2459 {
2460 return true;
2461 }
2462
2465 {
2466 return false;
2467 }
2468
2469 //camera direction check
2470 bool IsFacingCamera(string selection)
2471 {
2472 return true;
2473 }
2474
2475 //roof check
2477 {
2478 return false;
2479 }
2480
2481 //selection->player distance check
2482 bool HasProperDistance(string selection, PlayerBase player)
2483 {
2484 return true;
2485 }
2486
2487 //folding
2489 {
2490 if (HasBase() || GetInventory().AttachmentCount() > 0)
2491 return false;
2492
2493 return true;
2494 }
2495
2497 {
2500
2501 return item;
2502 }
2503
2504 //Damage triggers (barbed wire)
2505 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2506 {
2507 if (GetGame() && GetGame().IsServer())
2508 {
2509 //destroy area damage if some already exists
2511
2512 //create new area damage
2514 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2515
2516 vector min_max[2];
2517 if (MemoryPointExists(slot_name + "_min"))
2518 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2519 if (MemoryPointExists(slot_name + "_max"))
2520 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2521
2522 //get proper trigger extents (min<max)
2523 vector extents[2];
2524 GetConstruction().GetTriggerExtents(min_max, extents);
2525
2526 //get box center
2527 vector center;
2528 center = GetConstruction().GetBoxCenter(min_max);
2529 center = ModelToWorld(center);
2530
2531 //rotate center if needed
2534
2535 areaDamage.SetExtents(extents[0], extents[1]);
2536 areaDamage.SetAreaPosition(center);
2537 areaDamage.SetAreaOrientation(orientation);
2538 areaDamage.SetLoopInterval(1.0);
2539 areaDamage.SetDeferDuration(0.2);
2540 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2541 areaDamage.SetAmmoName("BarbedWireHit");
2542 areaDamage.Spawn();
2543
2545 }
2546 }
2547
2549 {
2550 if (angle_deg != 0)
2551 {
2552 //orientation
2554
2555 //center
2557 if (MemoryPointExists("rotate_axis"))
2558 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2561 center[0] = r_center_x;
2562 center[2] = r_center_z;
2563 }
2564 }
2565
2566 void DestroyAreaDamage(string slot_name)
2567 {
2568 if (GetGame() && GetGame().IsServer())
2569 {
2572 {
2573 if (areaDamage)
2574 areaDamage.Destroy();
2575
2577 }
2578 }
2579 }
2580
2581 override bool IsIgnoredByConstruction()
2582 {
2583 return true;
2584 }
2585
2586 //================================================================
2587 // SOUNDS
2588 //================================================================
2589 protected void SoundBuildStart(string part_name)
2590 {
2591 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2592 }
2593
2594 protected void SoundDismantleStart(string part_name)
2595 {
2596 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2597 }
2598
2599 protected void SoundDestroyStart(string part_name)
2600 {
2601 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2602 }
2603
2604 protected string GetBuildSoundByMaterial(string part_name)
2605 {
2607
2608 switch (material_type)
2609 {
2610 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2611 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2612 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2613 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2614 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2615 }
2616
2617 return "";
2618 }
2619
2620 protected string GetDismantleSoundByMaterial(string part_name)
2621 {
2623
2624 switch (material_type)
2625 {
2626 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2627 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2628 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2629 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2630 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2631 }
2632
2633 return "";
2634 }
2635
2636 //misc
2638 {
2639 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2640 {
2641 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2643 SetHealth(slot_name, "Health", item.GetHealth());
2644 }
2645 }
2646
2647 override int GetDamageSystemVersionChange()
2648 {
2649 return 111;
2650 }
2651
2652 override void SetActions()
2653 {
2654 super.SetActions();
2655
2657 //AddAction(ActionTakeHybridAttachment);
2658 //AddAction(ActionTakeHybridAttachmentToHands);
2661 }
2662
2663 //================================================================
2664 // DEBUG
2665 //================================================================
2666 protected void DebugCustomState()
2667 {
2668 }
2669
2672 {
2673 return null;
2674 }
2675
2676 override void OnDebugSpawn()
2677 {
2678 FullyBuild();
2679 }
2680
2681 void FullyBuild()
2682 {
2684 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2685
2686 Man p;
2687
2688#ifdef SERVER
2690 GetGame().GetWorld().GetPlayerList(players);
2691 if (players.Count())
2692 p = players[0];
2693#else
2694 p = GetGame().GetPlayer();
2695#endif
2696
2697 foreach (ConstructionPart part : parts)
2698 {
2699 bool excluded = false;
2700 string partName = part.GetPartName();
2701 if (excludes)
2702 {
2703 foreach (string exclude : excludes)
2704 {
2705 if (partName.Contains(exclude))
2706 {
2707 excluded = true;
2708 break;
2709 }
2710 }
2711 }
2712
2713 if (!excluded)
2715 }
2716
2717 GetConstruction().UpdateVisuals();
2718 }
2719}
2720
2721void bsbDebugPrint(string s)
2722{
2723#ifdef BSB_DEBUG
2724 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2725#else
2726 //Print("" + s); // comment/uncomment to hide/see debug logs
2727#endif
2728}
2729void bsbDebugSpam(string s)
2730{
2731#ifdef BSB_DEBUG_SPAM
2732 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2733#else
2734 //Print("" + s); // comment/uncomment to hide/see debug logs
2735#endif
2736}

Referenced by ItemBase::FoldBaseBuildingObject(), ItemBase::OnPartDestroyedServer(), and ItemBase::OnPartDismantledServer().

◆ DestroyConstructionKit()

void bsbDebugPrint::DestroyConstructionKit ( ItemBase construction_kit)
protected

Definition at line 1539 of file BaseBuildingBase.c.

1541{
1542 const string ANIMATION_DEPLOYED = "Deployed";
1543
1544 float m_ConstructionKitHealth; //stored health value for used construction kit
1545
1547
1548 bool m_HasBase;
1549 //variables for synchronization of base building parts (2x31 is the current limit)
1550 int m_SyncParts01; //synchronization for already built parts (31 parts)
1551 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1552 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1553 int m_InteractedPartId; //construction part id that an action was performed on
1554 int m_PerformedActionId; //action id that was performed on a construction part
1555
1556 //Sounds
1557 //build
1558 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1559 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1560 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1561 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1562 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1563 //dismantle
1564 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1565 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1566 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1567 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1568 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1569
1570 protected EffectSound m_Sound;
1571
1575
1576 // Constructor
1577 void BaseBuildingBase()
1578 {
1580
1581 //synchronized variables
1582 RegisterNetSyncVariableInt("m_SyncParts01");
1583 RegisterNetSyncVariableInt("m_SyncParts02");
1584 RegisterNetSyncVariableInt("m_SyncParts03");
1585 RegisterNetSyncVariableInt("m_InteractedPartId");
1586 RegisterNetSyncVariableInt("m_PerformedActionId");
1587 RegisterNetSyncVariableBool("m_HasBase");
1588
1589 //Construction init
1591
1592 if (ConfigIsExisting("hybridAttachments"))
1593 {
1595 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1596 }
1597 if (ConfigIsExisting("mountables"))
1598 {
1600 ConfigGetTextArray("mountables", m_Mountables);
1601 }
1602
1603 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1604 }
1605
1606 override void EEDelete(EntityAI parent)
1607 {
1608 super.EEDelete(parent);
1609
1610 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1612
1613 }
1614
1615 override string GetInvulnerabilityTypeString()
1616 {
1617 return "disableBaseDamage";
1618 }
1619
1620 override bool CanObstruct()
1621 {
1622 return true;
1623 }
1624
1625 override int GetHideIconMask()
1626 {
1627 return EInventoryIconVisibility.HIDE_VICINITY;
1628 }
1629
1630 // --- SYNCHRONIZATION
1632 {
1633 if (GetGame().IsServer())
1634 SetSynchDirty();
1635 }
1636
1637 override void OnVariablesSynchronized()
1638 {
1639 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1640 super.OnVariablesSynchronized();
1641
1642 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1643 }
1644
1645 protected void OnSynchronizedClient()
1646 {
1647 //update parts
1649
1650 //update action on part
1652
1653 //update visuals (client)
1654 UpdateVisuals();
1655 }
1656
1657 //parts synchronization
1659 {
1660 //part_id must starts from index = 1
1661 int offset;
1662 int mask;
1663
1664 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1665 {
1666 offset = part_id - 1;
1667 mask = 1 << offset;
1668
1670 }
1671 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1672 {
1673 offset = (part_id % 32);
1674 mask = 1 << offset;
1675
1677 }
1678 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1679 {
1680 offset = (part_id % 63);
1681 mask = 1 << offset;
1682
1684 }
1685 }
1686
1688 {
1689 //part_id must starts from index = 1
1690 int offset;
1691 int mask;
1692
1693 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1694 {
1695 offset = part_id - 1;
1696 mask = 1 << offset;
1697
1699 }
1700 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1701 {
1702 offset = (part_id % 32);
1703 mask = 1 << offset;
1704
1706 }
1707 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1708 {
1709 offset = (part_id % 63);
1710 mask = 1 << offset;
1711
1713 }
1714 }
1715
1717 {
1718 //part_id must starts from index = 1
1719 int offset;
1720 int mask;
1721
1722 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1723 {
1724 offset = part_id - 1;
1725 mask = 1 << offset;
1726
1727 if ((m_SyncParts01 & mask) > 0)
1728 return true;
1729 }
1730 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1731 {
1732 offset = (part_id % 32);
1733 mask = 1 << offset;
1734
1735 if ((m_SyncParts02 & mask) > 0)
1736 return true;
1737 }
1738 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1739 {
1740 offset = (part_id % 63);
1741 mask = 1 << offset;
1742
1743 if ((m_SyncParts03 & mask) > 0)
1744 return true;
1745 }
1746
1747 return false;
1748 }
1749
1750 protected void RegisterActionForSync(int part_id, int action_id)
1751 {
1754 }
1755
1756 protected void ResetActionSyncData()
1757 {
1758 //reset data
1759 m_InteractedPartId = -1;
1761 }
1762
1763 protected void SetActionFromSyncData()
1764 {
1765 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1766 {
1769
1770 switch (build_action_id)
1771 {
1775 }
1776 }
1777 }
1778 //------
1779
1781 {
1782 string key = part.m_PartName;
1783 bool is_base = part.IsBase();
1785 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1787 {
1788 if (!part.IsBuilt())
1789 {
1790 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1791 GetConstruction().AddToConstructedParts(key);
1792 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1793
1794 if (is_base)
1795 {
1797 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1798 }
1799 }
1800 }
1801 else
1802 {
1803 if (part.IsBuilt())
1804 {
1805 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1806 GetConstruction().RemoveFromConstructedParts(key);
1807 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1808
1809 if (is_base)
1810 {
1812 AddProxyPhysics(ANIMATION_DEPLOYED);
1813 }
1814 }
1815 }
1816
1817 //check slot lock for material attachments
1818 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1819 }
1820
1821 //set construction parts based on synchronized data
1823 {
1826
1827 for (int i = 0; i < construction_parts.Count(); ++i)
1828 {
1829 string key = construction_parts.GetKey(i);
1832 }
1833
1834 //regenerate navmesh
1835 UpdateNavmesh();
1836 }
1837
1839 {
1842
1843 for (int i = 0; i < construction_parts.Count(); ++i)
1844 {
1845 string key = construction_parts.GetKey(i);
1847
1848 if (value.GetId() == id)
1849 return value;
1850 }
1851
1852 return NULL;
1853 }
1854 //
1855
1856 //Base
1857 bool HasBase()
1858 {
1859 return m_HasBase;
1860 }
1861
1862 void SetBaseState(bool has_base)
1863 {
1865 }
1866
1867 override bool IsDeployable()
1868 {
1869 return true;
1870 }
1871
1872 bool IsOpened()
1873 {
1874 return false;
1875 }
1876
1877 //--- CONSTRUCTION KIT
1879 {
1883
1884 return construction_kit;
1885 }
1886
1888 {
1889 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1892 }
1893
1894 protected vector GetKitSpawnPosition()
1895 {
1896 return GetPosition();
1897 }
1898
1899 protected string GetConstructionKitType()
1900 {
1901 return "";
1902 }
1903
1905 {
1907 GetGame().ObjectDelete(construction_kit);
1908 }
1909
1910 //--- CONSTRUCTION
1911 void DestroyConstruction()
1912 {
1913 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1914 GetGame().ObjectDelete(this);
1915 }
1916
1917 // --- EVENTS
1918 override void OnStoreSave(ParamsWriteContext ctx)
1919 {
1920 super.OnStoreSave(ctx);
1921
1922 //sync parts 01
1923 ctx.Write(m_SyncParts01);
1924 ctx.Write(m_SyncParts02);
1925 ctx.Write(m_SyncParts03);
1926
1927 ctx.Write(m_HasBase);
1928 }
1929
1930 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1931 {
1932 if (!super.OnStoreLoad(ctx, version))
1933 return false;
1934
1935 //--- Base building data ---
1936 //Restore synced parts data
1937 if (!ctx.Read(m_SyncParts01))
1938 {
1939 m_SyncParts01 = 0; //set default
1940 return false;
1941 }
1942 if (!ctx.Read(m_SyncParts02))
1943 {
1944 m_SyncParts02 = 0; //set default
1945 return false;
1946 }
1947 if (!ctx.Read(m_SyncParts03))
1948 {
1949 m_SyncParts03 = 0; //set default
1950 return false;
1951 }
1952
1953 //has base
1954 if (!ctx.Read(m_HasBase))
1955 {
1956 m_HasBase = false;
1957 return false;
1958 }
1959 //---
1960
1961 return true;
1962 }
1963
1964 override void AfterStoreLoad()
1965 {
1966 super.AfterStoreLoad();
1967
1970 }
1971
1973 {
1974 //update server data
1976
1977 //set base state
1978 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1979 SetBaseState(construction_part.IsBuilt()) ;
1980
1981 //synchronize after load
1983 }
1984
1985 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1986 {
1988 return;
1989
1990 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1991
1992 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1993 return;
1994
1996 string part_name = zone;
1997 part_name.ToLower();
1998
2000 {
2002
2003 if (construction_part && construction.IsPartConstructed(part_name))
2004 {
2005 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2006 construction.DestroyConnectedParts(part_name);
2007 }
2008
2009 //barbed wire handling (hack-ish)
2010 if (part_name.Contains("barbed"))
2011 {
2012 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2013 if (barbed_wire)
2014 barbed_wire.SetMountedState(false);
2015 }
2016 }
2017 }
2018
2019 override void EEOnAfterLoad()
2020 {
2022 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2023
2024 super.EEOnAfterLoad();
2025 }
2026
2027 override void EEInit()
2028 {
2029 super.EEInit();
2030
2031 // init visuals and physics
2032 InitBaseState();
2033
2034 //debug
2035#ifdef DEVELOPER
2037#endif
2038 }
2039
2040 override void EEItemAttached(EntityAI item, string slot_name)
2041 {
2042 super.EEItemAttached(item, slot_name);
2043
2045 UpdateVisuals();
2047 }
2048
2049 override void EEItemDetached(EntityAI item, string slot_name)
2050 {
2051 super.EEItemDetached(item, slot_name);
2052
2053 UpdateVisuals();
2055 }
2056
2057 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2058 {
2060 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2061
2064 }
2065
2066 //ignore out of reach condition
2067 override bool IgnoreOutOfReachCondition()
2068 {
2069 return true;
2070 }
2071
2072 //CONSTRUCTION EVENTS
2073 //Build
2074 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2075 {
2077
2078 //check base state
2079 if (construtionPart.IsBase())
2080 {
2081 SetBaseState(true);
2082
2083 //spawn kit
2085 }
2086
2087 //register constructed parts for synchronization
2089
2090 //register action that was performed on part
2092
2093 //synchronize
2095
2096 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2097
2098 UpdateNavmesh();
2099
2100 //update visuals
2101 UpdateVisuals();
2102
2103 //reset action sync data
2104 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2105 }
2106
2107 void OnPartBuiltClient(string part_name, int action_id)
2108 {
2109 //play sound
2111 }
2112
2113 //Dismantle
2115 {
2116 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2118
2119 //register constructed parts for synchronization
2121
2122 //register action that was performed on part
2124
2125 //synchronize
2127
2128 // server part of sync, client will be synced from SetPartsFromSyncData
2130
2131 UpdateNavmesh();
2132
2133 //update visuals
2134 UpdateVisuals();
2135
2136 //reset action sync data
2137 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2138
2139 //check base state
2140 if (construtionPart.IsBase())
2141 {
2142 //Destroy construction
2143 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2144 }
2145 }
2146
2148 {
2149 //play sound
2151 }
2152
2153 //Destroy
2155 {
2156 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2158
2159 //register constructed parts for synchronization
2161
2162 //register action that was performed on part
2164
2165 //synchronize
2167
2168 // server part of sync, client will be synced from SetPartsFromSyncData
2170
2171 UpdateNavmesh();
2172
2173 //update visuals
2174 UpdateVisuals();
2175
2176 //reset action sync data
2177 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2178
2179 //check base state
2180 if (construtionPart.IsBase())
2181 {
2182 //Destroy construction
2183 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2184 }
2185 }
2186
2187 void OnPartDestroyedClient(string part_name, int action_id)
2188 {
2189 //play sound
2191 }
2192
2193 // --- UPDATE
2194 void InitBaseState()
2195 {
2196 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2197
2198 InitVisuals();
2199 UpdateNavmesh(); //regenerate navmesh
2200 GetConstruction().InitBaseState();
2201 }
2202
2203 void InitVisuals()
2204 {
2205 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2206 //check base
2207 if (!HasBase())
2208 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2209 else
2210 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2211
2212 GetConstruction().UpdateVisuals();
2213 }
2214
2215 void UpdateVisuals()
2216 {
2218
2220 foreach (string slotName : attachmentSlots)
2222
2223 //check base
2224 if (!HasBase())
2225 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2226 else
2227 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2228
2229 GetConstruction().UpdateVisuals();
2230 }
2231
2233 {
2234 string slotNameMounted = slot_name + "_Mounted";
2235 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2236
2237 if (attachment)
2238 {
2239 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2240 if (barbedWire && barbedWire.IsMounted())
2242 else
2244
2245 if (is_locked)
2246 {
2247 SetAnimationPhase(slotNameMounted, 0);
2248 SetAnimationPhase(slot_name, 1);
2249 }
2250 else
2251 {
2252 SetAnimationPhase(slotNameMounted, 1);
2253 SetAnimationPhase(slot_name, 0);
2254 }
2255 }
2256 else
2257 {
2258 SetAnimationPhase(slotNameMounted, 1);
2259 SetAnimationPhase(slot_name, 1);
2260
2262 }
2263 }
2264
2265 // avoid calling this function on frequent occasions, it's a massive performance hit
2266 void UpdatePhysics()
2267 {
2269 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2270
2273
2275 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2276
2277 foreach (string slotName : attachmentSlots)
2279
2280 //check base
2281 if (!HasBase())
2282 {
2284 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2285
2286 AddProxyPhysics(ANIMATION_DEPLOYED);
2287 }
2288 else
2289 {
2291 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2292
2293 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2294 }
2295
2296 GetConstruction().UpdatePhysics();
2297 UpdateNavmesh();
2298 }
2299
2301 {
2302 //checks for invalid appends; hotfix
2303 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2304 return;
2305 //----------------------------------
2306 string slot_name_mounted = slot_name + "_Mounted";
2307 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2308
2309 //remove proxy physics
2310 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2311 RemoveProxyPhysics(slot_name_mounted);
2312 RemoveProxyPhysics(slot_name);
2313
2314 if (attachment)
2315 {
2316 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2317 if (is_locked)
2318 {
2319 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2320 AddProxyPhysics(slot_name_mounted);
2321 }
2322 else
2323 {
2324 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2325 AddProxyPhysics(slot_name);
2326 }
2327 }
2328 }
2329
2330 protected void UpdateNavmesh()
2331 {
2332 SetAffectPathgraph(true, false);
2333 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2334 }
2335
2336 override bool CanUseConstruction()
2337 {
2338 return true;
2339 }
2340
2341 override bool CanUseConstructionBuild()
2342 {
2343 return true;
2344 }
2345
2347 {
2348 if (attachment)
2349 {
2351 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2352
2353 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2354 }
2355
2356 return false;
2357 }
2358
2359 protected bool IsAttachmentSlotLocked(string slot_name)
2360 {
2361 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2362 }
2363
2364 //--- ATTACHMENT SLOTS
2366 {
2367 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2368 if (GetGame().ConfigIsExisting(config_path))
2369 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2370 }
2371
2373 {
2374 return true;
2375 }
2376
2377 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2378 {
2379 return true;
2380 }
2381
2382 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2383 {
2384 return true;
2385 }
2386
2387 // --- INIT
2388 void ConstructionInit()
2389 {
2390 if (!m_Construction)
2391 m_Construction = new Construction(this);
2392
2393 GetConstruction().Init();
2394 }
2395
2397 {
2398 return m_Construction;
2399 }
2400
2401 //--- INVENTORY/ATTACHMENTS CONDITIONS
2402 //attachments
2404 {
2405 return super.CanReceiveAttachment(attachment, slotId);
2406 }
2407
2409 {
2410 int attachment_count = GetInventory().AttachmentCount();
2411 if (attachment_count > 0)
2412 {
2413 if (HasBase() && attachment_count == 1)
2414 return false;
2415
2416 return true;
2417 }
2418
2419 return false;
2420 }
2421
2422 override bool ShowZonesHealth()
2423 {
2424 return true;
2425 }
2426
2427 //this into/outo parent.Cargo
2428 override bool CanPutInCargo(EntityAI parent)
2429 {
2430 return false;
2431 }
2432
2433 override bool CanRemoveFromCargo(EntityAI parent)
2434 {
2435 return false;
2436 }
2437
2438 //hands
2439 override bool CanPutIntoHands(EntityAI parent)
2440 {
2441 return false;
2442 }
2443
2444 //--- ACTION CONDITIONS
2445 //direction
2446 override bool IsFacingPlayer(PlayerBase player, string selection)
2447 {
2448 return true;
2449 }
2450
2451 override bool IsPlayerInside(PlayerBase player, string selection)
2452 {
2453 return true;
2454 }
2455
2458 {
2459 return false;
2460 }
2461
2462 //camera direction check
2463 bool IsFacingCamera(string selection)
2464 {
2465 return true;
2466 }
2467
2468 //roof check
2470 {
2471 return false;
2472 }
2473
2474 //selection->player distance check
2475 bool HasProperDistance(string selection, PlayerBase player)
2476 {
2477 return true;
2478 }
2479
2480 //folding
2482 {
2483 if (HasBase() || GetInventory().AttachmentCount() > 0)
2484 return false;
2485
2486 return true;
2487 }
2488
2490 {
2493
2494 return item;
2495 }
2496
2497 //Damage triggers (barbed wire)
2498 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2499 {
2500 if (GetGame() && GetGame().IsServer())
2501 {
2502 //destroy area damage if some already exists
2504
2505 //create new area damage
2507 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2508
2509 vector min_max[2];
2510 if (MemoryPointExists(slot_name + "_min"))
2511 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2512 if (MemoryPointExists(slot_name + "_max"))
2513 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2514
2515 //get proper trigger extents (min<max)
2516 vector extents[2];
2517 GetConstruction().GetTriggerExtents(min_max, extents);
2518
2519 //get box center
2520 vector center;
2521 center = GetConstruction().GetBoxCenter(min_max);
2522 center = ModelToWorld(center);
2523
2524 //rotate center if needed
2527
2528 areaDamage.SetExtents(extents[0], extents[1]);
2529 areaDamage.SetAreaPosition(center);
2530 areaDamage.SetAreaOrientation(orientation);
2531 areaDamage.SetLoopInterval(1.0);
2532 areaDamage.SetDeferDuration(0.2);
2533 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2534 areaDamage.SetAmmoName("BarbedWireHit");
2535 areaDamage.Spawn();
2536
2538 }
2539 }
2540
2542 {
2543 if (angle_deg != 0)
2544 {
2545 //orientation
2547
2548 //center
2550 if (MemoryPointExists("rotate_axis"))
2551 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2554 center[0] = r_center_x;
2555 center[2] = r_center_z;
2556 }
2557 }
2558
2559 void DestroyAreaDamage(string slot_name)
2560 {
2561 if (GetGame() && GetGame().IsServer())
2562 {
2565 {
2566 if (areaDamage)
2567 areaDamage.Destroy();
2568
2570 }
2571 }
2572 }
2573
2574 override bool IsIgnoredByConstruction()
2575 {
2576 return true;
2577 }
2578
2579 //================================================================
2580 // SOUNDS
2581 //================================================================
2582 protected void SoundBuildStart(string part_name)
2583 {
2584 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2585 }
2586
2587 protected void SoundDismantleStart(string part_name)
2588 {
2589 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2590 }
2591
2592 protected void SoundDestroyStart(string part_name)
2593 {
2594 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2595 }
2596
2597 protected string GetBuildSoundByMaterial(string part_name)
2598 {
2600
2601 switch (material_type)
2602 {
2603 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2604 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2605 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2606 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2607 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2608 }
2609
2610 return "";
2611 }
2612
2613 protected string GetDismantleSoundByMaterial(string part_name)
2614 {
2616
2617 switch (material_type)
2618 {
2619 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2620 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2621 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2622 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2623 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2624 }
2625
2626 return "";
2627 }
2628
2629 //misc
2631 {
2632 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2633 {
2634 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2636 SetHealth(slot_name, "Health", item.GetHealth());
2637 }
2638 }
2639
2640 override int GetDamageSystemVersionChange()
2641 {
2642 return 111;
2643 }
2644
2645 override void SetActions()
2646 {
2647 super.SetActions();
2648
2650 //AddAction(ActionTakeHybridAttachment);
2651 //AddAction(ActionTakeHybridAttachmentToHands);
2654 }
2655
2656 //================================================================
2657 // DEBUG
2658 //================================================================
2659 protected void DebugCustomState()
2660 {
2661 }
2662
2665 {
2666 return null;
2667 }
2668
2669 override void OnDebugSpawn()
2670 {
2671 FullyBuild();
2672 }
2673
2674 void FullyBuild()
2675 {
2677 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2678
2679 Man p;
2680
2681#ifdef SERVER
2683 GetGame().GetWorld().GetPlayerList(players);
2684 if (players.Count())
2685 p = players[0];
2686#else
2687 p = GetGame().GetPlayer();
2688#endif
2689
2690 foreach (ConstructionPart part : parts)
2691 {
2692 bool excluded = false;
2693 string partName = part.GetPartName();
2694 if (excludes)
2695 {
2696 foreach (string exclude : excludes)
2697 {
2698 if (partName.Contains(exclude))
2699 {
2700 excluded = true;
2701 break;
2702 }
2703 }
2704 }
2705
2706 if (!excluded)
2708 }
2709
2710 GetConstruction().UpdateVisuals();
2711 }
2712}
2713
2714void bsbDebugPrint(string s)
2715{
2716#ifdef BSB_DEBUG
2717 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2718#else
2719 //Print("" + s); // comment/uncomment to hide/see debug logs
2720#endif
2721}
2722void bsbDebugSpam(string s)
2723{
2724#ifdef BSB_DEBUG_SPAM
2725 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2726#else
2727 //Print("" + s); // comment/uncomment to hide/see debug logs
2728#endif
2729}

◆ EEDelete()

override void bsbDebugPrint::EEDelete ( EntityAI parent)
protected

Definition at line 1241 of file BaseBuildingBase.c.

1243{
1244 const string ANIMATION_DEPLOYED = "Deployed";
1245
1246 float m_ConstructionKitHealth; //stored health value for used construction kit
1247
1249
1250 bool m_HasBase;
1251 //variables for synchronization of base building parts (2x31 is the current limit)
1252 int m_SyncParts01; //synchronization for already built parts (31 parts)
1253 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1254 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1255 int m_InteractedPartId; //construction part id that an action was performed on
1256 int m_PerformedActionId; //action id that was performed on a construction part
1257
1258 //Sounds
1259 //build
1260 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1261 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1262 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1263 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1264 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1265 //dismantle
1266 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1267 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1268 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1269 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1270 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1271
1272 protected EffectSound m_Sound;
1273
1277
1278 // Constructor
1279 void BaseBuildingBase()
1280 {
1282
1283 //synchronized variables
1284 RegisterNetSyncVariableInt("m_SyncParts01");
1285 RegisterNetSyncVariableInt("m_SyncParts02");
1286 RegisterNetSyncVariableInt("m_SyncParts03");
1287 RegisterNetSyncVariableInt("m_InteractedPartId");
1288 RegisterNetSyncVariableInt("m_PerformedActionId");
1289 RegisterNetSyncVariableBool("m_HasBase");
1290
1291 //Construction init
1293
1294 if (ConfigIsExisting("hybridAttachments"))
1295 {
1297 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1298 }
1299 if (ConfigIsExisting("mountables"))
1300 {
1302 ConfigGetTextArray("mountables", m_Mountables);
1303 }
1304
1305 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1306 }
1307
1308 override void EEDelete(EntityAI parent)
1309 {
1310 super.EEDelete(parent);
1311
1312 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1314
1315 }
1316
1317 override string GetInvulnerabilityTypeString()
1318 {
1319 return "disableBaseDamage";
1320 }
1321
1322 override bool CanObstruct()
1323 {
1324 return true;
1325 }
1326
1327 override int GetHideIconMask()
1328 {
1329 return EInventoryIconVisibility.HIDE_VICINITY;
1330 }
1331
1332 // --- SYNCHRONIZATION
1334 {
1335 if (GetGame().IsServer())
1336 SetSynchDirty();
1337 }
1338
1339 override void OnVariablesSynchronized()
1340 {
1341 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1342 super.OnVariablesSynchronized();
1343
1344 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1345 }
1346
1347 protected void OnSynchronizedClient()
1348 {
1349 //update parts
1351
1352 //update action on part
1354
1355 //update visuals (client)
1356 UpdateVisuals();
1357 }
1358
1359 //parts synchronization
1361 {
1362 //part_id must starts from index = 1
1363 int offset;
1364 int mask;
1365
1366 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1367 {
1368 offset = part_id - 1;
1369 mask = 1 << offset;
1370
1372 }
1373 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1374 {
1375 offset = (part_id % 32);
1376 mask = 1 << offset;
1377
1379 }
1380 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1381 {
1382 offset = (part_id % 63);
1383 mask = 1 << offset;
1384
1386 }
1387 }
1388
1390 {
1391 //part_id must starts from index = 1
1392 int offset;
1393 int mask;
1394
1395 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1396 {
1397 offset = part_id - 1;
1398 mask = 1 << offset;
1399
1401 }
1402 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1403 {
1404 offset = (part_id % 32);
1405 mask = 1 << offset;
1406
1408 }
1409 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1410 {
1411 offset = (part_id % 63);
1412 mask = 1 << offset;
1413
1415 }
1416 }
1417
1419 {
1420 //part_id must starts from index = 1
1421 int offset;
1422 int mask;
1423
1424 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1425 {
1426 offset = part_id - 1;
1427 mask = 1 << offset;
1428
1429 if ((m_SyncParts01 & mask) > 0)
1430 return true;
1431 }
1432 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1433 {
1434 offset = (part_id % 32);
1435 mask = 1 << offset;
1436
1437 if ((m_SyncParts02 & mask) > 0)
1438 return true;
1439 }
1440 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1441 {
1442 offset = (part_id % 63);
1443 mask = 1 << offset;
1444
1445 if ((m_SyncParts03 & mask) > 0)
1446 return true;
1447 }
1448
1449 return false;
1450 }
1451
1452 protected void RegisterActionForSync(int part_id, int action_id)
1453 {
1456 }
1457
1458 protected void ResetActionSyncData()
1459 {
1460 //reset data
1461 m_InteractedPartId = -1;
1463 }
1464
1465 protected void SetActionFromSyncData()
1466 {
1467 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1468 {
1471
1472 switch (build_action_id)
1473 {
1477 }
1478 }
1479 }
1480 //------
1481
1483 {
1484 string key = part.m_PartName;
1485 bool is_base = part.IsBase();
1487 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1489 {
1490 if (!part.IsBuilt())
1491 {
1492 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1493 GetConstruction().AddToConstructedParts(key);
1494 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1495
1496 if (is_base)
1497 {
1499 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1500 }
1501 }
1502 }
1503 else
1504 {
1505 if (part.IsBuilt())
1506 {
1507 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1508 GetConstruction().RemoveFromConstructedParts(key);
1509 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1510
1511 if (is_base)
1512 {
1514 AddProxyPhysics(ANIMATION_DEPLOYED);
1515 }
1516 }
1517 }
1518
1519 //check slot lock for material attachments
1520 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1521 }
1522
1523 //set construction parts based on synchronized data
1525 {
1528
1529 for (int i = 0; i < construction_parts.Count(); ++i)
1530 {
1531 string key = construction_parts.GetKey(i);
1534 }
1535
1536 //regenerate navmesh
1537 UpdateNavmesh();
1538 }
1539
1541 {
1544
1545 for (int i = 0; i < construction_parts.Count(); ++i)
1546 {
1547 string key = construction_parts.GetKey(i);
1549
1550 if (value.GetId() == id)
1551 return value;
1552 }
1553
1554 return NULL;
1555 }
1556 //
1557
1558 //Base
1559 bool HasBase()
1560 {
1561 return m_HasBase;
1562 }
1563
1564 void SetBaseState(bool has_base)
1565 {
1567 }
1568
1569 override bool IsDeployable()
1570 {
1571 return true;
1572 }
1573
1574 bool IsOpened()
1575 {
1576 return false;
1577 }
1578
1579 //--- CONSTRUCTION KIT
1581 {
1585
1586 return construction_kit;
1587 }
1588
1590 {
1591 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1594 }
1595
1596 protected vector GetKitSpawnPosition()
1597 {
1598 return GetPosition();
1599 }
1600
1601 protected string GetConstructionKitType()
1602 {
1603 return "";
1604 }
1605
1607 {
1609 GetGame().ObjectDelete(construction_kit);
1610 }
1611
1612 //--- CONSTRUCTION
1613 void DestroyConstruction()
1614 {
1615 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1616 GetGame().ObjectDelete(this);
1617 }
1618
1619 // --- EVENTS
1620 override void OnStoreSave(ParamsWriteContext ctx)
1621 {
1622 super.OnStoreSave(ctx);
1623
1624 //sync parts 01
1625 ctx.Write(m_SyncParts01);
1626 ctx.Write(m_SyncParts02);
1627 ctx.Write(m_SyncParts03);
1628
1629 ctx.Write(m_HasBase);
1630 }
1631
1632 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1633 {
1634 if (!super.OnStoreLoad(ctx, version))
1635 return false;
1636
1637 //--- Base building data ---
1638 //Restore synced parts data
1639 if (!ctx.Read(m_SyncParts01))
1640 {
1641 m_SyncParts01 = 0; //set default
1642 return false;
1643 }
1644 if (!ctx.Read(m_SyncParts02))
1645 {
1646 m_SyncParts02 = 0; //set default
1647 return false;
1648 }
1649 if (!ctx.Read(m_SyncParts03))
1650 {
1651 m_SyncParts03 = 0; //set default
1652 return false;
1653 }
1654
1655 //has base
1656 if (!ctx.Read(m_HasBase))
1657 {
1658 m_HasBase = false;
1659 return false;
1660 }
1661 //---
1662
1663 return true;
1664 }
1665
1666 override void AfterStoreLoad()
1667 {
1668 super.AfterStoreLoad();
1669
1672 }
1673
1675 {
1676 //update server data
1678
1679 //set base state
1680 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1681 SetBaseState(construction_part.IsBuilt()) ;
1682
1683 //synchronize after load
1685 }
1686
1687 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1688 {
1690 return;
1691
1692 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1693
1694 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1695 return;
1696
1698 string part_name = zone;
1699 part_name.ToLower();
1700
1702 {
1704
1705 if (construction_part && construction.IsPartConstructed(part_name))
1706 {
1707 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1708 construction.DestroyConnectedParts(part_name);
1709 }
1710
1711 //barbed wire handling (hack-ish)
1712 if (part_name.Contains("barbed"))
1713 {
1714 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1715 if (barbed_wire)
1716 barbed_wire.SetMountedState(false);
1717 }
1718 }
1719 }
1720
1721 override void EEOnAfterLoad()
1722 {
1724 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1725
1726 super.EEOnAfterLoad();
1727 }
1728
1729 override void EEInit()
1730 {
1731 super.EEInit();
1732
1733 // init visuals and physics
1734 InitBaseState();
1735
1736 //debug
1737#ifdef DEVELOPER
1739#endif
1740 }
1741
1742 override void EEItemAttached(EntityAI item, string slot_name)
1743 {
1744 super.EEItemAttached(item, slot_name);
1745
1747 UpdateVisuals();
1749 }
1750
1751 override void EEItemDetached(EntityAI item, string slot_name)
1752 {
1753 super.EEItemDetached(item, slot_name);
1754
1755 UpdateVisuals();
1757 }
1758
1759 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
1760 {
1762 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
1763
1766 }
1767
1768 //ignore out of reach condition
1769 override bool IgnoreOutOfReachCondition()
1770 {
1771 return true;
1772 }
1773
1774 //CONSTRUCTION EVENTS
1775 //Build
1776 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
1777 {
1779
1780 //check base state
1781 if (construtionPart.IsBase())
1782 {
1783 SetBaseState(true);
1784
1785 //spawn kit
1787 }
1788
1789 //register constructed parts for synchronization
1791
1792 //register action that was performed on part
1794
1795 //synchronize
1797
1798 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
1799
1800 UpdateNavmesh();
1801
1802 //update visuals
1803 UpdateVisuals();
1804
1805 //reset action sync data
1806 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1807 }
1808
1809 void OnPartBuiltClient(string part_name, int action_id)
1810 {
1811 //play sound
1813 }
1814
1815 //Dismantle
1817 {
1818 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
1820
1821 //register constructed parts for synchronization
1823
1824 //register action that was performed on part
1826
1827 //synchronize
1829
1830 // server part of sync, client will be synced from SetPartsFromSyncData
1832
1833 UpdateNavmesh();
1834
1835 //update visuals
1836 UpdateVisuals();
1837
1838 //reset action sync data
1839 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1840
1841 //check base state
1842 if (construtionPart.IsBase())
1843 {
1844 //Destroy construction
1845 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1846 }
1847 }
1848
1850 {
1851 //play sound
1853 }
1854
1855 //Destroy
1857 {
1858 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
1860
1861 //register constructed parts for synchronization
1863
1864 //register action that was performed on part
1866
1867 //synchronize
1869
1870 // server part of sync, client will be synced from SetPartsFromSyncData
1872
1873 UpdateNavmesh();
1874
1875 //update visuals
1876 UpdateVisuals();
1877
1878 //reset action sync data
1879 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1880
1881 //check base state
1882 if (construtionPart.IsBase())
1883 {
1884 //Destroy construction
1885 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1886 }
1887 }
1888
1889 void OnPartDestroyedClient(string part_name, int action_id)
1890 {
1891 //play sound
1893 }
1894
1895 // --- UPDATE
1896 void InitBaseState()
1897 {
1898 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
1899
1900 InitVisuals();
1901 UpdateNavmesh(); //regenerate navmesh
1902 GetConstruction().InitBaseState();
1903 }
1904
1905 void InitVisuals()
1906 {
1907 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
1908 //check base
1909 if (!HasBase())
1910 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1911 else
1912 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1913
1914 GetConstruction().UpdateVisuals();
1915 }
1916
1917 void UpdateVisuals()
1918 {
1920
1922 foreach (string slotName : attachmentSlots)
1924
1925 //check base
1926 if (!HasBase())
1927 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1928 else
1929 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1930
1931 GetConstruction().UpdateVisuals();
1932 }
1933
1935 {
1936 string slotNameMounted = slot_name + "_Mounted";
1937 EntityAI attachment = FindAttachmentBySlotName(slot_name);
1938
1939 if (attachment)
1940 {
1941 BarbedWire barbedWire = BarbedWire.Cast(attachment);
1942 if (barbedWire && barbedWire.IsMounted())
1944 else
1946
1947 if (is_locked)
1948 {
1949 SetAnimationPhase(slotNameMounted, 0);
1950 SetAnimationPhase(slot_name, 1);
1951 }
1952 else
1953 {
1954 SetAnimationPhase(slotNameMounted, 1);
1955 SetAnimationPhase(slot_name, 0);
1956 }
1957 }
1958 else
1959 {
1960 SetAnimationPhase(slotNameMounted, 1);
1961 SetAnimationPhase(slot_name, 1);
1962
1964 }
1965 }
1966
1967 // avoid calling this function on frequent occasions, it's a massive performance hit
1968 void UpdatePhysics()
1969 {
1971 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
1972
1975
1977 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
1978
1979 foreach (string slotName : attachmentSlots)
1981
1982 //check base
1983 if (!HasBase())
1984 {
1986 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
1987
1988 AddProxyPhysics(ANIMATION_DEPLOYED);
1989 }
1990 else
1991 {
1993 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
1994
1995 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1996 }
1997
1998 GetConstruction().UpdatePhysics();
1999 UpdateNavmesh();
2000 }
2001
2003 {
2004 //checks for invalid appends; hotfix
2005 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2006 return;
2007 //----------------------------------
2008 string slot_name_mounted = slot_name + "_Mounted";
2009 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2010
2011 //remove proxy physics
2012 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2013 RemoveProxyPhysics(slot_name_mounted);
2014 RemoveProxyPhysics(slot_name);
2015
2016 if (attachment)
2017 {
2018 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2019 if (is_locked)
2020 {
2021 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2022 AddProxyPhysics(slot_name_mounted);
2023 }
2024 else
2025 {
2026 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2027 AddProxyPhysics(slot_name);
2028 }
2029 }
2030 }
2031
2032 protected void UpdateNavmesh()
2033 {
2034 SetAffectPathgraph(true, false);
2035 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2036 }
2037
2038 override bool CanUseConstruction()
2039 {
2040 return true;
2041 }
2042
2043 override bool CanUseConstructionBuild()
2044 {
2045 return true;
2046 }
2047
2049 {
2050 if (attachment)
2051 {
2053 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2054
2055 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2056 }
2057
2058 return false;
2059 }
2060
2061 protected bool IsAttachmentSlotLocked(string slot_name)
2062 {
2063 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2064 }
2065
2066 //--- ATTACHMENT SLOTS
2068 {
2069 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2070 if (GetGame().ConfigIsExisting(config_path))
2071 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2072 }
2073
2075 {
2076 return true;
2077 }
2078
2079 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2080 {
2081 return true;
2082 }
2083
2084 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2085 {
2086 return true;
2087 }
2088
2089 // --- INIT
2090 void ConstructionInit()
2091 {
2092 if (!m_Construction)
2093 m_Construction = new Construction(this);
2094
2095 GetConstruction().Init();
2096 }
2097
2099 {
2100 return m_Construction;
2101 }
2102
2103 //--- INVENTORY/ATTACHMENTS CONDITIONS
2104 //attachments
2106 {
2107 return super.CanReceiveAttachment(attachment, slotId);
2108 }
2109
2111 {
2112 int attachment_count = GetInventory().AttachmentCount();
2113 if (attachment_count > 0)
2114 {
2115 if (HasBase() && attachment_count == 1)
2116 return false;
2117
2118 return true;
2119 }
2120
2121 return false;
2122 }
2123
2124 override bool ShowZonesHealth()
2125 {
2126 return true;
2127 }
2128
2129 //this into/outo parent.Cargo
2130 override bool CanPutInCargo(EntityAI parent)
2131 {
2132 return false;
2133 }
2134
2135 override bool CanRemoveFromCargo(EntityAI parent)
2136 {
2137 return false;
2138 }
2139
2140 //hands
2141 override bool CanPutIntoHands(EntityAI parent)
2142 {
2143 return false;
2144 }
2145
2146 //--- ACTION CONDITIONS
2147 //direction
2148 override bool IsFacingPlayer(PlayerBase player, string selection)
2149 {
2150 return true;
2151 }
2152
2153 override bool IsPlayerInside(PlayerBase player, string selection)
2154 {
2155 return true;
2156 }
2157
2160 {
2161 return false;
2162 }
2163
2164 //camera direction check
2165 bool IsFacingCamera(string selection)
2166 {
2167 return true;
2168 }
2169
2170 //roof check
2172 {
2173 return false;
2174 }
2175
2176 //selection->player distance check
2177 bool HasProperDistance(string selection, PlayerBase player)
2178 {
2179 return true;
2180 }
2181
2182 //folding
2184 {
2185 if (HasBase() || GetInventory().AttachmentCount() > 0)
2186 return false;
2187
2188 return true;
2189 }
2190
2192 {
2195
2196 return item;
2197 }
2198
2199 //Damage triggers (barbed wire)
2200 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2201 {
2202 if (GetGame() && GetGame().IsServer())
2203 {
2204 //destroy area damage if some already exists
2206
2207 //create new area damage
2209 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2210
2211 vector min_max[2];
2212 if (MemoryPointExists(slot_name + "_min"))
2213 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2214 if (MemoryPointExists(slot_name + "_max"))
2215 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2216
2217 //get proper trigger extents (min<max)
2218 vector extents[2];
2219 GetConstruction().GetTriggerExtents(min_max, extents);
2220
2221 //get box center
2222 vector center;
2223 center = GetConstruction().GetBoxCenter(min_max);
2224 center = ModelToWorld(center);
2225
2226 //rotate center if needed
2229
2230 areaDamage.SetExtents(extents[0], extents[1]);
2231 areaDamage.SetAreaPosition(center);
2232 areaDamage.SetAreaOrientation(orientation);
2233 areaDamage.SetLoopInterval(1.0);
2234 areaDamage.SetDeferDuration(0.2);
2235 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2236 areaDamage.SetAmmoName("BarbedWireHit");
2237 areaDamage.Spawn();
2238
2240 }
2241 }
2242
2244 {
2245 if (angle_deg != 0)
2246 {
2247 //orientation
2249
2250 //center
2252 if (MemoryPointExists("rotate_axis"))
2253 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2256 center[0] = r_center_x;
2257 center[2] = r_center_z;
2258 }
2259 }
2260
2261 void DestroyAreaDamage(string slot_name)
2262 {
2263 if (GetGame() && GetGame().IsServer())
2264 {
2267 {
2268 if (areaDamage)
2269 areaDamage.Destroy();
2270
2272 }
2273 }
2274 }
2275
2276 override bool IsIgnoredByConstruction()
2277 {
2278 return true;
2279 }
2280
2281 //================================================================
2282 // SOUNDS
2283 //================================================================
2284 protected void SoundBuildStart(string part_name)
2285 {
2286 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2287 }
2288
2289 protected void SoundDismantleStart(string part_name)
2290 {
2291 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2292 }
2293
2294 protected void SoundDestroyStart(string part_name)
2295 {
2296 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2297 }
2298
2299 protected string GetBuildSoundByMaterial(string part_name)
2300 {
2302
2303 switch (material_type)
2304 {
2305 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2306 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2307 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2308 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2309 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2310 }
2311
2312 return "";
2313 }
2314
2315 protected string GetDismantleSoundByMaterial(string part_name)
2316 {
2318
2319 switch (material_type)
2320 {
2321 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2322 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2323 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2324 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2325 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2326 }
2327
2328 return "";
2329 }
2330
2331 //misc
2333 {
2334 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2335 {
2336 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2338 SetHealth(slot_name, "Health", item.GetHealth());
2339 }
2340 }
2341
2342 override int GetDamageSystemVersionChange()
2343 {
2344 return 111;
2345 }
2346
2347 override void SetActions()
2348 {
2349 super.SetActions();
2350
2352 //AddAction(ActionTakeHybridAttachment);
2353 //AddAction(ActionTakeHybridAttachmentToHands);
2356 }
2357
2358 //================================================================
2359 // DEBUG
2360 //================================================================
2361 protected void DebugCustomState()
2362 {
2363 }
2364
2367 {
2368 return null;
2369 }
2370
2371 override void OnDebugSpawn()
2372 {
2373 FullyBuild();
2374 }
2375
2376 void FullyBuild()
2377 {
2379 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2380
2381 Man p;
2382
2383#ifdef SERVER
2385 GetGame().GetWorld().GetPlayerList(players);
2386 if (players.Count())
2387 p = players[0];
2388#else
2389 p = GetGame().GetPlayer();
2390#endif
2391
2392 foreach (ConstructionPart part : parts)
2393 {
2394 bool excluded = false;
2395 string partName = part.GetPartName();
2396 if (excludes)
2397 {
2398 foreach (string exclude : excludes)
2399 {
2400 if (partName.Contains(exclude))
2401 {
2402 excluded = true;
2403 break;
2404 }
2405 }
2406 }
2407
2408 if (!excluded)
2410 }
2411
2412 GetConstruction().UpdateVisuals();
2413 }
2414}
2415
2416void bsbDebugPrint(string s)
2417{
2418#ifdef BSB_DEBUG
2419 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2420#else
2421 //Print("" + s); // comment/uncomment to hide/see debug logs
2422#endif
2423}
2424void bsbDebugSpam(string s)
2425{
2426#ifdef BSB_DEBUG_SPAM
2427 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2428#else
2429 //Print("" + s); // comment/uncomment to hide/see debug logs
2430#endif
2431}

◆ EEHealthLevelChanged()

override void bsbDebugPrint::EEHealthLevelChanged ( int oldLevel,
int newLevel,
string zone )
protected

Definition at line 1620 of file BaseBuildingBase.c.

1622{
1623 const string ANIMATION_DEPLOYED = "Deployed";
1624
1625 float m_ConstructionKitHealth; //stored health value for used construction kit
1626
1628
1629 bool m_HasBase;
1630 //variables for synchronization of base building parts (2x31 is the current limit)
1631 int m_SyncParts01; //synchronization for already built parts (31 parts)
1632 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1633 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1634 int m_InteractedPartId; //construction part id that an action was performed on
1635 int m_PerformedActionId; //action id that was performed on a construction part
1636
1637 //Sounds
1638 //build
1639 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1640 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1641 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1642 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1643 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1644 //dismantle
1645 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1646 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1647 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1648 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1649 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1650
1651 protected EffectSound m_Sound;
1652
1656
1657 // Constructor
1658 void BaseBuildingBase()
1659 {
1661
1662 //synchronized variables
1663 RegisterNetSyncVariableInt("m_SyncParts01");
1664 RegisterNetSyncVariableInt("m_SyncParts02");
1665 RegisterNetSyncVariableInt("m_SyncParts03");
1666 RegisterNetSyncVariableInt("m_InteractedPartId");
1667 RegisterNetSyncVariableInt("m_PerformedActionId");
1668 RegisterNetSyncVariableBool("m_HasBase");
1669
1670 //Construction init
1672
1673 if (ConfigIsExisting("hybridAttachments"))
1674 {
1676 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1677 }
1678 if (ConfigIsExisting("mountables"))
1679 {
1681 ConfigGetTextArray("mountables", m_Mountables);
1682 }
1683
1684 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1685 }
1686
1687 override void EEDelete(EntityAI parent)
1688 {
1689 super.EEDelete(parent);
1690
1691 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1693
1694 }
1695
1696 override string GetInvulnerabilityTypeString()
1697 {
1698 return "disableBaseDamage";
1699 }
1700
1701 override bool CanObstruct()
1702 {
1703 return true;
1704 }
1705
1706 override int GetHideIconMask()
1707 {
1708 return EInventoryIconVisibility.HIDE_VICINITY;
1709 }
1710
1711 // --- SYNCHRONIZATION
1713 {
1714 if (GetGame().IsServer())
1715 SetSynchDirty();
1716 }
1717
1718 override void OnVariablesSynchronized()
1719 {
1720 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1721 super.OnVariablesSynchronized();
1722
1723 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1724 }
1725
1726 protected void OnSynchronizedClient()
1727 {
1728 //update parts
1730
1731 //update action on part
1733
1734 //update visuals (client)
1735 UpdateVisuals();
1736 }
1737
1738 //parts synchronization
1740 {
1741 //part_id must starts from index = 1
1742 int offset;
1743 int mask;
1744
1745 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1746 {
1747 offset = part_id - 1;
1748 mask = 1 << offset;
1749
1751 }
1752 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1753 {
1754 offset = (part_id % 32);
1755 mask = 1 << offset;
1756
1758 }
1759 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1760 {
1761 offset = (part_id % 63);
1762 mask = 1 << offset;
1763
1765 }
1766 }
1767
1769 {
1770 //part_id must starts from index = 1
1771 int offset;
1772 int mask;
1773
1774 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1775 {
1776 offset = part_id - 1;
1777 mask = 1 << offset;
1778
1780 }
1781 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1782 {
1783 offset = (part_id % 32);
1784 mask = 1 << offset;
1785
1787 }
1788 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1789 {
1790 offset = (part_id % 63);
1791 mask = 1 << offset;
1792
1794 }
1795 }
1796
1798 {
1799 //part_id must starts from index = 1
1800 int offset;
1801 int mask;
1802
1803 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1804 {
1805 offset = part_id - 1;
1806 mask = 1 << offset;
1807
1808 if ((m_SyncParts01 & mask) > 0)
1809 return true;
1810 }
1811 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1812 {
1813 offset = (part_id % 32);
1814 mask = 1 << offset;
1815
1816 if ((m_SyncParts02 & mask) > 0)
1817 return true;
1818 }
1819 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1820 {
1821 offset = (part_id % 63);
1822 mask = 1 << offset;
1823
1824 if ((m_SyncParts03 & mask) > 0)
1825 return true;
1826 }
1827
1828 return false;
1829 }
1830
1831 protected void RegisterActionForSync(int part_id, int action_id)
1832 {
1835 }
1836
1837 protected void ResetActionSyncData()
1838 {
1839 //reset data
1840 m_InteractedPartId = -1;
1842 }
1843
1844 protected void SetActionFromSyncData()
1845 {
1846 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1847 {
1850
1851 switch (build_action_id)
1852 {
1856 }
1857 }
1858 }
1859 //------
1860
1862 {
1863 string key = part.m_PartName;
1864 bool is_base = part.IsBase();
1866 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1868 {
1869 if (!part.IsBuilt())
1870 {
1871 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1872 GetConstruction().AddToConstructedParts(key);
1873 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1874
1875 if (is_base)
1876 {
1878 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1879 }
1880 }
1881 }
1882 else
1883 {
1884 if (part.IsBuilt())
1885 {
1886 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1887 GetConstruction().RemoveFromConstructedParts(key);
1888 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1889
1890 if (is_base)
1891 {
1893 AddProxyPhysics(ANIMATION_DEPLOYED);
1894 }
1895 }
1896 }
1897
1898 //check slot lock for material attachments
1899 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1900 }
1901
1902 //set construction parts based on synchronized data
1904 {
1907
1908 for (int i = 0; i < construction_parts.Count(); ++i)
1909 {
1910 string key = construction_parts.GetKey(i);
1913 }
1914
1915 //regenerate navmesh
1916 UpdateNavmesh();
1917 }
1918
1920 {
1923
1924 for (int i = 0; i < construction_parts.Count(); ++i)
1925 {
1926 string key = construction_parts.GetKey(i);
1928
1929 if (value.GetId() == id)
1930 return value;
1931 }
1932
1933 return NULL;
1934 }
1935 //
1936
1937 //Base
1938 bool HasBase()
1939 {
1940 return m_HasBase;
1941 }
1942
1943 void SetBaseState(bool has_base)
1944 {
1946 }
1947
1948 override bool IsDeployable()
1949 {
1950 return true;
1951 }
1952
1953 bool IsOpened()
1954 {
1955 return false;
1956 }
1957
1958 //--- CONSTRUCTION KIT
1960 {
1964
1965 return construction_kit;
1966 }
1967
1969 {
1970 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1973 }
1974
1975 protected vector GetKitSpawnPosition()
1976 {
1977 return GetPosition();
1978 }
1979
1980 protected string GetConstructionKitType()
1981 {
1982 return "";
1983 }
1984
1986 {
1988 GetGame().ObjectDelete(construction_kit);
1989 }
1990
1991 //--- CONSTRUCTION
1992 void DestroyConstruction()
1993 {
1994 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1995 GetGame().ObjectDelete(this);
1996 }
1997
1998 // --- EVENTS
1999 override void OnStoreSave(ParamsWriteContext ctx)
2000 {
2001 super.OnStoreSave(ctx);
2002
2003 //sync parts 01
2004 ctx.Write(m_SyncParts01);
2005 ctx.Write(m_SyncParts02);
2006 ctx.Write(m_SyncParts03);
2007
2008 ctx.Write(m_HasBase);
2009 }
2010
2011 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2012 {
2013 if (!super.OnStoreLoad(ctx, version))
2014 return false;
2015
2016 //--- Base building data ---
2017 //Restore synced parts data
2018 if (!ctx.Read(m_SyncParts01))
2019 {
2020 m_SyncParts01 = 0; //set default
2021 return false;
2022 }
2023 if (!ctx.Read(m_SyncParts02))
2024 {
2025 m_SyncParts02 = 0; //set default
2026 return false;
2027 }
2028 if (!ctx.Read(m_SyncParts03))
2029 {
2030 m_SyncParts03 = 0; //set default
2031 return false;
2032 }
2033
2034 //has base
2035 if (!ctx.Read(m_HasBase))
2036 {
2037 m_HasBase = false;
2038 return false;
2039 }
2040 //---
2041
2042 return true;
2043 }
2044
2045 override void AfterStoreLoad()
2046 {
2047 super.AfterStoreLoad();
2048
2051 }
2052
2054 {
2055 //update server data
2057
2058 //set base state
2059 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2060 SetBaseState(construction_part.IsBuilt()) ;
2061
2062 //synchronize after load
2064 }
2065
2066 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2067 {
2069 return;
2070
2071 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2072
2073 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2074 return;
2075
2077 string part_name = zone;
2078 part_name.ToLower();
2079
2081 {
2083
2084 if (construction_part && construction.IsPartConstructed(part_name))
2085 {
2086 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2087 construction.DestroyConnectedParts(part_name);
2088 }
2089
2090 //barbed wire handling (hack-ish)
2091 if (part_name.Contains("barbed"))
2092 {
2093 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2094 if (barbed_wire)
2095 barbed_wire.SetMountedState(false);
2096 }
2097 }
2098 }
2099
2100 override void EEOnAfterLoad()
2101 {
2103 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2104
2105 super.EEOnAfterLoad();
2106 }
2107
2108 override void EEInit()
2109 {
2110 super.EEInit();
2111
2112 // init visuals and physics
2113 InitBaseState();
2114
2115 //debug
2116#ifdef DEVELOPER
2118#endif
2119 }
2120
2121 override void EEItemAttached(EntityAI item, string slot_name)
2122 {
2123 super.EEItemAttached(item, slot_name);
2124
2126 UpdateVisuals();
2128 }
2129
2130 override void EEItemDetached(EntityAI item, string slot_name)
2131 {
2132 super.EEItemDetached(item, slot_name);
2133
2134 UpdateVisuals();
2136 }
2137
2138 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2139 {
2141 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2142
2145 }
2146
2147 //ignore out of reach condition
2148 override bool IgnoreOutOfReachCondition()
2149 {
2150 return true;
2151 }
2152
2153 //CONSTRUCTION EVENTS
2154 //Build
2155 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2156 {
2158
2159 //check base state
2160 if (construtionPart.IsBase())
2161 {
2162 SetBaseState(true);
2163
2164 //spawn kit
2166 }
2167
2168 //register constructed parts for synchronization
2170
2171 //register action that was performed on part
2173
2174 //synchronize
2176
2177 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2178
2179 UpdateNavmesh();
2180
2181 //update visuals
2182 UpdateVisuals();
2183
2184 //reset action sync data
2185 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2186 }
2187
2188 void OnPartBuiltClient(string part_name, int action_id)
2189 {
2190 //play sound
2192 }
2193
2194 //Dismantle
2196 {
2197 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2199
2200 //register constructed parts for synchronization
2202
2203 //register action that was performed on part
2205
2206 //synchronize
2208
2209 // server part of sync, client will be synced from SetPartsFromSyncData
2211
2212 UpdateNavmesh();
2213
2214 //update visuals
2215 UpdateVisuals();
2216
2217 //reset action sync data
2218 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2219
2220 //check base state
2221 if (construtionPart.IsBase())
2222 {
2223 //Destroy construction
2224 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2225 }
2226 }
2227
2229 {
2230 //play sound
2232 }
2233
2234 //Destroy
2236 {
2237 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2239
2240 //register constructed parts for synchronization
2242
2243 //register action that was performed on part
2245
2246 //synchronize
2248
2249 // server part of sync, client will be synced from SetPartsFromSyncData
2251
2252 UpdateNavmesh();
2253
2254 //update visuals
2255 UpdateVisuals();
2256
2257 //reset action sync data
2258 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2259
2260 //check base state
2261 if (construtionPart.IsBase())
2262 {
2263 //Destroy construction
2264 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2265 }
2266 }
2267
2268 void OnPartDestroyedClient(string part_name, int action_id)
2269 {
2270 //play sound
2272 }
2273
2274 // --- UPDATE
2275 void InitBaseState()
2276 {
2277 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2278
2279 InitVisuals();
2280 UpdateNavmesh(); //regenerate navmesh
2281 GetConstruction().InitBaseState();
2282 }
2283
2284 void InitVisuals()
2285 {
2286 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2287 //check base
2288 if (!HasBase())
2289 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2290 else
2291 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2292
2293 GetConstruction().UpdateVisuals();
2294 }
2295
2296 void UpdateVisuals()
2297 {
2299
2301 foreach (string slotName : attachmentSlots)
2303
2304 //check base
2305 if (!HasBase())
2306 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2307 else
2308 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2309
2310 GetConstruction().UpdateVisuals();
2311 }
2312
2314 {
2315 string slotNameMounted = slot_name + "_Mounted";
2316 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2317
2318 if (attachment)
2319 {
2320 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2321 if (barbedWire && barbedWire.IsMounted())
2323 else
2325
2326 if (is_locked)
2327 {
2328 SetAnimationPhase(slotNameMounted, 0);
2329 SetAnimationPhase(slot_name, 1);
2330 }
2331 else
2332 {
2333 SetAnimationPhase(slotNameMounted, 1);
2334 SetAnimationPhase(slot_name, 0);
2335 }
2336 }
2337 else
2338 {
2339 SetAnimationPhase(slotNameMounted, 1);
2340 SetAnimationPhase(slot_name, 1);
2341
2343 }
2344 }
2345
2346 // avoid calling this function on frequent occasions, it's a massive performance hit
2347 void UpdatePhysics()
2348 {
2350 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2351
2354
2356 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2357
2358 foreach (string slotName : attachmentSlots)
2360
2361 //check base
2362 if (!HasBase())
2363 {
2365 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2366
2367 AddProxyPhysics(ANIMATION_DEPLOYED);
2368 }
2369 else
2370 {
2372 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2373
2374 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2375 }
2376
2377 GetConstruction().UpdatePhysics();
2378 UpdateNavmesh();
2379 }
2380
2382 {
2383 //checks for invalid appends; hotfix
2384 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2385 return;
2386 //----------------------------------
2387 string slot_name_mounted = slot_name + "_Mounted";
2388 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2389
2390 //remove proxy physics
2391 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2392 RemoveProxyPhysics(slot_name_mounted);
2393 RemoveProxyPhysics(slot_name);
2394
2395 if (attachment)
2396 {
2397 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2398 if (is_locked)
2399 {
2400 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2401 AddProxyPhysics(slot_name_mounted);
2402 }
2403 else
2404 {
2405 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2406 AddProxyPhysics(slot_name);
2407 }
2408 }
2409 }
2410
2411 protected void UpdateNavmesh()
2412 {
2413 SetAffectPathgraph(true, false);
2414 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2415 }
2416
2417 override bool CanUseConstruction()
2418 {
2419 return true;
2420 }
2421
2422 override bool CanUseConstructionBuild()
2423 {
2424 return true;
2425 }
2426
2428 {
2429 if (attachment)
2430 {
2432 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2433
2434 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2435 }
2436
2437 return false;
2438 }
2439
2440 protected bool IsAttachmentSlotLocked(string slot_name)
2441 {
2442 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2443 }
2444
2445 //--- ATTACHMENT SLOTS
2447 {
2448 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2449 if (GetGame().ConfigIsExisting(config_path))
2450 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2451 }
2452
2454 {
2455 return true;
2456 }
2457
2458 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2459 {
2460 return true;
2461 }
2462
2463 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2464 {
2465 return true;
2466 }
2467
2468 // --- INIT
2469 void ConstructionInit()
2470 {
2471 if (!m_Construction)
2472 m_Construction = new Construction(this);
2473
2474 GetConstruction().Init();
2475 }
2476
2478 {
2479 return m_Construction;
2480 }
2481
2482 //--- INVENTORY/ATTACHMENTS CONDITIONS
2483 //attachments
2485 {
2486 return super.CanReceiveAttachment(attachment, slotId);
2487 }
2488
2490 {
2491 int attachment_count = GetInventory().AttachmentCount();
2492 if (attachment_count > 0)
2493 {
2494 if (HasBase() && attachment_count == 1)
2495 return false;
2496
2497 return true;
2498 }
2499
2500 return false;
2501 }
2502
2503 override bool ShowZonesHealth()
2504 {
2505 return true;
2506 }
2507
2508 //this into/outo parent.Cargo
2509 override bool CanPutInCargo(EntityAI parent)
2510 {
2511 return false;
2512 }
2513
2514 override bool CanRemoveFromCargo(EntityAI parent)
2515 {
2516 return false;
2517 }
2518
2519 //hands
2520 override bool CanPutIntoHands(EntityAI parent)
2521 {
2522 return false;
2523 }
2524
2525 //--- ACTION CONDITIONS
2526 //direction
2527 override bool IsFacingPlayer(PlayerBase player, string selection)
2528 {
2529 return true;
2530 }
2531
2532 override bool IsPlayerInside(PlayerBase player, string selection)
2533 {
2534 return true;
2535 }
2536
2539 {
2540 return false;
2541 }
2542
2543 //camera direction check
2544 bool IsFacingCamera(string selection)
2545 {
2546 return true;
2547 }
2548
2549 //roof check
2551 {
2552 return false;
2553 }
2554
2555 //selection->player distance check
2556 bool HasProperDistance(string selection, PlayerBase player)
2557 {
2558 return true;
2559 }
2560
2561 //folding
2563 {
2564 if (HasBase() || GetInventory().AttachmentCount() > 0)
2565 return false;
2566
2567 return true;
2568 }
2569
2571 {
2574
2575 return item;
2576 }
2577
2578 //Damage triggers (barbed wire)
2579 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2580 {
2581 if (GetGame() && GetGame().IsServer())
2582 {
2583 //destroy area damage if some already exists
2585
2586 //create new area damage
2588 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2589
2590 vector min_max[2];
2591 if (MemoryPointExists(slot_name + "_min"))
2592 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2593 if (MemoryPointExists(slot_name + "_max"))
2594 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2595
2596 //get proper trigger extents (min<max)
2597 vector extents[2];
2598 GetConstruction().GetTriggerExtents(min_max, extents);
2599
2600 //get box center
2601 vector center;
2602 center = GetConstruction().GetBoxCenter(min_max);
2603 center = ModelToWorld(center);
2604
2605 //rotate center if needed
2608
2609 areaDamage.SetExtents(extents[0], extents[1]);
2610 areaDamage.SetAreaPosition(center);
2611 areaDamage.SetAreaOrientation(orientation);
2612 areaDamage.SetLoopInterval(1.0);
2613 areaDamage.SetDeferDuration(0.2);
2614 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2615 areaDamage.SetAmmoName("BarbedWireHit");
2616 areaDamage.Spawn();
2617
2619 }
2620 }
2621
2623 {
2624 if (angle_deg != 0)
2625 {
2626 //orientation
2628
2629 //center
2631 if (MemoryPointExists("rotate_axis"))
2632 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2635 center[0] = r_center_x;
2636 center[2] = r_center_z;
2637 }
2638 }
2639
2640 void DestroyAreaDamage(string slot_name)
2641 {
2642 if (GetGame() && GetGame().IsServer())
2643 {
2646 {
2647 if (areaDamage)
2648 areaDamage.Destroy();
2649
2651 }
2652 }
2653 }
2654
2655 override bool IsIgnoredByConstruction()
2656 {
2657 return true;
2658 }
2659
2660 //================================================================
2661 // SOUNDS
2662 //================================================================
2663 protected void SoundBuildStart(string part_name)
2664 {
2665 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2666 }
2667
2668 protected void SoundDismantleStart(string part_name)
2669 {
2670 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2671 }
2672
2673 protected void SoundDestroyStart(string part_name)
2674 {
2675 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2676 }
2677
2678 protected string GetBuildSoundByMaterial(string part_name)
2679 {
2681
2682 switch (material_type)
2683 {
2684 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2685 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2686 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2687 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2688 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2689 }
2690
2691 return "";
2692 }
2693
2694 protected string GetDismantleSoundByMaterial(string part_name)
2695 {
2697
2698 switch (material_type)
2699 {
2700 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2701 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2702 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2703 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2704 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2705 }
2706
2707 return "";
2708 }
2709
2710 //misc
2712 {
2713 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2714 {
2715 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2717 SetHealth(slot_name, "Health", item.GetHealth());
2718 }
2719 }
2720
2721 override int GetDamageSystemVersionChange()
2722 {
2723 return 111;
2724 }
2725
2726 override void SetActions()
2727 {
2728 super.SetActions();
2729
2731 //AddAction(ActionTakeHybridAttachment);
2732 //AddAction(ActionTakeHybridAttachmentToHands);
2735 }
2736
2737 //================================================================
2738 // DEBUG
2739 //================================================================
2740 protected void DebugCustomState()
2741 {
2742 }
2743
2746 {
2747 return null;
2748 }
2749
2750 override void OnDebugSpawn()
2751 {
2752 FullyBuild();
2753 }
2754
2755 void FullyBuild()
2756 {
2758 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2759
2760 Man p;
2761
2762#ifdef SERVER
2764 GetGame().GetWorld().GetPlayerList(players);
2765 if (players.Count())
2766 p = players[0];
2767#else
2768 p = GetGame().GetPlayer();
2769#endif
2770
2771 foreach (ConstructionPart part : parts)
2772 {
2773 bool excluded = false;
2774 string partName = part.GetPartName();
2775 if (excludes)
2776 {
2777 foreach (string exclude : excludes)
2778 {
2779 if (partName.Contains(exclude))
2780 {
2781 excluded = true;
2782 break;
2783 }
2784 }
2785 }
2786
2787 if (!excluded)
2789 }
2790
2791 GetConstruction().UpdateVisuals();
2792 }
2793}
2794
2795void bsbDebugPrint(string s)
2796{
2797#ifdef BSB_DEBUG
2798 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2799#else
2800 //Print("" + s); // comment/uncomment to hide/see debug logs
2801#endif
2802}
2803void bsbDebugSpam(string s)
2804{
2805#ifdef BSB_DEBUG_SPAM
2806 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2807#else
2808 //Print("" + s); // comment/uncomment to hide/see debug logs
2809#endif
2810}

◆ EEInit()

override void bsbDebugPrint::EEInit ( )
protected

Definition at line 1662 of file BaseBuildingBase.c.

1664{
1665 const string ANIMATION_DEPLOYED = "Deployed";
1666
1667 float m_ConstructionKitHealth; //stored health value for used construction kit
1668
1670
1671 bool m_HasBase;
1672 //variables for synchronization of base building parts (2x31 is the current limit)
1673 int m_SyncParts01; //synchronization for already built parts (31 parts)
1674 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1675 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1676 int m_InteractedPartId; //construction part id that an action was performed on
1677 int m_PerformedActionId; //action id that was performed on a construction part
1678
1679 //Sounds
1680 //build
1681 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1682 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1683 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1684 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1685 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1686 //dismantle
1687 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1688 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1689 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1690 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1691 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1692
1693 protected EffectSound m_Sound;
1694
1698
1699 // Constructor
1700 void BaseBuildingBase()
1701 {
1703
1704 //synchronized variables
1705 RegisterNetSyncVariableInt("m_SyncParts01");
1706 RegisterNetSyncVariableInt("m_SyncParts02");
1707 RegisterNetSyncVariableInt("m_SyncParts03");
1708 RegisterNetSyncVariableInt("m_InteractedPartId");
1709 RegisterNetSyncVariableInt("m_PerformedActionId");
1710 RegisterNetSyncVariableBool("m_HasBase");
1711
1712 //Construction init
1714
1715 if (ConfigIsExisting("hybridAttachments"))
1716 {
1718 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1719 }
1720 if (ConfigIsExisting("mountables"))
1721 {
1723 ConfigGetTextArray("mountables", m_Mountables);
1724 }
1725
1726 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1727 }
1728
1729 override void EEDelete(EntityAI parent)
1730 {
1731 super.EEDelete(parent);
1732
1733 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1735
1736 }
1737
1738 override string GetInvulnerabilityTypeString()
1739 {
1740 return "disableBaseDamage";
1741 }
1742
1743 override bool CanObstruct()
1744 {
1745 return true;
1746 }
1747
1748 override int GetHideIconMask()
1749 {
1750 return EInventoryIconVisibility.HIDE_VICINITY;
1751 }
1752
1753 // --- SYNCHRONIZATION
1755 {
1756 if (GetGame().IsServer())
1757 SetSynchDirty();
1758 }
1759
1760 override void OnVariablesSynchronized()
1761 {
1762 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1763 super.OnVariablesSynchronized();
1764
1765 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1766 }
1767
1768 protected void OnSynchronizedClient()
1769 {
1770 //update parts
1772
1773 //update action on part
1775
1776 //update visuals (client)
1777 UpdateVisuals();
1778 }
1779
1780 //parts synchronization
1782 {
1783 //part_id must starts from index = 1
1784 int offset;
1785 int mask;
1786
1787 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1788 {
1789 offset = part_id - 1;
1790 mask = 1 << offset;
1791
1793 }
1794 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1795 {
1796 offset = (part_id % 32);
1797 mask = 1 << offset;
1798
1800 }
1801 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1802 {
1803 offset = (part_id % 63);
1804 mask = 1 << offset;
1805
1807 }
1808 }
1809
1811 {
1812 //part_id must starts from index = 1
1813 int offset;
1814 int mask;
1815
1816 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1817 {
1818 offset = part_id - 1;
1819 mask = 1 << offset;
1820
1822 }
1823 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1824 {
1825 offset = (part_id % 32);
1826 mask = 1 << offset;
1827
1829 }
1830 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1831 {
1832 offset = (part_id % 63);
1833 mask = 1 << offset;
1834
1836 }
1837 }
1838
1840 {
1841 //part_id must starts from index = 1
1842 int offset;
1843 int mask;
1844
1845 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1846 {
1847 offset = part_id - 1;
1848 mask = 1 << offset;
1849
1850 if ((m_SyncParts01 & mask) > 0)
1851 return true;
1852 }
1853 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1854 {
1855 offset = (part_id % 32);
1856 mask = 1 << offset;
1857
1858 if ((m_SyncParts02 & mask) > 0)
1859 return true;
1860 }
1861 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1862 {
1863 offset = (part_id % 63);
1864 mask = 1 << offset;
1865
1866 if ((m_SyncParts03 & mask) > 0)
1867 return true;
1868 }
1869
1870 return false;
1871 }
1872
1873 protected void RegisterActionForSync(int part_id, int action_id)
1874 {
1877 }
1878
1879 protected void ResetActionSyncData()
1880 {
1881 //reset data
1882 m_InteractedPartId = -1;
1884 }
1885
1886 protected void SetActionFromSyncData()
1887 {
1888 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1889 {
1892
1893 switch (build_action_id)
1894 {
1898 }
1899 }
1900 }
1901 //------
1902
1904 {
1905 string key = part.m_PartName;
1906 bool is_base = part.IsBase();
1908 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1910 {
1911 if (!part.IsBuilt())
1912 {
1913 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1914 GetConstruction().AddToConstructedParts(key);
1915 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1916
1917 if (is_base)
1918 {
1920 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1921 }
1922 }
1923 }
1924 else
1925 {
1926 if (part.IsBuilt())
1927 {
1928 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1929 GetConstruction().RemoveFromConstructedParts(key);
1930 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1931
1932 if (is_base)
1933 {
1935 AddProxyPhysics(ANIMATION_DEPLOYED);
1936 }
1937 }
1938 }
1939
1940 //check slot lock for material attachments
1941 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1942 }
1943
1944 //set construction parts based on synchronized data
1946 {
1949
1950 for (int i = 0; i < construction_parts.Count(); ++i)
1951 {
1952 string key = construction_parts.GetKey(i);
1955 }
1956
1957 //regenerate navmesh
1958 UpdateNavmesh();
1959 }
1960
1962 {
1965
1966 for (int i = 0; i < construction_parts.Count(); ++i)
1967 {
1968 string key = construction_parts.GetKey(i);
1970
1971 if (value.GetId() == id)
1972 return value;
1973 }
1974
1975 return NULL;
1976 }
1977 //
1978
1979 //Base
1980 bool HasBase()
1981 {
1982 return m_HasBase;
1983 }
1984
1985 void SetBaseState(bool has_base)
1986 {
1988 }
1989
1990 override bool IsDeployable()
1991 {
1992 return true;
1993 }
1994
1995 bool IsOpened()
1996 {
1997 return false;
1998 }
1999
2000 //--- CONSTRUCTION KIT
2002 {
2006
2007 return construction_kit;
2008 }
2009
2011 {
2012 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2015 }
2016
2017 protected vector GetKitSpawnPosition()
2018 {
2019 return GetPosition();
2020 }
2021
2022 protected string GetConstructionKitType()
2023 {
2024 return "";
2025 }
2026
2028 {
2030 GetGame().ObjectDelete(construction_kit);
2031 }
2032
2033 //--- CONSTRUCTION
2034 void DestroyConstruction()
2035 {
2036 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2037 GetGame().ObjectDelete(this);
2038 }
2039
2040 // --- EVENTS
2041 override void OnStoreSave(ParamsWriteContext ctx)
2042 {
2043 super.OnStoreSave(ctx);
2044
2045 //sync parts 01
2046 ctx.Write(m_SyncParts01);
2047 ctx.Write(m_SyncParts02);
2048 ctx.Write(m_SyncParts03);
2049
2050 ctx.Write(m_HasBase);
2051 }
2052
2053 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2054 {
2055 if (!super.OnStoreLoad(ctx, version))
2056 return false;
2057
2058 //--- Base building data ---
2059 //Restore synced parts data
2060 if (!ctx.Read(m_SyncParts01))
2061 {
2062 m_SyncParts01 = 0; //set default
2063 return false;
2064 }
2065 if (!ctx.Read(m_SyncParts02))
2066 {
2067 m_SyncParts02 = 0; //set default
2068 return false;
2069 }
2070 if (!ctx.Read(m_SyncParts03))
2071 {
2072 m_SyncParts03 = 0; //set default
2073 return false;
2074 }
2075
2076 //has base
2077 if (!ctx.Read(m_HasBase))
2078 {
2079 m_HasBase = false;
2080 return false;
2081 }
2082 //---
2083
2084 return true;
2085 }
2086
2087 override void AfterStoreLoad()
2088 {
2089 super.AfterStoreLoad();
2090
2093 }
2094
2096 {
2097 //update server data
2099
2100 //set base state
2101 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2102 SetBaseState(construction_part.IsBuilt()) ;
2103
2104 //synchronize after load
2106 }
2107
2108 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2109 {
2111 return;
2112
2113 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2114
2115 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2116 return;
2117
2119 string part_name = zone;
2120 part_name.ToLower();
2121
2123 {
2125
2126 if (construction_part && construction.IsPartConstructed(part_name))
2127 {
2128 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2129 construction.DestroyConnectedParts(part_name);
2130 }
2131
2132 //barbed wire handling (hack-ish)
2133 if (part_name.Contains("barbed"))
2134 {
2135 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2136 if (barbed_wire)
2137 barbed_wire.SetMountedState(false);
2138 }
2139 }
2140 }
2141
2142 override void EEOnAfterLoad()
2143 {
2145 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2146
2147 super.EEOnAfterLoad();
2148 }
2149
2150 override void EEInit()
2151 {
2152 super.EEInit();
2153
2154 // init visuals and physics
2155 InitBaseState();
2156
2157 //debug
2158#ifdef DEVELOPER
2160#endif
2161 }
2162
2163 override void EEItemAttached(EntityAI item, string slot_name)
2164 {
2165 super.EEItemAttached(item, slot_name);
2166
2168 UpdateVisuals();
2170 }
2171
2172 override void EEItemDetached(EntityAI item, string slot_name)
2173 {
2174 super.EEItemDetached(item, slot_name);
2175
2176 UpdateVisuals();
2178 }
2179
2180 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2181 {
2183 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2184
2187 }
2188
2189 //ignore out of reach condition
2190 override bool IgnoreOutOfReachCondition()
2191 {
2192 return true;
2193 }
2194
2195 //CONSTRUCTION EVENTS
2196 //Build
2197 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2198 {
2200
2201 //check base state
2202 if (construtionPart.IsBase())
2203 {
2204 SetBaseState(true);
2205
2206 //spawn kit
2208 }
2209
2210 //register constructed parts for synchronization
2212
2213 //register action that was performed on part
2215
2216 //synchronize
2218
2219 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2220
2221 UpdateNavmesh();
2222
2223 //update visuals
2224 UpdateVisuals();
2225
2226 //reset action sync data
2227 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2228 }
2229
2230 void OnPartBuiltClient(string part_name, int action_id)
2231 {
2232 //play sound
2234 }
2235
2236 //Dismantle
2238 {
2239 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2241
2242 //register constructed parts for synchronization
2244
2245 //register action that was performed on part
2247
2248 //synchronize
2250
2251 // server part of sync, client will be synced from SetPartsFromSyncData
2253
2254 UpdateNavmesh();
2255
2256 //update visuals
2257 UpdateVisuals();
2258
2259 //reset action sync data
2260 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2261
2262 //check base state
2263 if (construtionPart.IsBase())
2264 {
2265 //Destroy construction
2266 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2267 }
2268 }
2269
2271 {
2272 //play sound
2274 }
2275
2276 //Destroy
2278 {
2279 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2281
2282 //register constructed parts for synchronization
2284
2285 //register action that was performed on part
2287
2288 //synchronize
2290
2291 // server part of sync, client will be synced from SetPartsFromSyncData
2293
2294 UpdateNavmesh();
2295
2296 //update visuals
2297 UpdateVisuals();
2298
2299 //reset action sync data
2300 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2301
2302 //check base state
2303 if (construtionPart.IsBase())
2304 {
2305 //Destroy construction
2306 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2307 }
2308 }
2309
2310 void OnPartDestroyedClient(string part_name, int action_id)
2311 {
2312 //play sound
2314 }
2315
2316 // --- UPDATE
2317 void InitBaseState()
2318 {
2319 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2320
2321 InitVisuals();
2322 UpdateNavmesh(); //regenerate navmesh
2323 GetConstruction().InitBaseState();
2324 }
2325
2326 void InitVisuals()
2327 {
2328 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2329 //check base
2330 if (!HasBase())
2331 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2332 else
2333 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2334
2335 GetConstruction().UpdateVisuals();
2336 }
2337
2338 void UpdateVisuals()
2339 {
2341
2343 foreach (string slotName : attachmentSlots)
2345
2346 //check base
2347 if (!HasBase())
2348 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2349 else
2350 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2351
2352 GetConstruction().UpdateVisuals();
2353 }
2354
2356 {
2357 string slotNameMounted = slot_name + "_Mounted";
2358 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2359
2360 if (attachment)
2361 {
2362 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2363 if (barbedWire && barbedWire.IsMounted())
2365 else
2367
2368 if (is_locked)
2369 {
2370 SetAnimationPhase(slotNameMounted, 0);
2371 SetAnimationPhase(slot_name, 1);
2372 }
2373 else
2374 {
2375 SetAnimationPhase(slotNameMounted, 1);
2376 SetAnimationPhase(slot_name, 0);
2377 }
2378 }
2379 else
2380 {
2381 SetAnimationPhase(slotNameMounted, 1);
2382 SetAnimationPhase(slot_name, 1);
2383
2385 }
2386 }
2387
2388 // avoid calling this function on frequent occasions, it's a massive performance hit
2389 void UpdatePhysics()
2390 {
2392 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2393
2396
2398 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2399
2400 foreach (string slotName : attachmentSlots)
2402
2403 //check base
2404 if (!HasBase())
2405 {
2407 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2408
2409 AddProxyPhysics(ANIMATION_DEPLOYED);
2410 }
2411 else
2412 {
2414 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2415
2416 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2417 }
2418
2419 GetConstruction().UpdatePhysics();
2420 UpdateNavmesh();
2421 }
2422
2424 {
2425 //checks for invalid appends; hotfix
2426 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2427 return;
2428 //----------------------------------
2429 string slot_name_mounted = slot_name + "_Mounted";
2430 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2431
2432 //remove proxy physics
2433 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2434 RemoveProxyPhysics(slot_name_mounted);
2435 RemoveProxyPhysics(slot_name);
2436
2437 if (attachment)
2438 {
2439 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2440 if (is_locked)
2441 {
2442 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2443 AddProxyPhysics(slot_name_mounted);
2444 }
2445 else
2446 {
2447 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2448 AddProxyPhysics(slot_name);
2449 }
2450 }
2451 }
2452
2453 protected void UpdateNavmesh()
2454 {
2455 SetAffectPathgraph(true, false);
2456 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2457 }
2458
2459 override bool CanUseConstruction()
2460 {
2461 return true;
2462 }
2463
2464 override bool CanUseConstructionBuild()
2465 {
2466 return true;
2467 }
2468
2470 {
2471 if (attachment)
2472 {
2474 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2475
2476 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2477 }
2478
2479 return false;
2480 }
2481
2482 protected bool IsAttachmentSlotLocked(string slot_name)
2483 {
2484 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2485 }
2486
2487 //--- ATTACHMENT SLOTS
2489 {
2490 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2491 if (GetGame().ConfigIsExisting(config_path))
2492 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2493 }
2494
2496 {
2497 return true;
2498 }
2499
2500 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2501 {
2502 return true;
2503 }
2504
2505 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2506 {
2507 return true;
2508 }
2509
2510 // --- INIT
2511 void ConstructionInit()
2512 {
2513 if (!m_Construction)
2514 m_Construction = new Construction(this);
2515
2516 GetConstruction().Init();
2517 }
2518
2520 {
2521 return m_Construction;
2522 }
2523
2524 //--- INVENTORY/ATTACHMENTS CONDITIONS
2525 //attachments
2527 {
2528 return super.CanReceiveAttachment(attachment, slotId);
2529 }
2530
2532 {
2533 int attachment_count = GetInventory().AttachmentCount();
2534 if (attachment_count > 0)
2535 {
2536 if (HasBase() && attachment_count == 1)
2537 return false;
2538
2539 return true;
2540 }
2541
2542 return false;
2543 }
2544
2545 override bool ShowZonesHealth()
2546 {
2547 return true;
2548 }
2549
2550 //this into/outo parent.Cargo
2551 override bool CanPutInCargo(EntityAI parent)
2552 {
2553 return false;
2554 }
2555
2556 override bool CanRemoveFromCargo(EntityAI parent)
2557 {
2558 return false;
2559 }
2560
2561 //hands
2562 override bool CanPutIntoHands(EntityAI parent)
2563 {
2564 return false;
2565 }
2566
2567 //--- ACTION CONDITIONS
2568 //direction
2569 override bool IsFacingPlayer(PlayerBase player, string selection)
2570 {
2571 return true;
2572 }
2573
2574 override bool IsPlayerInside(PlayerBase player, string selection)
2575 {
2576 return true;
2577 }
2578
2581 {
2582 return false;
2583 }
2584
2585 //camera direction check
2586 bool IsFacingCamera(string selection)
2587 {
2588 return true;
2589 }
2590
2591 //roof check
2593 {
2594 return false;
2595 }
2596
2597 //selection->player distance check
2598 bool HasProperDistance(string selection, PlayerBase player)
2599 {
2600 return true;
2601 }
2602
2603 //folding
2605 {
2606 if (HasBase() || GetInventory().AttachmentCount() > 0)
2607 return false;
2608
2609 return true;
2610 }
2611
2613 {
2616
2617 return item;
2618 }
2619
2620 //Damage triggers (barbed wire)
2621 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2622 {
2623 if (GetGame() && GetGame().IsServer())
2624 {
2625 //destroy area damage if some already exists
2627
2628 //create new area damage
2630 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2631
2632 vector min_max[2];
2633 if (MemoryPointExists(slot_name + "_min"))
2634 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2635 if (MemoryPointExists(slot_name + "_max"))
2636 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2637
2638 //get proper trigger extents (min<max)
2639 vector extents[2];
2640 GetConstruction().GetTriggerExtents(min_max, extents);
2641
2642 //get box center
2643 vector center;
2644 center = GetConstruction().GetBoxCenter(min_max);
2645 center = ModelToWorld(center);
2646
2647 //rotate center if needed
2650
2651 areaDamage.SetExtents(extents[0], extents[1]);
2652 areaDamage.SetAreaPosition(center);
2653 areaDamage.SetAreaOrientation(orientation);
2654 areaDamage.SetLoopInterval(1.0);
2655 areaDamage.SetDeferDuration(0.2);
2656 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2657 areaDamage.SetAmmoName("BarbedWireHit");
2658 areaDamage.Spawn();
2659
2661 }
2662 }
2663
2665 {
2666 if (angle_deg != 0)
2667 {
2668 //orientation
2670
2671 //center
2673 if (MemoryPointExists("rotate_axis"))
2674 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2677 center[0] = r_center_x;
2678 center[2] = r_center_z;
2679 }
2680 }
2681
2682 void DestroyAreaDamage(string slot_name)
2683 {
2684 if (GetGame() && GetGame().IsServer())
2685 {
2688 {
2689 if (areaDamage)
2690 areaDamage.Destroy();
2691
2693 }
2694 }
2695 }
2696
2697 override bool IsIgnoredByConstruction()
2698 {
2699 return true;
2700 }
2701
2702 //================================================================
2703 // SOUNDS
2704 //================================================================
2705 protected void SoundBuildStart(string part_name)
2706 {
2707 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2708 }
2709
2710 protected void SoundDismantleStart(string part_name)
2711 {
2712 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2713 }
2714
2715 protected void SoundDestroyStart(string part_name)
2716 {
2717 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2718 }
2719
2720 protected string GetBuildSoundByMaterial(string part_name)
2721 {
2723
2724 switch (material_type)
2725 {
2726 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2727 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2728 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2729 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2730 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2731 }
2732
2733 return "";
2734 }
2735
2736 protected string GetDismantleSoundByMaterial(string part_name)
2737 {
2739
2740 switch (material_type)
2741 {
2742 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2743 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2744 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2745 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2746 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2747 }
2748
2749 return "";
2750 }
2751
2752 //misc
2754 {
2755 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2756 {
2757 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2759 SetHealth(slot_name, "Health", item.GetHealth());
2760 }
2761 }
2762
2763 override int GetDamageSystemVersionChange()
2764 {
2765 return 111;
2766 }
2767
2768 override void SetActions()
2769 {
2770 super.SetActions();
2771
2773 //AddAction(ActionTakeHybridAttachment);
2774 //AddAction(ActionTakeHybridAttachmentToHands);
2777 }
2778
2779 //================================================================
2780 // DEBUG
2781 //================================================================
2782 protected void DebugCustomState()
2783 {
2784 }
2785
2788 {
2789 return null;
2790 }
2791
2792 override void OnDebugSpawn()
2793 {
2794 FullyBuild();
2795 }
2796
2797 void FullyBuild()
2798 {
2800 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2801
2802 Man p;
2803
2804#ifdef SERVER
2806 GetGame().GetWorld().GetPlayerList(players);
2807 if (players.Count())
2808 p = players[0];
2809#else
2810 p = GetGame().GetPlayer();
2811#endif
2812
2813 foreach (ConstructionPart part : parts)
2814 {
2815 bool excluded = false;
2816 string partName = part.GetPartName();
2817 if (excludes)
2818 {
2819 foreach (string exclude : excludes)
2820 {
2821 if (partName.Contains(exclude))
2822 {
2823 excluded = true;
2824 break;
2825 }
2826 }
2827 }
2828
2829 if (!excluded)
2831 }
2832
2833 GetConstruction().UpdateVisuals();
2834 }
2835}
2836
2837void bsbDebugPrint(string s)
2838{
2839#ifdef BSB_DEBUG
2840 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2841#else
2842 //Print("" + s); // comment/uncomment to hide/see debug logs
2843#endif
2844}
2845void bsbDebugSpam(string s)
2846{
2847#ifdef BSB_DEBUG_SPAM
2848 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2849#else
2850 //Print("" + s); // comment/uncomment to hide/see debug logs
2851#endif
2852}

◆ EEItemAttached()

override void bsbDebugPrint::EEItemAttached ( EntityAI item,
string slot_name )
protected

Definition at line 1675 of file BaseBuildingBase.c.

1677{
1678 const string ANIMATION_DEPLOYED = "Deployed";
1679
1680 float m_ConstructionKitHealth; //stored health value for used construction kit
1681
1683
1684 bool m_HasBase;
1685 //variables for synchronization of base building parts (2x31 is the current limit)
1686 int m_SyncParts01; //synchronization for already built parts (31 parts)
1687 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1688 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1689 int m_InteractedPartId; //construction part id that an action was performed on
1690 int m_PerformedActionId; //action id that was performed on a construction part
1691
1692 //Sounds
1693 //build
1694 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1695 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1696 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1697 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1698 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1699 //dismantle
1700 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1701 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1702 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1703 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1704 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1705
1706 protected EffectSound m_Sound;
1707
1711
1712 // Constructor
1713 void BaseBuildingBase()
1714 {
1716
1717 //synchronized variables
1718 RegisterNetSyncVariableInt("m_SyncParts01");
1719 RegisterNetSyncVariableInt("m_SyncParts02");
1720 RegisterNetSyncVariableInt("m_SyncParts03");
1721 RegisterNetSyncVariableInt("m_InteractedPartId");
1722 RegisterNetSyncVariableInt("m_PerformedActionId");
1723 RegisterNetSyncVariableBool("m_HasBase");
1724
1725 //Construction init
1727
1728 if (ConfigIsExisting("hybridAttachments"))
1729 {
1731 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1732 }
1733 if (ConfigIsExisting("mountables"))
1734 {
1736 ConfigGetTextArray("mountables", m_Mountables);
1737 }
1738
1739 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1740 }
1741
1742 override void EEDelete(EntityAI parent)
1743 {
1744 super.EEDelete(parent);
1745
1746 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1748
1749 }
1750
1751 override string GetInvulnerabilityTypeString()
1752 {
1753 return "disableBaseDamage";
1754 }
1755
1756 override bool CanObstruct()
1757 {
1758 return true;
1759 }
1760
1761 override int GetHideIconMask()
1762 {
1763 return EInventoryIconVisibility.HIDE_VICINITY;
1764 }
1765
1766 // --- SYNCHRONIZATION
1768 {
1769 if (GetGame().IsServer())
1770 SetSynchDirty();
1771 }
1772
1773 override void OnVariablesSynchronized()
1774 {
1775 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1776 super.OnVariablesSynchronized();
1777
1778 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1779 }
1780
1781 protected void OnSynchronizedClient()
1782 {
1783 //update parts
1785
1786 //update action on part
1788
1789 //update visuals (client)
1790 UpdateVisuals();
1791 }
1792
1793 //parts synchronization
1795 {
1796 //part_id must starts from index = 1
1797 int offset;
1798 int mask;
1799
1800 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1801 {
1802 offset = part_id - 1;
1803 mask = 1 << offset;
1804
1806 }
1807 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1808 {
1809 offset = (part_id % 32);
1810 mask = 1 << offset;
1811
1813 }
1814 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1815 {
1816 offset = (part_id % 63);
1817 mask = 1 << offset;
1818
1820 }
1821 }
1822
1824 {
1825 //part_id must starts from index = 1
1826 int offset;
1827 int mask;
1828
1829 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1830 {
1831 offset = part_id - 1;
1832 mask = 1 << offset;
1833
1835 }
1836 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1837 {
1838 offset = (part_id % 32);
1839 mask = 1 << offset;
1840
1842 }
1843 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1844 {
1845 offset = (part_id % 63);
1846 mask = 1 << offset;
1847
1849 }
1850 }
1851
1853 {
1854 //part_id must starts from index = 1
1855 int offset;
1856 int mask;
1857
1858 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1859 {
1860 offset = part_id - 1;
1861 mask = 1 << offset;
1862
1863 if ((m_SyncParts01 & mask) > 0)
1864 return true;
1865 }
1866 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1867 {
1868 offset = (part_id % 32);
1869 mask = 1 << offset;
1870
1871 if ((m_SyncParts02 & mask) > 0)
1872 return true;
1873 }
1874 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1875 {
1876 offset = (part_id % 63);
1877 mask = 1 << offset;
1878
1879 if ((m_SyncParts03 & mask) > 0)
1880 return true;
1881 }
1882
1883 return false;
1884 }
1885
1886 protected void RegisterActionForSync(int part_id, int action_id)
1887 {
1890 }
1891
1892 protected void ResetActionSyncData()
1893 {
1894 //reset data
1895 m_InteractedPartId = -1;
1897 }
1898
1899 protected void SetActionFromSyncData()
1900 {
1901 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1902 {
1905
1906 switch (build_action_id)
1907 {
1911 }
1912 }
1913 }
1914 //------
1915
1917 {
1918 string key = part.m_PartName;
1919 bool is_base = part.IsBase();
1921 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1923 {
1924 if (!part.IsBuilt())
1925 {
1926 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1927 GetConstruction().AddToConstructedParts(key);
1928 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1929
1930 if (is_base)
1931 {
1933 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1934 }
1935 }
1936 }
1937 else
1938 {
1939 if (part.IsBuilt())
1940 {
1941 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1942 GetConstruction().RemoveFromConstructedParts(key);
1943 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1944
1945 if (is_base)
1946 {
1948 AddProxyPhysics(ANIMATION_DEPLOYED);
1949 }
1950 }
1951 }
1952
1953 //check slot lock for material attachments
1954 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1955 }
1956
1957 //set construction parts based on synchronized data
1959 {
1962
1963 for (int i = 0; i < construction_parts.Count(); ++i)
1964 {
1965 string key = construction_parts.GetKey(i);
1968 }
1969
1970 //regenerate navmesh
1971 UpdateNavmesh();
1972 }
1973
1975 {
1978
1979 for (int i = 0; i < construction_parts.Count(); ++i)
1980 {
1981 string key = construction_parts.GetKey(i);
1983
1984 if (value.GetId() == id)
1985 return value;
1986 }
1987
1988 return NULL;
1989 }
1990 //
1991
1992 //Base
1993 bool HasBase()
1994 {
1995 return m_HasBase;
1996 }
1997
1998 void SetBaseState(bool has_base)
1999 {
2001 }
2002
2003 override bool IsDeployable()
2004 {
2005 return true;
2006 }
2007
2008 bool IsOpened()
2009 {
2010 return false;
2011 }
2012
2013 //--- CONSTRUCTION KIT
2015 {
2019
2020 return construction_kit;
2021 }
2022
2024 {
2025 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2028 }
2029
2030 protected vector GetKitSpawnPosition()
2031 {
2032 return GetPosition();
2033 }
2034
2035 protected string GetConstructionKitType()
2036 {
2037 return "";
2038 }
2039
2041 {
2043 GetGame().ObjectDelete(construction_kit);
2044 }
2045
2046 //--- CONSTRUCTION
2047 void DestroyConstruction()
2048 {
2049 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2050 GetGame().ObjectDelete(this);
2051 }
2052
2053 // --- EVENTS
2054 override void OnStoreSave(ParamsWriteContext ctx)
2055 {
2056 super.OnStoreSave(ctx);
2057
2058 //sync parts 01
2059 ctx.Write(m_SyncParts01);
2060 ctx.Write(m_SyncParts02);
2061 ctx.Write(m_SyncParts03);
2062
2063 ctx.Write(m_HasBase);
2064 }
2065
2066 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2067 {
2068 if (!super.OnStoreLoad(ctx, version))
2069 return false;
2070
2071 //--- Base building data ---
2072 //Restore synced parts data
2073 if (!ctx.Read(m_SyncParts01))
2074 {
2075 m_SyncParts01 = 0; //set default
2076 return false;
2077 }
2078 if (!ctx.Read(m_SyncParts02))
2079 {
2080 m_SyncParts02 = 0; //set default
2081 return false;
2082 }
2083 if (!ctx.Read(m_SyncParts03))
2084 {
2085 m_SyncParts03 = 0; //set default
2086 return false;
2087 }
2088
2089 //has base
2090 if (!ctx.Read(m_HasBase))
2091 {
2092 m_HasBase = false;
2093 return false;
2094 }
2095 //---
2096
2097 return true;
2098 }
2099
2100 override void AfterStoreLoad()
2101 {
2102 super.AfterStoreLoad();
2103
2106 }
2107
2109 {
2110 //update server data
2112
2113 //set base state
2114 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2115 SetBaseState(construction_part.IsBuilt()) ;
2116
2117 //synchronize after load
2119 }
2120
2121 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2122 {
2124 return;
2125
2126 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2127
2128 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2129 return;
2130
2132 string part_name = zone;
2133 part_name.ToLower();
2134
2136 {
2138
2139 if (construction_part && construction.IsPartConstructed(part_name))
2140 {
2141 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2142 construction.DestroyConnectedParts(part_name);
2143 }
2144
2145 //barbed wire handling (hack-ish)
2146 if (part_name.Contains("barbed"))
2147 {
2148 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2149 if (barbed_wire)
2150 barbed_wire.SetMountedState(false);
2151 }
2152 }
2153 }
2154
2155 override void EEOnAfterLoad()
2156 {
2158 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2159
2160 super.EEOnAfterLoad();
2161 }
2162
2163 override void EEInit()
2164 {
2165 super.EEInit();
2166
2167 // init visuals and physics
2168 InitBaseState();
2169
2170 //debug
2171#ifdef DEVELOPER
2173#endif
2174 }
2175
2176 override void EEItemAttached(EntityAI item, string slot_name)
2177 {
2178 super.EEItemAttached(item, slot_name);
2179
2181 UpdateVisuals();
2183 }
2184
2185 override void EEItemDetached(EntityAI item, string slot_name)
2186 {
2187 super.EEItemDetached(item, slot_name);
2188
2189 UpdateVisuals();
2191 }
2192
2193 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2194 {
2196 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2197
2200 }
2201
2202 //ignore out of reach condition
2203 override bool IgnoreOutOfReachCondition()
2204 {
2205 return true;
2206 }
2207
2208 //CONSTRUCTION EVENTS
2209 //Build
2210 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2211 {
2213
2214 //check base state
2215 if (construtionPart.IsBase())
2216 {
2217 SetBaseState(true);
2218
2219 //spawn kit
2221 }
2222
2223 //register constructed parts for synchronization
2225
2226 //register action that was performed on part
2228
2229 //synchronize
2231
2232 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2233
2234 UpdateNavmesh();
2235
2236 //update visuals
2237 UpdateVisuals();
2238
2239 //reset action sync data
2240 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2241 }
2242
2243 void OnPartBuiltClient(string part_name, int action_id)
2244 {
2245 //play sound
2247 }
2248
2249 //Dismantle
2251 {
2252 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2254
2255 //register constructed parts for synchronization
2257
2258 //register action that was performed on part
2260
2261 //synchronize
2263
2264 // server part of sync, client will be synced from SetPartsFromSyncData
2266
2267 UpdateNavmesh();
2268
2269 //update visuals
2270 UpdateVisuals();
2271
2272 //reset action sync data
2273 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2274
2275 //check base state
2276 if (construtionPart.IsBase())
2277 {
2278 //Destroy construction
2279 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2280 }
2281 }
2282
2284 {
2285 //play sound
2287 }
2288
2289 //Destroy
2291 {
2292 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2294
2295 //register constructed parts for synchronization
2297
2298 //register action that was performed on part
2300
2301 //synchronize
2303
2304 // server part of sync, client will be synced from SetPartsFromSyncData
2306
2307 UpdateNavmesh();
2308
2309 //update visuals
2310 UpdateVisuals();
2311
2312 //reset action sync data
2313 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2314
2315 //check base state
2316 if (construtionPart.IsBase())
2317 {
2318 //Destroy construction
2319 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2320 }
2321 }
2322
2323 void OnPartDestroyedClient(string part_name, int action_id)
2324 {
2325 //play sound
2327 }
2328
2329 // --- UPDATE
2330 void InitBaseState()
2331 {
2332 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2333
2334 InitVisuals();
2335 UpdateNavmesh(); //regenerate navmesh
2336 GetConstruction().InitBaseState();
2337 }
2338
2339 void InitVisuals()
2340 {
2341 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2342 //check base
2343 if (!HasBase())
2344 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2345 else
2346 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2347
2348 GetConstruction().UpdateVisuals();
2349 }
2350
2351 void UpdateVisuals()
2352 {
2354
2356 foreach (string slotName : attachmentSlots)
2358
2359 //check base
2360 if (!HasBase())
2361 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2362 else
2363 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2364
2365 GetConstruction().UpdateVisuals();
2366 }
2367
2369 {
2370 string slotNameMounted = slot_name + "_Mounted";
2371 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2372
2373 if (attachment)
2374 {
2375 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2376 if (barbedWire && barbedWire.IsMounted())
2378 else
2380
2381 if (is_locked)
2382 {
2383 SetAnimationPhase(slotNameMounted, 0);
2384 SetAnimationPhase(slot_name, 1);
2385 }
2386 else
2387 {
2388 SetAnimationPhase(slotNameMounted, 1);
2389 SetAnimationPhase(slot_name, 0);
2390 }
2391 }
2392 else
2393 {
2394 SetAnimationPhase(slotNameMounted, 1);
2395 SetAnimationPhase(slot_name, 1);
2396
2398 }
2399 }
2400
2401 // avoid calling this function on frequent occasions, it's a massive performance hit
2402 void UpdatePhysics()
2403 {
2405 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2406
2409
2411 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2412
2413 foreach (string slotName : attachmentSlots)
2415
2416 //check base
2417 if (!HasBase())
2418 {
2420 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2421
2422 AddProxyPhysics(ANIMATION_DEPLOYED);
2423 }
2424 else
2425 {
2427 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2428
2429 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2430 }
2431
2432 GetConstruction().UpdatePhysics();
2433 UpdateNavmesh();
2434 }
2435
2437 {
2438 //checks for invalid appends; hotfix
2439 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2440 return;
2441 //----------------------------------
2442 string slot_name_mounted = slot_name + "_Mounted";
2443 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2444
2445 //remove proxy physics
2446 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2447 RemoveProxyPhysics(slot_name_mounted);
2448 RemoveProxyPhysics(slot_name);
2449
2450 if (attachment)
2451 {
2452 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2453 if (is_locked)
2454 {
2455 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2456 AddProxyPhysics(slot_name_mounted);
2457 }
2458 else
2459 {
2460 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2461 AddProxyPhysics(slot_name);
2462 }
2463 }
2464 }
2465
2466 protected void UpdateNavmesh()
2467 {
2468 SetAffectPathgraph(true, false);
2469 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2470 }
2471
2472 override bool CanUseConstruction()
2473 {
2474 return true;
2475 }
2476
2477 override bool CanUseConstructionBuild()
2478 {
2479 return true;
2480 }
2481
2483 {
2484 if (attachment)
2485 {
2487 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2488
2489 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2490 }
2491
2492 return false;
2493 }
2494
2495 protected bool IsAttachmentSlotLocked(string slot_name)
2496 {
2497 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2498 }
2499
2500 //--- ATTACHMENT SLOTS
2502 {
2503 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2504 if (GetGame().ConfigIsExisting(config_path))
2505 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2506 }
2507
2509 {
2510 return true;
2511 }
2512
2513 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2514 {
2515 return true;
2516 }
2517
2518 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2519 {
2520 return true;
2521 }
2522
2523 // --- INIT
2524 void ConstructionInit()
2525 {
2526 if (!m_Construction)
2527 m_Construction = new Construction(this);
2528
2529 GetConstruction().Init();
2530 }
2531
2533 {
2534 return m_Construction;
2535 }
2536
2537 //--- INVENTORY/ATTACHMENTS CONDITIONS
2538 //attachments
2540 {
2541 return super.CanReceiveAttachment(attachment, slotId);
2542 }
2543
2545 {
2546 int attachment_count = GetInventory().AttachmentCount();
2547 if (attachment_count > 0)
2548 {
2549 if (HasBase() && attachment_count == 1)
2550 return false;
2551
2552 return true;
2553 }
2554
2555 return false;
2556 }
2557
2558 override bool ShowZonesHealth()
2559 {
2560 return true;
2561 }
2562
2563 //this into/outo parent.Cargo
2564 override bool CanPutInCargo(EntityAI parent)
2565 {
2566 return false;
2567 }
2568
2569 override bool CanRemoveFromCargo(EntityAI parent)
2570 {
2571 return false;
2572 }
2573
2574 //hands
2575 override bool CanPutIntoHands(EntityAI parent)
2576 {
2577 return false;
2578 }
2579
2580 //--- ACTION CONDITIONS
2581 //direction
2582 override bool IsFacingPlayer(PlayerBase player, string selection)
2583 {
2584 return true;
2585 }
2586
2587 override bool IsPlayerInside(PlayerBase player, string selection)
2588 {
2589 return true;
2590 }
2591
2594 {
2595 return false;
2596 }
2597
2598 //camera direction check
2599 bool IsFacingCamera(string selection)
2600 {
2601 return true;
2602 }
2603
2604 //roof check
2606 {
2607 return false;
2608 }
2609
2610 //selection->player distance check
2611 bool HasProperDistance(string selection, PlayerBase player)
2612 {
2613 return true;
2614 }
2615
2616 //folding
2618 {
2619 if (HasBase() || GetInventory().AttachmentCount() > 0)
2620 return false;
2621
2622 return true;
2623 }
2624
2626 {
2629
2630 return item;
2631 }
2632
2633 //Damage triggers (barbed wire)
2634 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2635 {
2636 if (GetGame() && GetGame().IsServer())
2637 {
2638 //destroy area damage if some already exists
2640
2641 //create new area damage
2643 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2644
2645 vector min_max[2];
2646 if (MemoryPointExists(slot_name + "_min"))
2647 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2648 if (MemoryPointExists(slot_name + "_max"))
2649 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2650
2651 //get proper trigger extents (min<max)
2652 vector extents[2];
2653 GetConstruction().GetTriggerExtents(min_max, extents);
2654
2655 //get box center
2656 vector center;
2657 center = GetConstruction().GetBoxCenter(min_max);
2658 center = ModelToWorld(center);
2659
2660 //rotate center if needed
2663
2664 areaDamage.SetExtents(extents[0], extents[1]);
2665 areaDamage.SetAreaPosition(center);
2666 areaDamage.SetAreaOrientation(orientation);
2667 areaDamage.SetLoopInterval(1.0);
2668 areaDamage.SetDeferDuration(0.2);
2669 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2670 areaDamage.SetAmmoName("BarbedWireHit");
2671 areaDamage.Spawn();
2672
2674 }
2675 }
2676
2678 {
2679 if (angle_deg != 0)
2680 {
2681 //orientation
2683
2684 //center
2686 if (MemoryPointExists("rotate_axis"))
2687 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2690 center[0] = r_center_x;
2691 center[2] = r_center_z;
2692 }
2693 }
2694
2695 void DestroyAreaDamage(string slot_name)
2696 {
2697 if (GetGame() && GetGame().IsServer())
2698 {
2701 {
2702 if (areaDamage)
2703 areaDamage.Destroy();
2704
2706 }
2707 }
2708 }
2709
2710 override bool IsIgnoredByConstruction()
2711 {
2712 return true;
2713 }
2714
2715 //================================================================
2716 // SOUNDS
2717 //================================================================
2718 protected void SoundBuildStart(string part_name)
2719 {
2720 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2721 }
2722
2723 protected void SoundDismantleStart(string part_name)
2724 {
2725 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2726 }
2727
2728 protected void SoundDestroyStart(string part_name)
2729 {
2730 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2731 }
2732
2733 protected string GetBuildSoundByMaterial(string part_name)
2734 {
2736
2737 switch (material_type)
2738 {
2739 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2740 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2741 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2742 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2743 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2744 }
2745
2746 return "";
2747 }
2748
2749 protected string GetDismantleSoundByMaterial(string part_name)
2750 {
2752
2753 switch (material_type)
2754 {
2755 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2756 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2757 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2758 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2759 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2760 }
2761
2762 return "";
2763 }
2764
2765 //misc
2767 {
2768 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2769 {
2770 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2772 SetHealth(slot_name, "Health", item.GetHealth());
2773 }
2774 }
2775
2776 override int GetDamageSystemVersionChange()
2777 {
2778 return 111;
2779 }
2780
2781 override void SetActions()
2782 {
2783 super.SetActions();
2784
2786 //AddAction(ActionTakeHybridAttachment);
2787 //AddAction(ActionTakeHybridAttachmentToHands);
2790 }
2791
2792 //================================================================
2793 // DEBUG
2794 //================================================================
2795 protected void DebugCustomState()
2796 {
2797 }
2798
2801 {
2802 return null;
2803 }
2804
2805 override void OnDebugSpawn()
2806 {
2807 FullyBuild();
2808 }
2809
2810 void FullyBuild()
2811 {
2813 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2814
2815 Man p;
2816
2817#ifdef SERVER
2819 GetGame().GetWorld().GetPlayerList(players);
2820 if (players.Count())
2821 p = players[0];
2822#else
2823 p = GetGame().GetPlayer();
2824#endif
2825
2826 foreach (ConstructionPart part : parts)
2827 {
2828 bool excluded = false;
2829 string partName = part.GetPartName();
2830 if (excludes)
2831 {
2832 foreach (string exclude : excludes)
2833 {
2834 if (partName.Contains(exclude))
2835 {
2836 excluded = true;
2837 break;
2838 }
2839 }
2840 }
2841
2842 if (!excluded)
2844 }
2845
2846 GetConstruction().UpdateVisuals();
2847 }
2848}
2849
2850void bsbDebugPrint(string s)
2851{
2852#ifdef BSB_DEBUG
2853 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2854#else
2855 //Print("" + s); // comment/uncomment to hide/see debug logs
2856#endif
2857}
2858void bsbDebugSpam(string s)
2859{
2860#ifdef BSB_DEBUG_SPAM
2861 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2862#else
2863 //Print("" + s); // comment/uncomment to hide/see debug logs
2864#endif
2865}

◆ EEItemDetached()

override void bsbDebugPrint::EEItemDetached ( EntityAI item,
string slot_name )
protected

Definition at line 1684 of file BaseBuildingBase.c.

1686{
1687 const string ANIMATION_DEPLOYED = "Deployed";
1688
1689 float m_ConstructionKitHealth; //stored health value for used construction kit
1690
1692
1693 bool m_HasBase;
1694 //variables for synchronization of base building parts (2x31 is the current limit)
1695 int m_SyncParts01; //synchronization for already built parts (31 parts)
1696 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1697 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1698 int m_InteractedPartId; //construction part id that an action was performed on
1699 int m_PerformedActionId; //action id that was performed on a construction part
1700
1701 //Sounds
1702 //build
1703 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1704 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1705 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1706 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1707 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1708 //dismantle
1709 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1710 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1711 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1712 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1713 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1714
1715 protected EffectSound m_Sound;
1716
1720
1721 // Constructor
1722 void BaseBuildingBase()
1723 {
1725
1726 //synchronized variables
1727 RegisterNetSyncVariableInt("m_SyncParts01");
1728 RegisterNetSyncVariableInt("m_SyncParts02");
1729 RegisterNetSyncVariableInt("m_SyncParts03");
1730 RegisterNetSyncVariableInt("m_InteractedPartId");
1731 RegisterNetSyncVariableInt("m_PerformedActionId");
1732 RegisterNetSyncVariableBool("m_HasBase");
1733
1734 //Construction init
1736
1737 if (ConfigIsExisting("hybridAttachments"))
1738 {
1740 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1741 }
1742 if (ConfigIsExisting("mountables"))
1743 {
1745 ConfigGetTextArray("mountables", m_Mountables);
1746 }
1747
1748 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1749 }
1750
1751 override void EEDelete(EntityAI parent)
1752 {
1753 super.EEDelete(parent);
1754
1755 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1757
1758 }
1759
1760 override string GetInvulnerabilityTypeString()
1761 {
1762 return "disableBaseDamage";
1763 }
1764
1765 override bool CanObstruct()
1766 {
1767 return true;
1768 }
1769
1770 override int GetHideIconMask()
1771 {
1772 return EInventoryIconVisibility.HIDE_VICINITY;
1773 }
1774
1775 // --- SYNCHRONIZATION
1777 {
1778 if (GetGame().IsServer())
1779 SetSynchDirty();
1780 }
1781
1782 override void OnVariablesSynchronized()
1783 {
1784 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1785 super.OnVariablesSynchronized();
1786
1787 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1788 }
1789
1790 protected void OnSynchronizedClient()
1791 {
1792 //update parts
1794
1795 //update action on part
1797
1798 //update visuals (client)
1799 UpdateVisuals();
1800 }
1801
1802 //parts synchronization
1804 {
1805 //part_id must starts from index = 1
1806 int offset;
1807 int mask;
1808
1809 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1810 {
1811 offset = part_id - 1;
1812 mask = 1 << offset;
1813
1815 }
1816 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1817 {
1818 offset = (part_id % 32);
1819 mask = 1 << offset;
1820
1822 }
1823 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1824 {
1825 offset = (part_id % 63);
1826 mask = 1 << offset;
1827
1829 }
1830 }
1831
1833 {
1834 //part_id must starts from index = 1
1835 int offset;
1836 int mask;
1837
1838 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1839 {
1840 offset = part_id - 1;
1841 mask = 1 << offset;
1842
1844 }
1845 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1846 {
1847 offset = (part_id % 32);
1848 mask = 1 << offset;
1849
1851 }
1852 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1853 {
1854 offset = (part_id % 63);
1855 mask = 1 << offset;
1856
1858 }
1859 }
1860
1862 {
1863 //part_id must starts from index = 1
1864 int offset;
1865 int mask;
1866
1867 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1868 {
1869 offset = part_id - 1;
1870 mask = 1 << offset;
1871
1872 if ((m_SyncParts01 & mask) > 0)
1873 return true;
1874 }
1875 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1876 {
1877 offset = (part_id % 32);
1878 mask = 1 << offset;
1879
1880 if ((m_SyncParts02 & mask) > 0)
1881 return true;
1882 }
1883 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1884 {
1885 offset = (part_id % 63);
1886 mask = 1 << offset;
1887
1888 if ((m_SyncParts03 & mask) > 0)
1889 return true;
1890 }
1891
1892 return false;
1893 }
1894
1895 protected void RegisterActionForSync(int part_id, int action_id)
1896 {
1899 }
1900
1901 protected void ResetActionSyncData()
1902 {
1903 //reset data
1904 m_InteractedPartId = -1;
1906 }
1907
1908 protected void SetActionFromSyncData()
1909 {
1910 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1911 {
1914
1915 switch (build_action_id)
1916 {
1920 }
1921 }
1922 }
1923 //------
1924
1926 {
1927 string key = part.m_PartName;
1928 bool is_base = part.IsBase();
1930 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1932 {
1933 if (!part.IsBuilt())
1934 {
1935 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1936 GetConstruction().AddToConstructedParts(key);
1937 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1938
1939 if (is_base)
1940 {
1942 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1943 }
1944 }
1945 }
1946 else
1947 {
1948 if (part.IsBuilt())
1949 {
1950 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1951 GetConstruction().RemoveFromConstructedParts(key);
1952 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1953
1954 if (is_base)
1955 {
1957 AddProxyPhysics(ANIMATION_DEPLOYED);
1958 }
1959 }
1960 }
1961
1962 //check slot lock for material attachments
1963 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1964 }
1965
1966 //set construction parts based on synchronized data
1968 {
1971
1972 for (int i = 0; i < construction_parts.Count(); ++i)
1973 {
1974 string key = construction_parts.GetKey(i);
1977 }
1978
1979 //regenerate navmesh
1980 UpdateNavmesh();
1981 }
1982
1984 {
1987
1988 for (int i = 0; i < construction_parts.Count(); ++i)
1989 {
1990 string key = construction_parts.GetKey(i);
1992
1993 if (value.GetId() == id)
1994 return value;
1995 }
1996
1997 return NULL;
1998 }
1999 //
2000
2001 //Base
2002 bool HasBase()
2003 {
2004 return m_HasBase;
2005 }
2006
2007 void SetBaseState(bool has_base)
2008 {
2010 }
2011
2012 override bool IsDeployable()
2013 {
2014 return true;
2015 }
2016
2017 bool IsOpened()
2018 {
2019 return false;
2020 }
2021
2022 //--- CONSTRUCTION KIT
2024 {
2028
2029 return construction_kit;
2030 }
2031
2033 {
2034 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2037 }
2038
2039 protected vector GetKitSpawnPosition()
2040 {
2041 return GetPosition();
2042 }
2043
2044 protected string GetConstructionKitType()
2045 {
2046 return "";
2047 }
2048
2050 {
2052 GetGame().ObjectDelete(construction_kit);
2053 }
2054
2055 //--- CONSTRUCTION
2056 void DestroyConstruction()
2057 {
2058 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2059 GetGame().ObjectDelete(this);
2060 }
2061
2062 // --- EVENTS
2063 override void OnStoreSave(ParamsWriteContext ctx)
2064 {
2065 super.OnStoreSave(ctx);
2066
2067 //sync parts 01
2068 ctx.Write(m_SyncParts01);
2069 ctx.Write(m_SyncParts02);
2070 ctx.Write(m_SyncParts03);
2071
2072 ctx.Write(m_HasBase);
2073 }
2074
2075 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2076 {
2077 if (!super.OnStoreLoad(ctx, version))
2078 return false;
2079
2080 //--- Base building data ---
2081 //Restore synced parts data
2082 if (!ctx.Read(m_SyncParts01))
2083 {
2084 m_SyncParts01 = 0; //set default
2085 return false;
2086 }
2087 if (!ctx.Read(m_SyncParts02))
2088 {
2089 m_SyncParts02 = 0; //set default
2090 return false;
2091 }
2092 if (!ctx.Read(m_SyncParts03))
2093 {
2094 m_SyncParts03 = 0; //set default
2095 return false;
2096 }
2097
2098 //has base
2099 if (!ctx.Read(m_HasBase))
2100 {
2101 m_HasBase = false;
2102 return false;
2103 }
2104 //---
2105
2106 return true;
2107 }
2108
2109 override void AfterStoreLoad()
2110 {
2111 super.AfterStoreLoad();
2112
2115 }
2116
2118 {
2119 //update server data
2121
2122 //set base state
2123 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2124 SetBaseState(construction_part.IsBuilt()) ;
2125
2126 //synchronize after load
2128 }
2129
2130 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2131 {
2133 return;
2134
2135 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2136
2137 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2138 return;
2139
2141 string part_name = zone;
2142 part_name.ToLower();
2143
2145 {
2147
2148 if (construction_part && construction.IsPartConstructed(part_name))
2149 {
2150 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2151 construction.DestroyConnectedParts(part_name);
2152 }
2153
2154 //barbed wire handling (hack-ish)
2155 if (part_name.Contains("barbed"))
2156 {
2157 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2158 if (barbed_wire)
2159 barbed_wire.SetMountedState(false);
2160 }
2161 }
2162 }
2163
2164 override void EEOnAfterLoad()
2165 {
2167 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2168
2169 super.EEOnAfterLoad();
2170 }
2171
2172 override void EEInit()
2173 {
2174 super.EEInit();
2175
2176 // init visuals and physics
2177 InitBaseState();
2178
2179 //debug
2180#ifdef DEVELOPER
2182#endif
2183 }
2184
2185 override void EEItemAttached(EntityAI item, string slot_name)
2186 {
2187 super.EEItemAttached(item, slot_name);
2188
2190 UpdateVisuals();
2192 }
2193
2194 override void EEItemDetached(EntityAI item, string slot_name)
2195 {
2196 super.EEItemDetached(item, slot_name);
2197
2198 UpdateVisuals();
2200 }
2201
2202 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2203 {
2205 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2206
2209 }
2210
2211 //ignore out of reach condition
2212 override bool IgnoreOutOfReachCondition()
2213 {
2214 return true;
2215 }
2216
2217 //CONSTRUCTION EVENTS
2218 //Build
2219 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2220 {
2222
2223 //check base state
2224 if (construtionPart.IsBase())
2225 {
2226 SetBaseState(true);
2227
2228 //spawn kit
2230 }
2231
2232 //register constructed parts for synchronization
2234
2235 //register action that was performed on part
2237
2238 //synchronize
2240
2241 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2242
2243 UpdateNavmesh();
2244
2245 //update visuals
2246 UpdateVisuals();
2247
2248 //reset action sync data
2249 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2250 }
2251
2252 void OnPartBuiltClient(string part_name, int action_id)
2253 {
2254 //play sound
2256 }
2257
2258 //Dismantle
2260 {
2261 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2263
2264 //register constructed parts for synchronization
2266
2267 //register action that was performed on part
2269
2270 //synchronize
2272
2273 // server part of sync, client will be synced from SetPartsFromSyncData
2275
2276 UpdateNavmesh();
2277
2278 //update visuals
2279 UpdateVisuals();
2280
2281 //reset action sync data
2282 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2283
2284 //check base state
2285 if (construtionPart.IsBase())
2286 {
2287 //Destroy construction
2288 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2289 }
2290 }
2291
2293 {
2294 //play sound
2296 }
2297
2298 //Destroy
2300 {
2301 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2303
2304 //register constructed parts for synchronization
2306
2307 //register action that was performed on part
2309
2310 //synchronize
2312
2313 // server part of sync, client will be synced from SetPartsFromSyncData
2315
2316 UpdateNavmesh();
2317
2318 //update visuals
2319 UpdateVisuals();
2320
2321 //reset action sync data
2322 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2323
2324 //check base state
2325 if (construtionPart.IsBase())
2326 {
2327 //Destroy construction
2328 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2329 }
2330 }
2331
2332 void OnPartDestroyedClient(string part_name, int action_id)
2333 {
2334 //play sound
2336 }
2337
2338 // --- UPDATE
2339 void InitBaseState()
2340 {
2341 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2342
2343 InitVisuals();
2344 UpdateNavmesh(); //regenerate navmesh
2345 GetConstruction().InitBaseState();
2346 }
2347
2348 void InitVisuals()
2349 {
2350 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2351 //check base
2352 if (!HasBase())
2353 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2354 else
2355 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2356
2357 GetConstruction().UpdateVisuals();
2358 }
2359
2360 void UpdateVisuals()
2361 {
2363
2365 foreach (string slotName : attachmentSlots)
2367
2368 //check base
2369 if (!HasBase())
2370 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2371 else
2372 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2373
2374 GetConstruction().UpdateVisuals();
2375 }
2376
2378 {
2379 string slotNameMounted = slot_name + "_Mounted";
2380 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2381
2382 if (attachment)
2383 {
2384 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2385 if (barbedWire && barbedWire.IsMounted())
2387 else
2389
2390 if (is_locked)
2391 {
2392 SetAnimationPhase(slotNameMounted, 0);
2393 SetAnimationPhase(slot_name, 1);
2394 }
2395 else
2396 {
2397 SetAnimationPhase(slotNameMounted, 1);
2398 SetAnimationPhase(slot_name, 0);
2399 }
2400 }
2401 else
2402 {
2403 SetAnimationPhase(slotNameMounted, 1);
2404 SetAnimationPhase(slot_name, 1);
2405
2407 }
2408 }
2409
2410 // avoid calling this function on frequent occasions, it's a massive performance hit
2411 void UpdatePhysics()
2412 {
2414 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2415
2418
2420 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2421
2422 foreach (string slotName : attachmentSlots)
2424
2425 //check base
2426 if (!HasBase())
2427 {
2429 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2430
2431 AddProxyPhysics(ANIMATION_DEPLOYED);
2432 }
2433 else
2434 {
2436 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2437
2438 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2439 }
2440
2441 GetConstruction().UpdatePhysics();
2442 UpdateNavmesh();
2443 }
2444
2446 {
2447 //checks for invalid appends; hotfix
2448 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2449 return;
2450 //----------------------------------
2451 string slot_name_mounted = slot_name + "_Mounted";
2452 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2453
2454 //remove proxy physics
2455 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2456 RemoveProxyPhysics(slot_name_mounted);
2457 RemoveProxyPhysics(slot_name);
2458
2459 if (attachment)
2460 {
2461 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2462 if (is_locked)
2463 {
2464 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2465 AddProxyPhysics(slot_name_mounted);
2466 }
2467 else
2468 {
2469 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2470 AddProxyPhysics(slot_name);
2471 }
2472 }
2473 }
2474
2475 protected void UpdateNavmesh()
2476 {
2477 SetAffectPathgraph(true, false);
2478 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2479 }
2480
2481 override bool CanUseConstruction()
2482 {
2483 return true;
2484 }
2485
2486 override bool CanUseConstructionBuild()
2487 {
2488 return true;
2489 }
2490
2492 {
2493 if (attachment)
2494 {
2496 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2497
2498 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2499 }
2500
2501 return false;
2502 }
2503
2504 protected bool IsAttachmentSlotLocked(string slot_name)
2505 {
2506 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2507 }
2508
2509 //--- ATTACHMENT SLOTS
2511 {
2512 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2513 if (GetGame().ConfigIsExisting(config_path))
2514 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2515 }
2516
2518 {
2519 return true;
2520 }
2521
2522 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2523 {
2524 return true;
2525 }
2526
2527 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2528 {
2529 return true;
2530 }
2531
2532 // --- INIT
2533 void ConstructionInit()
2534 {
2535 if (!m_Construction)
2536 m_Construction = new Construction(this);
2537
2538 GetConstruction().Init();
2539 }
2540
2542 {
2543 return m_Construction;
2544 }
2545
2546 //--- INVENTORY/ATTACHMENTS CONDITIONS
2547 //attachments
2549 {
2550 return super.CanReceiveAttachment(attachment, slotId);
2551 }
2552
2554 {
2555 int attachment_count = GetInventory().AttachmentCount();
2556 if (attachment_count > 0)
2557 {
2558 if (HasBase() && attachment_count == 1)
2559 return false;
2560
2561 return true;
2562 }
2563
2564 return false;
2565 }
2566
2567 override bool ShowZonesHealth()
2568 {
2569 return true;
2570 }
2571
2572 //this into/outo parent.Cargo
2573 override bool CanPutInCargo(EntityAI parent)
2574 {
2575 return false;
2576 }
2577
2578 override bool CanRemoveFromCargo(EntityAI parent)
2579 {
2580 return false;
2581 }
2582
2583 //hands
2584 override bool CanPutIntoHands(EntityAI parent)
2585 {
2586 return false;
2587 }
2588
2589 //--- ACTION CONDITIONS
2590 //direction
2591 override bool IsFacingPlayer(PlayerBase player, string selection)
2592 {
2593 return true;
2594 }
2595
2596 override bool IsPlayerInside(PlayerBase player, string selection)
2597 {
2598 return true;
2599 }
2600
2603 {
2604 return false;
2605 }
2606
2607 //camera direction check
2608 bool IsFacingCamera(string selection)
2609 {
2610 return true;
2611 }
2612
2613 //roof check
2615 {
2616 return false;
2617 }
2618
2619 //selection->player distance check
2620 bool HasProperDistance(string selection, PlayerBase player)
2621 {
2622 return true;
2623 }
2624
2625 //folding
2627 {
2628 if (HasBase() || GetInventory().AttachmentCount() > 0)
2629 return false;
2630
2631 return true;
2632 }
2633
2635 {
2638
2639 return item;
2640 }
2641
2642 //Damage triggers (barbed wire)
2643 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2644 {
2645 if (GetGame() && GetGame().IsServer())
2646 {
2647 //destroy area damage if some already exists
2649
2650 //create new area damage
2652 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2653
2654 vector min_max[2];
2655 if (MemoryPointExists(slot_name + "_min"))
2656 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2657 if (MemoryPointExists(slot_name + "_max"))
2658 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2659
2660 //get proper trigger extents (min<max)
2661 vector extents[2];
2662 GetConstruction().GetTriggerExtents(min_max, extents);
2663
2664 //get box center
2665 vector center;
2666 center = GetConstruction().GetBoxCenter(min_max);
2667 center = ModelToWorld(center);
2668
2669 //rotate center if needed
2672
2673 areaDamage.SetExtents(extents[0], extents[1]);
2674 areaDamage.SetAreaPosition(center);
2675 areaDamage.SetAreaOrientation(orientation);
2676 areaDamage.SetLoopInterval(1.0);
2677 areaDamage.SetDeferDuration(0.2);
2678 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2679 areaDamage.SetAmmoName("BarbedWireHit");
2680 areaDamage.Spawn();
2681
2683 }
2684 }
2685
2687 {
2688 if (angle_deg != 0)
2689 {
2690 //orientation
2692
2693 //center
2695 if (MemoryPointExists("rotate_axis"))
2696 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2699 center[0] = r_center_x;
2700 center[2] = r_center_z;
2701 }
2702 }
2703
2704 void DestroyAreaDamage(string slot_name)
2705 {
2706 if (GetGame() && GetGame().IsServer())
2707 {
2710 {
2711 if (areaDamage)
2712 areaDamage.Destroy();
2713
2715 }
2716 }
2717 }
2718
2719 override bool IsIgnoredByConstruction()
2720 {
2721 return true;
2722 }
2723
2724 //================================================================
2725 // SOUNDS
2726 //================================================================
2727 protected void SoundBuildStart(string part_name)
2728 {
2729 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2730 }
2731
2732 protected void SoundDismantleStart(string part_name)
2733 {
2734 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2735 }
2736
2737 protected void SoundDestroyStart(string part_name)
2738 {
2739 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2740 }
2741
2742 protected string GetBuildSoundByMaterial(string part_name)
2743 {
2745
2746 switch (material_type)
2747 {
2748 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2749 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2750 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2751 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2752 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2753 }
2754
2755 return "";
2756 }
2757
2758 protected string GetDismantleSoundByMaterial(string part_name)
2759 {
2761
2762 switch (material_type)
2763 {
2764 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2765 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2766 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2767 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2768 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2769 }
2770
2771 return "";
2772 }
2773
2774 //misc
2776 {
2777 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2778 {
2779 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2781 SetHealth(slot_name, "Health", item.GetHealth());
2782 }
2783 }
2784
2785 override int GetDamageSystemVersionChange()
2786 {
2787 return 111;
2788 }
2789
2790 override void SetActions()
2791 {
2792 super.SetActions();
2793
2795 //AddAction(ActionTakeHybridAttachment);
2796 //AddAction(ActionTakeHybridAttachmentToHands);
2799 }
2800
2801 //================================================================
2802 // DEBUG
2803 //================================================================
2804 protected void DebugCustomState()
2805 {
2806 }
2807
2810 {
2811 return null;
2812 }
2813
2814 override void OnDebugSpawn()
2815 {
2816 FullyBuild();
2817 }
2818
2819 void FullyBuild()
2820 {
2822 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2823
2824 Man p;
2825
2826#ifdef SERVER
2828 GetGame().GetWorld().GetPlayerList(players);
2829 if (players.Count())
2830 p = players[0];
2831#else
2832 p = GetGame().GetPlayer();
2833#endif
2834
2835 foreach (ConstructionPart part : parts)
2836 {
2837 bool excluded = false;
2838 string partName = part.GetPartName();
2839 if (excludes)
2840 {
2841 foreach (string exclude : excludes)
2842 {
2843 if (partName.Contains(exclude))
2844 {
2845 excluded = true;
2846 break;
2847 }
2848 }
2849 }
2850
2851 if (!excluded)
2853 }
2854
2855 GetConstruction().UpdateVisuals();
2856 }
2857}
2858
2859void bsbDebugPrint(string s)
2860{
2861#ifdef BSB_DEBUG
2862 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2863#else
2864 //Print("" + s); // comment/uncomment to hide/see debug logs
2865#endif
2866}
2867void bsbDebugSpam(string s)
2868{
2869#ifdef BSB_DEBUG_SPAM
2870 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2871#else
2872 //Print("" + s); // comment/uncomment to hide/see debug logs
2873#endif
2874}

◆ EEOnAfterLoad()

override void bsbDebugPrint::EEOnAfterLoad ( )
protected

Definition at line 1654 of file BaseBuildingBase.c.

1656{
1657 const string ANIMATION_DEPLOYED = "Deployed";
1658
1659 float m_ConstructionKitHealth; //stored health value for used construction kit
1660
1662
1663 bool m_HasBase;
1664 //variables for synchronization of base building parts (2x31 is the current limit)
1665 int m_SyncParts01; //synchronization for already built parts (31 parts)
1666 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1667 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1668 int m_InteractedPartId; //construction part id that an action was performed on
1669 int m_PerformedActionId; //action id that was performed on a construction part
1670
1671 //Sounds
1672 //build
1673 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1674 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1675 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1676 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1677 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1678 //dismantle
1679 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1680 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1681 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1682 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1683 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1684
1685 protected EffectSound m_Sound;
1686
1690
1691 // Constructor
1692 void BaseBuildingBase()
1693 {
1695
1696 //synchronized variables
1697 RegisterNetSyncVariableInt("m_SyncParts01");
1698 RegisterNetSyncVariableInt("m_SyncParts02");
1699 RegisterNetSyncVariableInt("m_SyncParts03");
1700 RegisterNetSyncVariableInt("m_InteractedPartId");
1701 RegisterNetSyncVariableInt("m_PerformedActionId");
1702 RegisterNetSyncVariableBool("m_HasBase");
1703
1704 //Construction init
1706
1707 if (ConfigIsExisting("hybridAttachments"))
1708 {
1710 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1711 }
1712 if (ConfigIsExisting("mountables"))
1713 {
1715 ConfigGetTextArray("mountables", m_Mountables);
1716 }
1717
1718 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1719 }
1720
1721 override void EEDelete(EntityAI parent)
1722 {
1723 super.EEDelete(parent);
1724
1725 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1727
1728 }
1729
1730 override string GetInvulnerabilityTypeString()
1731 {
1732 return "disableBaseDamage";
1733 }
1734
1735 override bool CanObstruct()
1736 {
1737 return true;
1738 }
1739
1740 override int GetHideIconMask()
1741 {
1742 return EInventoryIconVisibility.HIDE_VICINITY;
1743 }
1744
1745 // --- SYNCHRONIZATION
1747 {
1748 if (GetGame().IsServer())
1749 SetSynchDirty();
1750 }
1751
1752 override void OnVariablesSynchronized()
1753 {
1754 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1755 super.OnVariablesSynchronized();
1756
1757 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1758 }
1759
1760 protected void OnSynchronizedClient()
1761 {
1762 //update parts
1764
1765 //update action on part
1767
1768 //update visuals (client)
1769 UpdateVisuals();
1770 }
1771
1772 //parts synchronization
1774 {
1775 //part_id must starts from index = 1
1776 int offset;
1777 int mask;
1778
1779 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1780 {
1781 offset = part_id - 1;
1782 mask = 1 << offset;
1783
1785 }
1786 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1787 {
1788 offset = (part_id % 32);
1789 mask = 1 << offset;
1790
1792 }
1793 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1794 {
1795 offset = (part_id % 63);
1796 mask = 1 << offset;
1797
1799 }
1800 }
1801
1803 {
1804 //part_id must starts from index = 1
1805 int offset;
1806 int mask;
1807
1808 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1809 {
1810 offset = part_id - 1;
1811 mask = 1 << offset;
1812
1814 }
1815 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1816 {
1817 offset = (part_id % 32);
1818 mask = 1 << offset;
1819
1821 }
1822 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1823 {
1824 offset = (part_id % 63);
1825 mask = 1 << offset;
1826
1828 }
1829 }
1830
1832 {
1833 //part_id must starts from index = 1
1834 int offset;
1835 int mask;
1836
1837 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1838 {
1839 offset = part_id - 1;
1840 mask = 1 << offset;
1841
1842 if ((m_SyncParts01 & mask) > 0)
1843 return true;
1844 }
1845 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1846 {
1847 offset = (part_id % 32);
1848 mask = 1 << offset;
1849
1850 if ((m_SyncParts02 & mask) > 0)
1851 return true;
1852 }
1853 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1854 {
1855 offset = (part_id % 63);
1856 mask = 1 << offset;
1857
1858 if ((m_SyncParts03 & mask) > 0)
1859 return true;
1860 }
1861
1862 return false;
1863 }
1864
1865 protected void RegisterActionForSync(int part_id, int action_id)
1866 {
1869 }
1870
1871 protected void ResetActionSyncData()
1872 {
1873 //reset data
1874 m_InteractedPartId = -1;
1876 }
1877
1878 protected void SetActionFromSyncData()
1879 {
1880 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1881 {
1884
1885 switch (build_action_id)
1886 {
1890 }
1891 }
1892 }
1893 //------
1894
1896 {
1897 string key = part.m_PartName;
1898 bool is_base = part.IsBase();
1900 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1902 {
1903 if (!part.IsBuilt())
1904 {
1905 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1906 GetConstruction().AddToConstructedParts(key);
1907 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1908
1909 if (is_base)
1910 {
1912 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1913 }
1914 }
1915 }
1916 else
1917 {
1918 if (part.IsBuilt())
1919 {
1920 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1921 GetConstruction().RemoveFromConstructedParts(key);
1922 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1923
1924 if (is_base)
1925 {
1927 AddProxyPhysics(ANIMATION_DEPLOYED);
1928 }
1929 }
1930 }
1931
1932 //check slot lock for material attachments
1933 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1934 }
1935
1936 //set construction parts based on synchronized data
1938 {
1941
1942 for (int i = 0; i < construction_parts.Count(); ++i)
1943 {
1944 string key = construction_parts.GetKey(i);
1947 }
1948
1949 //regenerate navmesh
1950 UpdateNavmesh();
1951 }
1952
1954 {
1957
1958 for (int i = 0; i < construction_parts.Count(); ++i)
1959 {
1960 string key = construction_parts.GetKey(i);
1962
1963 if (value.GetId() == id)
1964 return value;
1965 }
1966
1967 return NULL;
1968 }
1969 //
1970
1971 //Base
1972 bool HasBase()
1973 {
1974 return m_HasBase;
1975 }
1976
1977 void SetBaseState(bool has_base)
1978 {
1980 }
1981
1982 override bool IsDeployable()
1983 {
1984 return true;
1985 }
1986
1987 bool IsOpened()
1988 {
1989 return false;
1990 }
1991
1992 //--- CONSTRUCTION KIT
1994 {
1998
1999 return construction_kit;
2000 }
2001
2003 {
2004 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2007 }
2008
2009 protected vector GetKitSpawnPosition()
2010 {
2011 return GetPosition();
2012 }
2013
2014 protected string GetConstructionKitType()
2015 {
2016 return "";
2017 }
2018
2020 {
2022 GetGame().ObjectDelete(construction_kit);
2023 }
2024
2025 //--- CONSTRUCTION
2026 void DestroyConstruction()
2027 {
2028 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2029 GetGame().ObjectDelete(this);
2030 }
2031
2032 // --- EVENTS
2033 override void OnStoreSave(ParamsWriteContext ctx)
2034 {
2035 super.OnStoreSave(ctx);
2036
2037 //sync parts 01
2038 ctx.Write(m_SyncParts01);
2039 ctx.Write(m_SyncParts02);
2040 ctx.Write(m_SyncParts03);
2041
2042 ctx.Write(m_HasBase);
2043 }
2044
2045 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2046 {
2047 if (!super.OnStoreLoad(ctx, version))
2048 return false;
2049
2050 //--- Base building data ---
2051 //Restore synced parts data
2052 if (!ctx.Read(m_SyncParts01))
2053 {
2054 m_SyncParts01 = 0; //set default
2055 return false;
2056 }
2057 if (!ctx.Read(m_SyncParts02))
2058 {
2059 m_SyncParts02 = 0; //set default
2060 return false;
2061 }
2062 if (!ctx.Read(m_SyncParts03))
2063 {
2064 m_SyncParts03 = 0; //set default
2065 return false;
2066 }
2067
2068 //has base
2069 if (!ctx.Read(m_HasBase))
2070 {
2071 m_HasBase = false;
2072 return false;
2073 }
2074 //---
2075
2076 return true;
2077 }
2078
2079 override void AfterStoreLoad()
2080 {
2081 super.AfterStoreLoad();
2082
2085 }
2086
2088 {
2089 //update server data
2091
2092 //set base state
2093 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2094 SetBaseState(construction_part.IsBuilt()) ;
2095
2096 //synchronize after load
2098 }
2099
2100 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2101 {
2103 return;
2104
2105 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2106
2107 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2108 return;
2109
2111 string part_name = zone;
2112 part_name.ToLower();
2113
2115 {
2117
2118 if (construction_part && construction.IsPartConstructed(part_name))
2119 {
2120 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2121 construction.DestroyConnectedParts(part_name);
2122 }
2123
2124 //barbed wire handling (hack-ish)
2125 if (part_name.Contains("barbed"))
2126 {
2127 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2128 if (barbed_wire)
2129 barbed_wire.SetMountedState(false);
2130 }
2131 }
2132 }
2133
2134 override void EEOnAfterLoad()
2135 {
2137 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2138
2139 super.EEOnAfterLoad();
2140 }
2141
2142 override void EEInit()
2143 {
2144 super.EEInit();
2145
2146 // init visuals and physics
2147 InitBaseState();
2148
2149 //debug
2150#ifdef DEVELOPER
2152#endif
2153 }
2154
2155 override void EEItemAttached(EntityAI item, string slot_name)
2156 {
2157 super.EEItemAttached(item, slot_name);
2158
2160 UpdateVisuals();
2162 }
2163
2164 override void EEItemDetached(EntityAI item, string slot_name)
2165 {
2166 super.EEItemDetached(item, slot_name);
2167
2168 UpdateVisuals();
2170 }
2171
2172 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2173 {
2175 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2176
2179 }
2180
2181 //ignore out of reach condition
2182 override bool IgnoreOutOfReachCondition()
2183 {
2184 return true;
2185 }
2186
2187 //CONSTRUCTION EVENTS
2188 //Build
2189 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2190 {
2192
2193 //check base state
2194 if (construtionPart.IsBase())
2195 {
2196 SetBaseState(true);
2197
2198 //spawn kit
2200 }
2201
2202 //register constructed parts for synchronization
2204
2205 //register action that was performed on part
2207
2208 //synchronize
2210
2211 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2212
2213 UpdateNavmesh();
2214
2215 //update visuals
2216 UpdateVisuals();
2217
2218 //reset action sync data
2219 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2220 }
2221
2222 void OnPartBuiltClient(string part_name, int action_id)
2223 {
2224 //play sound
2226 }
2227
2228 //Dismantle
2230 {
2231 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2233
2234 //register constructed parts for synchronization
2236
2237 //register action that was performed on part
2239
2240 //synchronize
2242
2243 // server part of sync, client will be synced from SetPartsFromSyncData
2245
2246 UpdateNavmesh();
2247
2248 //update visuals
2249 UpdateVisuals();
2250
2251 //reset action sync data
2252 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2253
2254 //check base state
2255 if (construtionPart.IsBase())
2256 {
2257 //Destroy construction
2258 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2259 }
2260 }
2261
2263 {
2264 //play sound
2266 }
2267
2268 //Destroy
2270 {
2271 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2273
2274 //register constructed parts for synchronization
2276
2277 //register action that was performed on part
2279
2280 //synchronize
2282
2283 // server part of sync, client will be synced from SetPartsFromSyncData
2285
2286 UpdateNavmesh();
2287
2288 //update visuals
2289 UpdateVisuals();
2290
2291 //reset action sync data
2292 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2293
2294 //check base state
2295 if (construtionPart.IsBase())
2296 {
2297 //Destroy construction
2298 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2299 }
2300 }
2301
2302 void OnPartDestroyedClient(string part_name, int action_id)
2303 {
2304 //play sound
2306 }
2307
2308 // --- UPDATE
2309 void InitBaseState()
2310 {
2311 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2312
2313 InitVisuals();
2314 UpdateNavmesh(); //regenerate navmesh
2315 GetConstruction().InitBaseState();
2316 }
2317
2318 void InitVisuals()
2319 {
2320 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2321 //check base
2322 if (!HasBase())
2323 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2324 else
2325 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2326
2327 GetConstruction().UpdateVisuals();
2328 }
2329
2330 void UpdateVisuals()
2331 {
2333
2335 foreach (string slotName : attachmentSlots)
2337
2338 //check base
2339 if (!HasBase())
2340 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2341 else
2342 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2343
2344 GetConstruction().UpdateVisuals();
2345 }
2346
2348 {
2349 string slotNameMounted = slot_name + "_Mounted";
2350 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2351
2352 if (attachment)
2353 {
2354 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2355 if (barbedWire && barbedWire.IsMounted())
2357 else
2359
2360 if (is_locked)
2361 {
2362 SetAnimationPhase(slotNameMounted, 0);
2363 SetAnimationPhase(slot_name, 1);
2364 }
2365 else
2366 {
2367 SetAnimationPhase(slotNameMounted, 1);
2368 SetAnimationPhase(slot_name, 0);
2369 }
2370 }
2371 else
2372 {
2373 SetAnimationPhase(slotNameMounted, 1);
2374 SetAnimationPhase(slot_name, 1);
2375
2377 }
2378 }
2379
2380 // avoid calling this function on frequent occasions, it's a massive performance hit
2381 void UpdatePhysics()
2382 {
2384 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2385
2388
2390 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2391
2392 foreach (string slotName : attachmentSlots)
2394
2395 //check base
2396 if (!HasBase())
2397 {
2399 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2400
2401 AddProxyPhysics(ANIMATION_DEPLOYED);
2402 }
2403 else
2404 {
2406 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2407
2408 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2409 }
2410
2411 GetConstruction().UpdatePhysics();
2412 UpdateNavmesh();
2413 }
2414
2416 {
2417 //checks for invalid appends; hotfix
2418 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2419 return;
2420 //----------------------------------
2421 string slot_name_mounted = slot_name + "_Mounted";
2422 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2423
2424 //remove proxy physics
2425 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2426 RemoveProxyPhysics(slot_name_mounted);
2427 RemoveProxyPhysics(slot_name);
2428
2429 if (attachment)
2430 {
2431 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2432 if (is_locked)
2433 {
2434 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2435 AddProxyPhysics(slot_name_mounted);
2436 }
2437 else
2438 {
2439 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2440 AddProxyPhysics(slot_name);
2441 }
2442 }
2443 }
2444
2445 protected void UpdateNavmesh()
2446 {
2447 SetAffectPathgraph(true, false);
2448 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2449 }
2450
2451 override bool CanUseConstruction()
2452 {
2453 return true;
2454 }
2455
2456 override bool CanUseConstructionBuild()
2457 {
2458 return true;
2459 }
2460
2462 {
2463 if (attachment)
2464 {
2466 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2467
2468 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2469 }
2470
2471 return false;
2472 }
2473
2474 protected bool IsAttachmentSlotLocked(string slot_name)
2475 {
2476 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2477 }
2478
2479 //--- ATTACHMENT SLOTS
2481 {
2482 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2483 if (GetGame().ConfigIsExisting(config_path))
2484 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2485 }
2486
2488 {
2489 return true;
2490 }
2491
2492 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2493 {
2494 return true;
2495 }
2496
2497 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2498 {
2499 return true;
2500 }
2501
2502 // --- INIT
2503 void ConstructionInit()
2504 {
2505 if (!m_Construction)
2506 m_Construction = new Construction(this);
2507
2508 GetConstruction().Init();
2509 }
2510
2512 {
2513 return m_Construction;
2514 }
2515
2516 //--- INVENTORY/ATTACHMENTS CONDITIONS
2517 //attachments
2519 {
2520 return super.CanReceiveAttachment(attachment, slotId);
2521 }
2522
2524 {
2525 int attachment_count = GetInventory().AttachmentCount();
2526 if (attachment_count > 0)
2527 {
2528 if (HasBase() && attachment_count == 1)
2529 return false;
2530
2531 return true;
2532 }
2533
2534 return false;
2535 }
2536
2537 override bool ShowZonesHealth()
2538 {
2539 return true;
2540 }
2541
2542 //this into/outo parent.Cargo
2543 override bool CanPutInCargo(EntityAI parent)
2544 {
2545 return false;
2546 }
2547
2548 override bool CanRemoveFromCargo(EntityAI parent)
2549 {
2550 return false;
2551 }
2552
2553 //hands
2554 override bool CanPutIntoHands(EntityAI parent)
2555 {
2556 return false;
2557 }
2558
2559 //--- ACTION CONDITIONS
2560 //direction
2561 override bool IsFacingPlayer(PlayerBase player, string selection)
2562 {
2563 return true;
2564 }
2565
2566 override bool IsPlayerInside(PlayerBase player, string selection)
2567 {
2568 return true;
2569 }
2570
2573 {
2574 return false;
2575 }
2576
2577 //camera direction check
2578 bool IsFacingCamera(string selection)
2579 {
2580 return true;
2581 }
2582
2583 //roof check
2585 {
2586 return false;
2587 }
2588
2589 //selection->player distance check
2590 bool HasProperDistance(string selection, PlayerBase player)
2591 {
2592 return true;
2593 }
2594
2595 //folding
2597 {
2598 if (HasBase() || GetInventory().AttachmentCount() > 0)
2599 return false;
2600
2601 return true;
2602 }
2603
2605 {
2608
2609 return item;
2610 }
2611
2612 //Damage triggers (barbed wire)
2613 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2614 {
2615 if (GetGame() && GetGame().IsServer())
2616 {
2617 //destroy area damage if some already exists
2619
2620 //create new area damage
2622 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2623
2624 vector min_max[2];
2625 if (MemoryPointExists(slot_name + "_min"))
2626 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2627 if (MemoryPointExists(slot_name + "_max"))
2628 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2629
2630 //get proper trigger extents (min<max)
2631 vector extents[2];
2632 GetConstruction().GetTriggerExtents(min_max, extents);
2633
2634 //get box center
2635 vector center;
2636 center = GetConstruction().GetBoxCenter(min_max);
2637 center = ModelToWorld(center);
2638
2639 //rotate center if needed
2642
2643 areaDamage.SetExtents(extents[0], extents[1]);
2644 areaDamage.SetAreaPosition(center);
2645 areaDamage.SetAreaOrientation(orientation);
2646 areaDamage.SetLoopInterval(1.0);
2647 areaDamage.SetDeferDuration(0.2);
2648 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2649 areaDamage.SetAmmoName("BarbedWireHit");
2650 areaDamage.Spawn();
2651
2653 }
2654 }
2655
2657 {
2658 if (angle_deg != 0)
2659 {
2660 //orientation
2662
2663 //center
2665 if (MemoryPointExists("rotate_axis"))
2666 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2669 center[0] = r_center_x;
2670 center[2] = r_center_z;
2671 }
2672 }
2673
2674 void DestroyAreaDamage(string slot_name)
2675 {
2676 if (GetGame() && GetGame().IsServer())
2677 {
2680 {
2681 if (areaDamage)
2682 areaDamage.Destroy();
2683
2685 }
2686 }
2687 }
2688
2689 override bool IsIgnoredByConstruction()
2690 {
2691 return true;
2692 }
2693
2694 //================================================================
2695 // SOUNDS
2696 //================================================================
2697 protected void SoundBuildStart(string part_name)
2698 {
2699 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2700 }
2701
2702 protected void SoundDismantleStart(string part_name)
2703 {
2704 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2705 }
2706
2707 protected void SoundDestroyStart(string part_name)
2708 {
2709 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2710 }
2711
2712 protected string GetBuildSoundByMaterial(string part_name)
2713 {
2715
2716 switch (material_type)
2717 {
2718 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2719 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2720 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2721 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2722 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2723 }
2724
2725 return "";
2726 }
2727
2728 protected string GetDismantleSoundByMaterial(string part_name)
2729 {
2731
2732 switch (material_type)
2733 {
2734 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2735 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2736 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2737 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2738 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2739 }
2740
2741 return "";
2742 }
2743
2744 //misc
2746 {
2747 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2748 {
2749 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2751 SetHealth(slot_name, "Health", item.GetHealth());
2752 }
2753 }
2754
2755 override int GetDamageSystemVersionChange()
2756 {
2757 return 111;
2758 }
2759
2760 override void SetActions()
2761 {
2762 super.SetActions();
2763
2765 //AddAction(ActionTakeHybridAttachment);
2766 //AddAction(ActionTakeHybridAttachmentToHands);
2769 }
2770
2771 //================================================================
2772 // DEBUG
2773 //================================================================
2774 protected void DebugCustomState()
2775 {
2776 }
2777
2780 {
2781 return null;
2782 }
2783
2784 override void OnDebugSpawn()
2785 {
2786 FullyBuild();
2787 }
2788
2789 void FullyBuild()
2790 {
2792 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2793
2794 Man p;
2795
2796#ifdef SERVER
2798 GetGame().GetWorld().GetPlayerList(players);
2799 if (players.Count())
2800 p = players[0];
2801#else
2802 p = GetGame().GetPlayer();
2803#endif
2804
2805 foreach (ConstructionPart part : parts)
2806 {
2807 bool excluded = false;
2808 string partName = part.GetPartName();
2809 if (excludes)
2810 {
2811 foreach (string exclude : excludes)
2812 {
2813 if (partName.Contains(exclude))
2814 {
2815 excluded = true;
2816 break;
2817 }
2818 }
2819 }
2820
2821 if (!excluded)
2823 }
2824
2825 GetConstruction().UpdateVisuals();
2826 }
2827}
2828
2829void bsbDebugPrint(string s)
2830{
2831#ifdef BSB_DEBUG
2832 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2833#else
2834 //Print("" + s); // comment/uncomment to hide/see debug logs
2835#endif
2836}
2837void bsbDebugSpam(string s)
2838{
2839#ifdef BSB_DEBUG_SPAM
2840 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2841#else
2842 //Print("" + s); // comment/uncomment to hide/see debug logs
2843#endif
2844}

◆ FoldBaseBuildingObject()

ItemBase bsbDebugPrint::FoldBaseBuildingObject ( )
protected

Definition at line 2124 of file BaseBuildingBase.c.

2126{
2127 const string ANIMATION_DEPLOYED = "Deployed";
2128
2129 float m_ConstructionKitHealth; //stored health value for used construction kit
2130
2132
2133 bool m_HasBase;
2134 //variables for synchronization of base building parts (2x31 is the current limit)
2135 int m_SyncParts01; //synchronization for already built parts (31 parts)
2136 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2137 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2138 int m_InteractedPartId; //construction part id that an action was performed on
2139 int m_PerformedActionId; //action id that was performed on a construction part
2140
2141 //Sounds
2142 //build
2143 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2144 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2145 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2146 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2147 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2148 //dismantle
2149 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2150 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2151 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2152 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2153 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2154
2155 protected EffectSound m_Sound;
2156
2160
2161 // Constructor
2162 void BaseBuildingBase()
2163 {
2165
2166 //synchronized variables
2167 RegisterNetSyncVariableInt("m_SyncParts01");
2168 RegisterNetSyncVariableInt("m_SyncParts02");
2169 RegisterNetSyncVariableInt("m_SyncParts03");
2170 RegisterNetSyncVariableInt("m_InteractedPartId");
2171 RegisterNetSyncVariableInt("m_PerformedActionId");
2172 RegisterNetSyncVariableBool("m_HasBase");
2173
2174 //Construction init
2176
2177 if (ConfigIsExisting("hybridAttachments"))
2178 {
2180 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2181 }
2182 if (ConfigIsExisting("mountables"))
2183 {
2185 ConfigGetTextArray("mountables", m_Mountables);
2186 }
2187
2188 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2189 }
2190
2191 override void EEDelete(EntityAI parent)
2192 {
2193 super.EEDelete(parent);
2194
2195 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2197
2198 }
2199
2200 override string GetInvulnerabilityTypeString()
2201 {
2202 return "disableBaseDamage";
2203 }
2204
2205 override bool CanObstruct()
2206 {
2207 return true;
2208 }
2209
2210 override int GetHideIconMask()
2211 {
2212 return EInventoryIconVisibility.HIDE_VICINITY;
2213 }
2214
2215 // --- SYNCHRONIZATION
2217 {
2218 if (GetGame().IsServer())
2219 SetSynchDirty();
2220 }
2221
2222 override void OnVariablesSynchronized()
2223 {
2224 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2225 super.OnVariablesSynchronized();
2226
2227 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2228 }
2229
2230 protected void OnSynchronizedClient()
2231 {
2232 //update parts
2234
2235 //update action on part
2237
2238 //update visuals (client)
2239 UpdateVisuals();
2240 }
2241
2242 //parts synchronization
2244 {
2245 //part_id must starts from index = 1
2246 int offset;
2247 int mask;
2248
2249 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2250 {
2251 offset = part_id - 1;
2252 mask = 1 << offset;
2253
2255 }
2256 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2257 {
2258 offset = (part_id % 32);
2259 mask = 1 << offset;
2260
2262 }
2263 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2264 {
2265 offset = (part_id % 63);
2266 mask = 1 << offset;
2267
2269 }
2270 }
2271
2273 {
2274 //part_id must starts from index = 1
2275 int offset;
2276 int mask;
2277
2278 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2279 {
2280 offset = part_id - 1;
2281 mask = 1 << offset;
2282
2284 }
2285 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2286 {
2287 offset = (part_id % 32);
2288 mask = 1 << offset;
2289
2291 }
2292 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2293 {
2294 offset = (part_id % 63);
2295 mask = 1 << offset;
2296
2298 }
2299 }
2300
2302 {
2303 //part_id must starts from index = 1
2304 int offset;
2305 int mask;
2306
2307 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2308 {
2309 offset = part_id - 1;
2310 mask = 1 << offset;
2311
2312 if ((m_SyncParts01 & mask) > 0)
2313 return true;
2314 }
2315 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2316 {
2317 offset = (part_id % 32);
2318 mask = 1 << offset;
2319
2320 if ((m_SyncParts02 & mask) > 0)
2321 return true;
2322 }
2323 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2324 {
2325 offset = (part_id % 63);
2326 mask = 1 << offset;
2327
2328 if ((m_SyncParts03 & mask) > 0)
2329 return true;
2330 }
2331
2332 return false;
2333 }
2334
2335 protected void RegisterActionForSync(int part_id, int action_id)
2336 {
2339 }
2340
2341 protected void ResetActionSyncData()
2342 {
2343 //reset data
2344 m_InteractedPartId = -1;
2346 }
2347
2348 protected void SetActionFromSyncData()
2349 {
2350 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2351 {
2354
2355 switch (build_action_id)
2356 {
2360 }
2361 }
2362 }
2363 //------
2364
2366 {
2367 string key = part.m_PartName;
2368 bool is_base = part.IsBase();
2370 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2372 {
2373 if (!part.IsBuilt())
2374 {
2375 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2376 GetConstruction().AddToConstructedParts(key);
2377 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2378
2379 if (is_base)
2380 {
2382 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2383 }
2384 }
2385 }
2386 else
2387 {
2388 if (part.IsBuilt())
2389 {
2390 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2391 GetConstruction().RemoveFromConstructedParts(key);
2392 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2393
2394 if (is_base)
2395 {
2397 AddProxyPhysics(ANIMATION_DEPLOYED);
2398 }
2399 }
2400 }
2401
2402 //check slot lock for material attachments
2403 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2404 }
2405
2406 //set construction parts based on synchronized data
2408 {
2411
2412 for (int i = 0; i < construction_parts.Count(); ++i)
2413 {
2414 string key = construction_parts.GetKey(i);
2417 }
2418
2419 //regenerate navmesh
2420 UpdateNavmesh();
2421 }
2422
2424 {
2427
2428 for (int i = 0; i < construction_parts.Count(); ++i)
2429 {
2430 string key = construction_parts.GetKey(i);
2432
2433 if (value.GetId() == id)
2434 return value;
2435 }
2436
2437 return NULL;
2438 }
2439 //
2440
2441 //Base
2442 bool HasBase()
2443 {
2444 return m_HasBase;
2445 }
2446
2447 void SetBaseState(bool has_base)
2448 {
2450 }
2451
2452 override bool IsDeployable()
2453 {
2454 return true;
2455 }
2456
2457 bool IsOpened()
2458 {
2459 return false;
2460 }
2461
2462 //--- CONSTRUCTION KIT
2464 {
2468
2469 return construction_kit;
2470 }
2471
2473 {
2474 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2477 }
2478
2479 protected vector GetKitSpawnPosition()
2480 {
2481 return GetPosition();
2482 }
2483
2484 protected string GetConstructionKitType()
2485 {
2486 return "";
2487 }
2488
2490 {
2492 GetGame().ObjectDelete(construction_kit);
2493 }
2494
2495 //--- CONSTRUCTION
2496 void DestroyConstruction()
2497 {
2498 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2499 GetGame().ObjectDelete(this);
2500 }
2501
2502 // --- EVENTS
2503 override void OnStoreSave(ParamsWriteContext ctx)
2504 {
2505 super.OnStoreSave(ctx);
2506
2507 //sync parts 01
2508 ctx.Write(m_SyncParts01);
2509 ctx.Write(m_SyncParts02);
2510 ctx.Write(m_SyncParts03);
2511
2512 ctx.Write(m_HasBase);
2513 }
2514
2515 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2516 {
2517 if (!super.OnStoreLoad(ctx, version))
2518 return false;
2519
2520 //--- Base building data ---
2521 //Restore synced parts data
2522 if (!ctx.Read(m_SyncParts01))
2523 {
2524 m_SyncParts01 = 0; //set default
2525 return false;
2526 }
2527 if (!ctx.Read(m_SyncParts02))
2528 {
2529 m_SyncParts02 = 0; //set default
2530 return false;
2531 }
2532 if (!ctx.Read(m_SyncParts03))
2533 {
2534 m_SyncParts03 = 0; //set default
2535 return false;
2536 }
2537
2538 //has base
2539 if (!ctx.Read(m_HasBase))
2540 {
2541 m_HasBase = false;
2542 return false;
2543 }
2544 //---
2545
2546 return true;
2547 }
2548
2549 override void AfterStoreLoad()
2550 {
2551 super.AfterStoreLoad();
2552
2555 }
2556
2558 {
2559 //update server data
2561
2562 //set base state
2563 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2564 SetBaseState(construction_part.IsBuilt()) ;
2565
2566 //synchronize after load
2568 }
2569
2570 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2571 {
2573 return;
2574
2575 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2576
2577 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2578 return;
2579
2581 string part_name = zone;
2582 part_name.ToLower();
2583
2585 {
2587
2588 if (construction_part && construction.IsPartConstructed(part_name))
2589 {
2590 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2591 construction.DestroyConnectedParts(part_name);
2592 }
2593
2594 //barbed wire handling (hack-ish)
2595 if (part_name.Contains("barbed"))
2596 {
2597 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2598 if (barbed_wire)
2599 barbed_wire.SetMountedState(false);
2600 }
2601 }
2602 }
2603
2604 override void EEOnAfterLoad()
2605 {
2607 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2608
2609 super.EEOnAfterLoad();
2610 }
2611
2612 override void EEInit()
2613 {
2614 super.EEInit();
2615
2616 // init visuals and physics
2617 InitBaseState();
2618
2619 //debug
2620#ifdef DEVELOPER
2622#endif
2623 }
2624
2625 override void EEItemAttached(EntityAI item, string slot_name)
2626 {
2627 super.EEItemAttached(item, slot_name);
2628
2630 UpdateVisuals();
2632 }
2633
2634 override void EEItemDetached(EntityAI item, string slot_name)
2635 {
2636 super.EEItemDetached(item, slot_name);
2637
2638 UpdateVisuals();
2640 }
2641
2642 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2643 {
2645 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2646
2649 }
2650
2651 //ignore out of reach condition
2652 override bool IgnoreOutOfReachCondition()
2653 {
2654 return true;
2655 }
2656
2657 //CONSTRUCTION EVENTS
2658 //Build
2659 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2660 {
2662
2663 //check base state
2664 if (construtionPart.IsBase())
2665 {
2666 SetBaseState(true);
2667
2668 //spawn kit
2670 }
2671
2672 //register constructed parts for synchronization
2674
2675 //register action that was performed on part
2677
2678 //synchronize
2680
2681 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2682
2683 UpdateNavmesh();
2684
2685 //update visuals
2686 UpdateVisuals();
2687
2688 //reset action sync data
2689 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2690 }
2691
2692 void OnPartBuiltClient(string part_name, int action_id)
2693 {
2694 //play sound
2696 }
2697
2698 //Dismantle
2700 {
2701 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2703
2704 //register constructed parts for synchronization
2706
2707 //register action that was performed on part
2709
2710 //synchronize
2712
2713 // server part of sync, client will be synced from SetPartsFromSyncData
2715
2716 UpdateNavmesh();
2717
2718 //update visuals
2719 UpdateVisuals();
2720
2721 //reset action sync data
2722 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2723
2724 //check base state
2725 if (construtionPart.IsBase())
2726 {
2727 //Destroy construction
2728 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2729 }
2730 }
2731
2733 {
2734 //play sound
2736 }
2737
2738 //Destroy
2740 {
2741 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2743
2744 //register constructed parts for synchronization
2746
2747 //register action that was performed on part
2749
2750 //synchronize
2752
2753 // server part of sync, client will be synced from SetPartsFromSyncData
2755
2756 UpdateNavmesh();
2757
2758 //update visuals
2759 UpdateVisuals();
2760
2761 //reset action sync data
2762 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2763
2764 //check base state
2765 if (construtionPart.IsBase())
2766 {
2767 //Destroy construction
2768 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2769 }
2770 }
2771
2772 void OnPartDestroyedClient(string part_name, int action_id)
2773 {
2774 //play sound
2776 }
2777
2778 // --- UPDATE
2779 void InitBaseState()
2780 {
2781 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2782
2783 InitVisuals();
2784 UpdateNavmesh(); //regenerate navmesh
2785 GetConstruction().InitBaseState();
2786 }
2787
2788 void InitVisuals()
2789 {
2790 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2791 //check base
2792 if (!HasBase())
2793 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2794 else
2795 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2796
2797 GetConstruction().UpdateVisuals();
2798 }
2799
2800 void UpdateVisuals()
2801 {
2803
2805 foreach (string slotName : attachmentSlots)
2807
2808 //check base
2809 if (!HasBase())
2810 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2811 else
2812 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2813
2814 GetConstruction().UpdateVisuals();
2815 }
2816
2818 {
2819 string slotNameMounted = slot_name + "_Mounted";
2820 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2821
2822 if (attachment)
2823 {
2824 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2825 if (barbedWire && barbedWire.IsMounted())
2827 else
2829
2830 if (is_locked)
2831 {
2832 SetAnimationPhase(slotNameMounted, 0);
2833 SetAnimationPhase(slot_name, 1);
2834 }
2835 else
2836 {
2837 SetAnimationPhase(slotNameMounted, 1);
2838 SetAnimationPhase(slot_name, 0);
2839 }
2840 }
2841 else
2842 {
2843 SetAnimationPhase(slotNameMounted, 1);
2844 SetAnimationPhase(slot_name, 1);
2845
2847 }
2848 }
2849
2850 // avoid calling this function on frequent occasions, it's a massive performance hit
2851 void UpdatePhysics()
2852 {
2854 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2855
2858
2860 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2861
2862 foreach (string slotName : attachmentSlots)
2864
2865 //check base
2866 if (!HasBase())
2867 {
2869 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2870
2871 AddProxyPhysics(ANIMATION_DEPLOYED);
2872 }
2873 else
2874 {
2876 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2877
2878 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2879 }
2880
2881 GetConstruction().UpdatePhysics();
2882 UpdateNavmesh();
2883 }
2884
2886 {
2887 //checks for invalid appends; hotfix
2888 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2889 return;
2890 //----------------------------------
2891 string slot_name_mounted = slot_name + "_Mounted";
2892 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2893
2894 //remove proxy physics
2895 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2896 RemoveProxyPhysics(slot_name_mounted);
2897 RemoveProxyPhysics(slot_name);
2898
2899 if (attachment)
2900 {
2901 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2902 if (is_locked)
2903 {
2904 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2905 AddProxyPhysics(slot_name_mounted);
2906 }
2907 else
2908 {
2909 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2910 AddProxyPhysics(slot_name);
2911 }
2912 }
2913 }
2914
2915 protected void UpdateNavmesh()
2916 {
2917 SetAffectPathgraph(true, false);
2918 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2919 }
2920
2921 override bool CanUseConstruction()
2922 {
2923 return true;
2924 }
2925
2926 override bool CanUseConstructionBuild()
2927 {
2928 return true;
2929 }
2930
2932 {
2933 if (attachment)
2934 {
2936 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2937
2938 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2939 }
2940
2941 return false;
2942 }
2943
2944 protected bool IsAttachmentSlotLocked(string slot_name)
2945 {
2946 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2947 }
2948
2949 //--- ATTACHMENT SLOTS
2951 {
2952 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2953 if (GetGame().ConfigIsExisting(config_path))
2954 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2955 }
2956
2958 {
2959 return true;
2960 }
2961
2962 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2963 {
2964 return true;
2965 }
2966
2967 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2968 {
2969 return true;
2970 }
2971
2972 // --- INIT
2973 void ConstructionInit()
2974 {
2975 if (!m_Construction)
2976 m_Construction = new Construction(this);
2977
2978 GetConstruction().Init();
2979 }
2980
2982 {
2983 return m_Construction;
2984 }
2985
2986 //--- INVENTORY/ATTACHMENTS CONDITIONS
2987 //attachments
2989 {
2990 return super.CanReceiveAttachment(attachment, slotId);
2991 }
2992
2994 {
2995 int attachment_count = GetInventory().AttachmentCount();
2996 if (attachment_count > 0)
2997 {
2998 if (HasBase() && attachment_count == 1)
2999 return false;
3000
3001 return true;
3002 }
3003
3004 return false;
3005 }
3006
3007 override bool ShowZonesHealth()
3008 {
3009 return true;
3010 }
3011
3012 //this into/outo parent.Cargo
3013 override bool CanPutInCargo(EntityAI parent)
3014 {
3015 return false;
3016 }
3017
3018 override bool CanRemoveFromCargo(EntityAI parent)
3019 {
3020 return false;
3021 }
3022
3023 //hands
3024 override bool CanPutIntoHands(EntityAI parent)
3025 {
3026 return false;
3027 }
3028
3029 //--- ACTION CONDITIONS
3030 //direction
3031 override bool IsFacingPlayer(PlayerBase player, string selection)
3032 {
3033 return true;
3034 }
3035
3036 override bool IsPlayerInside(PlayerBase player, string selection)
3037 {
3038 return true;
3039 }
3040
3043 {
3044 return false;
3045 }
3046
3047 //camera direction check
3048 bool IsFacingCamera(string selection)
3049 {
3050 return true;
3051 }
3052
3053 //roof check
3055 {
3056 return false;
3057 }
3058
3059 //selection->player distance check
3060 bool HasProperDistance(string selection, PlayerBase player)
3061 {
3062 return true;
3063 }
3064
3065 //folding
3067 {
3068 if (HasBase() || GetInventory().AttachmentCount() > 0)
3069 return false;
3070
3071 return true;
3072 }
3073
3075 {
3078
3079 return item;
3080 }
3081
3082 //Damage triggers (barbed wire)
3083 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3084 {
3085 if (GetGame() && GetGame().IsServer())
3086 {
3087 //destroy area damage if some already exists
3089
3090 //create new area damage
3092 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3093
3094 vector min_max[2];
3095 if (MemoryPointExists(slot_name + "_min"))
3096 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3097 if (MemoryPointExists(slot_name + "_max"))
3098 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3099
3100 //get proper trigger extents (min<max)
3101 vector extents[2];
3102 GetConstruction().GetTriggerExtents(min_max, extents);
3103
3104 //get box center
3105 vector center;
3106 center = GetConstruction().GetBoxCenter(min_max);
3107 center = ModelToWorld(center);
3108
3109 //rotate center if needed
3112
3113 areaDamage.SetExtents(extents[0], extents[1]);
3114 areaDamage.SetAreaPosition(center);
3115 areaDamage.SetAreaOrientation(orientation);
3116 areaDamage.SetLoopInterval(1.0);
3117 areaDamage.SetDeferDuration(0.2);
3118 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3119 areaDamage.SetAmmoName("BarbedWireHit");
3120 areaDamage.Spawn();
3121
3123 }
3124 }
3125
3127 {
3128 if (angle_deg != 0)
3129 {
3130 //orientation
3132
3133 //center
3135 if (MemoryPointExists("rotate_axis"))
3136 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3139 center[0] = r_center_x;
3140 center[2] = r_center_z;
3141 }
3142 }
3143
3144 void DestroyAreaDamage(string slot_name)
3145 {
3146 if (GetGame() && GetGame().IsServer())
3147 {
3150 {
3151 if (areaDamage)
3152 areaDamage.Destroy();
3153
3155 }
3156 }
3157 }
3158
3159 override bool IsIgnoredByConstruction()
3160 {
3161 return true;
3162 }
3163
3164 //================================================================
3165 // SOUNDS
3166 //================================================================
3167 protected void SoundBuildStart(string part_name)
3168 {
3169 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3170 }
3171
3172 protected void SoundDismantleStart(string part_name)
3173 {
3174 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3175 }
3176
3177 protected void SoundDestroyStart(string part_name)
3178 {
3179 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3180 }
3181
3182 protected string GetBuildSoundByMaterial(string part_name)
3183 {
3185
3186 switch (material_type)
3187 {
3188 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3189 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3190 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3191 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3192 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3193 }
3194
3195 return "";
3196 }
3197
3198 protected string GetDismantleSoundByMaterial(string part_name)
3199 {
3201
3202 switch (material_type)
3203 {
3204 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3205 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3206 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3207 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3208 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3209 }
3210
3211 return "";
3212 }
3213
3214 //misc
3216 {
3217 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3218 {
3219 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3221 SetHealth(slot_name, "Health", item.GetHealth());
3222 }
3223 }
3224
3225 override int GetDamageSystemVersionChange()
3226 {
3227 return 111;
3228 }
3229
3230 override void SetActions()
3231 {
3232 super.SetActions();
3233
3235 //AddAction(ActionTakeHybridAttachment);
3236 //AddAction(ActionTakeHybridAttachmentToHands);
3239 }
3240
3241 //================================================================
3242 // DEBUG
3243 //================================================================
3244 protected void DebugCustomState()
3245 {
3246 }
3247
3250 {
3251 return null;
3252 }
3253
3254 override void OnDebugSpawn()
3255 {
3256 FullyBuild();
3257 }
3258
3259 void FullyBuild()
3260 {
3262 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3263
3264 Man p;
3265
3266#ifdef SERVER
3268 GetGame().GetWorld().GetPlayerList(players);
3269 if (players.Count())
3270 p = players[0];
3271#else
3272 p = GetGame().GetPlayer();
3273#endif
3274
3275 foreach (ConstructionPart part : parts)
3276 {
3277 bool excluded = false;
3278 string partName = part.GetPartName();
3279 if (excludes)
3280 {
3281 foreach (string exclude : excludes)
3282 {
3283 if (partName.Contains(exclude))
3284 {
3285 excluded = true;
3286 break;
3287 }
3288 }
3289 }
3290
3291 if (!excluded)
3293 }
3294
3295 GetConstruction().UpdateVisuals();
3296 }
3297}
3298
3299void bsbDebugPrint(string s)
3300{
3301#ifdef BSB_DEBUG
3302 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3303#else
3304 //Print("" + s); // comment/uncomment to hide/see debug logs
3305#endif
3306}
3307void bsbDebugSpam(string s)
3308{
3309#ifdef BSB_DEBUG_SPAM
3310 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3311#else
3312 //Print("" + s); // comment/uncomment to hide/see debug logs
3313#endif
3314}

◆ FullyBuild()

void bsbDebugPrint::FullyBuild ( )
protected

Definition at line 2309 of file BaseBuildingBase.c.

2311{
2312 const string ANIMATION_DEPLOYED = "Deployed";
2313
2314 float m_ConstructionKitHealth; //stored health value for used construction kit
2315
2317
2318 bool m_HasBase;
2319 //variables for synchronization of base building parts (2x31 is the current limit)
2320 int m_SyncParts01; //synchronization for already built parts (31 parts)
2321 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2322 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2323 int m_InteractedPartId; //construction part id that an action was performed on
2324 int m_PerformedActionId; //action id that was performed on a construction part
2325
2326 //Sounds
2327 //build
2328 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2329 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2330 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2331 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2332 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2333 //dismantle
2334 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2335 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2336 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2337 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2338 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2339
2340 protected EffectSound m_Sound;
2341
2345
2346 // Constructor
2347 void BaseBuildingBase()
2348 {
2350
2351 //synchronized variables
2352 RegisterNetSyncVariableInt("m_SyncParts01");
2353 RegisterNetSyncVariableInt("m_SyncParts02");
2354 RegisterNetSyncVariableInt("m_SyncParts03");
2355 RegisterNetSyncVariableInt("m_InteractedPartId");
2356 RegisterNetSyncVariableInt("m_PerformedActionId");
2357 RegisterNetSyncVariableBool("m_HasBase");
2358
2359 //Construction init
2361
2362 if (ConfigIsExisting("hybridAttachments"))
2363 {
2365 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2366 }
2367 if (ConfigIsExisting("mountables"))
2368 {
2370 ConfigGetTextArray("mountables", m_Mountables);
2371 }
2372
2373 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2374 }
2375
2376 override void EEDelete(EntityAI parent)
2377 {
2378 super.EEDelete(parent);
2379
2380 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2382
2383 }
2384
2385 override string GetInvulnerabilityTypeString()
2386 {
2387 return "disableBaseDamage";
2388 }
2389
2390 override bool CanObstruct()
2391 {
2392 return true;
2393 }
2394
2395 override int GetHideIconMask()
2396 {
2397 return EInventoryIconVisibility.HIDE_VICINITY;
2398 }
2399
2400 // --- SYNCHRONIZATION
2402 {
2403 if (GetGame().IsServer())
2404 SetSynchDirty();
2405 }
2406
2407 override void OnVariablesSynchronized()
2408 {
2409 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2410 super.OnVariablesSynchronized();
2411
2412 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2413 }
2414
2415 protected void OnSynchronizedClient()
2416 {
2417 //update parts
2419
2420 //update action on part
2422
2423 //update visuals (client)
2424 UpdateVisuals();
2425 }
2426
2427 //parts synchronization
2429 {
2430 //part_id must starts from index = 1
2431 int offset;
2432 int mask;
2433
2434 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2435 {
2436 offset = part_id - 1;
2437 mask = 1 << offset;
2438
2440 }
2441 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2442 {
2443 offset = (part_id % 32);
2444 mask = 1 << offset;
2445
2447 }
2448 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2449 {
2450 offset = (part_id % 63);
2451 mask = 1 << offset;
2452
2454 }
2455 }
2456
2458 {
2459 //part_id must starts from index = 1
2460 int offset;
2461 int mask;
2462
2463 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2464 {
2465 offset = part_id - 1;
2466 mask = 1 << offset;
2467
2469 }
2470 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2471 {
2472 offset = (part_id % 32);
2473 mask = 1 << offset;
2474
2476 }
2477 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2478 {
2479 offset = (part_id % 63);
2480 mask = 1 << offset;
2481
2483 }
2484 }
2485
2487 {
2488 //part_id must starts from index = 1
2489 int offset;
2490 int mask;
2491
2492 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2493 {
2494 offset = part_id - 1;
2495 mask = 1 << offset;
2496
2497 if ((m_SyncParts01 & mask) > 0)
2498 return true;
2499 }
2500 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2501 {
2502 offset = (part_id % 32);
2503 mask = 1 << offset;
2504
2505 if ((m_SyncParts02 & mask) > 0)
2506 return true;
2507 }
2508 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2509 {
2510 offset = (part_id % 63);
2511 mask = 1 << offset;
2512
2513 if ((m_SyncParts03 & mask) > 0)
2514 return true;
2515 }
2516
2517 return false;
2518 }
2519
2520 protected void RegisterActionForSync(int part_id, int action_id)
2521 {
2524 }
2525
2526 protected void ResetActionSyncData()
2527 {
2528 //reset data
2529 m_InteractedPartId = -1;
2531 }
2532
2533 protected void SetActionFromSyncData()
2534 {
2535 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2536 {
2539
2540 switch (build_action_id)
2541 {
2545 }
2546 }
2547 }
2548 //------
2549
2551 {
2552 string key = part.m_PartName;
2553 bool is_base = part.IsBase();
2555 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2557 {
2558 if (!part.IsBuilt())
2559 {
2560 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2561 GetConstruction().AddToConstructedParts(key);
2562 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2563
2564 if (is_base)
2565 {
2567 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2568 }
2569 }
2570 }
2571 else
2572 {
2573 if (part.IsBuilt())
2574 {
2575 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2576 GetConstruction().RemoveFromConstructedParts(key);
2577 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2578
2579 if (is_base)
2580 {
2582 AddProxyPhysics(ANIMATION_DEPLOYED);
2583 }
2584 }
2585 }
2586
2587 //check slot lock for material attachments
2588 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2589 }
2590
2591 //set construction parts based on synchronized data
2593 {
2596
2597 for (int i = 0; i < construction_parts.Count(); ++i)
2598 {
2599 string key = construction_parts.GetKey(i);
2602 }
2603
2604 //regenerate navmesh
2605 UpdateNavmesh();
2606 }
2607
2609 {
2612
2613 for (int i = 0; i < construction_parts.Count(); ++i)
2614 {
2615 string key = construction_parts.GetKey(i);
2617
2618 if (value.GetId() == id)
2619 return value;
2620 }
2621
2622 return NULL;
2623 }
2624 //
2625
2626 //Base
2627 bool HasBase()
2628 {
2629 return m_HasBase;
2630 }
2631
2632 void SetBaseState(bool has_base)
2633 {
2635 }
2636
2637 override bool IsDeployable()
2638 {
2639 return true;
2640 }
2641
2642 bool IsOpened()
2643 {
2644 return false;
2645 }
2646
2647 //--- CONSTRUCTION KIT
2649 {
2653
2654 return construction_kit;
2655 }
2656
2658 {
2659 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2662 }
2663
2664 protected vector GetKitSpawnPosition()
2665 {
2666 return GetPosition();
2667 }
2668
2669 protected string GetConstructionKitType()
2670 {
2671 return "";
2672 }
2673
2675 {
2677 GetGame().ObjectDelete(construction_kit);
2678 }
2679
2680 //--- CONSTRUCTION
2681 void DestroyConstruction()
2682 {
2683 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2684 GetGame().ObjectDelete(this);
2685 }
2686
2687 // --- EVENTS
2688 override void OnStoreSave(ParamsWriteContext ctx)
2689 {
2690 super.OnStoreSave(ctx);
2691
2692 //sync parts 01
2693 ctx.Write(m_SyncParts01);
2694 ctx.Write(m_SyncParts02);
2695 ctx.Write(m_SyncParts03);
2696
2697 ctx.Write(m_HasBase);
2698 }
2699
2700 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2701 {
2702 if (!super.OnStoreLoad(ctx, version))
2703 return false;
2704
2705 //--- Base building data ---
2706 //Restore synced parts data
2707 if (!ctx.Read(m_SyncParts01))
2708 {
2709 m_SyncParts01 = 0; //set default
2710 return false;
2711 }
2712 if (!ctx.Read(m_SyncParts02))
2713 {
2714 m_SyncParts02 = 0; //set default
2715 return false;
2716 }
2717 if (!ctx.Read(m_SyncParts03))
2718 {
2719 m_SyncParts03 = 0; //set default
2720 return false;
2721 }
2722
2723 //has base
2724 if (!ctx.Read(m_HasBase))
2725 {
2726 m_HasBase = false;
2727 return false;
2728 }
2729 //---
2730
2731 return true;
2732 }
2733
2734 override void AfterStoreLoad()
2735 {
2736 super.AfterStoreLoad();
2737
2740 }
2741
2743 {
2744 //update server data
2746
2747 //set base state
2748 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2749 SetBaseState(construction_part.IsBuilt()) ;
2750
2751 //synchronize after load
2753 }
2754
2755 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2756 {
2758 return;
2759
2760 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2761
2762 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2763 return;
2764
2766 string part_name = zone;
2767 part_name.ToLower();
2768
2770 {
2772
2773 if (construction_part && construction.IsPartConstructed(part_name))
2774 {
2775 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2776 construction.DestroyConnectedParts(part_name);
2777 }
2778
2779 //barbed wire handling (hack-ish)
2780 if (part_name.Contains("barbed"))
2781 {
2782 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2783 if (barbed_wire)
2784 barbed_wire.SetMountedState(false);
2785 }
2786 }
2787 }
2788
2789 override void EEOnAfterLoad()
2790 {
2792 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2793
2794 super.EEOnAfterLoad();
2795 }
2796
2797 override void EEInit()
2798 {
2799 super.EEInit();
2800
2801 // init visuals and physics
2802 InitBaseState();
2803
2804 //debug
2805#ifdef DEVELOPER
2807#endif
2808 }
2809
2810 override void EEItemAttached(EntityAI item, string slot_name)
2811 {
2812 super.EEItemAttached(item, slot_name);
2813
2815 UpdateVisuals();
2817 }
2818
2819 override void EEItemDetached(EntityAI item, string slot_name)
2820 {
2821 super.EEItemDetached(item, slot_name);
2822
2823 UpdateVisuals();
2825 }
2826
2827 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2828 {
2830 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2831
2834 }
2835
2836 //ignore out of reach condition
2837 override bool IgnoreOutOfReachCondition()
2838 {
2839 return true;
2840 }
2841
2842 //CONSTRUCTION EVENTS
2843 //Build
2844 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2845 {
2847
2848 //check base state
2849 if (construtionPart.IsBase())
2850 {
2851 SetBaseState(true);
2852
2853 //spawn kit
2855 }
2856
2857 //register constructed parts for synchronization
2859
2860 //register action that was performed on part
2862
2863 //synchronize
2865
2866 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2867
2868 UpdateNavmesh();
2869
2870 //update visuals
2871 UpdateVisuals();
2872
2873 //reset action sync data
2874 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2875 }
2876
2877 void OnPartBuiltClient(string part_name, int action_id)
2878 {
2879 //play sound
2881 }
2882
2883 //Dismantle
2885 {
2886 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2888
2889 //register constructed parts for synchronization
2891
2892 //register action that was performed on part
2894
2895 //synchronize
2897
2898 // server part of sync, client will be synced from SetPartsFromSyncData
2900
2901 UpdateNavmesh();
2902
2903 //update visuals
2904 UpdateVisuals();
2905
2906 //reset action sync data
2907 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2908
2909 //check base state
2910 if (construtionPart.IsBase())
2911 {
2912 //Destroy construction
2913 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2914 }
2915 }
2916
2918 {
2919 //play sound
2921 }
2922
2923 //Destroy
2925 {
2926 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2928
2929 //register constructed parts for synchronization
2931
2932 //register action that was performed on part
2934
2935 //synchronize
2937
2938 // server part of sync, client will be synced from SetPartsFromSyncData
2940
2941 UpdateNavmesh();
2942
2943 //update visuals
2944 UpdateVisuals();
2945
2946 //reset action sync data
2947 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2948
2949 //check base state
2950 if (construtionPart.IsBase())
2951 {
2952 //Destroy construction
2953 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2954 }
2955 }
2956
2957 void OnPartDestroyedClient(string part_name, int action_id)
2958 {
2959 //play sound
2961 }
2962
2963 // --- UPDATE
2964 void InitBaseState()
2965 {
2966 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2967
2968 InitVisuals();
2969 UpdateNavmesh(); //regenerate navmesh
2970 GetConstruction().InitBaseState();
2971 }
2972
2973 void InitVisuals()
2974 {
2975 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2976 //check base
2977 if (!HasBase())
2978 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2979 else
2980 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2981
2982 GetConstruction().UpdateVisuals();
2983 }
2984
2985 void UpdateVisuals()
2986 {
2988
2990 foreach (string slotName : attachmentSlots)
2992
2993 //check base
2994 if (!HasBase())
2995 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2996 else
2997 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2998
2999 GetConstruction().UpdateVisuals();
3000 }
3001
3003 {
3004 string slotNameMounted = slot_name + "_Mounted";
3005 EntityAI attachment = FindAttachmentBySlotName(slot_name);
3006
3007 if (attachment)
3008 {
3009 BarbedWire barbedWire = BarbedWire.Cast(attachment);
3010 if (barbedWire && barbedWire.IsMounted())
3012 else
3014
3015 if (is_locked)
3016 {
3017 SetAnimationPhase(slotNameMounted, 0);
3018 SetAnimationPhase(slot_name, 1);
3019 }
3020 else
3021 {
3022 SetAnimationPhase(slotNameMounted, 1);
3023 SetAnimationPhase(slot_name, 0);
3024 }
3025 }
3026 else
3027 {
3028 SetAnimationPhase(slotNameMounted, 1);
3029 SetAnimationPhase(slot_name, 1);
3030
3032 }
3033 }
3034
3035 // avoid calling this function on frequent occasions, it's a massive performance hit
3036 void UpdatePhysics()
3037 {
3039 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
3040
3043
3045 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
3046
3047 foreach (string slotName : attachmentSlots)
3049
3050 //check base
3051 if (!HasBase())
3052 {
3054 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
3055
3056 AddProxyPhysics(ANIMATION_DEPLOYED);
3057 }
3058 else
3059 {
3061 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
3062
3063 RemoveProxyPhysics(ANIMATION_DEPLOYED);
3064 }
3065
3066 GetConstruction().UpdatePhysics();
3067 UpdateNavmesh();
3068 }
3069
3071 {
3072 //checks for invalid appends; hotfix
3073 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
3074 return;
3075 //----------------------------------
3076 string slot_name_mounted = slot_name + "_Mounted";
3077 EntityAI attachment = FindAttachmentBySlotName(slot_name);
3078
3079 //remove proxy physics
3080 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
3081 RemoveProxyPhysics(slot_name_mounted);
3082 RemoveProxyPhysics(slot_name);
3083
3084 if (attachment)
3085 {
3086 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
3087 if (is_locked)
3088 {
3089 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
3090 AddProxyPhysics(slot_name_mounted);
3091 }
3092 else
3093 {
3094 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
3095 AddProxyPhysics(slot_name);
3096 }
3097 }
3098 }
3099
3100 protected void UpdateNavmesh()
3101 {
3102 SetAffectPathgraph(true, false);
3103 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
3104 }
3105
3106 override bool CanUseConstruction()
3107 {
3108 return true;
3109 }
3110
3111 override bool CanUseConstructionBuild()
3112 {
3113 return true;
3114 }
3115
3117 {
3118 if (attachment)
3119 {
3121 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
3122
3123 return GetInventory().GetSlotLock(inventory_location.GetSlot());
3124 }
3125
3126 return false;
3127 }
3128
3129 protected bool IsAttachmentSlotLocked(string slot_name)
3130 {
3131 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
3132 }
3133
3134 //--- ATTACHMENT SLOTS
3136 {
3137 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
3138 if (GetGame().ConfigIsExisting(config_path))
3139 GetGame().ConfigGetTextArray(config_path, attachment_slots);
3140 }
3141
3143 {
3144 return true;
3145 }
3146
3147 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
3148 {
3149 return true;
3150 }
3151
3152 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
3153 {
3154 return true;
3155 }
3156
3157 // --- INIT
3158 void ConstructionInit()
3159 {
3160 if (!m_Construction)
3161 m_Construction = new Construction(this);
3162
3163 GetConstruction().Init();
3164 }
3165
3167 {
3168 return m_Construction;
3169 }
3170
3171 //--- INVENTORY/ATTACHMENTS CONDITIONS
3172 //attachments
3174 {
3175 return super.CanReceiveAttachment(attachment, slotId);
3176 }
3177
3179 {
3180 int attachment_count = GetInventory().AttachmentCount();
3181 if (attachment_count > 0)
3182 {
3183 if (HasBase() && attachment_count == 1)
3184 return false;
3185
3186 return true;
3187 }
3188
3189 return false;
3190 }
3191
3192 override bool ShowZonesHealth()
3193 {
3194 return true;
3195 }
3196
3197 //this into/outo parent.Cargo
3198 override bool CanPutInCargo(EntityAI parent)
3199 {
3200 return false;
3201 }
3202
3203 override bool CanRemoveFromCargo(EntityAI parent)
3204 {
3205 return false;
3206 }
3207
3208 //hands
3209 override bool CanPutIntoHands(EntityAI parent)
3210 {
3211 return false;
3212 }
3213
3214 //--- ACTION CONDITIONS
3215 //direction
3216 override bool IsFacingPlayer(PlayerBase player, string selection)
3217 {
3218 return true;
3219 }
3220
3221 override bool IsPlayerInside(PlayerBase player, string selection)
3222 {
3223 return true;
3224 }
3225
3228 {
3229 return false;
3230 }
3231
3232 //camera direction check
3233 bool IsFacingCamera(string selection)
3234 {
3235 return true;
3236 }
3237
3238 //roof check
3240 {
3241 return false;
3242 }
3243
3244 //selection->player distance check
3245 bool HasProperDistance(string selection, PlayerBase player)
3246 {
3247 return true;
3248 }
3249
3250 //folding
3252 {
3253 if (HasBase() || GetInventory().AttachmentCount() > 0)
3254 return false;
3255
3256 return true;
3257 }
3258
3260 {
3263
3264 return item;
3265 }
3266
3267 //Damage triggers (barbed wire)
3268 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3269 {
3270 if (GetGame() && GetGame().IsServer())
3271 {
3272 //destroy area damage if some already exists
3274
3275 //create new area damage
3277 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3278
3279 vector min_max[2];
3280 if (MemoryPointExists(slot_name + "_min"))
3281 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3282 if (MemoryPointExists(slot_name + "_max"))
3283 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3284
3285 //get proper trigger extents (min<max)
3286 vector extents[2];
3287 GetConstruction().GetTriggerExtents(min_max, extents);
3288
3289 //get box center
3290 vector center;
3291 center = GetConstruction().GetBoxCenter(min_max);
3292 center = ModelToWorld(center);
3293
3294 //rotate center if needed
3297
3298 areaDamage.SetExtents(extents[0], extents[1]);
3299 areaDamage.SetAreaPosition(center);
3300 areaDamage.SetAreaOrientation(orientation);
3301 areaDamage.SetLoopInterval(1.0);
3302 areaDamage.SetDeferDuration(0.2);
3303 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3304 areaDamage.SetAmmoName("BarbedWireHit");
3305 areaDamage.Spawn();
3306
3308 }
3309 }
3310
3312 {
3313 if (angle_deg != 0)
3314 {
3315 //orientation
3317
3318 //center
3320 if (MemoryPointExists("rotate_axis"))
3321 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3324 center[0] = r_center_x;
3325 center[2] = r_center_z;
3326 }
3327 }
3328
3329 void DestroyAreaDamage(string slot_name)
3330 {
3331 if (GetGame() && GetGame().IsServer())
3332 {
3335 {
3336 if (areaDamage)
3337 areaDamage.Destroy();
3338
3340 }
3341 }
3342 }
3343
3344 override bool IsIgnoredByConstruction()
3345 {
3346 return true;
3347 }
3348
3349 //================================================================
3350 // SOUNDS
3351 //================================================================
3352 protected void SoundBuildStart(string part_name)
3353 {
3354 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3355 }
3356
3357 protected void SoundDismantleStart(string part_name)
3358 {
3359 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3360 }
3361
3362 protected void SoundDestroyStart(string part_name)
3363 {
3364 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3365 }
3366
3367 protected string GetBuildSoundByMaterial(string part_name)
3368 {
3370
3371 switch (material_type)
3372 {
3373 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3374 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3375 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3376 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3377 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3378 }
3379
3380 return "";
3381 }
3382
3383 protected string GetDismantleSoundByMaterial(string part_name)
3384 {
3386
3387 switch (material_type)
3388 {
3389 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3390 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3391 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3392 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3393 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3394 }
3395
3396 return "";
3397 }
3398
3399 //misc
3401 {
3402 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3403 {
3404 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3406 SetHealth(slot_name, "Health", item.GetHealth());
3407 }
3408 }
3409
3410 override int GetDamageSystemVersionChange()
3411 {
3412 return 111;
3413 }
3414
3415 override void SetActions()
3416 {
3417 super.SetActions();
3418
3420 //AddAction(ActionTakeHybridAttachment);
3421 //AddAction(ActionTakeHybridAttachmentToHands);
3424 }
3425
3426 //================================================================
3427 // DEBUG
3428 //================================================================
3429 protected void DebugCustomState()
3430 {
3431 }
3432
3435 {
3436 return null;
3437 }
3438
3439 override void OnDebugSpawn()
3440 {
3441 FullyBuild();
3442 }
3443
3444 void FullyBuild()
3445 {
3447 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3448
3449 Man p;
3450
3451#ifdef SERVER
3453 GetGame().GetWorld().GetPlayerList(players);
3454 if (players.Count())
3455 p = players[0];
3456#else
3457 p = GetGame().GetPlayer();
3458#endif
3459
3460 foreach (ConstructionPart part : parts)
3461 {
3462 bool excluded = false;
3463 string partName = part.GetPartName();
3464 if (excludes)
3465 {
3466 foreach (string exclude : excludes)
3467 {
3468 if (partName.Contains(exclude))
3469 {
3470 excluded = true;
3471 break;
3472 }
3473 }
3474 }
3475
3476 if (!excluded)
3478 }
3479
3480 GetConstruction().UpdateVisuals();
3481 }
3482}
3483
3484void bsbDebugPrint(string s)
3485{
3486#ifdef BSB_DEBUG
3487 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3488#else
3489 //Print("" + s); // comment/uncomment to hide/see debug logs
3490#endif
3491}
3492void bsbDebugSpam(string s)
3493{
3494#ifdef BSB_DEBUG_SPAM
3495 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3496#else
3497 //Print("" + s); // comment/uncomment to hide/see debug logs
3498#endif
3499}

Referenced by ItemBase::OnDebugSpawn().

◆ GetAttachmentSlots()

void bsbDebugPrint::GetAttachmentSlots ( EntityAI entity,
out array< string > attachment_slots )
protected

Definition at line 2000 of file BaseBuildingBase.c.

2002{
2003 const string ANIMATION_DEPLOYED = "Deployed";
2004
2005 float m_ConstructionKitHealth; //stored health value for used construction kit
2006
2008
2009 bool m_HasBase;
2010 //variables for synchronization of base building parts (2x31 is the current limit)
2011 int m_SyncParts01; //synchronization for already built parts (31 parts)
2012 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2013 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2014 int m_InteractedPartId; //construction part id that an action was performed on
2015 int m_PerformedActionId; //action id that was performed on a construction part
2016
2017 //Sounds
2018 //build
2019 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2020 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2021 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2022 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2023 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2024 //dismantle
2025 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2026 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2027 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2028 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2029 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2030
2031 protected EffectSound m_Sound;
2032
2036
2037 // Constructor
2038 void BaseBuildingBase()
2039 {
2041
2042 //synchronized variables
2043 RegisterNetSyncVariableInt("m_SyncParts01");
2044 RegisterNetSyncVariableInt("m_SyncParts02");
2045 RegisterNetSyncVariableInt("m_SyncParts03");
2046 RegisterNetSyncVariableInt("m_InteractedPartId");
2047 RegisterNetSyncVariableInt("m_PerformedActionId");
2048 RegisterNetSyncVariableBool("m_HasBase");
2049
2050 //Construction init
2052
2053 if (ConfigIsExisting("hybridAttachments"))
2054 {
2056 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2057 }
2058 if (ConfigIsExisting("mountables"))
2059 {
2061 ConfigGetTextArray("mountables", m_Mountables);
2062 }
2063
2064 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2065 }
2066
2067 override void EEDelete(EntityAI parent)
2068 {
2069 super.EEDelete(parent);
2070
2071 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2073
2074 }
2075
2076 override string GetInvulnerabilityTypeString()
2077 {
2078 return "disableBaseDamage";
2079 }
2080
2081 override bool CanObstruct()
2082 {
2083 return true;
2084 }
2085
2086 override int GetHideIconMask()
2087 {
2088 return EInventoryIconVisibility.HIDE_VICINITY;
2089 }
2090
2091 // --- SYNCHRONIZATION
2093 {
2094 if (GetGame().IsServer())
2095 SetSynchDirty();
2096 }
2097
2098 override void OnVariablesSynchronized()
2099 {
2100 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2101 super.OnVariablesSynchronized();
2102
2103 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2104 }
2105
2106 protected void OnSynchronizedClient()
2107 {
2108 //update parts
2110
2111 //update action on part
2113
2114 //update visuals (client)
2115 UpdateVisuals();
2116 }
2117
2118 //parts synchronization
2120 {
2121 //part_id must starts from index = 1
2122 int offset;
2123 int mask;
2124
2125 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2126 {
2127 offset = part_id - 1;
2128 mask = 1 << offset;
2129
2131 }
2132 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2133 {
2134 offset = (part_id % 32);
2135 mask = 1 << offset;
2136
2138 }
2139 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2140 {
2141 offset = (part_id % 63);
2142 mask = 1 << offset;
2143
2145 }
2146 }
2147
2149 {
2150 //part_id must starts from index = 1
2151 int offset;
2152 int mask;
2153
2154 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2155 {
2156 offset = part_id - 1;
2157 mask = 1 << offset;
2158
2160 }
2161 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2162 {
2163 offset = (part_id % 32);
2164 mask = 1 << offset;
2165
2167 }
2168 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2169 {
2170 offset = (part_id % 63);
2171 mask = 1 << offset;
2172
2174 }
2175 }
2176
2178 {
2179 //part_id must starts from index = 1
2180 int offset;
2181 int mask;
2182
2183 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2184 {
2185 offset = part_id - 1;
2186 mask = 1 << offset;
2187
2188 if ((m_SyncParts01 & mask) > 0)
2189 return true;
2190 }
2191 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2192 {
2193 offset = (part_id % 32);
2194 mask = 1 << offset;
2195
2196 if ((m_SyncParts02 & mask) > 0)
2197 return true;
2198 }
2199 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2200 {
2201 offset = (part_id % 63);
2202 mask = 1 << offset;
2203
2204 if ((m_SyncParts03 & mask) > 0)
2205 return true;
2206 }
2207
2208 return false;
2209 }
2210
2211 protected void RegisterActionForSync(int part_id, int action_id)
2212 {
2215 }
2216
2217 protected void ResetActionSyncData()
2218 {
2219 //reset data
2220 m_InteractedPartId = -1;
2222 }
2223
2224 protected void SetActionFromSyncData()
2225 {
2226 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2227 {
2230
2231 switch (build_action_id)
2232 {
2236 }
2237 }
2238 }
2239 //------
2240
2242 {
2243 string key = part.m_PartName;
2244 bool is_base = part.IsBase();
2246 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2248 {
2249 if (!part.IsBuilt())
2250 {
2251 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2252 GetConstruction().AddToConstructedParts(key);
2253 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2254
2255 if (is_base)
2256 {
2258 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2259 }
2260 }
2261 }
2262 else
2263 {
2264 if (part.IsBuilt())
2265 {
2266 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2267 GetConstruction().RemoveFromConstructedParts(key);
2268 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2269
2270 if (is_base)
2271 {
2273 AddProxyPhysics(ANIMATION_DEPLOYED);
2274 }
2275 }
2276 }
2277
2278 //check slot lock for material attachments
2279 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2280 }
2281
2282 //set construction parts based on synchronized data
2284 {
2287
2288 for (int i = 0; i < construction_parts.Count(); ++i)
2289 {
2290 string key = construction_parts.GetKey(i);
2293 }
2294
2295 //regenerate navmesh
2296 UpdateNavmesh();
2297 }
2298
2300 {
2303
2304 for (int i = 0; i < construction_parts.Count(); ++i)
2305 {
2306 string key = construction_parts.GetKey(i);
2308
2309 if (value.GetId() == id)
2310 return value;
2311 }
2312
2313 return NULL;
2314 }
2315 //
2316
2317 //Base
2318 bool HasBase()
2319 {
2320 return m_HasBase;
2321 }
2322
2323 void SetBaseState(bool has_base)
2324 {
2326 }
2327
2328 override bool IsDeployable()
2329 {
2330 return true;
2331 }
2332
2333 bool IsOpened()
2334 {
2335 return false;
2336 }
2337
2338 //--- CONSTRUCTION KIT
2340 {
2344
2345 return construction_kit;
2346 }
2347
2349 {
2350 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2353 }
2354
2355 protected vector GetKitSpawnPosition()
2356 {
2357 return GetPosition();
2358 }
2359
2360 protected string GetConstructionKitType()
2361 {
2362 return "";
2363 }
2364
2366 {
2368 GetGame().ObjectDelete(construction_kit);
2369 }
2370
2371 //--- CONSTRUCTION
2372 void DestroyConstruction()
2373 {
2374 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2375 GetGame().ObjectDelete(this);
2376 }
2377
2378 // --- EVENTS
2379 override void OnStoreSave(ParamsWriteContext ctx)
2380 {
2381 super.OnStoreSave(ctx);
2382
2383 //sync parts 01
2384 ctx.Write(m_SyncParts01);
2385 ctx.Write(m_SyncParts02);
2386 ctx.Write(m_SyncParts03);
2387
2388 ctx.Write(m_HasBase);
2389 }
2390
2391 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2392 {
2393 if (!super.OnStoreLoad(ctx, version))
2394 return false;
2395
2396 //--- Base building data ---
2397 //Restore synced parts data
2398 if (!ctx.Read(m_SyncParts01))
2399 {
2400 m_SyncParts01 = 0; //set default
2401 return false;
2402 }
2403 if (!ctx.Read(m_SyncParts02))
2404 {
2405 m_SyncParts02 = 0; //set default
2406 return false;
2407 }
2408 if (!ctx.Read(m_SyncParts03))
2409 {
2410 m_SyncParts03 = 0; //set default
2411 return false;
2412 }
2413
2414 //has base
2415 if (!ctx.Read(m_HasBase))
2416 {
2417 m_HasBase = false;
2418 return false;
2419 }
2420 //---
2421
2422 return true;
2423 }
2424
2425 override void AfterStoreLoad()
2426 {
2427 super.AfterStoreLoad();
2428
2431 }
2432
2434 {
2435 //update server data
2437
2438 //set base state
2439 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2440 SetBaseState(construction_part.IsBuilt()) ;
2441
2442 //synchronize after load
2444 }
2445
2446 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2447 {
2449 return;
2450
2451 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2452
2453 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2454 return;
2455
2457 string part_name = zone;
2458 part_name.ToLower();
2459
2461 {
2463
2464 if (construction_part && construction.IsPartConstructed(part_name))
2465 {
2466 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2467 construction.DestroyConnectedParts(part_name);
2468 }
2469
2470 //barbed wire handling (hack-ish)
2471 if (part_name.Contains("barbed"))
2472 {
2473 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2474 if (barbed_wire)
2475 barbed_wire.SetMountedState(false);
2476 }
2477 }
2478 }
2479
2480 override void EEOnAfterLoad()
2481 {
2483 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2484
2485 super.EEOnAfterLoad();
2486 }
2487
2488 override void EEInit()
2489 {
2490 super.EEInit();
2491
2492 // init visuals and physics
2493 InitBaseState();
2494
2495 //debug
2496#ifdef DEVELOPER
2498#endif
2499 }
2500
2501 override void EEItemAttached(EntityAI item, string slot_name)
2502 {
2503 super.EEItemAttached(item, slot_name);
2504
2506 UpdateVisuals();
2508 }
2509
2510 override void EEItemDetached(EntityAI item, string slot_name)
2511 {
2512 super.EEItemDetached(item, slot_name);
2513
2514 UpdateVisuals();
2516 }
2517
2518 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2519 {
2521 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2522
2525 }
2526
2527 //ignore out of reach condition
2528 override bool IgnoreOutOfReachCondition()
2529 {
2530 return true;
2531 }
2532
2533 //CONSTRUCTION EVENTS
2534 //Build
2535 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2536 {
2538
2539 //check base state
2540 if (construtionPart.IsBase())
2541 {
2542 SetBaseState(true);
2543
2544 //spawn kit
2546 }
2547
2548 //register constructed parts for synchronization
2550
2551 //register action that was performed on part
2553
2554 //synchronize
2556
2557 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2558
2559 UpdateNavmesh();
2560
2561 //update visuals
2562 UpdateVisuals();
2563
2564 //reset action sync data
2565 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2566 }
2567
2568 void OnPartBuiltClient(string part_name, int action_id)
2569 {
2570 //play sound
2572 }
2573
2574 //Dismantle
2576 {
2577 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2579
2580 //register constructed parts for synchronization
2582
2583 //register action that was performed on part
2585
2586 //synchronize
2588
2589 // server part of sync, client will be synced from SetPartsFromSyncData
2591
2592 UpdateNavmesh();
2593
2594 //update visuals
2595 UpdateVisuals();
2596
2597 //reset action sync data
2598 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2599
2600 //check base state
2601 if (construtionPart.IsBase())
2602 {
2603 //Destroy construction
2604 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2605 }
2606 }
2607
2609 {
2610 //play sound
2612 }
2613
2614 //Destroy
2616 {
2617 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2619
2620 //register constructed parts for synchronization
2622
2623 //register action that was performed on part
2625
2626 //synchronize
2628
2629 // server part of sync, client will be synced from SetPartsFromSyncData
2631
2632 UpdateNavmesh();
2633
2634 //update visuals
2635 UpdateVisuals();
2636
2637 //reset action sync data
2638 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2639
2640 //check base state
2641 if (construtionPart.IsBase())
2642 {
2643 //Destroy construction
2644 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2645 }
2646 }
2647
2648 void OnPartDestroyedClient(string part_name, int action_id)
2649 {
2650 //play sound
2652 }
2653
2654 // --- UPDATE
2655 void InitBaseState()
2656 {
2657 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2658
2659 InitVisuals();
2660 UpdateNavmesh(); //regenerate navmesh
2661 GetConstruction().InitBaseState();
2662 }
2663
2664 void InitVisuals()
2665 {
2666 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2667 //check base
2668 if (!HasBase())
2669 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2670 else
2671 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2672
2673 GetConstruction().UpdateVisuals();
2674 }
2675
2676 void UpdateVisuals()
2677 {
2679
2681 foreach (string slotName : attachmentSlots)
2683
2684 //check base
2685 if (!HasBase())
2686 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2687 else
2688 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2689
2690 GetConstruction().UpdateVisuals();
2691 }
2692
2694 {
2695 string slotNameMounted = slot_name + "_Mounted";
2696 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2697
2698 if (attachment)
2699 {
2700 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2701 if (barbedWire && barbedWire.IsMounted())
2703 else
2705
2706 if (is_locked)
2707 {
2708 SetAnimationPhase(slotNameMounted, 0);
2709 SetAnimationPhase(slot_name, 1);
2710 }
2711 else
2712 {
2713 SetAnimationPhase(slotNameMounted, 1);
2714 SetAnimationPhase(slot_name, 0);
2715 }
2716 }
2717 else
2718 {
2719 SetAnimationPhase(slotNameMounted, 1);
2720 SetAnimationPhase(slot_name, 1);
2721
2723 }
2724 }
2725
2726 // avoid calling this function on frequent occasions, it's a massive performance hit
2727 void UpdatePhysics()
2728 {
2730 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2731
2734
2736 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2737
2738 foreach (string slotName : attachmentSlots)
2740
2741 //check base
2742 if (!HasBase())
2743 {
2745 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2746
2747 AddProxyPhysics(ANIMATION_DEPLOYED);
2748 }
2749 else
2750 {
2752 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2753
2754 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2755 }
2756
2757 GetConstruction().UpdatePhysics();
2758 UpdateNavmesh();
2759 }
2760
2762 {
2763 //checks for invalid appends; hotfix
2764 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2765 return;
2766 //----------------------------------
2767 string slot_name_mounted = slot_name + "_Mounted";
2768 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2769
2770 //remove proxy physics
2771 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2772 RemoveProxyPhysics(slot_name_mounted);
2773 RemoveProxyPhysics(slot_name);
2774
2775 if (attachment)
2776 {
2777 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2778 if (is_locked)
2779 {
2780 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2781 AddProxyPhysics(slot_name_mounted);
2782 }
2783 else
2784 {
2785 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2786 AddProxyPhysics(slot_name);
2787 }
2788 }
2789 }
2790
2791 protected void UpdateNavmesh()
2792 {
2793 SetAffectPathgraph(true, false);
2794 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2795 }
2796
2797 override bool CanUseConstruction()
2798 {
2799 return true;
2800 }
2801
2802 override bool CanUseConstructionBuild()
2803 {
2804 return true;
2805 }
2806
2808 {
2809 if (attachment)
2810 {
2812 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2813
2814 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2815 }
2816
2817 return false;
2818 }
2819
2820 protected bool IsAttachmentSlotLocked(string slot_name)
2821 {
2822 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2823 }
2824
2825 //--- ATTACHMENT SLOTS
2827 {
2828 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2829 if (GetGame().ConfigIsExisting(config_path))
2830 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2831 }
2832
2834 {
2835 return true;
2836 }
2837
2838 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2839 {
2840 return true;
2841 }
2842
2843 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2844 {
2845 return true;
2846 }
2847
2848 // --- INIT
2849 void ConstructionInit()
2850 {
2851 if (!m_Construction)
2852 m_Construction = new Construction(this);
2853
2854 GetConstruction().Init();
2855 }
2856
2858 {
2859 return m_Construction;
2860 }
2861
2862 //--- INVENTORY/ATTACHMENTS CONDITIONS
2863 //attachments
2865 {
2866 return super.CanReceiveAttachment(attachment, slotId);
2867 }
2868
2870 {
2871 int attachment_count = GetInventory().AttachmentCount();
2872 if (attachment_count > 0)
2873 {
2874 if (HasBase() && attachment_count == 1)
2875 return false;
2876
2877 return true;
2878 }
2879
2880 return false;
2881 }
2882
2883 override bool ShowZonesHealth()
2884 {
2885 return true;
2886 }
2887
2888 //this into/outo parent.Cargo
2889 override bool CanPutInCargo(EntityAI parent)
2890 {
2891 return false;
2892 }
2893
2894 override bool CanRemoveFromCargo(EntityAI parent)
2895 {
2896 return false;
2897 }
2898
2899 //hands
2900 override bool CanPutIntoHands(EntityAI parent)
2901 {
2902 return false;
2903 }
2904
2905 //--- ACTION CONDITIONS
2906 //direction
2907 override bool IsFacingPlayer(PlayerBase player, string selection)
2908 {
2909 return true;
2910 }
2911
2912 override bool IsPlayerInside(PlayerBase player, string selection)
2913 {
2914 return true;
2915 }
2916
2919 {
2920 return false;
2921 }
2922
2923 //camera direction check
2924 bool IsFacingCamera(string selection)
2925 {
2926 return true;
2927 }
2928
2929 //roof check
2931 {
2932 return false;
2933 }
2934
2935 //selection->player distance check
2936 bool HasProperDistance(string selection, PlayerBase player)
2937 {
2938 return true;
2939 }
2940
2941 //folding
2943 {
2944 if (HasBase() || GetInventory().AttachmentCount() > 0)
2945 return false;
2946
2947 return true;
2948 }
2949
2951 {
2954
2955 return item;
2956 }
2957
2958 //Damage triggers (barbed wire)
2959 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2960 {
2961 if (GetGame() && GetGame().IsServer())
2962 {
2963 //destroy area damage if some already exists
2965
2966 //create new area damage
2968 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2969
2970 vector min_max[2];
2971 if (MemoryPointExists(slot_name + "_min"))
2972 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2973 if (MemoryPointExists(slot_name + "_max"))
2974 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2975
2976 //get proper trigger extents (min<max)
2977 vector extents[2];
2978 GetConstruction().GetTriggerExtents(min_max, extents);
2979
2980 //get box center
2981 vector center;
2982 center = GetConstruction().GetBoxCenter(min_max);
2983 center = ModelToWorld(center);
2984
2985 //rotate center if needed
2988
2989 areaDamage.SetExtents(extents[0], extents[1]);
2990 areaDamage.SetAreaPosition(center);
2991 areaDamage.SetAreaOrientation(orientation);
2992 areaDamage.SetLoopInterval(1.0);
2993 areaDamage.SetDeferDuration(0.2);
2994 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2995 areaDamage.SetAmmoName("BarbedWireHit");
2996 areaDamage.Spawn();
2997
2999 }
3000 }
3001
3003 {
3004 if (angle_deg != 0)
3005 {
3006 //orientation
3008
3009 //center
3011 if (MemoryPointExists("rotate_axis"))
3012 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3015 center[0] = r_center_x;
3016 center[2] = r_center_z;
3017 }
3018 }
3019
3020 void DestroyAreaDamage(string slot_name)
3021 {
3022 if (GetGame() && GetGame().IsServer())
3023 {
3026 {
3027 if (areaDamage)
3028 areaDamage.Destroy();
3029
3031 }
3032 }
3033 }
3034
3035 override bool IsIgnoredByConstruction()
3036 {
3037 return true;
3038 }
3039
3040 //================================================================
3041 // SOUNDS
3042 //================================================================
3043 protected void SoundBuildStart(string part_name)
3044 {
3045 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3046 }
3047
3048 protected void SoundDismantleStart(string part_name)
3049 {
3050 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3051 }
3052
3053 protected void SoundDestroyStart(string part_name)
3054 {
3055 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3056 }
3057
3058 protected string GetBuildSoundByMaterial(string part_name)
3059 {
3061
3062 switch (material_type)
3063 {
3064 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3065 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3066 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3067 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3068 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3069 }
3070
3071 return "";
3072 }
3073
3074 protected string GetDismantleSoundByMaterial(string part_name)
3075 {
3077
3078 switch (material_type)
3079 {
3080 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3081 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3082 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3083 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3084 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3085 }
3086
3087 return "";
3088 }
3089
3090 //misc
3092 {
3093 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3094 {
3095 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3097 SetHealth(slot_name, "Health", item.GetHealth());
3098 }
3099 }
3100
3101 override int GetDamageSystemVersionChange()
3102 {
3103 return 111;
3104 }
3105
3106 override void SetActions()
3107 {
3108 super.SetActions();
3109
3111 //AddAction(ActionTakeHybridAttachment);
3112 //AddAction(ActionTakeHybridAttachmentToHands);
3115 }
3116
3117 //================================================================
3118 // DEBUG
3119 //================================================================
3120 protected void DebugCustomState()
3121 {
3122 }
3123
3126 {
3127 return null;
3128 }
3129
3130 override void OnDebugSpawn()
3131 {
3132 FullyBuild();
3133 }
3134
3135 void FullyBuild()
3136 {
3138 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3139
3140 Man p;
3141
3142#ifdef SERVER
3144 GetGame().GetWorld().GetPlayerList(players);
3145 if (players.Count())
3146 p = players[0];
3147#else
3148 p = GetGame().GetPlayer();
3149#endif
3150
3151 foreach (ConstructionPart part : parts)
3152 {
3153 bool excluded = false;
3154 string partName = part.GetPartName();
3155 if (excludes)
3156 {
3157 foreach (string exclude : excludes)
3158 {
3159 if (partName.Contains(exclude))
3160 {
3161 excluded = true;
3162 break;
3163 }
3164 }
3165 }
3166
3167 if (!excluded)
3169 }
3170
3171 GetConstruction().UpdateVisuals();
3172 }
3173}
3174
3175void bsbDebugPrint(string s)
3176{
3177#ifdef BSB_DEBUG
3178 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3179#else
3180 //Print("" + s); // comment/uncomment to hide/see debug logs
3181#endif
3182}
3183void bsbDebugSpam(string s)
3184{
3185#ifdef BSB_DEBUG_SPAM
3186 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3187#else
3188 //Print("" + s); // comment/uncomment to hide/see debug logs
3189#endif
3190}

Referenced by ItemBase::UpdatePhysics(), and ItemBase::UpdateVisuals().

◆ GetBuildSoundByMaterial()

string bsbDebugPrint::GetBuildSoundByMaterial ( string part_name)
protected

Definition at line 2232 of file BaseBuildingBase.c.

2234{
2235 const string ANIMATION_DEPLOYED = "Deployed";
2236
2237 float m_ConstructionKitHealth; //stored health value for used construction kit
2238
2240
2241 bool m_HasBase;
2242 //variables for synchronization of base building parts (2x31 is the current limit)
2243 int m_SyncParts01; //synchronization for already built parts (31 parts)
2244 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2245 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2246 int m_InteractedPartId; //construction part id that an action was performed on
2247 int m_PerformedActionId; //action id that was performed on a construction part
2248
2249 //Sounds
2250 //build
2251 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2252 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2253 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2254 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2255 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2256 //dismantle
2257 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2258 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2259 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2260 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2261 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2262
2263 protected EffectSound m_Sound;
2264
2268
2269 // Constructor
2270 void BaseBuildingBase()
2271 {
2273
2274 //synchronized variables
2275 RegisterNetSyncVariableInt("m_SyncParts01");
2276 RegisterNetSyncVariableInt("m_SyncParts02");
2277 RegisterNetSyncVariableInt("m_SyncParts03");
2278 RegisterNetSyncVariableInt("m_InteractedPartId");
2279 RegisterNetSyncVariableInt("m_PerformedActionId");
2280 RegisterNetSyncVariableBool("m_HasBase");
2281
2282 //Construction init
2284
2285 if (ConfigIsExisting("hybridAttachments"))
2286 {
2288 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2289 }
2290 if (ConfigIsExisting("mountables"))
2291 {
2293 ConfigGetTextArray("mountables", m_Mountables);
2294 }
2295
2296 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2297 }
2298
2299 override void EEDelete(EntityAI parent)
2300 {
2301 super.EEDelete(parent);
2302
2303 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2305
2306 }
2307
2308 override string GetInvulnerabilityTypeString()
2309 {
2310 return "disableBaseDamage";
2311 }
2312
2313 override bool CanObstruct()
2314 {
2315 return true;
2316 }
2317
2318 override int GetHideIconMask()
2319 {
2320 return EInventoryIconVisibility.HIDE_VICINITY;
2321 }
2322
2323 // --- SYNCHRONIZATION
2325 {
2326 if (GetGame().IsServer())
2327 SetSynchDirty();
2328 }
2329
2330 override void OnVariablesSynchronized()
2331 {
2332 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2333 super.OnVariablesSynchronized();
2334
2335 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2336 }
2337
2338 protected void OnSynchronizedClient()
2339 {
2340 //update parts
2342
2343 //update action on part
2345
2346 //update visuals (client)
2347 UpdateVisuals();
2348 }
2349
2350 //parts synchronization
2352 {
2353 //part_id must starts from index = 1
2354 int offset;
2355 int mask;
2356
2357 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2358 {
2359 offset = part_id - 1;
2360 mask = 1 << offset;
2361
2363 }
2364 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2365 {
2366 offset = (part_id % 32);
2367 mask = 1 << offset;
2368
2370 }
2371 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2372 {
2373 offset = (part_id % 63);
2374 mask = 1 << offset;
2375
2377 }
2378 }
2379
2381 {
2382 //part_id must starts from index = 1
2383 int offset;
2384 int mask;
2385
2386 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2387 {
2388 offset = part_id - 1;
2389 mask = 1 << offset;
2390
2392 }
2393 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2394 {
2395 offset = (part_id % 32);
2396 mask = 1 << offset;
2397
2399 }
2400 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2401 {
2402 offset = (part_id % 63);
2403 mask = 1 << offset;
2404
2406 }
2407 }
2408
2410 {
2411 //part_id must starts from index = 1
2412 int offset;
2413 int mask;
2414
2415 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2416 {
2417 offset = part_id - 1;
2418 mask = 1 << offset;
2419
2420 if ((m_SyncParts01 & mask) > 0)
2421 return true;
2422 }
2423 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2424 {
2425 offset = (part_id % 32);
2426 mask = 1 << offset;
2427
2428 if ((m_SyncParts02 & mask) > 0)
2429 return true;
2430 }
2431 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2432 {
2433 offset = (part_id % 63);
2434 mask = 1 << offset;
2435
2436 if ((m_SyncParts03 & mask) > 0)
2437 return true;
2438 }
2439
2440 return false;
2441 }
2442
2443 protected void RegisterActionForSync(int part_id, int action_id)
2444 {
2447 }
2448
2449 protected void ResetActionSyncData()
2450 {
2451 //reset data
2452 m_InteractedPartId = -1;
2454 }
2455
2456 protected void SetActionFromSyncData()
2457 {
2458 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2459 {
2462
2463 switch (build_action_id)
2464 {
2468 }
2469 }
2470 }
2471 //------
2472
2474 {
2475 string key = part.m_PartName;
2476 bool is_base = part.IsBase();
2478 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2480 {
2481 if (!part.IsBuilt())
2482 {
2483 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2484 GetConstruction().AddToConstructedParts(key);
2485 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2486
2487 if (is_base)
2488 {
2490 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2491 }
2492 }
2493 }
2494 else
2495 {
2496 if (part.IsBuilt())
2497 {
2498 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2499 GetConstruction().RemoveFromConstructedParts(key);
2500 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2501
2502 if (is_base)
2503 {
2505 AddProxyPhysics(ANIMATION_DEPLOYED);
2506 }
2507 }
2508 }
2509
2510 //check slot lock for material attachments
2511 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2512 }
2513
2514 //set construction parts based on synchronized data
2516 {
2519
2520 for (int i = 0; i < construction_parts.Count(); ++i)
2521 {
2522 string key = construction_parts.GetKey(i);
2525 }
2526
2527 //regenerate navmesh
2528 UpdateNavmesh();
2529 }
2530
2532 {
2535
2536 for (int i = 0; i < construction_parts.Count(); ++i)
2537 {
2538 string key = construction_parts.GetKey(i);
2540
2541 if (value.GetId() == id)
2542 return value;
2543 }
2544
2545 return NULL;
2546 }
2547 //
2548
2549 //Base
2550 bool HasBase()
2551 {
2552 return m_HasBase;
2553 }
2554
2555 void SetBaseState(bool has_base)
2556 {
2558 }
2559
2560 override bool IsDeployable()
2561 {
2562 return true;
2563 }
2564
2565 bool IsOpened()
2566 {
2567 return false;
2568 }
2569
2570 //--- CONSTRUCTION KIT
2572 {
2576
2577 return construction_kit;
2578 }
2579
2581 {
2582 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2585 }
2586
2587 protected vector GetKitSpawnPosition()
2588 {
2589 return GetPosition();
2590 }
2591
2592 protected string GetConstructionKitType()
2593 {
2594 return "";
2595 }
2596
2598 {
2600 GetGame().ObjectDelete(construction_kit);
2601 }
2602
2603 //--- CONSTRUCTION
2604 void DestroyConstruction()
2605 {
2606 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2607 GetGame().ObjectDelete(this);
2608 }
2609
2610 // --- EVENTS
2611 override void OnStoreSave(ParamsWriteContext ctx)
2612 {
2613 super.OnStoreSave(ctx);
2614
2615 //sync parts 01
2616 ctx.Write(m_SyncParts01);
2617 ctx.Write(m_SyncParts02);
2618 ctx.Write(m_SyncParts03);
2619
2620 ctx.Write(m_HasBase);
2621 }
2622
2623 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2624 {
2625 if (!super.OnStoreLoad(ctx, version))
2626 return false;
2627
2628 //--- Base building data ---
2629 //Restore synced parts data
2630 if (!ctx.Read(m_SyncParts01))
2631 {
2632 m_SyncParts01 = 0; //set default
2633 return false;
2634 }
2635 if (!ctx.Read(m_SyncParts02))
2636 {
2637 m_SyncParts02 = 0; //set default
2638 return false;
2639 }
2640 if (!ctx.Read(m_SyncParts03))
2641 {
2642 m_SyncParts03 = 0; //set default
2643 return false;
2644 }
2645
2646 //has base
2647 if (!ctx.Read(m_HasBase))
2648 {
2649 m_HasBase = false;
2650 return false;
2651 }
2652 //---
2653
2654 return true;
2655 }
2656
2657 override void AfterStoreLoad()
2658 {
2659 super.AfterStoreLoad();
2660
2663 }
2664
2666 {
2667 //update server data
2669
2670 //set base state
2671 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2672 SetBaseState(construction_part.IsBuilt()) ;
2673
2674 //synchronize after load
2676 }
2677
2678 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2679 {
2681 return;
2682
2683 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2684
2685 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2686 return;
2687
2689 string part_name = zone;
2690 part_name.ToLower();
2691
2693 {
2695
2696 if (construction_part && construction.IsPartConstructed(part_name))
2697 {
2698 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2699 construction.DestroyConnectedParts(part_name);
2700 }
2701
2702 //barbed wire handling (hack-ish)
2703 if (part_name.Contains("barbed"))
2704 {
2705 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2706 if (barbed_wire)
2707 barbed_wire.SetMountedState(false);
2708 }
2709 }
2710 }
2711
2712 override void EEOnAfterLoad()
2713 {
2715 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2716
2717 super.EEOnAfterLoad();
2718 }
2719
2720 override void EEInit()
2721 {
2722 super.EEInit();
2723
2724 // init visuals and physics
2725 InitBaseState();
2726
2727 //debug
2728#ifdef DEVELOPER
2730#endif
2731 }
2732
2733 override void EEItemAttached(EntityAI item, string slot_name)
2734 {
2735 super.EEItemAttached(item, slot_name);
2736
2738 UpdateVisuals();
2740 }
2741
2742 override void EEItemDetached(EntityAI item, string slot_name)
2743 {
2744 super.EEItemDetached(item, slot_name);
2745
2746 UpdateVisuals();
2748 }
2749
2750 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2751 {
2753 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2754
2757 }
2758
2759 //ignore out of reach condition
2760 override bool IgnoreOutOfReachCondition()
2761 {
2762 return true;
2763 }
2764
2765 //CONSTRUCTION EVENTS
2766 //Build
2767 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2768 {
2770
2771 //check base state
2772 if (construtionPart.IsBase())
2773 {
2774 SetBaseState(true);
2775
2776 //spawn kit
2778 }
2779
2780 //register constructed parts for synchronization
2782
2783 //register action that was performed on part
2785
2786 //synchronize
2788
2789 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2790
2791 UpdateNavmesh();
2792
2793 //update visuals
2794 UpdateVisuals();
2795
2796 //reset action sync data
2797 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2798 }
2799
2800 void OnPartBuiltClient(string part_name, int action_id)
2801 {
2802 //play sound
2804 }
2805
2806 //Dismantle
2808 {
2809 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2811
2812 //register constructed parts for synchronization
2814
2815 //register action that was performed on part
2817
2818 //synchronize
2820
2821 // server part of sync, client will be synced from SetPartsFromSyncData
2823
2824 UpdateNavmesh();
2825
2826 //update visuals
2827 UpdateVisuals();
2828
2829 //reset action sync data
2830 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2831
2832 //check base state
2833 if (construtionPart.IsBase())
2834 {
2835 //Destroy construction
2836 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2837 }
2838 }
2839
2841 {
2842 //play sound
2844 }
2845
2846 //Destroy
2848 {
2849 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2851
2852 //register constructed parts for synchronization
2854
2855 //register action that was performed on part
2857
2858 //synchronize
2860
2861 // server part of sync, client will be synced from SetPartsFromSyncData
2863
2864 UpdateNavmesh();
2865
2866 //update visuals
2867 UpdateVisuals();
2868
2869 //reset action sync data
2870 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2871
2872 //check base state
2873 if (construtionPart.IsBase())
2874 {
2875 //Destroy construction
2876 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2877 }
2878 }
2879
2880 void OnPartDestroyedClient(string part_name, int action_id)
2881 {
2882 //play sound
2884 }
2885
2886 // --- UPDATE
2887 void InitBaseState()
2888 {
2889 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2890
2891 InitVisuals();
2892 UpdateNavmesh(); //regenerate navmesh
2893 GetConstruction().InitBaseState();
2894 }
2895
2896 void InitVisuals()
2897 {
2898 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2899 //check base
2900 if (!HasBase())
2901 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2902 else
2903 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2904
2905 GetConstruction().UpdateVisuals();
2906 }
2907
2908 void UpdateVisuals()
2909 {
2911
2913 foreach (string slotName : attachmentSlots)
2915
2916 //check base
2917 if (!HasBase())
2918 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2919 else
2920 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2921
2922 GetConstruction().UpdateVisuals();
2923 }
2924
2926 {
2927 string slotNameMounted = slot_name + "_Mounted";
2928 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2929
2930 if (attachment)
2931 {
2932 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2933 if (barbedWire && barbedWire.IsMounted())
2935 else
2937
2938 if (is_locked)
2939 {
2940 SetAnimationPhase(slotNameMounted, 0);
2941 SetAnimationPhase(slot_name, 1);
2942 }
2943 else
2944 {
2945 SetAnimationPhase(slotNameMounted, 1);
2946 SetAnimationPhase(slot_name, 0);
2947 }
2948 }
2949 else
2950 {
2951 SetAnimationPhase(slotNameMounted, 1);
2952 SetAnimationPhase(slot_name, 1);
2953
2955 }
2956 }
2957
2958 // avoid calling this function on frequent occasions, it's a massive performance hit
2959 void UpdatePhysics()
2960 {
2962 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2963
2966
2968 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2969
2970 foreach (string slotName : attachmentSlots)
2972
2973 //check base
2974 if (!HasBase())
2975 {
2977 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2978
2979 AddProxyPhysics(ANIMATION_DEPLOYED);
2980 }
2981 else
2982 {
2984 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2985
2986 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2987 }
2988
2989 GetConstruction().UpdatePhysics();
2990 UpdateNavmesh();
2991 }
2992
2994 {
2995 //checks for invalid appends; hotfix
2996 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2997 return;
2998 //----------------------------------
2999 string slot_name_mounted = slot_name + "_Mounted";
3000 EntityAI attachment = FindAttachmentBySlotName(slot_name);
3001
3002 //remove proxy physics
3003 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
3004 RemoveProxyPhysics(slot_name_mounted);
3005 RemoveProxyPhysics(slot_name);
3006
3007 if (attachment)
3008 {
3009 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
3010 if (is_locked)
3011 {
3012 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
3013 AddProxyPhysics(slot_name_mounted);
3014 }
3015 else
3016 {
3017 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
3018 AddProxyPhysics(slot_name);
3019 }
3020 }
3021 }
3022
3023 protected void UpdateNavmesh()
3024 {
3025 SetAffectPathgraph(true, false);
3026 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
3027 }
3028
3029 override bool CanUseConstruction()
3030 {
3031 return true;
3032 }
3033
3034 override bool CanUseConstructionBuild()
3035 {
3036 return true;
3037 }
3038
3040 {
3041 if (attachment)
3042 {
3044 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
3045
3046 return GetInventory().GetSlotLock(inventory_location.GetSlot());
3047 }
3048
3049 return false;
3050 }
3051
3052 protected bool IsAttachmentSlotLocked(string slot_name)
3053 {
3054 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
3055 }
3056
3057 //--- ATTACHMENT SLOTS
3059 {
3060 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
3061 if (GetGame().ConfigIsExisting(config_path))
3062 GetGame().ConfigGetTextArray(config_path, attachment_slots);
3063 }
3064
3066 {
3067 return true;
3068 }
3069
3070 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
3071 {
3072 return true;
3073 }
3074
3075 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
3076 {
3077 return true;
3078 }
3079
3080 // --- INIT
3081 void ConstructionInit()
3082 {
3083 if (!m_Construction)
3084 m_Construction = new Construction(this);
3085
3086 GetConstruction().Init();
3087 }
3088
3090 {
3091 return m_Construction;
3092 }
3093
3094 //--- INVENTORY/ATTACHMENTS CONDITIONS
3095 //attachments
3097 {
3098 return super.CanReceiveAttachment(attachment, slotId);
3099 }
3100
3102 {
3103 int attachment_count = GetInventory().AttachmentCount();
3104 if (attachment_count > 0)
3105 {
3106 if (HasBase() && attachment_count == 1)
3107 return false;
3108
3109 return true;
3110 }
3111
3112 return false;
3113 }
3114
3115 override bool ShowZonesHealth()
3116 {
3117 return true;
3118 }
3119
3120 //this into/outo parent.Cargo
3121 override bool CanPutInCargo(EntityAI parent)
3122 {
3123 return false;
3124 }
3125
3126 override bool CanRemoveFromCargo(EntityAI parent)
3127 {
3128 return false;
3129 }
3130
3131 //hands
3132 override bool CanPutIntoHands(EntityAI parent)
3133 {
3134 return false;
3135 }
3136
3137 //--- ACTION CONDITIONS
3138 //direction
3139 override bool IsFacingPlayer(PlayerBase player, string selection)
3140 {
3141 return true;
3142 }
3143
3144 override bool IsPlayerInside(PlayerBase player, string selection)
3145 {
3146 return true;
3147 }
3148
3151 {
3152 return false;
3153 }
3154
3155 //camera direction check
3156 bool IsFacingCamera(string selection)
3157 {
3158 return true;
3159 }
3160
3161 //roof check
3163 {
3164 return false;
3165 }
3166
3167 //selection->player distance check
3168 bool HasProperDistance(string selection, PlayerBase player)
3169 {
3170 return true;
3171 }
3172
3173 //folding
3175 {
3176 if (HasBase() || GetInventory().AttachmentCount() > 0)
3177 return false;
3178
3179 return true;
3180 }
3181
3183 {
3186
3187 return item;
3188 }
3189
3190 //Damage triggers (barbed wire)
3191 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3192 {
3193 if (GetGame() && GetGame().IsServer())
3194 {
3195 //destroy area damage if some already exists
3197
3198 //create new area damage
3200 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3201
3202 vector min_max[2];
3203 if (MemoryPointExists(slot_name + "_min"))
3204 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3205 if (MemoryPointExists(slot_name + "_max"))
3206 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3207
3208 //get proper trigger extents (min<max)
3209 vector extents[2];
3210 GetConstruction().GetTriggerExtents(min_max, extents);
3211
3212 //get box center
3213 vector center;
3214 center = GetConstruction().GetBoxCenter(min_max);
3215 center = ModelToWorld(center);
3216
3217 //rotate center if needed
3220
3221 areaDamage.SetExtents(extents[0], extents[1]);
3222 areaDamage.SetAreaPosition(center);
3223 areaDamage.SetAreaOrientation(orientation);
3224 areaDamage.SetLoopInterval(1.0);
3225 areaDamage.SetDeferDuration(0.2);
3226 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3227 areaDamage.SetAmmoName("BarbedWireHit");
3228 areaDamage.Spawn();
3229
3231 }
3232 }
3233
3235 {
3236 if (angle_deg != 0)
3237 {
3238 //orientation
3240
3241 //center
3243 if (MemoryPointExists("rotate_axis"))
3244 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3247 center[0] = r_center_x;
3248 center[2] = r_center_z;
3249 }
3250 }
3251
3252 void DestroyAreaDamage(string slot_name)
3253 {
3254 if (GetGame() && GetGame().IsServer())
3255 {
3258 {
3259 if (areaDamage)
3260 areaDamage.Destroy();
3261
3263 }
3264 }
3265 }
3266
3267 override bool IsIgnoredByConstruction()
3268 {
3269 return true;
3270 }
3271
3272 //================================================================
3273 // SOUNDS
3274 //================================================================
3275 protected void SoundBuildStart(string part_name)
3276 {
3277 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3278 }
3279
3280 protected void SoundDismantleStart(string part_name)
3281 {
3282 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3283 }
3284
3285 protected void SoundDestroyStart(string part_name)
3286 {
3287 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3288 }
3289
3290 protected string GetBuildSoundByMaterial(string part_name)
3291 {
3293
3294 switch (material_type)
3295 {
3296 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3297 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3298 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3299 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3300 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3301 }
3302
3303 return "";
3304 }
3305
3306 protected string GetDismantleSoundByMaterial(string part_name)
3307 {
3309
3310 switch (material_type)
3311 {
3312 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3313 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3314 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3315 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3316 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3317 }
3318
3319 return "";
3320 }
3321
3322 //misc
3324 {
3325 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3326 {
3327 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3329 SetHealth(slot_name, "Health", item.GetHealth());
3330 }
3331 }
3332
3333 override int GetDamageSystemVersionChange()
3334 {
3335 return 111;
3336 }
3337
3338 override void SetActions()
3339 {
3340 super.SetActions();
3341
3343 //AddAction(ActionTakeHybridAttachment);
3344 //AddAction(ActionTakeHybridAttachmentToHands);
3347 }
3348
3349 //================================================================
3350 // DEBUG
3351 //================================================================
3352 protected void DebugCustomState()
3353 {
3354 }
3355
3358 {
3359 return null;
3360 }
3361
3362 override void OnDebugSpawn()
3363 {
3364 FullyBuild();
3365 }
3366
3367 void FullyBuild()
3368 {
3370 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3371
3372 Man p;
3373
3374#ifdef SERVER
3376 GetGame().GetWorld().GetPlayerList(players);
3377 if (players.Count())
3378 p = players[0];
3379#else
3380 p = GetGame().GetPlayer();
3381#endif
3382
3383 foreach (ConstructionPart part : parts)
3384 {
3385 bool excluded = false;
3386 string partName = part.GetPartName();
3387 if (excludes)
3388 {
3389 foreach (string exclude : excludes)
3390 {
3391 if (partName.Contains(exclude))
3392 {
3393 excluded = true;
3394 break;
3395 }
3396 }
3397 }
3398
3399 if (!excluded)
3401 }
3402
3403 GetConstruction().UpdateVisuals();
3404 }
3405}
3406
3407void bsbDebugPrint(string s)
3408{
3409#ifdef BSB_DEBUG
3410 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3411#else
3412 //Print("" + s); // comment/uncomment to hide/see debug logs
3413#endif
3414}
3415void bsbDebugSpam(string s)
3416{
3417#ifdef BSB_DEBUG_SPAM
3418 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3419#else
3420 //Print("" + s); // comment/uncomment to hide/see debug logs
3421#endif
3422}

Referenced by ItemBase::SoundBuildStart().

◆ GetConstruction()

Construction bsbDebugPrint::GetConstruction ( )
protected

Definition at line 2031 of file BaseBuildingBase.c.

2033{
2034 const string ANIMATION_DEPLOYED = "Deployed";
2035
2036 float m_ConstructionKitHealth; //stored health value for used construction kit
2037
2039
2040 bool m_HasBase;
2041 //variables for synchronization of base building parts (2x31 is the current limit)
2042 int m_SyncParts01; //synchronization for already built parts (31 parts)
2043 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2044 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2045 int m_InteractedPartId; //construction part id that an action was performed on
2046 int m_PerformedActionId; //action id that was performed on a construction part
2047
2048 //Sounds
2049 //build
2050 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2051 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2052 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2053 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2054 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2055 //dismantle
2056 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2057 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2058 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2059 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2060 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2061
2062 protected EffectSound m_Sound;
2063
2067
2068 // Constructor
2069 void BaseBuildingBase()
2070 {
2072
2073 //synchronized variables
2074 RegisterNetSyncVariableInt("m_SyncParts01");
2075 RegisterNetSyncVariableInt("m_SyncParts02");
2076 RegisterNetSyncVariableInt("m_SyncParts03");
2077 RegisterNetSyncVariableInt("m_InteractedPartId");
2078 RegisterNetSyncVariableInt("m_PerformedActionId");
2079 RegisterNetSyncVariableBool("m_HasBase");
2080
2081 //Construction init
2083
2084 if (ConfigIsExisting("hybridAttachments"))
2085 {
2087 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2088 }
2089 if (ConfigIsExisting("mountables"))
2090 {
2092 ConfigGetTextArray("mountables", m_Mountables);
2093 }
2094
2095 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2096 }
2097
2098 override void EEDelete(EntityAI parent)
2099 {
2100 super.EEDelete(parent);
2101
2102 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2104
2105 }
2106
2107 override string GetInvulnerabilityTypeString()
2108 {
2109 return "disableBaseDamage";
2110 }
2111
2112 override bool CanObstruct()
2113 {
2114 return true;
2115 }
2116
2117 override int GetHideIconMask()
2118 {
2119 return EInventoryIconVisibility.HIDE_VICINITY;
2120 }
2121
2122 // --- SYNCHRONIZATION
2124 {
2125 if (GetGame().IsServer())
2126 SetSynchDirty();
2127 }
2128
2129 override void OnVariablesSynchronized()
2130 {
2131 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2132 super.OnVariablesSynchronized();
2133
2134 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2135 }
2136
2137 protected void OnSynchronizedClient()
2138 {
2139 //update parts
2141
2142 //update action on part
2144
2145 //update visuals (client)
2146 UpdateVisuals();
2147 }
2148
2149 //parts synchronization
2151 {
2152 //part_id must starts from index = 1
2153 int offset;
2154 int mask;
2155
2156 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2157 {
2158 offset = part_id - 1;
2159 mask = 1 << offset;
2160
2162 }
2163 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2164 {
2165 offset = (part_id % 32);
2166 mask = 1 << offset;
2167
2169 }
2170 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2171 {
2172 offset = (part_id % 63);
2173 mask = 1 << offset;
2174
2176 }
2177 }
2178
2180 {
2181 //part_id must starts from index = 1
2182 int offset;
2183 int mask;
2184
2185 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2186 {
2187 offset = part_id - 1;
2188 mask = 1 << offset;
2189
2191 }
2192 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2193 {
2194 offset = (part_id % 32);
2195 mask = 1 << offset;
2196
2198 }
2199 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2200 {
2201 offset = (part_id % 63);
2202 mask = 1 << offset;
2203
2205 }
2206 }
2207
2209 {
2210 //part_id must starts from index = 1
2211 int offset;
2212 int mask;
2213
2214 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2215 {
2216 offset = part_id - 1;
2217 mask = 1 << offset;
2218
2219 if ((m_SyncParts01 & mask) > 0)
2220 return true;
2221 }
2222 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2223 {
2224 offset = (part_id % 32);
2225 mask = 1 << offset;
2226
2227 if ((m_SyncParts02 & mask) > 0)
2228 return true;
2229 }
2230 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2231 {
2232 offset = (part_id % 63);
2233 mask = 1 << offset;
2234
2235 if ((m_SyncParts03 & mask) > 0)
2236 return true;
2237 }
2238
2239 return false;
2240 }
2241
2242 protected void RegisterActionForSync(int part_id, int action_id)
2243 {
2246 }
2247
2248 protected void ResetActionSyncData()
2249 {
2250 //reset data
2251 m_InteractedPartId = -1;
2253 }
2254
2255 protected void SetActionFromSyncData()
2256 {
2257 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2258 {
2261
2262 switch (build_action_id)
2263 {
2267 }
2268 }
2269 }
2270 //------
2271
2273 {
2274 string key = part.m_PartName;
2275 bool is_base = part.IsBase();
2277 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2279 {
2280 if (!part.IsBuilt())
2281 {
2282 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2283 GetConstruction().AddToConstructedParts(key);
2284 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2285
2286 if (is_base)
2287 {
2289 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2290 }
2291 }
2292 }
2293 else
2294 {
2295 if (part.IsBuilt())
2296 {
2297 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2298 GetConstruction().RemoveFromConstructedParts(key);
2299 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2300
2301 if (is_base)
2302 {
2304 AddProxyPhysics(ANIMATION_DEPLOYED);
2305 }
2306 }
2307 }
2308
2309 //check slot lock for material attachments
2310 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2311 }
2312
2313 //set construction parts based on synchronized data
2315 {
2318
2319 for (int i = 0; i < construction_parts.Count(); ++i)
2320 {
2321 string key = construction_parts.GetKey(i);
2324 }
2325
2326 //regenerate navmesh
2327 UpdateNavmesh();
2328 }
2329
2331 {
2334
2335 for (int i = 0; i < construction_parts.Count(); ++i)
2336 {
2337 string key = construction_parts.GetKey(i);
2339
2340 if (value.GetId() == id)
2341 return value;
2342 }
2343
2344 return NULL;
2345 }
2346 //
2347
2348 //Base
2349 bool HasBase()
2350 {
2351 return m_HasBase;
2352 }
2353
2354 void SetBaseState(bool has_base)
2355 {
2357 }
2358
2359 override bool IsDeployable()
2360 {
2361 return true;
2362 }
2363
2364 bool IsOpened()
2365 {
2366 return false;
2367 }
2368
2369 //--- CONSTRUCTION KIT
2371 {
2375
2376 return construction_kit;
2377 }
2378
2380 {
2381 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2384 }
2385
2386 protected vector GetKitSpawnPosition()
2387 {
2388 return GetPosition();
2389 }
2390
2391 protected string GetConstructionKitType()
2392 {
2393 return "";
2394 }
2395
2397 {
2399 GetGame().ObjectDelete(construction_kit);
2400 }
2401
2402 //--- CONSTRUCTION
2403 void DestroyConstruction()
2404 {
2405 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2406 GetGame().ObjectDelete(this);
2407 }
2408
2409 // --- EVENTS
2410 override void OnStoreSave(ParamsWriteContext ctx)
2411 {
2412 super.OnStoreSave(ctx);
2413
2414 //sync parts 01
2415 ctx.Write(m_SyncParts01);
2416 ctx.Write(m_SyncParts02);
2417 ctx.Write(m_SyncParts03);
2418
2419 ctx.Write(m_HasBase);
2420 }
2421
2422 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2423 {
2424 if (!super.OnStoreLoad(ctx, version))
2425 return false;
2426
2427 //--- Base building data ---
2428 //Restore synced parts data
2429 if (!ctx.Read(m_SyncParts01))
2430 {
2431 m_SyncParts01 = 0; //set default
2432 return false;
2433 }
2434 if (!ctx.Read(m_SyncParts02))
2435 {
2436 m_SyncParts02 = 0; //set default
2437 return false;
2438 }
2439 if (!ctx.Read(m_SyncParts03))
2440 {
2441 m_SyncParts03 = 0; //set default
2442 return false;
2443 }
2444
2445 //has base
2446 if (!ctx.Read(m_HasBase))
2447 {
2448 m_HasBase = false;
2449 return false;
2450 }
2451 //---
2452
2453 return true;
2454 }
2455
2456 override void AfterStoreLoad()
2457 {
2458 super.AfterStoreLoad();
2459
2462 }
2463
2465 {
2466 //update server data
2468
2469 //set base state
2470 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2471 SetBaseState(construction_part.IsBuilt()) ;
2472
2473 //synchronize after load
2475 }
2476
2477 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2478 {
2480 return;
2481
2482 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2483
2484 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2485 return;
2486
2488 string part_name = zone;
2489 part_name.ToLower();
2490
2492 {
2494
2495 if (construction_part && construction.IsPartConstructed(part_name))
2496 {
2497 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2498 construction.DestroyConnectedParts(part_name);
2499 }
2500
2501 //barbed wire handling (hack-ish)
2502 if (part_name.Contains("barbed"))
2503 {
2504 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2505 if (barbed_wire)
2506 barbed_wire.SetMountedState(false);
2507 }
2508 }
2509 }
2510
2511 override void EEOnAfterLoad()
2512 {
2514 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2515
2516 super.EEOnAfterLoad();
2517 }
2518
2519 override void EEInit()
2520 {
2521 super.EEInit();
2522
2523 // init visuals and physics
2524 InitBaseState();
2525
2526 //debug
2527#ifdef DEVELOPER
2529#endif
2530 }
2531
2532 override void EEItemAttached(EntityAI item, string slot_name)
2533 {
2534 super.EEItemAttached(item, slot_name);
2535
2537 UpdateVisuals();
2539 }
2540
2541 override void EEItemDetached(EntityAI item, string slot_name)
2542 {
2543 super.EEItemDetached(item, slot_name);
2544
2545 UpdateVisuals();
2547 }
2548
2549 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2550 {
2552 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2553
2556 }
2557
2558 //ignore out of reach condition
2559 override bool IgnoreOutOfReachCondition()
2560 {
2561 return true;
2562 }
2563
2564 //CONSTRUCTION EVENTS
2565 //Build
2566 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2567 {
2569
2570 //check base state
2571 if (construtionPart.IsBase())
2572 {
2573 SetBaseState(true);
2574
2575 //spawn kit
2577 }
2578
2579 //register constructed parts for synchronization
2581
2582 //register action that was performed on part
2584
2585 //synchronize
2587
2588 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2589
2590 UpdateNavmesh();
2591
2592 //update visuals
2593 UpdateVisuals();
2594
2595 //reset action sync data
2596 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2597 }
2598
2599 void OnPartBuiltClient(string part_name, int action_id)
2600 {
2601 //play sound
2603 }
2604
2605 //Dismantle
2607 {
2608 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2610
2611 //register constructed parts for synchronization
2613
2614 //register action that was performed on part
2616
2617 //synchronize
2619
2620 // server part of sync, client will be synced from SetPartsFromSyncData
2622
2623 UpdateNavmesh();
2624
2625 //update visuals
2626 UpdateVisuals();
2627
2628 //reset action sync data
2629 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2630
2631 //check base state
2632 if (construtionPart.IsBase())
2633 {
2634 //Destroy construction
2635 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2636 }
2637 }
2638
2640 {
2641 //play sound
2643 }
2644
2645 //Destroy
2647 {
2648 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2650
2651 //register constructed parts for synchronization
2653
2654 //register action that was performed on part
2656
2657 //synchronize
2659
2660 // server part of sync, client will be synced from SetPartsFromSyncData
2662
2663 UpdateNavmesh();
2664
2665 //update visuals
2666 UpdateVisuals();
2667
2668 //reset action sync data
2669 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2670
2671 //check base state
2672 if (construtionPart.IsBase())
2673 {
2674 //Destroy construction
2675 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2676 }
2677 }
2678
2679 void OnPartDestroyedClient(string part_name, int action_id)
2680 {
2681 //play sound
2683 }
2684
2685 // --- UPDATE
2686 void InitBaseState()
2687 {
2688 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2689
2690 InitVisuals();
2691 UpdateNavmesh(); //regenerate navmesh
2692 GetConstruction().InitBaseState();
2693 }
2694
2695 void InitVisuals()
2696 {
2697 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2698 //check base
2699 if (!HasBase())
2700 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2701 else
2702 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2703
2704 GetConstruction().UpdateVisuals();
2705 }
2706
2707 void UpdateVisuals()
2708 {
2710
2712 foreach (string slotName : attachmentSlots)
2714
2715 //check base
2716 if (!HasBase())
2717 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2718 else
2719 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2720
2721 GetConstruction().UpdateVisuals();
2722 }
2723
2725 {
2726 string slotNameMounted = slot_name + "_Mounted";
2727 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2728
2729 if (attachment)
2730 {
2731 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2732 if (barbedWire && barbedWire.IsMounted())
2734 else
2736
2737 if (is_locked)
2738 {
2739 SetAnimationPhase(slotNameMounted, 0);
2740 SetAnimationPhase(slot_name, 1);
2741 }
2742 else
2743 {
2744 SetAnimationPhase(slotNameMounted, 1);
2745 SetAnimationPhase(slot_name, 0);
2746 }
2747 }
2748 else
2749 {
2750 SetAnimationPhase(slotNameMounted, 1);
2751 SetAnimationPhase(slot_name, 1);
2752
2754 }
2755 }
2756
2757 // avoid calling this function on frequent occasions, it's a massive performance hit
2758 void UpdatePhysics()
2759 {
2761 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2762
2765
2767 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2768
2769 foreach (string slotName : attachmentSlots)
2771
2772 //check base
2773 if (!HasBase())
2774 {
2776 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2777
2778 AddProxyPhysics(ANIMATION_DEPLOYED);
2779 }
2780 else
2781 {
2783 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2784
2785 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2786 }
2787
2788 GetConstruction().UpdatePhysics();
2789 UpdateNavmesh();
2790 }
2791
2793 {
2794 //checks for invalid appends; hotfix
2795 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2796 return;
2797 //----------------------------------
2798 string slot_name_mounted = slot_name + "_Mounted";
2799 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2800
2801 //remove proxy physics
2802 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2803 RemoveProxyPhysics(slot_name_mounted);
2804 RemoveProxyPhysics(slot_name);
2805
2806 if (attachment)
2807 {
2808 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2809 if (is_locked)
2810 {
2811 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2812 AddProxyPhysics(slot_name_mounted);
2813 }
2814 else
2815 {
2816 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2817 AddProxyPhysics(slot_name);
2818 }
2819 }
2820 }
2821
2822 protected void UpdateNavmesh()
2823 {
2824 SetAffectPathgraph(true, false);
2825 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2826 }
2827
2828 override bool CanUseConstruction()
2829 {
2830 return true;
2831 }
2832
2833 override bool CanUseConstructionBuild()
2834 {
2835 return true;
2836 }
2837
2839 {
2840 if (attachment)
2841 {
2843 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2844
2845 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2846 }
2847
2848 return false;
2849 }
2850
2851 protected bool IsAttachmentSlotLocked(string slot_name)
2852 {
2853 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2854 }
2855
2856 //--- ATTACHMENT SLOTS
2858 {
2859 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2860 if (GetGame().ConfigIsExisting(config_path))
2861 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2862 }
2863
2865 {
2866 return true;
2867 }
2868
2869 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2870 {
2871 return true;
2872 }
2873
2874 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2875 {
2876 return true;
2877 }
2878
2879 // --- INIT
2880 void ConstructionInit()
2881 {
2882 if (!m_Construction)
2883 m_Construction = new Construction(this);
2884
2885 GetConstruction().Init();
2886 }
2887
2889 {
2890 return m_Construction;
2891 }
2892
2893 //--- INVENTORY/ATTACHMENTS CONDITIONS
2894 //attachments
2896 {
2897 return super.CanReceiveAttachment(attachment, slotId);
2898 }
2899
2901 {
2902 int attachment_count = GetInventory().AttachmentCount();
2903 if (attachment_count > 0)
2904 {
2905 if (HasBase() && attachment_count == 1)
2906 return false;
2907
2908 return true;
2909 }
2910
2911 return false;
2912 }
2913
2914 override bool ShowZonesHealth()
2915 {
2916 return true;
2917 }
2918
2919 //this into/outo parent.Cargo
2920 override bool CanPutInCargo(EntityAI parent)
2921 {
2922 return false;
2923 }
2924
2925 override bool CanRemoveFromCargo(EntityAI parent)
2926 {
2927 return false;
2928 }
2929
2930 //hands
2931 override bool CanPutIntoHands(EntityAI parent)
2932 {
2933 return false;
2934 }
2935
2936 //--- ACTION CONDITIONS
2937 //direction
2938 override bool IsFacingPlayer(PlayerBase player, string selection)
2939 {
2940 return true;
2941 }
2942
2943 override bool IsPlayerInside(PlayerBase player, string selection)
2944 {
2945 return true;
2946 }
2947
2950 {
2951 return false;
2952 }
2953
2954 //camera direction check
2955 bool IsFacingCamera(string selection)
2956 {
2957 return true;
2958 }
2959
2960 //roof check
2962 {
2963 return false;
2964 }
2965
2966 //selection->player distance check
2967 bool HasProperDistance(string selection, PlayerBase player)
2968 {
2969 return true;
2970 }
2971
2972 //folding
2974 {
2975 if (HasBase() || GetInventory().AttachmentCount() > 0)
2976 return false;
2977
2978 return true;
2979 }
2980
2982 {
2985
2986 return item;
2987 }
2988
2989 //Damage triggers (barbed wire)
2990 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2991 {
2992 if (GetGame() && GetGame().IsServer())
2993 {
2994 //destroy area damage if some already exists
2996
2997 //create new area damage
2999 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3000
3001 vector min_max[2];
3002 if (MemoryPointExists(slot_name + "_min"))
3003 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3004 if (MemoryPointExists(slot_name + "_max"))
3005 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3006
3007 //get proper trigger extents (min<max)
3008 vector extents[2];
3009 GetConstruction().GetTriggerExtents(min_max, extents);
3010
3011 //get box center
3012 vector center;
3013 center = GetConstruction().GetBoxCenter(min_max);
3014 center = ModelToWorld(center);
3015
3016 //rotate center if needed
3019
3020 areaDamage.SetExtents(extents[0], extents[1]);
3021 areaDamage.SetAreaPosition(center);
3022 areaDamage.SetAreaOrientation(orientation);
3023 areaDamage.SetLoopInterval(1.0);
3024 areaDamage.SetDeferDuration(0.2);
3025 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3026 areaDamage.SetAmmoName("BarbedWireHit");
3027 areaDamage.Spawn();
3028
3030 }
3031 }
3032
3034 {
3035 if (angle_deg != 0)
3036 {
3037 //orientation
3039
3040 //center
3042 if (MemoryPointExists("rotate_axis"))
3043 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3046 center[0] = r_center_x;
3047 center[2] = r_center_z;
3048 }
3049 }
3050
3051 void DestroyAreaDamage(string slot_name)
3052 {
3053 if (GetGame() && GetGame().IsServer())
3054 {
3057 {
3058 if (areaDamage)
3059 areaDamage.Destroy();
3060
3062 }
3063 }
3064 }
3065
3066 override bool IsIgnoredByConstruction()
3067 {
3068 return true;
3069 }
3070
3071 //================================================================
3072 // SOUNDS
3073 //================================================================
3074 protected void SoundBuildStart(string part_name)
3075 {
3076 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3077 }
3078
3079 protected void SoundDismantleStart(string part_name)
3080 {
3081 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3082 }
3083
3084 protected void SoundDestroyStart(string part_name)
3085 {
3086 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3087 }
3088
3089 protected string GetBuildSoundByMaterial(string part_name)
3090 {
3092
3093 switch (material_type)
3094 {
3095 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3096 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3097 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3098 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3099 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3100 }
3101
3102 return "";
3103 }
3104
3105 protected string GetDismantleSoundByMaterial(string part_name)
3106 {
3108
3109 switch (material_type)
3110 {
3111 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3112 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3113 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3114 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3115 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3116 }
3117
3118 return "";
3119 }
3120
3121 //misc
3123 {
3124 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3125 {
3126 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3128 SetHealth(slot_name, "Health", item.GetHealth());
3129 }
3130 }
3131
3132 override int GetDamageSystemVersionChange()
3133 {
3134 return 111;
3135 }
3136
3137 override void SetActions()
3138 {
3139 super.SetActions();
3140
3142 //AddAction(ActionTakeHybridAttachment);
3143 //AddAction(ActionTakeHybridAttachmentToHands);
3146 }
3147
3148 //================================================================
3149 // DEBUG
3150 //================================================================
3151 protected void DebugCustomState()
3152 {
3153 }
3154
3157 {
3158 return null;
3159 }
3160
3161 override void OnDebugSpawn()
3162 {
3163 FullyBuild();
3164 }
3165
3166 void FullyBuild()
3167 {
3169 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3170
3171 Man p;
3172
3173#ifdef SERVER
3175 GetGame().GetWorld().GetPlayerList(players);
3176 if (players.Count())
3177 p = players[0];
3178#else
3179 p = GetGame().GetPlayer();
3180#endif
3181
3182 foreach (ConstructionPart part : parts)
3183 {
3184 bool excluded = false;
3185 string partName = part.GetPartName();
3186 if (excludes)
3187 {
3188 foreach (string exclude : excludes)
3189 {
3190 if (partName.Contains(exclude))
3191 {
3192 excluded = true;
3193 break;
3194 }
3195 }
3196 }
3197
3198 if (!excluded)
3200 }
3201
3202 GetConstruction().UpdateVisuals();
3203 }
3204}
3205
3206void bsbDebugPrint(string s)
3207{
3208#ifdef BSB_DEBUG
3209 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3210#else
3211 //Print("" + s); // comment/uncomment to hide/see debug logs
3212#endif
3213}
3214void bsbDebugSpam(string s)
3215{
3216#ifdef BSB_DEBUG_SPAM
3217 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3218#else
3219 //Print("" + s); // comment/uncomment to hide/see debug logs
3220#endif
3221}

Referenced by BaseBuildingBase::AfterStoreLoad(), BaseBuildingBase::CanDisplayAttachmentCategory(), BaseBuildingBase::CanDisplayAttachmentSlot(), BaseBuildingBase::CanReceiveAttachment(), BaseBuildingBase::CheckGateState(), BaseBuildingBase::CheckSlotVerticalDistance(), ItemBase::ConstructionInit(), ItemBase::CreateAreaDamage(), ItemBase::EEHealthLevelChanged(), ItemBase::FullyBuild(), BaseBuildingBase::GateAttachmentConditions(), BaseBuildingBase::GateAttachmentsSanityCheck(), ItemBase::GetBuildSoundByMaterial(), ItemBase::GetConstructionPartById(), ItemBase::GetDismantleSoundByMaterial(), ItemBase::InitBaseState(), ItemBase::InitVisuals(), ItemBase::OnPartBuiltServer(), BaseBuildingBase::OnPartBuiltServer(), ItemBase::OnPartDestroyedServer(), BaseBuildingBase::OnPartDestroyedServer(), ItemBase::OnPartDismantledServer(), BaseBuildingBase::OnPartDismantledServer(), ItemBase::SetPartFromSyncData(), ItemBase::SetPartsAfterStoreLoad(), ItemBase::SetPartsFromSyncData(), ItemBase::UpdatePhysics(), ItemBase::UpdateVisuals(), and BaseBuildingBase::UpdateVisuals().

◆ GetConstructionKitType()

string bsbDebugPrint::GetConstructionKitType ( )
protected

Definition at line 1534 of file BaseBuildingBase.c.

1536{
1537 const string ANIMATION_DEPLOYED = "Deployed";
1538
1539 float m_ConstructionKitHealth; //stored health value for used construction kit
1540
1542
1543 bool m_HasBase;
1544 //variables for synchronization of base building parts (2x31 is the current limit)
1545 int m_SyncParts01; //synchronization for already built parts (31 parts)
1546 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1547 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1548 int m_InteractedPartId; //construction part id that an action was performed on
1549 int m_PerformedActionId; //action id that was performed on a construction part
1550
1551 //Sounds
1552 //build
1553 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1554 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1555 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1556 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1557 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1558 //dismantle
1559 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1560 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1561 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1562 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1563 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1564
1565 protected EffectSound m_Sound;
1566
1570
1571 // Constructor
1572 void BaseBuildingBase()
1573 {
1575
1576 //synchronized variables
1577 RegisterNetSyncVariableInt("m_SyncParts01");
1578 RegisterNetSyncVariableInt("m_SyncParts02");
1579 RegisterNetSyncVariableInt("m_SyncParts03");
1580 RegisterNetSyncVariableInt("m_InteractedPartId");
1581 RegisterNetSyncVariableInt("m_PerformedActionId");
1582 RegisterNetSyncVariableBool("m_HasBase");
1583
1584 //Construction init
1586
1587 if (ConfigIsExisting("hybridAttachments"))
1588 {
1590 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1591 }
1592 if (ConfigIsExisting("mountables"))
1593 {
1595 ConfigGetTextArray("mountables", m_Mountables);
1596 }
1597
1598 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1599 }
1600
1601 override void EEDelete(EntityAI parent)
1602 {
1603 super.EEDelete(parent);
1604
1605 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1607
1608 }
1609
1610 override string GetInvulnerabilityTypeString()
1611 {
1612 return "disableBaseDamage";
1613 }
1614
1615 override bool CanObstruct()
1616 {
1617 return true;
1618 }
1619
1620 override int GetHideIconMask()
1621 {
1622 return EInventoryIconVisibility.HIDE_VICINITY;
1623 }
1624
1625 // --- SYNCHRONIZATION
1627 {
1628 if (GetGame().IsServer())
1629 SetSynchDirty();
1630 }
1631
1632 override void OnVariablesSynchronized()
1633 {
1634 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1635 super.OnVariablesSynchronized();
1636
1637 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1638 }
1639
1640 protected void OnSynchronizedClient()
1641 {
1642 //update parts
1644
1645 //update action on part
1647
1648 //update visuals (client)
1649 UpdateVisuals();
1650 }
1651
1652 //parts synchronization
1654 {
1655 //part_id must starts from index = 1
1656 int offset;
1657 int mask;
1658
1659 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1660 {
1661 offset = part_id - 1;
1662 mask = 1 << offset;
1663
1665 }
1666 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1667 {
1668 offset = (part_id % 32);
1669 mask = 1 << offset;
1670
1672 }
1673 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1674 {
1675 offset = (part_id % 63);
1676 mask = 1 << offset;
1677
1679 }
1680 }
1681
1683 {
1684 //part_id must starts from index = 1
1685 int offset;
1686 int mask;
1687
1688 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1689 {
1690 offset = part_id - 1;
1691 mask = 1 << offset;
1692
1694 }
1695 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1696 {
1697 offset = (part_id % 32);
1698 mask = 1 << offset;
1699
1701 }
1702 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1703 {
1704 offset = (part_id % 63);
1705 mask = 1 << offset;
1706
1708 }
1709 }
1710
1712 {
1713 //part_id must starts from index = 1
1714 int offset;
1715 int mask;
1716
1717 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1718 {
1719 offset = part_id - 1;
1720 mask = 1 << offset;
1721
1722 if ((m_SyncParts01 & mask) > 0)
1723 return true;
1724 }
1725 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1726 {
1727 offset = (part_id % 32);
1728 mask = 1 << offset;
1729
1730 if ((m_SyncParts02 & mask) > 0)
1731 return true;
1732 }
1733 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1734 {
1735 offset = (part_id % 63);
1736 mask = 1 << offset;
1737
1738 if ((m_SyncParts03 & mask) > 0)
1739 return true;
1740 }
1741
1742 return false;
1743 }
1744
1745 protected void RegisterActionForSync(int part_id, int action_id)
1746 {
1749 }
1750
1751 protected void ResetActionSyncData()
1752 {
1753 //reset data
1754 m_InteractedPartId = -1;
1756 }
1757
1758 protected void SetActionFromSyncData()
1759 {
1760 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1761 {
1764
1765 switch (build_action_id)
1766 {
1770 }
1771 }
1772 }
1773 //------
1774
1776 {
1777 string key = part.m_PartName;
1778 bool is_base = part.IsBase();
1780 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1782 {
1783 if (!part.IsBuilt())
1784 {
1785 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1786 GetConstruction().AddToConstructedParts(key);
1787 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1788
1789 if (is_base)
1790 {
1792 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1793 }
1794 }
1795 }
1796 else
1797 {
1798 if (part.IsBuilt())
1799 {
1800 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1801 GetConstruction().RemoveFromConstructedParts(key);
1802 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1803
1804 if (is_base)
1805 {
1807 AddProxyPhysics(ANIMATION_DEPLOYED);
1808 }
1809 }
1810 }
1811
1812 //check slot lock for material attachments
1813 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1814 }
1815
1816 //set construction parts based on synchronized data
1818 {
1821
1822 for (int i = 0; i < construction_parts.Count(); ++i)
1823 {
1824 string key = construction_parts.GetKey(i);
1827 }
1828
1829 //regenerate navmesh
1830 UpdateNavmesh();
1831 }
1832
1834 {
1837
1838 for (int i = 0; i < construction_parts.Count(); ++i)
1839 {
1840 string key = construction_parts.GetKey(i);
1842
1843 if (value.GetId() == id)
1844 return value;
1845 }
1846
1847 return NULL;
1848 }
1849 //
1850
1851 //Base
1852 bool HasBase()
1853 {
1854 return m_HasBase;
1855 }
1856
1857 void SetBaseState(bool has_base)
1858 {
1860 }
1861
1862 override bool IsDeployable()
1863 {
1864 return true;
1865 }
1866
1867 bool IsOpened()
1868 {
1869 return false;
1870 }
1871
1872 //--- CONSTRUCTION KIT
1874 {
1878
1879 return construction_kit;
1880 }
1881
1883 {
1884 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1887 }
1888
1889 protected vector GetKitSpawnPosition()
1890 {
1891 return GetPosition();
1892 }
1893
1894 protected string GetConstructionKitType()
1895 {
1896 return "";
1897 }
1898
1900 {
1902 GetGame().ObjectDelete(construction_kit);
1903 }
1904
1905 //--- CONSTRUCTION
1906 void DestroyConstruction()
1907 {
1908 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1909 GetGame().ObjectDelete(this);
1910 }
1911
1912 // --- EVENTS
1913 override void OnStoreSave(ParamsWriteContext ctx)
1914 {
1915 super.OnStoreSave(ctx);
1916
1917 //sync parts 01
1918 ctx.Write(m_SyncParts01);
1919 ctx.Write(m_SyncParts02);
1920 ctx.Write(m_SyncParts03);
1921
1922 ctx.Write(m_HasBase);
1923 }
1924
1925 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1926 {
1927 if (!super.OnStoreLoad(ctx, version))
1928 return false;
1929
1930 //--- Base building data ---
1931 //Restore synced parts data
1932 if (!ctx.Read(m_SyncParts01))
1933 {
1934 m_SyncParts01 = 0; //set default
1935 return false;
1936 }
1937 if (!ctx.Read(m_SyncParts02))
1938 {
1939 m_SyncParts02 = 0; //set default
1940 return false;
1941 }
1942 if (!ctx.Read(m_SyncParts03))
1943 {
1944 m_SyncParts03 = 0; //set default
1945 return false;
1946 }
1947
1948 //has base
1949 if (!ctx.Read(m_HasBase))
1950 {
1951 m_HasBase = false;
1952 return false;
1953 }
1954 //---
1955
1956 return true;
1957 }
1958
1959 override void AfterStoreLoad()
1960 {
1961 super.AfterStoreLoad();
1962
1965 }
1966
1968 {
1969 //update server data
1971
1972 //set base state
1973 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1974 SetBaseState(construction_part.IsBuilt()) ;
1975
1976 //synchronize after load
1978 }
1979
1980 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1981 {
1983 return;
1984
1985 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1986
1987 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1988 return;
1989
1991 string part_name = zone;
1992 part_name.ToLower();
1993
1995 {
1997
1998 if (construction_part && construction.IsPartConstructed(part_name))
1999 {
2000 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2001 construction.DestroyConnectedParts(part_name);
2002 }
2003
2004 //barbed wire handling (hack-ish)
2005 if (part_name.Contains("barbed"))
2006 {
2007 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2008 if (barbed_wire)
2009 barbed_wire.SetMountedState(false);
2010 }
2011 }
2012 }
2013
2014 override void EEOnAfterLoad()
2015 {
2017 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2018
2019 super.EEOnAfterLoad();
2020 }
2021
2022 override void EEInit()
2023 {
2024 super.EEInit();
2025
2026 // init visuals and physics
2027 InitBaseState();
2028
2029 //debug
2030#ifdef DEVELOPER
2032#endif
2033 }
2034
2035 override void EEItemAttached(EntityAI item, string slot_name)
2036 {
2037 super.EEItemAttached(item, slot_name);
2038
2040 UpdateVisuals();
2042 }
2043
2044 override void EEItemDetached(EntityAI item, string slot_name)
2045 {
2046 super.EEItemDetached(item, slot_name);
2047
2048 UpdateVisuals();
2050 }
2051
2052 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2053 {
2055 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2056
2059 }
2060
2061 //ignore out of reach condition
2062 override bool IgnoreOutOfReachCondition()
2063 {
2064 return true;
2065 }
2066
2067 //CONSTRUCTION EVENTS
2068 //Build
2069 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2070 {
2072
2073 //check base state
2074 if (construtionPart.IsBase())
2075 {
2076 SetBaseState(true);
2077
2078 //spawn kit
2080 }
2081
2082 //register constructed parts for synchronization
2084
2085 //register action that was performed on part
2087
2088 //synchronize
2090
2091 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2092
2093 UpdateNavmesh();
2094
2095 //update visuals
2096 UpdateVisuals();
2097
2098 //reset action sync data
2099 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2100 }
2101
2102 void OnPartBuiltClient(string part_name, int action_id)
2103 {
2104 //play sound
2106 }
2107
2108 //Dismantle
2110 {
2111 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2113
2114 //register constructed parts for synchronization
2116
2117 //register action that was performed on part
2119
2120 //synchronize
2122
2123 // server part of sync, client will be synced from SetPartsFromSyncData
2125
2126 UpdateNavmesh();
2127
2128 //update visuals
2129 UpdateVisuals();
2130
2131 //reset action sync data
2132 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2133
2134 //check base state
2135 if (construtionPart.IsBase())
2136 {
2137 //Destroy construction
2138 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2139 }
2140 }
2141
2143 {
2144 //play sound
2146 }
2147
2148 //Destroy
2150 {
2151 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2153
2154 //register constructed parts for synchronization
2156
2157 //register action that was performed on part
2159
2160 //synchronize
2162
2163 // server part of sync, client will be synced from SetPartsFromSyncData
2165
2166 UpdateNavmesh();
2167
2168 //update visuals
2169 UpdateVisuals();
2170
2171 //reset action sync data
2172 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2173
2174 //check base state
2175 if (construtionPart.IsBase())
2176 {
2177 //Destroy construction
2178 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2179 }
2180 }
2181
2182 void OnPartDestroyedClient(string part_name, int action_id)
2183 {
2184 //play sound
2186 }
2187
2188 // --- UPDATE
2189 void InitBaseState()
2190 {
2191 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2192
2193 InitVisuals();
2194 UpdateNavmesh(); //regenerate navmesh
2195 GetConstruction().InitBaseState();
2196 }
2197
2198 void InitVisuals()
2199 {
2200 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2201 //check base
2202 if (!HasBase())
2203 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2204 else
2205 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2206
2207 GetConstruction().UpdateVisuals();
2208 }
2209
2210 void UpdateVisuals()
2211 {
2213
2215 foreach (string slotName : attachmentSlots)
2217
2218 //check base
2219 if (!HasBase())
2220 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2221 else
2222 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2223
2224 GetConstruction().UpdateVisuals();
2225 }
2226
2228 {
2229 string slotNameMounted = slot_name + "_Mounted";
2230 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2231
2232 if (attachment)
2233 {
2234 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2235 if (barbedWire && barbedWire.IsMounted())
2237 else
2239
2240 if (is_locked)
2241 {
2242 SetAnimationPhase(slotNameMounted, 0);
2243 SetAnimationPhase(slot_name, 1);
2244 }
2245 else
2246 {
2247 SetAnimationPhase(slotNameMounted, 1);
2248 SetAnimationPhase(slot_name, 0);
2249 }
2250 }
2251 else
2252 {
2253 SetAnimationPhase(slotNameMounted, 1);
2254 SetAnimationPhase(slot_name, 1);
2255
2257 }
2258 }
2259
2260 // avoid calling this function on frequent occasions, it's a massive performance hit
2261 void UpdatePhysics()
2262 {
2264 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2265
2268
2270 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2271
2272 foreach (string slotName : attachmentSlots)
2274
2275 //check base
2276 if (!HasBase())
2277 {
2279 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2280
2281 AddProxyPhysics(ANIMATION_DEPLOYED);
2282 }
2283 else
2284 {
2286 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2287
2288 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2289 }
2290
2291 GetConstruction().UpdatePhysics();
2292 UpdateNavmesh();
2293 }
2294
2296 {
2297 //checks for invalid appends; hotfix
2298 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2299 return;
2300 //----------------------------------
2301 string slot_name_mounted = slot_name + "_Mounted";
2302 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2303
2304 //remove proxy physics
2305 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2306 RemoveProxyPhysics(slot_name_mounted);
2307 RemoveProxyPhysics(slot_name);
2308
2309 if (attachment)
2310 {
2311 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2312 if (is_locked)
2313 {
2314 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2315 AddProxyPhysics(slot_name_mounted);
2316 }
2317 else
2318 {
2319 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2320 AddProxyPhysics(slot_name);
2321 }
2322 }
2323 }
2324
2325 protected void UpdateNavmesh()
2326 {
2327 SetAffectPathgraph(true, false);
2328 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2329 }
2330
2331 override bool CanUseConstruction()
2332 {
2333 return true;
2334 }
2335
2336 override bool CanUseConstructionBuild()
2337 {
2338 return true;
2339 }
2340
2342 {
2343 if (attachment)
2344 {
2346 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2347
2348 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2349 }
2350
2351 return false;
2352 }
2353
2354 protected bool IsAttachmentSlotLocked(string slot_name)
2355 {
2356 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2357 }
2358
2359 //--- ATTACHMENT SLOTS
2361 {
2362 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2363 if (GetGame().ConfigIsExisting(config_path))
2364 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2365 }
2366
2368 {
2369 return true;
2370 }
2371
2372 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2373 {
2374 return true;
2375 }
2376
2377 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2378 {
2379 return true;
2380 }
2381
2382 // --- INIT
2383 void ConstructionInit()
2384 {
2385 if (!m_Construction)
2386 m_Construction = new Construction(this);
2387
2388 GetConstruction().Init();
2389 }
2390
2392 {
2393 return m_Construction;
2394 }
2395
2396 //--- INVENTORY/ATTACHMENTS CONDITIONS
2397 //attachments
2399 {
2400 return super.CanReceiveAttachment(attachment, slotId);
2401 }
2402
2404 {
2405 int attachment_count = GetInventory().AttachmentCount();
2406 if (attachment_count > 0)
2407 {
2408 if (HasBase() && attachment_count == 1)
2409 return false;
2410
2411 return true;
2412 }
2413
2414 return false;
2415 }
2416
2417 override bool ShowZonesHealth()
2418 {
2419 return true;
2420 }
2421
2422 //this into/outo parent.Cargo
2423 override bool CanPutInCargo(EntityAI parent)
2424 {
2425 return false;
2426 }
2427
2428 override bool CanRemoveFromCargo(EntityAI parent)
2429 {
2430 return false;
2431 }
2432
2433 //hands
2434 override bool CanPutIntoHands(EntityAI parent)
2435 {
2436 return false;
2437 }
2438
2439 //--- ACTION CONDITIONS
2440 //direction
2441 override bool IsFacingPlayer(PlayerBase player, string selection)
2442 {
2443 return true;
2444 }
2445
2446 override bool IsPlayerInside(PlayerBase player, string selection)
2447 {
2448 return true;
2449 }
2450
2453 {
2454 return false;
2455 }
2456
2457 //camera direction check
2458 bool IsFacingCamera(string selection)
2459 {
2460 return true;
2461 }
2462
2463 //roof check
2465 {
2466 return false;
2467 }
2468
2469 //selection->player distance check
2470 bool HasProperDistance(string selection, PlayerBase player)
2471 {
2472 return true;
2473 }
2474
2475 //folding
2477 {
2478 if (HasBase() || GetInventory().AttachmentCount() > 0)
2479 return false;
2480
2481 return true;
2482 }
2483
2485 {
2488
2489 return item;
2490 }
2491
2492 //Damage triggers (barbed wire)
2493 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2494 {
2495 if (GetGame() && GetGame().IsServer())
2496 {
2497 //destroy area damage if some already exists
2499
2500 //create new area damage
2502 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2503
2504 vector min_max[2];
2505 if (MemoryPointExists(slot_name + "_min"))
2506 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2507 if (MemoryPointExists(slot_name + "_max"))
2508 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2509
2510 //get proper trigger extents (min<max)
2511 vector extents[2];
2512 GetConstruction().GetTriggerExtents(min_max, extents);
2513
2514 //get box center
2515 vector center;
2516 center = GetConstruction().GetBoxCenter(min_max);
2517 center = ModelToWorld(center);
2518
2519 //rotate center if needed
2522
2523 areaDamage.SetExtents(extents[0], extents[1]);
2524 areaDamage.SetAreaPosition(center);
2525 areaDamage.SetAreaOrientation(orientation);
2526 areaDamage.SetLoopInterval(1.0);
2527 areaDamage.SetDeferDuration(0.2);
2528 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2529 areaDamage.SetAmmoName("BarbedWireHit");
2530 areaDamage.Spawn();
2531
2533 }
2534 }
2535
2537 {
2538 if (angle_deg != 0)
2539 {
2540 //orientation
2542
2543 //center
2545 if (MemoryPointExists("rotate_axis"))
2546 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2549 center[0] = r_center_x;
2550 center[2] = r_center_z;
2551 }
2552 }
2553
2554 void DestroyAreaDamage(string slot_name)
2555 {
2556 if (GetGame() && GetGame().IsServer())
2557 {
2560 {
2561 if (areaDamage)
2562 areaDamage.Destroy();
2563
2565 }
2566 }
2567 }
2568
2569 override bool IsIgnoredByConstruction()
2570 {
2571 return true;
2572 }
2573
2574 //================================================================
2575 // SOUNDS
2576 //================================================================
2577 protected void SoundBuildStart(string part_name)
2578 {
2579 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2580 }
2581
2582 protected void SoundDismantleStart(string part_name)
2583 {
2584 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2585 }
2586
2587 protected void SoundDestroyStart(string part_name)
2588 {
2589 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2590 }
2591
2592 protected string GetBuildSoundByMaterial(string part_name)
2593 {
2595
2596 switch (material_type)
2597 {
2598 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2599 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2600 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2601 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2602 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2603 }
2604
2605 return "";
2606 }
2607
2608 protected string GetDismantleSoundByMaterial(string part_name)
2609 {
2611
2612 switch (material_type)
2613 {
2614 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2615 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2616 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2617 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2618 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2619 }
2620
2621 return "";
2622 }
2623
2624 //misc
2626 {
2627 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2628 {
2629 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2631 SetHealth(slot_name, "Health", item.GetHealth());
2632 }
2633 }
2634
2635 override int GetDamageSystemVersionChange()
2636 {
2637 return 111;
2638 }
2639
2640 override void SetActions()
2641 {
2642 super.SetActions();
2643
2645 //AddAction(ActionTakeHybridAttachment);
2646 //AddAction(ActionTakeHybridAttachmentToHands);
2649 }
2650
2651 //================================================================
2652 // DEBUG
2653 //================================================================
2654 protected void DebugCustomState()
2655 {
2656 }
2657
2660 {
2661 return null;
2662 }
2663
2664 override void OnDebugSpawn()
2665 {
2666 FullyBuild();
2667 }
2668
2669 void FullyBuild()
2670 {
2672 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2673
2674 Man p;
2675
2676#ifdef SERVER
2678 GetGame().GetWorld().GetPlayerList(players);
2679 if (players.Count())
2680 p = players[0];
2681#else
2682 p = GetGame().GetPlayer();
2683#endif
2684
2685 foreach (ConstructionPart part : parts)
2686 {
2687 bool excluded = false;
2688 string partName = part.GetPartName();
2689 if (excludes)
2690 {
2691 foreach (string exclude : excludes)
2692 {
2693 if (partName.Contains(exclude))
2694 {
2695 excluded = true;
2696 break;
2697 }
2698 }
2699 }
2700
2701 if (!excluded)
2703 }
2704
2705 GetConstruction().UpdateVisuals();
2706 }
2707}
2708
2709void bsbDebugPrint(string s)
2710{
2711#ifdef BSB_DEBUG
2712 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2713#else
2714 //Print("" + s); // comment/uncomment to hide/see debug logs
2715#endif
2716}
2717void bsbDebugSpam(string s)
2718{
2719#ifdef BSB_DEBUG_SPAM
2720 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2721#else
2722 //Print("" + s); // comment/uncomment to hide/see debug logs
2723#endif
2724}

◆ GetConstructionPartById()

ConstructionPart bsbDebugPrint::GetConstructionPartById ( int id)
protected

Definition at line 1473 of file BaseBuildingBase.c.

1475{
1476 const string ANIMATION_DEPLOYED = "Deployed";
1477
1478 float m_ConstructionKitHealth; //stored health value for used construction kit
1479
1481
1482 bool m_HasBase;
1483 //variables for synchronization of base building parts (2x31 is the current limit)
1484 int m_SyncParts01; //synchronization for already built parts (31 parts)
1485 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1486 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1487 int m_InteractedPartId; //construction part id that an action was performed on
1488 int m_PerformedActionId; //action id that was performed on a construction part
1489
1490 //Sounds
1491 //build
1492 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1493 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1494 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1495 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1496 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1497 //dismantle
1498 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1499 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1500 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1501 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1502 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1503
1504 protected EffectSound m_Sound;
1505
1509
1510 // Constructor
1511 void BaseBuildingBase()
1512 {
1514
1515 //synchronized variables
1516 RegisterNetSyncVariableInt("m_SyncParts01");
1517 RegisterNetSyncVariableInt("m_SyncParts02");
1518 RegisterNetSyncVariableInt("m_SyncParts03");
1519 RegisterNetSyncVariableInt("m_InteractedPartId");
1520 RegisterNetSyncVariableInt("m_PerformedActionId");
1521 RegisterNetSyncVariableBool("m_HasBase");
1522
1523 //Construction init
1525
1526 if (ConfigIsExisting("hybridAttachments"))
1527 {
1529 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1530 }
1531 if (ConfigIsExisting("mountables"))
1532 {
1534 ConfigGetTextArray("mountables", m_Mountables);
1535 }
1536
1537 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1538 }
1539
1540 override void EEDelete(EntityAI parent)
1541 {
1542 super.EEDelete(parent);
1543
1544 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1546
1547 }
1548
1549 override string GetInvulnerabilityTypeString()
1550 {
1551 return "disableBaseDamage";
1552 }
1553
1554 override bool CanObstruct()
1555 {
1556 return true;
1557 }
1558
1559 override int GetHideIconMask()
1560 {
1561 return EInventoryIconVisibility.HIDE_VICINITY;
1562 }
1563
1564 // --- SYNCHRONIZATION
1566 {
1567 if (GetGame().IsServer())
1568 SetSynchDirty();
1569 }
1570
1571 override void OnVariablesSynchronized()
1572 {
1573 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1574 super.OnVariablesSynchronized();
1575
1576 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1577 }
1578
1579 protected void OnSynchronizedClient()
1580 {
1581 //update parts
1583
1584 //update action on part
1586
1587 //update visuals (client)
1588 UpdateVisuals();
1589 }
1590
1591 //parts synchronization
1593 {
1594 //part_id must starts from index = 1
1595 int offset;
1596 int mask;
1597
1598 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1599 {
1600 offset = part_id - 1;
1601 mask = 1 << offset;
1602
1604 }
1605 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1606 {
1607 offset = (part_id % 32);
1608 mask = 1 << offset;
1609
1611 }
1612 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1613 {
1614 offset = (part_id % 63);
1615 mask = 1 << offset;
1616
1618 }
1619 }
1620
1622 {
1623 //part_id must starts from index = 1
1624 int offset;
1625 int mask;
1626
1627 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1628 {
1629 offset = part_id - 1;
1630 mask = 1 << offset;
1631
1633 }
1634 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1635 {
1636 offset = (part_id % 32);
1637 mask = 1 << offset;
1638
1640 }
1641 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1642 {
1643 offset = (part_id % 63);
1644 mask = 1 << offset;
1645
1647 }
1648 }
1649
1651 {
1652 //part_id must starts from index = 1
1653 int offset;
1654 int mask;
1655
1656 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1657 {
1658 offset = part_id - 1;
1659 mask = 1 << offset;
1660
1661 if ((m_SyncParts01 & mask) > 0)
1662 return true;
1663 }
1664 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1665 {
1666 offset = (part_id % 32);
1667 mask = 1 << offset;
1668
1669 if ((m_SyncParts02 & mask) > 0)
1670 return true;
1671 }
1672 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1673 {
1674 offset = (part_id % 63);
1675 mask = 1 << offset;
1676
1677 if ((m_SyncParts03 & mask) > 0)
1678 return true;
1679 }
1680
1681 return false;
1682 }
1683
1684 protected void RegisterActionForSync(int part_id, int action_id)
1685 {
1688 }
1689
1690 protected void ResetActionSyncData()
1691 {
1692 //reset data
1693 m_InteractedPartId = -1;
1695 }
1696
1697 protected void SetActionFromSyncData()
1698 {
1699 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1700 {
1703
1704 switch (build_action_id)
1705 {
1709 }
1710 }
1711 }
1712 //------
1713
1715 {
1716 string key = part.m_PartName;
1717 bool is_base = part.IsBase();
1719 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1721 {
1722 if (!part.IsBuilt())
1723 {
1724 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1725 GetConstruction().AddToConstructedParts(key);
1726 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1727
1728 if (is_base)
1729 {
1731 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1732 }
1733 }
1734 }
1735 else
1736 {
1737 if (part.IsBuilt())
1738 {
1739 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1740 GetConstruction().RemoveFromConstructedParts(key);
1741 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1742
1743 if (is_base)
1744 {
1746 AddProxyPhysics(ANIMATION_DEPLOYED);
1747 }
1748 }
1749 }
1750
1751 //check slot lock for material attachments
1752 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1753 }
1754
1755 //set construction parts based on synchronized data
1757 {
1760
1761 for (int i = 0; i < construction_parts.Count(); ++i)
1762 {
1763 string key = construction_parts.GetKey(i);
1766 }
1767
1768 //regenerate navmesh
1769 UpdateNavmesh();
1770 }
1771
1773 {
1776
1777 for (int i = 0; i < construction_parts.Count(); ++i)
1778 {
1779 string key = construction_parts.GetKey(i);
1781
1782 if (value.GetId() == id)
1783 return value;
1784 }
1785
1786 return NULL;
1787 }
1788 //
1789
1790 //Base
1791 bool HasBase()
1792 {
1793 return m_HasBase;
1794 }
1795
1796 void SetBaseState(bool has_base)
1797 {
1799 }
1800
1801 override bool IsDeployable()
1802 {
1803 return true;
1804 }
1805
1806 bool IsOpened()
1807 {
1808 return false;
1809 }
1810
1811 //--- CONSTRUCTION KIT
1813 {
1817
1818 return construction_kit;
1819 }
1820
1822 {
1823 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1826 }
1827
1828 protected vector GetKitSpawnPosition()
1829 {
1830 return GetPosition();
1831 }
1832
1833 protected string GetConstructionKitType()
1834 {
1835 return "";
1836 }
1837
1839 {
1841 GetGame().ObjectDelete(construction_kit);
1842 }
1843
1844 //--- CONSTRUCTION
1845 void DestroyConstruction()
1846 {
1847 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1848 GetGame().ObjectDelete(this);
1849 }
1850
1851 // --- EVENTS
1852 override void OnStoreSave(ParamsWriteContext ctx)
1853 {
1854 super.OnStoreSave(ctx);
1855
1856 //sync parts 01
1857 ctx.Write(m_SyncParts01);
1858 ctx.Write(m_SyncParts02);
1859 ctx.Write(m_SyncParts03);
1860
1861 ctx.Write(m_HasBase);
1862 }
1863
1864 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1865 {
1866 if (!super.OnStoreLoad(ctx, version))
1867 return false;
1868
1869 //--- Base building data ---
1870 //Restore synced parts data
1871 if (!ctx.Read(m_SyncParts01))
1872 {
1873 m_SyncParts01 = 0; //set default
1874 return false;
1875 }
1876 if (!ctx.Read(m_SyncParts02))
1877 {
1878 m_SyncParts02 = 0; //set default
1879 return false;
1880 }
1881 if (!ctx.Read(m_SyncParts03))
1882 {
1883 m_SyncParts03 = 0; //set default
1884 return false;
1885 }
1886
1887 //has base
1888 if (!ctx.Read(m_HasBase))
1889 {
1890 m_HasBase = false;
1891 return false;
1892 }
1893 //---
1894
1895 return true;
1896 }
1897
1898 override void AfterStoreLoad()
1899 {
1900 super.AfterStoreLoad();
1901
1904 }
1905
1907 {
1908 //update server data
1910
1911 //set base state
1912 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1913 SetBaseState(construction_part.IsBuilt()) ;
1914
1915 //synchronize after load
1917 }
1918
1919 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1920 {
1922 return;
1923
1924 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1925
1926 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1927 return;
1928
1930 string part_name = zone;
1931 part_name.ToLower();
1932
1934 {
1936
1937 if (construction_part && construction.IsPartConstructed(part_name))
1938 {
1939 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1940 construction.DestroyConnectedParts(part_name);
1941 }
1942
1943 //barbed wire handling (hack-ish)
1944 if (part_name.Contains("barbed"))
1945 {
1946 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1947 if (barbed_wire)
1948 barbed_wire.SetMountedState(false);
1949 }
1950 }
1951 }
1952
1953 override void EEOnAfterLoad()
1954 {
1956 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1957
1958 super.EEOnAfterLoad();
1959 }
1960
1961 override void EEInit()
1962 {
1963 super.EEInit();
1964
1965 // init visuals and physics
1966 InitBaseState();
1967
1968 //debug
1969#ifdef DEVELOPER
1971#endif
1972 }
1973
1974 override void EEItemAttached(EntityAI item, string slot_name)
1975 {
1976 super.EEItemAttached(item, slot_name);
1977
1979 UpdateVisuals();
1981 }
1982
1983 override void EEItemDetached(EntityAI item, string slot_name)
1984 {
1985 super.EEItemDetached(item, slot_name);
1986
1987 UpdateVisuals();
1989 }
1990
1991 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
1992 {
1994 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
1995
1998 }
1999
2000 //ignore out of reach condition
2001 override bool IgnoreOutOfReachCondition()
2002 {
2003 return true;
2004 }
2005
2006 //CONSTRUCTION EVENTS
2007 //Build
2008 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2009 {
2011
2012 //check base state
2013 if (construtionPart.IsBase())
2014 {
2015 SetBaseState(true);
2016
2017 //spawn kit
2019 }
2020
2021 //register constructed parts for synchronization
2023
2024 //register action that was performed on part
2026
2027 //synchronize
2029
2030 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2031
2032 UpdateNavmesh();
2033
2034 //update visuals
2035 UpdateVisuals();
2036
2037 //reset action sync data
2038 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2039 }
2040
2041 void OnPartBuiltClient(string part_name, int action_id)
2042 {
2043 //play sound
2045 }
2046
2047 //Dismantle
2049 {
2050 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2052
2053 //register constructed parts for synchronization
2055
2056 //register action that was performed on part
2058
2059 //synchronize
2061
2062 // server part of sync, client will be synced from SetPartsFromSyncData
2064
2065 UpdateNavmesh();
2066
2067 //update visuals
2068 UpdateVisuals();
2069
2070 //reset action sync data
2071 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2072
2073 //check base state
2074 if (construtionPart.IsBase())
2075 {
2076 //Destroy construction
2077 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2078 }
2079 }
2080
2082 {
2083 //play sound
2085 }
2086
2087 //Destroy
2089 {
2090 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2092
2093 //register constructed parts for synchronization
2095
2096 //register action that was performed on part
2098
2099 //synchronize
2101
2102 // server part of sync, client will be synced from SetPartsFromSyncData
2104
2105 UpdateNavmesh();
2106
2107 //update visuals
2108 UpdateVisuals();
2109
2110 //reset action sync data
2111 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2112
2113 //check base state
2114 if (construtionPart.IsBase())
2115 {
2116 //Destroy construction
2117 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2118 }
2119 }
2120
2121 void OnPartDestroyedClient(string part_name, int action_id)
2122 {
2123 //play sound
2125 }
2126
2127 // --- UPDATE
2128 void InitBaseState()
2129 {
2130 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2131
2132 InitVisuals();
2133 UpdateNavmesh(); //regenerate navmesh
2134 GetConstruction().InitBaseState();
2135 }
2136
2137 void InitVisuals()
2138 {
2139 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2140 //check base
2141 if (!HasBase())
2142 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2143 else
2144 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2145
2146 GetConstruction().UpdateVisuals();
2147 }
2148
2149 void UpdateVisuals()
2150 {
2152
2154 foreach (string slotName : attachmentSlots)
2156
2157 //check base
2158 if (!HasBase())
2159 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2160 else
2161 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2162
2163 GetConstruction().UpdateVisuals();
2164 }
2165
2167 {
2168 string slotNameMounted = slot_name + "_Mounted";
2169 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2170
2171 if (attachment)
2172 {
2173 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2174 if (barbedWire && barbedWire.IsMounted())
2176 else
2178
2179 if (is_locked)
2180 {
2181 SetAnimationPhase(slotNameMounted, 0);
2182 SetAnimationPhase(slot_name, 1);
2183 }
2184 else
2185 {
2186 SetAnimationPhase(slotNameMounted, 1);
2187 SetAnimationPhase(slot_name, 0);
2188 }
2189 }
2190 else
2191 {
2192 SetAnimationPhase(slotNameMounted, 1);
2193 SetAnimationPhase(slot_name, 1);
2194
2196 }
2197 }
2198
2199 // avoid calling this function on frequent occasions, it's a massive performance hit
2200 void UpdatePhysics()
2201 {
2203 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2204
2207
2209 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2210
2211 foreach (string slotName : attachmentSlots)
2213
2214 //check base
2215 if (!HasBase())
2216 {
2218 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2219
2220 AddProxyPhysics(ANIMATION_DEPLOYED);
2221 }
2222 else
2223 {
2225 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2226
2227 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2228 }
2229
2230 GetConstruction().UpdatePhysics();
2231 UpdateNavmesh();
2232 }
2233
2235 {
2236 //checks for invalid appends; hotfix
2237 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2238 return;
2239 //----------------------------------
2240 string slot_name_mounted = slot_name + "_Mounted";
2241 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2242
2243 //remove proxy physics
2244 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2245 RemoveProxyPhysics(slot_name_mounted);
2246 RemoveProxyPhysics(slot_name);
2247
2248 if (attachment)
2249 {
2250 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2251 if (is_locked)
2252 {
2253 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2254 AddProxyPhysics(slot_name_mounted);
2255 }
2256 else
2257 {
2258 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2259 AddProxyPhysics(slot_name);
2260 }
2261 }
2262 }
2263
2264 protected void UpdateNavmesh()
2265 {
2266 SetAffectPathgraph(true, false);
2267 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2268 }
2269
2270 override bool CanUseConstruction()
2271 {
2272 return true;
2273 }
2274
2275 override bool CanUseConstructionBuild()
2276 {
2277 return true;
2278 }
2279
2281 {
2282 if (attachment)
2283 {
2285 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2286
2287 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2288 }
2289
2290 return false;
2291 }
2292
2293 protected bool IsAttachmentSlotLocked(string slot_name)
2294 {
2295 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2296 }
2297
2298 //--- ATTACHMENT SLOTS
2300 {
2301 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2302 if (GetGame().ConfigIsExisting(config_path))
2303 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2304 }
2305
2307 {
2308 return true;
2309 }
2310
2311 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2312 {
2313 return true;
2314 }
2315
2316 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2317 {
2318 return true;
2319 }
2320
2321 // --- INIT
2322 void ConstructionInit()
2323 {
2324 if (!m_Construction)
2325 m_Construction = new Construction(this);
2326
2327 GetConstruction().Init();
2328 }
2329
2331 {
2332 return m_Construction;
2333 }
2334
2335 //--- INVENTORY/ATTACHMENTS CONDITIONS
2336 //attachments
2338 {
2339 return super.CanReceiveAttachment(attachment, slotId);
2340 }
2341
2343 {
2344 int attachment_count = GetInventory().AttachmentCount();
2345 if (attachment_count > 0)
2346 {
2347 if (HasBase() && attachment_count == 1)
2348 return false;
2349
2350 return true;
2351 }
2352
2353 return false;
2354 }
2355
2356 override bool ShowZonesHealth()
2357 {
2358 return true;
2359 }
2360
2361 //this into/outo parent.Cargo
2362 override bool CanPutInCargo(EntityAI parent)
2363 {
2364 return false;
2365 }
2366
2367 override bool CanRemoveFromCargo(EntityAI parent)
2368 {
2369 return false;
2370 }
2371
2372 //hands
2373 override bool CanPutIntoHands(EntityAI parent)
2374 {
2375 return false;
2376 }
2377
2378 //--- ACTION CONDITIONS
2379 //direction
2380 override bool IsFacingPlayer(PlayerBase player, string selection)
2381 {
2382 return true;
2383 }
2384
2385 override bool IsPlayerInside(PlayerBase player, string selection)
2386 {
2387 return true;
2388 }
2389
2392 {
2393 return false;
2394 }
2395
2396 //camera direction check
2397 bool IsFacingCamera(string selection)
2398 {
2399 return true;
2400 }
2401
2402 //roof check
2404 {
2405 return false;
2406 }
2407
2408 //selection->player distance check
2409 bool HasProperDistance(string selection, PlayerBase player)
2410 {
2411 return true;
2412 }
2413
2414 //folding
2416 {
2417 if (HasBase() || GetInventory().AttachmentCount() > 0)
2418 return false;
2419
2420 return true;
2421 }
2422
2424 {
2427
2428 return item;
2429 }
2430
2431 //Damage triggers (barbed wire)
2432 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2433 {
2434 if (GetGame() && GetGame().IsServer())
2435 {
2436 //destroy area damage if some already exists
2438
2439 //create new area damage
2441 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2442
2443 vector min_max[2];
2444 if (MemoryPointExists(slot_name + "_min"))
2445 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2446 if (MemoryPointExists(slot_name + "_max"))
2447 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2448
2449 //get proper trigger extents (min<max)
2450 vector extents[2];
2451 GetConstruction().GetTriggerExtents(min_max, extents);
2452
2453 //get box center
2454 vector center;
2455 center = GetConstruction().GetBoxCenter(min_max);
2456 center = ModelToWorld(center);
2457
2458 //rotate center if needed
2461
2462 areaDamage.SetExtents(extents[0], extents[1]);
2463 areaDamage.SetAreaPosition(center);
2464 areaDamage.SetAreaOrientation(orientation);
2465 areaDamage.SetLoopInterval(1.0);
2466 areaDamage.SetDeferDuration(0.2);
2467 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2468 areaDamage.SetAmmoName("BarbedWireHit");
2469 areaDamage.Spawn();
2470
2472 }
2473 }
2474
2476 {
2477 if (angle_deg != 0)
2478 {
2479 //orientation
2481
2482 //center
2484 if (MemoryPointExists("rotate_axis"))
2485 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2488 center[0] = r_center_x;
2489 center[2] = r_center_z;
2490 }
2491 }
2492
2493 void DestroyAreaDamage(string slot_name)
2494 {
2495 if (GetGame() && GetGame().IsServer())
2496 {
2499 {
2500 if (areaDamage)
2501 areaDamage.Destroy();
2502
2504 }
2505 }
2506 }
2507
2508 override bool IsIgnoredByConstruction()
2509 {
2510 return true;
2511 }
2512
2513 //================================================================
2514 // SOUNDS
2515 //================================================================
2516 protected void SoundBuildStart(string part_name)
2517 {
2518 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2519 }
2520
2521 protected void SoundDismantleStart(string part_name)
2522 {
2523 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2524 }
2525
2526 protected void SoundDestroyStart(string part_name)
2527 {
2528 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2529 }
2530
2531 protected string GetBuildSoundByMaterial(string part_name)
2532 {
2534
2535 switch (material_type)
2536 {
2537 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2538 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2539 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2540 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2541 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2542 }
2543
2544 return "";
2545 }
2546
2547 protected string GetDismantleSoundByMaterial(string part_name)
2548 {
2550
2551 switch (material_type)
2552 {
2553 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2554 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2555 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2556 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2557 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2558 }
2559
2560 return "";
2561 }
2562
2563 //misc
2565 {
2566 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2567 {
2568 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2570 SetHealth(slot_name, "Health", item.GetHealth());
2571 }
2572 }
2573
2574 override int GetDamageSystemVersionChange()
2575 {
2576 return 111;
2577 }
2578
2579 override void SetActions()
2580 {
2581 super.SetActions();
2582
2584 //AddAction(ActionTakeHybridAttachment);
2585 //AddAction(ActionTakeHybridAttachmentToHands);
2588 }
2589
2590 //================================================================
2591 // DEBUG
2592 //================================================================
2593 protected void DebugCustomState()
2594 {
2595 }
2596
2599 {
2600 return null;
2601 }
2602
2603 override void OnDebugSpawn()
2604 {
2605 FullyBuild();
2606 }
2607
2608 void FullyBuild()
2609 {
2611 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2612
2613 Man p;
2614
2615#ifdef SERVER
2617 GetGame().GetWorld().GetPlayerList(players);
2618 if (players.Count())
2619 p = players[0];
2620#else
2621 p = GetGame().GetPlayer();
2622#endif
2623
2624 foreach (ConstructionPart part : parts)
2625 {
2626 bool excluded = false;
2627 string partName = part.GetPartName();
2628 if (excludes)
2629 {
2630 foreach (string exclude : excludes)
2631 {
2632 if (partName.Contains(exclude))
2633 {
2634 excluded = true;
2635 break;
2636 }
2637 }
2638 }
2639
2640 if (!excluded)
2642 }
2643
2644 GetConstruction().UpdateVisuals();
2645 }
2646}
2647
2648void bsbDebugPrint(string s)
2649{
2650#ifdef BSB_DEBUG
2651 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2652#else
2653 //Print("" + s); // comment/uncomment to hide/see debug logs
2654#endif
2655}
2656void bsbDebugSpam(string s)
2657{
2658#ifdef BSB_DEBUG_SPAM
2659 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2660#else
2661 //Print("" + s); // comment/uncomment to hide/see debug logs
2662#endif
2663}

Referenced by ItemBase::SetActionFromSyncData().

◆ GetDamageSystemVersionChange()

override int bsbDebugPrint::GetDamageSystemVersionChange ( )
protected

Definition at line 2275 of file BaseBuildingBase.c.

2277{
2278 const string ANIMATION_DEPLOYED = "Deployed";
2279
2280 float m_ConstructionKitHealth; //stored health value for used construction kit
2281
2283
2284 bool m_HasBase;
2285 //variables for synchronization of base building parts (2x31 is the current limit)
2286 int m_SyncParts01; //synchronization for already built parts (31 parts)
2287 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2288 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2289 int m_InteractedPartId; //construction part id that an action was performed on
2290 int m_PerformedActionId; //action id that was performed on a construction part
2291
2292 //Sounds
2293 //build
2294 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2295 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2296 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2297 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2298 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2299 //dismantle
2300 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2301 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2302 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2303 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2304 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2305
2306 protected EffectSound m_Sound;
2307
2311
2312 // Constructor
2313 void BaseBuildingBase()
2314 {
2316
2317 //synchronized variables
2318 RegisterNetSyncVariableInt("m_SyncParts01");
2319 RegisterNetSyncVariableInt("m_SyncParts02");
2320 RegisterNetSyncVariableInt("m_SyncParts03");
2321 RegisterNetSyncVariableInt("m_InteractedPartId");
2322 RegisterNetSyncVariableInt("m_PerformedActionId");
2323 RegisterNetSyncVariableBool("m_HasBase");
2324
2325 //Construction init
2327
2328 if (ConfigIsExisting("hybridAttachments"))
2329 {
2331 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2332 }
2333 if (ConfigIsExisting("mountables"))
2334 {
2336 ConfigGetTextArray("mountables", m_Mountables);
2337 }
2338
2339 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2340 }
2341
2342 override void EEDelete(EntityAI parent)
2343 {
2344 super.EEDelete(parent);
2345
2346 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2348
2349 }
2350
2351 override string GetInvulnerabilityTypeString()
2352 {
2353 return "disableBaseDamage";
2354 }
2355
2356 override bool CanObstruct()
2357 {
2358 return true;
2359 }
2360
2361 override int GetHideIconMask()
2362 {
2363 return EInventoryIconVisibility.HIDE_VICINITY;
2364 }
2365
2366 // --- SYNCHRONIZATION
2368 {
2369 if (GetGame().IsServer())
2370 SetSynchDirty();
2371 }
2372
2373 override void OnVariablesSynchronized()
2374 {
2375 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2376 super.OnVariablesSynchronized();
2377
2378 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2379 }
2380
2381 protected void OnSynchronizedClient()
2382 {
2383 //update parts
2385
2386 //update action on part
2388
2389 //update visuals (client)
2390 UpdateVisuals();
2391 }
2392
2393 //parts synchronization
2395 {
2396 //part_id must starts from index = 1
2397 int offset;
2398 int mask;
2399
2400 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2401 {
2402 offset = part_id - 1;
2403 mask = 1 << offset;
2404
2406 }
2407 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2408 {
2409 offset = (part_id % 32);
2410 mask = 1 << offset;
2411
2413 }
2414 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2415 {
2416 offset = (part_id % 63);
2417 mask = 1 << offset;
2418
2420 }
2421 }
2422
2424 {
2425 //part_id must starts from index = 1
2426 int offset;
2427 int mask;
2428
2429 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2430 {
2431 offset = part_id - 1;
2432 mask = 1 << offset;
2433
2435 }
2436 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2437 {
2438 offset = (part_id % 32);
2439 mask = 1 << offset;
2440
2442 }
2443 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2444 {
2445 offset = (part_id % 63);
2446 mask = 1 << offset;
2447
2449 }
2450 }
2451
2453 {
2454 //part_id must starts from index = 1
2455 int offset;
2456 int mask;
2457
2458 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2459 {
2460 offset = part_id - 1;
2461 mask = 1 << offset;
2462
2463 if ((m_SyncParts01 & mask) > 0)
2464 return true;
2465 }
2466 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2467 {
2468 offset = (part_id % 32);
2469 mask = 1 << offset;
2470
2471 if ((m_SyncParts02 & mask) > 0)
2472 return true;
2473 }
2474 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2475 {
2476 offset = (part_id % 63);
2477 mask = 1 << offset;
2478
2479 if ((m_SyncParts03 & mask) > 0)
2480 return true;
2481 }
2482
2483 return false;
2484 }
2485
2486 protected void RegisterActionForSync(int part_id, int action_id)
2487 {
2490 }
2491
2492 protected void ResetActionSyncData()
2493 {
2494 //reset data
2495 m_InteractedPartId = -1;
2497 }
2498
2499 protected void SetActionFromSyncData()
2500 {
2501 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2502 {
2505
2506 switch (build_action_id)
2507 {
2511 }
2512 }
2513 }
2514 //------
2515
2517 {
2518 string key = part.m_PartName;
2519 bool is_base = part.IsBase();
2521 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2523 {
2524 if (!part.IsBuilt())
2525 {
2526 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2527 GetConstruction().AddToConstructedParts(key);
2528 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2529
2530 if (is_base)
2531 {
2533 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2534 }
2535 }
2536 }
2537 else
2538 {
2539 if (part.IsBuilt())
2540 {
2541 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2542 GetConstruction().RemoveFromConstructedParts(key);
2543 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2544
2545 if (is_base)
2546 {
2548 AddProxyPhysics(ANIMATION_DEPLOYED);
2549 }
2550 }
2551 }
2552
2553 //check slot lock for material attachments
2554 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2555 }
2556
2557 //set construction parts based on synchronized data
2559 {
2562
2563 for (int i = 0; i < construction_parts.Count(); ++i)
2564 {
2565 string key = construction_parts.GetKey(i);
2568 }
2569
2570 //regenerate navmesh
2571 UpdateNavmesh();
2572 }
2573
2575 {
2578
2579 for (int i = 0; i < construction_parts.Count(); ++i)
2580 {
2581 string key = construction_parts.GetKey(i);
2583
2584 if (value.GetId() == id)
2585 return value;
2586 }
2587
2588 return NULL;
2589 }
2590 //
2591
2592 //Base
2593 bool HasBase()
2594 {
2595 return m_HasBase;
2596 }
2597
2598 void SetBaseState(bool has_base)
2599 {
2601 }
2602
2603 override bool IsDeployable()
2604 {
2605 return true;
2606 }
2607
2608 bool IsOpened()
2609 {
2610 return false;
2611 }
2612
2613 //--- CONSTRUCTION KIT
2615 {
2619
2620 return construction_kit;
2621 }
2622
2624 {
2625 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2628 }
2629
2630 protected vector GetKitSpawnPosition()
2631 {
2632 return GetPosition();
2633 }
2634
2635 protected string GetConstructionKitType()
2636 {
2637 return "";
2638 }
2639
2641 {
2643 GetGame().ObjectDelete(construction_kit);
2644 }
2645
2646 //--- CONSTRUCTION
2647 void DestroyConstruction()
2648 {
2649 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2650 GetGame().ObjectDelete(this);
2651 }
2652
2653 // --- EVENTS
2654 override void OnStoreSave(ParamsWriteContext ctx)
2655 {
2656 super.OnStoreSave(ctx);
2657
2658 //sync parts 01
2659 ctx.Write(m_SyncParts01);
2660 ctx.Write(m_SyncParts02);
2661 ctx.Write(m_SyncParts03);
2662
2663 ctx.Write(m_HasBase);
2664 }
2665
2666 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2667 {
2668 if (!super.OnStoreLoad(ctx, version))
2669 return false;
2670
2671 //--- Base building data ---
2672 //Restore synced parts data
2673 if (!ctx.Read(m_SyncParts01))
2674 {
2675 m_SyncParts01 = 0; //set default
2676 return false;
2677 }
2678 if (!ctx.Read(m_SyncParts02))
2679 {
2680 m_SyncParts02 = 0; //set default
2681 return false;
2682 }
2683 if (!ctx.Read(m_SyncParts03))
2684 {
2685 m_SyncParts03 = 0; //set default
2686 return false;
2687 }
2688
2689 //has base
2690 if (!ctx.Read(m_HasBase))
2691 {
2692 m_HasBase = false;
2693 return false;
2694 }
2695 //---
2696
2697 return true;
2698 }
2699
2700 override void AfterStoreLoad()
2701 {
2702 super.AfterStoreLoad();
2703
2706 }
2707
2709 {
2710 //update server data
2712
2713 //set base state
2714 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2715 SetBaseState(construction_part.IsBuilt()) ;
2716
2717 //synchronize after load
2719 }
2720
2721 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2722 {
2724 return;
2725
2726 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2727
2728 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2729 return;
2730
2732 string part_name = zone;
2733 part_name.ToLower();
2734
2736 {
2738
2739 if (construction_part && construction.IsPartConstructed(part_name))
2740 {
2741 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2742 construction.DestroyConnectedParts(part_name);
2743 }
2744
2745 //barbed wire handling (hack-ish)
2746 if (part_name.Contains("barbed"))
2747 {
2748 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2749 if (barbed_wire)
2750 barbed_wire.SetMountedState(false);
2751 }
2752 }
2753 }
2754
2755 override void EEOnAfterLoad()
2756 {
2758 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2759
2760 super.EEOnAfterLoad();
2761 }
2762
2763 override void EEInit()
2764 {
2765 super.EEInit();
2766
2767 // init visuals and physics
2768 InitBaseState();
2769
2770 //debug
2771#ifdef DEVELOPER
2773#endif
2774 }
2775
2776 override void EEItemAttached(EntityAI item, string slot_name)
2777 {
2778 super.EEItemAttached(item, slot_name);
2779
2781 UpdateVisuals();
2783 }
2784
2785 override void EEItemDetached(EntityAI item, string slot_name)
2786 {
2787 super.EEItemDetached(item, slot_name);
2788
2789 UpdateVisuals();
2791 }
2792
2793 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2794 {
2796 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2797
2800 }
2801
2802 //ignore out of reach condition
2803 override bool IgnoreOutOfReachCondition()
2804 {
2805 return true;
2806 }
2807
2808 //CONSTRUCTION EVENTS
2809 //Build
2810 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2811 {
2813
2814 //check base state
2815 if (construtionPart.IsBase())
2816 {
2817 SetBaseState(true);
2818
2819 //spawn kit
2821 }
2822
2823 //register constructed parts for synchronization
2825
2826 //register action that was performed on part
2828
2829 //synchronize
2831
2832 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2833
2834 UpdateNavmesh();
2835
2836 //update visuals
2837 UpdateVisuals();
2838
2839 //reset action sync data
2840 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2841 }
2842
2843 void OnPartBuiltClient(string part_name, int action_id)
2844 {
2845 //play sound
2847 }
2848
2849 //Dismantle
2851 {
2852 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2854
2855 //register constructed parts for synchronization
2857
2858 //register action that was performed on part
2860
2861 //synchronize
2863
2864 // server part of sync, client will be synced from SetPartsFromSyncData
2866
2867 UpdateNavmesh();
2868
2869 //update visuals
2870 UpdateVisuals();
2871
2872 //reset action sync data
2873 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2874
2875 //check base state
2876 if (construtionPart.IsBase())
2877 {
2878 //Destroy construction
2879 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2880 }
2881 }
2882
2884 {
2885 //play sound
2887 }
2888
2889 //Destroy
2891 {
2892 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2894
2895 //register constructed parts for synchronization
2897
2898 //register action that was performed on part
2900
2901 //synchronize
2903
2904 // server part of sync, client will be synced from SetPartsFromSyncData
2906
2907 UpdateNavmesh();
2908
2909 //update visuals
2910 UpdateVisuals();
2911
2912 //reset action sync data
2913 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2914
2915 //check base state
2916 if (construtionPart.IsBase())
2917 {
2918 //Destroy construction
2919 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2920 }
2921 }
2922
2923 void OnPartDestroyedClient(string part_name, int action_id)
2924 {
2925 //play sound
2927 }
2928
2929 // --- UPDATE
2930 void InitBaseState()
2931 {
2932 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2933
2934 InitVisuals();
2935 UpdateNavmesh(); //regenerate navmesh
2936 GetConstruction().InitBaseState();
2937 }
2938
2939 void InitVisuals()
2940 {
2941 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2942 //check base
2943 if (!HasBase())
2944 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2945 else
2946 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2947
2948 GetConstruction().UpdateVisuals();
2949 }
2950
2951 void UpdateVisuals()
2952 {
2954
2956 foreach (string slotName : attachmentSlots)
2958
2959 //check base
2960 if (!HasBase())
2961 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2962 else
2963 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2964
2965 GetConstruction().UpdateVisuals();
2966 }
2967
2969 {
2970 string slotNameMounted = slot_name + "_Mounted";
2971 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2972
2973 if (attachment)
2974 {
2975 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2976 if (barbedWire && barbedWire.IsMounted())
2978 else
2980
2981 if (is_locked)
2982 {
2983 SetAnimationPhase(slotNameMounted, 0);
2984 SetAnimationPhase(slot_name, 1);
2985 }
2986 else
2987 {
2988 SetAnimationPhase(slotNameMounted, 1);
2989 SetAnimationPhase(slot_name, 0);
2990 }
2991 }
2992 else
2993 {
2994 SetAnimationPhase(slotNameMounted, 1);
2995 SetAnimationPhase(slot_name, 1);
2996
2998 }
2999 }
3000
3001 // avoid calling this function on frequent occasions, it's a massive performance hit
3002 void UpdatePhysics()
3003 {
3005 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
3006
3009
3011 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
3012
3013 foreach (string slotName : attachmentSlots)
3015
3016 //check base
3017 if (!HasBase())
3018 {
3020 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
3021
3022 AddProxyPhysics(ANIMATION_DEPLOYED);
3023 }
3024 else
3025 {
3027 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
3028
3029 RemoveProxyPhysics(ANIMATION_DEPLOYED);
3030 }
3031
3032 GetConstruction().UpdatePhysics();
3033 UpdateNavmesh();
3034 }
3035
3037 {
3038 //checks for invalid appends; hotfix
3039 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
3040 return;
3041 //----------------------------------
3042 string slot_name_mounted = slot_name + "_Mounted";
3043 EntityAI attachment = FindAttachmentBySlotName(slot_name);
3044
3045 //remove proxy physics
3046 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
3047 RemoveProxyPhysics(slot_name_mounted);
3048 RemoveProxyPhysics(slot_name);
3049
3050 if (attachment)
3051 {
3052 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
3053 if (is_locked)
3054 {
3055 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
3056 AddProxyPhysics(slot_name_mounted);
3057 }
3058 else
3059 {
3060 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
3061 AddProxyPhysics(slot_name);
3062 }
3063 }
3064 }
3065
3066 protected void UpdateNavmesh()
3067 {
3068 SetAffectPathgraph(true, false);
3069 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
3070 }
3071
3072 override bool CanUseConstruction()
3073 {
3074 return true;
3075 }
3076
3077 override bool CanUseConstructionBuild()
3078 {
3079 return true;
3080 }
3081
3083 {
3084 if (attachment)
3085 {
3087 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
3088
3089 return GetInventory().GetSlotLock(inventory_location.GetSlot());
3090 }
3091
3092 return false;
3093 }
3094
3095 protected bool IsAttachmentSlotLocked(string slot_name)
3096 {
3097 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
3098 }
3099
3100 //--- ATTACHMENT SLOTS
3102 {
3103 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
3104 if (GetGame().ConfigIsExisting(config_path))
3105 GetGame().ConfigGetTextArray(config_path, attachment_slots);
3106 }
3107
3109 {
3110 return true;
3111 }
3112
3113 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
3114 {
3115 return true;
3116 }
3117
3118 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
3119 {
3120 return true;
3121 }
3122
3123 // --- INIT
3124 void ConstructionInit()
3125 {
3126 if (!m_Construction)
3127 m_Construction = new Construction(this);
3128
3129 GetConstruction().Init();
3130 }
3131
3133 {
3134 return m_Construction;
3135 }
3136
3137 //--- INVENTORY/ATTACHMENTS CONDITIONS
3138 //attachments
3140 {
3141 return super.CanReceiveAttachment(attachment, slotId);
3142 }
3143
3145 {
3146 int attachment_count = GetInventory().AttachmentCount();
3147 if (attachment_count > 0)
3148 {
3149 if (HasBase() && attachment_count == 1)
3150 return false;
3151
3152 return true;
3153 }
3154
3155 return false;
3156 }
3157
3158 override bool ShowZonesHealth()
3159 {
3160 return true;
3161 }
3162
3163 //this into/outo parent.Cargo
3164 override bool CanPutInCargo(EntityAI parent)
3165 {
3166 return false;
3167 }
3168
3169 override bool CanRemoveFromCargo(EntityAI parent)
3170 {
3171 return false;
3172 }
3173
3174 //hands
3175 override bool CanPutIntoHands(EntityAI parent)
3176 {
3177 return false;
3178 }
3179
3180 //--- ACTION CONDITIONS
3181 //direction
3182 override bool IsFacingPlayer(PlayerBase player, string selection)
3183 {
3184 return true;
3185 }
3186
3187 override bool IsPlayerInside(PlayerBase player, string selection)
3188 {
3189 return true;
3190 }
3191
3194 {
3195 return false;
3196 }
3197
3198 //camera direction check
3199 bool IsFacingCamera(string selection)
3200 {
3201 return true;
3202 }
3203
3204 //roof check
3206 {
3207 return false;
3208 }
3209
3210 //selection->player distance check
3211 bool HasProperDistance(string selection, PlayerBase player)
3212 {
3213 return true;
3214 }
3215
3216 //folding
3218 {
3219 if (HasBase() || GetInventory().AttachmentCount() > 0)
3220 return false;
3221
3222 return true;
3223 }
3224
3226 {
3229
3230 return item;
3231 }
3232
3233 //Damage triggers (barbed wire)
3234 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3235 {
3236 if (GetGame() && GetGame().IsServer())
3237 {
3238 //destroy area damage if some already exists
3240
3241 //create new area damage
3243 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3244
3245 vector min_max[2];
3246 if (MemoryPointExists(slot_name + "_min"))
3247 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3248 if (MemoryPointExists(slot_name + "_max"))
3249 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3250
3251 //get proper trigger extents (min<max)
3252 vector extents[2];
3253 GetConstruction().GetTriggerExtents(min_max, extents);
3254
3255 //get box center
3256 vector center;
3257 center = GetConstruction().GetBoxCenter(min_max);
3258 center = ModelToWorld(center);
3259
3260 //rotate center if needed
3263
3264 areaDamage.SetExtents(extents[0], extents[1]);
3265 areaDamage.SetAreaPosition(center);
3266 areaDamage.SetAreaOrientation(orientation);
3267 areaDamage.SetLoopInterval(1.0);
3268 areaDamage.SetDeferDuration(0.2);
3269 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3270 areaDamage.SetAmmoName("BarbedWireHit");
3271 areaDamage.Spawn();
3272
3274 }
3275 }
3276
3278 {
3279 if (angle_deg != 0)
3280 {
3281 //orientation
3283
3284 //center
3286 if (MemoryPointExists("rotate_axis"))
3287 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3290 center[0] = r_center_x;
3291 center[2] = r_center_z;
3292 }
3293 }
3294
3295 void DestroyAreaDamage(string slot_name)
3296 {
3297 if (GetGame() && GetGame().IsServer())
3298 {
3301 {
3302 if (areaDamage)
3303 areaDamage.Destroy();
3304
3306 }
3307 }
3308 }
3309
3310 override bool IsIgnoredByConstruction()
3311 {
3312 return true;
3313 }
3314
3315 //================================================================
3316 // SOUNDS
3317 //================================================================
3318 protected void SoundBuildStart(string part_name)
3319 {
3320 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3321 }
3322
3323 protected void SoundDismantleStart(string part_name)
3324 {
3325 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3326 }
3327
3328 protected void SoundDestroyStart(string part_name)
3329 {
3330 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3331 }
3332
3333 protected string GetBuildSoundByMaterial(string part_name)
3334 {
3336
3337 switch (material_type)
3338 {
3339 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3340 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3341 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3342 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3343 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3344 }
3345
3346 return "";
3347 }
3348
3349 protected string GetDismantleSoundByMaterial(string part_name)
3350 {
3352
3353 switch (material_type)
3354 {
3355 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3356 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3357 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3358 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3359 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3360 }
3361
3362 return "";
3363 }
3364
3365 //misc
3367 {
3368 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3369 {
3370 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3372 SetHealth(slot_name, "Health", item.GetHealth());
3373 }
3374 }
3375
3376 override int GetDamageSystemVersionChange()
3377 {
3378 return 111;
3379 }
3380
3381 override void SetActions()
3382 {
3383 super.SetActions();
3384
3386 //AddAction(ActionTakeHybridAttachment);
3387 //AddAction(ActionTakeHybridAttachmentToHands);
3390 }
3391
3392 //================================================================
3393 // DEBUG
3394 //================================================================
3395 protected void DebugCustomState()
3396 {
3397 }
3398
3401 {
3402 return null;
3403 }
3404
3405 override void OnDebugSpawn()
3406 {
3407 FullyBuild();
3408 }
3409
3410 void FullyBuild()
3411 {
3413 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3414
3415 Man p;
3416
3417#ifdef SERVER
3419 GetGame().GetWorld().GetPlayerList(players);
3420 if (players.Count())
3421 p = players[0];
3422#else
3423 p = GetGame().GetPlayer();
3424#endif
3425
3426 foreach (ConstructionPart part : parts)
3427 {
3428 bool excluded = false;
3429 string partName = part.GetPartName();
3430 if (excludes)
3431 {
3432 foreach (string exclude : excludes)
3433 {
3434 if (partName.Contains(exclude))
3435 {
3436 excluded = true;
3437 break;
3438 }
3439 }
3440 }
3441
3442 if (!excluded)
3444 }
3445
3446 GetConstruction().UpdateVisuals();
3447 }
3448}
3449
3450void bsbDebugPrint(string s)
3451{
3452#ifdef BSB_DEBUG
3453 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3454#else
3455 //Print("" + s); // comment/uncomment to hide/see debug logs
3456#endif
3457}
3458void bsbDebugSpam(string s)
3459{
3460#ifdef BSB_DEBUG_SPAM
3461 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3462#else
3463 //Print("" + s); // comment/uncomment to hide/see debug logs
3464#endif
3465}

◆ GetDismantleSoundByMaterial()

string bsbDebugPrint::GetDismantleSoundByMaterial ( string part_name)
protected

Definition at line 2248 of file BaseBuildingBase.c.

2250{
2251 const string ANIMATION_DEPLOYED = "Deployed";
2252
2253 float m_ConstructionKitHealth; //stored health value for used construction kit
2254
2256
2257 bool m_HasBase;
2258 //variables for synchronization of base building parts (2x31 is the current limit)
2259 int m_SyncParts01; //synchronization for already built parts (31 parts)
2260 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2261 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2262 int m_InteractedPartId; //construction part id that an action was performed on
2263 int m_PerformedActionId; //action id that was performed on a construction part
2264
2265 //Sounds
2266 //build
2267 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2268 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2269 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2270 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2271 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2272 //dismantle
2273 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2274 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2275 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2276 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2277 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2278
2279 protected EffectSound m_Sound;
2280
2284
2285 // Constructor
2286 void BaseBuildingBase()
2287 {
2289
2290 //synchronized variables
2291 RegisterNetSyncVariableInt("m_SyncParts01");
2292 RegisterNetSyncVariableInt("m_SyncParts02");
2293 RegisterNetSyncVariableInt("m_SyncParts03");
2294 RegisterNetSyncVariableInt("m_InteractedPartId");
2295 RegisterNetSyncVariableInt("m_PerformedActionId");
2296 RegisterNetSyncVariableBool("m_HasBase");
2297
2298 //Construction init
2300
2301 if (ConfigIsExisting("hybridAttachments"))
2302 {
2304 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2305 }
2306 if (ConfigIsExisting("mountables"))
2307 {
2309 ConfigGetTextArray("mountables", m_Mountables);
2310 }
2311
2312 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2313 }
2314
2315 override void EEDelete(EntityAI parent)
2316 {
2317 super.EEDelete(parent);
2318
2319 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2321
2322 }
2323
2324 override string GetInvulnerabilityTypeString()
2325 {
2326 return "disableBaseDamage";
2327 }
2328
2329 override bool CanObstruct()
2330 {
2331 return true;
2332 }
2333
2334 override int GetHideIconMask()
2335 {
2336 return EInventoryIconVisibility.HIDE_VICINITY;
2337 }
2338
2339 // --- SYNCHRONIZATION
2341 {
2342 if (GetGame().IsServer())
2343 SetSynchDirty();
2344 }
2345
2346 override void OnVariablesSynchronized()
2347 {
2348 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2349 super.OnVariablesSynchronized();
2350
2351 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2352 }
2353
2354 protected void OnSynchronizedClient()
2355 {
2356 //update parts
2358
2359 //update action on part
2361
2362 //update visuals (client)
2363 UpdateVisuals();
2364 }
2365
2366 //parts synchronization
2368 {
2369 //part_id must starts from index = 1
2370 int offset;
2371 int mask;
2372
2373 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2374 {
2375 offset = part_id - 1;
2376 mask = 1 << offset;
2377
2379 }
2380 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2381 {
2382 offset = (part_id % 32);
2383 mask = 1 << offset;
2384
2386 }
2387 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2388 {
2389 offset = (part_id % 63);
2390 mask = 1 << offset;
2391
2393 }
2394 }
2395
2397 {
2398 //part_id must starts from index = 1
2399 int offset;
2400 int mask;
2401
2402 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2403 {
2404 offset = part_id - 1;
2405 mask = 1 << offset;
2406
2408 }
2409 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2410 {
2411 offset = (part_id % 32);
2412 mask = 1 << offset;
2413
2415 }
2416 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2417 {
2418 offset = (part_id % 63);
2419 mask = 1 << offset;
2420
2422 }
2423 }
2424
2426 {
2427 //part_id must starts from index = 1
2428 int offset;
2429 int mask;
2430
2431 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2432 {
2433 offset = part_id - 1;
2434 mask = 1 << offset;
2435
2436 if ((m_SyncParts01 & mask) > 0)
2437 return true;
2438 }
2439 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2440 {
2441 offset = (part_id % 32);
2442 mask = 1 << offset;
2443
2444 if ((m_SyncParts02 & mask) > 0)
2445 return true;
2446 }
2447 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2448 {
2449 offset = (part_id % 63);
2450 mask = 1 << offset;
2451
2452 if ((m_SyncParts03 & mask) > 0)
2453 return true;
2454 }
2455
2456 return false;
2457 }
2458
2459 protected void RegisterActionForSync(int part_id, int action_id)
2460 {
2463 }
2464
2465 protected void ResetActionSyncData()
2466 {
2467 //reset data
2468 m_InteractedPartId = -1;
2470 }
2471
2472 protected void SetActionFromSyncData()
2473 {
2474 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2475 {
2478
2479 switch (build_action_id)
2480 {
2484 }
2485 }
2486 }
2487 //------
2488
2490 {
2491 string key = part.m_PartName;
2492 bool is_base = part.IsBase();
2494 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2496 {
2497 if (!part.IsBuilt())
2498 {
2499 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2500 GetConstruction().AddToConstructedParts(key);
2501 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2502
2503 if (is_base)
2504 {
2506 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2507 }
2508 }
2509 }
2510 else
2511 {
2512 if (part.IsBuilt())
2513 {
2514 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2515 GetConstruction().RemoveFromConstructedParts(key);
2516 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2517
2518 if (is_base)
2519 {
2521 AddProxyPhysics(ANIMATION_DEPLOYED);
2522 }
2523 }
2524 }
2525
2526 //check slot lock for material attachments
2527 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2528 }
2529
2530 //set construction parts based on synchronized data
2532 {
2535
2536 for (int i = 0; i < construction_parts.Count(); ++i)
2537 {
2538 string key = construction_parts.GetKey(i);
2541 }
2542
2543 //regenerate navmesh
2544 UpdateNavmesh();
2545 }
2546
2548 {
2551
2552 for (int i = 0; i < construction_parts.Count(); ++i)
2553 {
2554 string key = construction_parts.GetKey(i);
2556
2557 if (value.GetId() == id)
2558 return value;
2559 }
2560
2561 return NULL;
2562 }
2563 //
2564
2565 //Base
2566 bool HasBase()
2567 {
2568 return m_HasBase;
2569 }
2570
2571 void SetBaseState(bool has_base)
2572 {
2574 }
2575
2576 override bool IsDeployable()
2577 {
2578 return true;
2579 }
2580
2581 bool IsOpened()
2582 {
2583 return false;
2584 }
2585
2586 //--- CONSTRUCTION KIT
2588 {
2592
2593 return construction_kit;
2594 }
2595
2597 {
2598 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2601 }
2602
2603 protected vector GetKitSpawnPosition()
2604 {
2605 return GetPosition();
2606 }
2607
2608 protected string GetConstructionKitType()
2609 {
2610 return "";
2611 }
2612
2614 {
2616 GetGame().ObjectDelete(construction_kit);
2617 }
2618
2619 //--- CONSTRUCTION
2620 void DestroyConstruction()
2621 {
2622 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2623 GetGame().ObjectDelete(this);
2624 }
2625
2626 // --- EVENTS
2627 override void OnStoreSave(ParamsWriteContext ctx)
2628 {
2629 super.OnStoreSave(ctx);
2630
2631 //sync parts 01
2632 ctx.Write(m_SyncParts01);
2633 ctx.Write(m_SyncParts02);
2634 ctx.Write(m_SyncParts03);
2635
2636 ctx.Write(m_HasBase);
2637 }
2638
2639 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2640 {
2641 if (!super.OnStoreLoad(ctx, version))
2642 return false;
2643
2644 //--- Base building data ---
2645 //Restore synced parts data
2646 if (!ctx.Read(m_SyncParts01))
2647 {
2648 m_SyncParts01 = 0; //set default
2649 return false;
2650 }
2651 if (!ctx.Read(m_SyncParts02))
2652 {
2653 m_SyncParts02 = 0; //set default
2654 return false;
2655 }
2656 if (!ctx.Read(m_SyncParts03))
2657 {
2658 m_SyncParts03 = 0; //set default
2659 return false;
2660 }
2661
2662 //has base
2663 if (!ctx.Read(m_HasBase))
2664 {
2665 m_HasBase = false;
2666 return false;
2667 }
2668 //---
2669
2670 return true;
2671 }
2672
2673 override void AfterStoreLoad()
2674 {
2675 super.AfterStoreLoad();
2676
2679 }
2680
2682 {
2683 //update server data
2685
2686 //set base state
2687 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2688 SetBaseState(construction_part.IsBuilt()) ;
2689
2690 //synchronize after load
2692 }
2693
2694 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2695 {
2697 return;
2698
2699 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2700
2701 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2702 return;
2703
2705 string part_name = zone;
2706 part_name.ToLower();
2707
2709 {
2711
2712 if (construction_part && construction.IsPartConstructed(part_name))
2713 {
2714 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2715 construction.DestroyConnectedParts(part_name);
2716 }
2717
2718 //barbed wire handling (hack-ish)
2719 if (part_name.Contains("barbed"))
2720 {
2721 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2722 if (barbed_wire)
2723 barbed_wire.SetMountedState(false);
2724 }
2725 }
2726 }
2727
2728 override void EEOnAfterLoad()
2729 {
2731 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2732
2733 super.EEOnAfterLoad();
2734 }
2735
2736 override void EEInit()
2737 {
2738 super.EEInit();
2739
2740 // init visuals and physics
2741 InitBaseState();
2742
2743 //debug
2744#ifdef DEVELOPER
2746#endif
2747 }
2748
2749 override void EEItemAttached(EntityAI item, string slot_name)
2750 {
2751 super.EEItemAttached(item, slot_name);
2752
2754 UpdateVisuals();
2756 }
2757
2758 override void EEItemDetached(EntityAI item, string slot_name)
2759 {
2760 super.EEItemDetached(item, slot_name);
2761
2762 UpdateVisuals();
2764 }
2765
2766 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2767 {
2769 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2770
2773 }
2774
2775 //ignore out of reach condition
2776 override bool IgnoreOutOfReachCondition()
2777 {
2778 return true;
2779 }
2780
2781 //CONSTRUCTION EVENTS
2782 //Build
2783 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2784 {
2786
2787 //check base state
2788 if (construtionPart.IsBase())
2789 {
2790 SetBaseState(true);
2791
2792 //spawn kit
2794 }
2795
2796 //register constructed parts for synchronization
2798
2799 //register action that was performed on part
2801
2802 //synchronize
2804
2805 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2806
2807 UpdateNavmesh();
2808
2809 //update visuals
2810 UpdateVisuals();
2811
2812 //reset action sync data
2813 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2814 }
2815
2816 void OnPartBuiltClient(string part_name, int action_id)
2817 {
2818 //play sound
2820 }
2821
2822 //Dismantle
2824 {
2825 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2827
2828 //register constructed parts for synchronization
2830
2831 //register action that was performed on part
2833
2834 //synchronize
2836
2837 // server part of sync, client will be synced from SetPartsFromSyncData
2839
2840 UpdateNavmesh();
2841
2842 //update visuals
2843 UpdateVisuals();
2844
2845 //reset action sync data
2846 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2847
2848 //check base state
2849 if (construtionPart.IsBase())
2850 {
2851 //Destroy construction
2852 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2853 }
2854 }
2855
2857 {
2858 //play sound
2860 }
2861
2862 //Destroy
2864 {
2865 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2867
2868 //register constructed parts for synchronization
2870
2871 //register action that was performed on part
2873
2874 //synchronize
2876
2877 // server part of sync, client will be synced from SetPartsFromSyncData
2879
2880 UpdateNavmesh();
2881
2882 //update visuals
2883 UpdateVisuals();
2884
2885 //reset action sync data
2886 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2887
2888 //check base state
2889 if (construtionPart.IsBase())
2890 {
2891 //Destroy construction
2892 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2893 }
2894 }
2895
2896 void OnPartDestroyedClient(string part_name, int action_id)
2897 {
2898 //play sound
2900 }
2901
2902 // --- UPDATE
2903 void InitBaseState()
2904 {
2905 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2906
2907 InitVisuals();
2908 UpdateNavmesh(); //regenerate navmesh
2909 GetConstruction().InitBaseState();
2910 }
2911
2912 void InitVisuals()
2913 {
2914 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2915 //check base
2916 if (!HasBase())
2917 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2918 else
2919 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2920
2921 GetConstruction().UpdateVisuals();
2922 }
2923
2924 void UpdateVisuals()
2925 {
2927
2929 foreach (string slotName : attachmentSlots)
2931
2932 //check base
2933 if (!HasBase())
2934 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2935 else
2936 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2937
2938 GetConstruction().UpdateVisuals();
2939 }
2940
2942 {
2943 string slotNameMounted = slot_name + "_Mounted";
2944 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2945
2946 if (attachment)
2947 {
2948 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2949 if (barbedWire && barbedWire.IsMounted())
2951 else
2953
2954 if (is_locked)
2955 {
2956 SetAnimationPhase(slotNameMounted, 0);
2957 SetAnimationPhase(slot_name, 1);
2958 }
2959 else
2960 {
2961 SetAnimationPhase(slotNameMounted, 1);
2962 SetAnimationPhase(slot_name, 0);
2963 }
2964 }
2965 else
2966 {
2967 SetAnimationPhase(slotNameMounted, 1);
2968 SetAnimationPhase(slot_name, 1);
2969
2971 }
2972 }
2973
2974 // avoid calling this function on frequent occasions, it's a massive performance hit
2975 void UpdatePhysics()
2976 {
2978 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2979
2982
2984 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2985
2986 foreach (string slotName : attachmentSlots)
2988
2989 //check base
2990 if (!HasBase())
2991 {
2993 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2994
2995 AddProxyPhysics(ANIMATION_DEPLOYED);
2996 }
2997 else
2998 {
3000 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
3001
3002 RemoveProxyPhysics(ANIMATION_DEPLOYED);
3003 }
3004
3005 GetConstruction().UpdatePhysics();
3006 UpdateNavmesh();
3007 }
3008
3010 {
3011 //checks for invalid appends; hotfix
3012 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
3013 return;
3014 //----------------------------------
3015 string slot_name_mounted = slot_name + "_Mounted";
3016 EntityAI attachment = FindAttachmentBySlotName(slot_name);
3017
3018 //remove proxy physics
3019 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
3020 RemoveProxyPhysics(slot_name_mounted);
3021 RemoveProxyPhysics(slot_name);
3022
3023 if (attachment)
3024 {
3025 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
3026 if (is_locked)
3027 {
3028 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
3029 AddProxyPhysics(slot_name_mounted);
3030 }
3031 else
3032 {
3033 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
3034 AddProxyPhysics(slot_name);
3035 }
3036 }
3037 }
3038
3039 protected void UpdateNavmesh()
3040 {
3041 SetAffectPathgraph(true, false);
3042 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
3043 }
3044
3045 override bool CanUseConstruction()
3046 {
3047 return true;
3048 }
3049
3050 override bool CanUseConstructionBuild()
3051 {
3052 return true;
3053 }
3054
3056 {
3057 if (attachment)
3058 {
3060 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
3061
3062 return GetInventory().GetSlotLock(inventory_location.GetSlot());
3063 }
3064
3065 return false;
3066 }
3067
3068 protected bool IsAttachmentSlotLocked(string slot_name)
3069 {
3070 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
3071 }
3072
3073 //--- ATTACHMENT SLOTS
3075 {
3076 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
3077 if (GetGame().ConfigIsExisting(config_path))
3078 GetGame().ConfigGetTextArray(config_path, attachment_slots);
3079 }
3080
3082 {
3083 return true;
3084 }
3085
3086 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
3087 {
3088 return true;
3089 }
3090
3091 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
3092 {
3093 return true;
3094 }
3095
3096 // --- INIT
3097 void ConstructionInit()
3098 {
3099 if (!m_Construction)
3100 m_Construction = new Construction(this);
3101
3102 GetConstruction().Init();
3103 }
3104
3106 {
3107 return m_Construction;
3108 }
3109
3110 //--- INVENTORY/ATTACHMENTS CONDITIONS
3111 //attachments
3113 {
3114 return super.CanReceiveAttachment(attachment, slotId);
3115 }
3116
3118 {
3119 int attachment_count = GetInventory().AttachmentCount();
3120 if (attachment_count > 0)
3121 {
3122 if (HasBase() && attachment_count == 1)
3123 return false;
3124
3125 return true;
3126 }
3127
3128 return false;
3129 }
3130
3131 override bool ShowZonesHealth()
3132 {
3133 return true;
3134 }
3135
3136 //this into/outo parent.Cargo
3137 override bool CanPutInCargo(EntityAI parent)
3138 {
3139 return false;
3140 }
3141
3142 override bool CanRemoveFromCargo(EntityAI parent)
3143 {
3144 return false;
3145 }
3146
3147 //hands
3148 override bool CanPutIntoHands(EntityAI parent)
3149 {
3150 return false;
3151 }
3152
3153 //--- ACTION CONDITIONS
3154 //direction
3155 override bool IsFacingPlayer(PlayerBase player, string selection)
3156 {
3157 return true;
3158 }
3159
3160 override bool IsPlayerInside(PlayerBase player, string selection)
3161 {
3162 return true;
3163 }
3164
3167 {
3168 return false;
3169 }
3170
3171 //camera direction check
3172 bool IsFacingCamera(string selection)
3173 {
3174 return true;
3175 }
3176
3177 //roof check
3179 {
3180 return false;
3181 }
3182
3183 //selection->player distance check
3184 bool HasProperDistance(string selection, PlayerBase player)
3185 {
3186 return true;
3187 }
3188
3189 //folding
3191 {
3192 if (HasBase() || GetInventory().AttachmentCount() > 0)
3193 return false;
3194
3195 return true;
3196 }
3197
3199 {
3202
3203 return item;
3204 }
3205
3206 //Damage triggers (barbed wire)
3207 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3208 {
3209 if (GetGame() && GetGame().IsServer())
3210 {
3211 //destroy area damage if some already exists
3213
3214 //create new area damage
3216 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3217
3218 vector min_max[2];
3219 if (MemoryPointExists(slot_name + "_min"))
3220 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3221 if (MemoryPointExists(slot_name + "_max"))
3222 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3223
3224 //get proper trigger extents (min<max)
3225 vector extents[2];
3226 GetConstruction().GetTriggerExtents(min_max, extents);
3227
3228 //get box center
3229 vector center;
3230 center = GetConstruction().GetBoxCenter(min_max);
3231 center = ModelToWorld(center);
3232
3233 //rotate center if needed
3236
3237 areaDamage.SetExtents(extents[0], extents[1]);
3238 areaDamage.SetAreaPosition(center);
3239 areaDamage.SetAreaOrientation(orientation);
3240 areaDamage.SetLoopInterval(1.0);
3241 areaDamage.SetDeferDuration(0.2);
3242 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3243 areaDamage.SetAmmoName("BarbedWireHit");
3244 areaDamage.Spawn();
3245
3247 }
3248 }
3249
3251 {
3252 if (angle_deg != 0)
3253 {
3254 //orientation
3256
3257 //center
3259 if (MemoryPointExists("rotate_axis"))
3260 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3263 center[0] = r_center_x;
3264 center[2] = r_center_z;
3265 }
3266 }
3267
3268 void DestroyAreaDamage(string slot_name)
3269 {
3270 if (GetGame() && GetGame().IsServer())
3271 {
3274 {
3275 if (areaDamage)
3276 areaDamage.Destroy();
3277
3279 }
3280 }
3281 }
3282
3283 override bool IsIgnoredByConstruction()
3284 {
3285 return true;
3286 }
3287
3288 //================================================================
3289 // SOUNDS
3290 //================================================================
3291 protected void SoundBuildStart(string part_name)
3292 {
3293 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3294 }
3295
3296 protected void SoundDismantleStart(string part_name)
3297 {
3298 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3299 }
3300
3301 protected void SoundDestroyStart(string part_name)
3302 {
3303 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3304 }
3305
3306 protected string GetBuildSoundByMaterial(string part_name)
3307 {
3309
3310 switch (material_type)
3311 {
3312 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3313 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3314 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3315 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3316 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3317 }
3318
3319 return "";
3320 }
3321
3322 protected string GetDismantleSoundByMaterial(string part_name)
3323 {
3325
3326 switch (material_type)
3327 {
3328 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3329 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3330 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3331 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3332 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3333 }
3334
3335 return "";
3336 }
3337
3338 //misc
3340 {
3341 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3342 {
3343 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3345 SetHealth(slot_name, "Health", item.GetHealth());
3346 }
3347 }
3348
3349 override int GetDamageSystemVersionChange()
3350 {
3351 return 111;
3352 }
3353
3354 override void SetActions()
3355 {
3356 super.SetActions();
3357
3359 //AddAction(ActionTakeHybridAttachment);
3360 //AddAction(ActionTakeHybridAttachmentToHands);
3363 }
3364
3365 //================================================================
3366 // DEBUG
3367 //================================================================
3368 protected void DebugCustomState()
3369 {
3370 }
3371
3374 {
3375 return null;
3376 }
3377
3378 override void OnDebugSpawn()
3379 {
3380 FullyBuild();
3381 }
3382
3383 void FullyBuild()
3384 {
3386 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3387
3388 Man p;
3389
3390#ifdef SERVER
3392 GetGame().GetWorld().GetPlayerList(players);
3393 if (players.Count())
3394 p = players[0];
3395#else
3396 p = GetGame().GetPlayer();
3397#endif
3398
3399 foreach (ConstructionPart part : parts)
3400 {
3401 bool excluded = false;
3402 string partName = part.GetPartName();
3403 if (excludes)
3404 {
3405 foreach (string exclude : excludes)
3406 {
3407 if (partName.Contains(exclude))
3408 {
3409 excluded = true;
3410 break;
3411 }
3412 }
3413 }
3414
3415 if (!excluded)
3417 }
3418
3419 GetConstruction().UpdateVisuals();
3420 }
3421}
3422
3423void bsbDebugPrint(string s)
3424{
3425#ifdef BSB_DEBUG
3426 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3427#else
3428 //Print("" + s); // comment/uncomment to hide/see debug logs
3429#endif
3430}
3431void bsbDebugSpam(string s)
3432{
3433#ifdef BSB_DEBUG_SPAM
3434 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3435#else
3436 //Print("" + s); // comment/uncomment to hide/see debug logs
3437#endif
3438}

Referenced by ItemBase::SoundDestroyStart(), and ItemBase::SoundDismantleStart().

◆ GetHideIconMask()

override int bsbDebugPrint::GetHideIconMask ( )
protected

Definition at line 1260 of file BaseBuildingBase.c.

1262{
1263 const string ANIMATION_DEPLOYED = "Deployed";
1264
1265 float m_ConstructionKitHealth; //stored health value for used construction kit
1266
1268
1269 bool m_HasBase;
1270 //variables for synchronization of base building parts (2x31 is the current limit)
1271 int m_SyncParts01; //synchronization for already built parts (31 parts)
1272 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1273 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1274 int m_InteractedPartId; //construction part id that an action was performed on
1275 int m_PerformedActionId; //action id that was performed on a construction part
1276
1277 //Sounds
1278 //build
1279 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1280 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1281 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1282 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1283 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1284 //dismantle
1285 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1286 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1287 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1288 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1289 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1290
1291 protected EffectSound m_Sound;
1292
1296
1297 // Constructor
1298 void BaseBuildingBase()
1299 {
1301
1302 //synchronized variables
1303 RegisterNetSyncVariableInt("m_SyncParts01");
1304 RegisterNetSyncVariableInt("m_SyncParts02");
1305 RegisterNetSyncVariableInt("m_SyncParts03");
1306 RegisterNetSyncVariableInt("m_InteractedPartId");
1307 RegisterNetSyncVariableInt("m_PerformedActionId");
1308 RegisterNetSyncVariableBool("m_HasBase");
1309
1310 //Construction init
1312
1313 if (ConfigIsExisting("hybridAttachments"))
1314 {
1316 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1317 }
1318 if (ConfigIsExisting("mountables"))
1319 {
1321 ConfigGetTextArray("mountables", m_Mountables);
1322 }
1323
1324 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1325 }
1326
1327 override void EEDelete(EntityAI parent)
1328 {
1329 super.EEDelete(parent);
1330
1331 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1333
1334 }
1335
1336 override string GetInvulnerabilityTypeString()
1337 {
1338 return "disableBaseDamage";
1339 }
1340
1341 override bool CanObstruct()
1342 {
1343 return true;
1344 }
1345
1346 override int GetHideIconMask()
1347 {
1348 return EInventoryIconVisibility.HIDE_VICINITY;
1349 }
1350
1351 // --- SYNCHRONIZATION
1353 {
1354 if (GetGame().IsServer())
1355 SetSynchDirty();
1356 }
1357
1358 override void OnVariablesSynchronized()
1359 {
1360 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1361 super.OnVariablesSynchronized();
1362
1363 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1364 }
1365
1366 protected void OnSynchronizedClient()
1367 {
1368 //update parts
1370
1371 //update action on part
1373
1374 //update visuals (client)
1375 UpdateVisuals();
1376 }
1377
1378 //parts synchronization
1380 {
1381 //part_id must starts from index = 1
1382 int offset;
1383 int mask;
1384
1385 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1386 {
1387 offset = part_id - 1;
1388 mask = 1 << offset;
1389
1391 }
1392 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1393 {
1394 offset = (part_id % 32);
1395 mask = 1 << offset;
1396
1398 }
1399 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1400 {
1401 offset = (part_id % 63);
1402 mask = 1 << offset;
1403
1405 }
1406 }
1407
1409 {
1410 //part_id must starts from index = 1
1411 int offset;
1412 int mask;
1413
1414 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1415 {
1416 offset = part_id - 1;
1417 mask = 1 << offset;
1418
1420 }
1421 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1422 {
1423 offset = (part_id % 32);
1424 mask = 1 << offset;
1425
1427 }
1428 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1429 {
1430 offset = (part_id % 63);
1431 mask = 1 << offset;
1432
1434 }
1435 }
1436
1438 {
1439 //part_id must starts from index = 1
1440 int offset;
1441 int mask;
1442
1443 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1444 {
1445 offset = part_id - 1;
1446 mask = 1 << offset;
1447
1448 if ((m_SyncParts01 & mask) > 0)
1449 return true;
1450 }
1451 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1452 {
1453 offset = (part_id % 32);
1454 mask = 1 << offset;
1455
1456 if ((m_SyncParts02 & mask) > 0)
1457 return true;
1458 }
1459 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1460 {
1461 offset = (part_id % 63);
1462 mask = 1 << offset;
1463
1464 if ((m_SyncParts03 & mask) > 0)
1465 return true;
1466 }
1467
1468 return false;
1469 }
1470
1471 protected void RegisterActionForSync(int part_id, int action_id)
1472 {
1475 }
1476
1477 protected void ResetActionSyncData()
1478 {
1479 //reset data
1480 m_InteractedPartId = -1;
1482 }
1483
1484 protected void SetActionFromSyncData()
1485 {
1486 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1487 {
1490
1491 switch (build_action_id)
1492 {
1496 }
1497 }
1498 }
1499 //------
1500
1502 {
1503 string key = part.m_PartName;
1504 bool is_base = part.IsBase();
1506 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1508 {
1509 if (!part.IsBuilt())
1510 {
1511 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1512 GetConstruction().AddToConstructedParts(key);
1513 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1514
1515 if (is_base)
1516 {
1518 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1519 }
1520 }
1521 }
1522 else
1523 {
1524 if (part.IsBuilt())
1525 {
1526 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1527 GetConstruction().RemoveFromConstructedParts(key);
1528 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1529
1530 if (is_base)
1531 {
1533 AddProxyPhysics(ANIMATION_DEPLOYED);
1534 }
1535 }
1536 }
1537
1538 //check slot lock for material attachments
1539 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1540 }
1541
1542 //set construction parts based on synchronized data
1544 {
1547
1548 for (int i = 0; i < construction_parts.Count(); ++i)
1549 {
1550 string key = construction_parts.GetKey(i);
1553 }
1554
1555 //regenerate navmesh
1556 UpdateNavmesh();
1557 }
1558
1560 {
1563
1564 for (int i = 0; i < construction_parts.Count(); ++i)
1565 {
1566 string key = construction_parts.GetKey(i);
1568
1569 if (value.GetId() == id)
1570 return value;
1571 }
1572
1573 return NULL;
1574 }
1575 //
1576
1577 //Base
1578 bool HasBase()
1579 {
1580 return m_HasBase;
1581 }
1582
1583 void SetBaseState(bool has_base)
1584 {
1586 }
1587
1588 override bool IsDeployable()
1589 {
1590 return true;
1591 }
1592
1593 bool IsOpened()
1594 {
1595 return false;
1596 }
1597
1598 //--- CONSTRUCTION KIT
1600 {
1604
1605 return construction_kit;
1606 }
1607
1609 {
1610 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1613 }
1614
1615 protected vector GetKitSpawnPosition()
1616 {
1617 return GetPosition();
1618 }
1619
1620 protected string GetConstructionKitType()
1621 {
1622 return "";
1623 }
1624
1626 {
1628 GetGame().ObjectDelete(construction_kit);
1629 }
1630
1631 //--- CONSTRUCTION
1632 void DestroyConstruction()
1633 {
1634 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1635 GetGame().ObjectDelete(this);
1636 }
1637
1638 // --- EVENTS
1639 override void OnStoreSave(ParamsWriteContext ctx)
1640 {
1641 super.OnStoreSave(ctx);
1642
1643 //sync parts 01
1644 ctx.Write(m_SyncParts01);
1645 ctx.Write(m_SyncParts02);
1646 ctx.Write(m_SyncParts03);
1647
1648 ctx.Write(m_HasBase);
1649 }
1650
1651 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1652 {
1653 if (!super.OnStoreLoad(ctx, version))
1654 return false;
1655
1656 //--- Base building data ---
1657 //Restore synced parts data
1658 if (!ctx.Read(m_SyncParts01))
1659 {
1660 m_SyncParts01 = 0; //set default
1661 return false;
1662 }
1663 if (!ctx.Read(m_SyncParts02))
1664 {
1665 m_SyncParts02 = 0; //set default
1666 return false;
1667 }
1668 if (!ctx.Read(m_SyncParts03))
1669 {
1670 m_SyncParts03 = 0; //set default
1671 return false;
1672 }
1673
1674 //has base
1675 if (!ctx.Read(m_HasBase))
1676 {
1677 m_HasBase = false;
1678 return false;
1679 }
1680 //---
1681
1682 return true;
1683 }
1684
1685 override void AfterStoreLoad()
1686 {
1687 super.AfterStoreLoad();
1688
1691 }
1692
1694 {
1695 //update server data
1697
1698 //set base state
1699 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1700 SetBaseState(construction_part.IsBuilt()) ;
1701
1702 //synchronize after load
1704 }
1705
1706 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1707 {
1709 return;
1710
1711 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1712
1713 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1714 return;
1715
1717 string part_name = zone;
1718 part_name.ToLower();
1719
1721 {
1723
1724 if (construction_part && construction.IsPartConstructed(part_name))
1725 {
1726 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1727 construction.DestroyConnectedParts(part_name);
1728 }
1729
1730 //barbed wire handling (hack-ish)
1731 if (part_name.Contains("barbed"))
1732 {
1733 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1734 if (barbed_wire)
1735 barbed_wire.SetMountedState(false);
1736 }
1737 }
1738 }
1739
1740 override void EEOnAfterLoad()
1741 {
1743 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1744
1745 super.EEOnAfterLoad();
1746 }
1747
1748 override void EEInit()
1749 {
1750 super.EEInit();
1751
1752 // init visuals and physics
1753 InitBaseState();
1754
1755 //debug
1756#ifdef DEVELOPER
1758#endif
1759 }
1760
1761 override void EEItemAttached(EntityAI item, string slot_name)
1762 {
1763 super.EEItemAttached(item, slot_name);
1764
1766 UpdateVisuals();
1768 }
1769
1770 override void EEItemDetached(EntityAI item, string slot_name)
1771 {
1772 super.EEItemDetached(item, slot_name);
1773
1774 UpdateVisuals();
1776 }
1777
1778 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
1779 {
1781 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
1782
1785 }
1786
1787 //ignore out of reach condition
1788 override bool IgnoreOutOfReachCondition()
1789 {
1790 return true;
1791 }
1792
1793 //CONSTRUCTION EVENTS
1794 //Build
1795 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
1796 {
1798
1799 //check base state
1800 if (construtionPart.IsBase())
1801 {
1802 SetBaseState(true);
1803
1804 //spawn kit
1806 }
1807
1808 //register constructed parts for synchronization
1810
1811 //register action that was performed on part
1813
1814 //synchronize
1816
1817 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
1818
1819 UpdateNavmesh();
1820
1821 //update visuals
1822 UpdateVisuals();
1823
1824 //reset action sync data
1825 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1826 }
1827
1828 void OnPartBuiltClient(string part_name, int action_id)
1829 {
1830 //play sound
1832 }
1833
1834 //Dismantle
1836 {
1837 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
1839
1840 //register constructed parts for synchronization
1842
1843 //register action that was performed on part
1845
1846 //synchronize
1848
1849 // server part of sync, client will be synced from SetPartsFromSyncData
1851
1852 UpdateNavmesh();
1853
1854 //update visuals
1855 UpdateVisuals();
1856
1857 //reset action sync data
1858 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1859
1860 //check base state
1861 if (construtionPart.IsBase())
1862 {
1863 //Destroy construction
1864 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1865 }
1866 }
1867
1869 {
1870 //play sound
1872 }
1873
1874 //Destroy
1876 {
1877 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
1879
1880 //register constructed parts for synchronization
1882
1883 //register action that was performed on part
1885
1886 //synchronize
1888
1889 // server part of sync, client will be synced from SetPartsFromSyncData
1891
1892 UpdateNavmesh();
1893
1894 //update visuals
1895 UpdateVisuals();
1896
1897 //reset action sync data
1898 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1899
1900 //check base state
1901 if (construtionPart.IsBase())
1902 {
1903 //Destroy construction
1904 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1905 }
1906 }
1907
1908 void OnPartDestroyedClient(string part_name, int action_id)
1909 {
1910 //play sound
1912 }
1913
1914 // --- UPDATE
1915 void InitBaseState()
1916 {
1917 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
1918
1919 InitVisuals();
1920 UpdateNavmesh(); //regenerate navmesh
1921 GetConstruction().InitBaseState();
1922 }
1923
1924 void InitVisuals()
1925 {
1926 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
1927 //check base
1928 if (!HasBase())
1929 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1930 else
1931 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1932
1933 GetConstruction().UpdateVisuals();
1934 }
1935
1936 void UpdateVisuals()
1937 {
1939
1941 foreach (string slotName : attachmentSlots)
1943
1944 //check base
1945 if (!HasBase())
1946 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1947 else
1948 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1949
1950 GetConstruction().UpdateVisuals();
1951 }
1952
1954 {
1955 string slotNameMounted = slot_name + "_Mounted";
1956 EntityAI attachment = FindAttachmentBySlotName(slot_name);
1957
1958 if (attachment)
1959 {
1960 BarbedWire barbedWire = BarbedWire.Cast(attachment);
1961 if (barbedWire && barbedWire.IsMounted())
1963 else
1965
1966 if (is_locked)
1967 {
1968 SetAnimationPhase(slotNameMounted, 0);
1969 SetAnimationPhase(slot_name, 1);
1970 }
1971 else
1972 {
1973 SetAnimationPhase(slotNameMounted, 1);
1974 SetAnimationPhase(slot_name, 0);
1975 }
1976 }
1977 else
1978 {
1979 SetAnimationPhase(slotNameMounted, 1);
1980 SetAnimationPhase(slot_name, 1);
1981
1983 }
1984 }
1985
1986 // avoid calling this function on frequent occasions, it's a massive performance hit
1987 void UpdatePhysics()
1988 {
1990 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
1991
1994
1996 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
1997
1998 foreach (string slotName : attachmentSlots)
2000
2001 //check base
2002 if (!HasBase())
2003 {
2005 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2006
2007 AddProxyPhysics(ANIMATION_DEPLOYED);
2008 }
2009 else
2010 {
2012 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2013
2014 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2015 }
2016
2017 GetConstruction().UpdatePhysics();
2018 UpdateNavmesh();
2019 }
2020
2022 {
2023 //checks for invalid appends; hotfix
2024 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2025 return;
2026 //----------------------------------
2027 string slot_name_mounted = slot_name + "_Mounted";
2028 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2029
2030 //remove proxy physics
2031 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2032 RemoveProxyPhysics(slot_name_mounted);
2033 RemoveProxyPhysics(slot_name);
2034
2035 if (attachment)
2036 {
2037 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2038 if (is_locked)
2039 {
2040 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2041 AddProxyPhysics(slot_name_mounted);
2042 }
2043 else
2044 {
2045 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2046 AddProxyPhysics(slot_name);
2047 }
2048 }
2049 }
2050
2051 protected void UpdateNavmesh()
2052 {
2053 SetAffectPathgraph(true, false);
2054 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2055 }
2056
2057 override bool CanUseConstruction()
2058 {
2059 return true;
2060 }
2061
2062 override bool CanUseConstructionBuild()
2063 {
2064 return true;
2065 }
2066
2068 {
2069 if (attachment)
2070 {
2072 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2073
2074 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2075 }
2076
2077 return false;
2078 }
2079
2080 protected bool IsAttachmentSlotLocked(string slot_name)
2081 {
2082 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2083 }
2084
2085 //--- ATTACHMENT SLOTS
2087 {
2088 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2089 if (GetGame().ConfigIsExisting(config_path))
2090 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2091 }
2092
2094 {
2095 return true;
2096 }
2097
2098 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2099 {
2100 return true;
2101 }
2102
2103 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2104 {
2105 return true;
2106 }
2107
2108 // --- INIT
2109 void ConstructionInit()
2110 {
2111 if (!m_Construction)
2112 m_Construction = new Construction(this);
2113
2114 GetConstruction().Init();
2115 }
2116
2118 {
2119 return m_Construction;
2120 }
2121
2122 //--- INVENTORY/ATTACHMENTS CONDITIONS
2123 //attachments
2125 {
2126 return super.CanReceiveAttachment(attachment, slotId);
2127 }
2128
2130 {
2131 int attachment_count = GetInventory().AttachmentCount();
2132 if (attachment_count > 0)
2133 {
2134 if (HasBase() && attachment_count == 1)
2135 return false;
2136
2137 return true;
2138 }
2139
2140 return false;
2141 }
2142
2143 override bool ShowZonesHealth()
2144 {
2145 return true;
2146 }
2147
2148 //this into/outo parent.Cargo
2149 override bool CanPutInCargo(EntityAI parent)
2150 {
2151 return false;
2152 }
2153
2154 override bool CanRemoveFromCargo(EntityAI parent)
2155 {
2156 return false;
2157 }
2158
2159 //hands
2160 override bool CanPutIntoHands(EntityAI parent)
2161 {
2162 return false;
2163 }
2164
2165 //--- ACTION CONDITIONS
2166 //direction
2167 override bool IsFacingPlayer(PlayerBase player, string selection)
2168 {
2169 return true;
2170 }
2171
2172 override bool IsPlayerInside(PlayerBase player, string selection)
2173 {
2174 return true;
2175 }
2176
2179 {
2180 return false;
2181 }
2182
2183 //camera direction check
2184 bool IsFacingCamera(string selection)
2185 {
2186 return true;
2187 }
2188
2189 //roof check
2191 {
2192 return false;
2193 }
2194
2195 //selection->player distance check
2196 bool HasProperDistance(string selection, PlayerBase player)
2197 {
2198 return true;
2199 }
2200
2201 //folding
2203 {
2204 if (HasBase() || GetInventory().AttachmentCount() > 0)
2205 return false;
2206
2207 return true;
2208 }
2209
2211 {
2214
2215 return item;
2216 }
2217
2218 //Damage triggers (barbed wire)
2219 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2220 {
2221 if (GetGame() && GetGame().IsServer())
2222 {
2223 //destroy area damage if some already exists
2225
2226 //create new area damage
2228 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2229
2230 vector min_max[2];
2231 if (MemoryPointExists(slot_name + "_min"))
2232 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2233 if (MemoryPointExists(slot_name + "_max"))
2234 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2235
2236 //get proper trigger extents (min<max)
2237 vector extents[2];
2238 GetConstruction().GetTriggerExtents(min_max, extents);
2239
2240 //get box center
2241 vector center;
2242 center = GetConstruction().GetBoxCenter(min_max);
2243 center = ModelToWorld(center);
2244
2245 //rotate center if needed
2248
2249 areaDamage.SetExtents(extents[0], extents[1]);
2250 areaDamage.SetAreaPosition(center);
2251 areaDamage.SetAreaOrientation(orientation);
2252 areaDamage.SetLoopInterval(1.0);
2253 areaDamage.SetDeferDuration(0.2);
2254 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2255 areaDamage.SetAmmoName("BarbedWireHit");
2256 areaDamage.Spawn();
2257
2259 }
2260 }
2261
2263 {
2264 if (angle_deg != 0)
2265 {
2266 //orientation
2268
2269 //center
2271 if (MemoryPointExists("rotate_axis"))
2272 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2275 center[0] = r_center_x;
2276 center[2] = r_center_z;
2277 }
2278 }
2279
2280 void DestroyAreaDamage(string slot_name)
2281 {
2282 if (GetGame() && GetGame().IsServer())
2283 {
2286 {
2287 if (areaDamage)
2288 areaDamage.Destroy();
2289
2291 }
2292 }
2293 }
2294
2295 override bool IsIgnoredByConstruction()
2296 {
2297 return true;
2298 }
2299
2300 //================================================================
2301 // SOUNDS
2302 //================================================================
2303 protected void SoundBuildStart(string part_name)
2304 {
2305 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2306 }
2307
2308 protected void SoundDismantleStart(string part_name)
2309 {
2310 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2311 }
2312
2313 protected void SoundDestroyStart(string part_name)
2314 {
2315 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2316 }
2317
2318 protected string GetBuildSoundByMaterial(string part_name)
2319 {
2321
2322 switch (material_type)
2323 {
2324 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2325 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2326 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2327 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2328 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2329 }
2330
2331 return "";
2332 }
2333
2334 protected string GetDismantleSoundByMaterial(string part_name)
2335 {
2337
2338 switch (material_type)
2339 {
2340 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2341 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2342 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2343 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2344 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2345 }
2346
2347 return "";
2348 }
2349
2350 //misc
2352 {
2353 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2354 {
2355 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2357 SetHealth(slot_name, "Health", item.GetHealth());
2358 }
2359 }
2360
2361 override int GetDamageSystemVersionChange()
2362 {
2363 return 111;
2364 }
2365
2366 override void SetActions()
2367 {
2368 super.SetActions();
2369
2371 //AddAction(ActionTakeHybridAttachment);
2372 //AddAction(ActionTakeHybridAttachmentToHands);
2375 }
2376
2377 //================================================================
2378 // DEBUG
2379 //================================================================
2380 protected void DebugCustomState()
2381 {
2382 }
2383
2386 {
2387 return null;
2388 }
2389
2390 override void OnDebugSpawn()
2391 {
2392 FullyBuild();
2393 }
2394
2395 void FullyBuild()
2396 {
2398 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2399
2400 Man p;
2401
2402#ifdef SERVER
2404 GetGame().GetWorld().GetPlayerList(players);
2405 if (players.Count())
2406 p = players[0];
2407#else
2408 p = GetGame().GetPlayer();
2409#endif
2410
2411 foreach (ConstructionPart part : parts)
2412 {
2413 bool excluded = false;
2414 string partName = part.GetPartName();
2415 if (excludes)
2416 {
2417 foreach (string exclude : excludes)
2418 {
2419 if (partName.Contains(exclude))
2420 {
2421 excluded = true;
2422 break;
2423 }
2424 }
2425 }
2426
2427 if (!excluded)
2429 }
2430
2431 GetConstruction().UpdateVisuals();
2432 }
2433}
2434
2435void bsbDebugPrint(string s)
2436{
2437#ifdef BSB_DEBUG
2438 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2439#else
2440 //Print("" + s); // comment/uncomment to hide/see debug logs
2441#endif
2442}
2443void bsbDebugSpam(string s)
2444{
2445#ifdef BSB_DEBUG_SPAM
2446 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2447#else
2448 //Print("" + s); // comment/uncomment to hide/see debug logs
2449#endif
2450}

◆ GetInvulnerabilityTypeString()

override string bsbDebugPrint::GetInvulnerabilityTypeString ( )
protected

Definition at line 1250 of file BaseBuildingBase.c.

1252{
1253 const string ANIMATION_DEPLOYED = "Deployed";
1254
1255 float m_ConstructionKitHealth; //stored health value for used construction kit
1256
1258
1259 bool m_HasBase;
1260 //variables for synchronization of base building parts (2x31 is the current limit)
1261 int m_SyncParts01; //synchronization for already built parts (31 parts)
1262 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1263 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1264 int m_InteractedPartId; //construction part id that an action was performed on
1265 int m_PerformedActionId; //action id that was performed on a construction part
1266
1267 //Sounds
1268 //build
1269 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1270 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1271 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1272 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1273 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1274 //dismantle
1275 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1276 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1277 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1278 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1279 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1280
1281 protected EffectSound m_Sound;
1282
1286
1287 // Constructor
1288 void BaseBuildingBase()
1289 {
1291
1292 //synchronized variables
1293 RegisterNetSyncVariableInt("m_SyncParts01");
1294 RegisterNetSyncVariableInt("m_SyncParts02");
1295 RegisterNetSyncVariableInt("m_SyncParts03");
1296 RegisterNetSyncVariableInt("m_InteractedPartId");
1297 RegisterNetSyncVariableInt("m_PerformedActionId");
1298 RegisterNetSyncVariableBool("m_HasBase");
1299
1300 //Construction init
1302
1303 if (ConfigIsExisting("hybridAttachments"))
1304 {
1306 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1307 }
1308 if (ConfigIsExisting("mountables"))
1309 {
1311 ConfigGetTextArray("mountables", m_Mountables);
1312 }
1313
1314 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1315 }
1316
1317 override void EEDelete(EntityAI parent)
1318 {
1319 super.EEDelete(parent);
1320
1321 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1323
1324 }
1325
1326 override string GetInvulnerabilityTypeString()
1327 {
1328 return "disableBaseDamage";
1329 }
1330
1331 override bool CanObstruct()
1332 {
1333 return true;
1334 }
1335
1336 override int GetHideIconMask()
1337 {
1338 return EInventoryIconVisibility.HIDE_VICINITY;
1339 }
1340
1341 // --- SYNCHRONIZATION
1343 {
1344 if (GetGame().IsServer())
1345 SetSynchDirty();
1346 }
1347
1348 override void OnVariablesSynchronized()
1349 {
1350 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1351 super.OnVariablesSynchronized();
1352
1353 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1354 }
1355
1356 protected void OnSynchronizedClient()
1357 {
1358 //update parts
1360
1361 //update action on part
1363
1364 //update visuals (client)
1365 UpdateVisuals();
1366 }
1367
1368 //parts synchronization
1370 {
1371 //part_id must starts from index = 1
1372 int offset;
1373 int mask;
1374
1375 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1376 {
1377 offset = part_id - 1;
1378 mask = 1 << offset;
1379
1381 }
1382 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1383 {
1384 offset = (part_id % 32);
1385 mask = 1 << offset;
1386
1388 }
1389 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1390 {
1391 offset = (part_id % 63);
1392 mask = 1 << offset;
1393
1395 }
1396 }
1397
1399 {
1400 //part_id must starts from index = 1
1401 int offset;
1402 int mask;
1403
1404 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1405 {
1406 offset = part_id - 1;
1407 mask = 1 << offset;
1408
1410 }
1411 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1412 {
1413 offset = (part_id % 32);
1414 mask = 1 << offset;
1415
1417 }
1418 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1419 {
1420 offset = (part_id % 63);
1421 mask = 1 << offset;
1422
1424 }
1425 }
1426
1428 {
1429 //part_id must starts from index = 1
1430 int offset;
1431 int mask;
1432
1433 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1434 {
1435 offset = part_id - 1;
1436 mask = 1 << offset;
1437
1438 if ((m_SyncParts01 & mask) > 0)
1439 return true;
1440 }
1441 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1442 {
1443 offset = (part_id % 32);
1444 mask = 1 << offset;
1445
1446 if ((m_SyncParts02 & mask) > 0)
1447 return true;
1448 }
1449 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1450 {
1451 offset = (part_id % 63);
1452 mask = 1 << offset;
1453
1454 if ((m_SyncParts03 & mask) > 0)
1455 return true;
1456 }
1457
1458 return false;
1459 }
1460
1461 protected void RegisterActionForSync(int part_id, int action_id)
1462 {
1465 }
1466
1467 protected void ResetActionSyncData()
1468 {
1469 //reset data
1470 m_InteractedPartId = -1;
1472 }
1473
1474 protected void SetActionFromSyncData()
1475 {
1476 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1477 {
1480
1481 switch (build_action_id)
1482 {
1486 }
1487 }
1488 }
1489 //------
1490
1492 {
1493 string key = part.m_PartName;
1494 bool is_base = part.IsBase();
1496 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1498 {
1499 if (!part.IsBuilt())
1500 {
1501 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1502 GetConstruction().AddToConstructedParts(key);
1503 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1504
1505 if (is_base)
1506 {
1508 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1509 }
1510 }
1511 }
1512 else
1513 {
1514 if (part.IsBuilt())
1515 {
1516 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1517 GetConstruction().RemoveFromConstructedParts(key);
1518 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1519
1520 if (is_base)
1521 {
1523 AddProxyPhysics(ANIMATION_DEPLOYED);
1524 }
1525 }
1526 }
1527
1528 //check slot lock for material attachments
1529 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1530 }
1531
1532 //set construction parts based on synchronized data
1534 {
1537
1538 for (int i = 0; i < construction_parts.Count(); ++i)
1539 {
1540 string key = construction_parts.GetKey(i);
1543 }
1544
1545 //regenerate navmesh
1546 UpdateNavmesh();
1547 }
1548
1550 {
1553
1554 for (int i = 0; i < construction_parts.Count(); ++i)
1555 {
1556 string key = construction_parts.GetKey(i);
1558
1559 if (value.GetId() == id)
1560 return value;
1561 }
1562
1563 return NULL;
1564 }
1565 //
1566
1567 //Base
1568 bool HasBase()
1569 {
1570 return m_HasBase;
1571 }
1572
1573 void SetBaseState(bool has_base)
1574 {
1576 }
1577
1578 override bool IsDeployable()
1579 {
1580 return true;
1581 }
1582
1583 bool IsOpened()
1584 {
1585 return false;
1586 }
1587
1588 //--- CONSTRUCTION KIT
1590 {
1594
1595 return construction_kit;
1596 }
1597
1599 {
1600 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1603 }
1604
1605 protected vector GetKitSpawnPosition()
1606 {
1607 return GetPosition();
1608 }
1609
1610 protected string GetConstructionKitType()
1611 {
1612 return "";
1613 }
1614
1616 {
1618 GetGame().ObjectDelete(construction_kit);
1619 }
1620
1621 //--- CONSTRUCTION
1622 void DestroyConstruction()
1623 {
1624 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1625 GetGame().ObjectDelete(this);
1626 }
1627
1628 // --- EVENTS
1629 override void OnStoreSave(ParamsWriteContext ctx)
1630 {
1631 super.OnStoreSave(ctx);
1632
1633 //sync parts 01
1634 ctx.Write(m_SyncParts01);
1635 ctx.Write(m_SyncParts02);
1636 ctx.Write(m_SyncParts03);
1637
1638 ctx.Write(m_HasBase);
1639 }
1640
1641 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1642 {
1643 if (!super.OnStoreLoad(ctx, version))
1644 return false;
1645
1646 //--- Base building data ---
1647 //Restore synced parts data
1648 if (!ctx.Read(m_SyncParts01))
1649 {
1650 m_SyncParts01 = 0; //set default
1651 return false;
1652 }
1653 if (!ctx.Read(m_SyncParts02))
1654 {
1655 m_SyncParts02 = 0; //set default
1656 return false;
1657 }
1658 if (!ctx.Read(m_SyncParts03))
1659 {
1660 m_SyncParts03 = 0; //set default
1661 return false;
1662 }
1663
1664 //has base
1665 if (!ctx.Read(m_HasBase))
1666 {
1667 m_HasBase = false;
1668 return false;
1669 }
1670 //---
1671
1672 return true;
1673 }
1674
1675 override void AfterStoreLoad()
1676 {
1677 super.AfterStoreLoad();
1678
1681 }
1682
1684 {
1685 //update server data
1687
1688 //set base state
1689 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1690 SetBaseState(construction_part.IsBuilt()) ;
1691
1692 //synchronize after load
1694 }
1695
1696 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1697 {
1699 return;
1700
1701 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1702
1703 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1704 return;
1705
1707 string part_name = zone;
1708 part_name.ToLower();
1709
1711 {
1713
1714 if (construction_part && construction.IsPartConstructed(part_name))
1715 {
1716 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1717 construction.DestroyConnectedParts(part_name);
1718 }
1719
1720 //barbed wire handling (hack-ish)
1721 if (part_name.Contains("barbed"))
1722 {
1723 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1724 if (barbed_wire)
1725 barbed_wire.SetMountedState(false);
1726 }
1727 }
1728 }
1729
1730 override void EEOnAfterLoad()
1731 {
1733 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1734
1735 super.EEOnAfterLoad();
1736 }
1737
1738 override void EEInit()
1739 {
1740 super.EEInit();
1741
1742 // init visuals and physics
1743 InitBaseState();
1744
1745 //debug
1746#ifdef DEVELOPER
1748#endif
1749 }
1750
1751 override void EEItemAttached(EntityAI item, string slot_name)
1752 {
1753 super.EEItemAttached(item, slot_name);
1754
1756 UpdateVisuals();
1758 }
1759
1760 override void EEItemDetached(EntityAI item, string slot_name)
1761 {
1762 super.EEItemDetached(item, slot_name);
1763
1764 UpdateVisuals();
1766 }
1767
1768 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
1769 {
1771 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
1772
1775 }
1776
1777 //ignore out of reach condition
1778 override bool IgnoreOutOfReachCondition()
1779 {
1780 return true;
1781 }
1782
1783 //CONSTRUCTION EVENTS
1784 //Build
1785 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
1786 {
1788
1789 //check base state
1790 if (construtionPart.IsBase())
1791 {
1792 SetBaseState(true);
1793
1794 //spawn kit
1796 }
1797
1798 //register constructed parts for synchronization
1800
1801 //register action that was performed on part
1803
1804 //synchronize
1806
1807 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
1808
1809 UpdateNavmesh();
1810
1811 //update visuals
1812 UpdateVisuals();
1813
1814 //reset action sync data
1815 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1816 }
1817
1818 void OnPartBuiltClient(string part_name, int action_id)
1819 {
1820 //play sound
1822 }
1823
1824 //Dismantle
1826 {
1827 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
1829
1830 //register constructed parts for synchronization
1832
1833 //register action that was performed on part
1835
1836 //synchronize
1838
1839 // server part of sync, client will be synced from SetPartsFromSyncData
1841
1842 UpdateNavmesh();
1843
1844 //update visuals
1845 UpdateVisuals();
1846
1847 //reset action sync data
1848 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1849
1850 //check base state
1851 if (construtionPart.IsBase())
1852 {
1853 //Destroy construction
1854 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1855 }
1856 }
1857
1859 {
1860 //play sound
1862 }
1863
1864 //Destroy
1866 {
1867 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
1869
1870 //register constructed parts for synchronization
1872
1873 //register action that was performed on part
1875
1876 //synchronize
1878
1879 // server part of sync, client will be synced from SetPartsFromSyncData
1881
1882 UpdateNavmesh();
1883
1884 //update visuals
1885 UpdateVisuals();
1886
1887 //reset action sync data
1888 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1889
1890 //check base state
1891 if (construtionPart.IsBase())
1892 {
1893 //Destroy construction
1894 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1895 }
1896 }
1897
1898 void OnPartDestroyedClient(string part_name, int action_id)
1899 {
1900 //play sound
1902 }
1903
1904 // --- UPDATE
1905 void InitBaseState()
1906 {
1907 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
1908
1909 InitVisuals();
1910 UpdateNavmesh(); //regenerate navmesh
1911 GetConstruction().InitBaseState();
1912 }
1913
1914 void InitVisuals()
1915 {
1916 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
1917 //check base
1918 if (!HasBase())
1919 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1920 else
1921 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1922
1923 GetConstruction().UpdateVisuals();
1924 }
1925
1926 void UpdateVisuals()
1927 {
1929
1931 foreach (string slotName : attachmentSlots)
1933
1934 //check base
1935 if (!HasBase())
1936 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1937 else
1938 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1939
1940 GetConstruction().UpdateVisuals();
1941 }
1942
1944 {
1945 string slotNameMounted = slot_name + "_Mounted";
1946 EntityAI attachment = FindAttachmentBySlotName(slot_name);
1947
1948 if (attachment)
1949 {
1950 BarbedWire barbedWire = BarbedWire.Cast(attachment);
1951 if (barbedWire && barbedWire.IsMounted())
1953 else
1955
1956 if (is_locked)
1957 {
1958 SetAnimationPhase(slotNameMounted, 0);
1959 SetAnimationPhase(slot_name, 1);
1960 }
1961 else
1962 {
1963 SetAnimationPhase(slotNameMounted, 1);
1964 SetAnimationPhase(slot_name, 0);
1965 }
1966 }
1967 else
1968 {
1969 SetAnimationPhase(slotNameMounted, 1);
1970 SetAnimationPhase(slot_name, 1);
1971
1973 }
1974 }
1975
1976 // avoid calling this function on frequent occasions, it's a massive performance hit
1977 void UpdatePhysics()
1978 {
1980 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
1981
1984
1986 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
1987
1988 foreach (string slotName : attachmentSlots)
1990
1991 //check base
1992 if (!HasBase())
1993 {
1995 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
1996
1997 AddProxyPhysics(ANIMATION_DEPLOYED);
1998 }
1999 else
2000 {
2002 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2003
2004 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2005 }
2006
2007 GetConstruction().UpdatePhysics();
2008 UpdateNavmesh();
2009 }
2010
2012 {
2013 //checks for invalid appends; hotfix
2014 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2015 return;
2016 //----------------------------------
2017 string slot_name_mounted = slot_name + "_Mounted";
2018 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2019
2020 //remove proxy physics
2021 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2022 RemoveProxyPhysics(slot_name_mounted);
2023 RemoveProxyPhysics(slot_name);
2024
2025 if (attachment)
2026 {
2027 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2028 if (is_locked)
2029 {
2030 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2031 AddProxyPhysics(slot_name_mounted);
2032 }
2033 else
2034 {
2035 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2036 AddProxyPhysics(slot_name);
2037 }
2038 }
2039 }
2040
2041 protected void UpdateNavmesh()
2042 {
2043 SetAffectPathgraph(true, false);
2044 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2045 }
2046
2047 override bool CanUseConstruction()
2048 {
2049 return true;
2050 }
2051
2052 override bool CanUseConstructionBuild()
2053 {
2054 return true;
2055 }
2056
2058 {
2059 if (attachment)
2060 {
2062 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2063
2064 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2065 }
2066
2067 return false;
2068 }
2069
2070 protected bool IsAttachmentSlotLocked(string slot_name)
2071 {
2072 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2073 }
2074
2075 //--- ATTACHMENT SLOTS
2077 {
2078 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2079 if (GetGame().ConfigIsExisting(config_path))
2080 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2081 }
2082
2084 {
2085 return true;
2086 }
2087
2088 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2089 {
2090 return true;
2091 }
2092
2093 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2094 {
2095 return true;
2096 }
2097
2098 // --- INIT
2099 void ConstructionInit()
2100 {
2101 if (!m_Construction)
2102 m_Construction = new Construction(this);
2103
2104 GetConstruction().Init();
2105 }
2106
2108 {
2109 return m_Construction;
2110 }
2111
2112 //--- INVENTORY/ATTACHMENTS CONDITIONS
2113 //attachments
2115 {
2116 return super.CanReceiveAttachment(attachment, slotId);
2117 }
2118
2120 {
2121 int attachment_count = GetInventory().AttachmentCount();
2122 if (attachment_count > 0)
2123 {
2124 if (HasBase() && attachment_count == 1)
2125 return false;
2126
2127 return true;
2128 }
2129
2130 return false;
2131 }
2132
2133 override bool ShowZonesHealth()
2134 {
2135 return true;
2136 }
2137
2138 //this into/outo parent.Cargo
2139 override bool CanPutInCargo(EntityAI parent)
2140 {
2141 return false;
2142 }
2143
2144 override bool CanRemoveFromCargo(EntityAI parent)
2145 {
2146 return false;
2147 }
2148
2149 //hands
2150 override bool CanPutIntoHands(EntityAI parent)
2151 {
2152 return false;
2153 }
2154
2155 //--- ACTION CONDITIONS
2156 //direction
2157 override bool IsFacingPlayer(PlayerBase player, string selection)
2158 {
2159 return true;
2160 }
2161
2162 override bool IsPlayerInside(PlayerBase player, string selection)
2163 {
2164 return true;
2165 }
2166
2169 {
2170 return false;
2171 }
2172
2173 //camera direction check
2174 bool IsFacingCamera(string selection)
2175 {
2176 return true;
2177 }
2178
2179 //roof check
2181 {
2182 return false;
2183 }
2184
2185 //selection->player distance check
2186 bool HasProperDistance(string selection, PlayerBase player)
2187 {
2188 return true;
2189 }
2190
2191 //folding
2193 {
2194 if (HasBase() || GetInventory().AttachmentCount() > 0)
2195 return false;
2196
2197 return true;
2198 }
2199
2201 {
2204
2205 return item;
2206 }
2207
2208 //Damage triggers (barbed wire)
2209 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2210 {
2211 if (GetGame() && GetGame().IsServer())
2212 {
2213 //destroy area damage if some already exists
2215
2216 //create new area damage
2218 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2219
2220 vector min_max[2];
2221 if (MemoryPointExists(slot_name + "_min"))
2222 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2223 if (MemoryPointExists(slot_name + "_max"))
2224 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2225
2226 //get proper trigger extents (min<max)
2227 vector extents[2];
2228 GetConstruction().GetTriggerExtents(min_max, extents);
2229
2230 //get box center
2231 vector center;
2232 center = GetConstruction().GetBoxCenter(min_max);
2233 center = ModelToWorld(center);
2234
2235 //rotate center if needed
2238
2239 areaDamage.SetExtents(extents[0], extents[1]);
2240 areaDamage.SetAreaPosition(center);
2241 areaDamage.SetAreaOrientation(orientation);
2242 areaDamage.SetLoopInterval(1.0);
2243 areaDamage.SetDeferDuration(0.2);
2244 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2245 areaDamage.SetAmmoName("BarbedWireHit");
2246 areaDamage.Spawn();
2247
2249 }
2250 }
2251
2253 {
2254 if (angle_deg != 0)
2255 {
2256 //orientation
2258
2259 //center
2261 if (MemoryPointExists("rotate_axis"))
2262 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2265 center[0] = r_center_x;
2266 center[2] = r_center_z;
2267 }
2268 }
2269
2270 void DestroyAreaDamage(string slot_name)
2271 {
2272 if (GetGame() && GetGame().IsServer())
2273 {
2276 {
2277 if (areaDamage)
2278 areaDamage.Destroy();
2279
2281 }
2282 }
2283 }
2284
2285 override bool IsIgnoredByConstruction()
2286 {
2287 return true;
2288 }
2289
2290 //================================================================
2291 // SOUNDS
2292 //================================================================
2293 protected void SoundBuildStart(string part_name)
2294 {
2295 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2296 }
2297
2298 protected void SoundDismantleStart(string part_name)
2299 {
2300 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2301 }
2302
2303 protected void SoundDestroyStart(string part_name)
2304 {
2305 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2306 }
2307
2308 protected string GetBuildSoundByMaterial(string part_name)
2309 {
2311
2312 switch (material_type)
2313 {
2314 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2315 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2316 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2317 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2318 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2319 }
2320
2321 return "";
2322 }
2323
2324 protected string GetDismantleSoundByMaterial(string part_name)
2325 {
2327
2328 switch (material_type)
2329 {
2330 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2331 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2332 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2333 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2334 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2335 }
2336
2337 return "";
2338 }
2339
2340 //misc
2342 {
2343 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2344 {
2345 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2347 SetHealth(slot_name, "Health", item.GetHealth());
2348 }
2349 }
2350
2351 override int GetDamageSystemVersionChange()
2352 {
2353 return 111;
2354 }
2355
2356 override void SetActions()
2357 {
2358 super.SetActions();
2359
2361 //AddAction(ActionTakeHybridAttachment);
2362 //AddAction(ActionTakeHybridAttachmentToHands);
2365 }
2366
2367 //================================================================
2368 // DEBUG
2369 //================================================================
2370 protected void DebugCustomState()
2371 {
2372 }
2373
2376 {
2377 return null;
2378 }
2379
2380 override void OnDebugSpawn()
2381 {
2382 FullyBuild();
2383 }
2384
2385 void FullyBuild()
2386 {
2388 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2389
2390 Man p;
2391
2392#ifdef SERVER
2394 GetGame().GetWorld().GetPlayerList(players);
2395 if (players.Count())
2396 p = players[0];
2397#else
2398 p = GetGame().GetPlayer();
2399#endif
2400
2401 foreach (ConstructionPart part : parts)
2402 {
2403 bool excluded = false;
2404 string partName = part.GetPartName();
2405 if (excludes)
2406 {
2407 foreach (string exclude : excludes)
2408 {
2409 if (partName.Contains(exclude))
2410 {
2411 excluded = true;
2412 break;
2413 }
2414 }
2415 }
2416
2417 if (!excluded)
2419 }
2420
2421 GetConstruction().UpdateVisuals();
2422 }
2423}
2424
2425void bsbDebugPrint(string s)
2426{
2427#ifdef BSB_DEBUG
2428 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2429#else
2430 //Print("" + s); // comment/uncomment to hide/see debug logs
2431#endif
2432}
2433void bsbDebugSpam(string s)
2434{
2435#ifdef BSB_DEBUG_SPAM
2436 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2437#else
2438 //Print("" + s); // comment/uncomment to hide/see debug logs
2439#endif
2440}

Referenced by FireplaceBase::BarrelHoles_ColorBase(), ItemBase::BaseBuildingBase(), BuildPartServer(), DeployableContainer_Base(), and ItemBase::TentBase().

◆ GetKitSpawnPosition()

vector bsbDebugPrint::GetKitSpawnPosition ( )
protected

Definition at line 1529 of file BaseBuildingBase.c.

1531{
1532 const string ANIMATION_DEPLOYED = "Deployed";
1533
1534 float m_ConstructionKitHealth; //stored health value for used construction kit
1535
1537
1538 bool m_HasBase;
1539 //variables for synchronization of base building parts (2x31 is the current limit)
1540 int m_SyncParts01; //synchronization for already built parts (31 parts)
1541 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1542 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1543 int m_InteractedPartId; //construction part id that an action was performed on
1544 int m_PerformedActionId; //action id that was performed on a construction part
1545
1546 //Sounds
1547 //build
1548 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1549 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1550 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1551 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1552 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1553 //dismantle
1554 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1555 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1556 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1557 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1558 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1559
1560 protected EffectSound m_Sound;
1561
1565
1566 // Constructor
1567 void BaseBuildingBase()
1568 {
1570
1571 //synchronized variables
1572 RegisterNetSyncVariableInt("m_SyncParts01");
1573 RegisterNetSyncVariableInt("m_SyncParts02");
1574 RegisterNetSyncVariableInt("m_SyncParts03");
1575 RegisterNetSyncVariableInt("m_InteractedPartId");
1576 RegisterNetSyncVariableInt("m_PerformedActionId");
1577 RegisterNetSyncVariableBool("m_HasBase");
1578
1579 //Construction init
1581
1582 if (ConfigIsExisting("hybridAttachments"))
1583 {
1585 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1586 }
1587 if (ConfigIsExisting("mountables"))
1588 {
1590 ConfigGetTextArray("mountables", m_Mountables);
1591 }
1592
1593 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1594 }
1595
1596 override void EEDelete(EntityAI parent)
1597 {
1598 super.EEDelete(parent);
1599
1600 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1602
1603 }
1604
1605 override string GetInvulnerabilityTypeString()
1606 {
1607 return "disableBaseDamage";
1608 }
1609
1610 override bool CanObstruct()
1611 {
1612 return true;
1613 }
1614
1615 override int GetHideIconMask()
1616 {
1617 return EInventoryIconVisibility.HIDE_VICINITY;
1618 }
1619
1620 // --- SYNCHRONIZATION
1622 {
1623 if (GetGame().IsServer())
1624 SetSynchDirty();
1625 }
1626
1627 override void OnVariablesSynchronized()
1628 {
1629 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1630 super.OnVariablesSynchronized();
1631
1632 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1633 }
1634
1635 protected void OnSynchronizedClient()
1636 {
1637 //update parts
1639
1640 //update action on part
1642
1643 //update visuals (client)
1644 UpdateVisuals();
1645 }
1646
1647 //parts synchronization
1649 {
1650 //part_id must starts from index = 1
1651 int offset;
1652 int mask;
1653
1654 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1655 {
1656 offset = part_id - 1;
1657 mask = 1 << offset;
1658
1660 }
1661 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1662 {
1663 offset = (part_id % 32);
1664 mask = 1 << offset;
1665
1667 }
1668 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1669 {
1670 offset = (part_id % 63);
1671 mask = 1 << offset;
1672
1674 }
1675 }
1676
1678 {
1679 //part_id must starts from index = 1
1680 int offset;
1681 int mask;
1682
1683 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1684 {
1685 offset = part_id - 1;
1686 mask = 1 << offset;
1687
1689 }
1690 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1691 {
1692 offset = (part_id % 32);
1693 mask = 1 << offset;
1694
1696 }
1697 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1698 {
1699 offset = (part_id % 63);
1700 mask = 1 << offset;
1701
1703 }
1704 }
1705
1707 {
1708 //part_id must starts from index = 1
1709 int offset;
1710 int mask;
1711
1712 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1713 {
1714 offset = part_id - 1;
1715 mask = 1 << offset;
1716
1717 if ((m_SyncParts01 & mask) > 0)
1718 return true;
1719 }
1720 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1721 {
1722 offset = (part_id % 32);
1723 mask = 1 << offset;
1724
1725 if ((m_SyncParts02 & mask) > 0)
1726 return true;
1727 }
1728 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1729 {
1730 offset = (part_id % 63);
1731 mask = 1 << offset;
1732
1733 if ((m_SyncParts03 & mask) > 0)
1734 return true;
1735 }
1736
1737 return false;
1738 }
1739
1740 protected void RegisterActionForSync(int part_id, int action_id)
1741 {
1744 }
1745
1746 protected void ResetActionSyncData()
1747 {
1748 //reset data
1749 m_InteractedPartId = -1;
1751 }
1752
1753 protected void SetActionFromSyncData()
1754 {
1755 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1756 {
1759
1760 switch (build_action_id)
1761 {
1765 }
1766 }
1767 }
1768 //------
1769
1771 {
1772 string key = part.m_PartName;
1773 bool is_base = part.IsBase();
1775 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1777 {
1778 if (!part.IsBuilt())
1779 {
1780 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1781 GetConstruction().AddToConstructedParts(key);
1782 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1783
1784 if (is_base)
1785 {
1787 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1788 }
1789 }
1790 }
1791 else
1792 {
1793 if (part.IsBuilt())
1794 {
1795 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1796 GetConstruction().RemoveFromConstructedParts(key);
1797 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1798
1799 if (is_base)
1800 {
1802 AddProxyPhysics(ANIMATION_DEPLOYED);
1803 }
1804 }
1805 }
1806
1807 //check slot lock for material attachments
1808 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1809 }
1810
1811 //set construction parts based on synchronized data
1813 {
1816
1817 for (int i = 0; i < construction_parts.Count(); ++i)
1818 {
1819 string key = construction_parts.GetKey(i);
1822 }
1823
1824 //regenerate navmesh
1825 UpdateNavmesh();
1826 }
1827
1829 {
1832
1833 for (int i = 0; i < construction_parts.Count(); ++i)
1834 {
1835 string key = construction_parts.GetKey(i);
1837
1838 if (value.GetId() == id)
1839 return value;
1840 }
1841
1842 return NULL;
1843 }
1844 //
1845
1846 //Base
1847 bool HasBase()
1848 {
1849 return m_HasBase;
1850 }
1851
1852 void SetBaseState(bool has_base)
1853 {
1855 }
1856
1857 override bool IsDeployable()
1858 {
1859 return true;
1860 }
1861
1862 bool IsOpened()
1863 {
1864 return false;
1865 }
1866
1867 //--- CONSTRUCTION KIT
1869 {
1873
1874 return construction_kit;
1875 }
1876
1878 {
1879 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1882 }
1883
1884 protected vector GetKitSpawnPosition()
1885 {
1886 return GetPosition();
1887 }
1888
1889 protected string GetConstructionKitType()
1890 {
1891 return "";
1892 }
1893
1895 {
1897 GetGame().ObjectDelete(construction_kit);
1898 }
1899
1900 //--- CONSTRUCTION
1901 void DestroyConstruction()
1902 {
1903 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1904 GetGame().ObjectDelete(this);
1905 }
1906
1907 // --- EVENTS
1908 override void OnStoreSave(ParamsWriteContext ctx)
1909 {
1910 super.OnStoreSave(ctx);
1911
1912 //sync parts 01
1913 ctx.Write(m_SyncParts01);
1914 ctx.Write(m_SyncParts02);
1915 ctx.Write(m_SyncParts03);
1916
1917 ctx.Write(m_HasBase);
1918 }
1919
1920 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1921 {
1922 if (!super.OnStoreLoad(ctx, version))
1923 return false;
1924
1925 //--- Base building data ---
1926 //Restore synced parts data
1927 if (!ctx.Read(m_SyncParts01))
1928 {
1929 m_SyncParts01 = 0; //set default
1930 return false;
1931 }
1932 if (!ctx.Read(m_SyncParts02))
1933 {
1934 m_SyncParts02 = 0; //set default
1935 return false;
1936 }
1937 if (!ctx.Read(m_SyncParts03))
1938 {
1939 m_SyncParts03 = 0; //set default
1940 return false;
1941 }
1942
1943 //has base
1944 if (!ctx.Read(m_HasBase))
1945 {
1946 m_HasBase = false;
1947 return false;
1948 }
1949 //---
1950
1951 return true;
1952 }
1953
1954 override void AfterStoreLoad()
1955 {
1956 super.AfterStoreLoad();
1957
1960 }
1961
1963 {
1964 //update server data
1966
1967 //set base state
1968 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1969 SetBaseState(construction_part.IsBuilt()) ;
1970
1971 //synchronize after load
1973 }
1974
1975 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1976 {
1978 return;
1979
1980 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1981
1982 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1983 return;
1984
1986 string part_name = zone;
1987 part_name.ToLower();
1988
1990 {
1992
1993 if (construction_part && construction.IsPartConstructed(part_name))
1994 {
1995 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1996 construction.DestroyConnectedParts(part_name);
1997 }
1998
1999 //barbed wire handling (hack-ish)
2000 if (part_name.Contains("barbed"))
2001 {
2002 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2003 if (barbed_wire)
2004 barbed_wire.SetMountedState(false);
2005 }
2006 }
2007 }
2008
2009 override void EEOnAfterLoad()
2010 {
2012 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2013
2014 super.EEOnAfterLoad();
2015 }
2016
2017 override void EEInit()
2018 {
2019 super.EEInit();
2020
2021 // init visuals and physics
2022 InitBaseState();
2023
2024 //debug
2025#ifdef DEVELOPER
2027#endif
2028 }
2029
2030 override void EEItemAttached(EntityAI item, string slot_name)
2031 {
2032 super.EEItemAttached(item, slot_name);
2033
2035 UpdateVisuals();
2037 }
2038
2039 override void EEItemDetached(EntityAI item, string slot_name)
2040 {
2041 super.EEItemDetached(item, slot_name);
2042
2043 UpdateVisuals();
2045 }
2046
2047 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2048 {
2050 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2051
2054 }
2055
2056 //ignore out of reach condition
2057 override bool IgnoreOutOfReachCondition()
2058 {
2059 return true;
2060 }
2061
2062 //CONSTRUCTION EVENTS
2063 //Build
2064 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2065 {
2067
2068 //check base state
2069 if (construtionPart.IsBase())
2070 {
2071 SetBaseState(true);
2072
2073 //spawn kit
2075 }
2076
2077 //register constructed parts for synchronization
2079
2080 //register action that was performed on part
2082
2083 //synchronize
2085
2086 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2087
2088 UpdateNavmesh();
2089
2090 //update visuals
2091 UpdateVisuals();
2092
2093 //reset action sync data
2094 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2095 }
2096
2097 void OnPartBuiltClient(string part_name, int action_id)
2098 {
2099 //play sound
2101 }
2102
2103 //Dismantle
2105 {
2106 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2108
2109 //register constructed parts for synchronization
2111
2112 //register action that was performed on part
2114
2115 //synchronize
2117
2118 // server part of sync, client will be synced from SetPartsFromSyncData
2120
2121 UpdateNavmesh();
2122
2123 //update visuals
2124 UpdateVisuals();
2125
2126 //reset action sync data
2127 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2128
2129 //check base state
2130 if (construtionPart.IsBase())
2131 {
2132 //Destroy construction
2133 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2134 }
2135 }
2136
2138 {
2139 //play sound
2141 }
2142
2143 //Destroy
2145 {
2146 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2148
2149 //register constructed parts for synchronization
2151
2152 //register action that was performed on part
2154
2155 //synchronize
2157
2158 // server part of sync, client will be synced from SetPartsFromSyncData
2160
2161 UpdateNavmesh();
2162
2163 //update visuals
2164 UpdateVisuals();
2165
2166 //reset action sync data
2167 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2168
2169 //check base state
2170 if (construtionPart.IsBase())
2171 {
2172 //Destroy construction
2173 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2174 }
2175 }
2176
2177 void OnPartDestroyedClient(string part_name, int action_id)
2178 {
2179 //play sound
2181 }
2182
2183 // --- UPDATE
2184 void InitBaseState()
2185 {
2186 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2187
2188 InitVisuals();
2189 UpdateNavmesh(); //regenerate navmesh
2190 GetConstruction().InitBaseState();
2191 }
2192
2193 void InitVisuals()
2194 {
2195 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2196 //check base
2197 if (!HasBase())
2198 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2199 else
2200 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2201
2202 GetConstruction().UpdateVisuals();
2203 }
2204
2205 void UpdateVisuals()
2206 {
2208
2210 foreach (string slotName : attachmentSlots)
2212
2213 //check base
2214 if (!HasBase())
2215 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2216 else
2217 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2218
2219 GetConstruction().UpdateVisuals();
2220 }
2221
2223 {
2224 string slotNameMounted = slot_name + "_Mounted";
2225 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2226
2227 if (attachment)
2228 {
2229 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2230 if (barbedWire && barbedWire.IsMounted())
2232 else
2234
2235 if (is_locked)
2236 {
2237 SetAnimationPhase(slotNameMounted, 0);
2238 SetAnimationPhase(slot_name, 1);
2239 }
2240 else
2241 {
2242 SetAnimationPhase(slotNameMounted, 1);
2243 SetAnimationPhase(slot_name, 0);
2244 }
2245 }
2246 else
2247 {
2248 SetAnimationPhase(slotNameMounted, 1);
2249 SetAnimationPhase(slot_name, 1);
2250
2252 }
2253 }
2254
2255 // avoid calling this function on frequent occasions, it's a massive performance hit
2256 void UpdatePhysics()
2257 {
2259 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2260
2263
2265 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2266
2267 foreach (string slotName : attachmentSlots)
2269
2270 //check base
2271 if (!HasBase())
2272 {
2274 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2275
2276 AddProxyPhysics(ANIMATION_DEPLOYED);
2277 }
2278 else
2279 {
2281 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2282
2283 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2284 }
2285
2286 GetConstruction().UpdatePhysics();
2287 UpdateNavmesh();
2288 }
2289
2291 {
2292 //checks for invalid appends; hotfix
2293 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2294 return;
2295 //----------------------------------
2296 string slot_name_mounted = slot_name + "_Mounted";
2297 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2298
2299 //remove proxy physics
2300 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2301 RemoveProxyPhysics(slot_name_mounted);
2302 RemoveProxyPhysics(slot_name);
2303
2304 if (attachment)
2305 {
2306 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2307 if (is_locked)
2308 {
2309 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2310 AddProxyPhysics(slot_name_mounted);
2311 }
2312 else
2313 {
2314 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2315 AddProxyPhysics(slot_name);
2316 }
2317 }
2318 }
2319
2320 protected void UpdateNavmesh()
2321 {
2322 SetAffectPathgraph(true, false);
2323 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2324 }
2325
2326 override bool CanUseConstruction()
2327 {
2328 return true;
2329 }
2330
2331 override bool CanUseConstructionBuild()
2332 {
2333 return true;
2334 }
2335
2337 {
2338 if (attachment)
2339 {
2341 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2342
2343 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2344 }
2345
2346 return false;
2347 }
2348
2349 protected bool IsAttachmentSlotLocked(string slot_name)
2350 {
2351 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2352 }
2353
2354 //--- ATTACHMENT SLOTS
2356 {
2357 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2358 if (GetGame().ConfigIsExisting(config_path))
2359 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2360 }
2361
2363 {
2364 return true;
2365 }
2366
2367 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2368 {
2369 return true;
2370 }
2371
2372 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2373 {
2374 return true;
2375 }
2376
2377 // --- INIT
2378 void ConstructionInit()
2379 {
2380 if (!m_Construction)
2381 m_Construction = new Construction(this);
2382
2383 GetConstruction().Init();
2384 }
2385
2387 {
2388 return m_Construction;
2389 }
2390
2391 //--- INVENTORY/ATTACHMENTS CONDITIONS
2392 //attachments
2394 {
2395 return super.CanReceiveAttachment(attachment, slotId);
2396 }
2397
2399 {
2400 int attachment_count = GetInventory().AttachmentCount();
2401 if (attachment_count > 0)
2402 {
2403 if (HasBase() && attachment_count == 1)
2404 return false;
2405
2406 return true;
2407 }
2408
2409 return false;
2410 }
2411
2412 override bool ShowZonesHealth()
2413 {
2414 return true;
2415 }
2416
2417 //this into/outo parent.Cargo
2418 override bool CanPutInCargo(EntityAI parent)
2419 {
2420 return false;
2421 }
2422
2423 override bool CanRemoveFromCargo(EntityAI parent)
2424 {
2425 return false;
2426 }
2427
2428 //hands
2429 override bool CanPutIntoHands(EntityAI parent)
2430 {
2431 return false;
2432 }
2433
2434 //--- ACTION CONDITIONS
2435 //direction
2436 override bool IsFacingPlayer(PlayerBase player, string selection)
2437 {
2438 return true;
2439 }
2440
2441 override bool IsPlayerInside(PlayerBase player, string selection)
2442 {
2443 return true;
2444 }
2445
2448 {
2449 return false;
2450 }
2451
2452 //camera direction check
2453 bool IsFacingCamera(string selection)
2454 {
2455 return true;
2456 }
2457
2458 //roof check
2460 {
2461 return false;
2462 }
2463
2464 //selection->player distance check
2465 bool HasProperDistance(string selection, PlayerBase player)
2466 {
2467 return true;
2468 }
2469
2470 //folding
2472 {
2473 if (HasBase() || GetInventory().AttachmentCount() > 0)
2474 return false;
2475
2476 return true;
2477 }
2478
2480 {
2483
2484 return item;
2485 }
2486
2487 //Damage triggers (barbed wire)
2488 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2489 {
2490 if (GetGame() && GetGame().IsServer())
2491 {
2492 //destroy area damage if some already exists
2494
2495 //create new area damage
2497 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2498
2499 vector min_max[2];
2500 if (MemoryPointExists(slot_name + "_min"))
2501 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2502 if (MemoryPointExists(slot_name + "_max"))
2503 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2504
2505 //get proper trigger extents (min<max)
2506 vector extents[2];
2507 GetConstruction().GetTriggerExtents(min_max, extents);
2508
2509 //get box center
2510 vector center;
2511 center = GetConstruction().GetBoxCenter(min_max);
2512 center = ModelToWorld(center);
2513
2514 //rotate center if needed
2517
2518 areaDamage.SetExtents(extents[0], extents[1]);
2519 areaDamage.SetAreaPosition(center);
2520 areaDamage.SetAreaOrientation(orientation);
2521 areaDamage.SetLoopInterval(1.0);
2522 areaDamage.SetDeferDuration(0.2);
2523 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2524 areaDamage.SetAmmoName("BarbedWireHit");
2525 areaDamage.Spawn();
2526
2528 }
2529 }
2530
2532 {
2533 if (angle_deg != 0)
2534 {
2535 //orientation
2537
2538 //center
2540 if (MemoryPointExists("rotate_axis"))
2541 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2544 center[0] = r_center_x;
2545 center[2] = r_center_z;
2546 }
2547 }
2548
2549 void DestroyAreaDamage(string slot_name)
2550 {
2551 if (GetGame() && GetGame().IsServer())
2552 {
2555 {
2556 if (areaDamage)
2557 areaDamage.Destroy();
2558
2560 }
2561 }
2562 }
2563
2564 override bool IsIgnoredByConstruction()
2565 {
2566 return true;
2567 }
2568
2569 //================================================================
2570 // SOUNDS
2571 //================================================================
2572 protected void SoundBuildStart(string part_name)
2573 {
2574 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2575 }
2576
2577 protected void SoundDismantleStart(string part_name)
2578 {
2579 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2580 }
2581
2582 protected void SoundDestroyStart(string part_name)
2583 {
2584 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2585 }
2586
2587 protected string GetBuildSoundByMaterial(string part_name)
2588 {
2590
2591 switch (material_type)
2592 {
2593 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2594 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2595 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2596 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2597 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2598 }
2599
2600 return "";
2601 }
2602
2603 protected string GetDismantleSoundByMaterial(string part_name)
2604 {
2606
2607 switch (material_type)
2608 {
2609 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2610 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2611 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2612 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2613 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2614 }
2615
2616 return "";
2617 }
2618
2619 //misc
2621 {
2622 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2623 {
2624 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2626 SetHealth(slot_name, "Health", item.GetHealth());
2627 }
2628 }
2629
2630 override int GetDamageSystemVersionChange()
2631 {
2632 return 111;
2633 }
2634
2635 override void SetActions()
2636 {
2637 super.SetActions();
2638
2640 //AddAction(ActionTakeHybridAttachment);
2641 //AddAction(ActionTakeHybridAttachmentToHands);
2644 }
2645
2646 //================================================================
2647 // DEBUG
2648 //================================================================
2649 protected void DebugCustomState()
2650 {
2651 }
2652
2655 {
2656 return null;
2657 }
2658
2659 override void OnDebugSpawn()
2660 {
2661 FullyBuild();
2662 }
2663
2664 void FullyBuild()
2665 {
2667 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2668
2669 Man p;
2670
2671#ifdef SERVER
2673 GetGame().GetWorld().GetPlayerList(players);
2674 if (players.Count())
2675 p = players[0];
2676#else
2677 p = GetGame().GetPlayer();
2678#endif
2679
2680 foreach (ConstructionPart part : parts)
2681 {
2682 bool excluded = false;
2683 string partName = part.GetPartName();
2684 if (excludes)
2685 {
2686 foreach (string exclude : excludes)
2687 {
2688 if (partName.Contains(exclude))
2689 {
2690 excluded = true;
2691 break;
2692 }
2693 }
2694 }
2695
2696 if (!excluded)
2698 }
2699
2700 GetConstruction().UpdateVisuals();
2701 }
2702}
2703
2704void bsbDebugPrint(string s)
2705{
2706#ifdef BSB_DEBUG
2707 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2708#else
2709 //Print("" + s); // comment/uncomment to hide/see debug logs
2710#endif
2711}
2712void bsbDebugSpam(string s)
2713{
2714#ifdef BSB_DEBUG_SPAM
2715 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2716#else
2717 //Print("" + s); // comment/uncomment to hide/see debug logs
2718#endif
2719}

◆ HasAttachmentsBesidesBase()

bool bsbDebugPrint::HasAttachmentsBesidesBase ( )
protected

Definition at line 2043 of file BaseBuildingBase.c.

2045{
2046 const string ANIMATION_DEPLOYED = "Deployed";
2047
2048 float m_ConstructionKitHealth; //stored health value for used construction kit
2049
2051
2052 bool m_HasBase;
2053 //variables for synchronization of base building parts (2x31 is the current limit)
2054 int m_SyncParts01; //synchronization for already built parts (31 parts)
2055 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2056 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2057 int m_InteractedPartId; //construction part id that an action was performed on
2058 int m_PerformedActionId; //action id that was performed on a construction part
2059
2060 //Sounds
2061 //build
2062 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2063 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2064 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2065 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2066 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2067 //dismantle
2068 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2069 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2070 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2071 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2072 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2073
2074 protected EffectSound m_Sound;
2075
2079
2080 // Constructor
2081 void BaseBuildingBase()
2082 {
2084
2085 //synchronized variables
2086 RegisterNetSyncVariableInt("m_SyncParts01");
2087 RegisterNetSyncVariableInt("m_SyncParts02");
2088 RegisterNetSyncVariableInt("m_SyncParts03");
2089 RegisterNetSyncVariableInt("m_InteractedPartId");
2090 RegisterNetSyncVariableInt("m_PerformedActionId");
2091 RegisterNetSyncVariableBool("m_HasBase");
2092
2093 //Construction init
2095
2096 if (ConfigIsExisting("hybridAttachments"))
2097 {
2099 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2100 }
2101 if (ConfigIsExisting("mountables"))
2102 {
2104 ConfigGetTextArray("mountables", m_Mountables);
2105 }
2106
2107 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2108 }
2109
2110 override void EEDelete(EntityAI parent)
2111 {
2112 super.EEDelete(parent);
2113
2114 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2116
2117 }
2118
2119 override string GetInvulnerabilityTypeString()
2120 {
2121 return "disableBaseDamage";
2122 }
2123
2124 override bool CanObstruct()
2125 {
2126 return true;
2127 }
2128
2129 override int GetHideIconMask()
2130 {
2131 return EInventoryIconVisibility.HIDE_VICINITY;
2132 }
2133
2134 // --- SYNCHRONIZATION
2136 {
2137 if (GetGame().IsServer())
2138 SetSynchDirty();
2139 }
2140
2141 override void OnVariablesSynchronized()
2142 {
2143 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2144 super.OnVariablesSynchronized();
2145
2146 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2147 }
2148
2149 protected void OnSynchronizedClient()
2150 {
2151 //update parts
2153
2154 //update action on part
2156
2157 //update visuals (client)
2158 UpdateVisuals();
2159 }
2160
2161 //parts synchronization
2163 {
2164 //part_id must starts from index = 1
2165 int offset;
2166 int mask;
2167
2168 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2169 {
2170 offset = part_id - 1;
2171 mask = 1 << offset;
2172
2174 }
2175 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2176 {
2177 offset = (part_id % 32);
2178 mask = 1 << offset;
2179
2181 }
2182 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2183 {
2184 offset = (part_id % 63);
2185 mask = 1 << offset;
2186
2188 }
2189 }
2190
2192 {
2193 //part_id must starts from index = 1
2194 int offset;
2195 int mask;
2196
2197 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2198 {
2199 offset = part_id - 1;
2200 mask = 1 << offset;
2201
2203 }
2204 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2205 {
2206 offset = (part_id % 32);
2207 mask = 1 << offset;
2208
2210 }
2211 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2212 {
2213 offset = (part_id % 63);
2214 mask = 1 << offset;
2215
2217 }
2218 }
2219
2221 {
2222 //part_id must starts from index = 1
2223 int offset;
2224 int mask;
2225
2226 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2227 {
2228 offset = part_id - 1;
2229 mask = 1 << offset;
2230
2231 if ((m_SyncParts01 & mask) > 0)
2232 return true;
2233 }
2234 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2235 {
2236 offset = (part_id % 32);
2237 mask = 1 << offset;
2238
2239 if ((m_SyncParts02 & mask) > 0)
2240 return true;
2241 }
2242 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2243 {
2244 offset = (part_id % 63);
2245 mask = 1 << offset;
2246
2247 if ((m_SyncParts03 & mask) > 0)
2248 return true;
2249 }
2250
2251 return false;
2252 }
2253
2254 protected void RegisterActionForSync(int part_id, int action_id)
2255 {
2258 }
2259
2260 protected void ResetActionSyncData()
2261 {
2262 //reset data
2263 m_InteractedPartId = -1;
2265 }
2266
2267 protected void SetActionFromSyncData()
2268 {
2269 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2270 {
2273
2274 switch (build_action_id)
2275 {
2279 }
2280 }
2281 }
2282 //------
2283
2285 {
2286 string key = part.m_PartName;
2287 bool is_base = part.IsBase();
2289 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2291 {
2292 if (!part.IsBuilt())
2293 {
2294 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2295 GetConstruction().AddToConstructedParts(key);
2296 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2297
2298 if (is_base)
2299 {
2301 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2302 }
2303 }
2304 }
2305 else
2306 {
2307 if (part.IsBuilt())
2308 {
2309 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2310 GetConstruction().RemoveFromConstructedParts(key);
2311 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2312
2313 if (is_base)
2314 {
2316 AddProxyPhysics(ANIMATION_DEPLOYED);
2317 }
2318 }
2319 }
2320
2321 //check slot lock for material attachments
2322 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2323 }
2324
2325 //set construction parts based on synchronized data
2327 {
2330
2331 for (int i = 0; i < construction_parts.Count(); ++i)
2332 {
2333 string key = construction_parts.GetKey(i);
2336 }
2337
2338 //regenerate navmesh
2339 UpdateNavmesh();
2340 }
2341
2343 {
2346
2347 for (int i = 0; i < construction_parts.Count(); ++i)
2348 {
2349 string key = construction_parts.GetKey(i);
2351
2352 if (value.GetId() == id)
2353 return value;
2354 }
2355
2356 return NULL;
2357 }
2358 //
2359
2360 //Base
2361 bool HasBase()
2362 {
2363 return m_HasBase;
2364 }
2365
2366 void SetBaseState(bool has_base)
2367 {
2369 }
2370
2371 override bool IsDeployable()
2372 {
2373 return true;
2374 }
2375
2376 bool IsOpened()
2377 {
2378 return false;
2379 }
2380
2381 //--- CONSTRUCTION KIT
2383 {
2387
2388 return construction_kit;
2389 }
2390
2392 {
2393 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2396 }
2397
2398 protected vector GetKitSpawnPosition()
2399 {
2400 return GetPosition();
2401 }
2402
2403 protected string GetConstructionKitType()
2404 {
2405 return "";
2406 }
2407
2409 {
2411 GetGame().ObjectDelete(construction_kit);
2412 }
2413
2414 //--- CONSTRUCTION
2415 void DestroyConstruction()
2416 {
2417 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2418 GetGame().ObjectDelete(this);
2419 }
2420
2421 // --- EVENTS
2422 override void OnStoreSave(ParamsWriteContext ctx)
2423 {
2424 super.OnStoreSave(ctx);
2425
2426 //sync parts 01
2427 ctx.Write(m_SyncParts01);
2428 ctx.Write(m_SyncParts02);
2429 ctx.Write(m_SyncParts03);
2430
2431 ctx.Write(m_HasBase);
2432 }
2433
2434 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2435 {
2436 if (!super.OnStoreLoad(ctx, version))
2437 return false;
2438
2439 //--- Base building data ---
2440 //Restore synced parts data
2441 if (!ctx.Read(m_SyncParts01))
2442 {
2443 m_SyncParts01 = 0; //set default
2444 return false;
2445 }
2446 if (!ctx.Read(m_SyncParts02))
2447 {
2448 m_SyncParts02 = 0; //set default
2449 return false;
2450 }
2451 if (!ctx.Read(m_SyncParts03))
2452 {
2453 m_SyncParts03 = 0; //set default
2454 return false;
2455 }
2456
2457 //has base
2458 if (!ctx.Read(m_HasBase))
2459 {
2460 m_HasBase = false;
2461 return false;
2462 }
2463 //---
2464
2465 return true;
2466 }
2467
2468 override void AfterStoreLoad()
2469 {
2470 super.AfterStoreLoad();
2471
2474 }
2475
2477 {
2478 //update server data
2480
2481 //set base state
2482 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2483 SetBaseState(construction_part.IsBuilt()) ;
2484
2485 //synchronize after load
2487 }
2488
2489 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2490 {
2492 return;
2493
2494 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2495
2496 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2497 return;
2498
2500 string part_name = zone;
2501 part_name.ToLower();
2502
2504 {
2506
2507 if (construction_part && construction.IsPartConstructed(part_name))
2508 {
2509 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2510 construction.DestroyConnectedParts(part_name);
2511 }
2512
2513 //barbed wire handling (hack-ish)
2514 if (part_name.Contains("barbed"))
2515 {
2516 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2517 if (barbed_wire)
2518 barbed_wire.SetMountedState(false);
2519 }
2520 }
2521 }
2522
2523 override void EEOnAfterLoad()
2524 {
2526 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2527
2528 super.EEOnAfterLoad();
2529 }
2530
2531 override void EEInit()
2532 {
2533 super.EEInit();
2534
2535 // init visuals and physics
2536 InitBaseState();
2537
2538 //debug
2539#ifdef DEVELOPER
2541#endif
2542 }
2543
2544 override void EEItemAttached(EntityAI item, string slot_name)
2545 {
2546 super.EEItemAttached(item, slot_name);
2547
2549 UpdateVisuals();
2551 }
2552
2553 override void EEItemDetached(EntityAI item, string slot_name)
2554 {
2555 super.EEItemDetached(item, slot_name);
2556
2557 UpdateVisuals();
2559 }
2560
2561 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2562 {
2564 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2565
2568 }
2569
2570 //ignore out of reach condition
2571 override bool IgnoreOutOfReachCondition()
2572 {
2573 return true;
2574 }
2575
2576 //CONSTRUCTION EVENTS
2577 //Build
2578 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2579 {
2581
2582 //check base state
2583 if (construtionPart.IsBase())
2584 {
2585 SetBaseState(true);
2586
2587 //spawn kit
2589 }
2590
2591 //register constructed parts for synchronization
2593
2594 //register action that was performed on part
2596
2597 //synchronize
2599
2600 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2601
2602 UpdateNavmesh();
2603
2604 //update visuals
2605 UpdateVisuals();
2606
2607 //reset action sync data
2608 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2609 }
2610
2611 void OnPartBuiltClient(string part_name, int action_id)
2612 {
2613 //play sound
2615 }
2616
2617 //Dismantle
2619 {
2620 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2622
2623 //register constructed parts for synchronization
2625
2626 //register action that was performed on part
2628
2629 //synchronize
2631
2632 // server part of sync, client will be synced from SetPartsFromSyncData
2634
2635 UpdateNavmesh();
2636
2637 //update visuals
2638 UpdateVisuals();
2639
2640 //reset action sync data
2641 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2642
2643 //check base state
2644 if (construtionPart.IsBase())
2645 {
2646 //Destroy construction
2647 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2648 }
2649 }
2650
2652 {
2653 //play sound
2655 }
2656
2657 //Destroy
2659 {
2660 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2662
2663 //register constructed parts for synchronization
2665
2666 //register action that was performed on part
2668
2669 //synchronize
2671
2672 // server part of sync, client will be synced from SetPartsFromSyncData
2674
2675 UpdateNavmesh();
2676
2677 //update visuals
2678 UpdateVisuals();
2679
2680 //reset action sync data
2681 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2682
2683 //check base state
2684 if (construtionPart.IsBase())
2685 {
2686 //Destroy construction
2687 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2688 }
2689 }
2690
2691 void OnPartDestroyedClient(string part_name, int action_id)
2692 {
2693 //play sound
2695 }
2696
2697 // --- UPDATE
2698 void InitBaseState()
2699 {
2700 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2701
2702 InitVisuals();
2703 UpdateNavmesh(); //regenerate navmesh
2704 GetConstruction().InitBaseState();
2705 }
2706
2707 void InitVisuals()
2708 {
2709 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2710 //check base
2711 if (!HasBase())
2712 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2713 else
2714 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2715
2716 GetConstruction().UpdateVisuals();
2717 }
2718
2719 void UpdateVisuals()
2720 {
2722
2724 foreach (string slotName : attachmentSlots)
2726
2727 //check base
2728 if (!HasBase())
2729 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2730 else
2731 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2732
2733 GetConstruction().UpdateVisuals();
2734 }
2735
2737 {
2738 string slotNameMounted = slot_name + "_Mounted";
2739 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2740
2741 if (attachment)
2742 {
2743 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2744 if (barbedWire && barbedWire.IsMounted())
2746 else
2748
2749 if (is_locked)
2750 {
2751 SetAnimationPhase(slotNameMounted, 0);
2752 SetAnimationPhase(slot_name, 1);
2753 }
2754 else
2755 {
2756 SetAnimationPhase(slotNameMounted, 1);
2757 SetAnimationPhase(slot_name, 0);
2758 }
2759 }
2760 else
2761 {
2762 SetAnimationPhase(slotNameMounted, 1);
2763 SetAnimationPhase(slot_name, 1);
2764
2766 }
2767 }
2768
2769 // avoid calling this function on frequent occasions, it's a massive performance hit
2770 void UpdatePhysics()
2771 {
2773 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2774
2777
2779 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2780
2781 foreach (string slotName : attachmentSlots)
2783
2784 //check base
2785 if (!HasBase())
2786 {
2788 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2789
2790 AddProxyPhysics(ANIMATION_DEPLOYED);
2791 }
2792 else
2793 {
2795 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2796
2797 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2798 }
2799
2800 GetConstruction().UpdatePhysics();
2801 UpdateNavmesh();
2802 }
2803
2805 {
2806 //checks for invalid appends; hotfix
2807 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2808 return;
2809 //----------------------------------
2810 string slot_name_mounted = slot_name + "_Mounted";
2811 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2812
2813 //remove proxy physics
2814 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2815 RemoveProxyPhysics(slot_name_mounted);
2816 RemoveProxyPhysics(slot_name);
2817
2818 if (attachment)
2819 {
2820 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2821 if (is_locked)
2822 {
2823 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2824 AddProxyPhysics(slot_name_mounted);
2825 }
2826 else
2827 {
2828 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2829 AddProxyPhysics(slot_name);
2830 }
2831 }
2832 }
2833
2834 protected void UpdateNavmesh()
2835 {
2836 SetAffectPathgraph(true, false);
2837 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2838 }
2839
2840 override bool CanUseConstruction()
2841 {
2842 return true;
2843 }
2844
2845 override bool CanUseConstructionBuild()
2846 {
2847 return true;
2848 }
2849
2851 {
2852 if (attachment)
2853 {
2855 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2856
2857 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2858 }
2859
2860 return false;
2861 }
2862
2863 protected bool IsAttachmentSlotLocked(string slot_name)
2864 {
2865 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2866 }
2867
2868 //--- ATTACHMENT SLOTS
2870 {
2871 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2872 if (GetGame().ConfigIsExisting(config_path))
2873 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2874 }
2875
2877 {
2878 return true;
2879 }
2880
2881 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2882 {
2883 return true;
2884 }
2885
2886 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2887 {
2888 return true;
2889 }
2890
2891 // --- INIT
2892 void ConstructionInit()
2893 {
2894 if (!m_Construction)
2895 m_Construction = new Construction(this);
2896
2897 GetConstruction().Init();
2898 }
2899
2901 {
2902 return m_Construction;
2903 }
2904
2905 //--- INVENTORY/ATTACHMENTS CONDITIONS
2906 //attachments
2908 {
2909 return super.CanReceiveAttachment(attachment, slotId);
2910 }
2911
2913 {
2914 int attachment_count = GetInventory().AttachmentCount();
2915 if (attachment_count > 0)
2916 {
2917 if (HasBase() && attachment_count == 1)
2918 return false;
2919
2920 return true;
2921 }
2922
2923 return false;
2924 }
2925
2926 override bool ShowZonesHealth()
2927 {
2928 return true;
2929 }
2930
2931 //this into/outo parent.Cargo
2932 override bool CanPutInCargo(EntityAI parent)
2933 {
2934 return false;
2935 }
2936
2937 override bool CanRemoveFromCargo(EntityAI parent)
2938 {
2939 return false;
2940 }
2941
2942 //hands
2943 override bool CanPutIntoHands(EntityAI parent)
2944 {
2945 return false;
2946 }
2947
2948 //--- ACTION CONDITIONS
2949 //direction
2950 override bool IsFacingPlayer(PlayerBase player, string selection)
2951 {
2952 return true;
2953 }
2954
2955 override bool IsPlayerInside(PlayerBase player, string selection)
2956 {
2957 return true;
2958 }
2959
2962 {
2963 return false;
2964 }
2965
2966 //camera direction check
2967 bool IsFacingCamera(string selection)
2968 {
2969 return true;
2970 }
2971
2972 //roof check
2974 {
2975 return false;
2976 }
2977
2978 //selection->player distance check
2979 bool HasProperDistance(string selection, PlayerBase player)
2980 {
2981 return true;
2982 }
2983
2984 //folding
2986 {
2987 if (HasBase() || GetInventory().AttachmentCount() > 0)
2988 return false;
2989
2990 return true;
2991 }
2992
2994 {
2997
2998 return item;
2999 }
3000
3001 //Damage triggers (barbed wire)
3002 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3003 {
3004 if (GetGame() && GetGame().IsServer())
3005 {
3006 //destroy area damage if some already exists
3008
3009 //create new area damage
3011 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3012
3013 vector min_max[2];
3014 if (MemoryPointExists(slot_name + "_min"))
3015 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3016 if (MemoryPointExists(slot_name + "_max"))
3017 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3018
3019 //get proper trigger extents (min<max)
3020 vector extents[2];
3021 GetConstruction().GetTriggerExtents(min_max, extents);
3022
3023 //get box center
3024 vector center;
3025 center = GetConstruction().GetBoxCenter(min_max);
3026 center = ModelToWorld(center);
3027
3028 //rotate center if needed
3031
3032 areaDamage.SetExtents(extents[0], extents[1]);
3033 areaDamage.SetAreaPosition(center);
3034 areaDamage.SetAreaOrientation(orientation);
3035 areaDamage.SetLoopInterval(1.0);
3036 areaDamage.SetDeferDuration(0.2);
3037 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3038 areaDamage.SetAmmoName("BarbedWireHit");
3039 areaDamage.Spawn();
3040
3042 }
3043 }
3044
3046 {
3047 if (angle_deg != 0)
3048 {
3049 //orientation
3051
3052 //center
3054 if (MemoryPointExists("rotate_axis"))
3055 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3058 center[0] = r_center_x;
3059 center[2] = r_center_z;
3060 }
3061 }
3062
3063 void DestroyAreaDamage(string slot_name)
3064 {
3065 if (GetGame() && GetGame().IsServer())
3066 {
3069 {
3070 if (areaDamage)
3071 areaDamage.Destroy();
3072
3074 }
3075 }
3076 }
3077
3078 override bool IsIgnoredByConstruction()
3079 {
3080 return true;
3081 }
3082
3083 //================================================================
3084 // SOUNDS
3085 //================================================================
3086 protected void SoundBuildStart(string part_name)
3087 {
3088 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3089 }
3090
3091 protected void SoundDismantleStart(string part_name)
3092 {
3093 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3094 }
3095
3096 protected void SoundDestroyStart(string part_name)
3097 {
3098 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3099 }
3100
3101 protected string GetBuildSoundByMaterial(string part_name)
3102 {
3104
3105 switch (material_type)
3106 {
3107 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3108 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3109 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3110 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3111 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3112 }
3113
3114 return "";
3115 }
3116
3117 protected string GetDismantleSoundByMaterial(string part_name)
3118 {
3120
3121 switch (material_type)
3122 {
3123 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3124 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3125 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3126 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3127 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3128 }
3129
3130 return "";
3131 }
3132
3133 //misc
3135 {
3136 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3137 {
3138 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3140 SetHealth(slot_name, "Health", item.GetHealth());
3141 }
3142 }
3143
3144 override int GetDamageSystemVersionChange()
3145 {
3146 return 111;
3147 }
3148
3149 override void SetActions()
3150 {
3151 super.SetActions();
3152
3154 //AddAction(ActionTakeHybridAttachment);
3155 //AddAction(ActionTakeHybridAttachmentToHands);
3158 }
3159
3160 //================================================================
3161 // DEBUG
3162 //================================================================
3163 protected void DebugCustomState()
3164 {
3165 }
3166
3169 {
3170 return null;
3171 }
3172
3173 override void OnDebugSpawn()
3174 {
3175 FullyBuild();
3176 }
3177
3178 void FullyBuild()
3179 {
3181 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3182
3183 Man p;
3184
3185#ifdef SERVER
3187 GetGame().GetWorld().GetPlayerList(players);
3188 if (players.Count())
3189 p = players[0];
3190#else
3191 p = GetGame().GetPlayer();
3192#endif
3193
3194 foreach (ConstructionPart part : parts)
3195 {
3196 bool excluded = false;
3197 string partName = part.GetPartName();
3198 if (excludes)
3199 {
3200 foreach (string exclude : excludes)
3201 {
3202 if (partName.Contains(exclude))
3203 {
3204 excluded = true;
3205 break;
3206 }
3207 }
3208 }
3209
3210 if (!excluded)
3212 }
3213
3214 GetConstruction().UpdateVisuals();
3215 }
3216}
3217
3218void bsbDebugPrint(string s)
3219{
3220#ifdef BSB_DEBUG
3221 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3222#else
3223 //Print("" + s); // comment/uncomment to hide/see debug logs
3224#endif
3225}
3226void bsbDebugSpam(string s)
3227{
3228#ifdef BSB_DEBUG_SPAM
3229 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3230#else
3231 //Print("" + s); // comment/uncomment to hide/see debug logs
3232#endif
3233}

◆ HasBase()

bool bsbDebugPrint::HasBase ( )
protected

Definition at line 1492 of file BaseBuildingBase.c.

1494{
1495 const string ANIMATION_DEPLOYED = "Deployed";
1496
1497 float m_ConstructionKitHealth; //stored health value for used construction kit
1498
1500
1501 bool m_HasBase;
1502 //variables for synchronization of base building parts (2x31 is the current limit)
1503 int m_SyncParts01; //synchronization for already built parts (31 parts)
1504 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1505 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1506 int m_InteractedPartId; //construction part id that an action was performed on
1507 int m_PerformedActionId; //action id that was performed on a construction part
1508
1509 //Sounds
1510 //build
1511 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1512 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1513 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1514 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1515 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1516 //dismantle
1517 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1518 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1519 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1520 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1521 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1522
1523 protected EffectSound m_Sound;
1524
1528
1529 // Constructor
1530 void BaseBuildingBase()
1531 {
1533
1534 //synchronized variables
1535 RegisterNetSyncVariableInt("m_SyncParts01");
1536 RegisterNetSyncVariableInt("m_SyncParts02");
1537 RegisterNetSyncVariableInt("m_SyncParts03");
1538 RegisterNetSyncVariableInt("m_InteractedPartId");
1539 RegisterNetSyncVariableInt("m_PerformedActionId");
1540 RegisterNetSyncVariableBool("m_HasBase");
1541
1542 //Construction init
1544
1545 if (ConfigIsExisting("hybridAttachments"))
1546 {
1548 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1549 }
1550 if (ConfigIsExisting("mountables"))
1551 {
1553 ConfigGetTextArray("mountables", m_Mountables);
1554 }
1555
1556 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1557 }
1558
1559 override void EEDelete(EntityAI parent)
1560 {
1561 super.EEDelete(parent);
1562
1563 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1565
1566 }
1567
1568 override string GetInvulnerabilityTypeString()
1569 {
1570 return "disableBaseDamage";
1571 }
1572
1573 override bool CanObstruct()
1574 {
1575 return true;
1576 }
1577
1578 override int GetHideIconMask()
1579 {
1580 return EInventoryIconVisibility.HIDE_VICINITY;
1581 }
1582
1583 // --- SYNCHRONIZATION
1585 {
1586 if (GetGame().IsServer())
1587 SetSynchDirty();
1588 }
1589
1590 override void OnVariablesSynchronized()
1591 {
1592 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1593 super.OnVariablesSynchronized();
1594
1595 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1596 }
1597
1598 protected void OnSynchronizedClient()
1599 {
1600 //update parts
1602
1603 //update action on part
1605
1606 //update visuals (client)
1607 UpdateVisuals();
1608 }
1609
1610 //parts synchronization
1612 {
1613 //part_id must starts from index = 1
1614 int offset;
1615 int mask;
1616
1617 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1618 {
1619 offset = part_id - 1;
1620 mask = 1 << offset;
1621
1623 }
1624 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1625 {
1626 offset = (part_id % 32);
1627 mask = 1 << offset;
1628
1630 }
1631 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1632 {
1633 offset = (part_id % 63);
1634 mask = 1 << offset;
1635
1637 }
1638 }
1639
1641 {
1642 //part_id must starts from index = 1
1643 int offset;
1644 int mask;
1645
1646 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1647 {
1648 offset = part_id - 1;
1649 mask = 1 << offset;
1650
1652 }
1653 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1654 {
1655 offset = (part_id % 32);
1656 mask = 1 << offset;
1657
1659 }
1660 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1661 {
1662 offset = (part_id % 63);
1663 mask = 1 << offset;
1664
1666 }
1667 }
1668
1670 {
1671 //part_id must starts from index = 1
1672 int offset;
1673 int mask;
1674
1675 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1676 {
1677 offset = part_id - 1;
1678 mask = 1 << offset;
1679
1680 if ((m_SyncParts01 & mask) > 0)
1681 return true;
1682 }
1683 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1684 {
1685 offset = (part_id % 32);
1686 mask = 1 << offset;
1687
1688 if ((m_SyncParts02 & mask) > 0)
1689 return true;
1690 }
1691 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1692 {
1693 offset = (part_id % 63);
1694 mask = 1 << offset;
1695
1696 if ((m_SyncParts03 & mask) > 0)
1697 return true;
1698 }
1699
1700 return false;
1701 }
1702
1703 protected void RegisterActionForSync(int part_id, int action_id)
1704 {
1707 }
1708
1709 protected void ResetActionSyncData()
1710 {
1711 //reset data
1712 m_InteractedPartId = -1;
1714 }
1715
1716 protected void SetActionFromSyncData()
1717 {
1718 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1719 {
1722
1723 switch (build_action_id)
1724 {
1728 }
1729 }
1730 }
1731 //------
1732
1734 {
1735 string key = part.m_PartName;
1736 bool is_base = part.IsBase();
1738 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1740 {
1741 if (!part.IsBuilt())
1742 {
1743 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1744 GetConstruction().AddToConstructedParts(key);
1745 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1746
1747 if (is_base)
1748 {
1750 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1751 }
1752 }
1753 }
1754 else
1755 {
1756 if (part.IsBuilt())
1757 {
1758 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1759 GetConstruction().RemoveFromConstructedParts(key);
1760 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1761
1762 if (is_base)
1763 {
1765 AddProxyPhysics(ANIMATION_DEPLOYED);
1766 }
1767 }
1768 }
1769
1770 //check slot lock for material attachments
1771 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1772 }
1773
1774 //set construction parts based on synchronized data
1776 {
1779
1780 for (int i = 0; i < construction_parts.Count(); ++i)
1781 {
1782 string key = construction_parts.GetKey(i);
1785 }
1786
1787 //regenerate navmesh
1788 UpdateNavmesh();
1789 }
1790
1792 {
1795
1796 for (int i = 0; i < construction_parts.Count(); ++i)
1797 {
1798 string key = construction_parts.GetKey(i);
1800
1801 if (value.GetId() == id)
1802 return value;
1803 }
1804
1805 return NULL;
1806 }
1807 //
1808
1809 //Base
1810 bool HasBase()
1811 {
1812 return m_HasBase;
1813 }
1814
1815 void SetBaseState(bool has_base)
1816 {
1818 }
1819
1820 override bool IsDeployable()
1821 {
1822 return true;
1823 }
1824
1825 bool IsOpened()
1826 {
1827 return false;
1828 }
1829
1830 //--- CONSTRUCTION KIT
1832 {
1836
1837 return construction_kit;
1838 }
1839
1841 {
1842 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1845 }
1846
1847 protected vector GetKitSpawnPosition()
1848 {
1849 return GetPosition();
1850 }
1851
1852 protected string GetConstructionKitType()
1853 {
1854 return "";
1855 }
1856
1858 {
1860 GetGame().ObjectDelete(construction_kit);
1861 }
1862
1863 //--- CONSTRUCTION
1864 void DestroyConstruction()
1865 {
1866 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1867 GetGame().ObjectDelete(this);
1868 }
1869
1870 // --- EVENTS
1871 override void OnStoreSave(ParamsWriteContext ctx)
1872 {
1873 super.OnStoreSave(ctx);
1874
1875 //sync parts 01
1876 ctx.Write(m_SyncParts01);
1877 ctx.Write(m_SyncParts02);
1878 ctx.Write(m_SyncParts03);
1879
1880 ctx.Write(m_HasBase);
1881 }
1882
1883 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1884 {
1885 if (!super.OnStoreLoad(ctx, version))
1886 return false;
1887
1888 //--- Base building data ---
1889 //Restore synced parts data
1890 if (!ctx.Read(m_SyncParts01))
1891 {
1892 m_SyncParts01 = 0; //set default
1893 return false;
1894 }
1895 if (!ctx.Read(m_SyncParts02))
1896 {
1897 m_SyncParts02 = 0; //set default
1898 return false;
1899 }
1900 if (!ctx.Read(m_SyncParts03))
1901 {
1902 m_SyncParts03 = 0; //set default
1903 return false;
1904 }
1905
1906 //has base
1907 if (!ctx.Read(m_HasBase))
1908 {
1909 m_HasBase = false;
1910 return false;
1911 }
1912 //---
1913
1914 return true;
1915 }
1916
1917 override void AfterStoreLoad()
1918 {
1919 super.AfterStoreLoad();
1920
1923 }
1924
1926 {
1927 //update server data
1929
1930 //set base state
1931 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1932 SetBaseState(construction_part.IsBuilt()) ;
1933
1934 //synchronize after load
1936 }
1937
1938 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1939 {
1941 return;
1942
1943 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1944
1945 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1946 return;
1947
1949 string part_name = zone;
1950 part_name.ToLower();
1951
1953 {
1955
1956 if (construction_part && construction.IsPartConstructed(part_name))
1957 {
1958 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1959 construction.DestroyConnectedParts(part_name);
1960 }
1961
1962 //barbed wire handling (hack-ish)
1963 if (part_name.Contains("barbed"))
1964 {
1965 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1966 if (barbed_wire)
1967 barbed_wire.SetMountedState(false);
1968 }
1969 }
1970 }
1971
1972 override void EEOnAfterLoad()
1973 {
1975 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1976
1977 super.EEOnAfterLoad();
1978 }
1979
1980 override void EEInit()
1981 {
1982 super.EEInit();
1983
1984 // init visuals and physics
1985 InitBaseState();
1986
1987 //debug
1988#ifdef DEVELOPER
1990#endif
1991 }
1992
1993 override void EEItemAttached(EntityAI item, string slot_name)
1994 {
1995 super.EEItemAttached(item, slot_name);
1996
1998 UpdateVisuals();
2000 }
2001
2002 override void EEItemDetached(EntityAI item, string slot_name)
2003 {
2004 super.EEItemDetached(item, slot_name);
2005
2006 UpdateVisuals();
2008 }
2009
2010 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2011 {
2013 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2014
2017 }
2018
2019 //ignore out of reach condition
2020 override bool IgnoreOutOfReachCondition()
2021 {
2022 return true;
2023 }
2024
2025 //CONSTRUCTION EVENTS
2026 //Build
2027 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2028 {
2030
2031 //check base state
2032 if (construtionPart.IsBase())
2033 {
2034 SetBaseState(true);
2035
2036 //spawn kit
2038 }
2039
2040 //register constructed parts for synchronization
2042
2043 //register action that was performed on part
2045
2046 //synchronize
2048
2049 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2050
2051 UpdateNavmesh();
2052
2053 //update visuals
2054 UpdateVisuals();
2055
2056 //reset action sync data
2057 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2058 }
2059
2060 void OnPartBuiltClient(string part_name, int action_id)
2061 {
2062 //play sound
2064 }
2065
2066 //Dismantle
2068 {
2069 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2071
2072 //register constructed parts for synchronization
2074
2075 //register action that was performed on part
2077
2078 //synchronize
2080
2081 // server part of sync, client will be synced from SetPartsFromSyncData
2083
2084 UpdateNavmesh();
2085
2086 //update visuals
2087 UpdateVisuals();
2088
2089 //reset action sync data
2090 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2091
2092 //check base state
2093 if (construtionPart.IsBase())
2094 {
2095 //Destroy construction
2096 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2097 }
2098 }
2099
2101 {
2102 //play sound
2104 }
2105
2106 //Destroy
2108 {
2109 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2111
2112 //register constructed parts for synchronization
2114
2115 //register action that was performed on part
2117
2118 //synchronize
2120
2121 // server part of sync, client will be synced from SetPartsFromSyncData
2123
2124 UpdateNavmesh();
2125
2126 //update visuals
2127 UpdateVisuals();
2128
2129 //reset action sync data
2130 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2131
2132 //check base state
2133 if (construtionPart.IsBase())
2134 {
2135 //Destroy construction
2136 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2137 }
2138 }
2139
2140 void OnPartDestroyedClient(string part_name, int action_id)
2141 {
2142 //play sound
2144 }
2145
2146 // --- UPDATE
2147 void InitBaseState()
2148 {
2149 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2150
2151 InitVisuals();
2152 UpdateNavmesh(); //regenerate navmesh
2153 GetConstruction().InitBaseState();
2154 }
2155
2156 void InitVisuals()
2157 {
2158 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2159 //check base
2160 if (!HasBase())
2161 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2162 else
2163 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2164
2165 GetConstruction().UpdateVisuals();
2166 }
2167
2168 void UpdateVisuals()
2169 {
2171
2173 foreach (string slotName : attachmentSlots)
2175
2176 //check base
2177 if (!HasBase())
2178 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2179 else
2180 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2181
2182 GetConstruction().UpdateVisuals();
2183 }
2184
2186 {
2187 string slotNameMounted = slot_name + "_Mounted";
2188 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2189
2190 if (attachment)
2191 {
2192 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2193 if (barbedWire && barbedWire.IsMounted())
2195 else
2197
2198 if (is_locked)
2199 {
2200 SetAnimationPhase(slotNameMounted, 0);
2201 SetAnimationPhase(slot_name, 1);
2202 }
2203 else
2204 {
2205 SetAnimationPhase(slotNameMounted, 1);
2206 SetAnimationPhase(slot_name, 0);
2207 }
2208 }
2209 else
2210 {
2211 SetAnimationPhase(slotNameMounted, 1);
2212 SetAnimationPhase(slot_name, 1);
2213
2215 }
2216 }
2217
2218 // avoid calling this function on frequent occasions, it's a massive performance hit
2219 void UpdatePhysics()
2220 {
2222 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2223
2226
2228 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2229
2230 foreach (string slotName : attachmentSlots)
2232
2233 //check base
2234 if (!HasBase())
2235 {
2237 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2238
2239 AddProxyPhysics(ANIMATION_DEPLOYED);
2240 }
2241 else
2242 {
2244 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2245
2246 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2247 }
2248
2249 GetConstruction().UpdatePhysics();
2250 UpdateNavmesh();
2251 }
2252
2254 {
2255 //checks for invalid appends; hotfix
2256 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2257 return;
2258 //----------------------------------
2259 string slot_name_mounted = slot_name + "_Mounted";
2260 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2261
2262 //remove proxy physics
2263 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2264 RemoveProxyPhysics(slot_name_mounted);
2265 RemoveProxyPhysics(slot_name);
2266
2267 if (attachment)
2268 {
2269 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2270 if (is_locked)
2271 {
2272 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2273 AddProxyPhysics(slot_name_mounted);
2274 }
2275 else
2276 {
2277 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2278 AddProxyPhysics(slot_name);
2279 }
2280 }
2281 }
2282
2283 protected void UpdateNavmesh()
2284 {
2285 SetAffectPathgraph(true, false);
2286 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2287 }
2288
2289 override bool CanUseConstruction()
2290 {
2291 return true;
2292 }
2293
2294 override bool CanUseConstructionBuild()
2295 {
2296 return true;
2297 }
2298
2300 {
2301 if (attachment)
2302 {
2304 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2305
2306 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2307 }
2308
2309 return false;
2310 }
2311
2312 protected bool IsAttachmentSlotLocked(string slot_name)
2313 {
2314 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2315 }
2316
2317 //--- ATTACHMENT SLOTS
2319 {
2320 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2321 if (GetGame().ConfigIsExisting(config_path))
2322 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2323 }
2324
2326 {
2327 return true;
2328 }
2329
2330 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2331 {
2332 return true;
2333 }
2334
2335 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2336 {
2337 return true;
2338 }
2339
2340 // --- INIT
2341 void ConstructionInit()
2342 {
2343 if (!m_Construction)
2344 m_Construction = new Construction(this);
2345
2346 GetConstruction().Init();
2347 }
2348
2350 {
2351 return m_Construction;
2352 }
2353
2354 //--- INVENTORY/ATTACHMENTS CONDITIONS
2355 //attachments
2357 {
2358 return super.CanReceiveAttachment(attachment, slotId);
2359 }
2360
2362 {
2363 int attachment_count = GetInventory().AttachmentCount();
2364 if (attachment_count > 0)
2365 {
2366 if (HasBase() && attachment_count == 1)
2367 return false;
2368
2369 return true;
2370 }
2371
2372 return false;
2373 }
2374
2375 override bool ShowZonesHealth()
2376 {
2377 return true;
2378 }
2379
2380 //this into/outo parent.Cargo
2381 override bool CanPutInCargo(EntityAI parent)
2382 {
2383 return false;
2384 }
2385
2386 override bool CanRemoveFromCargo(EntityAI parent)
2387 {
2388 return false;
2389 }
2390
2391 //hands
2392 override bool CanPutIntoHands(EntityAI parent)
2393 {
2394 return false;
2395 }
2396
2397 //--- ACTION CONDITIONS
2398 //direction
2399 override bool IsFacingPlayer(PlayerBase player, string selection)
2400 {
2401 return true;
2402 }
2403
2404 override bool IsPlayerInside(PlayerBase player, string selection)
2405 {
2406 return true;
2407 }
2408
2411 {
2412 return false;
2413 }
2414
2415 //camera direction check
2416 bool IsFacingCamera(string selection)
2417 {
2418 return true;
2419 }
2420
2421 //roof check
2423 {
2424 return false;
2425 }
2426
2427 //selection->player distance check
2428 bool HasProperDistance(string selection, PlayerBase player)
2429 {
2430 return true;
2431 }
2432
2433 //folding
2435 {
2436 if (HasBase() || GetInventory().AttachmentCount() > 0)
2437 return false;
2438
2439 return true;
2440 }
2441
2443 {
2446
2447 return item;
2448 }
2449
2450 //Damage triggers (barbed wire)
2451 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2452 {
2453 if (GetGame() && GetGame().IsServer())
2454 {
2455 //destroy area damage if some already exists
2457
2458 //create new area damage
2460 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2461
2462 vector min_max[2];
2463 if (MemoryPointExists(slot_name + "_min"))
2464 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2465 if (MemoryPointExists(slot_name + "_max"))
2466 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2467
2468 //get proper trigger extents (min<max)
2469 vector extents[2];
2470 GetConstruction().GetTriggerExtents(min_max, extents);
2471
2472 //get box center
2473 vector center;
2474 center = GetConstruction().GetBoxCenter(min_max);
2475 center = ModelToWorld(center);
2476
2477 //rotate center if needed
2480
2481 areaDamage.SetExtents(extents[0], extents[1]);
2482 areaDamage.SetAreaPosition(center);
2483 areaDamage.SetAreaOrientation(orientation);
2484 areaDamage.SetLoopInterval(1.0);
2485 areaDamage.SetDeferDuration(0.2);
2486 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2487 areaDamage.SetAmmoName("BarbedWireHit");
2488 areaDamage.Spawn();
2489
2491 }
2492 }
2493
2495 {
2496 if (angle_deg != 0)
2497 {
2498 //orientation
2500
2501 //center
2503 if (MemoryPointExists("rotate_axis"))
2504 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2507 center[0] = r_center_x;
2508 center[2] = r_center_z;
2509 }
2510 }
2511
2512 void DestroyAreaDamage(string slot_name)
2513 {
2514 if (GetGame() && GetGame().IsServer())
2515 {
2518 {
2519 if (areaDamage)
2520 areaDamage.Destroy();
2521
2523 }
2524 }
2525 }
2526
2527 override bool IsIgnoredByConstruction()
2528 {
2529 return true;
2530 }
2531
2532 //================================================================
2533 // SOUNDS
2534 //================================================================
2535 protected void SoundBuildStart(string part_name)
2536 {
2537 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2538 }
2539
2540 protected void SoundDismantleStart(string part_name)
2541 {
2542 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2543 }
2544
2545 protected void SoundDestroyStart(string part_name)
2546 {
2547 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2548 }
2549
2550 protected string GetBuildSoundByMaterial(string part_name)
2551 {
2553
2554 switch (material_type)
2555 {
2556 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2557 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2558 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2559 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2560 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2561 }
2562
2563 return "";
2564 }
2565
2566 protected string GetDismantleSoundByMaterial(string part_name)
2567 {
2569
2570 switch (material_type)
2571 {
2572 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2573 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2574 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2575 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2576 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2577 }
2578
2579 return "";
2580 }
2581
2582 //misc
2584 {
2585 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2586 {
2587 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2589 SetHealth(slot_name, "Health", item.GetHealth());
2590 }
2591 }
2592
2593 override int GetDamageSystemVersionChange()
2594 {
2595 return 111;
2596 }
2597
2598 override void SetActions()
2599 {
2600 super.SetActions();
2601
2603 //AddAction(ActionTakeHybridAttachment);
2604 //AddAction(ActionTakeHybridAttachmentToHands);
2607 }
2608
2609 //================================================================
2610 // DEBUG
2611 //================================================================
2612 protected void DebugCustomState()
2613 {
2614 }
2615
2618 {
2619 return null;
2620 }
2621
2622 override void OnDebugSpawn()
2623 {
2624 FullyBuild();
2625 }
2626
2627 void FullyBuild()
2628 {
2630 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2631
2632 Man p;
2633
2634#ifdef SERVER
2636 GetGame().GetWorld().GetPlayerList(players);
2637 if (players.Count())
2638 p = players[0];
2639#else
2640 p = GetGame().GetPlayer();
2641#endif
2642
2643 foreach (ConstructionPart part : parts)
2644 {
2645 bool excluded = false;
2646 string partName = part.GetPartName();
2647 if (excludes)
2648 {
2649 foreach (string exclude : excludes)
2650 {
2651 if (partName.Contains(exclude))
2652 {
2653 excluded = true;
2654 break;
2655 }
2656 }
2657 }
2658
2659 if (!excluded)
2661 }
2662
2663 GetConstruction().UpdateVisuals();
2664 }
2665}
2666
2667void bsbDebugPrint(string s)
2668{
2669#ifdef BSB_DEBUG
2670 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2671#else
2672 //Print("" + s); // comment/uncomment to hide/see debug logs
2673#endif
2674}
2675void bsbDebugSpam(string s)
2676{
2677#ifdef BSB_DEBUG_SPAM
2678 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2679#else
2680 //Print("" + s); // comment/uncomment to hide/see debug logs
2681#endif
2682}

Referenced by BaseBuildingBase::CanDisplayAttachmentCategory(), ItemBase::CanFoldBaseBuildingObject(), BaseBuildingBase::CanPutIntoHands(), BaseBuildingBase::CanReceiveAttachment(), ItemBase::HasAttachmentsBesidesBase(), ItemBase::InitVisuals(), ItemBase::UpdatePhysics(), and ItemBase::UpdateVisuals().

◆ HasProperDistance()

bool bsbDebugPrint::HasProperDistance ( string selection,
PlayerBase player )
protected

Definition at line 2110 of file BaseBuildingBase.c.

2112{
2113 const string ANIMATION_DEPLOYED = "Deployed";
2114
2115 float m_ConstructionKitHealth; //stored health value for used construction kit
2116
2118
2119 bool m_HasBase;
2120 //variables for synchronization of base building parts (2x31 is the current limit)
2121 int m_SyncParts01; //synchronization for already built parts (31 parts)
2122 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2123 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2124 int m_InteractedPartId; //construction part id that an action was performed on
2125 int m_PerformedActionId; //action id that was performed on a construction part
2126
2127 //Sounds
2128 //build
2129 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2130 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2131 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2132 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2133 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2134 //dismantle
2135 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2136 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2137 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2138 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2139 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2140
2141 protected EffectSound m_Sound;
2142
2146
2147 // Constructor
2148 void BaseBuildingBase()
2149 {
2151
2152 //synchronized variables
2153 RegisterNetSyncVariableInt("m_SyncParts01");
2154 RegisterNetSyncVariableInt("m_SyncParts02");
2155 RegisterNetSyncVariableInt("m_SyncParts03");
2156 RegisterNetSyncVariableInt("m_InteractedPartId");
2157 RegisterNetSyncVariableInt("m_PerformedActionId");
2158 RegisterNetSyncVariableBool("m_HasBase");
2159
2160 //Construction init
2162
2163 if (ConfigIsExisting("hybridAttachments"))
2164 {
2166 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2167 }
2168 if (ConfigIsExisting("mountables"))
2169 {
2171 ConfigGetTextArray("mountables", m_Mountables);
2172 }
2173
2174 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2175 }
2176
2177 override void EEDelete(EntityAI parent)
2178 {
2179 super.EEDelete(parent);
2180
2181 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2183
2184 }
2185
2186 override string GetInvulnerabilityTypeString()
2187 {
2188 return "disableBaseDamage";
2189 }
2190
2191 override bool CanObstruct()
2192 {
2193 return true;
2194 }
2195
2196 override int GetHideIconMask()
2197 {
2198 return EInventoryIconVisibility.HIDE_VICINITY;
2199 }
2200
2201 // --- SYNCHRONIZATION
2203 {
2204 if (GetGame().IsServer())
2205 SetSynchDirty();
2206 }
2207
2208 override void OnVariablesSynchronized()
2209 {
2210 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2211 super.OnVariablesSynchronized();
2212
2213 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2214 }
2215
2216 protected void OnSynchronizedClient()
2217 {
2218 //update parts
2220
2221 //update action on part
2223
2224 //update visuals (client)
2225 UpdateVisuals();
2226 }
2227
2228 //parts synchronization
2230 {
2231 //part_id must starts from index = 1
2232 int offset;
2233 int mask;
2234
2235 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2236 {
2237 offset = part_id - 1;
2238 mask = 1 << offset;
2239
2241 }
2242 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2243 {
2244 offset = (part_id % 32);
2245 mask = 1 << offset;
2246
2248 }
2249 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2250 {
2251 offset = (part_id % 63);
2252 mask = 1 << offset;
2253
2255 }
2256 }
2257
2259 {
2260 //part_id must starts from index = 1
2261 int offset;
2262 int mask;
2263
2264 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2265 {
2266 offset = part_id - 1;
2267 mask = 1 << offset;
2268
2270 }
2271 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2272 {
2273 offset = (part_id % 32);
2274 mask = 1 << offset;
2275
2277 }
2278 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2279 {
2280 offset = (part_id % 63);
2281 mask = 1 << offset;
2282
2284 }
2285 }
2286
2288 {
2289 //part_id must starts from index = 1
2290 int offset;
2291 int mask;
2292
2293 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2294 {
2295 offset = part_id - 1;
2296 mask = 1 << offset;
2297
2298 if ((m_SyncParts01 & mask) > 0)
2299 return true;
2300 }
2301 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2302 {
2303 offset = (part_id % 32);
2304 mask = 1 << offset;
2305
2306 if ((m_SyncParts02 & mask) > 0)
2307 return true;
2308 }
2309 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2310 {
2311 offset = (part_id % 63);
2312 mask = 1 << offset;
2313
2314 if ((m_SyncParts03 & mask) > 0)
2315 return true;
2316 }
2317
2318 return false;
2319 }
2320
2321 protected void RegisterActionForSync(int part_id, int action_id)
2322 {
2325 }
2326
2327 protected void ResetActionSyncData()
2328 {
2329 //reset data
2330 m_InteractedPartId = -1;
2332 }
2333
2334 protected void SetActionFromSyncData()
2335 {
2336 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2337 {
2340
2341 switch (build_action_id)
2342 {
2346 }
2347 }
2348 }
2349 //------
2350
2352 {
2353 string key = part.m_PartName;
2354 bool is_base = part.IsBase();
2356 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2358 {
2359 if (!part.IsBuilt())
2360 {
2361 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2362 GetConstruction().AddToConstructedParts(key);
2363 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2364
2365 if (is_base)
2366 {
2368 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2369 }
2370 }
2371 }
2372 else
2373 {
2374 if (part.IsBuilt())
2375 {
2376 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2377 GetConstruction().RemoveFromConstructedParts(key);
2378 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2379
2380 if (is_base)
2381 {
2383 AddProxyPhysics(ANIMATION_DEPLOYED);
2384 }
2385 }
2386 }
2387
2388 //check slot lock for material attachments
2389 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2390 }
2391
2392 //set construction parts based on synchronized data
2394 {
2397
2398 for (int i = 0; i < construction_parts.Count(); ++i)
2399 {
2400 string key = construction_parts.GetKey(i);
2403 }
2404
2405 //regenerate navmesh
2406 UpdateNavmesh();
2407 }
2408
2410 {
2413
2414 for (int i = 0; i < construction_parts.Count(); ++i)
2415 {
2416 string key = construction_parts.GetKey(i);
2418
2419 if (value.GetId() == id)
2420 return value;
2421 }
2422
2423 return NULL;
2424 }
2425 //
2426
2427 //Base
2428 bool HasBase()
2429 {
2430 return m_HasBase;
2431 }
2432
2433 void SetBaseState(bool has_base)
2434 {
2436 }
2437
2438 override bool IsDeployable()
2439 {
2440 return true;
2441 }
2442
2443 bool IsOpened()
2444 {
2445 return false;
2446 }
2447
2448 //--- CONSTRUCTION KIT
2450 {
2454
2455 return construction_kit;
2456 }
2457
2459 {
2460 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2463 }
2464
2465 protected vector GetKitSpawnPosition()
2466 {
2467 return GetPosition();
2468 }
2469
2470 protected string GetConstructionKitType()
2471 {
2472 return "";
2473 }
2474
2476 {
2478 GetGame().ObjectDelete(construction_kit);
2479 }
2480
2481 //--- CONSTRUCTION
2482 void DestroyConstruction()
2483 {
2484 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2485 GetGame().ObjectDelete(this);
2486 }
2487
2488 // --- EVENTS
2489 override void OnStoreSave(ParamsWriteContext ctx)
2490 {
2491 super.OnStoreSave(ctx);
2492
2493 //sync parts 01
2494 ctx.Write(m_SyncParts01);
2495 ctx.Write(m_SyncParts02);
2496 ctx.Write(m_SyncParts03);
2497
2498 ctx.Write(m_HasBase);
2499 }
2500
2501 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2502 {
2503 if (!super.OnStoreLoad(ctx, version))
2504 return false;
2505
2506 //--- Base building data ---
2507 //Restore synced parts data
2508 if (!ctx.Read(m_SyncParts01))
2509 {
2510 m_SyncParts01 = 0; //set default
2511 return false;
2512 }
2513 if (!ctx.Read(m_SyncParts02))
2514 {
2515 m_SyncParts02 = 0; //set default
2516 return false;
2517 }
2518 if (!ctx.Read(m_SyncParts03))
2519 {
2520 m_SyncParts03 = 0; //set default
2521 return false;
2522 }
2523
2524 //has base
2525 if (!ctx.Read(m_HasBase))
2526 {
2527 m_HasBase = false;
2528 return false;
2529 }
2530 //---
2531
2532 return true;
2533 }
2534
2535 override void AfterStoreLoad()
2536 {
2537 super.AfterStoreLoad();
2538
2541 }
2542
2544 {
2545 //update server data
2547
2548 //set base state
2549 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2550 SetBaseState(construction_part.IsBuilt()) ;
2551
2552 //synchronize after load
2554 }
2555
2556 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2557 {
2559 return;
2560
2561 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2562
2563 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2564 return;
2565
2567 string part_name = zone;
2568 part_name.ToLower();
2569
2571 {
2573
2574 if (construction_part && construction.IsPartConstructed(part_name))
2575 {
2576 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2577 construction.DestroyConnectedParts(part_name);
2578 }
2579
2580 //barbed wire handling (hack-ish)
2581 if (part_name.Contains("barbed"))
2582 {
2583 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2584 if (barbed_wire)
2585 barbed_wire.SetMountedState(false);
2586 }
2587 }
2588 }
2589
2590 override void EEOnAfterLoad()
2591 {
2593 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2594
2595 super.EEOnAfterLoad();
2596 }
2597
2598 override void EEInit()
2599 {
2600 super.EEInit();
2601
2602 // init visuals and physics
2603 InitBaseState();
2604
2605 //debug
2606#ifdef DEVELOPER
2608#endif
2609 }
2610
2611 override void EEItemAttached(EntityAI item, string slot_name)
2612 {
2613 super.EEItemAttached(item, slot_name);
2614
2616 UpdateVisuals();
2618 }
2619
2620 override void EEItemDetached(EntityAI item, string slot_name)
2621 {
2622 super.EEItemDetached(item, slot_name);
2623
2624 UpdateVisuals();
2626 }
2627
2628 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2629 {
2631 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2632
2635 }
2636
2637 //ignore out of reach condition
2638 override bool IgnoreOutOfReachCondition()
2639 {
2640 return true;
2641 }
2642
2643 //CONSTRUCTION EVENTS
2644 //Build
2645 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2646 {
2648
2649 //check base state
2650 if (construtionPart.IsBase())
2651 {
2652 SetBaseState(true);
2653
2654 //spawn kit
2656 }
2657
2658 //register constructed parts for synchronization
2660
2661 //register action that was performed on part
2663
2664 //synchronize
2666
2667 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2668
2669 UpdateNavmesh();
2670
2671 //update visuals
2672 UpdateVisuals();
2673
2674 //reset action sync data
2675 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2676 }
2677
2678 void OnPartBuiltClient(string part_name, int action_id)
2679 {
2680 //play sound
2682 }
2683
2684 //Dismantle
2686 {
2687 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2689
2690 //register constructed parts for synchronization
2692
2693 //register action that was performed on part
2695
2696 //synchronize
2698
2699 // server part of sync, client will be synced from SetPartsFromSyncData
2701
2702 UpdateNavmesh();
2703
2704 //update visuals
2705 UpdateVisuals();
2706
2707 //reset action sync data
2708 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2709
2710 //check base state
2711 if (construtionPart.IsBase())
2712 {
2713 //Destroy construction
2714 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2715 }
2716 }
2717
2719 {
2720 //play sound
2722 }
2723
2724 //Destroy
2726 {
2727 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2729
2730 //register constructed parts for synchronization
2732
2733 //register action that was performed on part
2735
2736 //synchronize
2738
2739 // server part of sync, client will be synced from SetPartsFromSyncData
2741
2742 UpdateNavmesh();
2743
2744 //update visuals
2745 UpdateVisuals();
2746
2747 //reset action sync data
2748 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2749
2750 //check base state
2751 if (construtionPart.IsBase())
2752 {
2753 //Destroy construction
2754 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2755 }
2756 }
2757
2758 void OnPartDestroyedClient(string part_name, int action_id)
2759 {
2760 //play sound
2762 }
2763
2764 // --- UPDATE
2765 void InitBaseState()
2766 {
2767 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2768
2769 InitVisuals();
2770 UpdateNavmesh(); //regenerate navmesh
2771 GetConstruction().InitBaseState();
2772 }
2773
2774 void InitVisuals()
2775 {
2776 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2777 //check base
2778 if (!HasBase())
2779 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2780 else
2781 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2782
2783 GetConstruction().UpdateVisuals();
2784 }
2785
2786 void UpdateVisuals()
2787 {
2789
2791 foreach (string slotName : attachmentSlots)
2793
2794 //check base
2795 if (!HasBase())
2796 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2797 else
2798 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2799
2800 GetConstruction().UpdateVisuals();
2801 }
2802
2804 {
2805 string slotNameMounted = slot_name + "_Mounted";
2806 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2807
2808 if (attachment)
2809 {
2810 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2811 if (barbedWire && barbedWire.IsMounted())
2813 else
2815
2816 if (is_locked)
2817 {
2818 SetAnimationPhase(slotNameMounted, 0);
2819 SetAnimationPhase(slot_name, 1);
2820 }
2821 else
2822 {
2823 SetAnimationPhase(slotNameMounted, 1);
2824 SetAnimationPhase(slot_name, 0);
2825 }
2826 }
2827 else
2828 {
2829 SetAnimationPhase(slotNameMounted, 1);
2830 SetAnimationPhase(slot_name, 1);
2831
2833 }
2834 }
2835
2836 // avoid calling this function on frequent occasions, it's a massive performance hit
2837 void UpdatePhysics()
2838 {
2840 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2841
2844
2846 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2847
2848 foreach (string slotName : attachmentSlots)
2850
2851 //check base
2852 if (!HasBase())
2853 {
2855 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2856
2857 AddProxyPhysics(ANIMATION_DEPLOYED);
2858 }
2859 else
2860 {
2862 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2863
2864 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2865 }
2866
2867 GetConstruction().UpdatePhysics();
2868 UpdateNavmesh();
2869 }
2870
2872 {
2873 //checks for invalid appends; hotfix
2874 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2875 return;
2876 //----------------------------------
2877 string slot_name_mounted = slot_name + "_Mounted";
2878 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2879
2880 //remove proxy physics
2881 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2882 RemoveProxyPhysics(slot_name_mounted);
2883 RemoveProxyPhysics(slot_name);
2884
2885 if (attachment)
2886 {
2887 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2888 if (is_locked)
2889 {
2890 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2891 AddProxyPhysics(slot_name_mounted);
2892 }
2893 else
2894 {
2895 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2896 AddProxyPhysics(slot_name);
2897 }
2898 }
2899 }
2900
2901 protected void UpdateNavmesh()
2902 {
2903 SetAffectPathgraph(true, false);
2904 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2905 }
2906
2907 override bool CanUseConstruction()
2908 {
2909 return true;
2910 }
2911
2912 override bool CanUseConstructionBuild()
2913 {
2914 return true;
2915 }
2916
2918 {
2919 if (attachment)
2920 {
2922 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2923
2924 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2925 }
2926
2927 return false;
2928 }
2929
2930 protected bool IsAttachmentSlotLocked(string slot_name)
2931 {
2932 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2933 }
2934
2935 //--- ATTACHMENT SLOTS
2937 {
2938 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2939 if (GetGame().ConfigIsExisting(config_path))
2940 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2941 }
2942
2944 {
2945 return true;
2946 }
2947
2948 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2949 {
2950 return true;
2951 }
2952
2953 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2954 {
2955 return true;
2956 }
2957
2958 // --- INIT
2959 void ConstructionInit()
2960 {
2961 if (!m_Construction)
2962 m_Construction = new Construction(this);
2963
2964 GetConstruction().Init();
2965 }
2966
2968 {
2969 return m_Construction;
2970 }
2971
2972 //--- INVENTORY/ATTACHMENTS CONDITIONS
2973 //attachments
2975 {
2976 return super.CanReceiveAttachment(attachment, slotId);
2977 }
2978
2980 {
2981 int attachment_count = GetInventory().AttachmentCount();
2982 if (attachment_count > 0)
2983 {
2984 if (HasBase() && attachment_count == 1)
2985 return false;
2986
2987 return true;
2988 }
2989
2990 return false;
2991 }
2992
2993 override bool ShowZonesHealth()
2994 {
2995 return true;
2996 }
2997
2998 //this into/outo parent.Cargo
2999 override bool CanPutInCargo(EntityAI parent)
3000 {
3001 return false;
3002 }
3003
3004 override bool CanRemoveFromCargo(EntityAI parent)
3005 {
3006 return false;
3007 }
3008
3009 //hands
3010 override bool CanPutIntoHands(EntityAI parent)
3011 {
3012 return false;
3013 }
3014
3015 //--- ACTION CONDITIONS
3016 //direction
3017 override bool IsFacingPlayer(PlayerBase player, string selection)
3018 {
3019 return true;
3020 }
3021
3022 override bool IsPlayerInside(PlayerBase player, string selection)
3023 {
3024 return true;
3025 }
3026
3029 {
3030 return false;
3031 }
3032
3033 //camera direction check
3034 bool IsFacingCamera(string selection)
3035 {
3036 return true;
3037 }
3038
3039 //roof check
3041 {
3042 return false;
3043 }
3044
3045 //selection->player distance check
3046 bool HasProperDistance(string selection, PlayerBase player)
3047 {
3048 return true;
3049 }
3050
3051 //folding
3053 {
3054 if (HasBase() || GetInventory().AttachmentCount() > 0)
3055 return false;
3056
3057 return true;
3058 }
3059
3061 {
3064
3065 return item;
3066 }
3067
3068 //Damage triggers (barbed wire)
3069 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3070 {
3071 if (GetGame() && GetGame().IsServer())
3072 {
3073 //destroy area damage if some already exists
3075
3076 //create new area damage
3078 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3079
3080 vector min_max[2];
3081 if (MemoryPointExists(slot_name + "_min"))
3082 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3083 if (MemoryPointExists(slot_name + "_max"))
3084 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3085
3086 //get proper trigger extents (min<max)
3087 vector extents[2];
3088 GetConstruction().GetTriggerExtents(min_max, extents);
3089
3090 //get box center
3091 vector center;
3092 center = GetConstruction().GetBoxCenter(min_max);
3093 center = ModelToWorld(center);
3094
3095 //rotate center if needed
3098
3099 areaDamage.SetExtents(extents[0], extents[1]);
3100 areaDamage.SetAreaPosition(center);
3101 areaDamage.SetAreaOrientation(orientation);
3102 areaDamage.SetLoopInterval(1.0);
3103 areaDamage.SetDeferDuration(0.2);
3104 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3105 areaDamage.SetAmmoName("BarbedWireHit");
3106 areaDamage.Spawn();
3107
3109 }
3110 }
3111
3113 {
3114 if (angle_deg != 0)
3115 {
3116 //orientation
3118
3119 //center
3121 if (MemoryPointExists("rotate_axis"))
3122 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3125 center[0] = r_center_x;
3126 center[2] = r_center_z;
3127 }
3128 }
3129
3130 void DestroyAreaDamage(string slot_name)
3131 {
3132 if (GetGame() && GetGame().IsServer())
3133 {
3136 {
3137 if (areaDamage)
3138 areaDamage.Destroy();
3139
3141 }
3142 }
3143 }
3144
3145 override bool IsIgnoredByConstruction()
3146 {
3147 return true;
3148 }
3149
3150 //================================================================
3151 // SOUNDS
3152 //================================================================
3153 protected void SoundBuildStart(string part_name)
3154 {
3155 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3156 }
3157
3158 protected void SoundDismantleStart(string part_name)
3159 {
3160 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3161 }
3162
3163 protected void SoundDestroyStart(string part_name)
3164 {
3165 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3166 }
3167
3168 protected string GetBuildSoundByMaterial(string part_name)
3169 {
3171
3172 switch (material_type)
3173 {
3174 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3175 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3176 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3177 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3178 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3179 }
3180
3181 return "";
3182 }
3183
3184 protected string GetDismantleSoundByMaterial(string part_name)
3185 {
3187
3188 switch (material_type)
3189 {
3190 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3191 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3192 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3193 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3194 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3195 }
3196
3197 return "";
3198 }
3199
3200 //misc
3202 {
3203 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3204 {
3205 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3207 SetHealth(slot_name, "Health", item.GetHealth());
3208 }
3209 }
3210
3211 override int GetDamageSystemVersionChange()
3212 {
3213 return 111;
3214 }
3215
3216 override void SetActions()
3217 {
3218 super.SetActions();
3219
3221 //AddAction(ActionTakeHybridAttachment);
3222 //AddAction(ActionTakeHybridAttachmentToHands);
3225 }
3226
3227 //================================================================
3228 // DEBUG
3229 //================================================================
3230 protected void DebugCustomState()
3231 {
3232 }
3233
3236 {
3237 return null;
3238 }
3239
3240 override void OnDebugSpawn()
3241 {
3242 FullyBuild();
3243 }
3244
3245 void FullyBuild()
3246 {
3248 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3249
3250 Man p;
3251
3252#ifdef SERVER
3254 GetGame().GetWorld().GetPlayerList(players);
3255 if (players.Count())
3256 p = players[0];
3257#else
3258 p = GetGame().GetPlayer();
3259#endif
3260
3261 foreach (ConstructionPart part : parts)
3262 {
3263 bool excluded = false;
3264 string partName = part.GetPartName();
3265 if (excludes)
3266 {
3267 foreach (string exclude : excludes)
3268 {
3269 if (partName.Contains(exclude))
3270 {
3271 excluded = true;
3272 break;
3273 }
3274 }
3275 }
3276
3277 if (!excluded)
3279 }
3280
3281 GetConstruction().UpdateVisuals();
3282 }
3283}
3284
3285void bsbDebugPrint(string s)
3286{
3287#ifdef BSB_DEBUG
3288 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3289#else
3290 //Print("" + s); // comment/uncomment to hide/see debug logs
3291#endif
3292}
3293void bsbDebugSpam(string s)
3294{
3295#ifdef BSB_DEBUG_SPAM
3296 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3297#else
3298 //Print("" + s); // comment/uncomment to hide/see debug logs
3299#endif
3300}

◆ IgnoreOutOfReachCondition()

override bool bsbDebugPrint::IgnoreOutOfReachCondition ( )
protected

Definition at line 1702 of file BaseBuildingBase.c.

1704{
1705 const string ANIMATION_DEPLOYED = "Deployed";
1706
1707 float m_ConstructionKitHealth; //stored health value for used construction kit
1708
1710
1711 bool m_HasBase;
1712 //variables for synchronization of base building parts (2x31 is the current limit)
1713 int m_SyncParts01; //synchronization for already built parts (31 parts)
1714 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1715 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1716 int m_InteractedPartId; //construction part id that an action was performed on
1717 int m_PerformedActionId; //action id that was performed on a construction part
1718
1719 //Sounds
1720 //build
1721 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1722 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1723 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1724 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1725 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1726 //dismantle
1727 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1728 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1729 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1730 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1731 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1732
1733 protected EffectSound m_Sound;
1734
1738
1739 // Constructor
1740 void BaseBuildingBase()
1741 {
1743
1744 //synchronized variables
1745 RegisterNetSyncVariableInt("m_SyncParts01");
1746 RegisterNetSyncVariableInt("m_SyncParts02");
1747 RegisterNetSyncVariableInt("m_SyncParts03");
1748 RegisterNetSyncVariableInt("m_InteractedPartId");
1749 RegisterNetSyncVariableInt("m_PerformedActionId");
1750 RegisterNetSyncVariableBool("m_HasBase");
1751
1752 //Construction init
1754
1755 if (ConfigIsExisting("hybridAttachments"))
1756 {
1758 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1759 }
1760 if (ConfigIsExisting("mountables"))
1761 {
1763 ConfigGetTextArray("mountables", m_Mountables);
1764 }
1765
1766 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1767 }
1768
1769 override void EEDelete(EntityAI parent)
1770 {
1771 super.EEDelete(parent);
1772
1773 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1775
1776 }
1777
1778 override string GetInvulnerabilityTypeString()
1779 {
1780 return "disableBaseDamage";
1781 }
1782
1783 override bool CanObstruct()
1784 {
1785 return true;
1786 }
1787
1788 override int GetHideIconMask()
1789 {
1790 return EInventoryIconVisibility.HIDE_VICINITY;
1791 }
1792
1793 // --- SYNCHRONIZATION
1795 {
1796 if (GetGame().IsServer())
1797 SetSynchDirty();
1798 }
1799
1800 override void OnVariablesSynchronized()
1801 {
1802 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1803 super.OnVariablesSynchronized();
1804
1805 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1806 }
1807
1808 protected void OnSynchronizedClient()
1809 {
1810 //update parts
1812
1813 //update action on part
1815
1816 //update visuals (client)
1817 UpdateVisuals();
1818 }
1819
1820 //parts synchronization
1822 {
1823 //part_id must starts from index = 1
1824 int offset;
1825 int mask;
1826
1827 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1828 {
1829 offset = part_id - 1;
1830 mask = 1 << offset;
1831
1833 }
1834 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1835 {
1836 offset = (part_id % 32);
1837 mask = 1 << offset;
1838
1840 }
1841 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1842 {
1843 offset = (part_id % 63);
1844 mask = 1 << offset;
1845
1847 }
1848 }
1849
1851 {
1852 //part_id must starts from index = 1
1853 int offset;
1854 int mask;
1855
1856 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1857 {
1858 offset = part_id - 1;
1859 mask = 1 << offset;
1860
1862 }
1863 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1864 {
1865 offset = (part_id % 32);
1866 mask = 1 << offset;
1867
1869 }
1870 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1871 {
1872 offset = (part_id % 63);
1873 mask = 1 << offset;
1874
1876 }
1877 }
1878
1880 {
1881 //part_id must starts from index = 1
1882 int offset;
1883 int mask;
1884
1885 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1886 {
1887 offset = part_id - 1;
1888 mask = 1 << offset;
1889
1890 if ((m_SyncParts01 & mask) > 0)
1891 return true;
1892 }
1893 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1894 {
1895 offset = (part_id % 32);
1896 mask = 1 << offset;
1897
1898 if ((m_SyncParts02 & mask) > 0)
1899 return true;
1900 }
1901 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1902 {
1903 offset = (part_id % 63);
1904 mask = 1 << offset;
1905
1906 if ((m_SyncParts03 & mask) > 0)
1907 return true;
1908 }
1909
1910 return false;
1911 }
1912
1913 protected void RegisterActionForSync(int part_id, int action_id)
1914 {
1917 }
1918
1919 protected void ResetActionSyncData()
1920 {
1921 //reset data
1922 m_InteractedPartId = -1;
1924 }
1925
1926 protected void SetActionFromSyncData()
1927 {
1928 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1929 {
1932
1933 switch (build_action_id)
1934 {
1938 }
1939 }
1940 }
1941 //------
1942
1944 {
1945 string key = part.m_PartName;
1946 bool is_base = part.IsBase();
1948 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1950 {
1951 if (!part.IsBuilt())
1952 {
1953 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1954 GetConstruction().AddToConstructedParts(key);
1955 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1956
1957 if (is_base)
1958 {
1960 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1961 }
1962 }
1963 }
1964 else
1965 {
1966 if (part.IsBuilt())
1967 {
1968 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1969 GetConstruction().RemoveFromConstructedParts(key);
1970 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1971
1972 if (is_base)
1973 {
1975 AddProxyPhysics(ANIMATION_DEPLOYED);
1976 }
1977 }
1978 }
1979
1980 //check slot lock for material attachments
1981 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1982 }
1983
1984 //set construction parts based on synchronized data
1986 {
1989
1990 for (int i = 0; i < construction_parts.Count(); ++i)
1991 {
1992 string key = construction_parts.GetKey(i);
1995 }
1996
1997 //regenerate navmesh
1998 UpdateNavmesh();
1999 }
2000
2002 {
2005
2006 for (int i = 0; i < construction_parts.Count(); ++i)
2007 {
2008 string key = construction_parts.GetKey(i);
2010
2011 if (value.GetId() == id)
2012 return value;
2013 }
2014
2015 return NULL;
2016 }
2017 //
2018
2019 //Base
2020 bool HasBase()
2021 {
2022 return m_HasBase;
2023 }
2024
2025 void SetBaseState(bool has_base)
2026 {
2028 }
2029
2030 override bool IsDeployable()
2031 {
2032 return true;
2033 }
2034
2035 bool IsOpened()
2036 {
2037 return false;
2038 }
2039
2040 //--- CONSTRUCTION KIT
2042 {
2046
2047 return construction_kit;
2048 }
2049
2051 {
2052 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2055 }
2056
2057 protected vector GetKitSpawnPosition()
2058 {
2059 return GetPosition();
2060 }
2061
2062 protected string GetConstructionKitType()
2063 {
2064 return "";
2065 }
2066
2068 {
2070 GetGame().ObjectDelete(construction_kit);
2071 }
2072
2073 //--- CONSTRUCTION
2074 void DestroyConstruction()
2075 {
2076 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2077 GetGame().ObjectDelete(this);
2078 }
2079
2080 // --- EVENTS
2081 override void OnStoreSave(ParamsWriteContext ctx)
2082 {
2083 super.OnStoreSave(ctx);
2084
2085 //sync parts 01
2086 ctx.Write(m_SyncParts01);
2087 ctx.Write(m_SyncParts02);
2088 ctx.Write(m_SyncParts03);
2089
2090 ctx.Write(m_HasBase);
2091 }
2092
2093 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2094 {
2095 if (!super.OnStoreLoad(ctx, version))
2096 return false;
2097
2098 //--- Base building data ---
2099 //Restore synced parts data
2100 if (!ctx.Read(m_SyncParts01))
2101 {
2102 m_SyncParts01 = 0; //set default
2103 return false;
2104 }
2105 if (!ctx.Read(m_SyncParts02))
2106 {
2107 m_SyncParts02 = 0; //set default
2108 return false;
2109 }
2110 if (!ctx.Read(m_SyncParts03))
2111 {
2112 m_SyncParts03 = 0; //set default
2113 return false;
2114 }
2115
2116 //has base
2117 if (!ctx.Read(m_HasBase))
2118 {
2119 m_HasBase = false;
2120 return false;
2121 }
2122 //---
2123
2124 return true;
2125 }
2126
2127 override void AfterStoreLoad()
2128 {
2129 super.AfterStoreLoad();
2130
2133 }
2134
2136 {
2137 //update server data
2139
2140 //set base state
2141 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2142 SetBaseState(construction_part.IsBuilt()) ;
2143
2144 //synchronize after load
2146 }
2147
2148 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2149 {
2151 return;
2152
2153 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2154
2155 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2156 return;
2157
2159 string part_name = zone;
2160 part_name.ToLower();
2161
2163 {
2165
2166 if (construction_part && construction.IsPartConstructed(part_name))
2167 {
2168 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2169 construction.DestroyConnectedParts(part_name);
2170 }
2171
2172 //barbed wire handling (hack-ish)
2173 if (part_name.Contains("barbed"))
2174 {
2175 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2176 if (barbed_wire)
2177 barbed_wire.SetMountedState(false);
2178 }
2179 }
2180 }
2181
2182 override void EEOnAfterLoad()
2183 {
2185 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2186
2187 super.EEOnAfterLoad();
2188 }
2189
2190 override void EEInit()
2191 {
2192 super.EEInit();
2193
2194 // init visuals and physics
2195 InitBaseState();
2196
2197 //debug
2198#ifdef DEVELOPER
2200#endif
2201 }
2202
2203 override void EEItemAttached(EntityAI item, string slot_name)
2204 {
2205 super.EEItemAttached(item, slot_name);
2206
2208 UpdateVisuals();
2210 }
2211
2212 override void EEItemDetached(EntityAI item, string slot_name)
2213 {
2214 super.EEItemDetached(item, slot_name);
2215
2216 UpdateVisuals();
2218 }
2219
2220 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2221 {
2223 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2224
2227 }
2228
2229 //ignore out of reach condition
2230 override bool IgnoreOutOfReachCondition()
2231 {
2232 return true;
2233 }
2234
2235 //CONSTRUCTION EVENTS
2236 //Build
2237 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2238 {
2240
2241 //check base state
2242 if (construtionPart.IsBase())
2243 {
2244 SetBaseState(true);
2245
2246 //spawn kit
2248 }
2249
2250 //register constructed parts for synchronization
2252
2253 //register action that was performed on part
2255
2256 //synchronize
2258
2259 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2260
2261 UpdateNavmesh();
2262
2263 //update visuals
2264 UpdateVisuals();
2265
2266 //reset action sync data
2267 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2268 }
2269
2270 void OnPartBuiltClient(string part_name, int action_id)
2271 {
2272 //play sound
2274 }
2275
2276 //Dismantle
2278 {
2279 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2281
2282 //register constructed parts for synchronization
2284
2285 //register action that was performed on part
2287
2288 //synchronize
2290
2291 // server part of sync, client will be synced from SetPartsFromSyncData
2293
2294 UpdateNavmesh();
2295
2296 //update visuals
2297 UpdateVisuals();
2298
2299 //reset action sync data
2300 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2301
2302 //check base state
2303 if (construtionPart.IsBase())
2304 {
2305 //Destroy construction
2306 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2307 }
2308 }
2309
2311 {
2312 //play sound
2314 }
2315
2316 //Destroy
2318 {
2319 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2321
2322 //register constructed parts for synchronization
2324
2325 //register action that was performed on part
2327
2328 //synchronize
2330
2331 // server part of sync, client will be synced from SetPartsFromSyncData
2333
2334 UpdateNavmesh();
2335
2336 //update visuals
2337 UpdateVisuals();
2338
2339 //reset action sync data
2340 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2341
2342 //check base state
2343 if (construtionPart.IsBase())
2344 {
2345 //Destroy construction
2346 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2347 }
2348 }
2349
2350 void OnPartDestroyedClient(string part_name, int action_id)
2351 {
2352 //play sound
2354 }
2355
2356 // --- UPDATE
2357 void InitBaseState()
2358 {
2359 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2360
2361 InitVisuals();
2362 UpdateNavmesh(); //regenerate navmesh
2363 GetConstruction().InitBaseState();
2364 }
2365
2366 void InitVisuals()
2367 {
2368 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2369 //check base
2370 if (!HasBase())
2371 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2372 else
2373 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2374
2375 GetConstruction().UpdateVisuals();
2376 }
2377
2378 void UpdateVisuals()
2379 {
2381
2383 foreach (string slotName : attachmentSlots)
2385
2386 //check base
2387 if (!HasBase())
2388 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2389 else
2390 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2391
2392 GetConstruction().UpdateVisuals();
2393 }
2394
2396 {
2397 string slotNameMounted = slot_name + "_Mounted";
2398 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2399
2400 if (attachment)
2401 {
2402 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2403 if (barbedWire && barbedWire.IsMounted())
2405 else
2407
2408 if (is_locked)
2409 {
2410 SetAnimationPhase(slotNameMounted, 0);
2411 SetAnimationPhase(slot_name, 1);
2412 }
2413 else
2414 {
2415 SetAnimationPhase(slotNameMounted, 1);
2416 SetAnimationPhase(slot_name, 0);
2417 }
2418 }
2419 else
2420 {
2421 SetAnimationPhase(slotNameMounted, 1);
2422 SetAnimationPhase(slot_name, 1);
2423
2425 }
2426 }
2427
2428 // avoid calling this function on frequent occasions, it's a massive performance hit
2429 void UpdatePhysics()
2430 {
2432 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2433
2436
2438 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2439
2440 foreach (string slotName : attachmentSlots)
2442
2443 //check base
2444 if (!HasBase())
2445 {
2447 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2448
2449 AddProxyPhysics(ANIMATION_DEPLOYED);
2450 }
2451 else
2452 {
2454 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2455
2456 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2457 }
2458
2459 GetConstruction().UpdatePhysics();
2460 UpdateNavmesh();
2461 }
2462
2464 {
2465 //checks for invalid appends; hotfix
2466 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2467 return;
2468 //----------------------------------
2469 string slot_name_mounted = slot_name + "_Mounted";
2470 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2471
2472 //remove proxy physics
2473 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2474 RemoveProxyPhysics(slot_name_mounted);
2475 RemoveProxyPhysics(slot_name);
2476
2477 if (attachment)
2478 {
2479 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2480 if (is_locked)
2481 {
2482 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2483 AddProxyPhysics(slot_name_mounted);
2484 }
2485 else
2486 {
2487 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2488 AddProxyPhysics(slot_name);
2489 }
2490 }
2491 }
2492
2493 protected void UpdateNavmesh()
2494 {
2495 SetAffectPathgraph(true, false);
2496 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2497 }
2498
2499 override bool CanUseConstruction()
2500 {
2501 return true;
2502 }
2503
2504 override bool CanUseConstructionBuild()
2505 {
2506 return true;
2507 }
2508
2510 {
2511 if (attachment)
2512 {
2514 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2515
2516 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2517 }
2518
2519 return false;
2520 }
2521
2522 protected bool IsAttachmentSlotLocked(string slot_name)
2523 {
2524 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2525 }
2526
2527 //--- ATTACHMENT SLOTS
2529 {
2530 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2531 if (GetGame().ConfigIsExisting(config_path))
2532 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2533 }
2534
2536 {
2537 return true;
2538 }
2539
2540 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2541 {
2542 return true;
2543 }
2544
2545 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2546 {
2547 return true;
2548 }
2549
2550 // --- INIT
2551 void ConstructionInit()
2552 {
2553 if (!m_Construction)
2554 m_Construction = new Construction(this);
2555
2556 GetConstruction().Init();
2557 }
2558
2560 {
2561 return m_Construction;
2562 }
2563
2564 //--- INVENTORY/ATTACHMENTS CONDITIONS
2565 //attachments
2567 {
2568 return super.CanReceiveAttachment(attachment, slotId);
2569 }
2570
2572 {
2573 int attachment_count = GetInventory().AttachmentCount();
2574 if (attachment_count > 0)
2575 {
2576 if (HasBase() && attachment_count == 1)
2577 return false;
2578
2579 return true;
2580 }
2581
2582 return false;
2583 }
2584
2585 override bool ShowZonesHealth()
2586 {
2587 return true;
2588 }
2589
2590 //this into/outo parent.Cargo
2591 override bool CanPutInCargo(EntityAI parent)
2592 {
2593 return false;
2594 }
2595
2596 override bool CanRemoveFromCargo(EntityAI parent)
2597 {
2598 return false;
2599 }
2600
2601 //hands
2602 override bool CanPutIntoHands(EntityAI parent)
2603 {
2604 return false;
2605 }
2606
2607 //--- ACTION CONDITIONS
2608 //direction
2609 override bool IsFacingPlayer(PlayerBase player, string selection)
2610 {
2611 return true;
2612 }
2613
2614 override bool IsPlayerInside(PlayerBase player, string selection)
2615 {
2616 return true;
2617 }
2618
2621 {
2622 return false;
2623 }
2624
2625 //camera direction check
2626 bool IsFacingCamera(string selection)
2627 {
2628 return true;
2629 }
2630
2631 //roof check
2633 {
2634 return false;
2635 }
2636
2637 //selection->player distance check
2638 bool HasProperDistance(string selection, PlayerBase player)
2639 {
2640 return true;
2641 }
2642
2643 //folding
2645 {
2646 if (HasBase() || GetInventory().AttachmentCount() > 0)
2647 return false;
2648
2649 return true;
2650 }
2651
2653 {
2656
2657 return item;
2658 }
2659
2660 //Damage triggers (barbed wire)
2661 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2662 {
2663 if (GetGame() && GetGame().IsServer())
2664 {
2665 //destroy area damage if some already exists
2667
2668 //create new area damage
2670 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2671
2672 vector min_max[2];
2673 if (MemoryPointExists(slot_name + "_min"))
2674 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2675 if (MemoryPointExists(slot_name + "_max"))
2676 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2677
2678 //get proper trigger extents (min<max)
2679 vector extents[2];
2680 GetConstruction().GetTriggerExtents(min_max, extents);
2681
2682 //get box center
2683 vector center;
2684 center = GetConstruction().GetBoxCenter(min_max);
2685 center = ModelToWorld(center);
2686
2687 //rotate center if needed
2690
2691 areaDamage.SetExtents(extents[0], extents[1]);
2692 areaDamage.SetAreaPosition(center);
2693 areaDamage.SetAreaOrientation(orientation);
2694 areaDamage.SetLoopInterval(1.0);
2695 areaDamage.SetDeferDuration(0.2);
2696 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2697 areaDamage.SetAmmoName("BarbedWireHit");
2698 areaDamage.Spawn();
2699
2701 }
2702 }
2703
2705 {
2706 if (angle_deg != 0)
2707 {
2708 //orientation
2710
2711 //center
2713 if (MemoryPointExists("rotate_axis"))
2714 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2717 center[0] = r_center_x;
2718 center[2] = r_center_z;
2719 }
2720 }
2721
2722 void DestroyAreaDamage(string slot_name)
2723 {
2724 if (GetGame() && GetGame().IsServer())
2725 {
2728 {
2729 if (areaDamage)
2730 areaDamage.Destroy();
2731
2733 }
2734 }
2735 }
2736
2737 override bool IsIgnoredByConstruction()
2738 {
2739 return true;
2740 }
2741
2742 //================================================================
2743 // SOUNDS
2744 //================================================================
2745 protected void SoundBuildStart(string part_name)
2746 {
2747 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2748 }
2749
2750 protected void SoundDismantleStart(string part_name)
2751 {
2752 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2753 }
2754
2755 protected void SoundDestroyStart(string part_name)
2756 {
2757 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2758 }
2759
2760 protected string GetBuildSoundByMaterial(string part_name)
2761 {
2763
2764 switch (material_type)
2765 {
2766 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2767 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2768 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2769 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2770 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2771 }
2772
2773 return "";
2774 }
2775
2776 protected string GetDismantleSoundByMaterial(string part_name)
2777 {
2779
2780 switch (material_type)
2781 {
2782 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2783 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2784 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2785 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2786 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2787 }
2788
2789 return "";
2790 }
2791
2792 //misc
2794 {
2795 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2796 {
2797 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2799 SetHealth(slot_name, "Health", item.GetHealth());
2800 }
2801 }
2802
2803 override int GetDamageSystemVersionChange()
2804 {
2805 return 111;
2806 }
2807
2808 override void SetActions()
2809 {
2810 super.SetActions();
2811
2813 //AddAction(ActionTakeHybridAttachment);
2814 //AddAction(ActionTakeHybridAttachmentToHands);
2817 }
2818
2819 //================================================================
2820 // DEBUG
2821 //================================================================
2822 protected void DebugCustomState()
2823 {
2824 }
2825
2828 {
2829 return null;
2830 }
2831
2832 override void OnDebugSpawn()
2833 {
2834 FullyBuild();
2835 }
2836
2837 void FullyBuild()
2838 {
2840 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2841
2842 Man p;
2843
2844#ifdef SERVER
2846 GetGame().GetWorld().GetPlayerList(players);
2847 if (players.Count())
2848 p = players[0];
2849#else
2850 p = GetGame().GetPlayer();
2851#endif
2852
2853 foreach (ConstructionPart part : parts)
2854 {
2855 bool excluded = false;
2856 string partName = part.GetPartName();
2857 if (excludes)
2858 {
2859 foreach (string exclude : excludes)
2860 {
2861 if (partName.Contains(exclude))
2862 {
2863 excluded = true;
2864 break;
2865 }
2866 }
2867 }
2868
2869 if (!excluded)
2871 }
2872
2873 GetConstruction().UpdateVisuals();
2874 }
2875}
2876
2877void bsbDebugPrint(string s)
2878{
2879#ifdef BSB_DEBUG
2880 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2881#else
2882 //Print("" + s); // comment/uncomment to hide/see debug logs
2883#endif
2884}
2885void bsbDebugSpam(string s)
2886{
2887#ifdef BSB_DEBUG_SPAM
2888 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2889#else
2890 //Print("" + s); // comment/uncomment to hide/see debug logs
2891#endif
2892}

◆ InitBaseState()

void bsbDebugPrint::InitBaseState ( )
protected

Definition at line 1829 of file BaseBuildingBase.c.

1831{
1832 const string ANIMATION_DEPLOYED = "Deployed";
1833
1834 float m_ConstructionKitHealth; //stored health value for used construction kit
1835
1837
1838 bool m_HasBase;
1839 //variables for synchronization of base building parts (2x31 is the current limit)
1840 int m_SyncParts01; //synchronization for already built parts (31 parts)
1841 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1842 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1843 int m_InteractedPartId; //construction part id that an action was performed on
1844 int m_PerformedActionId; //action id that was performed on a construction part
1845
1846 //Sounds
1847 //build
1848 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1849 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1850 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1851 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1852 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1853 //dismantle
1854 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1855 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1856 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1857 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1858 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1859
1860 protected EffectSound m_Sound;
1861
1865
1866 // Constructor
1867 void BaseBuildingBase()
1868 {
1870
1871 //synchronized variables
1872 RegisterNetSyncVariableInt("m_SyncParts01");
1873 RegisterNetSyncVariableInt("m_SyncParts02");
1874 RegisterNetSyncVariableInt("m_SyncParts03");
1875 RegisterNetSyncVariableInt("m_InteractedPartId");
1876 RegisterNetSyncVariableInt("m_PerformedActionId");
1877 RegisterNetSyncVariableBool("m_HasBase");
1878
1879 //Construction init
1881
1882 if (ConfigIsExisting("hybridAttachments"))
1883 {
1885 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1886 }
1887 if (ConfigIsExisting("mountables"))
1888 {
1890 ConfigGetTextArray("mountables", m_Mountables);
1891 }
1892
1893 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1894 }
1895
1896 override void EEDelete(EntityAI parent)
1897 {
1898 super.EEDelete(parent);
1899
1900 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1902
1903 }
1904
1905 override string GetInvulnerabilityTypeString()
1906 {
1907 return "disableBaseDamage";
1908 }
1909
1910 override bool CanObstruct()
1911 {
1912 return true;
1913 }
1914
1915 override int GetHideIconMask()
1916 {
1917 return EInventoryIconVisibility.HIDE_VICINITY;
1918 }
1919
1920 // --- SYNCHRONIZATION
1922 {
1923 if (GetGame().IsServer())
1924 SetSynchDirty();
1925 }
1926
1927 override void OnVariablesSynchronized()
1928 {
1929 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1930 super.OnVariablesSynchronized();
1931
1932 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1933 }
1934
1935 protected void OnSynchronizedClient()
1936 {
1937 //update parts
1939
1940 //update action on part
1942
1943 //update visuals (client)
1944 UpdateVisuals();
1945 }
1946
1947 //parts synchronization
1949 {
1950 //part_id must starts from index = 1
1951 int offset;
1952 int mask;
1953
1954 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1955 {
1956 offset = part_id - 1;
1957 mask = 1 << offset;
1958
1960 }
1961 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1962 {
1963 offset = (part_id % 32);
1964 mask = 1 << offset;
1965
1967 }
1968 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1969 {
1970 offset = (part_id % 63);
1971 mask = 1 << offset;
1972
1974 }
1975 }
1976
1978 {
1979 //part_id must starts from index = 1
1980 int offset;
1981 int mask;
1982
1983 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1984 {
1985 offset = part_id - 1;
1986 mask = 1 << offset;
1987
1989 }
1990 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1991 {
1992 offset = (part_id % 32);
1993 mask = 1 << offset;
1994
1996 }
1997 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1998 {
1999 offset = (part_id % 63);
2000 mask = 1 << offset;
2001
2003 }
2004 }
2005
2007 {
2008 //part_id must starts from index = 1
2009 int offset;
2010 int mask;
2011
2012 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2013 {
2014 offset = part_id - 1;
2015 mask = 1 << offset;
2016
2017 if ((m_SyncParts01 & mask) > 0)
2018 return true;
2019 }
2020 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2021 {
2022 offset = (part_id % 32);
2023 mask = 1 << offset;
2024
2025 if ((m_SyncParts02 & mask) > 0)
2026 return true;
2027 }
2028 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2029 {
2030 offset = (part_id % 63);
2031 mask = 1 << offset;
2032
2033 if ((m_SyncParts03 & mask) > 0)
2034 return true;
2035 }
2036
2037 return false;
2038 }
2039
2040 protected void RegisterActionForSync(int part_id, int action_id)
2041 {
2044 }
2045
2046 protected void ResetActionSyncData()
2047 {
2048 //reset data
2049 m_InteractedPartId = -1;
2051 }
2052
2053 protected void SetActionFromSyncData()
2054 {
2055 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2056 {
2059
2060 switch (build_action_id)
2061 {
2065 }
2066 }
2067 }
2068 //------
2069
2071 {
2072 string key = part.m_PartName;
2073 bool is_base = part.IsBase();
2075 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2077 {
2078 if (!part.IsBuilt())
2079 {
2080 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2081 GetConstruction().AddToConstructedParts(key);
2082 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2083
2084 if (is_base)
2085 {
2087 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2088 }
2089 }
2090 }
2091 else
2092 {
2093 if (part.IsBuilt())
2094 {
2095 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2096 GetConstruction().RemoveFromConstructedParts(key);
2097 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2098
2099 if (is_base)
2100 {
2102 AddProxyPhysics(ANIMATION_DEPLOYED);
2103 }
2104 }
2105 }
2106
2107 //check slot lock for material attachments
2108 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2109 }
2110
2111 //set construction parts based on synchronized data
2113 {
2116
2117 for (int i = 0; i < construction_parts.Count(); ++i)
2118 {
2119 string key = construction_parts.GetKey(i);
2122 }
2123
2124 //regenerate navmesh
2125 UpdateNavmesh();
2126 }
2127
2129 {
2132
2133 for (int i = 0; i < construction_parts.Count(); ++i)
2134 {
2135 string key = construction_parts.GetKey(i);
2137
2138 if (value.GetId() == id)
2139 return value;
2140 }
2141
2142 return NULL;
2143 }
2144 //
2145
2146 //Base
2147 bool HasBase()
2148 {
2149 return m_HasBase;
2150 }
2151
2152 void SetBaseState(bool has_base)
2153 {
2155 }
2156
2157 override bool IsDeployable()
2158 {
2159 return true;
2160 }
2161
2162 bool IsOpened()
2163 {
2164 return false;
2165 }
2166
2167 //--- CONSTRUCTION KIT
2169 {
2173
2174 return construction_kit;
2175 }
2176
2178 {
2179 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2182 }
2183
2184 protected vector GetKitSpawnPosition()
2185 {
2186 return GetPosition();
2187 }
2188
2189 protected string GetConstructionKitType()
2190 {
2191 return "";
2192 }
2193
2195 {
2197 GetGame().ObjectDelete(construction_kit);
2198 }
2199
2200 //--- CONSTRUCTION
2201 void DestroyConstruction()
2202 {
2203 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2204 GetGame().ObjectDelete(this);
2205 }
2206
2207 // --- EVENTS
2208 override void OnStoreSave(ParamsWriteContext ctx)
2209 {
2210 super.OnStoreSave(ctx);
2211
2212 //sync parts 01
2213 ctx.Write(m_SyncParts01);
2214 ctx.Write(m_SyncParts02);
2215 ctx.Write(m_SyncParts03);
2216
2217 ctx.Write(m_HasBase);
2218 }
2219
2220 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2221 {
2222 if (!super.OnStoreLoad(ctx, version))
2223 return false;
2224
2225 //--- Base building data ---
2226 //Restore synced parts data
2227 if (!ctx.Read(m_SyncParts01))
2228 {
2229 m_SyncParts01 = 0; //set default
2230 return false;
2231 }
2232 if (!ctx.Read(m_SyncParts02))
2233 {
2234 m_SyncParts02 = 0; //set default
2235 return false;
2236 }
2237 if (!ctx.Read(m_SyncParts03))
2238 {
2239 m_SyncParts03 = 0; //set default
2240 return false;
2241 }
2242
2243 //has base
2244 if (!ctx.Read(m_HasBase))
2245 {
2246 m_HasBase = false;
2247 return false;
2248 }
2249 //---
2250
2251 return true;
2252 }
2253
2254 override void AfterStoreLoad()
2255 {
2256 super.AfterStoreLoad();
2257
2260 }
2261
2263 {
2264 //update server data
2266
2267 //set base state
2268 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2269 SetBaseState(construction_part.IsBuilt()) ;
2270
2271 //synchronize after load
2273 }
2274
2275 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2276 {
2278 return;
2279
2280 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2281
2282 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2283 return;
2284
2286 string part_name = zone;
2287 part_name.ToLower();
2288
2290 {
2292
2293 if (construction_part && construction.IsPartConstructed(part_name))
2294 {
2295 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2296 construction.DestroyConnectedParts(part_name);
2297 }
2298
2299 //barbed wire handling (hack-ish)
2300 if (part_name.Contains("barbed"))
2301 {
2302 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2303 if (barbed_wire)
2304 barbed_wire.SetMountedState(false);
2305 }
2306 }
2307 }
2308
2309 override void EEOnAfterLoad()
2310 {
2312 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2313
2314 super.EEOnAfterLoad();
2315 }
2316
2317 override void EEInit()
2318 {
2319 super.EEInit();
2320
2321 // init visuals and physics
2322 InitBaseState();
2323
2324 //debug
2325#ifdef DEVELOPER
2327#endif
2328 }
2329
2330 override void EEItemAttached(EntityAI item, string slot_name)
2331 {
2332 super.EEItemAttached(item, slot_name);
2333
2335 UpdateVisuals();
2337 }
2338
2339 override void EEItemDetached(EntityAI item, string slot_name)
2340 {
2341 super.EEItemDetached(item, slot_name);
2342
2343 UpdateVisuals();
2345 }
2346
2347 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2348 {
2350 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2351
2354 }
2355
2356 //ignore out of reach condition
2357 override bool IgnoreOutOfReachCondition()
2358 {
2359 return true;
2360 }
2361
2362 //CONSTRUCTION EVENTS
2363 //Build
2364 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2365 {
2367
2368 //check base state
2369 if (construtionPart.IsBase())
2370 {
2371 SetBaseState(true);
2372
2373 //spawn kit
2375 }
2376
2377 //register constructed parts for synchronization
2379
2380 //register action that was performed on part
2382
2383 //synchronize
2385
2386 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2387
2388 UpdateNavmesh();
2389
2390 //update visuals
2391 UpdateVisuals();
2392
2393 //reset action sync data
2394 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2395 }
2396
2397 void OnPartBuiltClient(string part_name, int action_id)
2398 {
2399 //play sound
2401 }
2402
2403 //Dismantle
2405 {
2406 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2408
2409 //register constructed parts for synchronization
2411
2412 //register action that was performed on part
2414
2415 //synchronize
2417
2418 // server part of sync, client will be synced from SetPartsFromSyncData
2420
2421 UpdateNavmesh();
2422
2423 //update visuals
2424 UpdateVisuals();
2425
2426 //reset action sync data
2427 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2428
2429 //check base state
2430 if (construtionPart.IsBase())
2431 {
2432 //Destroy construction
2433 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2434 }
2435 }
2436
2438 {
2439 //play sound
2441 }
2442
2443 //Destroy
2445 {
2446 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2448
2449 //register constructed parts for synchronization
2451
2452 //register action that was performed on part
2454
2455 //synchronize
2457
2458 // server part of sync, client will be synced from SetPartsFromSyncData
2460
2461 UpdateNavmesh();
2462
2463 //update visuals
2464 UpdateVisuals();
2465
2466 //reset action sync data
2467 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2468
2469 //check base state
2470 if (construtionPart.IsBase())
2471 {
2472 //Destroy construction
2473 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2474 }
2475 }
2476
2477 void OnPartDestroyedClient(string part_name, int action_id)
2478 {
2479 //play sound
2481 }
2482
2483 // --- UPDATE
2484 void InitBaseState()
2485 {
2486 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2487
2488 InitVisuals();
2489 UpdateNavmesh(); //regenerate navmesh
2490 GetConstruction().InitBaseState();
2491 }
2492
2493 void InitVisuals()
2494 {
2495 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2496 //check base
2497 if (!HasBase())
2498 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2499 else
2500 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2501
2502 GetConstruction().UpdateVisuals();
2503 }
2504
2505 void UpdateVisuals()
2506 {
2508
2510 foreach (string slotName : attachmentSlots)
2512
2513 //check base
2514 if (!HasBase())
2515 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2516 else
2517 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2518
2519 GetConstruction().UpdateVisuals();
2520 }
2521
2523 {
2524 string slotNameMounted = slot_name + "_Mounted";
2525 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2526
2527 if (attachment)
2528 {
2529 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2530 if (barbedWire && barbedWire.IsMounted())
2532 else
2534
2535 if (is_locked)
2536 {
2537 SetAnimationPhase(slotNameMounted, 0);
2538 SetAnimationPhase(slot_name, 1);
2539 }
2540 else
2541 {
2542 SetAnimationPhase(slotNameMounted, 1);
2543 SetAnimationPhase(slot_name, 0);
2544 }
2545 }
2546 else
2547 {
2548 SetAnimationPhase(slotNameMounted, 1);
2549 SetAnimationPhase(slot_name, 1);
2550
2552 }
2553 }
2554
2555 // avoid calling this function on frequent occasions, it's a massive performance hit
2556 void UpdatePhysics()
2557 {
2559 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2560
2563
2565 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2566
2567 foreach (string slotName : attachmentSlots)
2569
2570 //check base
2571 if (!HasBase())
2572 {
2574 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2575
2576 AddProxyPhysics(ANIMATION_DEPLOYED);
2577 }
2578 else
2579 {
2581 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2582
2583 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2584 }
2585
2586 GetConstruction().UpdatePhysics();
2587 UpdateNavmesh();
2588 }
2589
2591 {
2592 //checks for invalid appends; hotfix
2593 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2594 return;
2595 //----------------------------------
2596 string slot_name_mounted = slot_name + "_Mounted";
2597 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2598
2599 //remove proxy physics
2600 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2601 RemoveProxyPhysics(slot_name_mounted);
2602 RemoveProxyPhysics(slot_name);
2603
2604 if (attachment)
2605 {
2606 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2607 if (is_locked)
2608 {
2609 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2610 AddProxyPhysics(slot_name_mounted);
2611 }
2612 else
2613 {
2614 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2615 AddProxyPhysics(slot_name);
2616 }
2617 }
2618 }
2619
2620 protected void UpdateNavmesh()
2621 {
2622 SetAffectPathgraph(true, false);
2623 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2624 }
2625
2626 override bool CanUseConstruction()
2627 {
2628 return true;
2629 }
2630
2631 override bool CanUseConstructionBuild()
2632 {
2633 return true;
2634 }
2635
2637 {
2638 if (attachment)
2639 {
2641 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2642
2643 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2644 }
2645
2646 return false;
2647 }
2648
2649 protected bool IsAttachmentSlotLocked(string slot_name)
2650 {
2651 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2652 }
2653
2654 //--- ATTACHMENT SLOTS
2656 {
2657 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2658 if (GetGame().ConfigIsExisting(config_path))
2659 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2660 }
2661
2663 {
2664 return true;
2665 }
2666
2667 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2668 {
2669 return true;
2670 }
2671
2672 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2673 {
2674 return true;
2675 }
2676
2677 // --- INIT
2678 void ConstructionInit()
2679 {
2680 if (!m_Construction)
2681 m_Construction = new Construction(this);
2682
2683 GetConstruction().Init();
2684 }
2685
2687 {
2688 return m_Construction;
2689 }
2690
2691 //--- INVENTORY/ATTACHMENTS CONDITIONS
2692 //attachments
2694 {
2695 return super.CanReceiveAttachment(attachment, slotId);
2696 }
2697
2699 {
2700 int attachment_count = GetInventory().AttachmentCount();
2701 if (attachment_count > 0)
2702 {
2703 if (HasBase() && attachment_count == 1)
2704 return false;
2705
2706 return true;
2707 }
2708
2709 return false;
2710 }
2711
2712 override bool ShowZonesHealth()
2713 {
2714 return true;
2715 }
2716
2717 //this into/outo parent.Cargo
2718 override bool CanPutInCargo(EntityAI parent)
2719 {
2720 return false;
2721 }
2722
2723 override bool CanRemoveFromCargo(EntityAI parent)
2724 {
2725 return false;
2726 }
2727
2728 //hands
2729 override bool CanPutIntoHands(EntityAI parent)
2730 {
2731 return false;
2732 }
2733
2734 //--- ACTION CONDITIONS
2735 //direction
2736 override bool IsFacingPlayer(PlayerBase player, string selection)
2737 {
2738 return true;
2739 }
2740
2741 override bool IsPlayerInside(PlayerBase player, string selection)
2742 {
2743 return true;
2744 }
2745
2748 {
2749 return false;
2750 }
2751
2752 //camera direction check
2753 bool IsFacingCamera(string selection)
2754 {
2755 return true;
2756 }
2757
2758 //roof check
2760 {
2761 return false;
2762 }
2763
2764 //selection->player distance check
2765 bool HasProperDistance(string selection, PlayerBase player)
2766 {
2767 return true;
2768 }
2769
2770 //folding
2772 {
2773 if (HasBase() || GetInventory().AttachmentCount() > 0)
2774 return false;
2775
2776 return true;
2777 }
2778
2780 {
2783
2784 return item;
2785 }
2786
2787 //Damage triggers (barbed wire)
2788 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2789 {
2790 if (GetGame() && GetGame().IsServer())
2791 {
2792 //destroy area damage if some already exists
2794
2795 //create new area damage
2797 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2798
2799 vector min_max[2];
2800 if (MemoryPointExists(slot_name + "_min"))
2801 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2802 if (MemoryPointExists(slot_name + "_max"))
2803 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2804
2805 //get proper trigger extents (min<max)
2806 vector extents[2];
2807 GetConstruction().GetTriggerExtents(min_max, extents);
2808
2809 //get box center
2810 vector center;
2811 center = GetConstruction().GetBoxCenter(min_max);
2812 center = ModelToWorld(center);
2813
2814 //rotate center if needed
2817
2818 areaDamage.SetExtents(extents[0], extents[1]);
2819 areaDamage.SetAreaPosition(center);
2820 areaDamage.SetAreaOrientation(orientation);
2821 areaDamage.SetLoopInterval(1.0);
2822 areaDamage.SetDeferDuration(0.2);
2823 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2824 areaDamage.SetAmmoName("BarbedWireHit");
2825 areaDamage.Spawn();
2826
2828 }
2829 }
2830
2832 {
2833 if (angle_deg != 0)
2834 {
2835 //orientation
2837
2838 //center
2840 if (MemoryPointExists("rotate_axis"))
2841 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2844 center[0] = r_center_x;
2845 center[2] = r_center_z;
2846 }
2847 }
2848
2849 void DestroyAreaDamage(string slot_name)
2850 {
2851 if (GetGame() && GetGame().IsServer())
2852 {
2855 {
2856 if (areaDamage)
2857 areaDamage.Destroy();
2858
2860 }
2861 }
2862 }
2863
2864 override bool IsIgnoredByConstruction()
2865 {
2866 return true;
2867 }
2868
2869 //================================================================
2870 // SOUNDS
2871 //================================================================
2872 protected void SoundBuildStart(string part_name)
2873 {
2874 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2875 }
2876
2877 protected void SoundDismantleStart(string part_name)
2878 {
2879 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2880 }
2881
2882 protected void SoundDestroyStart(string part_name)
2883 {
2884 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2885 }
2886
2887 protected string GetBuildSoundByMaterial(string part_name)
2888 {
2890
2891 switch (material_type)
2892 {
2893 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2894 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2895 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2896 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2897 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2898 }
2899
2900 return "";
2901 }
2902
2903 protected string GetDismantleSoundByMaterial(string part_name)
2904 {
2906
2907 switch (material_type)
2908 {
2909 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2910 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2911 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2912 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2913 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2914 }
2915
2916 return "";
2917 }
2918
2919 //misc
2921 {
2922 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2923 {
2924 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2926 SetHealth(slot_name, "Health", item.GetHealth());
2927 }
2928 }
2929
2930 override int GetDamageSystemVersionChange()
2931 {
2932 return 111;
2933 }
2934
2935 override void SetActions()
2936 {
2937 super.SetActions();
2938
2940 //AddAction(ActionTakeHybridAttachment);
2941 //AddAction(ActionTakeHybridAttachmentToHands);
2944 }
2945
2946 //================================================================
2947 // DEBUG
2948 //================================================================
2949 protected void DebugCustomState()
2950 {
2951 }
2952
2955 {
2956 return null;
2957 }
2958
2959 override void OnDebugSpawn()
2960 {
2961 FullyBuild();
2962 }
2963
2964 void FullyBuild()
2965 {
2967 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2968
2969 Man p;
2970
2971#ifdef SERVER
2973 GetGame().GetWorld().GetPlayerList(players);
2974 if (players.Count())
2975 p = players[0];
2976#else
2977 p = GetGame().GetPlayer();
2978#endif
2979
2980 foreach (ConstructionPart part : parts)
2981 {
2982 bool excluded = false;
2983 string partName = part.GetPartName();
2984 if (excludes)
2985 {
2986 foreach (string exclude : excludes)
2987 {
2988 if (partName.Contains(exclude))
2989 {
2990 excluded = true;
2991 break;
2992 }
2993 }
2994 }
2995
2996 if (!excluded)
2998 }
2999
3000 GetConstruction().UpdateVisuals();
3001 }
3002}
3003
3004void bsbDebugPrint(string s)
3005{
3006#ifdef BSB_DEBUG
3007 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3008#else
3009 //Print("" + s); // comment/uncomment to hide/see debug logs
3010#endif
3011}
3012void bsbDebugSpam(string s)
3013{
3014#ifdef BSB_DEBUG_SPAM
3015 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3016#else
3017 //Print("" + s); // comment/uncomment to hide/see debug logs
3018#endif
3019}

◆ InitVisuals()

void bsbDebugPrint::InitVisuals ( )
protected

Definition at line 1838 of file BaseBuildingBase.c.

1840{
1841 const string ANIMATION_DEPLOYED = "Deployed";
1842
1843 float m_ConstructionKitHealth; //stored health value for used construction kit
1844
1846
1847 bool m_HasBase;
1848 //variables for synchronization of base building parts (2x31 is the current limit)
1849 int m_SyncParts01; //synchronization for already built parts (31 parts)
1850 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1851 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1852 int m_InteractedPartId; //construction part id that an action was performed on
1853 int m_PerformedActionId; //action id that was performed on a construction part
1854
1855 //Sounds
1856 //build
1857 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1858 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1859 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1860 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1861 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1862 //dismantle
1863 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1864 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1865 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1866 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1867 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1868
1869 protected EffectSound m_Sound;
1870
1874
1875 // Constructor
1876 void BaseBuildingBase()
1877 {
1879
1880 //synchronized variables
1881 RegisterNetSyncVariableInt("m_SyncParts01");
1882 RegisterNetSyncVariableInt("m_SyncParts02");
1883 RegisterNetSyncVariableInt("m_SyncParts03");
1884 RegisterNetSyncVariableInt("m_InteractedPartId");
1885 RegisterNetSyncVariableInt("m_PerformedActionId");
1886 RegisterNetSyncVariableBool("m_HasBase");
1887
1888 //Construction init
1890
1891 if (ConfigIsExisting("hybridAttachments"))
1892 {
1894 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1895 }
1896 if (ConfigIsExisting("mountables"))
1897 {
1899 ConfigGetTextArray("mountables", m_Mountables);
1900 }
1901
1902 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1903 }
1904
1905 override void EEDelete(EntityAI parent)
1906 {
1907 super.EEDelete(parent);
1908
1909 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1911
1912 }
1913
1914 override string GetInvulnerabilityTypeString()
1915 {
1916 return "disableBaseDamage";
1917 }
1918
1919 override bool CanObstruct()
1920 {
1921 return true;
1922 }
1923
1924 override int GetHideIconMask()
1925 {
1926 return EInventoryIconVisibility.HIDE_VICINITY;
1927 }
1928
1929 // --- SYNCHRONIZATION
1931 {
1932 if (GetGame().IsServer())
1933 SetSynchDirty();
1934 }
1935
1936 override void OnVariablesSynchronized()
1937 {
1938 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1939 super.OnVariablesSynchronized();
1940
1941 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1942 }
1943
1944 protected void OnSynchronizedClient()
1945 {
1946 //update parts
1948
1949 //update action on part
1951
1952 //update visuals (client)
1953 UpdateVisuals();
1954 }
1955
1956 //parts synchronization
1958 {
1959 //part_id must starts from index = 1
1960 int offset;
1961 int mask;
1962
1963 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1964 {
1965 offset = part_id - 1;
1966 mask = 1 << offset;
1967
1969 }
1970 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1971 {
1972 offset = (part_id % 32);
1973 mask = 1 << offset;
1974
1976 }
1977 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1978 {
1979 offset = (part_id % 63);
1980 mask = 1 << offset;
1981
1983 }
1984 }
1985
1987 {
1988 //part_id must starts from index = 1
1989 int offset;
1990 int mask;
1991
1992 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1993 {
1994 offset = part_id - 1;
1995 mask = 1 << offset;
1996
1998 }
1999 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2000 {
2001 offset = (part_id % 32);
2002 mask = 1 << offset;
2003
2005 }
2006 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2007 {
2008 offset = (part_id % 63);
2009 mask = 1 << offset;
2010
2012 }
2013 }
2014
2016 {
2017 //part_id must starts from index = 1
2018 int offset;
2019 int mask;
2020
2021 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2022 {
2023 offset = part_id - 1;
2024 mask = 1 << offset;
2025
2026 if ((m_SyncParts01 & mask) > 0)
2027 return true;
2028 }
2029 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2030 {
2031 offset = (part_id % 32);
2032 mask = 1 << offset;
2033
2034 if ((m_SyncParts02 & mask) > 0)
2035 return true;
2036 }
2037 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2038 {
2039 offset = (part_id % 63);
2040 mask = 1 << offset;
2041
2042 if ((m_SyncParts03 & mask) > 0)
2043 return true;
2044 }
2045
2046 return false;
2047 }
2048
2049 protected void RegisterActionForSync(int part_id, int action_id)
2050 {
2053 }
2054
2055 protected void ResetActionSyncData()
2056 {
2057 //reset data
2058 m_InteractedPartId = -1;
2060 }
2061
2062 protected void SetActionFromSyncData()
2063 {
2064 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2065 {
2068
2069 switch (build_action_id)
2070 {
2074 }
2075 }
2076 }
2077 //------
2078
2080 {
2081 string key = part.m_PartName;
2082 bool is_base = part.IsBase();
2084 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2086 {
2087 if (!part.IsBuilt())
2088 {
2089 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2090 GetConstruction().AddToConstructedParts(key);
2091 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2092
2093 if (is_base)
2094 {
2096 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2097 }
2098 }
2099 }
2100 else
2101 {
2102 if (part.IsBuilt())
2103 {
2104 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2105 GetConstruction().RemoveFromConstructedParts(key);
2106 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2107
2108 if (is_base)
2109 {
2111 AddProxyPhysics(ANIMATION_DEPLOYED);
2112 }
2113 }
2114 }
2115
2116 //check slot lock for material attachments
2117 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2118 }
2119
2120 //set construction parts based on synchronized data
2122 {
2125
2126 for (int i = 0; i < construction_parts.Count(); ++i)
2127 {
2128 string key = construction_parts.GetKey(i);
2131 }
2132
2133 //regenerate navmesh
2134 UpdateNavmesh();
2135 }
2136
2138 {
2141
2142 for (int i = 0; i < construction_parts.Count(); ++i)
2143 {
2144 string key = construction_parts.GetKey(i);
2146
2147 if (value.GetId() == id)
2148 return value;
2149 }
2150
2151 return NULL;
2152 }
2153 //
2154
2155 //Base
2156 bool HasBase()
2157 {
2158 return m_HasBase;
2159 }
2160
2161 void SetBaseState(bool has_base)
2162 {
2164 }
2165
2166 override bool IsDeployable()
2167 {
2168 return true;
2169 }
2170
2171 bool IsOpened()
2172 {
2173 return false;
2174 }
2175
2176 //--- CONSTRUCTION KIT
2178 {
2182
2183 return construction_kit;
2184 }
2185
2187 {
2188 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2191 }
2192
2193 protected vector GetKitSpawnPosition()
2194 {
2195 return GetPosition();
2196 }
2197
2198 protected string GetConstructionKitType()
2199 {
2200 return "";
2201 }
2202
2204 {
2206 GetGame().ObjectDelete(construction_kit);
2207 }
2208
2209 //--- CONSTRUCTION
2210 void DestroyConstruction()
2211 {
2212 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2213 GetGame().ObjectDelete(this);
2214 }
2215
2216 // --- EVENTS
2217 override void OnStoreSave(ParamsWriteContext ctx)
2218 {
2219 super.OnStoreSave(ctx);
2220
2221 //sync parts 01
2222 ctx.Write(m_SyncParts01);
2223 ctx.Write(m_SyncParts02);
2224 ctx.Write(m_SyncParts03);
2225
2226 ctx.Write(m_HasBase);
2227 }
2228
2229 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2230 {
2231 if (!super.OnStoreLoad(ctx, version))
2232 return false;
2233
2234 //--- Base building data ---
2235 //Restore synced parts data
2236 if (!ctx.Read(m_SyncParts01))
2237 {
2238 m_SyncParts01 = 0; //set default
2239 return false;
2240 }
2241 if (!ctx.Read(m_SyncParts02))
2242 {
2243 m_SyncParts02 = 0; //set default
2244 return false;
2245 }
2246 if (!ctx.Read(m_SyncParts03))
2247 {
2248 m_SyncParts03 = 0; //set default
2249 return false;
2250 }
2251
2252 //has base
2253 if (!ctx.Read(m_HasBase))
2254 {
2255 m_HasBase = false;
2256 return false;
2257 }
2258 //---
2259
2260 return true;
2261 }
2262
2263 override void AfterStoreLoad()
2264 {
2265 super.AfterStoreLoad();
2266
2269 }
2270
2272 {
2273 //update server data
2275
2276 //set base state
2277 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2278 SetBaseState(construction_part.IsBuilt()) ;
2279
2280 //synchronize after load
2282 }
2283
2284 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2285 {
2287 return;
2288
2289 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2290
2291 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2292 return;
2293
2295 string part_name = zone;
2296 part_name.ToLower();
2297
2299 {
2301
2302 if (construction_part && construction.IsPartConstructed(part_name))
2303 {
2304 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2305 construction.DestroyConnectedParts(part_name);
2306 }
2307
2308 //barbed wire handling (hack-ish)
2309 if (part_name.Contains("barbed"))
2310 {
2311 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2312 if (barbed_wire)
2313 barbed_wire.SetMountedState(false);
2314 }
2315 }
2316 }
2317
2318 override void EEOnAfterLoad()
2319 {
2321 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2322
2323 super.EEOnAfterLoad();
2324 }
2325
2326 override void EEInit()
2327 {
2328 super.EEInit();
2329
2330 // init visuals and physics
2331 InitBaseState();
2332
2333 //debug
2334#ifdef DEVELOPER
2336#endif
2337 }
2338
2339 override void EEItemAttached(EntityAI item, string slot_name)
2340 {
2341 super.EEItemAttached(item, slot_name);
2342
2344 UpdateVisuals();
2346 }
2347
2348 override void EEItemDetached(EntityAI item, string slot_name)
2349 {
2350 super.EEItemDetached(item, slot_name);
2351
2352 UpdateVisuals();
2354 }
2355
2356 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2357 {
2359 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2360
2363 }
2364
2365 //ignore out of reach condition
2366 override bool IgnoreOutOfReachCondition()
2367 {
2368 return true;
2369 }
2370
2371 //CONSTRUCTION EVENTS
2372 //Build
2373 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2374 {
2376
2377 //check base state
2378 if (construtionPart.IsBase())
2379 {
2380 SetBaseState(true);
2381
2382 //spawn kit
2384 }
2385
2386 //register constructed parts for synchronization
2388
2389 //register action that was performed on part
2391
2392 //synchronize
2394
2395 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2396
2397 UpdateNavmesh();
2398
2399 //update visuals
2400 UpdateVisuals();
2401
2402 //reset action sync data
2403 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2404 }
2405
2406 void OnPartBuiltClient(string part_name, int action_id)
2407 {
2408 //play sound
2410 }
2411
2412 //Dismantle
2414 {
2415 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2417
2418 //register constructed parts for synchronization
2420
2421 //register action that was performed on part
2423
2424 //synchronize
2426
2427 // server part of sync, client will be synced from SetPartsFromSyncData
2429
2430 UpdateNavmesh();
2431
2432 //update visuals
2433 UpdateVisuals();
2434
2435 //reset action sync data
2436 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2437
2438 //check base state
2439 if (construtionPart.IsBase())
2440 {
2441 //Destroy construction
2442 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2443 }
2444 }
2445
2447 {
2448 //play sound
2450 }
2451
2452 //Destroy
2454 {
2455 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2457
2458 //register constructed parts for synchronization
2460
2461 //register action that was performed on part
2463
2464 //synchronize
2466
2467 // server part of sync, client will be synced from SetPartsFromSyncData
2469
2470 UpdateNavmesh();
2471
2472 //update visuals
2473 UpdateVisuals();
2474
2475 //reset action sync data
2476 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2477
2478 //check base state
2479 if (construtionPart.IsBase())
2480 {
2481 //Destroy construction
2482 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2483 }
2484 }
2485
2486 void OnPartDestroyedClient(string part_name, int action_id)
2487 {
2488 //play sound
2490 }
2491
2492 // --- UPDATE
2493 void InitBaseState()
2494 {
2495 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2496
2497 InitVisuals();
2498 UpdateNavmesh(); //regenerate navmesh
2499 GetConstruction().InitBaseState();
2500 }
2501
2502 void InitVisuals()
2503 {
2504 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2505 //check base
2506 if (!HasBase())
2507 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2508 else
2509 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2510
2511 GetConstruction().UpdateVisuals();
2512 }
2513
2514 void UpdateVisuals()
2515 {
2517
2519 foreach (string slotName : attachmentSlots)
2521
2522 //check base
2523 if (!HasBase())
2524 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2525 else
2526 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2527
2528 GetConstruction().UpdateVisuals();
2529 }
2530
2532 {
2533 string slotNameMounted = slot_name + "_Mounted";
2534 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2535
2536 if (attachment)
2537 {
2538 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2539 if (barbedWire && barbedWire.IsMounted())
2541 else
2543
2544 if (is_locked)
2545 {
2546 SetAnimationPhase(slotNameMounted, 0);
2547 SetAnimationPhase(slot_name, 1);
2548 }
2549 else
2550 {
2551 SetAnimationPhase(slotNameMounted, 1);
2552 SetAnimationPhase(slot_name, 0);
2553 }
2554 }
2555 else
2556 {
2557 SetAnimationPhase(slotNameMounted, 1);
2558 SetAnimationPhase(slot_name, 1);
2559
2561 }
2562 }
2563
2564 // avoid calling this function on frequent occasions, it's a massive performance hit
2565 void UpdatePhysics()
2566 {
2568 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2569
2572
2574 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2575
2576 foreach (string slotName : attachmentSlots)
2578
2579 //check base
2580 if (!HasBase())
2581 {
2583 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2584
2585 AddProxyPhysics(ANIMATION_DEPLOYED);
2586 }
2587 else
2588 {
2590 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2591
2592 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2593 }
2594
2595 GetConstruction().UpdatePhysics();
2596 UpdateNavmesh();
2597 }
2598
2600 {
2601 //checks for invalid appends; hotfix
2602 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2603 return;
2604 //----------------------------------
2605 string slot_name_mounted = slot_name + "_Mounted";
2606 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2607
2608 //remove proxy physics
2609 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2610 RemoveProxyPhysics(slot_name_mounted);
2611 RemoveProxyPhysics(slot_name);
2612
2613 if (attachment)
2614 {
2615 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2616 if (is_locked)
2617 {
2618 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2619 AddProxyPhysics(slot_name_mounted);
2620 }
2621 else
2622 {
2623 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2624 AddProxyPhysics(slot_name);
2625 }
2626 }
2627 }
2628
2629 protected void UpdateNavmesh()
2630 {
2631 SetAffectPathgraph(true, false);
2632 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2633 }
2634
2635 override bool CanUseConstruction()
2636 {
2637 return true;
2638 }
2639
2640 override bool CanUseConstructionBuild()
2641 {
2642 return true;
2643 }
2644
2646 {
2647 if (attachment)
2648 {
2650 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2651
2652 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2653 }
2654
2655 return false;
2656 }
2657
2658 protected bool IsAttachmentSlotLocked(string slot_name)
2659 {
2660 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2661 }
2662
2663 //--- ATTACHMENT SLOTS
2665 {
2666 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2667 if (GetGame().ConfigIsExisting(config_path))
2668 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2669 }
2670
2672 {
2673 return true;
2674 }
2675
2676 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2677 {
2678 return true;
2679 }
2680
2681 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2682 {
2683 return true;
2684 }
2685
2686 // --- INIT
2687 void ConstructionInit()
2688 {
2689 if (!m_Construction)
2690 m_Construction = new Construction(this);
2691
2692 GetConstruction().Init();
2693 }
2694
2696 {
2697 return m_Construction;
2698 }
2699
2700 //--- INVENTORY/ATTACHMENTS CONDITIONS
2701 //attachments
2703 {
2704 return super.CanReceiveAttachment(attachment, slotId);
2705 }
2706
2708 {
2709 int attachment_count = GetInventory().AttachmentCount();
2710 if (attachment_count > 0)
2711 {
2712 if (HasBase() && attachment_count == 1)
2713 return false;
2714
2715 return true;
2716 }
2717
2718 return false;
2719 }
2720
2721 override bool ShowZonesHealth()
2722 {
2723 return true;
2724 }
2725
2726 //this into/outo parent.Cargo
2727 override bool CanPutInCargo(EntityAI parent)
2728 {
2729 return false;
2730 }
2731
2732 override bool CanRemoveFromCargo(EntityAI parent)
2733 {
2734 return false;
2735 }
2736
2737 //hands
2738 override bool CanPutIntoHands(EntityAI parent)
2739 {
2740 return false;
2741 }
2742
2743 //--- ACTION CONDITIONS
2744 //direction
2745 override bool IsFacingPlayer(PlayerBase player, string selection)
2746 {
2747 return true;
2748 }
2749
2750 override bool IsPlayerInside(PlayerBase player, string selection)
2751 {
2752 return true;
2753 }
2754
2757 {
2758 return false;
2759 }
2760
2761 //camera direction check
2762 bool IsFacingCamera(string selection)
2763 {
2764 return true;
2765 }
2766
2767 //roof check
2769 {
2770 return false;
2771 }
2772
2773 //selection->player distance check
2774 bool HasProperDistance(string selection, PlayerBase player)
2775 {
2776 return true;
2777 }
2778
2779 //folding
2781 {
2782 if (HasBase() || GetInventory().AttachmentCount() > 0)
2783 return false;
2784
2785 return true;
2786 }
2787
2789 {
2792
2793 return item;
2794 }
2795
2796 //Damage triggers (barbed wire)
2797 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2798 {
2799 if (GetGame() && GetGame().IsServer())
2800 {
2801 //destroy area damage if some already exists
2803
2804 //create new area damage
2806 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2807
2808 vector min_max[2];
2809 if (MemoryPointExists(slot_name + "_min"))
2810 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2811 if (MemoryPointExists(slot_name + "_max"))
2812 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2813
2814 //get proper trigger extents (min<max)
2815 vector extents[2];
2816 GetConstruction().GetTriggerExtents(min_max, extents);
2817
2818 //get box center
2819 vector center;
2820 center = GetConstruction().GetBoxCenter(min_max);
2821 center = ModelToWorld(center);
2822
2823 //rotate center if needed
2826
2827 areaDamage.SetExtents(extents[0], extents[1]);
2828 areaDamage.SetAreaPosition(center);
2829 areaDamage.SetAreaOrientation(orientation);
2830 areaDamage.SetLoopInterval(1.0);
2831 areaDamage.SetDeferDuration(0.2);
2832 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2833 areaDamage.SetAmmoName("BarbedWireHit");
2834 areaDamage.Spawn();
2835
2837 }
2838 }
2839
2841 {
2842 if (angle_deg != 0)
2843 {
2844 //orientation
2846
2847 //center
2849 if (MemoryPointExists("rotate_axis"))
2850 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2853 center[0] = r_center_x;
2854 center[2] = r_center_z;
2855 }
2856 }
2857
2858 void DestroyAreaDamage(string slot_name)
2859 {
2860 if (GetGame() && GetGame().IsServer())
2861 {
2864 {
2865 if (areaDamage)
2866 areaDamage.Destroy();
2867
2869 }
2870 }
2871 }
2872
2873 override bool IsIgnoredByConstruction()
2874 {
2875 return true;
2876 }
2877
2878 //================================================================
2879 // SOUNDS
2880 //================================================================
2881 protected void SoundBuildStart(string part_name)
2882 {
2883 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2884 }
2885
2886 protected void SoundDismantleStart(string part_name)
2887 {
2888 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2889 }
2890
2891 protected void SoundDestroyStart(string part_name)
2892 {
2893 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2894 }
2895
2896 protected string GetBuildSoundByMaterial(string part_name)
2897 {
2899
2900 switch (material_type)
2901 {
2902 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2903 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2904 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2905 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2906 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2907 }
2908
2909 return "";
2910 }
2911
2912 protected string GetDismantleSoundByMaterial(string part_name)
2913 {
2915
2916 switch (material_type)
2917 {
2918 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2919 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2920 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2921 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2922 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2923 }
2924
2925 return "";
2926 }
2927
2928 //misc
2930 {
2931 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2932 {
2933 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2935 SetHealth(slot_name, "Health", item.GetHealth());
2936 }
2937 }
2938
2939 override int GetDamageSystemVersionChange()
2940 {
2941 return 111;
2942 }
2943
2944 override void SetActions()
2945 {
2946 super.SetActions();
2947
2949 //AddAction(ActionTakeHybridAttachment);
2950 //AddAction(ActionTakeHybridAttachmentToHands);
2953 }
2954
2955 //================================================================
2956 // DEBUG
2957 //================================================================
2958 protected void DebugCustomState()
2959 {
2960 }
2961
2964 {
2965 return null;
2966 }
2967
2968 override void OnDebugSpawn()
2969 {
2970 FullyBuild();
2971 }
2972
2973 void FullyBuild()
2974 {
2976 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2977
2978 Man p;
2979
2980#ifdef SERVER
2982 GetGame().GetWorld().GetPlayerList(players);
2983 if (players.Count())
2984 p = players[0];
2985#else
2986 p = GetGame().GetPlayer();
2987#endif
2988
2989 foreach (ConstructionPart part : parts)
2990 {
2991 bool excluded = false;
2992 string partName = part.GetPartName();
2993 if (excludes)
2994 {
2995 foreach (string exclude : excludes)
2996 {
2997 if (partName.Contains(exclude))
2998 {
2999 excluded = true;
3000 break;
3001 }
3002 }
3003 }
3004
3005 if (!excluded)
3007 }
3008
3009 GetConstruction().UpdateVisuals();
3010 }
3011}
3012
3013void bsbDebugPrint(string s)
3014{
3015#ifdef BSB_DEBUG
3016 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3017#else
3018 //Print("" + s); // comment/uncomment to hide/see debug logs
3019#endif
3020}
3021void bsbDebugSpam(string s)
3022{
3023#ifdef BSB_DEBUG_SPAM
3024 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3025#else
3026 //Print("" + s); // comment/uncomment to hide/see debug logs
3027#endif
3028}

◆ IsAttachmentSlotLocked() [1/2]

bool bsbDebugPrint::IsAttachmentSlotLocked ( EntityAI attachment)
protected

Definition at line 1981 of file BaseBuildingBase.c.

1983{
1984 const string ANIMATION_DEPLOYED = "Deployed";
1985
1986 float m_ConstructionKitHealth; //stored health value for used construction kit
1987
1989
1990 bool m_HasBase;
1991 //variables for synchronization of base building parts (2x31 is the current limit)
1992 int m_SyncParts01; //synchronization for already built parts (31 parts)
1993 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1994 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1995 int m_InteractedPartId; //construction part id that an action was performed on
1996 int m_PerformedActionId; //action id that was performed on a construction part
1997
1998 //Sounds
1999 //build
2000 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2001 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2002 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2003 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2004 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2005 //dismantle
2006 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2007 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2008 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2009 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2010 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2011
2012 protected EffectSound m_Sound;
2013
2017
2018 // Constructor
2019 void BaseBuildingBase()
2020 {
2022
2023 //synchronized variables
2024 RegisterNetSyncVariableInt("m_SyncParts01");
2025 RegisterNetSyncVariableInt("m_SyncParts02");
2026 RegisterNetSyncVariableInt("m_SyncParts03");
2027 RegisterNetSyncVariableInt("m_InteractedPartId");
2028 RegisterNetSyncVariableInt("m_PerformedActionId");
2029 RegisterNetSyncVariableBool("m_HasBase");
2030
2031 //Construction init
2033
2034 if (ConfigIsExisting("hybridAttachments"))
2035 {
2037 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2038 }
2039 if (ConfigIsExisting("mountables"))
2040 {
2042 ConfigGetTextArray("mountables", m_Mountables);
2043 }
2044
2045 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2046 }
2047
2048 override void EEDelete(EntityAI parent)
2049 {
2050 super.EEDelete(parent);
2051
2052 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2054
2055 }
2056
2057 override string GetInvulnerabilityTypeString()
2058 {
2059 return "disableBaseDamage";
2060 }
2061
2062 override bool CanObstruct()
2063 {
2064 return true;
2065 }
2066
2067 override int GetHideIconMask()
2068 {
2069 return EInventoryIconVisibility.HIDE_VICINITY;
2070 }
2071
2072 // --- SYNCHRONIZATION
2074 {
2075 if (GetGame().IsServer())
2076 SetSynchDirty();
2077 }
2078
2079 override void OnVariablesSynchronized()
2080 {
2081 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2082 super.OnVariablesSynchronized();
2083
2084 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2085 }
2086
2087 protected void OnSynchronizedClient()
2088 {
2089 //update parts
2091
2092 //update action on part
2094
2095 //update visuals (client)
2096 UpdateVisuals();
2097 }
2098
2099 //parts synchronization
2101 {
2102 //part_id must starts from index = 1
2103 int offset;
2104 int mask;
2105
2106 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2107 {
2108 offset = part_id - 1;
2109 mask = 1 << offset;
2110
2112 }
2113 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2114 {
2115 offset = (part_id % 32);
2116 mask = 1 << offset;
2117
2119 }
2120 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2121 {
2122 offset = (part_id % 63);
2123 mask = 1 << offset;
2124
2126 }
2127 }
2128
2130 {
2131 //part_id must starts from index = 1
2132 int offset;
2133 int mask;
2134
2135 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2136 {
2137 offset = part_id - 1;
2138 mask = 1 << offset;
2139
2141 }
2142 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2143 {
2144 offset = (part_id % 32);
2145 mask = 1 << offset;
2146
2148 }
2149 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2150 {
2151 offset = (part_id % 63);
2152 mask = 1 << offset;
2153
2155 }
2156 }
2157
2159 {
2160 //part_id must starts from index = 1
2161 int offset;
2162 int mask;
2163
2164 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2165 {
2166 offset = part_id - 1;
2167 mask = 1 << offset;
2168
2169 if ((m_SyncParts01 & mask) > 0)
2170 return true;
2171 }
2172 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2173 {
2174 offset = (part_id % 32);
2175 mask = 1 << offset;
2176
2177 if ((m_SyncParts02 & mask) > 0)
2178 return true;
2179 }
2180 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2181 {
2182 offset = (part_id % 63);
2183 mask = 1 << offset;
2184
2185 if ((m_SyncParts03 & mask) > 0)
2186 return true;
2187 }
2188
2189 return false;
2190 }
2191
2192 protected void RegisterActionForSync(int part_id, int action_id)
2193 {
2196 }
2197
2198 protected void ResetActionSyncData()
2199 {
2200 //reset data
2201 m_InteractedPartId = -1;
2203 }
2204
2205 protected void SetActionFromSyncData()
2206 {
2207 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2208 {
2211
2212 switch (build_action_id)
2213 {
2217 }
2218 }
2219 }
2220 //------
2221
2223 {
2224 string key = part.m_PartName;
2225 bool is_base = part.IsBase();
2227 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2229 {
2230 if (!part.IsBuilt())
2231 {
2232 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2233 GetConstruction().AddToConstructedParts(key);
2234 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2235
2236 if (is_base)
2237 {
2239 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2240 }
2241 }
2242 }
2243 else
2244 {
2245 if (part.IsBuilt())
2246 {
2247 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2248 GetConstruction().RemoveFromConstructedParts(key);
2249 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2250
2251 if (is_base)
2252 {
2254 AddProxyPhysics(ANIMATION_DEPLOYED);
2255 }
2256 }
2257 }
2258
2259 //check slot lock for material attachments
2260 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2261 }
2262
2263 //set construction parts based on synchronized data
2265 {
2268
2269 for (int i = 0; i < construction_parts.Count(); ++i)
2270 {
2271 string key = construction_parts.GetKey(i);
2274 }
2275
2276 //regenerate navmesh
2277 UpdateNavmesh();
2278 }
2279
2281 {
2284
2285 for (int i = 0; i < construction_parts.Count(); ++i)
2286 {
2287 string key = construction_parts.GetKey(i);
2289
2290 if (value.GetId() == id)
2291 return value;
2292 }
2293
2294 return NULL;
2295 }
2296 //
2297
2298 //Base
2299 bool HasBase()
2300 {
2301 return m_HasBase;
2302 }
2303
2304 void SetBaseState(bool has_base)
2305 {
2307 }
2308
2309 override bool IsDeployable()
2310 {
2311 return true;
2312 }
2313
2314 bool IsOpened()
2315 {
2316 return false;
2317 }
2318
2319 //--- CONSTRUCTION KIT
2321 {
2325
2326 return construction_kit;
2327 }
2328
2330 {
2331 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2334 }
2335
2336 protected vector GetKitSpawnPosition()
2337 {
2338 return GetPosition();
2339 }
2340
2341 protected string GetConstructionKitType()
2342 {
2343 return "";
2344 }
2345
2347 {
2349 GetGame().ObjectDelete(construction_kit);
2350 }
2351
2352 //--- CONSTRUCTION
2353 void DestroyConstruction()
2354 {
2355 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2356 GetGame().ObjectDelete(this);
2357 }
2358
2359 // --- EVENTS
2360 override void OnStoreSave(ParamsWriteContext ctx)
2361 {
2362 super.OnStoreSave(ctx);
2363
2364 //sync parts 01
2365 ctx.Write(m_SyncParts01);
2366 ctx.Write(m_SyncParts02);
2367 ctx.Write(m_SyncParts03);
2368
2369 ctx.Write(m_HasBase);
2370 }
2371
2372 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2373 {
2374 if (!super.OnStoreLoad(ctx, version))
2375 return false;
2376
2377 //--- Base building data ---
2378 //Restore synced parts data
2379 if (!ctx.Read(m_SyncParts01))
2380 {
2381 m_SyncParts01 = 0; //set default
2382 return false;
2383 }
2384 if (!ctx.Read(m_SyncParts02))
2385 {
2386 m_SyncParts02 = 0; //set default
2387 return false;
2388 }
2389 if (!ctx.Read(m_SyncParts03))
2390 {
2391 m_SyncParts03 = 0; //set default
2392 return false;
2393 }
2394
2395 //has base
2396 if (!ctx.Read(m_HasBase))
2397 {
2398 m_HasBase = false;
2399 return false;
2400 }
2401 //---
2402
2403 return true;
2404 }
2405
2406 override void AfterStoreLoad()
2407 {
2408 super.AfterStoreLoad();
2409
2412 }
2413
2415 {
2416 //update server data
2418
2419 //set base state
2420 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2421 SetBaseState(construction_part.IsBuilt()) ;
2422
2423 //synchronize after load
2425 }
2426
2427 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2428 {
2430 return;
2431
2432 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2433
2434 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2435 return;
2436
2438 string part_name = zone;
2439 part_name.ToLower();
2440
2442 {
2444
2445 if (construction_part && construction.IsPartConstructed(part_name))
2446 {
2447 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2448 construction.DestroyConnectedParts(part_name);
2449 }
2450
2451 //barbed wire handling (hack-ish)
2452 if (part_name.Contains("barbed"))
2453 {
2454 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2455 if (barbed_wire)
2456 barbed_wire.SetMountedState(false);
2457 }
2458 }
2459 }
2460
2461 override void EEOnAfterLoad()
2462 {
2464 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2465
2466 super.EEOnAfterLoad();
2467 }
2468
2469 override void EEInit()
2470 {
2471 super.EEInit();
2472
2473 // init visuals and physics
2474 InitBaseState();
2475
2476 //debug
2477#ifdef DEVELOPER
2479#endif
2480 }
2481
2482 override void EEItemAttached(EntityAI item, string slot_name)
2483 {
2484 super.EEItemAttached(item, slot_name);
2485
2487 UpdateVisuals();
2489 }
2490
2491 override void EEItemDetached(EntityAI item, string slot_name)
2492 {
2493 super.EEItemDetached(item, slot_name);
2494
2495 UpdateVisuals();
2497 }
2498
2499 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2500 {
2502 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2503
2506 }
2507
2508 //ignore out of reach condition
2509 override bool IgnoreOutOfReachCondition()
2510 {
2511 return true;
2512 }
2513
2514 //CONSTRUCTION EVENTS
2515 //Build
2516 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2517 {
2519
2520 //check base state
2521 if (construtionPart.IsBase())
2522 {
2523 SetBaseState(true);
2524
2525 //spawn kit
2527 }
2528
2529 //register constructed parts for synchronization
2531
2532 //register action that was performed on part
2534
2535 //synchronize
2537
2538 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2539
2540 UpdateNavmesh();
2541
2542 //update visuals
2543 UpdateVisuals();
2544
2545 //reset action sync data
2546 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2547 }
2548
2549 void OnPartBuiltClient(string part_name, int action_id)
2550 {
2551 //play sound
2553 }
2554
2555 //Dismantle
2557 {
2558 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2560
2561 //register constructed parts for synchronization
2563
2564 //register action that was performed on part
2566
2567 //synchronize
2569
2570 // server part of sync, client will be synced from SetPartsFromSyncData
2572
2573 UpdateNavmesh();
2574
2575 //update visuals
2576 UpdateVisuals();
2577
2578 //reset action sync data
2579 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2580
2581 //check base state
2582 if (construtionPart.IsBase())
2583 {
2584 //Destroy construction
2585 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2586 }
2587 }
2588
2590 {
2591 //play sound
2593 }
2594
2595 //Destroy
2597 {
2598 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2600
2601 //register constructed parts for synchronization
2603
2604 //register action that was performed on part
2606
2607 //synchronize
2609
2610 // server part of sync, client will be synced from SetPartsFromSyncData
2612
2613 UpdateNavmesh();
2614
2615 //update visuals
2616 UpdateVisuals();
2617
2618 //reset action sync data
2619 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2620
2621 //check base state
2622 if (construtionPart.IsBase())
2623 {
2624 //Destroy construction
2625 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2626 }
2627 }
2628
2629 void OnPartDestroyedClient(string part_name, int action_id)
2630 {
2631 //play sound
2633 }
2634
2635 // --- UPDATE
2636 void InitBaseState()
2637 {
2638 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2639
2640 InitVisuals();
2641 UpdateNavmesh(); //regenerate navmesh
2642 GetConstruction().InitBaseState();
2643 }
2644
2645 void InitVisuals()
2646 {
2647 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2648 //check base
2649 if (!HasBase())
2650 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2651 else
2652 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2653
2654 GetConstruction().UpdateVisuals();
2655 }
2656
2657 void UpdateVisuals()
2658 {
2660
2662 foreach (string slotName : attachmentSlots)
2664
2665 //check base
2666 if (!HasBase())
2667 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2668 else
2669 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2670
2671 GetConstruction().UpdateVisuals();
2672 }
2673
2675 {
2676 string slotNameMounted = slot_name + "_Mounted";
2677 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2678
2679 if (attachment)
2680 {
2681 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2682 if (barbedWire && barbedWire.IsMounted())
2684 else
2686
2687 if (is_locked)
2688 {
2689 SetAnimationPhase(slotNameMounted, 0);
2690 SetAnimationPhase(slot_name, 1);
2691 }
2692 else
2693 {
2694 SetAnimationPhase(slotNameMounted, 1);
2695 SetAnimationPhase(slot_name, 0);
2696 }
2697 }
2698 else
2699 {
2700 SetAnimationPhase(slotNameMounted, 1);
2701 SetAnimationPhase(slot_name, 1);
2702
2704 }
2705 }
2706
2707 // avoid calling this function on frequent occasions, it's a massive performance hit
2708 void UpdatePhysics()
2709 {
2711 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2712
2715
2717 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2718
2719 foreach (string slotName : attachmentSlots)
2721
2722 //check base
2723 if (!HasBase())
2724 {
2726 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2727
2728 AddProxyPhysics(ANIMATION_DEPLOYED);
2729 }
2730 else
2731 {
2733 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2734
2735 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2736 }
2737
2738 GetConstruction().UpdatePhysics();
2739 UpdateNavmesh();
2740 }
2741
2743 {
2744 //checks for invalid appends; hotfix
2745 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2746 return;
2747 //----------------------------------
2748 string slot_name_mounted = slot_name + "_Mounted";
2749 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2750
2751 //remove proxy physics
2752 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2753 RemoveProxyPhysics(slot_name_mounted);
2754 RemoveProxyPhysics(slot_name);
2755
2756 if (attachment)
2757 {
2758 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2759 if (is_locked)
2760 {
2761 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2762 AddProxyPhysics(slot_name_mounted);
2763 }
2764 else
2765 {
2766 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2767 AddProxyPhysics(slot_name);
2768 }
2769 }
2770 }
2771
2772 protected void UpdateNavmesh()
2773 {
2774 SetAffectPathgraph(true, false);
2775 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2776 }
2777
2778 override bool CanUseConstruction()
2779 {
2780 return true;
2781 }
2782
2783 override bool CanUseConstructionBuild()
2784 {
2785 return true;
2786 }
2787
2789 {
2790 if (attachment)
2791 {
2793 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2794
2795 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2796 }
2797
2798 return false;
2799 }
2800
2801 protected bool IsAttachmentSlotLocked(string slot_name)
2802 {
2803 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2804 }
2805
2806 //--- ATTACHMENT SLOTS
2808 {
2809 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2810 if (GetGame().ConfigIsExisting(config_path))
2811 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2812 }
2813
2815 {
2816 return true;
2817 }
2818
2819 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2820 {
2821 return true;
2822 }
2823
2824 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2825 {
2826 return true;
2827 }
2828
2829 // --- INIT
2830 void ConstructionInit()
2831 {
2832 if (!m_Construction)
2833 m_Construction = new Construction(this);
2834
2835 GetConstruction().Init();
2836 }
2837
2839 {
2840 return m_Construction;
2841 }
2842
2843 //--- INVENTORY/ATTACHMENTS CONDITIONS
2844 //attachments
2846 {
2847 return super.CanReceiveAttachment(attachment, slotId);
2848 }
2849
2851 {
2852 int attachment_count = GetInventory().AttachmentCount();
2853 if (attachment_count > 0)
2854 {
2855 if (HasBase() && attachment_count == 1)
2856 return false;
2857
2858 return true;
2859 }
2860
2861 return false;
2862 }
2863
2864 override bool ShowZonesHealth()
2865 {
2866 return true;
2867 }
2868
2869 //this into/outo parent.Cargo
2870 override bool CanPutInCargo(EntityAI parent)
2871 {
2872 return false;
2873 }
2874
2875 override bool CanRemoveFromCargo(EntityAI parent)
2876 {
2877 return false;
2878 }
2879
2880 //hands
2881 override bool CanPutIntoHands(EntityAI parent)
2882 {
2883 return false;
2884 }
2885
2886 //--- ACTION CONDITIONS
2887 //direction
2888 override bool IsFacingPlayer(PlayerBase player, string selection)
2889 {
2890 return true;
2891 }
2892
2893 override bool IsPlayerInside(PlayerBase player, string selection)
2894 {
2895 return true;
2896 }
2897
2900 {
2901 return false;
2902 }
2903
2904 //camera direction check
2905 bool IsFacingCamera(string selection)
2906 {
2907 return true;
2908 }
2909
2910 //roof check
2912 {
2913 return false;
2914 }
2915
2916 //selection->player distance check
2917 bool HasProperDistance(string selection, PlayerBase player)
2918 {
2919 return true;
2920 }
2921
2922 //folding
2924 {
2925 if (HasBase() || GetInventory().AttachmentCount() > 0)
2926 return false;
2927
2928 return true;
2929 }
2930
2932 {
2935
2936 return item;
2937 }
2938
2939 //Damage triggers (barbed wire)
2940 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2941 {
2942 if (GetGame() && GetGame().IsServer())
2943 {
2944 //destroy area damage if some already exists
2946
2947 //create new area damage
2949 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2950
2951 vector min_max[2];
2952 if (MemoryPointExists(slot_name + "_min"))
2953 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2954 if (MemoryPointExists(slot_name + "_max"))
2955 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2956
2957 //get proper trigger extents (min<max)
2958 vector extents[2];
2959 GetConstruction().GetTriggerExtents(min_max, extents);
2960
2961 //get box center
2962 vector center;
2963 center = GetConstruction().GetBoxCenter(min_max);
2964 center = ModelToWorld(center);
2965
2966 //rotate center if needed
2969
2970 areaDamage.SetExtents(extents[0], extents[1]);
2971 areaDamage.SetAreaPosition(center);
2972 areaDamage.SetAreaOrientation(orientation);
2973 areaDamage.SetLoopInterval(1.0);
2974 areaDamage.SetDeferDuration(0.2);
2975 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2976 areaDamage.SetAmmoName("BarbedWireHit");
2977 areaDamage.Spawn();
2978
2980 }
2981 }
2982
2984 {
2985 if (angle_deg != 0)
2986 {
2987 //orientation
2989
2990 //center
2992 if (MemoryPointExists("rotate_axis"))
2993 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2996 center[0] = r_center_x;
2997 center[2] = r_center_z;
2998 }
2999 }
3000
3001 void DestroyAreaDamage(string slot_name)
3002 {
3003 if (GetGame() && GetGame().IsServer())
3004 {
3007 {
3008 if (areaDamage)
3009 areaDamage.Destroy();
3010
3012 }
3013 }
3014 }
3015
3016 override bool IsIgnoredByConstruction()
3017 {
3018 return true;
3019 }
3020
3021 //================================================================
3022 // SOUNDS
3023 //================================================================
3024 protected void SoundBuildStart(string part_name)
3025 {
3026 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3027 }
3028
3029 protected void SoundDismantleStart(string part_name)
3030 {
3031 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3032 }
3033
3034 protected void SoundDestroyStart(string part_name)
3035 {
3036 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3037 }
3038
3039 protected string GetBuildSoundByMaterial(string part_name)
3040 {
3042
3043 switch (material_type)
3044 {
3045 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3046 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3047 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3048 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3049 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3050 }
3051
3052 return "";
3053 }
3054
3055 protected string GetDismantleSoundByMaterial(string part_name)
3056 {
3058
3059 switch (material_type)
3060 {
3061 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3062 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3063 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3064 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3065 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3066 }
3067
3068 return "";
3069 }
3070
3071 //misc
3073 {
3074 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3075 {
3076 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3078 SetHealth(slot_name, "Health", item.GetHealth());
3079 }
3080 }
3081
3082 override int GetDamageSystemVersionChange()
3083 {
3084 return 111;
3085 }
3086
3087 override void SetActions()
3088 {
3089 super.SetActions();
3090
3092 //AddAction(ActionTakeHybridAttachment);
3093 //AddAction(ActionTakeHybridAttachmentToHands);
3096 }
3097
3098 //================================================================
3099 // DEBUG
3100 //================================================================
3101 protected void DebugCustomState()
3102 {
3103 }
3104
3107 {
3108 return null;
3109 }
3110
3111 override void OnDebugSpawn()
3112 {
3113 FullyBuild();
3114 }
3115
3116 void FullyBuild()
3117 {
3119 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3120
3121 Man p;
3122
3123#ifdef SERVER
3125 GetGame().GetWorld().GetPlayerList(players);
3126 if (players.Count())
3127 p = players[0];
3128#else
3129 p = GetGame().GetPlayer();
3130#endif
3131
3132 foreach (ConstructionPart part : parts)
3133 {
3134 bool excluded = false;
3135 string partName = part.GetPartName();
3136 if (excludes)
3137 {
3138 foreach (string exclude : excludes)
3139 {
3140 if (partName.Contains(exclude))
3141 {
3142 excluded = true;
3143 break;
3144 }
3145 }
3146 }
3147
3148 if (!excluded)
3150 }
3151
3152 GetConstruction().UpdateVisuals();
3153 }
3154}
3155
3156void bsbDebugPrint(string s)
3157{
3158#ifdef BSB_DEBUG
3159 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3160#else
3161 //Print("" + s); // comment/uncomment to hide/see debug logs
3162#endif
3163}
3164void bsbDebugSpam(string s)
3165{
3166#ifdef BSB_DEBUG_SPAM
3167 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3168#else
3169 //Print("" + s); // comment/uncomment to hide/see debug logs
3170#endif
3171}

Referenced by ItemBase::UpdatePhysics(), and ItemBase::UpdateVisuals().

◆ IsAttachmentSlotLocked() [2/2]

bool bsbDebugPrint::IsAttachmentSlotLocked ( string slot_name)
protected

Definition at line 1994 of file BaseBuildingBase.c.

1996{
1997 const string ANIMATION_DEPLOYED = "Deployed";
1998
1999 float m_ConstructionKitHealth; //stored health value for used construction kit
2000
2002
2003 bool m_HasBase;
2004 //variables for synchronization of base building parts (2x31 is the current limit)
2005 int m_SyncParts01; //synchronization for already built parts (31 parts)
2006 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2007 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2008 int m_InteractedPartId; //construction part id that an action was performed on
2009 int m_PerformedActionId; //action id that was performed on a construction part
2010
2011 //Sounds
2012 //build
2013 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2014 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2015 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2016 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2017 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2018 //dismantle
2019 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2020 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2021 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2022 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2023 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2024
2025 protected EffectSound m_Sound;
2026
2030
2031 // Constructor
2032 void BaseBuildingBase()
2033 {
2035
2036 //synchronized variables
2037 RegisterNetSyncVariableInt("m_SyncParts01");
2038 RegisterNetSyncVariableInt("m_SyncParts02");
2039 RegisterNetSyncVariableInt("m_SyncParts03");
2040 RegisterNetSyncVariableInt("m_InteractedPartId");
2041 RegisterNetSyncVariableInt("m_PerformedActionId");
2042 RegisterNetSyncVariableBool("m_HasBase");
2043
2044 //Construction init
2046
2047 if (ConfigIsExisting("hybridAttachments"))
2048 {
2050 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2051 }
2052 if (ConfigIsExisting("mountables"))
2053 {
2055 ConfigGetTextArray("mountables", m_Mountables);
2056 }
2057
2058 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2059 }
2060
2061 override void EEDelete(EntityAI parent)
2062 {
2063 super.EEDelete(parent);
2064
2065 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2067
2068 }
2069
2070 override string GetInvulnerabilityTypeString()
2071 {
2072 return "disableBaseDamage";
2073 }
2074
2075 override bool CanObstruct()
2076 {
2077 return true;
2078 }
2079
2080 override int GetHideIconMask()
2081 {
2082 return EInventoryIconVisibility.HIDE_VICINITY;
2083 }
2084
2085 // --- SYNCHRONIZATION
2087 {
2088 if (GetGame().IsServer())
2089 SetSynchDirty();
2090 }
2091
2092 override void OnVariablesSynchronized()
2093 {
2094 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2095 super.OnVariablesSynchronized();
2096
2097 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2098 }
2099
2100 protected void OnSynchronizedClient()
2101 {
2102 //update parts
2104
2105 //update action on part
2107
2108 //update visuals (client)
2109 UpdateVisuals();
2110 }
2111
2112 //parts synchronization
2114 {
2115 //part_id must starts from index = 1
2116 int offset;
2117 int mask;
2118
2119 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2120 {
2121 offset = part_id - 1;
2122 mask = 1 << offset;
2123
2125 }
2126 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2127 {
2128 offset = (part_id % 32);
2129 mask = 1 << offset;
2130
2132 }
2133 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2134 {
2135 offset = (part_id % 63);
2136 mask = 1 << offset;
2137
2139 }
2140 }
2141
2143 {
2144 //part_id must starts from index = 1
2145 int offset;
2146 int mask;
2147
2148 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2149 {
2150 offset = part_id - 1;
2151 mask = 1 << offset;
2152
2154 }
2155 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2156 {
2157 offset = (part_id % 32);
2158 mask = 1 << offset;
2159
2161 }
2162 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2163 {
2164 offset = (part_id % 63);
2165 mask = 1 << offset;
2166
2168 }
2169 }
2170
2172 {
2173 //part_id must starts from index = 1
2174 int offset;
2175 int mask;
2176
2177 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2178 {
2179 offset = part_id - 1;
2180 mask = 1 << offset;
2181
2182 if ((m_SyncParts01 & mask) > 0)
2183 return true;
2184 }
2185 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2186 {
2187 offset = (part_id % 32);
2188 mask = 1 << offset;
2189
2190 if ((m_SyncParts02 & mask) > 0)
2191 return true;
2192 }
2193 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2194 {
2195 offset = (part_id % 63);
2196 mask = 1 << offset;
2197
2198 if ((m_SyncParts03 & mask) > 0)
2199 return true;
2200 }
2201
2202 return false;
2203 }
2204
2205 protected void RegisterActionForSync(int part_id, int action_id)
2206 {
2209 }
2210
2211 protected void ResetActionSyncData()
2212 {
2213 //reset data
2214 m_InteractedPartId = -1;
2216 }
2217
2218 protected void SetActionFromSyncData()
2219 {
2220 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2221 {
2224
2225 switch (build_action_id)
2226 {
2230 }
2231 }
2232 }
2233 //------
2234
2236 {
2237 string key = part.m_PartName;
2238 bool is_base = part.IsBase();
2240 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2242 {
2243 if (!part.IsBuilt())
2244 {
2245 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2246 GetConstruction().AddToConstructedParts(key);
2247 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2248
2249 if (is_base)
2250 {
2252 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2253 }
2254 }
2255 }
2256 else
2257 {
2258 if (part.IsBuilt())
2259 {
2260 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2261 GetConstruction().RemoveFromConstructedParts(key);
2262 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2263
2264 if (is_base)
2265 {
2267 AddProxyPhysics(ANIMATION_DEPLOYED);
2268 }
2269 }
2270 }
2271
2272 //check slot lock for material attachments
2273 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2274 }
2275
2276 //set construction parts based on synchronized data
2278 {
2281
2282 for (int i = 0; i < construction_parts.Count(); ++i)
2283 {
2284 string key = construction_parts.GetKey(i);
2287 }
2288
2289 //regenerate navmesh
2290 UpdateNavmesh();
2291 }
2292
2294 {
2297
2298 for (int i = 0; i < construction_parts.Count(); ++i)
2299 {
2300 string key = construction_parts.GetKey(i);
2302
2303 if (value.GetId() == id)
2304 return value;
2305 }
2306
2307 return NULL;
2308 }
2309 //
2310
2311 //Base
2312 bool HasBase()
2313 {
2314 return m_HasBase;
2315 }
2316
2317 void SetBaseState(bool has_base)
2318 {
2320 }
2321
2322 override bool IsDeployable()
2323 {
2324 return true;
2325 }
2326
2327 bool IsOpened()
2328 {
2329 return false;
2330 }
2331
2332 //--- CONSTRUCTION KIT
2334 {
2338
2339 return construction_kit;
2340 }
2341
2343 {
2344 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2347 }
2348
2349 protected vector GetKitSpawnPosition()
2350 {
2351 return GetPosition();
2352 }
2353
2354 protected string GetConstructionKitType()
2355 {
2356 return "";
2357 }
2358
2360 {
2362 GetGame().ObjectDelete(construction_kit);
2363 }
2364
2365 //--- CONSTRUCTION
2366 void DestroyConstruction()
2367 {
2368 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2369 GetGame().ObjectDelete(this);
2370 }
2371
2372 // --- EVENTS
2373 override void OnStoreSave(ParamsWriteContext ctx)
2374 {
2375 super.OnStoreSave(ctx);
2376
2377 //sync parts 01
2378 ctx.Write(m_SyncParts01);
2379 ctx.Write(m_SyncParts02);
2380 ctx.Write(m_SyncParts03);
2381
2382 ctx.Write(m_HasBase);
2383 }
2384
2385 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2386 {
2387 if (!super.OnStoreLoad(ctx, version))
2388 return false;
2389
2390 //--- Base building data ---
2391 //Restore synced parts data
2392 if (!ctx.Read(m_SyncParts01))
2393 {
2394 m_SyncParts01 = 0; //set default
2395 return false;
2396 }
2397 if (!ctx.Read(m_SyncParts02))
2398 {
2399 m_SyncParts02 = 0; //set default
2400 return false;
2401 }
2402 if (!ctx.Read(m_SyncParts03))
2403 {
2404 m_SyncParts03 = 0; //set default
2405 return false;
2406 }
2407
2408 //has base
2409 if (!ctx.Read(m_HasBase))
2410 {
2411 m_HasBase = false;
2412 return false;
2413 }
2414 //---
2415
2416 return true;
2417 }
2418
2419 override void AfterStoreLoad()
2420 {
2421 super.AfterStoreLoad();
2422
2425 }
2426
2428 {
2429 //update server data
2431
2432 //set base state
2433 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2434 SetBaseState(construction_part.IsBuilt()) ;
2435
2436 //synchronize after load
2438 }
2439
2440 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2441 {
2443 return;
2444
2445 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2446
2447 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2448 return;
2449
2451 string part_name = zone;
2452 part_name.ToLower();
2453
2455 {
2457
2458 if (construction_part && construction.IsPartConstructed(part_name))
2459 {
2460 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2461 construction.DestroyConnectedParts(part_name);
2462 }
2463
2464 //barbed wire handling (hack-ish)
2465 if (part_name.Contains("barbed"))
2466 {
2467 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2468 if (barbed_wire)
2469 barbed_wire.SetMountedState(false);
2470 }
2471 }
2472 }
2473
2474 override void EEOnAfterLoad()
2475 {
2477 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2478
2479 super.EEOnAfterLoad();
2480 }
2481
2482 override void EEInit()
2483 {
2484 super.EEInit();
2485
2486 // init visuals and physics
2487 InitBaseState();
2488
2489 //debug
2490#ifdef DEVELOPER
2492#endif
2493 }
2494
2495 override void EEItemAttached(EntityAI item, string slot_name)
2496 {
2497 super.EEItemAttached(item, slot_name);
2498
2500 UpdateVisuals();
2502 }
2503
2504 override void EEItemDetached(EntityAI item, string slot_name)
2505 {
2506 super.EEItemDetached(item, slot_name);
2507
2508 UpdateVisuals();
2510 }
2511
2512 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2513 {
2515 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2516
2519 }
2520
2521 //ignore out of reach condition
2522 override bool IgnoreOutOfReachCondition()
2523 {
2524 return true;
2525 }
2526
2527 //CONSTRUCTION EVENTS
2528 //Build
2529 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2530 {
2532
2533 //check base state
2534 if (construtionPart.IsBase())
2535 {
2536 SetBaseState(true);
2537
2538 //spawn kit
2540 }
2541
2542 //register constructed parts for synchronization
2544
2545 //register action that was performed on part
2547
2548 //synchronize
2550
2551 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2552
2553 UpdateNavmesh();
2554
2555 //update visuals
2556 UpdateVisuals();
2557
2558 //reset action sync data
2559 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2560 }
2561
2562 void OnPartBuiltClient(string part_name, int action_id)
2563 {
2564 //play sound
2566 }
2567
2568 //Dismantle
2570 {
2571 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2573
2574 //register constructed parts for synchronization
2576
2577 //register action that was performed on part
2579
2580 //synchronize
2582
2583 // server part of sync, client will be synced from SetPartsFromSyncData
2585
2586 UpdateNavmesh();
2587
2588 //update visuals
2589 UpdateVisuals();
2590
2591 //reset action sync data
2592 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2593
2594 //check base state
2595 if (construtionPart.IsBase())
2596 {
2597 //Destroy construction
2598 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2599 }
2600 }
2601
2603 {
2604 //play sound
2606 }
2607
2608 //Destroy
2610 {
2611 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2613
2614 //register constructed parts for synchronization
2616
2617 //register action that was performed on part
2619
2620 //synchronize
2622
2623 // server part of sync, client will be synced from SetPartsFromSyncData
2625
2626 UpdateNavmesh();
2627
2628 //update visuals
2629 UpdateVisuals();
2630
2631 //reset action sync data
2632 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2633
2634 //check base state
2635 if (construtionPart.IsBase())
2636 {
2637 //Destroy construction
2638 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2639 }
2640 }
2641
2642 void OnPartDestroyedClient(string part_name, int action_id)
2643 {
2644 //play sound
2646 }
2647
2648 // --- UPDATE
2649 void InitBaseState()
2650 {
2651 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2652
2653 InitVisuals();
2654 UpdateNavmesh(); //regenerate navmesh
2655 GetConstruction().InitBaseState();
2656 }
2657
2658 void InitVisuals()
2659 {
2660 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2661 //check base
2662 if (!HasBase())
2663 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2664 else
2665 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2666
2667 GetConstruction().UpdateVisuals();
2668 }
2669
2670 void UpdateVisuals()
2671 {
2673
2675 foreach (string slotName : attachmentSlots)
2677
2678 //check base
2679 if (!HasBase())
2680 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2681 else
2682 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2683
2684 GetConstruction().UpdateVisuals();
2685 }
2686
2688 {
2689 string slotNameMounted = slot_name + "_Mounted";
2690 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2691
2692 if (attachment)
2693 {
2694 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2695 if (barbedWire && barbedWire.IsMounted())
2697 else
2699
2700 if (is_locked)
2701 {
2702 SetAnimationPhase(slotNameMounted, 0);
2703 SetAnimationPhase(slot_name, 1);
2704 }
2705 else
2706 {
2707 SetAnimationPhase(slotNameMounted, 1);
2708 SetAnimationPhase(slot_name, 0);
2709 }
2710 }
2711 else
2712 {
2713 SetAnimationPhase(slotNameMounted, 1);
2714 SetAnimationPhase(slot_name, 1);
2715
2717 }
2718 }
2719
2720 // avoid calling this function on frequent occasions, it's a massive performance hit
2721 void UpdatePhysics()
2722 {
2724 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2725
2728
2730 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2731
2732 foreach (string slotName : attachmentSlots)
2734
2735 //check base
2736 if (!HasBase())
2737 {
2739 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2740
2741 AddProxyPhysics(ANIMATION_DEPLOYED);
2742 }
2743 else
2744 {
2746 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2747
2748 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2749 }
2750
2751 GetConstruction().UpdatePhysics();
2752 UpdateNavmesh();
2753 }
2754
2756 {
2757 //checks for invalid appends; hotfix
2758 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2759 return;
2760 //----------------------------------
2761 string slot_name_mounted = slot_name + "_Mounted";
2762 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2763
2764 //remove proxy physics
2765 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2766 RemoveProxyPhysics(slot_name_mounted);
2767 RemoveProxyPhysics(slot_name);
2768
2769 if (attachment)
2770 {
2771 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2772 if (is_locked)
2773 {
2774 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2775 AddProxyPhysics(slot_name_mounted);
2776 }
2777 else
2778 {
2779 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2780 AddProxyPhysics(slot_name);
2781 }
2782 }
2783 }
2784
2785 protected void UpdateNavmesh()
2786 {
2787 SetAffectPathgraph(true, false);
2788 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2789 }
2790
2791 override bool CanUseConstruction()
2792 {
2793 return true;
2794 }
2795
2796 override bool CanUseConstructionBuild()
2797 {
2798 return true;
2799 }
2800
2802 {
2803 if (attachment)
2804 {
2806 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2807
2808 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2809 }
2810
2811 return false;
2812 }
2813
2814 protected bool IsAttachmentSlotLocked(string slot_name)
2815 {
2816 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2817 }
2818
2819 //--- ATTACHMENT SLOTS
2821 {
2822 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2823 if (GetGame().ConfigIsExisting(config_path))
2824 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2825 }
2826
2828 {
2829 return true;
2830 }
2831
2832 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2833 {
2834 return true;
2835 }
2836
2837 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2838 {
2839 return true;
2840 }
2841
2842 // --- INIT
2843 void ConstructionInit()
2844 {
2845 if (!m_Construction)
2846 m_Construction = new Construction(this);
2847
2848 GetConstruction().Init();
2849 }
2850
2852 {
2853 return m_Construction;
2854 }
2855
2856 //--- INVENTORY/ATTACHMENTS CONDITIONS
2857 //attachments
2859 {
2860 return super.CanReceiveAttachment(attachment, slotId);
2861 }
2862
2864 {
2865 int attachment_count = GetInventory().AttachmentCount();
2866 if (attachment_count > 0)
2867 {
2868 if (HasBase() && attachment_count == 1)
2869 return false;
2870
2871 return true;
2872 }
2873
2874 return false;
2875 }
2876
2877 override bool ShowZonesHealth()
2878 {
2879 return true;
2880 }
2881
2882 //this into/outo parent.Cargo
2883 override bool CanPutInCargo(EntityAI parent)
2884 {
2885 return false;
2886 }
2887
2888 override bool CanRemoveFromCargo(EntityAI parent)
2889 {
2890 return false;
2891 }
2892
2893 //hands
2894 override bool CanPutIntoHands(EntityAI parent)
2895 {
2896 return false;
2897 }
2898
2899 //--- ACTION CONDITIONS
2900 //direction
2901 override bool IsFacingPlayer(PlayerBase player, string selection)
2902 {
2903 return true;
2904 }
2905
2906 override bool IsPlayerInside(PlayerBase player, string selection)
2907 {
2908 return true;
2909 }
2910
2913 {
2914 return false;
2915 }
2916
2917 //camera direction check
2918 bool IsFacingCamera(string selection)
2919 {
2920 return true;
2921 }
2922
2923 //roof check
2925 {
2926 return false;
2927 }
2928
2929 //selection->player distance check
2930 bool HasProperDistance(string selection, PlayerBase player)
2931 {
2932 return true;
2933 }
2934
2935 //folding
2937 {
2938 if (HasBase() || GetInventory().AttachmentCount() > 0)
2939 return false;
2940
2941 return true;
2942 }
2943
2945 {
2948
2949 return item;
2950 }
2951
2952 //Damage triggers (barbed wire)
2953 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2954 {
2955 if (GetGame() && GetGame().IsServer())
2956 {
2957 //destroy area damage if some already exists
2959
2960 //create new area damage
2962 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2963
2964 vector min_max[2];
2965 if (MemoryPointExists(slot_name + "_min"))
2966 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2967 if (MemoryPointExists(slot_name + "_max"))
2968 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2969
2970 //get proper trigger extents (min<max)
2971 vector extents[2];
2972 GetConstruction().GetTriggerExtents(min_max, extents);
2973
2974 //get box center
2975 vector center;
2976 center = GetConstruction().GetBoxCenter(min_max);
2977 center = ModelToWorld(center);
2978
2979 //rotate center if needed
2982
2983 areaDamage.SetExtents(extents[0], extents[1]);
2984 areaDamage.SetAreaPosition(center);
2985 areaDamage.SetAreaOrientation(orientation);
2986 areaDamage.SetLoopInterval(1.0);
2987 areaDamage.SetDeferDuration(0.2);
2988 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2989 areaDamage.SetAmmoName("BarbedWireHit");
2990 areaDamage.Spawn();
2991
2993 }
2994 }
2995
2997 {
2998 if (angle_deg != 0)
2999 {
3000 //orientation
3002
3003 //center
3005 if (MemoryPointExists("rotate_axis"))
3006 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3009 center[0] = r_center_x;
3010 center[2] = r_center_z;
3011 }
3012 }
3013
3014 void DestroyAreaDamage(string slot_name)
3015 {
3016 if (GetGame() && GetGame().IsServer())
3017 {
3020 {
3021 if (areaDamage)
3022 areaDamage.Destroy();
3023
3025 }
3026 }
3027 }
3028
3029 override bool IsIgnoredByConstruction()
3030 {
3031 return true;
3032 }
3033
3034 //================================================================
3035 // SOUNDS
3036 //================================================================
3037 protected void SoundBuildStart(string part_name)
3038 {
3039 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3040 }
3041
3042 protected void SoundDismantleStart(string part_name)
3043 {
3044 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3045 }
3046
3047 protected void SoundDestroyStart(string part_name)
3048 {
3049 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3050 }
3051
3052 protected string GetBuildSoundByMaterial(string part_name)
3053 {
3055
3056 switch (material_type)
3057 {
3058 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3059 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3060 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3061 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3062 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3063 }
3064
3065 return "";
3066 }
3067
3068 protected string GetDismantleSoundByMaterial(string part_name)
3069 {
3071
3072 switch (material_type)
3073 {
3074 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3075 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3076 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3077 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3078 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3079 }
3080
3081 return "";
3082 }
3083
3084 //misc
3086 {
3087 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3088 {
3089 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3091 SetHealth(slot_name, "Health", item.GetHealth());
3092 }
3093 }
3094
3095 override int GetDamageSystemVersionChange()
3096 {
3097 return 111;
3098 }
3099
3100 override void SetActions()
3101 {
3102 super.SetActions();
3103
3105 //AddAction(ActionTakeHybridAttachment);
3106 //AddAction(ActionTakeHybridAttachmentToHands);
3109 }
3110
3111 //================================================================
3112 // DEBUG
3113 //================================================================
3114 protected void DebugCustomState()
3115 {
3116 }
3117
3120 {
3121 return null;
3122 }
3123
3124 override void OnDebugSpawn()
3125 {
3126 FullyBuild();
3127 }
3128
3129 void FullyBuild()
3130 {
3132 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3133
3134 Man p;
3135
3136#ifdef SERVER
3138 GetGame().GetWorld().GetPlayerList(players);
3139 if (players.Count())
3140 p = players[0];
3141#else
3142 p = GetGame().GetPlayer();
3143#endif
3144
3145 foreach (ConstructionPart part : parts)
3146 {
3147 bool excluded = false;
3148 string partName = part.GetPartName();
3149 if (excludes)
3150 {
3151 foreach (string exclude : excludes)
3152 {
3153 if (partName.Contains(exclude))
3154 {
3155 excluded = true;
3156 break;
3157 }
3158 }
3159 }
3160
3161 if (!excluded)
3163 }
3164
3165 GetConstruction().UpdateVisuals();
3166 }
3167}
3168
3169void bsbDebugPrint(string s)
3170{
3171#ifdef BSB_DEBUG
3172 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3173#else
3174 //Print("" + s); // comment/uncomment to hide/see debug logs
3175#endif
3176}
3177void bsbDebugSpam(string s)
3178{
3179#ifdef BSB_DEBUG_SPAM
3180 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3181#else
3182 //Print("" + s); // comment/uncomment to hide/see debug logs
3183#endif
3184}

◆ IsDeployable()

override bool bsbDebugPrint::IsDeployable ( )
protected

Definition at line 1502 of file BaseBuildingBase.c.

1504{
1505 const string ANIMATION_DEPLOYED = "Deployed";
1506
1507 float m_ConstructionKitHealth; //stored health value for used construction kit
1508
1510
1511 bool m_HasBase;
1512 //variables for synchronization of base building parts (2x31 is the current limit)
1513 int m_SyncParts01; //synchronization for already built parts (31 parts)
1514 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1515 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1516 int m_InteractedPartId; //construction part id that an action was performed on
1517 int m_PerformedActionId; //action id that was performed on a construction part
1518
1519 //Sounds
1520 //build
1521 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1522 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1523 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1524 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1525 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1526 //dismantle
1527 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1528 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1529 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1530 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1531 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1532
1533 protected EffectSound m_Sound;
1534
1538
1539 // Constructor
1540 void BaseBuildingBase()
1541 {
1543
1544 //synchronized variables
1545 RegisterNetSyncVariableInt("m_SyncParts01");
1546 RegisterNetSyncVariableInt("m_SyncParts02");
1547 RegisterNetSyncVariableInt("m_SyncParts03");
1548 RegisterNetSyncVariableInt("m_InteractedPartId");
1549 RegisterNetSyncVariableInt("m_PerformedActionId");
1550 RegisterNetSyncVariableBool("m_HasBase");
1551
1552 //Construction init
1554
1555 if (ConfigIsExisting("hybridAttachments"))
1556 {
1558 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1559 }
1560 if (ConfigIsExisting("mountables"))
1561 {
1563 ConfigGetTextArray("mountables", m_Mountables);
1564 }
1565
1566 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1567 }
1568
1569 override void EEDelete(EntityAI parent)
1570 {
1571 super.EEDelete(parent);
1572
1573 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1575
1576 }
1577
1578 override string GetInvulnerabilityTypeString()
1579 {
1580 return "disableBaseDamage";
1581 }
1582
1583 override bool CanObstruct()
1584 {
1585 return true;
1586 }
1587
1588 override int GetHideIconMask()
1589 {
1590 return EInventoryIconVisibility.HIDE_VICINITY;
1591 }
1592
1593 // --- SYNCHRONIZATION
1595 {
1596 if (GetGame().IsServer())
1597 SetSynchDirty();
1598 }
1599
1600 override void OnVariablesSynchronized()
1601 {
1602 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1603 super.OnVariablesSynchronized();
1604
1605 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1606 }
1607
1608 protected void OnSynchronizedClient()
1609 {
1610 //update parts
1612
1613 //update action on part
1615
1616 //update visuals (client)
1617 UpdateVisuals();
1618 }
1619
1620 //parts synchronization
1622 {
1623 //part_id must starts from index = 1
1624 int offset;
1625 int mask;
1626
1627 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1628 {
1629 offset = part_id - 1;
1630 mask = 1 << offset;
1631
1633 }
1634 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1635 {
1636 offset = (part_id % 32);
1637 mask = 1 << offset;
1638
1640 }
1641 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1642 {
1643 offset = (part_id % 63);
1644 mask = 1 << offset;
1645
1647 }
1648 }
1649
1651 {
1652 //part_id must starts from index = 1
1653 int offset;
1654 int mask;
1655
1656 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1657 {
1658 offset = part_id - 1;
1659 mask = 1 << offset;
1660
1662 }
1663 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1664 {
1665 offset = (part_id % 32);
1666 mask = 1 << offset;
1667
1669 }
1670 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1671 {
1672 offset = (part_id % 63);
1673 mask = 1 << offset;
1674
1676 }
1677 }
1678
1680 {
1681 //part_id must starts from index = 1
1682 int offset;
1683 int mask;
1684
1685 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1686 {
1687 offset = part_id - 1;
1688 mask = 1 << offset;
1689
1690 if ((m_SyncParts01 & mask) > 0)
1691 return true;
1692 }
1693 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1694 {
1695 offset = (part_id % 32);
1696 mask = 1 << offset;
1697
1698 if ((m_SyncParts02 & mask) > 0)
1699 return true;
1700 }
1701 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1702 {
1703 offset = (part_id % 63);
1704 mask = 1 << offset;
1705
1706 if ((m_SyncParts03 & mask) > 0)
1707 return true;
1708 }
1709
1710 return false;
1711 }
1712
1713 protected void RegisterActionForSync(int part_id, int action_id)
1714 {
1717 }
1718
1719 protected void ResetActionSyncData()
1720 {
1721 //reset data
1722 m_InteractedPartId = -1;
1724 }
1725
1726 protected void SetActionFromSyncData()
1727 {
1728 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1729 {
1732
1733 switch (build_action_id)
1734 {
1738 }
1739 }
1740 }
1741 //------
1742
1744 {
1745 string key = part.m_PartName;
1746 bool is_base = part.IsBase();
1748 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1750 {
1751 if (!part.IsBuilt())
1752 {
1753 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1754 GetConstruction().AddToConstructedParts(key);
1755 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1756
1757 if (is_base)
1758 {
1760 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1761 }
1762 }
1763 }
1764 else
1765 {
1766 if (part.IsBuilt())
1767 {
1768 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1769 GetConstruction().RemoveFromConstructedParts(key);
1770 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1771
1772 if (is_base)
1773 {
1775 AddProxyPhysics(ANIMATION_DEPLOYED);
1776 }
1777 }
1778 }
1779
1780 //check slot lock for material attachments
1781 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1782 }
1783
1784 //set construction parts based on synchronized data
1786 {
1789
1790 for (int i = 0; i < construction_parts.Count(); ++i)
1791 {
1792 string key = construction_parts.GetKey(i);
1795 }
1796
1797 //regenerate navmesh
1798 UpdateNavmesh();
1799 }
1800
1802 {
1805
1806 for (int i = 0; i < construction_parts.Count(); ++i)
1807 {
1808 string key = construction_parts.GetKey(i);
1810
1811 if (value.GetId() == id)
1812 return value;
1813 }
1814
1815 return NULL;
1816 }
1817 //
1818
1819 //Base
1820 bool HasBase()
1821 {
1822 return m_HasBase;
1823 }
1824
1825 void SetBaseState(bool has_base)
1826 {
1828 }
1829
1830 override bool IsDeployable()
1831 {
1832 return true;
1833 }
1834
1835 bool IsOpened()
1836 {
1837 return false;
1838 }
1839
1840 //--- CONSTRUCTION KIT
1842 {
1846
1847 return construction_kit;
1848 }
1849
1851 {
1852 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1855 }
1856
1857 protected vector GetKitSpawnPosition()
1858 {
1859 return GetPosition();
1860 }
1861
1862 protected string GetConstructionKitType()
1863 {
1864 return "";
1865 }
1866
1868 {
1870 GetGame().ObjectDelete(construction_kit);
1871 }
1872
1873 //--- CONSTRUCTION
1874 void DestroyConstruction()
1875 {
1876 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1877 GetGame().ObjectDelete(this);
1878 }
1879
1880 // --- EVENTS
1881 override void OnStoreSave(ParamsWriteContext ctx)
1882 {
1883 super.OnStoreSave(ctx);
1884
1885 //sync parts 01
1886 ctx.Write(m_SyncParts01);
1887 ctx.Write(m_SyncParts02);
1888 ctx.Write(m_SyncParts03);
1889
1890 ctx.Write(m_HasBase);
1891 }
1892
1893 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1894 {
1895 if (!super.OnStoreLoad(ctx, version))
1896 return false;
1897
1898 //--- Base building data ---
1899 //Restore synced parts data
1900 if (!ctx.Read(m_SyncParts01))
1901 {
1902 m_SyncParts01 = 0; //set default
1903 return false;
1904 }
1905 if (!ctx.Read(m_SyncParts02))
1906 {
1907 m_SyncParts02 = 0; //set default
1908 return false;
1909 }
1910 if (!ctx.Read(m_SyncParts03))
1911 {
1912 m_SyncParts03 = 0; //set default
1913 return false;
1914 }
1915
1916 //has base
1917 if (!ctx.Read(m_HasBase))
1918 {
1919 m_HasBase = false;
1920 return false;
1921 }
1922 //---
1923
1924 return true;
1925 }
1926
1927 override void AfterStoreLoad()
1928 {
1929 super.AfterStoreLoad();
1930
1933 }
1934
1936 {
1937 //update server data
1939
1940 //set base state
1941 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1942 SetBaseState(construction_part.IsBuilt()) ;
1943
1944 //synchronize after load
1946 }
1947
1948 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1949 {
1951 return;
1952
1953 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1954
1955 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1956 return;
1957
1959 string part_name = zone;
1960 part_name.ToLower();
1961
1963 {
1965
1966 if (construction_part && construction.IsPartConstructed(part_name))
1967 {
1968 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1969 construction.DestroyConnectedParts(part_name);
1970 }
1971
1972 //barbed wire handling (hack-ish)
1973 if (part_name.Contains("barbed"))
1974 {
1975 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1976 if (barbed_wire)
1977 barbed_wire.SetMountedState(false);
1978 }
1979 }
1980 }
1981
1982 override void EEOnAfterLoad()
1983 {
1985 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1986
1987 super.EEOnAfterLoad();
1988 }
1989
1990 override void EEInit()
1991 {
1992 super.EEInit();
1993
1994 // init visuals and physics
1995 InitBaseState();
1996
1997 //debug
1998#ifdef DEVELOPER
2000#endif
2001 }
2002
2003 override void EEItemAttached(EntityAI item, string slot_name)
2004 {
2005 super.EEItemAttached(item, slot_name);
2006
2008 UpdateVisuals();
2010 }
2011
2012 override void EEItemDetached(EntityAI item, string slot_name)
2013 {
2014 super.EEItemDetached(item, slot_name);
2015
2016 UpdateVisuals();
2018 }
2019
2020 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2021 {
2023 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2024
2027 }
2028
2029 //ignore out of reach condition
2030 override bool IgnoreOutOfReachCondition()
2031 {
2032 return true;
2033 }
2034
2035 //CONSTRUCTION EVENTS
2036 //Build
2037 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2038 {
2040
2041 //check base state
2042 if (construtionPart.IsBase())
2043 {
2044 SetBaseState(true);
2045
2046 //spawn kit
2048 }
2049
2050 //register constructed parts for synchronization
2052
2053 //register action that was performed on part
2055
2056 //synchronize
2058
2059 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2060
2061 UpdateNavmesh();
2062
2063 //update visuals
2064 UpdateVisuals();
2065
2066 //reset action sync data
2067 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2068 }
2069
2070 void OnPartBuiltClient(string part_name, int action_id)
2071 {
2072 //play sound
2074 }
2075
2076 //Dismantle
2078 {
2079 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2081
2082 //register constructed parts for synchronization
2084
2085 //register action that was performed on part
2087
2088 //synchronize
2090
2091 // server part of sync, client will be synced from SetPartsFromSyncData
2093
2094 UpdateNavmesh();
2095
2096 //update visuals
2097 UpdateVisuals();
2098
2099 //reset action sync data
2100 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2101
2102 //check base state
2103 if (construtionPart.IsBase())
2104 {
2105 //Destroy construction
2106 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2107 }
2108 }
2109
2111 {
2112 //play sound
2114 }
2115
2116 //Destroy
2118 {
2119 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2121
2122 //register constructed parts for synchronization
2124
2125 //register action that was performed on part
2127
2128 //synchronize
2130
2131 // server part of sync, client will be synced from SetPartsFromSyncData
2133
2134 UpdateNavmesh();
2135
2136 //update visuals
2137 UpdateVisuals();
2138
2139 //reset action sync data
2140 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2141
2142 //check base state
2143 if (construtionPart.IsBase())
2144 {
2145 //Destroy construction
2146 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2147 }
2148 }
2149
2150 void OnPartDestroyedClient(string part_name, int action_id)
2151 {
2152 //play sound
2154 }
2155
2156 // --- UPDATE
2157 void InitBaseState()
2158 {
2159 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2160
2161 InitVisuals();
2162 UpdateNavmesh(); //regenerate navmesh
2163 GetConstruction().InitBaseState();
2164 }
2165
2166 void InitVisuals()
2167 {
2168 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2169 //check base
2170 if (!HasBase())
2171 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2172 else
2173 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2174
2175 GetConstruction().UpdateVisuals();
2176 }
2177
2178 void UpdateVisuals()
2179 {
2181
2183 foreach (string slotName : attachmentSlots)
2185
2186 //check base
2187 if (!HasBase())
2188 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2189 else
2190 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2191
2192 GetConstruction().UpdateVisuals();
2193 }
2194
2196 {
2197 string slotNameMounted = slot_name + "_Mounted";
2198 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2199
2200 if (attachment)
2201 {
2202 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2203 if (barbedWire && barbedWire.IsMounted())
2205 else
2207
2208 if (is_locked)
2209 {
2210 SetAnimationPhase(slotNameMounted, 0);
2211 SetAnimationPhase(slot_name, 1);
2212 }
2213 else
2214 {
2215 SetAnimationPhase(slotNameMounted, 1);
2216 SetAnimationPhase(slot_name, 0);
2217 }
2218 }
2219 else
2220 {
2221 SetAnimationPhase(slotNameMounted, 1);
2222 SetAnimationPhase(slot_name, 1);
2223
2225 }
2226 }
2227
2228 // avoid calling this function on frequent occasions, it's a massive performance hit
2229 void UpdatePhysics()
2230 {
2232 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2233
2236
2238 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2239
2240 foreach (string slotName : attachmentSlots)
2242
2243 //check base
2244 if (!HasBase())
2245 {
2247 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2248
2249 AddProxyPhysics(ANIMATION_DEPLOYED);
2250 }
2251 else
2252 {
2254 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2255
2256 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2257 }
2258
2259 GetConstruction().UpdatePhysics();
2260 UpdateNavmesh();
2261 }
2262
2264 {
2265 //checks for invalid appends; hotfix
2266 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2267 return;
2268 //----------------------------------
2269 string slot_name_mounted = slot_name + "_Mounted";
2270 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2271
2272 //remove proxy physics
2273 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2274 RemoveProxyPhysics(slot_name_mounted);
2275 RemoveProxyPhysics(slot_name);
2276
2277 if (attachment)
2278 {
2279 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2280 if (is_locked)
2281 {
2282 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2283 AddProxyPhysics(slot_name_mounted);
2284 }
2285 else
2286 {
2287 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2288 AddProxyPhysics(slot_name);
2289 }
2290 }
2291 }
2292
2293 protected void UpdateNavmesh()
2294 {
2295 SetAffectPathgraph(true, false);
2296 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2297 }
2298
2299 override bool CanUseConstruction()
2300 {
2301 return true;
2302 }
2303
2304 override bool CanUseConstructionBuild()
2305 {
2306 return true;
2307 }
2308
2310 {
2311 if (attachment)
2312 {
2314 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2315
2316 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2317 }
2318
2319 return false;
2320 }
2321
2322 protected bool IsAttachmentSlotLocked(string slot_name)
2323 {
2324 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2325 }
2326
2327 //--- ATTACHMENT SLOTS
2329 {
2330 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2331 if (GetGame().ConfigIsExisting(config_path))
2332 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2333 }
2334
2336 {
2337 return true;
2338 }
2339
2340 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2341 {
2342 return true;
2343 }
2344
2345 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2346 {
2347 return true;
2348 }
2349
2350 // --- INIT
2351 void ConstructionInit()
2352 {
2353 if (!m_Construction)
2354 m_Construction = new Construction(this);
2355
2356 GetConstruction().Init();
2357 }
2358
2360 {
2361 return m_Construction;
2362 }
2363
2364 //--- INVENTORY/ATTACHMENTS CONDITIONS
2365 //attachments
2367 {
2368 return super.CanReceiveAttachment(attachment, slotId);
2369 }
2370
2372 {
2373 int attachment_count = GetInventory().AttachmentCount();
2374 if (attachment_count > 0)
2375 {
2376 if (HasBase() && attachment_count == 1)
2377 return false;
2378
2379 return true;
2380 }
2381
2382 return false;
2383 }
2384
2385 override bool ShowZonesHealth()
2386 {
2387 return true;
2388 }
2389
2390 //this into/outo parent.Cargo
2391 override bool CanPutInCargo(EntityAI parent)
2392 {
2393 return false;
2394 }
2395
2396 override bool CanRemoveFromCargo(EntityAI parent)
2397 {
2398 return false;
2399 }
2400
2401 //hands
2402 override bool CanPutIntoHands(EntityAI parent)
2403 {
2404 return false;
2405 }
2406
2407 //--- ACTION CONDITIONS
2408 //direction
2409 override bool IsFacingPlayer(PlayerBase player, string selection)
2410 {
2411 return true;
2412 }
2413
2414 override bool IsPlayerInside(PlayerBase player, string selection)
2415 {
2416 return true;
2417 }
2418
2421 {
2422 return false;
2423 }
2424
2425 //camera direction check
2426 bool IsFacingCamera(string selection)
2427 {
2428 return true;
2429 }
2430
2431 //roof check
2433 {
2434 return false;
2435 }
2436
2437 //selection->player distance check
2438 bool HasProperDistance(string selection, PlayerBase player)
2439 {
2440 return true;
2441 }
2442
2443 //folding
2445 {
2446 if (HasBase() || GetInventory().AttachmentCount() > 0)
2447 return false;
2448
2449 return true;
2450 }
2451
2453 {
2456
2457 return item;
2458 }
2459
2460 //Damage triggers (barbed wire)
2461 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2462 {
2463 if (GetGame() && GetGame().IsServer())
2464 {
2465 //destroy area damage if some already exists
2467
2468 //create new area damage
2470 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2471
2472 vector min_max[2];
2473 if (MemoryPointExists(slot_name + "_min"))
2474 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2475 if (MemoryPointExists(slot_name + "_max"))
2476 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2477
2478 //get proper trigger extents (min<max)
2479 vector extents[2];
2480 GetConstruction().GetTriggerExtents(min_max, extents);
2481
2482 //get box center
2483 vector center;
2484 center = GetConstruction().GetBoxCenter(min_max);
2485 center = ModelToWorld(center);
2486
2487 //rotate center if needed
2490
2491 areaDamage.SetExtents(extents[0], extents[1]);
2492 areaDamage.SetAreaPosition(center);
2493 areaDamage.SetAreaOrientation(orientation);
2494 areaDamage.SetLoopInterval(1.0);
2495 areaDamage.SetDeferDuration(0.2);
2496 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2497 areaDamage.SetAmmoName("BarbedWireHit");
2498 areaDamage.Spawn();
2499
2501 }
2502 }
2503
2505 {
2506 if (angle_deg != 0)
2507 {
2508 //orientation
2510
2511 //center
2513 if (MemoryPointExists("rotate_axis"))
2514 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2517 center[0] = r_center_x;
2518 center[2] = r_center_z;
2519 }
2520 }
2521
2522 void DestroyAreaDamage(string slot_name)
2523 {
2524 if (GetGame() && GetGame().IsServer())
2525 {
2528 {
2529 if (areaDamage)
2530 areaDamage.Destroy();
2531
2533 }
2534 }
2535 }
2536
2537 override bool IsIgnoredByConstruction()
2538 {
2539 return true;
2540 }
2541
2542 //================================================================
2543 // SOUNDS
2544 //================================================================
2545 protected void SoundBuildStart(string part_name)
2546 {
2547 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2548 }
2549
2550 protected void SoundDismantleStart(string part_name)
2551 {
2552 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2553 }
2554
2555 protected void SoundDestroyStart(string part_name)
2556 {
2557 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2558 }
2559
2560 protected string GetBuildSoundByMaterial(string part_name)
2561 {
2563
2564 switch (material_type)
2565 {
2566 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2567 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2568 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2569 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2570 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2571 }
2572
2573 return "";
2574 }
2575
2576 protected string GetDismantleSoundByMaterial(string part_name)
2577 {
2579
2580 switch (material_type)
2581 {
2582 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2583 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2584 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2585 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2586 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2587 }
2588
2589 return "";
2590 }
2591
2592 //misc
2594 {
2595 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2596 {
2597 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2599 SetHealth(slot_name, "Health", item.GetHealth());
2600 }
2601 }
2602
2603 override int GetDamageSystemVersionChange()
2604 {
2605 return 111;
2606 }
2607
2608 override void SetActions()
2609 {
2610 super.SetActions();
2611
2613 //AddAction(ActionTakeHybridAttachment);
2614 //AddAction(ActionTakeHybridAttachmentToHands);
2617 }
2618
2619 //================================================================
2620 // DEBUG
2621 //================================================================
2622 protected void DebugCustomState()
2623 {
2624 }
2625
2628 {
2629 return null;
2630 }
2631
2632 override void OnDebugSpawn()
2633 {
2634 FullyBuild();
2635 }
2636
2637 void FullyBuild()
2638 {
2640 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2641
2642 Man p;
2643
2644#ifdef SERVER
2646 GetGame().GetWorld().GetPlayerList(players);
2647 if (players.Count())
2648 p = players[0];
2649#else
2650 p = GetGame().GetPlayer();
2651#endif
2652
2653 foreach (ConstructionPart part : parts)
2654 {
2655 bool excluded = false;
2656 string partName = part.GetPartName();
2657 if (excludes)
2658 {
2659 foreach (string exclude : excludes)
2660 {
2661 if (partName.Contains(exclude))
2662 {
2663 excluded = true;
2664 break;
2665 }
2666 }
2667 }
2668
2669 if (!excluded)
2671 }
2672
2673 GetConstruction().UpdateVisuals();
2674 }
2675}
2676
2677void bsbDebugPrint(string s)
2678{
2679#ifdef BSB_DEBUG
2680 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2681#else
2682 //Print("" + s); // comment/uncomment to hide/see debug logs
2683#endif
2684}
2685void bsbDebugSpam(string s)
2686{
2687#ifdef BSB_DEBUG_SPAM
2688 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2689#else
2690 //Print("" + s); // comment/uncomment to hide/see debug logs
2691#endif
2692}

◆ IsFacingCamera()

bool bsbDebugPrint::IsFacingCamera ( string selection)
protected

Definition at line 2098 of file BaseBuildingBase.c.

2100{
2101 const string ANIMATION_DEPLOYED = "Deployed";
2102
2103 float m_ConstructionKitHealth; //stored health value for used construction kit
2104
2106
2107 bool m_HasBase;
2108 //variables for synchronization of base building parts (2x31 is the current limit)
2109 int m_SyncParts01; //synchronization for already built parts (31 parts)
2110 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2111 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2112 int m_InteractedPartId; //construction part id that an action was performed on
2113 int m_PerformedActionId; //action id that was performed on a construction part
2114
2115 //Sounds
2116 //build
2117 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2118 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2119 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2120 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2121 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2122 //dismantle
2123 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2124 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2125 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2126 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2127 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2128
2129 protected EffectSound m_Sound;
2130
2134
2135 // Constructor
2136 void BaseBuildingBase()
2137 {
2139
2140 //synchronized variables
2141 RegisterNetSyncVariableInt("m_SyncParts01");
2142 RegisterNetSyncVariableInt("m_SyncParts02");
2143 RegisterNetSyncVariableInt("m_SyncParts03");
2144 RegisterNetSyncVariableInt("m_InteractedPartId");
2145 RegisterNetSyncVariableInt("m_PerformedActionId");
2146 RegisterNetSyncVariableBool("m_HasBase");
2147
2148 //Construction init
2150
2151 if (ConfigIsExisting("hybridAttachments"))
2152 {
2154 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2155 }
2156 if (ConfigIsExisting("mountables"))
2157 {
2159 ConfigGetTextArray("mountables", m_Mountables);
2160 }
2161
2162 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2163 }
2164
2165 override void EEDelete(EntityAI parent)
2166 {
2167 super.EEDelete(parent);
2168
2169 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2171
2172 }
2173
2174 override string GetInvulnerabilityTypeString()
2175 {
2176 return "disableBaseDamage";
2177 }
2178
2179 override bool CanObstruct()
2180 {
2181 return true;
2182 }
2183
2184 override int GetHideIconMask()
2185 {
2186 return EInventoryIconVisibility.HIDE_VICINITY;
2187 }
2188
2189 // --- SYNCHRONIZATION
2191 {
2192 if (GetGame().IsServer())
2193 SetSynchDirty();
2194 }
2195
2196 override void OnVariablesSynchronized()
2197 {
2198 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2199 super.OnVariablesSynchronized();
2200
2201 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2202 }
2203
2204 protected void OnSynchronizedClient()
2205 {
2206 //update parts
2208
2209 //update action on part
2211
2212 //update visuals (client)
2213 UpdateVisuals();
2214 }
2215
2216 //parts synchronization
2218 {
2219 //part_id must starts from index = 1
2220 int offset;
2221 int mask;
2222
2223 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2224 {
2225 offset = part_id - 1;
2226 mask = 1 << offset;
2227
2229 }
2230 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2231 {
2232 offset = (part_id % 32);
2233 mask = 1 << offset;
2234
2236 }
2237 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2238 {
2239 offset = (part_id % 63);
2240 mask = 1 << offset;
2241
2243 }
2244 }
2245
2247 {
2248 //part_id must starts from index = 1
2249 int offset;
2250 int mask;
2251
2252 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2253 {
2254 offset = part_id - 1;
2255 mask = 1 << offset;
2256
2258 }
2259 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2260 {
2261 offset = (part_id % 32);
2262 mask = 1 << offset;
2263
2265 }
2266 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2267 {
2268 offset = (part_id % 63);
2269 mask = 1 << offset;
2270
2272 }
2273 }
2274
2276 {
2277 //part_id must starts from index = 1
2278 int offset;
2279 int mask;
2280
2281 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2282 {
2283 offset = part_id - 1;
2284 mask = 1 << offset;
2285
2286 if ((m_SyncParts01 & mask) > 0)
2287 return true;
2288 }
2289 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2290 {
2291 offset = (part_id % 32);
2292 mask = 1 << offset;
2293
2294 if ((m_SyncParts02 & mask) > 0)
2295 return true;
2296 }
2297 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2298 {
2299 offset = (part_id % 63);
2300 mask = 1 << offset;
2301
2302 if ((m_SyncParts03 & mask) > 0)
2303 return true;
2304 }
2305
2306 return false;
2307 }
2308
2309 protected void RegisterActionForSync(int part_id, int action_id)
2310 {
2313 }
2314
2315 protected void ResetActionSyncData()
2316 {
2317 //reset data
2318 m_InteractedPartId = -1;
2320 }
2321
2322 protected void SetActionFromSyncData()
2323 {
2324 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2325 {
2328
2329 switch (build_action_id)
2330 {
2334 }
2335 }
2336 }
2337 //------
2338
2340 {
2341 string key = part.m_PartName;
2342 bool is_base = part.IsBase();
2344 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2346 {
2347 if (!part.IsBuilt())
2348 {
2349 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2350 GetConstruction().AddToConstructedParts(key);
2351 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2352
2353 if (is_base)
2354 {
2356 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2357 }
2358 }
2359 }
2360 else
2361 {
2362 if (part.IsBuilt())
2363 {
2364 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2365 GetConstruction().RemoveFromConstructedParts(key);
2366 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2367
2368 if (is_base)
2369 {
2371 AddProxyPhysics(ANIMATION_DEPLOYED);
2372 }
2373 }
2374 }
2375
2376 //check slot lock for material attachments
2377 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2378 }
2379
2380 //set construction parts based on synchronized data
2382 {
2385
2386 for (int i = 0; i < construction_parts.Count(); ++i)
2387 {
2388 string key = construction_parts.GetKey(i);
2391 }
2392
2393 //regenerate navmesh
2394 UpdateNavmesh();
2395 }
2396
2398 {
2401
2402 for (int i = 0; i < construction_parts.Count(); ++i)
2403 {
2404 string key = construction_parts.GetKey(i);
2406
2407 if (value.GetId() == id)
2408 return value;
2409 }
2410
2411 return NULL;
2412 }
2413 //
2414
2415 //Base
2416 bool HasBase()
2417 {
2418 return m_HasBase;
2419 }
2420
2421 void SetBaseState(bool has_base)
2422 {
2424 }
2425
2426 override bool IsDeployable()
2427 {
2428 return true;
2429 }
2430
2431 bool IsOpened()
2432 {
2433 return false;
2434 }
2435
2436 //--- CONSTRUCTION KIT
2438 {
2442
2443 return construction_kit;
2444 }
2445
2447 {
2448 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2451 }
2452
2453 protected vector GetKitSpawnPosition()
2454 {
2455 return GetPosition();
2456 }
2457
2458 protected string GetConstructionKitType()
2459 {
2460 return "";
2461 }
2462
2464 {
2466 GetGame().ObjectDelete(construction_kit);
2467 }
2468
2469 //--- CONSTRUCTION
2470 void DestroyConstruction()
2471 {
2472 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2473 GetGame().ObjectDelete(this);
2474 }
2475
2476 // --- EVENTS
2477 override void OnStoreSave(ParamsWriteContext ctx)
2478 {
2479 super.OnStoreSave(ctx);
2480
2481 //sync parts 01
2482 ctx.Write(m_SyncParts01);
2483 ctx.Write(m_SyncParts02);
2484 ctx.Write(m_SyncParts03);
2485
2486 ctx.Write(m_HasBase);
2487 }
2488
2489 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2490 {
2491 if (!super.OnStoreLoad(ctx, version))
2492 return false;
2493
2494 //--- Base building data ---
2495 //Restore synced parts data
2496 if (!ctx.Read(m_SyncParts01))
2497 {
2498 m_SyncParts01 = 0; //set default
2499 return false;
2500 }
2501 if (!ctx.Read(m_SyncParts02))
2502 {
2503 m_SyncParts02 = 0; //set default
2504 return false;
2505 }
2506 if (!ctx.Read(m_SyncParts03))
2507 {
2508 m_SyncParts03 = 0; //set default
2509 return false;
2510 }
2511
2512 //has base
2513 if (!ctx.Read(m_HasBase))
2514 {
2515 m_HasBase = false;
2516 return false;
2517 }
2518 //---
2519
2520 return true;
2521 }
2522
2523 override void AfterStoreLoad()
2524 {
2525 super.AfterStoreLoad();
2526
2529 }
2530
2532 {
2533 //update server data
2535
2536 //set base state
2537 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2538 SetBaseState(construction_part.IsBuilt()) ;
2539
2540 //synchronize after load
2542 }
2543
2544 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2545 {
2547 return;
2548
2549 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2550
2551 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2552 return;
2553
2555 string part_name = zone;
2556 part_name.ToLower();
2557
2559 {
2561
2562 if (construction_part && construction.IsPartConstructed(part_name))
2563 {
2564 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2565 construction.DestroyConnectedParts(part_name);
2566 }
2567
2568 //barbed wire handling (hack-ish)
2569 if (part_name.Contains("barbed"))
2570 {
2571 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2572 if (barbed_wire)
2573 barbed_wire.SetMountedState(false);
2574 }
2575 }
2576 }
2577
2578 override void EEOnAfterLoad()
2579 {
2581 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2582
2583 super.EEOnAfterLoad();
2584 }
2585
2586 override void EEInit()
2587 {
2588 super.EEInit();
2589
2590 // init visuals and physics
2591 InitBaseState();
2592
2593 //debug
2594#ifdef DEVELOPER
2596#endif
2597 }
2598
2599 override void EEItemAttached(EntityAI item, string slot_name)
2600 {
2601 super.EEItemAttached(item, slot_name);
2602
2604 UpdateVisuals();
2606 }
2607
2608 override void EEItemDetached(EntityAI item, string slot_name)
2609 {
2610 super.EEItemDetached(item, slot_name);
2611
2612 UpdateVisuals();
2614 }
2615
2616 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2617 {
2619 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2620
2623 }
2624
2625 //ignore out of reach condition
2626 override bool IgnoreOutOfReachCondition()
2627 {
2628 return true;
2629 }
2630
2631 //CONSTRUCTION EVENTS
2632 //Build
2633 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2634 {
2636
2637 //check base state
2638 if (construtionPart.IsBase())
2639 {
2640 SetBaseState(true);
2641
2642 //spawn kit
2644 }
2645
2646 //register constructed parts for synchronization
2648
2649 //register action that was performed on part
2651
2652 //synchronize
2654
2655 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2656
2657 UpdateNavmesh();
2658
2659 //update visuals
2660 UpdateVisuals();
2661
2662 //reset action sync data
2663 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2664 }
2665
2666 void OnPartBuiltClient(string part_name, int action_id)
2667 {
2668 //play sound
2670 }
2671
2672 //Dismantle
2674 {
2675 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2677
2678 //register constructed parts for synchronization
2680
2681 //register action that was performed on part
2683
2684 //synchronize
2686
2687 // server part of sync, client will be synced from SetPartsFromSyncData
2689
2690 UpdateNavmesh();
2691
2692 //update visuals
2693 UpdateVisuals();
2694
2695 //reset action sync data
2696 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2697
2698 //check base state
2699 if (construtionPart.IsBase())
2700 {
2701 //Destroy construction
2702 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2703 }
2704 }
2705
2707 {
2708 //play sound
2710 }
2711
2712 //Destroy
2714 {
2715 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2717
2718 //register constructed parts for synchronization
2720
2721 //register action that was performed on part
2723
2724 //synchronize
2726
2727 // server part of sync, client will be synced from SetPartsFromSyncData
2729
2730 UpdateNavmesh();
2731
2732 //update visuals
2733 UpdateVisuals();
2734
2735 //reset action sync data
2736 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2737
2738 //check base state
2739 if (construtionPart.IsBase())
2740 {
2741 //Destroy construction
2742 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2743 }
2744 }
2745
2746 void OnPartDestroyedClient(string part_name, int action_id)
2747 {
2748 //play sound
2750 }
2751
2752 // --- UPDATE
2753 void InitBaseState()
2754 {
2755 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2756
2757 InitVisuals();
2758 UpdateNavmesh(); //regenerate navmesh
2759 GetConstruction().InitBaseState();
2760 }
2761
2762 void InitVisuals()
2763 {
2764 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2765 //check base
2766 if (!HasBase())
2767 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2768 else
2769 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2770
2771 GetConstruction().UpdateVisuals();
2772 }
2773
2774 void UpdateVisuals()
2775 {
2777
2779 foreach (string slotName : attachmentSlots)
2781
2782 //check base
2783 if (!HasBase())
2784 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2785 else
2786 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2787
2788 GetConstruction().UpdateVisuals();
2789 }
2790
2792 {
2793 string slotNameMounted = slot_name + "_Mounted";
2794 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2795
2796 if (attachment)
2797 {
2798 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2799 if (barbedWire && barbedWire.IsMounted())
2801 else
2803
2804 if (is_locked)
2805 {
2806 SetAnimationPhase(slotNameMounted, 0);
2807 SetAnimationPhase(slot_name, 1);
2808 }
2809 else
2810 {
2811 SetAnimationPhase(slotNameMounted, 1);
2812 SetAnimationPhase(slot_name, 0);
2813 }
2814 }
2815 else
2816 {
2817 SetAnimationPhase(slotNameMounted, 1);
2818 SetAnimationPhase(slot_name, 1);
2819
2821 }
2822 }
2823
2824 // avoid calling this function on frequent occasions, it's a massive performance hit
2825 void UpdatePhysics()
2826 {
2828 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2829
2832
2834 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2835
2836 foreach (string slotName : attachmentSlots)
2838
2839 //check base
2840 if (!HasBase())
2841 {
2843 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2844
2845 AddProxyPhysics(ANIMATION_DEPLOYED);
2846 }
2847 else
2848 {
2850 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2851
2852 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2853 }
2854
2855 GetConstruction().UpdatePhysics();
2856 UpdateNavmesh();
2857 }
2858
2860 {
2861 //checks for invalid appends; hotfix
2862 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2863 return;
2864 //----------------------------------
2865 string slot_name_mounted = slot_name + "_Mounted";
2866 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2867
2868 //remove proxy physics
2869 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2870 RemoveProxyPhysics(slot_name_mounted);
2871 RemoveProxyPhysics(slot_name);
2872
2873 if (attachment)
2874 {
2875 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2876 if (is_locked)
2877 {
2878 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2879 AddProxyPhysics(slot_name_mounted);
2880 }
2881 else
2882 {
2883 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2884 AddProxyPhysics(slot_name);
2885 }
2886 }
2887 }
2888
2889 protected void UpdateNavmesh()
2890 {
2891 SetAffectPathgraph(true, false);
2892 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2893 }
2894
2895 override bool CanUseConstruction()
2896 {
2897 return true;
2898 }
2899
2900 override bool CanUseConstructionBuild()
2901 {
2902 return true;
2903 }
2904
2906 {
2907 if (attachment)
2908 {
2910 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2911
2912 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2913 }
2914
2915 return false;
2916 }
2917
2918 protected bool IsAttachmentSlotLocked(string slot_name)
2919 {
2920 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2921 }
2922
2923 //--- ATTACHMENT SLOTS
2925 {
2926 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2927 if (GetGame().ConfigIsExisting(config_path))
2928 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2929 }
2930
2932 {
2933 return true;
2934 }
2935
2936 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2937 {
2938 return true;
2939 }
2940
2941 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2942 {
2943 return true;
2944 }
2945
2946 // --- INIT
2947 void ConstructionInit()
2948 {
2949 if (!m_Construction)
2950 m_Construction = new Construction(this);
2951
2952 GetConstruction().Init();
2953 }
2954
2956 {
2957 return m_Construction;
2958 }
2959
2960 //--- INVENTORY/ATTACHMENTS CONDITIONS
2961 //attachments
2963 {
2964 return super.CanReceiveAttachment(attachment, slotId);
2965 }
2966
2968 {
2969 int attachment_count = GetInventory().AttachmentCount();
2970 if (attachment_count > 0)
2971 {
2972 if (HasBase() && attachment_count == 1)
2973 return false;
2974
2975 return true;
2976 }
2977
2978 return false;
2979 }
2980
2981 override bool ShowZonesHealth()
2982 {
2983 return true;
2984 }
2985
2986 //this into/outo parent.Cargo
2987 override bool CanPutInCargo(EntityAI parent)
2988 {
2989 return false;
2990 }
2991
2992 override bool CanRemoveFromCargo(EntityAI parent)
2993 {
2994 return false;
2995 }
2996
2997 //hands
2998 override bool CanPutIntoHands(EntityAI parent)
2999 {
3000 return false;
3001 }
3002
3003 //--- ACTION CONDITIONS
3004 //direction
3005 override bool IsFacingPlayer(PlayerBase player, string selection)
3006 {
3007 return true;
3008 }
3009
3010 override bool IsPlayerInside(PlayerBase player, string selection)
3011 {
3012 return true;
3013 }
3014
3017 {
3018 return false;
3019 }
3020
3021 //camera direction check
3022 bool IsFacingCamera(string selection)
3023 {
3024 return true;
3025 }
3026
3027 //roof check
3029 {
3030 return false;
3031 }
3032
3033 //selection->player distance check
3034 bool HasProperDistance(string selection, PlayerBase player)
3035 {
3036 return true;
3037 }
3038
3039 //folding
3041 {
3042 if (HasBase() || GetInventory().AttachmentCount() > 0)
3043 return false;
3044
3045 return true;
3046 }
3047
3049 {
3052
3053 return item;
3054 }
3055
3056 //Damage triggers (barbed wire)
3057 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3058 {
3059 if (GetGame() && GetGame().IsServer())
3060 {
3061 //destroy area damage if some already exists
3063
3064 //create new area damage
3066 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3067
3068 vector min_max[2];
3069 if (MemoryPointExists(slot_name + "_min"))
3070 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3071 if (MemoryPointExists(slot_name + "_max"))
3072 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3073
3074 //get proper trigger extents (min<max)
3075 vector extents[2];
3076 GetConstruction().GetTriggerExtents(min_max, extents);
3077
3078 //get box center
3079 vector center;
3080 center = GetConstruction().GetBoxCenter(min_max);
3081 center = ModelToWorld(center);
3082
3083 //rotate center if needed
3086
3087 areaDamage.SetExtents(extents[0], extents[1]);
3088 areaDamage.SetAreaPosition(center);
3089 areaDamage.SetAreaOrientation(orientation);
3090 areaDamage.SetLoopInterval(1.0);
3091 areaDamage.SetDeferDuration(0.2);
3092 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3093 areaDamage.SetAmmoName("BarbedWireHit");
3094 areaDamage.Spawn();
3095
3097 }
3098 }
3099
3101 {
3102 if (angle_deg != 0)
3103 {
3104 //orientation
3106
3107 //center
3109 if (MemoryPointExists("rotate_axis"))
3110 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3113 center[0] = r_center_x;
3114 center[2] = r_center_z;
3115 }
3116 }
3117
3118 void DestroyAreaDamage(string slot_name)
3119 {
3120 if (GetGame() && GetGame().IsServer())
3121 {
3124 {
3125 if (areaDamage)
3126 areaDamage.Destroy();
3127
3129 }
3130 }
3131 }
3132
3133 override bool IsIgnoredByConstruction()
3134 {
3135 return true;
3136 }
3137
3138 //================================================================
3139 // SOUNDS
3140 //================================================================
3141 protected void SoundBuildStart(string part_name)
3142 {
3143 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3144 }
3145
3146 protected void SoundDismantleStart(string part_name)
3147 {
3148 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3149 }
3150
3151 protected void SoundDestroyStart(string part_name)
3152 {
3153 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3154 }
3155
3156 protected string GetBuildSoundByMaterial(string part_name)
3157 {
3159
3160 switch (material_type)
3161 {
3162 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3163 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3164 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3165 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3166 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3167 }
3168
3169 return "";
3170 }
3171
3172 protected string GetDismantleSoundByMaterial(string part_name)
3173 {
3175
3176 switch (material_type)
3177 {
3178 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3179 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3180 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3181 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3182 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3183 }
3184
3185 return "";
3186 }
3187
3188 //misc
3190 {
3191 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3192 {
3193 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3195 SetHealth(slot_name, "Health", item.GetHealth());
3196 }
3197 }
3198
3199 override int GetDamageSystemVersionChange()
3200 {
3201 return 111;
3202 }
3203
3204 override void SetActions()
3205 {
3206 super.SetActions();
3207
3209 //AddAction(ActionTakeHybridAttachment);
3210 //AddAction(ActionTakeHybridAttachmentToHands);
3213 }
3214
3215 //================================================================
3216 // DEBUG
3217 //================================================================
3218 protected void DebugCustomState()
3219 {
3220 }
3221
3224 {
3225 return null;
3226 }
3227
3228 override void OnDebugSpawn()
3229 {
3230 FullyBuild();
3231 }
3232
3233 void FullyBuild()
3234 {
3236 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3237
3238 Man p;
3239
3240#ifdef SERVER
3242 GetGame().GetWorld().GetPlayerList(players);
3243 if (players.Count())
3244 p = players[0];
3245#else
3246 p = GetGame().GetPlayer();
3247#endif
3248
3249 foreach (ConstructionPart part : parts)
3250 {
3251 bool excluded = false;
3252 string partName = part.GetPartName();
3253 if (excludes)
3254 {
3255 foreach (string exclude : excludes)
3256 {
3257 if (partName.Contains(exclude))
3258 {
3259 excluded = true;
3260 break;
3261 }
3262 }
3263 }
3264
3265 if (!excluded)
3267 }
3268
3269 GetConstruction().UpdateVisuals();
3270 }
3271}
3272
3273void bsbDebugPrint(string s)
3274{
3275#ifdef BSB_DEBUG
3276 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3277#else
3278 //Print("" + s); // comment/uncomment to hide/see debug logs
3279#endif
3280}
3281void bsbDebugSpam(string s)
3282{
3283#ifdef BSB_DEBUG_SPAM
3284 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3285#else
3286 //Print("" + s); // comment/uncomment to hide/see debug logs
3287#endif
3288}

◆ IsFacingPlayer()

override bool bsbDebugPrint::IsFacingPlayer ( PlayerBase player,
string selection )
protected

Definition at line 2081 of file BaseBuildingBase.c.

2083{
2084 const string ANIMATION_DEPLOYED = "Deployed";
2085
2086 float m_ConstructionKitHealth; //stored health value for used construction kit
2087
2089
2090 bool m_HasBase;
2091 //variables for synchronization of base building parts (2x31 is the current limit)
2092 int m_SyncParts01; //synchronization for already built parts (31 parts)
2093 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2094 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2095 int m_InteractedPartId; //construction part id that an action was performed on
2096 int m_PerformedActionId; //action id that was performed on a construction part
2097
2098 //Sounds
2099 //build
2100 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2101 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2102 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2103 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2104 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2105 //dismantle
2106 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2107 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2108 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2109 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2110 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2111
2112 protected EffectSound m_Sound;
2113
2117
2118 // Constructor
2119 void BaseBuildingBase()
2120 {
2122
2123 //synchronized variables
2124 RegisterNetSyncVariableInt("m_SyncParts01");
2125 RegisterNetSyncVariableInt("m_SyncParts02");
2126 RegisterNetSyncVariableInt("m_SyncParts03");
2127 RegisterNetSyncVariableInt("m_InteractedPartId");
2128 RegisterNetSyncVariableInt("m_PerformedActionId");
2129 RegisterNetSyncVariableBool("m_HasBase");
2130
2131 //Construction init
2133
2134 if (ConfigIsExisting("hybridAttachments"))
2135 {
2137 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2138 }
2139 if (ConfigIsExisting("mountables"))
2140 {
2142 ConfigGetTextArray("mountables", m_Mountables);
2143 }
2144
2145 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2146 }
2147
2148 override void EEDelete(EntityAI parent)
2149 {
2150 super.EEDelete(parent);
2151
2152 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2154
2155 }
2156
2157 override string GetInvulnerabilityTypeString()
2158 {
2159 return "disableBaseDamage";
2160 }
2161
2162 override bool CanObstruct()
2163 {
2164 return true;
2165 }
2166
2167 override int GetHideIconMask()
2168 {
2169 return EInventoryIconVisibility.HIDE_VICINITY;
2170 }
2171
2172 // --- SYNCHRONIZATION
2174 {
2175 if (GetGame().IsServer())
2176 SetSynchDirty();
2177 }
2178
2179 override void OnVariablesSynchronized()
2180 {
2181 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2182 super.OnVariablesSynchronized();
2183
2184 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2185 }
2186
2187 protected void OnSynchronizedClient()
2188 {
2189 //update parts
2191
2192 //update action on part
2194
2195 //update visuals (client)
2196 UpdateVisuals();
2197 }
2198
2199 //parts synchronization
2201 {
2202 //part_id must starts from index = 1
2203 int offset;
2204 int mask;
2205
2206 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2207 {
2208 offset = part_id - 1;
2209 mask = 1 << offset;
2210
2212 }
2213 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2214 {
2215 offset = (part_id % 32);
2216 mask = 1 << offset;
2217
2219 }
2220 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2221 {
2222 offset = (part_id % 63);
2223 mask = 1 << offset;
2224
2226 }
2227 }
2228
2230 {
2231 //part_id must starts from index = 1
2232 int offset;
2233 int mask;
2234
2235 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2236 {
2237 offset = part_id - 1;
2238 mask = 1 << offset;
2239
2241 }
2242 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2243 {
2244 offset = (part_id % 32);
2245 mask = 1 << offset;
2246
2248 }
2249 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2250 {
2251 offset = (part_id % 63);
2252 mask = 1 << offset;
2253
2255 }
2256 }
2257
2259 {
2260 //part_id must starts from index = 1
2261 int offset;
2262 int mask;
2263
2264 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2265 {
2266 offset = part_id - 1;
2267 mask = 1 << offset;
2268
2269 if ((m_SyncParts01 & mask) > 0)
2270 return true;
2271 }
2272 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2273 {
2274 offset = (part_id % 32);
2275 mask = 1 << offset;
2276
2277 if ((m_SyncParts02 & mask) > 0)
2278 return true;
2279 }
2280 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2281 {
2282 offset = (part_id % 63);
2283 mask = 1 << offset;
2284
2285 if ((m_SyncParts03 & mask) > 0)
2286 return true;
2287 }
2288
2289 return false;
2290 }
2291
2292 protected void RegisterActionForSync(int part_id, int action_id)
2293 {
2296 }
2297
2298 protected void ResetActionSyncData()
2299 {
2300 //reset data
2301 m_InteractedPartId = -1;
2303 }
2304
2305 protected void SetActionFromSyncData()
2306 {
2307 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2308 {
2311
2312 switch (build_action_id)
2313 {
2317 }
2318 }
2319 }
2320 //------
2321
2323 {
2324 string key = part.m_PartName;
2325 bool is_base = part.IsBase();
2327 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2329 {
2330 if (!part.IsBuilt())
2331 {
2332 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2333 GetConstruction().AddToConstructedParts(key);
2334 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2335
2336 if (is_base)
2337 {
2339 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2340 }
2341 }
2342 }
2343 else
2344 {
2345 if (part.IsBuilt())
2346 {
2347 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2348 GetConstruction().RemoveFromConstructedParts(key);
2349 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2350
2351 if (is_base)
2352 {
2354 AddProxyPhysics(ANIMATION_DEPLOYED);
2355 }
2356 }
2357 }
2358
2359 //check slot lock for material attachments
2360 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2361 }
2362
2363 //set construction parts based on synchronized data
2365 {
2368
2369 for (int i = 0; i < construction_parts.Count(); ++i)
2370 {
2371 string key = construction_parts.GetKey(i);
2374 }
2375
2376 //regenerate navmesh
2377 UpdateNavmesh();
2378 }
2379
2381 {
2384
2385 for (int i = 0; i < construction_parts.Count(); ++i)
2386 {
2387 string key = construction_parts.GetKey(i);
2389
2390 if (value.GetId() == id)
2391 return value;
2392 }
2393
2394 return NULL;
2395 }
2396 //
2397
2398 //Base
2399 bool HasBase()
2400 {
2401 return m_HasBase;
2402 }
2403
2404 void SetBaseState(bool has_base)
2405 {
2407 }
2408
2409 override bool IsDeployable()
2410 {
2411 return true;
2412 }
2413
2414 bool IsOpened()
2415 {
2416 return false;
2417 }
2418
2419 //--- CONSTRUCTION KIT
2421 {
2425
2426 return construction_kit;
2427 }
2428
2430 {
2431 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2434 }
2435
2436 protected vector GetKitSpawnPosition()
2437 {
2438 return GetPosition();
2439 }
2440
2441 protected string GetConstructionKitType()
2442 {
2443 return "";
2444 }
2445
2447 {
2449 GetGame().ObjectDelete(construction_kit);
2450 }
2451
2452 //--- CONSTRUCTION
2453 void DestroyConstruction()
2454 {
2455 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2456 GetGame().ObjectDelete(this);
2457 }
2458
2459 // --- EVENTS
2460 override void OnStoreSave(ParamsWriteContext ctx)
2461 {
2462 super.OnStoreSave(ctx);
2463
2464 //sync parts 01
2465 ctx.Write(m_SyncParts01);
2466 ctx.Write(m_SyncParts02);
2467 ctx.Write(m_SyncParts03);
2468
2469 ctx.Write(m_HasBase);
2470 }
2471
2472 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2473 {
2474 if (!super.OnStoreLoad(ctx, version))
2475 return false;
2476
2477 //--- Base building data ---
2478 //Restore synced parts data
2479 if (!ctx.Read(m_SyncParts01))
2480 {
2481 m_SyncParts01 = 0; //set default
2482 return false;
2483 }
2484 if (!ctx.Read(m_SyncParts02))
2485 {
2486 m_SyncParts02 = 0; //set default
2487 return false;
2488 }
2489 if (!ctx.Read(m_SyncParts03))
2490 {
2491 m_SyncParts03 = 0; //set default
2492 return false;
2493 }
2494
2495 //has base
2496 if (!ctx.Read(m_HasBase))
2497 {
2498 m_HasBase = false;
2499 return false;
2500 }
2501 //---
2502
2503 return true;
2504 }
2505
2506 override void AfterStoreLoad()
2507 {
2508 super.AfterStoreLoad();
2509
2512 }
2513
2515 {
2516 //update server data
2518
2519 //set base state
2520 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2521 SetBaseState(construction_part.IsBuilt()) ;
2522
2523 //synchronize after load
2525 }
2526
2527 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2528 {
2530 return;
2531
2532 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2533
2534 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2535 return;
2536
2538 string part_name = zone;
2539 part_name.ToLower();
2540
2542 {
2544
2545 if (construction_part && construction.IsPartConstructed(part_name))
2546 {
2547 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2548 construction.DestroyConnectedParts(part_name);
2549 }
2550
2551 //barbed wire handling (hack-ish)
2552 if (part_name.Contains("barbed"))
2553 {
2554 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2555 if (barbed_wire)
2556 barbed_wire.SetMountedState(false);
2557 }
2558 }
2559 }
2560
2561 override void EEOnAfterLoad()
2562 {
2564 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2565
2566 super.EEOnAfterLoad();
2567 }
2568
2569 override void EEInit()
2570 {
2571 super.EEInit();
2572
2573 // init visuals and physics
2574 InitBaseState();
2575
2576 //debug
2577#ifdef DEVELOPER
2579#endif
2580 }
2581
2582 override void EEItemAttached(EntityAI item, string slot_name)
2583 {
2584 super.EEItemAttached(item, slot_name);
2585
2587 UpdateVisuals();
2589 }
2590
2591 override void EEItemDetached(EntityAI item, string slot_name)
2592 {
2593 super.EEItemDetached(item, slot_name);
2594
2595 UpdateVisuals();
2597 }
2598
2599 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2600 {
2602 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2603
2606 }
2607
2608 //ignore out of reach condition
2609 override bool IgnoreOutOfReachCondition()
2610 {
2611 return true;
2612 }
2613
2614 //CONSTRUCTION EVENTS
2615 //Build
2616 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2617 {
2619
2620 //check base state
2621 if (construtionPart.IsBase())
2622 {
2623 SetBaseState(true);
2624
2625 //spawn kit
2627 }
2628
2629 //register constructed parts for synchronization
2631
2632 //register action that was performed on part
2634
2635 //synchronize
2637
2638 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2639
2640 UpdateNavmesh();
2641
2642 //update visuals
2643 UpdateVisuals();
2644
2645 //reset action sync data
2646 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2647 }
2648
2649 void OnPartBuiltClient(string part_name, int action_id)
2650 {
2651 //play sound
2653 }
2654
2655 //Dismantle
2657 {
2658 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2660
2661 //register constructed parts for synchronization
2663
2664 //register action that was performed on part
2666
2667 //synchronize
2669
2670 // server part of sync, client will be synced from SetPartsFromSyncData
2672
2673 UpdateNavmesh();
2674
2675 //update visuals
2676 UpdateVisuals();
2677
2678 //reset action sync data
2679 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2680
2681 //check base state
2682 if (construtionPart.IsBase())
2683 {
2684 //Destroy construction
2685 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2686 }
2687 }
2688
2690 {
2691 //play sound
2693 }
2694
2695 //Destroy
2697 {
2698 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2700
2701 //register constructed parts for synchronization
2703
2704 //register action that was performed on part
2706
2707 //synchronize
2709
2710 // server part of sync, client will be synced from SetPartsFromSyncData
2712
2713 UpdateNavmesh();
2714
2715 //update visuals
2716 UpdateVisuals();
2717
2718 //reset action sync data
2719 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2720
2721 //check base state
2722 if (construtionPart.IsBase())
2723 {
2724 //Destroy construction
2725 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2726 }
2727 }
2728
2729 void OnPartDestroyedClient(string part_name, int action_id)
2730 {
2731 //play sound
2733 }
2734
2735 // --- UPDATE
2736 void InitBaseState()
2737 {
2738 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2739
2740 InitVisuals();
2741 UpdateNavmesh(); //regenerate navmesh
2742 GetConstruction().InitBaseState();
2743 }
2744
2745 void InitVisuals()
2746 {
2747 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2748 //check base
2749 if (!HasBase())
2750 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2751 else
2752 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2753
2754 GetConstruction().UpdateVisuals();
2755 }
2756
2757 void UpdateVisuals()
2758 {
2760
2762 foreach (string slotName : attachmentSlots)
2764
2765 //check base
2766 if (!HasBase())
2767 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2768 else
2769 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2770
2771 GetConstruction().UpdateVisuals();
2772 }
2773
2775 {
2776 string slotNameMounted = slot_name + "_Mounted";
2777 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2778
2779 if (attachment)
2780 {
2781 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2782 if (barbedWire && barbedWire.IsMounted())
2784 else
2786
2787 if (is_locked)
2788 {
2789 SetAnimationPhase(slotNameMounted, 0);
2790 SetAnimationPhase(slot_name, 1);
2791 }
2792 else
2793 {
2794 SetAnimationPhase(slotNameMounted, 1);
2795 SetAnimationPhase(slot_name, 0);
2796 }
2797 }
2798 else
2799 {
2800 SetAnimationPhase(slotNameMounted, 1);
2801 SetAnimationPhase(slot_name, 1);
2802
2804 }
2805 }
2806
2807 // avoid calling this function on frequent occasions, it's a massive performance hit
2808 void UpdatePhysics()
2809 {
2811 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2812
2815
2817 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2818
2819 foreach (string slotName : attachmentSlots)
2821
2822 //check base
2823 if (!HasBase())
2824 {
2826 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2827
2828 AddProxyPhysics(ANIMATION_DEPLOYED);
2829 }
2830 else
2831 {
2833 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2834
2835 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2836 }
2837
2838 GetConstruction().UpdatePhysics();
2839 UpdateNavmesh();
2840 }
2841
2843 {
2844 //checks for invalid appends; hotfix
2845 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2846 return;
2847 //----------------------------------
2848 string slot_name_mounted = slot_name + "_Mounted";
2849 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2850
2851 //remove proxy physics
2852 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2853 RemoveProxyPhysics(slot_name_mounted);
2854 RemoveProxyPhysics(slot_name);
2855
2856 if (attachment)
2857 {
2858 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2859 if (is_locked)
2860 {
2861 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2862 AddProxyPhysics(slot_name_mounted);
2863 }
2864 else
2865 {
2866 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2867 AddProxyPhysics(slot_name);
2868 }
2869 }
2870 }
2871
2872 protected void UpdateNavmesh()
2873 {
2874 SetAffectPathgraph(true, false);
2875 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2876 }
2877
2878 override bool CanUseConstruction()
2879 {
2880 return true;
2881 }
2882
2883 override bool CanUseConstructionBuild()
2884 {
2885 return true;
2886 }
2887
2889 {
2890 if (attachment)
2891 {
2893 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2894
2895 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2896 }
2897
2898 return false;
2899 }
2900
2901 protected bool IsAttachmentSlotLocked(string slot_name)
2902 {
2903 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2904 }
2905
2906 //--- ATTACHMENT SLOTS
2908 {
2909 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2910 if (GetGame().ConfigIsExisting(config_path))
2911 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2912 }
2913
2915 {
2916 return true;
2917 }
2918
2919 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2920 {
2921 return true;
2922 }
2923
2924 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2925 {
2926 return true;
2927 }
2928
2929 // --- INIT
2930 void ConstructionInit()
2931 {
2932 if (!m_Construction)
2933 m_Construction = new Construction(this);
2934
2935 GetConstruction().Init();
2936 }
2937
2939 {
2940 return m_Construction;
2941 }
2942
2943 //--- INVENTORY/ATTACHMENTS CONDITIONS
2944 //attachments
2946 {
2947 return super.CanReceiveAttachment(attachment, slotId);
2948 }
2949
2951 {
2952 int attachment_count = GetInventory().AttachmentCount();
2953 if (attachment_count > 0)
2954 {
2955 if (HasBase() && attachment_count == 1)
2956 return false;
2957
2958 return true;
2959 }
2960
2961 return false;
2962 }
2963
2964 override bool ShowZonesHealth()
2965 {
2966 return true;
2967 }
2968
2969 //this into/outo parent.Cargo
2970 override bool CanPutInCargo(EntityAI parent)
2971 {
2972 return false;
2973 }
2974
2975 override bool CanRemoveFromCargo(EntityAI parent)
2976 {
2977 return false;
2978 }
2979
2980 //hands
2981 override bool CanPutIntoHands(EntityAI parent)
2982 {
2983 return false;
2984 }
2985
2986 //--- ACTION CONDITIONS
2987 //direction
2988 override bool IsFacingPlayer(PlayerBase player, string selection)
2989 {
2990 return true;
2991 }
2992
2993 override bool IsPlayerInside(PlayerBase player, string selection)
2994 {
2995 return true;
2996 }
2997
3000 {
3001 return false;
3002 }
3003
3004 //camera direction check
3005 bool IsFacingCamera(string selection)
3006 {
3007 return true;
3008 }
3009
3010 //roof check
3012 {
3013 return false;
3014 }
3015
3016 //selection->player distance check
3017 bool HasProperDistance(string selection, PlayerBase player)
3018 {
3019 return true;
3020 }
3021
3022 //folding
3024 {
3025 if (HasBase() || GetInventory().AttachmentCount() > 0)
3026 return false;
3027
3028 return true;
3029 }
3030
3032 {
3035
3036 return item;
3037 }
3038
3039 //Damage triggers (barbed wire)
3040 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3041 {
3042 if (GetGame() && GetGame().IsServer())
3043 {
3044 //destroy area damage if some already exists
3046
3047 //create new area damage
3049 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3050
3051 vector min_max[2];
3052 if (MemoryPointExists(slot_name + "_min"))
3053 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3054 if (MemoryPointExists(slot_name + "_max"))
3055 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3056
3057 //get proper trigger extents (min<max)
3058 vector extents[2];
3059 GetConstruction().GetTriggerExtents(min_max, extents);
3060
3061 //get box center
3062 vector center;
3063 center = GetConstruction().GetBoxCenter(min_max);
3064 center = ModelToWorld(center);
3065
3066 //rotate center if needed
3069
3070 areaDamage.SetExtents(extents[0], extents[1]);
3071 areaDamage.SetAreaPosition(center);
3072 areaDamage.SetAreaOrientation(orientation);
3073 areaDamage.SetLoopInterval(1.0);
3074 areaDamage.SetDeferDuration(0.2);
3075 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3076 areaDamage.SetAmmoName("BarbedWireHit");
3077 areaDamage.Spawn();
3078
3080 }
3081 }
3082
3084 {
3085 if (angle_deg != 0)
3086 {
3087 //orientation
3089
3090 //center
3092 if (MemoryPointExists("rotate_axis"))
3093 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3096 center[0] = r_center_x;
3097 center[2] = r_center_z;
3098 }
3099 }
3100
3101 void DestroyAreaDamage(string slot_name)
3102 {
3103 if (GetGame() && GetGame().IsServer())
3104 {
3107 {
3108 if (areaDamage)
3109 areaDamage.Destroy();
3110
3112 }
3113 }
3114 }
3115
3116 override bool IsIgnoredByConstruction()
3117 {
3118 return true;
3119 }
3120
3121 //================================================================
3122 // SOUNDS
3123 //================================================================
3124 protected void SoundBuildStart(string part_name)
3125 {
3126 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3127 }
3128
3129 protected void SoundDismantleStart(string part_name)
3130 {
3131 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3132 }
3133
3134 protected void SoundDestroyStart(string part_name)
3135 {
3136 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3137 }
3138
3139 protected string GetBuildSoundByMaterial(string part_name)
3140 {
3142
3143 switch (material_type)
3144 {
3145 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3146 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3147 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3148 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3149 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3150 }
3151
3152 return "";
3153 }
3154
3155 protected string GetDismantleSoundByMaterial(string part_name)
3156 {
3158
3159 switch (material_type)
3160 {
3161 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3162 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3163 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3164 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3165 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3166 }
3167
3168 return "";
3169 }
3170
3171 //misc
3173 {
3174 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3175 {
3176 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3178 SetHealth(slot_name, "Health", item.GetHealth());
3179 }
3180 }
3181
3182 override int GetDamageSystemVersionChange()
3183 {
3184 return 111;
3185 }
3186
3187 override void SetActions()
3188 {
3189 super.SetActions();
3190
3192 //AddAction(ActionTakeHybridAttachment);
3193 //AddAction(ActionTakeHybridAttachmentToHands);
3196 }
3197
3198 //================================================================
3199 // DEBUG
3200 //================================================================
3201 protected void DebugCustomState()
3202 {
3203 }
3204
3207 {
3208 return null;
3209 }
3210
3211 override void OnDebugSpawn()
3212 {
3213 FullyBuild();
3214 }
3215
3216 void FullyBuild()
3217 {
3219 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3220
3221 Man p;
3222
3223#ifdef SERVER
3225 GetGame().GetWorld().GetPlayerList(players);
3226 if (players.Count())
3227 p = players[0];
3228#else
3229 p = GetGame().GetPlayer();
3230#endif
3231
3232 foreach (ConstructionPart part : parts)
3233 {
3234 bool excluded = false;
3235 string partName = part.GetPartName();
3236 if (excludes)
3237 {
3238 foreach (string exclude : excludes)
3239 {
3240 if (partName.Contains(exclude))
3241 {
3242 excluded = true;
3243 break;
3244 }
3245 }
3246 }
3247
3248 if (!excluded)
3250 }
3251
3252 GetConstruction().UpdateVisuals();
3253 }
3254}
3255
3256void bsbDebugPrint(string s)
3257{
3258#ifdef BSB_DEBUG
3259 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3260#else
3261 //Print("" + s); // comment/uncomment to hide/see debug logs
3262#endif
3263}
3264void bsbDebugSpam(string s)
3265{
3266#ifdef BSB_DEBUG_SPAM
3267 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3268#else
3269 //Print("" + s); // comment/uncomment to hide/see debug logs
3270#endif
3271}

◆ IsIgnoredByConstruction()

override bool bsbDebugPrint::IsIgnoredByConstruction ( )
protected

Definition at line 2209 of file BaseBuildingBase.c.

2211{
2212 const string ANIMATION_DEPLOYED = "Deployed";
2213
2214 float m_ConstructionKitHealth; //stored health value for used construction kit
2215
2217
2218 bool m_HasBase;
2219 //variables for synchronization of base building parts (2x31 is the current limit)
2220 int m_SyncParts01; //synchronization for already built parts (31 parts)
2221 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2222 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2223 int m_InteractedPartId; //construction part id that an action was performed on
2224 int m_PerformedActionId; //action id that was performed on a construction part
2225
2226 //Sounds
2227 //build
2228 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2229 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2230 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2231 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2232 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2233 //dismantle
2234 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2235 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2236 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2237 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2238 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2239
2240 protected EffectSound m_Sound;
2241
2245
2246 // Constructor
2247 void BaseBuildingBase()
2248 {
2250
2251 //synchronized variables
2252 RegisterNetSyncVariableInt("m_SyncParts01");
2253 RegisterNetSyncVariableInt("m_SyncParts02");
2254 RegisterNetSyncVariableInt("m_SyncParts03");
2255 RegisterNetSyncVariableInt("m_InteractedPartId");
2256 RegisterNetSyncVariableInt("m_PerformedActionId");
2257 RegisterNetSyncVariableBool("m_HasBase");
2258
2259 //Construction init
2261
2262 if (ConfigIsExisting("hybridAttachments"))
2263 {
2265 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2266 }
2267 if (ConfigIsExisting("mountables"))
2268 {
2270 ConfigGetTextArray("mountables", m_Mountables);
2271 }
2272
2273 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2274 }
2275
2276 override void EEDelete(EntityAI parent)
2277 {
2278 super.EEDelete(parent);
2279
2280 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2282
2283 }
2284
2285 override string GetInvulnerabilityTypeString()
2286 {
2287 return "disableBaseDamage";
2288 }
2289
2290 override bool CanObstruct()
2291 {
2292 return true;
2293 }
2294
2295 override int GetHideIconMask()
2296 {
2297 return EInventoryIconVisibility.HIDE_VICINITY;
2298 }
2299
2300 // --- SYNCHRONIZATION
2302 {
2303 if (GetGame().IsServer())
2304 SetSynchDirty();
2305 }
2306
2307 override void OnVariablesSynchronized()
2308 {
2309 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2310 super.OnVariablesSynchronized();
2311
2312 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2313 }
2314
2315 protected void OnSynchronizedClient()
2316 {
2317 //update parts
2319
2320 //update action on part
2322
2323 //update visuals (client)
2324 UpdateVisuals();
2325 }
2326
2327 //parts synchronization
2329 {
2330 //part_id must starts from index = 1
2331 int offset;
2332 int mask;
2333
2334 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2335 {
2336 offset = part_id - 1;
2337 mask = 1 << offset;
2338
2340 }
2341 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2342 {
2343 offset = (part_id % 32);
2344 mask = 1 << offset;
2345
2347 }
2348 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2349 {
2350 offset = (part_id % 63);
2351 mask = 1 << offset;
2352
2354 }
2355 }
2356
2358 {
2359 //part_id must starts from index = 1
2360 int offset;
2361 int mask;
2362
2363 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2364 {
2365 offset = part_id - 1;
2366 mask = 1 << offset;
2367
2369 }
2370 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2371 {
2372 offset = (part_id % 32);
2373 mask = 1 << offset;
2374
2376 }
2377 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2378 {
2379 offset = (part_id % 63);
2380 mask = 1 << offset;
2381
2383 }
2384 }
2385
2387 {
2388 //part_id must starts from index = 1
2389 int offset;
2390 int mask;
2391
2392 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2393 {
2394 offset = part_id - 1;
2395 mask = 1 << offset;
2396
2397 if ((m_SyncParts01 & mask) > 0)
2398 return true;
2399 }
2400 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2401 {
2402 offset = (part_id % 32);
2403 mask = 1 << offset;
2404
2405 if ((m_SyncParts02 & mask) > 0)
2406 return true;
2407 }
2408 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2409 {
2410 offset = (part_id % 63);
2411 mask = 1 << offset;
2412
2413 if ((m_SyncParts03 & mask) > 0)
2414 return true;
2415 }
2416
2417 return false;
2418 }
2419
2420 protected void RegisterActionForSync(int part_id, int action_id)
2421 {
2424 }
2425
2426 protected void ResetActionSyncData()
2427 {
2428 //reset data
2429 m_InteractedPartId = -1;
2431 }
2432
2433 protected void SetActionFromSyncData()
2434 {
2435 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2436 {
2439
2440 switch (build_action_id)
2441 {
2445 }
2446 }
2447 }
2448 //------
2449
2451 {
2452 string key = part.m_PartName;
2453 bool is_base = part.IsBase();
2455 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2457 {
2458 if (!part.IsBuilt())
2459 {
2460 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2461 GetConstruction().AddToConstructedParts(key);
2462 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2463
2464 if (is_base)
2465 {
2467 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2468 }
2469 }
2470 }
2471 else
2472 {
2473 if (part.IsBuilt())
2474 {
2475 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2476 GetConstruction().RemoveFromConstructedParts(key);
2477 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2478
2479 if (is_base)
2480 {
2482 AddProxyPhysics(ANIMATION_DEPLOYED);
2483 }
2484 }
2485 }
2486
2487 //check slot lock for material attachments
2488 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2489 }
2490
2491 //set construction parts based on synchronized data
2493 {
2496
2497 for (int i = 0; i < construction_parts.Count(); ++i)
2498 {
2499 string key = construction_parts.GetKey(i);
2502 }
2503
2504 //regenerate navmesh
2505 UpdateNavmesh();
2506 }
2507
2509 {
2512
2513 for (int i = 0; i < construction_parts.Count(); ++i)
2514 {
2515 string key = construction_parts.GetKey(i);
2517
2518 if (value.GetId() == id)
2519 return value;
2520 }
2521
2522 return NULL;
2523 }
2524 //
2525
2526 //Base
2527 bool HasBase()
2528 {
2529 return m_HasBase;
2530 }
2531
2532 void SetBaseState(bool has_base)
2533 {
2535 }
2536
2537 override bool IsDeployable()
2538 {
2539 return true;
2540 }
2541
2542 bool IsOpened()
2543 {
2544 return false;
2545 }
2546
2547 //--- CONSTRUCTION KIT
2549 {
2553
2554 return construction_kit;
2555 }
2556
2558 {
2559 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2562 }
2563
2564 protected vector GetKitSpawnPosition()
2565 {
2566 return GetPosition();
2567 }
2568
2569 protected string GetConstructionKitType()
2570 {
2571 return "";
2572 }
2573
2575 {
2577 GetGame().ObjectDelete(construction_kit);
2578 }
2579
2580 //--- CONSTRUCTION
2581 void DestroyConstruction()
2582 {
2583 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2584 GetGame().ObjectDelete(this);
2585 }
2586
2587 // --- EVENTS
2588 override void OnStoreSave(ParamsWriteContext ctx)
2589 {
2590 super.OnStoreSave(ctx);
2591
2592 //sync parts 01
2593 ctx.Write(m_SyncParts01);
2594 ctx.Write(m_SyncParts02);
2595 ctx.Write(m_SyncParts03);
2596
2597 ctx.Write(m_HasBase);
2598 }
2599
2600 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2601 {
2602 if (!super.OnStoreLoad(ctx, version))
2603 return false;
2604
2605 //--- Base building data ---
2606 //Restore synced parts data
2607 if (!ctx.Read(m_SyncParts01))
2608 {
2609 m_SyncParts01 = 0; //set default
2610 return false;
2611 }
2612 if (!ctx.Read(m_SyncParts02))
2613 {
2614 m_SyncParts02 = 0; //set default
2615 return false;
2616 }
2617 if (!ctx.Read(m_SyncParts03))
2618 {
2619 m_SyncParts03 = 0; //set default
2620 return false;
2621 }
2622
2623 //has base
2624 if (!ctx.Read(m_HasBase))
2625 {
2626 m_HasBase = false;
2627 return false;
2628 }
2629 //---
2630
2631 return true;
2632 }
2633
2634 override void AfterStoreLoad()
2635 {
2636 super.AfterStoreLoad();
2637
2640 }
2641
2643 {
2644 //update server data
2646
2647 //set base state
2648 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2649 SetBaseState(construction_part.IsBuilt()) ;
2650
2651 //synchronize after load
2653 }
2654
2655 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2656 {
2658 return;
2659
2660 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2661
2662 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2663 return;
2664
2666 string part_name = zone;
2667 part_name.ToLower();
2668
2670 {
2672
2673 if (construction_part && construction.IsPartConstructed(part_name))
2674 {
2675 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2676 construction.DestroyConnectedParts(part_name);
2677 }
2678
2679 //barbed wire handling (hack-ish)
2680 if (part_name.Contains("barbed"))
2681 {
2682 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2683 if (barbed_wire)
2684 barbed_wire.SetMountedState(false);
2685 }
2686 }
2687 }
2688
2689 override void EEOnAfterLoad()
2690 {
2692 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2693
2694 super.EEOnAfterLoad();
2695 }
2696
2697 override void EEInit()
2698 {
2699 super.EEInit();
2700
2701 // init visuals and physics
2702 InitBaseState();
2703
2704 //debug
2705#ifdef DEVELOPER
2707#endif
2708 }
2709
2710 override void EEItemAttached(EntityAI item, string slot_name)
2711 {
2712 super.EEItemAttached(item, slot_name);
2713
2715 UpdateVisuals();
2717 }
2718
2719 override void EEItemDetached(EntityAI item, string slot_name)
2720 {
2721 super.EEItemDetached(item, slot_name);
2722
2723 UpdateVisuals();
2725 }
2726
2727 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2728 {
2730 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2731
2734 }
2735
2736 //ignore out of reach condition
2737 override bool IgnoreOutOfReachCondition()
2738 {
2739 return true;
2740 }
2741
2742 //CONSTRUCTION EVENTS
2743 //Build
2744 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2745 {
2747
2748 //check base state
2749 if (construtionPart.IsBase())
2750 {
2751 SetBaseState(true);
2752
2753 //spawn kit
2755 }
2756
2757 //register constructed parts for synchronization
2759
2760 //register action that was performed on part
2762
2763 //synchronize
2765
2766 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2767
2768 UpdateNavmesh();
2769
2770 //update visuals
2771 UpdateVisuals();
2772
2773 //reset action sync data
2774 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2775 }
2776
2777 void OnPartBuiltClient(string part_name, int action_id)
2778 {
2779 //play sound
2781 }
2782
2783 //Dismantle
2785 {
2786 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2788
2789 //register constructed parts for synchronization
2791
2792 //register action that was performed on part
2794
2795 //synchronize
2797
2798 // server part of sync, client will be synced from SetPartsFromSyncData
2800
2801 UpdateNavmesh();
2802
2803 //update visuals
2804 UpdateVisuals();
2805
2806 //reset action sync data
2807 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2808
2809 //check base state
2810 if (construtionPart.IsBase())
2811 {
2812 //Destroy construction
2813 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2814 }
2815 }
2816
2818 {
2819 //play sound
2821 }
2822
2823 //Destroy
2825 {
2826 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2828
2829 //register constructed parts for synchronization
2831
2832 //register action that was performed on part
2834
2835 //synchronize
2837
2838 // server part of sync, client will be synced from SetPartsFromSyncData
2840
2841 UpdateNavmesh();
2842
2843 //update visuals
2844 UpdateVisuals();
2845
2846 //reset action sync data
2847 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2848
2849 //check base state
2850 if (construtionPart.IsBase())
2851 {
2852 //Destroy construction
2853 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2854 }
2855 }
2856
2857 void OnPartDestroyedClient(string part_name, int action_id)
2858 {
2859 //play sound
2861 }
2862
2863 // --- UPDATE
2864 void InitBaseState()
2865 {
2866 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2867
2868 InitVisuals();
2869 UpdateNavmesh(); //regenerate navmesh
2870 GetConstruction().InitBaseState();
2871 }
2872
2873 void InitVisuals()
2874 {
2875 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2876 //check base
2877 if (!HasBase())
2878 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2879 else
2880 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2881
2882 GetConstruction().UpdateVisuals();
2883 }
2884
2885 void UpdateVisuals()
2886 {
2888
2890 foreach (string slotName : attachmentSlots)
2892
2893 //check base
2894 if (!HasBase())
2895 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2896 else
2897 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2898
2899 GetConstruction().UpdateVisuals();
2900 }
2901
2903 {
2904 string slotNameMounted = slot_name + "_Mounted";
2905 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2906
2907 if (attachment)
2908 {
2909 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2910 if (barbedWire && barbedWire.IsMounted())
2912 else
2914
2915 if (is_locked)
2916 {
2917 SetAnimationPhase(slotNameMounted, 0);
2918 SetAnimationPhase(slot_name, 1);
2919 }
2920 else
2921 {
2922 SetAnimationPhase(slotNameMounted, 1);
2923 SetAnimationPhase(slot_name, 0);
2924 }
2925 }
2926 else
2927 {
2928 SetAnimationPhase(slotNameMounted, 1);
2929 SetAnimationPhase(slot_name, 1);
2930
2932 }
2933 }
2934
2935 // avoid calling this function on frequent occasions, it's a massive performance hit
2936 void UpdatePhysics()
2937 {
2939 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2940
2943
2945 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2946
2947 foreach (string slotName : attachmentSlots)
2949
2950 //check base
2951 if (!HasBase())
2952 {
2954 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2955
2956 AddProxyPhysics(ANIMATION_DEPLOYED);
2957 }
2958 else
2959 {
2961 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2962
2963 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2964 }
2965
2966 GetConstruction().UpdatePhysics();
2967 UpdateNavmesh();
2968 }
2969
2971 {
2972 //checks for invalid appends; hotfix
2973 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2974 return;
2975 //----------------------------------
2976 string slot_name_mounted = slot_name + "_Mounted";
2977 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2978
2979 //remove proxy physics
2980 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2981 RemoveProxyPhysics(slot_name_mounted);
2982 RemoveProxyPhysics(slot_name);
2983
2984 if (attachment)
2985 {
2986 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2987 if (is_locked)
2988 {
2989 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2990 AddProxyPhysics(slot_name_mounted);
2991 }
2992 else
2993 {
2994 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2995 AddProxyPhysics(slot_name);
2996 }
2997 }
2998 }
2999
3000 protected void UpdateNavmesh()
3001 {
3002 SetAffectPathgraph(true, false);
3003 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
3004 }
3005
3006 override bool CanUseConstruction()
3007 {
3008 return true;
3009 }
3010
3011 override bool CanUseConstructionBuild()
3012 {
3013 return true;
3014 }
3015
3017 {
3018 if (attachment)
3019 {
3021 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
3022
3023 return GetInventory().GetSlotLock(inventory_location.GetSlot());
3024 }
3025
3026 return false;
3027 }
3028
3029 protected bool IsAttachmentSlotLocked(string slot_name)
3030 {
3031 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
3032 }
3033
3034 //--- ATTACHMENT SLOTS
3036 {
3037 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
3038 if (GetGame().ConfigIsExisting(config_path))
3039 GetGame().ConfigGetTextArray(config_path, attachment_slots);
3040 }
3041
3043 {
3044 return true;
3045 }
3046
3047 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
3048 {
3049 return true;
3050 }
3051
3052 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
3053 {
3054 return true;
3055 }
3056
3057 // --- INIT
3058 void ConstructionInit()
3059 {
3060 if (!m_Construction)
3061 m_Construction = new Construction(this);
3062
3063 GetConstruction().Init();
3064 }
3065
3067 {
3068 return m_Construction;
3069 }
3070
3071 //--- INVENTORY/ATTACHMENTS CONDITIONS
3072 //attachments
3074 {
3075 return super.CanReceiveAttachment(attachment, slotId);
3076 }
3077
3079 {
3080 int attachment_count = GetInventory().AttachmentCount();
3081 if (attachment_count > 0)
3082 {
3083 if (HasBase() && attachment_count == 1)
3084 return false;
3085
3086 return true;
3087 }
3088
3089 return false;
3090 }
3091
3092 override bool ShowZonesHealth()
3093 {
3094 return true;
3095 }
3096
3097 //this into/outo parent.Cargo
3098 override bool CanPutInCargo(EntityAI parent)
3099 {
3100 return false;
3101 }
3102
3103 override bool CanRemoveFromCargo(EntityAI parent)
3104 {
3105 return false;
3106 }
3107
3108 //hands
3109 override bool CanPutIntoHands(EntityAI parent)
3110 {
3111 return false;
3112 }
3113
3114 //--- ACTION CONDITIONS
3115 //direction
3116 override bool IsFacingPlayer(PlayerBase player, string selection)
3117 {
3118 return true;
3119 }
3120
3121 override bool IsPlayerInside(PlayerBase player, string selection)
3122 {
3123 return true;
3124 }
3125
3128 {
3129 return false;
3130 }
3131
3132 //camera direction check
3133 bool IsFacingCamera(string selection)
3134 {
3135 return true;
3136 }
3137
3138 //roof check
3140 {
3141 return false;
3142 }
3143
3144 //selection->player distance check
3145 bool HasProperDistance(string selection, PlayerBase player)
3146 {
3147 return true;
3148 }
3149
3150 //folding
3152 {
3153 if (HasBase() || GetInventory().AttachmentCount() > 0)
3154 return false;
3155
3156 return true;
3157 }
3158
3160 {
3163
3164 return item;
3165 }
3166
3167 //Damage triggers (barbed wire)
3168 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3169 {
3170 if (GetGame() && GetGame().IsServer())
3171 {
3172 //destroy area damage if some already exists
3174
3175 //create new area damage
3177 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3178
3179 vector min_max[2];
3180 if (MemoryPointExists(slot_name + "_min"))
3181 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3182 if (MemoryPointExists(slot_name + "_max"))
3183 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3184
3185 //get proper trigger extents (min<max)
3186 vector extents[2];
3187 GetConstruction().GetTriggerExtents(min_max, extents);
3188
3189 //get box center
3190 vector center;
3191 center = GetConstruction().GetBoxCenter(min_max);
3192 center = ModelToWorld(center);
3193
3194 //rotate center if needed
3197
3198 areaDamage.SetExtents(extents[0], extents[1]);
3199 areaDamage.SetAreaPosition(center);
3200 areaDamage.SetAreaOrientation(orientation);
3201 areaDamage.SetLoopInterval(1.0);
3202 areaDamage.SetDeferDuration(0.2);
3203 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3204 areaDamage.SetAmmoName("BarbedWireHit");
3205 areaDamage.Spawn();
3206
3208 }
3209 }
3210
3212 {
3213 if (angle_deg != 0)
3214 {
3215 //orientation
3217
3218 //center
3220 if (MemoryPointExists("rotate_axis"))
3221 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3224 center[0] = r_center_x;
3225 center[2] = r_center_z;
3226 }
3227 }
3228
3229 void DestroyAreaDamage(string slot_name)
3230 {
3231 if (GetGame() && GetGame().IsServer())
3232 {
3235 {
3236 if (areaDamage)
3237 areaDamage.Destroy();
3238
3240 }
3241 }
3242 }
3243
3244 override bool IsIgnoredByConstruction()
3245 {
3246 return true;
3247 }
3248
3249 //================================================================
3250 // SOUNDS
3251 //================================================================
3252 protected void SoundBuildStart(string part_name)
3253 {
3254 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3255 }
3256
3257 protected void SoundDismantleStart(string part_name)
3258 {
3259 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3260 }
3261
3262 protected void SoundDestroyStart(string part_name)
3263 {
3264 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3265 }
3266
3267 protected string GetBuildSoundByMaterial(string part_name)
3268 {
3270
3271 switch (material_type)
3272 {
3273 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3274 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3275 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3276 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3277 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3278 }
3279
3280 return "";
3281 }
3282
3283 protected string GetDismantleSoundByMaterial(string part_name)
3284 {
3286
3287 switch (material_type)
3288 {
3289 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3290 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3291 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3292 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3293 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3294 }
3295
3296 return "";
3297 }
3298
3299 //misc
3301 {
3302 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3303 {
3304 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3306 SetHealth(slot_name, "Health", item.GetHealth());
3307 }
3308 }
3309
3310 override int GetDamageSystemVersionChange()
3311 {
3312 return 111;
3313 }
3314
3315 override void SetActions()
3316 {
3317 super.SetActions();
3318
3320 //AddAction(ActionTakeHybridAttachment);
3321 //AddAction(ActionTakeHybridAttachmentToHands);
3324 }
3325
3326 //================================================================
3327 // DEBUG
3328 //================================================================
3329 protected void DebugCustomState()
3330 {
3331 }
3332
3335 {
3336 return null;
3337 }
3338
3339 override void OnDebugSpawn()
3340 {
3341 FullyBuild();
3342 }
3343
3344 void FullyBuild()
3345 {
3347 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3348
3349 Man p;
3350
3351#ifdef SERVER
3353 GetGame().GetWorld().GetPlayerList(players);
3354 if (players.Count())
3355 p = players[0];
3356#else
3357 p = GetGame().GetPlayer();
3358#endif
3359
3360 foreach (ConstructionPart part : parts)
3361 {
3362 bool excluded = false;
3363 string partName = part.GetPartName();
3364 if (excludes)
3365 {
3366 foreach (string exclude : excludes)
3367 {
3368 if (partName.Contains(exclude))
3369 {
3370 excluded = true;
3371 break;
3372 }
3373 }
3374 }
3375
3376 if (!excluded)
3378 }
3379
3380 GetConstruction().UpdateVisuals();
3381 }
3382}
3383
3384void bsbDebugPrint(string s)
3385{
3386#ifdef BSB_DEBUG
3387 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3388#else
3389 //Print("" + s); // comment/uncomment to hide/see debug logs
3390#endif
3391}
3392void bsbDebugSpam(string s)
3393{
3394#ifdef BSB_DEBUG_SPAM
3395 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3396#else
3397 //Print("" + s); // comment/uncomment to hide/see debug logs
3398#endif
3399}

◆ IsOpened()

bool bsbDebugPrint::IsOpened ( )
protected

Definition at line 1507 of file BaseBuildingBase.c.

1509{
1510 const string ANIMATION_DEPLOYED = "Deployed";
1511
1512 float m_ConstructionKitHealth; //stored health value for used construction kit
1513
1515
1516 bool m_HasBase;
1517 //variables for synchronization of base building parts (2x31 is the current limit)
1518 int m_SyncParts01; //synchronization for already built parts (31 parts)
1519 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1520 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1521 int m_InteractedPartId; //construction part id that an action was performed on
1522 int m_PerformedActionId; //action id that was performed on a construction part
1523
1524 //Sounds
1525 //build
1526 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1527 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1528 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1529 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1530 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1531 //dismantle
1532 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1533 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1534 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1535 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1536 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1537
1538 protected EffectSound m_Sound;
1539
1543
1544 // Constructor
1545 void BaseBuildingBase()
1546 {
1548
1549 //synchronized variables
1550 RegisterNetSyncVariableInt("m_SyncParts01");
1551 RegisterNetSyncVariableInt("m_SyncParts02");
1552 RegisterNetSyncVariableInt("m_SyncParts03");
1553 RegisterNetSyncVariableInt("m_InteractedPartId");
1554 RegisterNetSyncVariableInt("m_PerformedActionId");
1555 RegisterNetSyncVariableBool("m_HasBase");
1556
1557 //Construction init
1559
1560 if (ConfigIsExisting("hybridAttachments"))
1561 {
1563 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1564 }
1565 if (ConfigIsExisting("mountables"))
1566 {
1568 ConfigGetTextArray("mountables", m_Mountables);
1569 }
1570
1571 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1572 }
1573
1574 override void EEDelete(EntityAI parent)
1575 {
1576 super.EEDelete(parent);
1577
1578 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1580
1581 }
1582
1583 override string GetInvulnerabilityTypeString()
1584 {
1585 return "disableBaseDamage";
1586 }
1587
1588 override bool CanObstruct()
1589 {
1590 return true;
1591 }
1592
1593 override int GetHideIconMask()
1594 {
1595 return EInventoryIconVisibility.HIDE_VICINITY;
1596 }
1597
1598 // --- SYNCHRONIZATION
1600 {
1601 if (GetGame().IsServer())
1602 SetSynchDirty();
1603 }
1604
1605 override void OnVariablesSynchronized()
1606 {
1607 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1608 super.OnVariablesSynchronized();
1609
1610 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1611 }
1612
1613 protected void OnSynchronizedClient()
1614 {
1615 //update parts
1617
1618 //update action on part
1620
1621 //update visuals (client)
1622 UpdateVisuals();
1623 }
1624
1625 //parts synchronization
1627 {
1628 //part_id must starts from index = 1
1629 int offset;
1630 int mask;
1631
1632 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1633 {
1634 offset = part_id - 1;
1635 mask = 1 << offset;
1636
1638 }
1639 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1640 {
1641 offset = (part_id % 32);
1642 mask = 1 << offset;
1643
1645 }
1646 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1647 {
1648 offset = (part_id % 63);
1649 mask = 1 << offset;
1650
1652 }
1653 }
1654
1656 {
1657 //part_id must starts from index = 1
1658 int offset;
1659 int mask;
1660
1661 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1662 {
1663 offset = part_id - 1;
1664 mask = 1 << offset;
1665
1667 }
1668 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1669 {
1670 offset = (part_id % 32);
1671 mask = 1 << offset;
1672
1674 }
1675 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1676 {
1677 offset = (part_id % 63);
1678 mask = 1 << offset;
1679
1681 }
1682 }
1683
1685 {
1686 //part_id must starts from index = 1
1687 int offset;
1688 int mask;
1689
1690 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1691 {
1692 offset = part_id - 1;
1693 mask = 1 << offset;
1694
1695 if ((m_SyncParts01 & mask) > 0)
1696 return true;
1697 }
1698 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1699 {
1700 offset = (part_id % 32);
1701 mask = 1 << offset;
1702
1703 if ((m_SyncParts02 & mask) > 0)
1704 return true;
1705 }
1706 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1707 {
1708 offset = (part_id % 63);
1709 mask = 1 << offset;
1710
1711 if ((m_SyncParts03 & mask) > 0)
1712 return true;
1713 }
1714
1715 return false;
1716 }
1717
1718 protected void RegisterActionForSync(int part_id, int action_id)
1719 {
1722 }
1723
1724 protected void ResetActionSyncData()
1725 {
1726 //reset data
1727 m_InteractedPartId = -1;
1729 }
1730
1731 protected void SetActionFromSyncData()
1732 {
1733 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1734 {
1737
1738 switch (build_action_id)
1739 {
1743 }
1744 }
1745 }
1746 //------
1747
1749 {
1750 string key = part.m_PartName;
1751 bool is_base = part.IsBase();
1753 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1755 {
1756 if (!part.IsBuilt())
1757 {
1758 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1759 GetConstruction().AddToConstructedParts(key);
1760 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1761
1762 if (is_base)
1763 {
1765 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1766 }
1767 }
1768 }
1769 else
1770 {
1771 if (part.IsBuilt())
1772 {
1773 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1774 GetConstruction().RemoveFromConstructedParts(key);
1775 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1776
1777 if (is_base)
1778 {
1780 AddProxyPhysics(ANIMATION_DEPLOYED);
1781 }
1782 }
1783 }
1784
1785 //check slot lock for material attachments
1786 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1787 }
1788
1789 //set construction parts based on synchronized data
1791 {
1794
1795 for (int i = 0; i < construction_parts.Count(); ++i)
1796 {
1797 string key = construction_parts.GetKey(i);
1800 }
1801
1802 //regenerate navmesh
1803 UpdateNavmesh();
1804 }
1805
1807 {
1810
1811 for (int i = 0; i < construction_parts.Count(); ++i)
1812 {
1813 string key = construction_parts.GetKey(i);
1815
1816 if (value.GetId() == id)
1817 return value;
1818 }
1819
1820 return NULL;
1821 }
1822 //
1823
1824 //Base
1825 bool HasBase()
1826 {
1827 return m_HasBase;
1828 }
1829
1830 void SetBaseState(bool has_base)
1831 {
1833 }
1834
1835 override bool IsDeployable()
1836 {
1837 return true;
1838 }
1839
1840 bool IsOpened()
1841 {
1842 return false;
1843 }
1844
1845 //--- CONSTRUCTION KIT
1847 {
1851
1852 return construction_kit;
1853 }
1854
1856 {
1857 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1860 }
1861
1862 protected vector GetKitSpawnPosition()
1863 {
1864 return GetPosition();
1865 }
1866
1867 protected string GetConstructionKitType()
1868 {
1869 return "";
1870 }
1871
1873 {
1875 GetGame().ObjectDelete(construction_kit);
1876 }
1877
1878 //--- CONSTRUCTION
1879 void DestroyConstruction()
1880 {
1881 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1882 GetGame().ObjectDelete(this);
1883 }
1884
1885 // --- EVENTS
1886 override void OnStoreSave(ParamsWriteContext ctx)
1887 {
1888 super.OnStoreSave(ctx);
1889
1890 //sync parts 01
1891 ctx.Write(m_SyncParts01);
1892 ctx.Write(m_SyncParts02);
1893 ctx.Write(m_SyncParts03);
1894
1895 ctx.Write(m_HasBase);
1896 }
1897
1898 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1899 {
1900 if (!super.OnStoreLoad(ctx, version))
1901 return false;
1902
1903 //--- Base building data ---
1904 //Restore synced parts data
1905 if (!ctx.Read(m_SyncParts01))
1906 {
1907 m_SyncParts01 = 0; //set default
1908 return false;
1909 }
1910 if (!ctx.Read(m_SyncParts02))
1911 {
1912 m_SyncParts02 = 0; //set default
1913 return false;
1914 }
1915 if (!ctx.Read(m_SyncParts03))
1916 {
1917 m_SyncParts03 = 0; //set default
1918 return false;
1919 }
1920
1921 //has base
1922 if (!ctx.Read(m_HasBase))
1923 {
1924 m_HasBase = false;
1925 return false;
1926 }
1927 //---
1928
1929 return true;
1930 }
1931
1932 override void AfterStoreLoad()
1933 {
1934 super.AfterStoreLoad();
1935
1938 }
1939
1941 {
1942 //update server data
1944
1945 //set base state
1946 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1947 SetBaseState(construction_part.IsBuilt()) ;
1948
1949 //synchronize after load
1951 }
1952
1953 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1954 {
1956 return;
1957
1958 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1959
1960 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1961 return;
1962
1964 string part_name = zone;
1965 part_name.ToLower();
1966
1968 {
1970
1971 if (construction_part && construction.IsPartConstructed(part_name))
1972 {
1973 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1974 construction.DestroyConnectedParts(part_name);
1975 }
1976
1977 //barbed wire handling (hack-ish)
1978 if (part_name.Contains("barbed"))
1979 {
1980 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1981 if (barbed_wire)
1982 barbed_wire.SetMountedState(false);
1983 }
1984 }
1985 }
1986
1987 override void EEOnAfterLoad()
1988 {
1990 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1991
1992 super.EEOnAfterLoad();
1993 }
1994
1995 override void EEInit()
1996 {
1997 super.EEInit();
1998
1999 // init visuals and physics
2000 InitBaseState();
2001
2002 //debug
2003#ifdef DEVELOPER
2005#endif
2006 }
2007
2008 override void EEItemAttached(EntityAI item, string slot_name)
2009 {
2010 super.EEItemAttached(item, slot_name);
2011
2013 UpdateVisuals();
2015 }
2016
2017 override void EEItemDetached(EntityAI item, string slot_name)
2018 {
2019 super.EEItemDetached(item, slot_name);
2020
2021 UpdateVisuals();
2023 }
2024
2025 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2026 {
2028 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2029
2032 }
2033
2034 //ignore out of reach condition
2035 override bool IgnoreOutOfReachCondition()
2036 {
2037 return true;
2038 }
2039
2040 //CONSTRUCTION EVENTS
2041 //Build
2042 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2043 {
2045
2046 //check base state
2047 if (construtionPart.IsBase())
2048 {
2049 SetBaseState(true);
2050
2051 //spawn kit
2053 }
2054
2055 //register constructed parts for synchronization
2057
2058 //register action that was performed on part
2060
2061 //synchronize
2063
2064 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2065
2066 UpdateNavmesh();
2067
2068 //update visuals
2069 UpdateVisuals();
2070
2071 //reset action sync data
2072 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2073 }
2074
2075 void OnPartBuiltClient(string part_name, int action_id)
2076 {
2077 //play sound
2079 }
2080
2081 //Dismantle
2083 {
2084 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2086
2087 //register constructed parts for synchronization
2089
2090 //register action that was performed on part
2092
2093 //synchronize
2095
2096 // server part of sync, client will be synced from SetPartsFromSyncData
2098
2099 UpdateNavmesh();
2100
2101 //update visuals
2102 UpdateVisuals();
2103
2104 //reset action sync data
2105 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2106
2107 //check base state
2108 if (construtionPart.IsBase())
2109 {
2110 //Destroy construction
2111 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2112 }
2113 }
2114
2116 {
2117 //play sound
2119 }
2120
2121 //Destroy
2123 {
2124 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2126
2127 //register constructed parts for synchronization
2129
2130 //register action that was performed on part
2132
2133 //synchronize
2135
2136 // server part of sync, client will be synced from SetPartsFromSyncData
2138
2139 UpdateNavmesh();
2140
2141 //update visuals
2142 UpdateVisuals();
2143
2144 //reset action sync data
2145 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2146
2147 //check base state
2148 if (construtionPart.IsBase())
2149 {
2150 //Destroy construction
2151 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2152 }
2153 }
2154
2155 void OnPartDestroyedClient(string part_name, int action_id)
2156 {
2157 //play sound
2159 }
2160
2161 // --- UPDATE
2162 void InitBaseState()
2163 {
2164 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2165
2166 InitVisuals();
2167 UpdateNavmesh(); //regenerate navmesh
2168 GetConstruction().InitBaseState();
2169 }
2170
2171 void InitVisuals()
2172 {
2173 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2174 //check base
2175 if (!HasBase())
2176 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2177 else
2178 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2179
2180 GetConstruction().UpdateVisuals();
2181 }
2182
2183 void UpdateVisuals()
2184 {
2186
2188 foreach (string slotName : attachmentSlots)
2190
2191 //check base
2192 if (!HasBase())
2193 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2194 else
2195 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2196
2197 GetConstruction().UpdateVisuals();
2198 }
2199
2201 {
2202 string slotNameMounted = slot_name + "_Mounted";
2203 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2204
2205 if (attachment)
2206 {
2207 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2208 if (barbedWire && barbedWire.IsMounted())
2210 else
2212
2213 if (is_locked)
2214 {
2215 SetAnimationPhase(slotNameMounted, 0);
2216 SetAnimationPhase(slot_name, 1);
2217 }
2218 else
2219 {
2220 SetAnimationPhase(slotNameMounted, 1);
2221 SetAnimationPhase(slot_name, 0);
2222 }
2223 }
2224 else
2225 {
2226 SetAnimationPhase(slotNameMounted, 1);
2227 SetAnimationPhase(slot_name, 1);
2228
2230 }
2231 }
2232
2233 // avoid calling this function on frequent occasions, it's a massive performance hit
2234 void UpdatePhysics()
2235 {
2237 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2238
2241
2243 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2244
2245 foreach (string slotName : attachmentSlots)
2247
2248 //check base
2249 if (!HasBase())
2250 {
2252 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2253
2254 AddProxyPhysics(ANIMATION_DEPLOYED);
2255 }
2256 else
2257 {
2259 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2260
2261 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2262 }
2263
2264 GetConstruction().UpdatePhysics();
2265 UpdateNavmesh();
2266 }
2267
2269 {
2270 //checks for invalid appends; hotfix
2271 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2272 return;
2273 //----------------------------------
2274 string slot_name_mounted = slot_name + "_Mounted";
2275 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2276
2277 //remove proxy physics
2278 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2279 RemoveProxyPhysics(slot_name_mounted);
2280 RemoveProxyPhysics(slot_name);
2281
2282 if (attachment)
2283 {
2284 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2285 if (is_locked)
2286 {
2287 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2288 AddProxyPhysics(slot_name_mounted);
2289 }
2290 else
2291 {
2292 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2293 AddProxyPhysics(slot_name);
2294 }
2295 }
2296 }
2297
2298 protected void UpdateNavmesh()
2299 {
2300 SetAffectPathgraph(true, false);
2301 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2302 }
2303
2304 override bool CanUseConstruction()
2305 {
2306 return true;
2307 }
2308
2309 override bool CanUseConstructionBuild()
2310 {
2311 return true;
2312 }
2313
2315 {
2316 if (attachment)
2317 {
2319 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2320
2321 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2322 }
2323
2324 return false;
2325 }
2326
2327 protected bool IsAttachmentSlotLocked(string slot_name)
2328 {
2329 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2330 }
2331
2332 //--- ATTACHMENT SLOTS
2334 {
2335 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2336 if (GetGame().ConfigIsExisting(config_path))
2337 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2338 }
2339
2341 {
2342 return true;
2343 }
2344
2345 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2346 {
2347 return true;
2348 }
2349
2350 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2351 {
2352 return true;
2353 }
2354
2355 // --- INIT
2356 void ConstructionInit()
2357 {
2358 if (!m_Construction)
2359 m_Construction = new Construction(this);
2360
2361 GetConstruction().Init();
2362 }
2363
2365 {
2366 return m_Construction;
2367 }
2368
2369 //--- INVENTORY/ATTACHMENTS CONDITIONS
2370 //attachments
2372 {
2373 return super.CanReceiveAttachment(attachment, slotId);
2374 }
2375
2377 {
2378 int attachment_count = GetInventory().AttachmentCount();
2379 if (attachment_count > 0)
2380 {
2381 if (HasBase() && attachment_count == 1)
2382 return false;
2383
2384 return true;
2385 }
2386
2387 return false;
2388 }
2389
2390 override bool ShowZonesHealth()
2391 {
2392 return true;
2393 }
2394
2395 //this into/outo parent.Cargo
2396 override bool CanPutInCargo(EntityAI parent)
2397 {
2398 return false;
2399 }
2400
2401 override bool CanRemoveFromCargo(EntityAI parent)
2402 {
2403 return false;
2404 }
2405
2406 //hands
2407 override bool CanPutIntoHands(EntityAI parent)
2408 {
2409 return false;
2410 }
2411
2412 //--- ACTION CONDITIONS
2413 //direction
2414 override bool IsFacingPlayer(PlayerBase player, string selection)
2415 {
2416 return true;
2417 }
2418
2419 override bool IsPlayerInside(PlayerBase player, string selection)
2420 {
2421 return true;
2422 }
2423
2426 {
2427 return false;
2428 }
2429
2430 //camera direction check
2431 bool IsFacingCamera(string selection)
2432 {
2433 return true;
2434 }
2435
2436 //roof check
2438 {
2439 return false;
2440 }
2441
2442 //selection->player distance check
2443 bool HasProperDistance(string selection, PlayerBase player)
2444 {
2445 return true;
2446 }
2447
2448 //folding
2450 {
2451 if (HasBase() || GetInventory().AttachmentCount() > 0)
2452 return false;
2453
2454 return true;
2455 }
2456
2458 {
2461
2462 return item;
2463 }
2464
2465 //Damage triggers (barbed wire)
2466 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2467 {
2468 if (GetGame() && GetGame().IsServer())
2469 {
2470 //destroy area damage if some already exists
2472
2473 //create new area damage
2475 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2476
2477 vector min_max[2];
2478 if (MemoryPointExists(slot_name + "_min"))
2479 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2480 if (MemoryPointExists(slot_name + "_max"))
2481 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2482
2483 //get proper trigger extents (min<max)
2484 vector extents[2];
2485 GetConstruction().GetTriggerExtents(min_max, extents);
2486
2487 //get box center
2488 vector center;
2489 center = GetConstruction().GetBoxCenter(min_max);
2490 center = ModelToWorld(center);
2491
2492 //rotate center if needed
2495
2496 areaDamage.SetExtents(extents[0], extents[1]);
2497 areaDamage.SetAreaPosition(center);
2498 areaDamage.SetAreaOrientation(orientation);
2499 areaDamage.SetLoopInterval(1.0);
2500 areaDamage.SetDeferDuration(0.2);
2501 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2502 areaDamage.SetAmmoName("BarbedWireHit");
2503 areaDamage.Spawn();
2504
2506 }
2507 }
2508
2510 {
2511 if (angle_deg != 0)
2512 {
2513 //orientation
2515
2516 //center
2518 if (MemoryPointExists("rotate_axis"))
2519 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2522 center[0] = r_center_x;
2523 center[2] = r_center_z;
2524 }
2525 }
2526
2527 void DestroyAreaDamage(string slot_name)
2528 {
2529 if (GetGame() && GetGame().IsServer())
2530 {
2533 {
2534 if (areaDamage)
2535 areaDamage.Destroy();
2536
2538 }
2539 }
2540 }
2541
2542 override bool IsIgnoredByConstruction()
2543 {
2544 return true;
2545 }
2546
2547 //================================================================
2548 // SOUNDS
2549 //================================================================
2550 protected void SoundBuildStart(string part_name)
2551 {
2552 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2553 }
2554
2555 protected void SoundDismantleStart(string part_name)
2556 {
2557 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2558 }
2559
2560 protected void SoundDestroyStart(string part_name)
2561 {
2562 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2563 }
2564
2565 protected string GetBuildSoundByMaterial(string part_name)
2566 {
2568
2569 switch (material_type)
2570 {
2571 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2572 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2573 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2574 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2575 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2576 }
2577
2578 return "";
2579 }
2580
2581 protected string GetDismantleSoundByMaterial(string part_name)
2582 {
2584
2585 switch (material_type)
2586 {
2587 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2588 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2589 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2590 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2591 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2592 }
2593
2594 return "";
2595 }
2596
2597 //misc
2599 {
2600 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2601 {
2602 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2604 SetHealth(slot_name, "Health", item.GetHealth());
2605 }
2606 }
2607
2608 override int GetDamageSystemVersionChange()
2609 {
2610 return 111;
2611 }
2612
2613 override void SetActions()
2614 {
2615 super.SetActions();
2616
2618 //AddAction(ActionTakeHybridAttachment);
2619 //AddAction(ActionTakeHybridAttachmentToHands);
2622 }
2623
2624 //================================================================
2625 // DEBUG
2626 //================================================================
2627 protected void DebugCustomState()
2628 {
2629 }
2630
2633 {
2634 return null;
2635 }
2636
2637 override void OnDebugSpawn()
2638 {
2639 FullyBuild();
2640 }
2641
2642 void FullyBuild()
2643 {
2645 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2646
2647 Man p;
2648
2649#ifdef SERVER
2651 GetGame().GetWorld().GetPlayerList(players);
2652 if (players.Count())
2653 p = players[0];
2654#else
2655 p = GetGame().GetPlayer();
2656#endif
2657
2658 foreach (ConstructionPart part : parts)
2659 {
2660 bool excluded = false;
2661 string partName = part.GetPartName();
2662 if (excludes)
2663 {
2664 foreach (string exclude : excludes)
2665 {
2666 if (partName.Contains(exclude))
2667 {
2668 excluded = true;
2669 break;
2670 }
2671 }
2672 }
2673
2674 if (!excluded)
2676 }
2677
2678 GetConstruction().UpdateVisuals();
2679 }
2680}
2681
2682void bsbDebugPrint(string s)
2683{
2684#ifdef BSB_DEBUG
2685 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2686#else
2687 //Print("" + s); // comment/uncomment to hide/see debug logs
2688#endif
2689}
2690void bsbDebugSpam(string s)
2691{
2692#ifdef BSB_DEBUG_SPAM
2693 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2694#else
2695 //Print("" + s); // comment/uncomment to hide/see debug logs
2696#endif
2697}

Referenced by BaseBuildingBase::AfterStoreLoad(), PluginBase::EditorToggle(), PluginBase::EditorUpdate(), LayoutHolder::Toggle(), and ClosableContainer::UpdateInterval().

◆ IsPartBuildInSyncData()

bool bsbDebugPrint::IsPartBuildInSyncData ( int part_id)
protected

Definition at line 1351 of file BaseBuildingBase.c.

1353{
1354 const string ANIMATION_DEPLOYED = "Deployed";
1355
1356 float m_ConstructionKitHealth; //stored health value for used construction kit
1357
1359
1360 bool m_HasBase;
1361 //variables for synchronization of base building parts (2x31 is the current limit)
1362 int m_SyncParts01; //synchronization for already built parts (31 parts)
1363 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1364 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1365 int m_InteractedPartId; //construction part id that an action was performed on
1366 int m_PerformedActionId; //action id that was performed on a construction part
1367
1368 //Sounds
1369 //build
1370 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1371 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1372 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1373 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1374 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1375 //dismantle
1376 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1377 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1378 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1379 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1380 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1381
1382 protected EffectSound m_Sound;
1383
1387
1388 // Constructor
1389 void BaseBuildingBase()
1390 {
1392
1393 //synchronized variables
1394 RegisterNetSyncVariableInt("m_SyncParts01");
1395 RegisterNetSyncVariableInt("m_SyncParts02");
1396 RegisterNetSyncVariableInt("m_SyncParts03");
1397 RegisterNetSyncVariableInt("m_InteractedPartId");
1398 RegisterNetSyncVariableInt("m_PerformedActionId");
1399 RegisterNetSyncVariableBool("m_HasBase");
1400
1401 //Construction init
1403
1404 if (ConfigIsExisting("hybridAttachments"))
1405 {
1407 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1408 }
1409 if (ConfigIsExisting("mountables"))
1410 {
1412 ConfigGetTextArray("mountables", m_Mountables);
1413 }
1414
1415 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1416 }
1417
1418 override void EEDelete(EntityAI parent)
1419 {
1420 super.EEDelete(parent);
1421
1422 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1424
1425 }
1426
1427 override string GetInvulnerabilityTypeString()
1428 {
1429 return "disableBaseDamage";
1430 }
1431
1432 override bool CanObstruct()
1433 {
1434 return true;
1435 }
1436
1437 override int GetHideIconMask()
1438 {
1439 return EInventoryIconVisibility.HIDE_VICINITY;
1440 }
1441
1442 // --- SYNCHRONIZATION
1444 {
1445 if (GetGame().IsServer())
1446 SetSynchDirty();
1447 }
1448
1449 override void OnVariablesSynchronized()
1450 {
1451 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1452 super.OnVariablesSynchronized();
1453
1454 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1455 }
1456
1457 protected void OnSynchronizedClient()
1458 {
1459 //update parts
1461
1462 //update action on part
1464
1465 //update visuals (client)
1466 UpdateVisuals();
1467 }
1468
1469 //parts synchronization
1471 {
1472 //part_id must starts from index = 1
1473 int offset;
1474 int mask;
1475
1476 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1477 {
1478 offset = part_id - 1;
1479 mask = 1 << offset;
1480
1482 }
1483 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1484 {
1485 offset = (part_id % 32);
1486 mask = 1 << offset;
1487
1489 }
1490 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1491 {
1492 offset = (part_id % 63);
1493 mask = 1 << offset;
1494
1496 }
1497 }
1498
1500 {
1501 //part_id must starts from index = 1
1502 int offset;
1503 int mask;
1504
1505 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1506 {
1507 offset = part_id - 1;
1508 mask = 1 << offset;
1509
1511 }
1512 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1513 {
1514 offset = (part_id % 32);
1515 mask = 1 << offset;
1516
1518 }
1519 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1520 {
1521 offset = (part_id % 63);
1522 mask = 1 << offset;
1523
1525 }
1526 }
1527
1529 {
1530 //part_id must starts from index = 1
1531 int offset;
1532 int mask;
1533
1534 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1535 {
1536 offset = part_id - 1;
1537 mask = 1 << offset;
1538
1539 if ((m_SyncParts01 & mask) > 0)
1540 return true;
1541 }
1542 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1543 {
1544 offset = (part_id % 32);
1545 mask = 1 << offset;
1546
1547 if ((m_SyncParts02 & mask) > 0)
1548 return true;
1549 }
1550 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1551 {
1552 offset = (part_id % 63);
1553 mask = 1 << offset;
1554
1555 if ((m_SyncParts03 & mask) > 0)
1556 return true;
1557 }
1558
1559 return false;
1560 }
1561
1562 protected void RegisterActionForSync(int part_id, int action_id)
1563 {
1566 }
1567
1568 protected void ResetActionSyncData()
1569 {
1570 //reset data
1571 m_InteractedPartId = -1;
1573 }
1574
1575 protected void SetActionFromSyncData()
1576 {
1577 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1578 {
1581
1582 switch (build_action_id)
1583 {
1587 }
1588 }
1589 }
1590 //------
1591
1593 {
1594 string key = part.m_PartName;
1595 bool is_base = part.IsBase();
1597 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1599 {
1600 if (!part.IsBuilt())
1601 {
1602 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1603 GetConstruction().AddToConstructedParts(key);
1604 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1605
1606 if (is_base)
1607 {
1609 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1610 }
1611 }
1612 }
1613 else
1614 {
1615 if (part.IsBuilt())
1616 {
1617 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1618 GetConstruction().RemoveFromConstructedParts(key);
1619 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1620
1621 if (is_base)
1622 {
1624 AddProxyPhysics(ANIMATION_DEPLOYED);
1625 }
1626 }
1627 }
1628
1629 //check slot lock for material attachments
1630 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1631 }
1632
1633 //set construction parts based on synchronized data
1635 {
1638
1639 for (int i = 0; i < construction_parts.Count(); ++i)
1640 {
1641 string key = construction_parts.GetKey(i);
1644 }
1645
1646 //regenerate navmesh
1647 UpdateNavmesh();
1648 }
1649
1651 {
1654
1655 for (int i = 0; i < construction_parts.Count(); ++i)
1656 {
1657 string key = construction_parts.GetKey(i);
1659
1660 if (value.GetId() == id)
1661 return value;
1662 }
1663
1664 return NULL;
1665 }
1666 //
1667
1668 //Base
1669 bool HasBase()
1670 {
1671 return m_HasBase;
1672 }
1673
1674 void SetBaseState(bool has_base)
1675 {
1677 }
1678
1679 override bool IsDeployable()
1680 {
1681 return true;
1682 }
1683
1684 bool IsOpened()
1685 {
1686 return false;
1687 }
1688
1689 //--- CONSTRUCTION KIT
1691 {
1695
1696 return construction_kit;
1697 }
1698
1700 {
1701 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1704 }
1705
1706 protected vector GetKitSpawnPosition()
1707 {
1708 return GetPosition();
1709 }
1710
1711 protected string GetConstructionKitType()
1712 {
1713 return "";
1714 }
1715
1717 {
1719 GetGame().ObjectDelete(construction_kit);
1720 }
1721
1722 //--- CONSTRUCTION
1723 void DestroyConstruction()
1724 {
1725 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1726 GetGame().ObjectDelete(this);
1727 }
1728
1729 // --- EVENTS
1730 override void OnStoreSave(ParamsWriteContext ctx)
1731 {
1732 super.OnStoreSave(ctx);
1733
1734 //sync parts 01
1735 ctx.Write(m_SyncParts01);
1736 ctx.Write(m_SyncParts02);
1737 ctx.Write(m_SyncParts03);
1738
1739 ctx.Write(m_HasBase);
1740 }
1741
1742 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1743 {
1744 if (!super.OnStoreLoad(ctx, version))
1745 return false;
1746
1747 //--- Base building data ---
1748 //Restore synced parts data
1749 if (!ctx.Read(m_SyncParts01))
1750 {
1751 m_SyncParts01 = 0; //set default
1752 return false;
1753 }
1754 if (!ctx.Read(m_SyncParts02))
1755 {
1756 m_SyncParts02 = 0; //set default
1757 return false;
1758 }
1759 if (!ctx.Read(m_SyncParts03))
1760 {
1761 m_SyncParts03 = 0; //set default
1762 return false;
1763 }
1764
1765 //has base
1766 if (!ctx.Read(m_HasBase))
1767 {
1768 m_HasBase = false;
1769 return false;
1770 }
1771 //---
1772
1773 return true;
1774 }
1775
1776 override void AfterStoreLoad()
1777 {
1778 super.AfterStoreLoad();
1779
1782 }
1783
1785 {
1786 //update server data
1788
1789 //set base state
1790 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1791 SetBaseState(construction_part.IsBuilt()) ;
1792
1793 //synchronize after load
1795 }
1796
1797 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1798 {
1800 return;
1801
1802 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1803
1804 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1805 return;
1806
1808 string part_name = zone;
1809 part_name.ToLower();
1810
1812 {
1814
1815 if (construction_part && construction.IsPartConstructed(part_name))
1816 {
1817 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1818 construction.DestroyConnectedParts(part_name);
1819 }
1820
1821 //barbed wire handling (hack-ish)
1822 if (part_name.Contains("barbed"))
1823 {
1824 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1825 if (barbed_wire)
1826 barbed_wire.SetMountedState(false);
1827 }
1828 }
1829 }
1830
1831 override void EEOnAfterLoad()
1832 {
1834 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1835
1836 super.EEOnAfterLoad();
1837 }
1838
1839 override void EEInit()
1840 {
1841 super.EEInit();
1842
1843 // init visuals and physics
1844 InitBaseState();
1845
1846 //debug
1847#ifdef DEVELOPER
1849#endif
1850 }
1851
1852 override void EEItemAttached(EntityAI item, string slot_name)
1853 {
1854 super.EEItemAttached(item, slot_name);
1855
1857 UpdateVisuals();
1859 }
1860
1861 override void EEItemDetached(EntityAI item, string slot_name)
1862 {
1863 super.EEItemDetached(item, slot_name);
1864
1865 UpdateVisuals();
1867 }
1868
1869 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
1870 {
1872 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
1873
1876 }
1877
1878 //ignore out of reach condition
1879 override bool IgnoreOutOfReachCondition()
1880 {
1881 return true;
1882 }
1883
1884 //CONSTRUCTION EVENTS
1885 //Build
1886 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
1887 {
1889
1890 //check base state
1891 if (construtionPart.IsBase())
1892 {
1893 SetBaseState(true);
1894
1895 //spawn kit
1897 }
1898
1899 //register constructed parts for synchronization
1901
1902 //register action that was performed on part
1904
1905 //synchronize
1907
1908 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
1909
1910 UpdateNavmesh();
1911
1912 //update visuals
1913 UpdateVisuals();
1914
1915 //reset action sync data
1916 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1917 }
1918
1919 void OnPartBuiltClient(string part_name, int action_id)
1920 {
1921 //play sound
1923 }
1924
1925 //Dismantle
1927 {
1928 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
1930
1931 //register constructed parts for synchronization
1933
1934 //register action that was performed on part
1936
1937 //synchronize
1939
1940 // server part of sync, client will be synced from SetPartsFromSyncData
1942
1943 UpdateNavmesh();
1944
1945 //update visuals
1946 UpdateVisuals();
1947
1948 //reset action sync data
1949 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1950
1951 //check base state
1952 if (construtionPart.IsBase())
1953 {
1954 //Destroy construction
1955 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1956 }
1957 }
1958
1960 {
1961 //play sound
1963 }
1964
1965 //Destroy
1967 {
1968 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
1970
1971 //register constructed parts for synchronization
1973
1974 //register action that was performed on part
1976
1977 //synchronize
1979
1980 // server part of sync, client will be synced from SetPartsFromSyncData
1982
1983 UpdateNavmesh();
1984
1985 //update visuals
1986 UpdateVisuals();
1987
1988 //reset action sync data
1989 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1990
1991 //check base state
1992 if (construtionPart.IsBase())
1993 {
1994 //Destroy construction
1995 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1996 }
1997 }
1998
1999 void OnPartDestroyedClient(string part_name, int action_id)
2000 {
2001 //play sound
2003 }
2004
2005 // --- UPDATE
2006 void InitBaseState()
2007 {
2008 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2009
2010 InitVisuals();
2011 UpdateNavmesh(); //regenerate navmesh
2012 GetConstruction().InitBaseState();
2013 }
2014
2015 void InitVisuals()
2016 {
2017 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2018 //check base
2019 if (!HasBase())
2020 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2021 else
2022 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2023
2024 GetConstruction().UpdateVisuals();
2025 }
2026
2027 void UpdateVisuals()
2028 {
2030
2032 foreach (string slotName : attachmentSlots)
2034
2035 //check base
2036 if (!HasBase())
2037 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2038 else
2039 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2040
2041 GetConstruction().UpdateVisuals();
2042 }
2043
2045 {
2046 string slotNameMounted = slot_name + "_Mounted";
2047 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2048
2049 if (attachment)
2050 {
2051 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2052 if (barbedWire && barbedWire.IsMounted())
2054 else
2056
2057 if (is_locked)
2058 {
2059 SetAnimationPhase(slotNameMounted, 0);
2060 SetAnimationPhase(slot_name, 1);
2061 }
2062 else
2063 {
2064 SetAnimationPhase(slotNameMounted, 1);
2065 SetAnimationPhase(slot_name, 0);
2066 }
2067 }
2068 else
2069 {
2070 SetAnimationPhase(slotNameMounted, 1);
2071 SetAnimationPhase(slot_name, 1);
2072
2074 }
2075 }
2076
2077 // avoid calling this function on frequent occasions, it's a massive performance hit
2078 void UpdatePhysics()
2079 {
2081 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2082
2085
2087 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2088
2089 foreach (string slotName : attachmentSlots)
2091
2092 //check base
2093 if (!HasBase())
2094 {
2096 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2097
2098 AddProxyPhysics(ANIMATION_DEPLOYED);
2099 }
2100 else
2101 {
2103 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2104
2105 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2106 }
2107
2108 GetConstruction().UpdatePhysics();
2109 UpdateNavmesh();
2110 }
2111
2113 {
2114 //checks for invalid appends; hotfix
2115 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2116 return;
2117 //----------------------------------
2118 string slot_name_mounted = slot_name + "_Mounted";
2119 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2120
2121 //remove proxy physics
2122 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2123 RemoveProxyPhysics(slot_name_mounted);
2124 RemoveProxyPhysics(slot_name);
2125
2126 if (attachment)
2127 {
2128 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2129 if (is_locked)
2130 {
2131 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2132 AddProxyPhysics(slot_name_mounted);
2133 }
2134 else
2135 {
2136 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2137 AddProxyPhysics(slot_name);
2138 }
2139 }
2140 }
2141
2142 protected void UpdateNavmesh()
2143 {
2144 SetAffectPathgraph(true, false);
2145 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2146 }
2147
2148 override bool CanUseConstruction()
2149 {
2150 return true;
2151 }
2152
2153 override bool CanUseConstructionBuild()
2154 {
2155 return true;
2156 }
2157
2159 {
2160 if (attachment)
2161 {
2163 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2164
2165 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2166 }
2167
2168 return false;
2169 }
2170
2171 protected bool IsAttachmentSlotLocked(string slot_name)
2172 {
2173 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2174 }
2175
2176 //--- ATTACHMENT SLOTS
2178 {
2179 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2180 if (GetGame().ConfigIsExisting(config_path))
2181 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2182 }
2183
2185 {
2186 return true;
2187 }
2188
2189 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2190 {
2191 return true;
2192 }
2193
2194 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2195 {
2196 return true;
2197 }
2198
2199 // --- INIT
2200 void ConstructionInit()
2201 {
2202 if (!m_Construction)
2203 m_Construction = new Construction(this);
2204
2205 GetConstruction().Init();
2206 }
2207
2209 {
2210 return m_Construction;
2211 }
2212
2213 //--- INVENTORY/ATTACHMENTS CONDITIONS
2214 //attachments
2216 {
2217 return super.CanReceiveAttachment(attachment, slotId);
2218 }
2219
2221 {
2222 int attachment_count = GetInventory().AttachmentCount();
2223 if (attachment_count > 0)
2224 {
2225 if (HasBase() && attachment_count == 1)
2226 return false;
2227
2228 return true;
2229 }
2230
2231 return false;
2232 }
2233
2234 override bool ShowZonesHealth()
2235 {
2236 return true;
2237 }
2238
2239 //this into/outo parent.Cargo
2240 override bool CanPutInCargo(EntityAI parent)
2241 {
2242 return false;
2243 }
2244
2245 override bool CanRemoveFromCargo(EntityAI parent)
2246 {
2247 return false;
2248 }
2249
2250 //hands
2251 override bool CanPutIntoHands(EntityAI parent)
2252 {
2253 return false;
2254 }
2255
2256 //--- ACTION CONDITIONS
2257 //direction
2258 override bool IsFacingPlayer(PlayerBase player, string selection)
2259 {
2260 return true;
2261 }
2262
2263 override bool IsPlayerInside(PlayerBase player, string selection)
2264 {
2265 return true;
2266 }
2267
2270 {
2271 return false;
2272 }
2273
2274 //camera direction check
2275 bool IsFacingCamera(string selection)
2276 {
2277 return true;
2278 }
2279
2280 //roof check
2282 {
2283 return false;
2284 }
2285
2286 //selection->player distance check
2287 bool HasProperDistance(string selection, PlayerBase player)
2288 {
2289 return true;
2290 }
2291
2292 //folding
2294 {
2295 if (HasBase() || GetInventory().AttachmentCount() > 0)
2296 return false;
2297
2298 return true;
2299 }
2300
2302 {
2305
2306 return item;
2307 }
2308
2309 //Damage triggers (barbed wire)
2310 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2311 {
2312 if (GetGame() && GetGame().IsServer())
2313 {
2314 //destroy area damage if some already exists
2316
2317 //create new area damage
2319 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2320
2321 vector min_max[2];
2322 if (MemoryPointExists(slot_name + "_min"))
2323 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2324 if (MemoryPointExists(slot_name + "_max"))
2325 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2326
2327 //get proper trigger extents (min<max)
2328 vector extents[2];
2329 GetConstruction().GetTriggerExtents(min_max, extents);
2330
2331 //get box center
2332 vector center;
2333 center = GetConstruction().GetBoxCenter(min_max);
2334 center = ModelToWorld(center);
2335
2336 //rotate center if needed
2339
2340 areaDamage.SetExtents(extents[0], extents[1]);
2341 areaDamage.SetAreaPosition(center);
2342 areaDamage.SetAreaOrientation(orientation);
2343 areaDamage.SetLoopInterval(1.0);
2344 areaDamage.SetDeferDuration(0.2);
2345 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2346 areaDamage.SetAmmoName("BarbedWireHit");
2347 areaDamage.Spawn();
2348
2350 }
2351 }
2352
2354 {
2355 if (angle_deg != 0)
2356 {
2357 //orientation
2359
2360 //center
2362 if (MemoryPointExists("rotate_axis"))
2363 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2366 center[0] = r_center_x;
2367 center[2] = r_center_z;
2368 }
2369 }
2370
2371 void DestroyAreaDamage(string slot_name)
2372 {
2373 if (GetGame() && GetGame().IsServer())
2374 {
2377 {
2378 if (areaDamage)
2379 areaDamage.Destroy();
2380
2382 }
2383 }
2384 }
2385
2386 override bool IsIgnoredByConstruction()
2387 {
2388 return true;
2389 }
2390
2391 //================================================================
2392 // SOUNDS
2393 //================================================================
2394 protected void SoundBuildStart(string part_name)
2395 {
2396 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2397 }
2398
2399 protected void SoundDismantleStart(string part_name)
2400 {
2401 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2402 }
2403
2404 protected void SoundDestroyStart(string part_name)
2405 {
2406 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2407 }
2408
2409 protected string GetBuildSoundByMaterial(string part_name)
2410 {
2412
2413 switch (material_type)
2414 {
2415 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2416 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2417 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2418 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2419 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2420 }
2421
2422 return "";
2423 }
2424
2425 protected string GetDismantleSoundByMaterial(string part_name)
2426 {
2428
2429 switch (material_type)
2430 {
2431 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2432 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2433 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2434 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2435 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2436 }
2437
2438 return "";
2439 }
2440
2441 //misc
2443 {
2444 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2445 {
2446 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2448 SetHealth(slot_name, "Health", item.GetHealth());
2449 }
2450 }
2451
2452 override int GetDamageSystemVersionChange()
2453 {
2454 return 111;
2455 }
2456
2457 override void SetActions()
2458 {
2459 super.SetActions();
2460
2462 //AddAction(ActionTakeHybridAttachment);
2463 //AddAction(ActionTakeHybridAttachmentToHands);
2466 }
2467
2468 //================================================================
2469 // DEBUG
2470 //================================================================
2471 protected void DebugCustomState()
2472 {
2473 }
2474
2477 {
2478 return null;
2479 }
2480
2481 override void OnDebugSpawn()
2482 {
2483 FullyBuild();
2484 }
2485
2486 void FullyBuild()
2487 {
2489 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2490
2491 Man p;
2492
2493#ifdef SERVER
2495 GetGame().GetWorld().GetPlayerList(players);
2496 if (players.Count())
2497 p = players[0];
2498#else
2499 p = GetGame().GetPlayer();
2500#endif
2501
2502 foreach (ConstructionPart part : parts)
2503 {
2504 bool excluded = false;
2505 string partName = part.GetPartName();
2506 if (excludes)
2507 {
2508 foreach (string exclude : excludes)
2509 {
2510 if (partName.Contains(exclude))
2511 {
2512 excluded = true;
2513 break;
2514 }
2515 }
2516 }
2517
2518 if (!excluded)
2520 }
2521
2522 GetConstruction().UpdateVisuals();
2523 }
2524}
2525
2526void bsbDebugPrint(string s)
2527{
2528#ifdef BSB_DEBUG
2529 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2530#else
2531 //Print("" + s); // comment/uncomment to hide/see debug logs
2532#endif
2533}
2534void bsbDebugSpam(string s)
2535{
2536#ifdef BSB_DEBUG_SPAM
2537 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2538#else
2539 //Print("" + s); // comment/uncomment to hide/see debug logs
2540#endif
2541}

Referenced by ItemBase::SetPartFromSyncData().

◆ IsPlayerInside()

override bool bsbDebugPrint::IsPlayerInside ( PlayerBase player,
string selection )
protected

Definition at line 2086 of file BaseBuildingBase.c.

2088{
2089 const string ANIMATION_DEPLOYED = "Deployed";
2090
2091 float m_ConstructionKitHealth; //stored health value for used construction kit
2092
2094
2095 bool m_HasBase;
2096 //variables for synchronization of base building parts (2x31 is the current limit)
2097 int m_SyncParts01; //synchronization for already built parts (31 parts)
2098 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2099 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2100 int m_InteractedPartId; //construction part id that an action was performed on
2101 int m_PerformedActionId; //action id that was performed on a construction part
2102
2103 //Sounds
2104 //build
2105 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2106 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2107 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2108 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2109 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2110 //dismantle
2111 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2112 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2113 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2114 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2115 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2116
2117 protected EffectSound m_Sound;
2118
2122
2123 // Constructor
2124 void BaseBuildingBase()
2125 {
2127
2128 //synchronized variables
2129 RegisterNetSyncVariableInt("m_SyncParts01");
2130 RegisterNetSyncVariableInt("m_SyncParts02");
2131 RegisterNetSyncVariableInt("m_SyncParts03");
2132 RegisterNetSyncVariableInt("m_InteractedPartId");
2133 RegisterNetSyncVariableInt("m_PerformedActionId");
2134 RegisterNetSyncVariableBool("m_HasBase");
2135
2136 //Construction init
2138
2139 if (ConfigIsExisting("hybridAttachments"))
2140 {
2142 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2143 }
2144 if (ConfigIsExisting("mountables"))
2145 {
2147 ConfigGetTextArray("mountables", m_Mountables);
2148 }
2149
2150 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2151 }
2152
2153 override void EEDelete(EntityAI parent)
2154 {
2155 super.EEDelete(parent);
2156
2157 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2159
2160 }
2161
2162 override string GetInvulnerabilityTypeString()
2163 {
2164 return "disableBaseDamage";
2165 }
2166
2167 override bool CanObstruct()
2168 {
2169 return true;
2170 }
2171
2172 override int GetHideIconMask()
2173 {
2174 return EInventoryIconVisibility.HIDE_VICINITY;
2175 }
2176
2177 // --- SYNCHRONIZATION
2179 {
2180 if (GetGame().IsServer())
2181 SetSynchDirty();
2182 }
2183
2184 override void OnVariablesSynchronized()
2185 {
2186 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2187 super.OnVariablesSynchronized();
2188
2189 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2190 }
2191
2192 protected void OnSynchronizedClient()
2193 {
2194 //update parts
2196
2197 //update action on part
2199
2200 //update visuals (client)
2201 UpdateVisuals();
2202 }
2203
2204 //parts synchronization
2206 {
2207 //part_id must starts from index = 1
2208 int offset;
2209 int mask;
2210
2211 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2212 {
2213 offset = part_id - 1;
2214 mask = 1 << offset;
2215
2217 }
2218 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2219 {
2220 offset = (part_id % 32);
2221 mask = 1 << offset;
2222
2224 }
2225 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2226 {
2227 offset = (part_id % 63);
2228 mask = 1 << offset;
2229
2231 }
2232 }
2233
2235 {
2236 //part_id must starts from index = 1
2237 int offset;
2238 int mask;
2239
2240 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2241 {
2242 offset = part_id - 1;
2243 mask = 1 << offset;
2244
2246 }
2247 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2248 {
2249 offset = (part_id % 32);
2250 mask = 1 << offset;
2251
2253 }
2254 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2255 {
2256 offset = (part_id % 63);
2257 mask = 1 << offset;
2258
2260 }
2261 }
2262
2264 {
2265 //part_id must starts from index = 1
2266 int offset;
2267 int mask;
2268
2269 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2270 {
2271 offset = part_id - 1;
2272 mask = 1 << offset;
2273
2274 if ((m_SyncParts01 & mask) > 0)
2275 return true;
2276 }
2277 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2278 {
2279 offset = (part_id % 32);
2280 mask = 1 << offset;
2281
2282 if ((m_SyncParts02 & mask) > 0)
2283 return true;
2284 }
2285 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2286 {
2287 offset = (part_id % 63);
2288 mask = 1 << offset;
2289
2290 if ((m_SyncParts03 & mask) > 0)
2291 return true;
2292 }
2293
2294 return false;
2295 }
2296
2297 protected void RegisterActionForSync(int part_id, int action_id)
2298 {
2301 }
2302
2303 protected void ResetActionSyncData()
2304 {
2305 //reset data
2306 m_InteractedPartId = -1;
2308 }
2309
2310 protected void SetActionFromSyncData()
2311 {
2312 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2313 {
2316
2317 switch (build_action_id)
2318 {
2322 }
2323 }
2324 }
2325 //------
2326
2328 {
2329 string key = part.m_PartName;
2330 bool is_base = part.IsBase();
2332 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2334 {
2335 if (!part.IsBuilt())
2336 {
2337 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2338 GetConstruction().AddToConstructedParts(key);
2339 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2340
2341 if (is_base)
2342 {
2344 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2345 }
2346 }
2347 }
2348 else
2349 {
2350 if (part.IsBuilt())
2351 {
2352 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2353 GetConstruction().RemoveFromConstructedParts(key);
2354 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2355
2356 if (is_base)
2357 {
2359 AddProxyPhysics(ANIMATION_DEPLOYED);
2360 }
2361 }
2362 }
2363
2364 //check slot lock for material attachments
2365 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2366 }
2367
2368 //set construction parts based on synchronized data
2370 {
2373
2374 for (int i = 0; i < construction_parts.Count(); ++i)
2375 {
2376 string key = construction_parts.GetKey(i);
2379 }
2380
2381 //regenerate navmesh
2382 UpdateNavmesh();
2383 }
2384
2386 {
2389
2390 for (int i = 0; i < construction_parts.Count(); ++i)
2391 {
2392 string key = construction_parts.GetKey(i);
2394
2395 if (value.GetId() == id)
2396 return value;
2397 }
2398
2399 return NULL;
2400 }
2401 //
2402
2403 //Base
2404 bool HasBase()
2405 {
2406 return m_HasBase;
2407 }
2408
2409 void SetBaseState(bool has_base)
2410 {
2412 }
2413
2414 override bool IsDeployable()
2415 {
2416 return true;
2417 }
2418
2419 bool IsOpened()
2420 {
2421 return false;
2422 }
2423
2424 //--- CONSTRUCTION KIT
2426 {
2430
2431 return construction_kit;
2432 }
2433
2435 {
2436 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2439 }
2440
2441 protected vector GetKitSpawnPosition()
2442 {
2443 return GetPosition();
2444 }
2445
2446 protected string GetConstructionKitType()
2447 {
2448 return "";
2449 }
2450
2452 {
2454 GetGame().ObjectDelete(construction_kit);
2455 }
2456
2457 //--- CONSTRUCTION
2458 void DestroyConstruction()
2459 {
2460 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2461 GetGame().ObjectDelete(this);
2462 }
2463
2464 // --- EVENTS
2465 override void OnStoreSave(ParamsWriteContext ctx)
2466 {
2467 super.OnStoreSave(ctx);
2468
2469 //sync parts 01
2470 ctx.Write(m_SyncParts01);
2471 ctx.Write(m_SyncParts02);
2472 ctx.Write(m_SyncParts03);
2473
2474 ctx.Write(m_HasBase);
2475 }
2476
2477 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2478 {
2479 if (!super.OnStoreLoad(ctx, version))
2480 return false;
2481
2482 //--- Base building data ---
2483 //Restore synced parts data
2484 if (!ctx.Read(m_SyncParts01))
2485 {
2486 m_SyncParts01 = 0; //set default
2487 return false;
2488 }
2489 if (!ctx.Read(m_SyncParts02))
2490 {
2491 m_SyncParts02 = 0; //set default
2492 return false;
2493 }
2494 if (!ctx.Read(m_SyncParts03))
2495 {
2496 m_SyncParts03 = 0; //set default
2497 return false;
2498 }
2499
2500 //has base
2501 if (!ctx.Read(m_HasBase))
2502 {
2503 m_HasBase = false;
2504 return false;
2505 }
2506 //---
2507
2508 return true;
2509 }
2510
2511 override void AfterStoreLoad()
2512 {
2513 super.AfterStoreLoad();
2514
2517 }
2518
2520 {
2521 //update server data
2523
2524 //set base state
2525 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2526 SetBaseState(construction_part.IsBuilt()) ;
2527
2528 //synchronize after load
2530 }
2531
2532 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2533 {
2535 return;
2536
2537 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2538
2539 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2540 return;
2541
2543 string part_name = zone;
2544 part_name.ToLower();
2545
2547 {
2549
2550 if (construction_part && construction.IsPartConstructed(part_name))
2551 {
2552 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2553 construction.DestroyConnectedParts(part_name);
2554 }
2555
2556 //barbed wire handling (hack-ish)
2557 if (part_name.Contains("barbed"))
2558 {
2559 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2560 if (barbed_wire)
2561 barbed_wire.SetMountedState(false);
2562 }
2563 }
2564 }
2565
2566 override void EEOnAfterLoad()
2567 {
2569 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2570
2571 super.EEOnAfterLoad();
2572 }
2573
2574 override void EEInit()
2575 {
2576 super.EEInit();
2577
2578 // init visuals and physics
2579 InitBaseState();
2580
2581 //debug
2582#ifdef DEVELOPER
2584#endif
2585 }
2586
2587 override void EEItemAttached(EntityAI item, string slot_name)
2588 {
2589 super.EEItemAttached(item, slot_name);
2590
2592 UpdateVisuals();
2594 }
2595
2596 override void EEItemDetached(EntityAI item, string slot_name)
2597 {
2598 super.EEItemDetached(item, slot_name);
2599
2600 UpdateVisuals();
2602 }
2603
2604 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2605 {
2607 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2608
2611 }
2612
2613 //ignore out of reach condition
2614 override bool IgnoreOutOfReachCondition()
2615 {
2616 return true;
2617 }
2618
2619 //CONSTRUCTION EVENTS
2620 //Build
2621 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2622 {
2624
2625 //check base state
2626 if (construtionPart.IsBase())
2627 {
2628 SetBaseState(true);
2629
2630 //spawn kit
2632 }
2633
2634 //register constructed parts for synchronization
2636
2637 //register action that was performed on part
2639
2640 //synchronize
2642
2643 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2644
2645 UpdateNavmesh();
2646
2647 //update visuals
2648 UpdateVisuals();
2649
2650 //reset action sync data
2651 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2652 }
2653
2654 void OnPartBuiltClient(string part_name, int action_id)
2655 {
2656 //play sound
2658 }
2659
2660 //Dismantle
2662 {
2663 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2665
2666 //register constructed parts for synchronization
2668
2669 //register action that was performed on part
2671
2672 //synchronize
2674
2675 // server part of sync, client will be synced from SetPartsFromSyncData
2677
2678 UpdateNavmesh();
2679
2680 //update visuals
2681 UpdateVisuals();
2682
2683 //reset action sync data
2684 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2685
2686 //check base state
2687 if (construtionPart.IsBase())
2688 {
2689 //Destroy construction
2690 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2691 }
2692 }
2693
2695 {
2696 //play sound
2698 }
2699
2700 //Destroy
2702 {
2703 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2705
2706 //register constructed parts for synchronization
2708
2709 //register action that was performed on part
2711
2712 //synchronize
2714
2715 // server part of sync, client will be synced from SetPartsFromSyncData
2717
2718 UpdateNavmesh();
2719
2720 //update visuals
2721 UpdateVisuals();
2722
2723 //reset action sync data
2724 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2725
2726 //check base state
2727 if (construtionPart.IsBase())
2728 {
2729 //Destroy construction
2730 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2731 }
2732 }
2733
2734 void OnPartDestroyedClient(string part_name, int action_id)
2735 {
2736 //play sound
2738 }
2739
2740 // --- UPDATE
2741 void InitBaseState()
2742 {
2743 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2744
2745 InitVisuals();
2746 UpdateNavmesh(); //regenerate navmesh
2747 GetConstruction().InitBaseState();
2748 }
2749
2750 void InitVisuals()
2751 {
2752 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2753 //check base
2754 if (!HasBase())
2755 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2756 else
2757 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2758
2759 GetConstruction().UpdateVisuals();
2760 }
2761
2762 void UpdateVisuals()
2763 {
2765
2767 foreach (string slotName : attachmentSlots)
2769
2770 //check base
2771 if (!HasBase())
2772 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2773 else
2774 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2775
2776 GetConstruction().UpdateVisuals();
2777 }
2778
2780 {
2781 string slotNameMounted = slot_name + "_Mounted";
2782 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2783
2784 if (attachment)
2785 {
2786 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2787 if (barbedWire && barbedWire.IsMounted())
2789 else
2791
2792 if (is_locked)
2793 {
2794 SetAnimationPhase(slotNameMounted, 0);
2795 SetAnimationPhase(slot_name, 1);
2796 }
2797 else
2798 {
2799 SetAnimationPhase(slotNameMounted, 1);
2800 SetAnimationPhase(slot_name, 0);
2801 }
2802 }
2803 else
2804 {
2805 SetAnimationPhase(slotNameMounted, 1);
2806 SetAnimationPhase(slot_name, 1);
2807
2809 }
2810 }
2811
2812 // avoid calling this function on frequent occasions, it's a massive performance hit
2813 void UpdatePhysics()
2814 {
2816 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2817
2820
2822 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2823
2824 foreach (string slotName : attachmentSlots)
2826
2827 //check base
2828 if (!HasBase())
2829 {
2831 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2832
2833 AddProxyPhysics(ANIMATION_DEPLOYED);
2834 }
2835 else
2836 {
2838 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2839
2840 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2841 }
2842
2843 GetConstruction().UpdatePhysics();
2844 UpdateNavmesh();
2845 }
2846
2848 {
2849 //checks for invalid appends; hotfix
2850 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2851 return;
2852 //----------------------------------
2853 string slot_name_mounted = slot_name + "_Mounted";
2854 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2855
2856 //remove proxy physics
2857 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2858 RemoveProxyPhysics(slot_name_mounted);
2859 RemoveProxyPhysics(slot_name);
2860
2861 if (attachment)
2862 {
2863 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2864 if (is_locked)
2865 {
2866 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2867 AddProxyPhysics(slot_name_mounted);
2868 }
2869 else
2870 {
2871 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2872 AddProxyPhysics(slot_name);
2873 }
2874 }
2875 }
2876
2877 protected void UpdateNavmesh()
2878 {
2879 SetAffectPathgraph(true, false);
2880 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2881 }
2882
2883 override bool CanUseConstruction()
2884 {
2885 return true;
2886 }
2887
2888 override bool CanUseConstructionBuild()
2889 {
2890 return true;
2891 }
2892
2894 {
2895 if (attachment)
2896 {
2898 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2899
2900 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2901 }
2902
2903 return false;
2904 }
2905
2906 protected bool IsAttachmentSlotLocked(string slot_name)
2907 {
2908 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2909 }
2910
2911 //--- ATTACHMENT SLOTS
2913 {
2914 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2915 if (GetGame().ConfigIsExisting(config_path))
2916 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2917 }
2918
2920 {
2921 return true;
2922 }
2923
2924 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2925 {
2926 return true;
2927 }
2928
2929 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2930 {
2931 return true;
2932 }
2933
2934 // --- INIT
2935 void ConstructionInit()
2936 {
2937 if (!m_Construction)
2938 m_Construction = new Construction(this);
2939
2940 GetConstruction().Init();
2941 }
2942
2944 {
2945 return m_Construction;
2946 }
2947
2948 //--- INVENTORY/ATTACHMENTS CONDITIONS
2949 //attachments
2951 {
2952 return super.CanReceiveAttachment(attachment, slotId);
2953 }
2954
2956 {
2957 int attachment_count = GetInventory().AttachmentCount();
2958 if (attachment_count > 0)
2959 {
2960 if (HasBase() && attachment_count == 1)
2961 return false;
2962
2963 return true;
2964 }
2965
2966 return false;
2967 }
2968
2969 override bool ShowZonesHealth()
2970 {
2971 return true;
2972 }
2973
2974 //this into/outo parent.Cargo
2975 override bool CanPutInCargo(EntityAI parent)
2976 {
2977 return false;
2978 }
2979
2980 override bool CanRemoveFromCargo(EntityAI parent)
2981 {
2982 return false;
2983 }
2984
2985 //hands
2986 override bool CanPutIntoHands(EntityAI parent)
2987 {
2988 return false;
2989 }
2990
2991 //--- ACTION CONDITIONS
2992 //direction
2993 override bool IsFacingPlayer(PlayerBase player, string selection)
2994 {
2995 return true;
2996 }
2997
2998 override bool IsPlayerInside(PlayerBase player, string selection)
2999 {
3000 return true;
3001 }
3002
3005 {
3006 return false;
3007 }
3008
3009 //camera direction check
3010 bool IsFacingCamera(string selection)
3011 {
3012 return true;
3013 }
3014
3015 //roof check
3017 {
3018 return false;
3019 }
3020
3021 //selection->player distance check
3022 bool HasProperDistance(string selection, PlayerBase player)
3023 {
3024 return true;
3025 }
3026
3027 //folding
3029 {
3030 if (HasBase() || GetInventory().AttachmentCount() > 0)
3031 return false;
3032
3033 return true;
3034 }
3035
3037 {
3040
3041 return item;
3042 }
3043
3044 //Damage triggers (barbed wire)
3045 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3046 {
3047 if (GetGame() && GetGame().IsServer())
3048 {
3049 //destroy area damage if some already exists
3051
3052 //create new area damage
3054 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3055
3056 vector min_max[2];
3057 if (MemoryPointExists(slot_name + "_min"))
3058 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3059 if (MemoryPointExists(slot_name + "_max"))
3060 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3061
3062 //get proper trigger extents (min<max)
3063 vector extents[2];
3064 GetConstruction().GetTriggerExtents(min_max, extents);
3065
3066 //get box center
3067 vector center;
3068 center = GetConstruction().GetBoxCenter(min_max);
3069 center = ModelToWorld(center);
3070
3071 //rotate center if needed
3074
3075 areaDamage.SetExtents(extents[0], extents[1]);
3076 areaDamage.SetAreaPosition(center);
3077 areaDamage.SetAreaOrientation(orientation);
3078 areaDamage.SetLoopInterval(1.0);
3079 areaDamage.SetDeferDuration(0.2);
3080 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3081 areaDamage.SetAmmoName("BarbedWireHit");
3082 areaDamage.Spawn();
3083
3085 }
3086 }
3087
3089 {
3090 if (angle_deg != 0)
3091 {
3092 //orientation
3094
3095 //center
3097 if (MemoryPointExists("rotate_axis"))
3098 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3101 center[0] = r_center_x;
3102 center[2] = r_center_z;
3103 }
3104 }
3105
3106 void DestroyAreaDamage(string slot_name)
3107 {
3108 if (GetGame() && GetGame().IsServer())
3109 {
3112 {
3113 if (areaDamage)
3114 areaDamage.Destroy();
3115
3117 }
3118 }
3119 }
3120
3121 override bool IsIgnoredByConstruction()
3122 {
3123 return true;
3124 }
3125
3126 //================================================================
3127 // SOUNDS
3128 //================================================================
3129 protected void SoundBuildStart(string part_name)
3130 {
3131 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3132 }
3133
3134 protected void SoundDismantleStart(string part_name)
3135 {
3136 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3137 }
3138
3139 protected void SoundDestroyStart(string part_name)
3140 {
3141 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3142 }
3143
3144 protected string GetBuildSoundByMaterial(string part_name)
3145 {
3147
3148 switch (material_type)
3149 {
3150 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3151 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3152 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3153 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3154 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3155 }
3156
3157 return "";
3158 }
3159
3160 protected string GetDismantleSoundByMaterial(string part_name)
3161 {
3163
3164 switch (material_type)
3165 {
3166 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3167 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3168 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3169 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3170 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3171 }
3172
3173 return "";
3174 }
3175
3176 //misc
3178 {
3179 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3180 {
3181 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3183 SetHealth(slot_name, "Health", item.GetHealth());
3184 }
3185 }
3186
3187 override int GetDamageSystemVersionChange()
3188 {
3189 return 111;
3190 }
3191
3192 override void SetActions()
3193 {
3194 super.SetActions();
3195
3197 //AddAction(ActionTakeHybridAttachment);
3198 //AddAction(ActionTakeHybridAttachmentToHands);
3201 }
3202
3203 //================================================================
3204 // DEBUG
3205 //================================================================
3206 protected void DebugCustomState()
3207 {
3208 }
3209
3212 {
3213 return null;
3214 }
3215
3216 override void OnDebugSpawn()
3217 {
3218 FullyBuild();
3219 }
3220
3221 void FullyBuild()
3222 {
3224 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3225
3226 Man p;
3227
3228#ifdef SERVER
3230 GetGame().GetWorld().GetPlayerList(players);
3231 if (players.Count())
3232 p = players[0];
3233#else
3234 p = GetGame().GetPlayer();
3235#endif
3236
3237 foreach (ConstructionPart part : parts)
3238 {
3239 bool excluded = false;
3240 string partName = part.GetPartName();
3241 if (excludes)
3242 {
3243 foreach (string exclude : excludes)
3244 {
3245 if (partName.Contains(exclude))
3246 {
3247 excluded = true;
3248 break;
3249 }
3250 }
3251 }
3252
3253 if (!excluded)
3255 }
3256
3257 GetConstruction().UpdateVisuals();
3258 }
3259}
3260
3261void bsbDebugPrint(string s)
3262{
3263#ifdef BSB_DEBUG
3264 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3265#else
3266 //Print("" + s); // comment/uncomment to hide/see debug logs
3267#endif
3268}
3269void bsbDebugSpam(string s)
3270{
3271#ifdef BSB_DEBUG_SPAM
3272 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3273#else
3274 //Print("" + s); // comment/uncomment to hide/see debug logs
3275#endif
3276}

◆ MustBeBuiltFromOutside()

bool bsbDebugPrint::MustBeBuiltFromOutside ( )
protected

Some buildings can only be built from outside.

Definition at line 2092 of file BaseBuildingBase.c.

2094{
2095 const string ANIMATION_DEPLOYED = "Deployed";
2096
2097 float m_ConstructionKitHealth; //stored health value for used construction kit
2098
2100
2101 bool m_HasBase;
2102 //variables for synchronization of base building parts (2x31 is the current limit)
2103 int m_SyncParts01; //synchronization for already built parts (31 parts)
2104 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2105 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2106 int m_InteractedPartId; //construction part id that an action was performed on
2107 int m_PerformedActionId; //action id that was performed on a construction part
2108
2109 //Sounds
2110 //build
2111 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2112 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2113 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2114 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2115 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2116 //dismantle
2117 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2118 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2119 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2120 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2121 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2122
2123 protected EffectSound m_Sound;
2124
2128
2129 // Constructor
2130 void BaseBuildingBase()
2131 {
2133
2134 //synchronized variables
2135 RegisterNetSyncVariableInt("m_SyncParts01");
2136 RegisterNetSyncVariableInt("m_SyncParts02");
2137 RegisterNetSyncVariableInt("m_SyncParts03");
2138 RegisterNetSyncVariableInt("m_InteractedPartId");
2139 RegisterNetSyncVariableInt("m_PerformedActionId");
2140 RegisterNetSyncVariableBool("m_HasBase");
2141
2142 //Construction init
2144
2145 if (ConfigIsExisting("hybridAttachments"))
2146 {
2148 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2149 }
2150 if (ConfigIsExisting("mountables"))
2151 {
2153 ConfigGetTextArray("mountables", m_Mountables);
2154 }
2155
2156 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2157 }
2158
2159 override void EEDelete(EntityAI parent)
2160 {
2161 super.EEDelete(parent);
2162
2163 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2165
2166 }
2167
2168 override string GetInvulnerabilityTypeString()
2169 {
2170 return "disableBaseDamage";
2171 }
2172
2173 override bool CanObstruct()
2174 {
2175 return true;
2176 }
2177
2178 override int GetHideIconMask()
2179 {
2180 return EInventoryIconVisibility.HIDE_VICINITY;
2181 }
2182
2183 // --- SYNCHRONIZATION
2185 {
2186 if (GetGame().IsServer())
2187 SetSynchDirty();
2188 }
2189
2190 override void OnVariablesSynchronized()
2191 {
2192 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2193 super.OnVariablesSynchronized();
2194
2195 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2196 }
2197
2198 protected void OnSynchronizedClient()
2199 {
2200 //update parts
2202
2203 //update action on part
2205
2206 //update visuals (client)
2207 UpdateVisuals();
2208 }
2209
2210 //parts synchronization
2212 {
2213 //part_id must starts from index = 1
2214 int offset;
2215 int mask;
2216
2217 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2218 {
2219 offset = part_id - 1;
2220 mask = 1 << offset;
2221
2223 }
2224 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2225 {
2226 offset = (part_id % 32);
2227 mask = 1 << offset;
2228
2230 }
2231 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2232 {
2233 offset = (part_id % 63);
2234 mask = 1 << offset;
2235
2237 }
2238 }
2239
2241 {
2242 //part_id must starts from index = 1
2243 int offset;
2244 int mask;
2245
2246 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2247 {
2248 offset = part_id - 1;
2249 mask = 1 << offset;
2250
2252 }
2253 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2254 {
2255 offset = (part_id % 32);
2256 mask = 1 << offset;
2257
2259 }
2260 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2261 {
2262 offset = (part_id % 63);
2263 mask = 1 << offset;
2264
2266 }
2267 }
2268
2270 {
2271 //part_id must starts from index = 1
2272 int offset;
2273 int mask;
2274
2275 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2276 {
2277 offset = part_id - 1;
2278 mask = 1 << offset;
2279
2280 if ((m_SyncParts01 & mask) > 0)
2281 return true;
2282 }
2283 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2284 {
2285 offset = (part_id % 32);
2286 mask = 1 << offset;
2287
2288 if ((m_SyncParts02 & mask) > 0)
2289 return true;
2290 }
2291 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2292 {
2293 offset = (part_id % 63);
2294 mask = 1 << offset;
2295
2296 if ((m_SyncParts03 & mask) > 0)
2297 return true;
2298 }
2299
2300 return false;
2301 }
2302
2303 protected void RegisterActionForSync(int part_id, int action_id)
2304 {
2307 }
2308
2309 protected void ResetActionSyncData()
2310 {
2311 //reset data
2312 m_InteractedPartId = -1;
2314 }
2315
2316 protected void SetActionFromSyncData()
2317 {
2318 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2319 {
2322
2323 switch (build_action_id)
2324 {
2328 }
2329 }
2330 }
2331 //------
2332
2334 {
2335 string key = part.m_PartName;
2336 bool is_base = part.IsBase();
2338 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2340 {
2341 if (!part.IsBuilt())
2342 {
2343 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2344 GetConstruction().AddToConstructedParts(key);
2345 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2346
2347 if (is_base)
2348 {
2350 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2351 }
2352 }
2353 }
2354 else
2355 {
2356 if (part.IsBuilt())
2357 {
2358 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2359 GetConstruction().RemoveFromConstructedParts(key);
2360 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2361
2362 if (is_base)
2363 {
2365 AddProxyPhysics(ANIMATION_DEPLOYED);
2366 }
2367 }
2368 }
2369
2370 //check slot lock for material attachments
2371 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2372 }
2373
2374 //set construction parts based on synchronized data
2376 {
2379
2380 for (int i = 0; i < construction_parts.Count(); ++i)
2381 {
2382 string key = construction_parts.GetKey(i);
2385 }
2386
2387 //regenerate navmesh
2388 UpdateNavmesh();
2389 }
2390
2392 {
2395
2396 for (int i = 0; i < construction_parts.Count(); ++i)
2397 {
2398 string key = construction_parts.GetKey(i);
2400
2401 if (value.GetId() == id)
2402 return value;
2403 }
2404
2405 return NULL;
2406 }
2407 //
2408
2409 //Base
2410 bool HasBase()
2411 {
2412 return m_HasBase;
2413 }
2414
2415 void SetBaseState(bool has_base)
2416 {
2418 }
2419
2420 override bool IsDeployable()
2421 {
2422 return true;
2423 }
2424
2425 bool IsOpened()
2426 {
2427 return false;
2428 }
2429
2430 //--- CONSTRUCTION KIT
2432 {
2436
2437 return construction_kit;
2438 }
2439
2441 {
2442 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2445 }
2446
2447 protected vector GetKitSpawnPosition()
2448 {
2449 return GetPosition();
2450 }
2451
2452 protected string GetConstructionKitType()
2453 {
2454 return "";
2455 }
2456
2458 {
2460 GetGame().ObjectDelete(construction_kit);
2461 }
2462
2463 //--- CONSTRUCTION
2464 void DestroyConstruction()
2465 {
2466 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2467 GetGame().ObjectDelete(this);
2468 }
2469
2470 // --- EVENTS
2471 override void OnStoreSave(ParamsWriteContext ctx)
2472 {
2473 super.OnStoreSave(ctx);
2474
2475 //sync parts 01
2476 ctx.Write(m_SyncParts01);
2477 ctx.Write(m_SyncParts02);
2478 ctx.Write(m_SyncParts03);
2479
2480 ctx.Write(m_HasBase);
2481 }
2482
2483 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2484 {
2485 if (!super.OnStoreLoad(ctx, version))
2486 return false;
2487
2488 //--- Base building data ---
2489 //Restore synced parts data
2490 if (!ctx.Read(m_SyncParts01))
2491 {
2492 m_SyncParts01 = 0; //set default
2493 return false;
2494 }
2495 if (!ctx.Read(m_SyncParts02))
2496 {
2497 m_SyncParts02 = 0; //set default
2498 return false;
2499 }
2500 if (!ctx.Read(m_SyncParts03))
2501 {
2502 m_SyncParts03 = 0; //set default
2503 return false;
2504 }
2505
2506 //has base
2507 if (!ctx.Read(m_HasBase))
2508 {
2509 m_HasBase = false;
2510 return false;
2511 }
2512 //---
2513
2514 return true;
2515 }
2516
2517 override void AfterStoreLoad()
2518 {
2519 super.AfterStoreLoad();
2520
2523 }
2524
2526 {
2527 //update server data
2529
2530 //set base state
2531 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2532 SetBaseState(construction_part.IsBuilt()) ;
2533
2534 //synchronize after load
2536 }
2537
2538 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2539 {
2541 return;
2542
2543 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2544
2545 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2546 return;
2547
2549 string part_name = zone;
2550 part_name.ToLower();
2551
2553 {
2555
2556 if (construction_part && construction.IsPartConstructed(part_name))
2557 {
2558 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2559 construction.DestroyConnectedParts(part_name);
2560 }
2561
2562 //barbed wire handling (hack-ish)
2563 if (part_name.Contains("barbed"))
2564 {
2565 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2566 if (barbed_wire)
2567 barbed_wire.SetMountedState(false);
2568 }
2569 }
2570 }
2571
2572 override void EEOnAfterLoad()
2573 {
2575 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2576
2577 super.EEOnAfterLoad();
2578 }
2579
2580 override void EEInit()
2581 {
2582 super.EEInit();
2583
2584 // init visuals and physics
2585 InitBaseState();
2586
2587 //debug
2588#ifdef DEVELOPER
2590#endif
2591 }
2592
2593 override void EEItemAttached(EntityAI item, string slot_name)
2594 {
2595 super.EEItemAttached(item, slot_name);
2596
2598 UpdateVisuals();
2600 }
2601
2602 override void EEItemDetached(EntityAI item, string slot_name)
2603 {
2604 super.EEItemDetached(item, slot_name);
2605
2606 UpdateVisuals();
2608 }
2609
2610 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2611 {
2613 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2614
2617 }
2618
2619 //ignore out of reach condition
2620 override bool IgnoreOutOfReachCondition()
2621 {
2622 return true;
2623 }
2624
2625 //CONSTRUCTION EVENTS
2626 //Build
2627 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2628 {
2630
2631 //check base state
2632 if (construtionPart.IsBase())
2633 {
2634 SetBaseState(true);
2635
2636 //spawn kit
2638 }
2639
2640 //register constructed parts for synchronization
2642
2643 //register action that was performed on part
2645
2646 //synchronize
2648
2649 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2650
2651 UpdateNavmesh();
2652
2653 //update visuals
2654 UpdateVisuals();
2655
2656 //reset action sync data
2657 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2658 }
2659
2660 void OnPartBuiltClient(string part_name, int action_id)
2661 {
2662 //play sound
2664 }
2665
2666 //Dismantle
2668 {
2669 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2671
2672 //register constructed parts for synchronization
2674
2675 //register action that was performed on part
2677
2678 //synchronize
2680
2681 // server part of sync, client will be synced from SetPartsFromSyncData
2683
2684 UpdateNavmesh();
2685
2686 //update visuals
2687 UpdateVisuals();
2688
2689 //reset action sync data
2690 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2691
2692 //check base state
2693 if (construtionPart.IsBase())
2694 {
2695 //Destroy construction
2696 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2697 }
2698 }
2699
2701 {
2702 //play sound
2704 }
2705
2706 //Destroy
2708 {
2709 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2711
2712 //register constructed parts for synchronization
2714
2715 //register action that was performed on part
2717
2718 //synchronize
2720
2721 // server part of sync, client will be synced from SetPartsFromSyncData
2723
2724 UpdateNavmesh();
2725
2726 //update visuals
2727 UpdateVisuals();
2728
2729 //reset action sync data
2730 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2731
2732 //check base state
2733 if (construtionPart.IsBase())
2734 {
2735 //Destroy construction
2736 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2737 }
2738 }
2739
2740 void OnPartDestroyedClient(string part_name, int action_id)
2741 {
2742 //play sound
2744 }
2745
2746 // --- UPDATE
2747 void InitBaseState()
2748 {
2749 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2750
2751 InitVisuals();
2752 UpdateNavmesh(); //regenerate navmesh
2753 GetConstruction().InitBaseState();
2754 }
2755
2756 void InitVisuals()
2757 {
2758 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2759 //check base
2760 if (!HasBase())
2761 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2762 else
2763 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2764
2765 GetConstruction().UpdateVisuals();
2766 }
2767
2768 void UpdateVisuals()
2769 {
2771
2773 foreach (string slotName : attachmentSlots)
2775
2776 //check base
2777 if (!HasBase())
2778 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2779 else
2780 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2781
2782 GetConstruction().UpdateVisuals();
2783 }
2784
2786 {
2787 string slotNameMounted = slot_name + "_Mounted";
2788 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2789
2790 if (attachment)
2791 {
2792 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2793 if (barbedWire && barbedWire.IsMounted())
2795 else
2797
2798 if (is_locked)
2799 {
2800 SetAnimationPhase(slotNameMounted, 0);
2801 SetAnimationPhase(slot_name, 1);
2802 }
2803 else
2804 {
2805 SetAnimationPhase(slotNameMounted, 1);
2806 SetAnimationPhase(slot_name, 0);
2807 }
2808 }
2809 else
2810 {
2811 SetAnimationPhase(slotNameMounted, 1);
2812 SetAnimationPhase(slot_name, 1);
2813
2815 }
2816 }
2817
2818 // avoid calling this function on frequent occasions, it's a massive performance hit
2819 void UpdatePhysics()
2820 {
2822 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2823
2826
2828 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2829
2830 foreach (string slotName : attachmentSlots)
2832
2833 //check base
2834 if (!HasBase())
2835 {
2837 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2838
2839 AddProxyPhysics(ANIMATION_DEPLOYED);
2840 }
2841 else
2842 {
2844 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2845
2846 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2847 }
2848
2849 GetConstruction().UpdatePhysics();
2850 UpdateNavmesh();
2851 }
2852
2854 {
2855 //checks for invalid appends; hotfix
2856 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2857 return;
2858 //----------------------------------
2859 string slot_name_mounted = slot_name + "_Mounted";
2860 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2861
2862 //remove proxy physics
2863 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2864 RemoveProxyPhysics(slot_name_mounted);
2865 RemoveProxyPhysics(slot_name);
2866
2867 if (attachment)
2868 {
2869 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2870 if (is_locked)
2871 {
2872 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2873 AddProxyPhysics(slot_name_mounted);
2874 }
2875 else
2876 {
2877 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2878 AddProxyPhysics(slot_name);
2879 }
2880 }
2881 }
2882
2883 protected void UpdateNavmesh()
2884 {
2885 SetAffectPathgraph(true, false);
2886 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2887 }
2888
2889 override bool CanUseConstruction()
2890 {
2891 return true;
2892 }
2893
2894 override bool CanUseConstructionBuild()
2895 {
2896 return true;
2897 }
2898
2900 {
2901 if (attachment)
2902 {
2904 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2905
2906 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2907 }
2908
2909 return false;
2910 }
2911
2912 protected bool IsAttachmentSlotLocked(string slot_name)
2913 {
2914 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2915 }
2916
2917 //--- ATTACHMENT SLOTS
2919 {
2920 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2921 if (GetGame().ConfigIsExisting(config_path))
2922 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2923 }
2924
2926 {
2927 return true;
2928 }
2929
2930 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2931 {
2932 return true;
2933 }
2934
2935 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2936 {
2937 return true;
2938 }
2939
2940 // --- INIT
2941 void ConstructionInit()
2942 {
2943 if (!m_Construction)
2944 m_Construction = new Construction(this);
2945
2946 GetConstruction().Init();
2947 }
2948
2950 {
2951 return m_Construction;
2952 }
2953
2954 //--- INVENTORY/ATTACHMENTS CONDITIONS
2955 //attachments
2957 {
2958 return super.CanReceiveAttachment(attachment, slotId);
2959 }
2960
2962 {
2963 int attachment_count = GetInventory().AttachmentCount();
2964 if (attachment_count > 0)
2965 {
2966 if (HasBase() && attachment_count == 1)
2967 return false;
2968
2969 return true;
2970 }
2971
2972 return false;
2973 }
2974
2975 override bool ShowZonesHealth()
2976 {
2977 return true;
2978 }
2979
2980 //this into/outo parent.Cargo
2981 override bool CanPutInCargo(EntityAI parent)
2982 {
2983 return false;
2984 }
2985
2986 override bool CanRemoveFromCargo(EntityAI parent)
2987 {
2988 return false;
2989 }
2990
2991 //hands
2992 override bool CanPutIntoHands(EntityAI parent)
2993 {
2994 return false;
2995 }
2996
2997 //--- ACTION CONDITIONS
2998 //direction
2999 override bool IsFacingPlayer(PlayerBase player, string selection)
3000 {
3001 return true;
3002 }
3003
3004 override bool IsPlayerInside(PlayerBase player, string selection)
3005 {
3006 return true;
3007 }
3008
3011 {
3012 return false;
3013 }
3014
3015 //camera direction check
3016 bool IsFacingCamera(string selection)
3017 {
3018 return true;
3019 }
3020
3021 //roof check
3023 {
3024 return false;
3025 }
3026
3027 //selection->player distance check
3028 bool HasProperDistance(string selection, PlayerBase player)
3029 {
3030 return true;
3031 }
3032
3033 //folding
3035 {
3036 if (HasBase() || GetInventory().AttachmentCount() > 0)
3037 return false;
3038
3039 return true;
3040 }
3041
3043 {
3046
3047 return item;
3048 }
3049
3050 //Damage triggers (barbed wire)
3051 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3052 {
3053 if (GetGame() && GetGame().IsServer())
3054 {
3055 //destroy area damage if some already exists
3057
3058 //create new area damage
3060 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3061
3062 vector min_max[2];
3063 if (MemoryPointExists(slot_name + "_min"))
3064 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3065 if (MemoryPointExists(slot_name + "_max"))
3066 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3067
3068 //get proper trigger extents (min<max)
3069 vector extents[2];
3070 GetConstruction().GetTriggerExtents(min_max, extents);
3071
3072 //get box center
3073 vector center;
3074 center = GetConstruction().GetBoxCenter(min_max);
3075 center = ModelToWorld(center);
3076
3077 //rotate center if needed
3080
3081 areaDamage.SetExtents(extents[0], extents[1]);
3082 areaDamage.SetAreaPosition(center);
3083 areaDamage.SetAreaOrientation(orientation);
3084 areaDamage.SetLoopInterval(1.0);
3085 areaDamage.SetDeferDuration(0.2);
3086 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3087 areaDamage.SetAmmoName("BarbedWireHit");
3088 areaDamage.Spawn();
3089
3091 }
3092 }
3093
3095 {
3096 if (angle_deg != 0)
3097 {
3098 //orientation
3100
3101 //center
3103 if (MemoryPointExists("rotate_axis"))
3104 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3107 center[0] = r_center_x;
3108 center[2] = r_center_z;
3109 }
3110 }
3111
3112 void DestroyAreaDamage(string slot_name)
3113 {
3114 if (GetGame() && GetGame().IsServer())
3115 {
3118 {
3119 if (areaDamage)
3120 areaDamage.Destroy();
3121
3123 }
3124 }
3125 }
3126
3127 override bool IsIgnoredByConstruction()
3128 {
3129 return true;
3130 }
3131
3132 //================================================================
3133 // SOUNDS
3134 //================================================================
3135 protected void SoundBuildStart(string part_name)
3136 {
3137 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3138 }
3139
3140 protected void SoundDismantleStart(string part_name)
3141 {
3142 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3143 }
3144
3145 protected void SoundDestroyStart(string part_name)
3146 {
3147 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3148 }
3149
3150 protected string GetBuildSoundByMaterial(string part_name)
3151 {
3153
3154 switch (material_type)
3155 {
3156 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3157 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3158 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3159 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3160 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3161 }
3162
3163 return "";
3164 }
3165
3166 protected string GetDismantleSoundByMaterial(string part_name)
3167 {
3169
3170 switch (material_type)
3171 {
3172 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3173 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3174 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3175 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3176 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3177 }
3178
3179 return "";
3180 }
3181
3182 //misc
3184 {
3185 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3186 {
3187 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3189 SetHealth(slot_name, "Health", item.GetHealth());
3190 }
3191 }
3192
3193 override int GetDamageSystemVersionChange()
3194 {
3195 return 111;
3196 }
3197
3198 override void SetActions()
3199 {
3200 super.SetActions();
3201
3203 //AddAction(ActionTakeHybridAttachment);
3204 //AddAction(ActionTakeHybridAttachmentToHands);
3207 }
3208
3209 //================================================================
3210 // DEBUG
3211 //================================================================
3212 protected void DebugCustomState()
3213 {
3214 }
3215
3218 {
3219 return null;
3220 }
3221
3222 override void OnDebugSpawn()
3223 {
3224 FullyBuild();
3225 }
3226
3227 void FullyBuild()
3228 {
3230 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3231
3232 Man p;
3233
3234#ifdef SERVER
3236 GetGame().GetWorld().GetPlayerList(players);
3237 if (players.Count())
3238 p = players[0];
3239#else
3240 p = GetGame().GetPlayer();
3241#endif
3242
3243 foreach (ConstructionPart part : parts)
3244 {
3245 bool excluded = false;
3246 string partName = part.GetPartName();
3247 if (excludes)
3248 {
3249 foreach (string exclude : excludes)
3250 {
3251 if (partName.Contains(exclude))
3252 {
3253 excluded = true;
3254 break;
3255 }
3256 }
3257 }
3258
3259 if (!excluded)
3261 }
3262
3263 GetConstruction().UpdateVisuals();
3264 }
3265}
3266
3267void bsbDebugPrint(string s)
3268{
3269#ifdef BSB_DEBUG
3270 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3271#else
3272 //Print("" + s); // comment/uncomment to hide/see debug logs
3273#endif
3274}
3275void bsbDebugSpam(string s)
3276{
3277#ifdef BSB_DEBUG_SPAM
3278 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3279#else
3280 //Print("" + s); // comment/uncomment to hide/see debug logs
3281#endif
3282}

◆ OnDebugSpawn()

override void bsbDebugPrint::OnDebugSpawn ( )
protected

Definition at line 2304 of file BaseBuildingBase.c.

2306{
2307 const string ANIMATION_DEPLOYED = "Deployed";
2308
2309 float m_ConstructionKitHealth; //stored health value for used construction kit
2310
2312
2313 bool m_HasBase;
2314 //variables for synchronization of base building parts (2x31 is the current limit)
2315 int m_SyncParts01; //synchronization for already built parts (31 parts)
2316 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2317 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2318 int m_InteractedPartId; //construction part id that an action was performed on
2319 int m_PerformedActionId; //action id that was performed on a construction part
2320
2321 //Sounds
2322 //build
2323 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2324 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2325 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2326 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2327 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2328 //dismantle
2329 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2330 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2331 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2332 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2333 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2334
2335 protected EffectSound m_Sound;
2336
2340
2341 // Constructor
2342 void BaseBuildingBase()
2343 {
2345
2346 //synchronized variables
2347 RegisterNetSyncVariableInt("m_SyncParts01");
2348 RegisterNetSyncVariableInt("m_SyncParts02");
2349 RegisterNetSyncVariableInt("m_SyncParts03");
2350 RegisterNetSyncVariableInt("m_InteractedPartId");
2351 RegisterNetSyncVariableInt("m_PerformedActionId");
2352 RegisterNetSyncVariableBool("m_HasBase");
2353
2354 //Construction init
2356
2357 if (ConfigIsExisting("hybridAttachments"))
2358 {
2360 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2361 }
2362 if (ConfigIsExisting("mountables"))
2363 {
2365 ConfigGetTextArray("mountables", m_Mountables);
2366 }
2367
2368 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2369 }
2370
2371 override void EEDelete(EntityAI parent)
2372 {
2373 super.EEDelete(parent);
2374
2375 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2377
2378 }
2379
2380 override string GetInvulnerabilityTypeString()
2381 {
2382 return "disableBaseDamage";
2383 }
2384
2385 override bool CanObstruct()
2386 {
2387 return true;
2388 }
2389
2390 override int GetHideIconMask()
2391 {
2392 return EInventoryIconVisibility.HIDE_VICINITY;
2393 }
2394
2395 // --- SYNCHRONIZATION
2397 {
2398 if (GetGame().IsServer())
2399 SetSynchDirty();
2400 }
2401
2402 override void OnVariablesSynchronized()
2403 {
2404 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2405 super.OnVariablesSynchronized();
2406
2407 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2408 }
2409
2410 protected void OnSynchronizedClient()
2411 {
2412 //update parts
2414
2415 //update action on part
2417
2418 //update visuals (client)
2419 UpdateVisuals();
2420 }
2421
2422 //parts synchronization
2424 {
2425 //part_id must starts from index = 1
2426 int offset;
2427 int mask;
2428
2429 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2430 {
2431 offset = part_id - 1;
2432 mask = 1 << offset;
2433
2435 }
2436 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2437 {
2438 offset = (part_id % 32);
2439 mask = 1 << offset;
2440
2442 }
2443 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2444 {
2445 offset = (part_id % 63);
2446 mask = 1 << offset;
2447
2449 }
2450 }
2451
2453 {
2454 //part_id must starts from index = 1
2455 int offset;
2456 int mask;
2457
2458 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2459 {
2460 offset = part_id - 1;
2461 mask = 1 << offset;
2462
2464 }
2465 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2466 {
2467 offset = (part_id % 32);
2468 mask = 1 << offset;
2469
2471 }
2472 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2473 {
2474 offset = (part_id % 63);
2475 mask = 1 << offset;
2476
2478 }
2479 }
2480
2482 {
2483 //part_id must starts from index = 1
2484 int offset;
2485 int mask;
2486
2487 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2488 {
2489 offset = part_id - 1;
2490 mask = 1 << offset;
2491
2492 if ((m_SyncParts01 & mask) > 0)
2493 return true;
2494 }
2495 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2496 {
2497 offset = (part_id % 32);
2498 mask = 1 << offset;
2499
2500 if ((m_SyncParts02 & mask) > 0)
2501 return true;
2502 }
2503 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2504 {
2505 offset = (part_id % 63);
2506 mask = 1 << offset;
2507
2508 if ((m_SyncParts03 & mask) > 0)
2509 return true;
2510 }
2511
2512 return false;
2513 }
2514
2515 protected void RegisterActionForSync(int part_id, int action_id)
2516 {
2519 }
2520
2521 protected void ResetActionSyncData()
2522 {
2523 //reset data
2524 m_InteractedPartId = -1;
2526 }
2527
2528 protected void SetActionFromSyncData()
2529 {
2530 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2531 {
2534
2535 switch (build_action_id)
2536 {
2540 }
2541 }
2542 }
2543 //------
2544
2546 {
2547 string key = part.m_PartName;
2548 bool is_base = part.IsBase();
2550 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2552 {
2553 if (!part.IsBuilt())
2554 {
2555 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2556 GetConstruction().AddToConstructedParts(key);
2557 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2558
2559 if (is_base)
2560 {
2562 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2563 }
2564 }
2565 }
2566 else
2567 {
2568 if (part.IsBuilt())
2569 {
2570 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2571 GetConstruction().RemoveFromConstructedParts(key);
2572 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2573
2574 if (is_base)
2575 {
2577 AddProxyPhysics(ANIMATION_DEPLOYED);
2578 }
2579 }
2580 }
2581
2582 //check slot lock for material attachments
2583 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2584 }
2585
2586 //set construction parts based on synchronized data
2588 {
2591
2592 for (int i = 0; i < construction_parts.Count(); ++i)
2593 {
2594 string key = construction_parts.GetKey(i);
2597 }
2598
2599 //regenerate navmesh
2600 UpdateNavmesh();
2601 }
2602
2604 {
2607
2608 for (int i = 0; i < construction_parts.Count(); ++i)
2609 {
2610 string key = construction_parts.GetKey(i);
2612
2613 if (value.GetId() == id)
2614 return value;
2615 }
2616
2617 return NULL;
2618 }
2619 //
2620
2621 //Base
2622 bool HasBase()
2623 {
2624 return m_HasBase;
2625 }
2626
2627 void SetBaseState(bool has_base)
2628 {
2630 }
2631
2632 override bool IsDeployable()
2633 {
2634 return true;
2635 }
2636
2637 bool IsOpened()
2638 {
2639 return false;
2640 }
2641
2642 //--- CONSTRUCTION KIT
2644 {
2648
2649 return construction_kit;
2650 }
2651
2653 {
2654 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2657 }
2658
2659 protected vector GetKitSpawnPosition()
2660 {
2661 return GetPosition();
2662 }
2663
2664 protected string GetConstructionKitType()
2665 {
2666 return "";
2667 }
2668
2670 {
2672 GetGame().ObjectDelete(construction_kit);
2673 }
2674
2675 //--- CONSTRUCTION
2676 void DestroyConstruction()
2677 {
2678 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2679 GetGame().ObjectDelete(this);
2680 }
2681
2682 // --- EVENTS
2683 override void OnStoreSave(ParamsWriteContext ctx)
2684 {
2685 super.OnStoreSave(ctx);
2686
2687 //sync parts 01
2688 ctx.Write(m_SyncParts01);
2689 ctx.Write(m_SyncParts02);
2690 ctx.Write(m_SyncParts03);
2691
2692 ctx.Write(m_HasBase);
2693 }
2694
2695 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2696 {
2697 if (!super.OnStoreLoad(ctx, version))
2698 return false;
2699
2700 //--- Base building data ---
2701 //Restore synced parts data
2702 if (!ctx.Read(m_SyncParts01))
2703 {
2704 m_SyncParts01 = 0; //set default
2705 return false;
2706 }
2707 if (!ctx.Read(m_SyncParts02))
2708 {
2709 m_SyncParts02 = 0; //set default
2710 return false;
2711 }
2712 if (!ctx.Read(m_SyncParts03))
2713 {
2714 m_SyncParts03 = 0; //set default
2715 return false;
2716 }
2717
2718 //has base
2719 if (!ctx.Read(m_HasBase))
2720 {
2721 m_HasBase = false;
2722 return false;
2723 }
2724 //---
2725
2726 return true;
2727 }
2728
2729 override void AfterStoreLoad()
2730 {
2731 super.AfterStoreLoad();
2732
2735 }
2736
2738 {
2739 //update server data
2741
2742 //set base state
2743 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2744 SetBaseState(construction_part.IsBuilt()) ;
2745
2746 //synchronize after load
2748 }
2749
2750 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2751 {
2753 return;
2754
2755 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2756
2757 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2758 return;
2759
2761 string part_name = zone;
2762 part_name.ToLower();
2763
2765 {
2767
2768 if (construction_part && construction.IsPartConstructed(part_name))
2769 {
2770 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2771 construction.DestroyConnectedParts(part_name);
2772 }
2773
2774 //barbed wire handling (hack-ish)
2775 if (part_name.Contains("barbed"))
2776 {
2777 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2778 if (barbed_wire)
2779 barbed_wire.SetMountedState(false);
2780 }
2781 }
2782 }
2783
2784 override void EEOnAfterLoad()
2785 {
2787 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2788
2789 super.EEOnAfterLoad();
2790 }
2791
2792 override void EEInit()
2793 {
2794 super.EEInit();
2795
2796 // init visuals and physics
2797 InitBaseState();
2798
2799 //debug
2800#ifdef DEVELOPER
2802#endif
2803 }
2804
2805 override void EEItemAttached(EntityAI item, string slot_name)
2806 {
2807 super.EEItemAttached(item, slot_name);
2808
2810 UpdateVisuals();
2812 }
2813
2814 override void EEItemDetached(EntityAI item, string slot_name)
2815 {
2816 super.EEItemDetached(item, slot_name);
2817
2818 UpdateVisuals();
2820 }
2821
2822 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2823 {
2825 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2826
2829 }
2830
2831 //ignore out of reach condition
2832 override bool IgnoreOutOfReachCondition()
2833 {
2834 return true;
2835 }
2836
2837 //CONSTRUCTION EVENTS
2838 //Build
2839 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2840 {
2842
2843 //check base state
2844 if (construtionPart.IsBase())
2845 {
2846 SetBaseState(true);
2847
2848 //spawn kit
2850 }
2851
2852 //register constructed parts for synchronization
2854
2855 //register action that was performed on part
2857
2858 //synchronize
2860
2861 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2862
2863 UpdateNavmesh();
2864
2865 //update visuals
2866 UpdateVisuals();
2867
2868 //reset action sync data
2869 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2870 }
2871
2872 void OnPartBuiltClient(string part_name, int action_id)
2873 {
2874 //play sound
2876 }
2877
2878 //Dismantle
2880 {
2881 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2883
2884 //register constructed parts for synchronization
2886
2887 //register action that was performed on part
2889
2890 //synchronize
2892
2893 // server part of sync, client will be synced from SetPartsFromSyncData
2895
2896 UpdateNavmesh();
2897
2898 //update visuals
2899 UpdateVisuals();
2900
2901 //reset action sync data
2902 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2903
2904 //check base state
2905 if (construtionPart.IsBase())
2906 {
2907 //Destroy construction
2908 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2909 }
2910 }
2911
2913 {
2914 //play sound
2916 }
2917
2918 //Destroy
2920 {
2921 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2923
2924 //register constructed parts for synchronization
2926
2927 //register action that was performed on part
2929
2930 //synchronize
2932
2933 // server part of sync, client will be synced from SetPartsFromSyncData
2935
2936 UpdateNavmesh();
2937
2938 //update visuals
2939 UpdateVisuals();
2940
2941 //reset action sync data
2942 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2943
2944 //check base state
2945 if (construtionPart.IsBase())
2946 {
2947 //Destroy construction
2948 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2949 }
2950 }
2951
2952 void OnPartDestroyedClient(string part_name, int action_id)
2953 {
2954 //play sound
2956 }
2957
2958 // --- UPDATE
2959 void InitBaseState()
2960 {
2961 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2962
2963 InitVisuals();
2964 UpdateNavmesh(); //regenerate navmesh
2965 GetConstruction().InitBaseState();
2966 }
2967
2968 void InitVisuals()
2969 {
2970 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2971 //check base
2972 if (!HasBase())
2973 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2974 else
2975 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2976
2977 GetConstruction().UpdateVisuals();
2978 }
2979
2980 void UpdateVisuals()
2981 {
2983
2985 foreach (string slotName : attachmentSlots)
2987
2988 //check base
2989 if (!HasBase())
2990 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2991 else
2992 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2993
2994 GetConstruction().UpdateVisuals();
2995 }
2996
2998 {
2999 string slotNameMounted = slot_name + "_Mounted";
3000 EntityAI attachment = FindAttachmentBySlotName(slot_name);
3001
3002 if (attachment)
3003 {
3004 BarbedWire barbedWire = BarbedWire.Cast(attachment);
3005 if (barbedWire && barbedWire.IsMounted())
3007 else
3009
3010 if (is_locked)
3011 {
3012 SetAnimationPhase(slotNameMounted, 0);
3013 SetAnimationPhase(slot_name, 1);
3014 }
3015 else
3016 {
3017 SetAnimationPhase(slotNameMounted, 1);
3018 SetAnimationPhase(slot_name, 0);
3019 }
3020 }
3021 else
3022 {
3023 SetAnimationPhase(slotNameMounted, 1);
3024 SetAnimationPhase(slot_name, 1);
3025
3027 }
3028 }
3029
3030 // avoid calling this function on frequent occasions, it's a massive performance hit
3031 void UpdatePhysics()
3032 {
3034 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
3035
3038
3040 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
3041
3042 foreach (string slotName : attachmentSlots)
3044
3045 //check base
3046 if (!HasBase())
3047 {
3049 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
3050
3051 AddProxyPhysics(ANIMATION_DEPLOYED);
3052 }
3053 else
3054 {
3056 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
3057
3058 RemoveProxyPhysics(ANIMATION_DEPLOYED);
3059 }
3060
3061 GetConstruction().UpdatePhysics();
3062 UpdateNavmesh();
3063 }
3064
3066 {
3067 //checks for invalid appends; hotfix
3068 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
3069 return;
3070 //----------------------------------
3071 string slot_name_mounted = slot_name + "_Mounted";
3072 EntityAI attachment = FindAttachmentBySlotName(slot_name);
3073
3074 //remove proxy physics
3075 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
3076 RemoveProxyPhysics(slot_name_mounted);
3077 RemoveProxyPhysics(slot_name);
3078
3079 if (attachment)
3080 {
3081 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
3082 if (is_locked)
3083 {
3084 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
3085 AddProxyPhysics(slot_name_mounted);
3086 }
3087 else
3088 {
3089 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
3090 AddProxyPhysics(slot_name);
3091 }
3092 }
3093 }
3094
3095 protected void UpdateNavmesh()
3096 {
3097 SetAffectPathgraph(true, false);
3098 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
3099 }
3100
3101 override bool CanUseConstruction()
3102 {
3103 return true;
3104 }
3105
3106 override bool CanUseConstructionBuild()
3107 {
3108 return true;
3109 }
3110
3112 {
3113 if (attachment)
3114 {
3116 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
3117
3118 return GetInventory().GetSlotLock(inventory_location.GetSlot());
3119 }
3120
3121 return false;
3122 }
3123
3124 protected bool IsAttachmentSlotLocked(string slot_name)
3125 {
3126 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
3127 }
3128
3129 //--- ATTACHMENT SLOTS
3131 {
3132 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
3133 if (GetGame().ConfigIsExisting(config_path))
3134 GetGame().ConfigGetTextArray(config_path, attachment_slots);
3135 }
3136
3138 {
3139 return true;
3140 }
3141
3142 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
3143 {
3144 return true;
3145 }
3146
3147 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
3148 {
3149 return true;
3150 }
3151
3152 // --- INIT
3153 void ConstructionInit()
3154 {
3155 if (!m_Construction)
3156 m_Construction = new Construction(this);
3157
3158 GetConstruction().Init();
3159 }
3160
3162 {
3163 return m_Construction;
3164 }
3165
3166 //--- INVENTORY/ATTACHMENTS CONDITIONS
3167 //attachments
3169 {
3170 return super.CanReceiveAttachment(attachment, slotId);
3171 }
3172
3174 {
3175 int attachment_count = GetInventory().AttachmentCount();
3176 if (attachment_count > 0)
3177 {
3178 if (HasBase() && attachment_count == 1)
3179 return false;
3180
3181 return true;
3182 }
3183
3184 return false;
3185 }
3186
3187 override bool ShowZonesHealth()
3188 {
3189 return true;
3190 }
3191
3192 //this into/outo parent.Cargo
3193 override bool CanPutInCargo(EntityAI parent)
3194 {
3195 return false;
3196 }
3197
3198 override bool CanRemoveFromCargo(EntityAI parent)
3199 {
3200 return false;
3201 }
3202
3203 //hands
3204 override bool CanPutIntoHands(EntityAI parent)
3205 {
3206 return false;
3207 }
3208
3209 //--- ACTION CONDITIONS
3210 //direction
3211 override bool IsFacingPlayer(PlayerBase player, string selection)
3212 {
3213 return true;
3214 }
3215
3216 override bool IsPlayerInside(PlayerBase player, string selection)
3217 {
3218 return true;
3219 }
3220
3223 {
3224 return false;
3225 }
3226
3227 //camera direction check
3228 bool IsFacingCamera(string selection)
3229 {
3230 return true;
3231 }
3232
3233 //roof check
3235 {
3236 return false;
3237 }
3238
3239 //selection->player distance check
3240 bool HasProperDistance(string selection, PlayerBase player)
3241 {
3242 return true;
3243 }
3244
3245 //folding
3247 {
3248 if (HasBase() || GetInventory().AttachmentCount() > 0)
3249 return false;
3250
3251 return true;
3252 }
3253
3255 {
3258
3259 return item;
3260 }
3261
3262 //Damage triggers (barbed wire)
3263 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3264 {
3265 if (GetGame() && GetGame().IsServer())
3266 {
3267 //destroy area damage if some already exists
3269
3270 //create new area damage
3272 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3273
3274 vector min_max[2];
3275 if (MemoryPointExists(slot_name + "_min"))
3276 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3277 if (MemoryPointExists(slot_name + "_max"))
3278 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3279
3280 //get proper trigger extents (min<max)
3281 vector extents[2];
3282 GetConstruction().GetTriggerExtents(min_max, extents);
3283
3284 //get box center
3285 vector center;
3286 center = GetConstruction().GetBoxCenter(min_max);
3287 center = ModelToWorld(center);
3288
3289 //rotate center if needed
3292
3293 areaDamage.SetExtents(extents[0], extents[1]);
3294 areaDamage.SetAreaPosition(center);
3295 areaDamage.SetAreaOrientation(orientation);
3296 areaDamage.SetLoopInterval(1.0);
3297 areaDamage.SetDeferDuration(0.2);
3298 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3299 areaDamage.SetAmmoName("BarbedWireHit");
3300 areaDamage.Spawn();
3301
3303 }
3304 }
3305
3307 {
3308 if (angle_deg != 0)
3309 {
3310 //orientation
3312
3313 //center
3315 if (MemoryPointExists("rotate_axis"))
3316 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3319 center[0] = r_center_x;
3320 center[2] = r_center_z;
3321 }
3322 }
3323
3324 void DestroyAreaDamage(string slot_name)
3325 {
3326 if (GetGame() && GetGame().IsServer())
3327 {
3330 {
3331 if (areaDamage)
3332 areaDamage.Destroy();
3333
3335 }
3336 }
3337 }
3338
3339 override bool IsIgnoredByConstruction()
3340 {
3341 return true;
3342 }
3343
3344 //================================================================
3345 // SOUNDS
3346 //================================================================
3347 protected void SoundBuildStart(string part_name)
3348 {
3349 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3350 }
3351
3352 protected void SoundDismantleStart(string part_name)
3353 {
3354 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3355 }
3356
3357 protected void SoundDestroyStart(string part_name)
3358 {
3359 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3360 }
3361
3362 protected string GetBuildSoundByMaterial(string part_name)
3363 {
3365
3366 switch (material_type)
3367 {
3368 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3369 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3370 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3371 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3372 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3373 }
3374
3375 return "";
3376 }
3377
3378 protected string GetDismantleSoundByMaterial(string part_name)
3379 {
3381
3382 switch (material_type)
3383 {
3384 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3385 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3386 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3387 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3388 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3389 }
3390
3391 return "";
3392 }
3393
3394 //misc
3396 {
3397 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3398 {
3399 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3401 SetHealth(slot_name, "Health", item.GetHealth());
3402 }
3403 }
3404
3405 override int GetDamageSystemVersionChange()
3406 {
3407 return 111;
3408 }
3409
3410 override void SetActions()
3411 {
3412 super.SetActions();
3413
3415 //AddAction(ActionTakeHybridAttachment);
3416 //AddAction(ActionTakeHybridAttachmentToHands);
3419 }
3420
3421 //================================================================
3422 // DEBUG
3423 //================================================================
3424 protected void DebugCustomState()
3425 {
3426 }
3427
3430 {
3431 return null;
3432 }
3433
3434 override void OnDebugSpawn()
3435 {
3436 FullyBuild();
3437 }
3438
3439 void FullyBuild()
3440 {
3442 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3443
3444 Man p;
3445
3446#ifdef SERVER
3448 GetGame().GetWorld().GetPlayerList(players);
3449 if (players.Count())
3450 p = players[0];
3451#else
3452 p = GetGame().GetPlayer();
3453#endif
3454
3455 foreach (ConstructionPart part : parts)
3456 {
3457 bool excluded = false;
3458 string partName = part.GetPartName();
3459 if (excludes)
3460 {
3461 foreach (string exclude : excludes)
3462 {
3463 if (partName.Contains(exclude))
3464 {
3465 excluded = true;
3466 break;
3467 }
3468 }
3469 }
3470
3471 if (!excluded)
3473 }
3474
3475 GetConstruction().UpdateVisuals();
3476 }
3477}
3478
3479void bsbDebugPrint(string s)
3480{
3481#ifdef BSB_DEBUG
3482 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3483#else
3484 //Print("" + s); // comment/uncomment to hide/see debug logs
3485#endif
3486}
3487void bsbDebugSpam(string s)
3488{
3489#ifdef BSB_DEBUG_SPAM
3490 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3491#else
3492 //Print("" + s); // comment/uncomment to hide/see debug logs
3493#endif
3494}

◆ OnDebugSpawnBuildExcludes()

array< string > bsbDebugPrint::OnDebugSpawnBuildExcludes ( )
protected

Excludes certain parts from being built by OnDebugSpawn, uses Contains to compare.

Definition at line 2299 of file BaseBuildingBase.c.

2301{
2302 const string ANIMATION_DEPLOYED = "Deployed";
2303
2304 float m_ConstructionKitHealth; //stored health value for used construction kit
2305
2307
2308 bool m_HasBase;
2309 //variables for synchronization of base building parts (2x31 is the current limit)
2310 int m_SyncParts01; //synchronization for already built parts (31 parts)
2311 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2312 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2313 int m_InteractedPartId; //construction part id that an action was performed on
2314 int m_PerformedActionId; //action id that was performed on a construction part
2315
2316 //Sounds
2317 //build
2318 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2319 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2320 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2321 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2322 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2323 //dismantle
2324 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2325 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2326 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2327 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2328 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2329
2330 protected EffectSound m_Sound;
2331
2335
2336 // Constructor
2337 void BaseBuildingBase()
2338 {
2340
2341 //synchronized variables
2342 RegisterNetSyncVariableInt("m_SyncParts01");
2343 RegisterNetSyncVariableInt("m_SyncParts02");
2344 RegisterNetSyncVariableInt("m_SyncParts03");
2345 RegisterNetSyncVariableInt("m_InteractedPartId");
2346 RegisterNetSyncVariableInt("m_PerformedActionId");
2347 RegisterNetSyncVariableBool("m_HasBase");
2348
2349 //Construction init
2351
2352 if (ConfigIsExisting("hybridAttachments"))
2353 {
2355 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2356 }
2357 if (ConfigIsExisting("mountables"))
2358 {
2360 ConfigGetTextArray("mountables", m_Mountables);
2361 }
2362
2363 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2364 }
2365
2366 override void EEDelete(EntityAI parent)
2367 {
2368 super.EEDelete(parent);
2369
2370 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2372
2373 }
2374
2375 override string GetInvulnerabilityTypeString()
2376 {
2377 return "disableBaseDamage";
2378 }
2379
2380 override bool CanObstruct()
2381 {
2382 return true;
2383 }
2384
2385 override int GetHideIconMask()
2386 {
2387 return EInventoryIconVisibility.HIDE_VICINITY;
2388 }
2389
2390 // --- SYNCHRONIZATION
2392 {
2393 if (GetGame().IsServer())
2394 SetSynchDirty();
2395 }
2396
2397 override void OnVariablesSynchronized()
2398 {
2399 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2400 super.OnVariablesSynchronized();
2401
2402 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2403 }
2404
2405 protected void OnSynchronizedClient()
2406 {
2407 //update parts
2409
2410 //update action on part
2412
2413 //update visuals (client)
2414 UpdateVisuals();
2415 }
2416
2417 //parts synchronization
2419 {
2420 //part_id must starts from index = 1
2421 int offset;
2422 int mask;
2423
2424 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2425 {
2426 offset = part_id - 1;
2427 mask = 1 << offset;
2428
2430 }
2431 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2432 {
2433 offset = (part_id % 32);
2434 mask = 1 << offset;
2435
2437 }
2438 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2439 {
2440 offset = (part_id % 63);
2441 mask = 1 << offset;
2442
2444 }
2445 }
2446
2448 {
2449 //part_id must starts from index = 1
2450 int offset;
2451 int mask;
2452
2453 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2454 {
2455 offset = part_id - 1;
2456 mask = 1 << offset;
2457
2459 }
2460 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2461 {
2462 offset = (part_id % 32);
2463 mask = 1 << offset;
2464
2466 }
2467 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2468 {
2469 offset = (part_id % 63);
2470 mask = 1 << offset;
2471
2473 }
2474 }
2475
2477 {
2478 //part_id must starts from index = 1
2479 int offset;
2480 int mask;
2481
2482 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2483 {
2484 offset = part_id - 1;
2485 mask = 1 << offset;
2486
2487 if ((m_SyncParts01 & mask) > 0)
2488 return true;
2489 }
2490 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2491 {
2492 offset = (part_id % 32);
2493 mask = 1 << offset;
2494
2495 if ((m_SyncParts02 & mask) > 0)
2496 return true;
2497 }
2498 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2499 {
2500 offset = (part_id % 63);
2501 mask = 1 << offset;
2502
2503 if ((m_SyncParts03 & mask) > 0)
2504 return true;
2505 }
2506
2507 return false;
2508 }
2509
2510 protected void RegisterActionForSync(int part_id, int action_id)
2511 {
2514 }
2515
2516 protected void ResetActionSyncData()
2517 {
2518 //reset data
2519 m_InteractedPartId = -1;
2521 }
2522
2523 protected void SetActionFromSyncData()
2524 {
2525 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2526 {
2529
2530 switch (build_action_id)
2531 {
2535 }
2536 }
2537 }
2538 //------
2539
2541 {
2542 string key = part.m_PartName;
2543 bool is_base = part.IsBase();
2545 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2547 {
2548 if (!part.IsBuilt())
2549 {
2550 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2551 GetConstruction().AddToConstructedParts(key);
2552 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2553
2554 if (is_base)
2555 {
2557 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2558 }
2559 }
2560 }
2561 else
2562 {
2563 if (part.IsBuilt())
2564 {
2565 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2566 GetConstruction().RemoveFromConstructedParts(key);
2567 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2568
2569 if (is_base)
2570 {
2572 AddProxyPhysics(ANIMATION_DEPLOYED);
2573 }
2574 }
2575 }
2576
2577 //check slot lock for material attachments
2578 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2579 }
2580
2581 //set construction parts based on synchronized data
2583 {
2586
2587 for (int i = 0; i < construction_parts.Count(); ++i)
2588 {
2589 string key = construction_parts.GetKey(i);
2592 }
2593
2594 //regenerate navmesh
2595 UpdateNavmesh();
2596 }
2597
2599 {
2602
2603 for (int i = 0; i < construction_parts.Count(); ++i)
2604 {
2605 string key = construction_parts.GetKey(i);
2607
2608 if (value.GetId() == id)
2609 return value;
2610 }
2611
2612 return NULL;
2613 }
2614 //
2615
2616 //Base
2617 bool HasBase()
2618 {
2619 return m_HasBase;
2620 }
2621
2622 void SetBaseState(bool has_base)
2623 {
2625 }
2626
2627 override bool IsDeployable()
2628 {
2629 return true;
2630 }
2631
2632 bool IsOpened()
2633 {
2634 return false;
2635 }
2636
2637 //--- CONSTRUCTION KIT
2639 {
2643
2644 return construction_kit;
2645 }
2646
2648 {
2649 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2652 }
2653
2654 protected vector GetKitSpawnPosition()
2655 {
2656 return GetPosition();
2657 }
2658
2659 protected string GetConstructionKitType()
2660 {
2661 return "";
2662 }
2663
2665 {
2667 GetGame().ObjectDelete(construction_kit);
2668 }
2669
2670 //--- CONSTRUCTION
2671 void DestroyConstruction()
2672 {
2673 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2674 GetGame().ObjectDelete(this);
2675 }
2676
2677 // --- EVENTS
2678 override void OnStoreSave(ParamsWriteContext ctx)
2679 {
2680 super.OnStoreSave(ctx);
2681
2682 //sync parts 01
2683 ctx.Write(m_SyncParts01);
2684 ctx.Write(m_SyncParts02);
2685 ctx.Write(m_SyncParts03);
2686
2687 ctx.Write(m_HasBase);
2688 }
2689
2690 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2691 {
2692 if (!super.OnStoreLoad(ctx, version))
2693 return false;
2694
2695 //--- Base building data ---
2696 //Restore synced parts data
2697 if (!ctx.Read(m_SyncParts01))
2698 {
2699 m_SyncParts01 = 0; //set default
2700 return false;
2701 }
2702 if (!ctx.Read(m_SyncParts02))
2703 {
2704 m_SyncParts02 = 0; //set default
2705 return false;
2706 }
2707 if (!ctx.Read(m_SyncParts03))
2708 {
2709 m_SyncParts03 = 0; //set default
2710 return false;
2711 }
2712
2713 //has base
2714 if (!ctx.Read(m_HasBase))
2715 {
2716 m_HasBase = false;
2717 return false;
2718 }
2719 //---
2720
2721 return true;
2722 }
2723
2724 override void AfterStoreLoad()
2725 {
2726 super.AfterStoreLoad();
2727
2730 }
2731
2733 {
2734 //update server data
2736
2737 //set base state
2738 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2739 SetBaseState(construction_part.IsBuilt()) ;
2740
2741 //synchronize after load
2743 }
2744
2745 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2746 {
2748 return;
2749
2750 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2751
2752 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2753 return;
2754
2756 string part_name = zone;
2757 part_name.ToLower();
2758
2760 {
2762
2763 if (construction_part && construction.IsPartConstructed(part_name))
2764 {
2765 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2766 construction.DestroyConnectedParts(part_name);
2767 }
2768
2769 //barbed wire handling (hack-ish)
2770 if (part_name.Contains("barbed"))
2771 {
2772 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2773 if (barbed_wire)
2774 barbed_wire.SetMountedState(false);
2775 }
2776 }
2777 }
2778
2779 override void EEOnAfterLoad()
2780 {
2782 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2783
2784 super.EEOnAfterLoad();
2785 }
2786
2787 override void EEInit()
2788 {
2789 super.EEInit();
2790
2791 // init visuals and physics
2792 InitBaseState();
2793
2794 //debug
2795#ifdef DEVELOPER
2797#endif
2798 }
2799
2800 override void EEItemAttached(EntityAI item, string slot_name)
2801 {
2802 super.EEItemAttached(item, slot_name);
2803
2805 UpdateVisuals();
2807 }
2808
2809 override void EEItemDetached(EntityAI item, string slot_name)
2810 {
2811 super.EEItemDetached(item, slot_name);
2812
2813 UpdateVisuals();
2815 }
2816
2817 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2818 {
2820 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2821
2824 }
2825
2826 //ignore out of reach condition
2827 override bool IgnoreOutOfReachCondition()
2828 {
2829 return true;
2830 }
2831
2832 //CONSTRUCTION EVENTS
2833 //Build
2834 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2835 {
2837
2838 //check base state
2839 if (construtionPart.IsBase())
2840 {
2841 SetBaseState(true);
2842
2843 //spawn kit
2845 }
2846
2847 //register constructed parts for synchronization
2849
2850 //register action that was performed on part
2852
2853 //synchronize
2855
2856 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2857
2858 UpdateNavmesh();
2859
2860 //update visuals
2861 UpdateVisuals();
2862
2863 //reset action sync data
2864 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2865 }
2866
2867 void OnPartBuiltClient(string part_name, int action_id)
2868 {
2869 //play sound
2871 }
2872
2873 //Dismantle
2875 {
2876 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2878
2879 //register constructed parts for synchronization
2881
2882 //register action that was performed on part
2884
2885 //synchronize
2887
2888 // server part of sync, client will be synced from SetPartsFromSyncData
2890
2891 UpdateNavmesh();
2892
2893 //update visuals
2894 UpdateVisuals();
2895
2896 //reset action sync data
2897 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2898
2899 //check base state
2900 if (construtionPart.IsBase())
2901 {
2902 //Destroy construction
2903 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2904 }
2905 }
2906
2908 {
2909 //play sound
2911 }
2912
2913 //Destroy
2915 {
2916 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2918
2919 //register constructed parts for synchronization
2921
2922 //register action that was performed on part
2924
2925 //synchronize
2927
2928 // server part of sync, client will be synced from SetPartsFromSyncData
2930
2931 UpdateNavmesh();
2932
2933 //update visuals
2934 UpdateVisuals();
2935
2936 //reset action sync data
2937 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2938
2939 //check base state
2940 if (construtionPart.IsBase())
2941 {
2942 //Destroy construction
2943 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2944 }
2945 }
2946
2947 void OnPartDestroyedClient(string part_name, int action_id)
2948 {
2949 //play sound
2951 }
2952
2953 // --- UPDATE
2954 void InitBaseState()
2955 {
2956 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2957
2958 InitVisuals();
2959 UpdateNavmesh(); //regenerate navmesh
2960 GetConstruction().InitBaseState();
2961 }
2962
2963 void InitVisuals()
2964 {
2965 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2966 //check base
2967 if (!HasBase())
2968 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2969 else
2970 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2971
2972 GetConstruction().UpdateVisuals();
2973 }
2974
2975 void UpdateVisuals()
2976 {
2978
2980 foreach (string slotName : attachmentSlots)
2982
2983 //check base
2984 if (!HasBase())
2985 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2986 else
2987 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2988
2989 GetConstruction().UpdateVisuals();
2990 }
2991
2993 {
2994 string slotNameMounted = slot_name + "_Mounted";
2995 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2996
2997 if (attachment)
2998 {
2999 BarbedWire barbedWire = BarbedWire.Cast(attachment);
3000 if (barbedWire && barbedWire.IsMounted())
3002 else
3004
3005 if (is_locked)
3006 {
3007 SetAnimationPhase(slotNameMounted, 0);
3008 SetAnimationPhase(slot_name, 1);
3009 }
3010 else
3011 {
3012 SetAnimationPhase(slotNameMounted, 1);
3013 SetAnimationPhase(slot_name, 0);
3014 }
3015 }
3016 else
3017 {
3018 SetAnimationPhase(slotNameMounted, 1);
3019 SetAnimationPhase(slot_name, 1);
3020
3022 }
3023 }
3024
3025 // avoid calling this function on frequent occasions, it's a massive performance hit
3026 void UpdatePhysics()
3027 {
3029 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
3030
3033
3035 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
3036
3037 foreach (string slotName : attachmentSlots)
3039
3040 //check base
3041 if (!HasBase())
3042 {
3044 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
3045
3046 AddProxyPhysics(ANIMATION_DEPLOYED);
3047 }
3048 else
3049 {
3051 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
3052
3053 RemoveProxyPhysics(ANIMATION_DEPLOYED);
3054 }
3055
3056 GetConstruction().UpdatePhysics();
3057 UpdateNavmesh();
3058 }
3059
3061 {
3062 //checks for invalid appends; hotfix
3063 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
3064 return;
3065 //----------------------------------
3066 string slot_name_mounted = slot_name + "_Mounted";
3067 EntityAI attachment = FindAttachmentBySlotName(slot_name);
3068
3069 //remove proxy physics
3070 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
3071 RemoveProxyPhysics(slot_name_mounted);
3072 RemoveProxyPhysics(slot_name);
3073
3074 if (attachment)
3075 {
3076 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
3077 if (is_locked)
3078 {
3079 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
3080 AddProxyPhysics(slot_name_mounted);
3081 }
3082 else
3083 {
3084 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
3085 AddProxyPhysics(slot_name);
3086 }
3087 }
3088 }
3089
3090 protected void UpdateNavmesh()
3091 {
3092 SetAffectPathgraph(true, false);
3093 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
3094 }
3095
3096 override bool CanUseConstruction()
3097 {
3098 return true;
3099 }
3100
3101 override bool CanUseConstructionBuild()
3102 {
3103 return true;
3104 }
3105
3107 {
3108 if (attachment)
3109 {
3111 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
3112
3113 return GetInventory().GetSlotLock(inventory_location.GetSlot());
3114 }
3115
3116 return false;
3117 }
3118
3119 protected bool IsAttachmentSlotLocked(string slot_name)
3120 {
3121 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
3122 }
3123
3124 //--- ATTACHMENT SLOTS
3126 {
3127 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
3128 if (GetGame().ConfigIsExisting(config_path))
3129 GetGame().ConfigGetTextArray(config_path, attachment_slots);
3130 }
3131
3133 {
3134 return true;
3135 }
3136
3137 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
3138 {
3139 return true;
3140 }
3141
3142 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
3143 {
3144 return true;
3145 }
3146
3147 // --- INIT
3148 void ConstructionInit()
3149 {
3150 if (!m_Construction)
3151 m_Construction = new Construction(this);
3152
3153 GetConstruction().Init();
3154 }
3155
3157 {
3158 return m_Construction;
3159 }
3160
3161 //--- INVENTORY/ATTACHMENTS CONDITIONS
3162 //attachments
3164 {
3165 return super.CanReceiveAttachment(attachment, slotId);
3166 }
3167
3169 {
3170 int attachment_count = GetInventory().AttachmentCount();
3171 if (attachment_count > 0)
3172 {
3173 if (HasBase() && attachment_count == 1)
3174 return false;
3175
3176 return true;
3177 }
3178
3179 return false;
3180 }
3181
3182 override bool ShowZonesHealth()
3183 {
3184 return true;
3185 }
3186
3187 //this into/outo parent.Cargo
3188 override bool CanPutInCargo(EntityAI parent)
3189 {
3190 return false;
3191 }
3192
3193 override bool CanRemoveFromCargo(EntityAI parent)
3194 {
3195 return false;
3196 }
3197
3198 //hands
3199 override bool CanPutIntoHands(EntityAI parent)
3200 {
3201 return false;
3202 }
3203
3204 //--- ACTION CONDITIONS
3205 //direction
3206 override bool IsFacingPlayer(PlayerBase player, string selection)
3207 {
3208 return true;
3209 }
3210
3211 override bool IsPlayerInside(PlayerBase player, string selection)
3212 {
3213 return true;
3214 }
3215
3218 {
3219 return false;
3220 }
3221
3222 //camera direction check
3223 bool IsFacingCamera(string selection)
3224 {
3225 return true;
3226 }
3227
3228 //roof check
3230 {
3231 return false;
3232 }
3233
3234 //selection->player distance check
3235 bool HasProperDistance(string selection, PlayerBase player)
3236 {
3237 return true;
3238 }
3239
3240 //folding
3242 {
3243 if (HasBase() || GetInventory().AttachmentCount() > 0)
3244 return false;
3245
3246 return true;
3247 }
3248
3250 {
3253
3254 return item;
3255 }
3256
3257 //Damage triggers (barbed wire)
3258 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3259 {
3260 if (GetGame() && GetGame().IsServer())
3261 {
3262 //destroy area damage if some already exists
3264
3265 //create new area damage
3267 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3268
3269 vector min_max[2];
3270 if (MemoryPointExists(slot_name + "_min"))
3271 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3272 if (MemoryPointExists(slot_name + "_max"))
3273 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3274
3275 //get proper trigger extents (min<max)
3276 vector extents[2];
3277 GetConstruction().GetTriggerExtents(min_max, extents);
3278
3279 //get box center
3280 vector center;
3281 center = GetConstruction().GetBoxCenter(min_max);
3282 center = ModelToWorld(center);
3283
3284 //rotate center if needed
3287
3288 areaDamage.SetExtents(extents[0], extents[1]);
3289 areaDamage.SetAreaPosition(center);
3290 areaDamage.SetAreaOrientation(orientation);
3291 areaDamage.SetLoopInterval(1.0);
3292 areaDamage.SetDeferDuration(0.2);
3293 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3294 areaDamage.SetAmmoName("BarbedWireHit");
3295 areaDamage.Spawn();
3296
3298 }
3299 }
3300
3302 {
3303 if (angle_deg != 0)
3304 {
3305 //orientation
3307
3308 //center
3310 if (MemoryPointExists("rotate_axis"))
3311 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3314 center[0] = r_center_x;
3315 center[2] = r_center_z;
3316 }
3317 }
3318
3319 void DestroyAreaDamage(string slot_name)
3320 {
3321 if (GetGame() && GetGame().IsServer())
3322 {
3325 {
3326 if (areaDamage)
3327 areaDamage.Destroy();
3328
3330 }
3331 }
3332 }
3333
3334 override bool IsIgnoredByConstruction()
3335 {
3336 return true;
3337 }
3338
3339 //================================================================
3340 // SOUNDS
3341 //================================================================
3342 protected void SoundBuildStart(string part_name)
3343 {
3344 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3345 }
3346
3347 protected void SoundDismantleStart(string part_name)
3348 {
3349 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3350 }
3351
3352 protected void SoundDestroyStart(string part_name)
3353 {
3354 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3355 }
3356
3357 protected string GetBuildSoundByMaterial(string part_name)
3358 {
3360
3361 switch (material_type)
3362 {
3363 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3364 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3365 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3366 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3367 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3368 }
3369
3370 return "";
3371 }
3372
3373 protected string GetDismantleSoundByMaterial(string part_name)
3374 {
3376
3377 switch (material_type)
3378 {
3379 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3380 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3381 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3382 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3383 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3384 }
3385
3386 return "";
3387 }
3388
3389 //misc
3391 {
3392 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3393 {
3394 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3396 SetHealth(slot_name, "Health", item.GetHealth());
3397 }
3398 }
3399
3400 override int GetDamageSystemVersionChange()
3401 {
3402 return 111;
3403 }
3404
3405 override void SetActions()
3406 {
3407 super.SetActions();
3408
3410 //AddAction(ActionTakeHybridAttachment);
3411 //AddAction(ActionTakeHybridAttachmentToHands);
3414 }
3415
3416 //================================================================
3417 // DEBUG
3418 //================================================================
3419 protected void DebugCustomState()
3420 {
3421 }
3422
3425 {
3426 return null;
3427 }
3428
3429 override void OnDebugSpawn()
3430 {
3431 FullyBuild();
3432 }
3433
3434 void FullyBuild()
3435 {
3437 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3438
3439 Man p;
3440
3441#ifdef SERVER
3443 GetGame().GetWorld().GetPlayerList(players);
3444 if (players.Count())
3445 p = players[0];
3446#else
3447 p = GetGame().GetPlayer();
3448#endif
3449
3450 foreach (ConstructionPart part : parts)
3451 {
3452 bool excluded = false;
3453 string partName = part.GetPartName();
3454 if (excludes)
3455 {
3456 foreach (string exclude : excludes)
3457 {
3458 if (partName.Contains(exclude))
3459 {
3460 excluded = true;
3461 break;
3462 }
3463 }
3464 }
3465
3466 if (!excluded)
3468 }
3469
3470 GetConstruction().UpdateVisuals();
3471 }
3472}
3473
3474void bsbDebugPrint(string s)
3475{
3476#ifdef BSB_DEBUG
3477 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3478#else
3479 //Print("" + s); // comment/uncomment to hide/see debug logs
3480#endif
3481}
3482void bsbDebugSpam(string s)
3483{
3484#ifdef BSB_DEBUG_SPAM
3485 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3486#else
3487 //Print("" + s); // comment/uncomment to hide/see debug logs
3488#endif
3489}

◆ OnPartBuiltClient()

void bsbDebugPrint::OnPartBuiltClient ( string part_name,
int action_id )
protected

Definition at line 1742 of file BaseBuildingBase.c.

1744{
1745 const string ANIMATION_DEPLOYED = "Deployed";
1746
1747 float m_ConstructionKitHealth; //stored health value for used construction kit
1748
1750
1751 bool m_HasBase;
1752 //variables for synchronization of base building parts (2x31 is the current limit)
1753 int m_SyncParts01; //synchronization for already built parts (31 parts)
1754 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1755 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1756 int m_InteractedPartId; //construction part id that an action was performed on
1757 int m_PerformedActionId; //action id that was performed on a construction part
1758
1759 //Sounds
1760 //build
1761 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1762 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1763 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1764 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1765 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1766 //dismantle
1767 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1768 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1769 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1770 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1771 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1772
1773 protected EffectSound m_Sound;
1774
1778
1779 // Constructor
1780 void BaseBuildingBase()
1781 {
1783
1784 //synchronized variables
1785 RegisterNetSyncVariableInt("m_SyncParts01");
1786 RegisterNetSyncVariableInt("m_SyncParts02");
1787 RegisterNetSyncVariableInt("m_SyncParts03");
1788 RegisterNetSyncVariableInt("m_InteractedPartId");
1789 RegisterNetSyncVariableInt("m_PerformedActionId");
1790 RegisterNetSyncVariableBool("m_HasBase");
1791
1792 //Construction init
1794
1795 if (ConfigIsExisting("hybridAttachments"))
1796 {
1798 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1799 }
1800 if (ConfigIsExisting("mountables"))
1801 {
1803 ConfigGetTextArray("mountables", m_Mountables);
1804 }
1805
1806 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1807 }
1808
1809 override void EEDelete(EntityAI parent)
1810 {
1811 super.EEDelete(parent);
1812
1813 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1815
1816 }
1817
1818 override string GetInvulnerabilityTypeString()
1819 {
1820 return "disableBaseDamage";
1821 }
1822
1823 override bool CanObstruct()
1824 {
1825 return true;
1826 }
1827
1828 override int GetHideIconMask()
1829 {
1830 return EInventoryIconVisibility.HIDE_VICINITY;
1831 }
1832
1833 // --- SYNCHRONIZATION
1835 {
1836 if (GetGame().IsServer())
1837 SetSynchDirty();
1838 }
1839
1840 override void OnVariablesSynchronized()
1841 {
1842 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1843 super.OnVariablesSynchronized();
1844
1845 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1846 }
1847
1848 protected void OnSynchronizedClient()
1849 {
1850 //update parts
1852
1853 //update action on part
1855
1856 //update visuals (client)
1857 UpdateVisuals();
1858 }
1859
1860 //parts synchronization
1862 {
1863 //part_id must starts from index = 1
1864 int offset;
1865 int mask;
1866
1867 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1868 {
1869 offset = part_id - 1;
1870 mask = 1 << offset;
1871
1873 }
1874 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1875 {
1876 offset = (part_id % 32);
1877 mask = 1 << offset;
1878
1880 }
1881 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1882 {
1883 offset = (part_id % 63);
1884 mask = 1 << offset;
1885
1887 }
1888 }
1889
1891 {
1892 //part_id must starts from index = 1
1893 int offset;
1894 int mask;
1895
1896 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1897 {
1898 offset = part_id - 1;
1899 mask = 1 << offset;
1900
1902 }
1903 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1904 {
1905 offset = (part_id % 32);
1906 mask = 1 << offset;
1907
1909 }
1910 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1911 {
1912 offset = (part_id % 63);
1913 mask = 1 << offset;
1914
1916 }
1917 }
1918
1920 {
1921 //part_id must starts from index = 1
1922 int offset;
1923 int mask;
1924
1925 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1926 {
1927 offset = part_id - 1;
1928 mask = 1 << offset;
1929
1930 if ((m_SyncParts01 & mask) > 0)
1931 return true;
1932 }
1933 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1934 {
1935 offset = (part_id % 32);
1936 mask = 1 << offset;
1937
1938 if ((m_SyncParts02 & mask) > 0)
1939 return true;
1940 }
1941 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1942 {
1943 offset = (part_id % 63);
1944 mask = 1 << offset;
1945
1946 if ((m_SyncParts03 & mask) > 0)
1947 return true;
1948 }
1949
1950 return false;
1951 }
1952
1953 protected void RegisterActionForSync(int part_id, int action_id)
1954 {
1957 }
1958
1959 protected void ResetActionSyncData()
1960 {
1961 //reset data
1962 m_InteractedPartId = -1;
1964 }
1965
1966 protected void SetActionFromSyncData()
1967 {
1968 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1969 {
1972
1973 switch (build_action_id)
1974 {
1978 }
1979 }
1980 }
1981 //------
1982
1984 {
1985 string key = part.m_PartName;
1986 bool is_base = part.IsBase();
1988 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1990 {
1991 if (!part.IsBuilt())
1992 {
1993 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1994 GetConstruction().AddToConstructedParts(key);
1995 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1996
1997 if (is_base)
1998 {
2000 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2001 }
2002 }
2003 }
2004 else
2005 {
2006 if (part.IsBuilt())
2007 {
2008 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2009 GetConstruction().RemoveFromConstructedParts(key);
2010 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2011
2012 if (is_base)
2013 {
2015 AddProxyPhysics(ANIMATION_DEPLOYED);
2016 }
2017 }
2018 }
2019
2020 //check slot lock for material attachments
2021 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2022 }
2023
2024 //set construction parts based on synchronized data
2026 {
2029
2030 for (int i = 0; i < construction_parts.Count(); ++i)
2031 {
2032 string key = construction_parts.GetKey(i);
2035 }
2036
2037 //regenerate navmesh
2038 UpdateNavmesh();
2039 }
2040
2042 {
2045
2046 for (int i = 0; i < construction_parts.Count(); ++i)
2047 {
2048 string key = construction_parts.GetKey(i);
2050
2051 if (value.GetId() == id)
2052 return value;
2053 }
2054
2055 return NULL;
2056 }
2057 //
2058
2059 //Base
2060 bool HasBase()
2061 {
2062 return m_HasBase;
2063 }
2064
2065 void SetBaseState(bool has_base)
2066 {
2068 }
2069
2070 override bool IsDeployable()
2071 {
2072 return true;
2073 }
2074
2075 bool IsOpened()
2076 {
2077 return false;
2078 }
2079
2080 //--- CONSTRUCTION KIT
2082 {
2086
2087 return construction_kit;
2088 }
2089
2091 {
2092 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2095 }
2096
2097 protected vector GetKitSpawnPosition()
2098 {
2099 return GetPosition();
2100 }
2101
2102 protected string GetConstructionKitType()
2103 {
2104 return "";
2105 }
2106
2108 {
2110 GetGame().ObjectDelete(construction_kit);
2111 }
2112
2113 //--- CONSTRUCTION
2114 void DestroyConstruction()
2115 {
2116 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2117 GetGame().ObjectDelete(this);
2118 }
2119
2120 // --- EVENTS
2121 override void OnStoreSave(ParamsWriteContext ctx)
2122 {
2123 super.OnStoreSave(ctx);
2124
2125 //sync parts 01
2126 ctx.Write(m_SyncParts01);
2127 ctx.Write(m_SyncParts02);
2128 ctx.Write(m_SyncParts03);
2129
2130 ctx.Write(m_HasBase);
2131 }
2132
2133 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2134 {
2135 if (!super.OnStoreLoad(ctx, version))
2136 return false;
2137
2138 //--- Base building data ---
2139 //Restore synced parts data
2140 if (!ctx.Read(m_SyncParts01))
2141 {
2142 m_SyncParts01 = 0; //set default
2143 return false;
2144 }
2145 if (!ctx.Read(m_SyncParts02))
2146 {
2147 m_SyncParts02 = 0; //set default
2148 return false;
2149 }
2150 if (!ctx.Read(m_SyncParts03))
2151 {
2152 m_SyncParts03 = 0; //set default
2153 return false;
2154 }
2155
2156 //has base
2157 if (!ctx.Read(m_HasBase))
2158 {
2159 m_HasBase = false;
2160 return false;
2161 }
2162 //---
2163
2164 return true;
2165 }
2166
2167 override void AfterStoreLoad()
2168 {
2169 super.AfterStoreLoad();
2170
2173 }
2174
2176 {
2177 //update server data
2179
2180 //set base state
2181 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2182 SetBaseState(construction_part.IsBuilt()) ;
2183
2184 //synchronize after load
2186 }
2187
2188 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2189 {
2191 return;
2192
2193 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2194
2195 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2196 return;
2197
2199 string part_name = zone;
2200 part_name.ToLower();
2201
2203 {
2205
2206 if (construction_part && construction.IsPartConstructed(part_name))
2207 {
2208 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2209 construction.DestroyConnectedParts(part_name);
2210 }
2211
2212 //barbed wire handling (hack-ish)
2213 if (part_name.Contains("barbed"))
2214 {
2215 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2216 if (barbed_wire)
2217 barbed_wire.SetMountedState(false);
2218 }
2219 }
2220 }
2221
2222 override void EEOnAfterLoad()
2223 {
2225 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2226
2227 super.EEOnAfterLoad();
2228 }
2229
2230 override void EEInit()
2231 {
2232 super.EEInit();
2233
2234 // init visuals and physics
2235 InitBaseState();
2236
2237 //debug
2238#ifdef DEVELOPER
2240#endif
2241 }
2242
2243 override void EEItemAttached(EntityAI item, string slot_name)
2244 {
2245 super.EEItemAttached(item, slot_name);
2246
2248 UpdateVisuals();
2250 }
2251
2252 override void EEItemDetached(EntityAI item, string slot_name)
2253 {
2254 super.EEItemDetached(item, slot_name);
2255
2256 UpdateVisuals();
2258 }
2259
2260 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2261 {
2263 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2264
2267 }
2268
2269 //ignore out of reach condition
2270 override bool IgnoreOutOfReachCondition()
2271 {
2272 return true;
2273 }
2274
2275 //CONSTRUCTION EVENTS
2276 //Build
2277 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2278 {
2280
2281 //check base state
2282 if (construtionPart.IsBase())
2283 {
2284 SetBaseState(true);
2285
2286 //spawn kit
2288 }
2289
2290 //register constructed parts for synchronization
2292
2293 //register action that was performed on part
2295
2296 //synchronize
2298
2299 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2300
2301 UpdateNavmesh();
2302
2303 //update visuals
2304 UpdateVisuals();
2305
2306 //reset action sync data
2307 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2308 }
2309
2310 void OnPartBuiltClient(string part_name, int action_id)
2311 {
2312 //play sound
2314 }
2315
2316 //Dismantle
2318 {
2319 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2321
2322 //register constructed parts for synchronization
2324
2325 //register action that was performed on part
2327
2328 //synchronize
2330
2331 // server part of sync, client will be synced from SetPartsFromSyncData
2333
2334 UpdateNavmesh();
2335
2336 //update visuals
2337 UpdateVisuals();
2338
2339 //reset action sync data
2340 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2341
2342 //check base state
2343 if (construtionPart.IsBase())
2344 {
2345 //Destroy construction
2346 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2347 }
2348 }
2349
2351 {
2352 //play sound
2354 }
2355
2356 //Destroy
2358 {
2359 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2361
2362 //register constructed parts for synchronization
2364
2365 //register action that was performed on part
2367
2368 //synchronize
2370
2371 // server part of sync, client will be synced from SetPartsFromSyncData
2373
2374 UpdateNavmesh();
2375
2376 //update visuals
2377 UpdateVisuals();
2378
2379 //reset action sync data
2380 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2381
2382 //check base state
2383 if (construtionPart.IsBase())
2384 {
2385 //Destroy construction
2386 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2387 }
2388 }
2389
2390 void OnPartDestroyedClient(string part_name, int action_id)
2391 {
2392 //play sound
2394 }
2395
2396 // --- UPDATE
2397 void InitBaseState()
2398 {
2399 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2400
2401 InitVisuals();
2402 UpdateNavmesh(); //regenerate navmesh
2403 GetConstruction().InitBaseState();
2404 }
2405
2406 void InitVisuals()
2407 {
2408 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2409 //check base
2410 if (!HasBase())
2411 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2412 else
2413 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2414
2415 GetConstruction().UpdateVisuals();
2416 }
2417
2418 void UpdateVisuals()
2419 {
2421
2423 foreach (string slotName : attachmentSlots)
2425
2426 //check base
2427 if (!HasBase())
2428 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2429 else
2430 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2431
2432 GetConstruction().UpdateVisuals();
2433 }
2434
2436 {
2437 string slotNameMounted = slot_name + "_Mounted";
2438 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2439
2440 if (attachment)
2441 {
2442 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2443 if (barbedWire && barbedWire.IsMounted())
2445 else
2447
2448 if (is_locked)
2449 {
2450 SetAnimationPhase(slotNameMounted, 0);
2451 SetAnimationPhase(slot_name, 1);
2452 }
2453 else
2454 {
2455 SetAnimationPhase(slotNameMounted, 1);
2456 SetAnimationPhase(slot_name, 0);
2457 }
2458 }
2459 else
2460 {
2461 SetAnimationPhase(slotNameMounted, 1);
2462 SetAnimationPhase(slot_name, 1);
2463
2465 }
2466 }
2467
2468 // avoid calling this function on frequent occasions, it's a massive performance hit
2469 void UpdatePhysics()
2470 {
2472 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2473
2476
2478 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2479
2480 foreach (string slotName : attachmentSlots)
2482
2483 //check base
2484 if (!HasBase())
2485 {
2487 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2488
2489 AddProxyPhysics(ANIMATION_DEPLOYED);
2490 }
2491 else
2492 {
2494 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2495
2496 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2497 }
2498
2499 GetConstruction().UpdatePhysics();
2500 UpdateNavmesh();
2501 }
2502
2504 {
2505 //checks for invalid appends; hotfix
2506 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2507 return;
2508 //----------------------------------
2509 string slot_name_mounted = slot_name + "_Mounted";
2510 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2511
2512 //remove proxy physics
2513 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2514 RemoveProxyPhysics(slot_name_mounted);
2515 RemoveProxyPhysics(slot_name);
2516
2517 if (attachment)
2518 {
2519 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2520 if (is_locked)
2521 {
2522 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2523 AddProxyPhysics(slot_name_mounted);
2524 }
2525 else
2526 {
2527 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2528 AddProxyPhysics(slot_name);
2529 }
2530 }
2531 }
2532
2533 protected void UpdateNavmesh()
2534 {
2535 SetAffectPathgraph(true, false);
2536 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2537 }
2538
2539 override bool CanUseConstruction()
2540 {
2541 return true;
2542 }
2543
2544 override bool CanUseConstructionBuild()
2545 {
2546 return true;
2547 }
2548
2550 {
2551 if (attachment)
2552 {
2554 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2555
2556 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2557 }
2558
2559 return false;
2560 }
2561
2562 protected bool IsAttachmentSlotLocked(string slot_name)
2563 {
2564 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2565 }
2566
2567 //--- ATTACHMENT SLOTS
2569 {
2570 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2571 if (GetGame().ConfigIsExisting(config_path))
2572 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2573 }
2574
2576 {
2577 return true;
2578 }
2579
2580 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2581 {
2582 return true;
2583 }
2584
2585 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2586 {
2587 return true;
2588 }
2589
2590 // --- INIT
2591 void ConstructionInit()
2592 {
2593 if (!m_Construction)
2594 m_Construction = new Construction(this);
2595
2596 GetConstruction().Init();
2597 }
2598
2600 {
2601 return m_Construction;
2602 }
2603
2604 //--- INVENTORY/ATTACHMENTS CONDITIONS
2605 //attachments
2607 {
2608 return super.CanReceiveAttachment(attachment, slotId);
2609 }
2610
2612 {
2613 int attachment_count = GetInventory().AttachmentCount();
2614 if (attachment_count > 0)
2615 {
2616 if (HasBase() && attachment_count == 1)
2617 return false;
2618
2619 return true;
2620 }
2621
2622 return false;
2623 }
2624
2625 override bool ShowZonesHealth()
2626 {
2627 return true;
2628 }
2629
2630 //this into/outo parent.Cargo
2631 override bool CanPutInCargo(EntityAI parent)
2632 {
2633 return false;
2634 }
2635
2636 override bool CanRemoveFromCargo(EntityAI parent)
2637 {
2638 return false;
2639 }
2640
2641 //hands
2642 override bool CanPutIntoHands(EntityAI parent)
2643 {
2644 return false;
2645 }
2646
2647 //--- ACTION CONDITIONS
2648 //direction
2649 override bool IsFacingPlayer(PlayerBase player, string selection)
2650 {
2651 return true;
2652 }
2653
2654 override bool IsPlayerInside(PlayerBase player, string selection)
2655 {
2656 return true;
2657 }
2658
2661 {
2662 return false;
2663 }
2664
2665 //camera direction check
2666 bool IsFacingCamera(string selection)
2667 {
2668 return true;
2669 }
2670
2671 //roof check
2673 {
2674 return false;
2675 }
2676
2677 //selection->player distance check
2678 bool HasProperDistance(string selection, PlayerBase player)
2679 {
2680 return true;
2681 }
2682
2683 //folding
2685 {
2686 if (HasBase() || GetInventory().AttachmentCount() > 0)
2687 return false;
2688
2689 return true;
2690 }
2691
2693 {
2696
2697 return item;
2698 }
2699
2700 //Damage triggers (barbed wire)
2701 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2702 {
2703 if (GetGame() && GetGame().IsServer())
2704 {
2705 //destroy area damage if some already exists
2707
2708 //create new area damage
2710 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2711
2712 vector min_max[2];
2713 if (MemoryPointExists(slot_name + "_min"))
2714 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2715 if (MemoryPointExists(slot_name + "_max"))
2716 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2717
2718 //get proper trigger extents (min<max)
2719 vector extents[2];
2720 GetConstruction().GetTriggerExtents(min_max, extents);
2721
2722 //get box center
2723 vector center;
2724 center = GetConstruction().GetBoxCenter(min_max);
2725 center = ModelToWorld(center);
2726
2727 //rotate center if needed
2730
2731 areaDamage.SetExtents(extents[0], extents[1]);
2732 areaDamage.SetAreaPosition(center);
2733 areaDamage.SetAreaOrientation(orientation);
2734 areaDamage.SetLoopInterval(1.0);
2735 areaDamage.SetDeferDuration(0.2);
2736 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2737 areaDamage.SetAmmoName("BarbedWireHit");
2738 areaDamage.Spawn();
2739
2741 }
2742 }
2743
2745 {
2746 if (angle_deg != 0)
2747 {
2748 //orientation
2750
2751 //center
2753 if (MemoryPointExists("rotate_axis"))
2754 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2757 center[0] = r_center_x;
2758 center[2] = r_center_z;
2759 }
2760 }
2761
2762 void DestroyAreaDamage(string slot_name)
2763 {
2764 if (GetGame() && GetGame().IsServer())
2765 {
2768 {
2769 if (areaDamage)
2770 areaDamage.Destroy();
2771
2773 }
2774 }
2775 }
2776
2777 override bool IsIgnoredByConstruction()
2778 {
2779 return true;
2780 }
2781
2782 //================================================================
2783 // SOUNDS
2784 //================================================================
2785 protected void SoundBuildStart(string part_name)
2786 {
2787 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2788 }
2789
2790 protected void SoundDismantleStart(string part_name)
2791 {
2792 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2793 }
2794
2795 protected void SoundDestroyStart(string part_name)
2796 {
2797 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2798 }
2799
2800 protected string GetBuildSoundByMaterial(string part_name)
2801 {
2803
2804 switch (material_type)
2805 {
2806 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2807 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2808 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2809 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2810 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2811 }
2812
2813 return "";
2814 }
2815
2816 protected string GetDismantleSoundByMaterial(string part_name)
2817 {
2819
2820 switch (material_type)
2821 {
2822 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2823 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2824 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2825 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2826 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2827 }
2828
2829 return "";
2830 }
2831
2832 //misc
2834 {
2835 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2836 {
2837 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2839 SetHealth(slot_name, "Health", item.GetHealth());
2840 }
2841 }
2842
2843 override int GetDamageSystemVersionChange()
2844 {
2845 return 111;
2846 }
2847
2848 override void SetActions()
2849 {
2850 super.SetActions();
2851
2853 //AddAction(ActionTakeHybridAttachment);
2854 //AddAction(ActionTakeHybridAttachmentToHands);
2857 }
2858
2859 //================================================================
2860 // DEBUG
2861 //================================================================
2862 protected void DebugCustomState()
2863 {
2864 }
2865
2868 {
2869 return null;
2870 }
2871
2872 override void OnDebugSpawn()
2873 {
2874 FullyBuild();
2875 }
2876
2877 void FullyBuild()
2878 {
2880 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2881
2882 Man p;
2883
2884#ifdef SERVER
2886 GetGame().GetWorld().GetPlayerList(players);
2887 if (players.Count())
2888 p = players[0];
2889#else
2890 p = GetGame().GetPlayer();
2891#endif
2892
2893 foreach (ConstructionPart part : parts)
2894 {
2895 bool excluded = false;
2896 string partName = part.GetPartName();
2897 if (excludes)
2898 {
2899 foreach (string exclude : excludes)
2900 {
2901 if (partName.Contains(exclude))
2902 {
2903 excluded = true;
2904 break;
2905 }
2906 }
2907 }
2908
2909 if (!excluded)
2911 }
2912
2913 GetConstruction().UpdateVisuals();
2914 }
2915}
2916
2917void bsbDebugPrint(string s)
2918{
2919#ifdef BSB_DEBUG
2920 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2921#else
2922 //Print("" + s); // comment/uncomment to hide/see debug logs
2923#endif
2924}
2925void bsbDebugSpam(string s)
2926{
2927#ifdef BSB_DEBUG_SPAM
2928 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2929#else
2930 //Print("" + s); // comment/uncomment to hide/see debug logs
2931#endif
2932}

Referenced by ItemBase::SetActionFromSyncData().

◆ OnPartBuiltServer()

void bsbDebugPrint::OnPartBuiltServer ( notnull Man player,
string part_name,
int action_id )
protected

Definition at line 1709 of file BaseBuildingBase.c.

1711{
1712 const string ANIMATION_DEPLOYED = "Deployed";
1713
1714 float m_ConstructionKitHealth; //stored health value for used construction kit
1715
1717
1718 bool m_HasBase;
1719 //variables for synchronization of base building parts (2x31 is the current limit)
1720 int m_SyncParts01; //synchronization for already built parts (31 parts)
1721 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1722 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1723 int m_InteractedPartId; //construction part id that an action was performed on
1724 int m_PerformedActionId; //action id that was performed on a construction part
1725
1726 //Sounds
1727 //build
1728 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1729 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1730 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1731 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1732 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1733 //dismantle
1734 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1735 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1736 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1737 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1738 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1739
1740 protected EffectSound m_Sound;
1741
1745
1746 // Constructor
1747 void BaseBuildingBase()
1748 {
1750
1751 //synchronized variables
1752 RegisterNetSyncVariableInt("m_SyncParts01");
1753 RegisterNetSyncVariableInt("m_SyncParts02");
1754 RegisterNetSyncVariableInt("m_SyncParts03");
1755 RegisterNetSyncVariableInt("m_InteractedPartId");
1756 RegisterNetSyncVariableInt("m_PerformedActionId");
1757 RegisterNetSyncVariableBool("m_HasBase");
1758
1759 //Construction init
1761
1762 if (ConfigIsExisting("hybridAttachments"))
1763 {
1765 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1766 }
1767 if (ConfigIsExisting("mountables"))
1768 {
1770 ConfigGetTextArray("mountables", m_Mountables);
1771 }
1772
1773 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1774 }
1775
1776 override void EEDelete(EntityAI parent)
1777 {
1778 super.EEDelete(parent);
1779
1780 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1782
1783 }
1784
1785 override string GetInvulnerabilityTypeString()
1786 {
1787 return "disableBaseDamage";
1788 }
1789
1790 override bool CanObstruct()
1791 {
1792 return true;
1793 }
1794
1795 override int GetHideIconMask()
1796 {
1797 return EInventoryIconVisibility.HIDE_VICINITY;
1798 }
1799
1800 // --- SYNCHRONIZATION
1802 {
1803 if (GetGame().IsServer())
1804 SetSynchDirty();
1805 }
1806
1807 override void OnVariablesSynchronized()
1808 {
1809 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1810 super.OnVariablesSynchronized();
1811
1812 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1813 }
1814
1815 protected void OnSynchronizedClient()
1816 {
1817 //update parts
1819
1820 //update action on part
1822
1823 //update visuals (client)
1824 UpdateVisuals();
1825 }
1826
1827 //parts synchronization
1829 {
1830 //part_id must starts from index = 1
1831 int offset;
1832 int mask;
1833
1834 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1835 {
1836 offset = part_id - 1;
1837 mask = 1 << offset;
1838
1840 }
1841 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1842 {
1843 offset = (part_id % 32);
1844 mask = 1 << offset;
1845
1847 }
1848 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1849 {
1850 offset = (part_id % 63);
1851 mask = 1 << offset;
1852
1854 }
1855 }
1856
1858 {
1859 //part_id must starts from index = 1
1860 int offset;
1861 int mask;
1862
1863 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1864 {
1865 offset = part_id - 1;
1866 mask = 1 << offset;
1867
1869 }
1870 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1871 {
1872 offset = (part_id % 32);
1873 mask = 1 << offset;
1874
1876 }
1877 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1878 {
1879 offset = (part_id % 63);
1880 mask = 1 << offset;
1881
1883 }
1884 }
1885
1887 {
1888 //part_id must starts from index = 1
1889 int offset;
1890 int mask;
1891
1892 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1893 {
1894 offset = part_id - 1;
1895 mask = 1 << offset;
1896
1897 if ((m_SyncParts01 & mask) > 0)
1898 return true;
1899 }
1900 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1901 {
1902 offset = (part_id % 32);
1903 mask = 1 << offset;
1904
1905 if ((m_SyncParts02 & mask) > 0)
1906 return true;
1907 }
1908 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1909 {
1910 offset = (part_id % 63);
1911 mask = 1 << offset;
1912
1913 if ((m_SyncParts03 & mask) > 0)
1914 return true;
1915 }
1916
1917 return false;
1918 }
1919
1920 protected void RegisterActionForSync(int part_id, int action_id)
1921 {
1924 }
1925
1926 protected void ResetActionSyncData()
1927 {
1928 //reset data
1929 m_InteractedPartId = -1;
1931 }
1932
1933 protected void SetActionFromSyncData()
1934 {
1935 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1936 {
1939
1940 switch (build_action_id)
1941 {
1945 }
1946 }
1947 }
1948 //------
1949
1951 {
1952 string key = part.m_PartName;
1953 bool is_base = part.IsBase();
1955 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1957 {
1958 if (!part.IsBuilt())
1959 {
1960 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1961 GetConstruction().AddToConstructedParts(key);
1962 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1963
1964 if (is_base)
1965 {
1967 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1968 }
1969 }
1970 }
1971 else
1972 {
1973 if (part.IsBuilt())
1974 {
1975 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1976 GetConstruction().RemoveFromConstructedParts(key);
1977 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1978
1979 if (is_base)
1980 {
1982 AddProxyPhysics(ANIMATION_DEPLOYED);
1983 }
1984 }
1985 }
1986
1987 //check slot lock for material attachments
1988 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1989 }
1990
1991 //set construction parts based on synchronized data
1993 {
1996
1997 for (int i = 0; i < construction_parts.Count(); ++i)
1998 {
1999 string key = construction_parts.GetKey(i);
2002 }
2003
2004 //regenerate navmesh
2005 UpdateNavmesh();
2006 }
2007
2009 {
2012
2013 for (int i = 0; i < construction_parts.Count(); ++i)
2014 {
2015 string key = construction_parts.GetKey(i);
2017
2018 if (value.GetId() == id)
2019 return value;
2020 }
2021
2022 return NULL;
2023 }
2024 //
2025
2026 //Base
2027 bool HasBase()
2028 {
2029 return m_HasBase;
2030 }
2031
2032 void SetBaseState(bool has_base)
2033 {
2035 }
2036
2037 override bool IsDeployable()
2038 {
2039 return true;
2040 }
2041
2042 bool IsOpened()
2043 {
2044 return false;
2045 }
2046
2047 //--- CONSTRUCTION KIT
2049 {
2053
2054 return construction_kit;
2055 }
2056
2058 {
2059 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2062 }
2063
2064 protected vector GetKitSpawnPosition()
2065 {
2066 return GetPosition();
2067 }
2068
2069 protected string GetConstructionKitType()
2070 {
2071 return "";
2072 }
2073
2075 {
2077 GetGame().ObjectDelete(construction_kit);
2078 }
2079
2080 //--- CONSTRUCTION
2081 void DestroyConstruction()
2082 {
2083 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2084 GetGame().ObjectDelete(this);
2085 }
2086
2087 // --- EVENTS
2088 override void OnStoreSave(ParamsWriteContext ctx)
2089 {
2090 super.OnStoreSave(ctx);
2091
2092 //sync parts 01
2093 ctx.Write(m_SyncParts01);
2094 ctx.Write(m_SyncParts02);
2095 ctx.Write(m_SyncParts03);
2096
2097 ctx.Write(m_HasBase);
2098 }
2099
2100 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2101 {
2102 if (!super.OnStoreLoad(ctx, version))
2103 return false;
2104
2105 //--- Base building data ---
2106 //Restore synced parts data
2107 if (!ctx.Read(m_SyncParts01))
2108 {
2109 m_SyncParts01 = 0; //set default
2110 return false;
2111 }
2112 if (!ctx.Read(m_SyncParts02))
2113 {
2114 m_SyncParts02 = 0; //set default
2115 return false;
2116 }
2117 if (!ctx.Read(m_SyncParts03))
2118 {
2119 m_SyncParts03 = 0; //set default
2120 return false;
2121 }
2122
2123 //has base
2124 if (!ctx.Read(m_HasBase))
2125 {
2126 m_HasBase = false;
2127 return false;
2128 }
2129 //---
2130
2131 return true;
2132 }
2133
2134 override void AfterStoreLoad()
2135 {
2136 super.AfterStoreLoad();
2137
2140 }
2141
2143 {
2144 //update server data
2146
2147 //set base state
2148 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2149 SetBaseState(construction_part.IsBuilt()) ;
2150
2151 //synchronize after load
2153 }
2154
2155 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2156 {
2158 return;
2159
2160 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2161
2162 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2163 return;
2164
2166 string part_name = zone;
2167 part_name.ToLower();
2168
2170 {
2172
2173 if (construction_part && construction.IsPartConstructed(part_name))
2174 {
2175 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2176 construction.DestroyConnectedParts(part_name);
2177 }
2178
2179 //barbed wire handling (hack-ish)
2180 if (part_name.Contains("barbed"))
2181 {
2182 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2183 if (barbed_wire)
2184 barbed_wire.SetMountedState(false);
2185 }
2186 }
2187 }
2188
2189 override void EEOnAfterLoad()
2190 {
2192 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2193
2194 super.EEOnAfterLoad();
2195 }
2196
2197 override void EEInit()
2198 {
2199 super.EEInit();
2200
2201 // init visuals and physics
2202 InitBaseState();
2203
2204 //debug
2205#ifdef DEVELOPER
2207#endif
2208 }
2209
2210 override void EEItemAttached(EntityAI item, string slot_name)
2211 {
2212 super.EEItemAttached(item, slot_name);
2213
2215 UpdateVisuals();
2217 }
2218
2219 override void EEItemDetached(EntityAI item, string slot_name)
2220 {
2221 super.EEItemDetached(item, slot_name);
2222
2223 UpdateVisuals();
2225 }
2226
2227 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2228 {
2230 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2231
2234 }
2235
2236 //ignore out of reach condition
2237 override bool IgnoreOutOfReachCondition()
2238 {
2239 return true;
2240 }
2241
2242 //CONSTRUCTION EVENTS
2243 //Build
2244 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2245 {
2247
2248 //check base state
2249 if (construtionPart.IsBase())
2250 {
2251 SetBaseState(true);
2252
2253 //spawn kit
2255 }
2256
2257 //register constructed parts for synchronization
2259
2260 //register action that was performed on part
2262
2263 //synchronize
2265
2266 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2267
2268 UpdateNavmesh();
2269
2270 //update visuals
2271 UpdateVisuals();
2272
2273 //reset action sync data
2274 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2275 }
2276
2277 void OnPartBuiltClient(string part_name, int action_id)
2278 {
2279 //play sound
2281 }
2282
2283 //Dismantle
2285 {
2286 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2288
2289 //register constructed parts for synchronization
2291
2292 //register action that was performed on part
2294
2295 //synchronize
2297
2298 // server part of sync, client will be synced from SetPartsFromSyncData
2300
2301 UpdateNavmesh();
2302
2303 //update visuals
2304 UpdateVisuals();
2305
2306 //reset action sync data
2307 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2308
2309 //check base state
2310 if (construtionPart.IsBase())
2311 {
2312 //Destroy construction
2313 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2314 }
2315 }
2316
2318 {
2319 //play sound
2321 }
2322
2323 //Destroy
2325 {
2326 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2328
2329 //register constructed parts for synchronization
2331
2332 //register action that was performed on part
2334
2335 //synchronize
2337
2338 // server part of sync, client will be synced from SetPartsFromSyncData
2340
2341 UpdateNavmesh();
2342
2343 //update visuals
2344 UpdateVisuals();
2345
2346 //reset action sync data
2347 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2348
2349 //check base state
2350 if (construtionPart.IsBase())
2351 {
2352 //Destroy construction
2353 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2354 }
2355 }
2356
2357 void OnPartDestroyedClient(string part_name, int action_id)
2358 {
2359 //play sound
2361 }
2362
2363 // --- UPDATE
2364 void InitBaseState()
2365 {
2366 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2367
2368 InitVisuals();
2369 UpdateNavmesh(); //regenerate navmesh
2370 GetConstruction().InitBaseState();
2371 }
2372
2373 void InitVisuals()
2374 {
2375 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2376 //check base
2377 if (!HasBase())
2378 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2379 else
2380 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2381
2382 GetConstruction().UpdateVisuals();
2383 }
2384
2385 void UpdateVisuals()
2386 {
2388
2390 foreach (string slotName : attachmentSlots)
2392
2393 //check base
2394 if (!HasBase())
2395 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2396 else
2397 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2398
2399 GetConstruction().UpdateVisuals();
2400 }
2401
2403 {
2404 string slotNameMounted = slot_name + "_Mounted";
2405 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2406
2407 if (attachment)
2408 {
2409 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2410 if (barbedWire && barbedWire.IsMounted())
2412 else
2414
2415 if (is_locked)
2416 {
2417 SetAnimationPhase(slotNameMounted, 0);
2418 SetAnimationPhase(slot_name, 1);
2419 }
2420 else
2421 {
2422 SetAnimationPhase(slotNameMounted, 1);
2423 SetAnimationPhase(slot_name, 0);
2424 }
2425 }
2426 else
2427 {
2428 SetAnimationPhase(slotNameMounted, 1);
2429 SetAnimationPhase(slot_name, 1);
2430
2432 }
2433 }
2434
2435 // avoid calling this function on frequent occasions, it's a massive performance hit
2436 void UpdatePhysics()
2437 {
2439 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2440
2443
2445 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2446
2447 foreach (string slotName : attachmentSlots)
2449
2450 //check base
2451 if (!HasBase())
2452 {
2454 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2455
2456 AddProxyPhysics(ANIMATION_DEPLOYED);
2457 }
2458 else
2459 {
2461 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2462
2463 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2464 }
2465
2466 GetConstruction().UpdatePhysics();
2467 UpdateNavmesh();
2468 }
2469
2471 {
2472 //checks for invalid appends; hotfix
2473 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2474 return;
2475 //----------------------------------
2476 string slot_name_mounted = slot_name + "_Mounted";
2477 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2478
2479 //remove proxy physics
2480 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2481 RemoveProxyPhysics(slot_name_mounted);
2482 RemoveProxyPhysics(slot_name);
2483
2484 if (attachment)
2485 {
2486 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2487 if (is_locked)
2488 {
2489 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2490 AddProxyPhysics(slot_name_mounted);
2491 }
2492 else
2493 {
2494 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2495 AddProxyPhysics(slot_name);
2496 }
2497 }
2498 }
2499
2500 protected void UpdateNavmesh()
2501 {
2502 SetAffectPathgraph(true, false);
2503 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2504 }
2505
2506 override bool CanUseConstruction()
2507 {
2508 return true;
2509 }
2510
2511 override bool CanUseConstructionBuild()
2512 {
2513 return true;
2514 }
2515
2517 {
2518 if (attachment)
2519 {
2521 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2522
2523 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2524 }
2525
2526 return false;
2527 }
2528
2529 protected bool IsAttachmentSlotLocked(string slot_name)
2530 {
2531 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2532 }
2533
2534 //--- ATTACHMENT SLOTS
2536 {
2537 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2538 if (GetGame().ConfigIsExisting(config_path))
2539 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2540 }
2541
2543 {
2544 return true;
2545 }
2546
2547 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2548 {
2549 return true;
2550 }
2551
2552 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2553 {
2554 return true;
2555 }
2556
2557 // --- INIT
2558 void ConstructionInit()
2559 {
2560 if (!m_Construction)
2561 m_Construction = new Construction(this);
2562
2563 GetConstruction().Init();
2564 }
2565
2567 {
2568 return m_Construction;
2569 }
2570
2571 //--- INVENTORY/ATTACHMENTS CONDITIONS
2572 //attachments
2574 {
2575 return super.CanReceiveAttachment(attachment, slotId);
2576 }
2577
2579 {
2580 int attachment_count = GetInventory().AttachmentCount();
2581 if (attachment_count > 0)
2582 {
2583 if (HasBase() && attachment_count == 1)
2584 return false;
2585
2586 return true;
2587 }
2588
2589 return false;
2590 }
2591
2592 override bool ShowZonesHealth()
2593 {
2594 return true;
2595 }
2596
2597 //this into/outo parent.Cargo
2598 override bool CanPutInCargo(EntityAI parent)
2599 {
2600 return false;
2601 }
2602
2603 override bool CanRemoveFromCargo(EntityAI parent)
2604 {
2605 return false;
2606 }
2607
2608 //hands
2609 override bool CanPutIntoHands(EntityAI parent)
2610 {
2611 return false;
2612 }
2613
2614 //--- ACTION CONDITIONS
2615 //direction
2616 override bool IsFacingPlayer(PlayerBase player, string selection)
2617 {
2618 return true;
2619 }
2620
2621 override bool IsPlayerInside(PlayerBase player, string selection)
2622 {
2623 return true;
2624 }
2625
2628 {
2629 return false;
2630 }
2631
2632 //camera direction check
2633 bool IsFacingCamera(string selection)
2634 {
2635 return true;
2636 }
2637
2638 //roof check
2640 {
2641 return false;
2642 }
2643
2644 //selection->player distance check
2645 bool HasProperDistance(string selection, PlayerBase player)
2646 {
2647 return true;
2648 }
2649
2650 //folding
2652 {
2653 if (HasBase() || GetInventory().AttachmentCount() > 0)
2654 return false;
2655
2656 return true;
2657 }
2658
2660 {
2663
2664 return item;
2665 }
2666
2667 //Damage triggers (barbed wire)
2668 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2669 {
2670 if (GetGame() && GetGame().IsServer())
2671 {
2672 //destroy area damage if some already exists
2674
2675 //create new area damage
2677 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2678
2679 vector min_max[2];
2680 if (MemoryPointExists(slot_name + "_min"))
2681 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2682 if (MemoryPointExists(slot_name + "_max"))
2683 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2684
2685 //get proper trigger extents (min<max)
2686 vector extents[2];
2687 GetConstruction().GetTriggerExtents(min_max, extents);
2688
2689 //get box center
2690 vector center;
2691 center = GetConstruction().GetBoxCenter(min_max);
2692 center = ModelToWorld(center);
2693
2694 //rotate center if needed
2697
2698 areaDamage.SetExtents(extents[0], extents[1]);
2699 areaDamage.SetAreaPosition(center);
2700 areaDamage.SetAreaOrientation(orientation);
2701 areaDamage.SetLoopInterval(1.0);
2702 areaDamage.SetDeferDuration(0.2);
2703 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2704 areaDamage.SetAmmoName("BarbedWireHit");
2705 areaDamage.Spawn();
2706
2708 }
2709 }
2710
2712 {
2713 if (angle_deg != 0)
2714 {
2715 //orientation
2717
2718 //center
2720 if (MemoryPointExists("rotate_axis"))
2721 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2724 center[0] = r_center_x;
2725 center[2] = r_center_z;
2726 }
2727 }
2728
2729 void DestroyAreaDamage(string slot_name)
2730 {
2731 if (GetGame() && GetGame().IsServer())
2732 {
2735 {
2736 if (areaDamage)
2737 areaDamage.Destroy();
2738
2740 }
2741 }
2742 }
2743
2744 override bool IsIgnoredByConstruction()
2745 {
2746 return true;
2747 }
2748
2749 //================================================================
2750 // SOUNDS
2751 //================================================================
2752 protected void SoundBuildStart(string part_name)
2753 {
2754 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2755 }
2756
2757 protected void SoundDismantleStart(string part_name)
2758 {
2759 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2760 }
2761
2762 protected void SoundDestroyStart(string part_name)
2763 {
2764 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2765 }
2766
2767 protected string GetBuildSoundByMaterial(string part_name)
2768 {
2770
2771 switch (material_type)
2772 {
2773 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2774 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2775 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2776 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2777 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2778 }
2779
2780 return "";
2781 }
2782
2783 protected string GetDismantleSoundByMaterial(string part_name)
2784 {
2786
2787 switch (material_type)
2788 {
2789 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2790 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2791 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2792 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2793 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2794 }
2795
2796 return "";
2797 }
2798
2799 //misc
2801 {
2802 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2803 {
2804 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2806 SetHealth(slot_name, "Health", item.GetHealth());
2807 }
2808 }
2809
2810 override int GetDamageSystemVersionChange()
2811 {
2812 return 111;
2813 }
2814
2815 override void SetActions()
2816 {
2817 super.SetActions();
2818
2820 //AddAction(ActionTakeHybridAttachment);
2821 //AddAction(ActionTakeHybridAttachmentToHands);
2824 }
2825
2826 //================================================================
2827 // DEBUG
2828 //================================================================
2829 protected void DebugCustomState()
2830 {
2831 }
2832
2835 {
2836 return null;
2837 }
2838
2839 override void OnDebugSpawn()
2840 {
2841 FullyBuild();
2842 }
2843
2844 void FullyBuild()
2845 {
2847 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2848
2849 Man p;
2850
2851#ifdef SERVER
2853 GetGame().GetWorld().GetPlayerList(players);
2854 if (players.Count())
2855 p = players[0];
2856#else
2857 p = GetGame().GetPlayer();
2858#endif
2859
2860 foreach (ConstructionPart part : parts)
2861 {
2862 bool excluded = false;
2863 string partName = part.GetPartName();
2864 if (excludes)
2865 {
2866 foreach (string exclude : excludes)
2867 {
2868 if (partName.Contains(exclude))
2869 {
2870 excluded = true;
2871 break;
2872 }
2873 }
2874 }
2875
2876 if (!excluded)
2878 }
2879
2880 GetConstruction().UpdateVisuals();
2881 }
2882}
2883
2884void bsbDebugPrint(string s)
2885{
2886#ifdef BSB_DEBUG
2887 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2888#else
2889 //Print("" + s); // comment/uncomment to hide/see debug logs
2890#endif
2891}
2892void bsbDebugSpam(string s)
2893{
2894#ifdef BSB_DEBUG_SPAM
2895 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2896#else
2897 //Print("" + s); // comment/uncomment to hide/see debug logs
2898#endif
2899}

◆ OnPartDestroyedClient()

void bsbDebugPrint::OnPartDestroyedClient ( string part_name,
int action_id )
protected

Definition at line 1822 of file BaseBuildingBase.c.

1824{
1825 const string ANIMATION_DEPLOYED = "Deployed";
1826
1827 float m_ConstructionKitHealth; //stored health value for used construction kit
1828
1830
1831 bool m_HasBase;
1832 //variables for synchronization of base building parts (2x31 is the current limit)
1833 int m_SyncParts01; //synchronization for already built parts (31 parts)
1834 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1835 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1836 int m_InteractedPartId; //construction part id that an action was performed on
1837 int m_PerformedActionId; //action id that was performed on a construction part
1838
1839 //Sounds
1840 //build
1841 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1842 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1843 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1844 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1845 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1846 //dismantle
1847 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1848 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1849 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1850 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1851 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1852
1853 protected EffectSound m_Sound;
1854
1858
1859 // Constructor
1860 void BaseBuildingBase()
1861 {
1863
1864 //synchronized variables
1865 RegisterNetSyncVariableInt("m_SyncParts01");
1866 RegisterNetSyncVariableInt("m_SyncParts02");
1867 RegisterNetSyncVariableInt("m_SyncParts03");
1868 RegisterNetSyncVariableInt("m_InteractedPartId");
1869 RegisterNetSyncVariableInt("m_PerformedActionId");
1870 RegisterNetSyncVariableBool("m_HasBase");
1871
1872 //Construction init
1874
1875 if (ConfigIsExisting("hybridAttachments"))
1876 {
1878 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1879 }
1880 if (ConfigIsExisting("mountables"))
1881 {
1883 ConfigGetTextArray("mountables", m_Mountables);
1884 }
1885
1886 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1887 }
1888
1889 override void EEDelete(EntityAI parent)
1890 {
1891 super.EEDelete(parent);
1892
1893 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1895
1896 }
1897
1898 override string GetInvulnerabilityTypeString()
1899 {
1900 return "disableBaseDamage";
1901 }
1902
1903 override bool CanObstruct()
1904 {
1905 return true;
1906 }
1907
1908 override int GetHideIconMask()
1909 {
1910 return EInventoryIconVisibility.HIDE_VICINITY;
1911 }
1912
1913 // --- SYNCHRONIZATION
1915 {
1916 if (GetGame().IsServer())
1917 SetSynchDirty();
1918 }
1919
1920 override void OnVariablesSynchronized()
1921 {
1922 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1923 super.OnVariablesSynchronized();
1924
1925 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1926 }
1927
1928 protected void OnSynchronizedClient()
1929 {
1930 //update parts
1932
1933 //update action on part
1935
1936 //update visuals (client)
1937 UpdateVisuals();
1938 }
1939
1940 //parts synchronization
1942 {
1943 //part_id must starts from index = 1
1944 int offset;
1945 int mask;
1946
1947 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1948 {
1949 offset = part_id - 1;
1950 mask = 1 << offset;
1951
1953 }
1954 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1955 {
1956 offset = (part_id % 32);
1957 mask = 1 << offset;
1958
1960 }
1961 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1962 {
1963 offset = (part_id % 63);
1964 mask = 1 << offset;
1965
1967 }
1968 }
1969
1971 {
1972 //part_id must starts from index = 1
1973 int offset;
1974 int mask;
1975
1976 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1977 {
1978 offset = part_id - 1;
1979 mask = 1 << offset;
1980
1982 }
1983 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1984 {
1985 offset = (part_id % 32);
1986 mask = 1 << offset;
1987
1989 }
1990 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1991 {
1992 offset = (part_id % 63);
1993 mask = 1 << offset;
1994
1996 }
1997 }
1998
2000 {
2001 //part_id must starts from index = 1
2002 int offset;
2003 int mask;
2004
2005 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2006 {
2007 offset = part_id - 1;
2008 mask = 1 << offset;
2009
2010 if ((m_SyncParts01 & mask) > 0)
2011 return true;
2012 }
2013 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2014 {
2015 offset = (part_id % 32);
2016 mask = 1 << offset;
2017
2018 if ((m_SyncParts02 & mask) > 0)
2019 return true;
2020 }
2021 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2022 {
2023 offset = (part_id % 63);
2024 mask = 1 << offset;
2025
2026 if ((m_SyncParts03 & mask) > 0)
2027 return true;
2028 }
2029
2030 return false;
2031 }
2032
2033 protected void RegisterActionForSync(int part_id, int action_id)
2034 {
2037 }
2038
2039 protected void ResetActionSyncData()
2040 {
2041 //reset data
2042 m_InteractedPartId = -1;
2044 }
2045
2046 protected void SetActionFromSyncData()
2047 {
2048 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2049 {
2052
2053 switch (build_action_id)
2054 {
2058 }
2059 }
2060 }
2061 //------
2062
2064 {
2065 string key = part.m_PartName;
2066 bool is_base = part.IsBase();
2068 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2070 {
2071 if (!part.IsBuilt())
2072 {
2073 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2074 GetConstruction().AddToConstructedParts(key);
2075 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2076
2077 if (is_base)
2078 {
2080 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2081 }
2082 }
2083 }
2084 else
2085 {
2086 if (part.IsBuilt())
2087 {
2088 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2089 GetConstruction().RemoveFromConstructedParts(key);
2090 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2091
2092 if (is_base)
2093 {
2095 AddProxyPhysics(ANIMATION_DEPLOYED);
2096 }
2097 }
2098 }
2099
2100 //check slot lock for material attachments
2101 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2102 }
2103
2104 //set construction parts based on synchronized data
2106 {
2109
2110 for (int i = 0; i < construction_parts.Count(); ++i)
2111 {
2112 string key = construction_parts.GetKey(i);
2115 }
2116
2117 //regenerate navmesh
2118 UpdateNavmesh();
2119 }
2120
2122 {
2125
2126 for (int i = 0; i < construction_parts.Count(); ++i)
2127 {
2128 string key = construction_parts.GetKey(i);
2130
2131 if (value.GetId() == id)
2132 return value;
2133 }
2134
2135 return NULL;
2136 }
2137 //
2138
2139 //Base
2140 bool HasBase()
2141 {
2142 return m_HasBase;
2143 }
2144
2145 void SetBaseState(bool has_base)
2146 {
2148 }
2149
2150 override bool IsDeployable()
2151 {
2152 return true;
2153 }
2154
2155 bool IsOpened()
2156 {
2157 return false;
2158 }
2159
2160 //--- CONSTRUCTION KIT
2162 {
2166
2167 return construction_kit;
2168 }
2169
2171 {
2172 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2175 }
2176
2177 protected vector GetKitSpawnPosition()
2178 {
2179 return GetPosition();
2180 }
2181
2182 protected string GetConstructionKitType()
2183 {
2184 return "";
2185 }
2186
2188 {
2190 GetGame().ObjectDelete(construction_kit);
2191 }
2192
2193 //--- CONSTRUCTION
2194 void DestroyConstruction()
2195 {
2196 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2197 GetGame().ObjectDelete(this);
2198 }
2199
2200 // --- EVENTS
2201 override void OnStoreSave(ParamsWriteContext ctx)
2202 {
2203 super.OnStoreSave(ctx);
2204
2205 //sync parts 01
2206 ctx.Write(m_SyncParts01);
2207 ctx.Write(m_SyncParts02);
2208 ctx.Write(m_SyncParts03);
2209
2210 ctx.Write(m_HasBase);
2211 }
2212
2213 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2214 {
2215 if (!super.OnStoreLoad(ctx, version))
2216 return false;
2217
2218 //--- Base building data ---
2219 //Restore synced parts data
2220 if (!ctx.Read(m_SyncParts01))
2221 {
2222 m_SyncParts01 = 0; //set default
2223 return false;
2224 }
2225 if (!ctx.Read(m_SyncParts02))
2226 {
2227 m_SyncParts02 = 0; //set default
2228 return false;
2229 }
2230 if (!ctx.Read(m_SyncParts03))
2231 {
2232 m_SyncParts03 = 0; //set default
2233 return false;
2234 }
2235
2236 //has base
2237 if (!ctx.Read(m_HasBase))
2238 {
2239 m_HasBase = false;
2240 return false;
2241 }
2242 //---
2243
2244 return true;
2245 }
2246
2247 override void AfterStoreLoad()
2248 {
2249 super.AfterStoreLoad();
2250
2253 }
2254
2256 {
2257 //update server data
2259
2260 //set base state
2261 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2262 SetBaseState(construction_part.IsBuilt()) ;
2263
2264 //synchronize after load
2266 }
2267
2268 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2269 {
2271 return;
2272
2273 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2274
2275 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2276 return;
2277
2279 string part_name = zone;
2280 part_name.ToLower();
2281
2283 {
2285
2286 if (construction_part && construction.IsPartConstructed(part_name))
2287 {
2288 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2289 construction.DestroyConnectedParts(part_name);
2290 }
2291
2292 //barbed wire handling (hack-ish)
2293 if (part_name.Contains("barbed"))
2294 {
2295 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2296 if (barbed_wire)
2297 barbed_wire.SetMountedState(false);
2298 }
2299 }
2300 }
2301
2302 override void EEOnAfterLoad()
2303 {
2305 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2306
2307 super.EEOnAfterLoad();
2308 }
2309
2310 override void EEInit()
2311 {
2312 super.EEInit();
2313
2314 // init visuals and physics
2315 InitBaseState();
2316
2317 //debug
2318#ifdef DEVELOPER
2320#endif
2321 }
2322
2323 override void EEItemAttached(EntityAI item, string slot_name)
2324 {
2325 super.EEItemAttached(item, slot_name);
2326
2328 UpdateVisuals();
2330 }
2331
2332 override void EEItemDetached(EntityAI item, string slot_name)
2333 {
2334 super.EEItemDetached(item, slot_name);
2335
2336 UpdateVisuals();
2338 }
2339
2340 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2341 {
2343 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2344
2347 }
2348
2349 //ignore out of reach condition
2350 override bool IgnoreOutOfReachCondition()
2351 {
2352 return true;
2353 }
2354
2355 //CONSTRUCTION EVENTS
2356 //Build
2357 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2358 {
2360
2361 //check base state
2362 if (construtionPart.IsBase())
2363 {
2364 SetBaseState(true);
2365
2366 //spawn kit
2368 }
2369
2370 //register constructed parts for synchronization
2372
2373 //register action that was performed on part
2375
2376 //synchronize
2378
2379 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2380
2381 UpdateNavmesh();
2382
2383 //update visuals
2384 UpdateVisuals();
2385
2386 //reset action sync data
2387 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2388 }
2389
2390 void OnPartBuiltClient(string part_name, int action_id)
2391 {
2392 //play sound
2394 }
2395
2396 //Dismantle
2398 {
2399 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2401
2402 //register constructed parts for synchronization
2404
2405 //register action that was performed on part
2407
2408 //synchronize
2410
2411 // server part of sync, client will be synced from SetPartsFromSyncData
2413
2414 UpdateNavmesh();
2415
2416 //update visuals
2417 UpdateVisuals();
2418
2419 //reset action sync data
2420 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2421
2422 //check base state
2423 if (construtionPart.IsBase())
2424 {
2425 //Destroy construction
2426 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2427 }
2428 }
2429
2431 {
2432 //play sound
2434 }
2435
2436 //Destroy
2438 {
2439 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2441
2442 //register constructed parts for synchronization
2444
2445 //register action that was performed on part
2447
2448 //synchronize
2450
2451 // server part of sync, client will be synced from SetPartsFromSyncData
2453
2454 UpdateNavmesh();
2455
2456 //update visuals
2457 UpdateVisuals();
2458
2459 //reset action sync data
2460 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2461
2462 //check base state
2463 if (construtionPart.IsBase())
2464 {
2465 //Destroy construction
2466 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2467 }
2468 }
2469
2470 void OnPartDestroyedClient(string part_name, int action_id)
2471 {
2472 //play sound
2474 }
2475
2476 // --- UPDATE
2477 void InitBaseState()
2478 {
2479 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2480
2481 InitVisuals();
2482 UpdateNavmesh(); //regenerate navmesh
2483 GetConstruction().InitBaseState();
2484 }
2485
2486 void InitVisuals()
2487 {
2488 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2489 //check base
2490 if (!HasBase())
2491 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2492 else
2493 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2494
2495 GetConstruction().UpdateVisuals();
2496 }
2497
2498 void UpdateVisuals()
2499 {
2501
2503 foreach (string slotName : attachmentSlots)
2505
2506 //check base
2507 if (!HasBase())
2508 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2509 else
2510 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2511
2512 GetConstruction().UpdateVisuals();
2513 }
2514
2516 {
2517 string slotNameMounted = slot_name + "_Mounted";
2518 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2519
2520 if (attachment)
2521 {
2522 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2523 if (barbedWire && barbedWire.IsMounted())
2525 else
2527
2528 if (is_locked)
2529 {
2530 SetAnimationPhase(slotNameMounted, 0);
2531 SetAnimationPhase(slot_name, 1);
2532 }
2533 else
2534 {
2535 SetAnimationPhase(slotNameMounted, 1);
2536 SetAnimationPhase(slot_name, 0);
2537 }
2538 }
2539 else
2540 {
2541 SetAnimationPhase(slotNameMounted, 1);
2542 SetAnimationPhase(slot_name, 1);
2543
2545 }
2546 }
2547
2548 // avoid calling this function on frequent occasions, it's a massive performance hit
2549 void UpdatePhysics()
2550 {
2552 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2553
2556
2558 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2559
2560 foreach (string slotName : attachmentSlots)
2562
2563 //check base
2564 if (!HasBase())
2565 {
2567 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2568
2569 AddProxyPhysics(ANIMATION_DEPLOYED);
2570 }
2571 else
2572 {
2574 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2575
2576 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2577 }
2578
2579 GetConstruction().UpdatePhysics();
2580 UpdateNavmesh();
2581 }
2582
2584 {
2585 //checks for invalid appends; hotfix
2586 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2587 return;
2588 //----------------------------------
2589 string slot_name_mounted = slot_name + "_Mounted";
2590 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2591
2592 //remove proxy physics
2593 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2594 RemoveProxyPhysics(slot_name_mounted);
2595 RemoveProxyPhysics(slot_name);
2596
2597 if (attachment)
2598 {
2599 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2600 if (is_locked)
2601 {
2602 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2603 AddProxyPhysics(slot_name_mounted);
2604 }
2605 else
2606 {
2607 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2608 AddProxyPhysics(slot_name);
2609 }
2610 }
2611 }
2612
2613 protected void UpdateNavmesh()
2614 {
2615 SetAffectPathgraph(true, false);
2616 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2617 }
2618
2619 override bool CanUseConstruction()
2620 {
2621 return true;
2622 }
2623
2624 override bool CanUseConstructionBuild()
2625 {
2626 return true;
2627 }
2628
2630 {
2631 if (attachment)
2632 {
2634 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2635
2636 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2637 }
2638
2639 return false;
2640 }
2641
2642 protected bool IsAttachmentSlotLocked(string slot_name)
2643 {
2644 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2645 }
2646
2647 //--- ATTACHMENT SLOTS
2649 {
2650 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2651 if (GetGame().ConfigIsExisting(config_path))
2652 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2653 }
2654
2656 {
2657 return true;
2658 }
2659
2660 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2661 {
2662 return true;
2663 }
2664
2665 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2666 {
2667 return true;
2668 }
2669
2670 // --- INIT
2671 void ConstructionInit()
2672 {
2673 if (!m_Construction)
2674 m_Construction = new Construction(this);
2675
2676 GetConstruction().Init();
2677 }
2678
2680 {
2681 return m_Construction;
2682 }
2683
2684 //--- INVENTORY/ATTACHMENTS CONDITIONS
2685 //attachments
2687 {
2688 return super.CanReceiveAttachment(attachment, slotId);
2689 }
2690
2692 {
2693 int attachment_count = GetInventory().AttachmentCount();
2694 if (attachment_count > 0)
2695 {
2696 if (HasBase() && attachment_count == 1)
2697 return false;
2698
2699 return true;
2700 }
2701
2702 return false;
2703 }
2704
2705 override bool ShowZonesHealth()
2706 {
2707 return true;
2708 }
2709
2710 //this into/outo parent.Cargo
2711 override bool CanPutInCargo(EntityAI parent)
2712 {
2713 return false;
2714 }
2715
2716 override bool CanRemoveFromCargo(EntityAI parent)
2717 {
2718 return false;
2719 }
2720
2721 //hands
2722 override bool CanPutIntoHands(EntityAI parent)
2723 {
2724 return false;
2725 }
2726
2727 //--- ACTION CONDITIONS
2728 //direction
2729 override bool IsFacingPlayer(PlayerBase player, string selection)
2730 {
2731 return true;
2732 }
2733
2734 override bool IsPlayerInside(PlayerBase player, string selection)
2735 {
2736 return true;
2737 }
2738
2741 {
2742 return false;
2743 }
2744
2745 //camera direction check
2746 bool IsFacingCamera(string selection)
2747 {
2748 return true;
2749 }
2750
2751 //roof check
2753 {
2754 return false;
2755 }
2756
2757 //selection->player distance check
2758 bool HasProperDistance(string selection, PlayerBase player)
2759 {
2760 return true;
2761 }
2762
2763 //folding
2765 {
2766 if (HasBase() || GetInventory().AttachmentCount() > 0)
2767 return false;
2768
2769 return true;
2770 }
2771
2773 {
2776
2777 return item;
2778 }
2779
2780 //Damage triggers (barbed wire)
2781 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2782 {
2783 if (GetGame() && GetGame().IsServer())
2784 {
2785 //destroy area damage if some already exists
2787
2788 //create new area damage
2790 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2791
2792 vector min_max[2];
2793 if (MemoryPointExists(slot_name + "_min"))
2794 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2795 if (MemoryPointExists(slot_name + "_max"))
2796 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2797
2798 //get proper trigger extents (min<max)
2799 vector extents[2];
2800 GetConstruction().GetTriggerExtents(min_max, extents);
2801
2802 //get box center
2803 vector center;
2804 center = GetConstruction().GetBoxCenter(min_max);
2805 center = ModelToWorld(center);
2806
2807 //rotate center if needed
2810
2811 areaDamage.SetExtents(extents[0], extents[1]);
2812 areaDamage.SetAreaPosition(center);
2813 areaDamage.SetAreaOrientation(orientation);
2814 areaDamage.SetLoopInterval(1.0);
2815 areaDamage.SetDeferDuration(0.2);
2816 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2817 areaDamage.SetAmmoName("BarbedWireHit");
2818 areaDamage.Spawn();
2819
2821 }
2822 }
2823
2825 {
2826 if (angle_deg != 0)
2827 {
2828 //orientation
2830
2831 //center
2833 if (MemoryPointExists("rotate_axis"))
2834 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2837 center[0] = r_center_x;
2838 center[2] = r_center_z;
2839 }
2840 }
2841
2842 void DestroyAreaDamage(string slot_name)
2843 {
2844 if (GetGame() && GetGame().IsServer())
2845 {
2848 {
2849 if (areaDamage)
2850 areaDamage.Destroy();
2851
2853 }
2854 }
2855 }
2856
2857 override bool IsIgnoredByConstruction()
2858 {
2859 return true;
2860 }
2861
2862 //================================================================
2863 // SOUNDS
2864 //================================================================
2865 protected void SoundBuildStart(string part_name)
2866 {
2867 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2868 }
2869
2870 protected void SoundDismantleStart(string part_name)
2871 {
2872 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2873 }
2874
2875 protected void SoundDestroyStart(string part_name)
2876 {
2877 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2878 }
2879
2880 protected string GetBuildSoundByMaterial(string part_name)
2881 {
2883
2884 switch (material_type)
2885 {
2886 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2887 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2888 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2889 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2890 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2891 }
2892
2893 return "";
2894 }
2895
2896 protected string GetDismantleSoundByMaterial(string part_name)
2897 {
2899
2900 switch (material_type)
2901 {
2902 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2903 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2904 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2905 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2906 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2907 }
2908
2909 return "";
2910 }
2911
2912 //misc
2914 {
2915 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2916 {
2917 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2919 SetHealth(slot_name, "Health", item.GetHealth());
2920 }
2921 }
2922
2923 override int GetDamageSystemVersionChange()
2924 {
2925 return 111;
2926 }
2927
2928 override void SetActions()
2929 {
2930 super.SetActions();
2931
2933 //AddAction(ActionTakeHybridAttachment);
2934 //AddAction(ActionTakeHybridAttachmentToHands);
2937 }
2938
2939 //================================================================
2940 // DEBUG
2941 //================================================================
2942 protected void DebugCustomState()
2943 {
2944 }
2945
2948 {
2949 return null;
2950 }
2951
2952 override void OnDebugSpawn()
2953 {
2954 FullyBuild();
2955 }
2956
2957 void FullyBuild()
2958 {
2960 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2961
2962 Man p;
2963
2964#ifdef SERVER
2966 GetGame().GetWorld().GetPlayerList(players);
2967 if (players.Count())
2968 p = players[0];
2969#else
2970 p = GetGame().GetPlayer();
2971#endif
2972
2973 foreach (ConstructionPart part : parts)
2974 {
2975 bool excluded = false;
2976 string partName = part.GetPartName();
2977 if (excludes)
2978 {
2979 foreach (string exclude : excludes)
2980 {
2981 if (partName.Contains(exclude))
2982 {
2983 excluded = true;
2984 break;
2985 }
2986 }
2987 }
2988
2989 if (!excluded)
2991 }
2992
2993 GetConstruction().UpdateVisuals();
2994 }
2995}
2996
2997void bsbDebugPrint(string s)
2998{
2999#ifdef BSB_DEBUG
3000 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3001#else
3002 //Print("" + s); // comment/uncomment to hide/see debug logs
3003#endif
3004}
3005void bsbDebugSpam(string s)
3006{
3007#ifdef BSB_DEBUG_SPAM
3008 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3009#else
3010 //Print("" + s); // comment/uncomment to hide/see debug logs
3011#endif
3012}

Referenced by ItemBase::SetActionFromSyncData().

◆ OnPartDestroyedServer()

void bsbDebugPrint::OnPartDestroyedServer ( Man player,
string part_name,
int action_id,
bool destroyed_by_connected_part = false )
protected

Definition at line 1789 of file BaseBuildingBase.c.

1791{
1792 const string ANIMATION_DEPLOYED = "Deployed";
1793
1794 float m_ConstructionKitHealth; //stored health value for used construction kit
1795
1797
1798 bool m_HasBase;
1799 //variables for synchronization of base building parts (2x31 is the current limit)
1800 int m_SyncParts01; //synchronization for already built parts (31 parts)
1801 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1802 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1803 int m_InteractedPartId; //construction part id that an action was performed on
1804 int m_PerformedActionId; //action id that was performed on a construction part
1805
1806 //Sounds
1807 //build
1808 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1809 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1810 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1811 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1812 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1813 //dismantle
1814 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1815 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1816 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1817 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1818 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1819
1820 protected EffectSound m_Sound;
1821
1825
1826 // Constructor
1827 void BaseBuildingBase()
1828 {
1830
1831 //synchronized variables
1832 RegisterNetSyncVariableInt("m_SyncParts01");
1833 RegisterNetSyncVariableInt("m_SyncParts02");
1834 RegisterNetSyncVariableInt("m_SyncParts03");
1835 RegisterNetSyncVariableInt("m_InteractedPartId");
1836 RegisterNetSyncVariableInt("m_PerformedActionId");
1837 RegisterNetSyncVariableBool("m_HasBase");
1838
1839 //Construction init
1841
1842 if (ConfigIsExisting("hybridAttachments"))
1843 {
1845 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1846 }
1847 if (ConfigIsExisting("mountables"))
1848 {
1850 ConfigGetTextArray("mountables", m_Mountables);
1851 }
1852
1853 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1854 }
1855
1856 override void EEDelete(EntityAI parent)
1857 {
1858 super.EEDelete(parent);
1859
1860 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1862
1863 }
1864
1865 override string GetInvulnerabilityTypeString()
1866 {
1867 return "disableBaseDamage";
1868 }
1869
1870 override bool CanObstruct()
1871 {
1872 return true;
1873 }
1874
1875 override int GetHideIconMask()
1876 {
1877 return EInventoryIconVisibility.HIDE_VICINITY;
1878 }
1879
1880 // --- SYNCHRONIZATION
1882 {
1883 if (GetGame().IsServer())
1884 SetSynchDirty();
1885 }
1886
1887 override void OnVariablesSynchronized()
1888 {
1889 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1890 super.OnVariablesSynchronized();
1891
1892 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1893 }
1894
1895 protected void OnSynchronizedClient()
1896 {
1897 //update parts
1899
1900 //update action on part
1902
1903 //update visuals (client)
1904 UpdateVisuals();
1905 }
1906
1907 //parts synchronization
1909 {
1910 //part_id must starts from index = 1
1911 int offset;
1912 int mask;
1913
1914 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1915 {
1916 offset = part_id - 1;
1917 mask = 1 << offset;
1918
1920 }
1921 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1922 {
1923 offset = (part_id % 32);
1924 mask = 1 << offset;
1925
1927 }
1928 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1929 {
1930 offset = (part_id % 63);
1931 mask = 1 << offset;
1932
1934 }
1935 }
1936
1938 {
1939 //part_id must starts from index = 1
1940 int offset;
1941 int mask;
1942
1943 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1944 {
1945 offset = part_id - 1;
1946 mask = 1 << offset;
1947
1949 }
1950 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1951 {
1952 offset = (part_id % 32);
1953 mask = 1 << offset;
1954
1956 }
1957 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1958 {
1959 offset = (part_id % 63);
1960 mask = 1 << offset;
1961
1963 }
1964 }
1965
1967 {
1968 //part_id must starts from index = 1
1969 int offset;
1970 int mask;
1971
1972 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1973 {
1974 offset = part_id - 1;
1975 mask = 1 << offset;
1976
1977 if ((m_SyncParts01 & mask) > 0)
1978 return true;
1979 }
1980 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1981 {
1982 offset = (part_id % 32);
1983 mask = 1 << offset;
1984
1985 if ((m_SyncParts02 & mask) > 0)
1986 return true;
1987 }
1988 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1989 {
1990 offset = (part_id % 63);
1991 mask = 1 << offset;
1992
1993 if ((m_SyncParts03 & mask) > 0)
1994 return true;
1995 }
1996
1997 return false;
1998 }
1999
2000 protected void RegisterActionForSync(int part_id, int action_id)
2001 {
2004 }
2005
2006 protected void ResetActionSyncData()
2007 {
2008 //reset data
2009 m_InteractedPartId = -1;
2011 }
2012
2013 protected void SetActionFromSyncData()
2014 {
2015 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2016 {
2019
2020 switch (build_action_id)
2021 {
2025 }
2026 }
2027 }
2028 //------
2029
2031 {
2032 string key = part.m_PartName;
2033 bool is_base = part.IsBase();
2035 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2037 {
2038 if (!part.IsBuilt())
2039 {
2040 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2041 GetConstruction().AddToConstructedParts(key);
2042 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2043
2044 if (is_base)
2045 {
2047 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2048 }
2049 }
2050 }
2051 else
2052 {
2053 if (part.IsBuilt())
2054 {
2055 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2056 GetConstruction().RemoveFromConstructedParts(key);
2057 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2058
2059 if (is_base)
2060 {
2062 AddProxyPhysics(ANIMATION_DEPLOYED);
2063 }
2064 }
2065 }
2066
2067 //check slot lock for material attachments
2068 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2069 }
2070
2071 //set construction parts based on synchronized data
2073 {
2076
2077 for (int i = 0; i < construction_parts.Count(); ++i)
2078 {
2079 string key = construction_parts.GetKey(i);
2082 }
2083
2084 //regenerate navmesh
2085 UpdateNavmesh();
2086 }
2087
2089 {
2092
2093 for (int i = 0; i < construction_parts.Count(); ++i)
2094 {
2095 string key = construction_parts.GetKey(i);
2097
2098 if (value.GetId() == id)
2099 return value;
2100 }
2101
2102 return NULL;
2103 }
2104 //
2105
2106 //Base
2107 bool HasBase()
2108 {
2109 return m_HasBase;
2110 }
2111
2112 void SetBaseState(bool has_base)
2113 {
2115 }
2116
2117 override bool IsDeployable()
2118 {
2119 return true;
2120 }
2121
2122 bool IsOpened()
2123 {
2124 return false;
2125 }
2126
2127 //--- CONSTRUCTION KIT
2129 {
2133
2134 return construction_kit;
2135 }
2136
2138 {
2139 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2142 }
2143
2144 protected vector GetKitSpawnPosition()
2145 {
2146 return GetPosition();
2147 }
2148
2149 protected string GetConstructionKitType()
2150 {
2151 return "";
2152 }
2153
2155 {
2157 GetGame().ObjectDelete(construction_kit);
2158 }
2159
2160 //--- CONSTRUCTION
2161 void DestroyConstruction()
2162 {
2163 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2164 GetGame().ObjectDelete(this);
2165 }
2166
2167 // --- EVENTS
2168 override void OnStoreSave(ParamsWriteContext ctx)
2169 {
2170 super.OnStoreSave(ctx);
2171
2172 //sync parts 01
2173 ctx.Write(m_SyncParts01);
2174 ctx.Write(m_SyncParts02);
2175 ctx.Write(m_SyncParts03);
2176
2177 ctx.Write(m_HasBase);
2178 }
2179
2180 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2181 {
2182 if (!super.OnStoreLoad(ctx, version))
2183 return false;
2184
2185 //--- Base building data ---
2186 //Restore synced parts data
2187 if (!ctx.Read(m_SyncParts01))
2188 {
2189 m_SyncParts01 = 0; //set default
2190 return false;
2191 }
2192 if (!ctx.Read(m_SyncParts02))
2193 {
2194 m_SyncParts02 = 0; //set default
2195 return false;
2196 }
2197 if (!ctx.Read(m_SyncParts03))
2198 {
2199 m_SyncParts03 = 0; //set default
2200 return false;
2201 }
2202
2203 //has base
2204 if (!ctx.Read(m_HasBase))
2205 {
2206 m_HasBase = false;
2207 return false;
2208 }
2209 //---
2210
2211 return true;
2212 }
2213
2214 override void AfterStoreLoad()
2215 {
2216 super.AfterStoreLoad();
2217
2220 }
2221
2223 {
2224 //update server data
2226
2227 //set base state
2228 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2229 SetBaseState(construction_part.IsBuilt()) ;
2230
2231 //synchronize after load
2233 }
2234
2235 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2236 {
2238 return;
2239
2240 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2241
2242 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2243 return;
2244
2246 string part_name = zone;
2247 part_name.ToLower();
2248
2250 {
2252
2253 if (construction_part && construction.IsPartConstructed(part_name))
2254 {
2255 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2256 construction.DestroyConnectedParts(part_name);
2257 }
2258
2259 //barbed wire handling (hack-ish)
2260 if (part_name.Contains("barbed"))
2261 {
2262 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2263 if (barbed_wire)
2264 barbed_wire.SetMountedState(false);
2265 }
2266 }
2267 }
2268
2269 override void EEOnAfterLoad()
2270 {
2272 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2273
2274 super.EEOnAfterLoad();
2275 }
2276
2277 override void EEInit()
2278 {
2279 super.EEInit();
2280
2281 // init visuals and physics
2282 InitBaseState();
2283
2284 //debug
2285#ifdef DEVELOPER
2287#endif
2288 }
2289
2290 override void EEItemAttached(EntityAI item, string slot_name)
2291 {
2292 super.EEItemAttached(item, slot_name);
2293
2295 UpdateVisuals();
2297 }
2298
2299 override void EEItemDetached(EntityAI item, string slot_name)
2300 {
2301 super.EEItemDetached(item, slot_name);
2302
2303 UpdateVisuals();
2305 }
2306
2307 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2308 {
2310 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2311
2314 }
2315
2316 //ignore out of reach condition
2317 override bool IgnoreOutOfReachCondition()
2318 {
2319 return true;
2320 }
2321
2322 //CONSTRUCTION EVENTS
2323 //Build
2324 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2325 {
2327
2328 //check base state
2329 if (construtionPart.IsBase())
2330 {
2331 SetBaseState(true);
2332
2333 //spawn kit
2335 }
2336
2337 //register constructed parts for synchronization
2339
2340 //register action that was performed on part
2342
2343 //synchronize
2345
2346 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2347
2348 UpdateNavmesh();
2349
2350 //update visuals
2351 UpdateVisuals();
2352
2353 //reset action sync data
2354 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2355 }
2356
2357 void OnPartBuiltClient(string part_name, int action_id)
2358 {
2359 //play sound
2361 }
2362
2363 //Dismantle
2365 {
2366 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2368
2369 //register constructed parts for synchronization
2371
2372 //register action that was performed on part
2374
2375 //synchronize
2377
2378 // server part of sync, client will be synced from SetPartsFromSyncData
2380
2381 UpdateNavmesh();
2382
2383 //update visuals
2384 UpdateVisuals();
2385
2386 //reset action sync data
2387 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2388
2389 //check base state
2390 if (construtionPart.IsBase())
2391 {
2392 //Destroy construction
2393 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2394 }
2395 }
2396
2398 {
2399 //play sound
2401 }
2402
2403 //Destroy
2405 {
2406 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2408
2409 //register constructed parts for synchronization
2411
2412 //register action that was performed on part
2414
2415 //synchronize
2417
2418 // server part of sync, client will be synced from SetPartsFromSyncData
2420
2421 UpdateNavmesh();
2422
2423 //update visuals
2424 UpdateVisuals();
2425
2426 //reset action sync data
2427 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2428
2429 //check base state
2430 if (construtionPart.IsBase())
2431 {
2432 //Destroy construction
2433 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2434 }
2435 }
2436
2437 void OnPartDestroyedClient(string part_name, int action_id)
2438 {
2439 //play sound
2441 }
2442
2443 // --- UPDATE
2444 void InitBaseState()
2445 {
2446 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2447
2448 InitVisuals();
2449 UpdateNavmesh(); //regenerate navmesh
2450 GetConstruction().InitBaseState();
2451 }
2452
2453 void InitVisuals()
2454 {
2455 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2456 //check base
2457 if (!HasBase())
2458 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2459 else
2460 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2461
2462 GetConstruction().UpdateVisuals();
2463 }
2464
2465 void UpdateVisuals()
2466 {
2468
2470 foreach (string slotName : attachmentSlots)
2472
2473 //check base
2474 if (!HasBase())
2475 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2476 else
2477 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2478
2479 GetConstruction().UpdateVisuals();
2480 }
2481
2483 {
2484 string slotNameMounted = slot_name + "_Mounted";
2485 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2486
2487 if (attachment)
2488 {
2489 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2490 if (barbedWire && barbedWire.IsMounted())
2492 else
2494
2495 if (is_locked)
2496 {
2497 SetAnimationPhase(slotNameMounted, 0);
2498 SetAnimationPhase(slot_name, 1);
2499 }
2500 else
2501 {
2502 SetAnimationPhase(slotNameMounted, 1);
2503 SetAnimationPhase(slot_name, 0);
2504 }
2505 }
2506 else
2507 {
2508 SetAnimationPhase(slotNameMounted, 1);
2509 SetAnimationPhase(slot_name, 1);
2510
2512 }
2513 }
2514
2515 // avoid calling this function on frequent occasions, it's a massive performance hit
2516 void UpdatePhysics()
2517 {
2519 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2520
2523
2525 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2526
2527 foreach (string slotName : attachmentSlots)
2529
2530 //check base
2531 if (!HasBase())
2532 {
2534 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2535
2536 AddProxyPhysics(ANIMATION_DEPLOYED);
2537 }
2538 else
2539 {
2541 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2542
2543 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2544 }
2545
2546 GetConstruction().UpdatePhysics();
2547 UpdateNavmesh();
2548 }
2549
2551 {
2552 //checks for invalid appends; hotfix
2553 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2554 return;
2555 //----------------------------------
2556 string slot_name_mounted = slot_name + "_Mounted";
2557 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2558
2559 //remove proxy physics
2560 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2561 RemoveProxyPhysics(slot_name_mounted);
2562 RemoveProxyPhysics(slot_name);
2563
2564 if (attachment)
2565 {
2566 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2567 if (is_locked)
2568 {
2569 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2570 AddProxyPhysics(slot_name_mounted);
2571 }
2572 else
2573 {
2574 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2575 AddProxyPhysics(slot_name);
2576 }
2577 }
2578 }
2579
2580 protected void UpdateNavmesh()
2581 {
2582 SetAffectPathgraph(true, false);
2583 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2584 }
2585
2586 override bool CanUseConstruction()
2587 {
2588 return true;
2589 }
2590
2591 override bool CanUseConstructionBuild()
2592 {
2593 return true;
2594 }
2595
2597 {
2598 if (attachment)
2599 {
2601 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2602
2603 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2604 }
2605
2606 return false;
2607 }
2608
2609 protected bool IsAttachmentSlotLocked(string slot_name)
2610 {
2611 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2612 }
2613
2614 //--- ATTACHMENT SLOTS
2616 {
2617 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2618 if (GetGame().ConfigIsExisting(config_path))
2619 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2620 }
2621
2623 {
2624 return true;
2625 }
2626
2627 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2628 {
2629 return true;
2630 }
2631
2632 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2633 {
2634 return true;
2635 }
2636
2637 // --- INIT
2638 void ConstructionInit()
2639 {
2640 if (!m_Construction)
2641 m_Construction = new Construction(this);
2642
2643 GetConstruction().Init();
2644 }
2645
2647 {
2648 return m_Construction;
2649 }
2650
2651 //--- INVENTORY/ATTACHMENTS CONDITIONS
2652 //attachments
2654 {
2655 return super.CanReceiveAttachment(attachment, slotId);
2656 }
2657
2659 {
2660 int attachment_count = GetInventory().AttachmentCount();
2661 if (attachment_count > 0)
2662 {
2663 if (HasBase() && attachment_count == 1)
2664 return false;
2665
2666 return true;
2667 }
2668
2669 return false;
2670 }
2671
2672 override bool ShowZonesHealth()
2673 {
2674 return true;
2675 }
2676
2677 //this into/outo parent.Cargo
2678 override bool CanPutInCargo(EntityAI parent)
2679 {
2680 return false;
2681 }
2682
2683 override bool CanRemoveFromCargo(EntityAI parent)
2684 {
2685 return false;
2686 }
2687
2688 //hands
2689 override bool CanPutIntoHands(EntityAI parent)
2690 {
2691 return false;
2692 }
2693
2694 //--- ACTION CONDITIONS
2695 //direction
2696 override bool IsFacingPlayer(PlayerBase player, string selection)
2697 {
2698 return true;
2699 }
2700
2701 override bool IsPlayerInside(PlayerBase player, string selection)
2702 {
2703 return true;
2704 }
2705
2708 {
2709 return false;
2710 }
2711
2712 //camera direction check
2713 bool IsFacingCamera(string selection)
2714 {
2715 return true;
2716 }
2717
2718 //roof check
2720 {
2721 return false;
2722 }
2723
2724 //selection->player distance check
2725 bool HasProperDistance(string selection, PlayerBase player)
2726 {
2727 return true;
2728 }
2729
2730 //folding
2732 {
2733 if (HasBase() || GetInventory().AttachmentCount() > 0)
2734 return false;
2735
2736 return true;
2737 }
2738
2740 {
2743
2744 return item;
2745 }
2746
2747 //Damage triggers (barbed wire)
2748 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2749 {
2750 if (GetGame() && GetGame().IsServer())
2751 {
2752 //destroy area damage if some already exists
2754
2755 //create new area damage
2757 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2758
2759 vector min_max[2];
2760 if (MemoryPointExists(slot_name + "_min"))
2761 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2762 if (MemoryPointExists(slot_name + "_max"))
2763 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2764
2765 //get proper trigger extents (min<max)
2766 vector extents[2];
2767 GetConstruction().GetTriggerExtents(min_max, extents);
2768
2769 //get box center
2770 vector center;
2771 center = GetConstruction().GetBoxCenter(min_max);
2772 center = ModelToWorld(center);
2773
2774 //rotate center if needed
2777
2778 areaDamage.SetExtents(extents[0], extents[1]);
2779 areaDamage.SetAreaPosition(center);
2780 areaDamage.SetAreaOrientation(orientation);
2781 areaDamage.SetLoopInterval(1.0);
2782 areaDamage.SetDeferDuration(0.2);
2783 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2784 areaDamage.SetAmmoName("BarbedWireHit");
2785 areaDamage.Spawn();
2786
2788 }
2789 }
2790
2792 {
2793 if (angle_deg != 0)
2794 {
2795 //orientation
2797
2798 //center
2800 if (MemoryPointExists("rotate_axis"))
2801 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2804 center[0] = r_center_x;
2805 center[2] = r_center_z;
2806 }
2807 }
2808
2809 void DestroyAreaDamage(string slot_name)
2810 {
2811 if (GetGame() && GetGame().IsServer())
2812 {
2815 {
2816 if (areaDamage)
2817 areaDamage.Destroy();
2818
2820 }
2821 }
2822 }
2823
2824 override bool IsIgnoredByConstruction()
2825 {
2826 return true;
2827 }
2828
2829 //================================================================
2830 // SOUNDS
2831 //================================================================
2832 protected void SoundBuildStart(string part_name)
2833 {
2834 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2835 }
2836
2837 protected void SoundDismantleStart(string part_name)
2838 {
2839 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2840 }
2841
2842 protected void SoundDestroyStart(string part_name)
2843 {
2844 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2845 }
2846
2847 protected string GetBuildSoundByMaterial(string part_name)
2848 {
2850
2851 switch (material_type)
2852 {
2853 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2854 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2855 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2856 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2857 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2858 }
2859
2860 return "";
2861 }
2862
2863 protected string GetDismantleSoundByMaterial(string part_name)
2864 {
2866
2867 switch (material_type)
2868 {
2869 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2870 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2871 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2872 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2873 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2874 }
2875
2876 return "";
2877 }
2878
2879 //misc
2881 {
2882 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2883 {
2884 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2886 SetHealth(slot_name, "Health", item.GetHealth());
2887 }
2888 }
2889
2890 override int GetDamageSystemVersionChange()
2891 {
2892 return 111;
2893 }
2894
2895 override void SetActions()
2896 {
2897 super.SetActions();
2898
2900 //AddAction(ActionTakeHybridAttachment);
2901 //AddAction(ActionTakeHybridAttachmentToHands);
2904 }
2905
2906 //================================================================
2907 // DEBUG
2908 //================================================================
2909 protected void DebugCustomState()
2910 {
2911 }
2912
2915 {
2916 return null;
2917 }
2918
2919 override void OnDebugSpawn()
2920 {
2921 FullyBuild();
2922 }
2923
2924 void FullyBuild()
2925 {
2927 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2928
2929 Man p;
2930
2931#ifdef SERVER
2933 GetGame().GetWorld().GetPlayerList(players);
2934 if (players.Count())
2935 p = players[0];
2936#else
2937 p = GetGame().GetPlayer();
2938#endif
2939
2940 foreach (ConstructionPart part : parts)
2941 {
2942 bool excluded = false;
2943 string partName = part.GetPartName();
2944 if (excludes)
2945 {
2946 foreach (string exclude : excludes)
2947 {
2948 if (partName.Contains(exclude))
2949 {
2950 excluded = true;
2951 break;
2952 }
2953 }
2954 }
2955
2956 if (!excluded)
2958 }
2959
2960 GetConstruction().UpdateVisuals();
2961 }
2962}
2963
2964void bsbDebugPrint(string s)
2965{
2966#ifdef BSB_DEBUG
2967 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2968#else
2969 //Print("" + s); // comment/uncomment to hide/see debug logs
2970#endif
2971}
2972void bsbDebugSpam(string s)
2973{
2974#ifdef BSB_DEBUG_SPAM
2975 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2976#else
2977 //Print("" + s); // comment/uncomment to hide/see debug logs
2978#endif
2979}

◆ OnPartDismantledClient()

void bsbDebugPrint::OnPartDismantledClient ( string part_name,
int action_id )
protected

Definition at line 1782 of file BaseBuildingBase.c.

1784{
1785 const string ANIMATION_DEPLOYED = "Deployed";
1786
1787 float m_ConstructionKitHealth; //stored health value for used construction kit
1788
1790
1791 bool m_HasBase;
1792 //variables for synchronization of base building parts (2x31 is the current limit)
1793 int m_SyncParts01; //synchronization for already built parts (31 parts)
1794 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1795 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1796 int m_InteractedPartId; //construction part id that an action was performed on
1797 int m_PerformedActionId; //action id that was performed on a construction part
1798
1799 //Sounds
1800 //build
1801 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1802 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1803 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1804 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1805 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1806 //dismantle
1807 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1808 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1809 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1810 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1811 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1812
1813 protected EffectSound m_Sound;
1814
1818
1819 // Constructor
1820 void BaseBuildingBase()
1821 {
1823
1824 //synchronized variables
1825 RegisterNetSyncVariableInt("m_SyncParts01");
1826 RegisterNetSyncVariableInt("m_SyncParts02");
1827 RegisterNetSyncVariableInt("m_SyncParts03");
1828 RegisterNetSyncVariableInt("m_InteractedPartId");
1829 RegisterNetSyncVariableInt("m_PerformedActionId");
1830 RegisterNetSyncVariableBool("m_HasBase");
1831
1832 //Construction init
1834
1835 if (ConfigIsExisting("hybridAttachments"))
1836 {
1838 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1839 }
1840 if (ConfigIsExisting("mountables"))
1841 {
1843 ConfigGetTextArray("mountables", m_Mountables);
1844 }
1845
1846 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1847 }
1848
1849 override void EEDelete(EntityAI parent)
1850 {
1851 super.EEDelete(parent);
1852
1853 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1855
1856 }
1857
1858 override string GetInvulnerabilityTypeString()
1859 {
1860 return "disableBaseDamage";
1861 }
1862
1863 override bool CanObstruct()
1864 {
1865 return true;
1866 }
1867
1868 override int GetHideIconMask()
1869 {
1870 return EInventoryIconVisibility.HIDE_VICINITY;
1871 }
1872
1873 // --- SYNCHRONIZATION
1875 {
1876 if (GetGame().IsServer())
1877 SetSynchDirty();
1878 }
1879
1880 override void OnVariablesSynchronized()
1881 {
1882 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1883 super.OnVariablesSynchronized();
1884
1885 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1886 }
1887
1888 protected void OnSynchronizedClient()
1889 {
1890 //update parts
1892
1893 //update action on part
1895
1896 //update visuals (client)
1897 UpdateVisuals();
1898 }
1899
1900 //parts synchronization
1902 {
1903 //part_id must starts from index = 1
1904 int offset;
1905 int mask;
1906
1907 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1908 {
1909 offset = part_id - 1;
1910 mask = 1 << offset;
1911
1913 }
1914 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1915 {
1916 offset = (part_id % 32);
1917 mask = 1 << offset;
1918
1920 }
1921 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1922 {
1923 offset = (part_id % 63);
1924 mask = 1 << offset;
1925
1927 }
1928 }
1929
1931 {
1932 //part_id must starts from index = 1
1933 int offset;
1934 int mask;
1935
1936 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1937 {
1938 offset = part_id - 1;
1939 mask = 1 << offset;
1940
1942 }
1943 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1944 {
1945 offset = (part_id % 32);
1946 mask = 1 << offset;
1947
1949 }
1950 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1951 {
1952 offset = (part_id % 63);
1953 mask = 1 << offset;
1954
1956 }
1957 }
1958
1960 {
1961 //part_id must starts from index = 1
1962 int offset;
1963 int mask;
1964
1965 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1966 {
1967 offset = part_id - 1;
1968 mask = 1 << offset;
1969
1970 if ((m_SyncParts01 & mask) > 0)
1971 return true;
1972 }
1973 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1974 {
1975 offset = (part_id % 32);
1976 mask = 1 << offset;
1977
1978 if ((m_SyncParts02 & mask) > 0)
1979 return true;
1980 }
1981 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1982 {
1983 offset = (part_id % 63);
1984 mask = 1 << offset;
1985
1986 if ((m_SyncParts03 & mask) > 0)
1987 return true;
1988 }
1989
1990 return false;
1991 }
1992
1993 protected void RegisterActionForSync(int part_id, int action_id)
1994 {
1997 }
1998
1999 protected void ResetActionSyncData()
2000 {
2001 //reset data
2002 m_InteractedPartId = -1;
2004 }
2005
2006 protected void SetActionFromSyncData()
2007 {
2008 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2009 {
2012
2013 switch (build_action_id)
2014 {
2018 }
2019 }
2020 }
2021 //------
2022
2024 {
2025 string key = part.m_PartName;
2026 bool is_base = part.IsBase();
2028 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2030 {
2031 if (!part.IsBuilt())
2032 {
2033 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2034 GetConstruction().AddToConstructedParts(key);
2035 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2036
2037 if (is_base)
2038 {
2040 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2041 }
2042 }
2043 }
2044 else
2045 {
2046 if (part.IsBuilt())
2047 {
2048 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2049 GetConstruction().RemoveFromConstructedParts(key);
2050 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2051
2052 if (is_base)
2053 {
2055 AddProxyPhysics(ANIMATION_DEPLOYED);
2056 }
2057 }
2058 }
2059
2060 //check slot lock for material attachments
2061 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2062 }
2063
2064 //set construction parts based on synchronized data
2066 {
2069
2070 for (int i = 0; i < construction_parts.Count(); ++i)
2071 {
2072 string key = construction_parts.GetKey(i);
2075 }
2076
2077 //regenerate navmesh
2078 UpdateNavmesh();
2079 }
2080
2082 {
2085
2086 for (int i = 0; i < construction_parts.Count(); ++i)
2087 {
2088 string key = construction_parts.GetKey(i);
2090
2091 if (value.GetId() == id)
2092 return value;
2093 }
2094
2095 return NULL;
2096 }
2097 //
2098
2099 //Base
2100 bool HasBase()
2101 {
2102 return m_HasBase;
2103 }
2104
2105 void SetBaseState(bool has_base)
2106 {
2108 }
2109
2110 override bool IsDeployable()
2111 {
2112 return true;
2113 }
2114
2115 bool IsOpened()
2116 {
2117 return false;
2118 }
2119
2120 //--- CONSTRUCTION KIT
2122 {
2126
2127 return construction_kit;
2128 }
2129
2131 {
2132 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2135 }
2136
2137 protected vector GetKitSpawnPosition()
2138 {
2139 return GetPosition();
2140 }
2141
2142 protected string GetConstructionKitType()
2143 {
2144 return "";
2145 }
2146
2148 {
2150 GetGame().ObjectDelete(construction_kit);
2151 }
2152
2153 //--- CONSTRUCTION
2154 void DestroyConstruction()
2155 {
2156 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2157 GetGame().ObjectDelete(this);
2158 }
2159
2160 // --- EVENTS
2161 override void OnStoreSave(ParamsWriteContext ctx)
2162 {
2163 super.OnStoreSave(ctx);
2164
2165 //sync parts 01
2166 ctx.Write(m_SyncParts01);
2167 ctx.Write(m_SyncParts02);
2168 ctx.Write(m_SyncParts03);
2169
2170 ctx.Write(m_HasBase);
2171 }
2172
2173 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2174 {
2175 if (!super.OnStoreLoad(ctx, version))
2176 return false;
2177
2178 //--- Base building data ---
2179 //Restore synced parts data
2180 if (!ctx.Read(m_SyncParts01))
2181 {
2182 m_SyncParts01 = 0; //set default
2183 return false;
2184 }
2185 if (!ctx.Read(m_SyncParts02))
2186 {
2187 m_SyncParts02 = 0; //set default
2188 return false;
2189 }
2190 if (!ctx.Read(m_SyncParts03))
2191 {
2192 m_SyncParts03 = 0; //set default
2193 return false;
2194 }
2195
2196 //has base
2197 if (!ctx.Read(m_HasBase))
2198 {
2199 m_HasBase = false;
2200 return false;
2201 }
2202 //---
2203
2204 return true;
2205 }
2206
2207 override void AfterStoreLoad()
2208 {
2209 super.AfterStoreLoad();
2210
2213 }
2214
2216 {
2217 //update server data
2219
2220 //set base state
2221 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2222 SetBaseState(construction_part.IsBuilt()) ;
2223
2224 //synchronize after load
2226 }
2227
2228 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2229 {
2231 return;
2232
2233 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2234
2235 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2236 return;
2237
2239 string part_name = zone;
2240 part_name.ToLower();
2241
2243 {
2245
2246 if (construction_part && construction.IsPartConstructed(part_name))
2247 {
2248 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2249 construction.DestroyConnectedParts(part_name);
2250 }
2251
2252 //barbed wire handling (hack-ish)
2253 if (part_name.Contains("barbed"))
2254 {
2255 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2256 if (barbed_wire)
2257 barbed_wire.SetMountedState(false);
2258 }
2259 }
2260 }
2261
2262 override void EEOnAfterLoad()
2263 {
2265 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2266
2267 super.EEOnAfterLoad();
2268 }
2269
2270 override void EEInit()
2271 {
2272 super.EEInit();
2273
2274 // init visuals and physics
2275 InitBaseState();
2276
2277 //debug
2278#ifdef DEVELOPER
2280#endif
2281 }
2282
2283 override void EEItemAttached(EntityAI item, string slot_name)
2284 {
2285 super.EEItemAttached(item, slot_name);
2286
2288 UpdateVisuals();
2290 }
2291
2292 override void EEItemDetached(EntityAI item, string slot_name)
2293 {
2294 super.EEItemDetached(item, slot_name);
2295
2296 UpdateVisuals();
2298 }
2299
2300 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2301 {
2303 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2304
2307 }
2308
2309 //ignore out of reach condition
2310 override bool IgnoreOutOfReachCondition()
2311 {
2312 return true;
2313 }
2314
2315 //CONSTRUCTION EVENTS
2316 //Build
2317 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2318 {
2320
2321 //check base state
2322 if (construtionPart.IsBase())
2323 {
2324 SetBaseState(true);
2325
2326 //spawn kit
2328 }
2329
2330 //register constructed parts for synchronization
2332
2333 //register action that was performed on part
2335
2336 //synchronize
2338
2339 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2340
2341 UpdateNavmesh();
2342
2343 //update visuals
2344 UpdateVisuals();
2345
2346 //reset action sync data
2347 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2348 }
2349
2350 void OnPartBuiltClient(string part_name, int action_id)
2351 {
2352 //play sound
2354 }
2355
2356 //Dismantle
2358 {
2359 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2361
2362 //register constructed parts for synchronization
2364
2365 //register action that was performed on part
2367
2368 //synchronize
2370
2371 // server part of sync, client will be synced from SetPartsFromSyncData
2373
2374 UpdateNavmesh();
2375
2376 //update visuals
2377 UpdateVisuals();
2378
2379 //reset action sync data
2380 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2381
2382 //check base state
2383 if (construtionPart.IsBase())
2384 {
2385 //Destroy construction
2386 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2387 }
2388 }
2389
2391 {
2392 //play sound
2394 }
2395
2396 //Destroy
2398 {
2399 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2401
2402 //register constructed parts for synchronization
2404
2405 //register action that was performed on part
2407
2408 //synchronize
2410
2411 // server part of sync, client will be synced from SetPartsFromSyncData
2413
2414 UpdateNavmesh();
2415
2416 //update visuals
2417 UpdateVisuals();
2418
2419 //reset action sync data
2420 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2421
2422 //check base state
2423 if (construtionPart.IsBase())
2424 {
2425 //Destroy construction
2426 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2427 }
2428 }
2429
2430 void OnPartDestroyedClient(string part_name, int action_id)
2431 {
2432 //play sound
2434 }
2435
2436 // --- UPDATE
2437 void InitBaseState()
2438 {
2439 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2440
2441 InitVisuals();
2442 UpdateNavmesh(); //regenerate navmesh
2443 GetConstruction().InitBaseState();
2444 }
2445
2446 void InitVisuals()
2447 {
2448 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2449 //check base
2450 if (!HasBase())
2451 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2452 else
2453 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2454
2455 GetConstruction().UpdateVisuals();
2456 }
2457
2458 void UpdateVisuals()
2459 {
2461
2463 foreach (string slotName : attachmentSlots)
2465
2466 //check base
2467 if (!HasBase())
2468 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2469 else
2470 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2471
2472 GetConstruction().UpdateVisuals();
2473 }
2474
2476 {
2477 string slotNameMounted = slot_name + "_Mounted";
2478 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2479
2480 if (attachment)
2481 {
2482 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2483 if (barbedWire && barbedWire.IsMounted())
2485 else
2487
2488 if (is_locked)
2489 {
2490 SetAnimationPhase(slotNameMounted, 0);
2491 SetAnimationPhase(slot_name, 1);
2492 }
2493 else
2494 {
2495 SetAnimationPhase(slotNameMounted, 1);
2496 SetAnimationPhase(slot_name, 0);
2497 }
2498 }
2499 else
2500 {
2501 SetAnimationPhase(slotNameMounted, 1);
2502 SetAnimationPhase(slot_name, 1);
2503
2505 }
2506 }
2507
2508 // avoid calling this function on frequent occasions, it's a massive performance hit
2509 void UpdatePhysics()
2510 {
2512 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2513
2516
2518 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2519
2520 foreach (string slotName : attachmentSlots)
2522
2523 //check base
2524 if (!HasBase())
2525 {
2527 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2528
2529 AddProxyPhysics(ANIMATION_DEPLOYED);
2530 }
2531 else
2532 {
2534 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2535
2536 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2537 }
2538
2539 GetConstruction().UpdatePhysics();
2540 UpdateNavmesh();
2541 }
2542
2544 {
2545 //checks for invalid appends; hotfix
2546 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2547 return;
2548 //----------------------------------
2549 string slot_name_mounted = slot_name + "_Mounted";
2550 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2551
2552 //remove proxy physics
2553 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2554 RemoveProxyPhysics(slot_name_mounted);
2555 RemoveProxyPhysics(slot_name);
2556
2557 if (attachment)
2558 {
2559 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2560 if (is_locked)
2561 {
2562 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2563 AddProxyPhysics(slot_name_mounted);
2564 }
2565 else
2566 {
2567 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2568 AddProxyPhysics(slot_name);
2569 }
2570 }
2571 }
2572
2573 protected void UpdateNavmesh()
2574 {
2575 SetAffectPathgraph(true, false);
2576 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2577 }
2578
2579 override bool CanUseConstruction()
2580 {
2581 return true;
2582 }
2583
2584 override bool CanUseConstructionBuild()
2585 {
2586 return true;
2587 }
2588
2590 {
2591 if (attachment)
2592 {
2594 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2595
2596 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2597 }
2598
2599 return false;
2600 }
2601
2602 protected bool IsAttachmentSlotLocked(string slot_name)
2603 {
2604 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2605 }
2606
2607 //--- ATTACHMENT SLOTS
2609 {
2610 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2611 if (GetGame().ConfigIsExisting(config_path))
2612 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2613 }
2614
2616 {
2617 return true;
2618 }
2619
2620 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2621 {
2622 return true;
2623 }
2624
2625 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2626 {
2627 return true;
2628 }
2629
2630 // --- INIT
2631 void ConstructionInit()
2632 {
2633 if (!m_Construction)
2634 m_Construction = new Construction(this);
2635
2636 GetConstruction().Init();
2637 }
2638
2640 {
2641 return m_Construction;
2642 }
2643
2644 //--- INVENTORY/ATTACHMENTS CONDITIONS
2645 //attachments
2647 {
2648 return super.CanReceiveAttachment(attachment, slotId);
2649 }
2650
2652 {
2653 int attachment_count = GetInventory().AttachmentCount();
2654 if (attachment_count > 0)
2655 {
2656 if (HasBase() && attachment_count == 1)
2657 return false;
2658
2659 return true;
2660 }
2661
2662 return false;
2663 }
2664
2665 override bool ShowZonesHealth()
2666 {
2667 return true;
2668 }
2669
2670 //this into/outo parent.Cargo
2671 override bool CanPutInCargo(EntityAI parent)
2672 {
2673 return false;
2674 }
2675
2676 override bool CanRemoveFromCargo(EntityAI parent)
2677 {
2678 return false;
2679 }
2680
2681 //hands
2682 override bool CanPutIntoHands(EntityAI parent)
2683 {
2684 return false;
2685 }
2686
2687 //--- ACTION CONDITIONS
2688 //direction
2689 override bool IsFacingPlayer(PlayerBase player, string selection)
2690 {
2691 return true;
2692 }
2693
2694 override bool IsPlayerInside(PlayerBase player, string selection)
2695 {
2696 return true;
2697 }
2698
2701 {
2702 return false;
2703 }
2704
2705 //camera direction check
2706 bool IsFacingCamera(string selection)
2707 {
2708 return true;
2709 }
2710
2711 //roof check
2713 {
2714 return false;
2715 }
2716
2717 //selection->player distance check
2718 bool HasProperDistance(string selection, PlayerBase player)
2719 {
2720 return true;
2721 }
2722
2723 //folding
2725 {
2726 if (HasBase() || GetInventory().AttachmentCount() > 0)
2727 return false;
2728
2729 return true;
2730 }
2731
2733 {
2736
2737 return item;
2738 }
2739
2740 //Damage triggers (barbed wire)
2741 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2742 {
2743 if (GetGame() && GetGame().IsServer())
2744 {
2745 //destroy area damage if some already exists
2747
2748 //create new area damage
2750 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2751
2752 vector min_max[2];
2753 if (MemoryPointExists(slot_name + "_min"))
2754 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2755 if (MemoryPointExists(slot_name + "_max"))
2756 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2757
2758 //get proper trigger extents (min<max)
2759 vector extents[2];
2760 GetConstruction().GetTriggerExtents(min_max, extents);
2761
2762 //get box center
2763 vector center;
2764 center = GetConstruction().GetBoxCenter(min_max);
2765 center = ModelToWorld(center);
2766
2767 //rotate center if needed
2770
2771 areaDamage.SetExtents(extents[0], extents[1]);
2772 areaDamage.SetAreaPosition(center);
2773 areaDamage.SetAreaOrientation(orientation);
2774 areaDamage.SetLoopInterval(1.0);
2775 areaDamage.SetDeferDuration(0.2);
2776 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2777 areaDamage.SetAmmoName("BarbedWireHit");
2778 areaDamage.Spawn();
2779
2781 }
2782 }
2783
2785 {
2786 if (angle_deg != 0)
2787 {
2788 //orientation
2790
2791 //center
2793 if (MemoryPointExists("rotate_axis"))
2794 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2797 center[0] = r_center_x;
2798 center[2] = r_center_z;
2799 }
2800 }
2801
2802 void DestroyAreaDamage(string slot_name)
2803 {
2804 if (GetGame() && GetGame().IsServer())
2805 {
2808 {
2809 if (areaDamage)
2810 areaDamage.Destroy();
2811
2813 }
2814 }
2815 }
2816
2817 override bool IsIgnoredByConstruction()
2818 {
2819 return true;
2820 }
2821
2822 //================================================================
2823 // SOUNDS
2824 //================================================================
2825 protected void SoundBuildStart(string part_name)
2826 {
2827 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2828 }
2829
2830 protected void SoundDismantleStart(string part_name)
2831 {
2832 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2833 }
2834
2835 protected void SoundDestroyStart(string part_name)
2836 {
2837 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2838 }
2839
2840 protected string GetBuildSoundByMaterial(string part_name)
2841 {
2843
2844 switch (material_type)
2845 {
2846 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2847 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2848 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2849 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2850 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2851 }
2852
2853 return "";
2854 }
2855
2856 protected string GetDismantleSoundByMaterial(string part_name)
2857 {
2859
2860 switch (material_type)
2861 {
2862 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2863 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2864 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2865 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2866 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2867 }
2868
2869 return "";
2870 }
2871
2872 //misc
2874 {
2875 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2876 {
2877 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2879 SetHealth(slot_name, "Health", item.GetHealth());
2880 }
2881 }
2882
2883 override int GetDamageSystemVersionChange()
2884 {
2885 return 111;
2886 }
2887
2888 override void SetActions()
2889 {
2890 super.SetActions();
2891
2893 //AddAction(ActionTakeHybridAttachment);
2894 //AddAction(ActionTakeHybridAttachmentToHands);
2897 }
2898
2899 //================================================================
2900 // DEBUG
2901 //================================================================
2902 protected void DebugCustomState()
2903 {
2904 }
2905
2908 {
2909 return null;
2910 }
2911
2912 override void OnDebugSpawn()
2913 {
2914 FullyBuild();
2915 }
2916
2917 void FullyBuild()
2918 {
2920 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2921
2922 Man p;
2923
2924#ifdef SERVER
2926 GetGame().GetWorld().GetPlayerList(players);
2927 if (players.Count())
2928 p = players[0];
2929#else
2930 p = GetGame().GetPlayer();
2931#endif
2932
2933 foreach (ConstructionPart part : parts)
2934 {
2935 bool excluded = false;
2936 string partName = part.GetPartName();
2937 if (excludes)
2938 {
2939 foreach (string exclude : excludes)
2940 {
2941 if (partName.Contains(exclude))
2942 {
2943 excluded = true;
2944 break;
2945 }
2946 }
2947 }
2948
2949 if (!excluded)
2951 }
2952
2953 GetConstruction().UpdateVisuals();
2954 }
2955}
2956
2957void bsbDebugPrint(string s)
2958{
2959#ifdef BSB_DEBUG
2960 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2961#else
2962 //Print("" + s); // comment/uncomment to hide/see debug logs
2963#endif
2964}
2965void bsbDebugSpam(string s)
2966{
2967#ifdef BSB_DEBUG_SPAM
2968 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2969#else
2970 //Print("" + s); // comment/uncomment to hide/see debug logs
2971#endif
2972}

Referenced by ItemBase::SetActionFromSyncData().

◆ OnPartDismantledServer()

void bsbDebugPrint::OnPartDismantledServer ( notnull Man player,
string part_name,
int action_id )
protected

Definition at line 1749 of file BaseBuildingBase.c.

1751{
1752 const string ANIMATION_DEPLOYED = "Deployed";
1753
1754 float m_ConstructionKitHealth; //stored health value for used construction kit
1755
1757
1758 bool m_HasBase;
1759 //variables for synchronization of base building parts (2x31 is the current limit)
1760 int m_SyncParts01; //synchronization for already built parts (31 parts)
1761 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1762 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1763 int m_InteractedPartId; //construction part id that an action was performed on
1764 int m_PerformedActionId; //action id that was performed on a construction part
1765
1766 //Sounds
1767 //build
1768 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1769 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1770 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1771 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1772 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1773 //dismantle
1774 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1775 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1776 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1777 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1778 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1779
1780 protected EffectSound m_Sound;
1781
1785
1786 // Constructor
1787 void BaseBuildingBase()
1788 {
1790
1791 //synchronized variables
1792 RegisterNetSyncVariableInt("m_SyncParts01");
1793 RegisterNetSyncVariableInt("m_SyncParts02");
1794 RegisterNetSyncVariableInt("m_SyncParts03");
1795 RegisterNetSyncVariableInt("m_InteractedPartId");
1796 RegisterNetSyncVariableInt("m_PerformedActionId");
1797 RegisterNetSyncVariableBool("m_HasBase");
1798
1799 //Construction init
1801
1802 if (ConfigIsExisting("hybridAttachments"))
1803 {
1805 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1806 }
1807 if (ConfigIsExisting("mountables"))
1808 {
1810 ConfigGetTextArray("mountables", m_Mountables);
1811 }
1812
1813 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1814 }
1815
1816 override void EEDelete(EntityAI parent)
1817 {
1818 super.EEDelete(parent);
1819
1820 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1822
1823 }
1824
1825 override string GetInvulnerabilityTypeString()
1826 {
1827 return "disableBaseDamage";
1828 }
1829
1830 override bool CanObstruct()
1831 {
1832 return true;
1833 }
1834
1835 override int GetHideIconMask()
1836 {
1837 return EInventoryIconVisibility.HIDE_VICINITY;
1838 }
1839
1840 // --- SYNCHRONIZATION
1842 {
1843 if (GetGame().IsServer())
1844 SetSynchDirty();
1845 }
1846
1847 override void OnVariablesSynchronized()
1848 {
1849 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1850 super.OnVariablesSynchronized();
1851
1852 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1853 }
1854
1855 protected void OnSynchronizedClient()
1856 {
1857 //update parts
1859
1860 //update action on part
1862
1863 //update visuals (client)
1864 UpdateVisuals();
1865 }
1866
1867 //parts synchronization
1869 {
1870 //part_id must starts from index = 1
1871 int offset;
1872 int mask;
1873
1874 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1875 {
1876 offset = part_id - 1;
1877 mask = 1 << offset;
1878
1880 }
1881 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1882 {
1883 offset = (part_id % 32);
1884 mask = 1 << offset;
1885
1887 }
1888 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1889 {
1890 offset = (part_id % 63);
1891 mask = 1 << offset;
1892
1894 }
1895 }
1896
1898 {
1899 //part_id must starts from index = 1
1900 int offset;
1901 int mask;
1902
1903 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1904 {
1905 offset = part_id - 1;
1906 mask = 1 << offset;
1907
1909 }
1910 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1911 {
1912 offset = (part_id % 32);
1913 mask = 1 << offset;
1914
1916 }
1917 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1918 {
1919 offset = (part_id % 63);
1920 mask = 1 << offset;
1921
1923 }
1924 }
1925
1927 {
1928 //part_id must starts from index = 1
1929 int offset;
1930 int mask;
1931
1932 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1933 {
1934 offset = part_id - 1;
1935 mask = 1 << offset;
1936
1937 if ((m_SyncParts01 & mask) > 0)
1938 return true;
1939 }
1940 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1941 {
1942 offset = (part_id % 32);
1943 mask = 1 << offset;
1944
1945 if ((m_SyncParts02 & mask) > 0)
1946 return true;
1947 }
1948 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1949 {
1950 offset = (part_id % 63);
1951 mask = 1 << offset;
1952
1953 if ((m_SyncParts03 & mask) > 0)
1954 return true;
1955 }
1956
1957 return false;
1958 }
1959
1960 protected void RegisterActionForSync(int part_id, int action_id)
1961 {
1964 }
1965
1966 protected void ResetActionSyncData()
1967 {
1968 //reset data
1969 m_InteractedPartId = -1;
1971 }
1972
1973 protected void SetActionFromSyncData()
1974 {
1975 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1976 {
1979
1980 switch (build_action_id)
1981 {
1985 }
1986 }
1987 }
1988 //------
1989
1991 {
1992 string key = part.m_PartName;
1993 bool is_base = part.IsBase();
1995 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1997 {
1998 if (!part.IsBuilt())
1999 {
2000 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2001 GetConstruction().AddToConstructedParts(key);
2002 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2003
2004 if (is_base)
2005 {
2007 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2008 }
2009 }
2010 }
2011 else
2012 {
2013 if (part.IsBuilt())
2014 {
2015 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2016 GetConstruction().RemoveFromConstructedParts(key);
2017 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2018
2019 if (is_base)
2020 {
2022 AddProxyPhysics(ANIMATION_DEPLOYED);
2023 }
2024 }
2025 }
2026
2027 //check slot lock for material attachments
2028 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2029 }
2030
2031 //set construction parts based on synchronized data
2033 {
2036
2037 for (int i = 0; i < construction_parts.Count(); ++i)
2038 {
2039 string key = construction_parts.GetKey(i);
2042 }
2043
2044 //regenerate navmesh
2045 UpdateNavmesh();
2046 }
2047
2049 {
2052
2053 for (int i = 0; i < construction_parts.Count(); ++i)
2054 {
2055 string key = construction_parts.GetKey(i);
2057
2058 if (value.GetId() == id)
2059 return value;
2060 }
2061
2062 return NULL;
2063 }
2064 //
2065
2066 //Base
2067 bool HasBase()
2068 {
2069 return m_HasBase;
2070 }
2071
2072 void SetBaseState(bool has_base)
2073 {
2075 }
2076
2077 override bool IsDeployable()
2078 {
2079 return true;
2080 }
2081
2082 bool IsOpened()
2083 {
2084 return false;
2085 }
2086
2087 //--- CONSTRUCTION KIT
2089 {
2093
2094 return construction_kit;
2095 }
2096
2098 {
2099 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2102 }
2103
2104 protected vector GetKitSpawnPosition()
2105 {
2106 return GetPosition();
2107 }
2108
2109 protected string GetConstructionKitType()
2110 {
2111 return "";
2112 }
2113
2115 {
2117 GetGame().ObjectDelete(construction_kit);
2118 }
2119
2120 //--- CONSTRUCTION
2121 void DestroyConstruction()
2122 {
2123 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2124 GetGame().ObjectDelete(this);
2125 }
2126
2127 // --- EVENTS
2128 override void OnStoreSave(ParamsWriteContext ctx)
2129 {
2130 super.OnStoreSave(ctx);
2131
2132 //sync parts 01
2133 ctx.Write(m_SyncParts01);
2134 ctx.Write(m_SyncParts02);
2135 ctx.Write(m_SyncParts03);
2136
2137 ctx.Write(m_HasBase);
2138 }
2139
2140 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2141 {
2142 if (!super.OnStoreLoad(ctx, version))
2143 return false;
2144
2145 //--- Base building data ---
2146 //Restore synced parts data
2147 if (!ctx.Read(m_SyncParts01))
2148 {
2149 m_SyncParts01 = 0; //set default
2150 return false;
2151 }
2152 if (!ctx.Read(m_SyncParts02))
2153 {
2154 m_SyncParts02 = 0; //set default
2155 return false;
2156 }
2157 if (!ctx.Read(m_SyncParts03))
2158 {
2159 m_SyncParts03 = 0; //set default
2160 return false;
2161 }
2162
2163 //has base
2164 if (!ctx.Read(m_HasBase))
2165 {
2166 m_HasBase = false;
2167 return false;
2168 }
2169 //---
2170
2171 return true;
2172 }
2173
2174 override void AfterStoreLoad()
2175 {
2176 super.AfterStoreLoad();
2177
2180 }
2181
2183 {
2184 //update server data
2186
2187 //set base state
2188 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2189 SetBaseState(construction_part.IsBuilt()) ;
2190
2191 //synchronize after load
2193 }
2194
2195 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2196 {
2198 return;
2199
2200 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2201
2202 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2203 return;
2204
2206 string part_name = zone;
2207 part_name.ToLower();
2208
2210 {
2212
2213 if (construction_part && construction.IsPartConstructed(part_name))
2214 {
2215 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2216 construction.DestroyConnectedParts(part_name);
2217 }
2218
2219 //barbed wire handling (hack-ish)
2220 if (part_name.Contains("barbed"))
2221 {
2222 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2223 if (barbed_wire)
2224 barbed_wire.SetMountedState(false);
2225 }
2226 }
2227 }
2228
2229 override void EEOnAfterLoad()
2230 {
2232 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2233
2234 super.EEOnAfterLoad();
2235 }
2236
2237 override void EEInit()
2238 {
2239 super.EEInit();
2240
2241 // init visuals and physics
2242 InitBaseState();
2243
2244 //debug
2245#ifdef DEVELOPER
2247#endif
2248 }
2249
2250 override void EEItemAttached(EntityAI item, string slot_name)
2251 {
2252 super.EEItemAttached(item, slot_name);
2253
2255 UpdateVisuals();
2257 }
2258
2259 override void EEItemDetached(EntityAI item, string slot_name)
2260 {
2261 super.EEItemDetached(item, slot_name);
2262
2263 UpdateVisuals();
2265 }
2266
2267 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2268 {
2270 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2271
2274 }
2275
2276 //ignore out of reach condition
2277 override bool IgnoreOutOfReachCondition()
2278 {
2279 return true;
2280 }
2281
2282 //CONSTRUCTION EVENTS
2283 //Build
2284 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2285 {
2287
2288 //check base state
2289 if (construtionPart.IsBase())
2290 {
2291 SetBaseState(true);
2292
2293 //spawn kit
2295 }
2296
2297 //register constructed parts for synchronization
2299
2300 //register action that was performed on part
2302
2303 //synchronize
2305
2306 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2307
2308 UpdateNavmesh();
2309
2310 //update visuals
2311 UpdateVisuals();
2312
2313 //reset action sync data
2314 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2315 }
2316
2317 void OnPartBuiltClient(string part_name, int action_id)
2318 {
2319 //play sound
2321 }
2322
2323 //Dismantle
2325 {
2326 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2328
2329 //register constructed parts for synchronization
2331
2332 //register action that was performed on part
2334
2335 //synchronize
2337
2338 // server part of sync, client will be synced from SetPartsFromSyncData
2340
2341 UpdateNavmesh();
2342
2343 //update visuals
2344 UpdateVisuals();
2345
2346 //reset action sync data
2347 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2348
2349 //check base state
2350 if (construtionPart.IsBase())
2351 {
2352 //Destroy construction
2353 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2354 }
2355 }
2356
2358 {
2359 //play sound
2361 }
2362
2363 //Destroy
2365 {
2366 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2368
2369 //register constructed parts for synchronization
2371
2372 //register action that was performed on part
2374
2375 //synchronize
2377
2378 // server part of sync, client will be synced from SetPartsFromSyncData
2380
2381 UpdateNavmesh();
2382
2383 //update visuals
2384 UpdateVisuals();
2385
2386 //reset action sync data
2387 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2388
2389 //check base state
2390 if (construtionPart.IsBase())
2391 {
2392 //Destroy construction
2393 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2394 }
2395 }
2396
2397 void OnPartDestroyedClient(string part_name, int action_id)
2398 {
2399 //play sound
2401 }
2402
2403 // --- UPDATE
2404 void InitBaseState()
2405 {
2406 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2407
2408 InitVisuals();
2409 UpdateNavmesh(); //regenerate navmesh
2410 GetConstruction().InitBaseState();
2411 }
2412
2413 void InitVisuals()
2414 {
2415 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2416 //check base
2417 if (!HasBase())
2418 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2419 else
2420 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2421
2422 GetConstruction().UpdateVisuals();
2423 }
2424
2425 void UpdateVisuals()
2426 {
2428
2430 foreach (string slotName : attachmentSlots)
2432
2433 //check base
2434 if (!HasBase())
2435 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2436 else
2437 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2438
2439 GetConstruction().UpdateVisuals();
2440 }
2441
2443 {
2444 string slotNameMounted = slot_name + "_Mounted";
2445 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2446
2447 if (attachment)
2448 {
2449 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2450 if (barbedWire && barbedWire.IsMounted())
2452 else
2454
2455 if (is_locked)
2456 {
2457 SetAnimationPhase(slotNameMounted, 0);
2458 SetAnimationPhase(slot_name, 1);
2459 }
2460 else
2461 {
2462 SetAnimationPhase(slotNameMounted, 1);
2463 SetAnimationPhase(slot_name, 0);
2464 }
2465 }
2466 else
2467 {
2468 SetAnimationPhase(slotNameMounted, 1);
2469 SetAnimationPhase(slot_name, 1);
2470
2472 }
2473 }
2474
2475 // avoid calling this function on frequent occasions, it's a massive performance hit
2476 void UpdatePhysics()
2477 {
2479 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2480
2483
2485 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2486
2487 foreach (string slotName : attachmentSlots)
2489
2490 //check base
2491 if (!HasBase())
2492 {
2494 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2495
2496 AddProxyPhysics(ANIMATION_DEPLOYED);
2497 }
2498 else
2499 {
2501 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2502
2503 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2504 }
2505
2506 GetConstruction().UpdatePhysics();
2507 UpdateNavmesh();
2508 }
2509
2511 {
2512 //checks for invalid appends; hotfix
2513 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2514 return;
2515 //----------------------------------
2516 string slot_name_mounted = slot_name + "_Mounted";
2517 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2518
2519 //remove proxy physics
2520 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2521 RemoveProxyPhysics(slot_name_mounted);
2522 RemoveProxyPhysics(slot_name);
2523
2524 if (attachment)
2525 {
2526 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2527 if (is_locked)
2528 {
2529 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2530 AddProxyPhysics(slot_name_mounted);
2531 }
2532 else
2533 {
2534 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2535 AddProxyPhysics(slot_name);
2536 }
2537 }
2538 }
2539
2540 protected void UpdateNavmesh()
2541 {
2542 SetAffectPathgraph(true, false);
2543 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2544 }
2545
2546 override bool CanUseConstruction()
2547 {
2548 return true;
2549 }
2550
2551 override bool CanUseConstructionBuild()
2552 {
2553 return true;
2554 }
2555
2557 {
2558 if (attachment)
2559 {
2561 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2562
2563 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2564 }
2565
2566 return false;
2567 }
2568
2569 protected bool IsAttachmentSlotLocked(string slot_name)
2570 {
2571 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2572 }
2573
2574 //--- ATTACHMENT SLOTS
2576 {
2577 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2578 if (GetGame().ConfigIsExisting(config_path))
2579 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2580 }
2581
2583 {
2584 return true;
2585 }
2586
2587 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2588 {
2589 return true;
2590 }
2591
2592 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2593 {
2594 return true;
2595 }
2596
2597 // --- INIT
2598 void ConstructionInit()
2599 {
2600 if (!m_Construction)
2601 m_Construction = new Construction(this);
2602
2603 GetConstruction().Init();
2604 }
2605
2607 {
2608 return m_Construction;
2609 }
2610
2611 //--- INVENTORY/ATTACHMENTS CONDITIONS
2612 //attachments
2614 {
2615 return super.CanReceiveAttachment(attachment, slotId);
2616 }
2617
2619 {
2620 int attachment_count = GetInventory().AttachmentCount();
2621 if (attachment_count > 0)
2622 {
2623 if (HasBase() && attachment_count == 1)
2624 return false;
2625
2626 return true;
2627 }
2628
2629 return false;
2630 }
2631
2632 override bool ShowZonesHealth()
2633 {
2634 return true;
2635 }
2636
2637 //this into/outo parent.Cargo
2638 override bool CanPutInCargo(EntityAI parent)
2639 {
2640 return false;
2641 }
2642
2643 override bool CanRemoveFromCargo(EntityAI parent)
2644 {
2645 return false;
2646 }
2647
2648 //hands
2649 override bool CanPutIntoHands(EntityAI parent)
2650 {
2651 return false;
2652 }
2653
2654 //--- ACTION CONDITIONS
2655 //direction
2656 override bool IsFacingPlayer(PlayerBase player, string selection)
2657 {
2658 return true;
2659 }
2660
2661 override bool IsPlayerInside(PlayerBase player, string selection)
2662 {
2663 return true;
2664 }
2665
2668 {
2669 return false;
2670 }
2671
2672 //camera direction check
2673 bool IsFacingCamera(string selection)
2674 {
2675 return true;
2676 }
2677
2678 //roof check
2680 {
2681 return false;
2682 }
2683
2684 //selection->player distance check
2685 bool HasProperDistance(string selection, PlayerBase player)
2686 {
2687 return true;
2688 }
2689
2690 //folding
2692 {
2693 if (HasBase() || GetInventory().AttachmentCount() > 0)
2694 return false;
2695
2696 return true;
2697 }
2698
2700 {
2703
2704 return item;
2705 }
2706
2707 //Damage triggers (barbed wire)
2708 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2709 {
2710 if (GetGame() && GetGame().IsServer())
2711 {
2712 //destroy area damage if some already exists
2714
2715 //create new area damage
2717 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2718
2719 vector min_max[2];
2720 if (MemoryPointExists(slot_name + "_min"))
2721 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2722 if (MemoryPointExists(slot_name + "_max"))
2723 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2724
2725 //get proper trigger extents (min<max)
2726 vector extents[2];
2727 GetConstruction().GetTriggerExtents(min_max, extents);
2728
2729 //get box center
2730 vector center;
2731 center = GetConstruction().GetBoxCenter(min_max);
2732 center = ModelToWorld(center);
2733
2734 //rotate center if needed
2737
2738 areaDamage.SetExtents(extents[0], extents[1]);
2739 areaDamage.SetAreaPosition(center);
2740 areaDamage.SetAreaOrientation(orientation);
2741 areaDamage.SetLoopInterval(1.0);
2742 areaDamage.SetDeferDuration(0.2);
2743 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2744 areaDamage.SetAmmoName("BarbedWireHit");
2745 areaDamage.Spawn();
2746
2748 }
2749 }
2750
2752 {
2753 if (angle_deg != 0)
2754 {
2755 //orientation
2757
2758 //center
2760 if (MemoryPointExists("rotate_axis"))
2761 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2764 center[0] = r_center_x;
2765 center[2] = r_center_z;
2766 }
2767 }
2768
2769 void DestroyAreaDamage(string slot_name)
2770 {
2771 if (GetGame() && GetGame().IsServer())
2772 {
2775 {
2776 if (areaDamage)
2777 areaDamage.Destroy();
2778
2780 }
2781 }
2782 }
2783
2784 override bool IsIgnoredByConstruction()
2785 {
2786 return true;
2787 }
2788
2789 //================================================================
2790 // SOUNDS
2791 //================================================================
2792 protected void SoundBuildStart(string part_name)
2793 {
2794 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2795 }
2796
2797 protected void SoundDismantleStart(string part_name)
2798 {
2799 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2800 }
2801
2802 protected void SoundDestroyStart(string part_name)
2803 {
2804 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2805 }
2806
2807 protected string GetBuildSoundByMaterial(string part_name)
2808 {
2810
2811 switch (material_type)
2812 {
2813 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2814 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2815 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2816 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2817 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2818 }
2819
2820 return "";
2821 }
2822
2823 protected string GetDismantleSoundByMaterial(string part_name)
2824 {
2826
2827 switch (material_type)
2828 {
2829 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2830 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2831 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2832 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2833 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2834 }
2835
2836 return "";
2837 }
2838
2839 //misc
2841 {
2842 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2843 {
2844 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2846 SetHealth(slot_name, "Health", item.GetHealth());
2847 }
2848 }
2849
2850 override int GetDamageSystemVersionChange()
2851 {
2852 return 111;
2853 }
2854
2855 override void SetActions()
2856 {
2857 super.SetActions();
2858
2860 //AddAction(ActionTakeHybridAttachment);
2861 //AddAction(ActionTakeHybridAttachmentToHands);
2864 }
2865
2866 //================================================================
2867 // DEBUG
2868 //================================================================
2869 protected void DebugCustomState()
2870 {
2871 }
2872
2875 {
2876 return null;
2877 }
2878
2879 override void OnDebugSpawn()
2880 {
2881 FullyBuild();
2882 }
2883
2884 void FullyBuild()
2885 {
2887 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2888
2889 Man p;
2890
2891#ifdef SERVER
2893 GetGame().GetWorld().GetPlayerList(players);
2894 if (players.Count())
2895 p = players[0];
2896#else
2897 p = GetGame().GetPlayer();
2898#endif
2899
2900 foreach (ConstructionPart part : parts)
2901 {
2902 bool excluded = false;
2903 string partName = part.GetPartName();
2904 if (excludes)
2905 {
2906 foreach (string exclude : excludes)
2907 {
2908 if (partName.Contains(exclude))
2909 {
2910 excluded = true;
2911 break;
2912 }
2913 }
2914 }
2915
2916 if (!excluded)
2918 }
2919
2920 GetConstruction().UpdateVisuals();
2921 }
2922}
2923
2924void bsbDebugPrint(string s)
2925{
2926#ifdef BSB_DEBUG
2927 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2928#else
2929 //Print("" + s); // comment/uncomment to hide/see debug logs
2930#endif
2931}
2932void bsbDebugSpam(string s)
2933{
2934#ifdef BSB_DEBUG_SPAM
2935 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2936#else
2937 //Print("" + s); // comment/uncomment to hide/see debug logs
2938#endif
2939}

◆ OnSetSlotLock()

void bsbDebugPrint::OnSetSlotLock ( int slotId,
bool locked,
bool was_locked )
protected

Definition at line 1692 of file BaseBuildingBase.c.

1694{
1695 const string ANIMATION_DEPLOYED = "Deployed";
1696
1697 float m_ConstructionKitHealth; //stored health value for used construction kit
1698
1700
1701 bool m_HasBase;
1702 //variables for synchronization of base building parts (2x31 is the current limit)
1703 int m_SyncParts01; //synchronization for already built parts (31 parts)
1704 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1705 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1706 int m_InteractedPartId; //construction part id that an action was performed on
1707 int m_PerformedActionId; //action id that was performed on a construction part
1708
1709 //Sounds
1710 //build
1711 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1712 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1713 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1714 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1715 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1716 //dismantle
1717 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1718 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1719 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1720 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1721 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1722
1723 protected EffectSound m_Sound;
1724
1728
1729 // Constructor
1730 void BaseBuildingBase()
1731 {
1733
1734 //synchronized variables
1735 RegisterNetSyncVariableInt("m_SyncParts01");
1736 RegisterNetSyncVariableInt("m_SyncParts02");
1737 RegisterNetSyncVariableInt("m_SyncParts03");
1738 RegisterNetSyncVariableInt("m_InteractedPartId");
1739 RegisterNetSyncVariableInt("m_PerformedActionId");
1740 RegisterNetSyncVariableBool("m_HasBase");
1741
1742 //Construction init
1744
1745 if (ConfigIsExisting("hybridAttachments"))
1746 {
1748 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1749 }
1750 if (ConfigIsExisting("mountables"))
1751 {
1753 ConfigGetTextArray("mountables", m_Mountables);
1754 }
1755
1756 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1757 }
1758
1759 override void EEDelete(EntityAI parent)
1760 {
1761 super.EEDelete(parent);
1762
1763 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1765
1766 }
1767
1768 override string GetInvulnerabilityTypeString()
1769 {
1770 return "disableBaseDamage";
1771 }
1772
1773 override bool CanObstruct()
1774 {
1775 return true;
1776 }
1777
1778 override int GetHideIconMask()
1779 {
1780 return EInventoryIconVisibility.HIDE_VICINITY;
1781 }
1782
1783 // --- SYNCHRONIZATION
1785 {
1786 if (GetGame().IsServer())
1787 SetSynchDirty();
1788 }
1789
1790 override void OnVariablesSynchronized()
1791 {
1792 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1793 super.OnVariablesSynchronized();
1794
1795 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1796 }
1797
1798 protected void OnSynchronizedClient()
1799 {
1800 //update parts
1802
1803 //update action on part
1805
1806 //update visuals (client)
1807 UpdateVisuals();
1808 }
1809
1810 //parts synchronization
1812 {
1813 //part_id must starts from index = 1
1814 int offset;
1815 int mask;
1816
1817 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1818 {
1819 offset = part_id - 1;
1820 mask = 1 << offset;
1821
1823 }
1824 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1825 {
1826 offset = (part_id % 32);
1827 mask = 1 << offset;
1828
1830 }
1831 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1832 {
1833 offset = (part_id % 63);
1834 mask = 1 << offset;
1835
1837 }
1838 }
1839
1841 {
1842 //part_id must starts from index = 1
1843 int offset;
1844 int mask;
1845
1846 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1847 {
1848 offset = part_id - 1;
1849 mask = 1 << offset;
1850
1852 }
1853 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1854 {
1855 offset = (part_id % 32);
1856 mask = 1 << offset;
1857
1859 }
1860 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1861 {
1862 offset = (part_id % 63);
1863 mask = 1 << offset;
1864
1866 }
1867 }
1868
1870 {
1871 //part_id must starts from index = 1
1872 int offset;
1873 int mask;
1874
1875 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1876 {
1877 offset = part_id - 1;
1878 mask = 1 << offset;
1879
1880 if ((m_SyncParts01 & mask) > 0)
1881 return true;
1882 }
1883 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1884 {
1885 offset = (part_id % 32);
1886 mask = 1 << offset;
1887
1888 if ((m_SyncParts02 & mask) > 0)
1889 return true;
1890 }
1891 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1892 {
1893 offset = (part_id % 63);
1894 mask = 1 << offset;
1895
1896 if ((m_SyncParts03 & mask) > 0)
1897 return true;
1898 }
1899
1900 return false;
1901 }
1902
1903 protected void RegisterActionForSync(int part_id, int action_id)
1904 {
1907 }
1908
1909 protected void ResetActionSyncData()
1910 {
1911 //reset data
1912 m_InteractedPartId = -1;
1914 }
1915
1916 protected void SetActionFromSyncData()
1917 {
1918 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1919 {
1922
1923 switch (build_action_id)
1924 {
1928 }
1929 }
1930 }
1931 //------
1932
1934 {
1935 string key = part.m_PartName;
1936 bool is_base = part.IsBase();
1938 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1940 {
1941 if (!part.IsBuilt())
1942 {
1943 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1944 GetConstruction().AddToConstructedParts(key);
1945 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1946
1947 if (is_base)
1948 {
1950 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1951 }
1952 }
1953 }
1954 else
1955 {
1956 if (part.IsBuilt())
1957 {
1958 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1959 GetConstruction().RemoveFromConstructedParts(key);
1960 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1961
1962 if (is_base)
1963 {
1965 AddProxyPhysics(ANIMATION_DEPLOYED);
1966 }
1967 }
1968 }
1969
1970 //check slot lock for material attachments
1971 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1972 }
1973
1974 //set construction parts based on synchronized data
1976 {
1979
1980 for (int i = 0; i < construction_parts.Count(); ++i)
1981 {
1982 string key = construction_parts.GetKey(i);
1985 }
1986
1987 //regenerate navmesh
1988 UpdateNavmesh();
1989 }
1990
1992 {
1995
1996 for (int i = 0; i < construction_parts.Count(); ++i)
1997 {
1998 string key = construction_parts.GetKey(i);
2000
2001 if (value.GetId() == id)
2002 return value;
2003 }
2004
2005 return NULL;
2006 }
2007 //
2008
2009 //Base
2010 bool HasBase()
2011 {
2012 return m_HasBase;
2013 }
2014
2015 void SetBaseState(bool has_base)
2016 {
2018 }
2019
2020 override bool IsDeployable()
2021 {
2022 return true;
2023 }
2024
2025 bool IsOpened()
2026 {
2027 return false;
2028 }
2029
2030 //--- CONSTRUCTION KIT
2032 {
2036
2037 return construction_kit;
2038 }
2039
2041 {
2042 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2045 }
2046
2047 protected vector GetKitSpawnPosition()
2048 {
2049 return GetPosition();
2050 }
2051
2052 protected string GetConstructionKitType()
2053 {
2054 return "";
2055 }
2056
2058 {
2060 GetGame().ObjectDelete(construction_kit);
2061 }
2062
2063 //--- CONSTRUCTION
2064 void DestroyConstruction()
2065 {
2066 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2067 GetGame().ObjectDelete(this);
2068 }
2069
2070 // --- EVENTS
2071 override void OnStoreSave(ParamsWriteContext ctx)
2072 {
2073 super.OnStoreSave(ctx);
2074
2075 //sync parts 01
2076 ctx.Write(m_SyncParts01);
2077 ctx.Write(m_SyncParts02);
2078 ctx.Write(m_SyncParts03);
2079
2080 ctx.Write(m_HasBase);
2081 }
2082
2083 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2084 {
2085 if (!super.OnStoreLoad(ctx, version))
2086 return false;
2087
2088 //--- Base building data ---
2089 //Restore synced parts data
2090 if (!ctx.Read(m_SyncParts01))
2091 {
2092 m_SyncParts01 = 0; //set default
2093 return false;
2094 }
2095 if (!ctx.Read(m_SyncParts02))
2096 {
2097 m_SyncParts02 = 0; //set default
2098 return false;
2099 }
2100 if (!ctx.Read(m_SyncParts03))
2101 {
2102 m_SyncParts03 = 0; //set default
2103 return false;
2104 }
2105
2106 //has base
2107 if (!ctx.Read(m_HasBase))
2108 {
2109 m_HasBase = false;
2110 return false;
2111 }
2112 //---
2113
2114 return true;
2115 }
2116
2117 override void AfterStoreLoad()
2118 {
2119 super.AfterStoreLoad();
2120
2123 }
2124
2126 {
2127 //update server data
2129
2130 //set base state
2131 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2132 SetBaseState(construction_part.IsBuilt()) ;
2133
2134 //synchronize after load
2136 }
2137
2138 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2139 {
2141 return;
2142
2143 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2144
2145 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2146 return;
2147
2149 string part_name = zone;
2150 part_name.ToLower();
2151
2153 {
2155
2156 if (construction_part && construction.IsPartConstructed(part_name))
2157 {
2158 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2159 construction.DestroyConnectedParts(part_name);
2160 }
2161
2162 //barbed wire handling (hack-ish)
2163 if (part_name.Contains("barbed"))
2164 {
2165 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2166 if (barbed_wire)
2167 barbed_wire.SetMountedState(false);
2168 }
2169 }
2170 }
2171
2172 override void EEOnAfterLoad()
2173 {
2175 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2176
2177 super.EEOnAfterLoad();
2178 }
2179
2180 override void EEInit()
2181 {
2182 super.EEInit();
2183
2184 // init visuals and physics
2185 InitBaseState();
2186
2187 //debug
2188#ifdef DEVELOPER
2190#endif
2191 }
2192
2193 override void EEItemAttached(EntityAI item, string slot_name)
2194 {
2195 super.EEItemAttached(item, slot_name);
2196
2198 UpdateVisuals();
2200 }
2201
2202 override void EEItemDetached(EntityAI item, string slot_name)
2203 {
2204 super.EEItemDetached(item, slot_name);
2205
2206 UpdateVisuals();
2208 }
2209
2210 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2211 {
2213 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2214
2217 }
2218
2219 //ignore out of reach condition
2220 override bool IgnoreOutOfReachCondition()
2221 {
2222 return true;
2223 }
2224
2225 //CONSTRUCTION EVENTS
2226 //Build
2227 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2228 {
2230
2231 //check base state
2232 if (construtionPart.IsBase())
2233 {
2234 SetBaseState(true);
2235
2236 //spawn kit
2238 }
2239
2240 //register constructed parts for synchronization
2242
2243 //register action that was performed on part
2245
2246 //synchronize
2248
2249 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2250
2251 UpdateNavmesh();
2252
2253 //update visuals
2254 UpdateVisuals();
2255
2256 //reset action sync data
2257 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2258 }
2259
2260 void OnPartBuiltClient(string part_name, int action_id)
2261 {
2262 //play sound
2264 }
2265
2266 //Dismantle
2268 {
2269 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2271
2272 //register constructed parts for synchronization
2274
2275 //register action that was performed on part
2277
2278 //synchronize
2280
2281 // server part of sync, client will be synced from SetPartsFromSyncData
2283
2284 UpdateNavmesh();
2285
2286 //update visuals
2287 UpdateVisuals();
2288
2289 //reset action sync data
2290 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2291
2292 //check base state
2293 if (construtionPart.IsBase())
2294 {
2295 //Destroy construction
2296 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2297 }
2298 }
2299
2301 {
2302 //play sound
2304 }
2305
2306 //Destroy
2308 {
2309 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2311
2312 //register constructed parts for synchronization
2314
2315 //register action that was performed on part
2317
2318 //synchronize
2320
2321 // server part of sync, client will be synced from SetPartsFromSyncData
2323
2324 UpdateNavmesh();
2325
2326 //update visuals
2327 UpdateVisuals();
2328
2329 //reset action sync data
2330 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2331
2332 //check base state
2333 if (construtionPart.IsBase())
2334 {
2335 //Destroy construction
2336 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2337 }
2338 }
2339
2340 void OnPartDestroyedClient(string part_name, int action_id)
2341 {
2342 //play sound
2344 }
2345
2346 // --- UPDATE
2347 void InitBaseState()
2348 {
2349 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2350
2351 InitVisuals();
2352 UpdateNavmesh(); //regenerate navmesh
2353 GetConstruction().InitBaseState();
2354 }
2355
2356 void InitVisuals()
2357 {
2358 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2359 //check base
2360 if (!HasBase())
2361 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2362 else
2363 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2364
2365 GetConstruction().UpdateVisuals();
2366 }
2367
2368 void UpdateVisuals()
2369 {
2371
2373 foreach (string slotName : attachmentSlots)
2375
2376 //check base
2377 if (!HasBase())
2378 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2379 else
2380 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2381
2382 GetConstruction().UpdateVisuals();
2383 }
2384
2386 {
2387 string slotNameMounted = slot_name + "_Mounted";
2388 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2389
2390 if (attachment)
2391 {
2392 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2393 if (barbedWire && barbedWire.IsMounted())
2395 else
2397
2398 if (is_locked)
2399 {
2400 SetAnimationPhase(slotNameMounted, 0);
2401 SetAnimationPhase(slot_name, 1);
2402 }
2403 else
2404 {
2405 SetAnimationPhase(slotNameMounted, 1);
2406 SetAnimationPhase(slot_name, 0);
2407 }
2408 }
2409 else
2410 {
2411 SetAnimationPhase(slotNameMounted, 1);
2412 SetAnimationPhase(slot_name, 1);
2413
2415 }
2416 }
2417
2418 // avoid calling this function on frequent occasions, it's a massive performance hit
2419 void UpdatePhysics()
2420 {
2422 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2423
2426
2428 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2429
2430 foreach (string slotName : attachmentSlots)
2432
2433 //check base
2434 if (!HasBase())
2435 {
2437 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2438
2439 AddProxyPhysics(ANIMATION_DEPLOYED);
2440 }
2441 else
2442 {
2444 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2445
2446 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2447 }
2448
2449 GetConstruction().UpdatePhysics();
2450 UpdateNavmesh();
2451 }
2452
2454 {
2455 //checks for invalid appends; hotfix
2456 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2457 return;
2458 //----------------------------------
2459 string slot_name_mounted = slot_name + "_Mounted";
2460 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2461
2462 //remove proxy physics
2463 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2464 RemoveProxyPhysics(slot_name_mounted);
2465 RemoveProxyPhysics(slot_name);
2466
2467 if (attachment)
2468 {
2469 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2470 if (is_locked)
2471 {
2472 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2473 AddProxyPhysics(slot_name_mounted);
2474 }
2475 else
2476 {
2477 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2478 AddProxyPhysics(slot_name);
2479 }
2480 }
2481 }
2482
2483 protected void UpdateNavmesh()
2484 {
2485 SetAffectPathgraph(true, false);
2486 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2487 }
2488
2489 override bool CanUseConstruction()
2490 {
2491 return true;
2492 }
2493
2494 override bool CanUseConstructionBuild()
2495 {
2496 return true;
2497 }
2498
2500 {
2501 if (attachment)
2502 {
2504 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2505
2506 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2507 }
2508
2509 return false;
2510 }
2511
2512 protected bool IsAttachmentSlotLocked(string slot_name)
2513 {
2514 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2515 }
2516
2517 //--- ATTACHMENT SLOTS
2519 {
2520 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2521 if (GetGame().ConfigIsExisting(config_path))
2522 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2523 }
2524
2526 {
2527 return true;
2528 }
2529
2530 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2531 {
2532 return true;
2533 }
2534
2535 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2536 {
2537 return true;
2538 }
2539
2540 // --- INIT
2541 void ConstructionInit()
2542 {
2543 if (!m_Construction)
2544 m_Construction = new Construction(this);
2545
2546 GetConstruction().Init();
2547 }
2548
2550 {
2551 return m_Construction;
2552 }
2553
2554 //--- INVENTORY/ATTACHMENTS CONDITIONS
2555 //attachments
2557 {
2558 return super.CanReceiveAttachment(attachment, slotId);
2559 }
2560
2562 {
2563 int attachment_count = GetInventory().AttachmentCount();
2564 if (attachment_count > 0)
2565 {
2566 if (HasBase() && attachment_count == 1)
2567 return false;
2568
2569 return true;
2570 }
2571
2572 return false;
2573 }
2574
2575 override bool ShowZonesHealth()
2576 {
2577 return true;
2578 }
2579
2580 //this into/outo parent.Cargo
2581 override bool CanPutInCargo(EntityAI parent)
2582 {
2583 return false;
2584 }
2585
2586 override bool CanRemoveFromCargo(EntityAI parent)
2587 {
2588 return false;
2589 }
2590
2591 //hands
2592 override bool CanPutIntoHands(EntityAI parent)
2593 {
2594 return false;
2595 }
2596
2597 //--- ACTION CONDITIONS
2598 //direction
2599 override bool IsFacingPlayer(PlayerBase player, string selection)
2600 {
2601 return true;
2602 }
2603
2604 override bool IsPlayerInside(PlayerBase player, string selection)
2605 {
2606 return true;
2607 }
2608
2611 {
2612 return false;
2613 }
2614
2615 //camera direction check
2616 bool IsFacingCamera(string selection)
2617 {
2618 return true;
2619 }
2620
2621 //roof check
2623 {
2624 return false;
2625 }
2626
2627 //selection->player distance check
2628 bool HasProperDistance(string selection, PlayerBase player)
2629 {
2630 return true;
2631 }
2632
2633 //folding
2635 {
2636 if (HasBase() || GetInventory().AttachmentCount() > 0)
2637 return false;
2638
2639 return true;
2640 }
2641
2643 {
2646
2647 return item;
2648 }
2649
2650 //Damage triggers (barbed wire)
2651 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2652 {
2653 if (GetGame() && GetGame().IsServer())
2654 {
2655 //destroy area damage if some already exists
2657
2658 //create new area damage
2660 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2661
2662 vector min_max[2];
2663 if (MemoryPointExists(slot_name + "_min"))
2664 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2665 if (MemoryPointExists(slot_name + "_max"))
2666 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2667
2668 //get proper trigger extents (min<max)
2669 vector extents[2];
2670 GetConstruction().GetTriggerExtents(min_max, extents);
2671
2672 //get box center
2673 vector center;
2674 center = GetConstruction().GetBoxCenter(min_max);
2675 center = ModelToWorld(center);
2676
2677 //rotate center if needed
2680
2681 areaDamage.SetExtents(extents[0], extents[1]);
2682 areaDamage.SetAreaPosition(center);
2683 areaDamage.SetAreaOrientation(orientation);
2684 areaDamage.SetLoopInterval(1.0);
2685 areaDamage.SetDeferDuration(0.2);
2686 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2687 areaDamage.SetAmmoName("BarbedWireHit");
2688 areaDamage.Spawn();
2689
2691 }
2692 }
2693
2695 {
2696 if (angle_deg != 0)
2697 {
2698 //orientation
2700
2701 //center
2703 if (MemoryPointExists("rotate_axis"))
2704 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2707 center[0] = r_center_x;
2708 center[2] = r_center_z;
2709 }
2710 }
2711
2712 void DestroyAreaDamage(string slot_name)
2713 {
2714 if (GetGame() && GetGame().IsServer())
2715 {
2718 {
2719 if (areaDamage)
2720 areaDamage.Destroy();
2721
2723 }
2724 }
2725 }
2726
2727 override bool IsIgnoredByConstruction()
2728 {
2729 return true;
2730 }
2731
2732 //================================================================
2733 // SOUNDS
2734 //================================================================
2735 protected void SoundBuildStart(string part_name)
2736 {
2737 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2738 }
2739
2740 protected void SoundDismantleStart(string part_name)
2741 {
2742 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2743 }
2744
2745 protected void SoundDestroyStart(string part_name)
2746 {
2747 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2748 }
2749
2750 protected string GetBuildSoundByMaterial(string part_name)
2751 {
2753
2754 switch (material_type)
2755 {
2756 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2757 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2758 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2759 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2760 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2761 }
2762
2763 return "";
2764 }
2765
2766 protected string GetDismantleSoundByMaterial(string part_name)
2767 {
2769
2770 switch (material_type)
2771 {
2772 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2773 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2774 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2775 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2776 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2777 }
2778
2779 return "";
2780 }
2781
2782 //misc
2784 {
2785 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2786 {
2787 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2789 SetHealth(slot_name, "Health", item.GetHealth());
2790 }
2791 }
2792
2793 override int GetDamageSystemVersionChange()
2794 {
2795 return 111;
2796 }
2797
2798 override void SetActions()
2799 {
2800 super.SetActions();
2801
2803 //AddAction(ActionTakeHybridAttachment);
2804 //AddAction(ActionTakeHybridAttachmentToHands);
2807 }
2808
2809 //================================================================
2810 // DEBUG
2811 //================================================================
2812 protected void DebugCustomState()
2813 {
2814 }
2815
2818 {
2819 return null;
2820 }
2821
2822 override void OnDebugSpawn()
2823 {
2824 FullyBuild();
2825 }
2826
2827 void FullyBuild()
2828 {
2830 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2831
2832 Man p;
2833
2834#ifdef SERVER
2836 GetGame().GetWorld().GetPlayerList(players);
2837 if (players.Count())
2838 p = players[0];
2839#else
2840 p = GetGame().GetPlayer();
2841#endif
2842
2843 foreach (ConstructionPart part : parts)
2844 {
2845 bool excluded = false;
2846 string partName = part.GetPartName();
2847 if (excludes)
2848 {
2849 foreach (string exclude : excludes)
2850 {
2851 if (partName.Contains(exclude))
2852 {
2853 excluded = true;
2854 break;
2855 }
2856 }
2857 }
2858
2859 if (!excluded)
2861 }
2862
2863 GetConstruction().UpdateVisuals();
2864 }
2865}
2866
2867void bsbDebugPrint(string s)
2868{
2869#ifdef BSB_DEBUG
2870 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2871#else
2872 //Print("" + s); // comment/uncomment to hide/see debug logs
2873#endif
2874}
2875void bsbDebugSpam(string s)
2876{
2877#ifdef BSB_DEBUG_SPAM
2878 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2879#else
2880 //Print("" + s); // comment/uncomment to hide/see debug logs
2881#endif
2882}

◆ OnStoreLoad()

override bool bsbDebugPrint::OnStoreLoad ( ParamsReadContext ctx,
int version )
protected

Definition at line 1565 of file BaseBuildingBase.c.

1567{
1568 const string ANIMATION_DEPLOYED = "Deployed";
1569
1570 float m_ConstructionKitHealth; //stored health value for used construction kit
1571
1573
1574 bool m_HasBase;
1575 //variables for synchronization of base building parts (2x31 is the current limit)
1576 int m_SyncParts01; //synchronization for already built parts (31 parts)
1577 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1578 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1579 int m_InteractedPartId; //construction part id that an action was performed on
1580 int m_PerformedActionId; //action id that was performed on a construction part
1581
1582 //Sounds
1583 //build
1584 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1585 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1586 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1587 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1588 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1589 //dismantle
1590 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1591 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1592 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1593 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1594 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1595
1596 protected EffectSound m_Sound;
1597
1601
1602 // Constructor
1603 void BaseBuildingBase()
1604 {
1606
1607 //synchronized variables
1608 RegisterNetSyncVariableInt("m_SyncParts01");
1609 RegisterNetSyncVariableInt("m_SyncParts02");
1610 RegisterNetSyncVariableInt("m_SyncParts03");
1611 RegisterNetSyncVariableInt("m_InteractedPartId");
1612 RegisterNetSyncVariableInt("m_PerformedActionId");
1613 RegisterNetSyncVariableBool("m_HasBase");
1614
1615 //Construction init
1617
1618 if (ConfigIsExisting("hybridAttachments"))
1619 {
1621 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1622 }
1623 if (ConfigIsExisting("mountables"))
1624 {
1626 ConfigGetTextArray("mountables", m_Mountables);
1627 }
1628
1629 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1630 }
1631
1632 override void EEDelete(EntityAI parent)
1633 {
1634 super.EEDelete(parent);
1635
1636 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1638
1639 }
1640
1641 override string GetInvulnerabilityTypeString()
1642 {
1643 return "disableBaseDamage";
1644 }
1645
1646 override bool CanObstruct()
1647 {
1648 return true;
1649 }
1650
1651 override int GetHideIconMask()
1652 {
1653 return EInventoryIconVisibility.HIDE_VICINITY;
1654 }
1655
1656 // --- SYNCHRONIZATION
1658 {
1659 if (GetGame().IsServer())
1660 SetSynchDirty();
1661 }
1662
1663 override void OnVariablesSynchronized()
1664 {
1665 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1666 super.OnVariablesSynchronized();
1667
1668 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1669 }
1670
1671 protected void OnSynchronizedClient()
1672 {
1673 //update parts
1675
1676 //update action on part
1678
1679 //update visuals (client)
1680 UpdateVisuals();
1681 }
1682
1683 //parts synchronization
1685 {
1686 //part_id must starts from index = 1
1687 int offset;
1688 int mask;
1689
1690 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1691 {
1692 offset = part_id - 1;
1693 mask = 1 << offset;
1694
1696 }
1697 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1698 {
1699 offset = (part_id % 32);
1700 mask = 1 << offset;
1701
1703 }
1704 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1705 {
1706 offset = (part_id % 63);
1707 mask = 1 << offset;
1708
1710 }
1711 }
1712
1714 {
1715 //part_id must starts from index = 1
1716 int offset;
1717 int mask;
1718
1719 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1720 {
1721 offset = part_id - 1;
1722 mask = 1 << offset;
1723
1725 }
1726 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1727 {
1728 offset = (part_id % 32);
1729 mask = 1 << offset;
1730
1732 }
1733 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1734 {
1735 offset = (part_id % 63);
1736 mask = 1 << offset;
1737
1739 }
1740 }
1741
1743 {
1744 //part_id must starts from index = 1
1745 int offset;
1746 int mask;
1747
1748 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1749 {
1750 offset = part_id - 1;
1751 mask = 1 << offset;
1752
1753 if ((m_SyncParts01 & mask) > 0)
1754 return true;
1755 }
1756 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1757 {
1758 offset = (part_id % 32);
1759 mask = 1 << offset;
1760
1761 if ((m_SyncParts02 & mask) > 0)
1762 return true;
1763 }
1764 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1765 {
1766 offset = (part_id % 63);
1767 mask = 1 << offset;
1768
1769 if ((m_SyncParts03 & mask) > 0)
1770 return true;
1771 }
1772
1773 return false;
1774 }
1775
1776 protected void RegisterActionForSync(int part_id, int action_id)
1777 {
1780 }
1781
1782 protected void ResetActionSyncData()
1783 {
1784 //reset data
1785 m_InteractedPartId = -1;
1787 }
1788
1789 protected void SetActionFromSyncData()
1790 {
1791 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1792 {
1795
1796 switch (build_action_id)
1797 {
1801 }
1802 }
1803 }
1804 //------
1805
1807 {
1808 string key = part.m_PartName;
1809 bool is_base = part.IsBase();
1811 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1813 {
1814 if (!part.IsBuilt())
1815 {
1816 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1817 GetConstruction().AddToConstructedParts(key);
1818 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1819
1820 if (is_base)
1821 {
1823 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1824 }
1825 }
1826 }
1827 else
1828 {
1829 if (part.IsBuilt())
1830 {
1831 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1832 GetConstruction().RemoveFromConstructedParts(key);
1833 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1834
1835 if (is_base)
1836 {
1838 AddProxyPhysics(ANIMATION_DEPLOYED);
1839 }
1840 }
1841 }
1842
1843 //check slot lock for material attachments
1844 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1845 }
1846
1847 //set construction parts based on synchronized data
1849 {
1852
1853 for (int i = 0; i < construction_parts.Count(); ++i)
1854 {
1855 string key = construction_parts.GetKey(i);
1858 }
1859
1860 //regenerate navmesh
1861 UpdateNavmesh();
1862 }
1863
1865 {
1868
1869 for (int i = 0; i < construction_parts.Count(); ++i)
1870 {
1871 string key = construction_parts.GetKey(i);
1873
1874 if (value.GetId() == id)
1875 return value;
1876 }
1877
1878 return NULL;
1879 }
1880 //
1881
1882 //Base
1883 bool HasBase()
1884 {
1885 return m_HasBase;
1886 }
1887
1888 void SetBaseState(bool has_base)
1889 {
1891 }
1892
1893 override bool IsDeployable()
1894 {
1895 return true;
1896 }
1897
1898 bool IsOpened()
1899 {
1900 return false;
1901 }
1902
1903 //--- CONSTRUCTION KIT
1905 {
1909
1910 return construction_kit;
1911 }
1912
1914 {
1915 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1918 }
1919
1920 protected vector GetKitSpawnPosition()
1921 {
1922 return GetPosition();
1923 }
1924
1925 protected string GetConstructionKitType()
1926 {
1927 return "";
1928 }
1929
1931 {
1933 GetGame().ObjectDelete(construction_kit);
1934 }
1935
1936 //--- CONSTRUCTION
1937 void DestroyConstruction()
1938 {
1939 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1940 GetGame().ObjectDelete(this);
1941 }
1942
1943 // --- EVENTS
1944 override void OnStoreSave(ParamsWriteContext ctx)
1945 {
1946 super.OnStoreSave(ctx);
1947
1948 //sync parts 01
1949 ctx.Write(m_SyncParts01);
1950 ctx.Write(m_SyncParts02);
1951 ctx.Write(m_SyncParts03);
1952
1953 ctx.Write(m_HasBase);
1954 }
1955
1956 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1957 {
1958 if (!super.OnStoreLoad(ctx, version))
1959 return false;
1960
1961 //--- Base building data ---
1962 //Restore synced parts data
1963 if (!ctx.Read(m_SyncParts01))
1964 {
1965 m_SyncParts01 = 0; //set default
1966 return false;
1967 }
1968 if (!ctx.Read(m_SyncParts02))
1969 {
1970 m_SyncParts02 = 0; //set default
1971 return false;
1972 }
1973 if (!ctx.Read(m_SyncParts03))
1974 {
1975 m_SyncParts03 = 0; //set default
1976 return false;
1977 }
1978
1979 //has base
1980 if (!ctx.Read(m_HasBase))
1981 {
1982 m_HasBase = false;
1983 return false;
1984 }
1985 //---
1986
1987 return true;
1988 }
1989
1990 override void AfterStoreLoad()
1991 {
1992 super.AfterStoreLoad();
1993
1996 }
1997
1999 {
2000 //update server data
2002
2003 //set base state
2004 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2005 SetBaseState(construction_part.IsBuilt()) ;
2006
2007 //synchronize after load
2009 }
2010
2011 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2012 {
2014 return;
2015
2016 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2017
2018 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2019 return;
2020
2022 string part_name = zone;
2023 part_name.ToLower();
2024
2026 {
2028
2029 if (construction_part && construction.IsPartConstructed(part_name))
2030 {
2031 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2032 construction.DestroyConnectedParts(part_name);
2033 }
2034
2035 //barbed wire handling (hack-ish)
2036 if (part_name.Contains("barbed"))
2037 {
2038 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2039 if (barbed_wire)
2040 barbed_wire.SetMountedState(false);
2041 }
2042 }
2043 }
2044
2045 override void EEOnAfterLoad()
2046 {
2048 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2049
2050 super.EEOnAfterLoad();
2051 }
2052
2053 override void EEInit()
2054 {
2055 super.EEInit();
2056
2057 // init visuals and physics
2058 InitBaseState();
2059
2060 //debug
2061#ifdef DEVELOPER
2063#endif
2064 }
2065
2066 override void EEItemAttached(EntityAI item, string slot_name)
2067 {
2068 super.EEItemAttached(item, slot_name);
2069
2071 UpdateVisuals();
2073 }
2074
2075 override void EEItemDetached(EntityAI item, string slot_name)
2076 {
2077 super.EEItemDetached(item, slot_name);
2078
2079 UpdateVisuals();
2081 }
2082
2083 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2084 {
2086 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2087
2090 }
2091
2092 //ignore out of reach condition
2093 override bool IgnoreOutOfReachCondition()
2094 {
2095 return true;
2096 }
2097
2098 //CONSTRUCTION EVENTS
2099 //Build
2100 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2101 {
2103
2104 //check base state
2105 if (construtionPart.IsBase())
2106 {
2107 SetBaseState(true);
2108
2109 //spawn kit
2111 }
2112
2113 //register constructed parts for synchronization
2115
2116 //register action that was performed on part
2118
2119 //synchronize
2121
2122 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2123
2124 UpdateNavmesh();
2125
2126 //update visuals
2127 UpdateVisuals();
2128
2129 //reset action sync data
2130 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2131 }
2132
2133 void OnPartBuiltClient(string part_name, int action_id)
2134 {
2135 //play sound
2137 }
2138
2139 //Dismantle
2141 {
2142 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2144
2145 //register constructed parts for synchronization
2147
2148 //register action that was performed on part
2150
2151 //synchronize
2153
2154 // server part of sync, client will be synced from SetPartsFromSyncData
2156
2157 UpdateNavmesh();
2158
2159 //update visuals
2160 UpdateVisuals();
2161
2162 //reset action sync data
2163 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2164
2165 //check base state
2166 if (construtionPart.IsBase())
2167 {
2168 //Destroy construction
2169 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2170 }
2171 }
2172
2174 {
2175 //play sound
2177 }
2178
2179 //Destroy
2181 {
2182 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2184
2185 //register constructed parts for synchronization
2187
2188 //register action that was performed on part
2190
2191 //synchronize
2193
2194 // server part of sync, client will be synced from SetPartsFromSyncData
2196
2197 UpdateNavmesh();
2198
2199 //update visuals
2200 UpdateVisuals();
2201
2202 //reset action sync data
2203 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2204
2205 //check base state
2206 if (construtionPart.IsBase())
2207 {
2208 //Destroy construction
2209 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2210 }
2211 }
2212
2213 void OnPartDestroyedClient(string part_name, int action_id)
2214 {
2215 //play sound
2217 }
2218
2219 // --- UPDATE
2220 void InitBaseState()
2221 {
2222 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2223
2224 InitVisuals();
2225 UpdateNavmesh(); //regenerate navmesh
2226 GetConstruction().InitBaseState();
2227 }
2228
2229 void InitVisuals()
2230 {
2231 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2232 //check base
2233 if (!HasBase())
2234 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2235 else
2236 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2237
2238 GetConstruction().UpdateVisuals();
2239 }
2240
2241 void UpdateVisuals()
2242 {
2244
2246 foreach (string slotName : attachmentSlots)
2248
2249 //check base
2250 if (!HasBase())
2251 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2252 else
2253 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2254
2255 GetConstruction().UpdateVisuals();
2256 }
2257
2259 {
2260 string slotNameMounted = slot_name + "_Mounted";
2261 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2262
2263 if (attachment)
2264 {
2265 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2266 if (barbedWire && barbedWire.IsMounted())
2268 else
2270
2271 if (is_locked)
2272 {
2273 SetAnimationPhase(slotNameMounted, 0);
2274 SetAnimationPhase(slot_name, 1);
2275 }
2276 else
2277 {
2278 SetAnimationPhase(slotNameMounted, 1);
2279 SetAnimationPhase(slot_name, 0);
2280 }
2281 }
2282 else
2283 {
2284 SetAnimationPhase(slotNameMounted, 1);
2285 SetAnimationPhase(slot_name, 1);
2286
2288 }
2289 }
2290
2291 // avoid calling this function on frequent occasions, it's a massive performance hit
2292 void UpdatePhysics()
2293 {
2295 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2296
2299
2301 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2302
2303 foreach (string slotName : attachmentSlots)
2305
2306 //check base
2307 if (!HasBase())
2308 {
2310 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2311
2312 AddProxyPhysics(ANIMATION_DEPLOYED);
2313 }
2314 else
2315 {
2317 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2318
2319 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2320 }
2321
2322 GetConstruction().UpdatePhysics();
2323 UpdateNavmesh();
2324 }
2325
2327 {
2328 //checks for invalid appends; hotfix
2329 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2330 return;
2331 //----------------------------------
2332 string slot_name_mounted = slot_name + "_Mounted";
2333 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2334
2335 //remove proxy physics
2336 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2337 RemoveProxyPhysics(slot_name_mounted);
2338 RemoveProxyPhysics(slot_name);
2339
2340 if (attachment)
2341 {
2342 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2343 if (is_locked)
2344 {
2345 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2346 AddProxyPhysics(slot_name_mounted);
2347 }
2348 else
2349 {
2350 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2351 AddProxyPhysics(slot_name);
2352 }
2353 }
2354 }
2355
2356 protected void UpdateNavmesh()
2357 {
2358 SetAffectPathgraph(true, false);
2359 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2360 }
2361
2362 override bool CanUseConstruction()
2363 {
2364 return true;
2365 }
2366
2367 override bool CanUseConstructionBuild()
2368 {
2369 return true;
2370 }
2371
2373 {
2374 if (attachment)
2375 {
2377 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2378
2379 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2380 }
2381
2382 return false;
2383 }
2384
2385 protected bool IsAttachmentSlotLocked(string slot_name)
2386 {
2387 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2388 }
2389
2390 //--- ATTACHMENT SLOTS
2392 {
2393 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2394 if (GetGame().ConfigIsExisting(config_path))
2395 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2396 }
2397
2399 {
2400 return true;
2401 }
2402
2403 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2404 {
2405 return true;
2406 }
2407
2408 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2409 {
2410 return true;
2411 }
2412
2413 // --- INIT
2414 void ConstructionInit()
2415 {
2416 if (!m_Construction)
2417 m_Construction = new Construction(this);
2418
2419 GetConstruction().Init();
2420 }
2421
2423 {
2424 return m_Construction;
2425 }
2426
2427 //--- INVENTORY/ATTACHMENTS CONDITIONS
2428 //attachments
2430 {
2431 return super.CanReceiveAttachment(attachment, slotId);
2432 }
2433
2435 {
2436 int attachment_count = GetInventory().AttachmentCount();
2437 if (attachment_count > 0)
2438 {
2439 if (HasBase() && attachment_count == 1)
2440 return false;
2441
2442 return true;
2443 }
2444
2445 return false;
2446 }
2447
2448 override bool ShowZonesHealth()
2449 {
2450 return true;
2451 }
2452
2453 //this into/outo parent.Cargo
2454 override bool CanPutInCargo(EntityAI parent)
2455 {
2456 return false;
2457 }
2458
2459 override bool CanRemoveFromCargo(EntityAI parent)
2460 {
2461 return false;
2462 }
2463
2464 //hands
2465 override bool CanPutIntoHands(EntityAI parent)
2466 {
2467 return false;
2468 }
2469
2470 //--- ACTION CONDITIONS
2471 //direction
2472 override bool IsFacingPlayer(PlayerBase player, string selection)
2473 {
2474 return true;
2475 }
2476
2477 override bool IsPlayerInside(PlayerBase player, string selection)
2478 {
2479 return true;
2480 }
2481
2484 {
2485 return false;
2486 }
2487
2488 //camera direction check
2489 bool IsFacingCamera(string selection)
2490 {
2491 return true;
2492 }
2493
2494 //roof check
2496 {
2497 return false;
2498 }
2499
2500 //selection->player distance check
2501 bool HasProperDistance(string selection, PlayerBase player)
2502 {
2503 return true;
2504 }
2505
2506 //folding
2508 {
2509 if (HasBase() || GetInventory().AttachmentCount() > 0)
2510 return false;
2511
2512 return true;
2513 }
2514
2516 {
2519
2520 return item;
2521 }
2522
2523 //Damage triggers (barbed wire)
2524 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2525 {
2526 if (GetGame() && GetGame().IsServer())
2527 {
2528 //destroy area damage if some already exists
2530
2531 //create new area damage
2533 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2534
2535 vector min_max[2];
2536 if (MemoryPointExists(slot_name + "_min"))
2537 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2538 if (MemoryPointExists(slot_name + "_max"))
2539 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2540
2541 //get proper trigger extents (min<max)
2542 vector extents[2];
2543 GetConstruction().GetTriggerExtents(min_max, extents);
2544
2545 //get box center
2546 vector center;
2547 center = GetConstruction().GetBoxCenter(min_max);
2548 center = ModelToWorld(center);
2549
2550 //rotate center if needed
2553
2554 areaDamage.SetExtents(extents[0], extents[1]);
2555 areaDamage.SetAreaPosition(center);
2556 areaDamage.SetAreaOrientation(orientation);
2557 areaDamage.SetLoopInterval(1.0);
2558 areaDamage.SetDeferDuration(0.2);
2559 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2560 areaDamage.SetAmmoName("BarbedWireHit");
2561 areaDamage.Spawn();
2562
2564 }
2565 }
2566
2568 {
2569 if (angle_deg != 0)
2570 {
2571 //orientation
2573
2574 //center
2576 if (MemoryPointExists("rotate_axis"))
2577 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2580 center[0] = r_center_x;
2581 center[2] = r_center_z;
2582 }
2583 }
2584
2585 void DestroyAreaDamage(string slot_name)
2586 {
2587 if (GetGame() && GetGame().IsServer())
2588 {
2591 {
2592 if (areaDamage)
2593 areaDamage.Destroy();
2594
2596 }
2597 }
2598 }
2599
2600 override bool IsIgnoredByConstruction()
2601 {
2602 return true;
2603 }
2604
2605 //================================================================
2606 // SOUNDS
2607 //================================================================
2608 protected void SoundBuildStart(string part_name)
2609 {
2610 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2611 }
2612
2613 protected void SoundDismantleStart(string part_name)
2614 {
2615 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2616 }
2617
2618 protected void SoundDestroyStart(string part_name)
2619 {
2620 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2621 }
2622
2623 protected string GetBuildSoundByMaterial(string part_name)
2624 {
2626
2627 switch (material_type)
2628 {
2629 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2630 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2631 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2632 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2633 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2634 }
2635
2636 return "";
2637 }
2638
2639 protected string GetDismantleSoundByMaterial(string part_name)
2640 {
2642
2643 switch (material_type)
2644 {
2645 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2646 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2647 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2648 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2649 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2650 }
2651
2652 return "";
2653 }
2654
2655 //misc
2657 {
2658 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2659 {
2660 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2662 SetHealth(slot_name, "Health", item.GetHealth());
2663 }
2664 }
2665
2666 override int GetDamageSystemVersionChange()
2667 {
2668 return 111;
2669 }
2670
2671 override void SetActions()
2672 {
2673 super.SetActions();
2674
2676 //AddAction(ActionTakeHybridAttachment);
2677 //AddAction(ActionTakeHybridAttachmentToHands);
2680 }
2681
2682 //================================================================
2683 // DEBUG
2684 //================================================================
2685 protected void DebugCustomState()
2686 {
2687 }
2688
2691 {
2692 return null;
2693 }
2694
2695 override void OnDebugSpawn()
2696 {
2697 FullyBuild();
2698 }
2699
2700 void FullyBuild()
2701 {
2703 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2704
2705 Man p;
2706
2707#ifdef SERVER
2709 GetGame().GetWorld().GetPlayerList(players);
2710 if (players.Count())
2711 p = players[0];
2712#else
2713 p = GetGame().GetPlayer();
2714#endif
2715
2716 foreach (ConstructionPart part : parts)
2717 {
2718 bool excluded = false;
2719 string partName = part.GetPartName();
2720 if (excludes)
2721 {
2722 foreach (string exclude : excludes)
2723 {
2724 if (partName.Contains(exclude))
2725 {
2726 excluded = true;
2727 break;
2728 }
2729 }
2730 }
2731
2732 if (!excluded)
2734 }
2735
2736 GetConstruction().UpdateVisuals();
2737 }
2738}
2739
2740void bsbDebugPrint(string s)
2741{
2742#ifdef BSB_DEBUG
2743 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2744#else
2745 //Print("" + s); // comment/uncomment to hide/see debug logs
2746#endif
2747}
2748void bsbDebugSpam(string s)
2749{
2750#ifdef BSB_DEBUG_SPAM
2751 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2752#else
2753 //Print("" + s); // comment/uncomment to hide/see debug logs
2754#endif
2755}

◆ OnStoreSave()

override void bsbDebugPrint::OnStoreSave ( ParamsWriteContext ctx)
protected

Definition at line 1553 of file BaseBuildingBase.c.

1555{
1556 const string ANIMATION_DEPLOYED = "Deployed";
1557
1558 float m_ConstructionKitHealth; //stored health value for used construction kit
1559
1561
1562 bool m_HasBase;
1563 //variables for synchronization of base building parts (2x31 is the current limit)
1564 int m_SyncParts01; //synchronization for already built parts (31 parts)
1565 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1566 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1567 int m_InteractedPartId; //construction part id that an action was performed on
1568 int m_PerformedActionId; //action id that was performed on a construction part
1569
1570 //Sounds
1571 //build
1572 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1573 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1574 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1575 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1576 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1577 //dismantle
1578 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1579 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1580 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1581 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1582 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1583
1584 protected EffectSound m_Sound;
1585
1589
1590 // Constructor
1591 void BaseBuildingBase()
1592 {
1594
1595 //synchronized variables
1596 RegisterNetSyncVariableInt("m_SyncParts01");
1597 RegisterNetSyncVariableInt("m_SyncParts02");
1598 RegisterNetSyncVariableInt("m_SyncParts03");
1599 RegisterNetSyncVariableInt("m_InteractedPartId");
1600 RegisterNetSyncVariableInt("m_PerformedActionId");
1601 RegisterNetSyncVariableBool("m_HasBase");
1602
1603 //Construction init
1605
1606 if (ConfigIsExisting("hybridAttachments"))
1607 {
1609 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1610 }
1611 if (ConfigIsExisting("mountables"))
1612 {
1614 ConfigGetTextArray("mountables", m_Mountables);
1615 }
1616
1617 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1618 }
1619
1620 override void EEDelete(EntityAI parent)
1621 {
1622 super.EEDelete(parent);
1623
1624 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1626
1627 }
1628
1629 override string GetInvulnerabilityTypeString()
1630 {
1631 return "disableBaseDamage";
1632 }
1633
1634 override bool CanObstruct()
1635 {
1636 return true;
1637 }
1638
1639 override int GetHideIconMask()
1640 {
1641 return EInventoryIconVisibility.HIDE_VICINITY;
1642 }
1643
1644 // --- SYNCHRONIZATION
1646 {
1647 if (GetGame().IsServer())
1648 SetSynchDirty();
1649 }
1650
1651 override void OnVariablesSynchronized()
1652 {
1653 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1654 super.OnVariablesSynchronized();
1655
1656 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1657 }
1658
1659 protected void OnSynchronizedClient()
1660 {
1661 //update parts
1663
1664 //update action on part
1666
1667 //update visuals (client)
1668 UpdateVisuals();
1669 }
1670
1671 //parts synchronization
1673 {
1674 //part_id must starts from index = 1
1675 int offset;
1676 int mask;
1677
1678 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1679 {
1680 offset = part_id - 1;
1681 mask = 1 << offset;
1682
1684 }
1685 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1686 {
1687 offset = (part_id % 32);
1688 mask = 1 << offset;
1689
1691 }
1692 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1693 {
1694 offset = (part_id % 63);
1695 mask = 1 << offset;
1696
1698 }
1699 }
1700
1702 {
1703 //part_id must starts from index = 1
1704 int offset;
1705 int mask;
1706
1707 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1708 {
1709 offset = part_id - 1;
1710 mask = 1 << offset;
1711
1713 }
1714 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1715 {
1716 offset = (part_id % 32);
1717 mask = 1 << offset;
1718
1720 }
1721 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1722 {
1723 offset = (part_id % 63);
1724 mask = 1 << offset;
1725
1727 }
1728 }
1729
1731 {
1732 //part_id must starts from index = 1
1733 int offset;
1734 int mask;
1735
1736 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1737 {
1738 offset = part_id - 1;
1739 mask = 1 << offset;
1740
1741 if ((m_SyncParts01 & mask) > 0)
1742 return true;
1743 }
1744 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1745 {
1746 offset = (part_id % 32);
1747 mask = 1 << offset;
1748
1749 if ((m_SyncParts02 & mask) > 0)
1750 return true;
1751 }
1752 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1753 {
1754 offset = (part_id % 63);
1755 mask = 1 << offset;
1756
1757 if ((m_SyncParts03 & mask) > 0)
1758 return true;
1759 }
1760
1761 return false;
1762 }
1763
1764 protected void RegisterActionForSync(int part_id, int action_id)
1765 {
1768 }
1769
1770 protected void ResetActionSyncData()
1771 {
1772 //reset data
1773 m_InteractedPartId = -1;
1775 }
1776
1777 protected void SetActionFromSyncData()
1778 {
1779 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1780 {
1783
1784 switch (build_action_id)
1785 {
1789 }
1790 }
1791 }
1792 //------
1793
1795 {
1796 string key = part.m_PartName;
1797 bool is_base = part.IsBase();
1799 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1801 {
1802 if (!part.IsBuilt())
1803 {
1804 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1805 GetConstruction().AddToConstructedParts(key);
1806 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1807
1808 if (is_base)
1809 {
1811 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1812 }
1813 }
1814 }
1815 else
1816 {
1817 if (part.IsBuilt())
1818 {
1819 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1820 GetConstruction().RemoveFromConstructedParts(key);
1821 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1822
1823 if (is_base)
1824 {
1826 AddProxyPhysics(ANIMATION_DEPLOYED);
1827 }
1828 }
1829 }
1830
1831 //check slot lock for material attachments
1832 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1833 }
1834
1835 //set construction parts based on synchronized data
1837 {
1840
1841 for (int i = 0; i < construction_parts.Count(); ++i)
1842 {
1843 string key = construction_parts.GetKey(i);
1846 }
1847
1848 //regenerate navmesh
1849 UpdateNavmesh();
1850 }
1851
1853 {
1856
1857 for (int i = 0; i < construction_parts.Count(); ++i)
1858 {
1859 string key = construction_parts.GetKey(i);
1861
1862 if (value.GetId() == id)
1863 return value;
1864 }
1865
1866 return NULL;
1867 }
1868 //
1869
1870 //Base
1871 bool HasBase()
1872 {
1873 return m_HasBase;
1874 }
1875
1876 void SetBaseState(bool has_base)
1877 {
1879 }
1880
1881 override bool IsDeployable()
1882 {
1883 return true;
1884 }
1885
1886 bool IsOpened()
1887 {
1888 return false;
1889 }
1890
1891 //--- CONSTRUCTION KIT
1893 {
1897
1898 return construction_kit;
1899 }
1900
1902 {
1903 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1906 }
1907
1908 protected vector GetKitSpawnPosition()
1909 {
1910 return GetPosition();
1911 }
1912
1913 protected string GetConstructionKitType()
1914 {
1915 return "";
1916 }
1917
1919 {
1921 GetGame().ObjectDelete(construction_kit);
1922 }
1923
1924 //--- CONSTRUCTION
1925 void DestroyConstruction()
1926 {
1927 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1928 GetGame().ObjectDelete(this);
1929 }
1930
1931 // --- EVENTS
1932 override void OnStoreSave(ParamsWriteContext ctx)
1933 {
1934 super.OnStoreSave(ctx);
1935
1936 //sync parts 01
1937 ctx.Write(m_SyncParts01);
1938 ctx.Write(m_SyncParts02);
1939 ctx.Write(m_SyncParts03);
1940
1941 ctx.Write(m_HasBase);
1942 }
1943
1944 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1945 {
1946 if (!super.OnStoreLoad(ctx, version))
1947 return false;
1948
1949 //--- Base building data ---
1950 //Restore synced parts data
1951 if (!ctx.Read(m_SyncParts01))
1952 {
1953 m_SyncParts01 = 0; //set default
1954 return false;
1955 }
1956 if (!ctx.Read(m_SyncParts02))
1957 {
1958 m_SyncParts02 = 0; //set default
1959 return false;
1960 }
1961 if (!ctx.Read(m_SyncParts03))
1962 {
1963 m_SyncParts03 = 0; //set default
1964 return false;
1965 }
1966
1967 //has base
1968 if (!ctx.Read(m_HasBase))
1969 {
1970 m_HasBase = false;
1971 return false;
1972 }
1973 //---
1974
1975 return true;
1976 }
1977
1978 override void AfterStoreLoad()
1979 {
1980 super.AfterStoreLoad();
1981
1984 }
1985
1987 {
1988 //update server data
1990
1991 //set base state
1992 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1993 SetBaseState(construction_part.IsBuilt()) ;
1994
1995 //synchronize after load
1997 }
1998
1999 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2000 {
2002 return;
2003
2004 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2005
2006 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2007 return;
2008
2010 string part_name = zone;
2011 part_name.ToLower();
2012
2014 {
2016
2017 if (construction_part && construction.IsPartConstructed(part_name))
2018 {
2019 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2020 construction.DestroyConnectedParts(part_name);
2021 }
2022
2023 //barbed wire handling (hack-ish)
2024 if (part_name.Contains("barbed"))
2025 {
2026 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2027 if (barbed_wire)
2028 barbed_wire.SetMountedState(false);
2029 }
2030 }
2031 }
2032
2033 override void EEOnAfterLoad()
2034 {
2036 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2037
2038 super.EEOnAfterLoad();
2039 }
2040
2041 override void EEInit()
2042 {
2043 super.EEInit();
2044
2045 // init visuals and physics
2046 InitBaseState();
2047
2048 //debug
2049#ifdef DEVELOPER
2051#endif
2052 }
2053
2054 override void EEItemAttached(EntityAI item, string slot_name)
2055 {
2056 super.EEItemAttached(item, slot_name);
2057
2059 UpdateVisuals();
2061 }
2062
2063 override void EEItemDetached(EntityAI item, string slot_name)
2064 {
2065 super.EEItemDetached(item, slot_name);
2066
2067 UpdateVisuals();
2069 }
2070
2071 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2072 {
2074 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2075
2078 }
2079
2080 //ignore out of reach condition
2081 override bool IgnoreOutOfReachCondition()
2082 {
2083 return true;
2084 }
2085
2086 //CONSTRUCTION EVENTS
2087 //Build
2088 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2089 {
2091
2092 //check base state
2093 if (construtionPart.IsBase())
2094 {
2095 SetBaseState(true);
2096
2097 //spawn kit
2099 }
2100
2101 //register constructed parts for synchronization
2103
2104 //register action that was performed on part
2106
2107 //synchronize
2109
2110 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2111
2112 UpdateNavmesh();
2113
2114 //update visuals
2115 UpdateVisuals();
2116
2117 //reset action sync data
2118 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2119 }
2120
2121 void OnPartBuiltClient(string part_name, int action_id)
2122 {
2123 //play sound
2125 }
2126
2127 //Dismantle
2129 {
2130 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2132
2133 //register constructed parts for synchronization
2135
2136 //register action that was performed on part
2138
2139 //synchronize
2141
2142 // server part of sync, client will be synced from SetPartsFromSyncData
2144
2145 UpdateNavmesh();
2146
2147 //update visuals
2148 UpdateVisuals();
2149
2150 //reset action sync data
2151 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2152
2153 //check base state
2154 if (construtionPart.IsBase())
2155 {
2156 //Destroy construction
2157 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2158 }
2159 }
2160
2162 {
2163 //play sound
2165 }
2166
2167 //Destroy
2169 {
2170 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2172
2173 //register constructed parts for synchronization
2175
2176 //register action that was performed on part
2178
2179 //synchronize
2181
2182 // server part of sync, client will be synced from SetPartsFromSyncData
2184
2185 UpdateNavmesh();
2186
2187 //update visuals
2188 UpdateVisuals();
2189
2190 //reset action sync data
2191 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2192
2193 //check base state
2194 if (construtionPart.IsBase())
2195 {
2196 //Destroy construction
2197 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2198 }
2199 }
2200
2201 void OnPartDestroyedClient(string part_name, int action_id)
2202 {
2203 //play sound
2205 }
2206
2207 // --- UPDATE
2208 void InitBaseState()
2209 {
2210 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2211
2212 InitVisuals();
2213 UpdateNavmesh(); //regenerate navmesh
2214 GetConstruction().InitBaseState();
2215 }
2216
2217 void InitVisuals()
2218 {
2219 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2220 //check base
2221 if (!HasBase())
2222 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2223 else
2224 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2225
2226 GetConstruction().UpdateVisuals();
2227 }
2228
2229 void UpdateVisuals()
2230 {
2232
2234 foreach (string slotName : attachmentSlots)
2236
2237 //check base
2238 if (!HasBase())
2239 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2240 else
2241 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2242
2243 GetConstruction().UpdateVisuals();
2244 }
2245
2247 {
2248 string slotNameMounted = slot_name + "_Mounted";
2249 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2250
2251 if (attachment)
2252 {
2253 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2254 if (barbedWire && barbedWire.IsMounted())
2256 else
2258
2259 if (is_locked)
2260 {
2261 SetAnimationPhase(slotNameMounted, 0);
2262 SetAnimationPhase(slot_name, 1);
2263 }
2264 else
2265 {
2266 SetAnimationPhase(slotNameMounted, 1);
2267 SetAnimationPhase(slot_name, 0);
2268 }
2269 }
2270 else
2271 {
2272 SetAnimationPhase(slotNameMounted, 1);
2273 SetAnimationPhase(slot_name, 1);
2274
2276 }
2277 }
2278
2279 // avoid calling this function on frequent occasions, it's a massive performance hit
2280 void UpdatePhysics()
2281 {
2283 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2284
2287
2289 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2290
2291 foreach (string slotName : attachmentSlots)
2293
2294 //check base
2295 if (!HasBase())
2296 {
2298 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2299
2300 AddProxyPhysics(ANIMATION_DEPLOYED);
2301 }
2302 else
2303 {
2305 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2306
2307 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2308 }
2309
2310 GetConstruction().UpdatePhysics();
2311 UpdateNavmesh();
2312 }
2313
2315 {
2316 //checks for invalid appends; hotfix
2317 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2318 return;
2319 //----------------------------------
2320 string slot_name_mounted = slot_name + "_Mounted";
2321 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2322
2323 //remove proxy physics
2324 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2325 RemoveProxyPhysics(slot_name_mounted);
2326 RemoveProxyPhysics(slot_name);
2327
2328 if (attachment)
2329 {
2330 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2331 if (is_locked)
2332 {
2333 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2334 AddProxyPhysics(slot_name_mounted);
2335 }
2336 else
2337 {
2338 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2339 AddProxyPhysics(slot_name);
2340 }
2341 }
2342 }
2343
2344 protected void UpdateNavmesh()
2345 {
2346 SetAffectPathgraph(true, false);
2347 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2348 }
2349
2350 override bool CanUseConstruction()
2351 {
2352 return true;
2353 }
2354
2355 override bool CanUseConstructionBuild()
2356 {
2357 return true;
2358 }
2359
2361 {
2362 if (attachment)
2363 {
2365 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2366
2367 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2368 }
2369
2370 return false;
2371 }
2372
2373 protected bool IsAttachmentSlotLocked(string slot_name)
2374 {
2375 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2376 }
2377
2378 //--- ATTACHMENT SLOTS
2380 {
2381 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2382 if (GetGame().ConfigIsExisting(config_path))
2383 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2384 }
2385
2387 {
2388 return true;
2389 }
2390
2391 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2392 {
2393 return true;
2394 }
2395
2396 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2397 {
2398 return true;
2399 }
2400
2401 // --- INIT
2402 void ConstructionInit()
2403 {
2404 if (!m_Construction)
2405 m_Construction = new Construction(this);
2406
2407 GetConstruction().Init();
2408 }
2409
2411 {
2412 return m_Construction;
2413 }
2414
2415 //--- INVENTORY/ATTACHMENTS CONDITIONS
2416 //attachments
2418 {
2419 return super.CanReceiveAttachment(attachment, slotId);
2420 }
2421
2423 {
2424 int attachment_count = GetInventory().AttachmentCount();
2425 if (attachment_count > 0)
2426 {
2427 if (HasBase() && attachment_count == 1)
2428 return false;
2429
2430 return true;
2431 }
2432
2433 return false;
2434 }
2435
2436 override bool ShowZonesHealth()
2437 {
2438 return true;
2439 }
2440
2441 //this into/outo parent.Cargo
2442 override bool CanPutInCargo(EntityAI parent)
2443 {
2444 return false;
2445 }
2446
2447 override bool CanRemoveFromCargo(EntityAI parent)
2448 {
2449 return false;
2450 }
2451
2452 //hands
2453 override bool CanPutIntoHands(EntityAI parent)
2454 {
2455 return false;
2456 }
2457
2458 //--- ACTION CONDITIONS
2459 //direction
2460 override bool IsFacingPlayer(PlayerBase player, string selection)
2461 {
2462 return true;
2463 }
2464
2465 override bool IsPlayerInside(PlayerBase player, string selection)
2466 {
2467 return true;
2468 }
2469
2472 {
2473 return false;
2474 }
2475
2476 //camera direction check
2477 bool IsFacingCamera(string selection)
2478 {
2479 return true;
2480 }
2481
2482 //roof check
2484 {
2485 return false;
2486 }
2487
2488 //selection->player distance check
2489 bool HasProperDistance(string selection, PlayerBase player)
2490 {
2491 return true;
2492 }
2493
2494 //folding
2496 {
2497 if (HasBase() || GetInventory().AttachmentCount() > 0)
2498 return false;
2499
2500 return true;
2501 }
2502
2504 {
2507
2508 return item;
2509 }
2510
2511 //Damage triggers (barbed wire)
2512 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2513 {
2514 if (GetGame() && GetGame().IsServer())
2515 {
2516 //destroy area damage if some already exists
2518
2519 //create new area damage
2521 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2522
2523 vector min_max[2];
2524 if (MemoryPointExists(slot_name + "_min"))
2525 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2526 if (MemoryPointExists(slot_name + "_max"))
2527 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2528
2529 //get proper trigger extents (min<max)
2530 vector extents[2];
2531 GetConstruction().GetTriggerExtents(min_max, extents);
2532
2533 //get box center
2534 vector center;
2535 center = GetConstruction().GetBoxCenter(min_max);
2536 center = ModelToWorld(center);
2537
2538 //rotate center if needed
2541
2542 areaDamage.SetExtents(extents[0], extents[1]);
2543 areaDamage.SetAreaPosition(center);
2544 areaDamage.SetAreaOrientation(orientation);
2545 areaDamage.SetLoopInterval(1.0);
2546 areaDamage.SetDeferDuration(0.2);
2547 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2548 areaDamage.SetAmmoName("BarbedWireHit");
2549 areaDamage.Spawn();
2550
2552 }
2553 }
2554
2556 {
2557 if (angle_deg != 0)
2558 {
2559 //orientation
2561
2562 //center
2564 if (MemoryPointExists("rotate_axis"))
2565 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2568 center[0] = r_center_x;
2569 center[2] = r_center_z;
2570 }
2571 }
2572
2573 void DestroyAreaDamage(string slot_name)
2574 {
2575 if (GetGame() && GetGame().IsServer())
2576 {
2579 {
2580 if (areaDamage)
2581 areaDamage.Destroy();
2582
2584 }
2585 }
2586 }
2587
2588 override bool IsIgnoredByConstruction()
2589 {
2590 return true;
2591 }
2592
2593 //================================================================
2594 // SOUNDS
2595 //================================================================
2596 protected void SoundBuildStart(string part_name)
2597 {
2598 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2599 }
2600
2601 protected void SoundDismantleStart(string part_name)
2602 {
2603 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2604 }
2605
2606 protected void SoundDestroyStart(string part_name)
2607 {
2608 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2609 }
2610
2611 protected string GetBuildSoundByMaterial(string part_name)
2612 {
2614
2615 switch (material_type)
2616 {
2617 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2618 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2619 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2620 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2621 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2622 }
2623
2624 return "";
2625 }
2626
2627 protected string GetDismantleSoundByMaterial(string part_name)
2628 {
2630
2631 switch (material_type)
2632 {
2633 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2634 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2635 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2636 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2637 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2638 }
2639
2640 return "";
2641 }
2642
2643 //misc
2645 {
2646 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2647 {
2648 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2650 SetHealth(slot_name, "Health", item.GetHealth());
2651 }
2652 }
2653
2654 override int GetDamageSystemVersionChange()
2655 {
2656 return 111;
2657 }
2658
2659 override void SetActions()
2660 {
2661 super.SetActions();
2662
2664 //AddAction(ActionTakeHybridAttachment);
2665 //AddAction(ActionTakeHybridAttachmentToHands);
2668 }
2669
2670 //================================================================
2671 // DEBUG
2672 //================================================================
2673 protected void DebugCustomState()
2674 {
2675 }
2676
2679 {
2680 return null;
2681 }
2682
2683 override void OnDebugSpawn()
2684 {
2685 FullyBuild();
2686 }
2687
2688 void FullyBuild()
2689 {
2691 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2692
2693 Man p;
2694
2695#ifdef SERVER
2697 GetGame().GetWorld().GetPlayerList(players);
2698 if (players.Count())
2699 p = players[0];
2700#else
2701 p = GetGame().GetPlayer();
2702#endif
2703
2704 foreach (ConstructionPart part : parts)
2705 {
2706 bool excluded = false;
2707 string partName = part.GetPartName();
2708 if (excludes)
2709 {
2710 foreach (string exclude : excludes)
2711 {
2712 if (partName.Contains(exclude))
2713 {
2714 excluded = true;
2715 break;
2716 }
2717 }
2718 }
2719
2720 if (!excluded)
2722 }
2723
2724 GetConstruction().UpdateVisuals();
2725 }
2726}
2727
2728void bsbDebugPrint(string s)
2729{
2730#ifdef BSB_DEBUG
2731 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2732#else
2733 //Print("" + s); // comment/uncomment to hide/see debug logs
2734#endif
2735}
2736void bsbDebugSpam(string s)
2737{
2738#ifdef BSB_DEBUG_SPAM
2739 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2740#else
2741 //Print("" + s); // comment/uncomment to hide/see debug logs
2742#endif
2743}

◆ OnSynchronizedClient()

void bsbDebugPrint::OnSynchronizedClient ( )
protected

Definition at line 1280 of file BaseBuildingBase.c.

1282{
1283 const string ANIMATION_DEPLOYED = "Deployed";
1284
1285 float m_ConstructionKitHealth; //stored health value for used construction kit
1286
1288
1289 bool m_HasBase;
1290 //variables for synchronization of base building parts (2x31 is the current limit)
1291 int m_SyncParts01; //synchronization for already built parts (31 parts)
1292 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1293 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1294 int m_InteractedPartId; //construction part id that an action was performed on
1295 int m_PerformedActionId; //action id that was performed on a construction part
1296
1297 //Sounds
1298 //build
1299 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1300 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1301 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1302 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1303 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1304 //dismantle
1305 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1306 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1307 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1308 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1309 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1310
1311 protected EffectSound m_Sound;
1312
1316
1317 // Constructor
1318 void BaseBuildingBase()
1319 {
1321
1322 //synchronized variables
1323 RegisterNetSyncVariableInt("m_SyncParts01");
1324 RegisterNetSyncVariableInt("m_SyncParts02");
1325 RegisterNetSyncVariableInt("m_SyncParts03");
1326 RegisterNetSyncVariableInt("m_InteractedPartId");
1327 RegisterNetSyncVariableInt("m_PerformedActionId");
1328 RegisterNetSyncVariableBool("m_HasBase");
1329
1330 //Construction init
1332
1333 if (ConfigIsExisting("hybridAttachments"))
1334 {
1336 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1337 }
1338 if (ConfigIsExisting("mountables"))
1339 {
1341 ConfigGetTextArray("mountables", m_Mountables);
1342 }
1343
1344 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1345 }
1346
1347 override void EEDelete(EntityAI parent)
1348 {
1349 super.EEDelete(parent);
1350
1351 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1353
1354 }
1355
1356 override string GetInvulnerabilityTypeString()
1357 {
1358 return "disableBaseDamage";
1359 }
1360
1361 override bool CanObstruct()
1362 {
1363 return true;
1364 }
1365
1366 override int GetHideIconMask()
1367 {
1368 return EInventoryIconVisibility.HIDE_VICINITY;
1369 }
1370
1371 // --- SYNCHRONIZATION
1373 {
1374 if (GetGame().IsServer())
1375 SetSynchDirty();
1376 }
1377
1378 override void OnVariablesSynchronized()
1379 {
1380 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1381 super.OnVariablesSynchronized();
1382
1383 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1384 }
1385
1386 protected void OnSynchronizedClient()
1387 {
1388 //update parts
1390
1391 //update action on part
1393
1394 //update visuals (client)
1395 UpdateVisuals();
1396 }
1397
1398 //parts synchronization
1400 {
1401 //part_id must starts from index = 1
1402 int offset;
1403 int mask;
1404
1405 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1406 {
1407 offset = part_id - 1;
1408 mask = 1 << offset;
1409
1411 }
1412 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1413 {
1414 offset = (part_id % 32);
1415 mask = 1 << offset;
1416
1418 }
1419 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1420 {
1421 offset = (part_id % 63);
1422 mask = 1 << offset;
1423
1425 }
1426 }
1427
1429 {
1430 //part_id must starts from index = 1
1431 int offset;
1432 int mask;
1433
1434 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1435 {
1436 offset = part_id - 1;
1437 mask = 1 << offset;
1438
1440 }
1441 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1442 {
1443 offset = (part_id % 32);
1444 mask = 1 << offset;
1445
1447 }
1448 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1449 {
1450 offset = (part_id % 63);
1451 mask = 1 << offset;
1452
1454 }
1455 }
1456
1458 {
1459 //part_id must starts from index = 1
1460 int offset;
1461 int mask;
1462
1463 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1464 {
1465 offset = part_id - 1;
1466 mask = 1 << offset;
1467
1468 if ((m_SyncParts01 & mask) > 0)
1469 return true;
1470 }
1471 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1472 {
1473 offset = (part_id % 32);
1474 mask = 1 << offset;
1475
1476 if ((m_SyncParts02 & mask) > 0)
1477 return true;
1478 }
1479 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1480 {
1481 offset = (part_id % 63);
1482 mask = 1 << offset;
1483
1484 if ((m_SyncParts03 & mask) > 0)
1485 return true;
1486 }
1487
1488 return false;
1489 }
1490
1491 protected void RegisterActionForSync(int part_id, int action_id)
1492 {
1495 }
1496
1497 protected void ResetActionSyncData()
1498 {
1499 //reset data
1500 m_InteractedPartId = -1;
1502 }
1503
1504 protected void SetActionFromSyncData()
1505 {
1506 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1507 {
1510
1511 switch (build_action_id)
1512 {
1516 }
1517 }
1518 }
1519 //------
1520
1522 {
1523 string key = part.m_PartName;
1524 bool is_base = part.IsBase();
1526 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1528 {
1529 if (!part.IsBuilt())
1530 {
1531 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1532 GetConstruction().AddToConstructedParts(key);
1533 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1534
1535 if (is_base)
1536 {
1538 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1539 }
1540 }
1541 }
1542 else
1543 {
1544 if (part.IsBuilt())
1545 {
1546 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1547 GetConstruction().RemoveFromConstructedParts(key);
1548 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1549
1550 if (is_base)
1551 {
1553 AddProxyPhysics(ANIMATION_DEPLOYED);
1554 }
1555 }
1556 }
1557
1558 //check slot lock for material attachments
1559 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1560 }
1561
1562 //set construction parts based on synchronized data
1564 {
1567
1568 for (int i = 0; i < construction_parts.Count(); ++i)
1569 {
1570 string key = construction_parts.GetKey(i);
1573 }
1574
1575 //regenerate navmesh
1576 UpdateNavmesh();
1577 }
1578
1580 {
1583
1584 for (int i = 0; i < construction_parts.Count(); ++i)
1585 {
1586 string key = construction_parts.GetKey(i);
1588
1589 if (value.GetId() == id)
1590 return value;
1591 }
1592
1593 return NULL;
1594 }
1595 //
1596
1597 //Base
1598 bool HasBase()
1599 {
1600 return m_HasBase;
1601 }
1602
1603 void SetBaseState(bool has_base)
1604 {
1606 }
1607
1608 override bool IsDeployable()
1609 {
1610 return true;
1611 }
1612
1613 bool IsOpened()
1614 {
1615 return false;
1616 }
1617
1618 //--- CONSTRUCTION KIT
1620 {
1624
1625 return construction_kit;
1626 }
1627
1629 {
1630 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1633 }
1634
1635 protected vector GetKitSpawnPosition()
1636 {
1637 return GetPosition();
1638 }
1639
1640 protected string GetConstructionKitType()
1641 {
1642 return "";
1643 }
1644
1646 {
1648 GetGame().ObjectDelete(construction_kit);
1649 }
1650
1651 //--- CONSTRUCTION
1652 void DestroyConstruction()
1653 {
1654 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1655 GetGame().ObjectDelete(this);
1656 }
1657
1658 // --- EVENTS
1659 override void OnStoreSave(ParamsWriteContext ctx)
1660 {
1661 super.OnStoreSave(ctx);
1662
1663 //sync parts 01
1664 ctx.Write(m_SyncParts01);
1665 ctx.Write(m_SyncParts02);
1666 ctx.Write(m_SyncParts03);
1667
1668 ctx.Write(m_HasBase);
1669 }
1670
1671 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1672 {
1673 if (!super.OnStoreLoad(ctx, version))
1674 return false;
1675
1676 //--- Base building data ---
1677 //Restore synced parts data
1678 if (!ctx.Read(m_SyncParts01))
1679 {
1680 m_SyncParts01 = 0; //set default
1681 return false;
1682 }
1683 if (!ctx.Read(m_SyncParts02))
1684 {
1685 m_SyncParts02 = 0; //set default
1686 return false;
1687 }
1688 if (!ctx.Read(m_SyncParts03))
1689 {
1690 m_SyncParts03 = 0; //set default
1691 return false;
1692 }
1693
1694 //has base
1695 if (!ctx.Read(m_HasBase))
1696 {
1697 m_HasBase = false;
1698 return false;
1699 }
1700 //---
1701
1702 return true;
1703 }
1704
1705 override void AfterStoreLoad()
1706 {
1707 super.AfterStoreLoad();
1708
1711 }
1712
1714 {
1715 //update server data
1717
1718 //set base state
1719 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1720 SetBaseState(construction_part.IsBuilt()) ;
1721
1722 //synchronize after load
1724 }
1725
1726 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1727 {
1729 return;
1730
1731 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1732
1733 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1734 return;
1735
1737 string part_name = zone;
1738 part_name.ToLower();
1739
1741 {
1743
1744 if (construction_part && construction.IsPartConstructed(part_name))
1745 {
1746 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1747 construction.DestroyConnectedParts(part_name);
1748 }
1749
1750 //barbed wire handling (hack-ish)
1751 if (part_name.Contains("barbed"))
1752 {
1753 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1754 if (barbed_wire)
1755 barbed_wire.SetMountedState(false);
1756 }
1757 }
1758 }
1759
1760 override void EEOnAfterLoad()
1761 {
1763 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1764
1765 super.EEOnAfterLoad();
1766 }
1767
1768 override void EEInit()
1769 {
1770 super.EEInit();
1771
1772 // init visuals and physics
1773 InitBaseState();
1774
1775 //debug
1776#ifdef DEVELOPER
1778#endif
1779 }
1780
1781 override void EEItemAttached(EntityAI item, string slot_name)
1782 {
1783 super.EEItemAttached(item, slot_name);
1784
1786 UpdateVisuals();
1788 }
1789
1790 override void EEItemDetached(EntityAI item, string slot_name)
1791 {
1792 super.EEItemDetached(item, slot_name);
1793
1794 UpdateVisuals();
1796 }
1797
1798 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
1799 {
1801 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
1802
1805 }
1806
1807 //ignore out of reach condition
1808 override bool IgnoreOutOfReachCondition()
1809 {
1810 return true;
1811 }
1812
1813 //CONSTRUCTION EVENTS
1814 //Build
1815 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
1816 {
1818
1819 //check base state
1820 if (construtionPart.IsBase())
1821 {
1822 SetBaseState(true);
1823
1824 //spawn kit
1826 }
1827
1828 //register constructed parts for synchronization
1830
1831 //register action that was performed on part
1833
1834 //synchronize
1836
1837 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
1838
1839 UpdateNavmesh();
1840
1841 //update visuals
1842 UpdateVisuals();
1843
1844 //reset action sync data
1845 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1846 }
1847
1848 void OnPartBuiltClient(string part_name, int action_id)
1849 {
1850 //play sound
1852 }
1853
1854 //Dismantle
1856 {
1857 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
1859
1860 //register constructed parts for synchronization
1862
1863 //register action that was performed on part
1865
1866 //synchronize
1868
1869 // server part of sync, client will be synced from SetPartsFromSyncData
1871
1872 UpdateNavmesh();
1873
1874 //update visuals
1875 UpdateVisuals();
1876
1877 //reset action sync data
1878 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1879
1880 //check base state
1881 if (construtionPart.IsBase())
1882 {
1883 //Destroy construction
1884 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1885 }
1886 }
1887
1889 {
1890 //play sound
1892 }
1893
1894 //Destroy
1896 {
1897 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
1899
1900 //register constructed parts for synchronization
1902
1903 //register action that was performed on part
1905
1906 //synchronize
1908
1909 // server part of sync, client will be synced from SetPartsFromSyncData
1911
1912 UpdateNavmesh();
1913
1914 //update visuals
1915 UpdateVisuals();
1916
1917 //reset action sync data
1918 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1919
1920 //check base state
1921 if (construtionPart.IsBase())
1922 {
1923 //Destroy construction
1924 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1925 }
1926 }
1927
1928 void OnPartDestroyedClient(string part_name, int action_id)
1929 {
1930 //play sound
1932 }
1933
1934 // --- UPDATE
1935 void InitBaseState()
1936 {
1937 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
1938
1939 InitVisuals();
1940 UpdateNavmesh(); //regenerate navmesh
1941 GetConstruction().InitBaseState();
1942 }
1943
1944 void InitVisuals()
1945 {
1946 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
1947 //check base
1948 if (!HasBase())
1949 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1950 else
1951 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1952
1953 GetConstruction().UpdateVisuals();
1954 }
1955
1956 void UpdateVisuals()
1957 {
1959
1961 foreach (string slotName : attachmentSlots)
1963
1964 //check base
1965 if (!HasBase())
1966 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1967 else
1968 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1969
1970 GetConstruction().UpdateVisuals();
1971 }
1972
1974 {
1975 string slotNameMounted = slot_name + "_Mounted";
1976 EntityAI attachment = FindAttachmentBySlotName(slot_name);
1977
1978 if (attachment)
1979 {
1980 BarbedWire barbedWire = BarbedWire.Cast(attachment);
1981 if (barbedWire && barbedWire.IsMounted())
1983 else
1985
1986 if (is_locked)
1987 {
1988 SetAnimationPhase(slotNameMounted, 0);
1989 SetAnimationPhase(slot_name, 1);
1990 }
1991 else
1992 {
1993 SetAnimationPhase(slotNameMounted, 1);
1994 SetAnimationPhase(slot_name, 0);
1995 }
1996 }
1997 else
1998 {
1999 SetAnimationPhase(slotNameMounted, 1);
2000 SetAnimationPhase(slot_name, 1);
2001
2003 }
2004 }
2005
2006 // avoid calling this function on frequent occasions, it's a massive performance hit
2007 void UpdatePhysics()
2008 {
2010 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2011
2014
2016 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2017
2018 foreach (string slotName : attachmentSlots)
2020
2021 //check base
2022 if (!HasBase())
2023 {
2025 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2026
2027 AddProxyPhysics(ANIMATION_DEPLOYED);
2028 }
2029 else
2030 {
2032 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2033
2034 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2035 }
2036
2037 GetConstruction().UpdatePhysics();
2038 UpdateNavmesh();
2039 }
2040
2042 {
2043 //checks for invalid appends; hotfix
2044 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2045 return;
2046 //----------------------------------
2047 string slot_name_mounted = slot_name + "_Mounted";
2048 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2049
2050 //remove proxy physics
2051 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2052 RemoveProxyPhysics(slot_name_mounted);
2053 RemoveProxyPhysics(slot_name);
2054
2055 if (attachment)
2056 {
2057 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2058 if (is_locked)
2059 {
2060 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2061 AddProxyPhysics(slot_name_mounted);
2062 }
2063 else
2064 {
2065 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2066 AddProxyPhysics(slot_name);
2067 }
2068 }
2069 }
2070
2071 protected void UpdateNavmesh()
2072 {
2073 SetAffectPathgraph(true, false);
2074 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2075 }
2076
2077 override bool CanUseConstruction()
2078 {
2079 return true;
2080 }
2081
2082 override bool CanUseConstructionBuild()
2083 {
2084 return true;
2085 }
2086
2088 {
2089 if (attachment)
2090 {
2092 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2093
2094 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2095 }
2096
2097 return false;
2098 }
2099
2100 protected bool IsAttachmentSlotLocked(string slot_name)
2101 {
2102 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2103 }
2104
2105 //--- ATTACHMENT SLOTS
2107 {
2108 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2109 if (GetGame().ConfigIsExisting(config_path))
2110 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2111 }
2112
2114 {
2115 return true;
2116 }
2117
2118 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2119 {
2120 return true;
2121 }
2122
2123 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2124 {
2125 return true;
2126 }
2127
2128 // --- INIT
2129 void ConstructionInit()
2130 {
2131 if (!m_Construction)
2132 m_Construction = new Construction(this);
2133
2134 GetConstruction().Init();
2135 }
2136
2138 {
2139 return m_Construction;
2140 }
2141
2142 //--- INVENTORY/ATTACHMENTS CONDITIONS
2143 //attachments
2145 {
2146 return super.CanReceiveAttachment(attachment, slotId);
2147 }
2148
2150 {
2151 int attachment_count = GetInventory().AttachmentCount();
2152 if (attachment_count > 0)
2153 {
2154 if (HasBase() && attachment_count == 1)
2155 return false;
2156
2157 return true;
2158 }
2159
2160 return false;
2161 }
2162
2163 override bool ShowZonesHealth()
2164 {
2165 return true;
2166 }
2167
2168 //this into/outo parent.Cargo
2169 override bool CanPutInCargo(EntityAI parent)
2170 {
2171 return false;
2172 }
2173
2174 override bool CanRemoveFromCargo(EntityAI parent)
2175 {
2176 return false;
2177 }
2178
2179 //hands
2180 override bool CanPutIntoHands(EntityAI parent)
2181 {
2182 return false;
2183 }
2184
2185 //--- ACTION CONDITIONS
2186 //direction
2187 override bool IsFacingPlayer(PlayerBase player, string selection)
2188 {
2189 return true;
2190 }
2191
2192 override bool IsPlayerInside(PlayerBase player, string selection)
2193 {
2194 return true;
2195 }
2196
2199 {
2200 return false;
2201 }
2202
2203 //camera direction check
2204 bool IsFacingCamera(string selection)
2205 {
2206 return true;
2207 }
2208
2209 //roof check
2211 {
2212 return false;
2213 }
2214
2215 //selection->player distance check
2216 bool HasProperDistance(string selection, PlayerBase player)
2217 {
2218 return true;
2219 }
2220
2221 //folding
2223 {
2224 if (HasBase() || GetInventory().AttachmentCount() > 0)
2225 return false;
2226
2227 return true;
2228 }
2229
2231 {
2234
2235 return item;
2236 }
2237
2238 //Damage triggers (barbed wire)
2239 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2240 {
2241 if (GetGame() && GetGame().IsServer())
2242 {
2243 //destroy area damage if some already exists
2245
2246 //create new area damage
2248 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2249
2250 vector min_max[2];
2251 if (MemoryPointExists(slot_name + "_min"))
2252 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2253 if (MemoryPointExists(slot_name + "_max"))
2254 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2255
2256 //get proper trigger extents (min<max)
2257 vector extents[2];
2258 GetConstruction().GetTriggerExtents(min_max, extents);
2259
2260 //get box center
2261 vector center;
2262 center = GetConstruction().GetBoxCenter(min_max);
2263 center = ModelToWorld(center);
2264
2265 //rotate center if needed
2268
2269 areaDamage.SetExtents(extents[0], extents[1]);
2270 areaDamage.SetAreaPosition(center);
2271 areaDamage.SetAreaOrientation(orientation);
2272 areaDamage.SetLoopInterval(1.0);
2273 areaDamage.SetDeferDuration(0.2);
2274 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2275 areaDamage.SetAmmoName("BarbedWireHit");
2276 areaDamage.Spawn();
2277
2279 }
2280 }
2281
2283 {
2284 if (angle_deg != 0)
2285 {
2286 //orientation
2288
2289 //center
2291 if (MemoryPointExists("rotate_axis"))
2292 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2295 center[0] = r_center_x;
2296 center[2] = r_center_z;
2297 }
2298 }
2299
2300 void DestroyAreaDamage(string slot_name)
2301 {
2302 if (GetGame() && GetGame().IsServer())
2303 {
2306 {
2307 if (areaDamage)
2308 areaDamage.Destroy();
2309
2311 }
2312 }
2313 }
2314
2315 override bool IsIgnoredByConstruction()
2316 {
2317 return true;
2318 }
2319
2320 //================================================================
2321 // SOUNDS
2322 //================================================================
2323 protected void SoundBuildStart(string part_name)
2324 {
2325 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2326 }
2327
2328 protected void SoundDismantleStart(string part_name)
2329 {
2330 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2331 }
2332
2333 protected void SoundDestroyStart(string part_name)
2334 {
2335 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2336 }
2337
2338 protected string GetBuildSoundByMaterial(string part_name)
2339 {
2341
2342 switch (material_type)
2343 {
2344 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2345 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2346 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2347 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2348 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2349 }
2350
2351 return "";
2352 }
2353
2354 protected string GetDismantleSoundByMaterial(string part_name)
2355 {
2357
2358 switch (material_type)
2359 {
2360 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2361 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2362 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2363 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2364 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2365 }
2366
2367 return "";
2368 }
2369
2370 //misc
2372 {
2373 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2374 {
2375 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2377 SetHealth(slot_name, "Health", item.GetHealth());
2378 }
2379 }
2380
2381 override int GetDamageSystemVersionChange()
2382 {
2383 return 111;
2384 }
2385
2386 override void SetActions()
2387 {
2388 super.SetActions();
2389
2391 //AddAction(ActionTakeHybridAttachment);
2392 //AddAction(ActionTakeHybridAttachmentToHands);
2395 }
2396
2397 //================================================================
2398 // DEBUG
2399 //================================================================
2400 protected void DebugCustomState()
2401 {
2402 }
2403
2406 {
2407 return null;
2408 }
2409
2410 override void OnDebugSpawn()
2411 {
2412 FullyBuild();
2413 }
2414
2415 void FullyBuild()
2416 {
2418 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2419
2420 Man p;
2421
2422#ifdef SERVER
2424 GetGame().GetWorld().GetPlayerList(players);
2425 if (players.Count())
2426 p = players[0];
2427#else
2428 p = GetGame().GetPlayer();
2429#endif
2430
2431 foreach (ConstructionPart part : parts)
2432 {
2433 bool excluded = false;
2434 string partName = part.GetPartName();
2435 if (excludes)
2436 {
2437 foreach (string exclude : excludes)
2438 {
2439 if (partName.Contains(exclude))
2440 {
2441 excluded = true;
2442 break;
2443 }
2444 }
2445 }
2446
2447 if (!excluded)
2449 }
2450
2451 GetConstruction().UpdateVisuals();
2452 }
2453}
2454
2455void bsbDebugPrint(string s)
2456{
2457#ifdef BSB_DEBUG
2458 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2459#else
2460 //Print("" + s); // comment/uncomment to hide/see debug logs
2461#endif
2462}
2463void bsbDebugSpam(string s)
2464{
2465#ifdef BSB_DEBUG_SPAM
2466 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2467#else
2468 //Print("" + s); // comment/uncomment to hide/see debug logs
2469#endif
2470}

Referenced by ItemBase::OnVariablesSynchronized().

◆ OnVariablesSynchronized()

override void bsbDebugPrint::OnVariablesSynchronized ( )
protected

Definition at line 1272 of file BaseBuildingBase.c.

1274{
1275 const string ANIMATION_DEPLOYED = "Deployed";
1276
1277 float m_ConstructionKitHealth; //stored health value for used construction kit
1278
1280
1281 bool m_HasBase;
1282 //variables for synchronization of base building parts (2x31 is the current limit)
1283 int m_SyncParts01; //synchronization for already built parts (31 parts)
1284 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1285 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1286 int m_InteractedPartId; //construction part id that an action was performed on
1287 int m_PerformedActionId; //action id that was performed on a construction part
1288
1289 //Sounds
1290 //build
1291 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1292 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1293 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1294 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1295 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1296 //dismantle
1297 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1298 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1299 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1300 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1301 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1302
1303 protected EffectSound m_Sound;
1304
1308
1309 // Constructor
1310 void BaseBuildingBase()
1311 {
1313
1314 //synchronized variables
1315 RegisterNetSyncVariableInt("m_SyncParts01");
1316 RegisterNetSyncVariableInt("m_SyncParts02");
1317 RegisterNetSyncVariableInt("m_SyncParts03");
1318 RegisterNetSyncVariableInt("m_InteractedPartId");
1319 RegisterNetSyncVariableInt("m_PerformedActionId");
1320 RegisterNetSyncVariableBool("m_HasBase");
1321
1322 //Construction init
1324
1325 if (ConfigIsExisting("hybridAttachments"))
1326 {
1328 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1329 }
1330 if (ConfigIsExisting("mountables"))
1331 {
1333 ConfigGetTextArray("mountables", m_Mountables);
1334 }
1335
1336 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1337 }
1338
1339 override void EEDelete(EntityAI parent)
1340 {
1341 super.EEDelete(parent);
1342
1343 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1345
1346 }
1347
1348 override string GetInvulnerabilityTypeString()
1349 {
1350 return "disableBaseDamage";
1351 }
1352
1353 override bool CanObstruct()
1354 {
1355 return true;
1356 }
1357
1358 override int GetHideIconMask()
1359 {
1360 return EInventoryIconVisibility.HIDE_VICINITY;
1361 }
1362
1363 // --- SYNCHRONIZATION
1365 {
1366 if (GetGame().IsServer())
1367 SetSynchDirty();
1368 }
1369
1370 override void OnVariablesSynchronized()
1371 {
1372 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1373 super.OnVariablesSynchronized();
1374
1375 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1376 }
1377
1378 protected void OnSynchronizedClient()
1379 {
1380 //update parts
1382
1383 //update action on part
1385
1386 //update visuals (client)
1387 UpdateVisuals();
1388 }
1389
1390 //parts synchronization
1392 {
1393 //part_id must starts from index = 1
1394 int offset;
1395 int mask;
1396
1397 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1398 {
1399 offset = part_id - 1;
1400 mask = 1 << offset;
1401
1403 }
1404 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1405 {
1406 offset = (part_id % 32);
1407 mask = 1 << offset;
1408
1410 }
1411 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1412 {
1413 offset = (part_id % 63);
1414 mask = 1 << offset;
1415
1417 }
1418 }
1419
1421 {
1422 //part_id must starts from index = 1
1423 int offset;
1424 int mask;
1425
1426 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1427 {
1428 offset = part_id - 1;
1429 mask = 1 << offset;
1430
1432 }
1433 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1434 {
1435 offset = (part_id % 32);
1436 mask = 1 << offset;
1437
1439 }
1440 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1441 {
1442 offset = (part_id % 63);
1443 mask = 1 << offset;
1444
1446 }
1447 }
1448
1450 {
1451 //part_id must starts from index = 1
1452 int offset;
1453 int mask;
1454
1455 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1456 {
1457 offset = part_id - 1;
1458 mask = 1 << offset;
1459
1460 if ((m_SyncParts01 & mask) > 0)
1461 return true;
1462 }
1463 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1464 {
1465 offset = (part_id % 32);
1466 mask = 1 << offset;
1467
1468 if ((m_SyncParts02 & mask) > 0)
1469 return true;
1470 }
1471 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1472 {
1473 offset = (part_id % 63);
1474 mask = 1 << offset;
1475
1476 if ((m_SyncParts03 & mask) > 0)
1477 return true;
1478 }
1479
1480 return false;
1481 }
1482
1483 protected void RegisterActionForSync(int part_id, int action_id)
1484 {
1487 }
1488
1489 protected void ResetActionSyncData()
1490 {
1491 //reset data
1492 m_InteractedPartId = -1;
1494 }
1495
1496 protected void SetActionFromSyncData()
1497 {
1498 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1499 {
1502
1503 switch (build_action_id)
1504 {
1508 }
1509 }
1510 }
1511 //------
1512
1514 {
1515 string key = part.m_PartName;
1516 bool is_base = part.IsBase();
1518 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1520 {
1521 if (!part.IsBuilt())
1522 {
1523 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1524 GetConstruction().AddToConstructedParts(key);
1525 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1526
1527 if (is_base)
1528 {
1530 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1531 }
1532 }
1533 }
1534 else
1535 {
1536 if (part.IsBuilt())
1537 {
1538 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1539 GetConstruction().RemoveFromConstructedParts(key);
1540 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1541
1542 if (is_base)
1543 {
1545 AddProxyPhysics(ANIMATION_DEPLOYED);
1546 }
1547 }
1548 }
1549
1550 //check slot lock for material attachments
1551 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1552 }
1553
1554 //set construction parts based on synchronized data
1556 {
1559
1560 for (int i = 0; i < construction_parts.Count(); ++i)
1561 {
1562 string key = construction_parts.GetKey(i);
1565 }
1566
1567 //regenerate navmesh
1568 UpdateNavmesh();
1569 }
1570
1572 {
1575
1576 for (int i = 0; i < construction_parts.Count(); ++i)
1577 {
1578 string key = construction_parts.GetKey(i);
1580
1581 if (value.GetId() == id)
1582 return value;
1583 }
1584
1585 return NULL;
1586 }
1587 //
1588
1589 //Base
1590 bool HasBase()
1591 {
1592 return m_HasBase;
1593 }
1594
1595 void SetBaseState(bool has_base)
1596 {
1598 }
1599
1600 override bool IsDeployable()
1601 {
1602 return true;
1603 }
1604
1605 bool IsOpened()
1606 {
1607 return false;
1608 }
1609
1610 //--- CONSTRUCTION KIT
1612 {
1616
1617 return construction_kit;
1618 }
1619
1621 {
1622 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1625 }
1626
1627 protected vector GetKitSpawnPosition()
1628 {
1629 return GetPosition();
1630 }
1631
1632 protected string GetConstructionKitType()
1633 {
1634 return "";
1635 }
1636
1638 {
1640 GetGame().ObjectDelete(construction_kit);
1641 }
1642
1643 //--- CONSTRUCTION
1644 void DestroyConstruction()
1645 {
1646 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1647 GetGame().ObjectDelete(this);
1648 }
1649
1650 // --- EVENTS
1651 override void OnStoreSave(ParamsWriteContext ctx)
1652 {
1653 super.OnStoreSave(ctx);
1654
1655 //sync parts 01
1656 ctx.Write(m_SyncParts01);
1657 ctx.Write(m_SyncParts02);
1658 ctx.Write(m_SyncParts03);
1659
1660 ctx.Write(m_HasBase);
1661 }
1662
1663 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1664 {
1665 if (!super.OnStoreLoad(ctx, version))
1666 return false;
1667
1668 //--- Base building data ---
1669 //Restore synced parts data
1670 if (!ctx.Read(m_SyncParts01))
1671 {
1672 m_SyncParts01 = 0; //set default
1673 return false;
1674 }
1675 if (!ctx.Read(m_SyncParts02))
1676 {
1677 m_SyncParts02 = 0; //set default
1678 return false;
1679 }
1680 if (!ctx.Read(m_SyncParts03))
1681 {
1682 m_SyncParts03 = 0; //set default
1683 return false;
1684 }
1685
1686 //has base
1687 if (!ctx.Read(m_HasBase))
1688 {
1689 m_HasBase = false;
1690 return false;
1691 }
1692 //---
1693
1694 return true;
1695 }
1696
1697 override void AfterStoreLoad()
1698 {
1699 super.AfterStoreLoad();
1700
1703 }
1704
1706 {
1707 //update server data
1709
1710 //set base state
1711 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1712 SetBaseState(construction_part.IsBuilt()) ;
1713
1714 //synchronize after load
1716 }
1717
1718 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1719 {
1721 return;
1722
1723 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1724
1725 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1726 return;
1727
1729 string part_name = zone;
1730 part_name.ToLower();
1731
1733 {
1735
1736 if (construction_part && construction.IsPartConstructed(part_name))
1737 {
1738 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1739 construction.DestroyConnectedParts(part_name);
1740 }
1741
1742 //barbed wire handling (hack-ish)
1743 if (part_name.Contains("barbed"))
1744 {
1745 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1746 if (barbed_wire)
1747 barbed_wire.SetMountedState(false);
1748 }
1749 }
1750 }
1751
1752 override void EEOnAfterLoad()
1753 {
1755 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1756
1757 super.EEOnAfterLoad();
1758 }
1759
1760 override void EEInit()
1761 {
1762 super.EEInit();
1763
1764 // init visuals and physics
1765 InitBaseState();
1766
1767 //debug
1768#ifdef DEVELOPER
1770#endif
1771 }
1772
1773 override void EEItemAttached(EntityAI item, string slot_name)
1774 {
1775 super.EEItemAttached(item, slot_name);
1776
1778 UpdateVisuals();
1780 }
1781
1782 override void EEItemDetached(EntityAI item, string slot_name)
1783 {
1784 super.EEItemDetached(item, slot_name);
1785
1786 UpdateVisuals();
1788 }
1789
1790 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
1791 {
1793 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
1794
1797 }
1798
1799 //ignore out of reach condition
1800 override bool IgnoreOutOfReachCondition()
1801 {
1802 return true;
1803 }
1804
1805 //CONSTRUCTION EVENTS
1806 //Build
1807 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
1808 {
1810
1811 //check base state
1812 if (construtionPart.IsBase())
1813 {
1814 SetBaseState(true);
1815
1816 //spawn kit
1818 }
1819
1820 //register constructed parts for synchronization
1822
1823 //register action that was performed on part
1825
1826 //synchronize
1828
1829 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
1830
1831 UpdateNavmesh();
1832
1833 //update visuals
1834 UpdateVisuals();
1835
1836 //reset action sync data
1837 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1838 }
1839
1840 void OnPartBuiltClient(string part_name, int action_id)
1841 {
1842 //play sound
1844 }
1845
1846 //Dismantle
1848 {
1849 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
1851
1852 //register constructed parts for synchronization
1854
1855 //register action that was performed on part
1857
1858 //synchronize
1860
1861 // server part of sync, client will be synced from SetPartsFromSyncData
1863
1864 UpdateNavmesh();
1865
1866 //update visuals
1867 UpdateVisuals();
1868
1869 //reset action sync data
1870 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1871
1872 //check base state
1873 if (construtionPart.IsBase())
1874 {
1875 //Destroy construction
1876 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1877 }
1878 }
1879
1881 {
1882 //play sound
1884 }
1885
1886 //Destroy
1888 {
1889 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
1891
1892 //register constructed parts for synchronization
1894
1895 //register action that was performed on part
1897
1898 //synchronize
1900
1901 // server part of sync, client will be synced from SetPartsFromSyncData
1903
1904 UpdateNavmesh();
1905
1906 //update visuals
1907 UpdateVisuals();
1908
1909 //reset action sync data
1910 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1911
1912 //check base state
1913 if (construtionPart.IsBase())
1914 {
1915 //Destroy construction
1916 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1917 }
1918 }
1919
1920 void OnPartDestroyedClient(string part_name, int action_id)
1921 {
1922 //play sound
1924 }
1925
1926 // --- UPDATE
1927 void InitBaseState()
1928 {
1929 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
1930
1931 InitVisuals();
1932 UpdateNavmesh(); //regenerate navmesh
1933 GetConstruction().InitBaseState();
1934 }
1935
1936 void InitVisuals()
1937 {
1938 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
1939 //check base
1940 if (!HasBase())
1941 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1942 else
1943 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1944
1945 GetConstruction().UpdateVisuals();
1946 }
1947
1948 void UpdateVisuals()
1949 {
1951
1953 foreach (string slotName : attachmentSlots)
1955
1956 //check base
1957 if (!HasBase())
1958 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1959 else
1960 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1961
1962 GetConstruction().UpdateVisuals();
1963 }
1964
1966 {
1967 string slotNameMounted = slot_name + "_Mounted";
1968 EntityAI attachment = FindAttachmentBySlotName(slot_name);
1969
1970 if (attachment)
1971 {
1972 BarbedWire barbedWire = BarbedWire.Cast(attachment);
1973 if (barbedWire && barbedWire.IsMounted())
1975 else
1977
1978 if (is_locked)
1979 {
1980 SetAnimationPhase(slotNameMounted, 0);
1981 SetAnimationPhase(slot_name, 1);
1982 }
1983 else
1984 {
1985 SetAnimationPhase(slotNameMounted, 1);
1986 SetAnimationPhase(slot_name, 0);
1987 }
1988 }
1989 else
1990 {
1991 SetAnimationPhase(slotNameMounted, 1);
1992 SetAnimationPhase(slot_name, 1);
1993
1995 }
1996 }
1997
1998 // avoid calling this function on frequent occasions, it's a massive performance hit
1999 void UpdatePhysics()
2000 {
2002 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2003
2006
2008 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2009
2010 foreach (string slotName : attachmentSlots)
2012
2013 //check base
2014 if (!HasBase())
2015 {
2017 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2018
2019 AddProxyPhysics(ANIMATION_DEPLOYED);
2020 }
2021 else
2022 {
2024 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2025
2026 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2027 }
2028
2029 GetConstruction().UpdatePhysics();
2030 UpdateNavmesh();
2031 }
2032
2034 {
2035 //checks for invalid appends; hotfix
2036 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2037 return;
2038 //----------------------------------
2039 string slot_name_mounted = slot_name + "_Mounted";
2040 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2041
2042 //remove proxy physics
2043 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2044 RemoveProxyPhysics(slot_name_mounted);
2045 RemoveProxyPhysics(slot_name);
2046
2047 if (attachment)
2048 {
2049 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2050 if (is_locked)
2051 {
2052 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2053 AddProxyPhysics(slot_name_mounted);
2054 }
2055 else
2056 {
2057 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2058 AddProxyPhysics(slot_name);
2059 }
2060 }
2061 }
2062
2063 protected void UpdateNavmesh()
2064 {
2065 SetAffectPathgraph(true, false);
2066 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2067 }
2068
2069 override bool CanUseConstruction()
2070 {
2071 return true;
2072 }
2073
2074 override bool CanUseConstructionBuild()
2075 {
2076 return true;
2077 }
2078
2080 {
2081 if (attachment)
2082 {
2084 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2085
2086 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2087 }
2088
2089 return false;
2090 }
2091
2092 protected bool IsAttachmentSlotLocked(string slot_name)
2093 {
2094 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2095 }
2096
2097 //--- ATTACHMENT SLOTS
2099 {
2100 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2101 if (GetGame().ConfigIsExisting(config_path))
2102 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2103 }
2104
2106 {
2107 return true;
2108 }
2109
2110 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2111 {
2112 return true;
2113 }
2114
2115 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2116 {
2117 return true;
2118 }
2119
2120 // --- INIT
2121 void ConstructionInit()
2122 {
2123 if (!m_Construction)
2124 m_Construction = new Construction(this);
2125
2126 GetConstruction().Init();
2127 }
2128
2130 {
2131 return m_Construction;
2132 }
2133
2134 //--- INVENTORY/ATTACHMENTS CONDITIONS
2135 //attachments
2137 {
2138 return super.CanReceiveAttachment(attachment, slotId);
2139 }
2140
2142 {
2143 int attachment_count = GetInventory().AttachmentCount();
2144 if (attachment_count > 0)
2145 {
2146 if (HasBase() && attachment_count == 1)
2147 return false;
2148
2149 return true;
2150 }
2151
2152 return false;
2153 }
2154
2155 override bool ShowZonesHealth()
2156 {
2157 return true;
2158 }
2159
2160 //this into/outo parent.Cargo
2161 override bool CanPutInCargo(EntityAI parent)
2162 {
2163 return false;
2164 }
2165
2166 override bool CanRemoveFromCargo(EntityAI parent)
2167 {
2168 return false;
2169 }
2170
2171 //hands
2172 override bool CanPutIntoHands(EntityAI parent)
2173 {
2174 return false;
2175 }
2176
2177 //--- ACTION CONDITIONS
2178 //direction
2179 override bool IsFacingPlayer(PlayerBase player, string selection)
2180 {
2181 return true;
2182 }
2183
2184 override bool IsPlayerInside(PlayerBase player, string selection)
2185 {
2186 return true;
2187 }
2188
2191 {
2192 return false;
2193 }
2194
2195 //camera direction check
2196 bool IsFacingCamera(string selection)
2197 {
2198 return true;
2199 }
2200
2201 //roof check
2203 {
2204 return false;
2205 }
2206
2207 //selection->player distance check
2208 bool HasProperDistance(string selection, PlayerBase player)
2209 {
2210 return true;
2211 }
2212
2213 //folding
2215 {
2216 if (HasBase() || GetInventory().AttachmentCount() > 0)
2217 return false;
2218
2219 return true;
2220 }
2221
2223 {
2226
2227 return item;
2228 }
2229
2230 //Damage triggers (barbed wire)
2231 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2232 {
2233 if (GetGame() && GetGame().IsServer())
2234 {
2235 //destroy area damage if some already exists
2237
2238 //create new area damage
2240 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2241
2242 vector min_max[2];
2243 if (MemoryPointExists(slot_name + "_min"))
2244 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2245 if (MemoryPointExists(slot_name + "_max"))
2246 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2247
2248 //get proper trigger extents (min<max)
2249 vector extents[2];
2250 GetConstruction().GetTriggerExtents(min_max, extents);
2251
2252 //get box center
2253 vector center;
2254 center = GetConstruction().GetBoxCenter(min_max);
2255 center = ModelToWorld(center);
2256
2257 //rotate center if needed
2260
2261 areaDamage.SetExtents(extents[0], extents[1]);
2262 areaDamage.SetAreaPosition(center);
2263 areaDamage.SetAreaOrientation(orientation);
2264 areaDamage.SetLoopInterval(1.0);
2265 areaDamage.SetDeferDuration(0.2);
2266 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2267 areaDamage.SetAmmoName("BarbedWireHit");
2268 areaDamage.Spawn();
2269
2271 }
2272 }
2273
2275 {
2276 if (angle_deg != 0)
2277 {
2278 //orientation
2280
2281 //center
2283 if (MemoryPointExists("rotate_axis"))
2284 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2287 center[0] = r_center_x;
2288 center[2] = r_center_z;
2289 }
2290 }
2291
2292 void DestroyAreaDamage(string slot_name)
2293 {
2294 if (GetGame() && GetGame().IsServer())
2295 {
2298 {
2299 if (areaDamage)
2300 areaDamage.Destroy();
2301
2303 }
2304 }
2305 }
2306
2307 override bool IsIgnoredByConstruction()
2308 {
2309 return true;
2310 }
2311
2312 //================================================================
2313 // SOUNDS
2314 //================================================================
2315 protected void SoundBuildStart(string part_name)
2316 {
2317 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2318 }
2319
2320 protected void SoundDismantleStart(string part_name)
2321 {
2322 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2323 }
2324
2325 protected void SoundDestroyStart(string part_name)
2326 {
2327 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2328 }
2329
2330 protected string GetBuildSoundByMaterial(string part_name)
2331 {
2333
2334 switch (material_type)
2335 {
2336 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2337 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2338 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2339 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2340 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2341 }
2342
2343 return "";
2344 }
2345
2346 protected string GetDismantleSoundByMaterial(string part_name)
2347 {
2349
2350 switch (material_type)
2351 {
2352 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2353 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2354 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2355 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2356 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2357 }
2358
2359 return "";
2360 }
2361
2362 //misc
2364 {
2365 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2366 {
2367 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2369 SetHealth(slot_name, "Health", item.GetHealth());
2370 }
2371 }
2372
2373 override int GetDamageSystemVersionChange()
2374 {
2375 return 111;
2376 }
2377
2378 override void SetActions()
2379 {
2380 super.SetActions();
2381
2383 //AddAction(ActionTakeHybridAttachment);
2384 //AddAction(ActionTakeHybridAttachmentToHands);
2387 }
2388
2389 //================================================================
2390 // DEBUG
2391 //================================================================
2392 protected void DebugCustomState()
2393 {
2394 }
2395
2398 {
2399 return null;
2400 }
2401
2402 override void OnDebugSpawn()
2403 {
2404 FullyBuild();
2405 }
2406
2407 void FullyBuild()
2408 {
2410 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2411
2412 Man p;
2413
2414#ifdef SERVER
2416 GetGame().GetWorld().GetPlayerList(players);
2417 if (players.Count())
2418 p = players[0];
2419#else
2420 p = GetGame().GetPlayer();
2421#endif
2422
2423 foreach (ConstructionPart part : parts)
2424 {
2425 bool excluded = false;
2426 string partName = part.GetPartName();
2427 if (excludes)
2428 {
2429 foreach (string exclude : excludes)
2430 {
2431 if (partName.Contains(exclude))
2432 {
2433 excluded = true;
2434 break;
2435 }
2436 }
2437 }
2438
2439 if (!excluded)
2441 }
2442
2443 GetConstruction().UpdateVisuals();
2444 }
2445}
2446
2447void bsbDebugPrint(string s)
2448{
2449#ifdef BSB_DEBUG
2450 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2451#else
2452 //Print("" + s); // comment/uncomment to hide/see debug logs
2453#endif
2454}
2455void bsbDebugSpam(string s)
2456{
2457#ifdef BSB_DEBUG_SPAM
2458 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2459#else
2460 //Print("" + s); // comment/uncomment to hide/see debug logs
2461#endif
2462}

◆ PerformRoofCheckForBase()

bool bsbDebugPrint::PerformRoofCheckForBase ( string partName,
PlayerBase player,
out bool result )
protected

Definition at line 2104 of file BaseBuildingBase.c.

2106{
2107 const string ANIMATION_DEPLOYED = "Deployed";
2108
2109 float m_ConstructionKitHealth; //stored health value for used construction kit
2110
2112
2113 bool m_HasBase;
2114 //variables for synchronization of base building parts (2x31 is the current limit)
2115 int m_SyncParts01; //synchronization for already built parts (31 parts)
2116 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2117 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2118 int m_InteractedPartId; //construction part id that an action was performed on
2119 int m_PerformedActionId; //action id that was performed on a construction part
2120
2121 //Sounds
2122 //build
2123 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2124 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2125 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2126 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2127 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2128 //dismantle
2129 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2130 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2131 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2132 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2133 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2134
2135 protected EffectSound m_Sound;
2136
2140
2141 // Constructor
2142 void BaseBuildingBase()
2143 {
2145
2146 //synchronized variables
2147 RegisterNetSyncVariableInt("m_SyncParts01");
2148 RegisterNetSyncVariableInt("m_SyncParts02");
2149 RegisterNetSyncVariableInt("m_SyncParts03");
2150 RegisterNetSyncVariableInt("m_InteractedPartId");
2151 RegisterNetSyncVariableInt("m_PerformedActionId");
2152 RegisterNetSyncVariableBool("m_HasBase");
2153
2154 //Construction init
2156
2157 if (ConfigIsExisting("hybridAttachments"))
2158 {
2160 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2161 }
2162 if (ConfigIsExisting("mountables"))
2163 {
2165 ConfigGetTextArray("mountables", m_Mountables);
2166 }
2167
2168 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2169 }
2170
2171 override void EEDelete(EntityAI parent)
2172 {
2173 super.EEDelete(parent);
2174
2175 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2177
2178 }
2179
2180 override string GetInvulnerabilityTypeString()
2181 {
2182 return "disableBaseDamage";
2183 }
2184
2185 override bool CanObstruct()
2186 {
2187 return true;
2188 }
2189
2190 override int GetHideIconMask()
2191 {
2192 return EInventoryIconVisibility.HIDE_VICINITY;
2193 }
2194
2195 // --- SYNCHRONIZATION
2197 {
2198 if (GetGame().IsServer())
2199 SetSynchDirty();
2200 }
2201
2202 override void OnVariablesSynchronized()
2203 {
2204 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2205 super.OnVariablesSynchronized();
2206
2207 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2208 }
2209
2210 protected void OnSynchronizedClient()
2211 {
2212 //update parts
2214
2215 //update action on part
2217
2218 //update visuals (client)
2219 UpdateVisuals();
2220 }
2221
2222 //parts synchronization
2224 {
2225 //part_id must starts from index = 1
2226 int offset;
2227 int mask;
2228
2229 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2230 {
2231 offset = part_id - 1;
2232 mask = 1 << offset;
2233
2235 }
2236 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2237 {
2238 offset = (part_id % 32);
2239 mask = 1 << offset;
2240
2242 }
2243 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2244 {
2245 offset = (part_id % 63);
2246 mask = 1 << offset;
2247
2249 }
2250 }
2251
2253 {
2254 //part_id must starts from index = 1
2255 int offset;
2256 int mask;
2257
2258 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2259 {
2260 offset = part_id - 1;
2261 mask = 1 << offset;
2262
2264 }
2265 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2266 {
2267 offset = (part_id % 32);
2268 mask = 1 << offset;
2269
2271 }
2272 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2273 {
2274 offset = (part_id % 63);
2275 mask = 1 << offset;
2276
2278 }
2279 }
2280
2282 {
2283 //part_id must starts from index = 1
2284 int offset;
2285 int mask;
2286
2287 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2288 {
2289 offset = part_id - 1;
2290 mask = 1 << offset;
2291
2292 if ((m_SyncParts01 & mask) > 0)
2293 return true;
2294 }
2295 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2296 {
2297 offset = (part_id % 32);
2298 mask = 1 << offset;
2299
2300 if ((m_SyncParts02 & mask) > 0)
2301 return true;
2302 }
2303 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2304 {
2305 offset = (part_id % 63);
2306 mask = 1 << offset;
2307
2308 if ((m_SyncParts03 & mask) > 0)
2309 return true;
2310 }
2311
2312 return false;
2313 }
2314
2315 protected void RegisterActionForSync(int part_id, int action_id)
2316 {
2319 }
2320
2321 protected void ResetActionSyncData()
2322 {
2323 //reset data
2324 m_InteractedPartId = -1;
2326 }
2327
2328 protected void SetActionFromSyncData()
2329 {
2330 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2331 {
2334
2335 switch (build_action_id)
2336 {
2340 }
2341 }
2342 }
2343 //------
2344
2346 {
2347 string key = part.m_PartName;
2348 bool is_base = part.IsBase();
2350 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2352 {
2353 if (!part.IsBuilt())
2354 {
2355 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2356 GetConstruction().AddToConstructedParts(key);
2357 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2358
2359 if (is_base)
2360 {
2362 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2363 }
2364 }
2365 }
2366 else
2367 {
2368 if (part.IsBuilt())
2369 {
2370 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2371 GetConstruction().RemoveFromConstructedParts(key);
2372 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2373
2374 if (is_base)
2375 {
2377 AddProxyPhysics(ANIMATION_DEPLOYED);
2378 }
2379 }
2380 }
2381
2382 //check slot lock for material attachments
2383 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2384 }
2385
2386 //set construction parts based on synchronized data
2388 {
2391
2392 for (int i = 0; i < construction_parts.Count(); ++i)
2393 {
2394 string key = construction_parts.GetKey(i);
2397 }
2398
2399 //regenerate navmesh
2400 UpdateNavmesh();
2401 }
2402
2404 {
2407
2408 for (int i = 0; i < construction_parts.Count(); ++i)
2409 {
2410 string key = construction_parts.GetKey(i);
2412
2413 if (value.GetId() == id)
2414 return value;
2415 }
2416
2417 return NULL;
2418 }
2419 //
2420
2421 //Base
2422 bool HasBase()
2423 {
2424 return m_HasBase;
2425 }
2426
2427 void SetBaseState(bool has_base)
2428 {
2430 }
2431
2432 override bool IsDeployable()
2433 {
2434 return true;
2435 }
2436
2437 bool IsOpened()
2438 {
2439 return false;
2440 }
2441
2442 //--- CONSTRUCTION KIT
2444 {
2448
2449 return construction_kit;
2450 }
2451
2453 {
2454 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2457 }
2458
2459 protected vector GetKitSpawnPosition()
2460 {
2461 return GetPosition();
2462 }
2463
2464 protected string GetConstructionKitType()
2465 {
2466 return "";
2467 }
2468
2470 {
2472 GetGame().ObjectDelete(construction_kit);
2473 }
2474
2475 //--- CONSTRUCTION
2476 void DestroyConstruction()
2477 {
2478 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2479 GetGame().ObjectDelete(this);
2480 }
2481
2482 // --- EVENTS
2483 override void OnStoreSave(ParamsWriteContext ctx)
2484 {
2485 super.OnStoreSave(ctx);
2486
2487 //sync parts 01
2488 ctx.Write(m_SyncParts01);
2489 ctx.Write(m_SyncParts02);
2490 ctx.Write(m_SyncParts03);
2491
2492 ctx.Write(m_HasBase);
2493 }
2494
2495 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2496 {
2497 if (!super.OnStoreLoad(ctx, version))
2498 return false;
2499
2500 //--- Base building data ---
2501 //Restore synced parts data
2502 if (!ctx.Read(m_SyncParts01))
2503 {
2504 m_SyncParts01 = 0; //set default
2505 return false;
2506 }
2507 if (!ctx.Read(m_SyncParts02))
2508 {
2509 m_SyncParts02 = 0; //set default
2510 return false;
2511 }
2512 if (!ctx.Read(m_SyncParts03))
2513 {
2514 m_SyncParts03 = 0; //set default
2515 return false;
2516 }
2517
2518 //has base
2519 if (!ctx.Read(m_HasBase))
2520 {
2521 m_HasBase = false;
2522 return false;
2523 }
2524 //---
2525
2526 return true;
2527 }
2528
2529 override void AfterStoreLoad()
2530 {
2531 super.AfterStoreLoad();
2532
2535 }
2536
2538 {
2539 //update server data
2541
2542 //set base state
2543 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2544 SetBaseState(construction_part.IsBuilt()) ;
2545
2546 //synchronize after load
2548 }
2549
2550 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2551 {
2553 return;
2554
2555 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2556
2557 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2558 return;
2559
2561 string part_name = zone;
2562 part_name.ToLower();
2563
2565 {
2567
2568 if (construction_part && construction.IsPartConstructed(part_name))
2569 {
2570 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2571 construction.DestroyConnectedParts(part_name);
2572 }
2573
2574 //barbed wire handling (hack-ish)
2575 if (part_name.Contains("barbed"))
2576 {
2577 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2578 if (barbed_wire)
2579 barbed_wire.SetMountedState(false);
2580 }
2581 }
2582 }
2583
2584 override void EEOnAfterLoad()
2585 {
2587 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2588
2589 super.EEOnAfterLoad();
2590 }
2591
2592 override void EEInit()
2593 {
2594 super.EEInit();
2595
2596 // init visuals and physics
2597 InitBaseState();
2598
2599 //debug
2600#ifdef DEVELOPER
2602#endif
2603 }
2604
2605 override void EEItemAttached(EntityAI item, string slot_name)
2606 {
2607 super.EEItemAttached(item, slot_name);
2608
2610 UpdateVisuals();
2612 }
2613
2614 override void EEItemDetached(EntityAI item, string slot_name)
2615 {
2616 super.EEItemDetached(item, slot_name);
2617
2618 UpdateVisuals();
2620 }
2621
2622 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2623 {
2625 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2626
2629 }
2630
2631 //ignore out of reach condition
2632 override bool IgnoreOutOfReachCondition()
2633 {
2634 return true;
2635 }
2636
2637 //CONSTRUCTION EVENTS
2638 //Build
2639 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2640 {
2642
2643 //check base state
2644 if (construtionPart.IsBase())
2645 {
2646 SetBaseState(true);
2647
2648 //spawn kit
2650 }
2651
2652 //register constructed parts for synchronization
2654
2655 //register action that was performed on part
2657
2658 //synchronize
2660
2661 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2662
2663 UpdateNavmesh();
2664
2665 //update visuals
2666 UpdateVisuals();
2667
2668 //reset action sync data
2669 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2670 }
2671
2672 void OnPartBuiltClient(string part_name, int action_id)
2673 {
2674 //play sound
2676 }
2677
2678 //Dismantle
2680 {
2681 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2683
2684 //register constructed parts for synchronization
2686
2687 //register action that was performed on part
2689
2690 //synchronize
2692
2693 // server part of sync, client will be synced from SetPartsFromSyncData
2695
2696 UpdateNavmesh();
2697
2698 //update visuals
2699 UpdateVisuals();
2700
2701 //reset action sync data
2702 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2703
2704 //check base state
2705 if (construtionPart.IsBase())
2706 {
2707 //Destroy construction
2708 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2709 }
2710 }
2711
2713 {
2714 //play sound
2716 }
2717
2718 //Destroy
2720 {
2721 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2723
2724 //register constructed parts for synchronization
2726
2727 //register action that was performed on part
2729
2730 //synchronize
2732
2733 // server part of sync, client will be synced from SetPartsFromSyncData
2735
2736 UpdateNavmesh();
2737
2738 //update visuals
2739 UpdateVisuals();
2740
2741 //reset action sync data
2742 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2743
2744 //check base state
2745 if (construtionPart.IsBase())
2746 {
2747 //Destroy construction
2748 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2749 }
2750 }
2751
2752 void OnPartDestroyedClient(string part_name, int action_id)
2753 {
2754 //play sound
2756 }
2757
2758 // --- UPDATE
2759 void InitBaseState()
2760 {
2761 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2762
2763 InitVisuals();
2764 UpdateNavmesh(); //regenerate navmesh
2765 GetConstruction().InitBaseState();
2766 }
2767
2768 void InitVisuals()
2769 {
2770 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2771 //check base
2772 if (!HasBase())
2773 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2774 else
2775 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2776
2777 GetConstruction().UpdateVisuals();
2778 }
2779
2780 void UpdateVisuals()
2781 {
2783
2785 foreach (string slotName : attachmentSlots)
2787
2788 //check base
2789 if (!HasBase())
2790 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2791 else
2792 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2793
2794 GetConstruction().UpdateVisuals();
2795 }
2796
2798 {
2799 string slotNameMounted = slot_name + "_Mounted";
2800 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2801
2802 if (attachment)
2803 {
2804 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2805 if (barbedWire && barbedWire.IsMounted())
2807 else
2809
2810 if (is_locked)
2811 {
2812 SetAnimationPhase(slotNameMounted, 0);
2813 SetAnimationPhase(slot_name, 1);
2814 }
2815 else
2816 {
2817 SetAnimationPhase(slotNameMounted, 1);
2818 SetAnimationPhase(slot_name, 0);
2819 }
2820 }
2821 else
2822 {
2823 SetAnimationPhase(slotNameMounted, 1);
2824 SetAnimationPhase(slot_name, 1);
2825
2827 }
2828 }
2829
2830 // avoid calling this function on frequent occasions, it's a massive performance hit
2831 void UpdatePhysics()
2832 {
2834 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2835
2838
2840 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2841
2842 foreach (string slotName : attachmentSlots)
2844
2845 //check base
2846 if (!HasBase())
2847 {
2849 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2850
2851 AddProxyPhysics(ANIMATION_DEPLOYED);
2852 }
2853 else
2854 {
2856 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2857
2858 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2859 }
2860
2861 GetConstruction().UpdatePhysics();
2862 UpdateNavmesh();
2863 }
2864
2866 {
2867 //checks for invalid appends; hotfix
2868 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2869 return;
2870 //----------------------------------
2871 string slot_name_mounted = slot_name + "_Mounted";
2872 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2873
2874 //remove proxy physics
2875 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2876 RemoveProxyPhysics(slot_name_mounted);
2877 RemoveProxyPhysics(slot_name);
2878
2879 if (attachment)
2880 {
2881 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2882 if (is_locked)
2883 {
2884 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2885 AddProxyPhysics(slot_name_mounted);
2886 }
2887 else
2888 {
2889 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2890 AddProxyPhysics(slot_name);
2891 }
2892 }
2893 }
2894
2895 protected void UpdateNavmesh()
2896 {
2897 SetAffectPathgraph(true, false);
2898 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2899 }
2900
2901 override bool CanUseConstruction()
2902 {
2903 return true;
2904 }
2905
2906 override bool CanUseConstructionBuild()
2907 {
2908 return true;
2909 }
2910
2912 {
2913 if (attachment)
2914 {
2916 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2917
2918 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2919 }
2920
2921 return false;
2922 }
2923
2924 protected bool IsAttachmentSlotLocked(string slot_name)
2925 {
2926 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2927 }
2928
2929 //--- ATTACHMENT SLOTS
2931 {
2932 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2933 if (GetGame().ConfigIsExisting(config_path))
2934 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2935 }
2936
2938 {
2939 return true;
2940 }
2941
2942 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2943 {
2944 return true;
2945 }
2946
2947 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2948 {
2949 return true;
2950 }
2951
2952 // --- INIT
2953 void ConstructionInit()
2954 {
2955 if (!m_Construction)
2956 m_Construction = new Construction(this);
2957
2958 GetConstruction().Init();
2959 }
2960
2962 {
2963 return m_Construction;
2964 }
2965
2966 //--- INVENTORY/ATTACHMENTS CONDITIONS
2967 //attachments
2969 {
2970 return super.CanReceiveAttachment(attachment, slotId);
2971 }
2972
2974 {
2975 int attachment_count = GetInventory().AttachmentCount();
2976 if (attachment_count > 0)
2977 {
2978 if (HasBase() && attachment_count == 1)
2979 return false;
2980
2981 return true;
2982 }
2983
2984 return false;
2985 }
2986
2987 override bool ShowZonesHealth()
2988 {
2989 return true;
2990 }
2991
2992 //this into/outo parent.Cargo
2993 override bool CanPutInCargo(EntityAI parent)
2994 {
2995 return false;
2996 }
2997
2998 override bool CanRemoveFromCargo(EntityAI parent)
2999 {
3000 return false;
3001 }
3002
3003 //hands
3004 override bool CanPutIntoHands(EntityAI parent)
3005 {
3006 return false;
3007 }
3008
3009 //--- ACTION CONDITIONS
3010 //direction
3011 override bool IsFacingPlayer(PlayerBase player, string selection)
3012 {
3013 return true;
3014 }
3015
3016 override bool IsPlayerInside(PlayerBase player, string selection)
3017 {
3018 return true;
3019 }
3020
3023 {
3024 return false;
3025 }
3026
3027 //camera direction check
3028 bool IsFacingCamera(string selection)
3029 {
3030 return true;
3031 }
3032
3033 //roof check
3035 {
3036 return false;
3037 }
3038
3039 //selection->player distance check
3040 bool HasProperDistance(string selection, PlayerBase player)
3041 {
3042 return true;
3043 }
3044
3045 //folding
3047 {
3048 if (HasBase() || GetInventory().AttachmentCount() > 0)
3049 return false;
3050
3051 return true;
3052 }
3053
3055 {
3058
3059 return item;
3060 }
3061
3062 //Damage triggers (barbed wire)
3063 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3064 {
3065 if (GetGame() && GetGame().IsServer())
3066 {
3067 //destroy area damage if some already exists
3069
3070 //create new area damage
3072 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3073
3074 vector min_max[2];
3075 if (MemoryPointExists(slot_name + "_min"))
3076 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3077 if (MemoryPointExists(slot_name + "_max"))
3078 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3079
3080 //get proper trigger extents (min<max)
3081 vector extents[2];
3082 GetConstruction().GetTriggerExtents(min_max, extents);
3083
3084 //get box center
3085 vector center;
3086 center = GetConstruction().GetBoxCenter(min_max);
3087 center = ModelToWorld(center);
3088
3089 //rotate center if needed
3092
3093 areaDamage.SetExtents(extents[0], extents[1]);
3094 areaDamage.SetAreaPosition(center);
3095 areaDamage.SetAreaOrientation(orientation);
3096 areaDamage.SetLoopInterval(1.0);
3097 areaDamage.SetDeferDuration(0.2);
3098 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3099 areaDamage.SetAmmoName("BarbedWireHit");
3100 areaDamage.Spawn();
3101
3103 }
3104 }
3105
3107 {
3108 if (angle_deg != 0)
3109 {
3110 //orientation
3112
3113 //center
3115 if (MemoryPointExists("rotate_axis"))
3116 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3119 center[0] = r_center_x;
3120 center[2] = r_center_z;
3121 }
3122 }
3123
3124 void DestroyAreaDamage(string slot_name)
3125 {
3126 if (GetGame() && GetGame().IsServer())
3127 {
3130 {
3131 if (areaDamage)
3132 areaDamage.Destroy();
3133
3135 }
3136 }
3137 }
3138
3139 override bool IsIgnoredByConstruction()
3140 {
3141 return true;
3142 }
3143
3144 //================================================================
3145 // SOUNDS
3146 //================================================================
3147 protected void SoundBuildStart(string part_name)
3148 {
3149 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3150 }
3151
3152 protected void SoundDismantleStart(string part_name)
3153 {
3154 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3155 }
3156
3157 protected void SoundDestroyStart(string part_name)
3158 {
3159 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3160 }
3161
3162 protected string GetBuildSoundByMaterial(string part_name)
3163 {
3165
3166 switch (material_type)
3167 {
3168 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3169 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3170 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3171 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3172 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3173 }
3174
3175 return "";
3176 }
3177
3178 protected string GetDismantleSoundByMaterial(string part_name)
3179 {
3181
3182 switch (material_type)
3183 {
3184 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3185 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3186 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3187 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3188 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3189 }
3190
3191 return "";
3192 }
3193
3194 //misc
3196 {
3197 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3198 {
3199 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3201 SetHealth(slot_name, "Health", item.GetHealth());
3202 }
3203 }
3204
3205 override int GetDamageSystemVersionChange()
3206 {
3207 return 111;
3208 }
3209
3210 override void SetActions()
3211 {
3212 super.SetActions();
3213
3215 //AddAction(ActionTakeHybridAttachment);
3216 //AddAction(ActionTakeHybridAttachmentToHands);
3219 }
3220
3221 //================================================================
3222 // DEBUG
3223 //================================================================
3224 protected void DebugCustomState()
3225 {
3226 }
3227
3230 {
3231 return null;
3232 }
3233
3234 override void OnDebugSpawn()
3235 {
3236 FullyBuild();
3237 }
3238
3239 void FullyBuild()
3240 {
3242 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3243
3244 Man p;
3245
3246#ifdef SERVER
3248 GetGame().GetWorld().GetPlayerList(players);
3249 if (players.Count())
3250 p = players[0];
3251#else
3252 p = GetGame().GetPlayer();
3253#endif
3254
3255 foreach (ConstructionPart part : parts)
3256 {
3257 bool excluded = false;
3258 string partName = part.GetPartName();
3259 if (excludes)
3260 {
3261 foreach (string exclude : excludes)
3262 {
3263 if (partName.Contains(exclude))
3264 {
3265 excluded = true;
3266 break;
3267 }
3268 }
3269 }
3270
3271 if (!excluded)
3273 }
3274
3275 GetConstruction().UpdateVisuals();
3276 }
3277}
3278
3279void bsbDebugPrint(string s)
3280{
3281#ifdef BSB_DEBUG
3282 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3283#else
3284 //Print("" + s); // comment/uncomment to hide/see debug logs
3285#endif
3286}
3287void bsbDebugSpam(string s)
3288{
3289#ifdef BSB_DEBUG_SPAM
3290 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3291#else
3292 //Print("" + s); // comment/uncomment to hide/see debug logs
3293#endif
3294}

◆ RegisterActionForSync()

void bsbDebugPrint::RegisterActionForSync ( int part_id,
int action_id )
protected

Definition at line 1385 of file BaseBuildingBase.c.

1387{
1388 const string ANIMATION_DEPLOYED = "Deployed";
1389
1390 float m_ConstructionKitHealth; //stored health value for used construction kit
1391
1393
1394 bool m_HasBase;
1395 //variables for synchronization of base building parts (2x31 is the current limit)
1396 int m_SyncParts01; //synchronization for already built parts (31 parts)
1397 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1398 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1399 int m_InteractedPartId; //construction part id that an action was performed on
1400 int m_PerformedActionId; //action id that was performed on a construction part
1401
1402 //Sounds
1403 //build
1404 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1405 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1406 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1407 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1408 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1409 //dismantle
1410 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1411 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1412 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1413 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1414 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1415
1416 protected EffectSound m_Sound;
1417
1421
1422 // Constructor
1423 void BaseBuildingBase()
1424 {
1426
1427 //synchronized variables
1428 RegisterNetSyncVariableInt("m_SyncParts01");
1429 RegisterNetSyncVariableInt("m_SyncParts02");
1430 RegisterNetSyncVariableInt("m_SyncParts03");
1431 RegisterNetSyncVariableInt("m_InteractedPartId");
1432 RegisterNetSyncVariableInt("m_PerformedActionId");
1433 RegisterNetSyncVariableBool("m_HasBase");
1434
1435 //Construction init
1437
1438 if (ConfigIsExisting("hybridAttachments"))
1439 {
1441 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1442 }
1443 if (ConfigIsExisting("mountables"))
1444 {
1446 ConfigGetTextArray("mountables", m_Mountables);
1447 }
1448
1449 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1450 }
1451
1452 override void EEDelete(EntityAI parent)
1453 {
1454 super.EEDelete(parent);
1455
1456 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1458
1459 }
1460
1461 override string GetInvulnerabilityTypeString()
1462 {
1463 return "disableBaseDamage";
1464 }
1465
1466 override bool CanObstruct()
1467 {
1468 return true;
1469 }
1470
1471 override int GetHideIconMask()
1472 {
1473 return EInventoryIconVisibility.HIDE_VICINITY;
1474 }
1475
1476 // --- SYNCHRONIZATION
1478 {
1479 if (GetGame().IsServer())
1480 SetSynchDirty();
1481 }
1482
1483 override void OnVariablesSynchronized()
1484 {
1485 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1486 super.OnVariablesSynchronized();
1487
1488 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1489 }
1490
1491 protected void OnSynchronizedClient()
1492 {
1493 //update parts
1495
1496 //update action on part
1498
1499 //update visuals (client)
1500 UpdateVisuals();
1501 }
1502
1503 //parts synchronization
1505 {
1506 //part_id must starts from index = 1
1507 int offset;
1508 int mask;
1509
1510 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1511 {
1512 offset = part_id - 1;
1513 mask = 1 << offset;
1514
1516 }
1517 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1518 {
1519 offset = (part_id % 32);
1520 mask = 1 << offset;
1521
1523 }
1524 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1525 {
1526 offset = (part_id % 63);
1527 mask = 1 << offset;
1528
1530 }
1531 }
1532
1534 {
1535 //part_id must starts from index = 1
1536 int offset;
1537 int mask;
1538
1539 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1540 {
1541 offset = part_id - 1;
1542 mask = 1 << offset;
1543
1545 }
1546 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1547 {
1548 offset = (part_id % 32);
1549 mask = 1 << offset;
1550
1552 }
1553 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1554 {
1555 offset = (part_id % 63);
1556 mask = 1 << offset;
1557
1559 }
1560 }
1561
1563 {
1564 //part_id must starts from index = 1
1565 int offset;
1566 int mask;
1567
1568 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1569 {
1570 offset = part_id - 1;
1571 mask = 1 << offset;
1572
1573 if ((m_SyncParts01 & mask) > 0)
1574 return true;
1575 }
1576 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1577 {
1578 offset = (part_id % 32);
1579 mask = 1 << offset;
1580
1581 if ((m_SyncParts02 & mask) > 0)
1582 return true;
1583 }
1584 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1585 {
1586 offset = (part_id % 63);
1587 mask = 1 << offset;
1588
1589 if ((m_SyncParts03 & mask) > 0)
1590 return true;
1591 }
1592
1593 return false;
1594 }
1595
1596 protected void RegisterActionForSync(int part_id, int action_id)
1597 {
1600 }
1601
1602 protected void ResetActionSyncData()
1603 {
1604 //reset data
1605 m_InteractedPartId = -1;
1607 }
1608
1609 protected void SetActionFromSyncData()
1610 {
1611 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1612 {
1615
1616 switch (build_action_id)
1617 {
1621 }
1622 }
1623 }
1624 //------
1625
1627 {
1628 string key = part.m_PartName;
1629 bool is_base = part.IsBase();
1631 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1633 {
1634 if (!part.IsBuilt())
1635 {
1636 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1637 GetConstruction().AddToConstructedParts(key);
1638 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1639
1640 if (is_base)
1641 {
1643 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1644 }
1645 }
1646 }
1647 else
1648 {
1649 if (part.IsBuilt())
1650 {
1651 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1652 GetConstruction().RemoveFromConstructedParts(key);
1653 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1654
1655 if (is_base)
1656 {
1658 AddProxyPhysics(ANIMATION_DEPLOYED);
1659 }
1660 }
1661 }
1662
1663 //check slot lock for material attachments
1664 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1665 }
1666
1667 //set construction parts based on synchronized data
1669 {
1672
1673 for (int i = 0; i < construction_parts.Count(); ++i)
1674 {
1675 string key = construction_parts.GetKey(i);
1678 }
1679
1680 //regenerate navmesh
1681 UpdateNavmesh();
1682 }
1683
1685 {
1688
1689 for (int i = 0; i < construction_parts.Count(); ++i)
1690 {
1691 string key = construction_parts.GetKey(i);
1693
1694 if (value.GetId() == id)
1695 return value;
1696 }
1697
1698 return NULL;
1699 }
1700 //
1701
1702 //Base
1703 bool HasBase()
1704 {
1705 return m_HasBase;
1706 }
1707
1708 void SetBaseState(bool has_base)
1709 {
1711 }
1712
1713 override bool IsDeployable()
1714 {
1715 return true;
1716 }
1717
1718 bool IsOpened()
1719 {
1720 return false;
1721 }
1722
1723 //--- CONSTRUCTION KIT
1725 {
1729
1730 return construction_kit;
1731 }
1732
1734 {
1735 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1738 }
1739
1740 protected vector GetKitSpawnPosition()
1741 {
1742 return GetPosition();
1743 }
1744
1745 protected string GetConstructionKitType()
1746 {
1747 return "";
1748 }
1749
1751 {
1753 GetGame().ObjectDelete(construction_kit);
1754 }
1755
1756 //--- CONSTRUCTION
1757 void DestroyConstruction()
1758 {
1759 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1760 GetGame().ObjectDelete(this);
1761 }
1762
1763 // --- EVENTS
1764 override void OnStoreSave(ParamsWriteContext ctx)
1765 {
1766 super.OnStoreSave(ctx);
1767
1768 //sync parts 01
1769 ctx.Write(m_SyncParts01);
1770 ctx.Write(m_SyncParts02);
1771 ctx.Write(m_SyncParts03);
1772
1773 ctx.Write(m_HasBase);
1774 }
1775
1776 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1777 {
1778 if (!super.OnStoreLoad(ctx, version))
1779 return false;
1780
1781 //--- Base building data ---
1782 //Restore synced parts data
1783 if (!ctx.Read(m_SyncParts01))
1784 {
1785 m_SyncParts01 = 0; //set default
1786 return false;
1787 }
1788 if (!ctx.Read(m_SyncParts02))
1789 {
1790 m_SyncParts02 = 0; //set default
1791 return false;
1792 }
1793 if (!ctx.Read(m_SyncParts03))
1794 {
1795 m_SyncParts03 = 0; //set default
1796 return false;
1797 }
1798
1799 //has base
1800 if (!ctx.Read(m_HasBase))
1801 {
1802 m_HasBase = false;
1803 return false;
1804 }
1805 //---
1806
1807 return true;
1808 }
1809
1810 override void AfterStoreLoad()
1811 {
1812 super.AfterStoreLoad();
1813
1816 }
1817
1819 {
1820 //update server data
1822
1823 //set base state
1824 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1825 SetBaseState(construction_part.IsBuilt()) ;
1826
1827 //synchronize after load
1829 }
1830
1831 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1832 {
1834 return;
1835
1836 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1837
1838 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1839 return;
1840
1842 string part_name = zone;
1843 part_name.ToLower();
1844
1846 {
1848
1849 if (construction_part && construction.IsPartConstructed(part_name))
1850 {
1851 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1852 construction.DestroyConnectedParts(part_name);
1853 }
1854
1855 //barbed wire handling (hack-ish)
1856 if (part_name.Contains("barbed"))
1857 {
1858 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1859 if (barbed_wire)
1860 barbed_wire.SetMountedState(false);
1861 }
1862 }
1863 }
1864
1865 override void EEOnAfterLoad()
1866 {
1868 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1869
1870 super.EEOnAfterLoad();
1871 }
1872
1873 override void EEInit()
1874 {
1875 super.EEInit();
1876
1877 // init visuals and physics
1878 InitBaseState();
1879
1880 //debug
1881#ifdef DEVELOPER
1883#endif
1884 }
1885
1886 override void EEItemAttached(EntityAI item, string slot_name)
1887 {
1888 super.EEItemAttached(item, slot_name);
1889
1891 UpdateVisuals();
1893 }
1894
1895 override void EEItemDetached(EntityAI item, string slot_name)
1896 {
1897 super.EEItemDetached(item, slot_name);
1898
1899 UpdateVisuals();
1901 }
1902
1903 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
1904 {
1906 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
1907
1910 }
1911
1912 //ignore out of reach condition
1913 override bool IgnoreOutOfReachCondition()
1914 {
1915 return true;
1916 }
1917
1918 //CONSTRUCTION EVENTS
1919 //Build
1920 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
1921 {
1923
1924 //check base state
1925 if (construtionPart.IsBase())
1926 {
1927 SetBaseState(true);
1928
1929 //spawn kit
1931 }
1932
1933 //register constructed parts for synchronization
1935
1936 //register action that was performed on part
1938
1939 //synchronize
1941
1942 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
1943
1944 UpdateNavmesh();
1945
1946 //update visuals
1947 UpdateVisuals();
1948
1949 //reset action sync data
1950 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1951 }
1952
1953 void OnPartBuiltClient(string part_name, int action_id)
1954 {
1955 //play sound
1957 }
1958
1959 //Dismantle
1961 {
1962 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
1964
1965 //register constructed parts for synchronization
1967
1968 //register action that was performed on part
1970
1971 //synchronize
1973
1974 // server part of sync, client will be synced from SetPartsFromSyncData
1976
1977 UpdateNavmesh();
1978
1979 //update visuals
1980 UpdateVisuals();
1981
1982 //reset action sync data
1983 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1984
1985 //check base state
1986 if (construtionPart.IsBase())
1987 {
1988 //Destroy construction
1989 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1990 }
1991 }
1992
1994 {
1995 //play sound
1997 }
1998
1999 //Destroy
2001 {
2002 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2004
2005 //register constructed parts for synchronization
2007
2008 //register action that was performed on part
2010
2011 //synchronize
2013
2014 // server part of sync, client will be synced from SetPartsFromSyncData
2016
2017 UpdateNavmesh();
2018
2019 //update visuals
2020 UpdateVisuals();
2021
2022 //reset action sync data
2023 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2024
2025 //check base state
2026 if (construtionPart.IsBase())
2027 {
2028 //Destroy construction
2029 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2030 }
2031 }
2032
2033 void OnPartDestroyedClient(string part_name, int action_id)
2034 {
2035 //play sound
2037 }
2038
2039 // --- UPDATE
2040 void InitBaseState()
2041 {
2042 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2043
2044 InitVisuals();
2045 UpdateNavmesh(); //regenerate navmesh
2046 GetConstruction().InitBaseState();
2047 }
2048
2049 void InitVisuals()
2050 {
2051 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2052 //check base
2053 if (!HasBase())
2054 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2055 else
2056 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2057
2058 GetConstruction().UpdateVisuals();
2059 }
2060
2061 void UpdateVisuals()
2062 {
2064
2066 foreach (string slotName : attachmentSlots)
2068
2069 //check base
2070 if (!HasBase())
2071 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2072 else
2073 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2074
2075 GetConstruction().UpdateVisuals();
2076 }
2077
2079 {
2080 string slotNameMounted = slot_name + "_Mounted";
2081 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2082
2083 if (attachment)
2084 {
2085 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2086 if (barbedWire && barbedWire.IsMounted())
2088 else
2090
2091 if (is_locked)
2092 {
2093 SetAnimationPhase(slotNameMounted, 0);
2094 SetAnimationPhase(slot_name, 1);
2095 }
2096 else
2097 {
2098 SetAnimationPhase(slotNameMounted, 1);
2099 SetAnimationPhase(slot_name, 0);
2100 }
2101 }
2102 else
2103 {
2104 SetAnimationPhase(slotNameMounted, 1);
2105 SetAnimationPhase(slot_name, 1);
2106
2108 }
2109 }
2110
2111 // avoid calling this function on frequent occasions, it's a massive performance hit
2112 void UpdatePhysics()
2113 {
2115 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2116
2119
2121 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2122
2123 foreach (string slotName : attachmentSlots)
2125
2126 //check base
2127 if (!HasBase())
2128 {
2130 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2131
2132 AddProxyPhysics(ANIMATION_DEPLOYED);
2133 }
2134 else
2135 {
2137 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2138
2139 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2140 }
2141
2142 GetConstruction().UpdatePhysics();
2143 UpdateNavmesh();
2144 }
2145
2147 {
2148 //checks for invalid appends; hotfix
2149 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2150 return;
2151 //----------------------------------
2152 string slot_name_mounted = slot_name + "_Mounted";
2153 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2154
2155 //remove proxy physics
2156 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2157 RemoveProxyPhysics(slot_name_mounted);
2158 RemoveProxyPhysics(slot_name);
2159
2160 if (attachment)
2161 {
2162 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2163 if (is_locked)
2164 {
2165 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2166 AddProxyPhysics(slot_name_mounted);
2167 }
2168 else
2169 {
2170 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2171 AddProxyPhysics(slot_name);
2172 }
2173 }
2174 }
2175
2176 protected void UpdateNavmesh()
2177 {
2178 SetAffectPathgraph(true, false);
2179 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2180 }
2181
2182 override bool CanUseConstruction()
2183 {
2184 return true;
2185 }
2186
2187 override bool CanUseConstructionBuild()
2188 {
2189 return true;
2190 }
2191
2193 {
2194 if (attachment)
2195 {
2197 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2198
2199 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2200 }
2201
2202 return false;
2203 }
2204
2205 protected bool IsAttachmentSlotLocked(string slot_name)
2206 {
2207 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2208 }
2209
2210 //--- ATTACHMENT SLOTS
2212 {
2213 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2214 if (GetGame().ConfigIsExisting(config_path))
2215 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2216 }
2217
2219 {
2220 return true;
2221 }
2222
2223 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2224 {
2225 return true;
2226 }
2227
2228 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2229 {
2230 return true;
2231 }
2232
2233 // --- INIT
2234 void ConstructionInit()
2235 {
2236 if (!m_Construction)
2237 m_Construction = new Construction(this);
2238
2239 GetConstruction().Init();
2240 }
2241
2243 {
2244 return m_Construction;
2245 }
2246
2247 //--- INVENTORY/ATTACHMENTS CONDITIONS
2248 //attachments
2250 {
2251 return super.CanReceiveAttachment(attachment, slotId);
2252 }
2253
2255 {
2256 int attachment_count = GetInventory().AttachmentCount();
2257 if (attachment_count > 0)
2258 {
2259 if (HasBase() && attachment_count == 1)
2260 return false;
2261
2262 return true;
2263 }
2264
2265 return false;
2266 }
2267
2268 override bool ShowZonesHealth()
2269 {
2270 return true;
2271 }
2272
2273 //this into/outo parent.Cargo
2274 override bool CanPutInCargo(EntityAI parent)
2275 {
2276 return false;
2277 }
2278
2279 override bool CanRemoveFromCargo(EntityAI parent)
2280 {
2281 return false;
2282 }
2283
2284 //hands
2285 override bool CanPutIntoHands(EntityAI parent)
2286 {
2287 return false;
2288 }
2289
2290 //--- ACTION CONDITIONS
2291 //direction
2292 override bool IsFacingPlayer(PlayerBase player, string selection)
2293 {
2294 return true;
2295 }
2296
2297 override bool IsPlayerInside(PlayerBase player, string selection)
2298 {
2299 return true;
2300 }
2301
2304 {
2305 return false;
2306 }
2307
2308 //camera direction check
2309 bool IsFacingCamera(string selection)
2310 {
2311 return true;
2312 }
2313
2314 //roof check
2316 {
2317 return false;
2318 }
2319
2320 //selection->player distance check
2321 bool HasProperDistance(string selection, PlayerBase player)
2322 {
2323 return true;
2324 }
2325
2326 //folding
2328 {
2329 if (HasBase() || GetInventory().AttachmentCount() > 0)
2330 return false;
2331
2332 return true;
2333 }
2334
2336 {
2339
2340 return item;
2341 }
2342
2343 //Damage triggers (barbed wire)
2344 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2345 {
2346 if (GetGame() && GetGame().IsServer())
2347 {
2348 //destroy area damage if some already exists
2350
2351 //create new area damage
2353 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2354
2355 vector min_max[2];
2356 if (MemoryPointExists(slot_name + "_min"))
2357 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2358 if (MemoryPointExists(slot_name + "_max"))
2359 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2360
2361 //get proper trigger extents (min<max)
2362 vector extents[2];
2363 GetConstruction().GetTriggerExtents(min_max, extents);
2364
2365 //get box center
2366 vector center;
2367 center = GetConstruction().GetBoxCenter(min_max);
2368 center = ModelToWorld(center);
2369
2370 //rotate center if needed
2373
2374 areaDamage.SetExtents(extents[0], extents[1]);
2375 areaDamage.SetAreaPosition(center);
2376 areaDamage.SetAreaOrientation(orientation);
2377 areaDamage.SetLoopInterval(1.0);
2378 areaDamage.SetDeferDuration(0.2);
2379 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2380 areaDamage.SetAmmoName("BarbedWireHit");
2381 areaDamage.Spawn();
2382
2384 }
2385 }
2386
2388 {
2389 if (angle_deg != 0)
2390 {
2391 //orientation
2393
2394 //center
2396 if (MemoryPointExists("rotate_axis"))
2397 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2400 center[0] = r_center_x;
2401 center[2] = r_center_z;
2402 }
2403 }
2404
2405 void DestroyAreaDamage(string slot_name)
2406 {
2407 if (GetGame() && GetGame().IsServer())
2408 {
2411 {
2412 if (areaDamage)
2413 areaDamage.Destroy();
2414
2416 }
2417 }
2418 }
2419
2420 override bool IsIgnoredByConstruction()
2421 {
2422 return true;
2423 }
2424
2425 //================================================================
2426 // SOUNDS
2427 //================================================================
2428 protected void SoundBuildStart(string part_name)
2429 {
2430 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2431 }
2432
2433 protected void SoundDismantleStart(string part_name)
2434 {
2435 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2436 }
2437
2438 protected void SoundDestroyStart(string part_name)
2439 {
2440 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2441 }
2442
2443 protected string GetBuildSoundByMaterial(string part_name)
2444 {
2446
2447 switch (material_type)
2448 {
2449 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2450 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2451 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2452 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2453 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2454 }
2455
2456 return "";
2457 }
2458
2459 protected string GetDismantleSoundByMaterial(string part_name)
2460 {
2462
2463 switch (material_type)
2464 {
2465 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2466 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2467 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2468 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2469 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2470 }
2471
2472 return "";
2473 }
2474
2475 //misc
2477 {
2478 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2479 {
2480 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2482 SetHealth(slot_name, "Health", item.GetHealth());
2483 }
2484 }
2485
2486 override int GetDamageSystemVersionChange()
2487 {
2488 return 111;
2489 }
2490
2491 override void SetActions()
2492 {
2493 super.SetActions();
2494
2496 //AddAction(ActionTakeHybridAttachment);
2497 //AddAction(ActionTakeHybridAttachmentToHands);
2500 }
2501
2502 //================================================================
2503 // DEBUG
2504 //================================================================
2505 protected void DebugCustomState()
2506 {
2507 }
2508
2511 {
2512 return null;
2513 }
2514
2515 override void OnDebugSpawn()
2516 {
2517 FullyBuild();
2518 }
2519
2520 void FullyBuild()
2521 {
2523 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2524
2525 Man p;
2526
2527#ifdef SERVER
2529 GetGame().GetWorld().GetPlayerList(players);
2530 if (players.Count())
2531 p = players[0];
2532#else
2533 p = GetGame().GetPlayer();
2534#endif
2535
2536 foreach (ConstructionPart part : parts)
2537 {
2538 bool excluded = false;
2539 string partName = part.GetPartName();
2540 if (excludes)
2541 {
2542 foreach (string exclude : excludes)
2543 {
2544 if (partName.Contains(exclude))
2545 {
2546 excluded = true;
2547 break;
2548 }
2549 }
2550 }
2551
2552 if (!excluded)
2554 }
2555
2556 GetConstruction().UpdateVisuals();
2557 }
2558}
2559
2560void bsbDebugPrint(string s)
2561{
2562#ifdef BSB_DEBUG
2563 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2564#else
2565 //Print("" + s); // comment/uncomment to hide/see debug logs
2566#endif
2567}
2568void bsbDebugSpam(string s)
2569{
2570#ifdef BSB_DEBUG_SPAM
2571 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2572#else
2573 //Print("" + s); // comment/uncomment to hide/see debug logs
2574#endif
2575}

Referenced by ItemBase::OnPartBuiltServer(), ItemBase::OnPartDestroyedServer(), and ItemBase::OnPartDismantledServer().

◆ RegisterPartForSync()

void bsbDebugPrint::RegisterPartForSync ( int part_id)
protected

Definition at line 1293 of file BaseBuildingBase.c.

1295{
1296 const string ANIMATION_DEPLOYED = "Deployed";
1297
1298 float m_ConstructionKitHealth; //stored health value for used construction kit
1299
1301
1302 bool m_HasBase;
1303 //variables for synchronization of base building parts (2x31 is the current limit)
1304 int m_SyncParts01; //synchronization for already built parts (31 parts)
1305 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1306 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1307 int m_InteractedPartId; //construction part id that an action was performed on
1308 int m_PerformedActionId; //action id that was performed on a construction part
1309
1310 //Sounds
1311 //build
1312 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1313 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1314 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1315 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1316 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1317 //dismantle
1318 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1319 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1320 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1321 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1322 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1323
1324 protected EffectSound m_Sound;
1325
1329
1330 // Constructor
1331 void BaseBuildingBase()
1332 {
1334
1335 //synchronized variables
1336 RegisterNetSyncVariableInt("m_SyncParts01");
1337 RegisterNetSyncVariableInt("m_SyncParts02");
1338 RegisterNetSyncVariableInt("m_SyncParts03");
1339 RegisterNetSyncVariableInt("m_InteractedPartId");
1340 RegisterNetSyncVariableInt("m_PerformedActionId");
1341 RegisterNetSyncVariableBool("m_HasBase");
1342
1343 //Construction init
1345
1346 if (ConfigIsExisting("hybridAttachments"))
1347 {
1349 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1350 }
1351 if (ConfigIsExisting("mountables"))
1352 {
1354 ConfigGetTextArray("mountables", m_Mountables);
1355 }
1356
1357 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1358 }
1359
1360 override void EEDelete(EntityAI parent)
1361 {
1362 super.EEDelete(parent);
1363
1364 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1366
1367 }
1368
1369 override string GetInvulnerabilityTypeString()
1370 {
1371 return "disableBaseDamage";
1372 }
1373
1374 override bool CanObstruct()
1375 {
1376 return true;
1377 }
1378
1379 override int GetHideIconMask()
1380 {
1381 return EInventoryIconVisibility.HIDE_VICINITY;
1382 }
1383
1384 // --- SYNCHRONIZATION
1386 {
1387 if (GetGame().IsServer())
1388 SetSynchDirty();
1389 }
1390
1391 override void OnVariablesSynchronized()
1392 {
1393 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1394 super.OnVariablesSynchronized();
1395
1396 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1397 }
1398
1399 protected void OnSynchronizedClient()
1400 {
1401 //update parts
1403
1404 //update action on part
1406
1407 //update visuals (client)
1408 UpdateVisuals();
1409 }
1410
1411 //parts synchronization
1413 {
1414 //part_id must starts from index = 1
1415 int offset;
1416 int mask;
1417
1418 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1419 {
1420 offset = part_id - 1;
1421 mask = 1 << offset;
1422
1424 }
1425 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1426 {
1427 offset = (part_id % 32);
1428 mask = 1 << offset;
1429
1431 }
1432 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1433 {
1434 offset = (part_id % 63);
1435 mask = 1 << offset;
1436
1438 }
1439 }
1440
1442 {
1443 //part_id must starts from index = 1
1444 int offset;
1445 int mask;
1446
1447 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1448 {
1449 offset = part_id - 1;
1450 mask = 1 << offset;
1451
1453 }
1454 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1455 {
1456 offset = (part_id % 32);
1457 mask = 1 << offset;
1458
1460 }
1461 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1462 {
1463 offset = (part_id % 63);
1464 mask = 1 << offset;
1465
1467 }
1468 }
1469
1471 {
1472 //part_id must starts from index = 1
1473 int offset;
1474 int mask;
1475
1476 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1477 {
1478 offset = part_id - 1;
1479 mask = 1 << offset;
1480
1481 if ((m_SyncParts01 & mask) > 0)
1482 return true;
1483 }
1484 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1485 {
1486 offset = (part_id % 32);
1487 mask = 1 << offset;
1488
1489 if ((m_SyncParts02 & mask) > 0)
1490 return true;
1491 }
1492 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1493 {
1494 offset = (part_id % 63);
1495 mask = 1 << offset;
1496
1497 if ((m_SyncParts03 & mask) > 0)
1498 return true;
1499 }
1500
1501 return false;
1502 }
1503
1504 protected void RegisterActionForSync(int part_id, int action_id)
1505 {
1508 }
1509
1510 protected void ResetActionSyncData()
1511 {
1512 //reset data
1513 m_InteractedPartId = -1;
1515 }
1516
1517 protected void SetActionFromSyncData()
1518 {
1519 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1520 {
1523
1524 switch (build_action_id)
1525 {
1529 }
1530 }
1531 }
1532 //------
1533
1535 {
1536 string key = part.m_PartName;
1537 bool is_base = part.IsBase();
1539 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1541 {
1542 if (!part.IsBuilt())
1543 {
1544 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1545 GetConstruction().AddToConstructedParts(key);
1546 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1547
1548 if (is_base)
1549 {
1551 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1552 }
1553 }
1554 }
1555 else
1556 {
1557 if (part.IsBuilt())
1558 {
1559 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1560 GetConstruction().RemoveFromConstructedParts(key);
1561 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1562
1563 if (is_base)
1564 {
1566 AddProxyPhysics(ANIMATION_DEPLOYED);
1567 }
1568 }
1569 }
1570
1571 //check slot lock for material attachments
1572 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1573 }
1574
1575 //set construction parts based on synchronized data
1577 {
1580
1581 for (int i = 0; i < construction_parts.Count(); ++i)
1582 {
1583 string key = construction_parts.GetKey(i);
1586 }
1587
1588 //regenerate navmesh
1589 UpdateNavmesh();
1590 }
1591
1593 {
1596
1597 for (int i = 0; i < construction_parts.Count(); ++i)
1598 {
1599 string key = construction_parts.GetKey(i);
1601
1602 if (value.GetId() == id)
1603 return value;
1604 }
1605
1606 return NULL;
1607 }
1608 //
1609
1610 //Base
1611 bool HasBase()
1612 {
1613 return m_HasBase;
1614 }
1615
1616 void SetBaseState(bool has_base)
1617 {
1619 }
1620
1621 override bool IsDeployable()
1622 {
1623 return true;
1624 }
1625
1626 bool IsOpened()
1627 {
1628 return false;
1629 }
1630
1631 //--- CONSTRUCTION KIT
1633 {
1637
1638 return construction_kit;
1639 }
1640
1642 {
1643 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1646 }
1647
1648 protected vector GetKitSpawnPosition()
1649 {
1650 return GetPosition();
1651 }
1652
1653 protected string GetConstructionKitType()
1654 {
1655 return "";
1656 }
1657
1659 {
1661 GetGame().ObjectDelete(construction_kit);
1662 }
1663
1664 //--- CONSTRUCTION
1665 void DestroyConstruction()
1666 {
1667 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1668 GetGame().ObjectDelete(this);
1669 }
1670
1671 // --- EVENTS
1672 override void OnStoreSave(ParamsWriteContext ctx)
1673 {
1674 super.OnStoreSave(ctx);
1675
1676 //sync parts 01
1677 ctx.Write(m_SyncParts01);
1678 ctx.Write(m_SyncParts02);
1679 ctx.Write(m_SyncParts03);
1680
1681 ctx.Write(m_HasBase);
1682 }
1683
1684 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1685 {
1686 if (!super.OnStoreLoad(ctx, version))
1687 return false;
1688
1689 //--- Base building data ---
1690 //Restore synced parts data
1691 if (!ctx.Read(m_SyncParts01))
1692 {
1693 m_SyncParts01 = 0; //set default
1694 return false;
1695 }
1696 if (!ctx.Read(m_SyncParts02))
1697 {
1698 m_SyncParts02 = 0; //set default
1699 return false;
1700 }
1701 if (!ctx.Read(m_SyncParts03))
1702 {
1703 m_SyncParts03 = 0; //set default
1704 return false;
1705 }
1706
1707 //has base
1708 if (!ctx.Read(m_HasBase))
1709 {
1710 m_HasBase = false;
1711 return false;
1712 }
1713 //---
1714
1715 return true;
1716 }
1717
1718 override void AfterStoreLoad()
1719 {
1720 super.AfterStoreLoad();
1721
1724 }
1725
1727 {
1728 //update server data
1730
1731 //set base state
1732 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1733 SetBaseState(construction_part.IsBuilt()) ;
1734
1735 //synchronize after load
1737 }
1738
1739 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1740 {
1742 return;
1743
1744 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1745
1746 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1747 return;
1748
1750 string part_name = zone;
1751 part_name.ToLower();
1752
1754 {
1756
1757 if (construction_part && construction.IsPartConstructed(part_name))
1758 {
1759 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1760 construction.DestroyConnectedParts(part_name);
1761 }
1762
1763 //barbed wire handling (hack-ish)
1764 if (part_name.Contains("barbed"))
1765 {
1766 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1767 if (barbed_wire)
1768 barbed_wire.SetMountedState(false);
1769 }
1770 }
1771 }
1772
1773 override void EEOnAfterLoad()
1774 {
1776 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1777
1778 super.EEOnAfterLoad();
1779 }
1780
1781 override void EEInit()
1782 {
1783 super.EEInit();
1784
1785 // init visuals and physics
1786 InitBaseState();
1787
1788 //debug
1789#ifdef DEVELOPER
1791#endif
1792 }
1793
1794 override void EEItemAttached(EntityAI item, string slot_name)
1795 {
1796 super.EEItemAttached(item, slot_name);
1797
1799 UpdateVisuals();
1801 }
1802
1803 override void EEItemDetached(EntityAI item, string slot_name)
1804 {
1805 super.EEItemDetached(item, slot_name);
1806
1807 UpdateVisuals();
1809 }
1810
1811 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
1812 {
1814 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
1815
1818 }
1819
1820 //ignore out of reach condition
1821 override bool IgnoreOutOfReachCondition()
1822 {
1823 return true;
1824 }
1825
1826 //CONSTRUCTION EVENTS
1827 //Build
1828 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
1829 {
1831
1832 //check base state
1833 if (construtionPart.IsBase())
1834 {
1835 SetBaseState(true);
1836
1837 //spawn kit
1839 }
1840
1841 //register constructed parts for synchronization
1843
1844 //register action that was performed on part
1846
1847 //synchronize
1849
1850 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
1851
1852 UpdateNavmesh();
1853
1854 //update visuals
1855 UpdateVisuals();
1856
1857 //reset action sync data
1858 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1859 }
1860
1861 void OnPartBuiltClient(string part_name, int action_id)
1862 {
1863 //play sound
1865 }
1866
1867 //Dismantle
1869 {
1870 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
1872
1873 //register constructed parts for synchronization
1875
1876 //register action that was performed on part
1878
1879 //synchronize
1881
1882 // server part of sync, client will be synced from SetPartsFromSyncData
1884
1885 UpdateNavmesh();
1886
1887 //update visuals
1888 UpdateVisuals();
1889
1890 //reset action sync data
1891 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1892
1893 //check base state
1894 if (construtionPart.IsBase())
1895 {
1896 //Destroy construction
1897 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1898 }
1899 }
1900
1902 {
1903 //play sound
1905 }
1906
1907 //Destroy
1909 {
1910 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
1912
1913 //register constructed parts for synchronization
1915
1916 //register action that was performed on part
1918
1919 //synchronize
1921
1922 // server part of sync, client will be synced from SetPartsFromSyncData
1924
1925 UpdateNavmesh();
1926
1927 //update visuals
1928 UpdateVisuals();
1929
1930 //reset action sync data
1931 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1932
1933 //check base state
1934 if (construtionPart.IsBase())
1935 {
1936 //Destroy construction
1937 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1938 }
1939 }
1940
1941 void OnPartDestroyedClient(string part_name, int action_id)
1942 {
1943 //play sound
1945 }
1946
1947 // --- UPDATE
1948 void InitBaseState()
1949 {
1950 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
1951
1952 InitVisuals();
1953 UpdateNavmesh(); //regenerate navmesh
1954 GetConstruction().InitBaseState();
1955 }
1956
1957 void InitVisuals()
1958 {
1959 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
1960 //check base
1961 if (!HasBase())
1962 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1963 else
1964 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1965
1966 GetConstruction().UpdateVisuals();
1967 }
1968
1969 void UpdateVisuals()
1970 {
1972
1974 foreach (string slotName : attachmentSlots)
1976
1977 //check base
1978 if (!HasBase())
1979 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1980 else
1981 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1982
1983 GetConstruction().UpdateVisuals();
1984 }
1985
1987 {
1988 string slotNameMounted = slot_name + "_Mounted";
1989 EntityAI attachment = FindAttachmentBySlotName(slot_name);
1990
1991 if (attachment)
1992 {
1993 BarbedWire barbedWire = BarbedWire.Cast(attachment);
1994 if (barbedWire && barbedWire.IsMounted())
1996 else
1998
1999 if (is_locked)
2000 {
2001 SetAnimationPhase(slotNameMounted, 0);
2002 SetAnimationPhase(slot_name, 1);
2003 }
2004 else
2005 {
2006 SetAnimationPhase(slotNameMounted, 1);
2007 SetAnimationPhase(slot_name, 0);
2008 }
2009 }
2010 else
2011 {
2012 SetAnimationPhase(slotNameMounted, 1);
2013 SetAnimationPhase(slot_name, 1);
2014
2016 }
2017 }
2018
2019 // avoid calling this function on frequent occasions, it's a massive performance hit
2020 void UpdatePhysics()
2021 {
2023 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2024
2027
2029 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2030
2031 foreach (string slotName : attachmentSlots)
2033
2034 //check base
2035 if (!HasBase())
2036 {
2038 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2039
2040 AddProxyPhysics(ANIMATION_DEPLOYED);
2041 }
2042 else
2043 {
2045 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2046
2047 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2048 }
2049
2050 GetConstruction().UpdatePhysics();
2051 UpdateNavmesh();
2052 }
2053
2055 {
2056 //checks for invalid appends; hotfix
2057 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2058 return;
2059 //----------------------------------
2060 string slot_name_mounted = slot_name + "_Mounted";
2061 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2062
2063 //remove proxy physics
2064 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2065 RemoveProxyPhysics(slot_name_mounted);
2066 RemoveProxyPhysics(slot_name);
2067
2068 if (attachment)
2069 {
2070 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2071 if (is_locked)
2072 {
2073 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2074 AddProxyPhysics(slot_name_mounted);
2075 }
2076 else
2077 {
2078 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2079 AddProxyPhysics(slot_name);
2080 }
2081 }
2082 }
2083
2084 protected void UpdateNavmesh()
2085 {
2086 SetAffectPathgraph(true, false);
2087 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2088 }
2089
2090 override bool CanUseConstruction()
2091 {
2092 return true;
2093 }
2094
2095 override bool CanUseConstructionBuild()
2096 {
2097 return true;
2098 }
2099
2101 {
2102 if (attachment)
2103 {
2105 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2106
2107 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2108 }
2109
2110 return false;
2111 }
2112
2113 protected bool IsAttachmentSlotLocked(string slot_name)
2114 {
2115 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2116 }
2117
2118 //--- ATTACHMENT SLOTS
2120 {
2121 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2122 if (GetGame().ConfigIsExisting(config_path))
2123 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2124 }
2125
2127 {
2128 return true;
2129 }
2130
2131 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2132 {
2133 return true;
2134 }
2135
2136 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2137 {
2138 return true;
2139 }
2140
2141 // --- INIT
2142 void ConstructionInit()
2143 {
2144 if (!m_Construction)
2145 m_Construction = new Construction(this);
2146
2147 GetConstruction().Init();
2148 }
2149
2151 {
2152 return m_Construction;
2153 }
2154
2155 //--- INVENTORY/ATTACHMENTS CONDITIONS
2156 //attachments
2158 {
2159 return super.CanReceiveAttachment(attachment, slotId);
2160 }
2161
2163 {
2164 int attachment_count = GetInventory().AttachmentCount();
2165 if (attachment_count > 0)
2166 {
2167 if (HasBase() && attachment_count == 1)
2168 return false;
2169
2170 return true;
2171 }
2172
2173 return false;
2174 }
2175
2176 override bool ShowZonesHealth()
2177 {
2178 return true;
2179 }
2180
2181 //this into/outo parent.Cargo
2182 override bool CanPutInCargo(EntityAI parent)
2183 {
2184 return false;
2185 }
2186
2187 override bool CanRemoveFromCargo(EntityAI parent)
2188 {
2189 return false;
2190 }
2191
2192 //hands
2193 override bool CanPutIntoHands(EntityAI parent)
2194 {
2195 return false;
2196 }
2197
2198 //--- ACTION CONDITIONS
2199 //direction
2200 override bool IsFacingPlayer(PlayerBase player, string selection)
2201 {
2202 return true;
2203 }
2204
2205 override bool IsPlayerInside(PlayerBase player, string selection)
2206 {
2207 return true;
2208 }
2209
2212 {
2213 return false;
2214 }
2215
2216 //camera direction check
2217 bool IsFacingCamera(string selection)
2218 {
2219 return true;
2220 }
2221
2222 //roof check
2224 {
2225 return false;
2226 }
2227
2228 //selection->player distance check
2229 bool HasProperDistance(string selection, PlayerBase player)
2230 {
2231 return true;
2232 }
2233
2234 //folding
2236 {
2237 if (HasBase() || GetInventory().AttachmentCount() > 0)
2238 return false;
2239
2240 return true;
2241 }
2242
2244 {
2247
2248 return item;
2249 }
2250
2251 //Damage triggers (barbed wire)
2252 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2253 {
2254 if (GetGame() && GetGame().IsServer())
2255 {
2256 //destroy area damage if some already exists
2258
2259 //create new area damage
2261 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2262
2263 vector min_max[2];
2264 if (MemoryPointExists(slot_name + "_min"))
2265 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2266 if (MemoryPointExists(slot_name + "_max"))
2267 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2268
2269 //get proper trigger extents (min<max)
2270 vector extents[2];
2271 GetConstruction().GetTriggerExtents(min_max, extents);
2272
2273 //get box center
2274 vector center;
2275 center = GetConstruction().GetBoxCenter(min_max);
2276 center = ModelToWorld(center);
2277
2278 //rotate center if needed
2281
2282 areaDamage.SetExtents(extents[0], extents[1]);
2283 areaDamage.SetAreaPosition(center);
2284 areaDamage.SetAreaOrientation(orientation);
2285 areaDamage.SetLoopInterval(1.0);
2286 areaDamage.SetDeferDuration(0.2);
2287 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2288 areaDamage.SetAmmoName("BarbedWireHit");
2289 areaDamage.Spawn();
2290
2292 }
2293 }
2294
2296 {
2297 if (angle_deg != 0)
2298 {
2299 //orientation
2301
2302 //center
2304 if (MemoryPointExists("rotate_axis"))
2305 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2308 center[0] = r_center_x;
2309 center[2] = r_center_z;
2310 }
2311 }
2312
2313 void DestroyAreaDamage(string slot_name)
2314 {
2315 if (GetGame() && GetGame().IsServer())
2316 {
2319 {
2320 if (areaDamage)
2321 areaDamage.Destroy();
2322
2324 }
2325 }
2326 }
2327
2328 override bool IsIgnoredByConstruction()
2329 {
2330 return true;
2331 }
2332
2333 //================================================================
2334 // SOUNDS
2335 //================================================================
2336 protected void SoundBuildStart(string part_name)
2337 {
2338 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2339 }
2340
2341 protected void SoundDismantleStart(string part_name)
2342 {
2343 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2344 }
2345
2346 protected void SoundDestroyStart(string part_name)
2347 {
2348 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2349 }
2350
2351 protected string GetBuildSoundByMaterial(string part_name)
2352 {
2354
2355 switch (material_type)
2356 {
2357 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2358 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2359 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2360 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2361 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2362 }
2363
2364 return "";
2365 }
2366
2367 protected string GetDismantleSoundByMaterial(string part_name)
2368 {
2370
2371 switch (material_type)
2372 {
2373 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2374 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2375 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2376 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2377 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2378 }
2379
2380 return "";
2381 }
2382
2383 //misc
2385 {
2386 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2387 {
2388 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2390 SetHealth(slot_name, "Health", item.GetHealth());
2391 }
2392 }
2393
2394 override int GetDamageSystemVersionChange()
2395 {
2396 return 111;
2397 }
2398
2399 override void SetActions()
2400 {
2401 super.SetActions();
2402
2404 //AddAction(ActionTakeHybridAttachment);
2405 //AddAction(ActionTakeHybridAttachmentToHands);
2408 }
2409
2410 //================================================================
2411 // DEBUG
2412 //================================================================
2413 protected void DebugCustomState()
2414 {
2415 }
2416
2419 {
2420 return null;
2421 }
2422
2423 override void OnDebugSpawn()
2424 {
2425 FullyBuild();
2426 }
2427
2428 void FullyBuild()
2429 {
2431 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2432
2433 Man p;
2434
2435#ifdef SERVER
2437 GetGame().GetWorld().GetPlayerList(players);
2438 if (players.Count())
2439 p = players[0];
2440#else
2441 p = GetGame().GetPlayer();
2442#endif
2443
2444 foreach (ConstructionPart part : parts)
2445 {
2446 bool excluded = false;
2447 string partName = part.GetPartName();
2448 if (excludes)
2449 {
2450 foreach (string exclude : excludes)
2451 {
2452 if (partName.Contains(exclude))
2453 {
2454 excluded = true;
2455 break;
2456 }
2457 }
2458 }
2459
2460 if (!excluded)
2462 }
2463
2464 GetConstruction().UpdateVisuals();
2465 }
2466}
2467
2468void bsbDebugPrint(string s)
2469{
2470#ifdef BSB_DEBUG
2471 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2472#else
2473 //Print("" + s); // comment/uncomment to hide/see debug logs
2474#endif
2475}
2476void bsbDebugSpam(string s)
2477{
2478#ifdef BSB_DEBUG_SPAM
2479 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2480#else
2481 //Print("" + s); // comment/uncomment to hide/see debug logs
2482#endif
2483}

Referenced by ItemBase::OnPartBuiltServer().

◆ ResetActionSyncData()

void bsbDebugPrint::ResetActionSyncData ( )
protected

Definition at line 1391 of file BaseBuildingBase.c.

1393{
1394 const string ANIMATION_DEPLOYED = "Deployed";
1395
1396 float m_ConstructionKitHealth; //stored health value for used construction kit
1397
1399
1400 bool m_HasBase;
1401 //variables for synchronization of base building parts (2x31 is the current limit)
1402 int m_SyncParts01; //synchronization for already built parts (31 parts)
1403 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1404 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1405 int m_InteractedPartId; //construction part id that an action was performed on
1406 int m_PerformedActionId; //action id that was performed on a construction part
1407
1408 //Sounds
1409 //build
1410 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1411 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1412 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1413 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1414 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1415 //dismantle
1416 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1417 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1418 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1419 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1420 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1421
1422 protected EffectSound m_Sound;
1423
1427
1428 // Constructor
1429 void BaseBuildingBase()
1430 {
1432
1433 //synchronized variables
1434 RegisterNetSyncVariableInt("m_SyncParts01");
1435 RegisterNetSyncVariableInt("m_SyncParts02");
1436 RegisterNetSyncVariableInt("m_SyncParts03");
1437 RegisterNetSyncVariableInt("m_InteractedPartId");
1438 RegisterNetSyncVariableInt("m_PerformedActionId");
1439 RegisterNetSyncVariableBool("m_HasBase");
1440
1441 //Construction init
1443
1444 if (ConfigIsExisting("hybridAttachments"))
1445 {
1447 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1448 }
1449 if (ConfigIsExisting("mountables"))
1450 {
1452 ConfigGetTextArray("mountables", m_Mountables);
1453 }
1454
1455 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1456 }
1457
1458 override void EEDelete(EntityAI parent)
1459 {
1460 super.EEDelete(parent);
1461
1462 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1464
1465 }
1466
1467 override string GetInvulnerabilityTypeString()
1468 {
1469 return "disableBaseDamage";
1470 }
1471
1472 override bool CanObstruct()
1473 {
1474 return true;
1475 }
1476
1477 override int GetHideIconMask()
1478 {
1479 return EInventoryIconVisibility.HIDE_VICINITY;
1480 }
1481
1482 // --- SYNCHRONIZATION
1484 {
1485 if (GetGame().IsServer())
1486 SetSynchDirty();
1487 }
1488
1489 override void OnVariablesSynchronized()
1490 {
1491 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1492 super.OnVariablesSynchronized();
1493
1494 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1495 }
1496
1497 protected void OnSynchronizedClient()
1498 {
1499 //update parts
1501
1502 //update action on part
1504
1505 //update visuals (client)
1506 UpdateVisuals();
1507 }
1508
1509 //parts synchronization
1511 {
1512 //part_id must starts from index = 1
1513 int offset;
1514 int mask;
1515
1516 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1517 {
1518 offset = part_id - 1;
1519 mask = 1 << offset;
1520
1522 }
1523 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1524 {
1525 offset = (part_id % 32);
1526 mask = 1 << offset;
1527
1529 }
1530 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1531 {
1532 offset = (part_id % 63);
1533 mask = 1 << offset;
1534
1536 }
1537 }
1538
1540 {
1541 //part_id must starts from index = 1
1542 int offset;
1543 int mask;
1544
1545 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1546 {
1547 offset = part_id - 1;
1548 mask = 1 << offset;
1549
1551 }
1552 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1553 {
1554 offset = (part_id % 32);
1555 mask = 1 << offset;
1556
1558 }
1559 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1560 {
1561 offset = (part_id % 63);
1562 mask = 1 << offset;
1563
1565 }
1566 }
1567
1569 {
1570 //part_id must starts from index = 1
1571 int offset;
1572 int mask;
1573
1574 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1575 {
1576 offset = part_id - 1;
1577 mask = 1 << offset;
1578
1579 if ((m_SyncParts01 & mask) > 0)
1580 return true;
1581 }
1582 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1583 {
1584 offset = (part_id % 32);
1585 mask = 1 << offset;
1586
1587 if ((m_SyncParts02 & mask) > 0)
1588 return true;
1589 }
1590 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1591 {
1592 offset = (part_id % 63);
1593 mask = 1 << offset;
1594
1595 if ((m_SyncParts03 & mask) > 0)
1596 return true;
1597 }
1598
1599 return false;
1600 }
1601
1602 protected void RegisterActionForSync(int part_id, int action_id)
1603 {
1606 }
1607
1608 protected void ResetActionSyncData()
1609 {
1610 //reset data
1611 m_InteractedPartId = -1;
1613 }
1614
1615 protected void SetActionFromSyncData()
1616 {
1617 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1618 {
1621
1622 switch (build_action_id)
1623 {
1627 }
1628 }
1629 }
1630 //------
1631
1633 {
1634 string key = part.m_PartName;
1635 bool is_base = part.IsBase();
1637 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1639 {
1640 if (!part.IsBuilt())
1641 {
1642 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1643 GetConstruction().AddToConstructedParts(key);
1644 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1645
1646 if (is_base)
1647 {
1649 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1650 }
1651 }
1652 }
1653 else
1654 {
1655 if (part.IsBuilt())
1656 {
1657 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1658 GetConstruction().RemoveFromConstructedParts(key);
1659 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1660
1661 if (is_base)
1662 {
1664 AddProxyPhysics(ANIMATION_DEPLOYED);
1665 }
1666 }
1667 }
1668
1669 //check slot lock for material attachments
1670 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1671 }
1672
1673 //set construction parts based on synchronized data
1675 {
1678
1679 for (int i = 0; i < construction_parts.Count(); ++i)
1680 {
1681 string key = construction_parts.GetKey(i);
1684 }
1685
1686 //regenerate navmesh
1687 UpdateNavmesh();
1688 }
1689
1691 {
1694
1695 for (int i = 0; i < construction_parts.Count(); ++i)
1696 {
1697 string key = construction_parts.GetKey(i);
1699
1700 if (value.GetId() == id)
1701 return value;
1702 }
1703
1704 return NULL;
1705 }
1706 //
1707
1708 //Base
1709 bool HasBase()
1710 {
1711 return m_HasBase;
1712 }
1713
1714 void SetBaseState(bool has_base)
1715 {
1717 }
1718
1719 override bool IsDeployable()
1720 {
1721 return true;
1722 }
1723
1724 bool IsOpened()
1725 {
1726 return false;
1727 }
1728
1729 //--- CONSTRUCTION KIT
1731 {
1735
1736 return construction_kit;
1737 }
1738
1740 {
1741 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1744 }
1745
1746 protected vector GetKitSpawnPosition()
1747 {
1748 return GetPosition();
1749 }
1750
1751 protected string GetConstructionKitType()
1752 {
1753 return "";
1754 }
1755
1757 {
1759 GetGame().ObjectDelete(construction_kit);
1760 }
1761
1762 //--- CONSTRUCTION
1763 void DestroyConstruction()
1764 {
1765 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1766 GetGame().ObjectDelete(this);
1767 }
1768
1769 // --- EVENTS
1770 override void OnStoreSave(ParamsWriteContext ctx)
1771 {
1772 super.OnStoreSave(ctx);
1773
1774 //sync parts 01
1775 ctx.Write(m_SyncParts01);
1776 ctx.Write(m_SyncParts02);
1777 ctx.Write(m_SyncParts03);
1778
1779 ctx.Write(m_HasBase);
1780 }
1781
1782 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1783 {
1784 if (!super.OnStoreLoad(ctx, version))
1785 return false;
1786
1787 //--- Base building data ---
1788 //Restore synced parts data
1789 if (!ctx.Read(m_SyncParts01))
1790 {
1791 m_SyncParts01 = 0; //set default
1792 return false;
1793 }
1794 if (!ctx.Read(m_SyncParts02))
1795 {
1796 m_SyncParts02 = 0; //set default
1797 return false;
1798 }
1799 if (!ctx.Read(m_SyncParts03))
1800 {
1801 m_SyncParts03 = 0; //set default
1802 return false;
1803 }
1804
1805 //has base
1806 if (!ctx.Read(m_HasBase))
1807 {
1808 m_HasBase = false;
1809 return false;
1810 }
1811 //---
1812
1813 return true;
1814 }
1815
1816 override void AfterStoreLoad()
1817 {
1818 super.AfterStoreLoad();
1819
1822 }
1823
1825 {
1826 //update server data
1828
1829 //set base state
1830 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1831 SetBaseState(construction_part.IsBuilt()) ;
1832
1833 //synchronize after load
1835 }
1836
1837 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1838 {
1840 return;
1841
1842 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1843
1844 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1845 return;
1846
1848 string part_name = zone;
1849 part_name.ToLower();
1850
1852 {
1854
1855 if (construction_part && construction.IsPartConstructed(part_name))
1856 {
1857 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1858 construction.DestroyConnectedParts(part_name);
1859 }
1860
1861 //barbed wire handling (hack-ish)
1862 if (part_name.Contains("barbed"))
1863 {
1864 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1865 if (barbed_wire)
1866 barbed_wire.SetMountedState(false);
1867 }
1868 }
1869 }
1870
1871 override void EEOnAfterLoad()
1872 {
1874 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1875
1876 super.EEOnAfterLoad();
1877 }
1878
1879 override void EEInit()
1880 {
1881 super.EEInit();
1882
1883 // init visuals and physics
1884 InitBaseState();
1885
1886 //debug
1887#ifdef DEVELOPER
1889#endif
1890 }
1891
1892 override void EEItemAttached(EntityAI item, string slot_name)
1893 {
1894 super.EEItemAttached(item, slot_name);
1895
1897 UpdateVisuals();
1899 }
1900
1901 override void EEItemDetached(EntityAI item, string slot_name)
1902 {
1903 super.EEItemDetached(item, slot_name);
1904
1905 UpdateVisuals();
1907 }
1908
1909 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
1910 {
1912 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
1913
1916 }
1917
1918 //ignore out of reach condition
1919 override bool IgnoreOutOfReachCondition()
1920 {
1921 return true;
1922 }
1923
1924 //CONSTRUCTION EVENTS
1925 //Build
1926 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
1927 {
1929
1930 //check base state
1931 if (construtionPart.IsBase())
1932 {
1933 SetBaseState(true);
1934
1935 //spawn kit
1937 }
1938
1939 //register constructed parts for synchronization
1941
1942 //register action that was performed on part
1944
1945 //synchronize
1947
1948 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
1949
1950 UpdateNavmesh();
1951
1952 //update visuals
1953 UpdateVisuals();
1954
1955 //reset action sync data
1956 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1957 }
1958
1959 void OnPartBuiltClient(string part_name, int action_id)
1960 {
1961 //play sound
1963 }
1964
1965 //Dismantle
1967 {
1968 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
1970
1971 //register constructed parts for synchronization
1973
1974 //register action that was performed on part
1976
1977 //synchronize
1979
1980 // server part of sync, client will be synced from SetPartsFromSyncData
1982
1983 UpdateNavmesh();
1984
1985 //update visuals
1986 UpdateVisuals();
1987
1988 //reset action sync data
1989 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1990
1991 //check base state
1992 if (construtionPart.IsBase())
1993 {
1994 //Destroy construction
1995 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1996 }
1997 }
1998
2000 {
2001 //play sound
2003 }
2004
2005 //Destroy
2007 {
2008 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2010
2011 //register constructed parts for synchronization
2013
2014 //register action that was performed on part
2016
2017 //synchronize
2019
2020 // server part of sync, client will be synced from SetPartsFromSyncData
2022
2023 UpdateNavmesh();
2024
2025 //update visuals
2026 UpdateVisuals();
2027
2028 //reset action sync data
2029 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2030
2031 //check base state
2032 if (construtionPart.IsBase())
2033 {
2034 //Destroy construction
2035 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2036 }
2037 }
2038
2039 void OnPartDestroyedClient(string part_name, int action_id)
2040 {
2041 //play sound
2043 }
2044
2045 // --- UPDATE
2046 void InitBaseState()
2047 {
2048 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2049
2050 InitVisuals();
2051 UpdateNavmesh(); //regenerate navmesh
2052 GetConstruction().InitBaseState();
2053 }
2054
2055 void InitVisuals()
2056 {
2057 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2058 //check base
2059 if (!HasBase())
2060 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2061 else
2062 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2063
2064 GetConstruction().UpdateVisuals();
2065 }
2066
2067 void UpdateVisuals()
2068 {
2070
2072 foreach (string slotName : attachmentSlots)
2074
2075 //check base
2076 if (!HasBase())
2077 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2078 else
2079 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2080
2081 GetConstruction().UpdateVisuals();
2082 }
2083
2085 {
2086 string slotNameMounted = slot_name + "_Mounted";
2087 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2088
2089 if (attachment)
2090 {
2091 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2092 if (barbedWire && barbedWire.IsMounted())
2094 else
2096
2097 if (is_locked)
2098 {
2099 SetAnimationPhase(slotNameMounted, 0);
2100 SetAnimationPhase(slot_name, 1);
2101 }
2102 else
2103 {
2104 SetAnimationPhase(slotNameMounted, 1);
2105 SetAnimationPhase(slot_name, 0);
2106 }
2107 }
2108 else
2109 {
2110 SetAnimationPhase(slotNameMounted, 1);
2111 SetAnimationPhase(slot_name, 1);
2112
2114 }
2115 }
2116
2117 // avoid calling this function on frequent occasions, it's a massive performance hit
2118 void UpdatePhysics()
2119 {
2121 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2122
2125
2127 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2128
2129 foreach (string slotName : attachmentSlots)
2131
2132 //check base
2133 if (!HasBase())
2134 {
2136 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2137
2138 AddProxyPhysics(ANIMATION_DEPLOYED);
2139 }
2140 else
2141 {
2143 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2144
2145 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2146 }
2147
2148 GetConstruction().UpdatePhysics();
2149 UpdateNavmesh();
2150 }
2151
2153 {
2154 //checks for invalid appends; hotfix
2155 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2156 return;
2157 //----------------------------------
2158 string slot_name_mounted = slot_name + "_Mounted";
2159 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2160
2161 //remove proxy physics
2162 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2163 RemoveProxyPhysics(slot_name_mounted);
2164 RemoveProxyPhysics(slot_name);
2165
2166 if (attachment)
2167 {
2168 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2169 if (is_locked)
2170 {
2171 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2172 AddProxyPhysics(slot_name_mounted);
2173 }
2174 else
2175 {
2176 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2177 AddProxyPhysics(slot_name);
2178 }
2179 }
2180 }
2181
2182 protected void UpdateNavmesh()
2183 {
2184 SetAffectPathgraph(true, false);
2185 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2186 }
2187
2188 override bool CanUseConstruction()
2189 {
2190 return true;
2191 }
2192
2193 override bool CanUseConstructionBuild()
2194 {
2195 return true;
2196 }
2197
2199 {
2200 if (attachment)
2201 {
2203 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2204
2205 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2206 }
2207
2208 return false;
2209 }
2210
2211 protected bool IsAttachmentSlotLocked(string slot_name)
2212 {
2213 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2214 }
2215
2216 //--- ATTACHMENT SLOTS
2218 {
2219 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2220 if (GetGame().ConfigIsExisting(config_path))
2221 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2222 }
2223
2225 {
2226 return true;
2227 }
2228
2229 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2230 {
2231 return true;
2232 }
2233
2234 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2235 {
2236 return true;
2237 }
2238
2239 // --- INIT
2240 void ConstructionInit()
2241 {
2242 if (!m_Construction)
2243 m_Construction = new Construction(this);
2244
2245 GetConstruction().Init();
2246 }
2247
2249 {
2250 return m_Construction;
2251 }
2252
2253 //--- INVENTORY/ATTACHMENTS CONDITIONS
2254 //attachments
2256 {
2257 return super.CanReceiveAttachment(attachment, slotId);
2258 }
2259
2261 {
2262 int attachment_count = GetInventory().AttachmentCount();
2263 if (attachment_count > 0)
2264 {
2265 if (HasBase() && attachment_count == 1)
2266 return false;
2267
2268 return true;
2269 }
2270
2271 return false;
2272 }
2273
2274 override bool ShowZonesHealth()
2275 {
2276 return true;
2277 }
2278
2279 //this into/outo parent.Cargo
2280 override bool CanPutInCargo(EntityAI parent)
2281 {
2282 return false;
2283 }
2284
2285 override bool CanRemoveFromCargo(EntityAI parent)
2286 {
2287 return false;
2288 }
2289
2290 //hands
2291 override bool CanPutIntoHands(EntityAI parent)
2292 {
2293 return false;
2294 }
2295
2296 //--- ACTION CONDITIONS
2297 //direction
2298 override bool IsFacingPlayer(PlayerBase player, string selection)
2299 {
2300 return true;
2301 }
2302
2303 override bool IsPlayerInside(PlayerBase player, string selection)
2304 {
2305 return true;
2306 }
2307
2310 {
2311 return false;
2312 }
2313
2314 //camera direction check
2315 bool IsFacingCamera(string selection)
2316 {
2317 return true;
2318 }
2319
2320 //roof check
2322 {
2323 return false;
2324 }
2325
2326 //selection->player distance check
2327 bool HasProperDistance(string selection, PlayerBase player)
2328 {
2329 return true;
2330 }
2331
2332 //folding
2334 {
2335 if (HasBase() || GetInventory().AttachmentCount() > 0)
2336 return false;
2337
2338 return true;
2339 }
2340
2342 {
2345
2346 return item;
2347 }
2348
2349 //Damage triggers (barbed wire)
2350 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2351 {
2352 if (GetGame() && GetGame().IsServer())
2353 {
2354 //destroy area damage if some already exists
2356
2357 //create new area damage
2359 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2360
2361 vector min_max[2];
2362 if (MemoryPointExists(slot_name + "_min"))
2363 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2364 if (MemoryPointExists(slot_name + "_max"))
2365 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2366
2367 //get proper trigger extents (min<max)
2368 vector extents[2];
2369 GetConstruction().GetTriggerExtents(min_max, extents);
2370
2371 //get box center
2372 vector center;
2373 center = GetConstruction().GetBoxCenter(min_max);
2374 center = ModelToWorld(center);
2375
2376 //rotate center if needed
2379
2380 areaDamage.SetExtents(extents[0], extents[1]);
2381 areaDamage.SetAreaPosition(center);
2382 areaDamage.SetAreaOrientation(orientation);
2383 areaDamage.SetLoopInterval(1.0);
2384 areaDamage.SetDeferDuration(0.2);
2385 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2386 areaDamage.SetAmmoName("BarbedWireHit");
2387 areaDamage.Spawn();
2388
2390 }
2391 }
2392
2394 {
2395 if (angle_deg != 0)
2396 {
2397 //orientation
2399
2400 //center
2402 if (MemoryPointExists("rotate_axis"))
2403 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2406 center[0] = r_center_x;
2407 center[2] = r_center_z;
2408 }
2409 }
2410
2411 void DestroyAreaDamage(string slot_name)
2412 {
2413 if (GetGame() && GetGame().IsServer())
2414 {
2417 {
2418 if (areaDamage)
2419 areaDamage.Destroy();
2420
2422 }
2423 }
2424 }
2425
2426 override bool IsIgnoredByConstruction()
2427 {
2428 return true;
2429 }
2430
2431 //================================================================
2432 // SOUNDS
2433 //================================================================
2434 protected void SoundBuildStart(string part_name)
2435 {
2436 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2437 }
2438
2439 protected void SoundDismantleStart(string part_name)
2440 {
2441 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2442 }
2443
2444 protected void SoundDestroyStart(string part_name)
2445 {
2446 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2447 }
2448
2449 protected string GetBuildSoundByMaterial(string part_name)
2450 {
2452
2453 switch (material_type)
2454 {
2455 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2456 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2457 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2458 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2459 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2460 }
2461
2462 return "";
2463 }
2464
2465 protected string GetDismantleSoundByMaterial(string part_name)
2466 {
2468
2469 switch (material_type)
2470 {
2471 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2472 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2473 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2474 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2475 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2476 }
2477
2478 return "";
2479 }
2480
2481 //misc
2483 {
2484 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2485 {
2486 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2488 SetHealth(slot_name, "Health", item.GetHealth());
2489 }
2490 }
2491
2492 override int GetDamageSystemVersionChange()
2493 {
2494 return 111;
2495 }
2496
2497 override void SetActions()
2498 {
2499 super.SetActions();
2500
2502 //AddAction(ActionTakeHybridAttachment);
2503 //AddAction(ActionTakeHybridAttachmentToHands);
2506 }
2507
2508 //================================================================
2509 // DEBUG
2510 //================================================================
2511 protected void DebugCustomState()
2512 {
2513 }
2514
2517 {
2518 return null;
2519 }
2520
2521 override void OnDebugSpawn()
2522 {
2523 FullyBuild();
2524 }
2525
2526 void FullyBuild()
2527 {
2529 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2530
2531 Man p;
2532
2533#ifdef SERVER
2535 GetGame().GetWorld().GetPlayerList(players);
2536 if (players.Count())
2537 p = players[0];
2538#else
2539 p = GetGame().GetPlayer();
2540#endif
2541
2542 foreach (ConstructionPart part : parts)
2543 {
2544 bool excluded = false;
2545 string partName = part.GetPartName();
2546 if (excludes)
2547 {
2548 foreach (string exclude : excludes)
2549 {
2550 if (partName.Contains(exclude))
2551 {
2552 excluded = true;
2553 break;
2554 }
2555 }
2556 }
2557
2558 if (!excluded)
2560 }
2561
2562 GetConstruction().UpdateVisuals();
2563 }
2564}
2565
2566void bsbDebugPrint(string s)
2567{
2568#ifdef BSB_DEBUG
2569 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2570#else
2571 //Print("" + s); // comment/uncomment to hide/see debug logs
2572#endif
2573}
2574void bsbDebugSpam(string s)
2575{
2576#ifdef BSB_DEBUG_SPAM
2577 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2578#else
2579 //Print("" + s); // comment/uncomment to hide/see debug logs
2580#endif
2581}

Referenced by ItemBase::OnPartBuiltServer(), ItemBase::OnPartDestroyedServer(), and ItemBase::OnPartDismantledServer().

◆ SetActionFromSyncData()

void bsbDebugPrint::SetActionFromSyncData ( )
protected

Definition at line 1398 of file BaseBuildingBase.c.

1400{
1401 const string ANIMATION_DEPLOYED = "Deployed";
1402
1403 float m_ConstructionKitHealth; //stored health value for used construction kit
1404
1406
1407 bool m_HasBase;
1408 //variables for synchronization of base building parts (2x31 is the current limit)
1409 int m_SyncParts01; //synchronization for already built parts (31 parts)
1410 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1411 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1412 int m_InteractedPartId; //construction part id that an action was performed on
1413 int m_PerformedActionId; //action id that was performed on a construction part
1414
1415 //Sounds
1416 //build
1417 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1418 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1419 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1420 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1421 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1422 //dismantle
1423 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1424 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1425 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1426 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1427 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1428
1429 protected EffectSound m_Sound;
1430
1434
1435 // Constructor
1436 void BaseBuildingBase()
1437 {
1439
1440 //synchronized variables
1441 RegisterNetSyncVariableInt("m_SyncParts01");
1442 RegisterNetSyncVariableInt("m_SyncParts02");
1443 RegisterNetSyncVariableInt("m_SyncParts03");
1444 RegisterNetSyncVariableInt("m_InteractedPartId");
1445 RegisterNetSyncVariableInt("m_PerformedActionId");
1446 RegisterNetSyncVariableBool("m_HasBase");
1447
1448 //Construction init
1450
1451 if (ConfigIsExisting("hybridAttachments"))
1452 {
1454 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1455 }
1456 if (ConfigIsExisting("mountables"))
1457 {
1459 ConfigGetTextArray("mountables", m_Mountables);
1460 }
1461
1462 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1463 }
1464
1465 override void EEDelete(EntityAI parent)
1466 {
1467 super.EEDelete(parent);
1468
1469 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1471
1472 }
1473
1474 override string GetInvulnerabilityTypeString()
1475 {
1476 return "disableBaseDamage";
1477 }
1478
1479 override bool CanObstruct()
1480 {
1481 return true;
1482 }
1483
1484 override int GetHideIconMask()
1485 {
1486 return EInventoryIconVisibility.HIDE_VICINITY;
1487 }
1488
1489 // --- SYNCHRONIZATION
1491 {
1492 if (GetGame().IsServer())
1493 SetSynchDirty();
1494 }
1495
1496 override void OnVariablesSynchronized()
1497 {
1498 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1499 super.OnVariablesSynchronized();
1500
1501 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1502 }
1503
1504 protected void OnSynchronizedClient()
1505 {
1506 //update parts
1508
1509 //update action on part
1511
1512 //update visuals (client)
1513 UpdateVisuals();
1514 }
1515
1516 //parts synchronization
1518 {
1519 //part_id must starts from index = 1
1520 int offset;
1521 int mask;
1522
1523 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1524 {
1525 offset = part_id - 1;
1526 mask = 1 << offset;
1527
1529 }
1530 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1531 {
1532 offset = (part_id % 32);
1533 mask = 1 << offset;
1534
1536 }
1537 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1538 {
1539 offset = (part_id % 63);
1540 mask = 1 << offset;
1541
1543 }
1544 }
1545
1547 {
1548 //part_id must starts from index = 1
1549 int offset;
1550 int mask;
1551
1552 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1553 {
1554 offset = part_id - 1;
1555 mask = 1 << offset;
1556
1558 }
1559 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1560 {
1561 offset = (part_id % 32);
1562 mask = 1 << offset;
1563
1565 }
1566 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1567 {
1568 offset = (part_id % 63);
1569 mask = 1 << offset;
1570
1572 }
1573 }
1574
1576 {
1577 //part_id must starts from index = 1
1578 int offset;
1579 int mask;
1580
1581 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1582 {
1583 offset = part_id - 1;
1584 mask = 1 << offset;
1585
1586 if ((m_SyncParts01 & mask) > 0)
1587 return true;
1588 }
1589 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1590 {
1591 offset = (part_id % 32);
1592 mask = 1 << offset;
1593
1594 if ((m_SyncParts02 & mask) > 0)
1595 return true;
1596 }
1597 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1598 {
1599 offset = (part_id % 63);
1600 mask = 1 << offset;
1601
1602 if ((m_SyncParts03 & mask) > 0)
1603 return true;
1604 }
1605
1606 return false;
1607 }
1608
1609 protected void RegisterActionForSync(int part_id, int action_id)
1610 {
1613 }
1614
1615 protected void ResetActionSyncData()
1616 {
1617 //reset data
1618 m_InteractedPartId = -1;
1620 }
1621
1622 protected void SetActionFromSyncData()
1623 {
1624 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1625 {
1628
1629 switch (build_action_id)
1630 {
1634 }
1635 }
1636 }
1637 //------
1638
1640 {
1641 string key = part.m_PartName;
1642 bool is_base = part.IsBase();
1644 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1646 {
1647 if (!part.IsBuilt())
1648 {
1649 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1650 GetConstruction().AddToConstructedParts(key);
1651 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1652
1653 if (is_base)
1654 {
1656 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1657 }
1658 }
1659 }
1660 else
1661 {
1662 if (part.IsBuilt())
1663 {
1664 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1665 GetConstruction().RemoveFromConstructedParts(key);
1666 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1667
1668 if (is_base)
1669 {
1671 AddProxyPhysics(ANIMATION_DEPLOYED);
1672 }
1673 }
1674 }
1675
1676 //check slot lock for material attachments
1677 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1678 }
1679
1680 //set construction parts based on synchronized data
1682 {
1685
1686 for (int i = 0; i < construction_parts.Count(); ++i)
1687 {
1688 string key = construction_parts.GetKey(i);
1691 }
1692
1693 //regenerate navmesh
1694 UpdateNavmesh();
1695 }
1696
1698 {
1701
1702 for (int i = 0; i < construction_parts.Count(); ++i)
1703 {
1704 string key = construction_parts.GetKey(i);
1706
1707 if (value.GetId() == id)
1708 return value;
1709 }
1710
1711 return NULL;
1712 }
1713 //
1714
1715 //Base
1716 bool HasBase()
1717 {
1718 return m_HasBase;
1719 }
1720
1721 void SetBaseState(bool has_base)
1722 {
1724 }
1725
1726 override bool IsDeployable()
1727 {
1728 return true;
1729 }
1730
1731 bool IsOpened()
1732 {
1733 return false;
1734 }
1735
1736 //--- CONSTRUCTION KIT
1738 {
1742
1743 return construction_kit;
1744 }
1745
1747 {
1748 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1751 }
1752
1753 protected vector GetKitSpawnPosition()
1754 {
1755 return GetPosition();
1756 }
1757
1758 protected string GetConstructionKitType()
1759 {
1760 return "";
1761 }
1762
1764 {
1766 GetGame().ObjectDelete(construction_kit);
1767 }
1768
1769 //--- CONSTRUCTION
1770 void DestroyConstruction()
1771 {
1772 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1773 GetGame().ObjectDelete(this);
1774 }
1775
1776 // --- EVENTS
1777 override void OnStoreSave(ParamsWriteContext ctx)
1778 {
1779 super.OnStoreSave(ctx);
1780
1781 //sync parts 01
1782 ctx.Write(m_SyncParts01);
1783 ctx.Write(m_SyncParts02);
1784 ctx.Write(m_SyncParts03);
1785
1786 ctx.Write(m_HasBase);
1787 }
1788
1789 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1790 {
1791 if (!super.OnStoreLoad(ctx, version))
1792 return false;
1793
1794 //--- Base building data ---
1795 //Restore synced parts data
1796 if (!ctx.Read(m_SyncParts01))
1797 {
1798 m_SyncParts01 = 0; //set default
1799 return false;
1800 }
1801 if (!ctx.Read(m_SyncParts02))
1802 {
1803 m_SyncParts02 = 0; //set default
1804 return false;
1805 }
1806 if (!ctx.Read(m_SyncParts03))
1807 {
1808 m_SyncParts03 = 0; //set default
1809 return false;
1810 }
1811
1812 //has base
1813 if (!ctx.Read(m_HasBase))
1814 {
1815 m_HasBase = false;
1816 return false;
1817 }
1818 //---
1819
1820 return true;
1821 }
1822
1823 override void AfterStoreLoad()
1824 {
1825 super.AfterStoreLoad();
1826
1829 }
1830
1832 {
1833 //update server data
1835
1836 //set base state
1837 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1838 SetBaseState(construction_part.IsBuilt()) ;
1839
1840 //synchronize after load
1842 }
1843
1844 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1845 {
1847 return;
1848
1849 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1850
1851 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1852 return;
1853
1855 string part_name = zone;
1856 part_name.ToLower();
1857
1859 {
1861
1862 if (construction_part && construction.IsPartConstructed(part_name))
1863 {
1864 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1865 construction.DestroyConnectedParts(part_name);
1866 }
1867
1868 //barbed wire handling (hack-ish)
1869 if (part_name.Contains("barbed"))
1870 {
1871 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1872 if (barbed_wire)
1873 barbed_wire.SetMountedState(false);
1874 }
1875 }
1876 }
1877
1878 override void EEOnAfterLoad()
1879 {
1881 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1882
1883 super.EEOnAfterLoad();
1884 }
1885
1886 override void EEInit()
1887 {
1888 super.EEInit();
1889
1890 // init visuals and physics
1891 InitBaseState();
1892
1893 //debug
1894#ifdef DEVELOPER
1896#endif
1897 }
1898
1899 override void EEItemAttached(EntityAI item, string slot_name)
1900 {
1901 super.EEItemAttached(item, slot_name);
1902
1904 UpdateVisuals();
1906 }
1907
1908 override void EEItemDetached(EntityAI item, string slot_name)
1909 {
1910 super.EEItemDetached(item, slot_name);
1911
1912 UpdateVisuals();
1914 }
1915
1916 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
1917 {
1919 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
1920
1923 }
1924
1925 //ignore out of reach condition
1926 override bool IgnoreOutOfReachCondition()
1927 {
1928 return true;
1929 }
1930
1931 //CONSTRUCTION EVENTS
1932 //Build
1933 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
1934 {
1936
1937 //check base state
1938 if (construtionPart.IsBase())
1939 {
1940 SetBaseState(true);
1941
1942 //spawn kit
1944 }
1945
1946 //register constructed parts for synchronization
1948
1949 //register action that was performed on part
1951
1952 //synchronize
1954
1955 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
1956
1957 UpdateNavmesh();
1958
1959 //update visuals
1960 UpdateVisuals();
1961
1962 //reset action sync data
1963 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1964 }
1965
1966 void OnPartBuiltClient(string part_name, int action_id)
1967 {
1968 //play sound
1970 }
1971
1972 //Dismantle
1974 {
1975 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
1977
1978 //register constructed parts for synchronization
1980
1981 //register action that was performed on part
1983
1984 //synchronize
1986
1987 // server part of sync, client will be synced from SetPartsFromSyncData
1989
1990 UpdateNavmesh();
1991
1992 //update visuals
1993 UpdateVisuals();
1994
1995 //reset action sync data
1996 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1997
1998 //check base state
1999 if (construtionPart.IsBase())
2000 {
2001 //Destroy construction
2002 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2003 }
2004 }
2005
2007 {
2008 //play sound
2010 }
2011
2012 //Destroy
2014 {
2015 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2017
2018 //register constructed parts for synchronization
2020
2021 //register action that was performed on part
2023
2024 //synchronize
2026
2027 // server part of sync, client will be synced from SetPartsFromSyncData
2029
2030 UpdateNavmesh();
2031
2032 //update visuals
2033 UpdateVisuals();
2034
2035 //reset action sync data
2036 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2037
2038 //check base state
2039 if (construtionPart.IsBase())
2040 {
2041 //Destroy construction
2042 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2043 }
2044 }
2045
2046 void OnPartDestroyedClient(string part_name, int action_id)
2047 {
2048 //play sound
2050 }
2051
2052 // --- UPDATE
2053 void InitBaseState()
2054 {
2055 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2056
2057 InitVisuals();
2058 UpdateNavmesh(); //regenerate navmesh
2059 GetConstruction().InitBaseState();
2060 }
2061
2062 void InitVisuals()
2063 {
2064 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2065 //check base
2066 if (!HasBase())
2067 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2068 else
2069 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2070
2071 GetConstruction().UpdateVisuals();
2072 }
2073
2074 void UpdateVisuals()
2075 {
2077
2079 foreach (string slotName : attachmentSlots)
2081
2082 //check base
2083 if (!HasBase())
2084 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2085 else
2086 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2087
2088 GetConstruction().UpdateVisuals();
2089 }
2090
2092 {
2093 string slotNameMounted = slot_name + "_Mounted";
2094 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2095
2096 if (attachment)
2097 {
2098 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2099 if (barbedWire && barbedWire.IsMounted())
2101 else
2103
2104 if (is_locked)
2105 {
2106 SetAnimationPhase(slotNameMounted, 0);
2107 SetAnimationPhase(slot_name, 1);
2108 }
2109 else
2110 {
2111 SetAnimationPhase(slotNameMounted, 1);
2112 SetAnimationPhase(slot_name, 0);
2113 }
2114 }
2115 else
2116 {
2117 SetAnimationPhase(slotNameMounted, 1);
2118 SetAnimationPhase(slot_name, 1);
2119
2121 }
2122 }
2123
2124 // avoid calling this function on frequent occasions, it's a massive performance hit
2125 void UpdatePhysics()
2126 {
2128 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2129
2132
2134 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2135
2136 foreach (string slotName : attachmentSlots)
2138
2139 //check base
2140 if (!HasBase())
2141 {
2143 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2144
2145 AddProxyPhysics(ANIMATION_DEPLOYED);
2146 }
2147 else
2148 {
2150 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2151
2152 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2153 }
2154
2155 GetConstruction().UpdatePhysics();
2156 UpdateNavmesh();
2157 }
2158
2160 {
2161 //checks for invalid appends; hotfix
2162 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2163 return;
2164 //----------------------------------
2165 string slot_name_mounted = slot_name + "_Mounted";
2166 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2167
2168 //remove proxy physics
2169 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2170 RemoveProxyPhysics(slot_name_mounted);
2171 RemoveProxyPhysics(slot_name);
2172
2173 if (attachment)
2174 {
2175 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2176 if (is_locked)
2177 {
2178 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2179 AddProxyPhysics(slot_name_mounted);
2180 }
2181 else
2182 {
2183 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2184 AddProxyPhysics(slot_name);
2185 }
2186 }
2187 }
2188
2189 protected void UpdateNavmesh()
2190 {
2191 SetAffectPathgraph(true, false);
2192 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2193 }
2194
2195 override bool CanUseConstruction()
2196 {
2197 return true;
2198 }
2199
2200 override bool CanUseConstructionBuild()
2201 {
2202 return true;
2203 }
2204
2206 {
2207 if (attachment)
2208 {
2210 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2211
2212 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2213 }
2214
2215 return false;
2216 }
2217
2218 protected bool IsAttachmentSlotLocked(string slot_name)
2219 {
2220 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2221 }
2222
2223 //--- ATTACHMENT SLOTS
2225 {
2226 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2227 if (GetGame().ConfigIsExisting(config_path))
2228 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2229 }
2230
2232 {
2233 return true;
2234 }
2235
2236 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2237 {
2238 return true;
2239 }
2240
2241 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2242 {
2243 return true;
2244 }
2245
2246 // --- INIT
2247 void ConstructionInit()
2248 {
2249 if (!m_Construction)
2250 m_Construction = new Construction(this);
2251
2252 GetConstruction().Init();
2253 }
2254
2256 {
2257 return m_Construction;
2258 }
2259
2260 //--- INVENTORY/ATTACHMENTS CONDITIONS
2261 //attachments
2263 {
2264 return super.CanReceiveAttachment(attachment, slotId);
2265 }
2266
2268 {
2269 int attachment_count = GetInventory().AttachmentCount();
2270 if (attachment_count > 0)
2271 {
2272 if (HasBase() && attachment_count == 1)
2273 return false;
2274
2275 return true;
2276 }
2277
2278 return false;
2279 }
2280
2281 override bool ShowZonesHealth()
2282 {
2283 return true;
2284 }
2285
2286 //this into/outo parent.Cargo
2287 override bool CanPutInCargo(EntityAI parent)
2288 {
2289 return false;
2290 }
2291
2292 override bool CanRemoveFromCargo(EntityAI parent)
2293 {
2294 return false;
2295 }
2296
2297 //hands
2298 override bool CanPutIntoHands(EntityAI parent)
2299 {
2300 return false;
2301 }
2302
2303 //--- ACTION CONDITIONS
2304 //direction
2305 override bool IsFacingPlayer(PlayerBase player, string selection)
2306 {
2307 return true;
2308 }
2309
2310 override bool IsPlayerInside(PlayerBase player, string selection)
2311 {
2312 return true;
2313 }
2314
2317 {
2318 return false;
2319 }
2320
2321 //camera direction check
2322 bool IsFacingCamera(string selection)
2323 {
2324 return true;
2325 }
2326
2327 //roof check
2329 {
2330 return false;
2331 }
2332
2333 //selection->player distance check
2334 bool HasProperDistance(string selection, PlayerBase player)
2335 {
2336 return true;
2337 }
2338
2339 //folding
2341 {
2342 if (HasBase() || GetInventory().AttachmentCount() > 0)
2343 return false;
2344
2345 return true;
2346 }
2347
2349 {
2352
2353 return item;
2354 }
2355
2356 //Damage triggers (barbed wire)
2357 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2358 {
2359 if (GetGame() && GetGame().IsServer())
2360 {
2361 //destroy area damage if some already exists
2363
2364 //create new area damage
2366 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2367
2368 vector min_max[2];
2369 if (MemoryPointExists(slot_name + "_min"))
2370 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2371 if (MemoryPointExists(slot_name + "_max"))
2372 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2373
2374 //get proper trigger extents (min<max)
2375 vector extents[2];
2376 GetConstruction().GetTriggerExtents(min_max, extents);
2377
2378 //get box center
2379 vector center;
2380 center = GetConstruction().GetBoxCenter(min_max);
2381 center = ModelToWorld(center);
2382
2383 //rotate center if needed
2386
2387 areaDamage.SetExtents(extents[0], extents[1]);
2388 areaDamage.SetAreaPosition(center);
2389 areaDamage.SetAreaOrientation(orientation);
2390 areaDamage.SetLoopInterval(1.0);
2391 areaDamage.SetDeferDuration(0.2);
2392 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2393 areaDamage.SetAmmoName("BarbedWireHit");
2394 areaDamage.Spawn();
2395
2397 }
2398 }
2399
2401 {
2402 if (angle_deg != 0)
2403 {
2404 //orientation
2406
2407 //center
2409 if (MemoryPointExists("rotate_axis"))
2410 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2413 center[0] = r_center_x;
2414 center[2] = r_center_z;
2415 }
2416 }
2417
2418 void DestroyAreaDamage(string slot_name)
2419 {
2420 if (GetGame() && GetGame().IsServer())
2421 {
2424 {
2425 if (areaDamage)
2426 areaDamage.Destroy();
2427
2429 }
2430 }
2431 }
2432
2433 override bool IsIgnoredByConstruction()
2434 {
2435 return true;
2436 }
2437
2438 //================================================================
2439 // SOUNDS
2440 //================================================================
2441 protected void SoundBuildStart(string part_name)
2442 {
2443 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2444 }
2445
2446 protected void SoundDismantleStart(string part_name)
2447 {
2448 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2449 }
2450
2451 protected void SoundDestroyStart(string part_name)
2452 {
2453 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2454 }
2455
2456 protected string GetBuildSoundByMaterial(string part_name)
2457 {
2459
2460 switch (material_type)
2461 {
2462 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2463 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2464 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2465 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2466 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2467 }
2468
2469 return "";
2470 }
2471
2472 protected string GetDismantleSoundByMaterial(string part_name)
2473 {
2475
2476 switch (material_type)
2477 {
2478 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2479 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2480 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2481 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2482 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2483 }
2484
2485 return "";
2486 }
2487
2488 //misc
2490 {
2491 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2492 {
2493 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2495 SetHealth(slot_name, "Health", item.GetHealth());
2496 }
2497 }
2498
2499 override int GetDamageSystemVersionChange()
2500 {
2501 return 111;
2502 }
2503
2504 override void SetActions()
2505 {
2506 super.SetActions();
2507
2509 //AddAction(ActionTakeHybridAttachment);
2510 //AddAction(ActionTakeHybridAttachmentToHands);
2513 }
2514
2515 //================================================================
2516 // DEBUG
2517 //================================================================
2518 protected void DebugCustomState()
2519 {
2520 }
2521
2524 {
2525 return null;
2526 }
2527
2528 override void OnDebugSpawn()
2529 {
2530 FullyBuild();
2531 }
2532
2533 void FullyBuild()
2534 {
2536 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2537
2538 Man p;
2539
2540#ifdef SERVER
2542 GetGame().GetWorld().GetPlayerList(players);
2543 if (players.Count())
2544 p = players[0];
2545#else
2546 p = GetGame().GetPlayer();
2547#endif
2548
2549 foreach (ConstructionPart part : parts)
2550 {
2551 bool excluded = false;
2552 string partName = part.GetPartName();
2553 if (excludes)
2554 {
2555 foreach (string exclude : excludes)
2556 {
2557 if (partName.Contains(exclude))
2558 {
2559 excluded = true;
2560 break;
2561 }
2562 }
2563 }
2564
2565 if (!excluded)
2567 }
2568
2569 GetConstruction().UpdateVisuals();
2570 }
2571}
2572
2573void bsbDebugPrint(string s)
2574{
2575#ifdef BSB_DEBUG
2576 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2577#else
2578 //Print("" + s); // comment/uncomment to hide/see debug logs
2579#endif
2580}
2581void bsbDebugSpam(string s)
2582{
2583#ifdef BSB_DEBUG_SPAM
2584 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2585#else
2586 //Print("" + s); // comment/uncomment to hide/see debug logs
2587#endif
2588}

Referenced by ItemBase::OnSynchronizedClient().

◆ SetActions()

override void bsbDebugPrint::SetActions ( )
protected

Definition at line 2280 of file BaseBuildingBase.c.

2282{
2283 const string ANIMATION_DEPLOYED = "Deployed";
2284
2285 float m_ConstructionKitHealth; //stored health value for used construction kit
2286
2288
2289 bool m_HasBase;
2290 //variables for synchronization of base building parts (2x31 is the current limit)
2291 int m_SyncParts01; //synchronization for already built parts (31 parts)
2292 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2293 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2294 int m_InteractedPartId; //construction part id that an action was performed on
2295 int m_PerformedActionId; //action id that was performed on a construction part
2296
2297 //Sounds
2298 //build
2299 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2300 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2301 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2302 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2303 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2304 //dismantle
2305 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2306 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2307 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2308 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2309 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2310
2311 protected EffectSound m_Sound;
2312
2316
2317 // Constructor
2318 void BaseBuildingBase()
2319 {
2321
2322 //synchronized variables
2323 RegisterNetSyncVariableInt("m_SyncParts01");
2324 RegisterNetSyncVariableInt("m_SyncParts02");
2325 RegisterNetSyncVariableInt("m_SyncParts03");
2326 RegisterNetSyncVariableInt("m_InteractedPartId");
2327 RegisterNetSyncVariableInt("m_PerformedActionId");
2328 RegisterNetSyncVariableBool("m_HasBase");
2329
2330 //Construction init
2332
2333 if (ConfigIsExisting("hybridAttachments"))
2334 {
2336 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2337 }
2338 if (ConfigIsExisting("mountables"))
2339 {
2341 ConfigGetTextArray("mountables", m_Mountables);
2342 }
2343
2344 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2345 }
2346
2347 override void EEDelete(EntityAI parent)
2348 {
2349 super.EEDelete(parent);
2350
2351 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2353
2354 }
2355
2356 override string GetInvulnerabilityTypeString()
2357 {
2358 return "disableBaseDamage";
2359 }
2360
2361 override bool CanObstruct()
2362 {
2363 return true;
2364 }
2365
2366 override int GetHideIconMask()
2367 {
2368 return EInventoryIconVisibility.HIDE_VICINITY;
2369 }
2370
2371 // --- SYNCHRONIZATION
2373 {
2374 if (GetGame().IsServer())
2375 SetSynchDirty();
2376 }
2377
2378 override void OnVariablesSynchronized()
2379 {
2380 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2381 super.OnVariablesSynchronized();
2382
2383 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2384 }
2385
2386 protected void OnSynchronizedClient()
2387 {
2388 //update parts
2390
2391 //update action on part
2393
2394 //update visuals (client)
2395 UpdateVisuals();
2396 }
2397
2398 //parts synchronization
2400 {
2401 //part_id must starts from index = 1
2402 int offset;
2403 int mask;
2404
2405 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2406 {
2407 offset = part_id - 1;
2408 mask = 1 << offset;
2409
2411 }
2412 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2413 {
2414 offset = (part_id % 32);
2415 mask = 1 << offset;
2416
2418 }
2419 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2420 {
2421 offset = (part_id % 63);
2422 mask = 1 << offset;
2423
2425 }
2426 }
2427
2429 {
2430 //part_id must starts from index = 1
2431 int offset;
2432 int mask;
2433
2434 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2435 {
2436 offset = part_id - 1;
2437 mask = 1 << offset;
2438
2440 }
2441 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2442 {
2443 offset = (part_id % 32);
2444 mask = 1 << offset;
2445
2447 }
2448 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2449 {
2450 offset = (part_id % 63);
2451 mask = 1 << offset;
2452
2454 }
2455 }
2456
2458 {
2459 //part_id must starts from index = 1
2460 int offset;
2461 int mask;
2462
2463 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2464 {
2465 offset = part_id - 1;
2466 mask = 1 << offset;
2467
2468 if ((m_SyncParts01 & mask) > 0)
2469 return true;
2470 }
2471 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2472 {
2473 offset = (part_id % 32);
2474 mask = 1 << offset;
2475
2476 if ((m_SyncParts02 & mask) > 0)
2477 return true;
2478 }
2479 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2480 {
2481 offset = (part_id % 63);
2482 mask = 1 << offset;
2483
2484 if ((m_SyncParts03 & mask) > 0)
2485 return true;
2486 }
2487
2488 return false;
2489 }
2490
2491 protected void RegisterActionForSync(int part_id, int action_id)
2492 {
2495 }
2496
2497 protected void ResetActionSyncData()
2498 {
2499 //reset data
2500 m_InteractedPartId = -1;
2502 }
2503
2504 protected void SetActionFromSyncData()
2505 {
2506 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2507 {
2510
2511 switch (build_action_id)
2512 {
2516 }
2517 }
2518 }
2519 //------
2520
2522 {
2523 string key = part.m_PartName;
2524 bool is_base = part.IsBase();
2526 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2528 {
2529 if (!part.IsBuilt())
2530 {
2531 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2532 GetConstruction().AddToConstructedParts(key);
2533 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2534
2535 if (is_base)
2536 {
2538 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2539 }
2540 }
2541 }
2542 else
2543 {
2544 if (part.IsBuilt())
2545 {
2546 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2547 GetConstruction().RemoveFromConstructedParts(key);
2548 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2549
2550 if (is_base)
2551 {
2553 AddProxyPhysics(ANIMATION_DEPLOYED);
2554 }
2555 }
2556 }
2557
2558 //check slot lock for material attachments
2559 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2560 }
2561
2562 //set construction parts based on synchronized data
2564 {
2567
2568 for (int i = 0; i < construction_parts.Count(); ++i)
2569 {
2570 string key = construction_parts.GetKey(i);
2573 }
2574
2575 //regenerate navmesh
2576 UpdateNavmesh();
2577 }
2578
2580 {
2583
2584 for (int i = 0; i < construction_parts.Count(); ++i)
2585 {
2586 string key = construction_parts.GetKey(i);
2588
2589 if (value.GetId() == id)
2590 return value;
2591 }
2592
2593 return NULL;
2594 }
2595 //
2596
2597 //Base
2598 bool HasBase()
2599 {
2600 return m_HasBase;
2601 }
2602
2603 void SetBaseState(bool has_base)
2604 {
2606 }
2607
2608 override bool IsDeployable()
2609 {
2610 return true;
2611 }
2612
2613 bool IsOpened()
2614 {
2615 return false;
2616 }
2617
2618 //--- CONSTRUCTION KIT
2620 {
2624
2625 return construction_kit;
2626 }
2627
2629 {
2630 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2633 }
2634
2635 protected vector GetKitSpawnPosition()
2636 {
2637 return GetPosition();
2638 }
2639
2640 protected string GetConstructionKitType()
2641 {
2642 return "";
2643 }
2644
2646 {
2648 GetGame().ObjectDelete(construction_kit);
2649 }
2650
2651 //--- CONSTRUCTION
2652 void DestroyConstruction()
2653 {
2654 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2655 GetGame().ObjectDelete(this);
2656 }
2657
2658 // --- EVENTS
2659 override void OnStoreSave(ParamsWriteContext ctx)
2660 {
2661 super.OnStoreSave(ctx);
2662
2663 //sync parts 01
2664 ctx.Write(m_SyncParts01);
2665 ctx.Write(m_SyncParts02);
2666 ctx.Write(m_SyncParts03);
2667
2668 ctx.Write(m_HasBase);
2669 }
2670
2671 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2672 {
2673 if (!super.OnStoreLoad(ctx, version))
2674 return false;
2675
2676 //--- Base building data ---
2677 //Restore synced parts data
2678 if (!ctx.Read(m_SyncParts01))
2679 {
2680 m_SyncParts01 = 0; //set default
2681 return false;
2682 }
2683 if (!ctx.Read(m_SyncParts02))
2684 {
2685 m_SyncParts02 = 0; //set default
2686 return false;
2687 }
2688 if (!ctx.Read(m_SyncParts03))
2689 {
2690 m_SyncParts03 = 0; //set default
2691 return false;
2692 }
2693
2694 //has base
2695 if (!ctx.Read(m_HasBase))
2696 {
2697 m_HasBase = false;
2698 return false;
2699 }
2700 //---
2701
2702 return true;
2703 }
2704
2705 override void AfterStoreLoad()
2706 {
2707 super.AfterStoreLoad();
2708
2711 }
2712
2714 {
2715 //update server data
2717
2718 //set base state
2719 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2720 SetBaseState(construction_part.IsBuilt()) ;
2721
2722 //synchronize after load
2724 }
2725
2726 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2727 {
2729 return;
2730
2731 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2732
2733 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2734 return;
2735
2737 string part_name = zone;
2738 part_name.ToLower();
2739
2741 {
2743
2744 if (construction_part && construction.IsPartConstructed(part_name))
2745 {
2746 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2747 construction.DestroyConnectedParts(part_name);
2748 }
2749
2750 //barbed wire handling (hack-ish)
2751 if (part_name.Contains("barbed"))
2752 {
2753 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2754 if (barbed_wire)
2755 barbed_wire.SetMountedState(false);
2756 }
2757 }
2758 }
2759
2760 override void EEOnAfterLoad()
2761 {
2763 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2764
2765 super.EEOnAfterLoad();
2766 }
2767
2768 override void EEInit()
2769 {
2770 super.EEInit();
2771
2772 // init visuals and physics
2773 InitBaseState();
2774
2775 //debug
2776#ifdef DEVELOPER
2778#endif
2779 }
2780
2781 override void EEItemAttached(EntityAI item, string slot_name)
2782 {
2783 super.EEItemAttached(item, slot_name);
2784
2786 UpdateVisuals();
2788 }
2789
2790 override void EEItemDetached(EntityAI item, string slot_name)
2791 {
2792 super.EEItemDetached(item, slot_name);
2793
2794 UpdateVisuals();
2796 }
2797
2798 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2799 {
2801 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2802
2805 }
2806
2807 //ignore out of reach condition
2808 override bool IgnoreOutOfReachCondition()
2809 {
2810 return true;
2811 }
2812
2813 //CONSTRUCTION EVENTS
2814 //Build
2815 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2816 {
2818
2819 //check base state
2820 if (construtionPart.IsBase())
2821 {
2822 SetBaseState(true);
2823
2824 //spawn kit
2826 }
2827
2828 //register constructed parts for synchronization
2830
2831 //register action that was performed on part
2833
2834 //synchronize
2836
2837 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2838
2839 UpdateNavmesh();
2840
2841 //update visuals
2842 UpdateVisuals();
2843
2844 //reset action sync data
2845 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2846 }
2847
2848 void OnPartBuiltClient(string part_name, int action_id)
2849 {
2850 //play sound
2852 }
2853
2854 //Dismantle
2856 {
2857 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2859
2860 //register constructed parts for synchronization
2862
2863 //register action that was performed on part
2865
2866 //synchronize
2868
2869 // server part of sync, client will be synced from SetPartsFromSyncData
2871
2872 UpdateNavmesh();
2873
2874 //update visuals
2875 UpdateVisuals();
2876
2877 //reset action sync data
2878 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2879
2880 //check base state
2881 if (construtionPart.IsBase())
2882 {
2883 //Destroy construction
2884 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2885 }
2886 }
2887
2889 {
2890 //play sound
2892 }
2893
2894 //Destroy
2896 {
2897 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2899
2900 //register constructed parts for synchronization
2902
2903 //register action that was performed on part
2905
2906 //synchronize
2908
2909 // server part of sync, client will be synced from SetPartsFromSyncData
2911
2912 UpdateNavmesh();
2913
2914 //update visuals
2915 UpdateVisuals();
2916
2917 //reset action sync data
2918 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2919
2920 //check base state
2921 if (construtionPart.IsBase())
2922 {
2923 //Destroy construction
2924 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2925 }
2926 }
2927
2928 void OnPartDestroyedClient(string part_name, int action_id)
2929 {
2930 //play sound
2932 }
2933
2934 // --- UPDATE
2935 void InitBaseState()
2936 {
2937 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2938
2939 InitVisuals();
2940 UpdateNavmesh(); //regenerate navmesh
2941 GetConstruction().InitBaseState();
2942 }
2943
2944 void InitVisuals()
2945 {
2946 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2947 //check base
2948 if (!HasBase())
2949 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2950 else
2951 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2952
2953 GetConstruction().UpdateVisuals();
2954 }
2955
2956 void UpdateVisuals()
2957 {
2959
2961 foreach (string slotName : attachmentSlots)
2963
2964 //check base
2965 if (!HasBase())
2966 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2967 else
2968 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2969
2970 GetConstruction().UpdateVisuals();
2971 }
2972
2974 {
2975 string slotNameMounted = slot_name + "_Mounted";
2976 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2977
2978 if (attachment)
2979 {
2980 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2981 if (barbedWire && barbedWire.IsMounted())
2983 else
2985
2986 if (is_locked)
2987 {
2988 SetAnimationPhase(slotNameMounted, 0);
2989 SetAnimationPhase(slot_name, 1);
2990 }
2991 else
2992 {
2993 SetAnimationPhase(slotNameMounted, 1);
2994 SetAnimationPhase(slot_name, 0);
2995 }
2996 }
2997 else
2998 {
2999 SetAnimationPhase(slotNameMounted, 1);
3000 SetAnimationPhase(slot_name, 1);
3001
3003 }
3004 }
3005
3006 // avoid calling this function on frequent occasions, it's a massive performance hit
3007 void UpdatePhysics()
3008 {
3010 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
3011
3014
3016 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
3017
3018 foreach (string slotName : attachmentSlots)
3020
3021 //check base
3022 if (!HasBase())
3023 {
3025 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
3026
3027 AddProxyPhysics(ANIMATION_DEPLOYED);
3028 }
3029 else
3030 {
3032 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
3033
3034 RemoveProxyPhysics(ANIMATION_DEPLOYED);
3035 }
3036
3037 GetConstruction().UpdatePhysics();
3038 UpdateNavmesh();
3039 }
3040
3042 {
3043 //checks for invalid appends; hotfix
3044 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
3045 return;
3046 //----------------------------------
3047 string slot_name_mounted = slot_name + "_Mounted";
3048 EntityAI attachment = FindAttachmentBySlotName(slot_name);
3049
3050 //remove proxy physics
3051 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
3052 RemoveProxyPhysics(slot_name_mounted);
3053 RemoveProxyPhysics(slot_name);
3054
3055 if (attachment)
3056 {
3057 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
3058 if (is_locked)
3059 {
3060 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
3061 AddProxyPhysics(slot_name_mounted);
3062 }
3063 else
3064 {
3065 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
3066 AddProxyPhysics(slot_name);
3067 }
3068 }
3069 }
3070
3071 protected void UpdateNavmesh()
3072 {
3073 SetAffectPathgraph(true, false);
3074 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
3075 }
3076
3077 override bool CanUseConstruction()
3078 {
3079 return true;
3080 }
3081
3082 override bool CanUseConstructionBuild()
3083 {
3084 return true;
3085 }
3086
3088 {
3089 if (attachment)
3090 {
3092 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
3093
3094 return GetInventory().GetSlotLock(inventory_location.GetSlot());
3095 }
3096
3097 return false;
3098 }
3099
3100 protected bool IsAttachmentSlotLocked(string slot_name)
3101 {
3102 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
3103 }
3104
3105 //--- ATTACHMENT SLOTS
3107 {
3108 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
3109 if (GetGame().ConfigIsExisting(config_path))
3110 GetGame().ConfigGetTextArray(config_path, attachment_slots);
3111 }
3112
3114 {
3115 return true;
3116 }
3117
3118 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
3119 {
3120 return true;
3121 }
3122
3123 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
3124 {
3125 return true;
3126 }
3127
3128 // --- INIT
3129 void ConstructionInit()
3130 {
3131 if (!m_Construction)
3132 m_Construction = new Construction(this);
3133
3134 GetConstruction().Init();
3135 }
3136
3138 {
3139 return m_Construction;
3140 }
3141
3142 //--- INVENTORY/ATTACHMENTS CONDITIONS
3143 //attachments
3145 {
3146 return super.CanReceiveAttachment(attachment, slotId);
3147 }
3148
3150 {
3151 int attachment_count = GetInventory().AttachmentCount();
3152 if (attachment_count > 0)
3153 {
3154 if (HasBase() && attachment_count == 1)
3155 return false;
3156
3157 return true;
3158 }
3159
3160 return false;
3161 }
3162
3163 override bool ShowZonesHealth()
3164 {
3165 return true;
3166 }
3167
3168 //this into/outo parent.Cargo
3169 override bool CanPutInCargo(EntityAI parent)
3170 {
3171 return false;
3172 }
3173
3174 override bool CanRemoveFromCargo(EntityAI parent)
3175 {
3176 return false;
3177 }
3178
3179 //hands
3180 override bool CanPutIntoHands(EntityAI parent)
3181 {
3182 return false;
3183 }
3184
3185 //--- ACTION CONDITIONS
3186 //direction
3187 override bool IsFacingPlayer(PlayerBase player, string selection)
3188 {
3189 return true;
3190 }
3191
3192 override bool IsPlayerInside(PlayerBase player, string selection)
3193 {
3194 return true;
3195 }
3196
3199 {
3200 return false;
3201 }
3202
3203 //camera direction check
3204 bool IsFacingCamera(string selection)
3205 {
3206 return true;
3207 }
3208
3209 //roof check
3211 {
3212 return false;
3213 }
3214
3215 //selection->player distance check
3216 bool HasProperDistance(string selection, PlayerBase player)
3217 {
3218 return true;
3219 }
3220
3221 //folding
3223 {
3224 if (HasBase() || GetInventory().AttachmentCount() > 0)
3225 return false;
3226
3227 return true;
3228 }
3229
3231 {
3234
3235 return item;
3236 }
3237
3238 //Damage triggers (barbed wire)
3239 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3240 {
3241 if (GetGame() && GetGame().IsServer())
3242 {
3243 //destroy area damage if some already exists
3245
3246 //create new area damage
3248 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3249
3250 vector min_max[2];
3251 if (MemoryPointExists(slot_name + "_min"))
3252 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3253 if (MemoryPointExists(slot_name + "_max"))
3254 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3255
3256 //get proper trigger extents (min<max)
3257 vector extents[2];
3258 GetConstruction().GetTriggerExtents(min_max, extents);
3259
3260 //get box center
3261 vector center;
3262 center = GetConstruction().GetBoxCenter(min_max);
3263 center = ModelToWorld(center);
3264
3265 //rotate center if needed
3268
3269 areaDamage.SetExtents(extents[0], extents[1]);
3270 areaDamage.SetAreaPosition(center);
3271 areaDamage.SetAreaOrientation(orientation);
3272 areaDamage.SetLoopInterval(1.0);
3273 areaDamage.SetDeferDuration(0.2);
3274 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3275 areaDamage.SetAmmoName("BarbedWireHit");
3276 areaDamage.Spawn();
3277
3279 }
3280 }
3281
3283 {
3284 if (angle_deg != 0)
3285 {
3286 //orientation
3288
3289 //center
3291 if (MemoryPointExists("rotate_axis"))
3292 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3295 center[0] = r_center_x;
3296 center[2] = r_center_z;
3297 }
3298 }
3299
3300 void DestroyAreaDamage(string slot_name)
3301 {
3302 if (GetGame() && GetGame().IsServer())
3303 {
3306 {
3307 if (areaDamage)
3308 areaDamage.Destroy();
3309
3311 }
3312 }
3313 }
3314
3315 override bool IsIgnoredByConstruction()
3316 {
3317 return true;
3318 }
3319
3320 //================================================================
3321 // SOUNDS
3322 //================================================================
3323 protected void SoundBuildStart(string part_name)
3324 {
3325 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3326 }
3327
3328 protected void SoundDismantleStart(string part_name)
3329 {
3330 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3331 }
3332
3333 protected void SoundDestroyStart(string part_name)
3334 {
3335 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3336 }
3337
3338 protected string GetBuildSoundByMaterial(string part_name)
3339 {
3341
3342 switch (material_type)
3343 {
3344 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3345 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3346 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3347 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3348 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3349 }
3350
3351 return "";
3352 }
3353
3354 protected string GetDismantleSoundByMaterial(string part_name)
3355 {
3357
3358 switch (material_type)
3359 {
3360 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3361 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3362 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3363 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3364 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3365 }
3366
3367 return "";
3368 }
3369
3370 //misc
3372 {
3373 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3374 {
3375 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3377 SetHealth(slot_name, "Health", item.GetHealth());
3378 }
3379 }
3380
3381 override int GetDamageSystemVersionChange()
3382 {
3383 return 111;
3384 }
3385
3386 override void SetActions()
3387 {
3388 super.SetActions();
3389
3391 //AddAction(ActionTakeHybridAttachment);
3392 //AddAction(ActionTakeHybridAttachmentToHands);
3395 }
3396
3397 //================================================================
3398 // DEBUG
3399 //================================================================
3400 protected void DebugCustomState()
3401 {
3402 }
3403
3406 {
3407 return null;
3408 }
3409
3410 override void OnDebugSpawn()
3411 {
3412 FullyBuild();
3413 }
3414
3415 void FullyBuild()
3416 {
3418 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3419
3420 Man p;
3421
3422#ifdef SERVER
3424 GetGame().GetWorld().GetPlayerList(players);
3425 if (players.Count())
3426 p = players[0];
3427#else
3428 p = GetGame().GetPlayer();
3429#endif
3430
3431 foreach (ConstructionPart part : parts)
3432 {
3433 bool excluded = false;
3434 string partName = part.GetPartName();
3435 if (excludes)
3436 {
3437 foreach (string exclude : excludes)
3438 {
3439 if (partName.Contains(exclude))
3440 {
3441 excluded = true;
3442 break;
3443 }
3444 }
3445 }
3446
3447 if (!excluded)
3449 }
3450
3451 GetConstruction().UpdateVisuals();
3452 }
3453}
3454
3455void bsbDebugPrint(string s)
3456{
3457#ifdef BSB_DEBUG
3458 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3459#else
3460 //Print("" + s); // comment/uncomment to hide/see debug logs
3461#endif
3462}
3463void bsbDebugSpam(string s)
3464{
3465#ifdef BSB_DEBUG_SPAM
3466 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3467#else
3468 //Print("" + s); // comment/uncomment to hide/see debug logs
3469#endif
3470}

◆ SetBaseState()

void bsbDebugPrint::SetBaseState ( bool has_base)
protected

Definition at line 1497 of file BaseBuildingBase.c.

1499{
1500 const string ANIMATION_DEPLOYED = "Deployed";
1501
1502 float m_ConstructionKitHealth; //stored health value for used construction kit
1503
1505
1506 bool m_HasBase;
1507 //variables for synchronization of base building parts (2x31 is the current limit)
1508 int m_SyncParts01; //synchronization for already built parts (31 parts)
1509 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1510 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1511 int m_InteractedPartId; //construction part id that an action was performed on
1512 int m_PerformedActionId; //action id that was performed on a construction part
1513
1514 //Sounds
1515 //build
1516 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1517 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1518 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1519 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1520 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1521 //dismantle
1522 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1523 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1524 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1525 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1526 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1527
1528 protected EffectSound m_Sound;
1529
1533
1534 // Constructor
1535 void BaseBuildingBase()
1536 {
1538
1539 //synchronized variables
1540 RegisterNetSyncVariableInt("m_SyncParts01");
1541 RegisterNetSyncVariableInt("m_SyncParts02");
1542 RegisterNetSyncVariableInt("m_SyncParts03");
1543 RegisterNetSyncVariableInt("m_InteractedPartId");
1544 RegisterNetSyncVariableInt("m_PerformedActionId");
1545 RegisterNetSyncVariableBool("m_HasBase");
1546
1547 //Construction init
1549
1550 if (ConfigIsExisting("hybridAttachments"))
1551 {
1553 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1554 }
1555 if (ConfigIsExisting("mountables"))
1556 {
1558 ConfigGetTextArray("mountables", m_Mountables);
1559 }
1560
1561 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1562 }
1563
1564 override void EEDelete(EntityAI parent)
1565 {
1566 super.EEDelete(parent);
1567
1568 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1570
1571 }
1572
1573 override string GetInvulnerabilityTypeString()
1574 {
1575 return "disableBaseDamage";
1576 }
1577
1578 override bool CanObstruct()
1579 {
1580 return true;
1581 }
1582
1583 override int GetHideIconMask()
1584 {
1585 return EInventoryIconVisibility.HIDE_VICINITY;
1586 }
1587
1588 // --- SYNCHRONIZATION
1590 {
1591 if (GetGame().IsServer())
1592 SetSynchDirty();
1593 }
1594
1595 override void OnVariablesSynchronized()
1596 {
1597 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1598 super.OnVariablesSynchronized();
1599
1600 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1601 }
1602
1603 protected void OnSynchronizedClient()
1604 {
1605 //update parts
1607
1608 //update action on part
1610
1611 //update visuals (client)
1612 UpdateVisuals();
1613 }
1614
1615 //parts synchronization
1617 {
1618 //part_id must starts from index = 1
1619 int offset;
1620 int mask;
1621
1622 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1623 {
1624 offset = part_id - 1;
1625 mask = 1 << offset;
1626
1628 }
1629 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1630 {
1631 offset = (part_id % 32);
1632 mask = 1 << offset;
1633
1635 }
1636 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1637 {
1638 offset = (part_id % 63);
1639 mask = 1 << offset;
1640
1642 }
1643 }
1644
1646 {
1647 //part_id must starts from index = 1
1648 int offset;
1649 int mask;
1650
1651 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1652 {
1653 offset = part_id - 1;
1654 mask = 1 << offset;
1655
1657 }
1658 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1659 {
1660 offset = (part_id % 32);
1661 mask = 1 << offset;
1662
1664 }
1665 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1666 {
1667 offset = (part_id % 63);
1668 mask = 1 << offset;
1669
1671 }
1672 }
1673
1675 {
1676 //part_id must starts from index = 1
1677 int offset;
1678 int mask;
1679
1680 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1681 {
1682 offset = part_id - 1;
1683 mask = 1 << offset;
1684
1685 if ((m_SyncParts01 & mask) > 0)
1686 return true;
1687 }
1688 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1689 {
1690 offset = (part_id % 32);
1691 mask = 1 << offset;
1692
1693 if ((m_SyncParts02 & mask) > 0)
1694 return true;
1695 }
1696 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1697 {
1698 offset = (part_id % 63);
1699 mask = 1 << offset;
1700
1701 if ((m_SyncParts03 & mask) > 0)
1702 return true;
1703 }
1704
1705 return false;
1706 }
1707
1708 protected void RegisterActionForSync(int part_id, int action_id)
1709 {
1712 }
1713
1714 protected void ResetActionSyncData()
1715 {
1716 //reset data
1717 m_InteractedPartId = -1;
1719 }
1720
1721 protected void SetActionFromSyncData()
1722 {
1723 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1724 {
1727
1728 switch (build_action_id)
1729 {
1733 }
1734 }
1735 }
1736 //------
1737
1739 {
1740 string key = part.m_PartName;
1741 bool is_base = part.IsBase();
1743 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1745 {
1746 if (!part.IsBuilt())
1747 {
1748 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1749 GetConstruction().AddToConstructedParts(key);
1750 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1751
1752 if (is_base)
1753 {
1755 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1756 }
1757 }
1758 }
1759 else
1760 {
1761 if (part.IsBuilt())
1762 {
1763 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1764 GetConstruction().RemoveFromConstructedParts(key);
1765 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1766
1767 if (is_base)
1768 {
1770 AddProxyPhysics(ANIMATION_DEPLOYED);
1771 }
1772 }
1773 }
1774
1775 //check slot lock for material attachments
1776 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1777 }
1778
1779 //set construction parts based on synchronized data
1781 {
1784
1785 for (int i = 0; i < construction_parts.Count(); ++i)
1786 {
1787 string key = construction_parts.GetKey(i);
1790 }
1791
1792 //regenerate navmesh
1793 UpdateNavmesh();
1794 }
1795
1797 {
1800
1801 for (int i = 0; i < construction_parts.Count(); ++i)
1802 {
1803 string key = construction_parts.GetKey(i);
1805
1806 if (value.GetId() == id)
1807 return value;
1808 }
1809
1810 return NULL;
1811 }
1812 //
1813
1814 //Base
1815 bool HasBase()
1816 {
1817 return m_HasBase;
1818 }
1819
1820 void SetBaseState(bool has_base)
1821 {
1823 }
1824
1825 override bool IsDeployable()
1826 {
1827 return true;
1828 }
1829
1830 bool IsOpened()
1831 {
1832 return false;
1833 }
1834
1835 //--- CONSTRUCTION KIT
1837 {
1841
1842 return construction_kit;
1843 }
1844
1846 {
1847 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1850 }
1851
1852 protected vector GetKitSpawnPosition()
1853 {
1854 return GetPosition();
1855 }
1856
1857 protected string GetConstructionKitType()
1858 {
1859 return "";
1860 }
1861
1863 {
1865 GetGame().ObjectDelete(construction_kit);
1866 }
1867
1868 //--- CONSTRUCTION
1869 void DestroyConstruction()
1870 {
1871 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1872 GetGame().ObjectDelete(this);
1873 }
1874
1875 // --- EVENTS
1876 override void OnStoreSave(ParamsWriteContext ctx)
1877 {
1878 super.OnStoreSave(ctx);
1879
1880 //sync parts 01
1881 ctx.Write(m_SyncParts01);
1882 ctx.Write(m_SyncParts02);
1883 ctx.Write(m_SyncParts03);
1884
1885 ctx.Write(m_HasBase);
1886 }
1887
1888 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1889 {
1890 if (!super.OnStoreLoad(ctx, version))
1891 return false;
1892
1893 //--- Base building data ---
1894 //Restore synced parts data
1895 if (!ctx.Read(m_SyncParts01))
1896 {
1897 m_SyncParts01 = 0; //set default
1898 return false;
1899 }
1900 if (!ctx.Read(m_SyncParts02))
1901 {
1902 m_SyncParts02 = 0; //set default
1903 return false;
1904 }
1905 if (!ctx.Read(m_SyncParts03))
1906 {
1907 m_SyncParts03 = 0; //set default
1908 return false;
1909 }
1910
1911 //has base
1912 if (!ctx.Read(m_HasBase))
1913 {
1914 m_HasBase = false;
1915 return false;
1916 }
1917 //---
1918
1919 return true;
1920 }
1921
1922 override void AfterStoreLoad()
1923 {
1924 super.AfterStoreLoad();
1925
1928 }
1929
1931 {
1932 //update server data
1934
1935 //set base state
1936 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1937 SetBaseState(construction_part.IsBuilt()) ;
1938
1939 //synchronize after load
1941 }
1942
1943 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1944 {
1946 return;
1947
1948 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1949
1950 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1951 return;
1952
1954 string part_name = zone;
1955 part_name.ToLower();
1956
1958 {
1960
1961 if (construction_part && construction.IsPartConstructed(part_name))
1962 {
1963 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1964 construction.DestroyConnectedParts(part_name);
1965 }
1966
1967 //barbed wire handling (hack-ish)
1968 if (part_name.Contains("barbed"))
1969 {
1970 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1971 if (barbed_wire)
1972 barbed_wire.SetMountedState(false);
1973 }
1974 }
1975 }
1976
1977 override void EEOnAfterLoad()
1978 {
1980 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1981
1982 super.EEOnAfterLoad();
1983 }
1984
1985 override void EEInit()
1986 {
1987 super.EEInit();
1988
1989 // init visuals and physics
1990 InitBaseState();
1991
1992 //debug
1993#ifdef DEVELOPER
1995#endif
1996 }
1997
1998 override void EEItemAttached(EntityAI item, string slot_name)
1999 {
2000 super.EEItemAttached(item, slot_name);
2001
2003 UpdateVisuals();
2005 }
2006
2007 override void EEItemDetached(EntityAI item, string slot_name)
2008 {
2009 super.EEItemDetached(item, slot_name);
2010
2011 UpdateVisuals();
2013 }
2014
2015 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2016 {
2018 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2019
2022 }
2023
2024 //ignore out of reach condition
2025 override bool IgnoreOutOfReachCondition()
2026 {
2027 return true;
2028 }
2029
2030 //CONSTRUCTION EVENTS
2031 //Build
2032 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2033 {
2035
2036 //check base state
2037 if (construtionPart.IsBase())
2038 {
2039 SetBaseState(true);
2040
2041 //spawn kit
2043 }
2044
2045 //register constructed parts for synchronization
2047
2048 //register action that was performed on part
2050
2051 //synchronize
2053
2054 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2055
2056 UpdateNavmesh();
2057
2058 //update visuals
2059 UpdateVisuals();
2060
2061 //reset action sync data
2062 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2063 }
2064
2065 void OnPartBuiltClient(string part_name, int action_id)
2066 {
2067 //play sound
2069 }
2070
2071 //Dismantle
2073 {
2074 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2076
2077 //register constructed parts for synchronization
2079
2080 //register action that was performed on part
2082
2083 //synchronize
2085
2086 // server part of sync, client will be synced from SetPartsFromSyncData
2088
2089 UpdateNavmesh();
2090
2091 //update visuals
2092 UpdateVisuals();
2093
2094 //reset action sync data
2095 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2096
2097 //check base state
2098 if (construtionPart.IsBase())
2099 {
2100 //Destroy construction
2101 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2102 }
2103 }
2104
2106 {
2107 //play sound
2109 }
2110
2111 //Destroy
2113 {
2114 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2116
2117 //register constructed parts for synchronization
2119
2120 //register action that was performed on part
2122
2123 //synchronize
2125
2126 // server part of sync, client will be synced from SetPartsFromSyncData
2128
2129 UpdateNavmesh();
2130
2131 //update visuals
2132 UpdateVisuals();
2133
2134 //reset action sync data
2135 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2136
2137 //check base state
2138 if (construtionPart.IsBase())
2139 {
2140 //Destroy construction
2141 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2142 }
2143 }
2144
2145 void OnPartDestroyedClient(string part_name, int action_id)
2146 {
2147 //play sound
2149 }
2150
2151 // --- UPDATE
2152 void InitBaseState()
2153 {
2154 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2155
2156 InitVisuals();
2157 UpdateNavmesh(); //regenerate navmesh
2158 GetConstruction().InitBaseState();
2159 }
2160
2161 void InitVisuals()
2162 {
2163 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2164 //check base
2165 if (!HasBase())
2166 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2167 else
2168 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2169
2170 GetConstruction().UpdateVisuals();
2171 }
2172
2173 void UpdateVisuals()
2174 {
2176
2178 foreach (string slotName : attachmentSlots)
2180
2181 //check base
2182 if (!HasBase())
2183 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2184 else
2185 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2186
2187 GetConstruction().UpdateVisuals();
2188 }
2189
2191 {
2192 string slotNameMounted = slot_name + "_Mounted";
2193 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2194
2195 if (attachment)
2196 {
2197 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2198 if (barbedWire && barbedWire.IsMounted())
2200 else
2202
2203 if (is_locked)
2204 {
2205 SetAnimationPhase(slotNameMounted, 0);
2206 SetAnimationPhase(slot_name, 1);
2207 }
2208 else
2209 {
2210 SetAnimationPhase(slotNameMounted, 1);
2211 SetAnimationPhase(slot_name, 0);
2212 }
2213 }
2214 else
2215 {
2216 SetAnimationPhase(slotNameMounted, 1);
2217 SetAnimationPhase(slot_name, 1);
2218
2220 }
2221 }
2222
2223 // avoid calling this function on frequent occasions, it's a massive performance hit
2224 void UpdatePhysics()
2225 {
2227 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2228
2231
2233 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2234
2235 foreach (string slotName : attachmentSlots)
2237
2238 //check base
2239 if (!HasBase())
2240 {
2242 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2243
2244 AddProxyPhysics(ANIMATION_DEPLOYED);
2245 }
2246 else
2247 {
2249 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2250
2251 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2252 }
2253
2254 GetConstruction().UpdatePhysics();
2255 UpdateNavmesh();
2256 }
2257
2259 {
2260 //checks for invalid appends; hotfix
2261 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2262 return;
2263 //----------------------------------
2264 string slot_name_mounted = slot_name + "_Mounted";
2265 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2266
2267 //remove proxy physics
2268 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2269 RemoveProxyPhysics(slot_name_mounted);
2270 RemoveProxyPhysics(slot_name);
2271
2272 if (attachment)
2273 {
2274 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2275 if (is_locked)
2276 {
2277 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2278 AddProxyPhysics(slot_name_mounted);
2279 }
2280 else
2281 {
2282 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2283 AddProxyPhysics(slot_name);
2284 }
2285 }
2286 }
2287
2288 protected void UpdateNavmesh()
2289 {
2290 SetAffectPathgraph(true, false);
2291 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2292 }
2293
2294 override bool CanUseConstruction()
2295 {
2296 return true;
2297 }
2298
2299 override bool CanUseConstructionBuild()
2300 {
2301 return true;
2302 }
2303
2305 {
2306 if (attachment)
2307 {
2309 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2310
2311 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2312 }
2313
2314 return false;
2315 }
2316
2317 protected bool IsAttachmentSlotLocked(string slot_name)
2318 {
2319 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2320 }
2321
2322 //--- ATTACHMENT SLOTS
2324 {
2325 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2326 if (GetGame().ConfigIsExisting(config_path))
2327 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2328 }
2329
2331 {
2332 return true;
2333 }
2334
2335 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2336 {
2337 return true;
2338 }
2339
2340 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2341 {
2342 return true;
2343 }
2344
2345 // --- INIT
2346 void ConstructionInit()
2347 {
2348 if (!m_Construction)
2349 m_Construction = new Construction(this);
2350
2351 GetConstruction().Init();
2352 }
2353
2355 {
2356 return m_Construction;
2357 }
2358
2359 //--- INVENTORY/ATTACHMENTS CONDITIONS
2360 //attachments
2362 {
2363 return super.CanReceiveAttachment(attachment, slotId);
2364 }
2365
2367 {
2368 int attachment_count = GetInventory().AttachmentCount();
2369 if (attachment_count > 0)
2370 {
2371 if (HasBase() && attachment_count == 1)
2372 return false;
2373
2374 return true;
2375 }
2376
2377 return false;
2378 }
2379
2380 override bool ShowZonesHealth()
2381 {
2382 return true;
2383 }
2384
2385 //this into/outo parent.Cargo
2386 override bool CanPutInCargo(EntityAI parent)
2387 {
2388 return false;
2389 }
2390
2391 override bool CanRemoveFromCargo(EntityAI parent)
2392 {
2393 return false;
2394 }
2395
2396 //hands
2397 override bool CanPutIntoHands(EntityAI parent)
2398 {
2399 return false;
2400 }
2401
2402 //--- ACTION CONDITIONS
2403 //direction
2404 override bool IsFacingPlayer(PlayerBase player, string selection)
2405 {
2406 return true;
2407 }
2408
2409 override bool IsPlayerInside(PlayerBase player, string selection)
2410 {
2411 return true;
2412 }
2413
2416 {
2417 return false;
2418 }
2419
2420 //camera direction check
2421 bool IsFacingCamera(string selection)
2422 {
2423 return true;
2424 }
2425
2426 //roof check
2428 {
2429 return false;
2430 }
2431
2432 //selection->player distance check
2433 bool HasProperDistance(string selection, PlayerBase player)
2434 {
2435 return true;
2436 }
2437
2438 //folding
2440 {
2441 if (HasBase() || GetInventory().AttachmentCount() > 0)
2442 return false;
2443
2444 return true;
2445 }
2446
2448 {
2451
2452 return item;
2453 }
2454
2455 //Damage triggers (barbed wire)
2456 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2457 {
2458 if (GetGame() && GetGame().IsServer())
2459 {
2460 //destroy area damage if some already exists
2462
2463 //create new area damage
2465 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2466
2467 vector min_max[2];
2468 if (MemoryPointExists(slot_name + "_min"))
2469 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2470 if (MemoryPointExists(slot_name + "_max"))
2471 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2472
2473 //get proper trigger extents (min<max)
2474 vector extents[2];
2475 GetConstruction().GetTriggerExtents(min_max, extents);
2476
2477 //get box center
2478 vector center;
2479 center = GetConstruction().GetBoxCenter(min_max);
2480 center = ModelToWorld(center);
2481
2482 //rotate center if needed
2485
2486 areaDamage.SetExtents(extents[0], extents[1]);
2487 areaDamage.SetAreaPosition(center);
2488 areaDamage.SetAreaOrientation(orientation);
2489 areaDamage.SetLoopInterval(1.0);
2490 areaDamage.SetDeferDuration(0.2);
2491 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2492 areaDamage.SetAmmoName("BarbedWireHit");
2493 areaDamage.Spawn();
2494
2496 }
2497 }
2498
2500 {
2501 if (angle_deg != 0)
2502 {
2503 //orientation
2505
2506 //center
2508 if (MemoryPointExists("rotate_axis"))
2509 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2512 center[0] = r_center_x;
2513 center[2] = r_center_z;
2514 }
2515 }
2516
2517 void DestroyAreaDamage(string slot_name)
2518 {
2519 if (GetGame() && GetGame().IsServer())
2520 {
2523 {
2524 if (areaDamage)
2525 areaDamage.Destroy();
2526
2528 }
2529 }
2530 }
2531
2532 override bool IsIgnoredByConstruction()
2533 {
2534 return true;
2535 }
2536
2537 //================================================================
2538 // SOUNDS
2539 //================================================================
2540 protected void SoundBuildStart(string part_name)
2541 {
2542 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2543 }
2544
2545 protected void SoundDismantleStart(string part_name)
2546 {
2547 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2548 }
2549
2550 protected void SoundDestroyStart(string part_name)
2551 {
2552 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2553 }
2554
2555 protected string GetBuildSoundByMaterial(string part_name)
2556 {
2558
2559 switch (material_type)
2560 {
2561 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2562 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2563 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2564 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2565 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2566 }
2567
2568 return "";
2569 }
2570
2571 protected string GetDismantleSoundByMaterial(string part_name)
2572 {
2574
2575 switch (material_type)
2576 {
2577 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2578 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2579 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2580 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2581 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2582 }
2583
2584 return "";
2585 }
2586
2587 //misc
2589 {
2590 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2591 {
2592 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2594 SetHealth(slot_name, "Health", item.GetHealth());
2595 }
2596 }
2597
2598 override int GetDamageSystemVersionChange()
2599 {
2600 return 111;
2601 }
2602
2603 override void SetActions()
2604 {
2605 super.SetActions();
2606
2608 //AddAction(ActionTakeHybridAttachment);
2609 //AddAction(ActionTakeHybridAttachmentToHands);
2612 }
2613
2614 //================================================================
2615 // DEBUG
2616 //================================================================
2617 protected void DebugCustomState()
2618 {
2619 }
2620
2623 {
2624 return null;
2625 }
2626
2627 override void OnDebugSpawn()
2628 {
2629 FullyBuild();
2630 }
2631
2632 void FullyBuild()
2633 {
2635 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2636
2637 Man p;
2638
2639#ifdef SERVER
2641 GetGame().GetWorld().GetPlayerList(players);
2642 if (players.Count())
2643 p = players[0];
2644#else
2645 p = GetGame().GetPlayer();
2646#endif
2647
2648 foreach (ConstructionPart part : parts)
2649 {
2650 bool excluded = false;
2651 string partName = part.GetPartName();
2652 if (excludes)
2653 {
2654 foreach (string exclude : excludes)
2655 {
2656 if (partName.Contains(exclude))
2657 {
2658 excluded = true;
2659 break;
2660 }
2661 }
2662 }
2663
2664 if (!excluded)
2666 }
2667
2668 GetConstruction().UpdateVisuals();
2669 }
2670}
2671
2672void bsbDebugPrint(string s)
2673{
2674#ifdef BSB_DEBUG
2675 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2676#else
2677 //Print("" + s); // comment/uncomment to hide/see debug logs
2678#endif
2679}
2680void bsbDebugSpam(string s)
2681{
2682#ifdef BSB_DEBUG_SPAM
2683 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2684#else
2685 //Print("" + s); // comment/uncomment to hide/see debug logs
2686#endif
2687}

Referenced by ItemBase::OnPartBuiltServer(), and ItemBase::SetPartsAfterStoreLoad().

◆ SetPartFromSyncData()

void bsbDebugPrint::SetPartFromSyncData ( ConstructionPart part)
protected

Definition at line 1415 of file BaseBuildingBase.c.

1417{
1418 const string ANIMATION_DEPLOYED = "Deployed";
1419
1420 float m_ConstructionKitHealth; //stored health value for used construction kit
1421
1423
1424 bool m_HasBase;
1425 //variables for synchronization of base building parts (2x31 is the current limit)
1426 int m_SyncParts01; //synchronization for already built parts (31 parts)
1427 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1428 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1429 int m_InteractedPartId; //construction part id that an action was performed on
1430 int m_PerformedActionId; //action id that was performed on a construction part
1431
1432 //Sounds
1433 //build
1434 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1435 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1436 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1437 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1438 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1439 //dismantle
1440 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1441 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1442 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1443 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1444 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1445
1446 protected EffectSound m_Sound;
1447
1451
1452 // Constructor
1453 void BaseBuildingBase()
1454 {
1456
1457 //synchronized variables
1458 RegisterNetSyncVariableInt("m_SyncParts01");
1459 RegisterNetSyncVariableInt("m_SyncParts02");
1460 RegisterNetSyncVariableInt("m_SyncParts03");
1461 RegisterNetSyncVariableInt("m_InteractedPartId");
1462 RegisterNetSyncVariableInt("m_PerformedActionId");
1463 RegisterNetSyncVariableBool("m_HasBase");
1464
1465 //Construction init
1467
1468 if (ConfigIsExisting("hybridAttachments"))
1469 {
1471 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1472 }
1473 if (ConfigIsExisting("mountables"))
1474 {
1476 ConfigGetTextArray("mountables", m_Mountables);
1477 }
1478
1479 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1480 }
1481
1482 override void EEDelete(EntityAI parent)
1483 {
1484 super.EEDelete(parent);
1485
1486 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1488
1489 }
1490
1491 override string GetInvulnerabilityTypeString()
1492 {
1493 return "disableBaseDamage";
1494 }
1495
1496 override bool CanObstruct()
1497 {
1498 return true;
1499 }
1500
1501 override int GetHideIconMask()
1502 {
1503 return EInventoryIconVisibility.HIDE_VICINITY;
1504 }
1505
1506 // --- SYNCHRONIZATION
1508 {
1509 if (GetGame().IsServer())
1510 SetSynchDirty();
1511 }
1512
1513 override void OnVariablesSynchronized()
1514 {
1515 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1516 super.OnVariablesSynchronized();
1517
1518 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1519 }
1520
1521 protected void OnSynchronizedClient()
1522 {
1523 //update parts
1525
1526 //update action on part
1528
1529 //update visuals (client)
1530 UpdateVisuals();
1531 }
1532
1533 //parts synchronization
1535 {
1536 //part_id must starts from index = 1
1537 int offset;
1538 int mask;
1539
1540 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1541 {
1542 offset = part_id - 1;
1543 mask = 1 << offset;
1544
1546 }
1547 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1548 {
1549 offset = (part_id % 32);
1550 mask = 1 << offset;
1551
1553 }
1554 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1555 {
1556 offset = (part_id % 63);
1557 mask = 1 << offset;
1558
1560 }
1561 }
1562
1564 {
1565 //part_id must starts from index = 1
1566 int offset;
1567 int mask;
1568
1569 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1570 {
1571 offset = part_id - 1;
1572 mask = 1 << offset;
1573
1575 }
1576 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1577 {
1578 offset = (part_id % 32);
1579 mask = 1 << offset;
1580
1582 }
1583 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1584 {
1585 offset = (part_id % 63);
1586 mask = 1 << offset;
1587
1589 }
1590 }
1591
1593 {
1594 //part_id must starts from index = 1
1595 int offset;
1596 int mask;
1597
1598 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1599 {
1600 offset = part_id - 1;
1601 mask = 1 << offset;
1602
1603 if ((m_SyncParts01 & mask) > 0)
1604 return true;
1605 }
1606 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1607 {
1608 offset = (part_id % 32);
1609 mask = 1 << offset;
1610
1611 if ((m_SyncParts02 & mask) > 0)
1612 return true;
1613 }
1614 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1615 {
1616 offset = (part_id % 63);
1617 mask = 1 << offset;
1618
1619 if ((m_SyncParts03 & mask) > 0)
1620 return true;
1621 }
1622
1623 return false;
1624 }
1625
1626 protected void RegisterActionForSync(int part_id, int action_id)
1627 {
1630 }
1631
1632 protected void ResetActionSyncData()
1633 {
1634 //reset data
1635 m_InteractedPartId = -1;
1637 }
1638
1639 protected void SetActionFromSyncData()
1640 {
1641 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1642 {
1645
1646 switch (build_action_id)
1647 {
1651 }
1652 }
1653 }
1654 //------
1655
1657 {
1658 string key = part.m_PartName;
1659 bool is_base = part.IsBase();
1661 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1663 {
1664 if (!part.IsBuilt())
1665 {
1666 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1667 GetConstruction().AddToConstructedParts(key);
1668 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1669
1670 if (is_base)
1671 {
1673 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1674 }
1675 }
1676 }
1677 else
1678 {
1679 if (part.IsBuilt())
1680 {
1681 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1682 GetConstruction().RemoveFromConstructedParts(key);
1683 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1684
1685 if (is_base)
1686 {
1688 AddProxyPhysics(ANIMATION_DEPLOYED);
1689 }
1690 }
1691 }
1692
1693 //check slot lock for material attachments
1694 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1695 }
1696
1697 //set construction parts based on synchronized data
1699 {
1702
1703 for (int i = 0; i < construction_parts.Count(); ++i)
1704 {
1705 string key = construction_parts.GetKey(i);
1708 }
1709
1710 //regenerate navmesh
1711 UpdateNavmesh();
1712 }
1713
1715 {
1718
1719 for (int i = 0; i < construction_parts.Count(); ++i)
1720 {
1721 string key = construction_parts.GetKey(i);
1723
1724 if (value.GetId() == id)
1725 return value;
1726 }
1727
1728 return NULL;
1729 }
1730 //
1731
1732 //Base
1733 bool HasBase()
1734 {
1735 return m_HasBase;
1736 }
1737
1738 void SetBaseState(bool has_base)
1739 {
1741 }
1742
1743 override bool IsDeployable()
1744 {
1745 return true;
1746 }
1747
1748 bool IsOpened()
1749 {
1750 return false;
1751 }
1752
1753 //--- CONSTRUCTION KIT
1755 {
1759
1760 return construction_kit;
1761 }
1762
1764 {
1765 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1768 }
1769
1770 protected vector GetKitSpawnPosition()
1771 {
1772 return GetPosition();
1773 }
1774
1775 protected string GetConstructionKitType()
1776 {
1777 return "";
1778 }
1779
1781 {
1783 GetGame().ObjectDelete(construction_kit);
1784 }
1785
1786 //--- CONSTRUCTION
1787 void DestroyConstruction()
1788 {
1789 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1790 GetGame().ObjectDelete(this);
1791 }
1792
1793 // --- EVENTS
1794 override void OnStoreSave(ParamsWriteContext ctx)
1795 {
1796 super.OnStoreSave(ctx);
1797
1798 //sync parts 01
1799 ctx.Write(m_SyncParts01);
1800 ctx.Write(m_SyncParts02);
1801 ctx.Write(m_SyncParts03);
1802
1803 ctx.Write(m_HasBase);
1804 }
1805
1806 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1807 {
1808 if (!super.OnStoreLoad(ctx, version))
1809 return false;
1810
1811 //--- Base building data ---
1812 //Restore synced parts data
1813 if (!ctx.Read(m_SyncParts01))
1814 {
1815 m_SyncParts01 = 0; //set default
1816 return false;
1817 }
1818 if (!ctx.Read(m_SyncParts02))
1819 {
1820 m_SyncParts02 = 0; //set default
1821 return false;
1822 }
1823 if (!ctx.Read(m_SyncParts03))
1824 {
1825 m_SyncParts03 = 0; //set default
1826 return false;
1827 }
1828
1829 //has base
1830 if (!ctx.Read(m_HasBase))
1831 {
1832 m_HasBase = false;
1833 return false;
1834 }
1835 //---
1836
1837 return true;
1838 }
1839
1840 override void AfterStoreLoad()
1841 {
1842 super.AfterStoreLoad();
1843
1846 }
1847
1849 {
1850 //update server data
1852
1853 //set base state
1854 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1855 SetBaseState(construction_part.IsBuilt()) ;
1856
1857 //synchronize after load
1859 }
1860
1861 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1862 {
1864 return;
1865
1866 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1867
1868 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1869 return;
1870
1872 string part_name = zone;
1873 part_name.ToLower();
1874
1876 {
1878
1879 if (construction_part && construction.IsPartConstructed(part_name))
1880 {
1881 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1882 construction.DestroyConnectedParts(part_name);
1883 }
1884
1885 //barbed wire handling (hack-ish)
1886 if (part_name.Contains("barbed"))
1887 {
1888 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1889 if (barbed_wire)
1890 barbed_wire.SetMountedState(false);
1891 }
1892 }
1893 }
1894
1895 override void EEOnAfterLoad()
1896 {
1898 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1899
1900 super.EEOnAfterLoad();
1901 }
1902
1903 override void EEInit()
1904 {
1905 super.EEInit();
1906
1907 // init visuals and physics
1908 InitBaseState();
1909
1910 //debug
1911#ifdef DEVELOPER
1913#endif
1914 }
1915
1916 override void EEItemAttached(EntityAI item, string slot_name)
1917 {
1918 super.EEItemAttached(item, slot_name);
1919
1921 UpdateVisuals();
1923 }
1924
1925 override void EEItemDetached(EntityAI item, string slot_name)
1926 {
1927 super.EEItemDetached(item, slot_name);
1928
1929 UpdateVisuals();
1931 }
1932
1933 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
1934 {
1936 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
1937
1940 }
1941
1942 //ignore out of reach condition
1943 override bool IgnoreOutOfReachCondition()
1944 {
1945 return true;
1946 }
1947
1948 //CONSTRUCTION EVENTS
1949 //Build
1950 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
1951 {
1953
1954 //check base state
1955 if (construtionPart.IsBase())
1956 {
1957 SetBaseState(true);
1958
1959 //spawn kit
1961 }
1962
1963 //register constructed parts for synchronization
1965
1966 //register action that was performed on part
1968
1969 //synchronize
1971
1972 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
1973
1974 UpdateNavmesh();
1975
1976 //update visuals
1977 UpdateVisuals();
1978
1979 //reset action sync data
1980 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1981 }
1982
1983 void OnPartBuiltClient(string part_name, int action_id)
1984 {
1985 //play sound
1987 }
1988
1989 //Dismantle
1991 {
1992 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
1994
1995 //register constructed parts for synchronization
1997
1998 //register action that was performed on part
2000
2001 //synchronize
2003
2004 // server part of sync, client will be synced from SetPartsFromSyncData
2006
2007 UpdateNavmesh();
2008
2009 //update visuals
2010 UpdateVisuals();
2011
2012 //reset action sync data
2013 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2014
2015 //check base state
2016 if (construtionPart.IsBase())
2017 {
2018 //Destroy construction
2019 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2020 }
2021 }
2022
2024 {
2025 //play sound
2027 }
2028
2029 //Destroy
2031 {
2032 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2034
2035 //register constructed parts for synchronization
2037
2038 //register action that was performed on part
2040
2041 //synchronize
2043
2044 // server part of sync, client will be synced from SetPartsFromSyncData
2046
2047 UpdateNavmesh();
2048
2049 //update visuals
2050 UpdateVisuals();
2051
2052 //reset action sync data
2053 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2054
2055 //check base state
2056 if (construtionPart.IsBase())
2057 {
2058 //Destroy construction
2059 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2060 }
2061 }
2062
2063 void OnPartDestroyedClient(string part_name, int action_id)
2064 {
2065 //play sound
2067 }
2068
2069 // --- UPDATE
2070 void InitBaseState()
2071 {
2072 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2073
2074 InitVisuals();
2075 UpdateNavmesh(); //regenerate navmesh
2076 GetConstruction().InitBaseState();
2077 }
2078
2079 void InitVisuals()
2080 {
2081 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2082 //check base
2083 if (!HasBase())
2084 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2085 else
2086 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2087
2088 GetConstruction().UpdateVisuals();
2089 }
2090
2091 void UpdateVisuals()
2092 {
2094
2096 foreach (string slotName : attachmentSlots)
2098
2099 //check base
2100 if (!HasBase())
2101 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2102 else
2103 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2104
2105 GetConstruction().UpdateVisuals();
2106 }
2107
2109 {
2110 string slotNameMounted = slot_name + "_Mounted";
2111 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2112
2113 if (attachment)
2114 {
2115 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2116 if (barbedWire && barbedWire.IsMounted())
2118 else
2120
2121 if (is_locked)
2122 {
2123 SetAnimationPhase(slotNameMounted, 0);
2124 SetAnimationPhase(slot_name, 1);
2125 }
2126 else
2127 {
2128 SetAnimationPhase(slotNameMounted, 1);
2129 SetAnimationPhase(slot_name, 0);
2130 }
2131 }
2132 else
2133 {
2134 SetAnimationPhase(slotNameMounted, 1);
2135 SetAnimationPhase(slot_name, 1);
2136
2138 }
2139 }
2140
2141 // avoid calling this function on frequent occasions, it's a massive performance hit
2142 void UpdatePhysics()
2143 {
2145 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2146
2149
2151 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2152
2153 foreach (string slotName : attachmentSlots)
2155
2156 //check base
2157 if (!HasBase())
2158 {
2160 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2161
2162 AddProxyPhysics(ANIMATION_DEPLOYED);
2163 }
2164 else
2165 {
2167 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2168
2169 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2170 }
2171
2172 GetConstruction().UpdatePhysics();
2173 UpdateNavmesh();
2174 }
2175
2177 {
2178 //checks for invalid appends; hotfix
2179 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2180 return;
2181 //----------------------------------
2182 string slot_name_mounted = slot_name + "_Mounted";
2183 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2184
2185 //remove proxy physics
2186 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2187 RemoveProxyPhysics(slot_name_mounted);
2188 RemoveProxyPhysics(slot_name);
2189
2190 if (attachment)
2191 {
2192 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2193 if (is_locked)
2194 {
2195 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2196 AddProxyPhysics(slot_name_mounted);
2197 }
2198 else
2199 {
2200 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2201 AddProxyPhysics(slot_name);
2202 }
2203 }
2204 }
2205
2206 protected void UpdateNavmesh()
2207 {
2208 SetAffectPathgraph(true, false);
2209 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2210 }
2211
2212 override bool CanUseConstruction()
2213 {
2214 return true;
2215 }
2216
2217 override bool CanUseConstructionBuild()
2218 {
2219 return true;
2220 }
2221
2223 {
2224 if (attachment)
2225 {
2227 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2228
2229 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2230 }
2231
2232 return false;
2233 }
2234
2235 protected bool IsAttachmentSlotLocked(string slot_name)
2236 {
2237 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2238 }
2239
2240 //--- ATTACHMENT SLOTS
2242 {
2243 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2244 if (GetGame().ConfigIsExisting(config_path))
2245 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2246 }
2247
2249 {
2250 return true;
2251 }
2252
2253 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2254 {
2255 return true;
2256 }
2257
2258 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2259 {
2260 return true;
2261 }
2262
2263 // --- INIT
2264 void ConstructionInit()
2265 {
2266 if (!m_Construction)
2267 m_Construction = new Construction(this);
2268
2269 GetConstruction().Init();
2270 }
2271
2273 {
2274 return m_Construction;
2275 }
2276
2277 //--- INVENTORY/ATTACHMENTS CONDITIONS
2278 //attachments
2280 {
2281 return super.CanReceiveAttachment(attachment, slotId);
2282 }
2283
2285 {
2286 int attachment_count = GetInventory().AttachmentCount();
2287 if (attachment_count > 0)
2288 {
2289 if (HasBase() && attachment_count == 1)
2290 return false;
2291
2292 return true;
2293 }
2294
2295 return false;
2296 }
2297
2298 override bool ShowZonesHealth()
2299 {
2300 return true;
2301 }
2302
2303 //this into/outo parent.Cargo
2304 override bool CanPutInCargo(EntityAI parent)
2305 {
2306 return false;
2307 }
2308
2309 override bool CanRemoveFromCargo(EntityAI parent)
2310 {
2311 return false;
2312 }
2313
2314 //hands
2315 override bool CanPutIntoHands(EntityAI parent)
2316 {
2317 return false;
2318 }
2319
2320 //--- ACTION CONDITIONS
2321 //direction
2322 override bool IsFacingPlayer(PlayerBase player, string selection)
2323 {
2324 return true;
2325 }
2326
2327 override bool IsPlayerInside(PlayerBase player, string selection)
2328 {
2329 return true;
2330 }
2331
2334 {
2335 return false;
2336 }
2337
2338 //camera direction check
2339 bool IsFacingCamera(string selection)
2340 {
2341 return true;
2342 }
2343
2344 //roof check
2346 {
2347 return false;
2348 }
2349
2350 //selection->player distance check
2351 bool HasProperDistance(string selection, PlayerBase player)
2352 {
2353 return true;
2354 }
2355
2356 //folding
2358 {
2359 if (HasBase() || GetInventory().AttachmentCount() > 0)
2360 return false;
2361
2362 return true;
2363 }
2364
2366 {
2369
2370 return item;
2371 }
2372
2373 //Damage triggers (barbed wire)
2374 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2375 {
2376 if (GetGame() && GetGame().IsServer())
2377 {
2378 //destroy area damage if some already exists
2380
2381 //create new area damage
2383 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2384
2385 vector min_max[2];
2386 if (MemoryPointExists(slot_name + "_min"))
2387 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2388 if (MemoryPointExists(slot_name + "_max"))
2389 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2390
2391 //get proper trigger extents (min<max)
2392 vector extents[2];
2393 GetConstruction().GetTriggerExtents(min_max, extents);
2394
2395 //get box center
2396 vector center;
2397 center = GetConstruction().GetBoxCenter(min_max);
2398 center = ModelToWorld(center);
2399
2400 //rotate center if needed
2403
2404 areaDamage.SetExtents(extents[0], extents[1]);
2405 areaDamage.SetAreaPosition(center);
2406 areaDamage.SetAreaOrientation(orientation);
2407 areaDamage.SetLoopInterval(1.0);
2408 areaDamage.SetDeferDuration(0.2);
2409 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2410 areaDamage.SetAmmoName("BarbedWireHit");
2411 areaDamage.Spawn();
2412
2414 }
2415 }
2416
2418 {
2419 if (angle_deg != 0)
2420 {
2421 //orientation
2423
2424 //center
2426 if (MemoryPointExists("rotate_axis"))
2427 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2430 center[0] = r_center_x;
2431 center[2] = r_center_z;
2432 }
2433 }
2434
2435 void DestroyAreaDamage(string slot_name)
2436 {
2437 if (GetGame() && GetGame().IsServer())
2438 {
2441 {
2442 if (areaDamage)
2443 areaDamage.Destroy();
2444
2446 }
2447 }
2448 }
2449
2450 override bool IsIgnoredByConstruction()
2451 {
2452 return true;
2453 }
2454
2455 //================================================================
2456 // SOUNDS
2457 //================================================================
2458 protected void SoundBuildStart(string part_name)
2459 {
2460 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2461 }
2462
2463 protected void SoundDismantleStart(string part_name)
2464 {
2465 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2466 }
2467
2468 protected void SoundDestroyStart(string part_name)
2469 {
2470 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2471 }
2472
2473 protected string GetBuildSoundByMaterial(string part_name)
2474 {
2476
2477 switch (material_type)
2478 {
2479 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2480 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2481 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2482 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2483 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2484 }
2485
2486 return "";
2487 }
2488
2489 protected string GetDismantleSoundByMaterial(string part_name)
2490 {
2492
2493 switch (material_type)
2494 {
2495 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2496 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2497 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2498 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2499 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2500 }
2501
2502 return "";
2503 }
2504
2505 //misc
2507 {
2508 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2509 {
2510 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2512 SetHealth(slot_name, "Health", item.GetHealth());
2513 }
2514 }
2515
2516 override int GetDamageSystemVersionChange()
2517 {
2518 return 111;
2519 }
2520
2521 override void SetActions()
2522 {
2523 super.SetActions();
2524
2526 //AddAction(ActionTakeHybridAttachment);
2527 //AddAction(ActionTakeHybridAttachmentToHands);
2530 }
2531
2532 //================================================================
2533 // DEBUG
2534 //================================================================
2535 protected void DebugCustomState()
2536 {
2537 }
2538
2541 {
2542 return null;
2543 }
2544
2545 override void OnDebugSpawn()
2546 {
2547 FullyBuild();
2548 }
2549
2550 void FullyBuild()
2551 {
2553 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2554
2555 Man p;
2556
2557#ifdef SERVER
2559 GetGame().GetWorld().GetPlayerList(players);
2560 if (players.Count())
2561 p = players[0];
2562#else
2563 p = GetGame().GetPlayer();
2564#endif
2565
2566 foreach (ConstructionPart part : parts)
2567 {
2568 bool excluded = false;
2569 string partName = part.GetPartName();
2570 if (excludes)
2571 {
2572 foreach (string exclude : excludes)
2573 {
2574 if (partName.Contains(exclude))
2575 {
2576 excluded = true;
2577 break;
2578 }
2579 }
2580 }
2581
2582 if (!excluded)
2584 }
2585
2586 GetConstruction().UpdateVisuals();
2587 }
2588}
2589
2590void bsbDebugPrint(string s)
2591{
2592#ifdef BSB_DEBUG
2593 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2594#else
2595 //Print("" + s); // comment/uncomment to hide/see debug logs
2596#endif
2597}
2598void bsbDebugSpam(string s)
2599{
2600#ifdef BSB_DEBUG_SPAM
2601 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2602#else
2603 //Print("" + s); // comment/uncomment to hide/see debug logs
2604#endif
2605}

Referenced by ItemBase::OnPartBuiltServer(), ItemBase::OnPartDestroyedServer(), ItemBase::OnPartDismantledServer(), and ItemBase::SetPartsFromSyncData().

◆ SetPartsAfterStoreLoad()

void bsbDebugPrint::SetPartsAfterStoreLoad ( )
protected

Definition at line 1607 of file BaseBuildingBase.c.

1609{
1610 const string ANIMATION_DEPLOYED = "Deployed";
1611
1612 float m_ConstructionKitHealth; //stored health value for used construction kit
1613
1615
1616 bool m_HasBase;
1617 //variables for synchronization of base building parts (2x31 is the current limit)
1618 int m_SyncParts01; //synchronization for already built parts (31 parts)
1619 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1620 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1621 int m_InteractedPartId; //construction part id that an action was performed on
1622 int m_PerformedActionId; //action id that was performed on a construction part
1623
1624 //Sounds
1625 //build
1626 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1627 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1628 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1629 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1630 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1631 //dismantle
1632 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1633 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1634 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1635 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1636 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1637
1638 protected EffectSound m_Sound;
1639
1643
1644 // Constructor
1645 void BaseBuildingBase()
1646 {
1648
1649 //synchronized variables
1650 RegisterNetSyncVariableInt("m_SyncParts01");
1651 RegisterNetSyncVariableInt("m_SyncParts02");
1652 RegisterNetSyncVariableInt("m_SyncParts03");
1653 RegisterNetSyncVariableInt("m_InteractedPartId");
1654 RegisterNetSyncVariableInt("m_PerformedActionId");
1655 RegisterNetSyncVariableBool("m_HasBase");
1656
1657 //Construction init
1659
1660 if (ConfigIsExisting("hybridAttachments"))
1661 {
1663 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1664 }
1665 if (ConfigIsExisting("mountables"))
1666 {
1668 ConfigGetTextArray("mountables", m_Mountables);
1669 }
1670
1671 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1672 }
1673
1674 override void EEDelete(EntityAI parent)
1675 {
1676 super.EEDelete(parent);
1677
1678 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1680
1681 }
1682
1683 override string GetInvulnerabilityTypeString()
1684 {
1685 return "disableBaseDamage";
1686 }
1687
1688 override bool CanObstruct()
1689 {
1690 return true;
1691 }
1692
1693 override int GetHideIconMask()
1694 {
1695 return EInventoryIconVisibility.HIDE_VICINITY;
1696 }
1697
1698 // --- SYNCHRONIZATION
1700 {
1701 if (GetGame().IsServer())
1702 SetSynchDirty();
1703 }
1704
1705 override void OnVariablesSynchronized()
1706 {
1707 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1708 super.OnVariablesSynchronized();
1709
1710 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1711 }
1712
1713 protected void OnSynchronizedClient()
1714 {
1715 //update parts
1717
1718 //update action on part
1720
1721 //update visuals (client)
1722 UpdateVisuals();
1723 }
1724
1725 //parts synchronization
1727 {
1728 //part_id must starts from index = 1
1729 int offset;
1730 int mask;
1731
1732 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1733 {
1734 offset = part_id - 1;
1735 mask = 1 << offset;
1736
1738 }
1739 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1740 {
1741 offset = (part_id % 32);
1742 mask = 1 << offset;
1743
1745 }
1746 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1747 {
1748 offset = (part_id % 63);
1749 mask = 1 << offset;
1750
1752 }
1753 }
1754
1756 {
1757 //part_id must starts from index = 1
1758 int offset;
1759 int mask;
1760
1761 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1762 {
1763 offset = part_id - 1;
1764 mask = 1 << offset;
1765
1767 }
1768 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1769 {
1770 offset = (part_id % 32);
1771 mask = 1 << offset;
1772
1774 }
1775 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1776 {
1777 offset = (part_id % 63);
1778 mask = 1 << offset;
1779
1781 }
1782 }
1783
1785 {
1786 //part_id must starts from index = 1
1787 int offset;
1788 int mask;
1789
1790 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1791 {
1792 offset = part_id - 1;
1793 mask = 1 << offset;
1794
1795 if ((m_SyncParts01 & mask) > 0)
1796 return true;
1797 }
1798 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1799 {
1800 offset = (part_id % 32);
1801 mask = 1 << offset;
1802
1803 if ((m_SyncParts02 & mask) > 0)
1804 return true;
1805 }
1806 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1807 {
1808 offset = (part_id % 63);
1809 mask = 1 << offset;
1810
1811 if ((m_SyncParts03 & mask) > 0)
1812 return true;
1813 }
1814
1815 return false;
1816 }
1817
1818 protected void RegisterActionForSync(int part_id, int action_id)
1819 {
1822 }
1823
1824 protected void ResetActionSyncData()
1825 {
1826 //reset data
1827 m_InteractedPartId = -1;
1829 }
1830
1831 protected void SetActionFromSyncData()
1832 {
1833 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1834 {
1837
1838 switch (build_action_id)
1839 {
1843 }
1844 }
1845 }
1846 //------
1847
1849 {
1850 string key = part.m_PartName;
1851 bool is_base = part.IsBase();
1853 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1855 {
1856 if (!part.IsBuilt())
1857 {
1858 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1859 GetConstruction().AddToConstructedParts(key);
1860 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1861
1862 if (is_base)
1863 {
1865 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1866 }
1867 }
1868 }
1869 else
1870 {
1871 if (part.IsBuilt())
1872 {
1873 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1874 GetConstruction().RemoveFromConstructedParts(key);
1875 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1876
1877 if (is_base)
1878 {
1880 AddProxyPhysics(ANIMATION_DEPLOYED);
1881 }
1882 }
1883 }
1884
1885 //check slot lock for material attachments
1886 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1887 }
1888
1889 //set construction parts based on synchronized data
1891 {
1894
1895 for (int i = 0; i < construction_parts.Count(); ++i)
1896 {
1897 string key = construction_parts.GetKey(i);
1900 }
1901
1902 //regenerate navmesh
1903 UpdateNavmesh();
1904 }
1905
1907 {
1910
1911 for (int i = 0; i < construction_parts.Count(); ++i)
1912 {
1913 string key = construction_parts.GetKey(i);
1915
1916 if (value.GetId() == id)
1917 return value;
1918 }
1919
1920 return NULL;
1921 }
1922 //
1923
1924 //Base
1925 bool HasBase()
1926 {
1927 return m_HasBase;
1928 }
1929
1930 void SetBaseState(bool has_base)
1931 {
1933 }
1934
1935 override bool IsDeployable()
1936 {
1937 return true;
1938 }
1939
1940 bool IsOpened()
1941 {
1942 return false;
1943 }
1944
1945 //--- CONSTRUCTION KIT
1947 {
1951
1952 return construction_kit;
1953 }
1954
1956 {
1957 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1960 }
1961
1962 protected vector GetKitSpawnPosition()
1963 {
1964 return GetPosition();
1965 }
1966
1967 protected string GetConstructionKitType()
1968 {
1969 return "";
1970 }
1971
1973 {
1975 GetGame().ObjectDelete(construction_kit);
1976 }
1977
1978 //--- CONSTRUCTION
1979 void DestroyConstruction()
1980 {
1981 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1982 GetGame().ObjectDelete(this);
1983 }
1984
1985 // --- EVENTS
1986 override void OnStoreSave(ParamsWriteContext ctx)
1987 {
1988 super.OnStoreSave(ctx);
1989
1990 //sync parts 01
1991 ctx.Write(m_SyncParts01);
1992 ctx.Write(m_SyncParts02);
1993 ctx.Write(m_SyncParts03);
1994
1995 ctx.Write(m_HasBase);
1996 }
1997
1998 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1999 {
2000 if (!super.OnStoreLoad(ctx, version))
2001 return false;
2002
2003 //--- Base building data ---
2004 //Restore synced parts data
2005 if (!ctx.Read(m_SyncParts01))
2006 {
2007 m_SyncParts01 = 0; //set default
2008 return false;
2009 }
2010 if (!ctx.Read(m_SyncParts02))
2011 {
2012 m_SyncParts02 = 0; //set default
2013 return false;
2014 }
2015 if (!ctx.Read(m_SyncParts03))
2016 {
2017 m_SyncParts03 = 0; //set default
2018 return false;
2019 }
2020
2021 //has base
2022 if (!ctx.Read(m_HasBase))
2023 {
2024 m_HasBase = false;
2025 return false;
2026 }
2027 //---
2028
2029 return true;
2030 }
2031
2032 override void AfterStoreLoad()
2033 {
2034 super.AfterStoreLoad();
2035
2038 }
2039
2041 {
2042 //update server data
2044
2045 //set base state
2046 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2047 SetBaseState(construction_part.IsBuilt()) ;
2048
2049 //synchronize after load
2051 }
2052
2053 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2054 {
2056 return;
2057
2058 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2059
2060 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2061 return;
2062
2064 string part_name = zone;
2065 part_name.ToLower();
2066
2068 {
2070
2071 if (construction_part && construction.IsPartConstructed(part_name))
2072 {
2073 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2074 construction.DestroyConnectedParts(part_name);
2075 }
2076
2077 //barbed wire handling (hack-ish)
2078 if (part_name.Contains("barbed"))
2079 {
2080 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2081 if (barbed_wire)
2082 barbed_wire.SetMountedState(false);
2083 }
2084 }
2085 }
2086
2087 override void EEOnAfterLoad()
2088 {
2090 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2091
2092 super.EEOnAfterLoad();
2093 }
2094
2095 override void EEInit()
2096 {
2097 super.EEInit();
2098
2099 // init visuals and physics
2100 InitBaseState();
2101
2102 //debug
2103#ifdef DEVELOPER
2105#endif
2106 }
2107
2108 override void EEItemAttached(EntityAI item, string slot_name)
2109 {
2110 super.EEItemAttached(item, slot_name);
2111
2113 UpdateVisuals();
2115 }
2116
2117 override void EEItemDetached(EntityAI item, string slot_name)
2118 {
2119 super.EEItemDetached(item, slot_name);
2120
2121 UpdateVisuals();
2123 }
2124
2125 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2126 {
2128 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2129
2132 }
2133
2134 //ignore out of reach condition
2135 override bool IgnoreOutOfReachCondition()
2136 {
2137 return true;
2138 }
2139
2140 //CONSTRUCTION EVENTS
2141 //Build
2142 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2143 {
2145
2146 //check base state
2147 if (construtionPart.IsBase())
2148 {
2149 SetBaseState(true);
2150
2151 //spawn kit
2153 }
2154
2155 //register constructed parts for synchronization
2157
2158 //register action that was performed on part
2160
2161 //synchronize
2163
2164 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2165
2166 UpdateNavmesh();
2167
2168 //update visuals
2169 UpdateVisuals();
2170
2171 //reset action sync data
2172 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2173 }
2174
2175 void OnPartBuiltClient(string part_name, int action_id)
2176 {
2177 //play sound
2179 }
2180
2181 //Dismantle
2183 {
2184 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2186
2187 //register constructed parts for synchronization
2189
2190 //register action that was performed on part
2192
2193 //synchronize
2195
2196 // server part of sync, client will be synced from SetPartsFromSyncData
2198
2199 UpdateNavmesh();
2200
2201 //update visuals
2202 UpdateVisuals();
2203
2204 //reset action sync data
2205 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2206
2207 //check base state
2208 if (construtionPart.IsBase())
2209 {
2210 //Destroy construction
2211 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2212 }
2213 }
2214
2216 {
2217 //play sound
2219 }
2220
2221 //Destroy
2223 {
2224 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2226
2227 //register constructed parts for synchronization
2229
2230 //register action that was performed on part
2232
2233 //synchronize
2235
2236 // server part of sync, client will be synced from SetPartsFromSyncData
2238
2239 UpdateNavmesh();
2240
2241 //update visuals
2242 UpdateVisuals();
2243
2244 //reset action sync data
2245 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2246
2247 //check base state
2248 if (construtionPart.IsBase())
2249 {
2250 //Destroy construction
2251 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2252 }
2253 }
2254
2255 void OnPartDestroyedClient(string part_name, int action_id)
2256 {
2257 //play sound
2259 }
2260
2261 // --- UPDATE
2262 void InitBaseState()
2263 {
2264 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2265
2266 InitVisuals();
2267 UpdateNavmesh(); //regenerate navmesh
2268 GetConstruction().InitBaseState();
2269 }
2270
2271 void InitVisuals()
2272 {
2273 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2274 //check base
2275 if (!HasBase())
2276 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2277 else
2278 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2279
2280 GetConstruction().UpdateVisuals();
2281 }
2282
2283 void UpdateVisuals()
2284 {
2286
2288 foreach (string slotName : attachmentSlots)
2290
2291 //check base
2292 if (!HasBase())
2293 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2294 else
2295 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2296
2297 GetConstruction().UpdateVisuals();
2298 }
2299
2301 {
2302 string slotNameMounted = slot_name + "_Mounted";
2303 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2304
2305 if (attachment)
2306 {
2307 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2308 if (barbedWire && barbedWire.IsMounted())
2310 else
2312
2313 if (is_locked)
2314 {
2315 SetAnimationPhase(slotNameMounted, 0);
2316 SetAnimationPhase(slot_name, 1);
2317 }
2318 else
2319 {
2320 SetAnimationPhase(slotNameMounted, 1);
2321 SetAnimationPhase(slot_name, 0);
2322 }
2323 }
2324 else
2325 {
2326 SetAnimationPhase(slotNameMounted, 1);
2327 SetAnimationPhase(slot_name, 1);
2328
2330 }
2331 }
2332
2333 // avoid calling this function on frequent occasions, it's a massive performance hit
2334 void UpdatePhysics()
2335 {
2337 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2338
2341
2343 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2344
2345 foreach (string slotName : attachmentSlots)
2347
2348 //check base
2349 if (!HasBase())
2350 {
2352 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2353
2354 AddProxyPhysics(ANIMATION_DEPLOYED);
2355 }
2356 else
2357 {
2359 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2360
2361 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2362 }
2363
2364 GetConstruction().UpdatePhysics();
2365 UpdateNavmesh();
2366 }
2367
2369 {
2370 //checks for invalid appends; hotfix
2371 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2372 return;
2373 //----------------------------------
2374 string slot_name_mounted = slot_name + "_Mounted";
2375 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2376
2377 //remove proxy physics
2378 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2379 RemoveProxyPhysics(slot_name_mounted);
2380 RemoveProxyPhysics(slot_name);
2381
2382 if (attachment)
2383 {
2384 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2385 if (is_locked)
2386 {
2387 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2388 AddProxyPhysics(slot_name_mounted);
2389 }
2390 else
2391 {
2392 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2393 AddProxyPhysics(slot_name);
2394 }
2395 }
2396 }
2397
2398 protected void UpdateNavmesh()
2399 {
2400 SetAffectPathgraph(true, false);
2401 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2402 }
2403
2404 override bool CanUseConstruction()
2405 {
2406 return true;
2407 }
2408
2409 override bool CanUseConstructionBuild()
2410 {
2411 return true;
2412 }
2413
2415 {
2416 if (attachment)
2417 {
2419 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2420
2421 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2422 }
2423
2424 return false;
2425 }
2426
2427 protected bool IsAttachmentSlotLocked(string slot_name)
2428 {
2429 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2430 }
2431
2432 //--- ATTACHMENT SLOTS
2434 {
2435 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2436 if (GetGame().ConfigIsExisting(config_path))
2437 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2438 }
2439
2441 {
2442 return true;
2443 }
2444
2445 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2446 {
2447 return true;
2448 }
2449
2450 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2451 {
2452 return true;
2453 }
2454
2455 // --- INIT
2456 void ConstructionInit()
2457 {
2458 if (!m_Construction)
2459 m_Construction = new Construction(this);
2460
2461 GetConstruction().Init();
2462 }
2463
2465 {
2466 return m_Construction;
2467 }
2468
2469 //--- INVENTORY/ATTACHMENTS CONDITIONS
2470 //attachments
2472 {
2473 return super.CanReceiveAttachment(attachment, slotId);
2474 }
2475
2477 {
2478 int attachment_count = GetInventory().AttachmentCount();
2479 if (attachment_count > 0)
2480 {
2481 if (HasBase() && attachment_count == 1)
2482 return false;
2483
2484 return true;
2485 }
2486
2487 return false;
2488 }
2489
2490 override bool ShowZonesHealth()
2491 {
2492 return true;
2493 }
2494
2495 //this into/outo parent.Cargo
2496 override bool CanPutInCargo(EntityAI parent)
2497 {
2498 return false;
2499 }
2500
2501 override bool CanRemoveFromCargo(EntityAI parent)
2502 {
2503 return false;
2504 }
2505
2506 //hands
2507 override bool CanPutIntoHands(EntityAI parent)
2508 {
2509 return false;
2510 }
2511
2512 //--- ACTION CONDITIONS
2513 //direction
2514 override bool IsFacingPlayer(PlayerBase player, string selection)
2515 {
2516 return true;
2517 }
2518
2519 override bool IsPlayerInside(PlayerBase player, string selection)
2520 {
2521 return true;
2522 }
2523
2526 {
2527 return false;
2528 }
2529
2530 //camera direction check
2531 bool IsFacingCamera(string selection)
2532 {
2533 return true;
2534 }
2535
2536 //roof check
2538 {
2539 return false;
2540 }
2541
2542 //selection->player distance check
2543 bool HasProperDistance(string selection, PlayerBase player)
2544 {
2545 return true;
2546 }
2547
2548 //folding
2550 {
2551 if (HasBase() || GetInventory().AttachmentCount() > 0)
2552 return false;
2553
2554 return true;
2555 }
2556
2558 {
2561
2562 return item;
2563 }
2564
2565 //Damage triggers (barbed wire)
2566 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2567 {
2568 if (GetGame() && GetGame().IsServer())
2569 {
2570 //destroy area damage if some already exists
2572
2573 //create new area damage
2575 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2576
2577 vector min_max[2];
2578 if (MemoryPointExists(slot_name + "_min"))
2579 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2580 if (MemoryPointExists(slot_name + "_max"))
2581 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2582
2583 //get proper trigger extents (min<max)
2584 vector extents[2];
2585 GetConstruction().GetTriggerExtents(min_max, extents);
2586
2587 //get box center
2588 vector center;
2589 center = GetConstruction().GetBoxCenter(min_max);
2590 center = ModelToWorld(center);
2591
2592 //rotate center if needed
2595
2596 areaDamage.SetExtents(extents[0], extents[1]);
2597 areaDamage.SetAreaPosition(center);
2598 areaDamage.SetAreaOrientation(orientation);
2599 areaDamage.SetLoopInterval(1.0);
2600 areaDamage.SetDeferDuration(0.2);
2601 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2602 areaDamage.SetAmmoName("BarbedWireHit");
2603 areaDamage.Spawn();
2604
2606 }
2607 }
2608
2610 {
2611 if (angle_deg != 0)
2612 {
2613 //orientation
2615
2616 //center
2618 if (MemoryPointExists("rotate_axis"))
2619 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2622 center[0] = r_center_x;
2623 center[2] = r_center_z;
2624 }
2625 }
2626
2627 void DestroyAreaDamage(string slot_name)
2628 {
2629 if (GetGame() && GetGame().IsServer())
2630 {
2633 {
2634 if (areaDamage)
2635 areaDamage.Destroy();
2636
2638 }
2639 }
2640 }
2641
2642 override bool IsIgnoredByConstruction()
2643 {
2644 return true;
2645 }
2646
2647 //================================================================
2648 // SOUNDS
2649 //================================================================
2650 protected void SoundBuildStart(string part_name)
2651 {
2652 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2653 }
2654
2655 protected void SoundDismantleStart(string part_name)
2656 {
2657 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2658 }
2659
2660 protected void SoundDestroyStart(string part_name)
2661 {
2662 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2663 }
2664
2665 protected string GetBuildSoundByMaterial(string part_name)
2666 {
2668
2669 switch (material_type)
2670 {
2671 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2672 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2673 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2674 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2675 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2676 }
2677
2678 return "";
2679 }
2680
2681 protected string GetDismantleSoundByMaterial(string part_name)
2682 {
2684
2685 switch (material_type)
2686 {
2687 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2688 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2689 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2690 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2691 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2692 }
2693
2694 return "";
2695 }
2696
2697 //misc
2699 {
2700 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2701 {
2702 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2704 SetHealth(slot_name, "Health", item.GetHealth());
2705 }
2706 }
2707
2708 override int GetDamageSystemVersionChange()
2709 {
2710 return 111;
2711 }
2712
2713 override void SetActions()
2714 {
2715 super.SetActions();
2716
2718 //AddAction(ActionTakeHybridAttachment);
2719 //AddAction(ActionTakeHybridAttachmentToHands);
2722 }
2723
2724 //================================================================
2725 // DEBUG
2726 //================================================================
2727 protected void DebugCustomState()
2728 {
2729 }
2730
2733 {
2734 return null;
2735 }
2736
2737 override void OnDebugSpawn()
2738 {
2739 FullyBuild();
2740 }
2741
2742 void FullyBuild()
2743 {
2745 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2746
2747 Man p;
2748
2749#ifdef SERVER
2751 GetGame().GetWorld().GetPlayerList(players);
2752 if (players.Count())
2753 p = players[0];
2754#else
2755 p = GetGame().GetPlayer();
2756#endif
2757
2758 foreach (ConstructionPart part : parts)
2759 {
2760 bool excluded = false;
2761 string partName = part.GetPartName();
2762 if (excludes)
2763 {
2764 foreach (string exclude : excludes)
2765 {
2766 if (partName.Contains(exclude))
2767 {
2768 excluded = true;
2769 break;
2770 }
2771 }
2772 }
2773
2774 if (!excluded)
2776 }
2777
2778 GetConstruction().UpdateVisuals();
2779 }
2780}
2781
2782void bsbDebugPrint(string s)
2783{
2784#ifdef BSB_DEBUG
2785 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2786#else
2787 //Print("" + s); // comment/uncomment to hide/see debug logs
2788#endif
2789}
2790void bsbDebugSpam(string s)
2791{
2792#ifdef BSB_DEBUG_SPAM
2793 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2794#else
2795 //Print("" + s); // comment/uncomment to hide/see debug logs
2796#endif
2797}

Referenced by ItemBase::AfterStoreLoad(), and ItemBase::EEOnAfterLoad().

◆ SetPartsFromSyncData()

void bsbDebugPrint::SetPartsFromSyncData ( )
protected

Definition at line 1457 of file BaseBuildingBase.c.

1459{
1460 const string ANIMATION_DEPLOYED = "Deployed";
1461
1462 float m_ConstructionKitHealth; //stored health value for used construction kit
1463
1465
1466 bool m_HasBase;
1467 //variables for synchronization of base building parts (2x31 is the current limit)
1468 int m_SyncParts01; //synchronization for already built parts (31 parts)
1469 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1470 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1471 int m_InteractedPartId; //construction part id that an action was performed on
1472 int m_PerformedActionId; //action id that was performed on a construction part
1473
1474 //Sounds
1475 //build
1476 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1477 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1478 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1479 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1480 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1481 //dismantle
1482 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1483 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1484 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1485 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1486 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1487
1488 protected EffectSound m_Sound;
1489
1493
1494 // Constructor
1495 void BaseBuildingBase()
1496 {
1498
1499 //synchronized variables
1500 RegisterNetSyncVariableInt("m_SyncParts01");
1501 RegisterNetSyncVariableInt("m_SyncParts02");
1502 RegisterNetSyncVariableInt("m_SyncParts03");
1503 RegisterNetSyncVariableInt("m_InteractedPartId");
1504 RegisterNetSyncVariableInt("m_PerformedActionId");
1505 RegisterNetSyncVariableBool("m_HasBase");
1506
1507 //Construction init
1509
1510 if (ConfigIsExisting("hybridAttachments"))
1511 {
1513 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1514 }
1515 if (ConfigIsExisting("mountables"))
1516 {
1518 ConfigGetTextArray("mountables", m_Mountables);
1519 }
1520
1521 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1522 }
1523
1524 override void EEDelete(EntityAI parent)
1525 {
1526 super.EEDelete(parent);
1527
1528 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1530
1531 }
1532
1533 override string GetInvulnerabilityTypeString()
1534 {
1535 return "disableBaseDamage";
1536 }
1537
1538 override bool CanObstruct()
1539 {
1540 return true;
1541 }
1542
1543 override int GetHideIconMask()
1544 {
1545 return EInventoryIconVisibility.HIDE_VICINITY;
1546 }
1547
1548 // --- SYNCHRONIZATION
1550 {
1551 if (GetGame().IsServer())
1552 SetSynchDirty();
1553 }
1554
1555 override void OnVariablesSynchronized()
1556 {
1557 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1558 super.OnVariablesSynchronized();
1559
1560 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1561 }
1562
1563 protected void OnSynchronizedClient()
1564 {
1565 //update parts
1567
1568 //update action on part
1570
1571 //update visuals (client)
1572 UpdateVisuals();
1573 }
1574
1575 //parts synchronization
1577 {
1578 //part_id must starts from index = 1
1579 int offset;
1580 int mask;
1581
1582 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1583 {
1584 offset = part_id - 1;
1585 mask = 1 << offset;
1586
1588 }
1589 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1590 {
1591 offset = (part_id % 32);
1592 mask = 1 << offset;
1593
1595 }
1596 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1597 {
1598 offset = (part_id % 63);
1599 mask = 1 << offset;
1600
1602 }
1603 }
1604
1606 {
1607 //part_id must starts from index = 1
1608 int offset;
1609 int mask;
1610
1611 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1612 {
1613 offset = part_id - 1;
1614 mask = 1 << offset;
1615
1617 }
1618 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1619 {
1620 offset = (part_id % 32);
1621 mask = 1 << offset;
1622
1624 }
1625 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1626 {
1627 offset = (part_id % 63);
1628 mask = 1 << offset;
1629
1631 }
1632 }
1633
1635 {
1636 //part_id must starts from index = 1
1637 int offset;
1638 int mask;
1639
1640 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1641 {
1642 offset = part_id - 1;
1643 mask = 1 << offset;
1644
1645 if ((m_SyncParts01 & mask) > 0)
1646 return true;
1647 }
1648 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1649 {
1650 offset = (part_id % 32);
1651 mask = 1 << offset;
1652
1653 if ((m_SyncParts02 & mask) > 0)
1654 return true;
1655 }
1656 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1657 {
1658 offset = (part_id % 63);
1659 mask = 1 << offset;
1660
1661 if ((m_SyncParts03 & mask) > 0)
1662 return true;
1663 }
1664
1665 return false;
1666 }
1667
1668 protected void RegisterActionForSync(int part_id, int action_id)
1669 {
1672 }
1673
1674 protected void ResetActionSyncData()
1675 {
1676 //reset data
1677 m_InteractedPartId = -1;
1679 }
1680
1681 protected void SetActionFromSyncData()
1682 {
1683 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1684 {
1687
1688 switch (build_action_id)
1689 {
1693 }
1694 }
1695 }
1696 //------
1697
1699 {
1700 string key = part.m_PartName;
1701 bool is_base = part.IsBase();
1703 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1705 {
1706 if (!part.IsBuilt())
1707 {
1708 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1709 GetConstruction().AddToConstructedParts(key);
1710 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1711
1712 if (is_base)
1713 {
1715 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1716 }
1717 }
1718 }
1719 else
1720 {
1721 if (part.IsBuilt())
1722 {
1723 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1724 GetConstruction().RemoveFromConstructedParts(key);
1725 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1726
1727 if (is_base)
1728 {
1730 AddProxyPhysics(ANIMATION_DEPLOYED);
1731 }
1732 }
1733 }
1734
1735 //check slot lock for material attachments
1736 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1737 }
1738
1739 //set construction parts based on synchronized data
1741 {
1744
1745 for (int i = 0; i < construction_parts.Count(); ++i)
1746 {
1747 string key = construction_parts.GetKey(i);
1750 }
1751
1752 //regenerate navmesh
1753 UpdateNavmesh();
1754 }
1755
1757 {
1760
1761 for (int i = 0; i < construction_parts.Count(); ++i)
1762 {
1763 string key = construction_parts.GetKey(i);
1765
1766 if (value.GetId() == id)
1767 return value;
1768 }
1769
1770 return NULL;
1771 }
1772 //
1773
1774 //Base
1775 bool HasBase()
1776 {
1777 return m_HasBase;
1778 }
1779
1780 void SetBaseState(bool has_base)
1781 {
1783 }
1784
1785 override bool IsDeployable()
1786 {
1787 return true;
1788 }
1789
1790 bool IsOpened()
1791 {
1792 return false;
1793 }
1794
1795 //--- CONSTRUCTION KIT
1797 {
1801
1802 return construction_kit;
1803 }
1804
1806 {
1807 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1810 }
1811
1812 protected vector GetKitSpawnPosition()
1813 {
1814 return GetPosition();
1815 }
1816
1817 protected string GetConstructionKitType()
1818 {
1819 return "";
1820 }
1821
1823 {
1825 GetGame().ObjectDelete(construction_kit);
1826 }
1827
1828 //--- CONSTRUCTION
1829 void DestroyConstruction()
1830 {
1831 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1832 GetGame().ObjectDelete(this);
1833 }
1834
1835 // --- EVENTS
1836 override void OnStoreSave(ParamsWriteContext ctx)
1837 {
1838 super.OnStoreSave(ctx);
1839
1840 //sync parts 01
1841 ctx.Write(m_SyncParts01);
1842 ctx.Write(m_SyncParts02);
1843 ctx.Write(m_SyncParts03);
1844
1845 ctx.Write(m_HasBase);
1846 }
1847
1848 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1849 {
1850 if (!super.OnStoreLoad(ctx, version))
1851 return false;
1852
1853 //--- Base building data ---
1854 //Restore synced parts data
1855 if (!ctx.Read(m_SyncParts01))
1856 {
1857 m_SyncParts01 = 0; //set default
1858 return false;
1859 }
1860 if (!ctx.Read(m_SyncParts02))
1861 {
1862 m_SyncParts02 = 0; //set default
1863 return false;
1864 }
1865 if (!ctx.Read(m_SyncParts03))
1866 {
1867 m_SyncParts03 = 0; //set default
1868 return false;
1869 }
1870
1871 //has base
1872 if (!ctx.Read(m_HasBase))
1873 {
1874 m_HasBase = false;
1875 return false;
1876 }
1877 //---
1878
1879 return true;
1880 }
1881
1882 override void AfterStoreLoad()
1883 {
1884 super.AfterStoreLoad();
1885
1888 }
1889
1891 {
1892 //update server data
1894
1895 //set base state
1896 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1897 SetBaseState(construction_part.IsBuilt()) ;
1898
1899 //synchronize after load
1901 }
1902
1903 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1904 {
1906 return;
1907
1908 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1909
1910 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1911 return;
1912
1914 string part_name = zone;
1915 part_name.ToLower();
1916
1918 {
1920
1921 if (construction_part && construction.IsPartConstructed(part_name))
1922 {
1923 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1924 construction.DestroyConnectedParts(part_name);
1925 }
1926
1927 //barbed wire handling (hack-ish)
1928 if (part_name.Contains("barbed"))
1929 {
1930 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1931 if (barbed_wire)
1932 barbed_wire.SetMountedState(false);
1933 }
1934 }
1935 }
1936
1937 override void EEOnAfterLoad()
1938 {
1940 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1941
1942 super.EEOnAfterLoad();
1943 }
1944
1945 override void EEInit()
1946 {
1947 super.EEInit();
1948
1949 // init visuals and physics
1950 InitBaseState();
1951
1952 //debug
1953#ifdef DEVELOPER
1955#endif
1956 }
1957
1958 override void EEItemAttached(EntityAI item, string slot_name)
1959 {
1960 super.EEItemAttached(item, slot_name);
1961
1963 UpdateVisuals();
1965 }
1966
1967 override void EEItemDetached(EntityAI item, string slot_name)
1968 {
1969 super.EEItemDetached(item, slot_name);
1970
1971 UpdateVisuals();
1973 }
1974
1975 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
1976 {
1978 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
1979
1982 }
1983
1984 //ignore out of reach condition
1985 override bool IgnoreOutOfReachCondition()
1986 {
1987 return true;
1988 }
1989
1990 //CONSTRUCTION EVENTS
1991 //Build
1992 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
1993 {
1995
1996 //check base state
1997 if (construtionPart.IsBase())
1998 {
1999 SetBaseState(true);
2000
2001 //spawn kit
2003 }
2004
2005 //register constructed parts for synchronization
2007
2008 //register action that was performed on part
2010
2011 //synchronize
2013
2014 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2015
2016 UpdateNavmesh();
2017
2018 //update visuals
2019 UpdateVisuals();
2020
2021 //reset action sync data
2022 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2023 }
2024
2025 void OnPartBuiltClient(string part_name, int action_id)
2026 {
2027 //play sound
2029 }
2030
2031 //Dismantle
2033 {
2034 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2036
2037 //register constructed parts for synchronization
2039
2040 //register action that was performed on part
2042
2043 //synchronize
2045
2046 // server part of sync, client will be synced from SetPartsFromSyncData
2048
2049 UpdateNavmesh();
2050
2051 //update visuals
2052 UpdateVisuals();
2053
2054 //reset action sync data
2055 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2056
2057 //check base state
2058 if (construtionPart.IsBase())
2059 {
2060 //Destroy construction
2061 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2062 }
2063 }
2064
2066 {
2067 //play sound
2069 }
2070
2071 //Destroy
2073 {
2074 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2076
2077 //register constructed parts for synchronization
2079
2080 //register action that was performed on part
2082
2083 //synchronize
2085
2086 // server part of sync, client will be synced from SetPartsFromSyncData
2088
2089 UpdateNavmesh();
2090
2091 //update visuals
2092 UpdateVisuals();
2093
2094 //reset action sync data
2095 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2096
2097 //check base state
2098 if (construtionPart.IsBase())
2099 {
2100 //Destroy construction
2101 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2102 }
2103 }
2104
2105 void OnPartDestroyedClient(string part_name, int action_id)
2106 {
2107 //play sound
2109 }
2110
2111 // --- UPDATE
2112 void InitBaseState()
2113 {
2114 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2115
2116 InitVisuals();
2117 UpdateNavmesh(); //regenerate navmesh
2118 GetConstruction().InitBaseState();
2119 }
2120
2121 void InitVisuals()
2122 {
2123 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2124 //check base
2125 if (!HasBase())
2126 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2127 else
2128 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2129
2130 GetConstruction().UpdateVisuals();
2131 }
2132
2133 void UpdateVisuals()
2134 {
2136
2138 foreach (string slotName : attachmentSlots)
2140
2141 //check base
2142 if (!HasBase())
2143 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2144 else
2145 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2146
2147 GetConstruction().UpdateVisuals();
2148 }
2149
2151 {
2152 string slotNameMounted = slot_name + "_Mounted";
2153 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2154
2155 if (attachment)
2156 {
2157 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2158 if (barbedWire && barbedWire.IsMounted())
2160 else
2162
2163 if (is_locked)
2164 {
2165 SetAnimationPhase(slotNameMounted, 0);
2166 SetAnimationPhase(slot_name, 1);
2167 }
2168 else
2169 {
2170 SetAnimationPhase(slotNameMounted, 1);
2171 SetAnimationPhase(slot_name, 0);
2172 }
2173 }
2174 else
2175 {
2176 SetAnimationPhase(slotNameMounted, 1);
2177 SetAnimationPhase(slot_name, 1);
2178
2180 }
2181 }
2182
2183 // avoid calling this function on frequent occasions, it's a massive performance hit
2184 void UpdatePhysics()
2185 {
2187 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2188
2191
2193 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2194
2195 foreach (string slotName : attachmentSlots)
2197
2198 //check base
2199 if (!HasBase())
2200 {
2202 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2203
2204 AddProxyPhysics(ANIMATION_DEPLOYED);
2205 }
2206 else
2207 {
2209 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2210
2211 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2212 }
2213
2214 GetConstruction().UpdatePhysics();
2215 UpdateNavmesh();
2216 }
2217
2219 {
2220 //checks for invalid appends; hotfix
2221 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2222 return;
2223 //----------------------------------
2224 string slot_name_mounted = slot_name + "_Mounted";
2225 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2226
2227 //remove proxy physics
2228 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2229 RemoveProxyPhysics(slot_name_mounted);
2230 RemoveProxyPhysics(slot_name);
2231
2232 if (attachment)
2233 {
2234 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2235 if (is_locked)
2236 {
2237 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2238 AddProxyPhysics(slot_name_mounted);
2239 }
2240 else
2241 {
2242 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2243 AddProxyPhysics(slot_name);
2244 }
2245 }
2246 }
2247
2248 protected void UpdateNavmesh()
2249 {
2250 SetAffectPathgraph(true, false);
2251 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2252 }
2253
2254 override bool CanUseConstruction()
2255 {
2256 return true;
2257 }
2258
2259 override bool CanUseConstructionBuild()
2260 {
2261 return true;
2262 }
2263
2265 {
2266 if (attachment)
2267 {
2269 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2270
2271 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2272 }
2273
2274 return false;
2275 }
2276
2277 protected bool IsAttachmentSlotLocked(string slot_name)
2278 {
2279 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2280 }
2281
2282 //--- ATTACHMENT SLOTS
2284 {
2285 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2286 if (GetGame().ConfigIsExisting(config_path))
2287 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2288 }
2289
2291 {
2292 return true;
2293 }
2294
2295 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2296 {
2297 return true;
2298 }
2299
2300 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2301 {
2302 return true;
2303 }
2304
2305 // --- INIT
2306 void ConstructionInit()
2307 {
2308 if (!m_Construction)
2309 m_Construction = new Construction(this);
2310
2311 GetConstruction().Init();
2312 }
2313
2315 {
2316 return m_Construction;
2317 }
2318
2319 //--- INVENTORY/ATTACHMENTS CONDITIONS
2320 //attachments
2322 {
2323 return super.CanReceiveAttachment(attachment, slotId);
2324 }
2325
2327 {
2328 int attachment_count = GetInventory().AttachmentCount();
2329 if (attachment_count > 0)
2330 {
2331 if (HasBase() && attachment_count == 1)
2332 return false;
2333
2334 return true;
2335 }
2336
2337 return false;
2338 }
2339
2340 override bool ShowZonesHealth()
2341 {
2342 return true;
2343 }
2344
2345 //this into/outo parent.Cargo
2346 override bool CanPutInCargo(EntityAI parent)
2347 {
2348 return false;
2349 }
2350
2351 override bool CanRemoveFromCargo(EntityAI parent)
2352 {
2353 return false;
2354 }
2355
2356 //hands
2357 override bool CanPutIntoHands(EntityAI parent)
2358 {
2359 return false;
2360 }
2361
2362 //--- ACTION CONDITIONS
2363 //direction
2364 override bool IsFacingPlayer(PlayerBase player, string selection)
2365 {
2366 return true;
2367 }
2368
2369 override bool IsPlayerInside(PlayerBase player, string selection)
2370 {
2371 return true;
2372 }
2373
2376 {
2377 return false;
2378 }
2379
2380 //camera direction check
2381 bool IsFacingCamera(string selection)
2382 {
2383 return true;
2384 }
2385
2386 //roof check
2388 {
2389 return false;
2390 }
2391
2392 //selection->player distance check
2393 bool HasProperDistance(string selection, PlayerBase player)
2394 {
2395 return true;
2396 }
2397
2398 //folding
2400 {
2401 if (HasBase() || GetInventory().AttachmentCount() > 0)
2402 return false;
2403
2404 return true;
2405 }
2406
2408 {
2411
2412 return item;
2413 }
2414
2415 //Damage triggers (barbed wire)
2416 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2417 {
2418 if (GetGame() && GetGame().IsServer())
2419 {
2420 //destroy area damage if some already exists
2422
2423 //create new area damage
2425 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2426
2427 vector min_max[2];
2428 if (MemoryPointExists(slot_name + "_min"))
2429 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2430 if (MemoryPointExists(slot_name + "_max"))
2431 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2432
2433 //get proper trigger extents (min<max)
2434 vector extents[2];
2435 GetConstruction().GetTriggerExtents(min_max, extents);
2436
2437 //get box center
2438 vector center;
2439 center = GetConstruction().GetBoxCenter(min_max);
2440 center = ModelToWorld(center);
2441
2442 //rotate center if needed
2445
2446 areaDamage.SetExtents(extents[0], extents[1]);
2447 areaDamage.SetAreaPosition(center);
2448 areaDamage.SetAreaOrientation(orientation);
2449 areaDamage.SetLoopInterval(1.0);
2450 areaDamage.SetDeferDuration(0.2);
2451 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2452 areaDamage.SetAmmoName("BarbedWireHit");
2453 areaDamage.Spawn();
2454
2456 }
2457 }
2458
2460 {
2461 if (angle_deg != 0)
2462 {
2463 //orientation
2465
2466 //center
2468 if (MemoryPointExists("rotate_axis"))
2469 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2472 center[0] = r_center_x;
2473 center[2] = r_center_z;
2474 }
2475 }
2476
2477 void DestroyAreaDamage(string slot_name)
2478 {
2479 if (GetGame() && GetGame().IsServer())
2480 {
2483 {
2484 if (areaDamage)
2485 areaDamage.Destroy();
2486
2488 }
2489 }
2490 }
2491
2492 override bool IsIgnoredByConstruction()
2493 {
2494 return true;
2495 }
2496
2497 //================================================================
2498 // SOUNDS
2499 //================================================================
2500 protected void SoundBuildStart(string part_name)
2501 {
2502 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2503 }
2504
2505 protected void SoundDismantleStart(string part_name)
2506 {
2507 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2508 }
2509
2510 protected void SoundDestroyStart(string part_name)
2511 {
2512 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2513 }
2514
2515 protected string GetBuildSoundByMaterial(string part_name)
2516 {
2518
2519 switch (material_type)
2520 {
2521 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2522 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2523 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2524 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2525 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2526 }
2527
2528 return "";
2529 }
2530
2531 protected string GetDismantleSoundByMaterial(string part_name)
2532 {
2534
2535 switch (material_type)
2536 {
2537 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2538 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2539 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2540 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2541 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2542 }
2543
2544 return "";
2545 }
2546
2547 //misc
2549 {
2550 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2551 {
2552 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2554 SetHealth(slot_name, "Health", item.GetHealth());
2555 }
2556 }
2557
2558 override int GetDamageSystemVersionChange()
2559 {
2560 return 111;
2561 }
2562
2563 override void SetActions()
2564 {
2565 super.SetActions();
2566
2568 //AddAction(ActionTakeHybridAttachment);
2569 //AddAction(ActionTakeHybridAttachmentToHands);
2572 }
2573
2574 //================================================================
2575 // DEBUG
2576 //================================================================
2577 protected void DebugCustomState()
2578 {
2579 }
2580
2583 {
2584 return null;
2585 }
2586
2587 override void OnDebugSpawn()
2588 {
2589 FullyBuild();
2590 }
2591
2592 void FullyBuild()
2593 {
2595 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2596
2597 Man p;
2598
2599#ifdef SERVER
2601 GetGame().GetWorld().GetPlayerList(players);
2602 if (players.Count())
2603 p = players[0];
2604#else
2605 p = GetGame().GetPlayer();
2606#endif
2607
2608 foreach (ConstructionPart part : parts)
2609 {
2610 bool excluded = false;
2611 string partName = part.GetPartName();
2612 if (excludes)
2613 {
2614 foreach (string exclude : excludes)
2615 {
2616 if (partName.Contains(exclude))
2617 {
2618 excluded = true;
2619 break;
2620 }
2621 }
2622 }
2623
2624 if (!excluded)
2626 }
2627
2628 GetConstruction().UpdateVisuals();
2629 }
2630}
2631
2632void bsbDebugPrint(string s)
2633{
2634#ifdef BSB_DEBUG
2635 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2636#else
2637 //Print("" + s); // comment/uncomment to hide/see debug logs
2638#endif
2639}
2640void bsbDebugSpam(string s)
2641{
2642#ifdef BSB_DEBUG_SPAM
2643 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2644#else
2645 //Print("" + s); // comment/uncomment to hide/see debug logs
2646#endif
2647}

Referenced by ItemBase::OnSynchronizedClient(), and ItemBase::SetPartsAfterStoreLoad().

◆ ShowZonesHealth()

override bool bsbDebugPrint::ShowZonesHealth ( )
protected

Definition at line 2057 of file BaseBuildingBase.c.

2059{
2060 const string ANIMATION_DEPLOYED = "Deployed";
2061
2062 float m_ConstructionKitHealth; //stored health value for used construction kit
2063
2065
2066 bool m_HasBase;
2067 //variables for synchronization of base building parts (2x31 is the current limit)
2068 int m_SyncParts01; //synchronization for already built parts (31 parts)
2069 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2070 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2071 int m_InteractedPartId; //construction part id that an action was performed on
2072 int m_PerformedActionId; //action id that was performed on a construction part
2073
2074 //Sounds
2075 //build
2076 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2077 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2078 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2079 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2080 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2081 //dismantle
2082 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2083 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2084 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2085 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2086 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2087
2088 protected EffectSound m_Sound;
2089
2093
2094 // Constructor
2095 void BaseBuildingBase()
2096 {
2098
2099 //synchronized variables
2100 RegisterNetSyncVariableInt("m_SyncParts01");
2101 RegisterNetSyncVariableInt("m_SyncParts02");
2102 RegisterNetSyncVariableInt("m_SyncParts03");
2103 RegisterNetSyncVariableInt("m_InteractedPartId");
2104 RegisterNetSyncVariableInt("m_PerformedActionId");
2105 RegisterNetSyncVariableBool("m_HasBase");
2106
2107 //Construction init
2109
2110 if (ConfigIsExisting("hybridAttachments"))
2111 {
2113 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2114 }
2115 if (ConfigIsExisting("mountables"))
2116 {
2118 ConfigGetTextArray("mountables", m_Mountables);
2119 }
2120
2121 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2122 }
2123
2124 override void EEDelete(EntityAI parent)
2125 {
2126 super.EEDelete(parent);
2127
2128 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2130
2131 }
2132
2133 override string GetInvulnerabilityTypeString()
2134 {
2135 return "disableBaseDamage";
2136 }
2137
2138 override bool CanObstruct()
2139 {
2140 return true;
2141 }
2142
2143 override int GetHideIconMask()
2144 {
2145 return EInventoryIconVisibility.HIDE_VICINITY;
2146 }
2147
2148 // --- SYNCHRONIZATION
2150 {
2151 if (GetGame().IsServer())
2152 SetSynchDirty();
2153 }
2154
2155 override void OnVariablesSynchronized()
2156 {
2157 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2158 super.OnVariablesSynchronized();
2159
2160 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2161 }
2162
2163 protected void OnSynchronizedClient()
2164 {
2165 //update parts
2167
2168 //update action on part
2170
2171 //update visuals (client)
2172 UpdateVisuals();
2173 }
2174
2175 //parts synchronization
2177 {
2178 //part_id must starts from index = 1
2179 int offset;
2180 int mask;
2181
2182 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2183 {
2184 offset = part_id - 1;
2185 mask = 1 << offset;
2186
2188 }
2189 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2190 {
2191 offset = (part_id % 32);
2192 mask = 1 << offset;
2193
2195 }
2196 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2197 {
2198 offset = (part_id % 63);
2199 mask = 1 << offset;
2200
2202 }
2203 }
2204
2206 {
2207 //part_id must starts from index = 1
2208 int offset;
2209 int mask;
2210
2211 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2212 {
2213 offset = part_id - 1;
2214 mask = 1 << offset;
2215
2217 }
2218 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2219 {
2220 offset = (part_id % 32);
2221 mask = 1 << offset;
2222
2224 }
2225 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2226 {
2227 offset = (part_id % 63);
2228 mask = 1 << offset;
2229
2231 }
2232 }
2233
2235 {
2236 //part_id must starts from index = 1
2237 int offset;
2238 int mask;
2239
2240 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2241 {
2242 offset = part_id - 1;
2243 mask = 1 << offset;
2244
2245 if ((m_SyncParts01 & mask) > 0)
2246 return true;
2247 }
2248 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2249 {
2250 offset = (part_id % 32);
2251 mask = 1 << offset;
2252
2253 if ((m_SyncParts02 & mask) > 0)
2254 return true;
2255 }
2256 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2257 {
2258 offset = (part_id % 63);
2259 mask = 1 << offset;
2260
2261 if ((m_SyncParts03 & mask) > 0)
2262 return true;
2263 }
2264
2265 return false;
2266 }
2267
2268 protected void RegisterActionForSync(int part_id, int action_id)
2269 {
2272 }
2273
2274 protected void ResetActionSyncData()
2275 {
2276 //reset data
2277 m_InteractedPartId = -1;
2279 }
2280
2281 protected void SetActionFromSyncData()
2282 {
2283 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2284 {
2287
2288 switch (build_action_id)
2289 {
2293 }
2294 }
2295 }
2296 //------
2297
2299 {
2300 string key = part.m_PartName;
2301 bool is_base = part.IsBase();
2303 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2305 {
2306 if (!part.IsBuilt())
2307 {
2308 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2309 GetConstruction().AddToConstructedParts(key);
2310 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2311
2312 if (is_base)
2313 {
2315 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2316 }
2317 }
2318 }
2319 else
2320 {
2321 if (part.IsBuilt())
2322 {
2323 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2324 GetConstruction().RemoveFromConstructedParts(key);
2325 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2326
2327 if (is_base)
2328 {
2330 AddProxyPhysics(ANIMATION_DEPLOYED);
2331 }
2332 }
2333 }
2334
2335 //check slot lock for material attachments
2336 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2337 }
2338
2339 //set construction parts based on synchronized data
2341 {
2344
2345 for (int i = 0; i < construction_parts.Count(); ++i)
2346 {
2347 string key = construction_parts.GetKey(i);
2350 }
2351
2352 //regenerate navmesh
2353 UpdateNavmesh();
2354 }
2355
2357 {
2360
2361 for (int i = 0; i < construction_parts.Count(); ++i)
2362 {
2363 string key = construction_parts.GetKey(i);
2365
2366 if (value.GetId() == id)
2367 return value;
2368 }
2369
2370 return NULL;
2371 }
2372 //
2373
2374 //Base
2375 bool HasBase()
2376 {
2377 return m_HasBase;
2378 }
2379
2380 void SetBaseState(bool has_base)
2381 {
2383 }
2384
2385 override bool IsDeployable()
2386 {
2387 return true;
2388 }
2389
2390 bool IsOpened()
2391 {
2392 return false;
2393 }
2394
2395 //--- CONSTRUCTION KIT
2397 {
2401
2402 return construction_kit;
2403 }
2404
2406 {
2407 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2410 }
2411
2412 protected vector GetKitSpawnPosition()
2413 {
2414 return GetPosition();
2415 }
2416
2417 protected string GetConstructionKitType()
2418 {
2419 return "";
2420 }
2421
2423 {
2425 GetGame().ObjectDelete(construction_kit);
2426 }
2427
2428 //--- CONSTRUCTION
2429 void DestroyConstruction()
2430 {
2431 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2432 GetGame().ObjectDelete(this);
2433 }
2434
2435 // --- EVENTS
2436 override void OnStoreSave(ParamsWriteContext ctx)
2437 {
2438 super.OnStoreSave(ctx);
2439
2440 //sync parts 01
2441 ctx.Write(m_SyncParts01);
2442 ctx.Write(m_SyncParts02);
2443 ctx.Write(m_SyncParts03);
2444
2445 ctx.Write(m_HasBase);
2446 }
2447
2448 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2449 {
2450 if (!super.OnStoreLoad(ctx, version))
2451 return false;
2452
2453 //--- Base building data ---
2454 //Restore synced parts data
2455 if (!ctx.Read(m_SyncParts01))
2456 {
2457 m_SyncParts01 = 0; //set default
2458 return false;
2459 }
2460 if (!ctx.Read(m_SyncParts02))
2461 {
2462 m_SyncParts02 = 0; //set default
2463 return false;
2464 }
2465 if (!ctx.Read(m_SyncParts03))
2466 {
2467 m_SyncParts03 = 0; //set default
2468 return false;
2469 }
2470
2471 //has base
2472 if (!ctx.Read(m_HasBase))
2473 {
2474 m_HasBase = false;
2475 return false;
2476 }
2477 //---
2478
2479 return true;
2480 }
2481
2482 override void AfterStoreLoad()
2483 {
2484 super.AfterStoreLoad();
2485
2488 }
2489
2491 {
2492 //update server data
2494
2495 //set base state
2496 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2497 SetBaseState(construction_part.IsBuilt()) ;
2498
2499 //synchronize after load
2501 }
2502
2503 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2504 {
2506 return;
2507
2508 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2509
2510 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2511 return;
2512
2514 string part_name = zone;
2515 part_name.ToLower();
2516
2518 {
2520
2521 if (construction_part && construction.IsPartConstructed(part_name))
2522 {
2523 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2524 construction.DestroyConnectedParts(part_name);
2525 }
2526
2527 //barbed wire handling (hack-ish)
2528 if (part_name.Contains("barbed"))
2529 {
2530 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2531 if (barbed_wire)
2532 barbed_wire.SetMountedState(false);
2533 }
2534 }
2535 }
2536
2537 override void EEOnAfterLoad()
2538 {
2540 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2541
2542 super.EEOnAfterLoad();
2543 }
2544
2545 override void EEInit()
2546 {
2547 super.EEInit();
2548
2549 // init visuals and physics
2550 InitBaseState();
2551
2552 //debug
2553#ifdef DEVELOPER
2555#endif
2556 }
2557
2558 override void EEItemAttached(EntityAI item, string slot_name)
2559 {
2560 super.EEItemAttached(item, slot_name);
2561
2563 UpdateVisuals();
2565 }
2566
2567 override void EEItemDetached(EntityAI item, string slot_name)
2568 {
2569 super.EEItemDetached(item, slot_name);
2570
2571 UpdateVisuals();
2573 }
2574
2575 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2576 {
2578 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2579
2582 }
2583
2584 //ignore out of reach condition
2585 override bool IgnoreOutOfReachCondition()
2586 {
2587 return true;
2588 }
2589
2590 //CONSTRUCTION EVENTS
2591 //Build
2592 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2593 {
2595
2596 //check base state
2597 if (construtionPart.IsBase())
2598 {
2599 SetBaseState(true);
2600
2601 //spawn kit
2603 }
2604
2605 //register constructed parts for synchronization
2607
2608 //register action that was performed on part
2610
2611 //synchronize
2613
2614 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2615
2616 UpdateNavmesh();
2617
2618 //update visuals
2619 UpdateVisuals();
2620
2621 //reset action sync data
2622 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2623 }
2624
2625 void OnPartBuiltClient(string part_name, int action_id)
2626 {
2627 //play sound
2629 }
2630
2631 //Dismantle
2633 {
2634 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2636
2637 //register constructed parts for synchronization
2639
2640 //register action that was performed on part
2642
2643 //synchronize
2645
2646 // server part of sync, client will be synced from SetPartsFromSyncData
2648
2649 UpdateNavmesh();
2650
2651 //update visuals
2652 UpdateVisuals();
2653
2654 //reset action sync data
2655 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2656
2657 //check base state
2658 if (construtionPart.IsBase())
2659 {
2660 //Destroy construction
2661 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2662 }
2663 }
2664
2666 {
2667 //play sound
2669 }
2670
2671 //Destroy
2673 {
2674 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2676
2677 //register constructed parts for synchronization
2679
2680 //register action that was performed on part
2682
2683 //synchronize
2685
2686 // server part of sync, client will be synced from SetPartsFromSyncData
2688
2689 UpdateNavmesh();
2690
2691 //update visuals
2692 UpdateVisuals();
2693
2694 //reset action sync data
2695 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2696
2697 //check base state
2698 if (construtionPart.IsBase())
2699 {
2700 //Destroy construction
2701 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2702 }
2703 }
2704
2705 void OnPartDestroyedClient(string part_name, int action_id)
2706 {
2707 //play sound
2709 }
2710
2711 // --- UPDATE
2712 void InitBaseState()
2713 {
2714 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2715
2716 InitVisuals();
2717 UpdateNavmesh(); //regenerate navmesh
2718 GetConstruction().InitBaseState();
2719 }
2720
2721 void InitVisuals()
2722 {
2723 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2724 //check base
2725 if (!HasBase())
2726 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2727 else
2728 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2729
2730 GetConstruction().UpdateVisuals();
2731 }
2732
2733 void UpdateVisuals()
2734 {
2736
2738 foreach (string slotName : attachmentSlots)
2740
2741 //check base
2742 if (!HasBase())
2743 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2744 else
2745 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2746
2747 GetConstruction().UpdateVisuals();
2748 }
2749
2751 {
2752 string slotNameMounted = slot_name + "_Mounted";
2753 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2754
2755 if (attachment)
2756 {
2757 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2758 if (barbedWire && barbedWire.IsMounted())
2760 else
2762
2763 if (is_locked)
2764 {
2765 SetAnimationPhase(slotNameMounted, 0);
2766 SetAnimationPhase(slot_name, 1);
2767 }
2768 else
2769 {
2770 SetAnimationPhase(slotNameMounted, 1);
2771 SetAnimationPhase(slot_name, 0);
2772 }
2773 }
2774 else
2775 {
2776 SetAnimationPhase(slotNameMounted, 1);
2777 SetAnimationPhase(slot_name, 1);
2778
2780 }
2781 }
2782
2783 // avoid calling this function on frequent occasions, it's a massive performance hit
2784 void UpdatePhysics()
2785 {
2787 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2788
2791
2793 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2794
2795 foreach (string slotName : attachmentSlots)
2797
2798 //check base
2799 if (!HasBase())
2800 {
2802 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2803
2804 AddProxyPhysics(ANIMATION_DEPLOYED);
2805 }
2806 else
2807 {
2809 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2810
2811 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2812 }
2813
2814 GetConstruction().UpdatePhysics();
2815 UpdateNavmesh();
2816 }
2817
2819 {
2820 //checks for invalid appends; hotfix
2821 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2822 return;
2823 //----------------------------------
2824 string slot_name_mounted = slot_name + "_Mounted";
2825 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2826
2827 //remove proxy physics
2828 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2829 RemoveProxyPhysics(slot_name_mounted);
2830 RemoveProxyPhysics(slot_name);
2831
2832 if (attachment)
2833 {
2834 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2835 if (is_locked)
2836 {
2837 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2838 AddProxyPhysics(slot_name_mounted);
2839 }
2840 else
2841 {
2842 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2843 AddProxyPhysics(slot_name);
2844 }
2845 }
2846 }
2847
2848 protected void UpdateNavmesh()
2849 {
2850 SetAffectPathgraph(true, false);
2851 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2852 }
2853
2854 override bool CanUseConstruction()
2855 {
2856 return true;
2857 }
2858
2859 override bool CanUseConstructionBuild()
2860 {
2861 return true;
2862 }
2863
2865 {
2866 if (attachment)
2867 {
2869 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2870
2871 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2872 }
2873
2874 return false;
2875 }
2876
2877 protected bool IsAttachmentSlotLocked(string slot_name)
2878 {
2879 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2880 }
2881
2882 //--- ATTACHMENT SLOTS
2884 {
2885 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2886 if (GetGame().ConfigIsExisting(config_path))
2887 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2888 }
2889
2891 {
2892 return true;
2893 }
2894
2895 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2896 {
2897 return true;
2898 }
2899
2900 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2901 {
2902 return true;
2903 }
2904
2905 // --- INIT
2906 void ConstructionInit()
2907 {
2908 if (!m_Construction)
2909 m_Construction = new Construction(this);
2910
2911 GetConstruction().Init();
2912 }
2913
2915 {
2916 return m_Construction;
2917 }
2918
2919 //--- INVENTORY/ATTACHMENTS CONDITIONS
2920 //attachments
2922 {
2923 return super.CanReceiveAttachment(attachment, slotId);
2924 }
2925
2927 {
2928 int attachment_count = GetInventory().AttachmentCount();
2929 if (attachment_count > 0)
2930 {
2931 if (HasBase() && attachment_count == 1)
2932 return false;
2933
2934 return true;
2935 }
2936
2937 return false;
2938 }
2939
2940 override bool ShowZonesHealth()
2941 {
2942 return true;
2943 }
2944
2945 //this into/outo parent.Cargo
2946 override bool CanPutInCargo(EntityAI parent)
2947 {
2948 return false;
2949 }
2950
2951 override bool CanRemoveFromCargo(EntityAI parent)
2952 {
2953 return false;
2954 }
2955
2956 //hands
2957 override bool CanPutIntoHands(EntityAI parent)
2958 {
2959 return false;
2960 }
2961
2962 //--- ACTION CONDITIONS
2963 //direction
2964 override bool IsFacingPlayer(PlayerBase player, string selection)
2965 {
2966 return true;
2967 }
2968
2969 override bool IsPlayerInside(PlayerBase player, string selection)
2970 {
2971 return true;
2972 }
2973
2976 {
2977 return false;
2978 }
2979
2980 //camera direction check
2981 bool IsFacingCamera(string selection)
2982 {
2983 return true;
2984 }
2985
2986 //roof check
2988 {
2989 return false;
2990 }
2991
2992 //selection->player distance check
2993 bool HasProperDistance(string selection, PlayerBase player)
2994 {
2995 return true;
2996 }
2997
2998 //folding
3000 {
3001 if (HasBase() || GetInventory().AttachmentCount() > 0)
3002 return false;
3003
3004 return true;
3005 }
3006
3008 {
3011
3012 return item;
3013 }
3014
3015 //Damage triggers (barbed wire)
3016 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3017 {
3018 if (GetGame() && GetGame().IsServer())
3019 {
3020 //destroy area damage if some already exists
3022
3023 //create new area damage
3025 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3026
3027 vector min_max[2];
3028 if (MemoryPointExists(slot_name + "_min"))
3029 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3030 if (MemoryPointExists(slot_name + "_max"))
3031 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3032
3033 //get proper trigger extents (min<max)
3034 vector extents[2];
3035 GetConstruction().GetTriggerExtents(min_max, extents);
3036
3037 //get box center
3038 vector center;
3039 center = GetConstruction().GetBoxCenter(min_max);
3040 center = ModelToWorld(center);
3041
3042 //rotate center if needed
3045
3046 areaDamage.SetExtents(extents[0], extents[1]);
3047 areaDamage.SetAreaPosition(center);
3048 areaDamage.SetAreaOrientation(orientation);
3049 areaDamage.SetLoopInterval(1.0);
3050 areaDamage.SetDeferDuration(0.2);
3051 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3052 areaDamage.SetAmmoName("BarbedWireHit");
3053 areaDamage.Spawn();
3054
3056 }
3057 }
3058
3060 {
3061 if (angle_deg != 0)
3062 {
3063 //orientation
3065
3066 //center
3068 if (MemoryPointExists("rotate_axis"))
3069 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3072 center[0] = r_center_x;
3073 center[2] = r_center_z;
3074 }
3075 }
3076
3077 void DestroyAreaDamage(string slot_name)
3078 {
3079 if (GetGame() && GetGame().IsServer())
3080 {
3083 {
3084 if (areaDamage)
3085 areaDamage.Destroy();
3086
3088 }
3089 }
3090 }
3091
3092 override bool IsIgnoredByConstruction()
3093 {
3094 return true;
3095 }
3096
3097 //================================================================
3098 // SOUNDS
3099 //================================================================
3100 protected void SoundBuildStart(string part_name)
3101 {
3102 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3103 }
3104
3105 protected void SoundDismantleStart(string part_name)
3106 {
3107 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3108 }
3109
3110 protected void SoundDestroyStart(string part_name)
3111 {
3112 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3113 }
3114
3115 protected string GetBuildSoundByMaterial(string part_name)
3116 {
3118
3119 switch (material_type)
3120 {
3121 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3122 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3123 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3124 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3125 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3126 }
3127
3128 return "";
3129 }
3130
3131 protected string GetDismantleSoundByMaterial(string part_name)
3132 {
3134
3135 switch (material_type)
3136 {
3137 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3138 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3139 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3140 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3141 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3142 }
3143
3144 return "";
3145 }
3146
3147 //misc
3149 {
3150 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3151 {
3152 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3154 SetHealth(slot_name, "Health", item.GetHealth());
3155 }
3156 }
3157
3158 override int GetDamageSystemVersionChange()
3159 {
3160 return 111;
3161 }
3162
3163 override void SetActions()
3164 {
3165 super.SetActions();
3166
3168 //AddAction(ActionTakeHybridAttachment);
3169 //AddAction(ActionTakeHybridAttachmentToHands);
3172 }
3173
3174 //================================================================
3175 // DEBUG
3176 //================================================================
3177 protected void DebugCustomState()
3178 {
3179 }
3180
3183 {
3184 return null;
3185 }
3186
3187 override void OnDebugSpawn()
3188 {
3189 FullyBuild();
3190 }
3191
3192 void FullyBuild()
3193 {
3195 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3196
3197 Man p;
3198
3199#ifdef SERVER
3201 GetGame().GetWorld().GetPlayerList(players);
3202 if (players.Count())
3203 p = players[0];
3204#else
3205 p = GetGame().GetPlayer();
3206#endif
3207
3208 foreach (ConstructionPart part : parts)
3209 {
3210 bool excluded = false;
3211 string partName = part.GetPartName();
3212 if (excludes)
3213 {
3214 foreach (string exclude : excludes)
3215 {
3216 if (partName.Contains(exclude))
3217 {
3218 excluded = true;
3219 break;
3220 }
3221 }
3222 }
3223
3224 if (!excluded)
3226 }
3227
3228 GetConstruction().UpdateVisuals();
3229 }
3230}
3231
3232void bsbDebugPrint(string s)
3233{
3234#ifdef BSB_DEBUG
3235 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3236#else
3237 //Print("" + s); // comment/uncomment to hide/see debug logs
3238#endif
3239}
3240void bsbDebugSpam(string s)
3241{
3242#ifdef BSB_DEBUG_SPAM
3243 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3244#else
3245 //Print("" + s); // comment/uncomment to hide/see debug logs
3246#endif
3247}

◆ SoundBuildStart()

void bsbDebugPrint::SoundBuildStart ( string part_name)
protected

Definition at line 2217 of file BaseBuildingBase.c.

2219{
2220 const string ANIMATION_DEPLOYED = "Deployed";
2221
2222 float m_ConstructionKitHealth; //stored health value for used construction kit
2223
2225
2226 bool m_HasBase;
2227 //variables for synchronization of base building parts (2x31 is the current limit)
2228 int m_SyncParts01; //synchronization for already built parts (31 parts)
2229 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2230 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2231 int m_InteractedPartId; //construction part id that an action was performed on
2232 int m_PerformedActionId; //action id that was performed on a construction part
2233
2234 //Sounds
2235 //build
2236 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2237 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2238 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2239 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2240 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2241 //dismantle
2242 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2243 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2244 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2245 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2246 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2247
2248 protected EffectSound m_Sound;
2249
2253
2254 // Constructor
2255 void BaseBuildingBase()
2256 {
2258
2259 //synchronized variables
2260 RegisterNetSyncVariableInt("m_SyncParts01");
2261 RegisterNetSyncVariableInt("m_SyncParts02");
2262 RegisterNetSyncVariableInt("m_SyncParts03");
2263 RegisterNetSyncVariableInt("m_InteractedPartId");
2264 RegisterNetSyncVariableInt("m_PerformedActionId");
2265 RegisterNetSyncVariableBool("m_HasBase");
2266
2267 //Construction init
2269
2270 if (ConfigIsExisting("hybridAttachments"))
2271 {
2273 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2274 }
2275 if (ConfigIsExisting("mountables"))
2276 {
2278 ConfigGetTextArray("mountables", m_Mountables);
2279 }
2280
2281 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2282 }
2283
2284 override void EEDelete(EntityAI parent)
2285 {
2286 super.EEDelete(parent);
2287
2288 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2290
2291 }
2292
2293 override string GetInvulnerabilityTypeString()
2294 {
2295 return "disableBaseDamage";
2296 }
2297
2298 override bool CanObstruct()
2299 {
2300 return true;
2301 }
2302
2303 override int GetHideIconMask()
2304 {
2305 return EInventoryIconVisibility.HIDE_VICINITY;
2306 }
2307
2308 // --- SYNCHRONIZATION
2310 {
2311 if (GetGame().IsServer())
2312 SetSynchDirty();
2313 }
2314
2315 override void OnVariablesSynchronized()
2316 {
2317 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2318 super.OnVariablesSynchronized();
2319
2320 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2321 }
2322
2323 protected void OnSynchronizedClient()
2324 {
2325 //update parts
2327
2328 //update action on part
2330
2331 //update visuals (client)
2332 UpdateVisuals();
2333 }
2334
2335 //parts synchronization
2337 {
2338 //part_id must starts from index = 1
2339 int offset;
2340 int mask;
2341
2342 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2343 {
2344 offset = part_id - 1;
2345 mask = 1 << offset;
2346
2348 }
2349 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2350 {
2351 offset = (part_id % 32);
2352 mask = 1 << offset;
2353
2355 }
2356 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2357 {
2358 offset = (part_id % 63);
2359 mask = 1 << offset;
2360
2362 }
2363 }
2364
2366 {
2367 //part_id must starts from index = 1
2368 int offset;
2369 int mask;
2370
2371 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2372 {
2373 offset = part_id - 1;
2374 mask = 1 << offset;
2375
2377 }
2378 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2379 {
2380 offset = (part_id % 32);
2381 mask = 1 << offset;
2382
2384 }
2385 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2386 {
2387 offset = (part_id % 63);
2388 mask = 1 << offset;
2389
2391 }
2392 }
2393
2395 {
2396 //part_id must starts from index = 1
2397 int offset;
2398 int mask;
2399
2400 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2401 {
2402 offset = part_id - 1;
2403 mask = 1 << offset;
2404
2405 if ((m_SyncParts01 & mask) > 0)
2406 return true;
2407 }
2408 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2409 {
2410 offset = (part_id % 32);
2411 mask = 1 << offset;
2412
2413 if ((m_SyncParts02 & mask) > 0)
2414 return true;
2415 }
2416 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2417 {
2418 offset = (part_id % 63);
2419 mask = 1 << offset;
2420
2421 if ((m_SyncParts03 & mask) > 0)
2422 return true;
2423 }
2424
2425 return false;
2426 }
2427
2428 protected void RegisterActionForSync(int part_id, int action_id)
2429 {
2432 }
2433
2434 protected void ResetActionSyncData()
2435 {
2436 //reset data
2437 m_InteractedPartId = -1;
2439 }
2440
2441 protected void SetActionFromSyncData()
2442 {
2443 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2444 {
2447
2448 switch (build_action_id)
2449 {
2453 }
2454 }
2455 }
2456 //------
2457
2459 {
2460 string key = part.m_PartName;
2461 bool is_base = part.IsBase();
2463 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2465 {
2466 if (!part.IsBuilt())
2467 {
2468 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2469 GetConstruction().AddToConstructedParts(key);
2470 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2471
2472 if (is_base)
2473 {
2475 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2476 }
2477 }
2478 }
2479 else
2480 {
2481 if (part.IsBuilt())
2482 {
2483 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2484 GetConstruction().RemoveFromConstructedParts(key);
2485 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2486
2487 if (is_base)
2488 {
2490 AddProxyPhysics(ANIMATION_DEPLOYED);
2491 }
2492 }
2493 }
2494
2495 //check slot lock for material attachments
2496 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2497 }
2498
2499 //set construction parts based on synchronized data
2501 {
2504
2505 for (int i = 0; i < construction_parts.Count(); ++i)
2506 {
2507 string key = construction_parts.GetKey(i);
2510 }
2511
2512 //regenerate navmesh
2513 UpdateNavmesh();
2514 }
2515
2517 {
2520
2521 for (int i = 0; i < construction_parts.Count(); ++i)
2522 {
2523 string key = construction_parts.GetKey(i);
2525
2526 if (value.GetId() == id)
2527 return value;
2528 }
2529
2530 return NULL;
2531 }
2532 //
2533
2534 //Base
2535 bool HasBase()
2536 {
2537 return m_HasBase;
2538 }
2539
2540 void SetBaseState(bool has_base)
2541 {
2543 }
2544
2545 override bool IsDeployable()
2546 {
2547 return true;
2548 }
2549
2550 bool IsOpened()
2551 {
2552 return false;
2553 }
2554
2555 //--- CONSTRUCTION KIT
2557 {
2561
2562 return construction_kit;
2563 }
2564
2566 {
2567 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2570 }
2571
2572 protected vector GetKitSpawnPosition()
2573 {
2574 return GetPosition();
2575 }
2576
2577 protected string GetConstructionKitType()
2578 {
2579 return "";
2580 }
2581
2583 {
2585 GetGame().ObjectDelete(construction_kit);
2586 }
2587
2588 //--- CONSTRUCTION
2589 void DestroyConstruction()
2590 {
2591 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2592 GetGame().ObjectDelete(this);
2593 }
2594
2595 // --- EVENTS
2596 override void OnStoreSave(ParamsWriteContext ctx)
2597 {
2598 super.OnStoreSave(ctx);
2599
2600 //sync parts 01
2601 ctx.Write(m_SyncParts01);
2602 ctx.Write(m_SyncParts02);
2603 ctx.Write(m_SyncParts03);
2604
2605 ctx.Write(m_HasBase);
2606 }
2607
2608 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2609 {
2610 if (!super.OnStoreLoad(ctx, version))
2611 return false;
2612
2613 //--- Base building data ---
2614 //Restore synced parts data
2615 if (!ctx.Read(m_SyncParts01))
2616 {
2617 m_SyncParts01 = 0; //set default
2618 return false;
2619 }
2620 if (!ctx.Read(m_SyncParts02))
2621 {
2622 m_SyncParts02 = 0; //set default
2623 return false;
2624 }
2625 if (!ctx.Read(m_SyncParts03))
2626 {
2627 m_SyncParts03 = 0; //set default
2628 return false;
2629 }
2630
2631 //has base
2632 if (!ctx.Read(m_HasBase))
2633 {
2634 m_HasBase = false;
2635 return false;
2636 }
2637 //---
2638
2639 return true;
2640 }
2641
2642 override void AfterStoreLoad()
2643 {
2644 super.AfterStoreLoad();
2645
2648 }
2649
2651 {
2652 //update server data
2654
2655 //set base state
2656 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2657 SetBaseState(construction_part.IsBuilt()) ;
2658
2659 //synchronize after load
2661 }
2662
2663 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2664 {
2666 return;
2667
2668 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2669
2670 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2671 return;
2672
2674 string part_name = zone;
2675 part_name.ToLower();
2676
2678 {
2680
2681 if (construction_part && construction.IsPartConstructed(part_name))
2682 {
2683 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2684 construction.DestroyConnectedParts(part_name);
2685 }
2686
2687 //barbed wire handling (hack-ish)
2688 if (part_name.Contains("barbed"))
2689 {
2690 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2691 if (barbed_wire)
2692 barbed_wire.SetMountedState(false);
2693 }
2694 }
2695 }
2696
2697 override void EEOnAfterLoad()
2698 {
2700 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2701
2702 super.EEOnAfterLoad();
2703 }
2704
2705 override void EEInit()
2706 {
2707 super.EEInit();
2708
2709 // init visuals and physics
2710 InitBaseState();
2711
2712 //debug
2713#ifdef DEVELOPER
2715#endif
2716 }
2717
2718 override void EEItemAttached(EntityAI item, string slot_name)
2719 {
2720 super.EEItemAttached(item, slot_name);
2721
2723 UpdateVisuals();
2725 }
2726
2727 override void EEItemDetached(EntityAI item, string slot_name)
2728 {
2729 super.EEItemDetached(item, slot_name);
2730
2731 UpdateVisuals();
2733 }
2734
2735 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2736 {
2738 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2739
2742 }
2743
2744 //ignore out of reach condition
2745 override bool IgnoreOutOfReachCondition()
2746 {
2747 return true;
2748 }
2749
2750 //CONSTRUCTION EVENTS
2751 //Build
2752 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2753 {
2755
2756 //check base state
2757 if (construtionPart.IsBase())
2758 {
2759 SetBaseState(true);
2760
2761 //spawn kit
2763 }
2764
2765 //register constructed parts for synchronization
2767
2768 //register action that was performed on part
2770
2771 //synchronize
2773
2774 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2775
2776 UpdateNavmesh();
2777
2778 //update visuals
2779 UpdateVisuals();
2780
2781 //reset action sync data
2782 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2783 }
2784
2785 void OnPartBuiltClient(string part_name, int action_id)
2786 {
2787 //play sound
2789 }
2790
2791 //Dismantle
2793 {
2794 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2796
2797 //register constructed parts for synchronization
2799
2800 //register action that was performed on part
2802
2803 //synchronize
2805
2806 // server part of sync, client will be synced from SetPartsFromSyncData
2808
2809 UpdateNavmesh();
2810
2811 //update visuals
2812 UpdateVisuals();
2813
2814 //reset action sync data
2815 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2816
2817 //check base state
2818 if (construtionPart.IsBase())
2819 {
2820 //Destroy construction
2821 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2822 }
2823 }
2824
2826 {
2827 //play sound
2829 }
2830
2831 //Destroy
2833 {
2834 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2836
2837 //register constructed parts for synchronization
2839
2840 //register action that was performed on part
2842
2843 //synchronize
2845
2846 // server part of sync, client will be synced from SetPartsFromSyncData
2848
2849 UpdateNavmesh();
2850
2851 //update visuals
2852 UpdateVisuals();
2853
2854 //reset action sync data
2855 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2856
2857 //check base state
2858 if (construtionPart.IsBase())
2859 {
2860 //Destroy construction
2861 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2862 }
2863 }
2864
2865 void OnPartDestroyedClient(string part_name, int action_id)
2866 {
2867 //play sound
2869 }
2870
2871 // --- UPDATE
2872 void InitBaseState()
2873 {
2874 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2875
2876 InitVisuals();
2877 UpdateNavmesh(); //regenerate navmesh
2878 GetConstruction().InitBaseState();
2879 }
2880
2881 void InitVisuals()
2882 {
2883 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2884 //check base
2885 if (!HasBase())
2886 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2887 else
2888 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2889
2890 GetConstruction().UpdateVisuals();
2891 }
2892
2893 void UpdateVisuals()
2894 {
2896
2898 foreach (string slotName : attachmentSlots)
2900
2901 //check base
2902 if (!HasBase())
2903 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2904 else
2905 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2906
2907 GetConstruction().UpdateVisuals();
2908 }
2909
2911 {
2912 string slotNameMounted = slot_name + "_Mounted";
2913 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2914
2915 if (attachment)
2916 {
2917 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2918 if (barbedWire && barbedWire.IsMounted())
2920 else
2922
2923 if (is_locked)
2924 {
2925 SetAnimationPhase(slotNameMounted, 0);
2926 SetAnimationPhase(slot_name, 1);
2927 }
2928 else
2929 {
2930 SetAnimationPhase(slotNameMounted, 1);
2931 SetAnimationPhase(slot_name, 0);
2932 }
2933 }
2934 else
2935 {
2936 SetAnimationPhase(slotNameMounted, 1);
2937 SetAnimationPhase(slot_name, 1);
2938
2940 }
2941 }
2942
2943 // avoid calling this function on frequent occasions, it's a massive performance hit
2944 void UpdatePhysics()
2945 {
2947 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2948
2951
2953 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2954
2955 foreach (string slotName : attachmentSlots)
2957
2958 //check base
2959 if (!HasBase())
2960 {
2962 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2963
2964 AddProxyPhysics(ANIMATION_DEPLOYED);
2965 }
2966 else
2967 {
2969 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2970
2971 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2972 }
2973
2974 GetConstruction().UpdatePhysics();
2975 UpdateNavmesh();
2976 }
2977
2979 {
2980 //checks for invalid appends; hotfix
2981 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2982 return;
2983 //----------------------------------
2984 string slot_name_mounted = slot_name + "_Mounted";
2985 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2986
2987 //remove proxy physics
2988 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2989 RemoveProxyPhysics(slot_name_mounted);
2990 RemoveProxyPhysics(slot_name);
2991
2992 if (attachment)
2993 {
2994 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2995 if (is_locked)
2996 {
2997 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2998 AddProxyPhysics(slot_name_mounted);
2999 }
3000 else
3001 {
3002 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
3003 AddProxyPhysics(slot_name);
3004 }
3005 }
3006 }
3007
3008 protected void UpdateNavmesh()
3009 {
3010 SetAffectPathgraph(true, false);
3011 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
3012 }
3013
3014 override bool CanUseConstruction()
3015 {
3016 return true;
3017 }
3018
3019 override bool CanUseConstructionBuild()
3020 {
3021 return true;
3022 }
3023
3025 {
3026 if (attachment)
3027 {
3029 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
3030
3031 return GetInventory().GetSlotLock(inventory_location.GetSlot());
3032 }
3033
3034 return false;
3035 }
3036
3037 protected bool IsAttachmentSlotLocked(string slot_name)
3038 {
3039 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
3040 }
3041
3042 //--- ATTACHMENT SLOTS
3044 {
3045 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
3046 if (GetGame().ConfigIsExisting(config_path))
3047 GetGame().ConfigGetTextArray(config_path, attachment_slots);
3048 }
3049
3051 {
3052 return true;
3053 }
3054
3055 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
3056 {
3057 return true;
3058 }
3059
3060 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
3061 {
3062 return true;
3063 }
3064
3065 // --- INIT
3066 void ConstructionInit()
3067 {
3068 if (!m_Construction)
3069 m_Construction = new Construction(this);
3070
3071 GetConstruction().Init();
3072 }
3073
3075 {
3076 return m_Construction;
3077 }
3078
3079 //--- INVENTORY/ATTACHMENTS CONDITIONS
3080 //attachments
3082 {
3083 return super.CanReceiveAttachment(attachment, slotId);
3084 }
3085
3087 {
3088 int attachment_count = GetInventory().AttachmentCount();
3089 if (attachment_count > 0)
3090 {
3091 if (HasBase() && attachment_count == 1)
3092 return false;
3093
3094 return true;
3095 }
3096
3097 return false;
3098 }
3099
3100 override bool ShowZonesHealth()
3101 {
3102 return true;
3103 }
3104
3105 //this into/outo parent.Cargo
3106 override bool CanPutInCargo(EntityAI parent)
3107 {
3108 return false;
3109 }
3110
3111 override bool CanRemoveFromCargo(EntityAI parent)
3112 {
3113 return false;
3114 }
3115
3116 //hands
3117 override bool CanPutIntoHands(EntityAI parent)
3118 {
3119 return false;
3120 }
3121
3122 //--- ACTION CONDITIONS
3123 //direction
3124 override bool IsFacingPlayer(PlayerBase player, string selection)
3125 {
3126 return true;
3127 }
3128
3129 override bool IsPlayerInside(PlayerBase player, string selection)
3130 {
3131 return true;
3132 }
3133
3136 {
3137 return false;
3138 }
3139
3140 //camera direction check
3141 bool IsFacingCamera(string selection)
3142 {
3143 return true;
3144 }
3145
3146 //roof check
3148 {
3149 return false;
3150 }
3151
3152 //selection->player distance check
3153 bool HasProperDistance(string selection, PlayerBase player)
3154 {
3155 return true;
3156 }
3157
3158 //folding
3160 {
3161 if (HasBase() || GetInventory().AttachmentCount() > 0)
3162 return false;
3163
3164 return true;
3165 }
3166
3168 {
3171
3172 return item;
3173 }
3174
3175 //Damage triggers (barbed wire)
3176 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3177 {
3178 if (GetGame() && GetGame().IsServer())
3179 {
3180 //destroy area damage if some already exists
3182
3183 //create new area damage
3185 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3186
3187 vector min_max[2];
3188 if (MemoryPointExists(slot_name + "_min"))
3189 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3190 if (MemoryPointExists(slot_name + "_max"))
3191 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3192
3193 //get proper trigger extents (min<max)
3194 vector extents[2];
3195 GetConstruction().GetTriggerExtents(min_max, extents);
3196
3197 //get box center
3198 vector center;
3199 center = GetConstruction().GetBoxCenter(min_max);
3200 center = ModelToWorld(center);
3201
3202 //rotate center if needed
3205
3206 areaDamage.SetExtents(extents[0], extents[1]);
3207 areaDamage.SetAreaPosition(center);
3208 areaDamage.SetAreaOrientation(orientation);
3209 areaDamage.SetLoopInterval(1.0);
3210 areaDamage.SetDeferDuration(0.2);
3211 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3212 areaDamage.SetAmmoName("BarbedWireHit");
3213 areaDamage.Spawn();
3214
3216 }
3217 }
3218
3220 {
3221 if (angle_deg != 0)
3222 {
3223 //orientation
3225
3226 //center
3228 if (MemoryPointExists("rotate_axis"))
3229 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3232 center[0] = r_center_x;
3233 center[2] = r_center_z;
3234 }
3235 }
3236
3237 void DestroyAreaDamage(string slot_name)
3238 {
3239 if (GetGame() && GetGame().IsServer())
3240 {
3243 {
3244 if (areaDamage)
3245 areaDamage.Destroy();
3246
3248 }
3249 }
3250 }
3251
3252 override bool IsIgnoredByConstruction()
3253 {
3254 return true;
3255 }
3256
3257 //================================================================
3258 // SOUNDS
3259 //================================================================
3260 protected void SoundBuildStart(string part_name)
3261 {
3262 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3263 }
3264
3265 protected void SoundDismantleStart(string part_name)
3266 {
3267 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3268 }
3269
3270 protected void SoundDestroyStart(string part_name)
3271 {
3272 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3273 }
3274
3275 protected string GetBuildSoundByMaterial(string part_name)
3276 {
3278
3279 switch (material_type)
3280 {
3281 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3282 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3283 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3284 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3285 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3286 }
3287
3288 return "";
3289 }
3290
3291 protected string GetDismantleSoundByMaterial(string part_name)
3292 {
3294
3295 switch (material_type)
3296 {
3297 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3298 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3299 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3300 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3301 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3302 }
3303
3304 return "";
3305 }
3306
3307 //misc
3309 {
3310 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3311 {
3312 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3314 SetHealth(slot_name, "Health", item.GetHealth());
3315 }
3316 }
3317
3318 override int GetDamageSystemVersionChange()
3319 {
3320 return 111;
3321 }
3322
3323 override void SetActions()
3324 {
3325 super.SetActions();
3326
3328 //AddAction(ActionTakeHybridAttachment);
3329 //AddAction(ActionTakeHybridAttachmentToHands);
3332 }
3333
3334 //================================================================
3335 // DEBUG
3336 //================================================================
3337 protected void DebugCustomState()
3338 {
3339 }
3340
3343 {
3344 return null;
3345 }
3346
3347 override void OnDebugSpawn()
3348 {
3349 FullyBuild();
3350 }
3351
3352 void FullyBuild()
3353 {
3355 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3356
3357 Man p;
3358
3359#ifdef SERVER
3361 GetGame().GetWorld().GetPlayerList(players);
3362 if (players.Count())
3363 p = players[0];
3364#else
3365 p = GetGame().GetPlayer();
3366#endif
3367
3368 foreach (ConstructionPart part : parts)
3369 {
3370 bool excluded = false;
3371 string partName = part.GetPartName();
3372 if (excludes)
3373 {
3374 foreach (string exclude : excludes)
3375 {
3376 if (partName.Contains(exclude))
3377 {
3378 excluded = true;
3379 break;
3380 }
3381 }
3382 }
3383
3384 if (!excluded)
3386 }
3387
3388 GetConstruction().UpdateVisuals();
3389 }
3390}
3391
3392void bsbDebugPrint(string s)
3393{
3394#ifdef BSB_DEBUG
3395 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3396#else
3397 //Print("" + s); // comment/uncomment to hide/see debug logs
3398#endif
3399}
3400void bsbDebugSpam(string s)
3401{
3402#ifdef BSB_DEBUG_SPAM
3403 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3404#else
3405 //Print("" + s); // comment/uncomment to hide/see debug logs
3406#endif
3407}

Referenced by ItemBase::OnPartBuiltClient().

◆ SoundDestroyStart()

void bsbDebugPrint::SoundDestroyStart ( string part_name)
protected

Definition at line 2227 of file BaseBuildingBase.c.

2229{
2230 const string ANIMATION_DEPLOYED = "Deployed";
2231
2232 float m_ConstructionKitHealth; //stored health value for used construction kit
2233
2235
2236 bool m_HasBase;
2237 //variables for synchronization of base building parts (2x31 is the current limit)
2238 int m_SyncParts01; //synchronization for already built parts (31 parts)
2239 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2240 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2241 int m_InteractedPartId; //construction part id that an action was performed on
2242 int m_PerformedActionId; //action id that was performed on a construction part
2243
2244 //Sounds
2245 //build
2246 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2247 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2248 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2249 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2250 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2251 //dismantle
2252 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2253 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2254 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2255 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2256 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2257
2258 protected EffectSound m_Sound;
2259
2263
2264 // Constructor
2265 void BaseBuildingBase()
2266 {
2268
2269 //synchronized variables
2270 RegisterNetSyncVariableInt("m_SyncParts01");
2271 RegisterNetSyncVariableInt("m_SyncParts02");
2272 RegisterNetSyncVariableInt("m_SyncParts03");
2273 RegisterNetSyncVariableInt("m_InteractedPartId");
2274 RegisterNetSyncVariableInt("m_PerformedActionId");
2275 RegisterNetSyncVariableBool("m_HasBase");
2276
2277 //Construction init
2279
2280 if (ConfigIsExisting("hybridAttachments"))
2281 {
2283 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2284 }
2285 if (ConfigIsExisting("mountables"))
2286 {
2288 ConfigGetTextArray("mountables", m_Mountables);
2289 }
2290
2291 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2292 }
2293
2294 override void EEDelete(EntityAI parent)
2295 {
2296 super.EEDelete(parent);
2297
2298 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2300
2301 }
2302
2303 override string GetInvulnerabilityTypeString()
2304 {
2305 return "disableBaseDamage";
2306 }
2307
2308 override bool CanObstruct()
2309 {
2310 return true;
2311 }
2312
2313 override int GetHideIconMask()
2314 {
2315 return EInventoryIconVisibility.HIDE_VICINITY;
2316 }
2317
2318 // --- SYNCHRONIZATION
2320 {
2321 if (GetGame().IsServer())
2322 SetSynchDirty();
2323 }
2324
2325 override void OnVariablesSynchronized()
2326 {
2327 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2328 super.OnVariablesSynchronized();
2329
2330 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2331 }
2332
2333 protected void OnSynchronizedClient()
2334 {
2335 //update parts
2337
2338 //update action on part
2340
2341 //update visuals (client)
2342 UpdateVisuals();
2343 }
2344
2345 //parts synchronization
2347 {
2348 //part_id must starts from index = 1
2349 int offset;
2350 int mask;
2351
2352 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2353 {
2354 offset = part_id - 1;
2355 mask = 1 << offset;
2356
2358 }
2359 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2360 {
2361 offset = (part_id % 32);
2362 mask = 1 << offset;
2363
2365 }
2366 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2367 {
2368 offset = (part_id % 63);
2369 mask = 1 << offset;
2370
2372 }
2373 }
2374
2376 {
2377 //part_id must starts from index = 1
2378 int offset;
2379 int mask;
2380
2381 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2382 {
2383 offset = part_id - 1;
2384 mask = 1 << offset;
2385
2387 }
2388 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2389 {
2390 offset = (part_id % 32);
2391 mask = 1 << offset;
2392
2394 }
2395 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2396 {
2397 offset = (part_id % 63);
2398 mask = 1 << offset;
2399
2401 }
2402 }
2403
2405 {
2406 //part_id must starts from index = 1
2407 int offset;
2408 int mask;
2409
2410 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2411 {
2412 offset = part_id - 1;
2413 mask = 1 << offset;
2414
2415 if ((m_SyncParts01 & mask) > 0)
2416 return true;
2417 }
2418 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2419 {
2420 offset = (part_id % 32);
2421 mask = 1 << offset;
2422
2423 if ((m_SyncParts02 & mask) > 0)
2424 return true;
2425 }
2426 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2427 {
2428 offset = (part_id % 63);
2429 mask = 1 << offset;
2430
2431 if ((m_SyncParts03 & mask) > 0)
2432 return true;
2433 }
2434
2435 return false;
2436 }
2437
2438 protected void RegisterActionForSync(int part_id, int action_id)
2439 {
2442 }
2443
2444 protected void ResetActionSyncData()
2445 {
2446 //reset data
2447 m_InteractedPartId = -1;
2449 }
2450
2451 protected void SetActionFromSyncData()
2452 {
2453 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2454 {
2457
2458 switch (build_action_id)
2459 {
2463 }
2464 }
2465 }
2466 //------
2467
2469 {
2470 string key = part.m_PartName;
2471 bool is_base = part.IsBase();
2473 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2475 {
2476 if (!part.IsBuilt())
2477 {
2478 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2479 GetConstruction().AddToConstructedParts(key);
2480 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2481
2482 if (is_base)
2483 {
2485 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2486 }
2487 }
2488 }
2489 else
2490 {
2491 if (part.IsBuilt())
2492 {
2493 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2494 GetConstruction().RemoveFromConstructedParts(key);
2495 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2496
2497 if (is_base)
2498 {
2500 AddProxyPhysics(ANIMATION_DEPLOYED);
2501 }
2502 }
2503 }
2504
2505 //check slot lock for material attachments
2506 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2507 }
2508
2509 //set construction parts based on synchronized data
2511 {
2514
2515 for (int i = 0; i < construction_parts.Count(); ++i)
2516 {
2517 string key = construction_parts.GetKey(i);
2520 }
2521
2522 //regenerate navmesh
2523 UpdateNavmesh();
2524 }
2525
2527 {
2530
2531 for (int i = 0; i < construction_parts.Count(); ++i)
2532 {
2533 string key = construction_parts.GetKey(i);
2535
2536 if (value.GetId() == id)
2537 return value;
2538 }
2539
2540 return NULL;
2541 }
2542 //
2543
2544 //Base
2545 bool HasBase()
2546 {
2547 return m_HasBase;
2548 }
2549
2550 void SetBaseState(bool has_base)
2551 {
2553 }
2554
2555 override bool IsDeployable()
2556 {
2557 return true;
2558 }
2559
2560 bool IsOpened()
2561 {
2562 return false;
2563 }
2564
2565 //--- CONSTRUCTION KIT
2567 {
2571
2572 return construction_kit;
2573 }
2574
2576 {
2577 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2580 }
2581
2582 protected vector GetKitSpawnPosition()
2583 {
2584 return GetPosition();
2585 }
2586
2587 protected string GetConstructionKitType()
2588 {
2589 return "";
2590 }
2591
2593 {
2595 GetGame().ObjectDelete(construction_kit);
2596 }
2597
2598 //--- CONSTRUCTION
2599 void DestroyConstruction()
2600 {
2601 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2602 GetGame().ObjectDelete(this);
2603 }
2604
2605 // --- EVENTS
2606 override void OnStoreSave(ParamsWriteContext ctx)
2607 {
2608 super.OnStoreSave(ctx);
2609
2610 //sync parts 01
2611 ctx.Write(m_SyncParts01);
2612 ctx.Write(m_SyncParts02);
2613 ctx.Write(m_SyncParts03);
2614
2615 ctx.Write(m_HasBase);
2616 }
2617
2618 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2619 {
2620 if (!super.OnStoreLoad(ctx, version))
2621 return false;
2622
2623 //--- Base building data ---
2624 //Restore synced parts data
2625 if (!ctx.Read(m_SyncParts01))
2626 {
2627 m_SyncParts01 = 0; //set default
2628 return false;
2629 }
2630 if (!ctx.Read(m_SyncParts02))
2631 {
2632 m_SyncParts02 = 0; //set default
2633 return false;
2634 }
2635 if (!ctx.Read(m_SyncParts03))
2636 {
2637 m_SyncParts03 = 0; //set default
2638 return false;
2639 }
2640
2641 //has base
2642 if (!ctx.Read(m_HasBase))
2643 {
2644 m_HasBase = false;
2645 return false;
2646 }
2647 //---
2648
2649 return true;
2650 }
2651
2652 override void AfterStoreLoad()
2653 {
2654 super.AfterStoreLoad();
2655
2658 }
2659
2661 {
2662 //update server data
2664
2665 //set base state
2666 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2667 SetBaseState(construction_part.IsBuilt()) ;
2668
2669 //synchronize after load
2671 }
2672
2673 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2674 {
2676 return;
2677
2678 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2679
2680 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2681 return;
2682
2684 string part_name = zone;
2685 part_name.ToLower();
2686
2688 {
2690
2691 if (construction_part && construction.IsPartConstructed(part_name))
2692 {
2693 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2694 construction.DestroyConnectedParts(part_name);
2695 }
2696
2697 //barbed wire handling (hack-ish)
2698 if (part_name.Contains("barbed"))
2699 {
2700 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2701 if (barbed_wire)
2702 barbed_wire.SetMountedState(false);
2703 }
2704 }
2705 }
2706
2707 override void EEOnAfterLoad()
2708 {
2710 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2711
2712 super.EEOnAfterLoad();
2713 }
2714
2715 override void EEInit()
2716 {
2717 super.EEInit();
2718
2719 // init visuals and physics
2720 InitBaseState();
2721
2722 //debug
2723#ifdef DEVELOPER
2725#endif
2726 }
2727
2728 override void EEItemAttached(EntityAI item, string slot_name)
2729 {
2730 super.EEItemAttached(item, slot_name);
2731
2733 UpdateVisuals();
2735 }
2736
2737 override void EEItemDetached(EntityAI item, string slot_name)
2738 {
2739 super.EEItemDetached(item, slot_name);
2740
2741 UpdateVisuals();
2743 }
2744
2745 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2746 {
2748 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2749
2752 }
2753
2754 //ignore out of reach condition
2755 override bool IgnoreOutOfReachCondition()
2756 {
2757 return true;
2758 }
2759
2760 //CONSTRUCTION EVENTS
2761 //Build
2762 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2763 {
2765
2766 //check base state
2767 if (construtionPart.IsBase())
2768 {
2769 SetBaseState(true);
2770
2771 //spawn kit
2773 }
2774
2775 //register constructed parts for synchronization
2777
2778 //register action that was performed on part
2780
2781 //synchronize
2783
2784 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2785
2786 UpdateNavmesh();
2787
2788 //update visuals
2789 UpdateVisuals();
2790
2791 //reset action sync data
2792 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2793 }
2794
2795 void OnPartBuiltClient(string part_name, int action_id)
2796 {
2797 //play sound
2799 }
2800
2801 //Dismantle
2803 {
2804 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2806
2807 //register constructed parts for synchronization
2809
2810 //register action that was performed on part
2812
2813 //synchronize
2815
2816 // server part of sync, client will be synced from SetPartsFromSyncData
2818
2819 UpdateNavmesh();
2820
2821 //update visuals
2822 UpdateVisuals();
2823
2824 //reset action sync data
2825 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2826
2827 //check base state
2828 if (construtionPart.IsBase())
2829 {
2830 //Destroy construction
2831 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2832 }
2833 }
2834
2836 {
2837 //play sound
2839 }
2840
2841 //Destroy
2843 {
2844 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2846
2847 //register constructed parts for synchronization
2849
2850 //register action that was performed on part
2852
2853 //synchronize
2855
2856 // server part of sync, client will be synced from SetPartsFromSyncData
2858
2859 UpdateNavmesh();
2860
2861 //update visuals
2862 UpdateVisuals();
2863
2864 //reset action sync data
2865 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2866
2867 //check base state
2868 if (construtionPart.IsBase())
2869 {
2870 //Destroy construction
2871 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2872 }
2873 }
2874
2875 void OnPartDestroyedClient(string part_name, int action_id)
2876 {
2877 //play sound
2879 }
2880
2881 // --- UPDATE
2882 void InitBaseState()
2883 {
2884 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2885
2886 InitVisuals();
2887 UpdateNavmesh(); //regenerate navmesh
2888 GetConstruction().InitBaseState();
2889 }
2890
2891 void InitVisuals()
2892 {
2893 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2894 //check base
2895 if (!HasBase())
2896 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2897 else
2898 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2899
2900 GetConstruction().UpdateVisuals();
2901 }
2902
2903 void UpdateVisuals()
2904 {
2906
2908 foreach (string slotName : attachmentSlots)
2910
2911 //check base
2912 if (!HasBase())
2913 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2914 else
2915 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2916
2917 GetConstruction().UpdateVisuals();
2918 }
2919
2921 {
2922 string slotNameMounted = slot_name + "_Mounted";
2923 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2924
2925 if (attachment)
2926 {
2927 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2928 if (barbedWire && barbedWire.IsMounted())
2930 else
2932
2933 if (is_locked)
2934 {
2935 SetAnimationPhase(slotNameMounted, 0);
2936 SetAnimationPhase(slot_name, 1);
2937 }
2938 else
2939 {
2940 SetAnimationPhase(slotNameMounted, 1);
2941 SetAnimationPhase(slot_name, 0);
2942 }
2943 }
2944 else
2945 {
2946 SetAnimationPhase(slotNameMounted, 1);
2947 SetAnimationPhase(slot_name, 1);
2948
2950 }
2951 }
2952
2953 // avoid calling this function on frequent occasions, it's a massive performance hit
2954 void UpdatePhysics()
2955 {
2957 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2958
2961
2963 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2964
2965 foreach (string slotName : attachmentSlots)
2967
2968 //check base
2969 if (!HasBase())
2970 {
2972 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2973
2974 AddProxyPhysics(ANIMATION_DEPLOYED);
2975 }
2976 else
2977 {
2979 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2980
2981 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2982 }
2983
2984 GetConstruction().UpdatePhysics();
2985 UpdateNavmesh();
2986 }
2987
2989 {
2990 //checks for invalid appends; hotfix
2991 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2992 return;
2993 //----------------------------------
2994 string slot_name_mounted = slot_name + "_Mounted";
2995 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2996
2997 //remove proxy physics
2998 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2999 RemoveProxyPhysics(slot_name_mounted);
3000 RemoveProxyPhysics(slot_name);
3001
3002 if (attachment)
3003 {
3004 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
3005 if (is_locked)
3006 {
3007 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
3008 AddProxyPhysics(slot_name_mounted);
3009 }
3010 else
3011 {
3012 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
3013 AddProxyPhysics(slot_name);
3014 }
3015 }
3016 }
3017
3018 protected void UpdateNavmesh()
3019 {
3020 SetAffectPathgraph(true, false);
3021 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
3022 }
3023
3024 override bool CanUseConstruction()
3025 {
3026 return true;
3027 }
3028
3029 override bool CanUseConstructionBuild()
3030 {
3031 return true;
3032 }
3033
3035 {
3036 if (attachment)
3037 {
3039 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
3040
3041 return GetInventory().GetSlotLock(inventory_location.GetSlot());
3042 }
3043
3044 return false;
3045 }
3046
3047 protected bool IsAttachmentSlotLocked(string slot_name)
3048 {
3049 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
3050 }
3051
3052 //--- ATTACHMENT SLOTS
3054 {
3055 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
3056 if (GetGame().ConfigIsExisting(config_path))
3057 GetGame().ConfigGetTextArray(config_path, attachment_slots);
3058 }
3059
3061 {
3062 return true;
3063 }
3064
3065 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
3066 {
3067 return true;
3068 }
3069
3070 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
3071 {
3072 return true;
3073 }
3074
3075 // --- INIT
3076 void ConstructionInit()
3077 {
3078 if (!m_Construction)
3079 m_Construction = new Construction(this);
3080
3081 GetConstruction().Init();
3082 }
3083
3085 {
3086 return m_Construction;
3087 }
3088
3089 //--- INVENTORY/ATTACHMENTS CONDITIONS
3090 //attachments
3092 {
3093 return super.CanReceiveAttachment(attachment, slotId);
3094 }
3095
3097 {
3098 int attachment_count = GetInventory().AttachmentCount();
3099 if (attachment_count > 0)
3100 {
3101 if (HasBase() && attachment_count == 1)
3102 return false;
3103
3104 return true;
3105 }
3106
3107 return false;
3108 }
3109
3110 override bool ShowZonesHealth()
3111 {
3112 return true;
3113 }
3114
3115 //this into/outo parent.Cargo
3116 override bool CanPutInCargo(EntityAI parent)
3117 {
3118 return false;
3119 }
3120
3121 override bool CanRemoveFromCargo(EntityAI parent)
3122 {
3123 return false;
3124 }
3125
3126 //hands
3127 override bool CanPutIntoHands(EntityAI parent)
3128 {
3129 return false;
3130 }
3131
3132 //--- ACTION CONDITIONS
3133 //direction
3134 override bool IsFacingPlayer(PlayerBase player, string selection)
3135 {
3136 return true;
3137 }
3138
3139 override bool IsPlayerInside(PlayerBase player, string selection)
3140 {
3141 return true;
3142 }
3143
3146 {
3147 return false;
3148 }
3149
3150 //camera direction check
3151 bool IsFacingCamera(string selection)
3152 {
3153 return true;
3154 }
3155
3156 //roof check
3158 {
3159 return false;
3160 }
3161
3162 //selection->player distance check
3163 bool HasProperDistance(string selection, PlayerBase player)
3164 {
3165 return true;
3166 }
3167
3168 //folding
3170 {
3171 if (HasBase() || GetInventory().AttachmentCount() > 0)
3172 return false;
3173
3174 return true;
3175 }
3176
3178 {
3181
3182 return item;
3183 }
3184
3185 //Damage triggers (barbed wire)
3186 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3187 {
3188 if (GetGame() && GetGame().IsServer())
3189 {
3190 //destroy area damage if some already exists
3192
3193 //create new area damage
3195 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3196
3197 vector min_max[2];
3198 if (MemoryPointExists(slot_name + "_min"))
3199 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3200 if (MemoryPointExists(slot_name + "_max"))
3201 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3202
3203 //get proper trigger extents (min<max)
3204 vector extents[2];
3205 GetConstruction().GetTriggerExtents(min_max, extents);
3206
3207 //get box center
3208 vector center;
3209 center = GetConstruction().GetBoxCenter(min_max);
3210 center = ModelToWorld(center);
3211
3212 //rotate center if needed
3215
3216 areaDamage.SetExtents(extents[0], extents[1]);
3217 areaDamage.SetAreaPosition(center);
3218 areaDamage.SetAreaOrientation(orientation);
3219 areaDamage.SetLoopInterval(1.0);
3220 areaDamage.SetDeferDuration(0.2);
3221 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3222 areaDamage.SetAmmoName("BarbedWireHit");
3223 areaDamage.Spawn();
3224
3226 }
3227 }
3228
3230 {
3231 if (angle_deg != 0)
3232 {
3233 //orientation
3235
3236 //center
3238 if (MemoryPointExists("rotate_axis"))
3239 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3242 center[0] = r_center_x;
3243 center[2] = r_center_z;
3244 }
3245 }
3246
3247 void DestroyAreaDamage(string slot_name)
3248 {
3249 if (GetGame() && GetGame().IsServer())
3250 {
3253 {
3254 if (areaDamage)
3255 areaDamage.Destroy();
3256
3258 }
3259 }
3260 }
3261
3262 override bool IsIgnoredByConstruction()
3263 {
3264 return true;
3265 }
3266
3267 //================================================================
3268 // SOUNDS
3269 //================================================================
3270 protected void SoundBuildStart(string part_name)
3271 {
3272 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3273 }
3274
3275 protected void SoundDismantleStart(string part_name)
3276 {
3277 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3278 }
3279
3280 protected void SoundDestroyStart(string part_name)
3281 {
3282 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3283 }
3284
3285 protected string GetBuildSoundByMaterial(string part_name)
3286 {
3288
3289 switch (material_type)
3290 {
3291 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3292 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3293 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3294 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3295 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3296 }
3297
3298 return "";
3299 }
3300
3301 protected string GetDismantleSoundByMaterial(string part_name)
3302 {
3304
3305 switch (material_type)
3306 {
3307 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3308 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3309 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3310 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3311 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3312 }
3313
3314 return "";
3315 }
3316
3317 //misc
3319 {
3320 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3321 {
3322 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3324 SetHealth(slot_name, "Health", item.GetHealth());
3325 }
3326 }
3327
3328 override int GetDamageSystemVersionChange()
3329 {
3330 return 111;
3331 }
3332
3333 override void SetActions()
3334 {
3335 super.SetActions();
3336
3338 //AddAction(ActionTakeHybridAttachment);
3339 //AddAction(ActionTakeHybridAttachmentToHands);
3342 }
3343
3344 //================================================================
3345 // DEBUG
3346 //================================================================
3347 protected void DebugCustomState()
3348 {
3349 }
3350
3353 {
3354 return null;
3355 }
3356
3357 override void OnDebugSpawn()
3358 {
3359 FullyBuild();
3360 }
3361
3362 void FullyBuild()
3363 {
3365 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3366
3367 Man p;
3368
3369#ifdef SERVER
3371 GetGame().GetWorld().GetPlayerList(players);
3372 if (players.Count())
3373 p = players[0];
3374#else
3375 p = GetGame().GetPlayer();
3376#endif
3377
3378 foreach (ConstructionPart part : parts)
3379 {
3380 bool excluded = false;
3381 string partName = part.GetPartName();
3382 if (excludes)
3383 {
3384 foreach (string exclude : excludes)
3385 {
3386 if (partName.Contains(exclude))
3387 {
3388 excluded = true;
3389 break;
3390 }
3391 }
3392 }
3393
3394 if (!excluded)
3396 }
3397
3398 GetConstruction().UpdateVisuals();
3399 }
3400}
3401
3402void bsbDebugPrint(string s)
3403{
3404#ifdef BSB_DEBUG
3405 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3406#else
3407 //Print("" + s); // comment/uncomment to hide/see debug logs
3408#endif
3409}
3410void bsbDebugSpam(string s)
3411{
3412#ifdef BSB_DEBUG_SPAM
3413 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3414#else
3415 //Print("" + s); // comment/uncomment to hide/see debug logs
3416#endif
3417}

Referenced by ItemBase::OnPartDestroyedClient().

◆ SoundDismantleStart()

void bsbDebugPrint::SoundDismantleStart ( string part_name)
protected

Definition at line 2222 of file BaseBuildingBase.c.

2224{
2225 const string ANIMATION_DEPLOYED = "Deployed";
2226
2227 float m_ConstructionKitHealth; //stored health value for used construction kit
2228
2230
2231 bool m_HasBase;
2232 //variables for synchronization of base building parts (2x31 is the current limit)
2233 int m_SyncParts01; //synchronization for already built parts (31 parts)
2234 int m_SyncParts02; //synchronization for already built parts (+31 parts)
2235 int m_SyncParts03; //synchronization for already built parts (+31 parts)
2236 int m_InteractedPartId; //construction part id that an action was performed on
2237 int m_PerformedActionId; //action id that was performed on a construction part
2238
2239 //Sounds
2240 //build
2241 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
2242 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
2243 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
2244 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
2245 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
2246 //dismantle
2247 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
2248 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
2249 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
2250 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
2251 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
2252
2253 protected EffectSound m_Sound;
2254
2258
2259 // Constructor
2260 void BaseBuildingBase()
2261 {
2263
2264 //synchronized variables
2265 RegisterNetSyncVariableInt("m_SyncParts01");
2266 RegisterNetSyncVariableInt("m_SyncParts02");
2267 RegisterNetSyncVariableInt("m_SyncParts03");
2268 RegisterNetSyncVariableInt("m_InteractedPartId");
2269 RegisterNetSyncVariableInt("m_PerformedActionId");
2270 RegisterNetSyncVariableBool("m_HasBase");
2271
2272 //Construction init
2274
2275 if (ConfigIsExisting("hybridAttachments"))
2276 {
2278 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2279 }
2280 if (ConfigIsExisting("mountables"))
2281 {
2283 ConfigGetTextArray("mountables", m_Mountables);
2284 }
2285
2286 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2287 }
2288
2289 override void EEDelete(EntityAI parent)
2290 {
2291 super.EEDelete(parent);
2292
2293 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2295
2296 }
2297
2298 override string GetInvulnerabilityTypeString()
2299 {
2300 return "disableBaseDamage";
2301 }
2302
2303 override bool CanObstruct()
2304 {
2305 return true;
2306 }
2307
2308 override int GetHideIconMask()
2309 {
2310 return EInventoryIconVisibility.HIDE_VICINITY;
2311 }
2312
2313 // --- SYNCHRONIZATION
2315 {
2316 if (GetGame().IsServer())
2317 SetSynchDirty();
2318 }
2319
2320 override void OnVariablesSynchronized()
2321 {
2322 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2323 super.OnVariablesSynchronized();
2324
2325 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2326 }
2327
2328 protected void OnSynchronizedClient()
2329 {
2330 //update parts
2332
2333 //update action on part
2335
2336 //update visuals (client)
2337 UpdateVisuals();
2338 }
2339
2340 //parts synchronization
2342 {
2343 //part_id must starts from index = 1
2344 int offset;
2345 int mask;
2346
2347 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2348 {
2349 offset = part_id - 1;
2350 mask = 1 << offset;
2351
2353 }
2354 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2355 {
2356 offset = (part_id % 32);
2357 mask = 1 << offset;
2358
2360 }
2361 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2362 {
2363 offset = (part_id % 63);
2364 mask = 1 << offset;
2365
2367 }
2368 }
2369
2371 {
2372 //part_id must starts from index = 1
2373 int offset;
2374 int mask;
2375
2376 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2377 {
2378 offset = part_id - 1;
2379 mask = 1 << offset;
2380
2382 }
2383 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2384 {
2385 offset = (part_id % 32);
2386 mask = 1 << offset;
2387
2389 }
2390 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2391 {
2392 offset = (part_id % 63);
2393 mask = 1 << offset;
2394
2396 }
2397 }
2398
2400 {
2401 //part_id must starts from index = 1
2402 int offset;
2403 int mask;
2404
2405 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2406 {
2407 offset = part_id - 1;
2408 mask = 1 << offset;
2409
2410 if ((m_SyncParts01 & mask) > 0)
2411 return true;
2412 }
2413 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2414 {
2415 offset = (part_id % 32);
2416 mask = 1 << offset;
2417
2418 if ((m_SyncParts02 & mask) > 0)
2419 return true;
2420 }
2421 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2422 {
2423 offset = (part_id % 63);
2424 mask = 1 << offset;
2425
2426 if ((m_SyncParts03 & mask) > 0)
2427 return true;
2428 }
2429
2430 return false;
2431 }
2432
2433 protected void RegisterActionForSync(int part_id, int action_id)
2434 {
2437 }
2438
2439 protected void ResetActionSyncData()
2440 {
2441 //reset data
2442 m_InteractedPartId = -1;
2444 }
2445
2446 protected void SetActionFromSyncData()
2447 {
2448 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2449 {
2452
2453 switch (build_action_id)
2454 {
2458 }
2459 }
2460 }
2461 //------
2462
2464 {
2465 string key = part.m_PartName;
2466 bool is_base = part.IsBase();
2468 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2470 {
2471 if (!part.IsBuilt())
2472 {
2473 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2474 GetConstruction().AddToConstructedParts(key);
2475 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2476
2477 if (is_base)
2478 {
2480 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2481 }
2482 }
2483 }
2484 else
2485 {
2486 if (part.IsBuilt())
2487 {
2488 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2489 GetConstruction().RemoveFromConstructedParts(key);
2490 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2491
2492 if (is_base)
2493 {
2495 AddProxyPhysics(ANIMATION_DEPLOYED);
2496 }
2497 }
2498 }
2499
2500 //check slot lock for material attachments
2501 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2502 }
2503
2504 //set construction parts based on synchronized data
2506 {
2509
2510 for (int i = 0; i < construction_parts.Count(); ++i)
2511 {
2512 string key = construction_parts.GetKey(i);
2515 }
2516
2517 //regenerate navmesh
2518 UpdateNavmesh();
2519 }
2520
2522 {
2525
2526 for (int i = 0; i < construction_parts.Count(); ++i)
2527 {
2528 string key = construction_parts.GetKey(i);
2530
2531 if (value.GetId() == id)
2532 return value;
2533 }
2534
2535 return NULL;
2536 }
2537 //
2538
2539 //Base
2540 bool HasBase()
2541 {
2542 return m_HasBase;
2543 }
2544
2545 void SetBaseState(bool has_base)
2546 {
2548 }
2549
2550 override bool IsDeployable()
2551 {
2552 return true;
2553 }
2554
2555 bool IsOpened()
2556 {
2557 return false;
2558 }
2559
2560 //--- CONSTRUCTION KIT
2562 {
2566
2567 return construction_kit;
2568 }
2569
2571 {
2572 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2575 }
2576
2577 protected vector GetKitSpawnPosition()
2578 {
2579 return GetPosition();
2580 }
2581
2582 protected string GetConstructionKitType()
2583 {
2584 return "";
2585 }
2586
2588 {
2590 GetGame().ObjectDelete(construction_kit);
2591 }
2592
2593 //--- CONSTRUCTION
2594 void DestroyConstruction()
2595 {
2596 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2597 GetGame().ObjectDelete(this);
2598 }
2599
2600 // --- EVENTS
2601 override void OnStoreSave(ParamsWriteContext ctx)
2602 {
2603 super.OnStoreSave(ctx);
2604
2605 //sync parts 01
2606 ctx.Write(m_SyncParts01);
2607 ctx.Write(m_SyncParts02);
2608 ctx.Write(m_SyncParts03);
2609
2610 ctx.Write(m_HasBase);
2611 }
2612
2613 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2614 {
2615 if (!super.OnStoreLoad(ctx, version))
2616 return false;
2617
2618 //--- Base building data ---
2619 //Restore synced parts data
2620 if (!ctx.Read(m_SyncParts01))
2621 {
2622 m_SyncParts01 = 0; //set default
2623 return false;
2624 }
2625 if (!ctx.Read(m_SyncParts02))
2626 {
2627 m_SyncParts02 = 0; //set default
2628 return false;
2629 }
2630 if (!ctx.Read(m_SyncParts03))
2631 {
2632 m_SyncParts03 = 0; //set default
2633 return false;
2634 }
2635
2636 //has base
2637 if (!ctx.Read(m_HasBase))
2638 {
2639 m_HasBase = false;
2640 return false;
2641 }
2642 //---
2643
2644 return true;
2645 }
2646
2647 override void AfterStoreLoad()
2648 {
2649 super.AfterStoreLoad();
2650
2653 }
2654
2656 {
2657 //update server data
2659
2660 //set base state
2661 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2662 SetBaseState(construction_part.IsBuilt()) ;
2663
2664 //synchronize after load
2666 }
2667
2668 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2669 {
2671 return;
2672
2673 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2674
2675 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2676 return;
2677
2679 string part_name = zone;
2680 part_name.ToLower();
2681
2683 {
2685
2686 if (construction_part && construction.IsPartConstructed(part_name))
2687 {
2688 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2689 construction.DestroyConnectedParts(part_name);
2690 }
2691
2692 //barbed wire handling (hack-ish)
2693 if (part_name.Contains("barbed"))
2694 {
2695 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2696 if (barbed_wire)
2697 barbed_wire.SetMountedState(false);
2698 }
2699 }
2700 }
2701
2702 override void EEOnAfterLoad()
2703 {
2705 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2706
2707 super.EEOnAfterLoad();
2708 }
2709
2710 override void EEInit()
2711 {
2712 super.EEInit();
2713
2714 // init visuals and physics
2715 InitBaseState();
2716
2717 //debug
2718#ifdef DEVELOPER
2720#endif
2721 }
2722
2723 override void EEItemAttached(EntityAI item, string slot_name)
2724 {
2725 super.EEItemAttached(item, slot_name);
2726
2728 UpdateVisuals();
2730 }
2731
2732 override void EEItemDetached(EntityAI item, string slot_name)
2733 {
2734 super.EEItemDetached(item, slot_name);
2735
2736 UpdateVisuals();
2738 }
2739
2740 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2741 {
2743 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2744
2747 }
2748
2749 //ignore out of reach condition
2750 override bool IgnoreOutOfReachCondition()
2751 {
2752 return true;
2753 }
2754
2755 //CONSTRUCTION EVENTS
2756 //Build
2757 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2758 {
2760
2761 //check base state
2762 if (construtionPart.IsBase())
2763 {
2764 SetBaseState(true);
2765
2766 //spawn kit
2768 }
2769
2770 //register constructed parts for synchronization
2772
2773 //register action that was performed on part
2775
2776 //synchronize
2778
2779 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2780
2781 UpdateNavmesh();
2782
2783 //update visuals
2784 UpdateVisuals();
2785
2786 //reset action sync data
2787 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2788 }
2789
2790 void OnPartBuiltClient(string part_name, int action_id)
2791 {
2792 //play sound
2794 }
2795
2796 //Dismantle
2798 {
2799 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2801
2802 //register constructed parts for synchronization
2804
2805 //register action that was performed on part
2807
2808 //synchronize
2810
2811 // server part of sync, client will be synced from SetPartsFromSyncData
2813
2814 UpdateNavmesh();
2815
2816 //update visuals
2817 UpdateVisuals();
2818
2819 //reset action sync data
2820 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2821
2822 //check base state
2823 if (construtionPart.IsBase())
2824 {
2825 //Destroy construction
2826 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2827 }
2828 }
2829
2831 {
2832 //play sound
2834 }
2835
2836 //Destroy
2838 {
2839 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2841
2842 //register constructed parts for synchronization
2844
2845 //register action that was performed on part
2847
2848 //synchronize
2850
2851 // server part of sync, client will be synced from SetPartsFromSyncData
2853
2854 UpdateNavmesh();
2855
2856 //update visuals
2857 UpdateVisuals();
2858
2859 //reset action sync data
2860 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2861
2862 //check base state
2863 if (construtionPart.IsBase())
2864 {
2865 //Destroy construction
2866 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2867 }
2868 }
2869
2870 void OnPartDestroyedClient(string part_name, int action_id)
2871 {
2872 //play sound
2874 }
2875
2876 // --- UPDATE
2877 void InitBaseState()
2878 {
2879 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2880
2881 InitVisuals();
2882 UpdateNavmesh(); //regenerate navmesh
2883 GetConstruction().InitBaseState();
2884 }
2885
2886 void InitVisuals()
2887 {
2888 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2889 //check base
2890 if (!HasBase())
2891 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2892 else
2893 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2894
2895 GetConstruction().UpdateVisuals();
2896 }
2897
2898 void UpdateVisuals()
2899 {
2901
2903 foreach (string slotName : attachmentSlots)
2905
2906 //check base
2907 if (!HasBase())
2908 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2909 else
2910 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2911
2912 GetConstruction().UpdateVisuals();
2913 }
2914
2916 {
2917 string slotNameMounted = slot_name + "_Mounted";
2918 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2919
2920 if (attachment)
2921 {
2922 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2923 if (barbedWire && barbedWire.IsMounted())
2925 else
2927
2928 if (is_locked)
2929 {
2930 SetAnimationPhase(slotNameMounted, 0);
2931 SetAnimationPhase(slot_name, 1);
2932 }
2933 else
2934 {
2935 SetAnimationPhase(slotNameMounted, 1);
2936 SetAnimationPhase(slot_name, 0);
2937 }
2938 }
2939 else
2940 {
2941 SetAnimationPhase(slotNameMounted, 1);
2942 SetAnimationPhase(slot_name, 1);
2943
2945 }
2946 }
2947
2948 // avoid calling this function on frequent occasions, it's a massive performance hit
2949 void UpdatePhysics()
2950 {
2952 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2953
2956
2958 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2959
2960 foreach (string slotName : attachmentSlots)
2962
2963 //check base
2964 if (!HasBase())
2965 {
2967 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2968
2969 AddProxyPhysics(ANIMATION_DEPLOYED);
2970 }
2971 else
2972 {
2974 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2975
2976 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2977 }
2978
2979 GetConstruction().UpdatePhysics();
2980 UpdateNavmesh();
2981 }
2982
2984 {
2985 //checks for invalid appends; hotfix
2986 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2987 return;
2988 //----------------------------------
2989 string slot_name_mounted = slot_name + "_Mounted";
2990 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2991
2992 //remove proxy physics
2993 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2994 RemoveProxyPhysics(slot_name_mounted);
2995 RemoveProxyPhysics(slot_name);
2996
2997 if (attachment)
2998 {
2999 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
3000 if (is_locked)
3001 {
3002 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
3003 AddProxyPhysics(slot_name_mounted);
3004 }
3005 else
3006 {
3007 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
3008 AddProxyPhysics(slot_name);
3009 }
3010 }
3011 }
3012
3013 protected void UpdateNavmesh()
3014 {
3015 SetAffectPathgraph(true, false);
3016 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
3017 }
3018
3019 override bool CanUseConstruction()
3020 {
3021 return true;
3022 }
3023
3024 override bool CanUseConstructionBuild()
3025 {
3026 return true;
3027 }
3028
3030 {
3031 if (attachment)
3032 {
3034 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
3035
3036 return GetInventory().GetSlotLock(inventory_location.GetSlot());
3037 }
3038
3039 return false;
3040 }
3041
3042 protected bool IsAttachmentSlotLocked(string slot_name)
3043 {
3044 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
3045 }
3046
3047 //--- ATTACHMENT SLOTS
3049 {
3050 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
3051 if (GetGame().ConfigIsExisting(config_path))
3052 GetGame().ConfigGetTextArray(config_path, attachment_slots);
3053 }
3054
3056 {
3057 return true;
3058 }
3059
3060 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
3061 {
3062 return true;
3063 }
3064
3065 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
3066 {
3067 return true;
3068 }
3069
3070 // --- INIT
3071 void ConstructionInit()
3072 {
3073 if (!m_Construction)
3074 m_Construction = new Construction(this);
3075
3076 GetConstruction().Init();
3077 }
3078
3080 {
3081 return m_Construction;
3082 }
3083
3084 //--- INVENTORY/ATTACHMENTS CONDITIONS
3085 //attachments
3087 {
3088 return super.CanReceiveAttachment(attachment, slotId);
3089 }
3090
3092 {
3093 int attachment_count = GetInventory().AttachmentCount();
3094 if (attachment_count > 0)
3095 {
3096 if (HasBase() && attachment_count == 1)
3097 return false;
3098
3099 return true;
3100 }
3101
3102 return false;
3103 }
3104
3105 override bool ShowZonesHealth()
3106 {
3107 return true;
3108 }
3109
3110 //this into/outo parent.Cargo
3111 override bool CanPutInCargo(EntityAI parent)
3112 {
3113 return false;
3114 }
3115
3116 override bool CanRemoveFromCargo(EntityAI parent)
3117 {
3118 return false;
3119 }
3120
3121 //hands
3122 override bool CanPutIntoHands(EntityAI parent)
3123 {
3124 return false;
3125 }
3126
3127 //--- ACTION CONDITIONS
3128 //direction
3129 override bool IsFacingPlayer(PlayerBase player, string selection)
3130 {
3131 return true;
3132 }
3133
3134 override bool IsPlayerInside(PlayerBase player, string selection)
3135 {
3136 return true;
3137 }
3138
3141 {
3142 return false;
3143 }
3144
3145 //camera direction check
3146 bool IsFacingCamera(string selection)
3147 {
3148 return true;
3149 }
3150
3151 //roof check
3153 {
3154 return false;
3155 }
3156
3157 //selection->player distance check
3158 bool HasProperDistance(string selection, PlayerBase player)
3159 {
3160 return true;
3161 }
3162
3163 //folding
3165 {
3166 if (HasBase() || GetInventory().AttachmentCount() > 0)
3167 return false;
3168
3169 return true;
3170 }
3171
3173 {
3176
3177 return item;
3178 }
3179
3180 //Damage triggers (barbed wire)
3181 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
3182 {
3183 if (GetGame() && GetGame().IsServer())
3184 {
3185 //destroy area damage if some already exists
3187
3188 //create new area damage
3190 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
3191
3192 vector min_max[2];
3193 if (MemoryPointExists(slot_name + "_min"))
3194 min_max[0] = GetMemoryPointPos(slot_name + "_min");
3195 if (MemoryPointExists(slot_name + "_max"))
3196 min_max[1] = GetMemoryPointPos(slot_name + "_max");
3197
3198 //get proper trigger extents (min<max)
3199 vector extents[2];
3200 GetConstruction().GetTriggerExtents(min_max, extents);
3201
3202 //get box center
3203 vector center;
3204 center = GetConstruction().GetBoxCenter(min_max);
3205 center = ModelToWorld(center);
3206
3207 //rotate center if needed
3210
3211 areaDamage.SetExtents(extents[0], extents[1]);
3212 areaDamage.SetAreaPosition(center);
3213 areaDamage.SetAreaOrientation(orientation);
3214 areaDamage.SetLoopInterval(1.0);
3215 areaDamage.SetDeferDuration(0.2);
3216 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
3217 areaDamage.SetAmmoName("BarbedWireHit");
3218 areaDamage.Spawn();
3219
3221 }
3222 }
3223
3225 {
3226 if (angle_deg != 0)
3227 {
3228 //orientation
3230
3231 //center
3233 if (MemoryPointExists("rotate_axis"))
3234 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
3237 center[0] = r_center_x;
3238 center[2] = r_center_z;
3239 }
3240 }
3241
3242 void DestroyAreaDamage(string slot_name)
3243 {
3244 if (GetGame() && GetGame().IsServer())
3245 {
3248 {
3249 if (areaDamage)
3250 areaDamage.Destroy();
3251
3253 }
3254 }
3255 }
3256
3257 override bool IsIgnoredByConstruction()
3258 {
3259 return true;
3260 }
3261
3262 //================================================================
3263 // SOUNDS
3264 //================================================================
3265 protected void SoundBuildStart(string part_name)
3266 {
3267 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3268 }
3269
3270 protected void SoundDismantleStart(string part_name)
3271 {
3272 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3273 }
3274
3275 protected void SoundDestroyStart(string part_name)
3276 {
3277 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3278 }
3279
3280 protected string GetBuildSoundByMaterial(string part_name)
3281 {
3283
3284 switch (material_type)
3285 {
3286 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3287 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3288 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3289 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3290 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3291 }
3292
3293 return "";
3294 }
3295
3296 protected string GetDismantleSoundByMaterial(string part_name)
3297 {
3299
3300 switch (material_type)
3301 {
3302 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3303 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3304 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3305 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3306 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3307 }
3308
3309 return "";
3310 }
3311
3312 //misc
3314 {
3315 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3316 {
3317 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3319 SetHealth(slot_name, "Health", item.GetHealth());
3320 }
3321 }
3322
3323 override int GetDamageSystemVersionChange()
3324 {
3325 return 111;
3326 }
3327
3328 override void SetActions()
3329 {
3330 super.SetActions();
3331
3333 //AddAction(ActionTakeHybridAttachment);
3334 //AddAction(ActionTakeHybridAttachmentToHands);
3337 }
3338
3339 //================================================================
3340 // DEBUG
3341 //================================================================
3342 protected void DebugCustomState()
3343 {
3344 }
3345
3348 {
3349 return null;
3350 }
3351
3352 override void OnDebugSpawn()
3353 {
3354 FullyBuild();
3355 }
3356
3357 void FullyBuild()
3358 {
3360 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3361
3362 Man p;
3363
3364#ifdef SERVER
3366 GetGame().GetWorld().GetPlayerList(players);
3367 if (players.Count())
3368 p = players[0];
3369#else
3370 p = GetGame().GetPlayer();
3371#endif
3372
3373 foreach (ConstructionPart part : parts)
3374 {
3375 bool excluded = false;
3376 string partName = part.GetPartName();
3377 if (excludes)
3378 {
3379 foreach (string exclude : excludes)
3380 {
3381 if (partName.Contains(exclude))
3382 {
3383 excluded = true;
3384 break;
3385 }
3386 }
3387 }
3388
3389 if (!excluded)
3391 }
3392
3393 GetConstruction().UpdateVisuals();
3394 }
3395}
3396
3397void bsbDebugPrint(string s)
3398{
3399#ifdef BSB_DEBUG
3400 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3401#else
3402 //Print("" + s); // comment/uncomment to hide/see debug logs
3403#endif
3404}
3405void bsbDebugSpam(string s)
3406{
3407#ifdef BSB_DEBUG_SPAM
3408 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3409#else
3410 //Print("" + s); // comment/uncomment to hide/see debug logs
3411#endif
3412}

Referenced by ItemBase::OnPartDismantledClient().

◆ SynchronizeBaseState()

void bsbDebugPrint::SynchronizeBaseState ( )
protected

Definition at line 1266 of file BaseBuildingBase.c.

1268{
1269 const string ANIMATION_DEPLOYED = "Deployed";
1270
1271 float m_ConstructionKitHealth; //stored health value for used construction kit
1272
1274
1275 bool m_HasBase;
1276 //variables for synchronization of base building parts (2x31 is the current limit)
1277 int m_SyncParts01; //synchronization for already built parts (31 parts)
1278 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1279 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1280 int m_InteractedPartId; //construction part id that an action was performed on
1281 int m_PerformedActionId; //action id that was performed on a construction part
1282
1283 //Sounds
1284 //build
1285 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1286 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1287 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1288 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1289 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1290 //dismantle
1291 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1292 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1293 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1294 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1295 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1296
1297 protected EffectSound m_Sound;
1298
1302
1303 // Constructor
1304 void BaseBuildingBase()
1305 {
1307
1308 //synchronized variables
1309 RegisterNetSyncVariableInt("m_SyncParts01");
1310 RegisterNetSyncVariableInt("m_SyncParts02");
1311 RegisterNetSyncVariableInt("m_SyncParts03");
1312 RegisterNetSyncVariableInt("m_InteractedPartId");
1313 RegisterNetSyncVariableInt("m_PerformedActionId");
1314 RegisterNetSyncVariableBool("m_HasBase");
1315
1316 //Construction init
1318
1319 if (ConfigIsExisting("hybridAttachments"))
1320 {
1322 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1323 }
1324 if (ConfigIsExisting("mountables"))
1325 {
1327 ConfigGetTextArray("mountables", m_Mountables);
1328 }
1329
1330 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1331 }
1332
1333 override void EEDelete(EntityAI parent)
1334 {
1335 super.EEDelete(parent);
1336
1337 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1339
1340 }
1341
1342 override string GetInvulnerabilityTypeString()
1343 {
1344 return "disableBaseDamage";
1345 }
1346
1347 override bool CanObstruct()
1348 {
1349 return true;
1350 }
1351
1352 override int GetHideIconMask()
1353 {
1354 return EInventoryIconVisibility.HIDE_VICINITY;
1355 }
1356
1357 // --- SYNCHRONIZATION
1359 {
1360 if (GetGame().IsServer())
1361 SetSynchDirty();
1362 }
1363
1364 override void OnVariablesSynchronized()
1365 {
1366 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1367 super.OnVariablesSynchronized();
1368
1369 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1370 }
1371
1372 protected void OnSynchronizedClient()
1373 {
1374 //update parts
1376
1377 //update action on part
1379
1380 //update visuals (client)
1381 UpdateVisuals();
1382 }
1383
1384 //parts synchronization
1386 {
1387 //part_id must starts from index = 1
1388 int offset;
1389 int mask;
1390
1391 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1392 {
1393 offset = part_id - 1;
1394 mask = 1 << offset;
1395
1397 }
1398 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1399 {
1400 offset = (part_id % 32);
1401 mask = 1 << offset;
1402
1404 }
1405 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1406 {
1407 offset = (part_id % 63);
1408 mask = 1 << offset;
1409
1411 }
1412 }
1413
1415 {
1416 //part_id must starts from index = 1
1417 int offset;
1418 int mask;
1419
1420 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1421 {
1422 offset = part_id - 1;
1423 mask = 1 << offset;
1424
1426 }
1427 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1428 {
1429 offset = (part_id % 32);
1430 mask = 1 << offset;
1431
1433 }
1434 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1435 {
1436 offset = (part_id % 63);
1437 mask = 1 << offset;
1438
1440 }
1441 }
1442
1444 {
1445 //part_id must starts from index = 1
1446 int offset;
1447 int mask;
1448
1449 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1450 {
1451 offset = part_id - 1;
1452 mask = 1 << offset;
1453
1454 if ((m_SyncParts01 & mask) > 0)
1455 return true;
1456 }
1457 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1458 {
1459 offset = (part_id % 32);
1460 mask = 1 << offset;
1461
1462 if ((m_SyncParts02 & mask) > 0)
1463 return true;
1464 }
1465 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1466 {
1467 offset = (part_id % 63);
1468 mask = 1 << offset;
1469
1470 if ((m_SyncParts03 & mask) > 0)
1471 return true;
1472 }
1473
1474 return false;
1475 }
1476
1477 protected void RegisterActionForSync(int part_id, int action_id)
1478 {
1481 }
1482
1483 protected void ResetActionSyncData()
1484 {
1485 //reset data
1486 m_InteractedPartId = -1;
1488 }
1489
1490 protected void SetActionFromSyncData()
1491 {
1492 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1493 {
1496
1497 switch (build_action_id)
1498 {
1502 }
1503 }
1504 }
1505 //------
1506
1508 {
1509 string key = part.m_PartName;
1510 bool is_base = part.IsBase();
1512 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1514 {
1515 if (!part.IsBuilt())
1516 {
1517 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1518 GetConstruction().AddToConstructedParts(key);
1519 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1520
1521 if (is_base)
1522 {
1524 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1525 }
1526 }
1527 }
1528 else
1529 {
1530 if (part.IsBuilt())
1531 {
1532 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1533 GetConstruction().RemoveFromConstructedParts(key);
1534 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1535
1536 if (is_base)
1537 {
1539 AddProxyPhysics(ANIMATION_DEPLOYED);
1540 }
1541 }
1542 }
1543
1544 //check slot lock for material attachments
1545 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1546 }
1547
1548 //set construction parts based on synchronized data
1550 {
1553
1554 for (int i = 0; i < construction_parts.Count(); ++i)
1555 {
1556 string key = construction_parts.GetKey(i);
1559 }
1560
1561 //regenerate navmesh
1562 UpdateNavmesh();
1563 }
1564
1566 {
1569
1570 for (int i = 0; i < construction_parts.Count(); ++i)
1571 {
1572 string key = construction_parts.GetKey(i);
1574
1575 if (value.GetId() == id)
1576 return value;
1577 }
1578
1579 return NULL;
1580 }
1581 //
1582
1583 //Base
1584 bool HasBase()
1585 {
1586 return m_HasBase;
1587 }
1588
1589 void SetBaseState(bool has_base)
1590 {
1592 }
1593
1594 override bool IsDeployable()
1595 {
1596 return true;
1597 }
1598
1599 bool IsOpened()
1600 {
1601 return false;
1602 }
1603
1604 //--- CONSTRUCTION KIT
1606 {
1610
1611 return construction_kit;
1612 }
1613
1615 {
1616 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1619 }
1620
1621 protected vector GetKitSpawnPosition()
1622 {
1623 return GetPosition();
1624 }
1625
1626 protected string GetConstructionKitType()
1627 {
1628 return "";
1629 }
1630
1632 {
1634 GetGame().ObjectDelete(construction_kit);
1635 }
1636
1637 //--- CONSTRUCTION
1638 void DestroyConstruction()
1639 {
1640 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1641 GetGame().ObjectDelete(this);
1642 }
1643
1644 // --- EVENTS
1645 override void OnStoreSave(ParamsWriteContext ctx)
1646 {
1647 super.OnStoreSave(ctx);
1648
1649 //sync parts 01
1650 ctx.Write(m_SyncParts01);
1651 ctx.Write(m_SyncParts02);
1652 ctx.Write(m_SyncParts03);
1653
1654 ctx.Write(m_HasBase);
1655 }
1656
1657 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1658 {
1659 if (!super.OnStoreLoad(ctx, version))
1660 return false;
1661
1662 //--- Base building data ---
1663 //Restore synced parts data
1664 if (!ctx.Read(m_SyncParts01))
1665 {
1666 m_SyncParts01 = 0; //set default
1667 return false;
1668 }
1669 if (!ctx.Read(m_SyncParts02))
1670 {
1671 m_SyncParts02 = 0; //set default
1672 return false;
1673 }
1674 if (!ctx.Read(m_SyncParts03))
1675 {
1676 m_SyncParts03 = 0; //set default
1677 return false;
1678 }
1679
1680 //has base
1681 if (!ctx.Read(m_HasBase))
1682 {
1683 m_HasBase = false;
1684 return false;
1685 }
1686 //---
1687
1688 return true;
1689 }
1690
1691 override void AfterStoreLoad()
1692 {
1693 super.AfterStoreLoad();
1694
1697 }
1698
1700 {
1701 //update server data
1703
1704 //set base state
1705 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1706 SetBaseState(construction_part.IsBuilt()) ;
1707
1708 //synchronize after load
1710 }
1711
1712 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1713 {
1715 return;
1716
1717 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1718
1719 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1720 return;
1721
1723 string part_name = zone;
1724 part_name.ToLower();
1725
1727 {
1729
1730 if (construction_part && construction.IsPartConstructed(part_name))
1731 {
1732 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1733 construction.DestroyConnectedParts(part_name);
1734 }
1735
1736 //barbed wire handling (hack-ish)
1737 if (part_name.Contains("barbed"))
1738 {
1739 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1740 if (barbed_wire)
1741 barbed_wire.SetMountedState(false);
1742 }
1743 }
1744 }
1745
1746 override void EEOnAfterLoad()
1747 {
1749 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1750
1751 super.EEOnAfterLoad();
1752 }
1753
1754 override void EEInit()
1755 {
1756 super.EEInit();
1757
1758 // init visuals and physics
1759 InitBaseState();
1760
1761 //debug
1762#ifdef DEVELOPER
1764#endif
1765 }
1766
1767 override void EEItemAttached(EntityAI item, string slot_name)
1768 {
1769 super.EEItemAttached(item, slot_name);
1770
1772 UpdateVisuals();
1774 }
1775
1776 override void EEItemDetached(EntityAI item, string slot_name)
1777 {
1778 super.EEItemDetached(item, slot_name);
1779
1780 UpdateVisuals();
1782 }
1783
1784 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
1785 {
1787 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
1788
1791 }
1792
1793 //ignore out of reach condition
1794 override bool IgnoreOutOfReachCondition()
1795 {
1796 return true;
1797 }
1798
1799 //CONSTRUCTION EVENTS
1800 //Build
1801 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
1802 {
1804
1805 //check base state
1806 if (construtionPart.IsBase())
1807 {
1808 SetBaseState(true);
1809
1810 //spawn kit
1812 }
1813
1814 //register constructed parts for synchronization
1816
1817 //register action that was performed on part
1819
1820 //synchronize
1822
1823 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
1824
1825 UpdateNavmesh();
1826
1827 //update visuals
1828 UpdateVisuals();
1829
1830 //reset action sync data
1831 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1832 }
1833
1834 void OnPartBuiltClient(string part_name, int action_id)
1835 {
1836 //play sound
1838 }
1839
1840 //Dismantle
1842 {
1843 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
1845
1846 //register constructed parts for synchronization
1848
1849 //register action that was performed on part
1851
1852 //synchronize
1854
1855 // server part of sync, client will be synced from SetPartsFromSyncData
1857
1858 UpdateNavmesh();
1859
1860 //update visuals
1861 UpdateVisuals();
1862
1863 //reset action sync data
1864 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1865
1866 //check base state
1867 if (construtionPart.IsBase())
1868 {
1869 //Destroy construction
1870 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1871 }
1872 }
1873
1875 {
1876 //play sound
1878 }
1879
1880 //Destroy
1882 {
1883 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
1885
1886 //register constructed parts for synchronization
1888
1889 //register action that was performed on part
1891
1892 //synchronize
1894
1895 // server part of sync, client will be synced from SetPartsFromSyncData
1897
1898 UpdateNavmesh();
1899
1900 //update visuals
1901 UpdateVisuals();
1902
1903 //reset action sync data
1904 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1905
1906 //check base state
1907 if (construtionPart.IsBase())
1908 {
1909 //Destroy construction
1910 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1911 }
1912 }
1913
1914 void OnPartDestroyedClient(string part_name, int action_id)
1915 {
1916 //play sound
1918 }
1919
1920 // --- UPDATE
1921 void InitBaseState()
1922 {
1923 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
1924
1925 InitVisuals();
1926 UpdateNavmesh(); //regenerate navmesh
1927 GetConstruction().InitBaseState();
1928 }
1929
1930 void InitVisuals()
1931 {
1932 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
1933 //check base
1934 if (!HasBase())
1935 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1936 else
1937 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1938
1939 GetConstruction().UpdateVisuals();
1940 }
1941
1942 void UpdateVisuals()
1943 {
1945
1947 foreach (string slotName : attachmentSlots)
1949
1950 //check base
1951 if (!HasBase())
1952 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1953 else
1954 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1955
1956 GetConstruction().UpdateVisuals();
1957 }
1958
1960 {
1961 string slotNameMounted = slot_name + "_Mounted";
1962 EntityAI attachment = FindAttachmentBySlotName(slot_name);
1963
1964 if (attachment)
1965 {
1966 BarbedWire barbedWire = BarbedWire.Cast(attachment);
1967 if (barbedWire && barbedWire.IsMounted())
1969 else
1971
1972 if (is_locked)
1973 {
1974 SetAnimationPhase(slotNameMounted, 0);
1975 SetAnimationPhase(slot_name, 1);
1976 }
1977 else
1978 {
1979 SetAnimationPhase(slotNameMounted, 1);
1980 SetAnimationPhase(slot_name, 0);
1981 }
1982 }
1983 else
1984 {
1985 SetAnimationPhase(slotNameMounted, 1);
1986 SetAnimationPhase(slot_name, 1);
1987
1989 }
1990 }
1991
1992 // avoid calling this function on frequent occasions, it's a massive performance hit
1993 void UpdatePhysics()
1994 {
1996 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
1997
2000
2002 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2003
2004 foreach (string slotName : attachmentSlots)
2006
2007 //check base
2008 if (!HasBase())
2009 {
2011 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2012
2013 AddProxyPhysics(ANIMATION_DEPLOYED);
2014 }
2015 else
2016 {
2018 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2019
2020 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2021 }
2022
2023 GetConstruction().UpdatePhysics();
2024 UpdateNavmesh();
2025 }
2026
2028 {
2029 //checks for invalid appends; hotfix
2030 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2031 return;
2032 //----------------------------------
2033 string slot_name_mounted = slot_name + "_Mounted";
2034 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2035
2036 //remove proxy physics
2037 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2038 RemoveProxyPhysics(slot_name_mounted);
2039 RemoveProxyPhysics(slot_name);
2040
2041 if (attachment)
2042 {
2043 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2044 if (is_locked)
2045 {
2046 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2047 AddProxyPhysics(slot_name_mounted);
2048 }
2049 else
2050 {
2051 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2052 AddProxyPhysics(slot_name);
2053 }
2054 }
2055 }
2056
2057 protected void UpdateNavmesh()
2058 {
2059 SetAffectPathgraph(true, false);
2060 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2061 }
2062
2063 override bool CanUseConstruction()
2064 {
2065 return true;
2066 }
2067
2068 override bool CanUseConstructionBuild()
2069 {
2070 return true;
2071 }
2072
2074 {
2075 if (attachment)
2076 {
2078 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2079
2080 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2081 }
2082
2083 return false;
2084 }
2085
2086 protected bool IsAttachmentSlotLocked(string slot_name)
2087 {
2088 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2089 }
2090
2091 //--- ATTACHMENT SLOTS
2093 {
2094 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2095 if (GetGame().ConfigIsExisting(config_path))
2096 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2097 }
2098
2100 {
2101 return true;
2102 }
2103
2104 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2105 {
2106 return true;
2107 }
2108
2109 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2110 {
2111 return true;
2112 }
2113
2114 // --- INIT
2115 void ConstructionInit()
2116 {
2117 if (!m_Construction)
2118 m_Construction = new Construction(this);
2119
2120 GetConstruction().Init();
2121 }
2122
2124 {
2125 return m_Construction;
2126 }
2127
2128 //--- INVENTORY/ATTACHMENTS CONDITIONS
2129 //attachments
2131 {
2132 return super.CanReceiveAttachment(attachment, slotId);
2133 }
2134
2136 {
2137 int attachment_count = GetInventory().AttachmentCount();
2138 if (attachment_count > 0)
2139 {
2140 if (HasBase() && attachment_count == 1)
2141 return false;
2142
2143 return true;
2144 }
2145
2146 return false;
2147 }
2148
2149 override bool ShowZonesHealth()
2150 {
2151 return true;
2152 }
2153
2154 //this into/outo parent.Cargo
2155 override bool CanPutInCargo(EntityAI parent)
2156 {
2157 return false;
2158 }
2159
2160 override bool CanRemoveFromCargo(EntityAI parent)
2161 {
2162 return false;
2163 }
2164
2165 //hands
2166 override bool CanPutIntoHands(EntityAI parent)
2167 {
2168 return false;
2169 }
2170
2171 //--- ACTION CONDITIONS
2172 //direction
2173 override bool IsFacingPlayer(PlayerBase player, string selection)
2174 {
2175 return true;
2176 }
2177
2178 override bool IsPlayerInside(PlayerBase player, string selection)
2179 {
2180 return true;
2181 }
2182
2185 {
2186 return false;
2187 }
2188
2189 //camera direction check
2190 bool IsFacingCamera(string selection)
2191 {
2192 return true;
2193 }
2194
2195 //roof check
2197 {
2198 return false;
2199 }
2200
2201 //selection->player distance check
2202 bool HasProperDistance(string selection, PlayerBase player)
2203 {
2204 return true;
2205 }
2206
2207 //folding
2209 {
2210 if (HasBase() || GetInventory().AttachmentCount() > 0)
2211 return false;
2212
2213 return true;
2214 }
2215
2217 {
2220
2221 return item;
2222 }
2223
2224 //Damage triggers (barbed wire)
2225 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2226 {
2227 if (GetGame() && GetGame().IsServer())
2228 {
2229 //destroy area damage if some already exists
2231
2232 //create new area damage
2234 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2235
2236 vector min_max[2];
2237 if (MemoryPointExists(slot_name + "_min"))
2238 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2239 if (MemoryPointExists(slot_name + "_max"))
2240 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2241
2242 //get proper trigger extents (min<max)
2243 vector extents[2];
2244 GetConstruction().GetTriggerExtents(min_max, extents);
2245
2246 //get box center
2247 vector center;
2248 center = GetConstruction().GetBoxCenter(min_max);
2249 center = ModelToWorld(center);
2250
2251 //rotate center if needed
2254
2255 areaDamage.SetExtents(extents[0], extents[1]);
2256 areaDamage.SetAreaPosition(center);
2257 areaDamage.SetAreaOrientation(orientation);
2258 areaDamage.SetLoopInterval(1.0);
2259 areaDamage.SetDeferDuration(0.2);
2260 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2261 areaDamage.SetAmmoName("BarbedWireHit");
2262 areaDamage.Spawn();
2263
2265 }
2266 }
2267
2269 {
2270 if (angle_deg != 0)
2271 {
2272 //orientation
2274
2275 //center
2277 if (MemoryPointExists("rotate_axis"))
2278 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2281 center[0] = r_center_x;
2282 center[2] = r_center_z;
2283 }
2284 }
2285
2286 void DestroyAreaDamage(string slot_name)
2287 {
2288 if (GetGame() && GetGame().IsServer())
2289 {
2292 {
2293 if (areaDamage)
2294 areaDamage.Destroy();
2295
2297 }
2298 }
2299 }
2300
2301 override bool IsIgnoredByConstruction()
2302 {
2303 return true;
2304 }
2305
2306 //================================================================
2307 // SOUNDS
2308 //================================================================
2309 protected void SoundBuildStart(string part_name)
2310 {
2311 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2312 }
2313
2314 protected void SoundDismantleStart(string part_name)
2315 {
2316 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2317 }
2318
2319 protected void SoundDestroyStart(string part_name)
2320 {
2321 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2322 }
2323
2324 protected string GetBuildSoundByMaterial(string part_name)
2325 {
2327
2328 switch (material_type)
2329 {
2330 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2331 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2332 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2333 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2334 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2335 }
2336
2337 return "";
2338 }
2339
2340 protected string GetDismantleSoundByMaterial(string part_name)
2341 {
2343
2344 switch (material_type)
2345 {
2346 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2347 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2348 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2349 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2350 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2351 }
2352
2353 return "";
2354 }
2355
2356 //misc
2358 {
2359 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2360 {
2361 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2363 SetHealth(slot_name, "Health", item.GetHealth());
2364 }
2365 }
2366
2367 override int GetDamageSystemVersionChange()
2368 {
2369 return 111;
2370 }
2371
2372 override void SetActions()
2373 {
2374 super.SetActions();
2375
2377 //AddAction(ActionTakeHybridAttachment);
2378 //AddAction(ActionTakeHybridAttachmentToHands);
2381 }
2382
2383 //================================================================
2384 // DEBUG
2385 //================================================================
2386 protected void DebugCustomState()
2387 {
2388 }
2389
2392 {
2393 return null;
2394 }
2395
2396 override void OnDebugSpawn()
2397 {
2398 FullyBuild();
2399 }
2400
2401 void FullyBuild()
2402 {
2404 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2405
2406 Man p;
2407
2408#ifdef SERVER
2410 GetGame().GetWorld().GetPlayerList(players);
2411 if (players.Count())
2412 p = players[0];
2413#else
2414 p = GetGame().GetPlayer();
2415#endif
2416
2417 foreach (ConstructionPart part : parts)
2418 {
2419 bool excluded = false;
2420 string partName = part.GetPartName();
2421 if (excludes)
2422 {
2423 foreach (string exclude : excludes)
2424 {
2425 if (partName.Contains(exclude))
2426 {
2427 excluded = true;
2428 break;
2429 }
2430 }
2431 }
2432
2433 if (!excluded)
2435 }
2436
2437 GetConstruction().UpdateVisuals();
2438 }
2439}
2440
2441void bsbDebugPrint(string s)
2442{
2443#ifdef BSB_DEBUG
2444 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2445#else
2446 //Print("" + s); // comment/uncomment to hide/see debug logs
2447#endif
2448}
2449void bsbDebugSpam(string s)
2450{
2451#ifdef BSB_DEBUG_SPAM
2452 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2453#else
2454 //Print("" + s); // comment/uncomment to hide/see debug logs
2455#endif
2456}

Referenced by BaseBuildingBase::CloseFence(), ItemBase::OnPartBuiltServer(), ItemBase::OnPartDestroyedServer(), ItemBase::OnPartDismantledServer(), BaseBuildingBase::OpenFence(), and ItemBase::SetPartsAfterStoreLoad().

◆ UnregisterPartForSync()

void bsbDebugPrint::UnregisterPartForSync ( int part_id)
protected

Definition at line 1322 of file BaseBuildingBase.c.

1324{
1325 const string ANIMATION_DEPLOYED = "Deployed";
1326
1327 float m_ConstructionKitHealth; //stored health value for used construction kit
1328
1330
1331 bool m_HasBase;
1332 //variables for synchronization of base building parts (2x31 is the current limit)
1333 int m_SyncParts01; //synchronization for already built parts (31 parts)
1334 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1335 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1336 int m_InteractedPartId; //construction part id that an action was performed on
1337 int m_PerformedActionId; //action id that was performed on a construction part
1338
1339 //Sounds
1340 //build
1341 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1342 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1343 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1344 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1345 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1346 //dismantle
1347 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1348 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1349 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1350 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1351 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1352
1353 protected EffectSound m_Sound;
1354
1358
1359 // Constructor
1360 void BaseBuildingBase()
1361 {
1363
1364 //synchronized variables
1365 RegisterNetSyncVariableInt("m_SyncParts01");
1366 RegisterNetSyncVariableInt("m_SyncParts02");
1367 RegisterNetSyncVariableInt("m_SyncParts03");
1368 RegisterNetSyncVariableInt("m_InteractedPartId");
1369 RegisterNetSyncVariableInt("m_PerformedActionId");
1370 RegisterNetSyncVariableBool("m_HasBase");
1371
1372 //Construction init
1374
1375 if (ConfigIsExisting("hybridAttachments"))
1376 {
1378 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1379 }
1380 if (ConfigIsExisting("mountables"))
1381 {
1383 ConfigGetTextArray("mountables", m_Mountables);
1384 }
1385
1386 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1387 }
1388
1389 override void EEDelete(EntityAI parent)
1390 {
1391 super.EEDelete(parent);
1392
1393 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1395
1396 }
1397
1398 override string GetInvulnerabilityTypeString()
1399 {
1400 return "disableBaseDamage";
1401 }
1402
1403 override bool CanObstruct()
1404 {
1405 return true;
1406 }
1407
1408 override int GetHideIconMask()
1409 {
1410 return EInventoryIconVisibility.HIDE_VICINITY;
1411 }
1412
1413 // --- SYNCHRONIZATION
1415 {
1416 if (GetGame().IsServer())
1417 SetSynchDirty();
1418 }
1419
1420 override void OnVariablesSynchronized()
1421 {
1422 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1423 super.OnVariablesSynchronized();
1424
1425 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1426 }
1427
1428 protected void OnSynchronizedClient()
1429 {
1430 //update parts
1432
1433 //update action on part
1435
1436 //update visuals (client)
1437 UpdateVisuals();
1438 }
1439
1440 //parts synchronization
1442 {
1443 //part_id must starts from index = 1
1444 int offset;
1445 int mask;
1446
1447 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1448 {
1449 offset = part_id - 1;
1450 mask = 1 << offset;
1451
1453 }
1454 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1455 {
1456 offset = (part_id % 32);
1457 mask = 1 << offset;
1458
1460 }
1461 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1462 {
1463 offset = (part_id % 63);
1464 mask = 1 << offset;
1465
1467 }
1468 }
1469
1471 {
1472 //part_id must starts from index = 1
1473 int offset;
1474 int mask;
1475
1476 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1477 {
1478 offset = part_id - 1;
1479 mask = 1 << offset;
1480
1482 }
1483 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1484 {
1485 offset = (part_id % 32);
1486 mask = 1 << offset;
1487
1489 }
1490 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1491 {
1492 offset = (part_id % 63);
1493 mask = 1 << offset;
1494
1496 }
1497 }
1498
1500 {
1501 //part_id must starts from index = 1
1502 int offset;
1503 int mask;
1504
1505 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1506 {
1507 offset = part_id - 1;
1508 mask = 1 << offset;
1509
1510 if ((m_SyncParts01 & mask) > 0)
1511 return true;
1512 }
1513 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1514 {
1515 offset = (part_id % 32);
1516 mask = 1 << offset;
1517
1518 if ((m_SyncParts02 & mask) > 0)
1519 return true;
1520 }
1521 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1522 {
1523 offset = (part_id % 63);
1524 mask = 1 << offset;
1525
1526 if ((m_SyncParts03 & mask) > 0)
1527 return true;
1528 }
1529
1530 return false;
1531 }
1532
1533 protected void RegisterActionForSync(int part_id, int action_id)
1534 {
1537 }
1538
1539 protected void ResetActionSyncData()
1540 {
1541 //reset data
1542 m_InteractedPartId = -1;
1544 }
1545
1546 protected void SetActionFromSyncData()
1547 {
1548 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
1549 {
1552
1553 switch (build_action_id)
1554 {
1558 }
1559 }
1560 }
1561 //------
1562
1564 {
1565 string key = part.m_PartName;
1566 bool is_base = part.IsBase();
1568 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
1570 {
1571 if (!part.IsBuilt())
1572 {
1573 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
1574 GetConstruction().AddToConstructedParts(key);
1575 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
1576
1577 if (is_base)
1578 {
1580 RemoveProxyPhysics(ANIMATION_DEPLOYED);
1581 }
1582 }
1583 }
1584 else
1585 {
1586 if (part.IsBuilt())
1587 {
1588 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
1589 GetConstruction().RemoveFromConstructedParts(key);
1590 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
1591
1592 if (is_base)
1593 {
1595 AddProxyPhysics(ANIMATION_DEPLOYED);
1596 }
1597 }
1598 }
1599
1600 //check slot lock for material attachments
1601 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
1602 }
1603
1604 //set construction parts based on synchronized data
1606 {
1609
1610 for (int i = 0; i < construction_parts.Count(); ++i)
1611 {
1612 string key = construction_parts.GetKey(i);
1615 }
1616
1617 //regenerate navmesh
1618 UpdateNavmesh();
1619 }
1620
1622 {
1625
1626 for (int i = 0; i < construction_parts.Count(); ++i)
1627 {
1628 string key = construction_parts.GetKey(i);
1630
1631 if (value.GetId() == id)
1632 return value;
1633 }
1634
1635 return NULL;
1636 }
1637 //
1638
1639 //Base
1640 bool HasBase()
1641 {
1642 return m_HasBase;
1643 }
1644
1645 void SetBaseState(bool has_base)
1646 {
1648 }
1649
1650 override bool IsDeployable()
1651 {
1652 return true;
1653 }
1654
1655 bool IsOpened()
1656 {
1657 return false;
1658 }
1659
1660 //--- CONSTRUCTION KIT
1662 {
1666
1667 return construction_kit;
1668 }
1669
1671 {
1672 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
1675 }
1676
1677 protected vector GetKitSpawnPosition()
1678 {
1679 return GetPosition();
1680 }
1681
1682 protected string GetConstructionKitType()
1683 {
1684 return "";
1685 }
1686
1688 {
1690 GetGame().ObjectDelete(construction_kit);
1691 }
1692
1693 //--- CONSTRUCTION
1694 void DestroyConstruction()
1695 {
1696 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
1697 GetGame().ObjectDelete(this);
1698 }
1699
1700 // --- EVENTS
1701 override void OnStoreSave(ParamsWriteContext ctx)
1702 {
1703 super.OnStoreSave(ctx);
1704
1705 //sync parts 01
1706 ctx.Write(m_SyncParts01);
1707 ctx.Write(m_SyncParts02);
1708 ctx.Write(m_SyncParts03);
1709
1710 ctx.Write(m_HasBase);
1711 }
1712
1713 override bool OnStoreLoad(ParamsReadContext ctx, int version)
1714 {
1715 if (!super.OnStoreLoad(ctx, version))
1716 return false;
1717
1718 //--- Base building data ---
1719 //Restore synced parts data
1720 if (!ctx.Read(m_SyncParts01))
1721 {
1722 m_SyncParts01 = 0; //set default
1723 return false;
1724 }
1725 if (!ctx.Read(m_SyncParts02))
1726 {
1727 m_SyncParts02 = 0; //set default
1728 return false;
1729 }
1730 if (!ctx.Read(m_SyncParts03))
1731 {
1732 m_SyncParts03 = 0; //set default
1733 return false;
1734 }
1735
1736 //has base
1737 if (!ctx.Read(m_HasBase))
1738 {
1739 m_HasBase = false;
1740 return false;
1741 }
1742 //---
1743
1744 return true;
1745 }
1746
1747 override void AfterStoreLoad()
1748 {
1749 super.AfterStoreLoad();
1750
1753 }
1754
1756 {
1757 //update server data
1759
1760 //set base state
1761 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
1762 SetBaseState(construction_part.IsBuilt()) ;
1763
1764 //synchronize after load
1766 }
1767
1768 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
1769 {
1771 return;
1772
1773 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
1774
1775 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
1776 return;
1777
1779 string part_name = zone;
1780 part_name.ToLower();
1781
1783 {
1785
1786 if (construction_part && construction.IsPartConstructed(part_name))
1787 {
1788 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
1789 construction.DestroyConnectedParts(part_name);
1790 }
1791
1792 //barbed wire handling (hack-ish)
1793 if (part_name.Contains("barbed"))
1794 {
1795 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
1796 if (barbed_wire)
1797 barbed_wire.SetMountedState(false);
1798 }
1799 }
1800 }
1801
1802 override void EEOnAfterLoad()
1803 {
1805 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
1806
1807 super.EEOnAfterLoad();
1808 }
1809
1810 override void EEInit()
1811 {
1812 super.EEInit();
1813
1814 // init visuals and physics
1815 InitBaseState();
1816
1817 //debug
1818#ifdef DEVELOPER
1820#endif
1821 }
1822
1823 override void EEItemAttached(EntityAI item, string slot_name)
1824 {
1825 super.EEItemAttached(item, slot_name);
1826
1828 UpdateVisuals();
1830 }
1831
1832 override void EEItemDetached(EntityAI item, string slot_name)
1833 {
1834 super.EEItemDetached(item, slot_name);
1835
1836 UpdateVisuals();
1838 }
1839
1840 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
1841 {
1843 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
1844
1847 }
1848
1849 //ignore out of reach condition
1850 override bool IgnoreOutOfReachCondition()
1851 {
1852 return true;
1853 }
1854
1855 //CONSTRUCTION EVENTS
1856 //Build
1857 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
1858 {
1860
1861 //check base state
1862 if (construtionPart.IsBase())
1863 {
1864 SetBaseState(true);
1865
1866 //spawn kit
1868 }
1869
1870 //register constructed parts for synchronization
1872
1873 //register action that was performed on part
1875
1876 //synchronize
1878
1879 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
1880
1881 UpdateNavmesh();
1882
1883 //update visuals
1884 UpdateVisuals();
1885
1886 //reset action sync data
1887 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1888 }
1889
1890 void OnPartBuiltClient(string part_name, int action_id)
1891 {
1892 //play sound
1894 }
1895
1896 //Dismantle
1898 {
1899 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
1901
1902 //register constructed parts for synchronization
1904
1905 //register action that was performed on part
1907
1908 //synchronize
1910
1911 // server part of sync, client will be synced from SetPartsFromSyncData
1913
1914 UpdateNavmesh();
1915
1916 //update visuals
1917 UpdateVisuals();
1918
1919 //reset action sync data
1920 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1921
1922 //check base state
1923 if (construtionPart.IsBase())
1924 {
1925 //Destroy construction
1926 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1927 }
1928 }
1929
1931 {
1932 //play sound
1934 }
1935
1936 //Destroy
1938 {
1939 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
1941
1942 //register constructed parts for synchronization
1944
1945 //register action that was performed on part
1947
1948 //synchronize
1950
1951 // server part of sync, client will be synced from SetPartsFromSyncData
1953
1954 UpdateNavmesh();
1955
1956 //update visuals
1957 UpdateVisuals();
1958
1959 //reset action sync data
1960 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
1961
1962 //check base state
1963 if (construtionPart.IsBase())
1964 {
1965 //Destroy construction
1966 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
1967 }
1968 }
1969
1970 void OnPartDestroyedClient(string part_name, int action_id)
1971 {
1972 //play sound
1974 }
1975
1976 // --- UPDATE
1977 void InitBaseState()
1978 {
1979 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
1980
1981 InitVisuals();
1982 UpdateNavmesh(); //regenerate navmesh
1983 GetConstruction().InitBaseState();
1984 }
1985
1986 void InitVisuals()
1987 {
1988 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
1989 //check base
1990 if (!HasBase())
1991 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
1992 else
1993 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
1994
1995 GetConstruction().UpdateVisuals();
1996 }
1997
1998 void UpdateVisuals()
1999 {
2001
2003 foreach (string slotName : attachmentSlots)
2005
2006 //check base
2007 if (!HasBase())
2008 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2009 else
2010 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2011
2012 GetConstruction().UpdateVisuals();
2013 }
2014
2016 {
2017 string slotNameMounted = slot_name + "_Mounted";
2018 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2019
2020 if (attachment)
2021 {
2022 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2023 if (barbedWire && barbedWire.IsMounted())
2025 else
2027
2028 if (is_locked)
2029 {
2030 SetAnimationPhase(slotNameMounted, 0);
2031 SetAnimationPhase(slot_name, 1);
2032 }
2033 else
2034 {
2035 SetAnimationPhase(slotNameMounted, 1);
2036 SetAnimationPhase(slot_name, 0);
2037 }
2038 }
2039 else
2040 {
2041 SetAnimationPhase(slotNameMounted, 1);
2042 SetAnimationPhase(slot_name, 1);
2043
2045 }
2046 }
2047
2048 // avoid calling this function on frequent occasions, it's a massive performance hit
2049 void UpdatePhysics()
2050 {
2052 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2053
2056
2058 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2059
2060 foreach (string slotName : attachmentSlots)
2062
2063 //check base
2064 if (!HasBase())
2065 {
2067 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2068
2069 AddProxyPhysics(ANIMATION_DEPLOYED);
2070 }
2071 else
2072 {
2074 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2075
2076 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2077 }
2078
2079 GetConstruction().UpdatePhysics();
2080 UpdateNavmesh();
2081 }
2082
2084 {
2085 //checks for invalid appends; hotfix
2086 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2087 return;
2088 //----------------------------------
2089 string slot_name_mounted = slot_name + "_Mounted";
2090 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2091
2092 //remove proxy physics
2093 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2094 RemoveProxyPhysics(slot_name_mounted);
2095 RemoveProxyPhysics(slot_name);
2096
2097 if (attachment)
2098 {
2099 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2100 if (is_locked)
2101 {
2102 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2103 AddProxyPhysics(slot_name_mounted);
2104 }
2105 else
2106 {
2107 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2108 AddProxyPhysics(slot_name);
2109 }
2110 }
2111 }
2112
2113 protected void UpdateNavmesh()
2114 {
2115 SetAffectPathgraph(true, false);
2116 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2117 }
2118
2119 override bool CanUseConstruction()
2120 {
2121 return true;
2122 }
2123
2124 override bool CanUseConstructionBuild()
2125 {
2126 return true;
2127 }
2128
2130 {
2131 if (attachment)
2132 {
2134 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2135
2136 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2137 }
2138
2139 return false;
2140 }
2141
2142 protected bool IsAttachmentSlotLocked(string slot_name)
2143 {
2144 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2145 }
2146
2147 //--- ATTACHMENT SLOTS
2149 {
2150 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2151 if (GetGame().ConfigIsExisting(config_path))
2152 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2153 }
2154
2156 {
2157 return true;
2158 }
2159
2160 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2161 {
2162 return true;
2163 }
2164
2165 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2166 {
2167 return true;
2168 }
2169
2170 // --- INIT
2171 void ConstructionInit()
2172 {
2173 if (!m_Construction)
2174 m_Construction = new Construction(this);
2175
2176 GetConstruction().Init();
2177 }
2178
2180 {
2181 return m_Construction;
2182 }
2183
2184 //--- INVENTORY/ATTACHMENTS CONDITIONS
2185 //attachments
2187 {
2188 return super.CanReceiveAttachment(attachment, slotId);
2189 }
2190
2192 {
2193 int attachment_count = GetInventory().AttachmentCount();
2194 if (attachment_count > 0)
2195 {
2196 if (HasBase() && attachment_count == 1)
2197 return false;
2198
2199 return true;
2200 }
2201
2202 return false;
2203 }
2204
2205 override bool ShowZonesHealth()
2206 {
2207 return true;
2208 }
2209
2210 //this into/outo parent.Cargo
2211 override bool CanPutInCargo(EntityAI parent)
2212 {
2213 return false;
2214 }
2215
2216 override bool CanRemoveFromCargo(EntityAI parent)
2217 {
2218 return false;
2219 }
2220
2221 //hands
2222 override bool CanPutIntoHands(EntityAI parent)
2223 {
2224 return false;
2225 }
2226
2227 //--- ACTION CONDITIONS
2228 //direction
2229 override bool IsFacingPlayer(PlayerBase player, string selection)
2230 {
2231 return true;
2232 }
2233
2234 override bool IsPlayerInside(PlayerBase player, string selection)
2235 {
2236 return true;
2237 }
2238
2241 {
2242 return false;
2243 }
2244
2245 //camera direction check
2246 bool IsFacingCamera(string selection)
2247 {
2248 return true;
2249 }
2250
2251 //roof check
2253 {
2254 return false;
2255 }
2256
2257 //selection->player distance check
2258 bool HasProperDistance(string selection, PlayerBase player)
2259 {
2260 return true;
2261 }
2262
2263 //folding
2265 {
2266 if (HasBase() || GetInventory().AttachmentCount() > 0)
2267 return false;
2268
2269 return true;
2270 }
2271
2273 {
2276
2277 return item;
2278 }
2279
2280 //Damage triggers (barbed wire)
2281 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2282 {
2283 if (GetGame() && GetGame().IsServer())
2284 {
2285 //destroy area damage if some already exists
2287
2288 //create new area damage
2290 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2291
2292 vector min_max[2];
2293 if (MemoryPointExists(slot_name + "_min"))
2294 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2295 if (MemoryPointExists(slot_name + "_max"))
2296 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2297
2298 //get proper trigger extents (min<max)
2299 vector extents[2];
2300 GetConstruction().GetTriggerExtents(min_max, extents);
2301
2302 //get box center
2303 vector center;
2304 center = GetConstruction().GetBoxCenter(min_max);
2305 center = ModelToWorld(center);
2306
2307 //rotate center if needed
2310
2311 areaDamage.SetExtents(extents[0], extents[1]);
2312 areaDamage.SetAreaPosition(center);
2313 areaDamage.SetAreaOrientation(orientation);
2314 areaDamage.SetLoopInterval(1.0);
2315 areaDamage.SetDeferDuration(0.2);
2316 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2317 areaDamage.SetAmmoName("BarbedWireHit");
2318 areaDamage.Spawn();
2319
2321 }
2322 }
2323
2325 {
2326 if (angle_deg != 0)
2327 {
2328 //orientation
2330
2331 //center
2333 if (MemoryPointExists("rotate_axis"))
2334 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2337 center[0] = r_center_x;
2338 center[2] = r_center_z;
2339 }
2340 }
2341
2342 void DestroyAreaDamage(string slot_name)
2343 {
2344 if (GetGame() && GetGame().IsServer())
2345 {
2348 {
2349 if (areaDamage)
2350 areaDamage.Destroy();
2351
2353 }
2354 }
2355 }
2356
2357 override bool IsIgnoredByConstruction()
2358 {
2359 return true;
2360 }
2361
2362 //================================================================
2363 // SOUNDS
2364 //================================================================
2365 protected void SoundBuildStart(string part_name)
2366 {
2367 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2368 }
2369
2370 protected void SoundDismantleStart(string part_name)
2371 {
2372 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2373 }
2374
2375 protected void SoundDestroyStart(string part_name)
2376 {
2377 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2378 }
2379
2380 protected string GetBuildSoundByMaterial(string part_name)
2381 {
2383
2384 switch (material_type)
2385 {
2386 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2387 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2388 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2389 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2390 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2391 }
2392
2393 return "";
2394 }
2395
2396 protected string GetDismantleSoundByMaterial(string part_name)
2397 {
2399
2400 switch (material_type)
2401 {
2402 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2403 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2404 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2405 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2406 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2407 }
2408
2409 return "";
2410 }
2411
2412 //misc
2414 {
2415 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2416 {
2417 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2419 SetHealth(slot_name, "Health", item.GetHealth());
2420 }
2421 }
2422
2423 override int GetDamageSystemVersionChange()
2424 {
2425 return 111;
2426 }
2427
2428 override void SetActions()
2429 {
2430 super.SetActions();
2431
2433 //AddAction(ActionTakeHybridAttachment);
2434 //AddAction(ActionTakeHybridAttachmentToHands);
2437 }
2438
2439 //================================================================
2440 // DEBUG
2441 //================================================================
2442 protected void DebugCustomState()
2443 {
2444 }
2445
2448 {
2449 return null;
2450 }
2451
2452 override void OnDebugSpawn()
2453 {
2454 FullyBuild();
2455 }
2456
2457 void FullyBuild()
2458 {
2460 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2461
2462 Man p;
2463
2464#ifdef SERVER
2466 GetGame().GetWorld().GetPlayerList(players);
2467 if (players.Count())
2468 p = players[0];
2469#else
2470 p = GetGame().GetPlayer();
2471#endif
2472
2473 foreach (ConstructionPart part : parts)
2474 {
2475 bool excluded = false;
2476 string partName = part.GetPartName();
2477 if (excludes)
2478 {
2479 foreach (string exclude : excludes)
2480 {
2481 if (partName.Contains(exclude))
2482 {
2483 excluded = true;
2484 break;
2485 }
2486 }
2487 }
2488
2489 if (!excluded)
2491 }
2492
2493 GetConstruction().UpdateVisuals();
2494 }
2495}
2496
2497void bsbDebugPrint(string s)
2498{
2499#ifdef BSB_DEBUG
2500 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2501#else
2502 //Print("" + s); // comment/uncomment to hide/see debug logs
2503#endif
2504}
2505void bsbDebugSpam(string s)
2506{
2507#ifdef BSB_DEBUG_SPAM
2508 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
2509#else
2510 //Print("" + s); // comment/uncomment to hide/see debug logs
2511#endif
2512}

Referenced by ItemBase::OnPartDestroyedServer(), and ItemBase::OnPartDismantledServer().

◆ UpdateAttachmentPhysics()

void bsbDebugPrint::UpdateAttachmentPhysics ( string slot_name,
bool is_locked )
protected

Definition at line 1935 of file BaseBuildingBase.c.

1937{
1938 const string ANIMATION_DEPLOYED = "Deployed";
1939
1940 float m_ConstructionKitHealth; //stored health value for used construction kit
1941
1943
1944 bool m_HasBase;
1945 //variables for synchronization of base building parts (2x31 is the current limit)
1946 int m_SyncParts01; //synchronization for already built parts (31 parts)
1947 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1948 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1949 int m_InteractedPartId; //construction part id that an action was performed on
1950 int m_PerformedActionId; //action id that was performed on a construction part
1951
1952 //Sounds
1953 //build
1954 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1955 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1956 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1957 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1958 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1959 //dismantle
1960 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1961 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1962 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1963 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1964 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1965
1966 protected EffectSound m_Sound;
1967
1971
1972 // Constructor
1973 void BaseBuildingBase()
1974 {
1976
1977 //synchronized variables
1978 RegisterNetSyncVariableInt("m_SyncParts01");
1979 RegisterNetSyncVariableInt("m_SyncParts02");
1980 RegisterNetSyncVariableInt("m_SyncParts03");
1981 RegisterNetSyncVariableInt("m_InteractedPartId");
1982 RegisterNetSyncVariableInt("m_PerformedActionId");
1983 RegisterNetSyncVariableBool("m_HasBase");
1984
1985 //Construction init
1987
1988 if (ConfigIsExisting("hybridAttachments"))
1989 {
1991 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1992 }
1993 if (ConfigIsExisting("mountables"))
1994 {
1996 ConfigGetTextArray("mountables", m_Mountables);
1997 }
1998
1999 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2000 }
2001
2002 override void EEDelete(EntityAI parent)
2003 {
2004 super.EEDelete(parent);
2005
2006 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2008
2009 }
2010
2011 override string GetInvulnerabilityTypeString()
2012 {
2013 return "disableBaseDamage";
2014 }
2015
2016 override bool CanObstruct()
2017 {
2018 return true;
2019 }
2020
2021 override int GetHideIconMask()
2022 {
2023 return EInventoryIconVisibility.HIDE_VICINITY;
2024 }
2025
2026 // --- SYNCHRONIZATION
2028 {
2029 if (GetGame().IsServer())
2030 SetSynchDirty();
2031 }
2032
2033 override void OnVariablesSynchronized()
2034 {
2035 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2036 super.OnVariablesSynchronized();
2037
2038 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2039 }
2040
2041 protected void OnSynchronizedClient()
2042 {
2043 //update parts
2045
2046 //update action on part
2048
2049 //update visuals (client)
2050 UpdateVisuals();
2051 }
2052
2053 //parts synchronization
2055 {
2056 //part_id must starts from index = 1
2057 int offset;
2058 int mask;
2059
2060 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2061 {
2062 offset = part_id - 1;
2063 mask = 1 << offset;
2064
2066 }
2067 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2068 {
2069 offset = (part_id % 32);
2070 mask = 1 << offset;
2071
2073 }
2074 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2075 {
2076 offset = (part_id % 63);
2077 mask = 1 << offset;
2078
2080 }
2081 }
2082
2084 {
2085 //part_id must starts from index = 1
2086 int offset;
2087 int mask;
2088
2089 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2090 {
2091 offset = part_id - 1;
2092 mask = 1 << offset;
2093
2095 }
2096 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2097 {
2098 offset = (part_id % 32);
2099 mask = 1 << offset;
2100
2102 }
2103 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2104 {
2105 offset = (part_id % 63);
2106 mask = 1 << offset;
2107
2109 }
2110 }
2111
2113 {
2114 //part_id must starts from index = 1
2115 int offset;
2116 int mask;
2117
2118 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2119 {
2120 offset = part_id - 1;
2121 mask = 1 << offset;
2122
2123 if ((m_SyncParts01 & mask) > 0)
2124 return true;
2125 }
2126 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2127 {
2128 offset = (part_id % 32);
2129 mask = 1 << offset;
2130
2131 if ((m_SyncParts02 & mask) > 0)
2132 return true;
2133 }
2134 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2135 {
2136 offset = (part_id % 63);
2137 mask = 1 << offset;
2138
2139 if ((m_SyncParts03 & mask) > 0)
2140 return true;
2141 }
2142
2143 return false;
2144 }
2145
2146 protected void RegisterActionForSync(int part_id, int action_id)
2147 {
2150 }
2151
2152 protected void ResetActionSyncData()
2153 {
2154 //reset data
2155 m_InteractedPartId = -1;
2157 }
2158
2159 protected void SetActionFromSyncData()
2160 {
2161 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2162 {
2165
2166 switch (build_action_id)
2167 {
2171 }
2172 }
2173 }
2174 //------
2175
2177 {
2178 string key = part.m_PartName;
2179 bool is_base = part.IsBase();
2181 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2183 {
2184 if (!part.IsBuilt())
2185 {
2186 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2187 GetConstruction().AddToConstructedParts(key);
2188 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2189
2190 if (is_base)
2191 {
2193 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2194 }
2195 }
2196 }
2197 else
2198 {
2199 if (part.IsBuilt())
2200 {
2201 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2202 GetConstruction().RemoveFromConstructedParts(key);
2203 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2204
2205 if (is_base)
2206 {
2208 AddProxyPhysics(ANIMATION_DEPLOYED);
2209 }
2210 }
2211 }
2212
2213 //check slot lock for material attachments
2214 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2215 }
2216
2217 //set construction parts based on synchronized data
2219 {
2222
2223 for (int i = 0; i < construction_parts.Count(); ++i)
2224 {
2225 string key = construction_parts.GetKey(i);
2228 }
2229
2230 //regenerate navmesh
2231 UpdateNavmesh();
2232 }
2233
2235 {
2238
2239 for (int i = 0; i < construction_parts.Count(); ++i)
2240 {
2241 string key = construction_parts.GetKey(i);
2243
2244 if (value.GetId() == id)
2245 return value;
2246 }
2247
2248 return NULL;
2249 }
2250 //
2251
2252 //Base
2253 bool HasBase()
2254 {
2255 return m_HasBase;
2256 }
2257
2258 void SetBaseState(bool has_base)
2259 {
2261 }
2262
2263 override bool IsDeployable()
2264 {
2265 return true;
2266 }
2267
2268 bool IsOpened()
2269 {
2270 return false;
2271 }
2272
2273 //--- CONSTRUCTION KIT
2275 {
2279
2280 return construction_kit;
2281 }
2282
2284 {
2285 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2288 }
2289
2290 protected vector GetKitSpawnPosition()
2291 {
2292 return GetPosition();
2293 }
2294
2295 protected string GetConstructionKitType()
2296 {
2297 return "";
2298 }
2299
2301 {
2303 GetGame().ObjectDelete(construction_kit);
2304 }
2305
2306 //--- CONSTRUCTION
2307 void DestroyConstruction()
2308 {
2309 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2310 GetGame().ObjectDelete(this);
2311 }
2312
2313 // --- EVENTS
2314 override void OnStoreSave(ParamsWriteContext ctx)
2315 {
2316 super.OnStoreSave(ctx);
2317
2318 //sync parts 01
2319 ctx.Write(m_SyncParts01);
2320 ctx.Write(m_SyncParts02);
2321 ctx.Write(m_SyncParts03);
2322
2323 ctx.Write(m_HasBase);
2324 }
2325
2326 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2327 {
2328 if (!super.OnStoreLoad(ctx, version))
2329 return false;
2330
2331 //--- Base building data ---
2332 //Restore synced parts data
2333 if (!ctx.Read(m_SyncParts01))
2334 {
2335 m_SyncParts01 = 0; //set default
2336 return false;
2337 }
2338 if (!ctx.Read(m_SyncParts02))
2339 {
2340 m_SyncParts02 = 0; //set default
2341 return false;
2342 }
2343 if (!ctx.Read(m_SyncParts03))
2344 {
2345 m_SyncParts03 = 0; //set default
2346 return false;
2347 }
2348
2349 //has base
2350 if (!ctx.Read(m_HasBase))
2351 {
2352 m_HasBase = false;
2353 return false;
2354 }
2355 //---
2356
2357 return true;
2358 }
2359
2360 override void AfterStoreLoad()
2361 {
2362 super.AfterStoreLoad();
2363
2366 }
2367
2369 {
2370 //update server data
2372
2373 //set base state
2374 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2375 SetBaseState(construction_part.IsBuilt()) ;
2376
2377 //synchronize after load
2379 }
2380
2381 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2382 {
2384 return;
2385
2386 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2387
2388 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2389 return;
2390
2392 string part_name = zone;
2393 part_name.ToLower();
2394
2396 {
2398
2399 if (construction_part && construction.IsPartConstructed(part_name))
2400 {
2401 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2402 construction.DestroyConnectedParts(part_name);
2403 }
2404
2405 //barbed wire handling (hack-ish)
2406 if (part_name.Contains("barbed"))
2407 {
2408 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2409 if (barbed_wire)
2410 barbed_wire.SetMountedState(false);
2411 }
2412 }
2413 }
2414
2415 override void EEOnAfterLoad()
2416 {
2418 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2419
2420 super.EEOnAfterLoad();
2421 }
2422
2423 override void EEInit()
2424 {
2425 super.EEInit();
2426
2427 // init visuals and physics
2428 InitBaseState();
2429
2430 //debug
2431#ifdef DEVELOPER
2433#endif
2434 }
2435
2436 override void EEItemAttached(EntityAI item, string slot_name)
2437 {
2438 super.EEItemAttached(item, slot_name);
2439
2441 UpdateVisuals();
2443 }
2444
2445 override void EEItemDetached(EntityAI item, string slot_name)
2446 {
2447 super.EEItemDetached(item, slot_name);
2448
2449 UpdateVisuals();
2451 }
2452
2453 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2454 {
2456 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2457
2460 }
2461
2462 //ignore out of reach condition
2463 override bool IgnoreOutOfReachCondition()
2464 {
2465 return true;
2466 }
2467
2468 //CONSTRUCTION EVENTS
2469 //Build
2470 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2471 {
2473
2474 //check base state
2475 if (construtionPart.IsBase())
2476 {
2477 SetBaseState(true);
2478
2479 //spawn kit
2481 }
2482
2483 //register constructed parts for synchronization
2485
2486 //register action that was performed on part
2488
2489 //synchronize
2491
2492 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2493
2494 UpdateNavmesh();
2495
2496 //update visuals
2497 UpdateVisuals();
2498
2499 //reset action sync data
2500 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2501 }
2502
2503 void OnPartBuiltClient(string part_name, int action_id)
2504 {
2505 //play sound
2507 }
2508
2509 //Dismantle
2511 {
2512 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2514
2515 //register constructed parts for synchronization
2517
2518 //register action that was performed on part
2520
2521 //synchronize
2523
2524 // server part of sync, client will be synced from SetPartsFromSyncData
2526
2527 UpdateNavmesh();
2528
2529 //update visuals
2530 UpdateVisuals();
2531
2532 //reset action sync data
2533 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2534
2535 //check base state
2536 if (construtionPart.IsBase())
2537 {
2538 //Destroy construction
2539 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2540 }
2541 }
2542
2544 {
2545 //play sound
2547 }
2548
2549 //Destroy
2551 {
2552 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2554
2555 //register constructed parts for synchronization
2557
2558 //register action that was performed on part
2560
2561 //synchronize
2563
2564 // server part of sync, client will be synced from SetPartsFromSyncData
2566
2567 UpdateNavmesh();
2568
2569 //update visuals
2570 UpdateVisuals();
2571
2572 //reset action sync data
2573 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2574
2575 //check base state
2576 if (construtionPart.IsBase())
2577 {
2578 //Destroy construction
2579 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2580 }
2581 }
2582
2583 void OnPartDestroyedClient(string part_name, int action_id)
2584 {
2585 //play sound
2587 }
2588
2589 // --- UPDATE
2590 void InitBaseState()
2591 {
2592 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2593
2594 InitVisuals();
2595 UpdateNavmesh(); //regenerate navmesh
2596 GetConstruction().InitBaseState();
2597 }
2598
2599 void InitVisuals()
2600 {
2601 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2602 //check base
2603 if (!HasBase())
2604 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2605 else
2606 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2607
2608 GetConstruction().UpdateVisuals();
2609 }
2610
2611 void UpdateVisuals()
2612 {
2614
2616 foreach (string slotName : attachmentSlots)
2618
2619 //check base
2620 if (!HasBase())
2621 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2622 else
2623 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2624
2625 GetConstruction().UpdateVisuals();
2626 }
2627
2629 {
2630 string slotNameMounted = slot_name + "_Mounted";
2631 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2632
2633 if (attachment)
2634 {
2635 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2636 if (barbedWire && barbedWire.IsMounted())
2638 else
2640
2641 if (is_locked)
2642 {
2643 SetAnimationPhase(slotNameMounted, 0);
2644 SetAnimationPhase(slot_name, 1);
2645 }
2646 else
2647 {
2648 SetAnimationPhase(slotNameMounted, 1);
2649 SetAnimationPhase(slot_name, 0);
2650 }
2651 }
2652 else
2653 {
2654 SetAnimationPhase(slotNameMounted, 1);
2655 SetAnimationPhase(slot_name, 1);
2656
2658 }
2659 }
2660
2661 // avoid calling this function on frequent occasions, it's a massive performance hit
2662 void UpdatePhysics()
2663 {
2665 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2666
2669
2671 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2672
2673 foreach (string slotName : attachmentSlots)
2675
2676 //check base
2677 if (!HasBase())
2678 {
2680 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2681
2682 AddProxyPhysics(ANIMATION_DEPLOYED);
2683 }
2684 else
2685 {
2687 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2688
2689 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2690 }
2691
2692 GetConstruction().UpdatePhysics();
2693 UpdateNavmesh();
2694 }
2695
2697 {
2698 //checks for invalid appends; hotfix
2699 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2700 return;
2701 //----------------------------------
2702 string slot_name_mounted = slot_name + "_Mounted";
2703 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2704
2705 //remove proxy physics
2706 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2707 RemoveProxyPhysics(slot_name_mounted);
2708 RemoveProxyPhysics(slot_name);
2709
2710 if (attachment)
2711 {
2712 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2713 if (is_locked)
2714 {
2715 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2716 AddProxyPhysics(slot_name_mounted);
2717 }
2718 else
2719 {
2720 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2721 AddProxyPhysics(slot_name);
2722 }
2723 }
2724 }
2725
2726 protected void UpdateNavmesh()
2727 {
2728 SetAffectPathgraph(true, false);
2729 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2730 }
2731
2732 override bool CanUseConstruction()
2733 {
2734 return true;
2735 }
2736
2737 override bool CanUseConstructionBuild()
2738 {
2739 return true;
2740 }
2741
2743 {
2744 if (attachment)
2745 {
2747 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2748
2749 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2750 }
2751
2752 return false;
2753 }
2754
2755 protected bool IsAttachmentSlotLocked(string slot_name)
2756 {
2757 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2758 }
2759
2760 //--- ATTACHMENT SLOTS
2762 {
2763 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2764 if (GetGame().ConfigIsExisting(config_path))
2765 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2766 }
2767
2769 {
2770 return true;
2771 }
2772
2773 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2774 {
2775 return true;
2776 }
2777
2778 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2779 {
2780 return true;
2781 }
2782
2783 // --- INIT
2784 void ConstructionInit()
2785 {
2786 if (!m_Construction)
2787 m_Construction = new Construction(this);
2788
2789 GetConstruction().Init();
2790 }
2791
2793 {
2794 return m_Construction;
2795 }
2796
2797 //--- INVENTORY/ATTACHMENTS CONDITIONS
2798 //attachments
2800 {
2801 return super.CanReceiveAttachment(attachment, slotId);
2802 }
2803
2805 {
2806 int attachment_count = GetInventory().AttachmentCount();
2807 if (attachment_count > 0)
2808 {
2809 if (HasBase() && attachment_count == 1)
2810 return false;
2811
2812 return true;
2813 }
2814
2815 return false;
2816 }
2817
2818 override bool ShowZonesHealth()
2819 {
2820 return true;
2821 }
2822
2823 //this into/outo parent.Cargo
2824 override bool CanPutInCargo(EntityAI parent)
2825 {
2826 return false;
2827 }
2828
2829 override bool CanRemoveFromCargo(EntityAI parent)
2830 {
2831 return false;
2832 }
2833
2834 //hands
2835 override bool CanPutIntoHands(EntityAI parent)
2836 {
2837 return false;
2838 }
2839
2840 //--- ACTION CONDITIONS
2841 //direction
2842 override bool IsFacingPlayer(PlayerBase player, string selection)
2843 {
2844 return true;
2845 }
2846
2847 override bool IsPlayerInside(PlayerBase player, string selection)
2848 {
2849 return true;
2850 }
2851
2854 {
2855 return false;
2856 }
2857
2858 //camera direction check
2859 bool IsFacingCamera(string selection)
2860 {
2861 return true;
2862 }
2863
2864 //roof check
2866 {
2867 return false;
2868 }
2869
2870 //selection->player distance check
2871 bool HasProperDistance(string selection, PlayerBase player)
2872 {
2873 return true;
2874 }
2875
2876 //folding
2878 {
2879 if (HasBase() || GetInventory().AttachmentCount() > 0)
2880 return false;
2881
2882 return true;
2883 }
2884
2886 {
2889
2890 return item;
2891 }
2892
2893 //Damage triggers (barbed wire)
2894 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2895 {
2896 if (GetGame() && GetGame().IsServer())
2897 {
2898 //destroy area damage if some already exists
2900
2901 //create new area damage
2903 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2904
2905 vector min_max[2];
2906 if (MemoryPointExists(slot_name + "_min"))
2907 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2908 if (MemoryPointExists(slot_name + "_max"))
2909 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2910
2911 //get proper trigger extents (min<max)
2912 vector extents[2];
2913 GetConstruction().GetTriggerExtents(min_max, extents);
2914
2915 //get box center
2916 vector center;
2917 center = GetConstruction().GetBoxCenter(min_max);
2918 center = ModelToWorld(center);
2919
2920 //rotate center if needed
2923
2924 areaDamage.SetExtents(extents[0], extents[1]);
2925 areaDamage.SetAreaPosition(center);
2926 areaDamage.SetAreaOrientation(orientation);
2927 areaDamage.SetLoopInterval(1.0);
2928 areaDamage.SetDeferDuration(0.2);
2929 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2930 areaDamage.SetAmmoName("BarbedWireHit");
2931 areaDamage.Spawn();
2932
2934 }
2935 }
2936
2938 {
2939 if (angle_deg != 0)
2940 {
2941 //orientation
2943
2944 //center
2946 if (MemoryPointExists("rotate_axis"))
2947 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2950 center[0] = r_center_x;
2951 center[2] = r_center_z;
2952 }
2953 }
2954
2955 void DestroyAreaDamage(string slot_name)
2956 {
2957 if (GetGame() && GetGame().IsServer())
2958 {
2961 {
2962 if (areaDamage)
2963 areaDamage.Destroy();
2964
2966 }
2967 }
2968 }
2969
2970 override bool IsIgnoredByConstruction()
2971 {
2972 return true;
2973 }
2974
2975 //================================================================
2976 // SOUNDS
2977 //================================================================
2978 protected void SoundBuildStart(string part_name)
2979 {
2980 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2981 }
2982
2983 protected void SoundDismantleStart(string part_name)
2984 {
2985 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2986 }
2987
2988 protected void SoundDestroyStart(string part_name)
2989 {
2990 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2991 }
2992
2993 protected string GetBuildSoundByMaterial(string part_name)
2994 {
2996
2997 switch (material_type)
2998 {
2999 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3000 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3001 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3002 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3003 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3004 }
3005
3006 return "";
3007 }
3008
3009 protected string GetDismantleSoundByMaterial(string part_name)
3010 {
3012
3013 switch (material_type)
3014 {
3015 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3016 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3017 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3018 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3019 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3020 }
3021
3022 return "";
3023 }
3024
3025 //misc
3027 {
3028 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3029 {
3030 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3032 SetHealth(slot_name, "Health", item.GetHealth());
3033 }
3034 }
3035
3036 override int GetDamageSystemVersionChange()
3037 {
3038 return 111;
3039 }
3040
3041 override void SetActions()
3042 {
3043 super.SetActions();
3044
3046 //AddAction(ActionTakeHybridAttachment);
3047 //AddAction(ActionTakeHybridAttachmentToHands);
3050 }
3051
3052 //================================================================
3053 // DEBUG
3054 //================================================================
3055 protected void DebugCustomState()
3056 {
3057 }
3058
3061 {
3062 return null;
3063 }
3064
3065 override void OnDebugSpawn()
3066 {
3067 FullyBuild();
3068 }
3069
3070 void FullyBuild()
3071 {
3073 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3074
3075 Man p;
3076
3077#ifdef SERVER
3079 GetGame().GetWorld().GetPlayerList(players);
3080 if (players.Count())
3081 p = players[0];
3082#else
3083 p = GetGame().GetPlayer();
3084#endif
3085
3086 foreach (ConstructionPart part : parts)
3087 {
3088 bool excluded = false;
3089 string partName = part.GetPartName();
3090 if (excludes)
3091 {
3092 foreach (string exclude : excludes)
3093 {
3094 if (partName.Contains(exclude))
3095 {
3096 excluded = true;
3097 break;
3098 }
3099 }
3100 }
3101
3102 if (!excluded)
3104 }
3105
3106 GetConstruction().UpdateVisuals();
3107 }
3108}
3109
3110void bsbDebugPrint(string s)
3111{
3112#ifdef BSB_DEBUG
3113 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3114#else
3115 //Print("" + s); // comment/uncomment to hide/see debug logs
3116#endif
3117}
3118void bsbDebugSpam(string s)
3119{
3120#ifdef BSB_DEBUG_SPAM
3121 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3122#else
3123 //Print("" + s); // comment/uncomment to hide/see debug logs
3124#endif
3125}

Referenced by ItemBase::EEItemAttached(), ItemBase::EEItemDetached(), ItemBase::OnSetSlotLock(), and ItemBase::UpdatePhysics().

◆ UpdateAttachmentVisuals()

void bsbDebugPrint::UpdateAttachmentVisuals ( string slot_name,
bool is_locked )
protected

Definition at line 1867 of file BaseBuildingBase.c.

1869{
1870 const string ANIMATION_DEPLOYED = "Deployed";
1871
1872 float m_ConstructionKitHealth; //stored health value for used construction kit
1873
1875
1876 bool m_HasBase;
1877 //variables for synchronization of base building parts (2x31 is the current limit)
1878 int m_SyncParts01; //synchronization for already built parts (31 parts)
1879 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1880 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1881 int m_InteractedPartId; //construction part id that an action was performed on
1882 int m_PerformedActionId; //action id that was performed on a construction part
1883
1884 //Sounds
1885 //build
1886 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1887 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1888 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1889 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1890 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1891 //dismantle
1892 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1893 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1894 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1895 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1896 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1897
1898 protected EffectSound m_Sound;
1899
1903
1904 // Constructor
1905 void BaseBuildingBase()
1906 {
1908
1909 //synchronized variables
1910 RegisterNetSyncVariableInt("m_SyncParts01");
1911 RegisterNetSyncVariableInt("m_SyncParts02");
1912 RegisterNetSyncVariableInt("m_SyncParts03");
1913 RegisterNetSyncVariableInt("m_InteractedPartId");
1914 RegisterNetSyncVariableInt("m_PerformedActionId");
1915 RegisterNetSyncVariableBool("m_HasBase");
1916
1917 //Construction init
1919
1920 if (ConfigIsExisting("hybridAttachments"))
1921 {
1923 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1924 }
1925 if (ConfigIsExisting("mountables"))
1926 {
1928 ConfigGetTextArray("mountables", m_Mountables);
1929 }
1930
1931 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1932 }
1933
1934 override void EEDelete(EntityAI parent)
1935 {
1936 super.EEDelete(parent);
1937
1938 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1940
1941 }
1942
1943 override string GetInvulnerabilityTypeString()
1944 {
1945 return "disableBaseDamage";
1946 }
1947
1948 override bool CanObstruct()
1949 {
1950 return true;
1951 }
1952
1953 override int GetHideIconMask()
1954 {
1955 return EInventoryIconVisibility.HIDE_VICINITY;
1956 }
1957
1958 // --- SYNCHRONIZATION
1960 {
1961 if (GetGame().IsServer())
1962 SetSynchDirty();
1963 }
1964
1965 override void OnVariablesSynchronized()
1966 {
1967 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1968 super.OnVariablesSynchronized();
1969
1970 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1971 }
1972
1973 protected void OnSynchronizedClient()
1974 {
1975 //update parts
1977
1978 //update action on part
1980
1981 //update visuals (client)
1982 UpdateVisuals();
1983 }
1984
1985 //parts synchronization
1987 {
1988 //part_id must starts from index = 1
1989 int offset;
1990 int mask;
1991
1992 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1993 {
1994 offset = part_id - 1;
1995 mask = 1 << offset;
1996
1998 }
1999 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2000 {
2001 offset = (part_id % 32);
2002 mask = 1 << offset;
2003
2005 }
2006 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2007 {
2008 offset = (part_id % 63);
2009 mask = 1 << offset;
2010
2012 }
2013 }
2014
2016 {
2017 //part_id must starts from index = 1
2018 int offset;
2019 int mask;
2020
2021 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2022 {
2023 offset = part_id - 1;
2024 mask = 1 << offset;
2025
2027 }
2028 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2029 {
2030 offset = (part_id % 32);
2031 mask = 1 << offset;
2032
2034 }
2035 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2036 {
2037 offset = (part_id % 63);
2038 mask = 1 << offset;
2039
2041 }
2042 }
2043
2045 {
2046 //part_id must starts from index = 1
2047 int offset;
2048 int mask;
2049
2050 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2051 {
2052 offset = part_id - 1;
2053 mask = 1 << offset;
2054
2055 if ((m_SyncParts01 & mask) > 0)
2056 return true;
2057 }
2058 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2059 {
2060 offset = (part_id % 32);
2061 mask = 1 << offset;
2062
2063 if ((m_SyncParts02 & mask) > 0)
2064 return true;
2065 }
2066 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2067 {
2068 offset = (part_id % 63);
2069 mask = 1 << offset;
2070
2071 if ((m_SyncParts03 & mask) > 0)
2072 return true;
2073 }
2074
2075 return false;
2076 }
2077
2078 protected void RegisterActionForSync(int part_id, int action_id)
2079 {
2082 }
2083
2084 protected void ResetActionSyncData()
2085 {
2086 //reset data
2087 m_InteractedPartId = -1;
2089 }
2090
2091 protected void SetActionFromSyncData()
2092 {
2093 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2094 {
2097
2098 switch (build_action_id)
2099 {
2103 }
2104 }
2105 }
2106 //------
2107
2109 {
2110 string key = part.m_PartName;
2111 bool is_base = part.IsBase();
2113 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2115 {
2116 if (!part.IsBuilt())
2117 {
2118 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2119 GetConstruction().AddToConstructedParts(key);
2120 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2121
2122 if (is_base)
2123 {
2125 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2126 }
2127 }
2128 }
2129 else
2130 {
2131 if (part.IsBuilt())
2132 {
2133 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2134 GetConstruction().RemoveFromConstructedParts(key);
2135 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2136
2137 if (is_base)
2138 {
2140 AddProxyPhysics(ANIMATION_DEPLOYED);
2141 }
2142 }
2143 }
2144
2145 //check slot lock for material attachments
2146 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2147 }
2148
2149 //set construction parts based on synchronized data
2151 {
2154
2155 for (int i = 0; i < construction_parts.Count(); ++i)
2156 {
2157 string key = construction_parts.GetKey(i);
2160 }
2161
2162 //regenerate navmesh
2163 UpdateNavmesh();
2164 }
2165
2167 {
2170
2171 for (int i = 0; i < construction_parts.Count(); ++i)
2172 {
2173 string key = construction_parts.GetKey(i);
2175
2176 if (value.GetId() == id)
2177 return value;
2178 }
2179
2180 return NULL;
2181 }
2182 //
2183
2184 //Base
2185 bool HasBase()
2186 {
2187 return m_HasBase;
2188 }
2189
2190 void SetBaseState(bool has_base)
2191 {
2193 }
2194
2195 override bool IsDeployable()
2196 {
2197 return true;
2198 }
2199
2200 bool IsOpened()
2201 {
2202 return false;
2203 }
2204
2205 //--- CONSTRUCTION KIT
2207 {
2211
2212 return construction_kit;
2213 }
2214
2216 {
2217 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2220 }
2221
2222 protected vector GetKitSpawnPosition()
2223 {
2224 return GetPosition();
2225 }
2226
2227 protected string GetConstructionKitType()
2228 {
2229 return "";
2230 }
2231
2233 {
2235 GetGame().ObjectDelete(construction_kit);
2236 }
2237
2238 //--- CONSTRUCTION
2239 void DestroyConstruction()
2240 {
2241 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2242 GetGame().ObjectDelete(this);
2243 }
2244
2245 // --- EVENTS
2246 override void OnStoreSave(ParamsWriteContext ctx)
2247 {
2248 super.OnStoreSave(ctx);
2249
2250 //sync parts 01
2251 ctx.Write(m_SyncParts01);
2252 ctx.Write(m_SyncParts02);
2253 ctx.Write(m_SyncParts03);
2254
2255 ctx.Write(m_HasBase);
2256 }
2257
2258 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2259 {
2260 if (!super.OnStoreLoad(ctx, version))
2261 return false;
2262
2263 //--- Base building data ---
2264 //Restore synced parts data
2265 if (!ctx.Read(m_SyncParts01))
2266 {
2267 m_SyncParts01 = 0; //set default
2268 return false;
2269 }
2270 if (!ctx.Read(m_SyncParts02))
2271 {
2272 m_SyncParts02 = 0; //set default
2273 return false;
2274 }
2275 if (!ctx.Read(m_SyncParts03))
2276 {
2277 m_SyncParts03 = 0; //set default
2278 return false;
2279 }
2280
2281 //has base
2282 if (!ctx.Read(m_HasBase))
2283 {
2284 m_HasBase = false;
2285 return false;
2286 }
2287 //---
2288
2289 return true;
2290 }
2291
2292 override void AfterStoreLoad()
2293 {
2294 super.AfterStoreLoad();
2295
2298 }
2299
2301 {
2302 //update server data
2304
2305 //set base state
2306 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2307 SetBaseState(construction_part.IsBuilt()) ;
2308
2309 //synchronize after load
2311 }
2312
2313 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2314 {
2316 return;
2317
2318 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2319
2320 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2321 return;
2322
2324 string part_name = zone;
2325 part_name.ToLower();
2326
2328 {
2330
2331 if (construction_part && construction.IsPartConstructed(part_name))
2332 {
2333 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2334 construction.DestroyConnectedParts(part_name);
2335 }
2336
2337 //barbed wire handling (hack-ish)
2338 if (part_name.Contains("barbed"))
2339 {
2340 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2341 if (barbed_wire)
2342 barbed_wire.SetMountedState(false);
2343 }
2344 }
2345 }
2346
2347 override void EEOnAfterLoad()
2348 {
2350 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2351
2352 super.EEOnAfterLoad();
2353 }
2354
2355 override void EEInit()
2356 {
2357 super.EEInit();
2358
2359 // init visuals and physics
2360 InitBaseState();
2361
2362 //debug
2363#ifdef DEVELOPER
2365#endif
2366 }
2367
2368 override void EEItemAttached(EntityAI item, string slot_name)
2369 {
2370 super.EEItemAttached(item, slot_name);
2371
2373 UpdateVisuals();
2375 }
2376
2377 override void EEItemDetached(EntityAI item, string slot_name)
2378 {
2379 super.EEItemDetached(item, slot_name);
2380
2381 UpdateVisuals();
2383 }
2384
2385 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2386 {
2388 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2389
2392 }
2393
2394 //ignore out of reach condition
2395 override bool IgnoreOutOfReachCondition()
2396 {
2397 return true;
2398 }
2399
2400 //CONSTRUCTION EVENTS
2401 //Build
2402 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2403 {
2405
2406 //check base state
2407 if (construtionPart.IsBase())
2408 {
2409 SetBaseState(true);
2410
2411 //spawn kit
2413 }
2414
2415 //register constructed parts for synchronization
2417
2418 //register action that was performed on part
2420
2421 //synchronize
2423
2424 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2425
2426 UpdateNavmesh();
2427
2428 //update visuals
2429 UpdateVisuals();
2430
2431 //reset action sync data
2432 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2433 }
2434
2435 void OnPartBuiltClient(string part_name, int action_id)
2436 {
2437 //play sound
2439 }
2440
2441 //Dismantle
2443 {
2444 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2446
2447 //register constructed parts for synchronization
2449
2450 //register action that was performed on part
2452
2453 //synchronize
2455
2456 // server part of sync, client will be synced from SetPartsFromSyncData
2458
2459 UpdateNavmesh();
2460
2461 //update visuals
2462 UpdateVisuals();
2463
2464 //reset action sync data
2465 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2466
2467 //check base state
2468 if (construtionPart.IsBase())
2469 {
2470 //Destroy construction
2471 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2472 }
2473 }
2474
2476 {
2477 //play sound
2479 }
2480
2481 //Destroy
2483 {
2484 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2486
2487 //register constructed parts for synchronization
2489
2490 //register action that was performed on part
2492
2493 //synchronize
2495
2496 // server part of sync, client will be synced from SetPartsFromSyncData
2498
2499 UpdateNavmesh();
2500
2501 //update visuals
2502 UpdateVisuals();
2503
2504 //reset action sync data
2505 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2506
2507 //check base state
2508 if (construtionPart.IsBase())
2509 {
2510 //Destroy construction
2511 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2512 }
2513 }
2514
2515 void OnPartDestroyedClient(string part_name, int action_id)
2516 {
2517 //play sound
2519 }
2520
2521 // --- UPDATE
2522 void InitBaseState()
2523 {
2524 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2525
2526 InitVisuals();
2527 UpdateNavmesh(); //regenerate navmesh
2528 GetConstruction().InitBaseState();
2529 }
2530
2531 void InitVisuals()
2532 {
2533 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2534 //check base
2535 if (!HasBase())
2536 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2537 else
2538 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2539
2540 GetConstruction().UpdateVisuals();
2541 }
2542
2543 void UpdateVisuals()
2544 {
2546
2548 foreach (string slotName : attachmentSlots)
2550
2551 //check base
2552 if (!HasBase())
2553 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2554 else
2555 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2556
2557 GetConstruction().UpdateVisuals();
2558 }
2559
2561 {
2562 string slotNameMounted = slot_name + "_Mounted";
2563 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2564
2565 if (attachment)
2566 {
2567 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2568 if (barbedWire && barbedWire.IsMounted())
2570 else
2572
2573 if (is_locked)
2574 {
2575 SetAnimationPhase(slotNameMounted, 0);
2576 SetAnimationPhase(slot_name, 1);
2577 }
2578 else
2579 {
2580 SetAnimationPhase(slotNameMounted, 1);
2581 SetAnimationPhase(slot_name, 0);
2582 }
2583 }
2584 else
2585 {
2586 SetAnimationPhase(slotNameMounted, 1);
2587 SetAnimationPhase(slot_name, 1);
2588
2590 }
2591 }
2592
2593 // avoid calling this function on frequent occasions, it's a massive performance hit
2594 void UpdatePhysics()
2595 {
2597 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2598
2601
2603 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2604
2605 foreach (string slotName : attachmentSlots)
2607
2608 //check base
2609 if (!HasBase())
2610 {
2612 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2613
2614 AddProxyPhysics(ANIMATION_DEPLOYED);
2615 }
2616 else
2617 {
2619 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2620
2621 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2622 }
2623
2624 GetConstruction().UpdatePhysics();
2625 UpdateNavmesh();
2626 }
2627
2629 {
2630 //checks for invalid appends; hotfix
2631 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2632 return;
2633 //----------------------------------
2634 string slot_name_mounted = slot_name + "_Mounted";
2635 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2636
2637 //remove proxy physics
2638 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2639 RemoveProxyPhysics(slot_name_mounted);
2640 RemoveProxyPhysics(slot_name);
2641
2642 if (attachment)
2643 {
2644 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2645 if (is_locked)
2646 {
2647 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2648 AddProxyPhysics(slot_name_mounted);
2649 }
2650 else
2651 {
2652 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2653 AddProxyPhysics(slot_name);
2654 }
2655 }
2656 }
2657
2658 protected void UpdateNavmesh()
2659 {
2660 SetAffectPathgraph(true, false);
2661 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2662 }
2663
2664 override bool CanUseConstruction()
2665 {
2666 return true;
2667 }
2668
2669 override bool CanUseConstructionBuild()
2670 {
2671 return true;
2672 }
2673
2675 {
2676 if (attachment)
2677 {
2679 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2680
2681 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2682 }
2683
2684 return false;
2685 }
2686
2687 protected bool IsAttachmentSlotLocked(string slot_name)
2688 {
2689 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2690 }
2691
2692 //--- ATTACHMENT SLOTS
2694 {
2695 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2696 if (GetGame().ConfigIsExisting(config_path))
2697 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2698 }
2699
2701 {
2702 return true;
2703 }
2704
2705 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2706 {
2707 return true;
2708 }
2709
2710 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2711 {
2712 return true;
2713 }
2714
2715 // --- INIT
2716 void ConstructionInit()
2717 {
2718 if (!m_Construction)
2719 m_Construction = new Construction(this);
2720
2721 GetConstruction().Init();
2722 }
2723
2725 {
2726 return m_Construction;
2727 }
2728
2729 //--- INVENTORY/ATTACHMENTS CONDITIONS
2730 //attachments
2732 {
2733 return super.CanReceiveAttachment(attachment, slotId);
2734 }
2735
2737 {
2738 int attachment_count = GetInventory().AttachmentCount();
2739 if (attachment_count > 0)
2740 {
2741 if (HasBase() && attachment_count == 1)
2742 return false;
2743
2744 return true;
2745 }
2746
2747 return false;
2748 }
2749
2750 override bool ShowZonesHealth()
2751 {
2752 return true;
2753 }
2754
2755 //this into/outo parent.Cargo
2756 override bool CanPutInCargo(EntityAI parent)
2757 {
2758 return false;
2759 }
2760
2761 override bool CanRemoveFromCargo(EntityAI parent)
2762 {
2763 return false;
2764 }
2765
2766 //hands
2767 override bool CanPutIntoHands(EntityAI parent)
2768 {
2769 return false;
2770 }
2771
2772 //--- ACTION CONDITIONS
2773 //direction
2774 override bool IsFacingPlayer(PlayerBase player, string selection)
2775 {
2776 return true;
2777 }
2778
2779 override bool IsPlayerInside(PlayerBase player, string selection)
2780 {
2781 return true;
2782 }
2783
2786 {
2787 return false;
2788 }
2789
2790 //camera direction check
2791 bool IsFacingCamera(string selection)
2792 {
2793 return true;
2794 }
2795
2796 //roof check
2798 {
2799 return false;
2800 }
2801
2802 //selection->player distance check
2803 bool HasProperDistance(string selection, PlayerBase player)
2804 {
2805 return true;
2806 }
2807
2808 //folding
2810 {
2811 if (HasBase() || GetInventory().AttachmentCount() > 0)
2812 return false;
2813
2814 return true;
2815 }
2816
2818 {
2821
2822 return item;
2823 }
2824
2825 //Damage triggers (barbed wire)
2826 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2827 {
2828 if (GetGame() && GetGame().IsServer())
2829 {
2830 //destroy area damage if some already exists
2832
2833 //create new area damage
2835 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2836
2837 vector min_max[2];
2838 if (MemoryPointExists(slot_name + "_min"))
2839 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2840 if (MemoryPointExists(slot_name + "_max"))
2841 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2842
2843 //get proper trigger extents (min<max)
2844 vector extents[2];
2845 GetConstruction().GetTriggerExtents(min_max, extents);
2846
2847 //get box center
2848 vector center;
2849 center = GetConstruction().GetBoxCenter(min_max);
2850 center = ModelToWorld(center);
2851
2852 //rotate center if needed
2855
2856 areaDamage.SetExtents(extents[0], extents[1]);
2857 areaDamage.SetAreaPosition(center);
2858 areaDamage.SetAreaOrientation(orientation);
2859 areaDamage.SetLoopInterval(1.0);
2860 areaDamage.SetDeferDuration(0.2);
2861 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2862 areaDamage.SetAmmoName("BarbedWireHit");
2863 areaDamage.Spawn();
2864
2866 }
2867 }
2868
2870 {
2871 if (angle_deg != 0)
2872 {
2873 //orientation
2875
2876 //center
2878 if (MemoryPointExists("rotate_axis"))
2879 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2882 center[0] = r_center_x;
2883 center[2] = r_center_z;
2884 }
2885 }
2886
2887 void DestroyAreaDamage(string slot_name)
2888 {
2889 if (GetGame() && GetGame().IsServer())
2890 {
2893 {
2894 if (areaDamage)
2895 areaDamage.Destroy();
2896
2898 }
2899 }
2900 }
2901
2902 override bool IsIgnoredByConstruction()
2903 {
2904 return true;
2905 }
2906
2907 //================================================================
2908 // SOUNDS
2909 //================================================================
2910 protected void SoundBuildStart(string part_name)
2911 {
2912 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2913 }
2914
2915 protected void SoundDismantleStart(string part_name)
2916 {
2917 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2918 }
2919
2920 protected void SoundDestroyStart(string part_name)
2921 {
2922 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2923 }
2924
2925 protected string GetBuildSoundByMaterial(string part_name)
2926 {
2928
2929 switch (material_type)
2930 {
2931 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2932 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2933 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2934 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2935 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2936 }
2937
2938 return "";
2939 }
2940
2941 protected string GetDismantleSoundByMaterial(string part_name)
2942 {
2944
2945 switch (material_type)
2946 {
2947 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2948 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2949 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2950 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2951 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2952 }
2953
2954 return "";
2955 }
2956
2957 //misc
2959 {
2960 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2961 {
2962 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2964 SetHealth(slot_name, "Health", item.GetHealth());
2965 }
2966 }
2967
2968 override int GetDamageSystemVersionChange()
2969 {
2970 return 111;
2971 }
2972
2973 override void SetActions()
2974 {
2975 super.SetActions();
2976
2978 //AddAction(ActionTakeHybridAttachment);
2979 //AddAction(ActionTakeHybridAttachmentToHands);
2982 }
2983
2984 //================================================================
2985 // DEBUG
2986 //================================================================
2987 protected void DebugCustomState()
2988 {
2989 }
2990
2993 {
2994 return null;
2995 }
2996
2997 override void OnDebugSpawn()
2998 {
2999 FullyBuild();
3000 }
3001
3002 void FullyBuild()
3003 {
3005 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3006
3007 Man p;
3008
3009#ifdef SERVER
3011 GetGame().GetWorld().GetPlayerList(players);
3012 if (players.Count())
3013 p = players[0];
3014#else
3015 p = GetGame().GetPlayer();
3016#endif
3017
3018 foreach (ConstructionPart part : parts)
3019 {
3020 bool excluded = false;
3021 string partName = part.GetPartName();
3022 if (excludes)
3023 {
3024 foreach (string exclude : excludes)
3025 {
3026 if (partName.Contains(exclude))
3027 {
3028 excluded = true;
3029 break;
3030 }
3031 }
3032 }
3033
3034 if (!excluded)
3036 }
3037
3038 GetConstruction().UpdateVisuals();
3039 }
3040}
3041
3042void bsbDebugPrint(string s)
3043{
3044#ifdef BSB_DEBUG
3045 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3046#else
3047 //Print("" + s); // comment/uncomment to hide/see debug logs
3048#endif
3049}
3050void bsbDebugSpam(string s)
3051{
3052#ifdef BSB_DEBUG_SPAM
3053 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3054#else
3055 //Print("" + s); // comment/uncomment to hide/see debug logs
3056#endif
3057}

Referenced by ItemBase::OnSetSlotLock(), and ItemBase::UpdateVisuals().

◆ UpdateNavmesh()

void bsbDebugPrint::UpdateNavmesh ( )
protected

Definition at line 1965 of file BaseBuildingBase.c.

1967{
1968 const string ANIMATION_DEPLOYED = "Deployed";
1969
1970 float m_ConstructionKitHealth; //stored health value for used construction kit
1971
1973
1974 bool m_HasBase;
1975 //variables for synchronization of base building parts (2x31 is the current limit)
1976 int m_SyncParts01; //synchronization for already built parts (31 parts)
1977 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1978 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1979 int m_InteractedPartId; //construction part id that an action was performed on
1980 int m_PerformedActionId; //action id that was performed on a construction part
1981
1982 //Sounds
1983 //build
1984 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1985 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1986 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1987 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1988 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1989 //dismantle
1990 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1991 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1992 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1993 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1994 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1995
1996 protected EffectSound m_Sound;
1997
2001
2002 // Constructor
2003 void BaseBuildingBase()
2004 {
2006
2007 //synchronized variables
2008 RegisterNetSyncVariableInt("m_SyncParts01");
2009 RegisterNetSyncVariableInt("m_SyncParts02");
2010 RegisterNetSyncVariableInt("m_SyncParts03");
2011 RegisterNetSyncVariableInt("m_InteractedPartId");
2012 RegisterNetSyncVariableInt("m_PerformedActionId");
2013 RegisterNetSyncVariableBool("m_HasBase");
2014
2015 //Construction init
2017
2018 if (ConfigIsExisting("hybridAttachments"))
2019 {
2021 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
2022 }
2023 if (ConfigIsExisting("mountables"))
2024 {
2026 ConfigGetTextArray("mountables", m_Mountables);
2027 }
2028
2029 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
2030 }
2031
2032 override void EEDelete(EntityAI parent)
2033 {
2034 super.EEDelete(parent);
2035
2036 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
2038
2039 }
2040
2041 override string GetInvulnerabilityTypeString()
2042 {
2043 return "disableBaseDamage";
2044 }
2045
2046 override bool CanObstruct()
2047 {
2048 return true;
2049 }
2050
2051 override int GetHideIconMask()
2052 {
2053 return EInventoryIconVisibility.HIDE_VICINITY;
2054 }
2055
2056 // --- SYNCHRONIZATION
2058 {
2059 if (GetGame().IsServer())
2060 SetSynchDirty();
2061 }
2062
2063 override void OnVariablesSynchronized()
2064 {
2065 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2066 super.OnVariablesSynchronized();
2067
2068 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2069 }
2070
2071 protected void OnSynchronizedClient()
2072 {
2073 //update parts
2075
2076 //update action on part
2078
2079 //update visuals (client)
2080 UpdateVisuals();
2081 }
2082
2083 //parts synchronization
2085 {
2086 //part_id must starts from index = 1
2087 int offset;
2088 int mask;
2089
2090 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2091 {
2092 offset = part_id - 1;
2093 mask = 1 << offset;
2094
2096 }
2097 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2098 {
2099 offset = (part_id % 32);
2100 mask = 1 << offset;
2101
2103 }
2104 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2105 {
2106 offset = (part_id % 63);
2107 mask = 1 << offset;
2108
2110 }
2111 }
2112
2114 {
2115 //part_id must starts from index = 1
2116 int offset;
2117 int mask;
2118
2119 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2120 {
2121 offset = part_id - 1;
2122 mask = 1 << offset;
2123
2125 }
2126 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2127 {
2128 offset = (part_id % 32);
2129 mask = 1 << offset;
2130
2132 }
2133 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2134 {
2135 offset = (part_id % 63);
2136 mask = 1 << offset;
2137
2139 }
2140 }
2141
2143 {
2144 //part_id must starts from index = 1
2145 int offset;
2146 int mask;
2147
2148 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2149 {
2150 offset = part_id - 1;
2151 mask = 1 << offset;
2152
2153 if ((m_SyncParts01 & mask) > 0)
2154 return true;
2155 }
2156 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2157 {
2158 offset = (part_id % 32);
2159 mask = 1 << offset;
2160
2161 if ((m_SyncParts02 & mask) > 0)
2162 return true;
2163 }
2164 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2165 {
2166 offset = (part_id % 63);
2167 mask = 1 << offset;
2168
2169 if ((m_SyncParts03 & mask) > 0)
2170 return true;
2171 }
2172
2173 return false;
2174 }
2175
2176 protected void RegisterActionForSync(int part_id, int action_id)
2177 {
2180 }
2181
2182 protected void ResetActionSyncData()
2183 {
2184 //reset data
2185 m_InteractedPartId = -1;
2187 }
2188
2189 protected void SetActionFromSyncData()
2190 {
2191 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2192 {
2195
2196 switch (build_action_id)
2197 {
2201 }
2202 }
2203 }
2204 //------
2205
2207 {
2208 string key = part.m_PartName;
2209 bool is_base = part.IsBase();
2211 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2213 {
2214 if (!part.IsBuilt())
2215 {
2216 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2217 GetConstruction().AddToConstructedParts(key);
2218 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2219
2220 if (is_base)
2221 {
2223 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2224 }
2225 }
2226 }
2227 else
2228 {
2229 if (part.IsBuilt())
2230 {
2231 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2232 GetConstruction().RemoveFromConstructedParts(key);
2233 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2234
2235 if (is_base)
2236 {
2238 AddProxyPhysics(ANIMATION_DEPLOYED);
2239 }
2240 }
2241 }
2242
2243 //check slot lock for material attachments
2244 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2245 }
2246
2247 //set construction parts based on synchronized data
2249 {
2252
2253 for (int i = 0; i < construction_parts.Count(); ++i)
2254 {
2255 string key = construction_parts.GetKey(i);
2258 }
2259
2260 //regenerate navmesh
2261 UpdateNavmesh();
2262 }
2263
2265 {
2268
2269 for (int i = 0; i < construction_parts.Count(); ++i)
2270 {
2271 string key = construction_parts.GetKey(i);
2273
2274 if (value.GetId() == id)
2275 return value;
2276 }
2277
2278 return NULL;
2279 }
2280 //
2281
2282 //Base
2283 bool HasBase()
2284 {
2285 return m_HasBase;
2286 }
2287
2288 void SetBaseState(bool has_base)
2289 {
2291 }
2292
2293 override bool IsDeployable()
2294 {
2295 return true;
2296 }
2297
2298 bool IsOpened()
2299 {
2300 return false;
2301 }
2302
2303 //--- CONSTRUCTION KIT
2305 {
2309
2310 return construction_kit;
2311 }
2312
2314 {
2315 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2318 }
2319
2320 protected vector GetKitSpawnPosition()
2321 {
2322 return GetPosition();
2323 }
2324
2325 protected string GetConstructionKitType()
2326 {
2327 return "";
2328 }
2329
2331 {
2333 GetGame().ObjectDelete(construction_kit);
2334 }
2335
2336 //--- CONSTRUCTION
2337 void DestroyConstruction()
2338 {
2339 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2340 GetGame().ObjectDelete(this);
2341 }
2342
2343 // --- EVENTS
2344 override void OnStoreSave(ParamsWriteContext ctx)
2345 {
2346 super.OnStoreSave(ctx);
2347
2348 //sync parts 01
2349 ctx.Write(m_SyncParts01);
2350 ctx.Write(m_SyncParts02);
2351 ctx.Write(m_SyncParts03);
2352
2353 ctx.Write(m_HasBase);
2354 }
2355
2356 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2357 {
2358 if (!super.OnStoreLoad(ctx, version))
2359 return false;
2360
2361 //--- Base building data ---
2362 //Restore synced parts data
2363 if (!ctx.Read(m_SyncParts01))
2364 {
2365 m_SyncParts01 = 0; //set default
2366 return false;
2367 }
2368 if (!ctx.Read(m_SyncParts02))
2369 {
2370 m_SyncParts02 = 0; //set default
2371 return false;
2372 }
2373 if (!ctx.Read(m_SyncParts03))
2374 {
2375 m_SyncParts03 = 0; //set default
2376 return false;
2377 }
2378
2379 //has base
2380 if (!ctx.Read(m_HasBase))
2381 {
2382 m_HasBase = false;
2383 return false;
2384 }
2385 //---
2386
2387 return true;
2388 }
2389
2390 override void AfterStoreLoad()
2391 {
2392 super.AfterStoreLoad();
2393
2396 }
2397
2399 {
2400 //update server data
2402
2403 //set base state
2404 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2405 SetBaseState(construction_part.IsBuilt()) ;
2406
2407 //synchronize after load
2409 }
2410
2411 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2412 {
2414 return;
2415
2416 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2417
2418 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2419 return;
2420
2422 string part_name = zone;
2423 part_name.ToLower();
2424
2426 {
2428
2429 if (construction_part && construction.IsPartConstructed(part_name))
2430 {
2431 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2432 construction.DestroyConnectedParts(part_name);
2433 }
2434
2435 //barbed wire handling (hack-ish)
2436 if (part_name.Contains("barbed"))
2437 {
2438 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2439 if (barbed_wire)
2440 barbed_wire.SetMountedState(false);
2441 }
2442 }
2443 }
2444
2445 override void EEOnAfterLoad()
2446 {
2448 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2449
2450 super.EEOnAfterLoad();
2451 }
2452
2453 override void EEInit()
2454 {
2455 super.EEInit();
2456
2457 // init visuals and physics
2458 InitBaseState();
2459
2460 //debug
2461#ifdef DEVELOPER
2463#endif
2464 }
2465
2466 override void EEItemAttached(EntityAI item, string slot_name)
2467 {
2468 super.EEItemAttached(item, slot_name);
2469
2471 UpdateVisuals();
2473 }
2474
2475 override void EEItemDetached(EntityAI item, string slot_name)
2476 {
2477 super.EEItemDetached(item, slot_name);
2478
2479 UpdateVisuals();
2481 }
2482
2483 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2484 {
2486 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2487
2490 }
2491
2492 //ignore out of reach condition
2493 override bool IgnoreOutOfReachCondition()
2494 {
2495 return true;
2496 }
2497
2498 //CONSTRUCTION EVENTS
2499 //Build
2500 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2501 {
2503
2504 //check base state
2505 if (construtionPart.IsBase())
2506 {
2507 SetBaseState(true);
2508
2509 //spawn kit
2511 }
2512
2513 //register constructed parts for synchronization
2515
2516 //register action that was performed on part
2518
2519 //synchronize
2521
2522 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2523
2524 UpdateNavmesh();
2525
2526 //update visuals
2527 UpdateVisuals();
2528
2529 //reset action sync data
2530 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2531 }
2532
2533 void OnPartBuiltClient(string part_name, int action_id)
2534 {
2535 //play sound
2537 }
2538
2539 //Dismantle
2541 {
2542 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2544
2545 //register constructed parts for synchronization
2547
2548 //register action that was performed on part
2550
2551 //synchronize
2553
2554 // server part of sync, client will be synced from SetPartsFromSyncData
2556
2557 UpdateNavmesh();
2558
2559 //update visuals
2560 UpdateVisuals();
2561
2562 //reset action sync data
2563 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2564
2565 //check base state
2566 if (construtionPart.IsBase())
2567 {
2568 //Destroy construction
2569 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2570 }
2571 }
2572
2574 {
2575 //play sound
2577 }
2578
2579 //Destroy
2581 {
2582 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2584
2585 //register constructed parts for synchronization
2587
2588 //register action that was performed on part
2590
2591 //synchronize
2593
2594 // server part of sync, client will be synced from SetPartsFromSyncData
2596
2597 UpdateNavmesh();
2598
2599 //update visuals
2600 UpdateVisuals();
2601
2602 //reset action sync data
2603 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2604
2605 //check base state
2606 if (construtionPart.IsBase())
2607 {
2608 //Destroy construction
2609 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2610 }
2611 }
2612
2613 void OnPartDestroyedClient(string part_name, int action_id)
2614 {
2615 //play sound
2617 }
2618
2619 // --- UPDATE
2620 void InitBaseState()
2621 {
2622 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2623
2624 InitVisuals();
2625 UpdateNavmesh(); //regenerate navmesh
2626 GetConstruction().InitBaseState();
2627 }
2628
2629 void InitVisuals()
2630 {
2631 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2632 //check base
2633 if (!HasBase())
2634 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2635 else
2636 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2637
2638 GetConstruction().UpdateVisuals();
2639 }
2640
2641 void UpdateVisuals()
2642 {
2644
2646 foreach (string slotName : attachmentSlots)
2648
2649 //check base
2650 if (!HasBase())
2651 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2652 else
2653 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2654
2655 GetConstruction().UpdateVisuals();
2656 }
2657
2659 {
2660 string slotNameMounted = slot_name + "_Mounted";
2661 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2662
2663 if (attachment)
2664 {
2665 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2666 if (barbedWire && barbedWire.IsMounted())
2668 else
2670
2671 if (is_locked)
2672 {
2673 SetAnimationPhase(slotNameMounted, 0);
2674 SetAnimationPhase(slot_name, 1);
2675 }
2676 else
2677 {
2678 SetAnimationPhase(slotNameMounted, 1);
2679 SetAnimationPhase(slot_name, 0);
2680 }
2681 }
2682 else
2683 {
2684 SetAnimationPhase(slotNameMounted, 1);
2685 SetAnimationPhase(slot_name, 1);
2686
2688 }
2689 }
2690
2691 // avoid calling this function on frequent occasions, it's a massive performance hit
2692 void UpdatePhysics()
2693 {
2695 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2696
2699
2701 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2702
2703 foreach (string slotName : attachmentSlots)
2705
2706 //check base
2707 if (!HasBase())
2708 {
2710 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2711
2712 AddProxyPhysics(ANIMATION_DEPLOYED);
2713 }
2714 else
2715 {
2717 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2718
2719 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2720 }
2721
2722 GetConstruction().UpdatePhysics();
2723 UpdateNavmesh();
2724 }
2725
2727 {
2728 //checks for invalid appends; hotfix
2729 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2730 return;
2731 //----------------------------------
2732 string slot_name_mounted = slot_name + "_Mounted";
2733 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2734
2735 //remove proxy physics
2736 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2737 RemoveProxyPhysics(slot_name_mounted);
2738 RemoveProxyPhysics(slot_name);
2739
2740 if (attachment)
2741 {
2742 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2743 if (is_locked)
2744 {
2745 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2746 AddProxyPhysics(slot_name_mounted);
2747 }
2748 else
2749 {
2750 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2751 AddProxyPhysics(slot_name);
2752 }
2753 }
2754 }
2755
2756 protected void UpdateNavmesh()
2757 {
2758 SetAffectPathgraph(true, false);
2759 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2760 }
2761
2762 override bool CanUseConstruction()
2763 {
2764 return true;
2765 }
2766
2767 override bool CanUseConstructionBuild()
2768 {
2769 return true;
2770 }
2771
2773 {
2774 if (attachment)
2775 {
2777 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2778
2779 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2780 }
2781
2782 return false;
2783 }
2784
2785 protected bool IsAttachmentSlotLocked(string slot_name)
2786 {
2787 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2788 }
2789
2790 //--- ATTACHMENT SLOTS
2792 {
2793 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2794 if (GetGame().ConfigIsExisting(config_path))
2795 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2796 }
2797
2799 {
2800 return true;
2801 }
2802
2803 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2804 {
2805 return true;
2806 }
2807
2808 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2809 {
2810 return true;
2811 }
2812
2813 // --- INIT
2814 void ConstructionInit()
2815 {
2816 if (!m_Construction)
2817 m_Construction = new Construction(this);
2818
2819 GetConstruction().Init();
2820 }
2821
2823 {
2824 return m_Construction;
2825 }
2826
2827 //--- INVENTORY/ATTACHMENTS CONDITIONS
2828 //attachments
2830 {
2831 return super.CanReceiveAttachment(attachment, slotId);
2832 }
2833
2835 {
2836 int attachment_count = GetInventory().AttachmentCount();
2837 if (attachment_count > 0)
2838 {
2839 if (HasBase() && attachment_count == 1)
2840 return false;
2841
2842 return true;
2843 }
2844
2845 return false;
2846 }
2847
2848 override bool ShowZonesHealth()
2849 {
2850 return true;
2851 }
2852
2853 //this into/outo parent.Cargo
2854 override bool CanPutInCargo(EntityAI parent)
2855 {
2856 return false;
2857 }
2858
2859 override bool CanRemoveFromCargo(EntityAI parent)
2860 {
2861 return false;
2862 }
2863
2864 //hands
2865 override bool CanPutIntoHands(EntityAI parent)
2866 {
2867 return false;
2868 }
2869
2870 //--- ACTION CONDITIONS
2871 //direction
2872 override bool IsFacingPlayer(PlayerBase player, string selection)
2873 {
2874 return true;
2875 }
2876
2877 override bool IsPlayerInside(PlayerBase player, string selection)
2878 {
2879 return true;
2880 }
2881
2884 {
2885 return false;
2886 }
2887
2888 //camera direction check
2889 bool IsFacingCamera(string selection)
2890 {
2891 return true;
2892 }
2893
2894 //roof check
2896 {
2897 return false;
2898 }
2899
2900 //selection->player distance check
2901 bool HasProperDistance(string selection, PlayerBase player)
2902 {
2903 return true;
2904 }
2905
2906 //folding
2908 {
2909 if (HasBase() || GetInventory().AttachmentCount() > 0)
2910 return false;
2911
2912 return true;
2913 }
2914
2916 {
2919
2920 return item;
2921 }
2922
2923 //Damage triggers (barbed wire)
2924 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2925 {
2926 if (GetGame() && GetGame().IsServer())
2927 {
2928 //destroy area damage if some already exists
2930
2931 //create new area damage
2933 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2934
2935 vector min_max[2];
2936 if (MemoryPointExists(slot_name + "_min"))
2937 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2938 if (MemoryPointExists(slot_name + "_max"))
2939 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2940
2941 //get proper trigger extents (min<max)
2942 vector extents[2];
2943 GetConstruction().GetTriggerExtents(min_max, extents);
2944
2945 //get box center
2946 vector center;
2947 center = GetConstruction().GetBoxCenter(min_max);
2948 center = ModelToWorld(center);
2949
2950 //rotate center if needed
2953
2954 areaDamage.SetExtents(extents[0], extents[1]);
2955 areaDamage.SetAreaPosition(center);
2956 areaDamage.SetAreaOrientation(orientation);
2957 areaDamage.SetLoopInterval(1.0);
2958 areaDamage.SetDeferDuration(0.2);
2959 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2960 areaDamage.SetAmmoName("BarbedWireHit");
2961 areaDamage.Spawn();
2962
2964 }
2965 }
2966
2968 {
2969 if (angle_deg != 0)
2970 {
2971 //orientation
2973
2974 //center
2976 if (MemoryPointExists("rotate_axis"))
2977 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2980 center[0] = r_center_x;
2981 center[2] = r_center_z;
2982 }
2983 }
2984
2985 void DestroyAreaDamage(string slot_name)
2986 {
2987 if (GetGame() && GetGame().IsServer())
2988 {
2991 {
2992 if (areaDamage)
2993 areaDamage.Destroy();
2994
2996 }
2997 }
2998 }
2999
3000 override bool IsIgnoredByConstruction()
3001 {
3002 return true;
3003 }
3004
3005 //================================================================
3006 // SOUNDS
3007 //================================================================
3008 protected void SoundBuildStart(string part_name)
3009 {
3010 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
3011 }
3012
3013 protected void SoundDismantleStart(string part_name)
3014 {
3015 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3016 }
3017
3018 protected void SoundDestroyStart(string part_name)
3019 {
3020 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
3021 }
3022
3023 protected string GetBuildSoundByMaterial(string part_name)
3024 {
3026
3027 switch (material_type)
3028 {
3029 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
3030 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
3031 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
3032 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
3033 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
3034 }
3035
3036 return "";
3037 }
3038
3039 protected string GetDismantleSoundByMaterial(string part_name)
3040 {
3042
3043 switch (material_type)
3044 {
3045 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
3046 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
3047 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
3048 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
3049 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
3050 }
3051
3052 return "";
3053 }
3054
3055 //misc
3057 {
3058 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
3059 {
3060 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
3062 SetHealth(slot_name, "Health", item.GetHealth());
3063 }
3064 }
3065
3066 override int GetDamageSystemVersionChange()
3067 {
3068 return 111;
3069 }
3070
3071 override void SetActions()
3072 {
3073 super.SetActions();
3074
3076 //AddAction(ActionTakeHybridAttachment);
3077 //AddAction(ActionTakeHybridAttachmentToHands);
3080 }
3081
3082 //================================================================
3083 // DEBUG
3084 //================================================================
3085 protected void DebugCustomState()
3086 {
3087 }
3088
3091 {
3092 return null;
3093 }
3094
3095 override void OnDebugSpawn()
3096 {
3097 FullyBuild();
3098 }
3099
3100 void FullyBuild()
3101 {
3103 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3104
3105 Man p;
3106
3107#ifdef SERVER
3109 GetGame().GetWorld().GetPlayerList(players);
3110 if (players.Count())
3111 p = players[0];
3112#else
3113 p = GetGame().GetPlayer();
3114#endif
3115
3116 foreach (ConstructionPart part : parts)
3117 {
3118 bool excluded = false;
3119 string partName = part.GetPartName();
3120 if (excludes)
3121 {
3122 foreach (string exclude : excludes)
3123 {
3124 if (partName.Contains(exclude))
3125 {
3126 excluded = true;
3127 break;
3128 }
3129 }
3130 }
3131
3132 if (!excluded)
3134 }
3135
3136 GetConstruction().UpdateVisuals();
3137 }
3138}
3139
3140void bsbDebugPrint(string s)
3141{
3142#ifdef BSB_DEBUG
3143 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3144#else
3145 //Print("" + s); // comment/uncomment to hide/see debug logs
3146#endif
3147}
3148void bsbDebugSpam(string s)
3149{
3150#ifdef BSB_DEBUG_SPAM
3151 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3152#else
3153 //Print("" + s); // comment/uncomment to hide/see debug logs
3154#endif
3155}

Referenced by BaseBuildingBase::CloseFence(), ItemBase::InitBaseState(), ItemBase::OnPartBuiltServer(), ItemBase::OnPartDestroyedServer(), ItemBase::OnPartDismantledServer(), BaseBuildingBase::OpenFence(), ItemBase::SetPartsFromSyncData(), and ItemBase::UpdatePhysics().

◆ UpdatePhysics()

void bsbDebugPrint::UpdatePhysics ( )
protected

Definition at line 1901 of file BaseBuildingBase.c.

1903{
1904 const string ANIMATION_DEPLOYED = "Deployed";
1905
1906 float m_ConstructionKitHealth; //stored health value for used construction kit
1907
1909
1910 bool m_HasBase;
1911 //variables for synchronization of base building parts (2x31 is the current limit)
1912 int m_SyncParts01; //synchronization for already built parts (31 parts)
1913 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1914 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1915 int m_InteractedPartId; //construction part id that an action was performed on
1916 int m_PerformedActionId; //action id that was performed on a construction part
1917
1918 //Sounds
1919 //build
1920 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1921 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1922 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1923 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1924 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1925 //dismantle
1926 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1927 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1928 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1929 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1930 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1931
1932 protected EffectSound m_Sound;
1933
1937
1938 // Constructor
1939 void BaseBuildingBase()
1940 {
1942
1943 //synchronized variables
1944 RegisterNetSyncVariableInt("m_SyncParts01");
1945 RegisterNetSyncVariableInt("m_SyncParts02");
1946 RegisterNetSyncVariableInt("m_SyncParts03");
1947 RegisterNetSyncVariableInt("m_InteractedPartId");
1948 RegisterNetSyncVariableInt("m_PerformedActionId");
1949 RegisterNetSyncVariableBool("m_HasBase");
1950
1951 //Construction init
1953
1954 if (ConfigIsExisting("hybridAttachments"))
1955 {
1957 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1958 }
1959 if (ConfigIsExisting("mountables"))
1960 {
1962 ConfigGetTextArray("mountables", m_Mountables);
1963 }
1964
1965 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1966 }
1967
1968 override void EEDelete(EntityAI parent)
1969 {
1970 super.EEDelete(parent);
1971
1972 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1974
1975 }
1976
1977 override string GetInvulnerabilityTypeString()
1978 {
1979 return "disableBaseDamage";
1980 }
1981
1982 override bool CanObstruct()
1983 {
1984 return true;
1985 }
1986
1987 override int GetHideIconMask()
1988 {
1989 return EInventoryIconVisibility.HIDE_VICINITY;
1990 }
1991
1992 // --- SYNCHRONIZATION
1994 {
1995 if (GetGame().IsServer())
1996 SetSynchDirty();
1997 }
1998
1999 override void OnVariablesSynchronized()
2000 {
2001 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
2002 super.OnVariablesSynchronized();
2003
2004 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
2005 }
2006
2007 protected void OnSynchronizedClient()
2008 {
2009 //update parts
2011
2012 //update action on part
2014
2015 //update visuals (client)
2016 UpdateVisuals();
2017 }
2018
2019 //parts synchronization
2021 {
2022 //part_id must starts from index = 1
2023 int offset;
2024 int mask;
2025
2026 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2027 {
2028 offset = part_id - 1;
2029 mask = 1 << offset;
2030
2032 }
2033 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2034 {
2035 offset = (part_id % 32);
2036 mask = 1 << offset;
2037
2039 }
2040 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2041 {
2042 offset = (part_id % 63);
2043 mask = 1 << offset;
2044
2046 }
2047 }
2048
2050 {
2051 //part_id must starts from index = 1
2052 int offset;
2053 int mask;
2054
2055 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2056 {
2057 offset = part_id - 1;
2058 mask = 1 << offset;
2059
2061 }
2062 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2063 {
2064 offset = (part_id % 32);
2065 mask = 1 << offset;
2066
2068 }
2069 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2070 {
2071 offset = (part_id % 63);
2072 mask = 1 << offset;
2073
2075 }
2076 }
2077
2079 {
2080 //part_id must starts from index = 1
2081 int offset;
2082 int mask;
2083
2084 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2085 {
2086 offset = part_id - 1;
2087 mask = 1 << offset;
2088
2089 if ((m_SyncParts01 & mask) > 0)
2090 return true;
2091 }
2092 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2093 {
2094 offset = (part_id % 32);
2095 mask = 1 << offset;
2096
2097 if ((m_SyncParts02 & mask) > 0)
2098 return true;
2099 }
2100 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2101 {
2102 offset = (part_id % 63);
2103 mask = 1 << offset;
2104
2105 if ((m_SyncParts03 & mask) > 0)
2106 return true;
2107 }
2108
2109 return false;
2110 }
2111
2112 protected void RegisterActionForSync(int part_id, int action_id)
2113 {
2116 }
2117
2118 protected void ResetActionSyncData()
2119 {
2120 //reset data
2121 m_InteractedPartId = -1;
2123 }
2124
2125 protected void SetActionFromSyncData()
2126 {
2127 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2128 {
2131
2132 switch (build_action_id)
2133 {
2137 }
2138 }
2139 }
2140 //------
2141
2143 {
2144 string key = part.m_PartName;
2145 bool is_base = part.IsBase();
2147 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2149 {
2150 if (!part.IsBuilt())
2151 {
2152 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2153 GetConstruction().AddToConstructedParts(key);
2154 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2155
2156 if (is_base)
2157 {
2159 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2160 }
2161 }
2162 }
2163 else
2164 {
2165 if (part.IsBuilt())
2166 {
2167 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2168 GetConstruction().RemoveFromConstructedParts(key);
2169 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2170
2171 if (is_base)
2172 {
2174 AddProxyPhysics(ANIMATION_DEPLOYED);
2175 }
2176 }
2177 }
2178
2179 //check slot lock for material attachments
2180 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2181 }
2182
2183 //set construction parts based on synchronized data
2185 {
2188
2189 for (int i = 0; i < construction_parts.Count(); ++i)
2190 {
2191 string key = construction_parts.GetKey(i);
2194 }
2195
2196 //regenerate navmesh
2197 UpdateNavmesh();
2198 }
2199
2201 {
2204
2205 for (int i = 0; i < construction_parts.Count(); ++i)
2206 {
2207 string key = construction_parts.GetKey(i);
2209
2210 if (value.GetId() == id)
2211 return value;
2212 }
2213
2214 return NULL;
2215 }
2216 //
2217
2218 //Base
2219 bool HasBase()
2220 {
2221 return m_HasBase;
2222 }
2223
2224 void SetBaseState(bool has_base)
2225 {
2227 }
2228
2229 override bool IsDeployable()
2230 {
2231 return true;
2232 }
2233
2234 bool IsOpened()
2235 {
2236 return false;
2237 }
2238
2239 //--- CONSTRUCTION KIT
2241 {
2245
2246 return construction_kit;
2247 }
2248
2250 {
2251 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2254 }
2255
2256 protected vector GetKitSpawnPosition()
2257 {
2258 return GetPosition();
2259 }
2260
2261 protected string GetConstructionKitType()
2262 {
2263 return "";
2264 }
2265
2267 {
2269 GetGame().ObjectDelete(construction_kit);
2270 }
2271
2272 //--- CONSTRUCTION
2273 void DestroyConstruction()
2274 {
2275 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2276 GetGame().ObjectDelete(this);
2277 }
2278
2279 // --- EVENTS
2280 override void OnStoreSave(ParamsWriteContext ctx)
2281 {
2282 super.OnStoreSave(ctx);
2283
2284 //sync parts 01
2285 ctx.Write(m_SyncParts01);
2286 ctx.Write(m_SyncParts02);
2287 ctx.Write(m_SyncParts03);
2288
2289 ctx.Write(m_HasBase);
2290 }
2291
2292 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2293 {
2294 if (!super.OnStoreLoad(ctx, version))
2295 return false;
2296
2297 //--- Base building data ---
2298 //Restore synced parts data
2299 if (!ctx.Read(m_SyncParts01))
2300 {
2301 m_SyncParts01 = 0; //set default
2302 return false;
2303 }
2304 if (!ctx.Read(m_SyncParts02))
2305 {
2306 m_SyncParts02 = 0; //set default
2307 return false;
2308 }
2309 if (!ctx.Read(m_SyncParts03))
2310 {
2311 m_SyncParts03 = 0; //set default
2312 return false;
2313 }
2314
2315 //has base
2316 if (!ctx.Read(m_HasBase))
2317 {
2318 m_HasBase = false;
2319 return false;
2320 }
2321 //---
2322
2323 return true;
2324 }
2325
2326 override void AfterStoreLoad()
2327 {
2328 super.AfterStoreLoad();
2329
2332 }
2333
2335 {
2336 //update server data
2338
2339 //set base state
2340 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2341 SetBaseState(construction_part.IsBuilt()) ;
2342
2343 //synchronize after load
2345 }
2346
2347 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2348 {
2350 return;
2351
2352 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2353
2354 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2355 return;
2356
2358 string part_name = zone;
2359 part_name.ToLower();
2360
2362 {
2364
2365 if (construction_part && construction.IsPartConstructed(part_name))
2366 {
2367 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2368 construction.DestroyConnectedParts(part_name);
2369 }
2370
2371 //barbed wire handling (hack-ish)
2372 if (part_name.Contains("barbed"))
2373 {
2374 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2375 if (barbed_wire)
2376 barbed_wire.SetMountedState(false);
2377 }
2378 }
2379 }
2380
2381 override void EEOnAfterLoad()
2382 {
2384 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2385
2386 super.EEOnAfterLoad();
2387 }
2388
2389 override void EEInit()
2390 {
2391 super.EEInit();
2392
2393 // init visuals and physics
2394 InitBaseState();
2395
2396 //debug
2397#ifdef DEVELOPER
2399#endif
2400 }
2401
2402 override void EEItemAttached(EntityAI item, string slot_name)
2403 {
2404 super.EEItemAttached(item, slot_name);
2405
2407 UpdateVisuals();
2409 }
2410
2411 override void EEItemDetached(EntityAI item, string slot_name)
2412 {
2413 super.EEItemDetached(item, slot_name);
2414
2415 UpdateVisuals();
2417 }
2418
2419 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2420 {
2422 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2423
2426 }
2427
2428 //ignore out of reach condition
2429 override bool IgnoreOutOfReachCondition()
2430 {
2431 return true;
2432 }
2433
2434 //CONSTRUCTION EVENTS
2435 //Build
2436 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2437 {
2439
2440 //check base state
2441 if (construtionPart.IsBase())
2442 {
2443 SetBaseState(true);
2444
2445 //spawn kit
2447 }
2448
2449 //register constructed parts for synchronization
2451
2452 //register action that was performed on part
2454
2455 //synchronize
2457
2458 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2459
2460 UpdateNavmesh();
2461
2462 //update visuals
2463 UpdateVisuals();
2464
2465 //reset action sync data
2466 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2467 }
2468
2469 void OnPartBuiltClient(string part_name, int action_id)
2470 {
2471 //play sound
2473 }
2474
2475 //Dismantle
2477 {
2478 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2480
2481 //register constructed parts for synchronization
2483
2484 //register action that was performed on part
2486
2487 //synchronize
2489
2490 // server part of sync, client will be synced from SetPartsFromSyncData
2492
2493 UpdateNavmesh();
2494
2495 //update visuals
2496 UpdateVisuals();
2497
2498 //reset action sync data
2499 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2500
2501 //check base state
2502 if (construtionPart.IsBase())
2503 {
2504 //Destroy construction
2505 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2506 }
2507 }
2508
2510 {
2511 //play sound
2513 }
2514
2515 //Destroy
2517 {
2518 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2520
2521 //register constructed parts for synchronization
2523
2524 //register action that was performed on part
2526
2527 //synchronize
2529
2530 // server part of sync, client will be synced from SetPartsFromSyncData
2532
2533 UpdateNavmesh();
2534
2535 //update visuals
2536 UpdateVisuals();
2537
2538 //reset action sync data
2539 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2540
2541 //check base state
2542 if (construtionPart.IsBase())
2543 {
2544 //Destroy construction
2545 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2546 }
2547 }
2548
2549 void OnPartDestroyedClient(string part_name, int action_id)
2550 {
2551 //play sound
2553 }
2554
2555 // --- UPDATE
2556 void InitBaseState()
2557 {
2558 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2559
2560 InitVisuals();
2561 UpdateNavmesh(); //regenerate navmesh
2562 GetConstruction().InitBaseState();
2563 }
2564
2565 void InitVisuals()
2566 {
2567 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2568 //check base
2569 if (!HasBase())
2570 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2571 else
2572 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2573
2574 GetConstruction().UpdateVisuals();
2575 }
2576
2577 void UpdateVisuals()
2578 {
2580
2582 foreach (string slotName : attachmentSlots)
2584
2585 //check base
2586 if (!HasBase())
2587 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2588 else
2589 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2590
2591 GetConstruction().UpdateVisuals();
2592 }
2593
2595 {
2596 string slotNameMounted = slot_name + "_Mounted";
2597 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2598
2599 if (attachment)
2600 {
2601 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2602 if (barbedWire && barbedWire.IsMounted())
2604 else
2606
2607 if (is_locked)
2608 {
2609 SetAnimationPhase(slotNameMounted, 0);
2610 SetAnimationPhase(slot_name, 1);
2611 }
2612 else
2613 {
2614 SetAnimationPhase(slotNameMounted, 1);
2615 SetAnimationPhase(slot_name, 0);
2616 }
2617 }
2618 else
2619 {
2620 SetAnimationPhase(slotNameMounted, 1);
2621 SetAnimationPhase(slot_name, 1);
2622
2624 }
2625 }
2626
2627 // avoid calling this function on frequent occasions, it's a massive performance hit
2628 void UpdatePhysics()
2629 {
2631 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2632
2635
2637 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2638
2639 foreach (string slotName : attachmentSlots)
2641
2642 //check base
2643 if (!HasBase())
2644 {
2646 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2647
2648 AddProxyPhysics(ANIMATION_DEPLOYED);
2649 }
2650 else
2651 {
2653 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2654
2655 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2656 }
2657
2658 GetConstruction().UpdatePhysics();
2659 UpdateNavmesh();
2660 }
2661
2663 {
2664 //checks for invalid appends; hotfix
2665 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2666 return;
2667 //----------------------------------
2668 string slot_name_mounted = slot_name + "_Mounted";
2669 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2670
2671 //remove proxy physics
2672 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2673 RemoveProxyPhysics(slot_name_mounted);
2674 RemoveProxyPhysics(slot_name);
2675
2676 if (attachment)
2677 {
2678 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2679 if (is_locked)
2680 {
2681 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2682 AddProxyPhysics(slot_name_mounted);
2683 }
2684 else
2685 {
2686 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2687 AddProxyPhysics(slot_name);
2688 }
2689 }
2690 }
2691
2692 protected void UpdateNavmesh()
2693 {
2694 SetAffectPathgraph(true, false);
2695 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2696 }
2697
2698 override bool CanUseConstruction()
2699 {
2700 return true;
2701 }
2702
2703 override bool CanUseConstructionBuild()
2704 {
2705 return true;
2706 }
2707
2709 {
2710 if (attachment)
2711 {
2713 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2714
2715 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2716 }
2717
2718 return false;
2719 }
2720
2721 protected bool IsAttachmentSlotLocked(string slot_name)
2722 {
2723 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2724 }
2725
2726 //--- ATTACHMENT SLOTS
2728 {
2729 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2730 if (GetGame().ConfigIsExisting(config_path))
2731 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2732 }
2733
2735 {
2736 return true;
2737 }
2738
2739 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2740 {
2741 return true;
2742 }
2743
2744 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2745 {
2746 return true;
2747 }
2748
2749 // --- INIT
2750 void ConstructionInit()
2751 {
2752 if (!m_Construction)
2753 m_Construction = new Construction(this);
2754
2755 GetConstruction().Init();
2756 }
2757
2759 {
2760 return m_Construction;
2761 }
2762
2763 //--- INVENTORY/ATTACHMENTS CONDITIONS
2764 //attachments
2766 {
2767 return super.CanReceiveAttachment(attachment, slotId);
2768 }
2769
2771 {
2772 int attachment_count = GetInventory().AttachmentCount();
2773 if (attachment_count > 0)
2774 {
2775 if (HasBase() && attachment_count == 1)
2776 return false;
2777
2778 return true;
2779 }
2780
2781 return false;
2782 }
2783
2784 override bool ShowZonesHealth()
2785 {
2786 return true;
2787 }
2788
2789 //this into/outo parent.Cargo
2790 override bool CanPutInCargo(EntityAI parent)
2791 {
2792 return false;
2793 }
2794
2795 override bool CanRemoveFromCargo(EntityAI parent)
2796 {
2797 return false;
2798 }
2799
2800 //hands
2801 override bool CanPutIntoHands(EntityAI parent)
2802 {
2803 return false;
2804 }
2805
2806 //--- ACTION CONDITIONS
2807 //direction
2808 override bool IsFacingPlayer(PlayerBase player, string selection)
2809 {
2810 return true;
2811 }
2812
2813 override bool IsPlayerInside(PlayerBase player, string selection)
2814 {
2815 return true;
2816 }
2817
2820 {
2821 return false;
2822 }
2823
2824 //camera direction check
2825 bool IsFacingCamera(string selection)
2826 {
2827 return true;
2828 }
2829
2830 //roof check
2832 {
2833 return false;
2834 }
2835
2836 //selection->player distance check
2837 bool HasProperDistance(string selection, PlayerBase player)
2838 {
2839 return true;
2840 }
2841
2842 //folding
2844 {
2845 if (HasBase() || GetInventory().AttachmentCount() > 0)
2846 return false;
2847
2848 return true;
2849 }
2850
2852 {
2855
2856 return item;
2857 }
2858
2859 //Damage triggers (barbed wire)
2860 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2861 {
2862 if (GetGame() && GetGame().IsServer())
2863 {
2864 //destroy area damage if some already exists
2866
2867 //create new area damage
2869 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2870
2871 vector min_max[2];
2872 if (MemoryPointExists(slot_name + "_min"))
2873 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2874 if (MemoryPointExists(slot_name + "_max"))
2875 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2876
2877 //get proper trigger extents (min<max)
2878 vector extents[2];
2879 GetConstruction().GetTriggerExtents(min_max, extents);
2880
2881 //get box center
2882 vector center;
2883 center = GetConstruction().GetBoxCenter(min_max);
2884 center = ModelToWorld(center);
2885
2886 //rotate center if needed
2889
2890 areaDamage.SetExtents(extents[0], extents[1]);
2891 areaDamage.SetAreaPosition(center);
2892 areaDamage.SetAreaOrientation(orientation);
2893 areaDamage.SetLoopInterval(1.0);
2894 areaDamage.SetDeferDuration(0.2);
2895 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2896 areaDamage.SetAmmoName("BarbedWireHit");
2897 areaDamage.Spawn();
2898
2900 }
2901 }
2902
2904 {
2905 if (angle_deg != 0)
2906 {
2907 //orientation
2909
2910 //center
2912 if (MemoryPointExists("rotate_axis"))
2913 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2916 center[0] = r_center_x;
2917 center[2] = r_center_z;
2918 }
2919 }
2920
2921 void DestroyAreaDamage(string slot_name)
2922 {
2923 if (GetGame() && GetGame().IsServer())
2924 {
2927 {
2928 if (areaDamage)
2929 areaDamage.Destroy();
2930
2932 }
2933 }
2934 }
2935
2936 override bool IsIgnoredByConstruction()
2937 {
2938 return true;
2939 }
2940
2941 //================================================================
2942 // SOUNDS
2943 //================================================================
2944 protected void SoundBuildStart(string part_name)
2945 {
2946 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2947 }
2948
2949 protected void SoundDismantleStart(string part_name)
2950 {
2951 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2952 }
2953
2954 protected void SoundDestroyStart(string part_name)
2955 {
2956 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2957 }
2958
2959 protected string GetBuildSoundByMaterial(string part_name)
2960 {
2962
2963 switch (material_type)
2964 {
2965 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2966 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2967 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2968 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2969 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2970 }
2971
2972 return "";
2973 }
2974
2975 protected string GetDismantleSoundByMaterial(string part_name)
2976 {
2978
2979 switch (material_type)
2980 {
2981 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2982 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2983 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2984 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2985 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2986 }
2987
2988 return "";
2989 }
2990
2991 //misc
2993 {
2994 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2995 {
2996 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2998 SetHealth(slot_name, "Health", item.GetHealth());
2999 }
3000 }
3001
3002 override int GetDamageSystemVersionChange()
3003 {
3004 return 111;
3005 }
3006
3007 override void SetActions()
3008 {
3009 super.SetActions();
3010
3012 //AddAction(ActionTakeHybridAttachment);
3013 //AddAction(ActionTakeHybridAttachmentToHands);
3016 }
3017
3018 //================================================================
3019 // DEBUG
3020 //================================================================
3021 protected void DebugCustomState()
3022 {
3023 }
3024
3027 {
3028 return null;
3029 }
3030
3031 override void OnDebugSpawn()
3032 {
3033 FullyBuild();
3034 }
3035
3036 void FullyBuild()
3037 {
3039 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
3040
3041 Man p;
3042
3043#ifdef SERVER
3045 GetGame().GetWorld().GetPlayerList(players);
3046 if (players.Count())
3047 p = players[0];
3048#else
3049 p = GetGame().GetPlayer();
3050#endif
3051
3052 foreach (ConstructionPart part : parts)
3053 {
3054 bool excluded = false;
3055 string partName = part.GetPartName();
3056 if (excludes)
3057 {
3058 foreach (string exclude : excludes)
3059 {
3060 if (partName.Contains(exclude))
3061 {
3062 excluded = true;
3063 break;
3064 }
3065 }
3066 }
3067
3068 if (!excluded)
3070 }
3071
3072 GetConstruction().UpdateVisuals();
3073 }
3074}
3075
3076void bsbDebugPrint(string s)
3077{
3078#ifdef BSB_DEBUG
3079 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3080#else
3081 //Print("" + s); // comment/uncomment to hide/see debug logs
3082#endif
3083}
3084void bsbDebugSpam(string s)
3085{
3086#ifdef BSB_DEBUG_SPAM
3087 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3088#else
3089 //Print("" + s); // comment/uncomment to hide/see debug logs
3090#endif
3091}

◆ UpdateVisuals()

void bsbDebugPrint::UpdateVisuals ( )
protected

Definition at line 1850 of file BaseBuildingBase.c.

1852{
1853 const string ANIMATION_DEPLOYED = "Deployed";
1854
1855 float m_ConstructionKitHealth; //stored health value for used construction kit
1856
1858
1859 bool m_HasBase;
1860 //variables for synchronization of base building parts (2x31 is the current limit)
1861 int m_SyncParts01; //synchronization for already built parts (31 parts)
1862 int m_SyncParts02; //synchronization for already built parts (+31 parts)
1863 int m_SyncParts03; //synchronization for already built parts (+31 parts)
1864 int m_InteractedPartId; //construction part id that an action was performed on
1865 int m_PerformedActionId; //action id that was performed on a construction part
1866
1867 //Sounds
1868 //build
1869 const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet";
1870 const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet";
1871 const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet";
1872 const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet";
1873 const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet";
1874 //dismantle
1875 const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet";
1876 const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet";
1877 const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet";
1878 const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet";
1879 const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet";
1880
1881 protected EffectSound m_Sound;
1882
1886
1887 // Constructor
1888 void BaseBuildingBase()
1889 {
1891
1892 //synchronized variables
1893 RegisterNetSyncVariableInt("m_SyncParts01");
1894 RegisterNetSyncVariableInt("m_SyncParts02");
1895 RegisterNetSyncVariableInt("m_SyncParts03");
1896 RegisterNetSyncVariableInt("m_InteractedPartId");
1897 RegisterNetSyncVariableInt("m_PerformedActionId");
1898 RegisterNetSyncVariableBool("m_HasBase");
1899
1900 //Construction init
1902
1903 if (ConfigIsExisting("hybridAttachments"))
1904 {
1906 ConfigGetTextArray("hybridAttachments", m_HybridAttachments);
1907 }
1908 if (ConfigIsExisting("mountables"))
1909 {
1911 ConfigGetTextArray("mountables", m_Mountables);
1912 }
1913
1914 ProcessInvulnerabilityCheck(GetInvulnerabilityTypeString());
1915 }
1916
1917 override void EEDelete(EntityAI parent)
1918 {
1919 super.EEDelete(parent);
1920
1921 foreach (AreaDamageManager areaDamage : m_DamageTriggers)
1923
1924 }
1925
1926 override string GetInvulnerabilityTypeString()
1927 {
1928 return "disableBaseDamage";
1929 }
1930
1931 override bool CanObstruct()
1932 {
1933 return true;
1934 }
1935
1936 override int GetHideIconMask()
1937 {
1938 return EInventoryIconVisibility.HIDE_VICINITY;
1939 }
1940
1941 // --- SYNCHRONIZATION
1943 {
1944 if (GetGame().IsServer())
1945 SetSynchDirty();
1946 }
1947
1948 override void OnVariablesSynchronized()
1949 {
1950 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnVariablesSynchronized");
1951 super.OnVariablesSynchronized();
1952
1953 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(OnSynchronizedClient, 100, false);
1954 }
1955
1956 protected void OnSynchronizedClient()
1957 {
1958 //update parts
1960
1961 //update action on part
1963
1964 //update visuals (client)
1965 UpdateVisuals();
1966 }
1967
1968 //parts synchronization
1970 {
1971 //part_id must starts from index = 1
1972 int offset;
1973 int mask;
1974
1975 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
1976 {
1977 offset = part_id - 1;
1978 mask = 1 << offset;
1979
1981 }
1982 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
1983 {
1984 offset = (part_id % 32);
1985 mask = 1 << offset;
1986
1988 }
1989 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
1990 {
1991 offset = (part_id % 63);
1992 mask = 1 << offset;
1993
1995 }
1996 }
1997
1999 {
2000 //part_id must starts from index = 1
2001 int offset;
2002 int mask;
2003
2004 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2005 {
2006 offset = part_id - 1;
2007 mask = 1 << offset;
2008
2010 }
2011 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2012 {
2013 offset = (part_id % 32);
2014 mask = 1 << offset;
2015
2017 }
2018 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2019 {
2020 offset = (part_id % 63);
2021 mask = 1 << offset;
2022
2024 }
2025 }
2026
2028 {
2029 //part_id must starts from index = 1
2030 int offset;
2031 int mask;
2032
2033 if (part_id >= 1 && part_id <= 31) //<1,31> (31 parts)
2034 {
2035 offset = part_id - 1;
2036 mask = 1 << offset;
2037
2038 if ((m_SyncParts01 & mask) > 0)
2039 return true;
2040 }
2041 else if (part_id >= 32 && part_id <= 62) //<32,62> (31 parts)
2042 {
2043 offset = (part_id % 32);
2044 mask = 1 << offset;
2045
2046 if ((m_SyncParts02 & mask) > 0)
2047 return true;
2048 }
2049 else if (part_id >= 63 && part_id <= 93) //<63,93> (31 parts)
2050 {
2051 offset = (part_id % 63);
2052 mask = 1 << offset;
2053
2054 if ((m_SyncParts03 & mask) > 0)
2055 return true;
2056 }
2057
2058 return false;
2059 }
2060
2061 protected void RegisterActionForSync(int part_id, int action_id)
2062 {
2065 }
2066
2067 protected void ResetActionSyncData()
2068 {
2069 //reset data
2070 m_InteractedPartId = -1;
2072 }
2073
2074 protected void SetActionFromSyncData()
2075 {
2076 if (m_InteractedPartId > -1 && m_PerformedActionId > -1)
2077 {
2080
2081 switch (build_action_id)
2082 {
2086 }
2087 }
2088 }
2089 //------
2090
2092 {
2093 string key = part.m_PartName;
2094 bool is_base = part.IsBase();
2096 bsbDebugSpam("[bsb] " + GetDebugName(this) + " SetPartFromSyncData try to sync: built=" + is_part_built_sync + " key=" + key + " part=" + part.GetPartName() + " part_built=" + part.IsBuilt());
2098 {
2099 if (!part.IsBuilt())
2100 {
2101 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData +++ " + key);
2102 GetConstruction().AddToConstructedParts(key);
2103 GetConstruction().ShowConstructionPartPhysics(part.GetPartName());
2104
2105 if (is_base)
2106 {
2108 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2109 }
2110 }
2111 }
2112 else
2113 {
2114 if (part.IsBuilt())
2115 {
2116 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " SetPartsFromSyncData --- " + key);
2117 GetConstruction().RemoveFromConstructedParts(key);
2118 GetConstruction().HideConstructionPartPhysics(part.GetPartName());
2119
2120 if (is_base)
2121 {
2123 AddProxyPhysics(ANIMATION_DEPLOYED);
2124 }
2125 }
2126 }
2127
2128 //check slot lock for material attachments
2129 GetConstruction().SetLockOnAttachedMaterials(part.GetPartName(), part.IsBuilt()); //failsafe for corrupted sync/storage data
2130 }
2131
2132 //set construction parts based on synchronized data
2134 {
2137
2138 for (int i = 0; i < construction_parts.Count(); ++i)
2139 {
2140 string key = construction_parts.GetKey(i);
2143 }
2144
2145 //regenerate navmesh
2146 UpdateNavmesh();
2147 }
2148
2150 {
2153
2154 for (int i = 0; i < construction_parts.Count(); ++i)
2155 {
2156 string key = construction_parts.GetKey(i);
2158
2159 if (value.GetId() == id)
2160 return value;
2161 }
2162
2163 return NULL;
2164 }
2165 //
2166
2167 //Base
2168 bool HasBase()
2169 {
2170 return m_HasBase;
2171 }
2172
2173 void SetBaseState(bool has_base)
2174 {
2176 }
2177
2178 override bool IsDeployable()
2179 {
2180 return true;
2181 }
2182
2183 bool IsOpened()
2184 {
2185 return false;
2186 }
2187
2188 //--- CONSTRUCTION KIT
2190 {
2194
2195 return construction_kit;
2196 }
2197
2199 {
2200 ItemBase construction_kit = ItemBase.Cast(player.GetHumanInventory().CreateInHands(GetConstructionKitType()));
2203 }
2204
2205 protected vector GetKitSpawnPosition()
2206 {
2207 return GetPosition();
2208 }
2209
2210 protected string GetConstructionKitType()
2211 {
2212 return "";
2213 }
2214
2216 {
2218 GetGame().ObjectDelete(construction_kit);
2219 }
2220
2221 //--- CONSTRUCTION
2222 void DestroyConstruction()
2223 {
2224 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " DestroyConstruction");
2225 GetGame().ObjectDelete(this);
2226 }
2227
2228 // --- EVENTS
2229 override void OnStoreSave(ParamsWriteContext ctx)
2230 {
2231 super.OnStoreSave(ctx);
2232
2233 //sync parts 01
2234 ctx.Write(m_SyncParts01);
2235 ctx.Write(m_SyncParts02);
2236 ctx.Write(m_SyncParts03);
2237
2238 ctx.Write(m_HasBase);
2239 }
2240
2241 override bool OnStoreLoad(ParamsReadContext ctx, int version)
2242 {
2243 if (!super.OnStoreLoad(ctx, version))
2244 return false;
2245
2246 //--- Base building data ---
2247 //Restore synced parts data
2248 if (!ctx.Read(m_SyncParts01))
2249 {
2250 m_SyncParts01 = 0; //set default
2251 return false;
2252 }
2253 if (!ctx.Read(m_SyncParts02))
2254 {
2255 m_SyncParts02 = 0; //set default
2256 return false;
2257 }
2258 if (!ctx.Read(m_SyncParts03))
2259 {
2260 m_SyncParts03 = 0; //set default
2261 return false;
2262 }
2263
2264 //has base
2265 if (!ctx.Read(m_HasBase))
2266 {
2267 m_HasBase = false;
2268 return false;
2269 }
2270 //---
2271
2272 return true;
2273 }
2274
2275 override void AfterStoreLoad()
2276 {
2277 super.AfterStoreLoad();
2278
2281 }
2282
2284 {
2285 //update server data
2287
2288 //set base state
2289 ConstructionPart construction_part = GetConstruction().GetBaseConstructionPart();
2290 SetBaseState(construction_part.IsBuilt()) ;
2291
2292 //synchronize after load
2294 }
2295
2296 override void EEHealthLevelChanged(int oldLevel, int newLevel, string zone)
2297 {
2299 return;
2300
2301 super.EEHealthLevelChanged(oldLevel, newLevel, zone);
2302
2303 if (GetGame().IsMultiplayer() && !GetGame().IsServer())
2304 return;
2305
2307 string part_name = zone;
2308 part_name.ToLower();
2309
2311 {
2313
2314 if (construction_part && construction.IsPartConstructed(part_name))
2315 {
2316 construction.DestroyPartServer(null, part_name, AT_DESTROY_PART);
2317 construction.DestroyConnectedParts(part_name);
2318 }
2319
2320 //barbed wire handling (hack-ish)
2321 if (part_name.Contains("barbed"))
2322 {
2323 BarbedWire barbed_wire = BarbedWire.Cast(FindAttachmentBySlotName(zone));
2324 if (barbed_wire)
2325 barbed_wire.SetMountedState(false);
2326 }
2327 }
2328 }
2329
2330 override void EEOnAfterLoad()
2331 {
2333 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(SetPartsAfterStoreLoad, 500, false, this);
2334
2335 super.EEOnAfterLoad();
2336 }
2337
2338 override void EEInit()
2339 {
2340 super.EEInit();
2341
2342 // init visuals and physics
2343 InitBaseState();
2344
2345 //debug
2346#ifdef DEVELOPER
2348#endif
2349 }
2350
2351 override void EEItemAttached(EntityAI item, string slot_name)
2352 {
2353 super.EEItemAttached(item, slot_name);
2354
2356 UpdateVisuals();
2358 }
2359
2360 override void EEItemDetached(EntityAI item, string slot_name)
2361 {
2362 super.EEItemDetached(item, slot_name);
2363
2364 UpdateVisuals();
2366 }
2367
2368 protected void OnSetSlotLock(int slotId, bool locked, bool was_locked)
2369 {
2371 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("inv: OnSetSlotLock " + GetDebugName(this) + " slot=" + slot_name + " locked=" + locked + " was_locked=" + was_locked);
2372
2375 }
2376
2377 //ignore out of reach condition
2378 override bool IgnoreOutOfReachCondition()
2379 {
2380 return true;
2381 }
2382
2383 //CONSTRUCTION EVENTS
2384 //Build
2385 void OnPartBuiltServer(notnull Man player, string part_name, int action_id)
2386 {
2388
2389 //check base state
2390 if (construtionPart.IsBase())
2391 {
2392 SetBaseState(true);
2393
2394 //spawn kit
2396 }
2397
2398 //register constructed parts for synchronization
2400
2401 //register action that was performed on part
2403
2404 //synchronize
2406
2407 SetPartFromSyncData(construtionPart); // server part of sync, client will be synced from SetPartsFromSyncData
2408
2409 UpdateNavmesh();
2410
2411 //update visuals
2412 UpdateVisuals();
2413
2414 //reset action sync data
2415 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2416 }
2417
2418 void OnPartBuiltClient(string part_name, int action_id)
2419 {
2420 //play sound
2422 }
2423
2424 //Dismantle
2426 {
2427 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDismantledServer " + part_name);
2429
2430 //register constructed parts for synchronization
2432
2433 //register action that was performed on part
2435
2436 //synchronize
2438
2439 // server part of sync, client will be synced from SetPartsFromSyncData
2441
2442 UpdateNavmesh();
2443
2444 //update visuals
2445 UpdateVisuals();
2446
2447 //reset action sync data
2448 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2449
2450 //check base state
2451 if (construtionPart.IsBase())
2452 {
2453 //Destroy construction
2454 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2455 }
2456 }
2457
2459 {
2460 //play sound
2462 }
2463
2464 //Destroy
2466 {
2467 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " OnPartDestroyedServer " + part_name);
2469
2470 //register constructed parts for synchronization
2472
2473 //register action that was performed on part
2475
2476 //synchronize
2478
2479 // server part of sync, client will be synced from SetPartsFromSyncData
2481
2482 UpdateNavmesh();
2483
2484 //update visuals
2485 UpdateVisuals();
2486
2487 //reset action sync data
2488 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(ResetActionSyncData, 100, false, this);
2489
2490 //check base state
2491 if (construtionPart.IsBase())
2492 {
2493 //Destroy construction
2494 GetGame().GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(DestroyConstruction, 200, false, this);
2495 }
2496 }
2497
2498 void OnPartDestroyedClient(string part_name, int action_id)
2499 {
2500 //play sound
2502 }
2503
2504 // --- UPDATE
2505 void InitBaseState()
2506 {
2507 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::InitBaseState ");
2508
2509 InitVisuals();
2510 UpdateNavmesh(); //regenerate navmesh
2511 GetConstruction().InitBaseState();
2512 }
2513
2514 void InitVisuals()
2515 {
2516 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " InitVisuals");
2517 //check base
2518 if (!HasBase())
2519 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2520 else
2521 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2522
2523 GetConstruction().UpdateVisuals();
2524 }
2525
2526 void UpdateVisuals()
2527 {
2529
2531 foreach (string slotName : attachmentSlots)
2533
2534 //check base
2535 if (!HasBase())
2536 SetAnimationPhase(ANIMATION_DEPLOYED, 0);
2537 else
2538 SetAnimationPhase(ANIMATION_DEPLOYED, 1);
2539
2540 GetConstruction().UpdateVisuals();
2541 }
2542
2544 {
2545 string slotNameMounted = slot_name + "_Mounted";
2546 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2547
2548 if (attachment)
2549 {
2550 BarbedWire barbedWire = BarbedWire.Cast(attachment);
2551 if (barbedWire && barbedWire.IsMounted())
2553 else
2555
2556 if (is_locked)
2557 {
2558 SetAnimationPhase(slotNameMounted, 0);
2559 SetAnimationPhase(slot_name, 1);
2560 }
2561 else
2562 {
2563 SetAnimationPhase(slotNameMounted, 1);
2564 SetAnimationPhase(slot_name, 0);
2565 }
2566 }
2567 else
2568 {
2569 SetAnimationPhase(slotNameMounted, 1);
2570 SetAnimationPhase(slot_name, 1);
2571
2573 }
2574 }
2575
2576 // avoid calling this function on frequent occasions, it's a massive performance hit
2577 void UpdatePhysics()
2578 {
2580 bsbDebugPrint("[bsb] " + GetDebugName(this) + " BaseBuildingBase::UpdatePhysics");
2581
2584
2586 bsbDebugPrint("[bsb] " + GetDebugName(this) + " att_cnt=" + attachmentSlots.Count());
2587
2588 foreach (string slotName : attachmentSlots)
2590
2591 //check base
2592 if (!HasBase())
2593 {
2595 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " ADD");
2596
2597 AddProxyPhysics(ANIMATION_DEPLOYED);
2598 }
2599 else
2600 {
2602 bsbDebugPrint("[bsb] " + GetDebugName(this) + ANIMATION_DEPLOYED + " RM");
2603
2604 RemoveProxyPhysics(ANIMATION_DEPLOYED);
2605 }
2606
2607 GetConstruction().UpdatePhysics();
2608 UpdateNavmesh();
2609 }
2610
2612 {
2613 //checks for invalid appends; hotfix
2614 if (!m_Mountables || m_Mountables.Find(slot_name) == -1)
2615 return;
2616 //----------------------------------
2617 string slot_name_mounted = slot_name + "_Mounted";
2618 EntityAI attachment = FindAttachmentBySlotName(slot_name);
2619
2620 //remove proxy physics
2621 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Removing ATT SLOT=" + slot_name + " RM / RM");
2622 RemoveProxyPhysics(slot_name_mounted);
2623 RemoveProxyPhysics(slot_name);
2624
2625 if (attachment)
2626 {
2627 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " Adding ATT=" + Object.GetDebugName(attachment));
2628 if (is_locked)
2629 {
2630 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " RM / RM");
2631 AddProxyPhysics(slot_name_mounted);
2632 }
2633 else
2634 {
2635 if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] " + GetDebugName(this) + " ADD");
2636 AddProxyPhysics(slot_name);
2637 }
2638 }
2639 }
2640
2641 protected void UpdateNavmesh()
2642 {
2643 SetAffectPathgraph(true, false);
2644 GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(GetGame().UpdatePathgraphRegionByObject, 100, false, this);
2645 }
2646
2647 override bool CanUseConstruction()
2648 {
2649 return true;
2650 }
2651
2652 override bool CanUseConstructionBuild()
2653 {
2654 return true;
2655 }
2656
2658 {
2659 if (attachment)
2660 {
2662 attachment.GetInventory().GetCurrentInventoryLocation(inventory_location);
2663
2664 return GetInventory().GetSlotLock(inventory_location.GetSlot());
2665 }
2666
2667 return false;
2668 }
2669
2670 protected bool IsAttachmentSlotLocked(string slot_name)
2671 {
2672 return GetInventory().GetSlotLock(InventorySlots.GetSlotIdFromString(slot_name));
2673 }
2674
2675 //--- ATTACHMENT SLOTS
2677 {
2678 string config_path = "CfgVehicles" + " " + entity.GetType() + " " + "attachments";
2679 if (GetGame().ConfigIsExisting(config_path))
2680 GetGame().ConfigGetTextArray(config_path, attachment_slots);
2681 }
2682
2684 {
2685 return true;
2686 }
2687
2688 protected bool CheckMemoryPointVerticalDistance(float max_dist, string selection, PlayerBase player)
2689 {
2690 return true;
2691 }
2692
2693 protected bool CheckLevelVerticalDistance(float max_dist, string selection, PlayerBase player)
2694 {
2695 return true;
2696 }
2697
2698 // --- INIT
2699 void ConstructionInit()
2700 {
2701 if (!m_Construction)
2702 m_Construction = new Construction(this);
2703
2704 GetConstruction().Init();
2705 }
2706
2708 {
2709 return m_Construction;
2710 }
2711
2712 //--- INVENTORY/ATTACHMENTS CONDITIONS
2713 //attachments
2715 {
2716 return super.CanReceiveAttachment(attachment, slotId);
2717 }
2718
2720 {
2721 int attachment_count = GetInventory().AttachmentCount();
2722 if (attachment_count > 0)
2723 {
2724 if (HasBase() && attachment_count == 1)
2725 return false;
2726
2727 return true;
2728 }
2729
2730 return false;
2731 }
2732
2733 override bool ShowZonesHealth()
2734 {
2735 return true;
2736 }
2737
2738 //this into/outo parent.Cargo
2739 override bool CanPutInCargo(EntityAI parent)
2740 {
2741 return false;
2742 }
2743
2744 override bool CanRemoveFromCargo(EntityAI parent)
2745 {
2746 return false;
2747 }
2748
2749 //hands
2750 override bool CanPutIntoHands(EntityAI parent)
2751 {
2752 return false;
2753 }
2754
2755 //--- ACTION CONDITIONS
2756 //direction
2757 override bool IsFacingPlayer(PlayerBase player, string selection)
2758 {
2759 return true;
2760 }
2761
2762 override bool IsPlayerInside(PlayerBase player, string selection)
2763 {
2764 return true;
2765 }
2766
2769 {
2770 return false;
2771 }
2772
2773 //camera direction check
2774 bool IsFacingCamera(string selection)
2775 {
2776 return true;
2777 }
2778
2779 //roof check
2781 {
2782 return false;
2783 }
2784
2785 //selection->player distance check
2786 bool HasProperDistance(string selection, PlayerBase player)
2787 {
2788 return true;
2789 }
2790
2791 //folding
2793 {
2794 if (HasBase() || GetInventory().AttachmentCount() > 0)
2795 return false;
2796
2797 return true;
2798 }
2799
2801 {
2804
2805 return item;
2806 }
2807
2808 //Damage triggers (barbed wire)
2809 void CreateAreaDamage(string slot_name, float rotation_angle = 0)
2810 {
2811 if (GetGame() && GetGame().IsServer())
2812 {
2813 //destroy area damage if some already exists
2815
2816 //create new area damage
2818 areaDamage.SetDamageComponentType(AreaDamageComponentTypes.HITZONE);
2819
2820 vector min_max[2];
2821 if (MemoryPointExists(slot_name + "_min"))
2822 min_max[0] = GetMemoryPointPos(slot_name + "_min");
2823 if (MemoryPointExists(slot_name + "_max"))
2824 min_max[1] = GetMemoryPointPos(slot_name + "_max");
2825
2826 //get proper trigger extents (min<max)
2827 vector extents[2];
2828 GetConstruction().GetTriggerExtents(min_max, extents);
2829
2830 //get box center
2831 vector center;
2832 center = GetConstruction().GetBoxCenter(min_max);
2833 center = ModelToWorld(center);
2834
2835 //rotate center if needed
2838
2839 areaDamage.SetExtents(extents[0], extents[1]);
2840 areaDamage.SetAreaPosition(center);
2841 areaDamage.SetAreaOrientation(orientation);
2842 areaDamage.SetLoopInterval(1.0);
2843 areaDamage.SetDeferDuration(0.2);
2844 areaDamage.SetHitZones({ "Torso", "LeftHand", "LeftLeg", "LeftFoot", "RightHand", "RightLeg", "RightFoot" });
2845 areaDamage.SetAmmoName("BarbedWireHit");
2846 areaDamage.Spawn();
2847
2849 }
2850 }
2851
2853 {
2854 if (angle_deg != 0)
2855 {
2856 //orientation
2858
2859 //center
2861 if (MemoryPointExists("rotate_axis"))
2862 rotate_axis = ModelToWorld(GetMemoryPointPos("rotate_axis"));
2865 center[0] = r_center_x;
2866 center[2] = r_center_z;
2867 }
2868 }
2869
2870 void DestroyAreaDamage(string slot_name)
2871 {
2872 if (GetGame() && GetGame().IsServer())
2873 {
2876 {
2877 if (areaDamage)
2878 areaDamage.Destroy();
2879
2881 }
2882 }
2883 }
2884
2885 override bool IsIgnoredByConstruction()
2886 {
2887 return true;
2888 }
2889
2890 //================================================================
2891 // SOUNDS
2892 //================================================================
2893 protected void SoundBuildStart(string part_name)
2894 {
2895 PlaySoundSet(m_Sound, GetBuildSoundByMaterial(part_name), 0.1, 0.1);
2896 }
2897
2898 protected void SoundDismantleStart(string part_name)
2899 {
2900 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2901 }
2902
2903 protected void SoundDestroyStart(string part_name)
2904 {
2905 PlaySoundSet(m_Sound, GetDismantleSoundByMaterial(part_name), 0.1, 0.1);
2906 }
2907
2908 protected string GetBuildSoundByMaterial(string part_name)
2909 {
2911
2912 switch (material_type)
2913 {
2914 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_BUILD_WOOD_LOG;
2915 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_BUILD_WOOD_PLANK;
2916 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_BUILD_WOOD_STAIRS;
2917 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_BUILD_METAL;
2918 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_BUILD_WIRE;
2919 }
2920
2921 return "";
2922 }
2923
2924 protected string GetDismantleSoundByMaterial(string part_name)
2925 {
2927
2928 switch (material_type)
2929 {
2930 case ConstructionMaterialType.MATERIAL_LOG: return SOUND_DISMANTLE_WOOD_LOG;
2931 case ConstructionMaterialType.MATERIAL_WOOD: return SOUND_DISMANTLE_WOOD_PLANK;
2932 case ConstructionMaterialType.MATERIAL_STAIRS: return SOUND_DISMANTLE_WOOD_STAIRS;
2933 case ConstructionMaterialType.MATERIAL_METAL: return SOUND_DISMANTLE_METAL;
2934 case ConstructionMaterialType.MATERIAL_WIRE: return SOUND_DISMANTLE_WIRE;
2935 }
2936
2937 return "";
2938 }
2939
2940 //misc
2942 {
2943 if (!GetGame().IsMultiplayer() || GetGame().IsServer())
2944 {
2945 //if this is a hybrid attachment, set damage of appropriate damage zone. Zone name must match slot name...WIP
2947 SetHealth(slot_name, "Health", item.GetHealth());
2948 }
2949 }
2950
2951 override int GetDamageSystemVersionChange()
2952 {
2953 return 111;
2954 }
2955
2956 override void SetActions()
2957 {
2958 super.SetActions();
2959
2961 //AddAction(ActionTakeHybridAttachment);
2962 //AddAction(ActionTakeHybridAttachmentToHands);
2965 }
2966
2967 //================================================================
2968 // DEBUG
2969 //================================================================
2970 protected void DebugCustomState()
2971 {
2972 }
2973
2976 {
2977 return null;
2978 }
2979
2980 override void OnDebugSpawn()
2981 {
2982 FullyBuild();
2983 }
2984
2985 void FullyBuild()
2986 {
2988 array<ConstructionPart> parts = GetConstruction().GetConstructionParts().GetValueArray();
2989
2990 Man p;
2991
2992#ifdef SERVER
2994 GetGame().GetWorld().GetPlayerList(players);
2995 if (players.Count())
2996 p = players[0];
2997#else
2998 p = GetGame().GetPlayer();
2999#endif
3000
3001 foreach (ConstructionPart part : parts)
3002 {
3003 bool excluded = false;
3004 string partName = part.GetPartName();
3005 if (excludes)
3006 {
3007 foreach (string exclude : excludes)
3008 {
3009 if (partName.Contains(exclude))
3010 {
3011 excluded = true;
3012 break;
3013 }
3014 }
3015 }
3016
3017 if (!excluded)
3019 }
3020
3021 GetConstruction().UpdateVisuals();
3022 }
3023}
3024
3025void bsbDebugPrint(string s)
3026{
3027#ifdef BSB_DEBUG
3028 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3029#else
3030 //Print("" + s); // comment/uncomment to hide/see debug logs
3031#endif
3032}
3033void bsbDebugSpam(string s)
3034{
3035#ifdef BSB_DEBUG_SPAM
3036 PrintToRPT("" + s); // comment/uncomment to hide/see debug logs
3037#else
3038 //Print("" + s); // comment/uncomment to hide/see debug logs
3039#endif
3040}

Variable Documentation

◆ ANIMATION_DEPLOYED

◆ m_Construction

ref Construction m_Construction

Definition at line 1181 of file BaseBuildingBase.c.

Referenced by ItemBase::ConstructionInit(), and ItemBase::GetConstruction().

◆ m_ConstructionKitHealth

◆ m_DamageTriggers

◆ m_HasBase

◆ m_HybridAttachments

ref array<string> m_HybridAttachments
protected

◆ m_InteractedPartId

◆ m_Mountables

ref array<string> m_Mountables
protected

◆ m_PerformedActionId

◆ m_Sound

EffectSound m_Sound
protected

Definition at line 1205 of file BaseBuildingBase.c.

◆ m_SyncParts01

◆ m_SyncParts02

◆ m_SyncParts03

◆ SOUND_BUILD_METAL

const string SOUND_BUILD_METAL = "putDown_MetalPlank_SoundSet"

Definition at line 1196 of file BaseBuildingBase.c.

Referenced by ItemBase::GetBuildSoundByMaterial().

◆ SOUND_BUILD_WIRE

const string SOUND_BUILD_WIRE = "putDown_BarbedWire_SoundSet"

Definition at line 1197 of file BaseBuildingBase.c.

Referenced by ItemBase::GetBuildSoundByMaterial().

◆ SOUND_BUILD_WOOD_LOG

const string SOUND_BUILD_WOOD_LOG = "putDown_WoodLog_SoundSet"

Definition at line 1193 of file BaseBuildingBase.c.

Referenced by ItemBase::GetBuildSoundByMaterial().

◆ SOUND_BUILD_WOOD_PLANK

const string SOUND_BUILD_WOOD_PLANK = "putDown_WoodPlank_SoundSet"

Definition at line 1194 of file BaseBuildingBase.c.

Referenced by ItemBase::GetBuildSoundByMaterial().

◆ SOUND_BUILD_WOOD_STAIRS

const string SOUND_BUILD_WOOD_STAIRS = "putDown_WoodStairs_SoundSet"

Definition at line 1195 of file BaseBuildingBase.c.

Referenced by ItemBase::GetBuildSoundByMaterial().

◆ SOUND_DISMANTLE_METAL

const string SOUND_DISMANTLE_METAL = "Crash_MetalPlank_SoundSet"

Definition at line 1202 of file BaseBuildingBase.c.

Referenced by ItemBase::GetDismantleSoundByMaterial().

◆ SOUND_DISMANTLE_WIRE

const string SOUND_DISMANTLE_WIRE = "putDown_BarbedWire_SoundSet"

Definition at line 1203 of file BaseBuildingBase.c.

Referenced by ItemBase::GetDismantleSoundByMaterial().

◆ SOUND_DISMANTLE_WOOD_LOG

const string SOUND_DISMANTLE_WOOD_LOG = "Crash_WoodPlank_SoundSet"

Definition at line 1199 of file BaseBuildingBase.c.

Referenced by ItemBase::GetDismantleSoundByMaterial().

◆ SOUND_DISMANTLE_WOOD_PLANK

const string SOUND_DISMANTLE_WOOD_PLANK = "Crash_WoodPlank_SoundSet"

Definition at line 1200 of file BaseBuildingBase.c.

Referenced by ItemBase::GetDismantleSoundByMaterial().

◆ SOUND_DISMANTLE_WOOD_STAIRS

const string SOUND_DISMANTLE_WOOD_STAIRS = "Crash_WoodPlank_SoundSet"

Definition at line 1201 of file BaseBuildingBase.c.

Referenced by ItemBase::GetDismantleSoundByMaterial().