enum CookingMethodType { NONE = 0, //no cooking method available BAKING = 1, BOILING = 2, DRYING = 3, TIME = 4, COUNT } class Cooking { static const float TIME_WITH_SUPPORT_MATERIAL_COEF = 1.0; //! time modifier used when not using support material static const float TIME_WITHOUT_SUPPORT_MATERIAL_COEF = 2.0; //! time modifier used when using support material static const float COOKING_FOOD_TIME_INC_VALUE = 2; //! time increase when cooking a food static const float COOKING_LARD_DECREASE_COEF = 25; //! how many units from quantity of lard are remove at each stage static const float COOKING_FOOD_QUANTITY_DECREASE_AMOUNT_NONE = 25; //! how many units from quantity of item are removed at each FoodStage change when support material is NOT used static const float COOKING_FOOD_QUANTITY_DECREASE_AMOUNT_LARD = 0; //! NOT USED. static const float DEFAULT_COOKING_TEMPERATURE = 150; //default temperature for cooking (e.g. cooking on stick) static const float FOOD_MAX_COOKING_TEMPERATURE = 150; // static const float PARAM_BURN_DAMAGE_COEF = 0.05; //value for calculating damage on items located in fireplace CargoGrid static const float LIQUID_BOILING_POINT = 150; //default boiling point for liquids, overriden by 'liquidBoilingThreshold' in cfgLiquidDefinitions static const float LIQUID_VAPOR_QUANTITY = 2; //vaporization quantity loss static const float SOLID_OVERHEAT_QUANTITY = 2; //solid overheat quantity loss static const float BURNING_WARNING_THRESHOLD = 0.75; //! 0..1, validly cooked item will pre-emptively start emitting burning sounds when this close to being burned typename COOKING_EQUIPMENT_POT = Pot; typename COOKING_EQUIPMENT_FRYINGPAN = FryingPan; typename COOKING_EQUIPMENT_CAULDRON = Cauldron; typename COOKING_INGREDIENT_LARD = Lard; protected float m_UpdateTime = 1; //set by the item/system using cooking void SetCookingUpdateTime(float val) { m_UpdateTime = val; } void ProcessItemToCook(notnull ItemBase pItem, ItemBase cookingEquip, Param2 pCookingMethod, out Param2 pStateFlags) { Edible_Base item_to_cook = Edible_Base.Cast(pItem); if (item_to_cook && item_to_cook.CanBeCooked()) { //! update food UpdateCookingState(item_to_cook, pCookingMethod.param1, cookingEquip, pCookingMethod.param2); //check for done state for boiling and drying if (item_to_cook.IsFoodBoiled() || item_to_cook.IsFoodDried()) { pStateFlags.param1 |= true; } //! check for done state from baking (exclude Lard from baked items) else if (item_to_cook.IsFoodBaked() && item_to_cook.Type() != Lard) { pStateFlags.param1 |= true; } //! check for burned state else if (item_to_cook.IsFoodBurned()) { pStateFlags.param2 |= true; } } else { //add temperature to item if (pItem != cookingEquip) //1st order item already handled by the fireplace directly! AddTemperatureToItem(pItem, null, 0); int liquidType = pItem.GetLiquidType(); bool handleLiquid = pItem.IsLiquidContainer() && liquidType != LIQUID_NONE; bool isLiquiBoiling = handleLiquid && pItem.GetTemperature() >= Liquid.GetBoilThreshold(liquidType); //handle items that can actually overheat if (pItem.CanItemOverheat()) { //handle qty first if (pItem.HasQuantity() && pItem.GetQuantityNormalized() > 0) { if (handleLiquid) { if (pItem.IsItemOverheated() || isLiquiBoiling) //overheat causes qty loss here! pItem.AddQuantity(-LIQUID_VAPOR_QUANTITY,false); } else if (pItem.IsItemOverheated()) { pItem.AddQuantity(-SOLID_OVERHEAT_QUANTITY,true); } }//next handle damage else if (!pItem.IsCookware() && pItem.IsItemOverheated()) //cookware already damaged by fireplace, skipping { pItem.DecreaseHealth(PARAM_BURN_DAMAGE_COEF * 100); } } else { if (!pItem.IsCookware()) //cookware already damaged by fireplace, skipping pItem.DecreaseHealth(PARAM_BURN_DAMAGE_COEF * 100); if (isLiquiBoiling) pItem.AddQuantity(-LIQUID_VAPOR_QUANTITY,false); } //last handle agents if (isLiquiBoiling) pItem.RemoveAllAgentsExcept(eAgents.HEAVYMETAL); } } //COOKING PROCESS //--- Cooking with equipment (pot) //Returns 1 if the item changed its cooking stage, 0 if not int CookWithEquipment(ItemBase cooking_equipment, float cooking_time_coef = 1) { bool is_empty = true; //check cooking conditions if (cooking_equipment == null) return 0; if (cooking_equipment.IsRuined()) return 0; //manage items in cooking equipment Param2 stateFlags = new Param2(false, false); // 1st - done; 2nd - burned Param2 cookingMethodWithTime = GetCookingMethodWithTimeOverride(cooking_equipment); if (cooking_time_coef != 1) cookingMethodWithTime.param2 = cooking_time_coef; //handle the cooking equipment/direct cooking first ProcessItemToCook(cooking_equipment, cooking_equipment, cookingMethodWithTime, stateFlags); //cooking method may have changed due to liquid evaporating, refresh.. if (cooking_equipment.IsCookware() && cooking_equipment.IsLiquidContainer()) { cookingMethodWithTime = GetCookingMethodWithTimeOverride(cooking_equipment); if (cooking_time_coef != 1) cookingMethodWithTime.param2 = cooking_time_coef; } //handle the cooking inside of a container last CargoBase cargo = cooking_equipment.GetInventory().GetCargo(); if (cargo) { int count = cargo.GetItemCount(); is_empty = count == 0; //process items for (int i = 0; i < count; i++) { ProcessItemToCook(ItemBase.Cast(cargo.GetItem(i)), cooking_equipment, cookingMethodWithTime, stateFlags); } } //manage equipment EFFECTS int liquidType = cooking_equipment.GetLiquidType(); //handle liquid boiling EFFECTS if (cooking_equipment.IsLiquidContainer() && liquidType != LIQUID_NONE) { if (cooking_equipment.GetTemperature() >= Liquid.GetBoilThreshold(liquidType)) { //handle boiling audiovisuals for any liquid container cooking_equipment.RefreshAudioVisualsOnClient(cookingMethodWithTime.param1, stateFlags.param1, is_empty, stateFlags.param2); } else { cooking_equipment.RemoveAudioVisualsOnClient(); } } else if (cooking_equipment.IsCookware()) { //handle non-boiling audiovisuals for cookware only cooking_equipment.RefreshAudioVisualsOnClient(cookingMethodWithTime.param1, stateFlags.param1, is_empty, stateFlags.param2); } return 1; } //Returns 1 if the item changed its cooking stage, 0 if not int CookOnStick( Edible_Base item_to_cook, float cook_time_inc ) { if ( item_to_cook && item_to_cook.CanBeCookedOnStick() ) { //update food return UpdateCookingStateOnStick( item_to_cook, cook_time_inc ); } return 0; } //Returns 1 if the item changed its cooking stage, 0 if not protected int UpdateCookingState(Edible_Base item_to_cook, CookingMethodType cooking_method, ItemBase cooking_equipment, float cooking_time_coef) { //food properties float food_temperature = item_to_cook.GetTemperature(); //{min_temperature, time_to_cook, max_temperature (optional)} //get next stage name - if next stage is not defined, next stage name will be empty "" and no cooking properties (food_min_temp, food_time_to_cook, food_max_temp) will be set FoodStageType new_stage_type = item_to_cook.GetNextFoodStageType(cooking_method); float food_min_temp = 0; float food_time_to_cook = 0; //Set next stage cooking properties if next stage possible if (item_to_cook.CanChangeToNewStage(cooking_method)) // new_stage_type != NONE { array next_stage_cooking_properties = new array(); next_stage_cooking_properties = FoodStage.GetAllCookingPropertiesForStage(new_stage_type, null, item_to_cook.GetType()); food_min_temp = next_stage_cooking_properties.Get(eCookingPropertyIndices.MIN_TEMP); //checked after temperature is changed food_time_to_cook = next_stage_cooking_properties.Get(eCookingPropertyIndices.COOK_TIME); } //add temperature AddTemperatureToItem(item_to_cook, cooking_equipment, food_min_temp); //decrease qty of burned items (or cookable items that can't be burned) if (item_to_cook.IsItemOverheated()) DecreaseCookedItemQuantity(item_to_cook,COOKING_FOOD_QUANTITY_DECREASE_AMOUNT_NONE); //add cooking time if the food can be cooked by this method if (food_min_temp > 0 && food_temperature >= food_min_temp) { //! enable cooking SoundEvent item_to_cook.MakeSoundsOnClient(true,cooking_method); float new_cooking_time = item_to_cook.GetCookingTime() + COOKING_FOOD_TIME_INC_VALUE * cooking_time_coef; item_to_cook.SetCookingTime(new_cooking_time); //progress to next stage if (item_to_cook.GetCookingTime() >= food_time_to_cook) { //! Change food stage to new, IF DIFFERENT if (item_to_cook.GetFoodStageType() != new_stage_type) { item_to_cook.ChangeFoodStage(new_stage_type); if (cooking_equipment && cooking_equipment != item_to_cook) { if (cooking_method == CookingMethodType.BAKING) { ItemBase lard = GetItemTypeFromCargo(COOKING_INGREDIENT_LARD, cooking_equipment); if (lard) { //decrease lard quantity float lardQuantity = lard.GetQuantity() - COOKING_LARD_DECREASE_COEF; lardQuantity = Math.Clamp(lardQuantity, 0, lard.GetQuantityMax()); lard.SetQuantity(lardQuantity); } else { //! any foodstage without lard DecreaseCookedItemQuantity(item_to_cook, COOKING_FOOD_QUANTITY_DECREASE_AMOUNT_NONE); } } } else { //! any foodstage without lard DecreaseCookedItemQuantity(item_to_cook, COOKING_FOOD_QUANTITY_DECREASE_AMOUNT_NONE); } } //reset cooking time item_to_cook.ResetCookingTime(); return 1; } } else { item_to_cook.MakeSoundsOnClient(false); } return 0; } //Returns 1 if the item changed its cooking stage, 0 if not protected int UpdateCookingStateOnStick( Edible_Base item_to_cook, float cook_time_inc ) { //food properties float food_temperature = item_to_cook.GetTemperature(); //{min_temperature, time_to_cook, max_temperature (optional)} //get next stage name - if next stage is not defined, next stage name will be empty "" and no cooking properties (food_min_temp, food_time_to_cook, food_max_temp) will be set FoodStageType new_stage_type = item_to_cook.GetNextFoodStageType( CookingMethodType.BAKING ); float food_min_temp = 0; float food_time_to_cook = 0; bool is_done = false; // baked bool is_burned = false; // burned //Set next stage cooking properties if next stage possible if ( item_to_cook.CanChangeToNewStage( CookingMethodType.BAKING ) ) { array next_stage_cooking_properties = new array; next_stage_cooking_properties = FoodStage.GetAllCookingPropertiesForStage( new_stage_type, null, item_to_cook.GetType() ); food_min_temp = next_stage_cooking_properties.Get( eCookingPropertyIndices.MIN_TEMP ); food_time_to_cook = next_stage_cooking_properties.Get( eCookingPropertyIndices.COOK_TIME ); } if (item_to_cook.GetInventory().IsAttachment()) { //add temperature AddTemperatureToItem(item_to_cook, null, food_min_temp); } //add cooking time if the food can be cooked by this method if (food_min_temp > 0 && food_temperature >= food_min_temp) { //refresh audio item_to_cook.MakeSoundsOnClient(true, CookingMethodType.BAKING); float new_cooking_time = item_to_cook.GetCookingTime() + cook_time_inc; item_to_cook.SetCookingTime( new_cooking_time ); //progress to next stage if (item_to_cook.GetCookingTime() >= food_time_to_cook) { //! Change food stage to new, IF DIFFERENT if (item_to_cook.GetFoodStageType() != new_stage_type) { item_to_cook.ChangeFoodStage(new_stage_type); DecreaseCookedItemQuantity(item_to_cook, COOKING_FOOD_QUANTITY_DECREASE_AMOUNT_NONE); } //reset cooking time item_to_cook.ResetCookingTime(); return 1; } } else { item_to_cook.MakeSoundsOnClient(false); } return 0; } void SmokeItem(Edible_Base item_to_cook, float cook_time_inc) { if (item_to_cook) { float new_cook_time = item_to_cook.GetCookingTime() + cook_time_inc; float drying_cook_time = FoodStage.GetCookingPropertyFromIndex(eCookingPropertyIndices.COOK_TIME, FoodStageType.DRIED, null, item_to_cook.GetType()); float drying_cook_temp = FoodStage.GetCookingPropertyFromIndex(eCookingPropertyIndices.MIN_TEMP, FoodStageType.DRIED, null, item_to_cook.GetType()); float itemTemp = item_to_cook.GetTemperature(); if (itemTemp >= drying_cook_temp) { switch (item_to_cook.GetFoodStageType()) { case FoodStageType.RAW: item_to_cook.SetCookingTime(new_cook_time); if (item_to_cook.GetCookingTime() >= drying_cook_time) { item_to_cook.ChangeFoodStage(FoodStageType.DRIED); item_to_cook.ResetCookingTime(); } break; default: item_to_cook.SetCookingTime(new_cook_time); if (item_to_cook.GetCookingTime() >= drying_cook_time) { item_to_cook.ChangeFoodStage(FoodStageType.BURNED); item_to_cook.ResetCookingTime(); } break; } } } } void TerminateCookingSounds(ItemBase pItem) { Edible_Base edible; if (pItem) { CargoBase cargo = pItem.GetInventory().GetCargo(); if (cargo) // cookware { for (int i = 0; i < cargo.GetItemCount(); i++) { edible = Edible_Base.Cast(cargo.GetItem(i)); if (edible) { edible.MakeSoundsOnClient(false); } } } else { edible = Edible_Base.Cast(pItem); if (edible) { edible.MakeSoundsOnClient(false); } } } } //! Cooking data protected ItemBase GetItemTypeFromCargo( typename item_type, ItemBase cooking_equipment ) { CargoBase cargo = cooking_equipment.GetInventory().GetCargo(); if (cargo) { for (int i = 0; i < cargo.GetItemCount(); i++) { EntityAI entity = cargo.GetItem(i); if (entity.Type() == item_type) { ItemBase item = ItemBase.Cast(entity); return item; } } } return null; } protected Param2 GetCookingMethodWithTimeOverride(ItemBase cooking_equipment) { if (cooking_equipment.IsCookware()) { if (cooking_equipment.GetQuantity() > 0) { if (cooking_equipment.GetLiquidType() == LIQUID_GASOLINE) { //! when cooking in gasoline, jump to drying state(will be burnt then) return new Param2(CookingMethodType.DRYING, TIME_WITHOUT_SUPPORT_MATERIAL_COEF); } return new Param2(CookingMethodType.BOILING, TIME_WITH_SUPPORT_MATERIAL_COEF); } if (GetItemTypeFromCargo(COOKING_INGREDIENT_LARD, cooking_equipment)) { //has lard in cargo, slower process return new Param2(CookingMethodType.BAKING, TIME_WITH_SUPPORT_MATERIAL_COEF); } if (cooking_equipment.GetInventory().GetCargo().GetItemCount() > 0) { return new Param2(CookingMethodType.BAKING, TIME_WITHOUT_SUPPORT_MATERIAL_COEF); } return new Param2(CookingMethodType.NONE, TIME_WITHOUT_SUPPORT_MATERIAL_COEF); } else if (cooking_equipment.IsLiquidContainer() && cooking_equipment.GetQuantity() > 0 && cooking_equipment.GetLiquidType() != LIQUID_GASOLINE) //fake 'boiling' on liquid containers, for effects playback { return new Param2(CookingMethodType.BOILING, TIME_WITH_SUPPORT_MATERIAL_COEF); } return new Param2(CookingMethodType.BAKING, TIME_WITHOUT_SUPPORT_MATERIAL_COEF); } Edible_Base GetFoodOnStick( ItemBase stick_item ) { Edible_Base food_on_stick = Edible_Base.Cast( stick_item.GetAttachmentByType( Edible_Base ) ); return food_on_stick; } float GetTimeToCook( Edible_Base item_to_cook, CookingMethodType cooking_method ) { FoodStageType food_stage_type = item_to_cook.GetNextFoodStageType( cooking_method ); return FoodStage.GetCookingPropertyFromIndex( eCookingPropertyIndices.COOK_TIME, food_stage_type, null, item_to_cook.GetType()); } float GetMinTempToCook( Edible_Base item_to_cook, CookingMethodType cooking_method ) { FoodStageType food_stage_type = item_to_cook.GetNextFoodStageType( cooking_method ); return FoodStage.GetCookingPropertyFromIndex( eCookingPropertyIndices.MIN_TEMP, food_stage_type, null, item_to_cook.GetType()); } //add temperature to item protected void AddTemperatureToItem( ItemBase cooked_item, ItemBase cooking_equipment, float min_temperature ) { if (!GetGame().IsServer()) return; if (cooked_item == cooking_equipment) //solves direct cooking double heating return; if (cooked_item.CanHaveTemperature()) { float itemTemp = cooked_item.GetTemperature(); //set target temperature float targetTemp; if (!cooked_item.GetHierarchyRoot().GetCookingTargetTemperature(targetTemp)) //if not valid, fallback values enter the equation { if (cooking_equipment) targetTemp = cooking_equipment.GetTemperature(); else targetTemp = DEFAULT_COOKING_TEMPERATURE; } //adjust temperature if (targetTemp != itemTemp || !cooked_item.IsFreezeThawProgressFinished()) { float heatPermCoef = 1.0; if (cooking_equipment) heatPermCoef = cooking_equipment.GetHeatPermeabilityCoef(); heatPermCoef *= cooked_item.GetHeatPermeabilityCoef(); cooked_item.SetTemperatureEx(new TemperatureDataInterpolated(targetTemp,ETemperatureAccessTypes.ACCESS_COOKING,m_UpdateTime,GameConstants.TEMP_COEF_COOKING_DEFAULT,heatPermCoef)); } } } protected void DecreaseCookedItemQuantity(notnull Edible_Base pItem, float pAmount = 0.0) { if (GetGame().IsServer()) { float quantity = pItem.GetQuantity() - pAmount; quantity = Math.Clamp(quantity, 0, pItem.GetQuantityMax()); pItem.SetQuantity(quantity); } } //////////////////////////// //DEPRECATED cooking stuff //! DEPRECATED protected CookingMethodType GetCookingMethod(ItemBase cooking_equipment) { if (cooking_equipment.Type() == COOKING_EQUIPMENT_POT || cooking_equipment.Type() == COOKING_EQUIPMENT_CAULDRON) { //has water, but not petrol dammit X) if (cooking_equipment.GetQuantity() > 0 && cooking_equipment.GetLiquidType() != LIQUID_GASOLINE) { return CookingMethodType.BOILING; } //has lard in cargo if (GetItemTypeFromCargo(COOKING_INGREDIENT_LARD, cooking_equipment)) { return CookingMethodType.BAKING; } return CookingMethodType.DRYING; } if (cooking_equipment.Type() == COOKING_EQUIPMENT_FRYINGPAN) { if (GetItemTypeFromCargo(COOKING_INGREDIENT_LARD, cooking_equipment)) { return CookingMethodType.BAKING; } return CookingMethodType.DRYING; } return CookingMethodType.NONE; } }