class Fireplace extends FireplaceBase
{
bool m_ContactEventProcessing = false;
protected bool m_ContactDeletionProcessing = false;
void Fireplace()
{
//Particles - default for FireplaceBase
PARTICLE_FIRE_START = ParticleList.CAMP_FIRE_START;
PARTICLE_SMALL_FIRE = ParticleList.CAMP_SMALL_FIRE;
PARTICLE_NORMAL_FIRE = ParticleList.CAMP_NORMAL_FIRE;
PARTICLE_SMALL_SMOKE = ParticleList.CAMP_SMALL_SMOKE;
PARTICLE_NORMAL_SMOKE = ParticleList.CAMP_NORMAL_SMOKE;
PARTICLE_FIRE_END = ParticleList.CAMP_FIRE_END;
PARTICLE_STEAM_END = ParticleList.CAMP_STEAM_2END;
SetEventMask( EntityEvent.CONTACT | EntityEvent.TOUCH );
//! universal temperature sources overrides
m_UTSSettings.m_TemperatureItemCap = GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_MIDDLE;
m_UTSSettings.m_TemperatureCap = 20;
m_ThawnSurfaceUnderSupport = true;
}
override protected void InitializeTemperatureSources()
{
m_UTSLFireplace = new UniversalTemperatureSourceLambdaFireplace();
m_UTSLFireplace.SetSmallFireplaceTemperatureMax(PARAM_SMALL_FIRE_TEMPERATURE);
m_UTSLFireplace.SetNormalFireplaceTemperatureMax(PARAM_OUTDOOR_FIRE_TEMPERATURE);
m_UTSource = new UniversalTemperatureSource(this, m_UTSSettings, m_UTSLFireplace);
}
override bool IsBaseFireplace()
{
return true;
}
override bool CanObstruct()
{
return IsOven();
}
override bool CanProxyObstruct()
{
return IsOven();
}
override void EOnTouch( IEntity other, int extra )
{
ContactEvent( other, GetPosition() );
}
override void EOnContact( IEntity other, Contact extra )
{
ContactEvent( other, extra.Position );
}
void ContactEvent( IEntity other, vector position )
{
if (g_Game.IsServer() && !m_ContactEventProcessing && dBodyIsActive(this) && !IsSetForDeletion())
m_ContactEventProcessing = true;
}
override protected void EOnPostSimulate(IEntity other, float timeSlice)
{
super.EOnPostSimulate(other,timeSlice);
if (m_ContactEventProcessing && !m_ContactDeletionProcessing)
{
MiscGameplayFunctions.ThrowAllItemsInInventory(this, 0);
CheckForDestroy();
m_ContactDeletionProcessing = true;
}
}
//attachments
override bool CanReceiveAttachment( EntityAI attachment, int slotId )
{
if ( !super.CanReceiveAttachment(attachment, slotId) )
return false;
ItemBase item = ItemBase.Cast( attachment );
//cookware
if ( ( item.Type() == ATTACHMENT_CAULDRON ) || ( item.Type() == ATTACHMENT_COOKING_POT ) )
{
if ( FindAttachmentBySlotName( "CookingTripod" ) || IsOven() )
return true;
return false;
}
if ( item.Type() == ATTACHMENT_FRYING_PAN )
{
if ( IsOven() )
return true;
return false;
}
// food on direct cooking slots
if ( item.IsKindOf( "Edible_Base" ) )
{
if ( IsOven() )
return true;
return false;
}
//tripod
if ( item.IsInherited(TripodBase) )
{
if (!IsOven() && !GetHierarchyParent())
return true;
return false;
}
//stones
if ( item.Type() == ATTACHMENT_STONES )
{
if ( !GetHierarchyParent() && !IsBurning() )
return true;
return false;
}
return true;
}
override bool CanDisplayAttachmentSlot( int slot_id )
{
string slot_name = InventorySlots.GetSlotName(slot_id);
if ( super.CanDisplayAttachmentSlot(slot_id) )
{
if ( slot_name != "CookingEquipment" || FindAttachmentBySlotName( "CookingTripod" ) )
return true;
}
return false;
}
override bool CanLoadAttachment( EntityAI attachment )
{
if ( !super.CanLoadAttachment(attachment) )
return false;
ItemBase item = ItemBase.Cast( attachment );
//cookware
if ( ( item.Type() == ATTACHMENT_CAULDRON ) || ( item.Type() == ATTACHMENT_COOKING_POT ) )
{
//if ( IsItemTypeAttached( ATTACHMENT_TRIPOD ) /*|| IsOven()*/ )
return true;
}
if ( item.Type() == ATTACHMENT_FRYING_PAN )
{
//if ( IsOven() )
return true;
}
// food on direct cooking slots
//if ( IsOven() )
//{
if ( item.IsKindOf( "Edible_Base" ) )
return true;
//}
//tripod
if ( item.Type() == ATTACHMENT_TRIPOD )
{
if ( /*!IsOven() &&*/ GetHierarchyParent() == NULL )
return true;
}
//stones
if ( item.Type() == ATTACHMENT_STONES )
{
if ( GetHierarchyParent() /*|| IsBurning()*/ )
return false;
return true;
}
return true;
}
override bool CanReleaseAttachment( EntityAI attachment )
{
if (!super.CanReleaseAttachment(attachment))
{
return false;
}
ItemBase item = ItemBase.Cast(attachment);
//stones
if (item.Type() == ATTACHMENT_STONES)
{
if (IsBurning())
{
return false;
}
int stone_quantity = item.GetQuantity();
if (HasStoneCircle() && stone_quantity <= 8)
{
return false;
}
if (IsOven())
{
return false;
}
}
return true;
}
override void EEItemAttached(EntityAI item, string slot_name)
{
super.EEItemAttached(item, slot_name);
ItemBase item_base = ItemBase.Cast(item);
if (IsKindling(item_base) || IsFuel(item_base))
{
AddToFireConsumables(item_base);
}
//cookware
if (item_base.IsCookware())
SetCookingEquipment(item_base);
if (g_Game.IsServer() && !IsOven())
{
if (item_base.Type() == ATTACHMENT_COOKING_POT)
item_base.SetAnimationPhase(ANIMATION_COOKWARE_HANDLE, 0);
if (item.Type() == ATTACHMENT_CAULDRON)
item_base.SetAnimationPhase(ANIMATION_CAULDRON_HANDLE, 0);
}
// direct cooking/smoking slots
bool edible_base_attached = false;
switch (slot_name)
{
case "DirectCookingA":
m_DirectCookingSlots[0] = item_base;
edible_base_attached = true;
break;
case "DirectCookingB":
m_DirectCookingSlots[1] = item_base;
edible_base_attached = true;
break;
case "DirectCookingC":
m_DirectCookingSlots[2] = item_base;
edible_base_attached = true;
break;
case "SmokingA":
m_SmokingSlots[0] = item_base;
edible_base_attached = true;
break;
case "SmokingB":
m_SmokingSlots[1] = item_base;
edible_base_attached = true;
break;
case "SmokingC":
m_SmokingSlots[2] = item_base;
edible_base_attached = true;
break;
case "SmokingD":
m_SmokingSlots[3] = item_base;
edible_base_attached = true;
break;
}
//TODO
//add SetViewIndex when attaching various attachments
RefreshFireplaceVisuals();
}
override void EEItemDetached(EntityAI item, string slot_name)
{
super.EEItemDetached(item, slot_name);
ItemBase item_base = ItemBase.Cast(item);
if (IsKindling(item_base) || IsFuel(item_base))
{
RemoveFromFireConsumables(GetFireConsumableByItem(item_base));
}
//cookware
if (item_base.IsCookware())
{
ClearCookingEquipment(item_base);
item_base.RemoveAudioVisualsOnClient();
}
if (item_base.IsLiquidContainer()) //boiling bottle effects stop
item_base.RemoveAudioVisualsOnClient();
if (g_Game.IsServer())
{
if (item_base.Type() == ATTACHMENT_COOKING_POT)
item_base.SetAnimationPhase(ANIMATION_COOKWARE_HANDLE, 1);
if (item_base.Type() == ATTACHMENT_CAULDRON)
item_base.SetAnimationPhase(ANIMATION_CAULDRON_HANDLE, 1);
}
// direct cooking/smoking slots
switch (slot_name)
{
case "DirectCookingA":
m_DirectCookingSlots[0] = null;
break;
case "DirectCookingB":
m_DirectCookingSlots[1] = null;
break;
case "DirectCookingC":
m_DirectCookingSlots[2] = null;
break;
case "SmokingA":
m_SmokingSlots[0] = null;
break;
case "SmokingB":
m_SmokingSlots[1] = null;
break;
case "SmokingC":
m_SmokingSlots[2] = null;
break;
case "SmokingD":
m_SmokingSlots[3] = null;
break;
}
//no attachments left, no cargo items & no ashes are present
CheckForDestroy();
RefreshFireplaceVisuals();
}
override void SetCookingEquipment(ItemBase equipment)
{
super.SetCookingEquipment(equipment);
TripodBase stand = TripodBase.Cast(FindAttachmentBySlotName("CookingTripod"));
if (stand)
{
if (equipment)
stand.LockToParent();
else
stand.UnlockFromParent();
}
}
override void OnBeforeTryDelete()
{
super.OnBeforeTryDelete();
MiscGameplayFunctions.DropAllItemsInInventoryInBounds(this, m_HalfExtents);
}
override bool IsPrepareToDelete()
{
return GetInventory().AttachmentCount() == 0 && !IsBurning() && !HasAshes();
}
//CONDITIONS
//this into/outo parent.Cargo
override bool CanPutInCargo(EntityAI parent)
{
if (!super.CanPutInCargo(parent))
{
return false;
}
if (HasAshes() || IsBurning() || HasStones() || HasStoneCircle() || IsOven() || !IsCargoEmpty() || HasCookingStand())
{
return false;
}
return true;
}
override bool CanRemoveFromCargo( EntityAI parent )
{
return true;
}
//cargo item into/outo this.Cargo
override bool CanReceiveItemIntoCargo( EntityAI item )
{
if ( GetHierarchyParent() )
return false;
return super.CanReceiveItemIntoCargo( item );
}
override bool CanLoadItemIntoCargo( EntityAI item )
{
if (!super.CanLoadItemIntoCargo( item ))
return false;
if ( GetHierarchyParent() )
return false;
return true;
}
//hands
override bool CanPutIntoHands(EntityAI parent)
{
if (!super.CanPutIntoHands(parent))
{
return false;
}
if (HasAshes() || IsBurning() || HasStones() || HasStoneCircle() || IsOven() || !IsCargoEmpty() || HasCookingStand())
{
return false;
}
return true;
}
override bool CanDisplayAttachmentCategory( string category_name )
{
if ( !super.CanDisplayAttachmentCategory( category_name ) )
return false;
if ( IsOven() )
{
if ( category_name == "CookingEquipment" )
return false;
if ( ( category_name == "DirectCooking" ) || ( category_name == "Smoking" ) )
return true;
}
else
{
if ( category_name == "CookingEquipment" )
return true;
if ( ( category_name == "DirectCooking" ) || ( category_name == "Smoking" ) )
return false;
}
return true;
}
override bool CanAssignAttachmentsToQuickbar()
{
return false;
}
override float HeightStartCheckOverride()
{
return 0.5;
}
//particles
override bool CanShowSmoke()
{
return !IsOven();
}
void DestroyClutterCutter( Object clutter_cutter )
{
g_Game.ObjectDelete( clutter_cutter );
}
override void RefreshPhysics()
{
super.RefreshPhysics();
//Oven
if ( IsOven() )
{
RemoveProxyPhysics( ANIMATION_OVEN );
AddProxyPhysics( ANIMATION_OVEN );
}
else
{
RemoveProxyPhysics( ANIMATION_OVEN );
}
//Tripod
if ( IsItemTypeAttached( ATTACHMENT_TRIPOD ) )
{
RemoveProxyPhysics( ANIMATION_TRIPOD );
AddProxyPhysics( ANIMATION_TRIPOD );
}
else
{
RemoveProxyPhysics( ANIMATION_TRIPOD );
}
}
override void RefreshFireplacePhysics()
{
RefreshPhysics();
}
//on store save/load
override void OnStoreSave( ParamsWriteContext ctx )
{
super.OnStoreSave(ctx);
if ( g_Game.SaveVersion() >= 110 )
{
// save stone circle state
ctx.Write( m_HasStoneCircle );
// save stone oven state
ctx.Write( m_IsOven );
}
}
override bool OnStoreLoad( ParamsReadContext ctx, int version )
{
if ( !super.OnStoreLoad(ctx, version) )
return false;
if ( version >= 110 )
{
// read stone circle state
if ( !ctx.Read( m_HasStoneCircle ) )
{
m_HasStoneCircle = false;
return false;
}
// read stone oven state
if ( !ctx.Read( m_IsOven ) )
{
m_IsOven = false;
return false;
}
}
return true;
}
override void AfterStoreLoad()
{
super.AfterStoreLoad();
if ( IsBurning() )
{
if ( !m_ClutterCutter )
{
m_ClutterCutter = g_Game.CreateObjectEx( OBJECT_CLUTTER_CUTTER, GetPosition(), ECE_PLACE_ON_SURFACE );
m_ClutterCutter.SetOrientation( GetOrientation() );
}
}
}
//================================================================
// IGNITION ACTION
//================================================================
// Item-to-item fire distribution
override bool HasFlammableMaterial()
{
return true;
}
override bool CanBeIgnitedBy(EntityAI igniter = NULL)
{
return HasAnyKindling() && !IsBurning() && !GetHierarchyParent();
}
override bool CanIgniteItem(EntityAI ignite_target = NULL)
{
return IsBurning();
}
override bool IsIgnited()
{
return IsBurning();
}
override void OnIgnitedTarget( EntityAI target_item )
{
}
override void OnIgnitedThis( EntityAI fire_source )
{
//remove grass
Object cc_object = g_Game.CreateObjectEx( OBJECT_CLUTTER_CUTTER , GetPosition(), ECE_PLACE_ON_SURFACE );
cc_object.SetOrientation ( GetOrientation() );
g_Game.GetCallQueue( CALL_CATEGORY_GAMEPLAY ).CallLater( DestroyClutterCutter, 200, false, cc_object );
//start fire
StartFire();
if ( fire_source )
{
Man player = fire_source.GetHierarchyRootPlayer();
if ( player )
{
EFireIgniteType ignate_type = EFireIgniteType.Unknow;
if ( fire_source.ClassName() == "Matchbox" )
{
ignate_type = EFireIgniteType.Matchbox;
}
else if ( fire_source.ClassName() == "Roadflare" )
{
ignate_type = EFireIgniteType.Roadflare;
}
else if ( fire_source.ClassName() == "HandDrillKit" )
{
ignate_type = EFireIgniteType.HandDrill;
}
SyncEvents.SendPlayerIgnatedFireplace( player, ignate_type );
}
}
}
override bool IsThisIgnitionSuccessful( EntityAI item_source = NULL )
{
SetIgniteFailure( false );
Param1<bool> failure;
//check kindling
if ( !HasAnyKindling() )
{
return false;
}
//check roof
if ( !IsOven() )
{
if ( !IsCeilingHighEnoughForSmoke() && IsOnInteriorSurface() )
{
return false;
}
}
//check surface
if ( IsOnWaterSurface() )
{
return false;
}
//check wetness
if ( IsWet() )
{
SetIgniteFailure( true );
failure = new Param1<bool>( GetIgniteFailure() );
g_Game.RPCSingleParam( this, FirePlaceFailure.WET, failure, true );
return false;
}
// check if the fireplace isnt below a roof
// excluding this check whein oven stage
CheckForRoofLimited(0);
if ( !IsOven() && !IsRoofAbove() )
{
// if not, check if there is strong rain or wind
if ( IsRainingAbove() )
{
SetIgniteFailure( true );
failure = new Param1<bool>( GetIgniteFailure() );
g_Game.RPCSingleParam( this, FirePlaceFailure.WET, failure, true );
return false;
}
if ( IsWindy() )
{
SetIgniteFailure( true );
failure = new Param1<bool>( GetIgniteFailure() );
g_Game.RPCSingleParam( this, FirePlaceFailure.WIND, failure, true );
return false;
}
}
return true;
}
//================================================================
// FIREPLACE ENTITY
//================================================================
static Fireplace IgniteEntityAsFireplace( notnull EntityAI entity, notnull EntityAI fire_source )
{
//get player
PlayerBase player = PlayerBase.Cast( fire_source.GetHierarchyRootPlayer() );
//create fireplace
Fireplace fireplace = Fireplace.Cast( g_Game.CreateObjectEx( "Fireplace" , entity.GetPosition(), ECE_PLACE_ON_SURFACE ) );
//attach
if ( !g_Game.IsMultiplayer() ) //clear inventory reservation (single player)
{
InventoryLocation loc = new InventoryLocation;
entity.GetInventory().GetCurrentInventoryLocation( loc );
player.GetInventory().ClearInventoryReservationEx( entity, loc );
}
if ( g_Game.IsServer() && g_Game.IsMultiplayer() )
{
player.ServerTakeEntityToTargetAttachment( fireplace, entity ); // multiplayer server side
}
else
{
player.LocalTakeEntityToTargetAttachment( fireplace, entity ); // single player or multiplayer client side
}
//start fire
fireplace.StartFire();
fireplace.OnIgnitedThis(fire_source);
return fireplace;
}
static bool CanIgniteEntityAsFireplace(notnull EntityAI entity)
{
//check surface
if (FireplaceBase.IsEntityOnWaterSurface(entity))
{
return false;
}
entity.CheckForRoofLimited(); //TODO: limit more severely? Should update at least once during UATimeSpent.FIREPLACE_IGNITE
if (!entity.IsRoofAbove())
return !FireplaceBase.IsRainingAboveEntity(entity);
//check ceiling (enough space for smoke)
if (IsEntityOnInteriorSurface(entity) && MiscGameplayFunctions.IsUnderRoof(entity, FireplaceBase.MIN_CEILING_HEIGHT))
{
return false;
}
return true;
}
//================================================================
// ADVANCED PLACEMENT
//================================================================
override void SetActions()
{
super.SetActions();
AddAction(ActionPlaceFireplaceIntoBarrel);
AddAction(ActionPlaceFireplaceIndoor);
AddAction(ActionPlaceOvenIndoor);
AddAction(ActionTogglePlaceObject);
AddAction(ActionPlaceObject);
AddAction(ActionBuildOven);
AddAction(ActionDismantleOven);
AddAction(ActionBuildStoneCircle);
AddAction(ActionDismantleStoneCircle);
}
}