Merge pull request 'feature/order_interaction' (#33) from feature/order_interaction into develop

Reviewed-on: #33
This commit is contained in:
Jeonghyeon 2025-08-29 05:43:30 +00:00
commit 6d1973a312
7 changed files with 140 additions and 69 deletions

View File

@ -26,10 +26,16 @@ public struct InteractionOutlineData
public float Opacity;
}
public interface IInteractionHighlight
{
void RegisterHighlightProxy(GameObject highlightProxyObject);
void ClearHighlightProxy();
}
[RequireComponent(typeof(HighlightEffect))]
[RequireComponent(typeof(RestaurantInteractionComponent))]
[AddComponentMenu("DDD/Interaction/InteractableHighlight")]
public class InteractableHighlight : MonoBehaviour
public class InteractableHighlight : MonoBehaviour, IInteractionHighlight
{
[Title("Outline Styles")]
[SerializeField] private InteractionOutlineData _availableStyle = new() {Color = Color.white, Width = 1f, Opacity = 1f};
@ -62,6 +68,7 @@ public class InteractableHighlight : MonoBehaviour
private float _opacityMultiply = 1.0f;
private float _breathingTime;
private HighlightEffect _highlightComponent;
private HighlightEffect _highlightProxy;
private RestaurantInteractionComponent _interactionComponent;
private IInteractor _interactor;
private InteractionOutlineType _lastAppliedType = InteractionOutlineType.None;
@ -76,7 +83,24 @@ private void Awake()
_interactionComponent = GetComponent<RestaurantInteractionComponent>();
// HighlightEffect 설정 적용
ApplyHighlightSettings();
ApplyHighlightSettings(_highlightComponent);
}
private HighlightEffect GetHighlightComponent()
{
return _highlightProxy? _highlightProxy : _highlightComponent;
}
public void RegisterHighlightProxy(GameObject highlightProxyObject)
{
var highlightProxy = highlightProxyObject.GetComponent<HighlightEffect>();
_highlightProxy = highlightProxy;
ApplyHighlightSettings(highlightProxy);
}
public void ClearHighlightProxy()
{
_highlightProxy = null;
}
private void InitializeOutlineData()
@ -91,17 +115,17 @@ private void InitializeOutlineData()
};
}
private void ApplyHighlightSettings()
private void ApplyHighlightSettings(HighlightEffect highlightComponent)
{
if (!_highlightComponent) return;
if (!highlightComponent) return;
_highlightComponent.alphaCutOff = _alphaCutOff;
_highlightComponent.combineMeshes = _combineMeshes;
_highlightComponent.constantWidth = _constantWidth;
_highlightComponent.outlineQuality = _outlineQuality;
_highlightComponent.outlineIndependent = _outlineIndependent;
_highlightComponent.outlineBlurPasses = _outlineBlurPasses;
_highlightComponent.outlineSharpness = _outlineSharpness;
highlightComponent.alphaCutOff = _alphaCutOff;
highlightComponent.combineMeshes = _combineMeshes;
highlightComponent.constantWidth = _constantWidth;
highlightComponent.outlineQuality = _outlineQuality;
highlightComponent.outlineIndependent = _outlineIndependent;
highlightComponent.outlineBlurPasses = _outlineBlurPasses;
highlightComponent.outlineSharpness = _outlineSharpness;
}
private void Update()
@ -200,7 +224,8 @@ private void ApplyOutlineType(InteractionOutlineType type)
_lastAppliedType = type;
if (!_highlightComponent)
var highlightComponent = GetHighlightComponent();
if (!highlightComponent)
return;
// OutlineData에서 해당 타입의 스타일 가져오기
@ -213,18 +238,18 @@ private void ApplyOutlineType(InteractionOutlineType type)
// HighlightEffect에 적용
if (type == InteractionOutlineType.None)
{
_highlightComponent.highlighted = false;
_highlightComponent.outline = 0;
highlightComponent.highlighted = false;
highlightComponent.outline = 0;
}
else
{
_highlightComponent.highlighted = true;
highlightComponent.highlighted = true;
Color color = data.Color;
// color.a = data.Opacity * _opacityMultiply;
color *= _opacityMultiply;
_highlightComponent.outlineColor = color;
_highlightComponent.outlineWidth = data.Width;
_highlightComponent.outline = 1;
highlightComponent.outlineColor = color;
highlightComponent.outlineWidth = data.Width;
highlightComponent.outline = 1;
}
}
@ -257,7 +282,7 @@ private void RefreshOutlineData()
[Button("Apply Highlight Settings")]
private void ApplyHighlightSettingsButton()
{
ApplyHighlightSettings();
ApplyHighlightSettings(_highlightComponent);
}
}
}

