Files
YachtDice/Assets/Editor/ModifierAssetCreator.cs
T
horooko ba626acb9b [Add] Universal modifier system, shop, inventory & persistence
Replace hardcoded BonusForOnes/MultiplierForSixes with data-driven
modifier system supporting 2 scopes (SelectedCategory, AnyCategoryClosed),
4 effect types, durability modes (Permanent, LimitedUses), and
configurable targets via ScriptableObject (ModifierData).

- Modifier domain: ModifierEnums, ModifierTarget, ModifierData,
  ModifierRuntime, ModifierEffect (dict-based strategy), ModifierPipeline
  (4-pass: cat-additive → cat-multiplicative → final-additive → final-multiplicative)
- ScoringSystem: replaced old modifier list with ModifierPipeline integration,
  added OnCategoryConfirmed event
- Shop MVC: ShopCatalog (SO), ShopModel, ShopView, ShopItemView, ShopController
- Inventory MVC: InventoryModel (activate/deactivate/sell/durability),
  InventoryView, InventorySlotView, InventoryController
- CurrencyBank: editor-adjustable balance with events
- Persistence: SaveData + SaveSystem (Newtonsoft JSON + PlayerPrefs)
- Editor: ModifierAssetCreator menu item to generate 6 example modifiers + catalog
- Tests: 6 test classes covering effects, pipeline, scoring, shop, inventory, save
- GameController: wired shop/inventory/save lifecycle
- GameInfoView: added currency display, shop/inventory toggle buttons

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 06:40:33 +07:00

124 lines
5.5 KiB
C#

#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
public static class ModifierAssetCreator
{
private const string BasePath = "Assets/ScriptableObjects/Modifiers";
private const string CatalogPath = "Assets/ScriptableObjects";
[MenuItem("YachtDice/Create Example Modifiers + Catalog")]
public static void CreateAll()
{
EnsureFolder(BasePath);
EnsureFolder(CatalogPath);
var m1 = CreateModifier("BonusPerOne", "Bonus Per One",
"+10 за каждый кубик со значением 1",
ModifierRarity.Common, 100, 50,
ModifierScope.SelectedCategory, ModifierEffectType.AddPerDieValue,
10f, 1, YachtCategory.Ones, false,
ModifierDurability.Permanent, 0);
var m2 = CreateModifier("MultiplierPerSix", "Multiplier Per Six",
"x6 за каждый кубик со значением 6",
ModifierRarity.Rare, 200, 100,
ModifierScope.SelectedCategory, ModifierEffectType.MultiplyPerDieValue,
6f, 6, YachtCategory.Ones, false,
ModifierDurability.Permanent, 0);
var m3 = CreateModifier("FullHouseFlat", "Full House Flat Bonus",
"+15 при закрытии Full House",
ModifierRarity.Uncommon, 150, 75,
ModifierScope.SelectedCategory, ModifierEffectType.AddFlatToFinalScore,
15f, 0, YachtCategory.FullHouse, true,
ModifierDurability.Permanent, 0);
var m4 = CreateModifier("YachtDoubler", "Yacht Doubler",
"x2 при закрытии Yacht (3 использования)",
ModifierRarity.Epic, 300, 150,
ModifierScope.SelectedCategory, ModifierEffectType.MultiplyFinalScore,
2f, 0, YachtCategory.Yacht, true,
ModifierDurability.LimitedUses, 3);
var m5 = CreateModifier("FiveBonusGlobal", "Five Bonus Global",
"+5 за каждую пятёрку при закрытии любой категории",
ModifierRarity.Uncommon, 250, 125,
ModifierScope.AnyCategoryClosed, ModifierEffectType.AddPerDieValue,
5f, 5, YachtCategory.Ones, false,
ModifierDurability.Permanent, 0);
var m6 = CreateModifier("CloseMultiplier", "Close Multiplier",
"x1.1 при закрытии любой категории (5 использований)",
ModifierRarity.Rare, 350, 175,
ModifierScope.AnyCategoryClosed, ModifierEffectType.MultiplyFinalScore,
1.1f, 0, YachtCategory.Ones, false,
ModifierDurability.LimitedUses, 5);
// Create ShopCatalog
var catalog = ScriptableObject.CreateInstance<ShopCatalog>();
var catalogSO = new SerializedObject(catalog);
var listProp = catalogSO.FindProperty("availableModifiers");
listProp.arraySize = 6;
listProp.GetArrayElementAtIndex(0).objectReferenceValue = m1;
listProp.GetArrayElementAtIndex(1).objectReferenceValue = m2;
listProp.GetArrayElementAtIndex(2).objectReferenceValue = m3;
listProp.GetArrayElementAtIndex(3).objectReferenceValue = m4;
listProp.GetArrayElementAtIndex(4).objectReferenceValue = m5;
listProp.GetArrayElementAtIndex(5).objectReferenceValue = m6;
catalogSO.ApplyModifiedProperties();
AssetDatabase.CreateAsset(catalog, $"{CatalogPath}/ShopCatalog.asset");
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log("Created 6 example modifiers and ShopCatalog.");
}
private static ModifierData CreateModifier(
string id, string displayName, string description,
ModifierRarity rarity, int shopPrice, int sellPrice,
ModifierScope scope, ModifierEffectType effectType,
float effectValue, int dieValue, YachtCategory targetCategory, bool hasCategoryFilter,
ModifierDurability durability, int maxUses)
{
var data = ScriptableObject.CreateInstance<ModifierData>();
var so = new SerializedObject(data);
so.FindProperty("id").stringValue = id;
so.FindProperty("displayName").stringValue = displayName;
so.FindProperty("description").stringValue = description;
so.FindProperty("rarity").enumValueIndex = (int)rarity;
so.FindProperty("shopPrice").intValue = shopPrice;
so.FindProperty("sellPrice").intValue = sellPrice;
so.FindProperty("scope").enumValueIndex = (int)scope;
so.FindProperty("effectType").enumValueIndex = (int)effectType;
so.FindProperty("effectValue").floatValue = effectValue;
so.FindProperty("durability").enumValueIndex = (int)durability;
so.FindProperty("maxUses").intValue = maxUses;
var targetProp = so.FindProperty("target");
targetProp.FindPropertyRelative("DieValue").intValue = dieValue;
targetProp.FindPropertyRelative("TargetCategory").enumValueIndex = (int)targetCategory;
targetProp.FindPropertyRelative("HasCategoryFilter").boolValue = hasCategoryFilter;
so.ApplyModifiedProperties();
AssetDatabase.CreateAsset(data, $"{BasePath}/{id}.asset");
return data;
}
private static void EnsureFolder(string path)
{
string[] parts = path.Split('/');
string current = parts[0];
for (int i = 1; i < parts.Length; i++)
{
string next = current + "/" + parts[i];
if (!AssetDatabase.IsValidFolder(next))
AssetDatabase.CreateFolder(current, parts[i]);
current = next;
}
}
}
#endif