class Rebuilding : ConstructionBase
{
static int REBUILDING_STORAGE_VERSION = 1;
protected int m_LastStorageVersion;
protected int m_SyncParts1;
protected int m_SyncParts2;
protected int m_SyncParts3;
protected int m_SyncParts4;
protected int m_SyncParts5;
protected int m_SyncParts6;
protected int m_SyncParts7;
protected int m_SyncParts8;
protected int m_SyncParts9;
protected int m_SyncParts10;
protected int m_SyncParts1Local;
protected int m_SyncParts2Local;
protected int m_SyncParts3Local;
protected int m_SyncParts4Local;
protected int m_SyncParts5Local;
protected int m_SyncParts6Local;
protected int m_SyncParts7Local;
protected int m_SyncParts8Local;
protected int m_SyncParts9Local;
protected int m_SyncParts10Local;
protected ref map<int, int> m_SupportRequirement;
protected ref map<int, int> m_SupportFulfillment;
//BuildPart
override void BuildPartServerEx(Man player, string part_name, int action_id)
{
super.BuildPartServerEx(player,part_name,action_id);
ProcessOnPartBuiltServer(player, part_name, action_id);
}
//DismantlePart
override void DismantlePartServerEx(Man player, string part_name, int action_id)
{
super.DismantlePartServerEx(player,part_name,action_id);
ProcessOnPartDismantledServer(player, part_name, action_id);
}
//DestroyPart
override void DestroyPartServer(Man player, string part_name, int action_id, bool destroyed_by_connected_part = false)
{
//set DamageZone health to zero
string damage_zone;
if (DamageSystem.GetDamageZoneFromComponentName(m_EntityParent, part_name, damage_zone) && m_EntityParent.GetHealth(damage_zone, "Health") > 0)
{
m_EntityParent.SetHealth(damage_zone, "Health", 0);
}
super.DestroyPartServer(player,part_name,action_id,destroyed_by_connected_part);
ProcessOnPartDestroyedServer(player, part_name, action_id, destroyed_by_connected_part);
}
//============================================
// Syncing
//============================================
override void OnConstructionVariablesSynchronized()
{
super.OnConstructionVariablesSynchronized();
HandleSyncedParts();
}
protected void HandleSyncedParts()
{
bool changed = false;
if (m_SyncParts1Local != m_SyncParts1)
{
m_SyncParts1Local = m_SyncParts1;
changed = true;
}
if (m_SyncParts2Local != m_SyncParts2)
{
m_SyncParts2Local = m_SyncParts2;
changed = true;
}
if (m_SyncParts3Local != m_SyncParts3)
{
m_SyncParts3Local = m_SyncParts3;
changed = true;
}
if (m_SyncParts4Local != m_SyncParts4)
{
m_SyncParts4Local = m_SyncParts4;
changed = true;
}
if (m_SyncParts5Local != m_SyncParts5)
{
m_SyncParts5Local = m_SyncParts5;
changed = true;
}
if (m_SyncParts6Local != m_SyncParts6)
{
m_SyncParts6Local = m_SyncParts6;
changed = true;
}
if (m_SyncParts7Local != m_SyncParts7)
{
m_SyncParts7Local = m_SyncParts7;
changed = true;
}
if (m_SyncParts8Local != m_SyncParts8)
{
m_SyncParts8Local = m_SyncParts8;
changed = true;
}
if (m_SyncParts9Local != m_SyncParts9)
{
m_SyncParts9Local = m_SyncParts9;
changed = true;
}
if (m_SyncParts10Local != m_SyncParts10)
{
m_SyncParts10Local = m_SyncParts10;
changed = true;
}
//do local client stuff here
if (changed)
{
array<ConstructionPart> changedParts = new array<ConstructionPart>();
if (SetAllPartsFromSyncData(changedParts))
{
QueueFullUpdateParts(changedParts);
}
changedParts = null;
}
}
override void HandleSyncDataBuild(ConstructionPart part)
{
int partId = part.GetId();
if (partId <= 0)
return;
//in case of variable part sync data sizes, prepare some map with info to be used in the switch. Sadly the syncVariables themselves can't be handled as such.
int adjustedID = Math.Clamp((partId - 1), 0, int.MAX) * part.GetSyncDataSize();
int syncIntIdx = adjustedID / BIT_INT_SIZE;
int offset = adjustedID % BIT_INT_SIZE;
switch (syncIntIdx)
{
case 0:
part.ComposeSyncDataOnBuild(m_SyncParts1, offset);
break;
case 1:
part.ComposeSyncDataOnBuild(m_SyncParts2, offset);
break;
case 2:
part.ComposeSyncDataOnBuild(m_SyncParts3, offset);
break;
case 3:
part.ComposeSyncDataOnBuild(m_SyncParts4, offset);
break;
case 4:
part.ComposeSyncDataOnBuild(m_SyncParts5, offset);
break;
case 5:
part.ComposeSyncDataOnBuild(m_SyncParts6, offset);
break;
case 6:
part.ComposeSyncDataOnBuild(m_SyncParts7, offset);
break;
case 7:
part.ComposeSyncDataOnBuild(m_SyncParts8, offset);
break;
case 8:
part.ComposeSyncDataOnBuild(m_SyncParts9, offset);
break;
case 9:
part.ComposeSyncDataOnBuild(m_SyncParts10, offset);
break;
}
}
override void HandleSyncDataDismantle(ConstructionPart part)
{
int partId = part.GetId();
if (partId <= 0)
return;
int adjustedID = Math.Clamp((partId - 1), 0, int.MAX) * part.GetSyncDataSize();
int syncIntIdx = adjustedID / BIT_INT_SIZE;
int offset = adjustedID % BIT_INT_SIZE;
switch (syncIntIdx)
{
case 0:
part.ComposeSyncDataOnDismantle(m_SyncParts1, offset);
break;
case 1:
part.ComposeSyncDataOnDismantle(m_SyncParts2, offset);
break;
case 2:
part.ComposeSyncDataOnDismantle(m_SyncParts3, offset);
break;
case 3:
part.ComposeSyncDataOnDismantle(m_SyncParts4, offset);
break;
case 4:
part.ComposeSyncDataOnDismantle(m_SyncParts5, offset);
break;
case 5:
part.ComposeSyncDataOnDismantle(m_SyncParts6, offset);
break;
case 6:
part.ComposeSyncDataOnDismantle(m_SyncParts7, offset);
break;
case 7:
part.ComposeSyncDataOnDismantle(m_SyncParts8, offset);
break;
case 8:
part.ComposeSyncDataOnDismantle(m_SyncParts9, offset);
break;
case 9:
part.ComposeSyncDataOnDismantle(m_SyncParts10, offset);
break;
}
}
override void HandleSyncDataDestroy(ConstructionPart part)
{
int partId = part.GetId();
if (partId <= 0)
return;
int adjustedID = Math.Clamp((partId - 1), 0, int.MAX) * part.GetSyncDataSize();
int syncIntIdx = adjustedID / BIT_INT_SIZE;
int offset = adjustedID % BIT_INT_SIZE;
switch (syncIntIdx)
{
case 0:
part.ComposeSyncDataOnDestroy(m_SyncParts1, offset);
break;
case 1:
part.ComposeSyncDataOnDestroy(m_SyncParts2, offset);
break;
case 2:
part.ComposeSyncDataOnDestroy(m_SyncParts3, offset);
break;
case 3:
part.ComposeSyncDataOnDestroy(m_SyncParts4, offset);
break;
case 4:
part.ComposeSyncDataOnDestroy(m_SyncParts5, offset);
break;
case 5:
part.ComposeSyncDataOnDestroy(m_SyncParts6, offset);
break;
case 6:
part.ComposeSyncDataOnDestroy(m_SyncParts7, offset);
break;
case 7:
part.ComposeSyncDataOnDestroy(m_SyncParts8, offset);
break;
case 8:
part.ComposeSyncDataOnDestroy(m_SyncParts9, offset);
break;
case 9:
part.ComposeSyncDataOnDestroy(m_SyncParts10, offset);
break;
}
}
//! Returns the translated value of 'GetSyncDataSize' size (currently 2 bits)
int GetPartLocalSyncBitMaskValue(ConstructionPart part)
{
int partId = part.GetId();
if (partId <= 0)
return false;
int adjustedID = Math.Clamp((partId - 1), 0, int.MAX) * part.GetSyncDataSize();
int syncIntIdx = adjustedID / BIT_INT_SIZE;
int offset = adjustedID % BIT_INT_SIZE;
int tmp;
switch (syncIntIdx)
{
case 0:
tmp = m_SyncParts1;
break;
case 1:
tmp = m_SyncParts2;
break;
case 2:
tmp = m_SyncParts3;
break;
case 3:
tmp = m_SyncParts4;
break;
case 4:
tmp = m_SyncParts5;
break;
case 5:
tmp = m_SyncParts6;
break;
case 6:
tmp = m_SyncParts7;
break;
case 7:
tmp = m_SyncParts8;
break;
case 8:
tmp = m_SyncParts9;
break;
case 9:
tmp = m_SyncParts10;
break;
}
return part.CalculateLocalSyncBitMask(tmp, offset);
}
//! returns 'true' if any part state was changed
bool SetAllPartsFromSyncData(out array<ConstructionPart> changedParts = null)
{
bool wasChanged = false;
foreach (ConstructionPart part : m_ConstructionPartArray)
{
if (UpdatePartSyncStateLocal(part))
{
if (changedParts)
changedParts.Insert(part);
wasChanged = true;
}
}
return wasChanged;
}
//! Sets local construction part state
override bool UpdatePartSyncStateLocal(ConstructionPart part)
{
if (part.GetId() < 1)
{
if (!part.IsUtilityPart())
ErrorEx("invalid or corrupted part ID for part " + part.GetPartName() + ". Part ID must be > 0");
return false;
}
part.StoreLocalSyncBitMask(GetPartLocalSyncBitMaskValue(part)); //stores translated synced info on the local part instance
return SetPartStateLocal(part);
}
protected bool SetPartStateLocal(ConstructionPart part)
{
bool changed = false;
bool isSyncedAsBuilt = part.IsLocalSyncBuilt();
bool isBuilt = part.IsBuilt();
string name = part.GetPartName();
if (isSyncedAsBuilt)
{
if (!isBuilt)
{
AddToConstructedParts(name);
SetLockOnAttachedMaterials(name, true);
changed = true;
}
}
else
{
if (isBuilt)
{
RemoveFromConstructedParts(name);
SetLockOnAttachedMaterials(name, false);
changed = true;
}
}
return changed;
}
override void AddToConstructedParts(string part_name)
{
AddSupportFulfillmentFromSourcePart(part_name);
AddSupportRequirementFromSourcePart(part_name);
super.AddToConstructedParts(part_name);
}
override void RemoveFromConstructedParts(string part_name)
{
RemoveSupportFulfillmentFromSourcePart(part_name);
RemoveSupportRequirementFromSourcePart(part_name);
super.RemoveFromConstructedParts(part_name);
}
//============================================
// Events
//============================================
override void OnConstructionDoorOpenStart(DoorStartParams params)
{
super.OnConstructionDoorOpenStart(params);
int doorIdx = (int)params.param1;
BuildingBase building;
AdditionalDoorInfo info;
if (Class.CastTo(building, m_EntityParent) && Class.CastTo(info, building.GetDoorInfo(doorIdx)) && info.m_DoorConstructionPhysicsSource != string.Empty)
{
building.SetAnimationPhase(info.m_DoorConstructionPhysicsSource, 1.0);
}
}
override void OnConstructionDoorCloseStart(DoorStartParams params)
{
super.OnConstructionDoorCloseStart(params);
int doorIdx = (int)params.param1;
BuildingBase building;
AdditionalDoorInfo info;
if (Class.CastTo(building, m_EntityParent) && Class.CastTo(info, building.GetDoorInfo(doorIdx)) && info.m_DoorConstructionPhysicsSource != string.Empty)
{
building.SetAnimationPhase(info.m_DoorConstructionPhysicsSource, 0.0);
}
}
//============================================
// Persistence
//============================================
override bool HandleStorageEvents()
{
return true;
}
override void HandleStoreSave(ParamsWriteContext ctx)
{
if (HandleStorageEvents())
{
super.HandleStoreSave(ctx);
SerializeConstructionData(ctx);
}
}
override void HandleStoreLoad(ParamsReadContext ctx, int version)
{
if (HandleStorageEvents())
{
super.HandleStoreLoad(ctx,version);
if (!DeserializeConstructionData(ctx,version))
ErrorEx("deserialization of construction data of " + m_EntityParent.GetType() + " FAILED, presumed corrupted data",ErrorExSeverity.INFO);
//true if changed, we need to react to and sync the changes to clients
if (SetAllPartsFromSyncData())
{
QueueFullUpdateParts(m_ConstructionPartArray);
}
m_EntityParent.SetSynchDirty();
}
}
protected void SerializeConstructionData(ParamsWriteContext ctx)
{
ctx.Write(REBUILDING_STORAGE_VERSION);
ctx.Write(m_SyncParts1);
ctx.Write(m_SyncParts2);
ctx.Write(m_SyncParts3);
ctx.Write(m_SyncParts4);
ctx.Write(m_SyncParts5);
ctx.Write(m_SyncParts6);
ctx.Write(m_SyncParts7);
ctx.Write(m_SyncParts8);
ctx.Write(m_SyncParts9);
ctx.Write(m_SyncParts10);
}
protected bool DeserializeConstructionData(ParamsReadContext ctx, int version)
{
if (!ctx.Read(m_LastStorageVersion))
return false;
if (m_LastStorageVersion < REBUILDING_STORAGE_VERSION)
{
ErrorEx("Invalid rebuilding storage version | " + m_LastStorageVersion + "/" + REBUILDING_STORAGE_VERSION, ErrorExSeverity.INFO);
return false;
}
if (!ctx.Read(m_SyncParts1))
return false;
if (!ctx.Read(m_SyncParts2))
return false;
if (!ctx.Read(m_SyncParts3))
return false;
if (!ctx.Read(m_SyncParts4))
return false;
if (!ctx.Read(m_SyncParts5))
return false;
if (!ctx.Read(m_SyncParts6))
return false;
if (!ctx.Read(m_SyncParts7))
return false;
if (!ctx.Read(m_SyncParts8))
return false;
if (!ctx.Read(m_SyncParts9))
return false;
if (!ctx.Read(m_SyncParts10))
return false;
return true;
}
//============================================
// Inits
//============================================
override void Init()
{
super.Init();
InitSupportMap();
}
protected void InitBaseStateDeferred()
{
QueueFullUpdateParts(m_ConstructionPartArray);
}
override void InitBaseState()
{
g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).Call(InitBaseStateDeferred);
}
protected void InitSupportMap()
{
if (!m_SupportFulfillment)
m_SupportFulfillment = new map<int, int>();
if (!m_SupportRequirement)
m_SupportRequirement = new map<int, int>();
}
override protected typename GetDefaultPartInstanceTypename()
{
return ConstructionPartRebuild;
}
//============================================
// Parts
//============================================
protected ConstructionPartRebuild GetRebuildingPart(string part_name)
{
return ConstructionPartRebuild.Cast(GetConstructionPart(part_name));
}
//! Also checks for all listed variant categories (at least 1 from each is required)
override protected bool HasRequiredPart(string part_name)
{
//simple requirements check
if (!super.HasRequiredPart(part_name))
return false;
//now look into weighted variants
TConstructionPartVariantArray tmp = GetRebuildingPart(part_name).GetRequiredPartsVariants();
if (tmp)
{
bool anyVariantBuilt = false;
foreach (array<string> variantGroup : tmp)
{
anyVariantBuilt = false;
foreach (string partName : variantGroup)
{
anyVariantBuilt |= IsPartConstructed(partName);
}
if (!anyVariantBuilt)
return false;
}
}
return true;
}
////////////
//supports//
////////////
override protected bool PartHasAnySupportFulfilled(string part_name)
{
if (!super.PartHasAnySupportFulfilled(part_name))
return false;
ConstructionPartRebuild rebPart = GetRebuildingPart(part_name);
set<int> tmp = rebPart.GetConfiguredSupportCategories();
if (tmp.Count() > 0) //can fail only if some support category is defined
{
foreach (int category : tmp)
{
if (rebPart.GetSupportRequirement(category) <= m_SupportFulfillment.Get(category))
return true;
}
return false;
}
return true;
}
override bool SupportRemovalCheck(string part_name)
{
ConstructionPartRebuild rebPart = GetRebuildingPart(part_name);
set<int> tmp = rebPart.GetConfiguredSupportCategories();
foreach (int category : tmp)
{
if (m_SupportRequirement.Get(category) > (m_SupportFulfillment.Get(category) - rebPart.GetSupportProvided(category)))
return false;
}
return true;
}
protected void AddSupportFulfillmentFromSourcePart(string part_name)
{
ConstructionPartRebuild rebPart = GetRebuildingPart(part_name);
set<int> tmp = rebPart.GetConfiguredSupportCategories();
int value;
foreach (int category : tmp)
{
value = rebPart.GetSupportProvided(category);
if (m_SupportFulfillment.Contains(category))
{
value = Math.Clamp(m_SupportFulfillment.Get(category) + value, 0, int.MAX);
}
m_SupportFulfillment.Set(category, value);
}
}
protected void RemoveSupportFulfillmentFromSourcePart(string part_name)
{
ConstructionPartRebuild rebPart = GetRebuildingPart(part_name);
set<int> tmp = rebPart.GetConfiguredSupportCategories();
int value;
foreach (int category : tmp)
{
value = rebPart.GetSupportProvided(category);
if (m_SupportFulfillment.Contains(category))
{
value = Math.Clamp(m_SupportFulfillment.Get(category) - value, 0, int.MAX);
}
m_SupportFulfillment.Set(category, value);
}
}
protected void AddSupportRequirementFromSourcePart(string part_name)
{
ConstructionPartRebuild rebPart = GetRebuildingPart(part_name);
//skipping utility part requirement propagation
if (rebPart.IsUtilityPart())
return;
set<int> tmp = rebPart.GetConfiguredSupportCategories();
int value;
foreach (int category : tmp)
{
value = rebPart.GetSupportRequirement(category);
if (m_SupportRequirement.Contains(category))
{
value = Math.Clamp(m_SupportRequirement.Get(category) + value, 0, int.MAX);
}
m_SupportRequirement.Set(category, value);
}
}
protected void RemoveSupportRequirementFromSourcePart(string part_name)
{
ConstructionPartRebuild rebPart = GetRebuildingPart(part_name);
//skipping utility part requirement propagation
if (rebPart.IsUtilityPart())
return;
set<int> tmp = rebPart.GetConfiguredSupportCategories();
int value;
foreach (int category : tmp)
{
value = rebPart.GetSupportRequirement(category);
if (m_SupportRequirement.Contains(category))
{
value = Math.Clamp(m_SupportRequirement.Get(category) - value, 0, int.MAX);
}
m_SupportRequirement.Set(category, value);
}
}
override bool HasDependentPart(string part_name)
{
ConstructionPartRebuild rebPart;
foreach (ConstructionPart part : m_BuiltConstructionPartArray)
{
if (Class.CastTo(rebPart, part) && !rebPart.IsUtilityPart())
{
if (rebPart.GetRequiredParts().Find(part_name) != -1)
return true;
TConstructionPartVariantArray tmpArr = rebPart.GetRequiredPartsVariants();
if (tmpArr)
{
foreach (array<string> variantGroup : tmpArr)
{
foreach (string partName : variantGroup)
{
if (partName == part_name) //part_name needed
return true;
}
}
}
}
}
return false;
}
override protected array<string> GetValidDepenentPartsArray(string part_name, array<string> recurs = null)
{
string name = "";
array<string> dependentParts = new array<string>();
ConstructionPartRebuild rebPart;
foreach (ConstructionPart part : m_BuiltConstructionPartArray)
{
if (Class.CastTo(rebPart, part))
{
name = rebPart.GetPartName();
array<string> reqPartsArray = rebPart.GetRequiredParts();
if (reqPartsArray && reqPartsArray.Find(part_name) > -1)
{
if (!recurs || (recurs.Find(name) == -1))
dependentParts.Insert(name);
}
TConstructionPartVariantArray tmpArr = rebPart.GetRequiredPartsVariants();
if (tmpArr)
{
foreach (array<string> variantGroup : tmpArr)
{
foreach (string partName : variantGroup)
{
if (partName == part_name) //part_name needed
dependentParts.Insert(name);
}
}
}
if (!PartHasAnySupportFulfilled(name) && dependentParts.Find(name) == -1)
{
dependentParts.Insert(name);
}
}
}
return dependentParts;
}
//============================================
//UI-related getters
//============================================
//! client-side distance checks (valid memory point and configuration required)
override bool CanDisplayAttachmentCategoryConstruction(string categoryName)
{
if (!super.CanDisplayAttachmentCategoryConstruction(categoryName))
return false;
array<ConstructionPartTypeData> GUIRequiredParts = m_TypeLevelHolder.GetGuiRequiredPartTypesMap().Get(categoryName);
if (GUIRequiredParts.Count() > 0)
{
foreach (ConstructionPartTypeData partTypeData : GUIRequiredParts)
{
if (!GetConstructionPart(partTypeData.GetPartName()).IsBuilt())
return false;
}
}
//legacy dependency check, just in case
ConstructionPartRebuild rebPart = GetRebuildingPart(categoryName);
if (rebPart)
{
if (!rebPart.IsBuilt() || !HasRequiredPart(rebPart.GetPartName()))
return false;
}
return true;
}
}