using System;
using Babushka.scripts.CSharp.Common.CharacterControls;
using Babushka.scripts.CSharp.Common.DayAndNight;
using Babushka.scripts.CSharp.Common.Inventory;
using Babushka.scripts.CSharp.Common.Savegame;
using Babushka.scripts.CSharp.Low_Code.Events;
using Babushka.scripts.CSharp.Low_Code.Variables;
using Godot;
using Godot.Collections;
namespace Babushka.scripts.CSharp.Common.Farming;
///
/// Defines the behaviour of the field, i.e. interactions, states and effects.
///
[GlobalClass]
public partial class FieldBehaviour2D : Sprite2D, ISaveable
{
[ExportGroup("Persistence")]
[Export] private VariableNode _fieldIndex;
[Export] private Node _saveIdHolder;
[Export] public VariableResource _sceneKeyProvider;
[Export] public FieldState FieldState = FieldState.Tilled;
[ExportGroup("Field Visuals")]
[Export] private Sprite2D _fieldSprite;
[Export] private Sprite2D _maskSprite;
[Export] private Sprite2D _outlineSprite;
[Export] private Texture2D[] _maskOutlineTextures;
[Export] private Texture2D[] _maskTexture;
[Export] private Texture2D Tilled;
[Export] private Texture2D Watered;
[ExportGroup("Field Interactions")]
[Export] public InteractionArea2D PlantingInteraction;
[Export] public InteractionArea2D FieldInteractionArea;
[ExportGroup("Configuration")]
[Export] public Node2D PlantingPlaceholder;
[Export] public ItemRepository ItemRepository;
[Export] private CpuParticles2D _wateringParticles;
[Export] private EventResource _wateringEvent;
private bool _seedsActive;
private bool _wateringCanActive;
private bool _canPlant;
private bool _canWater;
private int _currentDay;
private PlantBehaviour2D? _currentPlant;
private const string DAY_COUNTER_SAVE_ID = "12c6da2e-fc71-4281-a04a-dfd3c7943975";
[Signal] public delegate void PlantedEventHandler();
private void UpdateInteractionArea()
{
// fieldstate == tilled / watered && samen im Inventar
_canPlant = (FieldState == FieldState.Tilled || FieldState == FieldState.Watered) && _seedsActive;
// fieldstate == tilled && watering can ausgewählt
_canWater = (FieldState == FieldState.Tilled || FieldState == FieldState.Planted) && _wateringCanActive;
FieldInteractionArea.IsActive = _canPlant || _canWater;
}
public void ActivatedSeedInInventory(bool activated)
{
_seedsActive = activated;
UpdateInteractionArea();
}
public void ActivateWateringCanInInventory(bool activated)
{
_wateringCanActive = activated;
UpdateInteractionArea();
}
public override void _EnterTree()
{
LoadFromSaveData();
}
public override void _Ready()
{
if(PlantingPlaceholder.GetChildCount() > 0)
_currentPlant = PlantingPlaceholder.GetChild(0);
UpdateFieldState(FieldState);
int randomIndex = new Random().Next(0, _maskTexture.Length);
_maskSprite.Texture = _maskTexture[randomIndex];
_outlineSprite.Texture = _maskOutlineTextures[randomIndex];
base._Ready();
}
public void UpdateFieldState(FieldState state, bool updateSaveAfter = true)
{
switch (state)
{
case FieldState.Empty:
FieldState = FieldState.Empty;
PlantingInteraction.IsActive = false;
break;
case FieldState.Tilled:
FieldState = FieldState.Tilled;
_fieldSprite.Texture = Tilled;
PlantingInteraction.IsActive = true;
break;
case FieldState.Watered:
FieldState = FieldState.Watered;
_fieldSprite.Texture = Watered;
PlantingInteraction.IsActive = true;
break;
case FieldState.Planted:
FieldState = FieldState.Planted;
_fieldSprite.Texture = Tilled;
PlantingInteraction.IsActive = false;
break;
default:
FieldState = FieldState.NotFound;
break;
}
UpdateInteractionArea();
if(updateSaveAfter)
UpdateSaveData();
}
public void Water()
{
if (WateringCanState.GetFillState() > 0 && FieldState != FieldState.Watered)
{
UpdateFieldState(FieldState.Watered);
_wateringParticles.Emitting = true;
WateringCanState.Water();
_wateringEvent.Raise();
if (_currentPlant != null)
{
_currentPlant.DaysWatered++;
UpdateSaveData();
}
}
}
///
/// Called when the player enters the field's interaction area and presses or clicks.
///
public void Farm()
{
if (_canPlant && TryPlant())
{
EmitSignal(SignalName.Planted);
UpdateFieldState(FieldState.Planted);
}
if (_canWater)
{
Water();
}
}
private bool TryPlant()
{
bool success = false;
int currentSlotIndex = InventoryManager.Instance.CurrentSelectedSlotIndex;
ItemInstance? item = InventoryManager.Instance.playerInventory.Slots[currentSlotIndex].itemInstance;
if (item == null || PlantingPlaceholder.GetChildCount() > 0 || item.amount == 0)
return success;
string plantPrefabPath = ItemRepository.TryGetPrefabPath(item.blueprint);
if (!string.IsNullOrEmpty(plantPrefabPath))
{
PlantPrefab(plantPrefabPath);
InventoryManager.Instance.playerInventory.RemoveItem(currentSlotIndex);
success = true;
}
return success;
}
private void PlantPrefab(string prefabPath)
{
InstantiatePlant(prefabPath);
if (_currentPlant != null)
{
_currentPlant.DayPlanted = _currentDay;
}
}
private void InstantiatePlant(string prefabPath)
{
PackedScene prefab = ResourceLoader.Load(prefabPath, nameof(PackedScene));
Node2D plant2d = prefab.Instantiate();
PlantingPlaceholder.AddChild(plant2d);
plant2d.GlobalPosition = PlantingPlaceholder.GlobalPosition;
_currentPlant = plant2d as PlantBehaviour2D;
if (_currentPlant != null)
{
_currentPlant.Field = this;
}
}
#region SAVE AND LOAD
///
/// Update save data as prep for scene transition (when data is saved and loaded from disk).
///
public void UpdateSaveData()
{
var payloadData = new Dictionary
{
{ "field_state", (int)FieldState },
{ "day_count_on_last_exit", _currentDay}
};
if (_currentPlant != null)
{
payloadData.Add(
"plant_data", new Dictionary()
{
{ "prefab_path", _currentPlant.PrefabPath },
{ "plant_start_day", _currentPlant.DayPlanted },
{ "plant_watered_days", _currentPlant.DaysWatered }
}
);
}
string id = _saveIdHolder.GetMeta("SaveID").AsString();
SavegameService.AppendDataToSave(id, payloadData);
}
///
/// Loads on scene enter.
///
public void LoadFromSaveData()
{
// Get field and plant data
string id = _saveIdHolder.GetMeta("SaveID").AsString();
Dictionary save = SavegameService.GetSaveData(id);
// if we already have a plant, don't instantiate another one
int plantCount = PlantingPlaceholder.GetChildCount();
if (save.Count > 0)
{
// get plant first because it's also relevant for the field state
if (save.TryGetValue("plant_data", out Variant plantDataVar))
{
Dictionary plantDataDict = plantDataVar.AsGodotDictionary();
if (plantDataDict.TryGetValue("prefab_path", out Variant prefabPathVar))
{
if(plantCount == 0)
InstantiatePlant(prefabPathVar.AsString());
else
_currentPlant = PlantingPlaceholder.GetChild(0) as PlantBehaviour2D;
}
else
{
return;
}
if (plantDataDict.TryGetValue("plant_start_day", out Variant plantStartDay) && _currentPlant != null)
{
_currentPlant.DayPlanted = plantStartDay.AsInt32();
}
if (plantDataDict.TryGetValue("plant_watered_days", out Variant plantDaysWatered) && _currentPlant != null)
{
_currentPlant.DaysWatered = plantDaysWatered.AsInt32();
}
}
// Get current day count: Load only. Saving the day count is handled on the day and night prefab.
Dictionary dayCountSave = SavegameService.GetSaveData(DAY_COUNTER_SAVE_ID);
if (dayCountSave.Count > 0)
{
if (dayCountSave.TryGetValue("payload", out Variant dayCountVar))
{
_currentDay = dayCountVar.AsInt32();
if (_currentPlant != null)
{
_currentPlant.CurrentDayInCalendar = _currentDay;
}
}
}
// get field state
if (save.TryGetValue("field_state", out Variant fieldStateVar))
{
int fieldStateInt = fieldStateVar.AsInt32();
// if the field has been unlocked, make it visible.
if (fieldStateInt != 0)
{
Visible = true;
if (save.TryGetValue("day_count_on_last_exit", out Variant lastDayCountVar))
{
int lastDayCount = lastDayCountVar.AsInt32();
// if day is today, then just use the provided field state as is.
if (CalendarController.Instance != null && _currentDay != lastDayCount)
{
// if the field was watered the day before, set it to tilled or planted.
if (fieldStateInt == 3)
{
if (_currentPlant != null)
{
fieldStateInt = 2;
}
else
{
fieldStateInt = 1;
}
}
}
}
FieldState = (FieldState) fieldStateInt;
UpdateFieldState(FieldState, false);
}
}
}
}
#endregion
}