enum FoodStageType
{
NONE = 0, //food stage not defined
RAW = 1, //default
BAKED = 2,
BOILED = 3,
DRIED = 4,
BURNED = 5,
ROTTEN = 6,
COUNT //for net sync purposes
}
// Used to make getting data more readable
enum eCookingPropertyIndices
{
MIN_TEMP = 0,
COOK_TIME = 1,
MAX_TEMP = 2
}
class FoodStage
{
protected Edible_Base m_FoodItem;
protected int m_SelectionIndex; //visual properties
protected int m_TextureIndex;
protected int m_MaterialIndex;
//synced variables
protected FoodStageType m_FoodStageType;
protected float m_CookingTime;
//mirror variables (local, as needed)
protected FoodStageType m_FoodStageTypeClientLast;
// Lookup and search values
// STRINGs are mostly used for CONFIG searches
// INTs are mostly used for MAP searches
static const string VISUAL_PROPERTIES = "visual_properties";
static const string NUTRITION_PROPERTIES = "nutrition_properties";
static const string COOKING_PROPERTIES = "cooking_properties";
static const int VISUAL_PROPERTIES_HASH = VISUAL_PROPERTIES.Hash();
static const int NUTRITION_PROPERTIES_HASH = NUTRITION_PROPERTIES.Hash();
static const int COOKING_PROPERTIES_HASH = COOKING_PROPERTIES.Hash();
static const int TRANSITION_FOODSTAGE_IDX = 0;
static const int TRANSITION_COOKINGMETHOD_IDX = 1;
// The following will be filled in constructor
private static int m_StageRawHash = 0;
private static int m_StageBakedHash = 0;
private static int m_StageBoiledHash = 0;
private static int m_StageDriedHash = 0;
private static int m_StageBurnedHash = 0;
private static int m_StageRottenHash = 0;
// Cache food stage data for each Edible_Base
// Used to get information for specific Edible_Base, information from 'class FoodStages'
static ref map<int, ref map<int, ref map<int, ref array<float>>>> m_EdibleBasePropertiesMap; //<foodTypeHash,<FoodStageNameHash,<stagePropertiesIdx,<visual_properties,nutrition_properties,cooking_properties>>>>
// Used to store food stage transitions for every Edible_Base, information from 'class FoodStageTransitions'
static ref map<int, ref map<int, ref map<int, ref array<int>>>> m_EdibleBaseTransitionsMap; //<<foodTypeHash,<FoodStageNameHash,<transitionClassHash('ToBaked' etc.),<transition_to,cooking_method>>>>
// Used to store the Hashed key of all possible food transitions ( including modded ones )
static ref array<int> m_FoodStageTransitionKeys;
//constructor
void FoodStage( Edible_Base food_item )
{
m_FoodStageType = FoodStageType.NONE;
m_FoodStageTypeClientLast = FoodStageType.NONE;
m_FoodItem = food_item;
//reset cooking time
m_CookingTime = 0;
// We fill all FoodStageHash values
if ( m_StageRawHash == 0 )
m_StageRawHash = "Raw".Hash();
if ( m_StageBakedHash == 0 )
m_StageBakedHash = "Baked".Hash();
if ( m_StageBoiledHash == 0 )
m_StageBoiledHash = "Boiled".Hash();
if ( m_StageDriedHash == 0 )
m_StageDriedHash = "Dried".Hash();
if ( m_StageBurnedHash == 0 )
m_StageBurnedHash = "Burned".Hash();
if ( m_StageRottenHash == 0 )
m_StageRottenHash = "Rotten".Hash();
//get config data for all food stages
SetupFoodStageMapping();
// Get all config data relative to food stage transitions
SetupFoodStageTransitionMapping();
//set default food type
ChangeFoodStage(FoodStageType.RAW);
}
void SetupFoodStageMapping()
{
// We ensure we have the map is setup before trying to fill it
if ( !m_EdibleBasePropertiesMap )
m_EdibleBasePropertiesMap = new map<int, ref map<int, ref map< int, ref array<float>>>>;
string foodType = m_FoodItem.GetType();
int hashedFood = foodType.Hash();
// We start to fill the map, we don't want duplicates of the same food type
if ( !m_EdibleBasePropertiesMap.Contains( hashedFood ) )
{
map<int, ref map<int, ref array<float>>>foodStagesMap = new map<int, ref map<int, ref array<float>>>;
for ( int i = 1; i < FoodStageType.COUNT; ++i )
{
map<int, ref array<float>> stagePropertiesMap = new map<int, ref array<float>>;
// Insert visual properties
array<float> visual_properties = new array<float>;
string path = string.Format("CfgVehicles %1 Food FoodStages %2 visual_properties", foodType, GetFoodStageName(i));
g_Game.ConfigGetFloatArray( path, visual_properties);
stagePropertiesMap.Insert( VISUAL_PROPERTIES_HASH , visual_properties );
// Insert nutrition properties
array<float> nutrition_properties = new array<float>;
path = string.Format("CfgVehicles %1 Food FoodStages %2 nutrition_properties", foodType, GetFoodStageName(i));
g_Game.ConfigGetFloatArray( path, nutrition_properties);
stagePropertiesMap.Insert( NUTRITION_PROPERTIES_HASH, nutrition_properties );
// Insert cooking properties
array<float> cooking_properties = new array<float>;
path = string.Format("CfgVehicles %1 Food FoodStages %2 cooking_properties", foodType, GetFoodStageName(i));
g_Game.ConfigGetFloatArray( path, cooking_properties);
stagePropertiesMap.Insert( COOKING_PROPERTIES_HASH , cooking_properties );
// Insert all properties for relevant food stage
foodStagesMap.Insert( GetFoodStageNameHash( i ), stagePropertiesMap );
}
m_EdibleBasePropertiesMap.Insert( hashedFood, foodStagesMap );
}
}
void SetupFoodStageTransitionMapping()
{
// We ensure we have only one map and that it does exist
if ( !m_EdibleBaseTransitionsMap )
m_EdibleBaseTransitionsMap = new map<int, ref map<int, ref map< int, ref array<int>>>>;
// We ensure we have our key array setup
if ( !m_FoodStageTransitionKeys )
m_FoodStageTransitionKeys = new array<int>;
string foodType = m_FoodItem.GetType();
int hashedFood = foodType.Hash();
// We start to fill the map, we don't want duplicates of the same food type
if ( !m_EdibleBaseTransitionsMap.Contains( hashedFood ) )
{
map<int, ref map<int, ref array<int>>> foodStagesMap = new map<int, ref map<int, ref array<int>>>;
for ( int i = 1; i < FoodStageType.COUNT; ++i )
{
map<int, ref array<int>> stageTransitionsMap = new map<int, ref array<int>>;
string config_path = string.Format("CfgVehicles %1 Food FoodStageTransitions %2", foodType, GetFoodStageName( i ) );
for ( int j = 0; j < g_Game.ConfigGetChildrenCount( config_path ); ++j )
{
array<int> stageTransition = new array<int>;
string classCheck; // Used to get any existing transition class
g_Game.ConfigGetChildName( config_path, j, classCheck );
string transition_path = string.Format("%1 %2", config_path, classCheck );
if ( g_Game.ConfigIsExisting( transition_path ) ) //TODO: we already know that from 'ConfigGetChildName', redundant?
{
int transitionClassHash = classCheck.Hash();
stageTransition.Insert( g_Game.ConfigGetInt( string.Format("%1 transition_to", transition_path) ) );
stageTransition.Insert( g_Game.ConfigGetInt( string.Format("%1 cooking_method", transition_path) ) );
stageTransitionsMap.Insert( transitionClassHash, stageTransition);
// We only want one entry per key
if ( m_FoodStageTransitionKeys.Find( transitionClassHash ) == -1 )
{
m_FoodStageTransitionKeys.Insert( transitionClassHash );
}
}
}
foodStagesMap.Insert( GetFoodStageNameHash(i), stageTransitionsMap );
}
m_EdibleBaseTransitionsMap.Insert( hashedFood, foodStagesMap );
}
}
//Food Stage Type
FoodStageType GetFoodStageType()
{
return m_FoodStageType;
}
void SetFoodStageType( FoodStageType food_stage_type )
{
FoodStageType stageOld = m_FoodStageType;
m_FoodStageType = food_stage_type;
OnFoodStageChange(stageOld,food_stage_type);
GetFoodItem().Synchronize();
}
//Selection index
int GetSelectionIndex()
{
return m_SelectionIndex;
}
void SetSelectionIndex( int index )
{
m_SelectionIndex = index;
}
//Texture index
int GetTextureIndex()
{
return m_TextureIndex;
}
void SetTextureIndex( int index )
{
m_TextureIndex = index;
}
//Material index
int GetMaterialIndex()
{
return m_MaterialIndex;
}
void SetMaterialIndex( int index )
{
m_MaterialIndex = index;
}
//Food properties
protected static float GetNutritionPropertyFromIndex( int index, FoodStageType stage_type, FoodStage stage, string classname )
{
if ( stage )
{
stage_type = stage.m_FoodStageType;
classname = stage.GetFoodItem().GetType();
}
string food_stage_name = GetFoodStageName( stage_type );
int hashedStageName = GetFoodStageNameHash( stage_type );
array<float> nutrition_properties;
map<int, ref map<int, ref array<float>>>foodStagesMap;
map<int, ref array<float>> stagePropertiesMap;
if( !m_EdibleBasePropertiesMap.Find(classname.Hash(), foodStagesMap))
return 0;
if( !foodStagesMap.Find(hashedStageName, stagePropertiesMap))
return 0;
if( !stagePropertiesMap.Find(NUTRITION_PROPERTIES_HASH, nutrition_properties))
return 0;
if ( nutrition_properties.Count() > 0 )
{
if ( index > (nutrition_properties.Count() - 1) )
{
return 0;
}
else
{
return nutrition_properties.Get( index );
}
}
//calculate nutrition properties from base stage and nutrition modifiers
else
{
// Will not attempt to optimize this as it is for a setup we do not support internally
//get modifiers class for nutrition values
string config_path = string.Format("CfgVehicles %1 Food nutrition_modifiers_class", classname);
if ( g_Game.ConfigIsExisting( config_path ) )
{
string nutr_mod_class;
g_Game.ConfigGetText( config_path, nutr_mod_class );
config_path = string.Format("CfgVehicles NutritionModifiers %1 base_stage", nutr_mod_class);
string nutr_base_stage;
g_Game.ConfigGetText( config_path, nutr_base_stage );
//get nutrition values for food stage and modifiers
config_path = string.Format("CfgVehicles %1 Food FoodStages %2 nutrition_properties", classname, nutr_base_stage);
array<float> base_nutr_properties = new array<float>;
g_Game.ConfigGetFloatArray( config_path, base_nutr_properties );
config_path = string.Format("CfgVehicles NutritionModifiers %1 %2 nutrition_properties", nutr_mod_class, food_stage_name);
array<float> nutr_mod_properties = new array<float>;
g_Game.ConfigGetFloatArray( config_path, nutr_mod_properties );
//base nutrition * food stage nutrition modifier
if ( base_nutr_properties.Count() > 0 && nutr_mod_properties.Count() > 0 )
{
return ( base_nutr_properties.Get( index ) * nutr_mod_properties.Get( index ) );
}
}
}
return 0;
}
static float GetFullnessIndex(FoodStage stage, int stage_type = -1, string classname = "")
{
return GetNutritionPropertyFromIndex( 0 , stage_type, stage, classname );
}
static float GetEnergy(FoodStage stage, int stage_type = -1, string classname = "")
{
return GetNutritionPropertyFromIndex( 1 , stage_type, stage, classname );
}
static float GetWater(FoodStage stage, int stage_type = -1, string classname = "")
{
return GetNutritionPropertyFromIndex( 2 , stage_type, stage, classname );
}
static float GetNutritionalIndex(FoodStage stage, int stage_type = -1, string classname = "")
{
return GetNutritionPropertyFromIndex( 3 , stage_type , stage, classname);
}
static float GetToxicity(FoodStage stage, int stage_type = -1, string classname = "")
{
return GetNutritionPropertyFromIndex( 4 , stage_type, stage, classname );
}
static int GetAgents(FoodStage stage, int stage_type = -1, string classname = "")
{
return GetNutritionPropertyFromIndex( 5 , stage_type, stage, classname );
}
static float GetDigestibility(FoodStage stage, int stage_type = -1, string classname = "")
{
return GetNutritionPropertyFromIndex( 6 , stage_type, stage, classname );
}
static float GetAgentsPerDigest(FoodStage stage, int stageType = -1, string className = "")
{
return GetNutritionPropertyFromIndex(7, stageType, stage, className);
}
//Food item
protected Edible_Base GetFoodItem()
{
return m_FoodItem;
}
//Cooking time
float GetCookingTime()
{
return m_CookingTime;
}
void SetCookingTime( float time )
{
m_CookingTime = time;
}
static float GetCookingPropertyFromIndex( int index, FoodStageType stage_type, FoodStage stage, string classname )
{
if ( stage )
{
stage_type = stage.m_FoodStageType;
classname = stage.GetFoodItem().GetType();
}
string food_stage_name = GetFoodStageName( stage_type );
int hashedStageName = GetFoodStageNameHash( stage_type );
array<float> cooking_properties = new array<float>;
map<int, ref map<int, ref array<float>>>foodStagesMap = new map<int, ref map<int, ref array<float>>>;
map<int, ref array<float>> stagePropertiesMap = new map<int, ref array<float>>;
m_EdibleBasePropertiesMap.Find(classname.Hash(), foodStagesMap);
foodStagesMap.Find(hashedStageName, stagePropertiesMap);
stagePropertiesMap.Find(COOKING_PROPERTIES_HASH, cooking_properties);
if ( cooking_properties.Count() > 0 )
{
if ( index > (cooking_properties.Count() - 1) )
{
return -1;
}
else
{
return cooking_properties.Get( index );
}
}
return 0;
}
static array<float> GetAllCookingPropertiesForStage( FoodStageType stage_type, FoodStage stage, string classname )
{
if ( stage )
{
stage_type = stage.m_FoodStageType;
classname = stage.GetFoodItem().GetType();
}
string food_stage_name = GetFoodStageName( stage_type );
int hashedStageName = GetFoodStageNameHash( stage_type );
array<float> cooking_properties = new array<float>;
map<int, ref map<int, ref array<float>>>foodStagesMap = new map<int, ref map<int, ref array<float>>>;
map<int, ref array<float>> stagePropertiesMap = new map<int, ref array<float>>;
m_EdibleBasePropertiesMap.Find(classname.Hash(), foodStagesMap);
foodStagesMap.Find(hashedStageName, stagePropertiesMap);
stagePropertiesMap.Find(COOKING_PROPERTIES_HASH, cooking_properties);
if ( cooking_properties.Count() > 0 )
{
return cooking_properties;
}
return null;
}
//********************************************/
// FOOD STAGE CHANGE
//********************************************/
//Checks if food stage can be changed to another stage
bool CanChangeToNewStage( CookingMethodType cooking_method )
{
if ( GetNextFoodStageType( cooking_method ) == FoodStageType.NONE )
{
return false;
}
return true;
}
//returns possible food stage type according to given cooking method
FoodStageType GetNextFoodStageType(CookingMethodType cooking_method)
{
array<int> food_transition = new array<int>;
map<int, ref map<int, ref array<int>>> foodStagesMap = new map<int, ref map<int, ref array<int>>>;
map<int, ref array<int>> foodTransitionsMap = new map<int, ref array<int>>;
m_EdibleBaseTransitionsMap.Find(GetFoodItem().GetType().Hash(), foodStagesMap);
foodStagesMap.Find( GetFoodStageNameHash( GetFoodStageType() ), foodTransitionsMap );
// We go through the key array, checking every possible transition
int count = m_FoodStageTransitionKeys.Count();
for (int i = 0; i < count; ++i) //TODO: foodTransitionsMap should contain same info (keys) as m_FoodStageTransitionKeys. Just iterate through those instead?
{
// We test if a given transition is setup on this item
foodTransitionsMap.Find(m_FoodStageTransitionKeys[i], food_transition);
if (food_transition)
{
// We now check if the given transition class is relevant
if (food_transition[TRANSITION_COOKINGMETHOD_IDX] == cooking_method)
{
return food_transition[TRANSITION_FOODSTAGE_IDX];
}
}
}
return FoodStageType.BURNED; //If the item cannot transition out of current state, burn it //TODO: return 'NONE' and substitute BURNED from that, where appropriate
}
bool CanTransitionToFoodStageType(FoodStageType type)
{
array<int> food_transition = new array<int>;
map<int, ref map<int, ref array<int>>> foodStagesMap = new map<int, ref map<int, ref array<int>>>;
map<int, ref array<int>> foodTransitionsMap = new map<int, ref array<int>>;
m_EdibleBaseTransitionsMap.Find(GetFoodItem().GetType().Hash(), foodStagesMap);
foodStagesMap.Find(GetFoodStageNameHash(GetFoodStageType()), foodTransitionsMap);
// We go through the key array, checking every possible transition
int count = m_FoodStageTransitionKeys.Count();
for (int i = 0; i < count; ++i) //TODO: foodTransitionsMap should contain same info (keys) as m_FoodStageTransitionKeys. Just iterate through those instead?
{
// We test if a given transition is setup on this item
foodTransitionsMap.Find(m_FoodStageTransitionKeys[i], food_transition);
if (food_transition )
{
// We now check if the given transition class is relevant
if (food_transition[0] == type)
{
return true;
}
}
}
return false;
}
void ChangeFoodStage(FoodStageType new_stage_type)
{
SetFoodStageType(new_stage_type);
}
void OnFoodStageChange(FoodStageType stageOld, FoodStageType stageNew)
{
//init value setting, not a change
if (stageOld == FoodStageType.NONE)
return ;
//not a change
if (stageOld == stageNew)
return ;
m_FoodItem.OnFoodStageChange(stageOld,stageNew);
}
void UpdateVisualsEx(bool forced = false)
{
if (forced || m_FoodStageTypeClientLast != m_FoodStageType)
{
Edible_Base food_item = GetFoodItem();
map<int, ref map<int, ref array<float>>>foodStagesMap = new map<int, ref map<int, ref array<float>>>;
map<int, ref array<float>> stagePropertiesMap = new map<int, ref array<float>>;
array<float> visual_properties = new array<float>;
m_EdibleBasePropertiesMap.Find(food_item.GetType().Hash(), foodStagesMap);
foodStagesMap.Find(GetFoodStageNameHash( GetFoodStageType() ), stagePropertiesMap);
stagePropertiesMap.Find(VISUAL_PROPERTIES_HASH, visual_properties);
if (visual_properties.Count() > 0)
{
//selection index
int index = visual_properties.Get( 0 );
if ( index >= 0 ) //leaves last valid idx otherwise
{
SetSelectionIndex( index );
}
//texture index
index = visual_properties.Get( 1 );
if ( index >= 0 ) //leaves last valid idx otherwise
{
SetTextureIndex( index );
}
//material index
index = visual_properties.Get( 2 );
if ( index >= 0 ) //leaves last valid idx otherwise
{
SetMaterialIndex( index );
}
}
array<string> config_selections = food_item.GetHiddenSelections();
array<string> config_textures = food_item.GetHiddenSelectionsTextures();
array<string> config_materials = food_item.GetHiddenSelectionsMaterials();
//selection index
int selection_index;
if ( GetSelectionIndex() >= 0 && config_selections.Count() > GetSelectionIndex() ) //USES the last valid idx
{
selection_index = GetSelectionIndex();
}
//texture index
int texture_index;
if ( GetTextureIndex() >= 0 && config_textures.Count() > GetTextureIndex() ) //USES the last valid idx
{
texture_index = GetTextureIndex();
}
//material index
int material_index;
if ( GetMaterialIndex() >= 0 && config_materials.Count() > GetMaterialIndex() ) //USES the last valid idx
{
material_index = GetMaterialIndex();
}
//hide all selection except the configured one
for ( int i = 0; i < config_selections.Count(); i++ )
{
if ( config_selections.Get( i ) != config_selections.Get( selection_index ) )
{
food_item.SetAnimationPhase( config_selections.Get( i ), 1 );
}
}
//show selection
food_item.SetAnimationPhase( config_selections.Get( selection_index ), 0 );
//set texture
food_item.SetObjectTexture( selection_index, config_textures.Get( texture_index ) );
//set materials
food_item.SetObjectMaterial( selection_index, config_materials.Get( material_index ) );
m_FoodStageTypeClientLast = m_FoodStageType;
}
}
//Food States
//check food stages
bool IsFoodInStage( FoodStageType food_stage_type )
{
if ( GetFoodStageType() == food_stage_type )
{
return true;
}
return false;
}
bool IsFoodRaw()
{
return IsFoodInStage( FoodStageType.RAW );
}
bool IsFoodBaked()
{
return IsFoodInStage( FoodStageType.BAKED );
}
bool IsFoodBoiled()
{
return IsFoodInStage( FoodStageType.BOILED );
}
bool IsFoodDried()
{
return IsFoodInStage( FoodStageType.DRIED );
}
bool IsFoodBurned()
{
return IsFoodInStage( FoodStageType.BURNED );
}
bool IsFoodRotten()
{
return IsFoodInStage( FoodStageType.ROTTEN );
}
//get name of food stage type
static string GetFoodStageName( FoodStageType food_stage_type )
{
switch ( food_stage_type )
{
case FoodStageType.RAW: return "Raw";
case FoodStageType.BAKED: return "Baked";
case FoodStageType.BOILED: return "Boiled";
case FoodStageType.DRIED: return "Dried";
case FoodStageType.BURNED: return "Burned";
case FoodStageType.ROTTEN: return "Rotten";
}
return "Raw";
}
// Get hashed name of food stage type for quicker access
static int GetFoodStageNameHash( FoodStageType food_stage_type )
{
switch ( food_stage_type )
{
case FoodStageType.RAW: return m_StageRawHash;
case FoodStageType.BAKED: return m_StageBakedHash;
case FoodStageType.BOILED: return m_StageBoiledHash;
case FoodStageType.DRIED: return m_StageDriedHash;
case FoodStageType.BURNED: return m_StageBurnedHash;
case FoodStageType.ROTTEN: return m_StageRottenHash;
}
return m_StageRawHash;
}
//================================================================
// SERIALIZATION
//================================================================
void OnStoreSave( ParamsWriteContext ctx )
{
//Food stage type
ctx.Write( m_FoodStageType );
//Selection index
ctx.Write( m_SelectionIndex );
//Texture index
ctx.Write( m_TextureIndex );
//Material index
ctx.Write( m_MaterialIndex );
}
bool OnStoreLoad( ParamsReadContext ctx, int version )
{
//Food stage type
if ( !ctx.Read( m_FoodStageType ) )
{
m_FoodStageType = FoodStageType.RAW; //set default
return false;
}
//Selection index
if ( !ctx.Read( m_SelectionIndex ) )
{
m_SelectionIndex = 0; //set default
return false;
}
//Texture index
if ( !ctx.Read( m_TextureIndex ) )
{
m_TextureIndex = 0; //set default
return false;
}
//Material index
if ( !ctx.Read( m_MaterialIndex ) )
{
m_MaterialIndex = 0; //set default
return false;
}
return true;
}
//////////////////////////////////////////
//DEPRECATED
//////////////////////////////////////////
void UpdateVisuals()
{
UpdateVisualsEx();
}
}