enum EPlantState
{
PAUSED = 0,
GROWING,
MATURE,
SPOILED,
DRY
}
class PlantBase extends ItemBase
{
private float m_SprayUsage; // How much spray is needed to stop infestation of plant
private float m_InfestationChance;
private int m_GrowthStagesCount;
private int m_CropsCount;
private bool m_HasCrops;
private string m_CropsType;
private float m_PlantMaterialMultiplier;
private int m_PlantStateIndex;
private float m_CurrentPlantMaterialQuantity;
protected EPlantState m_PlantState;
private bool m_IsInfested;
private float m_SprayQuantity;
bool m_MarkForDeletion = false;
int m_DeleteDryPlantTime; // For how long in seconds can an unwatered plant exist before it disappears
int m_SpoiledRemoveTime; // For how long in seconds a spoiled plant will exist
int m_FullMaturityTime; // How much time needs plant to be full grown in seconds
int m_SpoilAfterFullMaturityTime; // How long in seconds it takes for plant to be spoiled after it is full grown
float m_StateChangeTime; // For how long in seconds will plant stay in one state before its going to next state
protected ref Timer m_TimeTicker;
protected float m_TimeTracker;
private GardenBase m_GardenBase = NULL;
private ref Slot m_Slot = NULL;
private PluginHorticulture m_ModuleHorticulture;
private const float SPOIL_AFTER_MATURITY_TIME = 14400.0; //The time it takes for a fully grown plant to spoil, in seconds (4 hours)
private const int TICK_FREQUENCY = 1; // seconds
// debug
static int m_DebugFullMaturityTime;
static int m_DebugSpoilTime;
static int m_DebugSpoilRemoveTime;
static int m_DebugDeleteDryTime;
static float m_DebugTickSpeedMultiplier = 1;
void PlantBase()
{
m_ModuleHorticulture = PluginHorticulture.Cast( GetPlugin( PluginHorticulture ) );
m_SprayUsage = 5;
m_DeleteDryPlantTime = (60 * 10) + Math.RandomInt(0, 60 * 2);
m_SpoiledRemoveTime = (60 * 20) + Math.RandomInt(0, 60 * 5);
string plant_type = this.GetType();
m_GrowthStagesCount = g_Game.ConfigGetInt( "cfgVehicles " + plant_type + " Horticulture GrowthStagesCount" );
m_CropsCount = g_Game.ConfigGetInt( "cfgVehicles " + plant_type + " Horticulture CropsCount" );
g_Game.ConfigGetText( "cfgVehicles " + plant_type + " Horticulture CropsType", m_CropsType );
if (m_GrowthStagesCount == 0)
m_GrowthStagesCount = 1;
m_InfestationChance = 0.2 / m_GrowthStagesCount; //Must be between 0 and 1
m_PlantStateIndex = -1;
m_CurrentPlantMaterialQuantity = 0;
m_IsInfested = false;
m_SprayQuantity = 0.0;
m_HasCrops = true;
SetTakeable( false );
RegisterNetSyncVariableBool("m_HasCrops");
RegisterNetSyncVariableInt("m_PlantState");
RegisterNetSyncVariableInt("m_PlantStateIndex");
if (g_Game.IsServer())
{
m_TimeTicker = new Timer( CALL_CATEGORY_SYSTEM );
m_TimeTicker.Run(TICK_FREQUENCY, this, "Tick", NULL, true);
}
}
void ~PlantBase()
{
if (m_TimeTicker)
m_TimeTicker.Stop();
if (!m_MarkForDeletion && !IsPendingDeletion())
{
DestroyPlant();
}
}
void Init( GardenBase garden_base, float fertility, float harvesting_efficiency, float water )
{
m_GardenBase = garden_base;
if (m_DebugFullMaturityTime != 0)
m_FullMaturityTime = m_DebugFullMaturityTime;
else
m_FullMaturityTime += Math.RandomInt(-60,180);
if (m_DebugSpoilTime != 0)
m_SpoilAfterFullMaturityTime = m_DebugSpoilTime;
else
m_SpoilAfterFullMaturityTime = SPOIL_AFTER_MATURITY_TIME;
if (m_DebugSpoilRemoveTime != 0)
m_SpoiledRemoveTime = m_DebugSpoilRemoveTime;
if (m_DebugDeleteDryTime != 0)
m_DeleteDryPlantTime = m_DebugDeleteDryTime;
m_StateChangeTime = m_FullMaturityTime / (m_GrowthStagesCount - 2);
float count = m_CropsCount * fertility * harvesting_efficiency;
m_CropsCount = (int)Math.Ceil( count );
m_PlantMaterialMultiplier = 0.1 * harvesting_efficiency;
float rain_intensity = g_Game.GetWeather().GetRain().GetActual();
if (m_PlantState < EPlantState.MATURE && !NeedsWater())
{
SetPlantState(EPlantState.GROWING);
GrowthTimerTick(); // first tick happens straight away
}
if (rain_intensity <= 0.0)
{
if (NeedsWater())
SetPlantState(EPlantState.PAUSED);
}
}
void Tick()
{
m_TimeTracker += m_TimeTicker.GetDuration() * m_DebugTickSpeedMultiplier;
switch (m_PlantState)
{
case (EPlantState.GROWING):
if (m_TimeTracker >= m_StateChangeTime)
GrowthTimerTick();
break;
case (EPlantState.MATURE):
if (m_TimeTracker >= m_SpoilAfterFullMaturityTime)
SetSpoiled();
break;
case (EPlantState.SPOILED):
if (m_TimeTracker >= m_SpoiledRemoveTime)
RemoveSlot();
break;
case (EPlantState.DRY):
if (m_TimeTracker >= m_DeleteDryPlantTime)
RemoveSlot();
break;
}
}
override bool OnStoreLoad( ParamsReadContext ctx, int version )
{
if ( !super.OnStoreLoad( ctx, version ) )
return false;
GardenBase garden = GardenBase.Cast( GetHierarchyParent() );
int slot_index = -1;
if (!ctx.Read(slot_index))
return false;
Slot slot = garden.GetSlotByIndex(slot_index);
SetSlot(slot);
if ( !OnStoreLoadCustom( ctx, version ) )
return false;
return true;
}
override void OnStoreSave( ParamsWriteContext ctx )
{
super.OnStoreSave( ctx );
Slot slot = GetSlot();
if (slot)
{
int slot_index = slot.GetSlotIndex();
slot.SetPlant(this); // hack
ctx.Write( slot_index );
OnStoreSaveCustom( ctx );
}
else
{
ErrorEx("[Warning] A plant existed without a garden. Therefore it was deleted from the world to prevent issues! Position: " + GetPosition(), ErrorExSeverity.INFO);
g_Game.ObjectDelete(this); // Plants that exist without a garden must be deleted. Otherwise they might cause problems.
}
}
string GetCropsType()
{
return m_CropsType;
}
bool OnStoreLoadCustom(ParamsReadContext ctx, int version)
{
if (!ctx.Read(m_SprayUsage))
return false;
if (!ctx.Read(m_DeleteDryPlantTime))
return false;
if (!ctx.Read(m_SpoiledRemoveTime))
return false;
if (!ctx.Read(m_FullMaturityTime))
return false;
if (!ctx.Read(m_SpoilAfterFullMaturityTime))
return false;
if (!ctx.Read(m_StateChangeTime))
return false;
if (!ctx.Read(m_InfestationChance))
return false;
if (!ctx.Read(m_GrowthStagesCount))
return false;
if (!ctx.Read(m_CropsCount))
return false;
if (!ctx.Read(m_CropsType))
return false;
if (!ctx.Read(m_PlantMaterialMultiplier))
return false;
if (!ctx.Read(m_PlantState))
return false;
if (!ctx.Read(m_PlantStateIndex))
return false;
if (!ctx.Read(m_CurrentPlantMaterialQuantity))
return false;
if (!ctx.Read(m_IsInfested))
return false;
if (!ctx.Read(m_SprayQuantity))
return false;
bool loadBool; // deprec
if (!ctx.Read(loadBool))
return false;
float loadFloat = 0.0;
if (!ctx.Read(loadFloat)) // deprec
return false;
loadFloat = 0.0;
if (ctx.Read(loadFloat))
{
if (loadFloat > 0.0)
m_TimeTracker = loadFloat; // spoil
}
else
{
return false;
}
loadFloat = 0.0;
if (ctx.Read(loadFloat))
{
if (loadFloat > 0.0)
m_TimeTracker = loadFloat; // spoil delete
}
else
{
return false;
}
loadFloat = 0.0;
if (ctx.Read(loadFloat))
{
if ( loadFloat > 0.0 )
m_TimeTracker = loadFloat; // dry delete
}
else
{
return false;
}
if (m_StateChangeTime != (m_FullMaturityTime / (m_GrowthStagesCount - 2)))
{
m_StateChangeTime = m_FullMaturityTime / (m_GrowthStagesCount - 2);
ErrorEx("[Warning] A plant loaded from storage had a corrupted state change time. Time was now adjusted! Position: " + GetPosition(), ErrorExSeverity.INFO);
}
if (version >= 142)
{
if (!ctx.Read(m_HasCrops))
return false;
}
UpdatePlant();
return true;
}
void OnStoreSaveCustom( ParamsWriteContext ctx )
{
ctx.Write(m_SprayUsage);
ctx.Write(m_DeleteDryPlantTime);
ctx.Write(m_SpoiledRemoveTime);
ctx.Write(m_FullMaturityTime);
ctx.Write(m_SpoilAfterFullMaturityTime);
ctx.Write(m_StateChangeTime);
ctx.Write(m_InfestationChance);
ctx.Write(m_GrowthStagesCount);
ctx.Write(m_CropsCount);
ctx.Write(m_CropsType);
ctx.Write(m_PlantMaterialMultiplier);
ctx.Write(m_PlantState);
ctx.Write(m_PlantStateIndex);
ctx.Write(m_CurrentPlantMaterialQuantity);
ctx.Write(m_IsInfested);
ctx.Write(m_SprayQuantity);
bool saveBool = false; // deprec
ctx.Write( saveBool );
float saveFloat = 0.0; // deprec
ctx.Write( saveFloat );
saveFloat = 0.0;
if (m_PlantState == EPlantState.MATURE)
{
saveFloat = m_TimeTracker;
}
ctx.Write( saveFloat );
saveFloat = 0.0;
if (m_PlantState == EPlantState.SPOILED)
{
saveFloat = m_TimeTracker;
}
ctx.Write( saveFloat );
saveFloat = 0.0;
if (m_PlantState == EPlantState.DRY)
{
saveFloat = m_TimeTracker;
}
ctx.Write( saveFloat );
ctx.Write(m_HasCrops);
}
void PrintValues()
{
Print("PRINT ALL VALUES OF PLANT...");
Print(this);
Print(m_GrowthStagesCount);
Print(m_HasCrops);
Print(typename.EnumToString(EPlantState, m_PlantState));
Print(m_PlantStateIndex);
Print(m_CurrentPlantMaterialQuantity);
Print(m_IsInfested);
Print(m_SprayQuantity);
Print(m_Slot);
Print(m_GardenBase);
Print(m_StateChangeTime);
Print(m_FullMaturityTime);
Print(m_SpoilAfterFullMaturityTime);
Print(m_TimeTracker);
Print("----------------------------------------------------------");
}
override bool CanPutInCargo( EntityAI parent )
{
return super.CanPutInCargo(parent);
}
override bool CanPutIntoHands( EntityAI parent )
{
return super.CanPutIntoHands(parent);
}
override bool CanRemoveFromHands( EntityAI parent )
{
return false;
}
void ChangeInfestation( bool is_infested )
{
m_IsInfested = is_infested;
string plant_type = GetType();
PlantMaterialHealth material = m_ModuleHorticulture.GetPlantMaterial( plant_type );
if ( m_IsInfested )
{
if ( material.m_InfestedTex != "" )
{
SetObjectTexture( 0, material.m_InfestedTex );
}
if ( material.m_InfestedMat != "" )
{
SetObjectMaterial( 0, material.m_InfestedMat );
}
}
else
{
if ( material.m_HealthyTex != "" )
{
SetObjectTexture( 0, material.m_HealthyTex );
}
if ( material.m_HealthyMat != "" )
{
SetObjectMaterial( 0, material.m_HealthyMat );
}
}
}
void UpdatePlant()
{
if ( m_PlantStateIndex > 0 )
{
string plant_state_index = m_PlantStateIndex.ToStringLen(2);
string prev_plant_state_index = ( m_PlantStateIndex - 1 ).ToStringLen( 2 );
// HIDING PREVIOUS PLANT STATE AND SHOWING THE CURRENT ONE
ShowSelection( "plantStage_" + plant_state_index ); // SHOW!
HideSelection( "plantStage_" + prev_plant_state_index ); // HIDE!
// HIDING PREVIOUS CROPS STATE AND SHOWING THE CURRENT ONE
if ( HasCrops() )
{
ShowSelection( "plantStage_" + plant_state_index + "_crops" ); // SHOW!
HideSelection( "plantStage_" + prev_plant_state_index + "_crops" ); // HIDE!
}
else
{
HideSelection( "plantStage_" + plant_state_index + "_crops" ); // HIDE!
HideSelection( "plantStage_" + prev_plant_state_index + "_crops" ); // HIDE!
}
// HIDING PREVIOUS SHADOW STATE AND SHOWING THE CURRENT ONE
ShowSelection( "plantStage_" + plant_state_index + "_shadow" ); // SHOW!
HideSelection( "plantStage_" + prev_plant_state_index + "_shadow" ); // HIDE!
}
float float_plant_state_index = (float)m_PlantStateIndex;
m_CurrentPlantMaterialQuantity = m_PlantMaterialMultiplier * float_plant_state_index;
}
void GrowthTimerTick()
{
m_TimeTracker = 0;
if ( m_PlantStateIndex < m_GrowthStagesCount - 2 )
{
m_PlantStateIndex++;
UpdatePlant();
SetSynchDirty();
float infestation_rnd = Math.RandomFloat01();
if ( m_InfestationChance > infestation_rnd )
ChangeInfestation(true);
if ( m_PlantStateIndex == m_GrowthStagesCount - 2 )
{
if (m_IsInfested)
SetDry();
else
SetPlantState(EPlantState.MATURE);
}
}
}
void SetSpoiled()
{
if (m_PlantState != EPlantState.SPOILED)
{
m_PlantStateIndex++;
UpdatePlant();
SetPlantState(EPlantState.SPOILED);
}
}
void SetDry()
{
if (m_PlantState != EPlantState.DRY)
{
m_PlantStateIndex++;
UpdatePlant();
SetPlantState(EPlantState.DRY);
}
}
//NEW METHOD FOR PLANT SPRAYING
void SprayPlant( float consumed_quantity )
{
//Rework this to have something smooth
m_SprayQuantity += consumed_quantity;
if (m_SprayQuantity >= m_SprayUsage)
{
m_IsInfested = false;
m_InfestationChance = 0;
ChangeInfestation( false );
UpdatePlant();
}
}
void RemovePlantEx( vector pos )
{
if ( g_Game && g_Game.IsServer() )
{
UnlockFromParent();
if ( m_CurrentPlantMaterialQuantity > 0.0 )
{
ItemBase item = ItemBase.Cast( g_Game.CreateObjectEx( "PlantMaterial", pos, ECE_PLACE_ON_SURFACE ) );
item.SetQuantity( m_CurrentPlantMaterialQuantity * 1000.0 );
}
RemoveSlot();
}
}
void DestroyPlant()
{
if ( g_Game && g_Game.IsServer() )
{
UnlockFromParent();
RemoveSlot();
}
}
void Harvest( PlayerBase player )
{
if (IsHarvestable())
{
for ( int i = 0; i < m_CropsCount; i++ )
{
vector pos = player.GetPosition();
ItemBase item = ItemBase.Cast( g_Game.CreateObjectEx( m_CropsType, pos, ECE_PLACE_ON_SURFACE ) );
item.SetQuantity( item.GetQuantityMax() );
}
}
m_HasCrops = false;
SetSynchDirty();
UpdatePlant();
m_GardenBase.SyncSlots();
}
void SetPlantState(int state)
{
m_PlantState = state;
m_TimeTracker = 0;
SetSynchDirty();
}
EPlantState GetPlantState()
{
return m_PlantState;
}
int GetPlantStateIndex()
{
return m_PlantStateIndex;
}
float GetWater()
{
if ( GetSlot() )
return GetSlot().GetWater();
return 0;
}
float GetWaterMax()
{
if ( GetSlot() )
return GetSlot().GetWaterUsage();
return 0;
}
bool NeedsWater()
{
Slot slotPlant = m_Slot;
if ( m_PlantState == EPlantState.PAUSED && slotPlant && slotPlant.GetWater() < slotPlant.GetWaterUsage() )
return true;
return false;
}
float GetSprayQuantity()
{
return m_SprayQuantity;
}
float GetSprayUsage()
{
return m_SprayUsage;
}
void RemoveSlot()
{
GardenBase garden = GardenBase.Cast( GetHierarchyParent() );
if ( garden )
garden.RemoveSlotPlant( this );
}
void SetSlot(Slot slot)
{
if ( slot )
{
m_Slot = slot;
}
}
Slot GetSlot()
{
return m_Slot;
}
void SetGarden(GardenBase gardenBase)
{
m_GardenBase = gardenBase;
}
GardenBase GetGarden()
{
return m_GardenBase;
}
bool IsSprayable()
{
if (m_PlantState == EPlantState.GROWING && m_SprayQuantity < m_SprayUsage)
return true;
return false;
}
bool IsHarvestable()
{
if (m_PlantState == EPlantState.MATURE && m_HasCrops)
return true;
return false;
}
bool HasCrops()
{
return m_HasCrops;
}
override void SetActions()
{
super.SetActions();
AddAction(ActionHarvestCrops);
AddAction(ActionRemovePlant);
}
void DebugSetTimes(int maturity, int spoil, int spoilRemove, int dryDelete)
{
if (maturity != 0)
{
m_FullMaturityTime = maturity;
m_StateChangeTime = m_FullMaturityTime / (m_GrowthStagesCount - 2);
}
if (spoil != 0)
m_SpoilAfterFullMaturityTime = spoil;
if (spoilRemove != 0)
m_SpoiledRemoveTime = spoilRemove;
if (dryDelete != 0)
m_DeleteDryPlantTime = dryDelete;
}
static void DebugSetGlobalTimes(int maturity, int spoil, int spoilRemove, int dryDelete)
{
m_DebugFullMaturityTime = maturity;
m_DebugSpoilTime = spoil;
m_DebugSpoilRemoveTime = spoilRemove;
m_DebugDeleteDryTime = dryDelete;
}
static void DebugSetTickSpeedMultiplier(float multiplier)
{
m_DebugTickSpeedMultiplier = multiplier;
}
// DEPRECATED
static const int STATE_DRY = 0;
static const int STATE_GROWING = 1;
static const int STATE_MATURE = 2;
static const int STATE_SPOILED = 3;
ref Timer m_SpoiledRemoveTimer;
ref Timer m_DeleteDryPlantTimer;
ref Timer m_SpoilAfterFullMaturityTimer;
ref Timer m_GrowthTimer;
ref Timer m_InfestationTimer;
void DeleteDryPlantTick();
void SpoiledRemoveTimerTick();
void InfestationTimerTick();
void CheckWater();
bool IsMature();
bool IsSpoiled();
bool IsDry();
bool IsGrowing();
bool NeedsSpraying();
void RemovePlant();
string StopInfestation( float consumed_quantity )
{
return "";
}
}