feat(lucky-dice): 提交 demo 前核心流程
This commit is contained in:
29
.gitignore
vendored
Normal file
29
.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
Confs
|
||||||
|
Client/ServerData
|
||||||
|
/assetbundles
|
||||||
|
.DS_Store
|
||||||
|
.vscode/
|
||||||
|
/cfgPack/encryptcfg
|
||||||
|
.gemini/
|
||||||
|
GEMINI.md
|
||||||
|
CLAUDE.md
|
||||||
|
AGENTS.md
|
||||||
|
QWEN.md
|
||||||
|
Client/.claude/
|
||||||
|
dev-ab-svr/debugconfig.json
|
||||||
|
doc/
|
||||||
|
|
||||||
|
# Codex/AI local runtime and generated artifacts.
|
||||||
|
# CasualFishingSkills is the shared source repo; this game repo only keeps local installs/outputs.
|
||||||
|
/.agents/
|
||||||
|
/.worktrees/
|
||||||
|
/CodexAgent/
|
||||||
|
/CodexOutput/
|
||||||
|
/skills-lock.json
|
||||||
|
|
||||||
|
# Legacy local output roots kept ignored while workflows migrate to CodexOutput.
|
||||||
|
/output/
|
||||||
|
/outputs/
|
||||||
|
/temp/
|
||||||
|
/tmp/
|
||||||
|
/FishingFrenzy/output/
|
||||||
101
FishDice/.gitignore
vendored
Normal file
101
FishDice/.gitignore
vendored
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
# This .gitignore file should be placed at the root of your Unity project directory
|
||||||
|
#
|
||||||
|
# Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore
|
||||||
|
#
|
||||||
|
/[Ll]ibrary/
|
||||||
|
/[Tt]emp/
|
||||||
|
/[Oo]bj/
|
||||||
|
/[Bb]uild/
|
||||||
|
/[Bb]uilds/
|
||||||
|
/[Ll]ogs/
|
||||||
|
/[Uu]ser[Ss]ettings/
|
||||||
|
/[Bb]in/
|
||||||
|
/SerializedBuildInfo/
|
||||||
|
|
||||||
|
# ignore all files and folders ends with "~"
|
||||||
|
*~
|
||||||
|
|
||||||
|
# MemoryCaptures can get excessive in size.
|
||||||
|
# They also could contain extremely sensitive data
|
||||||
|
/[Mm]emoryCaptures/
|
||||||
|
|
||||||
|
# Asset meta data should only be ignored when the corresponding asset is also ignored
|
||||||
|
!/[Aa]ssets/**/*.meta
|
||||||
|
|
||||||
|
# Uncomment this line if you wish to ignore the asset store tools plugin
|
||||||
|
# /[Aa]ssets/AssetStoreTools*
|
||||||
|
|
||||||
|
# Autogenerated Jetbrains Rider plugin
|
||||||
|
/[Aa]ssets/Plugins/Editor/JetBrains*
|
||||||
|
|
||||||
|
# Visual Studio cache directory
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
# Gradle cache directory
|
||||||
|
.gradle/
|
||||||
|
|
||||||
|
# Autogenerated VS/MD/Consulo solution and project files
|
||||||
|
ExportedObj/
|
||||||
|
.consulo/
|
||||||
|
*.csproj
|
||||||
|
*.unityproj
|
||||||
|
*.sln
|
||||||
|
*.suo
|
||||||
|
*.tmp
|
||||||
|
*.user
|
||||||
|
*.userprefs
|
||||||
|
*.pidb
|
||||||
|
*.booproj
|
||||||
|
*.svd
|
||||||
|
*.pdb
|
||||||
|
*.mdb
|
||||||
|
*.opendb
|
||||||
|
*.VC.db
|
||||||
|
.vsconfig
|
||||||
|
.vshistory
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Unity3D generated meta files
|
||||||
|
*.pidb.meta
|
||||||
|
*.pdb.meta
|
||||||
|
*.mdb.meta
|
||||||
|
|
||||||
|
# Unity3D generated file on crash reports
|
||||||
|
sysinfo.txt
|
||||||
|
|
||||||
|
# Builds
|
||||||
|
*.apk
|
||||||
|
*.unitypackage
|
||||||
|
|
||||||
|
# Crashlytics generated file
|
||||||
|
crashlytics-build.properties
|
||||||
|
|
||||||
|
|
||||||
|
# ignore Test Scripts & resources in test folder
|
||||||
|
/[Aa]ssets/[Tt]emp[Tt]est/
|
||||||
|
/[Aa]ssets/[Tt]emp[Tt]est
|
||||||
|
/[Aa]ssets/[Tt]emp[Tt]est.meta
|
||||||
|
|
||||||
|
|
||||||
|
# ignore build info from reporter
|
||||||
|
/[Aa]ssets/StreamingAssets/build_info/
|
||||||
|
/[Aa]ssets/StreamingAssets/build_info
|
||||||
|
/[Aa]ssets/StreamingAssets/build_info.meta
|
||||||
|
|
||||||
|
## ignore generated playfab async scripts
|
||||||
|
#/LocalPackages/asap.playfab.async/Runtime/Gen.meta
|
||||||
|
#/LocalPackages/asap.playfab.async/Runtime/Gen/
|
||||||
|
#/LocalPackages/asap.playfab.async/Runtime/Gen
|
||||||
|
|
||||||
|
# ignore core source code
|
||||||
|
/LocalPackages/GameCore/
|
||||||
|
/LocalPackages/asap.core/
|
||||||
|
|
||||||
|
# HybridCLR Temp project
|
||||||
|
/HybridCLRData/StrippedAOTDllsTempProj
|
||||||
|
|
||||||
|
# ignore idea files
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# ignore new slnx files
|
||||||
|
*.slnx
|
||||||
9
FishDice/Assets/FishDice.meta
Normal file
9
FishDice/Assets/FishDice.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5f266ba0b7dd4949b3e14abe82b5fc32
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
9
FishDice/Assets/FishDice/Runtime.meta
Normal file
9
FishDice/Assets/FishDice/Runtime.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 678f1a9001a34e9eb1a79bcc58b8f347
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
14
FishDice/Assets/FishDice/Runtime/FishDice.Runtime.asmdef
Normal file
14
FishDice/Assets/FishDice/Runtime/FishDice.Runtime.asmdef
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "FishDice.Runtime",
|
||||||
|
"rootNamespace": "FishDice",
|
||||||
|
"references": [],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ea1dba60786d4669be927ea3ec48f6f3
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
9
FishDice/Assets/FishDice/Runtime/LuckyDice.meta
Normal file
9
FishDice/Assets/FishDice/Runtime/LuckyDice.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 62d522d99c944966acf214919da3046a
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
9
FishDice/Assets/FishDice/Runtime/LuckyDice/Actions.meta
Normal file
9
FishDice/Assets/FishDice/Runtime/LuckyDice/Actions.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 64a35bdcdd7447d49db30885a065cc3d
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class GrantRewardExecutor : IRollActionExecutor
|
||||||
|
{
|
||||||
|
public string ActionType => LuckyDiceKeys.GrantRewardAction;
|
||||||
|
|
||||||
|
public RollActionResult Execute(RollActionContext context, RollActionSpec action)
|
||||||
|
{
|
||||||
|
return RollActionResult.Succeeded(
|
||||||
|
action.ActionType,
|
||||||
|
new RollEffect[] { new RewardEffect() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class AddMultiplierExecutor : IRollActionExecutor
|
||||||
|
{
|
||||||
|
public string ActionType => LuckyDiceKeys.AddMultiplierAction;
|
||||||
|
|
||||||
|
public RollActionResult Execute(RollActionContext context, RollActionSpec action)
|
||||||
|
{
|
||||||
|
action.Params.TryGetValue("reason", out var reason);
|
||||||
|
return RollActionResult.Succeeded(
|
||||||
|
action.ActionType,
|
||||||
|
new RollEffect[] { new MultiplierEffect(reason) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TriggerLuckyDiceExecutor : IRollActionExecutor
|
||||||
|
{
|
||||||
|
public string ActionType => LuckyDiceKeys.TriggerLuckyDiceAction;
|
||||||
|
|
||||||
|
public RollActionResult Execute(RollActionContext context, RollActionSpec action)
|
||||||
|
{
|
||||||
|
return RollActionResult.Succeeded(
|
||||||
|
action.ActionType,
|
||||||
|
new RollEffect[] { new LuckyDiceRequestedEffect() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d2973688c08e47babf53588cd7d3e72c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public interface IRollActionExecutor
|
||||||
|
{
|
||||||
|
string ActionType { get; }
|
||||||
|
|
||||||
|
RollActionResult Execute(RollActionContext context, RollActionSpec action);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a96e9c7c4cd442fa9e4f74d201c9cbdf
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class RollActionDispatcher
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, IRollActionExecutor> _executors;
|
||||||
|
|
||||||
|
public RollActionDispatcher(IEnumerable<IRollActionExecutor> executors)
|
||||||
|
{
|
||||||
|
if (executors == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(executors));
|
||||||
|
}
|
||||||
|
|
||||||
|
_executors = executors.ToDictionary(executor => executor.ActionType, executor => executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<RollActionResult> Execute(
|
||||||
|
RollActionContext context,
|
||||||
|
IReadOnlyList<RollActionSpec> actions)
|
||||||
|
{
|
||||||
|
if (context == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actions == null)
|
||||||
|
{
|
||||||
|
return Array.Empty<RollActionResult>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var results = new List<RollActionResult>(actions.Count);
|
||||||
|
foreach (var action in actions)
|
||||||
|
{
|
||||||
|
var result = ExecuteSingle(context, action);
|
||||||
|
results.Add(result);
|
||||||
|
context.Trace.RecordActionResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RollActionResult ExecuteSingle(RollActionContext context, RollActionSpec action)
|
||||||
|
{
|
||||||
|
if (!_executors.TryGetValue(action.ActionType, out var executor))
|
||||||
|
{
|
||||||
|
return RollActionResult.Failure(
|
||||||
|
action.ActionType,
|
||||||
|
"No executor registered for action type: " + action.ActionType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return executor.Execute(context, action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 771e68c81ca14d2fb5998ac049f9ca5a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public static class RollActionExecutorRegistry
|
||||||
|
{
|
||||||
|
public static IReadOnlyList<IRollActionExecutor> CreateDefaultExecutors()
|
||||||
|
{
|
||||||
|
return new IRollActionExecutor[]
|
||||||
|
{
|
||||||
|
new GrantRewardExecutor(),
|
||||||
|
new AddMultiplierExecutor(),
|
||||||
|
new TriggerLuckyDiceExecutor()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6bc162f33bbf4660b1599dce107dc223
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
9
FishDice/Assets/FishDice/Runtime/LuckyDice/Config.meta
Normal file
9
FishDice/Assets/FishDice/Runtime/LuckyDice/Config.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d1eeebb9f2ae47058ce0524e8539b8cc
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class ComboRuleDefinition
|
||||||
|
{
|
||||||
|
public string RuleId;
|
||||||
|
public int Priority;
|
||||||
|
public string MatcherType;
|
||||||
|
public string ComboKey;
|
||||||
|
public string FaceKey;
|
||||||
|
public int? RequiredCount;
|
||||||
|
public int? MinCount;
|
||||||
|
public int? MaxCount;
|
||||||
|
public int? DiceCount;
|
||||||
|
public int? OfAKindCount;
|
||||||
|
public int? MinSum;
|
||||||
|
public int? MaxSum;
|
||||||
|
public bool UseOrderedKey;
|
||||||
|
public bool StopAfterMatched = true;
|
||||||
|
public RollActionDefinition[] Actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class RollActionDefinition
|
||||||
|
{
|
||||||
|
public string ActionType;
|
||||||
|
public ActionParamDefinition[] Params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ActionParamDefinition
|
||||||
|
{
|
||||||
|
public string Key;
|
||||||
|
public string Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f1f14033988649cebb5429c594a53de9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public static class ComboRuleDefinitionLoader
|
||||||
|
{
|
||||||
|
public static IReadOnlyList<ComboRule> Load(IReadOnlyList<ComboRuleDefinition> definitions)
|
||||||
|
{
|
||||||
|
if (definitions == null)
|
||||||
|
{
|
||||||
|
return Array.Empty<ComboRule>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return definitions.Select(ToRuntimeRule).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ComboRule ToRuntimeRule(ComboRuleDefinition definition)
|
||||||
|
{
|
||||||
|
if (definition == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(definition));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ComboRule(
|
||||||
|
definition.RuleId,
|
||||||
|
definition.Priority,
|
||||||
|
new ComboMatcherSpec(
|
||||||
|
definition.MatcherType,
|
||||||
|
comboKey: definition.ComboKey,
|
||||||
|
faceKey: definition.FaceKey,
|
||||||
|
requiredCount: definition.RequiredCount,
|
||||||
|
minCount: definition.MinCount,
|
||||||
|
maxCount: definition.MaxCount,
|
||||||
|
diceCount: definition.DiceCount,
|
||||||
|
ofAKindCount: definition.OfAKindCount,
|
||||||
|
minSum: definition.MinSum,
|
||||||
|
maxSum: definition.MaxSum,
|
||||||
|
useOrderedKey: definition.UseOrderedKey),
|
||||||
|
ToRuntimeActions(definition.Actions),
|
||||||
|
definition.StopAfterMatched);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IReadOnlyList<RollActionSpec> ToRuntimeActions(IReadOnlyList<RollActionDefinition> definitions)
|
||||||
|
{
|
||||||
|
if (definitions == null)
|
||||||
|
{
|
||||||
|
return Array.Empty<RollActionSpec>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return definitions.Select(ToRuntimeAction).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RollActionSpec ToRuntimeAction(RollActionDefinition definition)
|
||||||
|
{
|
||||||
|
if (definition == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(definition));
|
||||||
|
}
|
||||||
|
|
||||||
|
var parameters = definition.Params == null
|
||||||
|
? null
|
||||||
|
: definition.Params.ToDictionary(param => param.Key, param => param.Value);
|
||||||
|
|
||||||
|
return new RollActionSpec(definition.ActionType, parameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d8b93de4100240c792be3c214c06218c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class InvalidDiceSetException : Exception
|
||||||
|
{
|
||||||
|
public InvalidDiceSetException(string diceSetId)
|
||||||
|
: base("Invalid dice set id: " + diceSetId)
|
||||||
|
{
|
||||||
|
DiceSetId = diceSetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DiceSetId { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b6f07727f4e84a88a31eb2d4caec3dc6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class InvalidLuckyDiceConfigException : Exception
|
||||||
|
{
|
||||||
|
public InvalidLuckyDiceConfigException(ConfigValidationResult validationResult)
|
||||||
|
: base(BuildMessage(validationResult))
|
||||||
|
{
|
||||||
|
ValidationResult = validationResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigValidationResult ValidationResult { get; }
|
||||||
|
|
||||||
|
private static string BuildMessage(ConfigValidationResult validationResult)
|
||||||
|
{
|
||||||
|
if (validationResult == null || validationResult.Errors.Count == 0)
|
||||||
|
{
|
||||||
|
return "Invalid Lucky Dice config.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Invalid Lucky Dice config: " + validationResult.Errors[0].Code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 56d5d29378624c30b6510846b4fd45e7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class LuckyDiceCandidateConfig
|
||||||
|
{
|
||||||
|
public LuckyDiceCandidateConfig(
|
||||||
|
string resultKey,
|
||||||
|
string targetKey,
|
||||||
|
int? resultSlotCount,
|
||||||
|
int weight,
|
||||||
|
bool enabled,
|
||||||
|
int minLevel,
|
||||||
|
IReadOnlyList<string> sourceFilter,
|
||||||
|
bool isDefault)
|
||||||
|
{
|
||||||
|
ResultKey = resultKey;
|
||||||
|
TargetKey = targetKey;
|
||||||
|
ResultSlotCount = resultSlotCount;
|
||||||
|
Weight = weight;
|
||||||
|
Enabled = enabled;
|
||||||
|
MinLevel = minLevel;
|
||||||
|
SourceFilter = sourceFilter == null
|
||||||
|
? Array.Empty<string>()
|
||||||
|
: new List<string>(sourceFilter).AsReadOnly();
|
||||||
|
IsDefault = isDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ResultKey { get; }
|
||||||
|
|
||||||
|
public string TargetKey { get; }
|
||||||
|
|
||||||
|
public int? ResultSlotCount { get; }
|
||||||
|
|
||||||
|
public int Weight { get; }
|
||||||
|
|
||||||
|
public bool Enabled { get; }
|
||||||
|
|
||||||
|
public int MinLevel { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<string> SourceFilter { get; }
|
||||||
|
|
||||||
|
public bool IsDefault { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TargetModeConfig
|
||||||
|
{
|
||||||
|
public TargetModeConfig(string resultKey, string targetKey, string modeKey)
|
||||||
|
{
|
||||||
|
ResultKey = resultKey;
|
||||||
|
TargetKey = targetKey;
|
||||||
|
ModeKey = modeKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ResultKey { get; }
|
||||||
|
|
||||||
|
public string TargetKey { get; }
|
||||||
|
|
||||||
|
public string ModeKey { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d22247243d584a8fa28b45e708517c1d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class LuckyDiceCandidateDefinition
|
||||||
|
{
|
||||||
|
public string ResultKey;
|
||||||
|
public string TargetKey;
|
||||||
|
public int? ResultSlotCount;
|
||||||
|
public int Weight;
|
||||||
|
public bool Enabled = true;
|
||||||
|
public int MinLevel;
|
||||||
|
public string[] SourceFilter;
|
||||||
|
public bool IsDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TargetModeDefinition
|
||||||
|
{
|
||||||
|
public string ResultKey;
|
||||||
|
public string TargetKey;
|
||||||
|
public string ModeKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 98f58d378176481380ec6413753c5717
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class LuckyDiceConfigValidationContext
|
||||||
|
{
|
||||||
|
public LuckyDiceConfigValidationContext(
|
||||||
|
IReadOnlyList<IComboMatcher> matchers,
|
||||||
|
IReadOnlyList<IRollActionExecutor> actionExecutors)
|
||||||
|
{
|
||||||
|
Matchers = matchers == null ? Array.Empty<IComboMatcher>() : matchers.ToArray();
|
||||||
|
ActionExecutors = actionExecutors == null ? Array.Empty<IRollActionExecutor>() : actionExecutors.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<IComboMatcher> Matchers { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<IRollActionExecutor> ActionExecutors { get; }
|
||||||
|
|
||||||
|
public static LuckyDiceConfigValidationContext CreateDefault()
|
||||||
|
{
|
||||||
|
return new LuckyDiceConfigValidationContext(
|
||||||
|
ComboMatcherRegistry.CreateDefaultMatchers(),
|
||||||
|
RollActionExecutorRegistry.CreateDefaultExecutors());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2bd13dcfd4a54d9fa90b11b60df1a7e0
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public static class LuckyDiceConfigValidator
|
||||||
|
{
|
||||||
|
public static ConfigValidationResult Validate(
|
||||||
|
IReadOnlyList<ComboRuleDefinition> rules,
|
||||||
|
IReadOnlyList<LuckyDiceCandidateDefinition> candidates,
|
||||||
|
IReadOnlyList<TargetModeDefinition> targetModes,
|
||||||
|
IReadOnlyList<IComboMatcher> matchers,
|
||||||
|
IReadOnlyList<IRollActionExecutor> actionExecutors)
|
||||||
|
{
|
||||||
|
var errors = new List<ConfigValidationError>();
|
||||||
|
var matcherTypes = ValidateMatchers(errors, matchers);
|
||||||
|
var actionTypes = ValidateActionExecutors(errors, actionExecutors);
|
||||||
|
var ruleIds = new HashSet<string>();
|
||||||
|
|
||||||
|
foreach (var rule in rules ?? Array.Empty<ComboRuleDefinition>())
|
||||||
|
{
|
||||||
|
if (rule == null)
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("NullRule", "Rule cannot be null."));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(rule.RuleId))
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("EmptyRuleId", "Rule id is required."));
|
||||||
|
}
|
||||||
|
else if (!ruleIds.Add(rule.RuleId))
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("DuplicateRuleId", "Duplicate rule id: " + rule.RuleId));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matcherTypes.Contains(rule.MatcherType))
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("UnknownMatcher", "Unknown matcher: " + rule.MatcherType));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var action in rule.Actions ?? Array.Empty<RollActionDefinition>())
|
||||||
|
{
|
||||||
|
if (action == null)
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("NullAction", "Action cannot be null."));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!actionTypes.Contains(action.ActionType))
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("UnknownAction", "Unknown action: " + action.ActionType));
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidateActionParams(errors, action.Params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((candidates ?? Array.Empty<LuckyDiceCandidateDefinition>()).All(candidate => candidate == null || !candidate.IsDefault))
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("MissingDefaultCandidate", "Lucky Dice needs one default candidate."));
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetModeKeys = new HashSet<string>();
|
||||||
|
foreach (var targetMode in targetModes ?? Array.Empty<TargetModeDefinition>())
|
||||||
|
{
|
||||||
|
if (targetMode == null)
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("NullTargetMode", "Target mode cannot be null."));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(targetMode.ResultKey))
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("EmptyTargetModeResultKey", "Target mode result key is required."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(targetMode.TargetKey))
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("EmptyTargetModeTargetKey", "Target mode target key is required."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(targetMode.ModeKey))
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("EmptyTargetModeModeKey", "Target mode mode key is required."));
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetModeKey = targetMode.ResultKey + "|" + targetMode.TargetKey;
|
||||||
|
if (!targetModeKeys.Add(targetModeKey))
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("DuplicateTargetMode", "Duplicate target mode: " + targetModeKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var candidateKeys = new HashSet<string>();
|
||||||
|
foreach (var candidate in candidates ?? Array.Empty<LuckyDiceCandidateDefinition>())
|
||||||
|
{
|
||||||
|
if (candidate == null)
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("NullCandidate", "Candidate cannot be null."));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(candidate.ResultKey))
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("EmptyCandidateResultKey", "Candidate result key is required."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(candidate.TargetKey))
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("EmptyCandidateTargetKey", "Candidate target key is required."));
|
||||||
|
}
|
||||||
|
|
||||||
|
var candidateKey = candidate.ResultKey + "|" + candidate.TargetKey;
|
||||||
|
if (!candidateKeys.Add(candidateKey))
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("DuplicateCandidate", "Duplicate candidate: " + candidateKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidate.Weight < 0)
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("NegativeCandidateWeight", "Candidate weight cannot be negative: " + candidate.ResultKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidate.ResultSlotCount.HasValue && candidate.ResultSlotCount.Value <= 0)
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("InvalidResultSlotCount", "Result slot count must be greater than zero: " + candidate.ResultKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidate.IsDefault && !candidate.Enabled)
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("DisabledDefaultCandidate", "Default candidate cannot be disabled: " + candidate.ResultKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasMode = (targetModes ?? Array.Empty<TargetModeDefinition>())
|
||||||
|
.Any(mode => mode != null && mode.ResultKey == candidate.ResultKey && mode.TargetKey == candidate.TargetKey);
|
||||||
|
if (!hasMode)
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("MissingTargetMode", "Missing target mode for result: " + candidate.ResultKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ConfigValidationResult(errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HashSet<string> ValidateMatchers(
|
||||||
|
List<ConfigValidationError> errors,
|
||||||
|
IReadOnlyList<IComboMatcher> matchers)
|
||||||
|
{
|
||||||
|
var matcherTypes = new HashSet<string>();
|
||||||
|
foreach (var matcher in matchers ?? Array.Empty<IComboMatcher>())
|
||||||
|
{
|
||||||
|
if (matcher == null)
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("NullMatcher", "Matcher cannot be null."));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(matcher.MatcherType))
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("EmptyMatcherType", "Matcher type is required."));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matcherTypes.Add(matcher.MatcherType))
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("DuplicateMatcherType", "Duplicate matcher type: " + matcher.MatcherType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matcherTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HashSet<string> ValidateActionExecutors(
|
||||||
|
List<ConfigValidationError> errors,
|
||||||
|
IReadOnlyList<IRollActionExecutor> actionExecutors)
|
||||||
|
{
|
||||||
|
var actionTypes = new HashSet<string>();
|
||||||
|
foreach (var executor in actionExecutors ?? Array.Empty<IRollActionExecutor>())
|
||||||
|
{
|
||||||
|
if (executor == null)
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("NullActionExecutor", "Action executor cannot be null."));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(executor.ActionType))
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("EmptyActionExecutorType", "Action executor type is required."));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!actionTypes.Add(executor.ActionType))
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("DuplicateActionExecutorType", "Duplicate action executor type: " + executor.ActionType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return actionTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ValidateActionParams(
|
||||||
|
List<ConfigValidationError> errors,
|
||||||
|
IReadOnlyList<ActionParamDefinition> actionParams)
|
||||||
|
{
|
||||||
|
var paramKeys = new HashSet<string>();
|
||||||
|
foreach (var param in actionParams ?? Array.Empty<ActionParamDefinition>())
|
||||||
|
{
|
||||||
|
if (param == null)
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("NullActionParam", "Action param cannot be null."));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(param.Key))
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("EmptyActionParamKey", "Action param key is required."));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!paramKeys.Add(param.Key))
|
||||||
|
{
|
||||||
|
errors.Add(new ConfigValidationError("DuplicateActionParamKey", "Duplicate action param key: " + param.Key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ConfigValidationResult
|
||||||
|
{
|
||||||
|
public ConfigValidationResult(IReadOnlyList<ConfigValidationError> errors)
|
||||||
|
{
|
||||||
|
Errors = errors == null ? Array.Empty<ConfigValidationError>() : errors.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<ConfigValidationError> Errors { get; }
|
||||||
|
|
||||||
|
public bool IsValid => Errors.Count == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ConfigValidationError
|
||||||
|
{
|
||||||
|
public ConfigValidationError(string code, string message)
|
||||||
|
{
|
||||||
|
Code = code;
|
||||||
|
Message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Code { get; }
|
||||||
|
|
||||||
|
public string Message { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 31bec0fb5e6c45ddadf3a0039a287423
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,238 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class LuckyDiceDefaultConfig
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, DiceSetConfig> _diceSets;
|
||||||
|
|
||||||
|
private LuckyDiceDefaultConfig(
|
||||||
|
Dictionary<string, DiceSetConfig> diceSets,
|
||||||
|
IReadOnlyList<ComboRule> comboRules,
|
||||||
|
IReadOnlyList<LuckyDiceCandidateConfig> luckyDiceCandidates,
|
||||||
|
IReadOnlyList<TargetModeConfig> targetModeDefinitions)
|
||||||
|
{
|
||||||
|
_diceSets = diceSets;
|
||||||
|
ComboRules = comboRules;
|
||||||
|
LuckyDiceCandidates = luckyDiceCandidates;
|
||||||
|
TargetModeDefinitions = targetModeDefinitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<ComboRule> ComboRules { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<LuckyDiceCandidateConfig> LuckyDiceCandidates { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<TargetModeConfig> TargetModeDefinitions { get; }
|
||||||
|
|
||||||
|
public DiceSetConfig GetDiceSet(string diceSetId)
|
||||||
|
{
|
||||||
|
DiceSetConfig diceSet;
|
||||||
|
if (!_diceSets.TryGetValue(diceSetId, out diceSet))
|
||||||
|
{
|
||||||
|
throw new InvalidDiceSetException(diceSetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return diceSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LuckyDiceDefaultConfig Create()
|
||||||
|
{
|
||||||
|
return CreateValidated(LuckyDiceConfigValidationContext.CreateDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LuckyDiceDefaultConfig CreateValidated(LuckyDiceConfigValidationContext validationContext)
|
||||||
|
{
|
||||||
|
return CreateValidated(
|
||||||
|
DefaultComboRuleDefinitions,
|
||||||
|
DefaultLuckyDiceCandidateDefinitions,
|
||||||
|
DefaultTargetModeDefinitions,
|
||||||
|
validationContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LuckyDiceDefaultConfig CreateValidated(
|
||||||
|
IReadOnlyList<ComboRuleDefinition> ruleDefinitions,
|
||||||
|
IReadOnlyList<LuckyDiceCandidateDefinition> candidateDefinitions,
|
||||||
|
IReadOnlyList<TargetModeDefinition> targetModeDefinitions)
|
||||||
|
{
|
||||||
|
return CreateValidated(
|
||||||
|
ruleDefinitions,
|
||||||
|
candidateDefinitions,
|
||||||
|
targetModeDefinitions,
|
||||||
|
LuckyDiceConfigValidationContext.CreateDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LuckyDiceDefaultConfig CreateValidated(
|
||||||
|
IReadOnlyList<ComboRuleDefinition> ruleDefinitions,
|
||||||
|
IReadOnlyList<LuckyDiceCandidateDefinition> candidateDefinitions,
|
||||||
|
IReadOnlyList<TargetModeDefinition> targetModeDefinitions,
|
||||||
|
LuckyDiceConfigValidationContext validationContext)
|
||||||
|
{
|
||||||
|
validationContext = validationContext ?? LuckyDiceConfigValidationContext.CreateDefault();
|
||||||
|
var validation = LuckyDiceConfigValidator.Validate(
|
||||||
|
ruleDefinitions,
|
||||||
|
candidateDefinitions,
|
||||||
|
targetModeDefinitions,
|
||||||
|
validationContext.Matchers,
|
||||||
|
validationContext.ActionExecutors);
|
||||||
|
if (!validation.IsValid)
|
||||||
|
{
|
||||||
|
throw new InvalidLuckyDiceConfigException(validation);
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalSet = new DiceSetConfig(
|
||||||
|
LuckyDiceKeys.NormalDiceSetId,
|
||||||
|
DiceSetType.Normal,
|
||||||
|
diceCount: 2,
|
||||||
|
faces: new[]
|
||||||
|
{
|
||||||
|
DiceFace.NormalNumber(2),
|
||||||
|
DiceFace.NormalNumber(3),
|
||||||
|
DiceFace.NormalNumber(4),
|
||||||
|
DiceFace.NormalNumber(5),
|
||||||
|
DiceFace.NormalNumber(6),
|
||||||
|
DiceFace.NormalClover()
|
||||||
|
},
|
||||||
|
defaultResultSlotCount: null);
|
||||||
|
|
||||||
|
var luckySet = new DiceSetConfig(
|
||||||
|
LuckyDiceKeys.LuckyDiceSetId,
|
||||||
|
DiceSetType.Lucky,
|
||||||
|
diceCount: 1,
|
||||||
|
faces: new[]
|
||||||
|
{
|
||||||
|
DiceFace.LuckyResult(LuckyDiceKeys.RocketResultKey),
|
||||||
|
DiceFace.LuckyResult(LuckyDiceKeys.ThiefResultKey)
|
||||||
|
},
|
||||||
|
defaultResultSlotCount: 3);
|
||||||
|
|
||||||
|
return new LuckyDiceDefaultConfig(
|
||||||
|
new Dictionary<string, DiceSetConfig>
|
||||||
|
{
|
||||||
|
{ normalSet.DiceSetId, normalSet },
|
||||||
|
{ luckySet.DiceSetId, luckySet }
|
||||||
|
},
|
||||||
|
ComboRuleDefinitionLoader.Load(ruleDefinitions),
|
||||||
|
ToCandidateConfigs(candidateDefinitions),
|
||||||
|
ToTargetModeConfigs(targetModeDefinitions));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LuckyDiceCandidateConfig[] ToCandidateConfigs(IReadOnlyList<LuckyDiceCandidateDefinition> definitions)
|
||||||
|
{
|
||||||
|
var result = new LuckyDiceCandidateConfig[definitions.Count];
|
||||||
|
for (var i = 0; i < definitions.Count; i++)
|
||||||
|
{
|
||||||
|
var definition = definitions[i];
|
||||||
|
result[i] = new LuckyDiceCandidateConfig(
|
||||||
|
definition.ResultKey,
|
||||||
|
definition.TargetKey,
|
||||||
|
definition.ResultSlotCount,
|
||||||
|
definition.Weight,
|
||||||
|
definition.Enabled,
|
||||||
|
definition.MinLevel,
|
||||||
|
definition.SourceFilter,
|
||||||
|
definition.IsDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TargetModeConfig[] ToTargetModeConfigs(IReadOnlyList<TargetModeDefinition> definitions)
|
||||||
|
{
|
||||||
|
var result = new TargetModeConfig[definitions.Count];
|
||||||
|
for (var i = 0; i < definitions.Count; i++)
|
||||||
|
{
|
||||||
|
var definition = definitions[i];
|
||||||
|
result[i] = new TargetModeConfig(definition.ResultKey, definition.TargetKey, definition.ModeKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly ComboRuleDefinition[] DefaultComboRuleDefinitions =
|
||||||
|
{
|
||||||
|
new ComboRuleDefinition
|
||||||
|
{
|
||||||
|
RuleId = "normal_clover_clover_lucky_dice",
|
||||||
|
Priority = 100,
|
||||||
|
MatcherType = LuckyDiceKeys.ExactComboMatcher,
|
||||||
|
ComboKey = "clover_clover",
|
||||||
|
Actions = new[]
|
||||||
|
{
|
||||||
|
new RollActionDefinition { ActionType = LuckyDiceKeys.TriggerLuckyDiceAction }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new ComboRuleDefinition
|
||||||
|
{
|
||||||
|
RuleId = "normal_number_clover_bonus",
|
||||||
|
Priority = 50,
|
||||||
|
MatcherType = LuckyDiceKeys.ContainsFaceMatcher,
|
||||||
|
FaceKey = LuckyDiceKeys.CloverFaceKey,
|
||||||
|
RequiredCount = 1,
|
||||||
|
Actions = new[]
|
||||||
|
{
|
||||||
|
new RollActionDefinition { ActionType = LuckyDiceKeys.GrantRewardAction },
|
||||||
|
new RollActionDefinition
|
||||||
|
{
|
||||||
|
ActionType = LuckyDiceKeys.AddMultiplierAction,
|
||||||
|
Params = new[]
|
||||||
|
{
|
||||||
|
new ActionParamDefinition { Key = "reason", Value = "clover_bonus" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new ComboRuleDefinition
|
||||||
|
{
|
||||||
|
RuleId = "normal_all_numbers_reward",
|
||||||
|
Priority = 10,
|
||||||
|
MatcherType = LuckyDiceKeys.AllNumbersMatcher,
|
||||||
|
Actions = new[]
|
||||||
|
{
|
||||||
|
new RollActionDefinition { ActionType = LuckyDiceKeys.GrantRewardAction }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly LuckyDiceCandidateDefinition[] DefaultLuckyDiceCandidateDefinitions =
|
||||||
|
{
|
||||||
|
new LuckyDiceCandidateDefinition
|
||||||
|
{
|
||||||
|
ResultKey = LuckyDiceKeys.RocketResultKey,
|
||||||
|
TargetKey = "slap_down",
|
||||||
|
ResultSlotCount = 3,
|
||||||
|
Weight = 1,
|
||||||
|
Enabled = true,
|
||||||
|
MinLevel = 0,
|
||||||
|
SourceFilter = new[] { LuckyDiceKeys.NormalRollTriggerSource },
|
||||||
|
IsDefault = true
|
||||||
|
},
|
||||||
|
new LuckyDiceCandidateDefinition
|
||||||
|
{
|
||||||
|
ResultKey = LuckyDiceKeys.ThiefResultKey,
|
||||||
|
TargetKey = "treasure_heist",
|
||||||
|
ResultSlotCount = 3,
|
||||||
|
Weight = 1,
|
||||||
|
Enabled = true,
|
||||||
|
MinLevel = 0,
|
||||||
|
SourceFilter = new[] { LuckyDiceKeys.NormalRollTriggerSource },
|
||||||
|
IsDefault = false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly TargetModeDefinition[] DefaultTargetModeDefinitions =
|
||||||
|
{
|
||||||
|
new TargetModeDefinition
|
||||||
|
{
|
||||||
|
ResultKey = LuckyDiceKeys.RocketResultKey,
|
||||||
|
TargetKey = "slap_down",
|
||||||
|
ModeKey = "slap_down_normal"
|
||||||
|
},
|
||||||
|
new TargetModeDefinition
|
||||||
|
{
|
||||||
|
ResultKey = LuckyDiceKeys.ThiefResultKey,
|
||||||
|
TargetKey = "treasure_heist",
|
||||||
|
ModeKey = "treasure_heist_normal"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2872fbab847848f9bf968e717a4acd70
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public static class LuckyDiceKeys
|
||||||
|
{
|
||||||
|
public const string NormalDiceSetId = "normal_main";
|
||||||
|
public const string LuckyDiceSetId = "lucky_dice";
|
||||||
|
|
||||||
|
public const string CloverFaceKey = "clover";
|
||||||
|
public const string RocketResultKey = "rocket";
|
||||||
|
public const string ThiefResultKey = "thief";
|
||||||
|
|
||||||
|
public const string NormalRollTriggerSource = "normal_roll";
|
||||||
|
|
||||||
|
public const string ExactComboMatcher = "ExactCombo";
|
||||||
|
public const string AllNumbersMatcher = "AllNumbers";
|
||||||
|
public const string ContainsFaceMatcher = "ContainsFace";
|
||||||
|
public const string FaceCountMatcher = "FaceCount";
|
||||||
|
public const string FaceCountRangeMatcher = "FaceCountRange";
|
||||||
|
public const string DiceCountMatcher = "DiceCount";
|
||||||
|
public const string NumberPairMatcher = "NumberPair";
|
||||||
|
public const string NumberOfAKindMatcher = "NumberOfAKind";
|
||||||
|
public const string NumberSumRangeMatcher = "NumberSumRange";
|
||||||
|
|
||||||
|
public const string GrantRewardAction = "grant_reward";
|
||||||
|
public const string AddMultiplierAction = "add_multiplier";
|
||||||
|
public const string TriggerLuckyDiceAction = "trigger_lucky_dice";
|
||||||
|
public const string EnterModeAction = "enter_mode";
|
||||||
|
public const string ShowPopupAction = "show_popup";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3a0439dddf734a14a1f49eeae48e349b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
9
FishDice/Assets/FishDice/Runtime/LuckyDice/CoreFlow.meta
Normal file
9
FishDice/Assets/FishDice/Runtime/LuckyDice/CoreFlow.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2867533dcede4ee9b8222a2b4d63deab
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class LuckyDiceRollFlowService
|
||||||
|
{
|
||||||
|
private readonly LuckyDiceDefaultConfig _config;
|
||||||
|
private readonly DiceRollService _rollService;
|
||||||
|
private readonly ComboRuleMatcher _ruleMatcher;
|
||||||
|
private readonly RollActionDispatcher _actionDispatcher;
|
||||||
|
private readonly LuckyDiceFlowService _luckyDiceFlowService;
|
||||||
|
private readonly TargetModeResolver _targetModeResolver;
|
||||||
|
|
||||||
|
public LuckyDiceRollFlowService(
|
||||||
|
LuckyDiceDefaultConfig config,
|
||||||
|
DiceRollService rollService,
|
||||||
|
ComboRuleMatcher ruleMatcher,
|
||||||
|
RollActionDispatcher actionDispatcher,
|
||||||
|
LuckyDiceFlowService luckyDiceFlowService,
|
||||||
|
TargetModeResolver targetModeResolver)
|
||||||
|
{
|
||||||
|
_config = config ?? throw new ArgumentNullException(nameof(config));
|
||||||
|
_rollService = rollService ?? throw new ArgumentNullException(nameof(rollService));
|
||||||
|
_ruleMatcher = ruleMatcher ?? throw new ArgumentNullException(nameof(ruleMatcher));
|
||||||
|
_actionDispatcher = actionDispatcher ?? throw new ArgumentNullException(nameof(actionDispatcher));
|
||||||
|
_luckyDiceFlowService = luckyDiceFlowService ?? throw new ArgumentNullException(nameof(luckyDiceFlowService));
|
||||||
|
_targetModeResolver = targetModeResolver ?? throw new ArgumentNullException(nameof(targetModeResolver));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RollFlowResult Execute(RollFlowRequest request)
|
||||||
|
{
|
||||||
|
if (request == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(request.RollSessionId))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Roll session id is required.", nameof(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(request.DiceSetId))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Dice set id is required.", nameof(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(request.TriggerSource))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Trigger source is required.", nameof(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
var trace = new RollTrace(request.RollSessionId);
|
||||||
|
var rollResult = _rollService.Roll(
|
||||||
|
_config.GetDiceSet(request.DiceSetId),
|
||||||
|
request.RollSessionId,
|
||||||
|
request.TriggerSource);
|
||||||
|
var facts = ComboFactBuilder.Build(rollResult);
|
||||||
|
trace.RecordComboFacts(facts);
|
||||||
|
|
||||||
|
var match = _ruleMatcher.Match(facts, _config.ComboRules);
|
||||||
|
trace.RecordRuleMatch(match);
|
||||||
|
|
||||||
|
var actionContext = new RollActionContext(request.RollSessionId, facts.NormalizedKey, trace);
|
||||||
|
var actionResults = _actionDispatcher.Execute(
|
||||||
|
actionContext,
|
||||||
|
match.Matches.SelectMany(rule => rule.Actions).ToArray());
|
||||||
|
var effects = actionResults.SelectMany(result => result.Effects).ToList();
|
||||||
|
|
||||||
|
LuckyDiceSelectionResult luckyDiceResult = null;
|
||||||
|
TargetModeEntry targetMode = null;
|
||||||
|
if (effects.OfType<LuckyDiceRequestedEffect>().Any())
|
||||||
|
{
|
||||||
|
luckyDiceResult = _luckyDiceFlowService.Select(
|
||||||
|
_config.LuckyDiceCandidates,
|
||||||
|
request,
|
||||||
|
_config.GetDiceSet(LuckyDiceKeys.LuckyDiceSetId));
|
||||||
|
trace.RecordLuckyDiceSelection(luckyDiceResult);
|
||||||
|
|
||||||
|
var targetModeResult = _targetModeResolver.Resolve(
|
||||||
|
luckyDiceResult.Selected,
|
||||||
|
request,
|
||||||
|
rollResult.Multiplier,
|
||||||
|
_config.TargetModeDefinitions);
|
||||||
|
|
||||||
|
if (targetModeResult.Success)
|
||||||
|
{
|
||||||
|
targetMode = targetModeResult.Entry;
|
||||||
|
trace.RecordTargetMode(targetMode);
|
||||||
|
effects.Add(new EnterTargetModeEffect(targetMode.TargetKey, targetMode.ModeKey));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trace.RecordTargetModeFailure(targetModeResult.FailureReason);
|
||||||
|
effects.Add(new TargetModeFailedEffect(targetModeResult.FailureReason, targetModeResult.FallbackMode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RollFlowResult(
|
||||||
|
request.RollSessionId,
|
||||||
|
rollResult,
|
||||||
|
facts,
|
||||||
|
match.Matches,
|
||||||
|
actionResults,
|
||||||
|
effects,
|
||||||
|
luckyDiceResult,
|
||||||
|
targetMode,
|
||||||
|
trace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1934c9ee9d2f40d897afde268a169778
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class LuckyDiceRuntimeFactory
|
||||||
|
{
|
||||||
|
private readonly LuckyDiceConfigValidationContext _context;
|
||||||
|
private readonly IRandomSource _random;
|
||||||
|
|
||||||
|
public LuckyDiceRuntimeFactory(
|
||||||
|
LuckyDiceConfigValidationContext context,
|
||||||
|
IRandomSource random)
|
||||||
|
{
|
||||||
|
_context = context ?? LuckyDiceConfigValidationContext.CreateDefault();
|
||||||
|
_random = random ?? throw new ArgumentNullException(nameof(random));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LuckyDiceRuntimeFactory CreateDefault(IRandomSource random)
|
||||||
|
{
|
||||||
|
return new LuckyDiceRuntimeFactory(LuckyDiceConfigValidationContext.CreateDefault(), random);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LuckyDiceRollFlowService CreateDefaultFlow()
|
||||||
|
{
|
||||||
|
return CreateFlow(LuckyDiceDefaultConfig.CreateValidated(_context));
|
||||||
|
}
|
||||||
|
|
||||||
|
public LuckyDiceRollFlowService CreateFlow(
|
||||||
|
IReadOnlyList<ComboRuleDefinition> ruleDefinitions,
|
||||||
|
IReadOnlyList<LuckyDiceCandidateDefinition> candidateDefinitions,
|
||||||
|
IReadOnlyList<TargetModeDefinition> targetModeDefinitions)
|
||||||
|
{
|
||||||
|
var config = LuckyDiceDefaultConfig.CreateValidated(
|
||||||
|
ruleDefinitions,
|
||||||
|
candidateDefinitions,
|
||||||
|
targetModeDefinitions,
|
||||||
|
_context);
|
||||||
|
|
||||||
|
return CreateFlow(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LuckyDiceRollFlowService CreateFlow(LuckyDiceDefaultConfig config)
|
||||||
|
{
|
||||||
|
if (config == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LuckyDiceRollFlowService(
|
||||||
|
config,
|
||||||
|
new DiceRollService(_random),
|
||||||
|
new ComboRuleMatcher(_context.Matchers),
|
||||||
|
new RollActionDispatcher(_context.ActionExecutors),
|
||||||
|
new LuckyDiceFlowService(_random),
|
||||||
|
new TargetModeResolver());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 19d0cc9a1e284be7a23c2ee1c6b7bd21
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
9
FishDice/Assets/FishDice/Runtime/LuckyDice/Data.meta
Normal file
9
FishDice/Assets/FishDice/Runtime/LuckyDice/Data.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1ff1f71d729f48438f1a81077076ca5a
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class ComboFacts
|
||||||
|
{
|
||||||
|
public ComboFacts(
|
||||||
|
string rollSessionId,
|
||||||
|
DiceSetType diceSetType,
|
||||||
|
int diceCount,
|
||||||
|
IReadOnlyList<DiceFace> faces,
|
||||||
|
IReadOnlyDictionary<string, int> faceCounts,
|
||||||
|
int numberSum,
|
||||||
|
IReadOnlyList<int> numberValues,
|
||||||
|
int cloverCount,
|
||||||
|
string normalizedKey,
|
||||||
|
string orderedKey)
|
||||||
|
{
|
||||||
|
RollSessionId = rollSessionId;
|
||||||
|
DiceSetType = diceSetType;
|
||||||
|
DiceCount = diceCount;
|
||||||
|
Faces = faces.ToArray();
|
||||||
|
FaceCounts = faceCounts.ToDictionary(pair => pair.Key, pair => pair.Value);
|
||||||
|
NumberSum = numberSum;
|
||||||
|
NumberValues = numberValues.ToArray();
|
||||||
|
CloverCount = cloverCount;
|
||||||
|
HasClover = cloverCount > 0;
|
||||||
|
NormalizedKey = normalizedKey;
|
||||||
|
OrderedKey = orderedKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string RollSessionId { get; }
|
||||||
|
|
||||||
|
public DiceSetType DiceSetType { get; }
|
||||||
|
|
||||||
|
public int DiceCount { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<DiceFace> Faces { get; }
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, int> FaceCounts { get; }
|
||||||
|
|
||||||
|
public int NumberSum { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<int> NumberValues { get; }
|
||||||
|
|
||||||
|
public int CloverCount { get; }
|
||||||
|
|
||||||
|
public bool HasClover { get; }
|
||||||
|
|
||||||
|
public string NormalizedKey { get; }
|
||||||
|
|
||||||
|
public string OrderedKey { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 74faceaa58a144008abe8f08f3b3db27
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class ComboMatcherSpec
|
||||||
|
{
|
||||||
|
public ComboMatcherSpec(
|
||||||
|
string matcherType,
|
||||||
|
string comboKey = null,
|
||||||
|
string faceKey = null,
|
||||||
|
int? requiredCount = null,
|
||||||
|
int? minCount = null,
|
||||||
|
int? maxCount = null,
|
||||||
|
int? diceCount = null,
|
||||||
|
int? ofAKindCount = null,
|
||||||
|
int? minSum = null,
|
||||||
|
int? maxSum = null,
|
||||||
|
bool useOrderedKey = false)
|
||||||
|
{
|
||||||
|
MatcherType = matcherType ?? throw new ArgumentNullException(nameof(matcherType));
|
||||||
|
ComboKey = comboKey;
|
||||||
|
FaceKey = faceKey;
|
||||||
|
RequiredCount = requiredCount;
|
||||||
|
MinCount = minCount;
|
||||||
|
MaxCount = maxCount;
|
||||||
|
DiceCount = diceCount;
|
||||||
|
OfAKindCount = ofAKindCount;
|
||||||
|
MinSum = minSum;
|
||||||
|
MaxSum = maxSum;
|
||||||
|
UseOrderedKey = useOrderedKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string MatcherType { get; }
|
||||||
|
|
||||||
|
public string ComboKey { get; }
|
||||||
|
|
||||||
|
public string FaceKey { get; }
|
||||||
|
|
||||||
|
public int? RequiredCount { get; }
|
||||||
|
|
||||||
|
public int? MinCount { get; }
|
||||||
|
|
||||||
|
public int? MaxCount { get; }
|
||||||
|
|
||||||
|
public int? DiceCount { get; }
|
||||||
|
|
||||||
|
public int? OfAKindCount { get; }
|
||||||
|
|
||||||
|
public int? MinSum { get; }
|
||||||
|
|
||||||
|
public int? MaxSum { get; }
|
||||||
|
|
||||||
|
public bool UseOrderedKey { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 11a380073489461c997f9ce85c45d607
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
33
FishDice/Assets/FishDice/Runtime/LuckyDice/Data/ComboRule.cs
Normal file
33
FishDice/Assets/FishDice/Runtime/LuckyDice/Data/ComboRule.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class ComboRule
|
||||||
|
{
|
||||||
|
public ComboRule(
|
||||||
|
string ruleId,
|
||||||
|
int priority,
|
||||||
|
ComboMatcherSpec matcher,
|
||||||
|
IReadOnlyList<RollActionSpec> actions,
|
||||||
|
bool stopAfterMatched)
|
||||||
|
{
|
||||||
|
RuleId = ruleId ?? throw new ArgumentNullException(nameof(ruleId));
|
||||||
|
Priority = priority;
|
||||||
|
Matcher = matcher ?? throw new ArgumentNullException(nameof(matcher));
|
||||||
|
Actions = actions == null ? Array.Empty<RollActionSpec>() : actions.ToArray();
|
||||||
|
StopAfterMatched = stopAfterMatched;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string RuleId { get; }
|
||||||
|
|
||||||
|
public int Priority { get; }
|
||||||
|
|
||||||
|
public ComboMatcherSpec Matcher { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<RollActionSpec> Actions { get; }
|
||||||
|
|
||||||
|
public bool StopAfterMatched { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a94f5fa117fb4bada6467d3d7d0c31d2
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class ComboRuleMatch
|
||||||
|
{
|
||||||
|
public ComboRuleMatch(ComboRule rule)
|
||||||
|
{
|
||||||
|
RuleId = rule.RuleId;
|
||||||
|
Priority = rule.Priority;
|
||||||
|
MatcherType = rule.Matcher.MatcherType;
|
||||||
|
Actions = rule.Actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string RuleId { get; }
|
||||||
|
|
||||||
|
public int Priority { get; }
|
||||||
|
|
||||||
|
public string MatcherType { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<RollActionSpec> Actions { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ComboRuleMatchResult
|
||||||
|
{
|
||||||
|
public ComboRuleMatchResult(IReadOnlyList<ComboRuleMatch> matches)
|
||||||
|
{
|
||||||
|
Matches = matches == null ? Array.Empty<ComboRuleMatch>() : matches.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<ComboRuleMatch> Matches { get; }
|
||||||
|
|
||||||
|
public bool HasMatch => Matches.Count > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d9df28fce97b4d4fa5cf29a5b6f38d23
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
35
FishDice/Assets/FishDice/Runtime/LuckyDice/Data/DiceFace.cs
Normal file
35
FishDice/Assets/FishDice/Runtime/LuckyDice/Data/DiceFace.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public readonly struct DiceFace
|
||||||
|
{
|
||||||
|
public DiceFace(DiceSetType setType, string key, int? numberValue)
|
||||||
|
{
|
||||||
|
SetType = setType;
|
||||||
|
Key = key ?? throw new ArgumentNullException(nameof(key));
|
||||||
|
NumberValue = numberValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiceSetType SetType { get; }
|
||||||
|
|
||||||
|
public string Key { get; }
|
||||||
|
|
||||||
|
public int? NumberValue { get; }
|
||||||
|
|
||||||
|
public static DiceFace NormalNumber(int value)
|
||||||
|
{
|
||||||
|
return new DiceFace(DiceSetType.Normal, value.ToString(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DiceFace NormalClover()
|
||||||
|
{
|
||||||
|
return new DiceFace(DiceSetType.Normal, LuckyDiceKeys.CloverFaceKey, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DiceFace LuckyResult(string key)
|
||||||
|
{
|
||||||
|
return new DiceFace(DiceSetType.Lucky, key, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9175dd08e215408faa01a3ee1cc86a13
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class DiceRollResult
|
||||||
|
{
|
||||||
|
private DiceRollResult(
|
||||||
|
string rollSessionId,
|
||||||
|
string diceSetId,
|
||||||
|
DiceSetType setType,
|
||||||
|
int diceCount,
|
||||||
|
IReadOnlyList<DiceFace> faces,
|
||||||
|
int multiplier,
|
||||||
|
string triggerSource)
|
||||||
|
{
|
||||||
|
RollSessionId = rollSessionId;
|
||||||
|
DiceSetId = diceSetId;
|
||||||
|
SetType = setType;
|
||||||
|
DiceCount = diceCount;
|
||||||
|
Faces = faces.ToArray();
|
||||||
|
Multiplier = multiplier;
|
||||||
|
TriggerSource = triggerSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string RollSessionId { get; }
|
||||||
|
|
||||||
|
public string DiceSetId { get; }
|
||||||
|
|
||||||
|
public DiceSetType SetType { get; }
|
||||||
|
|
||||||
|
public int DiceCount { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<DiceFace> Faces { get; }
|
||||||
|
|
||||||
|
public int Multiplier { get; }
|
||||||
|
|
||||||
|
public string TriggerSource { get; }
|
||||||
|
|
||||||
|
public static DiceRollResult Create(
|
||||||
|
string rollSessionId,
|
||||||
|
string diceSetId,
|
||||||
|
DiceSetType setType,
|
||||||
|
IReadOnlyList<DiceFace> faces,
|
||||||
|
int multiplier,
|
||||||
|
string triggerSource)
|
||||||
|
{
|
||||||
|
if (faces == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(faces));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DiceRollResult(
|
||||||
|
rollSessionId,
|
||||||
|
diceSetId,
|
||||||
|
setType,
|
||||||
|
faces.Count,
|
||||||
|
faces,
|
||||||
|
multiplier,
|
||||||
|
triggerSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DiceRollResult FromConfig(
|
||||||
|
string rollSessionId,
|
||||||
|
DiceSetConfig config,
|
||||||
|
IReadOnlyList<DiceFace> faces,
|
||||||
|
int multiplier,
|
||||||
|
string triggerSource)
|
||||||
|
{
|
||||||
|
return new DiceRollResult(
|
||||||
|
rollSessionId,
|
||||||
|
config.DiceSetId,
|
||||||
|
config.SetType,
|
||||||
|
config.DiceCount,
|
||||||
|
faces,
|
||||||
|
multiplier,
|
||||||
|
triggerSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 839ecc30eed6498d93ce34d9ffe847eb
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class DiceSetConfig
|
||||||
|
{
|
||||||
|
public DiceSetConfig(
|
||||||
|
string diceSetId,
|
||||||
|
DiceSetType setType,
|
||||||
|
int diceCount,
|
||||||
|
IReadOnlyList<DiceFace> faces,
|
||||||
|
int? defaultResultSlotCount)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(diceSetId))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Dice set id is required.", nameof(diceSetId));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diceCount <= 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(diceCount), "Dice count must be greater than zero.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (faces == null || faces.Count == 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("At least one face is required.", nameof(faces));
|
||||||
|
}
|
||||||
|
|
||||||
|
DiceSetId = diceSetId;
|
||||||
|
SetType = setType;
|
||||||
|
DiceCount = diceCount;
|
||||||
|
Faces = faces.ToArray();
|
||||||
|
DefaultResultSlotCount = defaultResultSlotCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DiceSetId { get; }
|
||||||
|
|
||||||
|
public DiceSetType SetType { get; }
|
||||||
|
|
||||||
|
public int DiceCount { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<DiceFace> Faces { get; }
|
||||||
|
|
||||||
|
public int? DefaultResultSlotCount { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6c890293bb854609a2b916d1d35e6063
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public enum DiceSetType
|
||||||
|
{
|
||||||
|
Normal,
|
||||||
|
Lucky
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0147bf70503045fbb65baec35ba74936
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class LuckyDiceSelectedResult
|
||||||
|
{
|
||||||
|
internal LuckyDiceSelectedResult(string resultKey, string targetKey, int slotCount)
|
||||||
|
{
|
||||||
|
ResultKey = resultKey;
|
||||||
|
TargetKey = targetKey;
|
||||||
|
SlotCount = slotCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ResultKey { get; }
|
||||||
|
|
||||||
|
public string TargetKey { get; }
|
||||||
|
|
||||||
|
public int SlotCount { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class LuckyDiceSelectionResult
|
||||||
|
{
|
||||||
|
internal LuckyDiceSelectionResult(
|
||||||
|
LuckyDiceCandidateConfig selectedCandidate,
|
||||||
|
int initialCandidateCount,
|
||||||
|
int filteredCandidateCount,
|
||||||
|
int? randomValue,
|
||||||
|
string fallbackType,
|
||||||
|
int slotCount)
|
||||||
|
{
|
||||||
|
InitialCandidateCount = initialCandidateCount;
|
||||||
|
FilteredCandidateCount = filteredCandidateCount;
|
||||||
|
RandomValue = randomValue;
|
||||||
|
FallbackType = fallbackType;
|
||||||
|
SlotCount = selectedCandidate == null ? 0 : slotCount;
|
||||||
|
Selected = selectedCandidate == null
|
||||||
|
? null
|
||||||
|
: new LuckyDiceSelectedResult(selectedCandidate.ResultKey, selectedCandidate.TargetKey, SlotCount);
|
||||||
|
SlotResultKeys = selectedCandidate == null ? null : Enumerable.Repeat(selectedCandidate.ResultKey, SlotCount).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LuckyDiceSelectedResult Selected { get; }
|
||||||
|
|
||||||
|
public int InitialCandidateCount { get; }
|
||||||
|
|
||||||
|
public int FilteredCandidateCount { get; }
|
||||||
|
|
||||||
|
public int? RandomValue { get; }
|
||||||
|
|
||||||
|
public string FallbackType { get; }
|
||||||
|
|
||||||
|
public int SlotCount { get; }
|
||||||
|
|
||||||
|
public string[] SlotResultKeys { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TargetModeEntry
|
||||||
|
{
|
||||||
|
private TargetModeEntry(
|
||||||
|
string targetKey,
|
||||||
|
string modeKey,
|
||||||
|
string resultKey,
|
||||||
|
int multiplier,
|
||||||
|
string triggerSource,
|
||||||
|
string rollSessionId)
|
||||||
|
{
|
||||||
|
TargetKey = targetKey;
|
||||||
|
ModeKey = modeKey;
|
||||||
|
ResultKey = resultKey;
|
||||||
|
Multiplier = multiplier;
|
||||||
|
TriggerSource = triggerSource;
|
||||||
|
RollSessionId = rollSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TargetKey { get; }
|
||||||
|
|
||||||
|
public string ModeKey { get; }
|
||||||
|
|
||||||
|
public string ResultKey { get; }
|
||||||
|
|
||||||
|
public int Multiplier { get; }
|
||||||
|
|
||||||
|
public string TriggerSource { get; }
|
||||||
|
|
||||||
|
public string RollSessionId { get; }
|
||||||
|
|
||||||
|
internal static TargetModeEntry From(TargetModeConfig definition, RollFlowRequest request, int multiplier)
|
||||||
|
{
|
||||||
|
return new TargetModeEntry(
|
||||||
|
definition.TargetKey,
|
||||||
|
definition.ModeKey,
|
||||||
|
definition.ResultKey,
|
||||||
|
multiplier,
|
||||||
|
request.TriggerSource,
|
||||||
|
request.RollSessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TargetModeResolveResult
|
||||||
|
{
|
||||||
|
private TargetModeResolveResult(bool success, TargetModeEntry entry, string failureReason, string fallbackMode)
|
||||||
|
{
|
||||||
|
Success = success;
|
||||||
|
Entry = entry;
|
||||||
|
FailureReason = failureReason;
|
||||||
|
FallbackMode = fallbackMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Success { get; }
|
||||||
|
|
||||||
|
public TargetModeEntry Entry { get; }
|
||||||
|
|
||||||
|
public string FailureReason { get; }
|
||||||
|
|
||||||
|
public string FallbackMode { get; }
|
||||||
|
|
||||||
|
public static TargetModeResolveResult Resolved(TargetModeEntry entry)
|
||||||
|
{
|
||||||
|
return new TargetModeResolveResult(true, entry, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TargetModeResolveResult Failed(string failureReason, string fallbackMode = null)
|
||||||
|
{
|
||||||
|
return new TargetModeResolveResult(false, null, failureReason, fallbackMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2e21edd33e2d469d9736fa7ce87409f4
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class RollActionContext
|
||||||
|
{
|
||||||
|
public RollActionContext(string rollSessionId, string comboNormalizedKey, RollTrace trace)
|
||||||
|
{
|
||||||
|
RollSessionId = rollSessionId;
|
||||||
|
ComboNormalizedKey = comboNormalizedKey;
|
||||||
|
Trace = trace ?? throw new ArgumentNullException(nameof(trace));
|
||||||
|
}
|
||||||
|
|
||||||
|
public string RollSessionId { get; }
|
||||||
|
|
||||||
|
public string ComboNormalizedKey { get; }
|
||||||
|
|
||||||
|
public RollTrace Trace { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8b28add99b6540b19611b2e1ad429bae
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class RollActionResult
|
||||||
|
{
|
||||||
|
private RollActionResult(
|
||||||
|
string actionType,
|
||||||
|
bool success,
|
||||||
|
IReadOnlyList<RollEffect> effects,
|
||||||
|
string outcome,
|
||||||
|
string error)
|
||||||
|
{
|
||||||
|
ActionType = actionType;
|
||||||
|
Success = success;
|
||||||
|
Effects = effects == null ? Array.Empty<RollEffect>() : effects.ToArray();
|
||||||
|
Outcome = outcome;
|
||||||
|
Error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ActionType { get; }
|
||||||
|
|
||||||
|
public bool Success { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<RollEffect> Effects { get; }
|
||||||
|
|
||||||
|
public string Outcome { get; }
|
||||||
|
|
||||||
|
public string Error { get; }
|
||||||
|
|
||||||
|
public static RollActionResult Succeeded(string actionType, string outcome)
|
||||||
|
{
|
||||||
|
return new RollActionResult(actionType, true, Array.Empty<RollEffect>(), outcome, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RollActionResult Succeeded(string actionType, IReadOnlyList<RollEffect> effects)
|
||||||
|
{
|
||||||
|
var outcome = effects == null || effects.Count == 0 ? null : effects[effects.Count - 1].EffectType;
|
||||||
|
return new RollActionResult(actionType, true, effects, outcome, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RollActionResult Failure(string actionType, string error)
|
||||||
|
{
|
||||||
|
return new RollActionResult(actionType, false, Array.Empty<RollEffect>(), null, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 218cb0b26b39475fb0b5eff1b7f939fd
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class RollActionSpec
|
||||||
|
{
|
||||||
|
public RollActionSpec(string actionType)
|
||||||
|
: this(actionType, null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public RollActionSpec(string actionType, IReadOnlyDictionary<string, string> parameters)
|
||||||
|
{
|
||||||
|
ActionType = actionType ?? throw new ArgumentNullException(nameof(actionType));
|
||||||
|
Params = parameters == null
|
||||||
|
? new Dictionary<string, string>()
|
||||||
|
: parameters.ToDictionary(pair => pair.Key, pair => pair.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ActionType { get; }
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, string> Params { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6f8ccfed08184b1b9f553c455e747c10
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public abstract class RollEffect
|
||||||
|
{
|
||||||
|
protected RollEffect(string effectType)
|
||||||
|
{
|
||||||
|
EffectType = effectType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string EffectType { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class RewardEffect : RollEffect
|
||||||
|
{
|
||||||
|
public RewardEffect()
|
||||||
|
: base("Reward")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class MultiplierEffect : RollEffect
|
||||||
|
{
|
||||||
|
public MultiplierEffect(string reason)
|
||||||
|
: base("Multiplier")
|
||||||
|
{
|
||||||
|
Reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Reason { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class LuckyDiceRequestedEffect : RollEffect
|
||||||
|
{
|
||||||
|
public LuckyDiceRequestedEffect()
|
||||||
|
: base("LuckyDiceRequested")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class EnterTargetModeEffect : RollEffect
|
||||||
|
{
|
||||||
|
public EnterTargetModeEffect(string targetKey, string modeKey)
|
||||||
|
: base("EnterTargetMode")
|
||||||
|
{
|
||||||
|
TargetKey = targetKey;
|
||||||
|
ModeKey = modeKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TargetKey { get; }
|
||||||
|
|
||||||
|
public string ModeKey { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TargetModeFailedEffect : RollEffect
|
||||||
|
{
|
||||||
|
public TargetModeFailedEffect(string failureReason, string fallbackMode)
|
||||||
|
: base("TargetModeFailed")
|
||||||
|
{
|
||||||
|
FailureReason = failureReason;
|
||||||
|
FallbackMode = fallbackMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FailureReason { get; }
|
||||||
|
|
||||||
|
public string FallbackMode { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5524489d53e74a4c88a00bba1de976b9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class RollFlowRequest
|
||||||
|
{
|
||||||
|
public RollFlowRequest(string rollSessionId)
|
||||||
|
: this(
|
||||||
|
rollSessionId,
|
||||||
|
LuckyDiceKeys.NormalDiceSetId,
|
||||||
|
LuckyDiceKeys.NormalRollTriggerSource,
|
||||||
|
playerLevel: 0,
|
||||||
|
isTutorial: false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public RollFlowRequest(
|
||||||
|
string rollSessionId,
|
||||||
|
string diceSetId,
|
||||||
|
string triggerSource,
|
||||||
|
int playerLevel,
|
||||||
|
bool isTutorial)
|
||||||
|
{
|
||||||
|
RollSessionId = rollSessionId;
|
||||||
|
DiceSetId = diceSetId;
|
||||||
|
TriggerSource = triggerSource;
|
||||||
|
PlayerLevel = playerLevel;
|
||||||
|
IsTutorial = isTutorial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string RollSessionId { get; }
|
||||||
|
|
||||||
|
public string DiceSetId { get; }
|
||||||
|
|
||||||
|
public string TriggerSource { get; }
|
||||||
|
|
||||||
|
public int PlayerLevel { get; }
|
||||||
|
|
||||||
|
public bool IsTutorial { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2cb236f5ac214d77aae6c15c3c8429d0
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class RollFlowResult
|
||||||
|
{
|
||||||
|
public RollFlowResult(
|
||||||
|
string rollSessionId,
|
||||||
|
DiceRollResult rollResult,
|
||||||
|
ComboFacts comboFacts,
|
||||||
|
IReadOnlyList<ComboRuleMatch> matchedRules,
|
||||||
|
IReadOnlyList<RollActionResult> actionResults,
|
||||||
|
IReadOnlyList<RollEffect> effects,
|
||||||
|
LuckyDiceSelectionResult luckyDiceResult,
|
||||||
|
TargetModeEntry targetMode,
|
||||||
|
RollTrace trace)
|
||||||
|
{
|
||||||
|
RollSessionId = rollSessionId;
|
||||||
|
RollResult = rollResult;
|
||||||
|
ComboFacts = comboFacts;
|
||||||
|
MatchedRules = matchedRules == null ? Array.Empty<ComboRuleMatch>() : matchedRules.ToArray();
|
||||||
|
ActionResults = actionResults == null ? Array.Empty<RollActionResult>() : actionResults.ToArray();
|
||||||
|
Effects = effects == null ? Array.Empty<RollEffect>() : effects.ToArray();
|
||||||
|
LuckyDiceResult = luckyDiceResult;
|
||||||
|
TargetMode = targetMode;
|
||||||
|
Trace = trace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string RollSessionId { get; }
|
||||||
|
|
||||||
|
public DiceRollResult RollResult { get; }
|
||||||
|
|
||||||
|
public ComboFacts ComboFacts { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<ComboRuleMatch> MatchedRules { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<RollActionResult> ActionResults { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<RollEffect> Effects { get; }
|
||||||
|
|
||||||
|
public LuckyDiceSelectionResult LuckyDiceResult { get; }
|
||||||
|
|
||||||
|
public TargetModeEntry TargetMode { get; }
|
||||||
|
|
||||||
|
public RollTrace Trace { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ebb54a7e748c4b838f46a5d51e00e89f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 92ce07ad741540bbbaee1c4c8b349ad7
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class LuckyDiceFlowService
|
||||||
|
{
|
||||||
|
private readonly IRandomSource _random;
|
||||||
|
|
||||||
|
public LuckyDiceFlowService(IRandomSource random)
|
||||||
|
{
|
||||||
|
_random = random ?? throw new ArgumentNullException(nameof(random));
|
||||||
|
}
|
||||||
|
|
||||||
|
public LuckyDiceSelectionResult Select(
|
||||||
|
IReadOnlyList<LuckyDiceCandidateConfig> candidates,
|
||||||
|
RollFlowRequest request,
|
||||||
|
DiceSetConfig luckyDiceSetConfig)
|
||||||
|
{
|
||||||
|
var source = candidates ?? Array.Empty<LuckyDiceCandidateConfig>();
|
||||||
|
var filtered = source
|
||||||
|
.Where(candidate => candidate.Enabled)
|
||||||
|
.Where(candidate => request.PlayerLevel >= candidate.MinLevel)
|
||||||
|
.Where(candidate => candidate.SourceFilter == null ||
|
||||||
|
candidate.SourceFilter.Count == 0 ||
|
||||||
|
candidate.SourceFilter.Contains(request.TriggerSource))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
var fallbackType = "None";
|
||||||
|
int? randomValue = null;
|
||||||
|
var selected = PickWeighted(filtered, out randomValue);
|
||||||
|
|
||||||
|
if (selected == null)
|
||||||
|
{
|
||||||
|
selected = filtered.FirstOrDefault(candidate => candidate.IsDefault);
|
||||||
|
fallbackType = selected == null ? "NormalRewardFallback" : "DefaultResult";
|
||||||
|
}
|
||||||
|
|
||||||
|
var slotCount = selected == null ? 0 : selected.ResultSlotCount ?? luckyDiceSetConfig.DefaultResultSlotCount ?? 3;
|
||||||
|
|
||||||
|
return new LuckyDiceSelectionResult(
|
||||||
|
selected,
|
||||||
|
source.Count,
|
||||||
|
filtered.Length,
|
||||||
|
randomValue,
|
||||||
|
fallbackType,
|
||||||
|
slotCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LuckyDiceCandidateConfig PickWeighted(IReadOnlyList<LuckyDiceCandidateConfig> candidates, out int? randomValue)
|
||||||
|
{
|
||||||
|
var weighted = candidates.Where(candidate => candidate.Weight > 0).ToArray();
|
||||||
|
var totalWeight = weighted.Sum(candidate => candidate.Weight);
|
||||||
|
if (totalWeight <= 0)
|
||||||
|
{
|
||||||
|
randomValue = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var roll = _random.Next(0, totalWeight);
|
||||||
|
randomValue = roll;
|
||||||
|
var cursor = 0;
|
||||||
|
foreach (var candidate in weighted)
|
||||||
|
{
|
||||||
|
cursor += candidate.Weight;
|
||||||
|
if (roll < cursor)
|
||||||
|
{
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return weighted[weighted.Length - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e6956f55ed064f2d872f52a9c9eba554
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
9
FishDice/Assets/FishDice/Runtime/LuckyDice/Rules.meta
Normal file
9
FishDice/Assets/FishDice/Runtime/LuckyDice/Rules.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ac0d6f0f00124234809917e89e30eba3
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class ComboRuleMatcher
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyDictionary<string, IComboMatcher> _matchers;
|
||||||
|
|
||||||
|
public ComboRuleMatcher()
|
||||||
|
: this(ComboMatcherRegistry.CreateDefaultMatchers())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ComboRuleMatcher(IEnumerable<IComboMatcher> matchers)
|
||||||
|
{
|
||||||
|
if (matchers == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(matchers));
|
||||||
|
}
|
||||||
|
|
||||||
|
_matchers = matchers.ToDictionary(matcher => matcher.MatcherType, matcher => matcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ComboRuleMatchResult Match(ComboFacts facts, IReadOnlyList<ComboRule> rules)
|
||||||
|
{
|
||||||
|
if (facts == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(facts));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rules == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(rules));
|
||||||
|
}
|
||||||
|
|
||||||
|
var matches = new List<ComboRuleMatch>();
|
||||||
|
foreach (var rule in rules.OrderByDescending(rule => rule.Priority))
|
||||||
|
{
|
||||||
|
if (!MatchesRule(facts, rule.Matcher))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
matches.Add(new ComboRuleMatch(rule));
|
||||||
|
if (rule.StopAfterMatched)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ComboRuleMatchResult(matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MatchesRule(ComboFacts facts, ComboMatcherSpec matcher)
|
||||||
|
{
|
||||||
|
if (!_matchers.TryGetValue(matcher.MatcherType, out var registeredMatcher))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return registeredMatcher.IsMatch(facts, matcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5ffdaec353584cbf8006d846816af810
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f02fda27051d4ff8b44d459bd69c176b
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public static class ComboMatcherRegistry
|
||||||
|
{
|
||||||
|
public static IReadOnlyList<IComboMatcher> CreateDefaultMatchers()
|
||||||
|
{
|
||||||
|
return new IComboMatcher[]
|
||||||
|
{
|
||||||
|
new ExactComboMatcher(),
|
||||||
|
new AllNumbersMatcher(),
|
||||||
|
new ContainsFaceMatcher(),
|
||||||
|
new FaceCountMatcher(),
|
||||||
|
new FaceCountRangeMatcher(),
|
||||||
|
new DiceCountMatcher(),
|
||||||
|
new NumberPairMatcher(),
|
||||||
|
new NumberOfAKindMatcher(),
|
||||||
|
new NumberSumRangeMatcher()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 024047cd3f684a5a813d1abe30fc350a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
internal sealed class ExactComboMatcher : IComboMatcher
|
||||||
|
{
|
||||||
|
public string MatcherType => LuckyDiceKeys.ExactComboMatcher;
|
||||||
|
|
||||||
|
public bool IsMatch(ComboFacts facts, ComboMatcherSpec spec)
|
||||||
|
{
|
||||||
|
return string.Equals(
|
||||||
|
spec.UseOrderedKey ? facts.OrderedKey : facts.NormalizedKey,
|
||||||
|
spec.ComboKey,
|
||||||
|
StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class AllNumbersMatcher : IComboMatcher
|
||||||
|
{
|
||||||
|
public string MatcherType => LuckyDiceKeys.AllNumbersMatcher;
|
||||||
|
|
||||||
|
public bool IsMatch(ComboFacts facts, ComboMatcherSpec spec)
|
||||||
|
{
|
||||||
|
return facts.NumberValues.Count == facts.DiceCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class ContainsFaceMatcher : IComboMatcher
|
||||||
|
{
|
||||||
|
public string MatcherType => LuckyDiceKeys.ContainsFaceMatcher;
|
||||||
|
|
||||||
|
public bool IsMatch(ComboFacts facts, ComboMatcherSpec spec)
|
||||||
|
{
|
||||||
|
return ComboMatcherFacts.FaceCount(facts, spec.FaceKey) >= (spec.RequiredCount ?? 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class FaceCountMatcher : IComboMatcher
|
||||||
|
{
|
||||||
|
public string MatcherType => LuckyDiceKeys.FaceCountMatcher;
|
||||||
|
|
||||||
|
public bool IsMatch(ComboFacts facts, ComboMatcherSpec spec)
|
||||||
|
{
|
||||||
|
return ComboMatcherFacts.FaceCount(facts, spec.FaceKey) == (spec.RequiredCount ?? 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class FaceCountRangeMatcher : IComboMatcher
|
||||||
|
{
|
||||||
|
public string MatcherType => LuckyDiceKeys.FaceCountRangeMatcher;
|
||||||
|
|
||||||
|
public bool IsMatch(ComboFacts facts, ComboMatcherSpec spec)
|
||||||
|
{
|
||||||
|
return ComboMatcherFacts.IsInRange(
|
||||||
|
ComboMatcherFacts.FaceCount(facts, spec.FaceKey),
|
||||||
|
spec.MinCount,
|
||||||
|
spec.MaxCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class DiceCountMatcher : IComboMatcher
|
||||||
|
{
|
||||||
|
public string MatcherType => LuckyDiceKeys.DiceCountMatcher;
|
||||||
|
|
||||||
|
public bool IsMatch(ComboFacts facts, ComboMatcherSpec spec)
|
||||||
|
{
|
||||||
|
return !spec.DiceCount.HasValue || facts.DiceCount == spec.DiceCount.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class NumberPairMatcher : IComboMatcher
|
||||||
|
{
|
||||||
|
public string MatcherType => LuckyDiceKeys.NumberPairMatcher;
|
||||||
|
|
||||||
|
public bool IsMatch(ComboFacts facts, ComboMatcherSpec spec)
|
||||||
|
{
|
||||||
|
return facts.NumberValues.Count == 2 && facts.NumberValues[0] == facts.NumberValues[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class NumberOfAKindMatcher : IComboMatcher
|
||||||
|
{
|
||||||
|
public string MatcherType => LuckyDiceKeys.NumberOfAKindMatcher;
|
||||||
|
|
||||||
|
public bool IsMatch(ComboFacts facts, ComboMatcherSpec spec)
|
||||||
|
{
|
||||||
|
return facts.NumberValues
|
||||||
|
.GroupBy(value => value)
|
||||||
|
.Any(group => group.Count() >= (spec.OfAKindCount ?? 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class NumberSumRangeMatcher : IComboMatcher
|
||||||
|
{
|
||||||
|
public string MatcherType => LuckyDiceKeys.NumberSumRangeMatcher;
|
||||||
|
|
||||||
|
public bool IsMatch(ComboFacts facts, ComboMatcherSpec spec)
|
||||||
|
{
|
||||||
|
return ComboMatcherFacts.IsInRange(facts.NumberSum, spec.MinSum, spec.MaxSum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class ComboMatcherFacts
|
||||||
|
{
|
||||||
|
public static int FaceCount(ComboFacts facts, string faceKey)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(faceKey))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return facts.FaceCounts.TryGetValue(faceKey, out var count) ? count : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsInRange(int value, int? min, int? max)
|
||||||
|
{
|
||||||
|
if (min.HasValue && value < min.Value)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max.HasValue && value > max.Value)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 670344b8b7c74696baef9fe7a61ffc34
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public interface IComboMatcher
|
||||||
|
{
|
||||||
|
string MatcherType { get; }
|
||||||
|
|
||||||
|
bool IsMatch(ComboFacts facts, ComboMatcherSpec spec);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5252627bba5a463bb7006295c672a164
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
9
FishDice/Assets/FishDice/Runtime/LuckyDice/Services.meta
Normal file
9
FishDice/Assets/FishDice/Runtime/LuckyDice/Services.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 32a9a61586304026ad6aabe05b1316c2
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public static class ComboFactBuilder
|
||||||
|
{
|
||||||
|
public static ComboFacts Build(DiceRollResult result)
|
||||||
|
{
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.SetType != DiceSetType.Normal)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Only normal dice rolls can be converted to combo facts.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.DiceCount != result.Faces.Count)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Dice count does not match rolled face count.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.Faces.Any(face => face.SetType != DiceSetType.Normal))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Combo facts cannot include lucky dice result faces.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var faceCounts = result.Faces
|
||||||
|
.GroupBy(face => face.Key)
|
||||||
|
.ToDictionary(group => group.Key, group => group.Count());
|
||||||
|
var numberValues = result.Faces
|
||||||
|
.Where(face => face.NumberValue.HasValue)
|
||||||
|
.Select(face => face.NumberValue.Value)
|
||||||
|
.ToArray();
|
||||||
|
var cloverCount = faceCounts.TryGetValue(LuckyDiceKeys.CloverFaceKey, out var count) ? count : 0;
|
||||||
|
var orderedKey = string.Join("_", result.Faces.Select(face => face.Key));
|
||||||
|
var normalizedKey = string.Join("_", result.Faces.Select(face => face.Key).OrderBy(key => key, StringComparer.Ordinal));
|
||||||
|
|
||||||
|
return new ComboFacts(
|
||||||
|
result.RollSessionId,
|
||||||
|
result.SetType,
|
||||||
|
result.DiceCount,
|
||||||
|
result.Faces,
|
||||||
|
faceCounts,
|
||||||
|
numberValues.Sum(),
|
||||||
|
numberValues,
|
||||||
|
cloverCount,
|
||||||
|
normalizedKey,
|
||||||
|
orderedKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fb0a92fa21434dc6b216eaf039c8eb86
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public interface IRandomSource
|
||||||
|
{
|
||||||
|
int Next(int minInclusive, int maxExclusive);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class SystemRandomSource : IRandomSource
|
||||||
|
{
|
||||||
|
private readonly Random _random;
|
||||||
|
|
||||||
|
public SystemRandomSource()
|
||||||
|
: this(new Random())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public SystemRandomSource(Random random)
|
||||||
|
{
|
||||||
|
_random = random ?? throw new ArgumentNullException(nameof(random));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Next(int minInclusive, int maxExclusive)
|
||||||
|
{
|
||||||
|
return _random.Next(minInclusive, maxExclusive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class DiceRollService
|
||||||
|
{
|
||||||
|
private readonly IRandomSource _random;
|
||||||
|
|
||||||
|
public DiceRollService(IRandomSource random)
|
||||||
|
{
|
||||||
|
_random = random ?? throw new ArgumentNullException(nameof(random));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiceRollResult Roll(DiceSetConfig config, string rollSessionId, string triggerSource)
|
||||||
|
{
|
||||||
|
if (config == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
var rolledFaces = new List<DiceFace>(config.DiceCount);
|
||||||
|
for (var i = 0; i < config.DiceCount; i++)
|
||||||
|
{
|
||||||
|
var index = _random.Next(0, config.Faces.Count);
|
||||||
|
rolledFaces.Add(config.Faces[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DiceRollResult.FromConfig(
|
||||||
|
rollSessionId,
|
||||||
|
config,
|
||||||
|
rolledFaces,
|
||||||
|
multiplier: 1,
|
||||||
|
triggerSource: triggerSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8a9d95f67cf84b62b960aae1b964e9db
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 48e76359cf424f43b578f3098179d64f
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class TargetModeResolver
|
||||||
|
{
|
||||||
|
public TargetModeResolveResult Resolve(
|
||||||
|
LuckyDiceSelectedResult selected,
|
||||||
|
RollFlowRequest request,
|
||||||
|
int multiplier,
|
||||||
|
IReadOnlyList<TargetModeConfig> definitions)
|
||||||
|
{
|
||||||
|
if (selected == null)
|
||||||
|
{
|
||||||
|
return TargetModeResolveResult.Failed("MissingLuckyDiceCandidate");
|
||||||
|
}
|
||||||
|
|
||||||
|
var definition = (definitions ?? Array.Empty<TargetModeConfig>())
|
||||||
|
.FirstOrDefault(item =>
|
||||||
|
item.ResultKey == selected.ResultKey &&
|
||||||
|
item.TargetKey == selected.TargetKey);
|
||||||
|
|
||||||
|
if (definition == null)
|
||||||
|
{
|
||||||
|
return TargetModeResolveResult.Failed("MissingTargetMode");
|
||||||
|
}
|
||||||
|
|
||||||
|
return TargetModeResolveResult.Resolved(TargetModeEntry.From(definition, request, multiplier));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c8560c502d0b42cd856b23761cf7ea4b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
9
FishDice/Assets/FishDice/Runtime/LuckyDice/Trace.meta
Normal file
9
FishDice/Assets/FishDice/Runtime/LuckyDice/Trace.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d407eb4e046b45ff99f112e6646ab1d0
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
121
FishDice/Assets/FishDice/Runtime/LuckyDice/Trace/RollTrace.cs
Normal file
121
FishDice/Assets/FishDice/Runtime/LuckyDice/Trace/RollTrace.cs
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace FishDice.LuckyDice
|
||||||
|
{
|
||||||
|
public sealed class RollTrace
|
||||||
|
{
|
||||||
|
private readonly List<string> _executedActions = new List<string>();
|
||||||
|
private readonly List<ComboRuleMatch> _matchedRules = new List<ComboRuleMatch>();
|
||||||
|
private readonly List<RollActionResult> _actionResults = new List<RollActionResult>();
|
||||||
|
|
||||||
|
public RollTrace(string rollSessionId)
|
||||||
|
{
|
||||||
|
RollSessionId = rollSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string RollSessionId { get; }
|
||||||
|
|
||||||
|
public IReadOnlyList<string> NormalFaces { get; private set; }
|
||||||
|
|
||||||
|
public int NormalDiceCount { get; private set; }
|
||||||
|
|
||||||
|
public string ComboNormalizedKey { get; private set; }
|
||||||
|
|
||||||
|
public string MatchedRuleId { get; private set; }
|
||||||
|
|
||||||
|
public int? MatchedRulePriority { get; private set; }
|
||||||
|
|
||||||
|
public IReadOnlyList<ComboRuleMatch> MatchedRules => _matchedRules;
|
||||||
|
|
||||||
|
public IReadOnlyList<string> ExecutedActions => _executedActions;
|
||||||
|
|
||||||
|
public IReadOnlyList<RollActionResult> ActionResults => _actionResults;
|
||||||
|
|
||||||
|
public int LuckyDiceInitialCandidateCount { get; private set; }
|
||||||
|
|
||||||
|
public int LuckyDiceFilteredCandidateCount { get; private set; }
|
||||||
|
|
||||||
|
public string LuckyDiceFallbackType { get; private set; }
|
||||||
|
|
||||||
|
public int? LuckyDiceRandomValue { get; private set; }
|
||||||
|
|
||||||
|
public string LuckyDiceSelectedResultKey { get; private set; }
|
||||||
|
|
||||||
|
public string LuckyDiceSelectedTargetKey { get; private set; }
|
||||||
|
|
||||||
|
public int LuckyDiceResultSlotCount { get; private set; }
|
||||||
|
|
||||||
|
public IReadOnlyList<string> LuckyDiceSlotResultKeys { get; private set; }
|
||||||
|
|
||||||
|
public string ResolvedTargetKey { get; private set; }
|
||||||
|
|
||||||
|
public string ResolvedModeKey { get; private set; }
|
||||||
|
|
||||||
|
public string TargetModeFailureReason { get; private set; }
|
||||||
|
|
||||||
|
public string FinalOutcome { get; private set; }
|
||||||
|
|
||||||
|
public void RecordComboFacts(ComboFacts facts)
|
||||||
|
{
|
||||||
|
NormalFaces = facts.Faces.Select(face => face.Key).ToArray();
|
||||||
|
NormalDiceCount = facts.DiceCount;
|
||||||
|
ComboNormalizedKey = facts.NormalizedKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecordRuleMatch(ComboRuleMatchResult matchResult)
|
||||||
|
{
|
||||||
|
_matchedRules.Clear();
|
||||||
|
_matchedRules.AddRange(matchResult.Matches);
|
||||||
|
|
||||||
|
var first = matchResult.Matches.FirstOrDefault();
|
||||||
|
if (first == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatchedRuleId = first.RuleId;
|
||||||
|
MatchedRulePriority = first.Priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecordActionResult(RollActionResult result)
|
||||||
|
{
|
||||||
|
_executedActions.Add(result.ActionType);
|
||||||
|
_actionResults.Add(result);
|
||||||
|
if (result.Success && !string.IsNullOrEmpty(result.Outcome))
|
||||||
|
{
|
||||||
|
FinalOutcome = result.Outcome;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecordLuckyDiceSelection(LuckyDiceSelectionResult result)
|
||||||
|
{
|
||||||
|
LuckyDiceInitialCandidateCount = result.InitialCandidateCount;
|
||||||
|
LuckyDiceFilteredCandidateCount = result.FilteredCandidateCount;
|
||||||
|
LuckyDiceFallbackType = result.FallbackType;
|
||||||
|
LuckyDiceRandomValue = result.RandomValue;
|
||||||
|
LuckyDiceSelectedResultKey = result.Selected == null ? null : result.Selected.ResultKey;
|
||||||
|
LuckyDiceSelectedTargetKey = result.Selected == null ? null : result.Selected.TargetKey;
|
||||||
|
LuckyDiceResultSlotCount = result.SlotCount;
|
||||||
|
LuckyDiceSlotResultKeys = result.SlotResultKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecordTargetMode(TargetModeEntry entry)
|
||||||
|
{
|
||||||
|
if (entry == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResolvedTargetKey = entry.TargetKey;
|
||||||
|
ResolvedModeKey = entry.ModeKey;
|
||||||
|
FinalOutcome = "TargetModeResolved";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RecordTargetModeFailure(string failureReason)
|
||||||
|
{
|
||||||
|
TargetModeFailureReason = failureReason;
|
||||||
|
FinalOutcome = "TargetModeFailed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d2c57c4ac2f54a638326e104f5afac01
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
9
FishDice/Assets/FishDice/Tests.meta
Normal file
9
FishDice/Assets/FishDice/Tests.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 243feaaf2c05440ebb1fff35768a653c
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
9
FishDice/Assets/FishDice/Tests/EditMode.meta
Normal file
9
FishDice/Assets/FishDice/Tests/EditMode.meta
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 020efc0ae0324888bb93be8b772cd5fd
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "FishDice.Tests.EditMode",
|
||||||
|
"rootNamespace": "FishDice.Tests",
|
||||||
|
"references": [
|
||||||
|
"FishDice.Runtime",
|
||||||
|
"FishDice.Demo"
|
||||||
|
],
|
||||||
|
"includePlatforms": [
|
||||||
|
"Editor"
|
||||||
|
],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": false,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false,
|
||||||
|
"optionalUnityReferences": [
|
||||||
|
"TestAssemblies"
|
||||||
|
]
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user