View File

@ -57,10 +57,10 @@ public bool RequestInteraction(GameObject causer, GameObject target, Interaction
// Cast solverComponent to IInteractable
if (solver is not null && interactor is not null)
{
bool canExecute = solver.CanExecuteInteraction(interactor, interactable, payload);
bool canExecute = solver.CanExecuteInteraction(interactor, interactable, evt.Payload);
if (canExecute)
{
evt.EventResult = solver.ExecuteInteraction(interactor, interactable, payload);
evt.EventResult = solver.ExecuteInteraction(interactor, interactable, evt.Payload);
}
else
{

View File

@ -1,11 +1,43 @@
using UnityEngine;
using UnityEngine.Serialization;
namespace DDD.Restaurant
{
public class RestaurantOrderMenuPayload : ScriptableObject
{
[SerializeField] private string _menuId;
public string MenuId
{
get => _menuId;
set => _menuId = value;
}
}
public class RestaurantOrderSolver_Order : RestaurantOrderSolverBase
{
public override bool ExecuteInteractionSubsystem(IInteractor interactor, IInteractable interactable, ScriptableObject payload = null)
{
// Clear interaction highlight proxy
var highlightObject = interactable?.GetInteractableGameObject();
var highlightComponent = highlightObject?.GetComponent<IInteractionHighlight>();
highlightComponent?.ClearHighlightProxy();
// Pick random menu from today's menu list
var foodCandidates = RestaurantState.Instance.ManagementState.GetTodayFoodMenus();
if (foodCandidates == null || foodCandidates.Count == 0)
{
Debug.LogError("[RestaurantOrderSolver_Order] No food menu found");
return false;
}
// TODO : 손님 데이터를 바탕으로 선호하는 음식을 골라야 할수도 있음. interactable에서 직접 가져오거나 payload로 전달받아야 함. payload를 받을 경우 RestaurantOrderMenuPayload와 통합해야함
var foodMenu = foodCandidates[Random.Range(0, foodCandidates.Count)];
// Create payload and set the menu
RestaurantOrderMenuPayload orderPayload = ScriptableObject.CreateInstance<RestaurantOrderMenuPayload>();
orderPayload.MenuId = foodMenu;
payload = orderPayload;
return base.ExecuteInteractionSubsystem(interactor, interactable, payload);
}

View File

@ -6,6 +6,10 @@ public class RestaurantOrderSolver_Reserved : RestaurantOrderSolverBase
{
public override bool ExecuteInteractionSubsystem(IInteractor interactor, IInteractable interactable, ScriptableObject payload = null)
{
// Register interaction highlight proxy as interactor
var highlightObject = interactable?.GetInteractableGameObject();
var highlightComponent = highlightObject?.GetComponent<IInteractionHighlight>();
highlightComponent?.RegisterHighlightProxy(interactor?.GetInteractorGameObject());
return base.ExecuteInteractionSubsystem(interactor, interactable, payload);
}

View File

@ -7,32 +7,32 @@ namespace DDD.Restaurant
{
public class RestaurantManagementState : ScriptableObject
{
private Dictionary<string, int> _todayFoodRecipeIds = new();
private Dictionary<string, int> _todayDrinkRecipeIds = new();
private List<string> _todayWorkerIds = new();
private Dictionary<string, HashSet<string>> _cookwareToRecipeIds = new();
private Dictionary<string, int> _todayFoodRecipeAndAmounts = new();
private Dictionary<string, int> _todayDrinkRecipeAndAmounts = new();
private List<string> _todayWorkers = new();
private Dictionary<string, HashSet<string>> _cookwareToRecipeMapping = new();
public IReadOnlyDictionary<string, int> TodayFoodRecipeIds => _todayFoodRecipeIds;
public IReadOnlyDictionary<string, int> TodayDrinkRecipeIds => _todayDrinkRecipeIds;
public IReadOnlyList<string> TodayWorkerIds => _todayWorkerIds;
public IReadOnlyDictionary<string, HashSet<string>> CookwareToRecipeIds => _cookwareToRecipeIds;
public IReadOnlyDictionary<string, int> TodayFoodRecipeAndAmounts => _todayFoodRecipeAndAmounts;
public IReadOnlyDictionary<string, int> TodayDrinkRecipeAndAmounts => _todayDrinkRecipeAndAmounts;
public IReadOnlyList<string> TodayWorkers => _todayWorkers;
public IReadOnlyDictionary<string, HashSet<string>> CookwareToRecipeMapping => _cookwareToRecipeMapping;
public int AddedTodayMenuCount => AddedTodayFoodCount + AddedTodayDrinkCount;
public int AddedTodayFoodCount => _todayFoodRecipeIds.Count;
public int AddedTodayDrinkCount => _todayDrinkRecipeIds.Count;
public int AddedTodayWorkerCount => _todayWorkerIds.Count;
public int AddedTodayCookwareCount => _cookwareToRecipeIds.Count;
public int MatchedTodayMenuWithCookwareCount => _cookwareToRecipeIds.Values.Count(recipeSet => recipeSet.Count > 0);
public int AddedTodayFoodCount => _todayFoodRecipeAndAmounts.Count;
public int AddedTodayDrinkCount => _todayDrinkRecipeAndAmounts.Count;
public int AddedTodayWorkerCount => _todayWorkers.Count;
public int AddedTodayCookwareCount => _cookwareToRecipeMapping.Count;
public int MatchedTodayMenuWithCookwareCount => _cookwareToRecipeMapping.Values.Count(recipeSet => recipeSet.Count > 0);
private RestaurantManagementData GetRestaurantManagementData() => RestaurantData.Instance.ManagementData;
public void InitializeReadyForRestaurant()
{
// TODO : Load from disk if possible (save data)
_todayFoodRecipeIds.Clear();
_todayDrinkRecipeIds.Clear();
_todayWorkerIds.Clear();
_cookwareToRecipeIds.Clear();
_todayFoodRecipeAndAmounts.Clear();
_todayDrinkRecipeAndAmounts.Clear();
_todayWorkers.Clear();
_cookwareToRecipeMapping.Clear();
}
/// <summary>
@ -50,24 +50,34 @@ public bool[] GetChecklistStates()
public bool HasFoodRecipes()
{
return _todayFoodRecipeIds.Count > 0;
return _todayFoodRecipeAndAmounts.Count > 0;
}
public bool HasCookwares()
{
return _cookwareToRecipeIds.Count > 0;
return _cookwareToRecipeMapping.Count > 0;
}
public bool HasMatchedMenuWithCookware()
{
return _cookwareToRecipeIds.Values.Any(recipeSet => recipeSet.Count > 0);
return _cookwareToRecipeMapping.Values.Any(recipeSet => recipeSet.Count > 0);
}
public List<string> GetTodayFoodMenus()
{
return _todayFoodRecipeAndAmounts.Keys.ToList();
}
public int GetTodayFoodAvailableCount(string recipeId)
{
return _todayFoodRecipeAndAmounts.GetValueOrDefault(recipeId, 0);
}
public bool IsOpenable()
{
// TODO : 영업 가능한 상태인지 조건 추가 (최소 요리, 요리도구 배치 등)
bool isExistedCookware = CookwareToRecipeIds.Count > 0;
bool isExistedMatchedMenu = _cookwareToRecipeIds.Values.Any(recipeSet => recipeSet is { Count: > 0 });
bool isExistedCookware = CookwareToRecipeMapping.Count > 0;
bool isExistedMatchedMenu = _cookwareToRecipeMapping.Values.Any(recipeSet => recipeSet is { Count: > 0 });
var isOpenable = isExistedCookware && isExistedMatchedMenu;
return isOpenable;
@ -85,31 +95,31 @@ public bool TryAddTodayMenu(ItemModel model)
if (recipeData.RecipeType == RecipeType.FoodRecipe)
{
if (_todayFoodRecipeIds.Count >= GetRestaurantManagementData().MaxFoodCount || _todayFoodRecipeIds.ContainsKey(recipeId)) return false;
if (_todayFoodRecipeAndAmounts.Count >= GetRestaurantManagementData().MaxFoodCount || _todayFoodRecipeAndAmounts.ContainsKey(recipeId)) return false;
var foodData = DataManager.Instance.GetDataAsset<FoodDataAsset>().GetDataById(recipeData.RecipeResult);
var craftableCount = foodData.GetCraftableCount();
foodData.ConsumeAllCraftableIngredients();
_todayFoodRecipeIds[recipeId] = craftableCount;
_todayFoodRecipeAndAmounts[recipeId] = craftableCount;
added = true;
}
else if (recipeData.RecipeType == RecipeType.DrinkRecipe)
{
if (_todayDrinkRecipeIds.Count >= GetRestaurantManagementData().MaxDrinkCount || _todayDrinkRecipeIds.ContainsKey(recipeId)) return false;
if (_todayDrinkRecipeAndAmounts.Count >= GetRestaurantManagementData().MaxDrinkCount || _todayDrinkRecipeAndAmounts.ContainsKey(recipeId)) return false;
var drinkData = DataManager.Instance.GetDataAsset<DrinkDataAsset>().GetDataById(recipeData.RecipeResult);
var craftableCount = drinkData.GetCraftableCount();
drinkData.ConsumeAllCraftableIngredients();
_todayDrinkRecipeIds[recipeId] = craftableCount;
_todayDrinkRecipeAndAmounts[recipeId] = craftableCount;
added = true;
}
if (added)
{
var cookwareKey = GetRequiredCookwareKey(recipeId);
if (string.IsNullOrWhiteSpace(cookwareKey) == false && _cookwareToRecipeIds.TryGetValue(cookwareKey, out var recipeSet))
if (string.IsNullOrWhiteSpace(cookwareKey) == false && _cookwareToRecipeMapping.TryGetValue(cookwareKey, out var recipeSet))
{
recipeSet.Add(recipeId);
}
@ -135,9 +145,9 @@ public bool TryRemoveTodayMenu(ItemModel model)
if (recipeData.RecipeType == RecipeType.FoodRecipe)
{
if (_todayFoodRecipeIds.TryGetValue(recipeId, out refundCount))
if (_todayFoodRecipeAndAmounts.TryGetValue(recipeId, out refundCount))
{
removed = _todayFoodRecipeIds.Remove(recipeId);
removed = _todayFoodRecipeAndAmounts.Remove(recipeId);
removedEvt.InventoryCategoryType = InventoryCategoryType.Food;
if (removed)
@ -149,9 +159,9 @@ public bool TryRemoveTodayMenu(ItemModel model)
}
else if (recipeData.RecipeType == RecipeType.DrinkRecipe)
{
if (_todayDrinkRecipeIds.TryGetValue(recipeId, out refundCount))
if (_todayDrinkRecipeAndAmounts.TryGetValue(recipeId, out refundCount))
{
removed = _todayDrinkRecipeIds.Remove(recipeId);
removed = _todayDrinkRecipeAndAmounts.Remove(recipeId);
removedEvt.InventoryCategoryType = InventoryCategoryType.Drink;
if (removed)
@ -165,7 +175,7 @@ public bool TryRemoveTodayMenu(ItemModel model)
if (removed)
{
var cookwareKey = GetRequiredCookwareKey(recipeId);
if (string.IsNullOrWhiteSpace(cookwareKey) == false && _cookwareToRecipeIds.TryGetValue(cookwareKey, out var recipeSet))
if (string.IsNullOrWhiteSpace(cookwareKey) == false && _cookwareToRecipeMapping.TryGetValue(cookwareKey, out var recipeSet))
{
recipeSet.Remove(recipeId);
}
@ -185,16 +195,16 @@ public bool TryAddTodayCookware(ItemModel model)
if (model.HasItem == false || DataManager.Instance.GetDataAsset<CookwareDataAsset>().ContainsData(cookwareId) == false) return false;
if (_cookwareToRecipeIds.Count >= GetRestaurantManagementData().MaxCookwareCount || _cookwareToRecipeIds.ContainsKey(cookwareId)) return false;
if (_cookwareToRecipeMapping.Count >= GetRestaurantManagementData().MaxCookwareCount || _cookwareToRecipeMapping.ContainsKey(cookwareId)) return false;
_cookwareToRecipeIds[cookwareId] = new HashSet<string>();
_cookwareToRecipeMapping[cookwareId] = new HashSet<string>();
foreach (var recipeId in _todayFoodRecipeIds.Keys.Concat(_todayDrinkRecipeIds.Keys))
foreach (var recipeId in _todayFoodRecipeAndAmounts.Keys.Concat(_todayDrinkRecipeAndAmounts.Keys))
{
var required = GetRequiredCookwareKey(recipeId);
if (required == cookwareId)
{
_cookwareToRecipeIds[cookwareId].Add(recipeId);
_cookwareToRecipeMapping[cookwareId].Add(recipeId);
}
}
@ -206,7 +216,7 @@ public bool TryAddTodayCookware(ItemModel model)
return true;
}
public bool IsContainTodayMenu(string recipeId) => _todayFoodRecipeIds.ContainsKey(recipeId) || _todayDrinkRecipeIds.ContainsKey(recipeId);
public bool IsContainTodayMenu(string recipeId) => _todayFoodRecipeAndAmounts.ContainsKey(recipeId) || _todayDrinkRecipeAndAmounts.ContainsKey(recipeId);
public bool TryRemoveTodayCookware(ItemModel model)
{
@ -214,7 +224,7 @@ public bool TryRemoveTodayCookware(ItemModel model)
if (DataManager.Instance.GetDataAsset<CookwareDataAsset>().ContainsData(cookwareId) == false) return false;
if (_cookwareToRecipeIds.Remove(cookwareId) == false) return false;
if (_cookwareToRecipeMapping.Remove(cookwareId) == false) return false;
var dirtyEvt = GameEvents.SmartVariablesDirtyEvent;
dirtyEvt.DomainFlags = SmartVariablesDomain.RestaurantToday;
@ -242,12 +252,12 @@ private string GetRequiredCookwareKey(string recipeId)
public bool IsCookwareMatched(string recipeId)
{
return _cookwareToRecipeIds.Values.Any(recipeHashSets => recipeHashSets.Contains(recipeId));
return _cookwareToRecipeMapping.Values.Any(recipeHashSets => recipeHashSets.Contains(recipeId));
}
public bool IsTodayMenuMatched(string cookwareId)
{
if (_cookwareToRecipeIds.TryGetValue(cookwareId, out var recipeSet))
if (_cookwareToRecipeMapping.TryGetValue(cookwareId, out var recipeSet))
{
return recipeSet.Count > 0;
}
@ -257,7 +267,7 @@ public bool IsTodayMenuMatched(string cookwareId)
public bool HasAddedCookByCookwareKey(string cookwareKey)
{
return _cookwareToRecipeIds.ContainsKey(cookwareKey) && _cookwareToRecipeIds[cookwareKey].Count > 0;;
return _cookwareToRecipeMapping.ContainsKey(cookwareKey) && _cookwareToRecipeMapping[cookwareKey].Count > 0;;
}
}
}

View File

@ -124,7 +124,7 @@ public void CreateAddedCookItemSlot(Transform parent)
var matchingRecipes = new Dictionary<string, int>();
// CookwareType에 맞는 레시피들을 수집
foreach (var cookwareToRecipe in GetRestaurantManagementState().CookwareToRecipeIds)
foreach (var cookwareToRecipe in GetRestaurantManagementState().CookwareToRecipeMapping)
{
var cookwareId = cookwareToRecipe.Key;
var recipeIds = cookwareToRecipe.Value;
@ -137,8 +137,8 @@ public void CreateAddedCookItemSlot(Transform parent)
if (matchingRecipes.ContainsKey(recipeId) == false)
{
// 레시피 개수 가져오기
int count = GetRestaurantManagementState().TodayFoodRecipeIds.TryGetValue(recipeId, out var foodCount) ? foodCount
: GetRestaurantManagementState().TodayDrinkRecipeIds.TryGetValue(recipeId, out var drinkCount) ? drinkCount
int count = GetRestaurantManagementState().TodayFoodRecipeAndAmounts.TryGetValue(recipeId, out var foodCount) ? foodCount
: GetRestaurantManagementState().TodayDrinkRecipeAndAmounts.TryGetValue(recipeId, out var drinkCount) ? drinkCount
: 0;
matchingRecipes[recipeId] = count;

View File

@ -486,7 +486,7 @@ public void CreateDrinkSlot(Transform parent)
public void UpdateTodayMenuItems()
{
int foodIndex = 0;
foreach (var foodRecipeIdCountPair in GetRestaurantManagementState().TodayFoodRecipeIds)
foreach (var foodRecipeIdCountPair in GetRestaurantManagementState().TodayFoodRecipeAndAmounts)
{
if (foodIndex >= _foodSlots.Count) break;
@ -503,7 +503,7 @@ public void UpdateTodayMenuItems()
}
int drinkIndex = 0;
foreach (var drinkRecipeIdCountPair in GetRestaurantManagementState().TodayDrinkRecipeIds)
foreach (var drinkRecipeIdCountPair in GetRestaurantManagementState().TodayDrinkRecipeAndAmounts)
{
if (drinkIndex >= _drinkSlots.Count) break;
@ -566,7 +566,7 @@ public void CreateTodayCookwareSlot(Transform parent)
public void UpdateTodayRestaurantStateView()
{
int workerIndex = 0;
foreach (var workerKey in GetRestaurantManagementState().TodayWorkerIds)
foreach (var workerKey in GetRestaurantManagementState().TodayWorkers)
{
if (workerIndex >= _workerSlots.Count) break;
@ -583,7 +583,7 @@ public void UpdateTodayRestaurantStateView()
}
int cookwareIndex = 0;
foreach (var cookwareKey in GetRestaurantManagementState().CookwareToRecipeIds.Keys)
foreach (var cookwareKey in GetRestaurantManagementState().CookwareToRecipeMapping.Keys)
{
if (cookwareIndex >= _cookwareSlots.Count) break;